Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | initial commit of v0.4.3 of AI Empire |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | trunk |
Files: | files | file ages | folders |
SHA1: | 28693ba5f2fef65a48df55bf51306291 |
User & Date: | pollen 2019-06-19 04:29:05 |
2019-06-19
| ||
04:29 | initial commit of v0.4.3 of AI Empire Leaf check-in: 28693ba5f2 user: pollen tags: trunk | |
04:07 | initial empty check-in check-in: bf6341cca7 user: pollen tags: trunk | |
Added data/skin definitions/default.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 |
Font: Small = DroidSans_8 Font: Normal = DroidSans_11 Font: Bold = DroidSans_11_Bold Font: Italic = OpenSans_11_Italic Font: Medium = DroidSans_16 Font: Big = DroidSans_20 Font: Subsys = DroidSans_11 Font: Name = GoodTimes_9 Font: Detail = DroidSans_8 Font: Title = GoodTimes_18 Font: Subtitle = DroidSans_14 Font: Button = DroidSans_11 Color: Text = #fff Color: Selected = #a0000080 Color: Disabled = #888888ff Color: ButtonText = #fff // {{{ Generic Styles Style: RoundedBox Element: Normal Rect: [3,3][182,39] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [3,43][182,79] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active Rect: [3,83][182,119] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered, Active Inherit: RoundedBox, Active Element: Disabled Rect: [195,343][374,379] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: StraightBox Element: Normal Rect: [194,4][373,39] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active Rect: [194,84][373,119] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [194,44][373,79] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active, Hovered Rect: [194,124][373,159] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: TinyBox Element: Normal Rect: [194,4][373,39] Margin: 2 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [194,84][373,119] Margin: 2 Vertical: Scaled Horizontal: Scaled Style: ItemBox Element: Normal Rect: [3,124][182,159] Margin: 4 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [3,164][182,199] Margin: 4 Vertical: Scaled Horizontal: Scaled Element: Active Rect: [3,204][182,239] Margin: 4 Vertical: Scaled Horizontal: Scaled Element: Hovered, Active Rect: [3,699][182,734] Margin: 4 Vertical: Scaled Horizontal: Scaled Element: Disabled Rect: [3,124][182,159] Margin: 4 Vertical: Scaled Horizontal: Scaled Add Gradient: TopLeft : #00000020 TopRight: #00000020 BotLeft : #00000020 BotRight: #00000020 GX1: 0 GY1: 0 GX2: 100% GY2: 100% Style: InputBox Element: Normal Rect: [2,493][181,528] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active Rect: [2,533][181,568] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [2,573][181,608] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active, Hovered Rect: [2,613][181,648] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Disabled Rect: [2,653][181,692] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: PlainBox Element: Normal Rect: [400,40][426,61] Margin: 2 Horizontal: Scaled Vertical: Scaled Style: PlainNameBox Element: Normal Rect: [429,42][459,56] Margin: 16,14 Horizontal: Scaled Style: HexPattern Element: Normal Rect: [390,11][412,36] Margin: 1 Vertical: Tiled Horizontal: Tiled Style: SmallHexPattern Element: Normal Rect: [390,40][395,56] Margin: 0 Vertical: Tiled Horizontal: Tiled Style: PatternBox Element: Normal Layer: PlainBox Layer: SmallHexPattern Color Override: #ffffff40 OX1: +3 OY1: +3 OX2: -3 OY2: -3 GradientMode: Overlay Add Gradient: TopLeft : #4443 TopRight: #4443 BotLeft : #2221 BotRight: #2221 GX1: +3 GY1: +3 GX2: -3 GY2: -3 Style: Panel Element: Normal Rect: [195,179][375,215] Margin: 6 Vertical: Scaled Horizontal: Scaled Add Gradient: TopLeft : #1b1b1bff TopRight: #1b1b1bff BotLeft : #101010ff BotRight: #101010ff GX1: +4 GY1: 50% GX2: -5 GY2: -5 Style: HorizBar Element: Normal Rect: [205,221][365,257] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: PlainOverlay Element: Normal Rect: [400,40][426,61] Margin: 2 Horizontal: Scaled Vertical: Scaled Add Gradient: TopLeft : #1c1c1cff TopRight: #1c1c1cff BotLeft : #151515ff BotRight: #151515ff GX1: 1 GY1: 1 GX2: -1 GY2: -1 Element: Hovered Rect: [400,40][426,61] Margin: 2 Horizontal: Scaled Vertical: Scaled Add Gradient: TopLeft : #2c2c2cff TopRight: #2c2c2cff BotLeft : #202020ff BotRight: #202020ff GX1: 1 GY1: 1 GX2: -1 GY2: -1 Element: Disabled Inherit: PlainOverlay, Normal Style: OverlayBox Element: Normal Inherit: Panel Style: LightPanel Element: Normal Rect: [195,221][375,257] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: Highlight Element: Normal Rect: [5,740][182,773] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: Glow Element: Normal Rect: [4,780][184,849] Margin: 10 Vertical: Scaled Horizontal: Scaled Style: SubtleGlow Element: Normal Rect: [4,854][184,922] Margin: 10 Vertical: Scaled Horizontal: Scaled Style: HighlightPanel Element: Normal Inherit: Panel, Normal Element: Hovered Inherit: Panel, Normal Add Gradient: TopLeft : #2c2c2c40 TopRight: #2c2c2c40 BotLeft : #20202040 BotRight: #20202040 GX1: 3 GY1: 3 GX2: -3 GY2: -3 Style: RoundedTitle Element: Normal Rect: [195,264][393,294] Margin: 6, 6, 120, 6 Horizontal: Scaled Vertical: Scaled Style: CenterTitle Element: Normal Rect: [195,308][410,334] Margin: 90, 6 Horizontal: Scaled Vertical: Scaled Style: FullTitle Element: Normal Rect: [198,450][332,480] Margin: 6 Horizontal: Scaled Vertical: Scaled Style: PanelTitle Element: Normal Layer: FullTitle Color Override: #fff OX1: 0 OY1: 0 OX2: 100% OY2: 100% Layer: RoundedTitle OX1: 0 OY1: 0 OX2: 70% OY2: 100% Style: WindowTitle Inherit: FullTitle Style: SubTitle Element: Normal Rect: [201,487][327,517] Margin: 6 Horizontal: Scaled Vertical: Scaled Style: HorizAccent Element: Normal Rect: [197,385][302,439] Margin: 8 Horizontal: Scaled Vertical: Scaled // }}} // {{{ Generic Parts Style: DownArrow Element: Normal Rect: [443,6][455,19] Style: UpArrow Element: Normal Rect: [457,6][470,19] Style: RightArrow Element: Normal Rect: [429,6][441,19] Style: LeftArrow Element: Normal Rect: [416,6][428,19] Style: Field Inherit: PlainBox Style: FieldName Inherit: PlainNameBox Style: BG3D Element: Normal Rect: [202,526][446,636] Style: ProgressBarBG Element: Normal Rect: [382,70][488,80] Margin: 6, 4 Horizontal: Scaled Vertical: Scaled Style: ProgressBar Element: Normal Rect: [383,83][487,91] Margin: 3, 4 Horizontal: Scaled Vertical: Scaled Style: DragHandle Element: Normal Inherit: ItemBox, Active Style: ResizeHandle Element: Normal Inherit: ItemBox, Normal Element: Active Inherit: ItemBox, Active Style: Checkmark Element: Normal Rect: [419,23][430,34] // }}} // {{{ Basic GUI Elements Style: Button Inherit: RoundedBox Style: BaselineButton Element: Normal Rect: [3,3][182,35] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered Rect: [3,43][182,75] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Active Rect: [3,83][182,115] Margin: 6 Vertical: Scaled Horizontal: Scaled Element: Hovered, Active Inherit: BaselineButton, Active Element: Disabled Rect: [195,343][374,375] Margin: 6 Vertical: Scaled Horizontal: Scaled Style: IconButton Element: Normal Element: Hovered Inherit: Highlight Element: Active Inherit: Highlight Element: Hovered,Active Inherit: Highlight Style: IconToggle Element: Normal Element: Hovered Inherit: Highlight Element: Active Inherit: Highlight Layer: Button Element: Hovered,Active Inherit: Highlight Layer: Button Style: Tab Inherit: Button Style: PageTab Element: Normal Layer: Button Element: Hovered Layer: Button Layer: SubtleGlow Color Override: #ffffff Element: Active Layer: Button Layer: SubtleGlow Style: Listbox Style: ListboxItem Inherit: ItemBox Style: StaticListboxItem Element: Normal Inherit: ItemBox, Normal Style: DropdownList Style: DropdownListItem Element: Normal Inherit: ItemBox Element: Hovered Inherit: ItemBox, Active Element: Active Inherit: ItemBox, Active Element: Active, Hovered Inherit: ItemBox, Active, Hovered Style: Dropdown Inherit: RoundedBox Style: DropdownArrow Element: Normal Layer: DownArrow OX1: +7 OY1: +7 OX2: -7 OY2: -7 Style: Dialog Element: Normal Inherit: Panel Style: Tooltip Element: Normal Rect: [2,454][181,489] Margin: 6 Horizontal: Scaled Vertical: Scaled Style: Textbox Inherit: InputBox Element: Focused Inherit: InputBox, Active Style: HoverTextbox Element: Normal Element: Disabled Element: Disabled, Hovered Element: Hovered Inherit: InputBox Element: Focused Inherit: InputBox Element: Hovered, Focused Inherit: InputBox, Active Style: HoverButton Element: Normal Element: Disabled Element: Disabled, Hovered Element: Active Inherit: Button, Active Element: Hovered Inherit: Button Element: Hovered, Active Inherit: Button, Active Style: GlowButton Element: Normal Inherit: PlainBox Element: Hovered Layer: PlainBox Layer: Highlight Color Override: #00c0ff Element: Hovered, Disabled Inherit: PlainBox Element: Active GradientMode: Overlay Layer: PlainBox Add Gradient: TopLeft : #2228 TopRight: #2228 BotLeft : #00c0ff48 BotRight: #00c0ff48 GX1: 0 GY1: 0 GX2: 100% GY2: 100% Element: Active, Hovered Layer: GlowButton, Active Layer: Highlight Color Override: #00c0ff Style: TabButton Element: Normal GradientMode: Overlay Layer: PlainBox Add Gradient: TopLeft : #4448 TopRight: #4448 BotLeft : #2228 BotRight: #2228 GX1: 1 GY1: 1 GX2: -1 GY2: -1 Element: Hovered GradientMode: Overlay Layer: TabButton, Normal Add Gradient: TopLeft : #fff0 TopRight: #fff0 BotLeft : #fff3 BotRight: #fff3 GX1: 1 GY1: 1 GX2: -1 GY2: -1 Element: Active GradientMode: Overlay Layer: PlainBox Color Override: #00c0ff Add Gradient: TopLeft : #2228 TopRight: #2228 BotLeft : #00c0ff48 BotRight: #00c0ff48 GX1: 1 GY1: 1 GX2: -1 GY2: -1 Element: Active, Hovered GradientMode: Overlay Layer: TabButton, Active Add Gradient: TopLeft : #fff0 TopRight: #fff0 BotLeft : #fff2 BotRight: #fff2 GX1: 1 GY1: 1 GX2: -1 GY2: -1 Style: AccordionHeader Element: Normal Inherit: PatternBox Element: Hovered Inherit: PatternBox Layer: Highlight Element: Active Inherit: PatternBox Element: Hovered, Active Inherit: PatternBox Layer: Highlight Element: Disabled GradientMode: Overlay Add Gradient: TopLeft : #2221 TopRight: #2221 BotLeft : #4446 BotRight: #4446 GX1: 0 GY1: 0 GX2: 100% GY2: -2 Add Gradient: TopLeft : #444f TopRight: #444f BotLeft : #444f BotRight: #444f GX1: 0 GY1: -2 GX2: 100% GY2: 100% Style: BuildElement Element: Normal GradientMode: Overlay Add Gradient: TopLeft : #0000 TopRight: #0000 BotLeft : #2224 BotRight: #2224 GX1: 0 GY1: 0 GX2: 100% GY2: -1 Add Gradient: TopLeft : #222f TopRight: #222f BotLeft : #222f BotRight: #222f GX1: 0 GY1: -1 GX2: 100% GY2: 100% Element: Hovered GradientMode: Overlay Add Gradient: TopLeft : #222a TopRight: #222a BotLeft : #444a BotRight: #444a GX1: 0 GY1: 0 GX2: 100% GY2: -1 Add Gradient: TopLeft : #222f TopRight: #222f BotLeft : #222f BotRight: #222f GX1: 0 GY1: -1 GX2: 100% GY2: 100% Style: SpinButton Inherit: TinyBox Style: ContextMenu Style: ContextMenuItem Element: Normal Inherit: ItemBox Element: Hovered Inherit: ItemBox, Active Element: Active Inherit: ItemBox, Active Element: Active, Hovered Inherit: ItemBox, Active Style: Checkbox Element: Normal Inherit: ItemBox Element: Hovered Inherit: ItemBox, Hovered Element: Active Layer: ItemBox, Active Layer: Checkmark OX1: 4 OY1: 0 OX2: 100% OY2: -4 Element: Active, Hovered Layer: ItemBox, Active, Hovered Layer: Checkmark OX1: 4 OY1: 0 OX2: 100% OY2: -4 Style: Radiobox Element: Normal Inherit: ItemBox Element: Hovered Inherit: ItemBox, Hovered Element: Active Layer: ItemBox Layer: ItemBox, Active OX1: 20% OY1: 20% OX2: -20% OY2: -20% Element: Active, Hovered Layer: ItemBox, Hovered Layer: ItemBox, Active OX1: 20% OY1: 20% OX2: -20% OY2: -20% Style: ScrollVert Inherit: Panel Style: ScrollVertHandle Element: Normal Inherit: StraightBox Element: Hovered Inherit: StraightBox, Hovered Element: Active Inherit: StraightBox, Active Style: ScrollHoriz Inherit: Panel Style: ScrollHorizHandle Element: Normal Inherit: StraightBox Element: Hovered Inherit: StraightBox, Hovered Element: Active Inherit: StraightBox, Active Style: ScrollButton Inherit: Button Style: ScrollUp Element: Normal Layer: ScrollButton Layer: UpArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Hovered Layer: ScrollButton, Hovered Layer: UpArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Active Layer: ScrollButton, Active Layer: UpArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Style: ScrollDown Element: Normal Layer: ScrollButton Layer: DownArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Hovered Layer: ScrollButton, Hovered Layer: DownArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Active Layer: ScrollButton, Active Layer: DownArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Style: ScrollLeft Element: Normal Layer: ScrollButton Layer: LeftArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Hovered Layer: ScrollButton, Hovered Layer: LeftArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Active Layer: ScrollButton, Active Layer: LeftArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Style: ScrollRight Element: Normal Layer: ScrollButton Layer: RightArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Hovered Layer: ScrollButton, Hovered Layer: RightArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Element: Active Layer: ScrollButton, Active Layer: RightArrow OX1: +4 OY1: +4 OX2: -4 OY2: -4 Style: DistributionBar Inherit: StraightBox Style: DistributionElement Element: Normal GradientMode: Overlay Add Gradient: TopLeft : #bbb9 TopRight: #bbb9 BotLeft : #4449 BotRight: #4449 GX1: 0 GY1: 0 GX2: 100% GY2: 100% Style: ChoiceBox Inherit: ItemBox // }}} // {{{ Tab Bar Style: GameTabBar Element: Normal Rect: [1,400] [100,424] Margin: 0 Vertical: Scaled Horizontal: Scaled Style: GameTab Element: Normal Rect: [2,345] [86,371] Margin: 17, 0 Horizontal: Scaled Element: Hovered Rect: [2,317] [86,343] Margin: 17, 0 Horizontal: Scaled Element: Pressed Inherit: GameTab, Normal Element: Active Rect: [2,372] [86,398] AspectMargin: Horizontal Margin: 17,0 Horizontal: Scaled Style: GameTabClose Element: Normal Rect: [3,429][17,443] Element: Hovered Rect: [17,429][31,443] Element: Active Rect: [32,429][46,443] Style: GameTabNew Element: Normal Rect: [138,350][169,369] Margin: 9 Horizontal: Scaled Element: Hovered Rect: [106,350][136,369] Margin: 9 Horizontal: Scaled Element: Active Rect: [138,373][169,392] Margin: 9 Horizontal: Scaled Style: HomeIcon Element: Normal Rect: [105, 300][136, 319] Element: Hovered Rect: [138, 300][169, 319] Element: Active Rect: [105, 324][136,343] Style: GoIcon Element: Normal Rect: [105,253][136,272] Element: Hovered Rect: [138,253][169,272] Element: Active Rect: [105,277][136,296] Style: GalaxyIcon Element: Normal Rect: [132,428] [157,451] Vertical: Uniform Horizontal: Uniform Style: PlanetIcon Inherit: GalaxyIcon Style: SupportIcon Inherit: GalaxyIcon Style: SystemIcon Inherit: GalaxyIcon Style: DesignsIcon Element: Normal Rect: [94,429] [119,451] Vertical: Uniform Horizontal: Uniform Style: ResearchIcon Element: Normal Rect: [62,428] [87,451] Vertical: Uniform Horizontal: Uniform Style: GlobalBar Element: Normal Rect: [107,374] [136,425] Margin: 4 Vertical: Scaled Horizontal: Scaled Add Gradient: TopLeft : #1b1b1b44 TopRight: #1b1b1b44 BotLeft : #00000044 BotRight: #00000044 GX1: 1 GY1: 1 GX2: -1 GY2: -1 Style: GoDialog Element: Normal Inherit: Panel Style: GoItem Element: Normal Inherit: ItemBox Element: Active Inherit: ItemBox, Active Element: Hovered Inherit: ItemBox, Hovered Element: Active, Hovered Inherit: ItemBox, Active, Hovered // }}} // {{{ Global Bar Style: BudgetProgress Inherit: ProgressBarBG Style: BudgetProgressBar Element: Normal Inherit: ProgressBar Style: ResearchProgress Inherit: ProgressBarBG Style: ResearchProgressBar Element: Normal Inherit: ProgressBar Style: Notification Element: Normal Inherit: PlainBox Style: TimeDisplay Element: Normal Inherit: PlainBox Layer: SmallHexPattern OX1: +3 OY1: +3 OX2: -3 OY2: -3 // }}} // {{{ AI Empire Tab Style: AIEmpireBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #333f TopRight: #333f BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% Style: ResearchField Inherit: HighlightPanel // }}} // {{{ Research Tab Style: ResearchBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #212e TopRight: #212e BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% Style: ResearchField Inherit: HighlightPanel // }}} // {{{ Design Tabs Style: DesignOverviewBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #112e TopRight: #112e BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% Style: DesignClassHeader Element: Normal Inherit: PanelTitle Style: DesignClass Element: Normal Inherit: Panel Style: DesignBorder Element: Normal Inherit: Panel Element: Hovered Inherit: Panel Add Gradient: TopLeft : #1112 TopRight: #1112 BotLeft : #aaa2 BotRight: #aaa2 GX1: +4 GY1: +4 GX2: -4 GY2: -4 Element: Active Inherit: Panel Add Gradient: TopLeft : #fff3 TopRight: #fff3 BotLeft : #fff3 BotRight: #fff3 GX1: 0 GY1: 0 GX2: 100% GY2: +4 Add Gradient: TopLeft : #fff3 TopRight: #fff3 BotLeft : #fff3 BotRight: #fff3 GX1: 0 GY1: -4 GX2: 100% GY2: 100% Add Gradient: TopLeft : #fff3 TopRight: #fff3 BotLeft : #fff3 BotRight: #fff3 GX1: 0 GY1: +4 GX2: +4 GY2: -4 Add Gradient: TopLeft : #fff3 TopRight: #fff3 BotLeft : #fff3 BotRight: #fff3 GX1: -4 GY1: +4 GX2: 100% GY2: -4 Element: Hovered, Active Inherit: DesignBorder, Active Add Gradient: TopLeft : #1112 TopRight: #1112 BotLeft : #aaa2 BotRight: #aaa2 GX1: +4 GY1: +4 GX2: -4 GY2: -4 Style: DesignGradient Element: Normal GradientMode: Overlay Add Gradient: TopLeft : #aaa3 TopRight: #8883 BotLeft : #8883 BotRight: #2223 GX1: 0 GY1: 0 GX2: 100% GY2: 100% Style: DesignSummary Element: Normal Layer: DesignBorder, Normal Color Override: #fff Layer: DesignGradient OX1: +4 OY1: +4 OX2: -4 OY2: -4 Layer: PlainBox OX1: 2 OY1: -34 OX2: -2 OY2: -2 Style: DesignEditorBG Inherit: DesignOverviewBG Style: DesignNavigationClass Inherit: LightPanel Style: DesignNavigationIcon Inherit: RoundedBox Style: ModuleButton Inherit: Button // }}} // {{{ Diplomacy Tabs Style: DiplomacyBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #121e TopRight: #121e BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% Style: EmpireBox Element: Normal Layer: RoundedBox Layer: Panel Color Override: #fff OX1: +4 OY1: +4 OX2: -4 OY2: -4 Style: PlayerEmpireBox Inherit: EmpireBox Style: DelegationBox Element: Normal Layer: RoundedBox Color Override: #fff OX1: 0 OY1: 4 OX2: 100% OY2: -4 Layer: RoundedBox OX1: 0 OY1: 0 OX2: -46 OY2: 100% Style: VotingBox Element: Normal Inherit: PlainBox Style: VoteTotal Element: Normal Layer: PlainBox Style: InfluenceVoteBox Inherit: PatternBox Style: TreatyBox Inherit: PatternBox Style: InfluenceEffectBox Inherit: PatternBox // }}} // {{{ Planet Tab Style: QueueBackground Inherit: SmallHexPattern Style: ConstructionBox Inherit: Panel Style: PlanetBox Element: Normal Layer: RoundedBox Color Override: #ffffff80 Style: PlanetElement Element: Normal Layer: Panel OX1: 0 OY1: 0 OX2: 100% OY2: 100% Layer: PlainBox OX1: +4 OY1: +4 OX2: -4 OY2: -4 // }}} // {{{ System Tab Style: SystemListBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #210e TopRight: #210e BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% Style: SystemPanel Element: Normal Layer: Panel Color Override: #fff Layer: PanelTitle OX1: +1 OY1: +1 OX2: -2 OY2: +31 Style: PlanetBar Element: Normal Inherit: PlainBox // }}} // {{{ Support Tab Style: GroupPanel Inherit: Panel Style: GroupSupportClass Inherit: PatternBox // }}} // {{{ Wiki Tab Style: WikiBG Element: Normal Inherit: HexPattern Add Gradient: TopLeft : #211e TopRight: #211e BotLeft : #000e BotRight: #000e GX1: 0% GY1: 0% GX2: 100% GY2: 100% // }}} // {{{ Popups Style: PopupBG Element: Normal Layer: Panel Color Override: #fff Layer: SubTitle OX1: +2 OY1: +1 OX2: -3 OY2: 25 Layer: BG3D OX1: +3 OY1: +24 OX2: -4 OY2: -35 Style: ShipPopupBG Element: Normal Layer: Panel Color Override: #fff Layer: SubTitle OX1: +2 OY1: +1 OX2: -3 OY2: 25 Layer: BG3D OX1: +3 OY1: +24 OX2: -4 OY2: -80 Style: GenericPopupBG Element: Normal Layer: Panel Color Override: #fff Layer: SubTitle OX1: +2 OY1: +1 OX2: -3 OY2: 25 Layer: BG3D OX1: +3 OY1: +24 OX2: -4 OY2: -4 Style: SelectablePopup Inherit: PopupBG Style: ManageButton Element: Normal Rect: [203,644][280,664] // }}} // {{{ Info Bar Style: InfoBar Element: Normal Inherit: PlainBox Style: InfoBarPlain Element: Normal Layer: Panel Color Override: #fff OX1: 0 OY1: 0 OX2: 100% OY2: 130% // }}} // {{{ Main Menu Style: MapSelectorItem Element: Normal Inherit: Panel Element: Hovered Inherit: Panel, Active Style: EmpireSetupItem Element: Normal Layer: Panel OX1: 0 OY1: 0 OX2: 100% OY2: 100% Style: GalaxySetupItem Element: Normal Layer: PatternBox OX1: 0 OY1: 0 OX2: 100% OY2: 100% Layer: Panel OX1: 0 OY1: 0 OX2: 100% OY2: 40 Style: MainMenuPanel Element: Normal Layer: Panel Color Override: #ffffffff Style: MainMenuDescPanel Inherit: MainMenuPanel Style: MainMenuItem Element: Normal Element: Hovered Inherit: ItemBox, Active Element: Active Inherit: PlainBox, Normal Add Gradient: TopLeft : #aa444440 TopRight: #aa444440 BotLeft : #aa444440 BotRight: #aa444440 GX1: 0 GY1: 0 GX2: 100% GY2: 100% Element: Hovered, Active Inherit: ItemBox, Active // }}} |
Added logo.png.
cannot compute difference between binary files
Added modinfo.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Name: AI Empire Compatibility: 200 Description: << Forces the AI on for player Empires, allows configuration of player AI (difficulty, cheats) at the new game screen, and allows runtime configuration of what roles the AI manages. [h1]Current version: 0.4.3[/h1] [h1][b]Defects[/b][/h1] - I'm having a lot of trouble preventing all the construction that the AI wants to do. In particular it buys a factory right away and it'll fill up fleets for you. - The tab UI hasn't gotten a lot of love yet. - This probably doesn't save/load well. - This probably doesn't work with multiplayer well. [h1][b]Changelog[/b][/h1] - 0.4.3 - clean up the UI a bit. controllable: scuttling. - 0.4.2 - controllable: artifact use - 0.4.1 - controls are now also in AI's race and FTL components - 0.4.0 - controllable: construction. 'diplomacy' now includes treaties and war/peace declarations. - controllable: scouting and also (Anomaly Resolution) whether scouts will decide for themselves which option to pick after scanning an anomaly. - 0.3.3 - minor fixes, depend on Missing Expansion rather than directly on the community patch - 0.3.0 - added 'prevent achievements' button. Doesn't turn cheats on, but flags game as having ever had cheats on. - 0.2.0 - first version with AI controls - 'AI Empire' tab added - controllable: diplomacy, colonization, remnant hunting, research also, defense of systems and attacks on enemy systems when at war - all disabled initially - 0.1.0 - initial version, AI is on for player but you have no control over it >> Derives From: Missing Expansion Override: scripts/menu/new_game.as Override: scripts/server/empire_ai/EmpireAI.as Override: scripts/server/empire_ai/weasel Override: scripts/server/cheats.as Override: data/skin definitions/default.txt |
Added scripts/gui/tabs/AIEmpireTab.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
import tabs.Tab; import elements.GuiButton; import elements.GuiPanel; import elements.GuiMarkupText; import icons; from tabs.tabbar import newTab, switchToTab; const Color colorForbidden = Color(0xaaaaaaff); const Color colorAllowed = colors::Energy; Tab@ createAIEmpireTab() { return AIEmpireTab(); } void init() { Tab@ tab = createAIEmpireTab(); newTab(tab); cheatCommandAI(playerEmpire, "forbid all"); } class AIEmpireTab : Tab { GuiPanel@ panel; GuiButton@ forbidDiplomacyButton; GuiMarkupText@ forbidDiplomacyText; bool forbidDiplomacy; GuiButton@ forbidColonizationButton; GuiMarkupText@ forbidColonizationText; bool forbidColonization; GuiButton@ forbidCreepingButton; GuiMarkupText@ forbidCreepingText; bool forbidCreeping; GuiButton@ forbidResearchButton; GuiMarkupText@ forbidResearchText; bool forbidResearch; GuiButton@ forbidDefenseButton; GuiMarkupText@ forbidDefenseText; bool forbidDefense; GuiButton@ forbidAttackButton; GuiMarkupText@ forbidAttackText; bool forbidAttack; GuiButton@ forbidConstructionButton; GuiMarkupText@ forbidConstructionText; bool forbidConstruction; GuiButton@ forbidScoutingButton; GuiMarkupText@ forbidScoutingText; bool forbidScouting; GuiButton@ forbidAnomalyChoiceButton; GuiMarkupText@ forbidAnomalyChoiceText; bool forbidAnomalyChoice; GuiButton@ forbidArtifactButton; GuiMarkupText@ forbidArtifactText; bool forbidArtifact; GuiButton@ forbidScuttleButton; GuiMarkupText@ forbidScuttleText; bool forbidScuttle; GuiButton@ preventAchievementsButton; GuiMarkupText@ preventAchievementsText; bool preventAchievements; GuiButton@ enableAIButton; GuiMarkupText@ enableAIText; GuiButton@ disableAIButton; GuiMarkupText@ disableAIText; AIEmpireTab() { super(); title = "AI Empire"; forbidDiplomacy = true; forbidColonization = true; forbidCreeping = true; forbidResearch = true; forbidDefense = true; forbidAttack = true; forbidConstruction = true; forbidScouting = true; forbidAnomalyChoice = true; forbidArtifact = true; forbidScuttle = true; preventAchievements = false; @panel = GuiPanel(this, Alignment()); @forbidDiplomacyButton = GuiButton(panel, recti_area(15,50, 120,25)); @forbidDiplomacyText = GuiMarkupText(forbidDiplomacyButton, Alignment(Left, Top, Right, Bottom)); forbidDiplomacyText.text = "[center]Diplomacy[/center]"; forbidDiplomacyButton.color = colorForbidden; @preventAchievementsButton = GuiButton(panel, recti_area(0,370, 200,25)); @preventAchievementsText = GuiMarkupText(preventAchievementsButton, Alignment(Left, Top, Right, Bottom)); preventAchievementsText.text = "[center]Prevent Achievements[/center]"; preventAchievementsButton.color = colorForbidden; @forbidColonizationButton = GuiButton(panel, recti_area(15,80, 120,25)); @forbidColonizationText = GuiMarkupText(forbidColonizationButton, Alignment(Left, Top, Right, Bottom)); forbidColonizationText.text = "[center]Colonization[/center]"; forbidColonizationButton.color = colorForbidden; @forbidCreepingButton = GuiButton(panel, recti_area(15,110, 120,25)); @forbidCreepingText = GuiMarkupText(forbidCreepingButton, Alignment(Left, Top, Right, Bottom)); forbidCreepingText.text = "[center]Remnant Hunting[/center]"; forbidCreepingButton.color = colorForbidden; @forbidResearchButton = GuiButton(panel, recti_area(15,140, 120,25)); @forbidResearchText = GuiMarkupText(forbidResearchButton, Alignment(Left, Top, Right, Bottom)); forbidResearchText.text = "[center]Research[/center]"; forbidResearchButton.color = colorForbidden; @forbidDefenseButton = GuiButton(panel, recti_area(15,230, 120,25)); @forbidDefenseText = GuiMarkupText(forbidDefenseButton, Alignment(Left, Top, Right, Bottom)); forbidDefenseText.text = "[center]Wartime Defense[/center]"; forbidDefenseButton.color = colorForbidden; @forbidAttackButton = GuiButton(panel, recti_area(15,200, 120,25)); @forbidAttackText = GuiMarkupText(forbidAttackButton, Alignment(Left, Top, Right, Bottom)); forbidAttackText.text = "[center]Wartime Offense[/center]"; forbidAttackButton.color = colorForbidden; @forbidConstructionButton = GuiButton(panel, recti_area(15+200,50, 120,25)); @forbidConstructionText = GuiMarkupText(forbidConstructionButton, Alignment(Left, Top, Right, Bottom)); forbidConstructionText.text = "[center]Construction[/center]"; forbidConstructionButton.color = colorForbidden; @forbidScoutingButton = GuiButton(panel, recti_area(15,170, 120,25)); @forbidScoutingText = GuiMarkupText(forbidScoutingButton, Alignment(Left, Top, Right, Bottom)); forbidScoutingText.text = "[center]Scouting[/center]"; forbidScoutingButton.color = colorForbidden; @forbidAnomalyChoiceButton = GuiButton(panel, recti_area(15,260, 200,25)); @forbidAnomalyChoiceText = GuiMarkupText(forbidAnomalyChoiceButton, Alignment(Left, Top, Right, Bottom)); forbidAnomalyChoiceText.text = "[center]Anomaly Resolution[/center]"; forbidAnomalyChoiceButton.color = colorForbidden; @forbidArtifactButton = GuiButton(panel, recti_area(15,290, 120,25)); @forbidArtifactText = GuiMarkupText(forbidArtifactButton, Alignment(Left, Top, Right, Bottom)); forbidArtifactText.text = "[center]Artifact Use[/center]"; forbidArtifactButton.color = colorForbidden; @forbidScuttleButton = GuiButton(panel, recti_area(15,320, 120,25)); @forbidScuttleText = GuiMarkupText(forbidScuttleButton, Alignment(Left, Top, Right, Bottom)); forbidScuttleText.text = "[center]Scuttling[/center]"; forbidScuttleButton.color = colorForbidden; @enableAIButton = GuiButton(panel, recti_area(0,0, 60,25)); @enableAIText = GuiMarkupText(enableAIButton, Alignment(Left, Top, Right, Bottom)); enableAIText.text = "[center]All on[/center]"; enableAIButton.color = colorAllowed; @disableAIButton = GuiButton(panel, recti_area(70,0, 60,25)); @disableAIText = GuiMarkupText(disableAIButton, Alignment(Left, Top, Right, Bottom)); disableAIText.text = "[center]All off[/center]"; disableAIButton.color = colorForbidden; } void tick(double time) override { } bool onGuiEvent(const GuiEvent& event) { if (event.type == GUI_Clicked) { if (event.caller is enableAIButton) { cheatCommandAI(playerEmpire, "allow all"); setAll(colorAllowed, false); return true; } else if (event.caller is disableAIButton) { cheatCommandAI(playerEmpire, "forbid all"); setAll(colorForbidden, true); return true; } else if (event.caller is forbidDiplomacyButton) { toggle(forbidDiplomacyButton, forbidDiplomacy, "Diplomacy"); return true; } else if (event.caller is forbidColonizationButton) { toggle(forbidColonizationButton, forbidColonization, "Colonization"); return true; } else if (event.caller is forbidCreepingButton) { toggle(forbidCreepingButton, forbidCreeping, "Creeping"); return true; } else if (event.caller is forbidResearchButton) { toggle(forbidResearchButton, forbidResearch, "Research"); return true; } else if (event.caller is forbidDefenseButton) { toggle(forbidDefenseButton, forbidDefense, "Defense"); return true; } else if (event.caller is forbidAttackButton) { toggle(forbidAttackButton, forbidAttack, "Attack"); return true; } else if (event.caller is forbidConstructionButton) { toggle(forbidConstructionButton, forbidConstruction, "Construction"); return true; } else if (event.caller is forbidScoutingButton) { toggle(forbidScoutingButton, forbidScouting, "Scouting"); return true; } else if (event.caller is forbidAnomalyChoiceButton) { toggle(forbidAnomalyChoiceButton, forbidAnomalyChoice, "AnomalyChoice"); return true; } else if (event.caller is forbidArtifactButton) { toggle(forbidArtifactButton, forbidArtifact, "Artifact"); return true; } else if (event.caller is forbidScuttleButton) { toggle(forbidScuttleButton, forbidScuttle, "Scuttle"); return true; } else if (event.caller is preventAchievementsButton) { cheatCommandAI(playerEmpire, "no achievements"); preventAchievementsButton.color = colorAllowed; preventAchievements = true; return true; } } return BaseGuiElement::onGuiEvent(event); } void toggle (GuiButton& btn, bool& flag, string cmd) { if (flag) { btn.color = colorAllowed; cheatCommandAI(playerEmpire, "allow " + cmd); flag = false; } else { btn.color = colorForbidden; cheatCommandAI(playerEmpire, "forbid " + cmd); flag = true; } } void setAll(Color color, bool b) { forbidDiplomacy = b; forbidColonization = b; forbidCreeping = b; forbidResearch = b; forbidDefense = b; forbidAttack = b; forbidConstruction = b; forbidScouting = b; forbidAnomalyChoice = b; forbidArtifact = b; forbidScuttle = b; forbidDiplomacyButton.color = color; forbidColonizationButton.color = color; forbidCreepingButton.color = color; forbidResearchButton.color = color; forbidDefenseButton.color = color; forbidAttackButton.color = color; forbidConstructionButton.color = color; forbidScoutingButton.color = color; forbidAnomalyChoiceButton.color = color; forbidArtifactButton.color = color; forbidScuttleButton.color = color; } void draw() { skin.draw(SS_AIEmpireBG, SF_Normal, AbsolutePosition); Tab::draw(); } } |
Added scripts/menu/new_game.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 |
import menus; import elements.BaseGuiElement; import elements.GuiButton; import elements.GuiPanel; import elements.GuiOverlay; import elements.GuiSprite; import elements.GuiText; import elements.GuiTextbox; import elements.GuiSpinbox; import elements.GuiCheckbox; import elements.GuiDropdown; import elements.GuiContextMenu; import elements.GuiIconGrid; import elements.GuiEmpire; import elements.GuiMarkupText; import elements.MarkupTooltip; import elements.GuiBackgroundPanel; import dialogs.SaveDialog; import dialogs.LoadDialog; import dialogs.MessageDialog; import dialogs.QuestionDialog; import util.settings_page; import empire_data; import traits; import icons; from util.draw_model import drawLitModel; import void showMultiplayer() from "multiplayer_menu"; from maps import Map, maps, mapCount, getMap; import settings.game_settings; import util.game_options; const int EMPIRE_SETUP_HEIGHT = 96; const int GALAXY_SETUP_HEIGHT = 200; const int REC_MAX_PEREMP = 25; const int REC_MAX_OPTIMAL = 150; const int REC_MAX_BAD = 400; const int REC_MAX_OHGOD = 1000; const array<Color> QDIFF_COLORS = {Color(0x00ff00ff), Color(0x1197e0ff), Color(0xff0000ff)}; const array<string> QDIFF_NAMES = {locale::AI_DIFF_EASY, locale::AI_DIFF_NORMAL, locale::AI_DIFF_HARD}; const array<string> QDIFF_DESC = {locale::AI_DIFF_EASY_DESC, locale::AI_DIFF_NORMAL_DESC, locale::AI_DIFF_HARD_DESC}; const array<Sprite> QDIFF_ICONS = {Sprite(spritesheet::AIDifficulty, 0), Sprite(spritesheet::AIDifficulty, 1), Sprite(spritesheet::AIDifficulty, 2)}; NameGenerator empireNames; bool empireNamesInitialized = false; class ConfirmStart : QuestionDialogCallback { void questionCallback(QuestionDialog@ dialog, int answer) { if(answer == QA_Yes) { new_game.start(); hideNewGame(true); } } }; class NewGame : BaseGuiElement { GameSettings settings; GuiBackgroundPanel@ empireBG; GuiBackgroundPanel@ gameBG; GuiBackgroundPanel@ chatBG; GuiButton@ backButton; GuiButton@ inviteButton; GuiButton@ playButton; EmpirePortraitCreation portraits; int nextEmpNum = 1; GuiPanel@ empirePanel; EmpireSetup@[] empires; GuiButton@ addAIButton; GuiSkinElement@ gameHeader; GuiButton@ mapsButton; array<GuiButton@> settingsButtons; array<GuiPanel@> settingsPanels; GuiButton@ resetButton; GuiPanel@ galaxyPanel; GalaxySetup@[] galaxies; GuiButton@ addGalaxyButton; GuiPanel@ mapPanel; GuiText@ mapHeader; GuiListbox@ mapList; GuiPanel@ chatPanel; GuiMarkupText@ chatLog; GuiTextbox@ chatBox; bool animating = false; bool hide = false; bool fromMP = false; bool choosingMap = false; string chatMessages; NewGame() { super(null, recti()); @empireBG = GuiBackgroundPanel(this, Alignment( Left+0.05f, Top+0.1f, Left+0.5f-6, Bottom-0.1f)); empireBG.title = locale::MENU_EMPIRES; empireBG.titleColor = Color(0x00ffe9ff); @gameBG = GuiBackgroundPanel(this, Alignment( Left+0.5f+6, Top+0.1f, Left+0.95f, Bottom-0.1f)); @gameHeader = GuiSkinElement(gameBG, Alignment(Left+1, Top+1, Right-2, Top+41), SS_FullTitle); @mapsButton = GuiButton(gameHeader, Alignment(Left, Top+1, Width=200, Height=38)); mapsButton.text = locale::MENU_GALAXIES; mapsButton.buttonIcon = Sprite(material::SystemUnderAttack); mapsButton.toggleButton = true; mapsButton.font = FT_Medium; mapsButton.pressed = true; mapsButton.style = SS_TabButton; @chatBG = GuiBackgroundPanel(this, Alignment( Left+0.05f, Bottom-0.1f-250, Left+0.5f-6, Bottom-0.1f)); chatBG.title = locale::CHAT; chatBG.titleColor = Color(0xff8000ff); chatBG.visible = false; //Empire list @empirePanel = GuiPanel(empireBG, Alignment(Left, Top+34, Right, Bottom-4)); //Game settings for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) { auto@ panel = GuiPanel(gameBG, Alignment(Left, Top+46, Right, Bottom-40)); panel.visible = false; settingsPanels.insertLast(panel); auto@ page = GAME_SETTINGS_PAGES[i]; page.create(panel); auto@ button = GuiButton(gameHeader, Alignment(Left+200+(i*200), Top+1, Width=200, Height=38)); button.text = page.header; button.buttonIcon = page.icon; button.toggleButton = true; button.pressed = false; button.font = FT_Medium; button.style = SS_TabButton; settingsButtons.insertLast(button); } @resetButton = GuiButton(gameBG, Alignment(Left+0.5f-120, Bottom-40, Width=240, Height=35), locale::NG_RESET); resetButton.color = Color(0xff8080ff); resetButton.buttonIcon = icons::Reset; resetButton.visible = false; //Galaxy list @galaxyPanel = GuiPanel(gameBG, Alignment(Left, Top+46, Right, Bottom-4)); galaxyPanel.visible = false; @addGalaxyButton = GuiButton(galaxyPanel, recti_area(vec2i(), vec2i(260, 36)), locale::ADD_GALAXY); addGalaxyButton.buttonIcon = Sprite(spritesheet::CardCategoryIcons, 3); //Maps choice list @mapPanel = GuiPanel(gameBG, Alignment(Left, Top+46, Right, Bottom-4)); mapPanel.visible = true; choosingMap = true; @mapHeader = GuiText(mapPanel, Alignment(Left, Top, Right, Top+30)); mapHeader.font = FT_Medium; mapHeader.horizAlign = 0.5; mapHeader.stroke = colors::Black; mapHeader.text = locale::CHOOSE_MAP; @mapList = GuiListbox(mapPanel, Alignment(Left+4, Top+34, Right-4, Bottom-4)); mapList.itemStyle = SS_DropdownListItem; mapList.itemHeight = 100; updateMapList(); //Chat @chatPanel = GuiPanel(chatBG, Alignment(Left+8, Top+34, Right-8, Bottom-38)); @chatLog = GuiMarkupText(chatPanel, recti_area(0, 0, 100, 100)); @chatBox = GuiTextbox(chatBG, Alignment(Left+6, Bottom-36, Right-6, Bottom-6)); //Actions @playButton = GuiButton(this, Alignment( Right-0.05f-200, Bottom-0.1f+6, Width=200, Height=46), locale::START_GAME); playButton.buttonIcon = Sprite(spritesheet::MenuIcons, 9); @addAIButton = GuiButton(this, Alignment( Left+300, Bottom-0.1f+6, Width=200, Height=46), locale::ADD_AI); addAIButton.buttonIcon = icons::Add; @backButton = GuiButton(this, Alignment( Left+0.05f, Bottom-0.1f+6, Width=200, Height=46), locale::BACK); backButton.buttonIcon = Sprite(spritesheet::MenuIcons, 11); @inviteButton = GuiButton(this, Alignment( Left+0.05f+408, Bottom-0.1f+6, Width=200, Height=46), locale::INVITE_FRIEND); inviteButton.buttonIcon = Sprite(spritesheet::MenuIcons, 13); inviteButton.visible = cloud::inLobby; updateAbsolutePosition(); } void updateMapList() { mapList.clearItems(); for(uint i = 0, cnt = mapCount; i < cnt; ++i) { auto@ mp = getMap(i); if(mp.isUnique) { bool found = false; for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { if(galaxies[i].mp.id == mp.id) { found = true; break; } } if(found) continue; } if(mp.isListed && !mp.isScenario && (mp.dlc.length == 0 || hasDLC(mp.dlc))) mapList.addItem(MapElement(mp)); } } void init() { if(!empireNamesInitialized) { empireNames.read("data/empire_names.txt"); empireNames.useGeneration = false; empireNamesInitialized = true; } portraits.reset(); clearEmpires(); addEmpire(true, getRacePreset(0)); if(!mpServer && !fromMP) { addEmpire(false); addEmpire(false); RaceChooser(empires[0], true); } updateAbsolutePosition(); switchPage(0); if(fromMP) { mapPanel.visible = false; galaxyPanel.visible = true; choosingMap = false; } else { mapPanel.visible = true; galaxyPanel.visible = false; choosingMap = true; updateMapList(); } addGalaxyButton.visible = !fromMP; addAIButton.visible = !fromMP; chatMessages = ""; if(fromMP) { playButton.text = locale::MP_NOT_READY; playButton.color = colors::Orange; } else { playButton.text = locale::START_GAME; playButton.color = colors::White; } } void addChat(const string& str) { chatMessages += str+"\n"; bool wasBottom = chatPanel.vert.pos >= (chatPanel.vert.end - chatPanel.vert.page); chatLog.text = chatMessages; chatPanel.updateAbsolutePosition(); if(wasBottom) { chatPanel.vert.pos = max(0.0, chatPanel.vert.end - chatPanel.vert.page); chatPanel.updateAbsolutePosition(); } } void resetAIColors() { for(uint i = 0, cnt = empires.length; i < cnt; ++i) { auto@ setup = empires[i]; if(setup.player) continue; setup.settings.color = colors::Invisible; } for(uint i = 0, cnt = empires.length; i < cnt; ++i) { auto@ setup = empires[i]; if(setup.player) continue; setUniqueColor(setup); } } void resetAIRaces() { for(uint i = 0, cnt = empires.length; i < cnt; ++i) { auto@ setup = empires[i]; if(setup.player) continue; setup.settings.raceName = ""; } for(uint i = 0, cnt = empires.length; i < cnt; ++i) { auto@ setup = empires[i]; if(setup.player) continue; setup.applyRace(getUniquePreset()); } } RacePreset@ getUniquePreset() { uint index = randomi(0, getRacePresetCount() - 1); for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) { auto@ preset = getRacePreset((index+i) % cnt); if(preset.dlc.length != 0 && !hasDLC(preset.dlc)) continue; bool has = false; for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { if(empires[n].settings.raceName == preset.name) { has = true; break; } } if(!has) { return preset; } } for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) { auto@ preset = getRacePreset((index+i) % cnt); if(preset.dlc.length != 0 && !hasDLC(preset.dlc)) continue; return preset; } return getRacePreset(index); } void setUniqueColor(EmpireSetup@ setup) { bool found = false; Color setColor; for(uint i = 0, cnt = getEmpireColorCount(); i < cnt; ++i) { Color col = getEmpireColor(i).color; bool has = false; for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { if(empires[n] !is setup && empires[n].settings.color.color == col.color) { has = true; break; } } if(!has) { found = true; setColor = col; break; } } if(!found) { Colorf rnd; rnd.fromHSV(randomd(0, 360.0), randomd(0.5, 1.0), 1.0); setColor = Color(rnd); } setup.settings.color = setColor; setup.update(); } void tick(double time) { if(mapIcons.length == 0) { mapIcons.length = mapCount; for(uint i = 0, cnt = mapCount; i < cnt; ++i) { auto@ mp = getMap(i); if(mp.isListed && !mp.isScenario && mp.icon.length != 0) mapIcons[i].load(mp.icon); } } inviteButton.visible = cloud::inLobby; addAIButton.disabled = empires.length >= 28; if(mpServer) { bool allReady = true; for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { auto@ emp = empires[n]; if(emp.playerId != -1 && emp.playerId != CURRENT_PLAYER.id) { emp.found = false; if(!emp.settings.ready) allReady = false; } } array<Player@>@ players = getPlayers(); for(uint i = 0, cnt = players.length; i < cnt; ++i) { Player@ pl = players[i]; if(pl == CURRENT_PLAYER) continue; //Find if we already have an empire bool found = false; for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { auto@ emp = empires[n]; if(emp.playerId == pl.id) { emp.found = true; found = true; if(pl.name.length != 0 && emp.name.text.length == 0) emp.name.text = pl.name; } } if(!found) { auto@ emp = addEmpire(false, getRacePreset(0)); emp.name.text = pl.name; emp.address = pl.address; emp.setPlayer(pl.id); } } //Prune disconnected players for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { auto@ emp = empires[n]; if(emp.playerId != -1 && !emp.found) { removeEmpire(emp); --n; --ncnt; } } //Update play button if(allReady) playButton.color = colors::Green; else playButton.color = colors::Orange; } else if(fromMP) { if(game_running) { hideNewGame(true); switchToMenu(main_menu, snap=true); return; } if(awaitingGalaxy) { hideNewGame(true); switchToMenu(main_menu, snap=true); showMultiplayer(); return; } auto@ pl = findPlayer(CURRENT_PLAYER.id); if(pl !is null && pl.settings.ready) { playButton.text = locale::MP_READY; playButton.color = colors::Green; } else { playButton.text = locale::MP_NOT_READY; playButton.color = colors::Orange; } if(!mpIsConnected()) { message("Lost connection to server:\n " +localize("DISCONNECT_"+uint(mpDisconnectReason))); hideNewGame(true); switchToMenu(main_menu, snap=true); showMultiplayer(); } } } EmpireSetup@ addEmpire(bool player = false, const RacePreset@ preset = null) { if(empires.length >= 28) return null; empireBG.title = locale::MENU_EMPIRES + " (" + (empires.length + 1) + ")"; uint y = empires.length * (EMPIRE_SETUP_HEIGHT + 8) + 8; EmpireSetup@ emp = EmpireSetup(this, Alignment(Left+4, Top+y, Right-4, Top+y + EMPIRE_SETUP_HEIGHT), player); portraits.randomize(emp.settings); if(player && settings::sNickname.length != 0) emp.name.text = settings::sNickname; else emp.name.text = "Empire "+(nextEmpNum++); if(preset is null) { if(player) @preset = getRacePreset(0); else @preset = getUniquePreset(); } emp.defaultName = emp.name.text; emp.update(); empires.insertLast(emp); empirePanel.updateAbsolutePosition(); if(preset !is null) emp.applyRace(preset); else if(!player) emp.resetName(); if(!player) setUniqueColor(emp); return emp; } EmpireSetup@ findPlayer(int id) { for(uint i = 0, cnt = empires.length; i < cnt; ++i) { if(empires[i].playerId == id) return empires[i]; } return null; } void clearEmpires() { for(uint i = 0, cnt = empires.length; i < cnt; ++i) empires[i].remove(); empires.length = 0; nextEmpNum = 2; updateEmpirePositions(); } void removeEmpire(EmpireSetup@ emp) { emp.remove(); empires.remove(emp); updateEmpirePositions(); } void updateEmpirePositions() { uint cnt = empires.length; for(uint i = 0; i < cnt; ++i) { EmpireSetup@ emp = empires[i]; emp.alignment.top.pixels = i * (EMPIRE_SETUP_HEIGHT + 8) + 8; emp.alignment.bottom.pixels = emp.alignment.top.pixels + EMPIRE_SETUP_HEIGHT; emp.updateAbsolutePosition(); } } GalaxySetup@ addGalaxy(Map@ mp) { uint y = galaxies.length * (GALAXY_SETUP_HEIGHT + 8) + 8; GalaxySetup@ glx = GalaxySetup(this, Alignment(Left+8, Top+y, Right-8, Top+y + GALAXY_SETUP_HEIGHT), mp); if(mp.eatsPlayers) { for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].setHomeworlds(false); } else { bool haveEating = false; for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { if(galaxies[i].mp.eatsPlayers) { haveEating = true; } } if(haveEating) glx.setHomeworlds(false); } addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, y + GALAXY_SETUP_HEIGHT); galaxies.insertLast(glx); galaxyPanel.updateAbsolutePosition(); updateGalaxyPositions(); return glx; } void removeGalaxy(GalaxySetup@ glx) { glx.remove(); galaxies.remove(glx); updateGalaxyPositions(); if(glx.mp.eatsPlayers) { bool haveEating = false; for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { if(galaxies[i].mp.eatsPlayers) { haveEating = true; } } if(!haveEating) { for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { galaxies[i].setHomeworlds(true); } } } if(galaxies.length == 0) { mapHeader.text = locale::CHOOSE_MAP; mapPanel.visible = true; galaxyPanel.visible = false; choosingMap = true; updateMapList(); } } void updateGalaxyPositions() { uint cnt = galaxies.length; for(uint i = 0; i < cnt; ++i) { GalaxySetup@ glx = galaxies[i]; glx.alignment.top.pixels = i * (GALAXY_SETUP_HEIGHT + 8) + 8; glx.alignment.bottom.pixels = glx.alignment.top.pixels + GALAXY_SETUP_HEIGHT; glx.updateAbsolutePosition(); } addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, cnt * (GALAXY_SETUP_HEIGHT + 8) + 6); galaxyPanel.updateAbsolutePosition(); } void apply() { apply(settings); } void reset() { uint newCnt = settings.empires.length; uint oldCnt = empires.length; for(uint i = newCnt; i < oldCnt; ++i) { removeEmpire(empires[i]); --i; --oldCnt; } for(uint i = 0; i < newCnt; ++i) { EmpireSetup@ setup; if(i >= oldCnt) @setup = addEmpire(); else @setup = empires[i]; auto@ sett = settings.empires[i]; if(setup.playerId == sett.playerId && setup.playerId == CURRENT_PLAYER.id) { if(setup.settings.delta > sett.delta) setup.apply(settings.empires[i]); else setup.load(settings.empires[i]); } else { setup.load(settings.empires[i]); } } updateEmpirePositions(); newCnt = settings.galaxies.length; oldCnt = galaxies.length; for(uint i = newCnt; i < oldCnt; ++i) { removeGalaxy(galaxies[i]); --i; --oldCnt; } for(uint i = 0; i < newCnt; ++i) { GalaxySetup@ setup; if(i >= oldCnt) @setup = addGalaxy(getMap(settings.galaxies[i].map_id)); else @setup = galaxies[i]; setup.load(settings.galaxies[i]); } updateGalaxyPositions(); for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) GAME_SETTINGS_PAGES[i].load(settings); addGalaxyButton.visible = !mpClient; addAIButton.visible = !mpClient; } void reset(GameSettings& settings) { this.settings = settings; reset(); } void apply(GameSettings& settings) { uint empCnt = empires.length; settings.empires.length = empCnt; for(uint i = 0; i < empCnt; ++i) { settings.empires[i].index = i; empires[i].apply(settings.empires[i]); } uint glxCnt = galaxies.length; settings.galaxies.length = glxCnt; for(uint i = 0; i < glxCnt; ++i) galaxies[i].apply(settings.galaxies[i]); for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) GAME_SETTINGS_PAGES[i].apply(settings); } void start(){ apply(); Message msg; settings.write(msg); startNewGame(msg); } void switchPage(uint page) { mapsButton.pressed = page == 0; galaxyPanel.visible = page == 0 && !choosingMap; mapPanel.visible = page == 0 && choosingMap; if(mapPanel.visible) updateMapList(); //if(page == 0) // gameHeader.color = Color(0xff003fff); resetButton.visible = page != 0 && !mpClient; for(uint i = 0, cnt = settingsButtons.length; i < cnt; ++i) { settingsButtons[i].pressed = page == i+1; settingsPanels[i].visible = page == i+1; //if(page == i+1) // gameHeader.color = GAME_SETTINGS_PAGES[i].color; } } bool onGuiEvent(const GuiEvent& event) { switch(event.type) { case GUI_Clicked: if(event.caller is playButton) { if(fromMP) { auto@ pl = findPlayer(CURRENT_PLAYER.id); if(pl !is null) { pl.settings.ready = !pl.settings.ready; pl.submit(); } } else { if(mpServer) { bool allReady = true; for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) { auto@ emp = empires[n]; if(emp.playerId != -1 && emp.playerId != CURRENT_PLAYER.id) { if(!emp.settings.ready) allReady = false; } } if(!allReady) { question(locale::MP_CONFIRM_NOT_READY, ConfirmStart()); return true; } } else { uint sysCount = 0; apply(); for(uint i = 0, cnt = settings.galaxies.length; i < cnt; ++i) sysCount += settings.galaxies[i].systemCount * settings.galaxies[i].galaxyCount; uint empCount = empires.length; if(sysCount > REC_MAX_OHGOD) { question(locale::NG_WARN_OHGOD, ConfirmStart()); return true; } else if(sysCount > REC_MAX_BAD) { question(locale::NG_WARN_BAD, ConfirmStart()); return true; } else if(sysCount > REC_MAX_OPTIMAL) { question(locale::NG_WARN_OPTIMAL, ConfirmStart()); return true; } else if(sysCount > REC_MAX_PEREMP * empCount) { question(locale::NG_WARN_PEREMP, ConfirmStart()); return true; } } start(); hideNewGame(true); } return true; } else if(event.caller is backButton) { if(!game_running) mpDisconnect(); hideNewGame(); return true; } else if(event.caller is inviteButton) { cloud::inviteFriend(); return true; } else if(event.caller is addAIButton) { addEmpire(); return true; } else if(event.caller is addGalaxyButton) { mapHeader.text = locale::ADD_GALAXY; mapPanel.visible = true; galaxyPanel.visible = false; updateMapList(); choosingMap = true; return true; } else if(event.caller is resetButton) { for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) { if(settingsPanels[i].visible) GAME_SETTINGS_PAGES[i].reset(); } return true; } else if(event.caller is mapsButton) { switchPage(0); return true; } else { for(uint i = 0, cnt = settingsButtons.length; i < cnt; ++i) { if(event.caller is settingsButtons[i]) { switchPage(i+1); return true; } } } break; case GUI_Confirmed: if(event.caller is chatBox) { string message = chatBox.text; if(message.length != 0) menuChat(message); chatBox.text = ""; } break; case GUI_Changed: if(event.caller is mapList) { if(mapList.selected != -1) addGalaxy(cast<MapElement>(mapList.selectedItem).mp); if(galaxies.length != 0) { mapList.clearSelection(); mapPanel.visible = false; galaxyPanel.visible = true; choosingMap = false; } return true; } break; case GUI_Animation_Complete: animating = false; return true; } return BaseGuiElement::onGuiEvent(event); } void updateAbsolutePosition() { if(!animating) { if(!hide) { size = parent.size; position = vec2i(0, 0); } else { size = parent.size; position = vec2i(size.x, 0); } } if(fromMP || mpServer) { chatBG.visible = true; chatLog.size = vec2i(chatPanel.size.width-20, chatLog.size.height); empireBG.alignment.bottom.pixels = 262; } else { chatBG.visible = false; empireBG.alignment.bottom.pixels = 0; } addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, addGalaxyButton.position.y); BaseGuiElement::updateAbsolutePosition(); } void animateIn() { animating = true; hide = false; rect = recti_area(vec2i(parent.size.x, 0), parent.size); animate_time(this, recti_area(vec2i(), parent.size), MSLIDE_TIME); } void animateOut() { animating = true; hide = true; rect = recti_area(vec2i(), parent.size); animate_time(this, recti_area(vec2i(parent.size.x, 0), parent.size), MSLIDE_TIME); } }; void drawRace(const Skin@ skin, const recti& absPos, const string& name, const string& portrait, const array<const Trait@>@ traits = null, bool showTraits = true) { const Font@ normal = skin.getFont(FT_Normal); const Font@ bold = skin.getFont(FT_Bold); recti namePos = recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.width * 0.35, absPos.height)); //Portrait auto@ prt = getEmpirePortrait(portrait); if(prt !is null) { prt.portrait.draw(recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.height, absPos.height))); namePos.topLeft.x += absPos.height+8; } //Race name bold.draw(pos=namePos, text=name); //FTL Method recti ftlPos = recti_area(absPos.topLeft + vec2i(absPos.width*0.35 + 16, 0), vec2i(absPos.width * 0.35, absPos.height)); //Traits if(traits !is null) { recti pos = recti_area(vec2i(absPos.botRight.x - 32, absPos.topLeft.y + 3), vec2i(24, 24)); for(uint i = 0, cnt = traits.length; i < cnt; ++i) { auto@ trait = traits[i]; if(trait.unique == "FTL") { trait.icon.draw(recti_area(ftlPos.topLeft, vec2i(absPos.height, absPos.height)).aspectAligned(trait.icon.aspect)); ftlPos.topLeft.x += absPos.height+8; normal.draw(text=trait.name, pos=ftlPos); } else if(showTraits) { traits[i].icon.draw(pos.aspectAligned(traits[i].icon.aspect)); pos -= vec2i(24, 0); } } } } Color colorFromNumber(int num) { float hue = (num*26534371)%360; Colorf col; col.fromHSV(hue, 1.f, 1.f); return Color(col); } class RaceElement : GuiListElement { const RacePreset@ preset; RaceElement(const RacePreset@ preset) { @this.preset = preset; } void draw(GuiListbox@ ele, uint flags, const recti& absPos) { drawRace(ele.skin, absPos, preset.name, preset.portrait, preset.traits); } }; class CustomRaceElement : GuiListElement { const EmpireSettings@ settings; CustomRaceElement(const EmpireSettings@ settings) { @this.settings = settings; } void draw(GuiListbox@ ele, uint flags, const recti& absPos) { drawRace(ele.skin, absPos, settings.raceName, settings.portrait, settings.traits); } }; class CurrentRaceElement : GuiListElement { EmpireSettings@ settings; bool valid = true; CurrentRaceElement(EmpireSettings@ settings) { @this.settings = settings; } void update() { valid = settings.getTraitPoints() >= 0 && !settings.hasTraitConflicts(); } void draw(GuiListbox@ ele, uint flags, const recti& absPos) { if(!valid) { Color color(0xff0000ff); color.a = abs((frameTime % 1.0) - 0.5) * 2.0 * 255.0; ele.skin.draw(SS_Button, SF_Normal, absPos.padded(-5, -3), color); } drawRace(ele.skin, absPos, settings.raceName, settings.portrait, traits=settings.traits, showTraits=false); } }; class CustomizeOption : GuiListElement { void draw(GuiListbox@ ele, uint flags, const recti& absPos) { const Font@ bold = ele.skin.getFont(FT_Bold); recti namePos = recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.width * 0.95, absPos.height)); icons::Customize.draw(recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.height, absPos.height))); namePos.topLeft.x += absPos.height+8; bold.draw(pos=namePos, text=locale::CUSTOMIZE_RACE, color=Color(0xff8000ff)); } }; class TraitList : GuiIconGrid { array<const Trait@> traits; TraitList(IGuiElement@ parent, Alignment@ align) { super(parent, align); MarkupTooltip tt(350, 0.f, true, true); tt.Lazy = true; tt.LazyUpdate = false; tt.Padding = 4; @tooltipObject = tt; } uint get_length() override { return traits.length; } string get_tooltip() override { if(hovered < 0 || hovered >= int(length)) return ""; auto@ trait = traits[hovered]; return format("[color=$1][b]$2[/b][/color]\n$3", toString(trait.color), trait.name, trait.description); } void drawElement(uint i, const recti& pos) override { traits[i].icon.draw(pos.aspectAligned(traits[i].icon.aspect)); } }; class ChangeWelfare : GuiContextOption { ChangeWelfare(const string& text, uint index) { value = int(index); this.text = text; icon = Sprite(spritesheet::ConvertIcon, index); } void call(GuiContextMenu@ menu) override { playerEmpire.WelfareMode = uint(value); } }; const Sprite[] DIFF_SPRITES = { Sprite(material::HappyFace), Sprite(material::StatusPeace), Sprite(material::StatusWar), Sprite(material::StatusCeaseFire), Sprite(spritesheet::AttributeIcons, 3), Sprite(spritesheet::AttributeIcons, 0), Sprite(spritesheet::VoteIcons, 3), Sprite(spritesheet::VoteIcons, 3, colors::Red) }; const Color[] DIFF_COLORS = { colors::Green, colors::White, colors::White, colors::White, colors::Orange, colors::Red, colors::Red, colors::Red }; const string[] DIFF_TOOLTIPS = { locale::DIFF_PASSIVE, locale::DIFF_EASY, locale::DIFF_NORMAL, locale::DIFF_HARD, locale::DIFF_MURDEROUS, locale::DIFF_CHEATING, locale::DIFF_SAVAGE, locale::DIFF_BARBARIC, }; class ChangeDifficulty : GuiMarkupContextOption { int level; EmpireSetup@ setup; ChangeDifficulty(EmpireSetup@ setup, int value, const string& text) { level = value; set(text); @this.setup = setup; } void call(GuiContextMenu@ menu) override { setup.settings.difficulty = level; setup.update(); } }; class ChangeTeam : GuiMarkupContextOption { int team; EmpireSetup@ setup; ChangeTeam(EmpireSetup@ setup, int value) { team = value; if(value >= 0) set(format("[b][color=$2]$1[/color][/b]", format(locale::TEAM_TEXT, toString(value)), toString(colorFromNumber(value)))); else set(format("[b][color=#aaa]$1...[/color][/b]", locale::NO_TEAM)); @this.setup = setup; } void call(GuiContextMenu@ menu) override { setup.settings.team = team; setup.submit(); } }; class Chooser : GuiIconGrid { Color spriteColor; array<Color> colors; array<Sprite> sprites; uint selected = 0; Chooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) { super(parent, align); horizAlign = 0.5; vertAlign = 0.0; iconSize = itemSize; updateAbsolutePosition(); } void add(const Color& col) { colors.insertLast(col); } void add(const Sprite& sprt) { sprites.insertLast(sprt); } uint get_length() override { return max(colors.length, sprites.length); } void drawElement(uint index, const recti& pos) override { if(uint(selected) == index) drawRectangle(pos, Color(0xffffff30)); if(uint(hovered) == index) drawRectangle(pos, Color(0xffffff30)); if(index < colors.length) drawRectangle(pos.padded(5), colors[index]); if(index < sprites.length) sprites[index].draw(pos, spriteColor); } }; class RaceChooser : GuiOverlay { EmpireSetup@ setup; GuiSkinElement@ panel; GuiText@ header; GuiPanel@ list; const RacePreset@ selectedRace; array<GuiButton@> presetButtons; array<const RacePreset@> racePresets; GuiSprite@ portrait; GuiSprite@ flag; GuiSprite@ bgDisplay; GuiPanel@ descScroll; GuiMarkupText@ description; GuiPanel@ loreScroll; GuiMarkupText@ lore; GuiButton@ playButton; GuiButton@ customizeButton; GuiButton@ randomizeButton; GuiButton@ loadButton; GuiButton@ backButton; bool isInitial; bool hasChosenRace = false; bool chosenShipset = false; Chooser@ flags; Chooser@ colors; ShipsetChooser@ shipsets; RaceChooser(EmpireSetup@ setup, bool isInitial = false) { @this.setup = setup; this.isInitial = isInitial; super(null); closeSelf = false; @panel = GuiSkinElement(this, Alignment(Left-4, Top+0.05f, Right+4, Bottom-0.05f), SS_Panel); @customizeButton = GuiButton(panel, Alignment(Right-232, Bottom-78, Width=220, Height=33)); customizeButton.text = locale::CUSTOMIZE_RACE; customizeButton.setIcon(icons::Edit); @randomizeButton = GuiButton(panel, Alignment(Right-452, Bottom-78, Width=220, Height=33)); randomizeButton.text = "Randomize Race"; @loadButton = GuiButton(panel, Alignment(Right-232, Bottom-78+33, Width=220, Height=33)); loadButton.text = locale::LOAD_CUSTOM_RACE; loadButton.setIcon(icons::Load); int w = 250, h = 140; int off = max((size.width - (getRacePresetCount() * w)) / 2 - 20, 0); GuiSkinElement listBG(panel, Alignment(Left-4, Top+12, Right+4, Top+154), SS_PlainBox); @list = GuiPanel(panel, Alignment(Left+off, Top+12, Right-off, Top+174)); updateAbsolutePosition(); vec2i pos; uint curSelection = 0; for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) { auto@ preset = getRacePreset(i); if(preset.dlc.length != 0 && !hasDLC(preset.dlc)) continue; racePresets.insertLast(preset); GuiButton btn(list, recti_area(pos.x, pos.y, w, h)); btn.toggleButton = true; btn.style = SS_GlowButton; btn.pressed = i == 0; GuiSprite icon(btn, recti_area(2, 2, w*0.75, h-4)); icon.horizAlign = 0.0; icon.vertAlign = 1.0; icon.desc = getSprite(preset.portrait); GuiText name(btn, recti_area(0, 0, w-4, h)); name.font = FT_Big; name.stroke = colors::Black; name.text = preset.name; name.vertAlign = 0.4; name.horizAlign = 0.9; GuiSkinElement tagbar(btn, recti_area(1, h-28, w-3, 24), SS_PlainBox); tagbar.color = Color(0xffffff80); GuiText tagline(btn, recti_area(0, h-30, w-4, 24)); tagline.font = FT_Italic; tagline.stroke = colors::Black; tagline.color = Color(0xaaaaaaff); tagline.text = preset.tagline; tagline.horizAlign = 1.0; TraitList traits(btn, Alignment(Left, Bottom-56, Right, Bottom-28)); traits.iconSize = vec2i(24, 24); traits.horizAlign = 1.0; traits.fallThrough = true; traits.traits = preset.traits; if(preset.equals(setup.settings)) { curSelection = i; hasChosenRace = true; } if(!setup.player && !preset.aiSupport) { icon.saturation = 0.f; traits.visible = false; btn.disabled = true; btn.color = Color(0xffffffaa); name.color = Color(0xaa3030ff); setMarkupTooltip(btn, locale::AI_CANNOT_PLAY); } presetButtons.insertLast(btn); pos.x += w; } BaseGuiElement leftBG(panel, Alignment(Left+12, Top+174, Left+0.33f-6, Bottom-90)); int y = 0; GuiSkinElement portBG(leftBG, Alignment(Left, Top+y, Right, Bottom), SS_PlainBox); @bgDisplay = GuiSprite(portBG, Alignment().padded(2), Sprite(getEmpireColor(setup.settings.color).background)); bgDisplay.color = Color(0xffffff80); bgDisplay.stretchOutside = true; @portrait = GuiSprite(portBG, Alignment(Left+2, Top, Right-2, Height=232)); portrait.horizAlign = 0.0; portrait.vertAlign = 1.0; @flag = GuiSprite(portBG, Alignment(Right-164, Top+4, Width=160, Height=160)); flag.horizAlign = 1.0; flag.vertAlign = 0.0; flag.color = setup.settings.color; flag.color.a = 0xc0; flag.desc = getSprite(setup.settings.flag); y += 220 + 12; GuiSkinElement colBG(leftBG, Alignment(Left, Top+y, Right, Height=34), SS_PlainBox); @colors = Chooser(colBG, Alignment().padded(8, 0), vec2i(48, 32)); for(uint i = 0, cnt = getEmpireColorCount(); i < cnt; ++i) { Color color = getEmpireColor(i).color; colors.add(color); if(color.color == setup.settings.color.color) colors.selected = i; } updateAbsolutePosition(); y += 34 + 12; GuiSkinElement flagBG(leftBG, Alignment(Left, Top+y, Right, Height=110), SS_PlainBox); @flags = Chooser(flagBG, Alignment().padded(8, 0), vec2i(48, 48)); flags.spriteColor = setup.settings.color; for(uint i = 0, cnt = getEmpireFlagCount(); i < cnt; ++i) { string flag = getSpriteDesc(Sprite(getEmpireFlag(i).flag)); flags.add(getSprite(flag)); if(flag == setup.settings.flag) flags.selected = i; } y += 110 + 12; // DOF - Adjust shipset selection box size. // Trying dynamic size based on number of shipsets. Target 6 per row, max 4 rows (don't want to get too tall for lower resolutions). uint SSBoxHeight = min(int(ceil(getShipsetCount()/8.0)),4) * 75; GuiSkinElement shipsetBG(leftBG, Alignment(Left, Top+y, Right, Height=SSBoxHeight), SS_PlainBox); @shipsets = ShipsetChooser(shipsetBG, Alignment().padded(8, 0), vec2i(160, 70)); shipsets.selectedColor = setup.settings.color; shipsets.selected = 0; shipsets.horizAlign = 0.0; for(uint i = 0, cnt = getShipsetCount(); i < cnt; ++i) { auto@ ss = getShipset(i); if(ss.available && (ss.dlc.length == 0 || hasDLC(ss.dlc))) shipsets.add(ss); if(ss.ident == setup.settings.shipset) shipsets.selected = shipsets.length-1; } GuiSkinElement loreBox(panel, Alignment(Left+0.33f+6, Top+174, Left+0.66f-6, Bottom-90), SS_PlainBox); @loreScroll = GuiPanel(loreBox, Alignment().fill()); @lore = GuiMarkupText(loreScroll, recti_area(12, 12, 376, 100)); lore.fitWidth = true; GuiSkinElement descBox(panel, Alignment(Left+0.66f+6, Top+174, Right-12, Bottom-90), SS_PlainBox); @descScroll = GuiPanel(descBox, Alignment().fill()); @description = GuiMarkupText(descScroll, recti_area(12, 12, 376, 100)); description.fitWidth = true; @playButton = GuiButton(panel, Alignment(Left+0.5f-150, Bottom-78, Left+0.5f+150, Bottom-12)); playButton.font = FT_Medium; playButton.color = Color(0x00c0ffff); @backButton = GuiButton(panel, Alignment(Left+12, Bottom-78, Left+220, Bottom-12), locale::BACK); backButton.font = FT_Medium; backButton.buttonIcon = icons::Back; selectRace(curSelection); updateAbsolutePosition(); updateAbsolutePosition(); } void updateAbsolutePosition() { if(shipsets !is null && shipsets.parent !is null) shipsets.parent.visible = screenSize.height >= 900; BaseGuiElement::updateAbsolutePosition(); } void close() override { if(isInitial) return; GuiOverlay::close(); } void selectRace(uint select) { for(uint i = 0, cnt = presetButtons.length; i < cnt; ++i) presetButtons[i].pressed = i == select; auto@ preset = racePresets[select]; string desc; if(preset.isHard) desc += format("[font=Subtitle][color=#ffc000]$1[/color][/font]", locale::RACE_IS_HARD); for(uint i = 0, cnt = preset.traits.length; i < cnt; ++i) { if(desc.length != 0) desc += "\n\n"; desc += format("[font=Medium]$1[/font][vspace=4/]\n[offset=20]", preset.traits[i].name); desc += preset.traits[i].description; desc += "[/offset]"; } description.text = desc; string txt = format("[font=Big]$1[/font]\n", preset.name); txt += format("[right][font=Medium][color=#aaa]$1[/color][/font][/right]\n\n", preset.tagline); txt += preset.lore; lore.text = txt; if(isInitial) playButton.text = format(locale::PLAY_AS_RACE, preset.name); else playButton.text = format(locale::CHOOSE_A_RACE, preset.name); playButton.buttonIcon = getSprite(preset.portrait); portrait.desc = getSprite(preset.portrait); loreScroll.updateAbsolutePosition(); descScroll.updateAbsolutePosition(); @selectedRace = preset; if(!chosenShipset) { setup.settings.shipset = preset.shipset; for(uint i = 0, cnt = shipsets.length; i < cnt; ++i) { if(shipsets.items[i].ident == preset.shipset) { shipsets.selected = i; break; } } } } bool onGuiEvent(const GuiEvent& evt) { if(evt.type == GUI_Clicked) { if(evt.caller is flags) { uint sel = flags.hovered; if(sel != uint(-1)) { string sprt = getSpriteDesc(Sprite(getEmpireFlag(sel).flag)); setup.settings.flag = sprt; flag.desc = getSprite(sprt); flags.selected = sel; } return true; } else if(evt.caller is colors) { uint sel = colors.hovered; if(sel != uint(-1)) { auto empCol = getEmpireColor(sel); Color col = empCol.color; setup.settings.color = col; bgDisplay.desc = Sprite(empCol.background); flag.color = col; flag.color.a = 0xc0; flags.spriteColor = col; shipsets.selectedColor = col; colors.selected = sel; } return true; } else if(evt.caller is shipsets) { uint sel = shipsets.hovered; if(sel != uint(-1)) { chosenShipset = true; setup.settings.shipset = shipsets.items[sel].ident; shipsets.selected = sel; } return true; } else if(evt.caller is customizeButton) { isInitial = false; if(hasChosenRace) { setup.applyRace(selectedRace); setup.submit(); } setup.openRaceWindow(); close(); return true; } else if(evt.caller is backButton) { if(isInitial) { isInitial = false; new_game.backButton.emitClicked(); close(); return true; } close(); return true; } else if(evt.caller is randomizeButton) { hasChosenRace = true; selectRace(randomi(0, presetButtons.length - 1)); } else if(evt.caller is loadButton) { isInitial = false; LoadRaceDialog(null, setup.settings, setup); close(); return true; } else if(evt.caller is playButton) { setup.applyRace(selectedRace); setup.submit(); if(isInitial) { isInitial = false; setup.resetName(); setup.ng.resetAIColors(); setup.ng.resetAIRaces(); } close(); return true; } else { for(uint i = 0, cnt = presetButtons.length; i < cnt; ++i) { if(evt.caller.isChildOf(presetButtons[i])) { hasChosenRace = true; selectRace(i); return true; } } } } return GuiOverlay::onGuiEvent(evt); } }; class EmpireSetup : BaseGuiElement, IGuiCallback { GuiButton@ portraitButton; GuiEmpire@ portrait; EmpireSettings settings; NewGame@ ng; GuiTextbox@ name; GuiButton@ removeButton; GuiText@ handicapLabel; GuiSpinbox@ handicap; GuiButton@ raceBox; GameAddress address; GuiButton@ colorButton; GuiButton@ flagButton; GuiButton@ difficulty; GuiButton@ aiSettings; GuiSprite@ aiIcon; GuiText@ aiText; GuiButton@ teamButton; GuiText@ raceName; GuiSprite@ raceFTLIcon; GuiText@ raceFTL; TraitList@ traitList; bool player; bool found = true; int playerId = -1; ChoosePopup@ popup; GuiSprite@ readyness; string defaultName; EmpireSetup(NewGame@ menu, Alignment@ align, bool Player = false) { super(menu.empirePanel, align); @portraitButton = GuiButton(this, Alignment(Left+8, Top+4, Left+EMPIRE_SETUP_HEIGHT, Bottom-4)); portraitButton.style = SS_NULL; @portrait = GuiEmpire(portraitButton, Alignment().fill()); @portrait.settings = settings; @ng = menu; @name = GuiTextbox(this, Alignment(Left+EMPIRE_SETUP_HEIGHT+8, Top+14, Right-310, Top+0.5f-4)); name.font = FT_Subtitle; name.style = SS_HoverTextbox; name.selectionColor = Color(0xffffff40); @colorButton = GuiButton(this, Alignment(Right-302, Top+14, Width=50, Height=30)); colorButton.style = SS_HoverButton; @flagButton = GuiButton(this, Alignment(Right-244, Top+14, Width=50, Height=30)); flagButton.style = SS_HoverButton; @teamButton = GuiButton(this, Alignment(Right-186, Top+14, Width=50, Height=30)); teamButton.style = SS_HoverButton; @difficulty = GuiButton(this, Alignment(Right-128, Top+14, Width=50, Height=30)); difficulty.style = SS_HoverButton; difficulty.visible = false; @aiSettings = GuiButton(this, Alignment(Right-128, Top+10, Width=66, Height=38)); aiSettings.style = SS_HoverButton; aiSettings.visible = false; @aiIcon = GuiSprite(aiSettings, Alignment().padded(1, 1, 1, 5)); @aiText = GuiText(aiSettings, Alignment()); aiText.horizAlign = 0.5; aiText.vertAlign = 0.2; aiText.font = FT_Small; aiText.stroke = colors::Black; @raceBox = GuiButton(this, Alignment(Left+EMPIRE_SETUP_HEIGHT+8, Top+0.5f+4, Right-8, Bottom-14)); raceBox.style = SS_HoverButton; @raceName = GuiText(raceBox, Alignment(Left+8, Top, Left+0.35f, Bottom)); raceName.font = FT_Bold; @raceFTLIcon = GuiSprite(raceBox, Alignment(Left+0.4f, Top, Left+0.4f+22, Bottom)); @raceFTL = GuiText(raceBox, Alignment(Left+0.4f+26, Top, Right-0.3f, Bottom)); @traitList = TraitList(raceBox, Alignment(Right-0.3f, Top+2, Right-30, Bottom)); traitList.iconSize = vec2i(24, 24); traitList.horizAlign = 1.0; traitList.fallThrough = true; player = Player; @removeButton = GuiButton(this, Alignment(Right-50, Top, Right, Top+30)); removeButton.color = colors::Red; removeButton.setIcon(icons::Remove); // if(!player) { removeButton.visible = true; aiSettings.visible = true; // } // else { // removeButton.visible = false; // playerId = 1; // } @readyness = GuiSprite(portrait, Alignment(Right-40, Bottom-40, Right, Bottom)); readyness.visible = false; applyRace(getRacePreset(randomi(0, getRacePresetCount()-1))); updateAbsolutePosition(); } void showDifficulties() { GuiContextMenu menu(mousePos); menu.itemHeight = 54; menu.addOption(ChangeDifficulty(this, 0, locale::DIFF_PASSIVE)); menu.addOption(ChangeDifficulty(this, 1, locale::DIFF_EASY)); menu.addOption(ChangeDifficulty(this, 2, locale::DIFF_NORMAL)); menu.addOption(ChangeDifficulty(this, 3, locale::DIFF_HARD)); menu.addOption(ChangeDifficulty(this, 4, locale::DIFF_MURDEROUS)); menu.addOption(ChangeDifficulty(this, 5, locale::DIFF_CHEATING)); menu.addOption(ChangeDifficulty(this, 6, locale::DIFF_SAVAGE)); menu.addOption(ChangeDifficulty(this, 7, locale::DIFF_BARBARIC)); menu.updateAbsolutePosition(); } void showAISettings() { AIPopup popup(aiSettings, this); aiSettings.Hovered = false; aiSettings.Pressed = false; } void showTeams() { GuiContextMenu menu(mousePos); menu.itemHeight = 30; //Figure out how many distinct teams we have uint distinctTeams = 0; uint teamMask = 0; int maxTeam = 0; for(uint i = 0, cnt = ng.empires.length; i < cnt; ++i) { int team = ng.empires[i].settings.team; if(team < 0) continue; maxTeam = max(maxTeam, team); uint mask = 1<<(team-1); if(mask & teamMask == 0) { teamMask |= mask; ++distinctTeams; } } //Add more teams than we currently have menu.addOption(ChangeTeam(this, -1)); for(uint i = 1; i <= min(max(distinctTeams+5, maxTeam+1), 30); ++i) menu.addOption(ChangeTeam(this, i)); menu.updateAbsolutePosition(); } void forceAITraits(EmpireSettings& settings) { for(uint i = 0, cnt = settings.traits.length; i < cnt; ++i) { auto@ trait = settings.traits[i]; if(!trait.aiSupport) { if(trait.unique.length == 0) { settings.traits.removeAt(i); --cnt; --i; } else { const Trait@ repl; uint replCount = 0; for(uint n = 0, ncnt = getTraitCount(); n < ncnt; ++n) { auto@ other = getTrait(n); if(other.unique == trait.unique && other.aiSupport && other.hasDLC) { replCount += 1; if(randomd() < 1.0 / double(replCount)) @repl = other; } } if(repl !is null) { @settings.traits[i] = repl; } else { settings.traits.removeAt(i); --cnt; --i; } } } } } void applyRace(const RacePreset@ preset) { preset.apply(settings); forceAITraits(settings); if(!player) { //forceAITraits(settings); if(defaultName == name.text) resetName(); } update(); } void applyRace(const EmpireSettings@ custom) { settings.copyRaceFrom(custom); forceAITraits(settings); if(!player) { //settings.copyRaceFrom(custom); if(defaultName == name.text) resetName(); } } void resetName() { string race = settings.raceName; if(race.startswith_nocase("the ")) race = race.substr(4); name.text = format(localize(empireNames.generate()), race); defaultName = name.text; } void setPlayer(int id) { player = id != -1; playerId = id; name.disabled = player || !mpClient; removeButton.visible = !mpClient && (!player || id != CURRENT_PLAYER.id); aiSettings.visible = !player; readyness.visible = player && id != 1; bool editable = id == CURRENT_PLAYER.id || (!mpClient && id == -1); raceBox.disabled = !editable; colorButton.disabled = !editable; flagButton.disabled = !editable; teamButton.disabled = !editable; aiSettings.disabled = !editable; } void openRaceWindow() { TraitsWindow win(this); } void update() { updateTraits(); if(difficulty.visible) { difficulty.color = DIFF_COLORS[settings.difficulty]; setMarkupTooltip(difficulty, locale::TT_DIFF+"\n"+DIFF_TOOLTIPS[settings.difficulty], width=300); if(difficulty.color.color != colors::White.color) difficulty.style = SS_Button; else difficulty.style = SS_HoverButton; } if(aiSettings.visible) { aiIcon.desc = QDIFF_ICONS[clamp(settings.difficulty, 0, 2)]; aiText.color = QDIFF_COLORS[clamp(settings.difficulty, 0, 2)]; aiText.text = getAIName(settings); } name.textColor = settings.color; raceName.text = settings.raceName; for(uint i = 0, cnt = settings.traits.length; i < cnt; ++i) { auto@ trait = settings.traits[i]; if(trait.unique == "FTL") { raceFTLIcon.desc = trait.icon; raceFTL.text = trait.name; } } } void updateTraits() { traitList.traits.length = 0; for(uint i = 0, cnt = getTraitCount(); i < cnt; ++i) { auto@ trait = getTrait(i); if(settings.hasTrait(trait) && trait.unique != "FTL") traitList.traits.insertLast(trait); } if(settings.ready) { readyness.tooltip = locale::MP_PLAYER_READY; readyness.desc = icons::Ready; } else { readyness.tooltip = locale::MP_PLAYER_NOT_READY; readyness.desc = icons::NotReady; } } void submit() { if(mpClient) changeEmpireSettings(settings); //if(!player) forceAITraits(settings); settings.delta += 1; update(); } bool onGuiEvent(const GuiEvent& evt) { switch(evt.type) { case GUI_Clicked: if(evt.caller is removeButton) { if(player) mpKick(playerId); else ng.removeEmpire(this); return true; } else if(evt.caller is difficulty) { showDifficulties(); return true; } else if(evt.caller is aiSettings) { showAISettings(); return true; } else if(evt.caller is teamButton) { showTeams(); return true; } else if(evt.caller is raceBox || evt.caller is portraitButton) { RaceChooser(this); raceBox.Hovered = false; return true; } else if(evt.caller is colorButton) { vec2i pos(evt.caller.absolutePosition.topLeft.x, evt.caller.absolutePosition.botRight.y); uint cnt = getEmpireColorCount(); vec2i size(220, ceil(double(cnt)/4.0) * 38.0); @popup = ChoosePopup(pos, size, vec2i(48, 32)); @popup.callback = this; popup.extraHeight = 60; ColorPicker picker(popup.overlay, recti_area(pos+vec2i(20,size.y+2), vec2i(size.x-40, 50))); @picker.callback = this; for(uint i = 0; i < cnt; ++i) popup.add(getEmpireColor(i).color); return true; } else if(evt.caller is flagButton) { vec2i pos(evt.caller.absolutePosition.topLeft.x, evt.caller.absolutePosition.botRight.y); uint cnt = getEmpireFlagCount(); vec2i size(220, ceil(double(cnt)/4.0) * 52.0); @popup = ChoosePopup(pos, size, vec2i(48, 48)); @popup.callback = this; popup.spriteColor = settings.color; for(uint i = 0; i < cnt; ++i) popup.add(Sprite(getEmpireFlag(i).flag)); return true; } else if(evt.caller is popup) { } break; case GUI_Confirmed: if(evt.caller is popup) { if(popup.colors.length > 0) settings.color = getEmpireColor(evt.value).color; else if(popup.sprites.length > 0) settings.flag = getEmpireFlag(evt.value).flagDef; @popup = null; submit(); return true; } if(cast<ColorPicker>(evt.caller) !is null) { settings.color = cast<ColorPicker>(evt.caller).picked; popup.remove(); @popup = null; submit(); } break; } return BaseGuiElement::onGuiEvent(evt); } void apply(EmpireSettings& es) { es = settings; es.name = name.text; es.playerId = playerId; //if(player || playerId != -1) // es.type = ET_Player; //else if(es.type == ET_Player) es.type = ET_WeaselAI; } void load(EmpireSettings& es) { name.text = es.name; player = es.type == uint(ET_Player); setPlayer(es.playerId); settings = es; update(); } void draw() { Color color = settings.color; skin.draw(SS_EmpireSetupItem, SF_Normal, AbsolutePosition.padded(-10, 0), color); BaseGuiElement::draw(); if(colorButton.visible) { setClip(colorButton.absoluteClipRect); drawRectangle(colorButton.absolutePosition.padded(6), color); } auto@ flag = getEmpireFlag(settings.flag); if(flag !is null && flagButton.visible) { setClip(flagButton.absoluteClipRect); flag.flag.draw(recti_centered(flagButton.absolutePosition, vec2i(flagButton.size.height, flagButton.size.height)), color); } if(difficulty.visible) { setClip(difficulty.absoluteClipRect); DIFF_SPRITES[settings.difficulty].draw(recti_centered(difficulty.absolutePosition, vec2i(difficulty.size.height, difficulty.size.height))); } if(teamButton.visible) { setClip(teamButton.absoluteClipRect); if(settings.team >= 0) { material::TabDiplomacy.draw(recti_centered(teamButton.absolutePosition, vec2i(teamButton.size.height, teamButton.size.height))); skin.getFont(FT_Small).draw( pos=teamButton.absolutePosition, text=locale::TEAM, horizAlign=0.5, vertAlign=0.0, stroke=colors::Black, color=colors::White); skin.getFont(FT_Medium).draw( pos=teamButton.absolutePosition, text=toString(settings.team), horizAlign=0.5, vertAlign=1.0, stroke=colors::Black, color=colorFromNumber(settings.team)); } else { shader::SATURATION_LEVEL = 0.f; material::TabDiplomacy.draw(recti_centered(teamButton.absolutePosition, vec2i(teamButton.size.height, teamButton.size.height)), Color(0xffffff80), shader::Desaturate); } } } }; string getAIName(EmpireSettings& settings) { string text; text = QDIFF_NAMES[clamp(settings.difficulty, 0, 2)]; if(settings.aiFlags & AIF_Passive != 0) text += "|"; if(settings.aiFlags & AIF_Aggressive != 0) text += "@"; if(settings.aiFlags & AIF_Biased != 0) text += "^"; if(settings.aiFlags & AIF_CheatPrivileged != 0) text += "$"; if(settings.type == ET_BumAI) text += "?"; int cheatLevel = 0; if(settings.cheatWealth > 0) cheatLevel += ceil(double(settings.cheatWealth) / 10.0); if(settings.cheatStrength > 0) cheatLevel += settings.cheatStrength; if(settings.cheatAbundance > 0) cheatLevel += settings.cheatAbundance; if(cheatLevel > 0) { if(cheatLevel > 3) cheatLevel = 3; while(cheatLevel > 0) { text += "+"; cheatLevel -= 1; } } return text; } class AIPopup : BaseGuiElement { GuiOverlay@ overlay; GuiListbox@ difficulties; GuiText@ behaveHeading; GuiText@ cheatHeading; EmpireSetup@ setup; GuiCheckbox@ aggressive; GuiCheckbox@ passive; GuiCheckbox@ biased; GuiCheckbox@ legacy; GuiCheckbox@ wealth; GuiSpinbox@ wealthAmt; GuiCheckbox@ strength; GuiSpinbox@ strengthAmt; GuiCheckbox@ abundance; GuiSpinbox@ abundanceAmt; GuiCheckbox@ privileged; GuiButton@ okButton; GuiButton@ applyToAllButton; AIPopup(IGuiElement@ around, EmpireSetup@ setup) { @overlay = GuiOverlay(null); overlay.closeSelf = false; overlay.fade.a = 0; @this.setup = setup; recti pos = recti_area( vec2i(around.absolutePosition.botRight.x, around.absolutePosition.topLeft.y), vec2i(600, 200)); if(pos.botRight.y > screenSize.y) pos += vec2i(0, screenSize.y - pos.botRight.y); if(pos.botRight.x > screenSize.x) pos += vec2i(screenSize.x - pos.botRight.x, 0); super(overlay, pos); updateAbsolutePosition(); setGuiFocus(this); @difficulties = GuiListbox(this, Alignment(Left+4, Top+4, Left+250, Bottom-4)); difficulties.required = true; difficulties.itemHeight = 64; for(uint i = 0; i < 3; ++i) { difficulties.addItem(GuiMarkupListText( format("[color=$1][font=Medium][stroke=#000]$2[/stroke][/font][/color]\n[color=#aaa][i]$3[/i][/color]", toString(QDIFF_COLORS[i]), QDIFF_NAMES[i], QDIFF_DESC[i]))); } @behaveHeading = GuiText(this, Alignment(Left+260, Top+6, Left+260+170, Top+36)); behaveHeading.font = FT_Medium; behaveHeading.stroke = colors::Black; behaveHeading.text = locale::AI_BEHAVIOR; pos = recti_area(vec2i(260, 36), vec2i(170, 30)); @aggressive = GuiCheckbox(this, pos, locale::AI_AGGRESSIVE); setMarkupTooltip(aggressive, locale::AI_AGGRESSIVE_DESC); pos += vec2i(0, 30); @passive = GuiCheckbox(this, pos, locale::AI_PASSIVE); setMarkupTooltip(passive, locale::AI_PASSIVE_DESC); pos += vec2i(0, 30); @biased = GuiCheckbox(this, pos, locale::AI_BIASED); setMarkupTooltip(biased, locale::AI_BIASED_DESC); pos += vec2i(0, 30); @legacy = GuiCheckbox(this, pos, locale::AI_LEGACY); legacy.textColor = Color(0xaaaaaaff); legacy.visible = !hasDLC("Heralds"); setMarkupTooltip(legacy, locale::AI_LEGACY_DESC); pos += vec2i(0, 30); @cheatHeading = GuiText(this, Alignment(Left+260+165, Top+6, Right-12, Top+36)); cheatHeading.font = FT_Medium; cheatHeading.stroke = colors::Black; cheatHeading.text = locale::AI_CHEATS; pos = recti_area(vec2i(260+165, 36), vec2i(170, 30)); @wealth = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_WEALTH); setMarkupTooltip(wealth, locale::AI_WEALTH_DESC); @wealthAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 10, 0, 1000, 1, 0); pos += vec2i(0, 30); @strength = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_STRENGTH); setMarkupTooltip(strength, locale::AI_STRENGTH_DESC); @strengthAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 1, 0, 100, 1, 0); pos += vec2i(0, 30); @abundance = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_ABUNDANCE); setMarkupTooltip(abundance, locale::AI_ABUNDANCE_DESC); @abundanceAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 1, 0, 100, 1, 0); pos += vec2i(0, 30); @privileged = GuiCheckbox(this, pos, locale::AI_PRIVILEGED); setMarkupTooltip(privileged, locale::AI_PRIVILEGED_DESC); pos += vec2i(0, 30); @okButton = GuiButton(this, Alignment(Left+260+165, Bottom-34, Width=70, Height=30), locale::OK); @applyToAllButton = GuiButton(this, Alignment(Left+260, Bottom-34, Width=120, Height=30), "Apply to all"); reset(); } void reset() { difficulties.selected = clamp(setup.settings.difficulty, 0, 2); aggressive.checked = setup.settings.aiFlags & AIF_Aggressive != 0; passive.checked = setup.settings.aiFlags & AIF_Passive != 0; biased.checked = setup.settings.aiFlags & AIF_Biased != 0; privileged.checked = setup.settings.aiFlags & AIF_CheatPrivileged != 0; legacy.checked = setup.settings.type == ET_BumAI; if(legacy.checked) legacy.visible = true; wealth.checked = setup.settings.cheatWealth > 0; wealthAmt.visible = wealth.checked; if(wealth.checked) wealthAmt.value = setup.settings.cheatWealth; strength.checked = setup.settings.cheatStrength > 0; strengthAmt.visible = strength.checked; if(strength.checked) strengthAmt.value = setup.settings.cheatStrength; abundance.checked = setup.settings.cheatAbundance > 0; abundanceAmt.visible = abundance.checked; if(abundance.checked) abundanceAmt.value = setup.settings.cheatAbundance; } void apply(bool toAll = false) { uint flags = 0; if(aggressive.checked) flags |= AIF_Aggressive; if(passive.checked) flags |= AIF_Passive; if(biased.checked) flags |= AIF_Biased; if(privileged.checked) flags |= AIF_CheatPrivileged; if(legacy.checked) setup.settings.type = ET_BumAI; else setup.settings.type = ET_WeaselAI; wealthAmt.visible = wealth.checked; if(wealthAmt.visible) setup.settings.cheatWealth = wealthAmt.value; else setup.settings.cheatWealth = 0; strengthAmt.visible = strength.checked; if(strengthAmt.visible) setup.settings.cheatStrength = strengthAmt.value; else setup.settings.cheatStrength = 0; abundanceAmt.visible = abundance.checked; if(abundanceAmt.visible) setup.settings.cheatAbundance = abundanceAmt.value; else setup.settings.cheatAbundance = 0; if (toAll) { for (uint i = 0; i < setup.ng.empires.length; i++) { setup.ng.empires[i].settings.difficulty = difficulties.selected; setup.ng.empires[i].settings.aiFlags = flags; setup.ng.empires[i].submit(); } } else { setup.settings.difficulty = difficulties.selected; setup.settings.aiFlags = flags; setup.submit(); } } bool onGuiEvent(const GuiEvent& evt) override { if(evt.type == GUI_Changed) { if(evt.caller is passive) { if(passive.checked) aggressive.checked = false; apply(); return true; } if(evt.caller is aggressive) { if(aggressive.checked) passive.checked = false; apply(); return true; } apply(); } if(evt.type == GUI_Clicked) { if(evt.caller is okButton) { apply(); remove(); return true; } else if(evt.caller is applyToAllButton) { apply(true); remove(); return true; } } return BaseGuiElement::onGuiEvent(evt); } void remove() { overlay.remove(); @overlay = null; BaseGuiElement::remove(); } void draw() override { clearClip(); skin.draw(SS_Panel, SF_Normal, AbsolutePosition); BaseGuiElement::draw(); } }; class ChoosePopup : GuiIconGrid { GuiOverlay@ overlay; int extraHeight = 0; Color spriteColor; array<Color> colors; array<Sprite> sprites; ChoosePopup(const vec2i& pos, const vec2i& size, const vec2i& itemSize) { @overlay = GuiOverlay(null); overlay.closeSelf = false; overlay.fade.a = 0; super(overlay, recti_area(pos, size)); horizAlign = 0.5; vertAlign = 0.0; iconSize = itemSize; updateAbsolutePosition(); } bool onGuiEvent(const GuiEvent& evt) override { if(evt.caller is this && evt.type == GUI_Clicked) { if(hovered != -1) emitConfirmed(uint(hovered)); overlay.close(); return true; } return GuiIconGrid::onGuiEvent(evt); } void remove() { overlay.remove(); @overlay = null; GuiIconGrid::remove(); } void add(const Color& col) { colors.insertLast(col); } void add(const Sprite& sprt) { sprites.insertLast(sprt); } uint get_length() override { return max(colors.length, sprites.length); } void drawElement(uint index, const recti& pos) override { if(uint(hovered) == index) drawRectangle(pos, Color(0xffffff30)); if(index < colors.length) drawRectangle(pos.padded(5), colors[index]); if(index < sprites.length) sprites[index].draw(pos, spriteColor); } void draw() override { clearClip(); skin.draw(SS_Panel, SF_Normal, AbsolutePosition.padded(0,0,0,-extraHeight)); GuiIconGrid::draw(); } }; class ColorPicker : BaseGuiElement { Color picked; bool pressed = false; ColorPicker(IGuiElement@ parent, const recti& pos) { super(parent, pos); updateAbsolutePosition(); } void draw() { shader::HSV_VALUE = 1.f; shader::HSV_SAT_START = 0.5f; shader::HSV_SAT_END = 1.f; drawRectangle(AbsolutePosition, material::HSVPalette, Color()); if(AbsolutePosition.isWithin(mousePos)) { clearClip(); recti area = recti_area(mousePos-vec2i(10), vec2i(20)); drawRectangle(area.padded(-1), colors::Black); drawRectangle(area, getColor(mousePos-AbsolutePosition.topLeft)); } BaseGuiElement::draw(); } Color getColor(vec2i offset) { Colorf col; float hue = float(offset.x) / float(AbsolutePosition.width) * 360.f; float sat = (1.f - float(offset.y) / float(AbsolutePosition.height)) * 0.5f + 0.5f; col.fromHSV(hue, sat, 1.f); col.a = 1.f; return Color(col); } bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) { if(event.type == MET_Button_Down || (event.type == MET_Moved && pressed)) { pressed = true; picked = getColor(mousePos - AbsolutePosition.topLeft); GuiEvent evt; @evt.caller = this; evt.type = GUI_Changed; onGuiEvent(evt); return true; } else if(pressed && event.type == MET_Button_Up) { pressed = false; emitConfirmed(); return true; } return BaseGuiElement::onMouseEvent(event, source); } }; class PortraitChooser : GuiIconGrid { array<Sprite> sprites; uint selected = 0; Color selectedColor; PortraitChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) { super(parent, align); horizAlign = 0.5; vertAlign = 0.0; iconSize = itemSize; updateAbsolutePosition(); } void add(const Sprite& sprt) { sprites.insertLast(sprt); } uint get_length() override { return sprites.length; } void drawElement(uint index, const recti& pos) override { if(selected == index) drawRectangle(pos, selectedColor); if(uint(hovered) == index) drawRectangle(pos, Color(0xffffff30)); if(index < sprites.length) sprites[index].draw(pos); } }; class ShipsetChooser : GuiIconGrid { array<const Shipset@> items; uint selected = 0; Color selectedColor; ShipsetChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) { super(parent, align); horizAlign = 0.5; vertAlign = 0.0; iconSize = itemSize; updateAbsolutePosition(); } void add(const Shipset@ shipset) { items.insertLast(shipset); } uint get_length() override { return items.length; } void drawElement(uint index, const recti& pos) override { if(selected == index) { Color col = selectedColor; col.a = 0x15; drawRectangle(pos, col); } if(uint(hovered) == index) drawRectangle(pos, Color(0xffffff15)); if(index < items.length) { const Shipset@ shipset = items[index]; const Hull@ hull = shipset.hulls[0]; if(hull !is null) { quaterniond rot; rot = quaterniond_fromAxisAngle(vec3d_front(), -0.9); rot *= quaterniond_fromAxisAngle(vec3d_up(), 0.6); rot *= quaterniond_fromAxisAngle(vec3d_right(), -0.5); setClip(pos); Color lightColor = colors::White; if(selected == index) { NODE_COLOR = Colorf(selectedColor); lightColor = selectedColor; } else NODE_COLOR = Colorf(1.f, 1.f, 1.f, 1.f); drawLitModel(hull.model, hull.material, pos+vec2i(-4,0), rot, 1.9, lightColor=lightColor); clearClip(); } const Font@ ft = skin.getFont(FT_Bold); if(selected == index || uint(hovered) == index) ft.draw(text=shipset.name, pos=pos.padded(0,4), horizAlign=0.5, vertAlign=0.0, stroke=colors::Black, color=(selected == index ? selectedColor : colors::White)); } } }; class WeaponSkinChooser : GuiIconGrid { array<const EmpireWeaponSkin@> items; uint selected = 0; Color selectedColor; WeaponSkinChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) { super(parent, align); horizAlign = 0.5; vertAlign = 0.0; iconSize = itemSize; updateAbsolutePosition(); } void add(const EmpireWeaponSkin@ it) { items.insertLast(it); } uint get_length() override { return items.length; } void drawElement(uint index, const recti& pos) override { if(selected == index) { Color col = selectedColor; col.a = 0x15; drawRectangle(pos, col); } if(uint(hovered) == index) drawRectangle(pos, Color(0xffffff15)); if(index < items.length) items[index].icon.draw(pos); } }; class TraitDisplay : BaseGuiElement { const Trait@ trait; GuiSprite@ icon; GuiMarkupText@ name; GuiMarkupText@ description; GuiText@ points; GuiText@ conflicts; GuiCheckbox@ check; bool hovered = false; bool conflict = false; TraitDisplay(IGuiElement@ parent) { super(parent, recti()); @icon = GuiSprite(this, Alignment(Left+20, Top+12, Left+52, Bottom-12)); @name = GuiMarkupText(this, Alignment(Left+65, Top+8, Right-168, Top+38)); name.defaultFont = FT_Medium; name.defaultStroke = colors::Black; @description = GuiMarkupText(this, Alignment(Left+124, Top+34, Right-168, Bottom-8)); @conflicts = GuiText(this, Alignment(Right-360, Top+8, Right-56, Bottom-8)); conflicts.vertAlign = 0.1; conflicts.horizAlign = 1.0; @points = GuiText(this, Alignment(Right-160, Top+8, Right-56, Bottom-8)); points.horizAlign = 1.0; points.font = FT_Subtitle; @check = GuiCheckbox(this, Alignment(Right-48, Top+0.5f-20, Right-8, Top+0.5f+20), ""); } bool onGuiEvent(const GuiEvent& evt) override { switch(evt.type) { case GUI_Mouse_Entered: hovered = true; break; case GUI_Mouse_Left: hovered = false; break; case GUI_Changed: if(evt.caller is check) { check.checked = !check.checked; emitClicked(); return true; } break; } return BaseGuiElement::onGuiEvent(evt); } bool onMouseEvent(const MouseEvent& evt, IGuiElement@ caller) override { switch(evt.type) { case MET_Button_Down: if(evt.button == 0) return true; break; case MET_Button_Up: if(evt.button == 0) { emitClicked(); return true; } break; } return BaseGuiElement::onMouseEvent(evt, caller); } void set(const Trait@ trait, bool selected, bool conflict) { @this.trait = trait; this.conflict = conflict; description.text = trait.description; icon.desc = trait.icon; name.defaultColor = trait.color; if(trait.gives > 0) { points.text = format(locale::RACE_POINTS_POS, toString(trait.gives)); points.color = colors::Green; points.visible = true; } else if(trait.cost > 0) { points.text = format(locale::RACE_POINTS_NEG, toString(trait.cost)); points.color = colors::Red; points.visible = true; } else { points.text = locale::RACE_POINTS_NEU; points.color = Color(0xaaaaaaff); points.visible = false; } bool displayConflicts = false; if(trait.conflicts.length > 0) { if(conflict) { conflicts.color = colors::Red; conflicts.font = FT_Bold; conflicts.vertAlign = 0.2; } else { conflicts.color = Color(0xaaaaaaff); conflicts.font = FT_Italic; conflicts.vertAlign = 0.1; } string str = locale::CONFLICTS+" "; for(uint i = 0, cnt = trait.conflicts.length; i < cnt; ++i) { if(!trait.conflicts[i].available) continue; if(i != 0) str += ", "; str += trait.conflicts[i].name; displayConflicts = true; } conflicts.text = str; } if(displayConflicts) { conflicts.visible = true; points.vertAlign = 0.7; } else { conflicts.visible = false; points.vertAlign = 0.5; } if(trait.unique.length != 0) { check.style = SS_Radiobox; if(description.alignment.right.pixels != 52) { description.alignment.right.pixels = 52; description.updateAbsolutePosition(); } } else { check.style = SS_Checkbox; if(description.alignment.right.pixels != 168) { description.alignment.right.pixels = 168; description.updateAbsolutePosition(); } } name.text = trait.name; check.checked = selected; } void draw() { if(check.checked) skin.draw(SS_Glow, SF_Normal, AbsolutePosition, trait.color); skin.draw(SS_Panel, SF_Normal, AbsolutePosition.padded(4), trait.color); if(hovered) drawRectangle(AbsolutePosition.padded(8), Color(0xffffff10)); BaseGuiElement::draw(); } }; class SaveRaceDialog : SaveDialog { EmpireSettings settings; EmpireSetup@ setup; SaveRaceDialog(IGuiElement@ bind, EmpireSettings@ settings, EmpireSetup@ setup) { this.settings = settings; @this.setup = setup; super(bind, modProfile["races"], settings.raceName+".race"); } void clickConfirm() override { exportRace(settings, path); } }; class LoadRaceDialog : LoadDialog { EmpireSettings settings; EmpireSetup@ setup; TraitsWindow@ win; LoadRaceDialog(TraitsWindow@ win, EmpireSettings@ settings, EmpireSetup@ setup) { this.settings = settings; @this.setup = setup; @this.win = win; super(win, modProfile["races"]); } void clickConfirm() override { importRace(setup.settings, path); if(win !is null) win.update(); setup.submit(); } }; class TraitElement : GuiListElement { const Trait@ trait; void draw(GuiListbox@ ele, uint flags, const recti& absPos) { recti iconPos = recti_area(absPos.topLeft+vec2i(10, 5), vec2i(absPos.height-10, absPos.height-10)); trait.icon.draw(iconPos); recti textPos = absPos.padded(absPos.height + 10, 0, 10, 4); ele.skin.getFont(FT_Medium).draw( text=trait.name, pos=textPos); } string get_tooltipText() { return format("[color=$1][b]$2[/b][/color]\n$3", toString(trait.color), trait.name, trait.description); } }; class TraitsWindow : BaseGuiElement { GuiOverlay@ overlay; EmpireSetup@ setup; GuiBackgroundPanel@ bg; GuiListbox@ categories; array<const TraitCategory@> usedCategories; GuiPanel@ profilePanel; GuiText@ nameLabel; GuiTextbox@ name; GuiText@ portraitLabel; PortraitChooser@ portrait; GuiText@ shipsetLabel; ShipsetChooser@ shipset; GuiText@ weaponSkinLabel; WeaponSkinChooser@ weaponSkin; GuiText@ traitsLabel; GuiListbox@ traitList; GuiText@ pointsLabel; GuiPanel@ traitPanel; GuiText@ noTraits; array<TraitDisplay@> traits; GuiButton@ acceptButton; GuiButton@ saveButton; GuiButton@ loadButton; TraitsWindow(EmpireSetup@ setup) { @this.setup = setup; @overlay = GuiOverlay(null); overlay.closeSelf = false; super(overlay, Alignment(Left+0.11f, Top+0.11f, Right-0.11f, Bottom-0.11f)); updateAbsolutePosition(); @bg = GuiBackgroundPanel(this, Alignment().fill()); bg.titleColor = Color(0xff8000ff); bg.title = locale::CUSTOMIZE_RACE; @categories = GuiListbox(bg, Alignment(Left+4, Top+32, Left+250, Bottom-4)); categories.itemHeight = 44; categories.style = SS_PlainOverlay; categories.itemStyle = SS_TabButton; categories.addItem(GuiMarkupListText(locale::RACE_PROFILE)); categories.required = true; for(uint i = 0, cnt = getTraitCategoryCount(); i < cnt; ++i) { auto@ cat = getTraitCategory(i); bool hasTraits = false; for(uint n = 0, ncnt = getTraitCount(); n < ncnt; ++n) { if(getTrait(n).category is cat && getTrait(n).available && getTrait(n).hasDLC) { hasTraits = true; break; } } if(hasTraits) { categories.addItem(GuiMarkupListText(cat.name)); usedCategories.insertLast(cat); } } @acceptButton = GuiButton(bg, Alignment(Right-140, Bottom-40, Right-3, Bottom-3), locale::ACCEPT); @loadButton = GuiButton(bg, Alignment(Right-274, Bottom-40, Right-154, Bottom-3), locale::LOAD); @saveButton = GuiButton(bg, Alignment(Right-400, Bottom-40, Right-280, Bottom-3), locale::SAVE); @pointsLabel = GuiText(bg, Alignment(Left+264, Bottom-40, Right-410, Bottom-3)); pointsLabel.font = FT_Medium; Alignment panelAlign(Left+258, Top+32, Right-4, Bottom-40); @profilePanel = GuiPanel(bg, panelAlign); @traitPanel = GuiPanel(bg, panelAlign); traitPanel.visible = false; int y = 8; @nameLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::RACE_NAME, FT_Bold); @name = GuiTextbox(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+30), setup.settings.raceName); y += 38; int h = 80 + (getEmpirePortraitCount() / ((size.width - 200) / 70)) * 80; @portraitLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::PORTRAIT, FT_Bold); @portrait = PortraitChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+h), vec2i(70, 70)); portrait.selectedColor = setup.settings.color; portrait.selected = randomi(0, getEmpirePortraitCount()-1); portrait.horizAlign = 0.0; for(uint i = 0, cnt = getEmpirePortraitCount(); i < cnt; ++i) { auto@ img = getEmpirePortrait(i); portrait.add(Sprite(img.portrait)); if(img.ident == setup.settings.portrait) portrait.selected = i; } y += h+8; h = 80 + (getShipsetCount() / ((size.width - 200) / 150)) * 80; @shipsetLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::SHIPSET, FT_Bold); @shipset = ShipsetChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+h), vec2i(150, 70)); shipset.selectedColor = setup.settings.color; shipset.selected = 0; shipset.horizAlign = 0.0; for(uint i = 0, cnt = getShipsetCount(); i < cnt; ++i) { auto@ ss = getShipset(i); if(ss.available && (ss.dlc.length == 0 || hasDLC(ss.dlc))) shipset.add(ss); if(ss.ident == setup.settings.shipset) shipset.selected = shipset.length-1; } y += h+8; @weaponSkinLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::WEAPON_SKIN, FT_Bold); @weaponSkin = WeaponSkinChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+80), vec2i(120, 70)); weaponSkin.selectedColor = setup.settings.color; weaponSkin.selected = 0; weaponSkin.horizAlign = 0.0; for(uint i = 0, cnt = getEmpireWeaponSkinCount(); i < cnt; ++i) { auto@ skin = getEmpireWeaponSkin(i); weaponSkin.add(skin); if(skin.ident == setup.settings.effectorSkin) weaponSkin.selected = weaponSkin.length-1; } y += 88; @traitsLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::TRAITS, FT_Bold); @traitList = GuiListbox(profilePanel, Alignment(Left+200, Top+y, Right-12, Bottom-8)); traitList.itemStyle = SS_StaticListboxItem; traitList.itemHeight = 50; addLazyMarkupTooltip(traitList); @noTraits = GuiText(profilePanel, Alignment(Left+240, Top+y+10, Right-12, Top+y+50), locale::NO_TRAITS); noTraits.color = Color(0xaaaaaaff); noTraits.vertAlign = 0.0; y += 58; update(); updateAbsolutePosition(); } void update() { int sel = categories.selected; profilePanel.visible = sel == 0; traitPanel.visible = sel != 0; uint index = 0; const TraitCategory@ cat; if(sel > 0) @cat = usedCategories[sel - 1]; int points = STARTING_TRAIT_POINTS; for(uint i = 0, cnt = setup.settings.traits.length; i < cnt; ++i) { points += setup.settings.traits[i].gives; points -= setup.settings.traits[i].cost; } if(traitPanel.visible) { int y = 0; array<const Trait@> list; for(uint i = 0, cnt = getTraitCount(); i < cnt; ++i) { auto@ trait = getTrait(i); if(cat !is null && cat !is trait.category) continue; if(!setup.player && !trait.aiSupport) continue; if(!trait.available) continue; if(!trait.hasDLC) continue; list.insertLast(trait); } list.sortAsc(); for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ trait = list[i]; TraitDisplay@ disp; if(index < traits.length) { @disp = traits[index]; } else { @disp = TraitDisplay(traitPanel); traits.insertLast(disp); } disp.set(trait, setup.settings.hasTrait(trait), trait.hasConflicts(setup.settings.traits)); disp.alignment.set(Left, Top+y, Right, Top+y+140); disp.updateAbsolutePosition(); int needH = disp.description.renderer.height+48; if(needH != 140) { disp.alignment.set(Left, Top+y, Right, Top+y+needH); disp.updateAbsolutePosition(); } ++index; y += needH; } for(uint i = index, cnt = traits.length; i < cnt; ++i) traits[i].remove(); traits.length = index; traitPanel.updateAbsolutePosition(); } if(profilePanel.visible) { uint cnt = setup.settings.traits.length; traitList.removeItemsFrom(cnt); for(uint i = 0; i < cnt; ++i) { auto@ item = cast<TraitElement>(traitList.getItemElement(i)); if(item is null) { @item = TraitElement(); traitList.addItem(item); } @item.trait = setup.settings.traits[i]; } noTraits.visible = cnt == 0; } if(points > 0) { pointsLabel.color = colors::Green; pointsLabel.text = format(locale::RACE_POINTS_AVAIL_POS, toString(points)); pointsLabel.visible = true; } else if(points < 0) { pointsLabel.color = colors::Red; pointsLabel.text = format(locale::RACE_POINTS_AVAIL_NEG, toString(-points)); pointsLabel.visible = true; } else { pointsLabel.color = Color(0xaaaaaaff); pointsLabel.text = format(locale::RACE_POINTS_AVAIL_POS, toString(points)); pointsLabel.visible = false; } if(points >= 0 && !setup.settings.hasTraitConflicts()) acceptButton.color = colors::Green; else acceptButton.color = colors::Red; } bool onGuiEvent(const GuiEvent& evt) override { if(evt.caller is acceptButton) { if(evt.type == GUI_Clicked) { overlay.close(); return true; } } if(evt.caller is saveButton) { if(evt.type == GUI_Clicked) { SaveRaceDialog(this, setup.settings, setup); return true; } } else if(evt.caller is loadButton) { if(evt.type == GUI_Clicked) { LoadRaceDialog(this, setup.settings, setup); return true; } } if(evt.type == GUI_Clicked) { if(evt.caller is portrait) { int hov = portrait.hovered; if(hov >= 0) { setup.settings.portrait = getEmpirePortrait(hov).ident; portrait.selected = hov; } setup.submit(); return true; } if(evt.caller is shipset) { int hov = shipset.hovered; if(hov >= 0) { setup.settings.shipset = shipset.items[hov].ident; shipset.selected = hov; } setup.submit(); return true; } if(evt.caller is weaponSkin) { int hov = weaponSkin.hovered; if(hov >= 0) { setup.settings.effectorSkin = weaponSkin.items[hov].ident; weaponSkin.selected = hov; } setup.submit(); return true; } auto@ disp = cast<TraitDisplay>(evt.caller); if(disp !is null) { if(disp.trait.unique.length != 0) setup.settings.chooseTrait(disp.trait); else if(setup.settings.hasTrait(disp.trait)) setup.settings.removeTrait(disp.trait); else setup.settings.addTrait(disp.trait); update(); setup.submit(); } } if(evt.type == GUI_Changed) { if(evt.caller is name) { setup.settings.raceName = name.text; setup.submit(); return true; } if(evt.caller is categories) { update(); return true; } } return BaseGuiElement::onGuiEvent(evt); } void draw() override { BaseGuiElement::draw(); } }; class GalaxySetup : BaseGuiElement { Map@ mp; NewGame@ ng; GuiText@ name; GuiText@ timesLabel; GuiSpinbox@ timesBox; GuiPanel@ settings; GuiButton@ removeButton; GuiButton@ hwButton; GuiSprite@ hwX; GalaxySetup(NewGame@ menu, Alignment@ align, Map@ fromMap) { super(menu.galaxyPanel, align); @mp = fromMap.create(); @ng = menu; @name = GuiText(this, Alignment(Left+6, Top+5, Right-262, Height=28)); name.text = mp.name; name.font = FT_Medium; name.color = mp.color; name.stroke = colors::Black; @timesBox = GuiSpinbox(this, Alignment(Right-190, Top+7, Width=52, Height=22), 1.0); timesBox.min = 1.0; timesBox.max = 100.0; timesBox.decimals = 0; timesBox.color = Color(0xffffff60); @timesLabel = GuiText(this, Alignment(Right-135, Top+7, Width=25, Height=22), "x"); timesBox.visible = !mp.isUnique; timesLabel.visible = !mp.isUnique; @removeButton = GuiButton(this, Alignment(Right-84, Top+4, Right-25, Top+34)); removeButton.setIcon(icons::Remove); removeButton.color = colors::Red; @hwButton = GuiButton(this, Alignment(Right-230, Top+5, Width=26, Height=26)); hwButton.setIcon(Sprite(spritesheet::PlanetType, 2, Color(0xffffffaa)), padding=0); hwButton.toggleButton = true; hwButton.pressed = false; hwButton.style = SS_IconButton; hwButton.color = Color(0xff0000ff); setMarkupTooltip(hwButton, locale::NGTT_MAP_HW); @hwX = GuiSprite(hwButton, Alignment(), Sprite(spritesheet::QuickbarIcons, 3, Color(0xffffff80))); hwX.visible = false; @settings = GuiPanel(this, Alignment(Left, Top+42, Right, Bottom-4)); mp.create(settings); } void setHomeworlds(bool value) { hwButton.pressed = !value; hwX.visible = hwButton.pressed; if(hwButton.pressed) hwButton.fullIcon.color = Color(0xffffffff); else hwButton.fullIcon.color = Color(0xffffffaa); } void apply(MapSettings& set) { set.map_id = mp.id; set.galaxyCount = timesBox.value; @set.parent = ng.settings; set.allowHomeworlds = !hwButton.pressed; mp.apply(set); } void load(MapSettings& set) { auto@ _map = getMap(set.map_id); if(getClass(mp) !is getClass(_map)) @mp = cast<Map>(getClass(_map).create()); timesBox.value = set.galaxyCount; hwButton.pressed = !set.allowHomeworlds; hwX.visible = hwButton.pressed; if(hwButton.pressed) hwButton.fullIcon.color = Color(0xffffffff); else hwButton.fullIcon.color = Color(0xffffffaa); mp.load(set); } bool onGuiEvent(const GuiEvent& evt) { switch(evt.type) { case GUI_Clicked: if(evt.caller is removeButton) { ng.removeGalaxy(this); return true; } else if(evt.caller is hwButton) { setHomeworlds(!hwButton.pressed); return true; } break; } return BaseGuiElement::onGuiEvent(evt); } void draw() { recti bgPos = AbsolutePosition.padded(-5,0,-4,0); clipParent(bgPos); skin.draw(SS_GalaxySetupItem, SF_Normal, bgPos.padded(-4,0), mp.color); resetClip(); auto@ icon = mapIcons[mp.index]; if(mp.icon.length != 0 && icon.isLoaded(0)) { recti pos = AbsolutePosition.padded(0,42,0,0).aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0); icon.draw(pos, Color(0xffffff80)); } BaseGuiElement::draw(); } }; class MapElement : GuiListElement { Map@ mp; MapElement(Map@ _map) { @mp = _map; } void draw(GuiListbox@ ele, uint flags, const recti& absPos) override { const Font@ title = ele.skin.getFont(FT_Subtitle); const Font@ normal = ele.skin.getFont(FT_Normal); ele.skin.draw(SS_ListboxItem, flags, absPos, mp.color); auto@ icon = mapIcons[mp.index]; if(mp.icon.length != 0 && icon.isLoaded(0)) { recti pos = absPos.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0); icon.draw(pos, Color(0xffffff80)); } title.draw(pos=absPos.resized(0, 32).padded(12,4), text=mp.name, color=mp.color, stroke=colors::Black); normal.draw(pos=absPos.padded(12,36,12+absPos.height,0), offset=vec2i(), lineHeight=-1, text=mp.description, color=colors::White); } }; class Quickstart : ConsoleCommand { void execute(const string& args) { new_game.start(); } }; NewGame@ new_game; array<DynamicTexture> mapIcons; void init() { @new_game = NewGame(); new_game.visible = false; addConsoleCommand("quickstart", Quickstart()); } array<Player@> connectedPlayers; set_int connectedSet; void tick(double time) { if(new_game.visible) new_game.tick(time); if(!game_running && mpServer) { array<Player@>@ players = getPlayers(); //Send connect events for(uint i = 0, cnt = players.length; i < cnt; ++i) { Player@ pl = players[i]; if(pl.id == CURRENT_PLAYER.id) continue; string name = pl.name; if(name.length == 0) continue; if(!connectedSet.contains(pl.id)) { string msg = format("[color=#aaa]* "+locale::MP_CONNECT_EVENT+"[/color]", format("[b]$1[/b]", bbescape(name))); recvMenuJoin(ALL_PLAYERS, msg); connectedPlayers.insertLast(pl); connectedSet.insert(pl.id); } } connectedSet.clear(); for(uint i = 0, cnt = players.length; i < cnt; ++i) connectedSet.insert(players[i].id); //Send disconnect events for(uint i = 0, cnt = connectedPlayers.length; i < cnt; ++i) { if(!connectedSet.contains(connectedPlayers[i].id)) { Color color; string name = connectedPlayers[i].name; string msg = format("[color=#aaa]* "+locale::MP_DISCONNECT_EVENT+"[/color]", format("[b]$2[/b]", toString(color), bbescape(name))); recvMenuLeave(ALL_PLAYERS, msg); connectedPlayers.removeAt(i); --i; --cnt; } } } } void showNewGame(bool fromMP = false) { new_game.visible = true; new_game.fromMP = fromMP; new_game.init(); menu_container.visible = false; menu_container.animateOut(); new_game.animateIn(); } void hideNewGame(bool snap = false) { new_game.fromMP = false; menu_container.visible = true; if(!snap) { menu_container.animateIn(); new_game.animateOut(); } else { animate_remove(new_game); new_game.visible = false; menu_container.show(); } } void changeEmpireSettings_client(Player& pl, EmpireSettings@ settings) { auto@ emp = new_game.findPlayer(pl.id); emp.settings.raceName = settings.raceName; emp.settings.traits = settings.traits; emp.settings.portrait = settings.portrait; emp.settings.shipset = settings.shipset; emp.settings.effectorSkin = settings.effectorSkin; emp.settings.color = settings.color; emp.settings.flag = settings.flag; emp.settings.ready = settings.ready; emp.settings.team = settings.team; emp.update(); } bool sendPeriodic(Message& msg) { if(game_running) return false; new_game.apply(); msg << new_game.settings; return true; } void recvPeriodic(Message& msg) { msg >> new_game.settings; new_game.reset(); new_game.updateAbsolutePosition(); } void chatMessage(Player& pl, string text) { auto@ emp = new_game.findPlayer(pl.id); Color color = emp.settings.color; string msg = format("[b][color=$1]$2[/color][/b] [offset=100]$3[/offset]", toString(color), bbescape(emp.name.text), bbescape(text)); recvMenuChat(ALL_PLAYERS, msg); } void chatMessage_client(string text) { new_game.addChat(text); sound::generic_click.play(); } void chatJoin_client(string text) { new_game.addChat(text); sound::generic_ok.play(); } void chatLeave_client(string text) { new_game.addChat(text); sound::generic_warn.play(); } |
Added scripts/server/cheats.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
import orbitals; import object_creation; import tile_resources; import void setInstantColonize(bool) from "planets.SurfaceComponent"; from empire import sendChatMessage; import influence; from bonus_effects import BonusEffect; from generic_effects import GenericEffect; import hooks; bool CHEATS_ENABLED_THIS_GAME = false; bool CHEATS_ENABLED = false; bool getCheatsEnabled() { return CHEATS_ENABLED; } bool getCheatsEverOn() { return CHEATS_ENABLED_THIS_GAME; } void setCheatsEnabled(Player& player, bool enabled) { if(player != HOST_PLAYER) return; CHEATS_ENABLED = enabled; if(enabled) CHEATS_ENABLED_THIS_GAME = true; cheatsEnabled(ALL_PLAYERS, enabled); if(mpServer) { if(enabled) sendChatMessage(locale::MP_CHEATS_ENABLED, color=Color(0xaaaaaaff), offset=30); else sendChatMessage(locale::MP_CHEATS_DISABLED, color=Color(0xaaaaaaff), offset=30); } } void cheatSeeAll(Player& player, bool enabled) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.visionMask = enabled ? ~0 : player.emp.mask; } void cheatColonize(bool enabled) { if(!CHEATS_ENABLED) return; setInstantColonize(enabled); } void cheatSpawnFlagship(Object@ spawnAt, const Design@ design, Empire@ owner) { if(!CHEATS_ENABLED) return; if(design.hasTag(ST_IsSupport)) return; createShip(spawnAt, design, owner, free=true); } void cheatSpawnFlagship(vec3d spawnAt, const Design@ design, Empire@ owner) { if(!CHEATS_ENABLED) return; if(design.hasTag(ST_IsSupport)) return; Ship@ ship = createShip(spawnAt, design, owner, free=true); ship.addMoveOrder(spawnAt); } void cheatSpawnSupports(Object@ spawnAt, const Design@ design, uint count) { if(!CHEATS_ENABLED) return; if(!design.hasTag(ST_IsSupport)) return; if(!spawnAt.hasLeaderAI || spawnAt.owner is null || !spawnAt.owner.valid) return; for(uint i = 0; i < count; ++i) createShip(spawnAt, design, spawnAt.owner); } void cheatSpawnSupports(vec3d spawnAt, const Design@ design, uint count, Empire@ owner) { if(!CHEATS_ENABLED) return; if(!design.hasTag(ST_IsSupport)) return; for(uint i = 0; i < count; ++i) createShip(spawnAt, design, owner); } void cheatSpawnOrbital(vec3d spawnAt, uint orbitalType, Empire@ owner) { if(!CHEATS_ENABLED) return; const OrbitalModule@ def = getOrbitalModule(orbitalType); if(def is null) return; createOrbital(spawnAt, def, owner); } void cheatInfluence(Player& player, int amount) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.addInfluence(amount); } void cheatResearch(Player& player, double amount) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.generatePoints(amount); } void cheatMoney(Player& player, int amount) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.addBonusBudget(amount); } void cheatEnergy(Player& player, int amount) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.modEnergyStored(amount); } void cheatFTL(Player& player, int amount) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; if(player.emp.FTLCapacity < amount) player.emp.modFTLCapacity(amount); player.emp.modFTLStored(amount); } void cheatActivateAI(Player& player) { if(!CHEATS_ENABLED || player.emp is null || !player.emp.valid) return; player.emp.initBasicAI(); } void cheatDebugAI(Empire@ emp) { if(!CHEATS_ENABLED || emp is null) return; emp.debugAI(); } void commandPlayerAI(string cmd) { playerEmpire.commandAI(cmd); } void cheatCommandAI(Empire@ emp, string cmd) { if(emp is playerEmpire) { if (cmd == "no achievements") { CHEATS_ENABLED_THIS_GAME = true; } else { emp.commandAI(cmd); } return; } if(!CHEATS_ENABLED || emp is null) return; emp.commandAI(cmd); } void cheatTrigger(Player& player, Object@ obj, Empire@ emp, string hook) { Empire@ plEmp = player.emp; if(!CHEATS_ENABLED || plEmp is null || !plEmp.valid) return; BonusEffect@ trig = cast<BonusEffect>(parseHook(hook, "bonus_effects::", required=false)); if(trig !is null) { trig.activate(obj, emp); return; } GenericEffect@ eff = cast<GenericEffect>(parseHook(hook, "planet_effects::")); if(eff !is null) { eff.enable(obj, null); return; } } void cheatChangeOwner(Object@ obj, Empire@ newOwner) { if(!CHEATS_ENABLED || obj is null || newOwner is null) return; if(obj.isPlanet) { obj.takeoverPlanet(newOwner); } else if(obj.isShip) { if(obj.hasLeaderAI) { uint cnt = obj.supportCount; for(uint i = 0; i < cnt; ++i) @obj.supportShip[i].owner = newOwner; } @obj.owner = newOwner; } else { @obj.owner = newOwner; } } void cheatAlliance(Empire& from, Empire& to) { if(!CHEATS_ENABLED) return; if(from is to) return; if(!from.valid || !to.valid) return; } void cheatDestroy(Object@ obj) { if(!CHEATS_ENABLED || obj is null) return; obj.destroy(); } void cheatLabor(Object@ obj, double amount) { if(!CHEATS_ENABLED || obj is null) return; obj.modLaborIncome(amount); } void syncInitial(Message& msg) { msg << CHEATS_ENABLED; msg << CHEATS_ENABLED_THIS_GAME; } void save(SaveFile& msg) { msg << CHEATS_ENABLED; msg << CHEATS_ENABLED_THIS_GAME; } void load(SaveFile& msg) { msg >> CHEATS_ENABLED; if(msg >= SV_0025) msg >> CHEATS_ENABLED_THIS_GAME; else CHEATS_ENABLED_THIS_GAME = CHEATS_ENABLED; } |
Added scripts/server/empire_ai/EmpireAI.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
import settings.game_settings; import AIController@ createBumAI() from "empire_ai.BumAI"; import AIController@ createBasicAI() from "empire_ai.BasicAI"; import AIController@ createWeaselAI() from "empire_ai.weasel.WeaselAI"; interface AIController { void debugAI(); void commandAI(string cmd); void aiPing(Empire@ fromEmpire, vec3d position, uint type); void init(Empire& emp, EmpireSettings& settings); void init(Empire& emp); void tick(Empire& emp, double time); void pause(Empire& emp); void resume(Empire& emp); void load(SaveFile& msg); void save(SaveFile& msg); int getDifficultyLevel(); vec3d get_aiFocus(); string getOpinionOf(Empire& emp, Empire@ other); int getStandingTo(Empire& emp, Empire@ other); } class EmpireAI : Component_EmpireAI, Savable { AIController@ ctrl; uint aiType; bool paused = false; bool override = true; EmpireAI() { } vec3d get_aiFocus() { if(ctrl !is null) return ctrl.aiFocus; else return vec3d(); } int get_difficulty() { if(ctrl !is null) return ctrl.getDifficultyLevel(); else return -1; } bool get_isAI(Empire& emp) { return ctrl !is null && (emp.player is null || override); } string getRelation(Player& pl, Empire& emp) { auto@ other = pl.emp; if(ctrl is null || other is null || !other.valid || other.ContactMask.value & emp.mask == 0) return ""; else return ctrl.getOpinionOf(emp, other); } int getRelationState(Player& pl, Empire& emp) { auto@ other = pl.emp; if(ctrl is null || other is null || !other.valid || other.ContactMask.value & emp.mask == 0) return 0; else return ctrl.getStandingTo(emp, other); } uint getAIType() { return aiType; } void debugAI() { if(ctrl !is null) ctrl.debugAI(); } void commandAI(string cmd) { if(ctrl !is null) ctrl.commandAI(cmd); } void load(SaveFile& msg) { msg >> paused; msg >> override; msg >> aiType; createAI(aiType); if(ctrl !is null) ctrl.load(msg); } void save(SaveFile& msg) { msg << paused; msg << override; msg << aiType; if(ctrl !is null) ctrl.save(msg); } void createAI(uint type) { aiType = type; //Create the controller switch(type) { case ET_Player: //Do nothing break; case ET_BumAI: @ctrl = createBasicAI(); break; case ET_WeaselAI: @ctrl = createWeaselAI(); break; } } void aiPing(Empire@ fromEmpire, vec3d position, uint type = 0) { if(ctrl !is null) ctrl.aiPing(fromEmpire, position, type); } void init(Empire& emp, EmpireSettings& settings) { createAI(settings.type); //Initialize if(ctrl !is null) ctrl.init(emp, settings); } void initBasicAI(Empire& emp) { override = true; if(ctrl !is null) return; createAI(ET_WeaselAI); if(ctrl !is null) { EmpireSettings settings; settings.difficulty = 2; settings.aiFlags |= AIF_Aggressive; ctrl.init(emp, settings); ctrl.init(emp); } } void init(Empire& emp) { if(ctrl !is null) ctrl.init(emp); } void aiTick(Empire& emp, double tick) { if(ctrl is null) return; if(emp.player is null || override) { if(paused) { ctrl.resume(emp); paused = false; } ctrl.tick(emp, tick); } else { if(!paused) { ctrl.pause(emp); paused = true; } } } }; |
Added scripts/server/empire_ai/weasel/Budget.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
// Budget // ------ // Tasked with managing the empire's money and making sure we have enough to spend // on various things, as well as dealing with prioritization and budget allocation. // import empire_ai.weasel.WeaselAI; enum BudgetType { BT_Military, BT_Infrastructure, BT_Colonization, BT_Development, BT_COUNT }; final class AllocateBudget { int id = -1; uint type; int cost = 0; int maintenance = 0; double requestTime = 0; double priority = 1; bool allocated = false; int opCmp(const AllocateBudget@ other) const { if(priority < other.priority) return -1; if(priority > other.priority) return 1; if(requestTime < other.requestTime) return 1; if(requestTime > other.requestTime) return -1; return 0; } void save(SaveFile& file) { file << id; file << type; file << cost; file << maintenance; file << requestTime; file << priority; file << allocated; } void load(SaveFile& file) { file >> id; file >> type; file >> cost; file >> maintenance; file >> requestTime; file >> priority; file >> allocated; } }; final class BudgetPart { uint type; array<AllocateBudget@> allocations; //How much we've spent this cycle int spent = 0; //How much is remaining to be spent this cycle int remaining = 0; //How much maintenance we've gained this cycle int gainedMaintenance = 0; //How much maintenance we can still gain this cycle int remainingMaintenance = 0; void update(AI& ai, Budget& budget) { for(uint i = 0, cnt = allocations.length; i < cnt; ++i) { auto@ alloc = allocations[i]; if(alloc.priority < 1.0) { if(alloc.cost >= remaining && alloc.maintenance >= remainingMaintenance) { budget.spend(type, alloc.cost, alloc.maintenance); alloc.allocated = true; allocations.removeAt(i); break; } } else { if(budget.canSpend(type, alloc.cost, alloc.maintenance, alloc.priority)) { budget.spend(type, alloc.cost, alloc.maintenance); alloc.allocated = true; allocations.removeAt(i); break; } } } } void turn(AI& ai, Budget& budget) { spent = 0; remaining = 0; gainedMaintenance = 0; remainingMaintenance = 0; } void save(Budget& budget, SaveFile& file) { file << spent; file << remaining; file << gainedMaintenance; file << remainingMaintenance; uint cnt = allocations.length; file << cnt; for(uint i = 0; i < cnt; ++i) { budget.saveAlloc(file, allocations[i]); allocations[i].save(file); } } void load(Budget& budget, SaveFile& file) { file >> spent; file >> remaining; file >> gainedMaintenance; file >> remainingMaintenance; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ alloc = budget.loadAlloc(file); alloc.load(file); } } }; final class Budget : AIComponent { //Budget thresholds private int _criticalThreshold = 350; private int _lowThreshold = 400; private int _mediumThreshold = 500; private int _highThreshold = 1000; private int _veryHighThreshold = 2000; //Focus flags private bool _askedFocus = false; private bool _focusing = false; //Focused budget type private uint _focus; int get_criticalThreshold() const { return _criticalThreshold; } int get_lowThreshold() const { return _lowThreshold; } int get_mediumThreshold() const { return _mediumThreshold; } int get_highThreshold() const { return _highThreshold; } int get_veryHighThreshold() const { return _veryHighThreshold; } array<BudgetPart@> parts; int NextAllocId = 0; int InitialBudget = 0; int InitialUpcoming = 0; double Progress = 0; double RemainingTime = 0; int FreeBudget = 0; int FreeMaintenance = 0; bool checkedMilitarySpending = false; void create() { parts.length = BT_COUNT; for(uint i = 0; i < BT_COUNT; ++i) { @parts[i] = BudgetPart(); parts[i].type = BudgetType(i); } } void save(SaveFile& file) { file << InitialBudget; file << InitialUpcoming; file << Progress; file << RemainingTime; file << FreeBudget; file << FreeMaintenance; file << NextAllocId; file << checkedMilitarySpending; file << _askedFocus; file << _focusing; file << _focus; for(uint i = 0, cnt = parts.length; i < cnt; ++i) parts[i].save(this, file); } void load(SaveFile& file) { file >> InitialBudget; file >> InitialUpcoming; file >> Progress; file >> RemainingTime; file >> FreeBudget; file >> FreeMaintenance; file >> NextAllocId; file >> checkedMilitarySpending; file >> _askedFocus; file >> _focusing; file >> _focus; for(uint i = 0, cnt = parts.length; i < cnt; ++i) parts[i].load(this, file); } array<AllocateBudget@> loadIds; AllocateBudget@ loadAlloc(int id) { if(id == -1) return null; for(uint i = 0, cnt = loadIds.length; i < cnt; ++i) { if(loadIds[i].id == id) return loadIds[i]; } AllocateBudget alloc; alloc.id = id; loadIds.insertLast(alloc); return alloc; } AllocateBudget@ loadAlloc(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadAlloc(id); } void saveAlloc(SaveFile& file, AllocateBudget@ alloc) { int id = -1; if(alloc !is null) id = alloc.id; file << id; } void postLoad(AI& ai) { loadIds.length = 0; } void spend(uint type, int money, int maint = 0) { auto@ part = parts[type]; part.spent += money; part.gainedMaintenance += maint; if(part.remaining >= money) { part.remaining -= money; } else if(part.remaining >= 0) { money -= part.remaining; FreeBudget -= money; part.remaining = 0; } else { FreeBudget -= money; } if(part.remainingMaintenance >= maint) { part.remainingMaintenance -= maint; } else if(part.remainingMaintenance >= 0) { maint -= part.remainingMaintenance; FreeMaintenance -= maint; part.remainingMaintenance = 0; } else { FreeMaintenance -= money; } } bool canSpend(uint type, int money, int maint = 0, double priority = 1.0) { int canFree = FreeBudget; int canFreeMaint = FreeMaintenance; if (priority < 2.0) { //Rules for normal priority requests //Don't allow any spending not in our current focus if (_focusing) { if (type != _focus) return false; } if (type != BT_Colonization && (maint > 200 && ai.empire.EstNextBudget < mediumThreshold) || (maint > 100 && ai.empire.EstNextBudget < lowThreshold) || (maint > 0 && ai.empire.EstNextBudget < criticalThreshold)) //Don't allow any high maintenance cost if our estimated next budget is too low return false; //Don't allow generic spending until we've checked if we need to spend on military this cycle if(type == BT_Development && !checkedMilitarySpending && Progress < 0.33) canFree = 0; if(type == BT_Colonization) canFree += 160; } else { //Rules for high priority requests if (money > FreeBudget && ai.empire.canBorrow(money - FreeBudget)) //Allow borrowing from next budget for high priority requests canFree = money; } auto@ part = parts[type]; if(money > part.remaining + canFree) return false; if(maint != 0 && maint > part.remainingMaintenance + canFreeMaint) return false; return true; } int spendable(uint type) { return FreeBudget + parts[type].remaining; } int maintainable(uint type) { return FreeMaintenance + parts[type].remainingMaintenance; } void claim(uint type, int money, int maint = 0) { auto@ part = parts[type]; FreeBudget -= money; part.remaining += money; FreeMaintenance -= maint; part.remainingMaintenance += maint; } void turn() { if(log && gameTime > 10.0) { ai.print("=============="); ai.print("Unspent:"); ai.print(" Military: "+parts[BT_Military].remaining+" / "+parts[BT_Military].remainingMaintenance); ai.print(" Infrastructure: "+parts[BT_Infrastructure].remaining+" / "+parts[BT_Infrastructure].remainingMaintenance); ai.print(" Colonization: "+parts[BT_Colonization].remaining+" / "+parts[BT_Colonization].remainingMaintenance); ai.print(" Development: "+parts[BT_Development].remaining+" / "+parts[BT_Development].remainingMaintenance); ai.print(" FREE: "+FreeBudget+" / "+FreeMaintenance); ai.print("=============="); ai.print("Total Expenditures:"); ai.print(" Military: "+parts[BT_Military].spent+" / "+parts[BT_Military].gainedMaintenance); ai.print(" Infrastructure: "+parts[BT_Infrastructure].spent+" / "+parts[BT_Infrastructure].gainedMaintenance); ai.print(" Colonization: "+parts[BT_Colonization].spent+" / "+parts[BT_Colonization].gainedMaintenance); ai.print(" Development: "+parts[BT_Development].spent+" / "+parts[BT_Development].gainedMaintenance); ai.print("=============="); } //Collect some data about this turn InitialBudget = ai.empire.RemainingBudget; InitialUpcoming = ai.empire.EstNextBudget; FreeBudget = InitialBudget; FreeMaintenance = InitialUpcoming; checkedMilitarySpending = false; //Handle focus status if (_focusing) { _focusing = false; if (log) ai.print("Budget: ending focus"); } else if (_askedFocus) { _focusing = true; _askedFocus = false; if (log) ai.print("Budget: starting focus"); } //Tell the budget parts to perform turns for(uint i = 0, cnt = parts.length; i < cnt; ++i) parts[i].turn(ai, this); } void remove(AllocateBudget@ alloc) { if(alloc is null) return; if(alloc.allocated) { FreeBudget += alloc.cost; FreeMaintenance += alloc.maintenance; } parts[alloc.type].allocations.remove(alloc); } AllocateBudget@ allocate(uint type, int cost, int maint = 0, double priority = 1.0) { AllocateBudget alloc; alloc.id = NextAllocId++; alloc.type = type; alloc.cost = cost; alloc.maintenance = maint; alloc.priority = priority; return allocate(alloc); } AllocateBudget@ allocate(AllocateBudget@ allocation) { allocation.requestTime = gameTime; parts[allocation.type].allocations.insertLast(allocation); parts[allocation.type].allocations.sortDesc(); return allocation; } void applyNow(AllocateBudget@ alloc) { auto@ part = parts[alloc.type]; spend(alloc.type, alloc.cost, alloc.maintenance); alloc.allocated = true; part.allocations.remove(alloc); } void grantBonus(int cost, int maint = 0) { //Spread some bonus budget across all the different parts FreeBudget += cost; FreeMaintenance += maint; } bool canFocus() { return !(ai.empire.EstNextBudget <= criticalThreshold || _askedFocus || _focusing); } //Focus spendings on one particular budget part for one turn //Only high priority requests will be considered for other parts //Should be called at the start of a turn for best results void focus(BudgetType type) { if (ai.empire.EstNextBudget > criticalThreshold && !_askedFocus && !_focusing) { _focus = type; //If we are still at the start of a turn, focus immediately, else wait until next turn //The second condition compensates for slight timing inaccuracies and execution delay if (Progress < 0.33 || Progress > 0.995) { _focusing = true; _askedFocus = false; if (log) ai.print("Budget: starting focus"); } else _askedFocus = true; } } void tick(double time) { //Record some simple data Progress = ai.empire.BudgetTimer / ai.empire.BudgetCycle; RemainingTime = ai.empire.BudgetCycle - ai.empire.BudgetTimer; //Update one of the budget parts for(uint i = 0, cnt = parts.length; i < cnt; ++i) { auto@ part = parts[i]; part.update(ai, this); } } void focusTick(double time) { //Detect any extra budget we need to use int ExpectBudget = FreeBudget; int ExpectMaint = FreeMaintenance; for(uint i = 0, cnt = parts.length; i < cnt; ++i) { ExpectBudget += parts[i].remaining; ExpectMaint += parts[i].remainingMaintenance; } int HaveBudget = ai.empire.RemainingBudget; int HaveMaint = ai.empire.EstNextBudget; if(ExpectBudget != HaveBudget || ExpectMaint != HaveMaint) grantBonus(HaveBudget - ExpectBudget, max(0, HaveMaint - ExpectMaint)); } }; AIComponent@ createBudget() { return Budget(); } |
Added scripts/server/empire_ai/weasel/Colonization.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 |
// Colonization // ------------ // Deals with colonization for requested resources. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.ImportData; import empire_ai.weasel.Resources; import empire_ai.weasel.Planets; import empire_ai.weasel.Systems; import empire_ai.weasel.Budget; import empire_ai.weasel.Creeping; import util.formatting; import systems; enum ColonizationPhase { CP_Expansion, CP_Stabilization, }; interface RaceColonization { bool orderColonization(ColonizeData& data, Planet@ sourcePlanet); double getGenericUsefulness(const ResourceType@ type); }; final class ColonizeData { int id = -1; Planet@ target; Planet@ colonizeFrom; bool completed = false; bool canceled = false; double checkTime = -1.0; void save(Colonization& colonization, SaveFile& file) { file << target; file << colonizeFrom; file << completed; file << canceled; file << checkTime; } void load(Colonization& colonization, SaveFile& file) { file >> target; file >> colonizeFrom; file >> completed; file >> canceled; file >> checkTime; } }; tidy final class WaitUsed { ImportData@ forData; ExportData@ resource; void save(Colonization& colonization, SaveFile& file) { colonization.resources.saveImport(file, forData); colonization.resources.saveExport(file, resource); } void load(Colonization& colonization, SaveFile& file) { @forData = colonization.resources.loadImport(file); @resource = colonization.resources.loadExport(file); } }; final class ColonizePenalty : Savable { Planet@ pl; double until; void save(SaveFile& file) { file << pl; file << until; } void load(SaveFile& file) { file >> pl; file >> until; } }; final class PotentialColonize { Planet@ pl; const ResourceType@ resource; double weight = 0; }; final class ColonizeLog { int typeId; double time; }; tidy final class ColonizeQueue { ResourceSpec@ spec; Planet@ target; ColonizeData@ step; ImportData@ forData; ColonizeQueue@ parent; array<ColonizeQueue@> children; void save(Colonization& colonization, SaveFile& file) { file << spec; file << target; colonization.saveColonize(file, step); colonization.resources.saveImport(file, forData); uint cnt = children.length; file << cnt; for(uint i = 0; i < cnt; ++i) children[i].save(colonization, file); } void load(Colonization& colonization, SaveFile& file) { @spec = ResourceSpec(); file >> spec; file >> target; @step = colonization.loadColonize(file); @forData = colonization.resources.loadImport(file); uint cnt = 0; file >> cnt; children.length = cnt; for(uint i = 0; i < cnt; ++i) { @children[i] = ColonizeQueue(); @children[i].parent = this; children[i].load(colonization, file); } } }; final class Colonization : AIComponent { const ResourceClass@ foodClass, waterClass, scalableClass; Resources@ resources; Planets@ planets; Systems@ systems; Budget@ budget; Creeping@ creeping; RaceColonization@ race; array<ColonizeQueue@> queue; array<ColonizeData@> colonizing; array<ColonizeData@> awaitingSource; array<WaitUsed@> waiting; array<ColonizePenalty@> penalties; set_int penaltySet; int nextColonizeId = 0; array<ColonizeLog@> colonizeLog; array<PotentialSource@> sources; double sourceUpdate = 0; //Maximum colonizations that can still be done this turn uint remainColonizations = 0; //Amount of colonizations that have happened so far this budget cycle uint curColonizations = 0; //Amount of colonizations that happened the previous budget cycle uint prevColonizations = 0; //Whether to automatically find sources and order colonizations bool performColonization = true; bool queueColonization = true; //Colonization focus private uint _phase = CP_Expansion; //Territory request data private bool _needsMoreTerritory = false; private bool _needsNewTerritory = false; private uint _territoryRequests = 0; private Region@ _newTerritoryTarget; Object@ colonizeWeightObj; bool get_needsMoreTerritory() const { return _needsMoreTerritory; } bool get_needsNewTerritory() const { return _needsNewTerritory; } void create() { @resources = cast<Resources>(ai.resources); @planets = cast<Planets>(ai.planets); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); @creeping = cast<Creeping>(ai.creeping); @race = cast<RaceColonization>(ai.race); //Get some heuristic resource classes @foodClass = getResourceClass("Food"); @waterClass = getResourceClass("WaterType"); @scalableClass = getResourceClass("Scalable"); } void save(SaveFile& file) { file << nextColonizeId; file << remainColonizations; file << curColonizations; file << prevColonizations; file << _phase; file << _needsMoreTerritory; file << _needsNewTerritory; file << _territoryRequests; if (_newTerritoryTarget !is null) { file.write1(); file << _newTerritoryTarget; } else file.write0(); uint cnt = colonizing.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveColonize(file, colonizing[i]); colonizing[i].save(this, file); } cnt = waiting.length; file << cnt; for(uint i = 0; i < cnt; ++i) waiting[i].save(this, file); cnt = penalties.length; file << cnt; for(uint i = 0; i < cnt; ++i) penalties[i].save(file); cnt = colonizeLog.length; file << cnt; for(uint i = 0; i < cnt; ++i) { file.writeIdentifier(SI_Resource, colonizeLog[i].typeId); file << colonizeLog[i].time; } cnt = queue.length; file << cnt; for(uint i = 0; i < cnt; ++i) queue[i].save(this, file); } void load(SaveFile& file) { file >> nextColonizeId; file >> remainColonizations; file >> curColonizations; file >> prevColonizations; file >> _phase; file >> _needsMoreTerritory; file >> _needsNewTerritory; file >> _territoryRequests; if(file.readBit()) { file >> _newTerritoryTarget; } uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadColonize(file); if(data !is null) { data.load(this, file); if(data.target !is null) { colonizing.insertLast(data); if(data.colonizeFrom is null) awaitingSource.insertLast(data); } else { data.canceled = true; } } else { ColonizeData().load(this, file); } } file >> cnt; waiting.length = cnt; for(uint i = 0; i < cnt; ++i) { @waiting[i] = WaitUsed(); waiting[i].load(this, file); } file >> cnt; for(uint i = 0; i < cnt; ++i) { ColonizePenalty pen; pen.load(file); if(pen.pl !is null) { penaltySet.insert(pen.pl.id); penalties.insertLast(pen); } } file >> cnt; for(uint i = 0; i < cnt; ++i) { ColonizeLog logEntry; logEntry.typeId = file.readIdentifier(SI_Resource); file >> logEntry.time; colonizeLog.insertLast(logEntry); } file >> cnt; queue.length = cnt; for(uint i = 0; i < cnt; ++i) { @queue[i] = ColonizeQueue(); queue[i].load(this, file); } } array<ColonizeData@> loadIds; ColonizeData@ loadColonize(int id) { if(id == -1) return null; for(uint i = 0, cnt = loadIds.length; i < cnt; ++i) { if(loadIds[i].id == id) return loadIds[i]; } ColonizeData data; data.id = id; loadIds.insertLast(data); return data; } ColonizeData@ loadColonize(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadColonize(id); } void saveColonize(SaveFile& file, ColonizeData@ data) { int id = -1; if(data !is null) id = data.id; file << id; } void postLoad(AI& ai) { loadIds.length = 0; } bool canBeColonized(Planet& target) { if(!target.valid) return false; if(target.owner.valid) return false; return true; } bool canColonize(Planet& source) { if(source.level == 0) return false; if(source.owner !is ai.empire) return false; return true; } bool shouldForceExpansion() { uint otherColonizedSystems = 0; for (uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) { auto@ sys = systems.outsideBorder[i]; //Check if any system in our tradable area is unexplored if (!sys.explored) return false; if (sys.planets.length > 0) { uint otherColonizedPlanets = 0; for (uint j = 0, cnt = sys.planets.length; j < cnt; ++j) { auto@ pl = sys.planets[j]; int resId = pl.primaryResourceType; if (resId != -1) { //Check if any planet can still be colonized in our tradable area if (!pl.owner.valid && !pl.quarantined) return false; else ++otherColonizedPlanets; } } //Check if all planets in the system are colonized if (otherColonizedPlanets == sys.planets.length) ++otherColonizedSystems; } } //Check if all systems in our tradable area belong to other empires if (otherColonizedSystems == systems.outsideBorder.length) //If 0, we colonized everything! return false; return true; } double getSourceWeight(PotentialSource& source, ColonizeData& data) { double w = source.weight; w /= data.target.position.distanceTo(source.pl.position); return w; } void updateSources() { planets.getColonizeSources(sources); } void focusTick(double time) { if(sourceUpdate < gameTime && performColonization) { updateSources(); if(sources.length == 0 && gameTime < 60.0) sourceUpdate = gameTime + 1.0; else sourceUpdate = gameTime + 10.0; } if (ai.behavior.forbidColonization) return; //Find some new colonizations we can queue up from resources fillQueueFromRequests(); //If we've gained any requests, see if we can order another colonize if(remainColonizations > 0 && (budget.Progress < ai.behavior.colonizeMaxBudgetProgress || gameTime < 3.0 * 60.0) && (sources.length > 0 || !performColonization) && canColonize() && queueColonization) { //Actually go order some colonizations from the queue if(orderFromQueue()) { doColonize(); } else if(awaitingSource.length == 0) { if(genericExpand() !is null) doColonize(); } } //Find colonization sources for everything that needs them if(awaitingSource.length != 0 && performColonization) { for(uint i = 0, cnt = awaitingSource.length; i < cnt; ++i) { auto@ target = awaitingSource[i]; PotentialSource@ src; double bestSource = 0; for(uint j = 0, jcnt = sources.length; j < jcnt; ++j) { double w = getSourceWeight(sources[j], target); if(w > bestSource) { bestSource = w; @src = sources[j]; } } if(src !is null) { orderColonization(target, src.pl); sources.remove(src); --i; --cnt; } } } //Check if any resources we're waiting for are being used for(uint i = 0, cnt = waiting.length; i < cnt; ++i) { auto@ wait = waiting[i]; if(wait.resource.obj is null || !wait.resource.obj.valid || wait.resource.obj.owner !is ai.empire || wait.resource.request !is null) { wait.forData.isColonizing = false; waiting.removeAt(i); --i; --cnt; } } //Prune old colonization penalties for(uint i = 0, cnt = penalties.length; i < cnt; ++i) { auto@ pen = penalties[i]; if(pen.pl !is null && pen.pl.owner is ai.empire) pen.pl.forceAbandon(); if(pen.until < gameTime) { if(pen.pl !is null) penaltySet.erase(pen.pl.id); penalties.removeAt(i); --i; --cnt; } } } void orderColonization(ColonizeData& data, Planet& sourcePlanet) { if(log) ai.print("start colonizing "+data.target.name, sourcePlanet); if(race !is null) { if(race.orderColonization(data, sourcePlanet)) return; } @data.colonizeFrom = sourcePlanet; awaitingSource.remove(data); sourcePlanet.colonize(data.target); } void tick(double time) { //Check if we've finished colonizing anything for(uint i = 0, cnt = colonizing.length; i < cnt; ++i) { auto@ c = colonizing[i]; //Remove if we can no longer colonize it Empire@ visOwner = c.target.visibleOwnerToEmp(ai.empire); if(visOwner !is ai.empire && (visOwner is null || visOwner.valid)) { //Fail out this colonization cancelColonization(c); --i; --cnt; continue; } //Check for succesful colonization if(visOwner is ai.empire) { double population = c.target.population; if(population >= 1.0) { finishColonization(c); colonizing.removeAt(i); --i; --cnt; continue; } else { if(c.checkTime == -1.0) { c.checkTime = gameTime; } else { double grace = ai.behavior.colonizeFailGraceTime; if(population > 0.9) grace *= 2.0; if(c.checkTime + grace < gameTime) { //Fail out this colonization and penalize the target creeping.requestClear(systems.getAI(c.target.region)); cancelColonization(c, penalize=ai.behavior.colonizePenalizeTime); --i; --cnt; continue; } } } } //This colonization is still waiting for a good source if(c.colonizeFrom is null) continue; //Check for failed colonization if(!canColonize(c.colonizeFrom) || !performColonization) { if(c.target.owner is ai.empire && performColonization) c.target.stopColonizing(c.target); @c.colonizeFrom = null; awaitingSource.insertAt(0, c); } } //Update the colonization queue updateQueue(); } void cancelColonization(ColonizeData@ data, double penalize = 0) { if(data.colonizeFrom !is null && data.colonizeFrom.owner is ai.empire) data.colonizeFrom.stopColonizing(data.target); if(data.colonizeFrom is null) awaitingSource.remove(data); if(data.target.owner is ai.empire) data.target.forceAbandon(); data.canceled = true; sourceUpdate = 0; colonizing.remove(data); if(penalize != 0) { ColonizePenalty pen; @pen.pl = data.target; pen.until = gameTime + penalize; penaltySet.insert(pen.pl.id); penalties.insertLast(pen); } } void finishColonization(ColonizeData@ data) { if(data.colonizeFrom is null) awaitingSource.remove(data); //If we just colonized a new territory, reset request data if (data.target.region is _newTerritoryTarget) { _needsNewTerritory = false; @_newTerritoryTarget = null; } PlanetAI@ plAI = planets.register(data.target); ColonizeLog logEntry; logEntry.typeId = data.target.primaryResourceType; logEntry.time = gameTime; colonizeLog.insertLast(logEntry); data.completed = true; sourceUpdate = 0; } double timeSinceMatchingColonize(ResourceSpec& spec) { for(int i = colonizeLog.length - 1; i >= 0; --i) { auto@ res = getResource(colonizeLog[i].typeId); if(res !is null && spec.meets(res)) return gameTime - colonizeLog[i].time; } return gameTime; } bool isColonizing(Planet& pl) { for(uint i = 0, cnt = colonizing.length; i < cnt; ++i) { if(colonizing[i].target is pl) return true; } for(uint i = 0, cnt = queue.length; i < cnt; ++i) { if(isColonizing(pl, queue[i])) return true; } return false; } bool isColonizing(Planet& pl, ColonizeQueue@ q) { if(q.target is pl) return true; for(uint i = 0, cnt = q.children.length; i < cnt; ++i) { if(isColonizing(pl, q.children[i])) return true; } return false; } double getGenericUsefulness(const ResourceType@ type) { //Return a relative value for colonizing the resource this planet has in a vacuum, //rather than as an explicit requirement for a planet. double weight = 1.0; if(type.level == 0) { weight *= 2.0; } else { weight /= sqr(double(1 + type.level)); weight *= 0.001; } if(type.cls is foodClass || type.cls is waterClass) weight *= 10.0; if(type.cls is scalableClass) weight *= 0.0001; if(type.totalPressure > 0) weight *= double(type.totalPressure); if(race !is null) weight *= race.getGenericUsefulness(type); return weight; } ColonizeData@ colonize(Planet& pl) { if(log) ai.print("queue colonization", pl); ColonizeData data; data.id = nextColonizeId++; @data.target = pl; budget.spend(BT_Colonization, 0, ai.behavior.colonizeBudgetCost); colonizing.insertLast(data); awaitingSource.insertLast(data); return data; } ColonizeData@ colonize(ResourceSpec@ spec) { Planet@ newColony; double w; double bestWeight = 0.0; for(uint i = 0, cnt = potentials.length; i < cnt; ++i) { auto@ p = potentials[i]; Region@ reg = p.pl.region; if(reg is null) continue; if(!spec.meets(p.resource)) continue; if(isColonizing(p.pl)) continue; //Skip planets out of our new territory target if we are colonizing a new one if (_newTerritoryTarget !is null && p.pl.region !is _newTerritoryTarget) continue; auto@ sys = systems.getAI(reg); w = 1.0; if (sys.border) w *= 0.25; if (!sys.owned && !sys.border) w /= 0.25; if (sys.obj.PlanetsMask & ~ai.mask != 0) w *= 0.25; if (w > bestWeight) { @newColony = p.pl; bestWeight = w; } } if(newColony !is null) return colonize(newColony); else return null; } array<PotentialColonize@> potentials; void checkSystem(SystemAI@ sys) { uint presentMask = sys.seenPresent; if(presentMask & ai.mask == 0) { if(!ai.behavior.colonizeEnemySystems && (presentMask & ai.enemyMask) != 0) return; if(!ai.behavior.colonizeNeutralOwnedSystems && (presentMask & ai.neutralMask) != 0) return; if(!ai.behavior.colonizeAllySystems && (presentMask & ai.allyMask) != 0) return; } double sysWeight = 1.0; if(presentMask & ai.mask == 0) sysWeight *= ai.behavior.weightOutwardExpand; uint plCnt = sys.planets.length; for(uint n = 0; n < plCnt; ++n) { Planet@ pl = sys.planets[n]; Empire@ visOwner = pl.visibleOwnerToEmp(ai.empire); if(!pl.valid || visOwner.valid) continue; if(isColonizing(pl)) continue; if(penaltySet.contains(pl.id)) continue; if(pl.quarantined) continue; int resId = pl.primaryResourceType; if(resId == -1) continue; PotentialColonize p; @p.pl = pl; @p.resource = getResource(resId); p.weight = 1.0 * sysWeight; //TODO: this should be weighted according to the position of the planet, //we should try to colonize things in favorable positions potentials.insertLast(p); } } double nextPotentialCheck = 0.0; array<PotentialColonize@>@ getPotentialColonize() { if(gameTime < nextPotentialCheck) return potentials; potentials.length = 0; for(uint i = 0, cnt = systems.owned.length; i < cnt; ++i) checkSystem(systems.owned[i]); for(uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) checkSystem(systems.outsideBorder[i]); if(needsNewTerritory) { for(uint i = 0, cnt = systems.all.length; i < cnt; ++i) { if(systems.all[i].explored) checkSystem(systems.all[i]); } } if(systems.owned.length == 0) { Region@ homeSys = ai.empire.HomeSystem; if(homeSys !is null) { auto@ homeAI = systems.getAI(homeSys); if(homeAI !is null) checkSystem(homeAI); } else { for(uint i = 0, cnt = systems.all.length; i < cnt; ++i) { if(systems.all[i].visible) checkSystem(systems.all[i]); } } } if(potentials.length == 0 && gameTime < 60.0) nextPotentialCheck = gameTime + 1.0; else nextPotentialCheck = gameTime + randomd(10.0, 40.0); //TODO: This should be able to colonize across empires we have trade agreements with? return potentials; } bool canColonize() { if(remainColonizations == 0) return false; if(curColonizations >= ai.behavior.guaranteeColonizations) { if(!budget.canSpend(BT_Colonization, 0, ai.behavior.colonizeBudgetCost)) return false; } if(ai.behavior.maxConcurrentColonizations <= colonizing.length) return false; return true; } void doColonize() { remainColonizations -= 1; curColonizations += 1; budget.spend(BT_Colonization, 0, ai.behavior.colonizeBudgetCost); } ColonizeData@ genericExpand() { auto@ potentials = getPotentialColonize(); //Do generic expansion using any remaining colonization steps we have if(ai.behavior.colonizeGenericExpand) { PotentialColonize@ expand; double w; double bestWeight = 0.0; for(uint i = 0, cnt = potentials.length; i < cnt; ++i) { auto@ p = potentials[i]; w = p.weight * getGenericUsefulness(p.resource); modPotentialWeight(p, w); Region@ reg = p.pl.region; if(reg is null) continue; if(reg.PlanetsMask & ai.mask != 0) continue; //Skip planets out of our new territory target if we are colonizing a new one if (_newTerritoryTarget !is null && p.pl.region !is _newTerritoryTarget) continue; if(w == 0) continue; if (w > bestWeight) { @expand = p; bestWeight = w; } } if(expand !is null) { auto@ data = colonize(expand.pl); potentials.remove(expand); if (needsNewTerritory && _newTerritoryTarget is null) { //Check if our target planet is outside our tradable area bool found = false; for (uint i = 0, cnt = systems.owned.length; i < cnt; ++i) { if (systems.owned[i].obj is expand.pl.region) found = true; } for (uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) { if (systems.outsideBorder[i].obj is expand.pl.region) found = true; } if (!found) @_newTerritoryTarget = expand.pl.region; } return data; } } return null; } void turn() { //Figure out how much we can colonize remainColonizations = ai.behavior.maxColonizations; //Decide colonization phase if (_phase == CP_Expansion) { if (ai.empire.EstNextBudget < budget.criticalThreshold) { remainColonizations = 0; _phase = CP_Stabilization; if (log) ai.print("Colonization: entering stabilization phase with estimated next budget: " + ai.empire.EstNextBudget); } else if (ai.empire.EstNextBudget < budget.lowThreshold) { remainColonizations = 1; if (log) ai.print("Colonization: continuing expansion phase with estimated next budget: " + ai.empire.EstNextBudget); } else if (ai.empire.EstNextBudget < budget.mediumThreshold) { remainColonizations = min(2, ai.behavior.maxColonizations); if (log) ai.print("Colonization: continuing expansion phase with estimated next budget: " + ai.empire.EstNextBudget); } } else if (_phase == CP_Stabilization) { if (ai.empire.RemainingBudget > budget.mediumThreshold) { _phase = CP_Expansion; if (log) ai.print("Colonization: entering expansion phase with budget: " + ai.empire.RemainingBudget); } else { remainColonizations = 1; if (log) ai.print("Colonization: continuing stabilization phase with budget: " + ai.empire.RemainingBudget); } } if (ai.empire.EstNextBudget <= 0 && !ai.behavior.forbidScuttle) { //We are in trouble. Abandon planets sucking budget up if (log) ai.print("Colonization: negative budget, abandoning planets"); auto@ homeworld = ai.empire.Homeworld; for (uint i = 0, cnt = planets.planets.length; i < cnt; i++) { auto@ pl = planets.planets[i].obj; if (pl is homeworld) continue; int resId = pl.primaryResourceType; if(resId == -1) continue; const ResourceType@ type = getResource(resId); if ((type.cls is scalableClass || type.level > 0) && pl.resourceLevel == 0) { pl.forceAbandon(); } } //If we are still in trouble, abandon more planets if (ai.empire.EstNextBudget <= 0) { for (uint i = 0, cnt = planets.planets.length; i < cnt; i++) { auto@ pl = planets.planets[i].obj; if (pl is homeworld) continue; int resId = pl.primaryResourceType; if(resId == -1) continue; const ResourceType@ type = getResource(resId); if ((type.cls is foodClass || type.cls is waterClass) && !pl.primaryResourceExported) pl.forceAbandon(); } //More! if (ai.empire.EstNextBudget <= 0) { for (uint i = 0, cnt = planets.planets.length; i < cnt; i++) { auto@ pl = planets.planets[i].obj; if (pl is homeworld) continue; int resId = pl.primaryResourceType; if(resId == -1) continue; const ResourceType@ type = getResource(resId); if (!(type.cls is foodClass || type.cls is waterClass || type.cls is scalableClass) && type.level == 0) pl.forceAbandon(); } } } } //Check if we need to push for territory if (shouldForceExpansion()) { //If we already spent at least three turns trying to extend our territory, colonize a new one if (needsMoreTerritory && _territoryRequests >= 3) _needsNewTerritory = true; else { _needsMoreTerritory = true; _territoryRequests++; } } else { _needsMoreTerritory = false; _needsNewTerritory = false; _territoryRequests = 0; @_newTerritoryTarget = null; } prevColonizations = curColonizations; curColonizations = 0; updateSources(); if(log) { ai.print("Empire colonization standings at "+formatGameTime(gameTime)+":"); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ other = getEmpire(i); if(other.major) ai.print(" "+ai.pad(other.name, 20)+" - "+ai.pad(other.TotalPlanets.value+" planets", 15)+" - "+other.points.value+" points"); } } } bool shouldQueueFor(const ResourceSpec@ spec, ColonizeQueue@ inside = null) { auto@ list = inside is null ? this.queue : inside.children; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ q = list[i]; //haven't managed to resolve it fully, skip it as well if(spec.type == RST_Level_Specific) { if(q.spec.type == RST_Level_Specific && q.spec.level == spec.level) { if(!isResolved(q)) return false; } } //Check anything inner to this tree element if(!shouldQueueFor(spec, q)) return false; } return true; } bool shouldQueueFor(ImportData@ imp, ColonizeQueue@ inside = null) { auto@ list = inside is null ? this.queue : inside.children; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ q = list[i]; //If we already have this in our queue tree, don't colonize it again if(imp.forLevel) { if(q.forData is imp) return false; if(q.parent !is null && q.parent.step !is null && q.parent.step.target is imp.obj) { if(q.spec == imp.spec) return false; } } //If we're already trying to get something of this level, but we //haven't managed to resolve it fully, skip it as well if(imp.spec.type == RST_Level_Specific) { if(q.spec.type == RST_Level_Specific && q.spec.level == imp.spec.level) { if(!isResolved(q)) return false; } } //Check anything inner to this tree element if(!shouldQueueFor(imp, q)) return false; } return true; } ColonizeQueue@ queueColonize(ResourceSpec& spec, bool place = true) { ColonizeQueue q; @q.spec = spec; if(place) queue.insertLast(q); return q; } bool unresolvedInQueue() { for(uint i = 0, cnt = queue.length; i < cnt; ++i) { auto@ q = queue[i]; if(q.parent !is null) continue; if(!isResolved(q)) return true; } return false; } bool isResolved(ColonizeQueue@ q) { if(q.step is null || q.step.canceled) return false; for(uint i = 0 , cnt = q.children.length; i < cnt; ++i) { if(!isResolved(q.children[i])) return false; } return true; } bool isResolved(ImportData@ req, ColonizeQueue@ inside = null) { auto@ list = inside is null ? this.queue : inside.children; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ q = list[i]; if(q.forData is req) return isResolved(q); if(isResolved(req, inside=q)) return true; } return false; } Planet@ resolve(ColonizeQueue@ q) { if(q.step !is null) return q.step.target; auto@ potentials = getPotentialColonize(); PotentialColonize@ take; double takeWeight = 0.0; for(uint i = 0, cnt = potentials.length; i < cnt; ++i) { auto@ p = potentials[i]; if(!q.spec.meets(p.resource)) continue; //Skip planets out of our new territory target if we are colonizing a new one if (_newTerritoryTarget !is null && p.pl.region !is _newTerritoryTarget) continue; if(p.weight > takeWeight) { takeWeight = p.weight; @take = p; } } if(take !is null) { @q.target = take.pl; potentials.remove(take); array<ResourceSpec@> allReqs; for(uint i = 1, cnt = take.resource.level; i <= cnt; ++i) { const PlanetLevel@ lvl = getPlanetLevel(take.pl, i); if(lvl !is null) { array<ResourceSpec@> reqList; array<ResourceSpec@> curReqs; curReqs = allReqs; const ResourceRequirements@ reqs = lvl.reqs; for(uint i = 0, cnt = reqs.reqs.length; i < cnt; ++i) { auto@ need = reqs.reqs[i]; bool found = false; for(uint n = 0, ncnt = curReqs.length; n < ncnt; ++n) { if(curReqs[n].implements(need)) { found = true; curReqs.removeAt(n); break; } } if(!found) reqList.insertLast(implementSpec(need)); } reqList.sortDesc(); auto@ resRace = cast<RaceResources>(race); if(resRace !is null) resRace.levelRequirements(take.pl, i, reqList); for(uint i = 0, cnt = reqList.length; i < cnt; ++i) { auto@ spec = reqList[i]; allReqs.insertLast(spec); auto@ inner = queueColonize(spec, place=false); @inner.parent = q; q.children.insertLast(inner); resolve(inner); } } } return take.pl; } return null; } void kill(ColonizeQueue@ q) { for(uint i = 0, cnt = q.children.length; i < cnt; ++i) kill(q.children[i]); q.children.length = 0; if(q.forData !is null) q.forData.isColonizing = false; @q.parent = null; } void modPotentialWeight(PotentialColonize@ c, double& weight) { if(colonizeWeightObj !is null) weight /= c.pl.position.distanceTo(colonizeWeightObj.position)/1000.0; } bool update(ColonizeQueue@ q) { //See if we can find a matching import request if(q.forData is null && q.parent !is null && q.parent.target !is null) { for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) { auto@ req = resources.requested[i]; if(req.isColonizing) continue; if(req.obj !is q.parent.target) continue; if(req.spec != q.spec) continue; req.isColonizing = true; @q.forData = req; } } //Cancel everything if our request is already being met if(q.forData !is null && q.forData.beingMet) { kill(q); return false; } //If it's not resolved, try to resolve it if(q.target is null) resolve(q); //If the colonization failed, try to find a new planet for it if((q.step !is null && q.step.canceled) || (q.step is null && q.target !is null && !canBeColonized(q.target))) { auto@ potentials = getPotentialColonize(); PotentialColonize@ take; double takeWeight = 0.0; for(uint i = 0, cnt = potentials.length; i < cnt; ++i) { auto@ p = potentials[i]; if(!q.spec.meets(p.resource)) continue; //Skip planets out of our new territory target if we are colonizing a new one if (_newTerritoryTarget !is null && p.pl.region !is _newTerritoryTarget) continue; double w = p.weight; modPotentialWeight(p, w); if(w > takeWeight) { takeWeight = p.weight; @take = p; } } if(take !is null) { @q.target = take.pl; @q.step = null; potentials.remove(take); } } for(uint i = 0, cnt = q.children.length; i < cnt; ++i) { if(!update(q.children[i])) { @q.children[i].parent = null; q.children.removeAt(i); --i; --cnt; } } if(q.children.length == 0 && q.step !is null && q.step.completed) { if(q.forData !is null) { q.forData.isColonizing = false; PlanetAI@ plAI = planets.getAI(q.target); if(plAI !is null) { if(plAI.resources.length != 0) { WaitUsed wait; @wait.forData = q.forData; @wait.resource = plAI.resources[0]; waiting.insertLast(wait); q.forData.isColonizing = true; } } } return false; } return true; } void updateQueue() { for(uint i = 0, cnt = queue.length; i < cnt; ++i) { auto@ q = queue[i]; if(!update(q)) { queue.removeAt(i); --i; --cnt; } } } bool orderFromQueue(ColonizeQueue@ inside = null) { auto@ list = inside is null ? this.queue : inside.children; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ q = list[i]; if(q.step is null && q.target !is null) { @q.step = colonize(q.target); return true; } if(orderFromQueue(q)) return true; } return false; } void dumpQueue(ColonizeQueue@ inside = null) { auto@ list = inside is null ? this.queue : inside.children; string prefix = ""; if(inside !is null) { prefix += " "; ColonizeQueue@ top = inside.parent; while(top !is null) { prefix += " "; @top = top.parent; } } for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ q = list[i]; string txt = "- "+q.spec.dump(); if(q.forData !is null) txt += " for request "+q.forData.obj.name+""; else if(q.parent !is null && q.parent.target !is null) txt += " for parent "+q.parent.target.name+""; if(q.target !is null) txt += " ==> "+q.target.name; print(prefix+txt); dumpQueue(q); } } void fillQueueFromRequests() { for(uint i = 0, cnt = resources.requested.length; i < cnt && remainColonizations > 0; ++i) { auto@ req = resources.requested[i]; if(!req.isOpen) continue; if(!req.cycled) continue; if(req.claimedFor) continue; if(req.isColonizing) continue; if(shouldQueueFor(req)) { auto@ q = queueColonize(req.spec); @q.forData = req; req.isColonizing = true; } } } }; AIComponent@ createColonization() { return Colonization(); } |
Added scripts/server/empire_ai/weasel/Consider.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
// Consider // -------- // Helps AI usage hints to consider various things in the empire. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Systems; import empire_ai.weasel.Planets; import empire_ai.weasel.Development; import empire_ai.weasel.Construction; import empire_ai.weasel.Fleets; import empire_ai.weasel.Resources; import empire_ai.weasel.Colonization; import empire_ai.weasel.Intelligence; import buildings; import ai.consider; from ai.artifacts import ArtifactConsider; from orbitals import OrbitalModule; class Consider : AIComponent, Considerer { Systems@ systems; Fleets@ fleets; Planets@ planets; Construction@ construction; Development@ development; Resources@ resources; Intelligence@ intelligence; Colonization@ colonization; void create() { @systems = cast<Systems>(ai.systems); @fleets = cast<Fleets>(ai.fleets); @planets = cast<Planets>(ai.planets); @development = cast<Development>(ai.development); @construction = cast<Construction>(ai.construction); @resources = cast<Resources>(ai.resources); @intelligence = cast<Intelligence>(ai.intelligence); @colonization = cast<Colonization>(ai.colonization); } Empire@ get_empire() { return ai.empire; } Object@ secondary; ArtifactConsider@ artifactConsider; double bestWeight; ImportData@ request; const BuildingType@ bldType; const OrbitalModule@ _module; ConsiderComponent@ comp; ConsiderFilter@ cfilter; double get_selectedWeight() { return bestWeight; } Object@ get_currentSupplier() { return secondary; } ArtifactConsider@ get_artifact() { return artifactConsider; } void set_artifact(ArtifactConsider@ cons) { @artifactConsider = cons; } double get_idleTime() { if(request !is null) return gameTime - request.idleSince; return 0.0; } double timeSinceMatchingColonize() { if(request is null) return INFINITY; return colonization.timeSinceMatchingColonize(request.spec); } const BuildingType@ get_building() { return bldType; } void set_building(const BuildingType@ type) { @bldType = type; } const OrbitalModule@ get_module() { return _module; } void set_module(const OrbitalModule@ type) { @_module = type; } ConsiderComponent@ get_component() { return comp; } void set_component(ConsiderComponent@ comp) { @this.comp = comp; } void set_filter(ConsiderFilter@ filter) { @this.cfilter = filter; } void clear() { @secondary = null; @artifactConsider = null; @request = null; @comp = null; @bldType = null; @cfilter = null; } Object@ OwnedSystems(const ConsiderHook& hook, uint limit = uint(-1)) { Object@ best; bestWeight = 0.0; uint offset = randomi(0, systems.owned.length-1); uint cnt = min(systems.owned.length, limit); for(uint i = 0; i < cnt; ++i) { uint index = (i+offset) % systems.owned.length; Region@ obj = systems.owned[index].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ Fleets(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { Object@ fleet = fleets.fleets[i].obj; if(fleet !is null) { if(cfilter !is null && !cfilter.filter(fleet)) continue; double w = hook.consider(this, fleet); if(w > bestWeight) { bestWeight = w; @best = fleet; } } } clear(); return best; } Object@ BorderSystems(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = systems.border.length; i < cnt; ++i) { Region@ obj = systems.border[i].obj; if(obj.PlanetsMask & ~ai.mask == 0) continue; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } for(uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) { Region@ obj = systems.outsideBorder[i].obj; if(obj.PlanetsMask & ~ai.mask == 0) continue; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ OtherSystems(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = intelligence.intel.length; i < cnt; ++i) { auto@ intel = intelligence.intel[i]; if(intel is null) continue; for(uint i = 0, cnt = intel.theirOwned.length; i < cnt; ++i) { Region@ obj = intel.theirOwned[i].obj; if(obj.PlanetsMask & ~ai.mask == 0) continue; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } } clear(); return best; } Object@ SystemsInTerritory(const ConsiderHook& hook, const Territory& territory, uint limit = uint(-1)) { Object@ best; bestWeight = 0.0; uint regionCount = territory.getRegionCount(); uint offset = randomi(0, regionCount -1); uint cnt = min(regionCount, limit); for(uint i = 0; i < cnt; ++i) { uint index = (i+offset) % regionCount; Region@ obj = territory.getRegion(index); if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ ImportantPlanets(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ ImportantPlanetsInTerritory(const ConsiderHook& hook, const Territory& territory) { Object@ best;; bestWeight = 0.0; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; if (obj.region !is null) { if (obj.region.getTerritory(ai.empire) !is territory) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } } clear(); return best; } Object@ AllPlanets(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) { Object@ obj = planets.planets[i].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ PlanetsInTerritory(const ConsiderHook& hook, const Territory& territory) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) { Object@ obj = planets.planets[i].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; if (obj.region !is null) { if (obj.region.getTerritory(ai.empire) !is territory) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } } clear(); return best; } Object@ SomePlanets(const ConsiderHook& hook, uint count, bool alwaysImportant) { Object@ best; bestWeight = 0.0; if(alwaysImportant) { for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } } uint planetCount = planets.planets.length; uint offset = randomi(0, planetCount-1); uint cnt = min(count, planetCount); for(uint i = 0; i < cnt; ++i) { uint index = (offset+i) % planetCount; Object@ obj = planets.planets[index].obj; if(obj !is null) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ FactoryPlanets(const ConsiderHook& hook) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = construction.factories.length; i < cnt; ++i) { Object@ obj = construction.factories[i].obj; if(obj !is null && obj.isPlanet) { if(cfilter !is null && !cfilter.filter(obj)) continue; double w = hook.consider(this, obj); if(w > bestWeight) { bestWeight = w; @best = obj; } } } clear(); return best; } Object@ MatchingImportRequests(const ConsiderHook& hook, const ResourceType@ type, bool considerExisting) { Object@ best; bestWeight = 0.0; for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) { ImportData@ req = resources.requested[i]; if(!considerExisting) { if(req.beingMet || req.claimedFor) continue; } if(req.spec.meets(type, req.obj, req.obj)) { @secondary = null; @request = req; double w = hook.consider(this, req.obj); if(w > bestWeight) { if(cfilter !is null && !cfilter.filter(req.obj)) continue; bestWeight = w; @best = req.obj; } } } if(considerExisting) { for(uint i = 0, cnt = resources.used.length; i < cnt; ++i) { ExportData@ res = resources.used[i]; ImportData@ req = res.request; if(req !is null && req.spec.meets(type, req.obj, req.obj)) { @secondary = res.obj; @request = req; double w = hook.consider(this, req.obj); if(w > bestWeight) { if(cfilter !is null && !cfilter.filter(req.obj)) continue; bestWeight = w; @best = req.obj; } } } } clear(); return best; } }; AIComponent@ createConsider() { return Consider(); } |
Added scripts/server/empire_ai/weasel/Construction.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 |
// Construction // ------------ // Manages factories and allows build requests for flagships, orbitals, and // anything else that requires labor. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Budget; import empire_ai.weasel.Planets; import empire_ai.weasel.Designs; import empire_ai.weasel.Resources; import empire_ai.weasel.Systems; import empire_ai.weasel.Orbitals; import orbitals; import saving; import systems; import regions.regions; import ai.construction; from constructible import ConstructibleType; from constructions import ConstructionType, getConstructionType; class AllocateConstruction : IConstruction { protected bool _completed = false; protected bool _started = false; protected int _id = -1; uint moneyType = BT_Development; Factory@ tryFactory; double maxTime = INFINITY; double completedAt = 0; AllocateBudget@ alloc; int cost = 0; int maintenance = 0; double priority = 1.0; AllocateConstruction() { } int id { get const { return _id; } set { _id = value; } } bool get_started() const { return _started; } bool completed { get const { return _completed; } set { _completed = value; } } void _save(Construction& construction, SaveFile& file) { file << moneyType; construction.saveFactory(file, tryFactory); file << maxTime; file << _completed; file << _started; file << completedAt; construction.budget.saveAlloc(file, alloc); file << cost; file << maintenance; file << priority; save(construction, file); } void save(Construction& construction, SaveFile& file) { } void _load(Construction& construction, SaveFile& file) { file >> moneyType; @tryFactory = construction.loadFactory(file); file >> maxTime; file >> _completed; file >> _started; file >> completedAt; @alloc = construction.budget.loadAlloc(file); file >> cost; file >> maintenance; file >> priority; load(construction, file); } void load(Construction& construction, SaveFile& file) { } bool tick(AI& ai, Construction& construction, double time) { if(tryFactory !is null && alloc.allocated) { construction.start(tryFactory, this); return false; } return true; } void update(AI& ai, Factory@ f) { @alloc = cast<Budget>(ai.budget).allocate(moneyType, cost, maintenance, priority); } double laborCost(AI& ai, Object@ obj) { return 0.0; } bool canBuild(AI& ai, Factory@ f) { return true; } void construct(AI& ai, Factory@ f) { _started = true; } string toString() { return "construction"; } }; class BuildFlagship : AllocateConstruction, IFlagshipConstruction { protected const Design@ _design; double baseLabor = 0.0; DesignTarget@ target; BuildFlagship() { } BuildFlagship(const Design@ dsg) { set(dsg); } BuildFlagship(DesignTarget@ target) { @this.target = target; } const Design@ get_design() const { return _design; } void save(Construction& construction, SaveFile& file) { file << baseLabor; if(_design !is null) { file.write1(); file << _design; } else { file.write0(); } construction.designs.saveDesign(file, target); } void load(Construction& construction, SaveFile& file) { file >> baseLabor; if(file.readBit()) file >> _design; @target = construction.designs.loadDesign(file); } void set(const Design& dsg) { @_design = dsg.mostUpdated(); baseLabor = _design.total(HV_LaborCost); } double laborCost(AI& ai, Object@ obj) { return baseLabor; } bool tick(AI& ai, Construction& construction, double time) override { if(target !is null) { if(target.active !is null) { set(target.active); @target = null; } } return AllocateConstruction::tick(ai, construction, time); } bool canBuild(AI& ai, Factory@ f) override { if(!f.obj.canBuildShips) return false; return _design !is null; } void update(AI& ai, Factory@ f) { double c = _design.total(HV_BuildCost); c *= double(f.obj.shipBuildCost) / 100.0; c *= f.obj.constructionCostMod; cost = ceil(c); maintenance = ceil(_design.total(HV_MaintainCost)); AllocateConstruction::update(ai, f); } void construct(AI& ai, Factory@ f) { f.obj.buildFlagship(_design); AllocateConstruction::construct(ai, f); } string toString() { if(_design is null) return "flagship (design in progress)"; return "flagship " + _design.name; } }; class BuildFlagshipSourced : BuildFlagship { Object@ buildAt; Object@ buildFrom; BuildFlagshipSourced() { } BuildFlagshipSourced(const Design@ dsg) { set(dsg); } BuildFlagshipSourced(DesignTarget@ target) { @this.target = target; } void save(Construction& construction, SaveFile& file) override { BuildFlagship::save(construction, file); file << buildAt; file << buildFrom; } void load(Construction& construction, SaveFile& file) override { BuildFlagship::load(construction, file); file >> buildAt; file >> buildFrom; } bool canBuild(AI& ai, Factory@ f) override { if(buildAt !is null && f.obj !is buildAt) return false; return BuildFlagship::canBuild(ai, f); } void construct(AI& ai, Factory@ f) override { f.obj.buildFlagship(_design, constructFrom=buildFrom); AllocateConstruction::construct(ai, f); } }; class BuildStation : AllocateConstruction, IStationConstruction { protected const Design@ _design; double baseLabor = 0.0; DesignTarget@ target; vec3d position; bool local = false; BuildStation() { } BuildStation(const Design@ dsg, const vec3d& position) { this.position = position; set(dsg); } BuildStation(DesignTarget@ target, const vec3d& position) { this.position = position; @this.target = target; } BuildStation(const Design@ dsg, bool local) { this.local = true; set(dsg); } BuildStation(DesignTarget@ target, bool local) { @this.target = target; this.local = true; } const Design@ get_design() const { return _design; } void save(Construction& construction, SaveFile& file) { file << baseLabor; file << position; file << local; if(_design !is null) { file.write1(); file << _design; } else { file.write0(); } construction.designs.saveDesign(file, target); } void load(Construction& construction, SaveFile& file) { file >> baseLabor; file >> position; file >> local; if(file.readBit()) file >> _design; @target = construction.designs.loadDesign(file); } void set(const Design& dsg) { @_design = dsg.mostUpdated(); baseLabor = _design.total(HV_LaborCost); } double laborCost(AI& ai, Object@ obj) { double labor = baseLabor; labor *= obj.owner.OrbitalLaborCostFactor; if(!local) { Region@ reg = getRegion(position); Region@ targReg = obj.region; if(reg !is null && targReg !is null) { int hops = cast<Systems>(ai.systems).tradeDistance(targReg, reg); if(hops > 0) { double penalty = 1.0 + config::ORBITAL_LABOR_COST_STEP * double(hops); baseLabor *= penalty; } } } return labor; } bool tick(AI& ai, Construction& construction, double time) override { if(target !is null) { if(target.active !is null) { set(target.active); @target = null; } } return AllocateConstruction::tick(ai, construction, time); } bool canBuild(AI& ai, Factory@ f) override { if(_design is null) return false; if(!f.obj.canBuildOrbitals) return false; Region@ targReg = f.obj.region; if(targReg is null) return false; if(!local) { Region@ reg = getRegion(position); if(reg is null) return false; if(!cast<Systems>(ai.systems).canTrade(targReg, reg)) return false; } return true; } void update(AI& ai, Factory@ f) { double c = _design.total(HV_BuildCost); c *= f.obj.owner.OrbitalBuildCostFactor; c *= f.obj.constructionCostMod; cost = ceil(c); maintenance = ceil(_design.total(HV_MaintainCost)); AllocateConstruction::update(ai, f); } void construct(AI& ai, Factory@ f) { if(local) { position = f.obj.position; vec2d offset = random2d(f.obj.radius + 10.0, f.obj.radius + 100.0); position.x += offset.x; position.z += offset.y; } f.obj.buildStation(_design, position); AllocateConstruction::construct(ai, f); } string toString() { if(_design is null) return "station (design in progress)"; return "station " + _design.name; } }; class BuildOrbital : AllocateConstruction, IOrbitalConstruction { protected const OrbitalModule@ _module; double baseLabor = 0.0; const Planet@ planet; bool local = false; vec3d position; BuildOrbital() { } BuildOrbital(const OrbitalModule@ module, const vec3d& position) { this.position = position; @this._module = module; baseLabor = module.laborCost; } BuildOrbital(const OrbitalModule@ module, bool local) { this.local = true; @this._module = module; baseLabor = module.laborCost; } BuildOrbital(const OrbitalModule@ module, const Planet@ planet) { this.local = true; @this.planet = planet; @this._module = module; baseLabor = module.laborCost; } const OrbitalModule@ get_module() const { return _module; } void save(Construction& construction, SaveFile& file) { file << baseLabor; file << position; file << local; file.writeIdentifier(SI_Orbital, _module.id); } void load(Construction& construction, SaveFile& file) { file >> baseLabor; file >> position; file >> local; @_module = getOrbitalModule(file.readIdentifier(SI_Orbital)); } double laborCost(AI& ai, Object@ obj) { double labor = baseLabor; labor *= obj.owner.OrbitalLaborCostFactor; if(!local) { Region@ reg = getRegion(position); Region@ targReg = obj.region; if(reg !is null && targReg !is null) { int hops = cast<Systems>(ai.systems).tradeDistance(targReg, reg); if(hops > 0) { double penalty = 1.0 + config::ORBITAL_LABOR_COST_STEP * double(hops); baseLabor *= penalty; } } } return labor; } bool tick(AI& ai, Construction& construction, double time) override { return AllocateConstruction::tick(ai, construction, time); } bool canBuild(AI& ai, Factory@ f) override { if(_module is null) return false; if(!f.obj.canBuildOrbitals) return false; Region@ targReg = f.obj.region; if(targReg is null) return false; if(!local) { Region@ reg = getRegion(position); if(reg is null) return false; if(!cast<Systems>(ai.systems).canTrade(targReg, reg)) return false; } return true; } void update(AI& ai, Factory@ f) { double c = _module.buildCost; c *= f.obj.owner.OrbitalBuildCostFactor; c *= f.obj.constructionCostMod; cost = ceil(c); maintenance = _module.maintenance; AllocateConstruction::update(ai, f); } void construct(AI& ai, Factory@ f) { if(local) { if (planet !is null) { position = planet.position; vec2d offset = random2d(planet.OrbitSize * 0.8, planet.OrbitSize * 0.9); position.x += offset.x; position.z += offset.y; } else { position = f.obj.position; //vec2d offset = random2d(f.obj.radius + 10.0, f.obj.radius + 100.0); if (f.plAI !is null) { vec2d offset = random2d(f.plAI.obj.OrbitSize * 0.8, f.plAI.obj.OrbitSize * 0.9); position.x += offset.x; position.z += offset.y; } } } f.obj.buildOrbital(_module.id, position); AllocateConstruction::construct(ai, f); } string toString() { return "orbital " + _module.name; } }; class RetrofitShip : AllocateConstruction { Ship@ ship; double labor; RetrofitShip() { } RetrofitShip(Ship@ ship) { @this.ship = ship; labor = ship.getRetrofitLabor(); cost = ship.getRetrofitCost(); } void save(Construction& construction, SaveFile& file) { file << ship; file << labor; } void load(Construction& construction, SaveFile& file) { file >> ship; file >> labor; } double laborCost(AI& ai, Object@ obj) { return labor; } bool canBuild(AI& ai, Factory@ f) override { if(!f.obj.canBuildShips) return false; Region@ reg = ship.region; return reg !is null && reg is f.obj.region; } void construct(AI& ai, Factory@ f) { ship.retrofitFleetAt(f.obj); AllocateConstruction::construct(ai, f); } string toString() { return "retrofit "+ship.name; } }; class BuildConstruction : AllocateConstruction { const ConstructionType@ consType; BuildConstruction() { } BuildConstruction(const ConstructionType@ consType) { @this.consType = consType; } void save(Construction& construction, SaveFile& file) { file.writeIdentifier(SI_ConstructionType, consType.id); } void load(Construction& construction, SaveFile& file) { @consType = getConstructionType(file.readIdentifier(SI_ConstructionType)); } double laborCost(AI& ai, Object@ obj) { if(obj is null) return consType.laborCost; return consType.getLaborCost(obj); } bool canBuild(AI& ai, Factory@ f) override { return consType.canBuild(f.obj, ignoreCost=true); } void update(AI& ai, Factory@ f) { cost = consType.getBuildCost(f.obj); maintenance = consType.getMaintainCost(f.obj); AllocateConstruction::update(ai, f); } void construct(AI& ai, Factory@ f) { f.obj.buildConstruction(consType.id); AllocateConstruction::construct(ai, f); } string toString() { return "construction "+consType.name; } }; class Factory { Object@ obj; PlanetAI@ plAI; Factory@ exportingTo; AllocateConstruction@ active; double laborAim = 0.0; double laborIncome = 0.0; double idleSince = 0.0; double storedLabor = 0.0; double laborMaxStorage = 0.0; double buildingPenalty = 0.0; bool needsSupportLabor = false; double waitingSupportLabor = 0.0; uint curConstructionType = 0; bool valid = true; bool significantLabor = true; uint backgrounded = 0; Asteroid@ bgAsteroid; BuildingRequest@ curBuilding; ImportData@ curImport; void save(Construction& construction, SaveFile& file) { construction.planets.saveAI(file, plAI); construction.saveConstruction(file, active); file << laborAim; file << laborIncome; file << idleSince; file << storedLabor; file << laborMaxStorage; file << buildingPenalty; construction.planets.saveBuildingRequest(file, curBuilding); construction.resources.saveImport(file, curImport); file << backgrounded; file << bgAsteroid; file << curConstructionType; file << valid; file << needsSupportLabor; file << waitingSupportLabor; construction.saveFactory(file, exportingTo); } void load(Construction& construction, SaveFile& file) { @plAI = construction.planets.loadAI(file); @active = construction.loadConstruction(file); file >> laborAim; file >> laborIncome; file >> idleSince; file >> storedLabor; file >> laborMaxStorage; file >> buildingPenalty; @curBuilding = construction.planets.loadBuildingRequest(file); @curImport = construction.resources.loadImport(file); file >> backgrounded; file >> bgAsteroid; file >> curConstructionType; file >> valid; file >> needsSupportLabor; file >> waitingSupportLabor; @exportingTo = construction.loadFactory(file); } bool get_busy() { return active !is null; } bool get_needsLabor() { if(!valid) return false; if(obj.hasOrderedSupports) return true; if(active !is null) return true; if(needsSupportLabor) return true; if(obj.constructionCount > 0 && curConstructionType != CT_Export) return true; return false; } double laborToBear(AI& ai) { return laborIncome * ai.behavior.constructionMaxTime + storedLabor; } bool viable(AI& ai, AllocateConstruction@ alloc) { double labor = obj.laborIncome; double estTime = (alloc.laborCost(ai, obj) - storedLabor) / labor; if(estTime > alloc.maxTime) return false; return true; } bool tick(AI& ai, Construction& construction, double time) { if(obj is null || !obj.valid || obj.owner !is ai.empire) { valid = false; return false; } if(ai.behavior.forbidConstruction) return true; uint curCount = obj.constructionCount; curConstructionType = 0; bool isBackground = false; if(curCount != 0) { curConstructionType = obj.constructionType; isBackground = curConstructionType == CT_Asteroid || curConstructionType == CT_Export; } if(active !is null) { if(curCount <= backgrounded || (curCount == 1 && isBackground)) { if(construction.log) ai.print("Completed construction of "+active.toString()+" "+backgrounded+" / "+curCount, obj); active.completed = true; active.completedAt = gameTime; @active = null; idleSince = gameTime; backgrounded = 0; } } else { if(curCount < backgrounded) { backgrounded = curCount; } } //Background constructibles we don't need to do right now if(curCount > 1 && curConstructionType == CT_Asteroid && bgAsteroid !is null) { obj.moveConstruction(obj.constructionID[0], -1); backgrounded += 1; } if(curCount > 1 && curConstructionType == CT_Export && exportingTo !is null) { obj.cancelConstruction(obj.constructionID[0]); @exportingTo = null; } if(bgAsteroid !is null && (bgAsteroid.owner.valid || curCount == 0)) { if(bgAsteroid.owner is ai.empire) construction.planets.register(bgAsteroid); @bgAsteroid = null; } //Build warehouse(s) if we've been idle laborIncome = obj.laborIncome; storedLabor = obj.currentLaborStored; laborMaxStorage = obj.laborStorageCapacity; significantLabor = laborIncome >= 0.4 * construction.bestLabor && obj.baseLaborIncome > 4.0/60.0; if(storedLabor < laborMaxStorage) idleSince = gameTime; if(active is null && curBuilding is null && plAI !is null && gameTime - idleSince > ai.behavior.laborStoreIdleTimer && ai.behavior.buildLaborStorage && (laborMaxStorage+50) < ai.behavior.laborStoreMaxFillTime * max(obj.baseLaborIncome, laborAim) && significantLabor) { auto@ bld = ai.defs.LaborStorage; if(bld !is null && buildingPenalty < gameTime) { if(construction.log) ai.print("Build building "+bld.name+" for labor storage", obj); @curBuilding = construction.planets.requestBuilding(plAI, bld); } } //Remove waits on completed labor gains if(curBuilding !is null) { if(curBuilding.canceled || (curBuilding.built && curBuilding.getProgress() >= 1.0)) { if(construction.log) ai.print("Building construction for labor finished", obj); if(curBuilding.canceled) buildingPenalty = gameTime + 60.0; @curBuilding = null; } } if(curImport !is null) { if(curImport.beingMet) { if(construction.log) ai.print("Resource import for labor finished", obj); @curImport = null; } } //See if we need a new labor gain if(laborIncome < laborAim) { if(curImport is null && plAI !is null && obj.isPressureSaturated(TR_Labor) && obj.pressureCap < uint(obj.totalPressure) && gameTime > 6.0 * 60.0 && ai.behavior.buildFactoryForLabor) { ResourceSpec spec; spec.type = RST_Pressure_Level0; spec.pressureType = TR_Labor; if(construction.log) ai.print("Queue resource import for labor", obj); @curImport = construction.resources.requestResource(obj, spec, prioritize=true); } if(curBuilding is null && plAI !is null && ai.behavior.buildLaborStorage) { auto@ bld = ai.defs.Factory; if(bld !is null && buildingPenalty < gameTime) { if(construction.log) ai.print("Build building "+bld.name+" for labor", obj); @curBuilding = construction.planets.requestBuilding(plAI, bld); } } } //See if we should spend our labor on a labor export somewhere else if(exportingTo !is null && curConstructionType == CT_Export) { if(!exportingTo.valid || (!exportingTo.needsLabor && exportingTo !is construction.primaryFactory)) { obj.cancelConstruction(obj.constructionID[0]); @exportingTo = null; } } if(ai.behavior.distributeLaborExports) { if(curCount == 0 && obj.canExportLabor) { uint offset = randomi(0, construction.factories.length-1); for(uint i = 0, cnt = construction.factories.length; i < cnt; ++i) { auto@ other = construction.factories[(i+offset) % cnt]; if(other is this) continue; if(!other.obj.canImportLabor) continue; //Check if this is currently busy if(other !is construction.primaryFactory) { if(!other.needsLabor) continue; } obj.exportLaborTo(other.obj); @exportingTo = other; } } } //See if we should spend our labor trying to build an asteroid if(ai.behavior.backgroundBuildAsteroids) { if((curCount == 0 || (curConstructionType == CT_Export && curCount == 1)) && storedLabor >= laborMaxStorage * 0.5 && obj.canBuildAsteroids) { Asteroid@ roid = construction.getBackgroundAsteroid(this); if(roid !is null) { uint resCount = roid.getAvailableCount(); if(resCount != 0) { uint bestIndex = 0; int bestId = -1; double bestWeight = 0.0; if(ai.behavior.chooseAsteroidResource) { for(uint i = 0; i < resCount; ++i) { int resourceId = roid.getAvailable(i); double w = asteroidResourceValue(getResource(resourceId)); if(w > bestWeight) { bestWeight = w; bestId = resourceId; bestIndex = i; } } } else { bestIndex = randomi(0, resCount-1); bestId = roid.getAvailable(bestIndex); } double laborCost = roid.getAvailableCost(bestIndex); Region@ fromReg = obj.region; Region@ toReg = roid.region; if(fromReg !is null && toReg !is null) laborCost *= 1.0 + config::ASTEROID_COST_STEP * double(construction.systems.hopDistance(fromReg, toReg)); double timeTaken = laborIncome / laborCost; if(timeTaken < ai.behavior.constructionMaxTime || storedLabor >= laborMaxStorage * 0.95) { @bgAsteroid = roid; obj.buildAsteroid(roid, bestId); if(construction.log) ai.print("Use background labor to mine "+roid.name+" in "+roid.region.name, obj); } } } } } return true; } void aimForLabor(double labor) { if(labor > laborAim) laborAim = labor; } }; class Construction : AIComponent { array<Factory@> factories; Factory@ primaryFactory; double noFactoryTimer = 0.0; int nextAllocId = 0; array<AllocateConstruction@> allocations; double totalLabor = 0.0; double bestLabor = 0.0; BuildOrbital@ buildConsolidate; Budget@ budget; Planets@ planets; Orbitals@ orbitals; Resources@ resources; Designs@ designs; Systems@ systems; void create() { @budget = cast<Budget>(ai.budget); @planets = cast<Planets>(ai.planets); @resources = cast<Resources>(ai.resources); @designs = cast<Designs>(ai.designs); @systems = cast<Systems>(ai.systems); @orbitals = cast<Orbitals>(ai.orbitals); } void save(SaveFile& file) { file << nextAllocId; uint cnt = allocations.length; file << cnt; for(uint i = 0; i < cnt; ++i) saveConstruction(file, allocations[i]); cnt = factories.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveFactory(file, factories[i]); factories[i].save(this, file); } saveFactory(file, primaryFactory); file << noFactoryTimer; saveConstruction(file, buildConsolidate); } void load(SaveFile& file) { file >> nextAllocId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ alloc = loadConstruction(file); if(alloc !is null) allocations.insertLast(alloc); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Factory@ f = loadFactory(file); if(f !is null) f.load(this, file); else Factory().load(this, file); } @primaryFactory = loadFactory(file); file >> noFactoryTimer; @buildConsolidate = cast<BuildOrbital>(loadConstruction(file)); } void saveFactory(SaveFile& file, Factory@ f) { if(f !is null) { file.write1(); file << f.obj; } else { file.write0(); } } Factory@ loadFactory(SaveFile& file) { if(!file.readBit()) return null; Object@ obj; file >> obj; if(obj is null) return null; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].obj is obj) return factories[i]; } Factory f; @f.obj = obj; factories.insertLast(f); return f; } array<AllocateConstruction@> savedConstructions; array<AllocateConstruction@> loadedConstructions; void postSave(AI& ai) { savedConstructions.length = 0; } void postLoad(AI& ai) { loadedConstructions.length = 0; } void saveConstruction(SaveFile& file, AllocateConstruction@ alloc) { if(alloc is null) { file.write0(); return; } file.write1(); file << alloc.id; if(alloc.id == -1) { storeConstruction(file, alloc); } else { bool found = false; for(uint i = 0, cnt = savedConstructions.length; i < cnt; ++i) { if(savedConstructions[i] is alloc) { found = true; break; } } if(!found) { storeConstruction(file, alloc); savedConstructions.insertLast(alloc); } } } AllocateConstruction@ loadConstruction(SaveFile& file) { if(!file.readBit()) return null; int id = 0; file >> id; if(id == -1) { AllocateConstruction@ alloc = createConstruction(file); alloc.id = id; return alloc; } else { for(uint i = 0, cnt = loadedConstructions.length; i < cnt; ++i) { if(loadedConstructions[i].id == id) return loadedConstructions[i]; } AllocateConstruction@ alloc = createConstruction(file); alloc.id = id; loadedConstructions.insertLast(alloc); return alloc; } } void storeConstruction(SaveFile& file, AllocateConstruction@ alloc) { auto@ cls = getClass(alloc); auto@ mod = cls.module; file << mod.name; file << cls.name; alloc._save(this, file); } AllocateConstruction@ createConstruction(SaveFile& file) { string modName; string clsName; file >> modName; file >> clsName; auto@ mod = getScriptModule(modName); if(mod is null) { error("ERROR: AI Load could not find module for alloc "+modName+"::"+clsName); return null; } auto@ cls = mod.getClass(clsName); if(cls is null) { error("ERROR: AI Load could not find class for alloc "+modName+"::"+clsName); return null; } auto@ alloc = cast<AllocateConstruction>(cls.create()); if(alloc is null) { error("ERROR: AI Load could not create class instance for alloc "+modName+"::"+clsName); return null; } alloc._load(this, file); return alloc; } void start() { Object@ hw = ai.empire.Homeworld; if(hw !is null) { Factory f; @f.obj = hw; @f.plAI = planets.getAI(cast<Planet>(hw)); factories.insertLast(f); } } Factory@ get(Object@ obj) { for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].obj is obj) return factories[i]; } return null; } Factory@ registerFactory(Object@ obj) { for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].obj is obj) return factories[i]; } Factory f; @f.obj = obj; factories.insertLast(f); return f; } Factory@ getFactory(Region@ region) { Factory@ best; double bestLabor = 0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].obj.region !is region) continue; double l = factories[i].obj.laborIncome; if(l > bestLabor) { bestLabor = l; @best = factories[i]; } } return best; } BuildConstruction@ buildConstruction(const ConstructionType@ type, double priority = 1.0, bool force = false, uint moneyType = BT_Development) { //Potentially build a flagship BuildConstruction f(type); f.moneyType = moneyType; f.priority = priority; build(f, force=force); return f; } BuildFlagship@ buildFlagship(const Design@ dsg, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildFlagship f(dsg); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildFlagship@ buildFlagship(DesignTarget@ target, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildFlagship f(target); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildStation@ buildStation(const Design@ dsg, const vec3d& position, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildStation f(dsg, position); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildStation@ buildStation(DesignTarget@ target, const vec3d& position, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildStation f(target, position); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildOrbital@ buildOrbital(const OrbitalModule@ module, const vec3d& position, double priority = 1.0, bool force = false, uint moneyType = BT_Infrastructure) { //Potentially build a flagship BuildOrbital f(module, position); f.moneyType = moneyType; f.priority = priority; build(f, force=force); return f; } BuildStation@ buildLocalStation(const Design@ dsg, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildStation f(dsg, local=true); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildStation@ buildLocalStation(DesignTarget@ target, double priority = 1.0, bool force = false) { //Potentially build a flagship BuildStation f(target, local=true); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } BuildOrbital@ buildLocalOrbital(const OrbitalModule@ module, double priority = 1.0, bool force = false, uint moneyType = BT_Infrastructure) { //Potentially build a flagship BuildOrbital f(module, local=true); f.moneyType = moneyType; f.priority = priority; build(f, force=force); return f; } BuildOrbital@ buildLocalOrbital(const OrbitalModule@ module, Planet@ planet, double priority = 1.0, bool force = false, uint moneyType = BT_Infrastructure) { //Potentially build a flagship BuildOrbital f(module, planet); f.moneyType = moneyType; f.priority = priority; build(f, force=force); return f; } RetrofitShip@ retrofit(Ship@ ship, double priority = 1.0, bool force = false) { //Potentially build a flagship RetrofitShip f(ship); f.moneyType = BT_Military; f.priority = priority; build(f, force=force); return f; } AllocateConstruction@ build(AllocateConstruction@ alloc, bool force = false) { //Add a construction into the potential constructions queue if(!force) alloc.maxTime = ai.behavior.constructionMaxTime; alloc.id = nextAllocId++; allocations.insertLast(alloc); if(log) ai.print("Queue construction: "+alloc.toString()); return alloc; } AllocateConstruction@ buildNow(AllocateConstruction@ alloc, Factory@ f) { if(f.busy) return null; if(alloc.alloc !is null) budget.applyNow(alloc.alloc); start(f, alloc); allocations.remove(alloc); return alloc; } void cancel(AllocateConstruction@ alloc) { if(alloc.started || (alloc.alloc !is null && alloc.alloc.allocated)) return; //TODO allocations.remove(alloc); for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].active is alloc) @factories[i].active = null; } if(alloc.alloc !is null) budget.remove(alloc.alloc); } uint factInd = 0; void tick(double time) { //Manage factories if(factories.length != 0) { factInd = (factInd+1) % factories.length; auto@ f = factories[factInd]; if(!f.tick(ai, this, time)) factories.removeAt(factInd); } } void start(Factory@ f, AllocateConstruction@ c) { if(ai.behavior.forbidConstruction) { cancel(c); return; } //Actually construct something we've allocated budget for @f.active = c; @c.tryFactory = null; c.construct(ai, f); if(log) ai.print("Construct: "+c.toString(), f.obj); for(uint i = 0, cnt = allocations.length; i < cnt; ++i) { if(allocations[i].tryFactory is f) @allocations[i].tryFactory = null; } } uint plCheck = 0; uint orbCheck = 0; double consTimer = 0.0; void focusTick(double time) { //Progress the allocations for(uint n = 0, ncnt = allocations.length; n < ncnt; ++n) { if(!allocations[n].tick(ai, this, time)) { allocations.removeAt(n); --n; --ncnt; } } if(ai.behavior.forbidConstruction) return; //See if anything we can potentially construct is constructible totalLabor = 0.0; bestLabor = 0.0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { auto@ f = factories[i]; totalLabor += f.laborIncome; if(f.laborIncome > bestLabor) bestLabor = f.laborIncome; } for(uint n = 0, ncnt = allocations.length; n < ncnt; ++n) { auto@ alloc = allocations[n]; if(alloc.tryFactory !is null) continue; Factory@ bestFact; double bestCur = 0.0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { auto@ f = factories[i]; if(f.busy) continue; if(!alloc.canBuild(ai, f)) continue; if(!f.viable(ai, alloc)) continue; double w = f.laborIncome; if(f is primaryFactory) w *= 1.5; if(f.exportingTo !is null) w /= 0.75; if(w > bestCur) { bestCur = w; @bestFact = f; } } if(bestFact !is null) { @alloc.tryFactory = bestFact; alloc.update(ai, bestFact); } } //Classify our primary factory if(primaryFactory is null) { //Find our best factory Factory@ best; double bestWeight = 0.0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { auto@ f = factories[i]; double w = f.laborIncome; w += 0.1 * f.laborAim; if(f.obj.isPlanet) w *= 100.0; if(f.obj.isShip) w *= 0.1; if(w > bestWeight) { bestWeight = w; @best = f; } } if(best !is null) { @primaryFactory = best; } else { noFactoryTimer += time; if(noFactoryTimer > 3.0 * 60.0 && ai.defs.Factory !is null) { //Just pick our highest level planet and hope for the best PlanetAI@ best; double bestWeight = 0.0; for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) { auto@ plAI = planets.planets[i]; double w = plAI.obj.level; w += 0.5 * plAI.obj.resourceLevel; if(w > bestWeight) { bestWeight = w; @best = plAI; } } if(best !is null) { Factory f; @f.obj = best.obj; @f.plAI = best; factories.insertLast(f); @primaryFactory = f; } noFactoryTimer = 0.0; } } } else { noFactoryTimer = 0.0; } //Find new factories if(planets.planets.length != 0) { plCheck = (plCheck+1) % planets.planets.length; PlanetAI@ plAI = planets.planets[plCheck]; if(plAI.obj.laborIncome > 0 && plAI.obj.canBuildShips) { if(get(plAI.obj) is null) { Factory f; @f.obj = plAI.obj; @f.plAI = plAI; factories.insertLast(f); } } } if(orbitals.orbitals.length != 0) { orbCheck = (orbCheck+1) % orbitals.orbitals.length; OrbitalAI@ orbAI = orbitals.orbitals[orbCheck]; if(orbAI.obj.hasConstruction && orbAI.obj.laborIncome > 0 && !cast<Orbital>(orbAI.obj).hasMaster()) { if(get(orbAI.obj) is null) { Factory f; @f.obj = orbAI.obj; factories.insertLast(f); } } } //See if we should switch our primary factory if(primaryFactory !is null) { if(!primaryFactory.valid) { @primaryFactory = null; } else { Factory@ best; double bestLabor = 0.0; double primaryLabor = primaryFactory.laborIncome; bool canImport = primaryFactory.obj.canImportLabor; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { auto@ f = factories[i]; double checkLabor = f.laborIncome; if(f.obj.isShip) checkLabor *= 0.1; if(f.exportingTo !is primaryFactory && canImport) primaryLabor += checkLabor * 0.75; if(checkLabor > bestLabor) { bestLabor = checkLabor; @best = f; } } if(best !is null && bestLabor > 1.5 * primaryLabor) @primaryFactory = best; } } //See if we should consolidate at a shipyard if(buildConsolidate !is null && buildConsolidate.completed) { @buildConsolidate = null; consTimer = gameTime + 60.0; } else if(ai.behavior.consolidateLaborExports && primaryFactory !is null && ai.defs.Shipyard !is null && buildConsolidate is null && !primaryFactory.obj.canImportLabor && consTimer < gameTime) { double totalLabor = 0.0, bestLabor = 0.0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { double inc = factories[i].obj.baseLaborIncome; if(factories[i].obj.canExportLabor) totalLabor += inc; if(factories[i].laborIncome > bestLabor) bestLabor = factories[i].laborIncome; } if(bestLabor < totalLabor * 0.6) { Factory@ bestConsolidate; double bestWeight = 0.0; for(uint i = 0, cnt = factories.length; i < cnt; ++i) { auto@ f = factories[i]; if(!f.obj.canImportLabor) continue; double w = f.obj.baseLaborIncome; w /= f.obj.position.distanceTo(primaryFactory.obj.position); if(w > bestWeight) { bestWeight = w; @bestConsolidate = f; } } if(bestConsolidate !is null) { if(log) ai.print("Set shipyard for consolidate.", bestConsolidate.obj.region); @primaryFactory = bestConsolidate; } else { Region@ reg = primaryFactory.obj.region; if(reg !is null) { vec3d pos = reg.position; vec2d offset = random2d(reg.radius * 0.4, reg.radius * 0.8); pos.x += offset.x; pos.z += offset.y; if(log) ai.print("Build shipyard for consolidate.", reg); @buildConsolidate = buildOrbital(ai.defs.Shipyard, pos); } } } } } bool isGettingAsteroid(Asteroid@ asteroid) { for(uint i = 0, cnt = factories.length; i < cnt; ++i) { if(factories[i].bgAsteroid is asteroid) return true; } return false; } Asteroid@ getBackgroundAsteroid(Factory& f) { double closest = INFINITY; Asteroid@ best; Region@ reg = f.obj.region; if(reg is null) return null; uint cnt = systems.owned.length; uint offset = randomi(0, cnt-1); for(uint i = 0, check = min(3, cnt); i < check; ++i) { auto@ sys = systems.owned[(i+offset)%cnt]; double dist = sys.obj.position.distanceToSQ(f.obj.position); if(dist > closest) continue; if(!sys.obj.sharesTerritory(ai.empire, reg)) continue; for(uint n = 0, ncnt = sys.asteroids.length; n < ncnt; ++n) { Asteroid@ roid = sys.asteroids[n]; if(roid.owner.valid) continue; if(roid.getAvailableCount() == 0) continue; if(isGettingAsteroid(roid)) continue; closest = dist; @best = roid; break; } } cnt = systems.outsideBorder.length; offset = randomi(0, cnt-1); for(uint i = 0, check = min(3, cnt); i < check; ++i) { auto@ sys = systems.outsideBorder[(i+offset)%cnt]; double dist = sys.obj.position.distanceToSQ(f.obj.position); if(dist > closest) continue; if(!sys.obj.sharesTerritory(ai.empire, reg)) continue; for(uint n = 0, ncnt = sys.asteroids.length; n < ncnt; ++n) { Asteroid@ roid = sys.asteroids[n]; if(roid.owner.valid) continue; if(roid.getAvailableCount() == 0) continue; if(isGettingAsteroid(roid)) continue; closest = dist; @best = roid; break; } } return best; } }; double asteroidResourceValue(const ResourceType@ type) { if(type is null) return 0.0; double w = 1.0; w += type.level * 10.0; w += type.totalPressure; if(type.cls !is null) w += 5.0; return w; } AIComponent@ createConstruction() { return Construction(); } |
Added scripts/server/empire_ai/weasel/Creeping.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
// Creeping // -------- // Uses fleets that aren't currently doing anything to eliminate creeps. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import empire_ai.weasel.Movement; import empire_ai.weasel.searches; import saving; from empire import Creeps; class CreepingMission : Mission { Pickup@ pickup; Object@ protector; MoveOrder@ move; void save(Fleets& fleets, SaveFile& file) { file << pickup; file << protector; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) { file >> pickup; file >> protector; @move = fleets.movement.loadMoveOrder(file); } void start(AI& ai, FleetAI& fleet) override { vec3d position = pickup.position; double dist = fleet.radius; if(protector !is null && protector.valid) dist = fleet.obj.getEngagementRange(); position += (fleet.obj.position - pickup.position).normalized(dist); @move = cast<Movement>(ai.movement).move(fleet.obj, position); } void tick(AI& ai, FleetAI& fleet, double time) { if(move !is null) { if(move.completed) { if(protector !is null && protector.valid) { if(!protector.isVisibleTo(ai.empire)) { //Yo nebulas are scary yo fleet.obj.addMoveOrder(protector.position); fleet.obj.addAttackOrder(protector, append=true); } else { fleet.obj.addAttackOrder(protector); } } @move = null; } else if(move.failed) { canceled = true; return; } else return; } if(protector is null || !protector.valid) { if(!fleet.obj.hasOrders) { if(pickup is null || !pickup.valid) { if(cast<Creeping>(ai.creeping).log) ai.print("Finished clearing creep camp", fleet.obj); completed = true; } else { fleet.obj.addPickupOrder(pickup); @protector = null; } } } else { if((fleet.filled < 0.3 || fleet.supplies < 0.3 || fleet.flagshipHealth < 0.4) && protector.getFleetStrength() * ai.behavior.remnantOverkillFactor > fleet.strength) { //Holy shit what's going on? ABORT! ABORT! if(cast<Creeping>(ai.creeping).logCritical) ai.print("ABORTED CREEPING: About to lose fight", fleet.obj); canceled = true; cast<Fleets>(ai.fleets).returnToBase(fleet, MP_Critical); } } } }; class ClearMission : Mission { Region@ region; Object@ eliminate; MoveOrder@ move; void save(Fleets& fleets, SaveFile& file) { file << region; file << eliminate; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) { file >> region; file >> eliminate; @move = fleets.movement.loadMoveOrder(file); } void start(AI& ai, FleetAI& fleet) override { @move = cast<Movement>(ai.movement).move(fleet.obj, region); } void tick(AI& ai, FleetAI& fleet, double time) { if(move !is null) { if(move.completed) { @move = null; } else if(move.failed) { canceled = true; return; } else return; } if(ai.behavior.forbidCreeping) return; if(eliminate is null) { @eliminate = cast<Creeping>(ai.creeping).findRemnants(region); if(eliminate is null) { completed = true; return; } } if(eliminate !is null) { if(!eliminate.valid) { @eliminate = null; } else { if(!fleet.obj.hasOrders) fleet.obj.addAttackOrder(eliminate); if((fleet.filled < 0.3 || fleet.supplies < 0.3 || fleet.flagshipHealth < 0.4) && eliminate.getFleetStrength() * ai.behavior.remnantOverkillFactor > fleet.strength) { //Holy shit what's going on? ABORT! ABORT! if(cast<Creeping>(ai.creeping).logCritical) ai.print("ABORTED CREEPING: About to lose fight", fleet.obj); canceled = true; cast<Fleets>(ai.fleets).returnToBase(fleet, MP_Critical); } } } } }; final class CreepPenalty : Savable { Object@ obj; double until; void save(SaveFile& file) { file << obj; file << until; } void load(SaveFile& file) { file >> obj; file >> until; } }; final class ClearSystem { SystemAI@ sys; array<Ship@> remnants; void save(Creeping& creeping, SaveFile& file) { creeping.systems.saveAI(file, sys); uint cnt = remnants.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << remnants[i]; } void load(Creeping& creeping, SaveFile& file) { @sys = creeping.systems.loadAI(file); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { Ship@ remn; file >> remn; if(remn !is null) remnants.insertLast(remn); } } void record() { auto@ objs = findEnemies(sys.obj, null, Creeps.mask); for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Ship@ ship = cast<Ship>(objs[i]); if(ship !is null) remnants.insertLast(ship); } } double getStrength() { double str = 0.0; for(uint i = 0, cnt = remnants.length; i < cnt; ++i) { if(remnants[i].valid) str += sqrt(remnants[i].getFleetStrength()); } return str * str; } }; class Creeping : AIComponent { Systems@ systems; Fleets@ fleets; array<SystemAI@> requested; array<CreepPenalty@> penalties; array<CreepingMission@> active; array<ClearSystem@> quarantined; void create() { @systems = cast<Systems>(ai.systems); @fleets = cast<Fleets>(ai.fleets); } void save(SaveFile& file) { uint cnt = requested.length; file << cnt; for(uint i = 0; i < cnt; ++i) systems.saveAI(file, requested[i]); cnt = penalties.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << penalties[i]; cnt = active.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, active[i]); cnt = quarantined.length; file << cnt; for(uint i = 0; i < cnt; ++i) quarantined[i].save(this, file); } void load(SaveFile& file) { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ sys = systems.loadAI(file); if(sys !is null) requested.insertLast(sys); } file >> cnt; for(uint i = 0; i < cnt; ++i) { CreepPenalty pen; file >> pen; if(pen.obj !is null) penalties.insertLast(pen); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<CreepingMission>(fleets.loadMission(file)); if(miss !is null) active.insertLast(miss); } if(file >= SV_0151) { file >> cnt; for(uint i = 0; i < cnt; ++i) { ClearSystem qsys; qsys.load(this, file); quarantined.insertLast(qsys); } } } void requestClear(SystemAI@ system) { if(system is null) return; if(log) ai.print("Requested creep camp clear", system.obj); if(requested.find(system) == -1) requested.insertLast(system); } CreepingMission@ creepWithFleet(FleetAI@ fleet, Pickup@ pickup, Object@ protector = null) { if(protector is null) @protector = pickup.getProtector(); if(log) ai.print("Clearing creep camp in "+pickup.region.name, fleet.obj); CreepingMission mission; @mission.pickup = pickup; @mission.protector = protector; fleets.performMission(fleet, mission); active.insertLast(mission); return mission; } Pickup@ best; Object@ bestProtector; vec3d ourPosition; double bestWeight; double ourStrength; void check(SystemAI@ sys, double weight = 1.0) { for(uint n = 0, ncnt = sys.pickups.length; n < ncnt; ++n) { Pickup@ pickup = sys.pickups[n]; Object@ protector = sys.pickupProtectors[n]; if(!pickup.valid) continue; double protStrength; if(protector !is null && protector.valid) { protStrength = protector.getFleetStrength(); if(protStrength * ai.behavior.remnantOverkillFactor > ourStrength) continue; } else protStrength = 1.0; if(isCreeping(pickup)) continue; double w = weight; w /= protStrength / 1000.0; w /= pickup.position.distanceTo(ourPosition); if(w > bestWeight) { bestWeight = w; @best = pickup; @bestProtector = protector; } } } void penalize(Object@ obj, double time) { for(uint i = 0, cnt = penalties.length; i < cnt; ++i) { if(penalties[i].obj is obj) { penalties[i].until = max(penalties[i].until, gameTime + time); return; } } CreepPenalty p; @p.obj = obj; p.until = gameTime + time; penalties.insertLast(p); } bool isPenalized(Object@ obj) { for(uint i = 0, cnt = penalties.length; i < cnt; ++i) { if(penalties[i].obj is obj) return true; } return false; } bool isCreeping(Pickup@ pickup) { for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].pickup is pickup) return true; } return false; } CreepingMission@ creepWithFleet(FleetAI@ fleet) { @best = null; @bestProtector = null; bestWeight = 0.0; ourStrength = fleet.strength; ourPosition = fleet.obj.position; //Check requested systems first for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ sys = requested[i]; if(sys.pickups.length == 0) { requested.removeAt(i); --i; --cnt; continue; } if(haveQuarantinedSystem(sys)) continue; check(sys); } if(best !is null) return creepWithFleet(fleet, best, bestProtector); if(!ai.behavior.remnantAllowArbitraryClear) return null; if(log) ai.print("Attempted to find creep camp to clear", fleet.obj); //Check systems in our territory for(uint i = 0, cnt = systems.owned.length; i < cnt; ++i) { SystemAI@ sys = systems.owned[i]; if(sys.pickups.length != 0) check(sys); } if(best !is null) return creepWithFleet(fleet, best, bestProtector); //Check systems just outside our border for(uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) { SystemAI@ sys = systems.outsideBorder[i]; if(sys.seenPresent & ai.otherMask != 0) continue; if(haveQuarantinedSystem(sys)) continue; if(sys.pickups.length != 0) check(sys, 1.0 / double(1.0 + sys.hopDistance)); } if(best !is null) return creepWithFleet(fleet, best, bestProtector); penalize(fleet.obj, 90.0); return null; } Object@ findRemnants(Region@ reg) { for(uint i = 0, cnt = quarantined.length; i < cnt; ++i) { auto@ qsys = quarantined[i]; if(qsys.sys.obj !is reg) continue; for(uint n = 0, ncnt = qsys.remnants.length; n < ncnt; ++n) { auto@ remn = qsys.remnants[n]; if(remn is null || !remn.valid) continue; return remn; } } return null; } ClearMission@ sendToClear(FleetAI@ fleet, ClearSystem@ system) { ClearMission miss; @miss.region = system.sys.obj; fleets.performMission(fleet, miss); if(log) ai.print("Clear remnant defenders in "+miss.region.name, fleet.obj); return miss; } bool isQuarantined(SystemAI@ sys) { if(sys.planets.length == 0) return false; for(uint i = 0, cnt = sys.planets.length; i < cnt; ++i) { if(!sys.planets[i].quarantined) return false; } return true; } bool isQuarantined(Region@ region) { for(uint i = 0, cnt = quarantined.length; i < cnt; ++i) { if(quarantined[i].sys.obj is region) return true; } return false; } bool haveQuarantinedSystem(SystemAI@ sys) { for(uint i = 0, cnt = quarantined.length; i < cnt; ++i) { if(quarantined[i].sys is sys) return true; } return false; } void recordQuarantinedSystem(SystemAI@ sys) { ClearSystem qsys; @qsys.sys = sys; quarantined.insertLast(qsys); qsys.record(); } uint ownedCheck = 0; uint outsideCheck = 0; void focusTick(double time) { //Manage creeping check penalties for(uint i = 0, cnt = penalties.length; i < cnt; ++i) { if(penalties[i].until < gameTime) { penalties.removeAt(i); --i; --cnt; } } //Manage current creeping missions for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].completed || active[i].canceled) { active.removeAt(i); --i; --cnt; } } //Find new systems that are quarantined if(systems.owned.length != 0) { ownedCheck = (ownedCheck+1) % systems.owned.length; auto@ sys = systems.owned[ownedCheck]; if(sys.explored && isQuarantined(sys)) { if(!haveQuarantinedSystem(sys)) recordQuarantinedSystem(sys); } } if(systems.outsideBorder.length != 0) { outsideCheck = (outsideCheck+1) % systems.outsideBorder.length; auto@ sys = systems.outsideBorder[outsideCheck]; if(sys.explored && isQuarantined(sys)) { if(!haveQuarantinedSystem(sys)) recordQuarantinedSystem(sys); } } //Update existing quarantined systems list for(uint i = 0, cnt = quarantined.length; i < cnt; ++i) { auto@ qsys = quarantined[i]; if(!isQuarantined(qsys.sys)) { quarantined.removeAt(i); --i; --cnt; continue; } for(uint n = 0, ncnt = qsys.remnants.length; n < ncnt; ++n) { auto@ remn = qsys.remnants[n]; if(remn is null || !remn.valid || remn.region !is qsys.sys.obj) { qsys.remnants.removeAt(n); --n; --ncnt; } } } //See if we should try to clear a quarantined system bool waitingForGather = false; if(ai.behavior.remnantAllowArbitraryClear) { ClearSystem@ best; double bestStr = INFINITY; for(uint i = 0, cnt = quarantined.length; i < cnt; ++i) { double str = quarantined[i].getStrength(); if(quarantined[i].remnants.length == 0) continue; if(str < bestStr) { bestStr = str; @best = quarantined[i]; } } if(best !is null) { double needStr = bestStr * ai.behavior.remnantOverkillFactor; if(fleets.getTotalStrength(FC_Combat) > needStr) { waitingForGather = true; if(fleets.getTotalStrength(FC_Combat, readyOnly=true) > needStr) { //Order sufficient fleets to go clear this system double takeStr = sqrt(needStr); double haveStr = 0.0; uint offset = randomi(0, fleets.fleets.length-1); for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { FleetAI@ fleet = fleets.fleets[(i+offset)%cnt]; if(fleet.fleetClass != FC_Combat) continue; if(!fleet.readyForAction) continue; haveStr += sqrt(fleet.strength); sendToClear(fleet, best); if(haveStr > takeStr) break; } } } } } //Find new fleets to creep with if(!waitingForGather) { uint offset = randomi(0, fleets.fleets.length-1); for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { FleetAI@ fleet = fleets.fleets[(i+offset)%cnt]; if(fleet.fleetClass != FC_Combat) continue; if(!fleet.readyForAction) continue; if(isPenalized(fleet.obj)) continue; creepWithFleet(fleet); break; } } } }; AIComponent@ createCreeping() { return Creeping(); } |
Added scripts/server/empire_ai/weasel/Designs.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
import empire_ai.weasel.WeaselAI; import util.design_export; import util.random_designs; interface RaceDesigns { bool preCompose(DesignTarget@ target); bool postCompose(DesignTarget@ target); bool design(DesignTarget@ target, int size, const Design@& output); }; enum DesignPurpose { DP_Scout, DP_Combat, DP_Defense, DP_Support, DP_Gate, DP_Slipstream, DP_Mothership, DP_Miner, DP_COUNT, DP_Unknown, }; tidy final class DesignTarget { int id = -1; const Design@ active; string customName; array<const Design@> potential; array<double> scores; uint purpose; double targetBuildCost = 0; double targetMaintenance = 0; double targetLaborCost = 0; double dps = 0.0; double hp = 0.0; double supplyDrain = 0.0; double targetSize = 0; bool findSize = false; Designer@ designer; DesignTarget() { } DesignTarget(uint type, double targetSize) { this.purpose = type; this.targetSize = targetSize; } uint get_designType() { switch(purpose) { case DP_Scout: return DT_Flagship; case DP_Combat: return DT_Flagship; case DP_Defense: return DT_Station; case DP_Support: return DT_Support; case DP_Gate: return DT_Station; case DP_Slipstream: return DT_Flagship; case DP_Mothership: return DT_Flagship; } return DT_Flagship; } void save(Designs& designs, SaveFile& file) { if(active !is null) { file.write1(); file << active; file << dps; file << supplyDrain; file << hp; } else { file.write0(); } file << purpose; file << targetBuildCost; file << targetMaintenance; file << targetLaborCost; file << targetSize; file << findSize; file << customName; } void load(Designs& designs, SaveFile& file) { if(file.readBit()) { file >> active; file >> dps; file >> supplyDrain; file >> hp; } file >> purpose; file >> targetBuildCost; file >> targetMaintenance; file >> targetLaborCost; file >> targetSize; file >> findSize; file >> customName; } void prepare(AI& ai) { @designer = Designer(designType, targetSize, ai.empire, compose=false); designer.randomHull = true; switch(purpose) { case DP_Scout: designer.composeScout(); break; case DP_Combat: designer.composeFlagship(); break; case DP_Defense: designer.composeStation(); break; case DP_Support: designer.composeSupport(); break; case DP_Gate: designer.composeGate(); break; case DP_Slipstream: designer.composeSlipstream(); break; case DP_Mothership: designer.composeMothership(); break; } } double weight(double value, double goal) { if(value < goal) return sqr(value / goal); else if(value > goal * 1.5) return goal / value; return 1.0; } double costWeight(double value, double goal) { if(findSize) { if(value < goal) return 1.0; else return 0.000001; } else { if(value < goal) return goal / value; else return pow(0.2, ((value / goal) - 1.0) * 10.0); } } double evaluate(AI& ai, const Design& dsg) { double w = 1.0; //Try to stick as close to our target as we can if(targetBuildCost != 0) w *= costWeight(dsg.total(HV_BuildCost), targetBuildCost); if(targetLaborCost != 0) w *= costWeight(dsg.total(HV_LaborCost), targetLaborCost); if(targetMaintenance != 0) w *= costWeight(dsg.total(HV_MaintainCost), targetMaintenance); double predictHP = 0.0; double predictDPS = 0.0; double predictDrain = 0.0; //Value support capacity where appropriate if(purpose == DP_Combat) { double supCap = dsg.total(SV_SupportCapacity); double avgHP = 0, avgDPS = 0, avgDrain = 0.0; cast<Designs>(ai.designs).getSupportAverages(avgHP, avgDPS, avgDrain); predictHP += supCap * avgHP; predictDPS += supCap * avgDPS; predictDrain += supCap * avgDrain; } //Value combat strength where appropriate if(purpose != DP_Scout && purpose != DP_Slipstream && purpose != DP_Mothership) { predictDPS += dsg.total(SV_DPS); predictHP += dsg.totalHP + dsg.total(SV_ShieldCapacity); predictDrain += dsg.total(SV_SupplyDrain); if(purpose != DP_Support) { w *= (predictHP * predictDPS) * 0.001; double supplyStores = dsg.total(SV_SupplyCapacity); double actionTime = supplyStores / predictDrain; w *= weight(actionTime, ai.behavior.fleetAimSupplyDuration); } } //Value acceleration on a target if(purpose != DP_Defense && purpose != DP_Gate) { double targetAccel = 2.0; if(purpose == DP_Support) targetAccel *= 1.5; else if(purpose == DP_Scout) targetAccel *= 3.0; w *= weight(dsg.total(SV_Thrust) / max(dsg.total(HV_Mass), 0.01), targetAccel); } //Penalties for having important systems easy to shoot down uint holes = 0; for(uint i = 0, cnt = dsg.subsystemCount; i < cnt; ++i) { auto@ sys = dsg.subsystem(i); if(!sys.type.hasTag(ST_Important)) continue; //TODO: We should be able to penalize for exposed supply storage if(sys.type.hasTag(ST_NoCore)) continue; vec2u core = sys.core; for(uint d = 0; d < 6; ++d) { if(!traceContainsArmor(dsg, core, d)) holes += 1; } } if(holes != 0) w /= pow(0.9, double(holes)); //TODO: Check FTL return w; } bool traceContainsArmor(const Design@ dsg, const vec2u& startPos, uint direction) { vec2u pos = startPos; while(dsg.hull.active.valid(pos)) { if(!dsg.hull.active.advance(pos, HexGridAdjacency(direction))) break; auto@ sys = dsg.subsystem(pos.x, pos.y); if(sys is null) continue; if(sys.type.hasTag(ST_IsArmor)) return true; } return false; } bool contains(const Design& dsg) { if(active is null) return false; if(dsg.mostUpdated() is active.mostUpdated()) return true; return false; } const Design@ design(AI& ai, Designs& designs) { int trySize = targetSize; if(findSize) { trySize = randomd(0.75, 1.25) * targetSize; trySize = 5 * round(double(designer.size) / 5.0); } if(designs.race !is null) { const Design@ fromRace; if(designs.race.design(this, trySize, fromRace)) return fromRace; } if(designer !is null) { designer.size = trySize; return designer.design(1); } return null; } void choose(AI& ai, const Design@ dsg, bool randomizeName=true) { set(dsg); @designer = null; findSize = false; string baseName = dsg.name; if(customName.length != 0) { baseName = customName; } else if(randomizeName) { if(dsg.hasTag(ST_IsSupport)) baseName = autoSupportNames[randomi(0,autoSupportNames.length-1)]; else baseName = autoFlagNames[randomi(0,autoFlagNames.length-1)]; } string name = baseName; uint try = 0; while(ai.empire.getDesign(name) !is null) { name = baseName + " "; appendRoman(++try, name); } if(name != dsg.name) dsg.rename(name); //Set design settings/support behavior if(purpose == DP_Support) { if(dsg.total(SV_SupportSupplyCapacity) > 0.01) { DesignSettings settings; settings.behavior = SG_Brawler; dsg.setSettings(settings); } else if(dsg.totalHP > 50 * dsg.size) { DesignSettings settings; settings.behavior = SG_Shield; dsg.setSettings(settings); } else { DesignSettings settings; settings.behavior = SG_Cannon; dsg.setSettings(settings); } } ai.empire.addDesign(ai.empire.getDesignClass("AI", true), dsg); if(cast<Designs>(ai.designs).log) ai.print("Chose design for purpose "+uint(purpose)+" at size "+dsg.size); } void step(AI& ai, Designs& designs) { if(active is null) { if(designer is null) { if(designs.race is null || !designs.race.preCompose(this)) prepare(ai); if(designs.race !is null && designs.race.postCompose(this)) return; } if(potential.length >= ai.behavior.designEvaluateCount) { //Find the best design out of all our potentials const Design@ best; double bestScore = 0.0; for(uint i = 0, cnt = potential.length; i < cnt; ++i) { double w = scores[i]; if(w > bestScore) { bestScore = w; @best = potential[i]; } } potential.length = 0; scores.length = 0; if(best !is null) choose(ai, best); } else if(designer !is null && active is null) { //Add a new design onto the list to be evaluated const Design@ dsg = design(ai, designs); if(dsg !is null && !dsg.hasFatalErrors()) { potential.insertLast(dsg); scores.insertLast(evaluate(ai, dsg)); /*if(designs.log)*/ /* ai.print("Designed for purpose "+uint(purpose)+" at size "+dsg.size+", weight "+evaluate(ai, dsg));*/ } } } else { set(active.mostUpdated()); } } void set(const Design@ dsg) { if(active is dsg) return; @active = dsg; targetBuildCost = dsg.total(HV_BuildCost); targetMaintenance = dsg.total(HV_MaintainCost); targetLaborCost = dsg.total(HV_LaborCost); targetSize = dsg.size; dps = dsg.total(SV_DPS); hp = dsg.totalHP + dsg.total(SV_ShieldCapacity); supplyDrain = dsg.total(SV_SupplyDrain); } }; const Design@ scaleDesign(const Design@ orig, int newSize) { DesignDescriptor desc; resizeDesign(orig, newSize, desc); return makeDesign(desc); } final class Designs : AIComponent { RaceDesigns@ race; int nextTargetId = 0; array<DesignTarget@> designing; array<DesignTarget@> completed; array<DesignTarget@> automatic; void create() { @race = cast<RaceDesigns>(ai.race); } void start() { //Design some basic support sizes design(DP_Support, 1); design(DP_Support, 2); design(DP_Support, 4); design(DP_Support, 8); design(DP_Support, 16); } void save(SaveFile& file) { file << nextTargetId; uint cnt = designing.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveDesign(file, designing[i]); designing[i].save(this, file); } cnt = automatic.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveDesign(file, automatic[i]); if(!isDesigning(automatic[i])) { file.write1(); automatic[i].save(this, file); } else { file.write0(); } } cnt = completed.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveDesign(file, completed[i]); if(!isDesigning(completed[i])) { file.write1(); completed[i].save(this, file); } else { file.write0(); } } } bool isDesigning(DesignTarget@ targ) { for(uint i = 0, cnt = designing.length; i < cnt; ++i) { if(designing[i] is targ) return true; } return false; } void load(SaveFile& file) { file >> nextTargetId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ targ = loadDesign(file); targ.load(this, file); designing.insertLast(targ); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ targ = loadDesign(file); if(file.readBit()) targ.load(this, file); automatic.insertLast(targ); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ targ = loadDesign(file); if(file.readBit()) targ.load(this, file); completed.insertLast(targ); } } array<DesignTarget@> loadIds; DesignTarget@ loadDesign(int id) { if(id == -1) return null; for(uint i = 0, cnt = loadIds.length; i < cnt; ++i) { if(loadIds[i].id == id) return loadIds[i]; } DesignTarget data; data.id = id; loadIds.insertLast(data); return data; } DesignTarget@ loadDesign(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadDesign(id); } void saveDesign(SaveFile& file, DesignTarget@ data) { int id = -1; if(data !is null) id = data.id; file << id; } void postLoad(AI& ai) { loadIds.length = 0; } const Design@ get_currentSupport() { for(int i = automatic.length - 1; i >= 0; --i) { if(automatic[i].purpose == DP_Support && automatic[i].active !is null) return automatic[i].active; } return null; } void getSupportAverages(double& hp, double& dps, double& supDrain) { hp = 0; dps = 0; supDrain = 0; uint count = 0; for(uint i = 0, cnt = automatic.length; i < cnt; ++i) { auto@ targ = automatic[i]; if(targ.purpose != DP_Support) continue; if(targ.active is null) continue; hp += targ.hp / double(targ.targetSize); dps += targ.dps / double(targ.targetSize); supDrain += targ.supplyDrain / double(targ.targetSize); count += 1; } if(count == 0) { hp = 40.0; dps = 0.30; supDrain = 1.0; } else { hp /= double(count); dps /= double(count); supDrain /= double(count); } } DesignPurpose classify(Object@ obj) { if(obj is null || !obj.isShip) return DP_Combat; Ship@ ship = cast<Ship>(obj); return classify(ship.blueprint.design); } DesignPurpose classify(const Design@ dsg, DesignPurpose defaultPurpose = DP_Combat) { if(dsg is null) return defaultPurpose; for(uint i = 0, cnt = automatic.length; i < cnt; ++i) { if(automatic[i].contains(dsg)) return DesignPurpose(automatic[i].purpose); } for(uint i = 0, cnt = completed.length; i < cnt; ++i) { if(completed[i].contains(dsg)) return DesignPurpose(completed[i].purpose); } if(dsg.hasTag(ST_Mothership)) return DP_Mothership; if(dsg.hasTag(ST_Gate)) return DP_Gate; if(dsg.hasTag(ST_Slipstream)) return DP_Slipstream; if(dsg.hasTag(ST_Support)) return DP_Support; if(dsg.hasTag(ST_Station)) return DP_Defense; double dps = dsg.total(SV_DPS); if(dsg.total(SV_MiningRate) > 0) return DP_Miner; if(dsg.size == 16.0 && dsg.total(SV_DPS) < 2.0) return DP_Scout; if(dps > 0.1 * dsg.size || dsg.total(SV_SupportCapacity) > 0) return DP_Combat; return defaultPurpose; } DesignTarget@ design(uint purpose, int size, int targetCost = 0, int targetMaint = 0, double targetLabor = 0, bool findSize = false) { for(uint i = 0, cnt = automatic.length; i < cnt; ++i) { auto@ target = automatic[i]; if(target.purpose != purpose) continue; if(target.targetSize != size) continue; if(targetCost != 0 && target.targetBuildCost > targetCost) continue; if(targetMaint != 0 && target.targetMaintenance > targetMaint) continue; if(targetLabor != 0 && target.targetLaborCost > targetLabor) continue; if(target.findSize != findSize) continue; return target; } DesignTarget targ(purpose, size); targ.findSize = findSize; targ.targetBuildCost = targetCost; targ.targetMaintenance = targetMaint; targ.targetLaborCost = targetLabor; automatic.insertLast(targ); return design(targ); } DesignTarget@ design(DesignTarget@ target) { target.id = nextTargetId++; designing.insertLast(target); return target; } DesignTarget@ get(const Design@ dsg) { for(uint i = 0, cnt = automatic.length; i < cnt; ++i) { if(automatic[i].contains(dsg)) return automatic[i]; } return null; } DesignTarget@ scale(const Design@ dsg, int newSize) { if(dsg.newer !is null) { auto@ newTarg = get(dsg.newest()); if(newTarg.targetSize == newSize) return newTarg; @dsg = dsg.newest(); } DesignTarget@ previous = get(dsg); uint purpose = DP_Combat; if(previous !is null) purpose = previous.purpose; else purpose = classify(dsg); DesignTarget target(purpose, newSize); target.id = nextTargetId++; @target.active = scaleDesign(dsg, newSize); ai.empire.changeDesign(dsg, target.active, ai.empire.getDesignClass(dsg.cls.name, true)); if(previous !is null) automatic.remove(previous); automatic.insertLast(target); return target; } uint chkInd = 0; void tick(double time) { if(designing.length != 0) { //chkInd = (chkInd+1) % designing.length; // Getting 1 design first is better than getting all of them later chkInd = 0; auto@ target = designing[chkInd]; target.step(ai, this); if(target.active !is null) { designing.removeAt(chkInd); if(automatic.find(target) == -1) completed.insertLast(target); } } } }; AIComponent@ createDesigns() { return Designs(); } |
Added scripts/server/empire_ai/weasel/Development.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Planets; import empire_ai.weasel.Resources; import empire_ai.weasel.Colonization; import empire_ai.weasel.Systems; import planet_levels; import buildings; import ai.consider; from ai.buildings import Buildings, BuildingAI, RegisterForLaborUse, AsCreatedResource, BuildingUse; from ai.resources import AIResources, ResourceAI; interface RaceDevelopment { bool shouldBeFocus(Planet& pl, const ResourceType@ resource); }; class DevelopmentFocus { Object@ obj; PlanetAI@ plAI; int targetLevel = 0; int requestedLevel = 0; int maximumLevel = INT_MAX; array<ExportData@> managedPressure; double weight = 1.0; void tick(AI& ai, Development& dev, double time) { if(targetLevel != requestedLevel) { if(targetLevel > requestedLevel) { int nextLevel = min(targetLevel, min(obj.resourceLevel, requestedLevel)+1); if(nextLevel != requestedLevel) { for(int i = requestedLevel+1; i <= nextLevel; ++i) dev.resources.organizeImports(obj, i); requestedLevel = nextLevel; } } else { dev.resources.organizeImports(obj, targetLevel); requestedLevel = targetLevel; } } //Remove managed pressure resources that are no longer valid for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) { ExportData@ res = managedPressure[i]; if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable || res.developUse !is obj) { if(res.developUse is obj) @res.developUse = null; managedPressure.removeAt(i); --i; --cnt; } } //Make sure we're not exporting our resource if(plAI !is null && plAI.resources !is null && plAI.resources.length != 0) { auto@ res = plAI.resources[0]; res.localOnly = true; if(res.request !is null && res.request.obj !is res.obj) dev.resources.breakImport(res); } //TODO: We should be able to bump managed pressure resources back to Development for //redistribution if we run out of pressure capacity. } void save(Development& development, SaveFile& file) { file << obj; development.planets.saveAI(file, plAI); file << targetLevel; file << requestedLevel; file << maximumLevel; file << weight; uint cnt = managedPressure.length; file << cnt; for(uint i = 0; i < cnt; ++i) development.resources.saveExport(file, managedPressure[i]); } void load(Development& development, SaveFile& file) { file >> obj; @plAI = development.planets.loadAI(file); file >> targetLevel; file >> requestedLevel; file >> maximumLevel; file >> weight; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = development.resources.loadExport(file); managedPressure.insertLast(data); } } }; class Development : AIComponent, Buildings, ConsiderFilter, AIResources { RaceDevelopment@ race; Planets@ planets; Resources@ resources; Colonization@ colonization; Systems@ systems; array<DevelopmentFocus@> focuses; array<ExportData@> managedPressure; array<ColonizeData@> pendingFocuses; array<ColonizeData@> pendingResources; array<BuildingRequest@> genericBuilds; array<ExportData@> aiResources; double aimFTLStorage = 0.0; double aimResearchRate = 0.0; bool managePlanetPressure = true; bool manageAsteroidPressure = true; bool buildBuildings = true; bool colonizeResources = true; void create() { @planets = cast<Planets>(ai.planets); @resources = cast<Resources>(ai.resources); @colonization = cast<Colonization>(ai.colonization); @systems = cast<Systems>(ai.systems); @race = cast<RaceDevelopment>(ai.race); //Register specialized building types for(uint i = 0, cnt = getBuildingTypeCount(); i < cnt; ++i) { auto@ type = getBuildingType(i); for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) { auto@ hook = cast<BuildingAI>(type.ai[n]); if(hook !is null) hook.register(this, type); } } } Empire@ get_empire() { return ai.empire; } Considerer@ get_consider() { return cast<Considerer>(ai.consider); } void registerUse(BuildingUse use, const BuildingType& type) { switch(use) { case BU_Factory: @ai.defs.Factory = type; break; case BU_LaborStorage: @ai.defs.LaborStorage = type; break; } } void save(SaveFile& file) { file << aimFTLStorage; uint cnt = focuses.length; file << cnt; for(uint i = 0; i < cnt; ++i) { auto@ focus = focuses[i]; focus.save(this, file); } cnt = managedPressure.length; file << cnt; for(uint i = 0; i < cnt; ++i) resources.saveExport(file, managedPressure[i]); cnt = pendingFocuses.length; file << cnt; for(uint i = 0; i < cnt; ++i) colonization.saveColonize(file, pendingFocuses[i]); cnt = pendingResources.length; file << cnt; for(uint i = 0; i < cnt; ++i) colonization.saveColonize(file, pendingResources[i]); cnt = genericBuilds.length; file << cnt; for(uint i = 0; i < cnt; ++i) planets.saveBuildingRequest(file, genericBuilds[i]); cnt = aiResources.length; file << cnt; for(uint i = 0; i < cnt; ++i) resources.saveExport(file, aiResources[i]); } void load(SaveFile& file) { file >> aimFTLStorage; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ focus = DevelopmentFocus(); focus.load(this, file); if(focus.obj !is null) focuses.insertLast(focus); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = resources.loadExport(file); if(data !is null) managedPressure.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = colonization.loadColonize(file); if(data !is null) pendingFocuses.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = colonization.loadColonize(file); if(data !is null) pendingResources.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = planets.loadBuildingRequest(file); if(data !is null) genericBuilds.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = resources.loadExport(file); if(data !is null) aiResources.insertLast(data); } } bool requestsFTLStorage() { double capacity = ai.empire.FTLCapacity; if(aimFTLStorage <= capacity) return false; if(ai.empire.FTLStored < capacity * 0.5) return false; return true; } bool requestsResearchGeneration() { double rate = ai.empire.ResearchRate; if (aimResearchRate <= rate) return false; return true; } bool isBuilding(const BuildingType& type) { for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) { if(genericBuilds[i].type is type) return true; } return false; } bool isLeveling() { for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { if(focuses[i].obj.resourceLevel < uint(focuses[i].targetLevel)) { auto@ focus = focuses[i].obj; //If all our requirements are resolved, then we can safely assume it will be leveled up bool allResolved = true; for(uint n = 0, ncnt = resources.requested.length; n < ncnt; ++n) { auto@ req = resources.requested[n]; if(req.obj !is focus) continue; if(req.beingMet) continue; if(!req.isColonizing) { allResolved = false; break; } if(!colonization.isResolved(req)) { allResolved = false; break; } } if(!allResolved) return true; } } return false; } bool isBusy() { if(pendingFocuses.length != 0) return true; if(pendingResources.length != 0) return true; if(isLeveling()) return true; return false; } bool isFocus(Object@ obj) { for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { if(focuses[i].obj is obj) return true; } return false; } bool isManaging(ExportData@ res) { for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) { if(managedPressure[i] is res) return true; } for(uint i = 0, cnt = aiResources.length; i < cnt; ++i) { if(aiResources[i] is res) return true; } for(uint n = 0, ncnt = focuses.length; n < ncnt; ++n) { auto@ f = focuses[n]; if(f.obj is res.obj) return true; for(uint i = 0, cnt = f.managedPressure.length; i < cnt; ++i) { if(f.managedPressure[i] is res) return true; } } return false; } bool isDevelopingIn(Region@ reg) { if(reg is null) return false; for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { if(focuses[i].obj.region is reg) return true; } return false; } void start() { //Level up the homeworld to level 3 to start with for(uint i = 0, cnt = ai.empire.planetCount; i < cnt; ++i) { Planet@ homeworld = ai.empire.planetList[i]; if(homeworld !is null && homeworld.valid) { auto@ hwFocus = addFocus(planets.register(homeworld)); if(homeworld.nativeResourceCount >= 2 || homeworld.primaryResourceLimitLevel >= 3 || cnt == 1) hwFocus.targetLevel = 3; } } } double idlePenalty = 0; void findSomethingToDo() { if(idlePenalty > gameTime) return; double totalChance = ai.behavior.focusDevelopWeight + ai.behavior.focusColonizeNewWeight * sqr(1.0 / double(focuses.length)) + ai.behavior.focusColonizeHighTierWeight; double roll = randomd(0.0, totalChance); //Level up one of our existing focuses roll -= ai.behavior.focusDevelopWeight; if(roll <= 0) { DevelopmentFocus@ levelup; double totalWeight = 0.0; for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { auto@ f = focuses[i]; if(f.weight == 0) continue; if(f.targetLevel >= f.maximumLevel) continue; totalWeight += f.weight; if(randomd() < f.weight / totalWeight) @levelup = f; } if(levelup !is null) { levelup.targetLevel += 1; if(log) ai.print("Develop chose to level this up to "+levelup.targetLevel, levelup.obj); return; } else { if(log) ai.print("Develop ran out of things to level up."); } } if(!colonizeResources) return; //Find a scalable or high tier resource to colonize and turn into a focus roll -= ai.behavior.focusColonizeNewWeight * sqr(1.0 / double(focuses.length)); if(roll <= 0) { Planet@ newFocus; double w; double bestWeight = 0.0; for(uint i = 0, cnt = colonization.potentials.length; i < cnt; ++i) { auto@ p = colonization.potentials[i]; if(p.resource.level < 3 && p.resource.cls !is colonization.scalableClass) continue; Region@ reg = p.pl.region; if(reg is null) continue; if(colonization.isColonizing(p.pl)) continue; vec2i surfaceSize = p.pl.surfaceGridSize; int tiles = surfaceSize.width * surfaceSize.height; if(tiles < 144) continue; auto@ sys = systems.getAI(reg); w = 1.0; if(sys.border) w *= 0.25; if (!sys.owned && !sys.border) w /= 0.25; if(sys.obj.PlanetsMask & ~ai.mask != 0) w *= 0.25; if(p.resource.cls is colonization.scalableClass) w *= 10.0; if (w > bestWeight) { @newFocus = p.pl; bestWeight = w; } } if(newFocus !is null) { auto@ data = colonization.colonize(newFocus); if(data !is null) pendingFocuses.insertLast(data); if(log) ai.print("Colonize to become develop focus", data.target); return; } else { if(log) ai.print("Develop could not find a scalable or high tier resource to make a focus."); } } if(focuses.length == 0) return; //Find a high tier resource to import to one of our focuses roll -= ai.behavior.focusColonizeHighTierWeight; if(roll <= 0) { ResourceSpec spec; spec.type = RST_Level_Minimum; spec.level = 3; spec.isLevelRequirement = false; auto@ data = colonization.colonize(spec); if(data !is null) { if(log) ai.print("Colonize as free resource", data.target); pendingResources.insertLast(data); return; } else { if(log) ai.print("Develop could not find a high tier resource to colonize as free resource."); } } //Try to find a level 2 resource if everything else failed { ResourceSpec spec; spec.type = RST_Level_Minimum; spec.level = 2; spec.isLevelRequirement = false; if(colonization.shouldQueueFor(spec)) { auto@ data = colonization.colonize(spec); if(data !is null) { if(log) ai.print("Colonize as free resource", data.target); pendingResources.insertLast(data); return; } else { if(log) ai.print("Develop could not find a level 2 resource to colonize as free resource."); } } } idlePenalty = gameTime + randomd(10.0, 40.0); } uint bldIndex = 0; uint aiInd = 0; uint presInd = 0; uint chkInd = 0; void focusTick(double time) override { //Remove any resources we're managing that got used for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) { ExportData@ res = managedPressure[i]; if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable) { managedPressure.removeAt(i); --i; --cnt; } } //Find new resources that we can put in our pressure manager uint avCnt = resources.available.length; if(avCnt != 0) { uint index = randomi(0, avCnt-1); for(uint i = 0, cnt = min(avCnt, 3); i < cnt; ++i) { uint resInd = (index+i) % avCnt; ExportData@ res = resources.available[resInd]; if(res.usable && res.request is null && res.obj !is null && res.obj.valid && res.obj.owner is ai.empire && res.developUse is null) { if(res.resource.ai.length != 0) { if(!isManaging(res)) aiResources.insertLast(res); } else if(res.resource.totalPressure > 0 && res.resource.exportable) { if(!managePlanetPressure && res.obj.isPlanet) continue; if(!manageAsteroidPressure && res.obj.isAsteroid) continue; if(!isManaging(res)) managedPressure.insertLast(res); } } } } //Distribute managed pressure resources if(managedPressure.length != 0) { presInd = (presInd+1) % managedPressure.length; ExportData@ res = managedPressure[presInd]; int pressure = res.resource.totalPressure; DevelopmentFocus@ onFocus; double bestWeight = 0; bool havePressure = ai.empire.HasPressure != 0.0; for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { auto@ f = focuses[i]; int cap = f.obj.pressureCap; if(!havePressure) cap = 10000; int cur = f.obj.totalPressure; if(cur + pressure > 2 * cap) continue; double w = 1.0; if(cur + pressure > cap) w *= 0.1; if(w > bestWeight) { bestWeight = w; @onFocus = f; } } if(onFocus !is null) { if(res.obj !is onFocus.obj) res.obj.exportResourceByID(res.resourceId, onFocus.obj); else res.obj.exportResourceByID(res.resourceId, null); @res.developUse = onFocus.obj; onFocus.managedPressure.insertLast(res); managedPressure.removeAt(presInd); if(log) ai.print("Take "+res.resource.name+" from "+res.obj.name+" for pressure", onFocus.obj); } } //Use generic AI distribution hooks if(aiResources.length != 0) { aiInd = (aiInd+1) % aiResources.length; ExportData@ res = aiResources[aiInd]; if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable) { aiResources.removeAt(aiInd); } else { Object@ newTarget = res.developUse; if(newTarget !is null) { if(!newTarget.valid || newTarget.owner !is ai.empire) @newTarget = null; } for(uint i = 0, cnt = res.resource.ai.length; i < cnt; ++i) { auto@ hook = cast<ResourceAI>(res.resource.ai[i]); if(hook !is null) @newTarget = hook.distribute(this, res.resource, newTarget); } if(newTarget !is res.developUse) { if(res.obj !is newTarget) res.obj.exportResourceByID(res.resourceId, newTarget); else res.obj.exportResourceByID(res.resourceId, null); @res.developUse = newTarget; } } } //Deal with focuses we're colonizing for(uint i = 0, cnt = pendingFocuses.length; i < cnt; ++i) { auto@ data = pendingFocuses[i]; if(data.completed) { auto@ focus = addFocus(planets.register(data.target)); focus.targetLevel = 3; pendingFocuses.removeAt(i); --i; --cnt; } else if(data.canceled) { pendingFocuses.removeAt(i); --i; --cnt; } } for(uint i = 0, cnt = pendingResources.length; i < cnt; ++i) { auto@ data = pendingResources[i]; if(data.completed) { planets.requestLevel(planets.register(data.target), data.target.primaryResourceLevel); pendingResources.removeAt(i); --i; --cnt; } else if(data.canceled) { pendingResources.removeAt(i); --i; --cnt; } } //If we're not currently leveling something up, find something else to do if(!isBusy()) findSomethingToDo(); //Deal with building AI hints for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) { auto@ build = genericBuilds[i]; if(build.canceled) { genericBuilds.removeAt(i); --i; --cnt; } else if(build.built) { if(build.getProgress() >= 1.f) { if(build.expires < gameTime) { genericBuilds.removeAt(i); --i; --cnt; } } else build.expires = gameTime + 60.0; } } if(buildBuildings) { for(uint i = 0, cnt = getBuildingTypeCount(); i < cnt; ++i) { bldIndex = (bldIndex+1) % cnt; auto@ type = getBuildingType(bldIndex); if(type.ai.length == 0) continue; //If we're already generically building something of this type, wait bool existing = false; for(uint n = 0, ncnt = genericBuilds.length; n < ncnt; ++n) { auto@ build = genericBuilds[n]; if(build.type is type && !build.built) { existing = true; break; } } if(existing) break; @filterType = type; @consider.filter = this; //See if we should generically build something of this type for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) { auto@ hook = cast<BuildingAI>(type.ai[n]); if(hook !is null) { Object@ buildOn = hook.considerBuild(this, type); if(buildOn !is null && buildOn.isPlanet) { auto@ plAI = planets.getAI(cast<Planet>(buildOn)); if(plAI !is null) { if(log) ai.print("AI hook generically requested building of type "+type.name, buildOn); double priority = 1.0; //Resource buildings should be built as soon as possible if (cast<AsCreatedResource>(hook) !is null) priority = 2.0; auto@ req = planets.requestBuilding(plAI, type, priority, expire=ai.behavior.genericBuildExpire); if(req !is null) genericBuilds.insertLast(req); break; } } } } break; } } //Find planets we've acquired 'somehow' that have scalable resources and should be development focuses if(planets.planets.length != 0) { chkInd = (chkInd+1) % planets.planets.length; auto@ plAI = planets.planets[chkInd]; if(plAI.resources.length != 0) { auto@ res = plAI.resources[0]; if(res.resource.cls is colonization.scalableClass || focuses.length == 0 && res.resource.level >= 2 || (race !is null && race.shouldBeFocus(plAI.obj, res.resource))) { if(!isFocus(plAI.obj)) { auto@ focus = addFocus(plAI); focus.targetLevel = max(1, res.resource.level); } } } } } DevelopmentFocus@ addFocus(PlanetAI@ plAI) { DevelopmentFocus focus; @focus.obj = plAI.obj; @focus.plAI = plAI; focus.maximumLevel = getMaxPlanetLevel(plAI.obj); focuses.insertLast(focus); return focus; } DevelopmentFocus@ getFocus(Planet& pl) { for(uint i = 0, cnt = focuses.length; i < cnt; ++i) { if(focuses[i].obj is pl) return focuses[i]; } return null; } void tick(double time) override { for(uint i = 0, cnt = focuses.length; i < cnt; ++i) focuses[i].tick(ai, this, time); } const BuildingType@ filterType; bool filter(Object@ obj) { for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) { auto@ build = genericBuilds[i]; if(build.type is filterType && build.plAI.obj is obj) return false; } return true; } Planet@ getLaborAt(Territory@ territory, double&out expires) { if (territory is null) { if (log) ai.print("invalid territory to get labor at"); return null; } expires = 600.0; const BuildingType@ type = ai.defs.Factory; BuildingRequest@ request = null; Planet@ pl = null; for (uint i = 0, cnt = type.ai.length; i < cnt; ++i) { auto@ hook = cast<RegisterForLaborUse>(type.ai[i]); if (hook !is null) { Object@ obj = hook.considerBuild(this, type, territory); if (obj !is null) { @pl = cast<Planet>(obj); if (pl !is null) { planets.requestBuilding(planets.getAI(pl), type, 2.0, expires); if (log) ai.print("requesting building " + type.name + " at " + pl.name + " to get labor at " + addrstr(territory)); break; } } } } return pl; } }; AIComponent@ createDevelopment() { return Development(); } |
Added scripts/server/empire_ai/weasel/Diplomacy.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
// Diplomacy // --------- // Acts as an adaptor for using the generically developed DiplomacyAI. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Systems; import empire_ai.weasel.Planets; import empire_ai.weasel.Development; import empire_ai.weasel.Construction; import empire_ai.weasel.Fleets; import empire_ai.weasel.Resources; import empire_ai.weasel.War; import empire_ai.weasel.Intelligence; import influence; from empire_ai.DiplomacyAI import DiplomacyAI, VoteData, CardAI, VoteState; import systems; class Diplomacy : DiplomacyAI, IAIComponent { Systems@ systems; Fleets@ fleets; Planets@ planets; Construction@ construction; Development@ development; Resources@ resources; War@ war; Intelligence@ intelligence; //Adapt to AI component AI@ ai; double prevFocus = 0; bool logCritical = false; bool logErrors = true; double getPrevFocus() { return prevFocus; } void setPrevFocus(double value) { prevFocus = value; } void setLog() { log = true; } void setLogCritical() { logCritical = true; } void set(AI& ai) { @this.ai = ai; } void start() {} void tick(double time) {} void turn() {} void postLoad(AI& ai) {} void postSave(AI& ai) {} void loadFinalize(AI& ai) {} //Actual AI component implementations void create() { @systems = cast<Systems>(ai.systems); @fleets = cast<Fleets>(ai.fleets); @planets = cast<Planets>(ai.planets); @development = cast<Development>(ai.development); @construction = cast<Construction>(ai.construction); @resources = cast<Resources>(ai.resources); @war = cast<War>(ai.war); @intelligence = cast<Intelligence>(ai.intelligence); } //IMPLEMENTED BY DiplomacyAI /*void save(SaveFile& file) {}*/ /*void load(SaveFile& file) {}*/ uint nextStep = 0; void focusTick(double time) { summarize(); if (ai.behavior.forbidDiplomacy) return; switch(nextStep++ % 3) { case 0: buyCards(); break; case 1: considerActions(); break; case 2: considerVotes(); break; } } //Adapt to diplomacy AI Empire@ get_empire() { return ai.empire; } uint get_allyMask() { return ai.allyMask; } int getStanding(Empire@ emp) { //TODO: Use relations module for this generically if(emp.isHostile(ai.empire)) return -50; if(ai.allyMask & emp.mask != 0) return 100; return 0; } void print(const string& str) { ai.print(str); } Object@ considerImportantPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } return best; } Object@ considerOwnedPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { //Consider our important ones first double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } //Consider some random other ones uint planetCount = planets.planets.length; if(planetCount != 0) { uint offset = randomi(0, planetCount-1); for(uint n = 0; n < 5; ++n) { Object@ obj = planets.planets[(offset+n) % planetCount].obj; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } } return best; } Object@ considerImportantSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj.region; if(obj is null) continue; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } return best; } Object@ considerOwnedSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { //Consider our important ones first double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { Object@ obj = development.focuses[i].obj.region; if(obj is null) continue; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } //Consider some random other ones uint sysCount = systems.owned.length; if(sysCount != 0) { uint offset = randomi(0, sysCount-1); for(uint n = 0; n < 5; ++n) { Object@ obj = systems.owned[(offset+n) % sysCount].obj; double w = hook.consider(this, targets, vote, card, obj); if(w > bestWeight) { @best = obj; bestWeight = w; } } } return best; } Object@ considerDefendingSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = war.battles.length; i < cnt; ++i) { auto@ battle = war.battles[i]; Region@ sys = battle.system.obj; if(sys.SiegedMask & empire.mask == 0) continue; double w = hook.consider(this, targets, vote, card, sys); if(w > bestWeight) { @best = sys; bestWeight = w; } } return best; } Object@ considerDefendingPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = war.battles.length; i < cnt; ++i) { auto@ battle = war.battles[i]; Region@ sys = battle.system.obj; if(sys.SiegedMask & empire.mask == 0) continue; for(uint n = 0, ncnt = battle.system.planets.length; n < ncnt; ++n) { Object@ pl = battle.system.planets[n]; if(pl.owner !is empire) continue; double w = hook.consider(this, targets, vote, card, pl); if(w > bestWeight) { @best = pl; bestWeight = w; } } } return best; } Object@ considerEnemySystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(!empire.isHostile(emp)) continue; auto@ intel = intelligence.get(emp); if(intel is null) continue; for(uint n = 0, ncnt = intel.theirBorder.length; n < ncnt; ++n) { auto@ sysIntel = intel.theirBorder[n]; double w = hook.consider(this, targets, vote, card, sysIntel.obj); if(w > bestWeight) { @best = sysIntel.obj; bestWeight = w; } } } return best; } Object@ considerEnemyPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(!empire.isHostile(emp)) continue; auto@ intel = intelligence.get(emp); if(intel is null) continue; for(uint n = 0, ncnt = intel.theirBorder.length; n < ncnt; ++n) { auto@ sysIntel = intel.theirBorder[n]; for(uint j = 0, jcnt = sysIntel.planets.length; j < jcnt; ++j) { Planet@ pl = sysIntel.planets[j]; if(pl.visibleOwnerToEmp(empire) !is emp) continue; double w = hook.consider(this, targets, vote, card, pl); if(w > bestWeight) { @best = pl; bestWeight = w; } } } } return best; } Object@ considerFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { Object@ best; double bestWeight = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { Object@ fleet = fleets.fleets[i].obj; if(fleet !is null) { double w = hook.consider(this, targets, vote, card, fleet); if(w > bestWeight) { @best = fleet; bestWeight = w; } } } return best; } Object@ considerEnemyFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) { double bestWeight = 0.0; Object@ best; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(!empire.isHostile(emp)) continue; auto@ intel = intelligence.get(emp); if(intel is null) continue; for(uint n = 0, ncnt = intel.fleets.length; n < ncnt; ++n) { auto@ flIntel = intel.fleets[n]; if(!flIntel.known) continue; double w = hook.consider(this, targets, vote, card, flIntel.obj); if(w > bestWeight) { @best = flIntel.obj; bestWeight = w; } } } return best; } Object@ considerMatchingImportRequests(const CardAI& hook, Targets& targets, VoteState@ vote, const InfluenceCard@ card, const ResourceType@ type, bool considerExisting) { Object@ best; double bestWeight = 0.0; for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) { ImportData@ req = resources.requested[i]; if(req.spec.meets(type)) { double w = hook.consider(this, targets, vote, card, req.obj, null); if(w > bestWeight) { bestWeight = w; @best = req.obj; } } } if(considerExisting) { for(uint i = 0, cnt = resources.used.length; i < cnt; ++i) { ExportData@ res = resources.used[i]; ImportData@ req = res.request; if(req !is null && req.spec.meets(type)) { double w = hook.consider(this, targets, vote, card, req.obj, res.obj); if(w > bestWeight) { bestWeight = w; @best = req.obj; } } } } return best; } }; IAIComponent@ createDiplomacy() { return Diplomacy(); } |
Added scripts/server/empire_ai/weasel/Energy.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
// Energy // ------ // Manage the use of energy on artifacts and other things. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Systems; import ai.consider; import artifacts; import abilities; import systems; from ai.artifacts import Artifacts, ArtifactConsider, ArtifactAI; double effCostEstimate(double cost, double freeStorage) { double free = min(cost, freeStorage); cost -= free; double effStep = config::ENERGY_EFFICIENCY_STEP; double eff = 0.0; double step = 1.0; while(cost > 0) { eff += (cost / effStep) * step; cost -= effStep; step *= 2.0; } return eff * effStep + free; } class ConsiderEnergy : ArtifactConsider { int id = -1; const ArtifactType@ type; Artifact@ artifact; Ability@ ability; Object@ target; vec3d pointTarget; double cost = 0.0; double value = 0.0; void save(AI& ai, SaveFile& file) { file << id; file << artifact; file << target; file << cost; file << value; file << pointTarget; } void load(AI& ai, SaveFile& file) { file >> id; file >> artifact; file >> target; file >> cost; file >> value; file >> pointTarget; if(artifact !is null) init(ai, artifact); } void setTarget(Object@ obj) { @target = obj; } Object@ getTarget() { return target; } bool canTarget(Object@ obj) { if(ability.targets.length != 0) { auto@ targ = ability.targets[0]; @targ.obj = obj; targ.filled = true; return ability.isValidTarget(0, targ); } else return false; } void setTargetPosition(const vec3d& point) { pointTarget = point; } vec3d getTargetPosition() { return pointTarget; } bool canTargetPosition(const vec3d& point) { if(ability.targets.length != 0) { auto@ targ = ability.targets[0]; targ.point = point; targ.filled = true; return ability.isValidTarget(0, targ); } else return false; } void init(AI& ai, Artifact@ artifact) { @this.artifact = artifact; @type = getArtifactType(artifact.ArtifactType); if(ability is null) @ability = Ability(); if(type.secondaryChance > 0 && type.abilities.length >= 2 && randomd() < type.secondaryChance) { ability.id = 1; @ability.type = type.abilities[1]; } else { ability.id = 0; @ability.type = type.abilities[0]; } ability.targets = Targets(ability.type.targets); @ability.obj = artifact; @ability.emp = ai.empire; } bool isValid(AI& ai, Energy& energy) { return energy.canUse(artifact); } void considerEnergy(AI& ai, Energy& energy) { if(type !is null && type.abilities.length != 0) { value = 1.0; for(uint i = 0, cnt = type.ai.length; i < cnt; ++i) { ArtifactAI@ ai; if(ability.id == 0) @ai = cast<ArtifactAI>(type.ai[i]); else @ai = cast<ArtifactAI>(type.secondaryAI[i]); if(ai !is null) { if(!ai.consider(energy, this, value)) { value = 0.0; break; } } } if(type.ai.length == 0) value = 0.0; if(ability.targets.length != 0) { if(ability.targets[0].type == TT_Object) { @ability.targets[0].obj = target; ability.targets[0].filled = true; if(target is null) value = 0.0; } else if(ability.targets[0].type == TT_Point) { ability.targets[0].point = pointTarget; ability.targets[0].filled = true; } } if(value > 0.0) { if(!ability.canActivate(ability.targets, ignoreCost=true)) { value = 0.0; } else { cost = ability.getEnergyCost(ability.targets); if(cost != 0.0) { //Estimate the amount of turns it would take to trigger this, //and devalue it based on that. This is ceiled in order to allow //for artifacts of similar cost to not be affected by cost differences. double effCost = effCostEstimate(cost, energy.freeStorage); double estTime = effCost / max(energy.baseIncome, 0.01); double turns = ceil(estTime / (3.0 * 60.0)); value /= turns; } else { value *= 1000.0; } } } } else { value = 0.0; } } void execute(AI& ai, Energy& energy) { if(artifact !is null && type.abilities.length != 0) { if(energy.log) ai.print("Activate artifact "+artifact.name, artifact.region); if(ability.type.targets.length != 0) { if(ability.type.targets[0].type == TT_Object) artifact.activateAbilityTypeFor(ai.empire, ability.type.id, target); else if(ability.type.targets[0].type == TT_Point) artifact.activateAbilityTypeFor(ai.empire, ability.type.id, pointTarget); } else { artifact.activateAbilityTypeFor(ai.empire, ability.type.id); } } } int opCmp(const ConsiderEnergy@ other) const { if(value < other.value) return -1; if(value > other.value) return 1; return 0; } }; class Energy : AIComponent, Artifacts { Systems@ systems; double baseIncome; double freeStorage; array<ConsiderEnergy@> queue; int nextEnergyId = 0; void save(SaveFile& file) { file << nextEnergyId; uint cnt = queue.length; file << cnt; for(uint i = 0; i < cnt; ++i) queue[i].save(ai, file); } void load(SaveFile& file) { file >> nextEnergyId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { ConsiderEnergy c; c.load(ai, file); if(c.artifact !is null) queue.insertLast(c); } } void create() { @systems = cast<Systems>(ai.systems); } Considerer@ get_consider() { return cast<Considerer>(ai.consider); } Empire@ get_empire() { return ai.empire; } bool canUse(Artifact@ artifact) { if(artifact is null || !artifact.valid) return false; Empire@ owner = artifact.owner; if(owner.valid && owner !is ai.empire) return false; Region@ reg = artifact.region; if(reg is null) return false; if(reg.PlanetsMask != 0) return reg.PlanetsMask & ai.mask != 0; else return hasTradeAdjacent(ai.empire, reg); } ConsiderEnergy@ registerArtifact(Artifact@ artifact) { if(!canUse(artifact)) return null; for(uint i = 0, cnt = queue.length; i < cnt; ++i) { if(queue[i].artifact is artifact) return queue[i]; } ConsiderEnergy c; c.id = nextEnergyId++; c.init(ai, artifact); if(log) ai.print("Detect artifact "+artifact.name, artifact.region); queue.insertLast(c); return c; } uint updateIdx = 0; bool update() { if(queue.length == 0) return false; updateIdx = (updateIdx+1) % queue.length; auto@ c = queue[updateIdx]; double prevValue = c.value; //Make sure this is still valid if(!c.isValid(ai, this)) { queue.removeAt(updateIdx); return false; } //Update the current target and value c.considerEnergy(ai, this); /*if(log)*/ /* ai.print(c.artifact.name+": consider "+c.value+" for cost "+c.cost, c.target);*/ //Only re-sort when needed bool changed = false; if(prevValue != c.value) { if(updateIdx > 0) { if(c.value > queue[updateIdx-1].value) changed = true; } if(updateIdx < queue.length-1) { if(c.value < queue[updateIdx+1].value) changed = true; } } return changed; } uint sysIdx = 0; void updateSystem() { uint totCnt = systems.owned.length + systems.outsideBorder.length; if(totCnt == 0) return; sysIdx = (sysIdx+1) % totCnt; SystemAI@ sys; if(sysIdx < systems.owned.length) @sys = systems.owned[sysIdx]; else @sys = systems.outsideBorder[sysIdx - systems.owned.length]; for(uint i = 0, cnt = sys.artifacts.length; i < cnt; ++i) registerArtifact(sys.artifacts[i]); } void tick(double time) { if(ai.behavior.forbidArtifact) return; //Update current income baseIncome = empire.EnergyIncome; freeStorage = empire.FreeEnergyStorage; //See if we can use anything right now if(queue.length != 0) { auto@ c = queue[0]; if(!c.isValid(ai, this)) { queue.removeAt(0); } else if(c.value > 0.0 && ai.empire.EnergyStored >= c.cost) { c.execute(ai, this); queue.removeAt(0); } } } void focusTick(double time) { if(ai.behavior.forbidArtifact) return; //Consider artifact usage bool changed = false; for(uint n = 0; n < min(queue.length, max(ai.behavior.artifactFocusConsiderCount, queue.length/20)); ++n) { if(update()) changed = true; } //Re-sort consideration if(changed) queue.sortDesc(); //Try to find new artifacts updateSystem(); } }; AIComponent@ createEnergy() { return Energy(); } |
Added scripts/server/empire_ai/weasel/Events.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
// Events // ------ // Notifies subscribed components of events raised by other components. // import empire_ai.weasel.WeaselAI; import ai.events; final class Events : AIComponent { //Event callbacks private array<EventHandler@> _onOwnedSystemAdded; private array<EventHandler@> _onOwnedSystemRemoved; private array<EventHandler@> _onBorderSystemAdded; private array<EventHandler@> _onBorderSystemRemoved; private array<EventHandler@> _onOutsideBorderSystemAdded; private array<EventHandler@> _onOutsideBorderSystemRemoved; private array<EventHandler@> _onPlanetAdded; private array<EventHandler@> _onPlanetRemoved; private array<EventHandler@> _onTradeRouteNeeded; private array<EventHandler@> _onOrbitalRequested; void create() { } //Event delegate registration Events@ opAddAssign(IOwnedSystemEvents& events) { _onOwnedSystemAdded.insertLast(EventHandler(events.onOwnedSystemAdded)); _onOwnedSystemRemoved.insertLast(EventHandler(events.onOwnedSystemRemoved)); return this; } Events@ opAddAssign(IBorderSystemEvents& events) { _onBorderSystemAdded.insertLast(EventHandler(events.onBorderSystemAdded)); _onBorderSystemRemoved.insertLast(EventHandler(events.onBorderSystemRemoved)); return this; } Events@ opAddAssign(IOutsideBorderSystemEvents& events) { _onOutsideBorderSystemAdded.insertLast(EventHandler(events.onOutsideBorderSystemAdded)); _onOutsideBorderSystemRemoved.insertLast(EventHandler(events.onOutsideBorderSystemRemoved)); return this; } Events@ opAddAssign(IPlanetEvents& events) { _onPlanetAdded.insertLast(EventHandler(events.onPlanetAdded)); _onPlanetRemoved.insertLast(EventHandler(events.onPlanetRemoved)); return this; } Events@ opAddAssign(ITradeRouteEvents& events) { _onTradeRouteNeeded.insertLast(EventHandler(events.onTradeRouteNeeded)); return this; } Events@ opAddAssign(IOrbitalRequestEvents& events) { _onOrbitalRequested.insertLast(EventHandler(events.onOrbitalRequested)); return this; } //Event notifications private void raiseEvent(array<EventHandler@>& subscribed, ref@ sender, EventArgs& args) { for (uint i = 0, cnt = subscribed.length; i < cnt; ++i) subscribed[i](sender, args); } void notifyOwnedSystemAdded(ref@ sender, EventArgs& args) { raiseEvent(_onOwnedSystemAdded, sender, args); } void notifyOwnedSystemRemoved(ref@ sender, EventArgs& args) { raiseEvent(_onOwnedSystemRemoved, sender, args); } void notifyBorderSystemAdded(ref@ sender, EventArgs& args) { raiseEvent(_onBorderSystemAdded, sender, args); } void notifyBorderSystemRemoved(ref@ sender, EventArgs& args) { raiseEvent(_onBorderSystemRemoved, sender, args); } void notifyOutsideBorderSystemAdded(ref@ sender, EventArgs& args) { raiseEvent(_onOutsideBorderSystemAdded, sender, args); } void notifyOutsideBorderSystemRemoved(ref@ sender, EventArgs& args) { raiseEvent(_onOutsideBorderSystemRemoved, sender, args); } void notifyPlanetAdded(ref@ sender, EventArgs& args) { raiseEvent(_onPlanetAdded, sender, args); } void notifyPlanetRemoved(ref@ sender, EventArgs& args) { raiseEvent(_onPlanetRemoved, sender, args); } void notifyTradeRouteNeeded(ref@ sender, EventArgs& args) { raiseEvent(_onTradeRouteNeeded, sender, args); } void notifyOrbitalRequested(ref@ sender, EventArgs& args) { raiseEvent(_onOrbitalRequested, sender, args); } void save(SaveFile& file) { } void load(SaveFile& file) { } }; AIComponent@ createEvents() { return Events(); } |
Added scripts/server/empire_ai/weasel/Fleets.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
// Fleets // ------ // Manages data about fleets and missions, as well as making sure fleets // return to their station after a mission. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Systems; import empire_ai.weasel.Designs; import empire_ai.weasel.Movement; enum FleetClass { FC_Scout, FC_Combat, FC_Slipstream, FC_Mothership, FC_Defense, FC_ALL }; enum MissionPriority { MiP_Background, MiP_Normal, MiP_High, MiP_Critical, } class Mission { int id = -1; bool completed = false; bool canceled = false; uint priority = MiP_Normal; void _save(Fleets& fleets, SaveFile& file) { file << completed; file << canceled; file << priority; save(fleets, file); } void _load(Fleets& fleets, SaveFile& file) { file >> completed; file >> canceled; file >> priority; load(fleets, file); } void save(Fleets& fleets, SaveFile& file) { } void load(Fleets& fleets, SaveFile& file) { } bool get_isActive() { return true; } double getPerformWeight(AI& ai, FleetAI& fleet) { return 1.0; } void start(AI& ai, FleetAI& fleet) { } void cancel(AI& ai, FleetAI& fleet) { } void tick(AI& ai, FleetAI& fleet, double time) { } }; final class FleetAI { uint fleetClass; Object@ obj; Mission@ mission; Region@ stationed; bool stationedFactory = true; double filled = 0.0; double idleSince = 0.0; double fillStaticSince = 0.0; void save(Fleets& fleets, SaveFile& file) { file << fleetClass; file << stationed; file << filled; file << idleSince; file << fillStaticSince; file << stationedFactory; fleets.saveMission(file, mission); } void load(Fleets& fleets, SaveFile& file) { file >> fleetClass; file >> stationed; file >> filled; file >> idleSince; file >> fillStaticSince; file >> stationedFactory; @mission = fleets.loadMission(file); } bool get_isHome() { if(stationed is null) return true; return obj.region is stationed; } bool get_busy() { return mission !is null; } double get_strength() { return obj.getFleetStrength(); } double get_supplies() { Ship@ ship = cast<Ship>(obj); if(ship is null) return 1.0; double maxSupply = ship.MaxSupply; if(maxSupply <= 0) return 1.0; return ship.Supply / maxSupply; } double get_remainingSupplies() { Ship@ ship = cast<Ship>(obj); if(ship is null) return 0.0; return ship.Supply; } double get_radius() { return obj.getFormationRadius(); } double get_fleetHealth() { return obj.getFleetStrength() / obj.getFleetMaxStrength(); } double get_flagshipHealth() { Ship@ ship = cast<Ship>(obj); if(ship is null) return 1.0; return ship.blueprint.currentHP / ship.blueprint.design.totalHP; } bool get_actionableState() { if(isHome && obj.hasOrderedSupports && stationedFactory) return false; if(supplies < 0.75) return false; if(filled < 0.5) return false; if(filled < 1.0 && gameTime < fillStaticSince + 90.0) return false; return true; } bool get_readyForAction() { if(mission !is null) return false; if(isHome && obj.hasOrderedSupports && stationedFactory) return false; if(supplies < 0.75) return false; if(filled < 0.5) return false; if(filled < 1.0 && gameTime < fillStaticSince + 90.0) return false; if(obj.isMoving) { if(obj.velocity.length / obj.maxAcceleration > 16.0) return false; } //DOF - Do not send badly damaged flagships Ship@ flagship = cast<Ship>(obj); auto@ bp = flagship.blueprint; if(bp.currentHP / bp.design.totalHP < 0.75) { return false; } return true; } bool tick(AI& ai, Fleets& fleets, double time) { //Make sure we still exist if(!obj.valid || obj.owner !is ai.empire) { if(mission !is null) { mission.canceled = true; @mission = null; } return false; } //Record data int supUsed = obj.SupplyUsed; int supCap = obj.SupplyCapacity; int supGhost = obj.SupplyGhost; int supOrdered = obj.SupplyOrdered; double newFill = 1.0; if(supCap > 0.0) newFill = double(supUsed - supGhost - supOrdered) / double(supCap); if(newFill != filled) { fillStaticSince = gameTime; filled = newFill; } //Perform our mission if(mission !is null) { if(!mission.completed && !mission.canceled) mission.tick(ai, this, time); if(mission.completed || mission.canceled) { @mission = null; idleSince = gameTime; } } //Return to where we're stationed if we're not doing anything if(mission is null && stationed !is null && fleetClass != FC_Scout) { if(gameTime >= idleSince + ai.behavior.fleetIdleReturnStationedTime) { if(obj.region !is stationed && !obj.hasOrders) { if(fleets.log) ai.print("Returning to station in "+stationed.name, obj); fleets.movement.move(obj, stationed, spread=true); } } } return true; } }; class Fleets : AIComponent { Systems@ systems; Designs@ designs; Movement@ movement; array<FleetAI@> fleets; int nextMissionId = 0; double totalStrength = 0; double totalMaxStrength = 0; void create() { @systems = cast<Systems>(ai.systems); @designs = cast<Designs>(ai.designs); @movement = cast<Movement>(ai.movement); } void save(SaveFile& file) { file << nextMissionId; file << totalStrength; file << totalMaxStrength; uint cnt = fleets.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveAI(file, fleets[i]); fleets[i].save(this, file); } } void load(SaveFile& file) { file >> nextMissionId; file >> totalStrength; file >> totalMaxStrength; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { FleetAI@ flAI = loadAI(file); if(flAI !is null) flAI.load(this, file); else FleetAI().load(this, file); } } void saveAI(SaveFile& file, FleetAI@ flAI) { if(flAI is null) { file.write0(); return; } file.write1(); file << flAI.obj; } FleetAI@ loadAI(SaveFile& file) { if(!file.readBit()) return null; Object@ obj; file >> obj; if(obj is null) return null; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].obj is obj) return fleets[i]; } FleetAI flAI; @flAI.obj = obj; fleets.insertLast(flAI); return flAI; } array<Mission@> savedMissions; array<Mission@> loadedMissions; void postSave(AI& ai) { savedMissions.length = 0; } void postLoad(AI& ai) { loadedMissions.length = 0; } void saveMission(SaveFile& file, Mission@ mission) { if(mission is null) { file.write0(); return; } file.write1(); file << mission.id; if(mission.id == -1) { storeMission(file, mission); } else { bool found = false; for(uint i = 0, cnt = savedMissions.length; i < cnt; ++i) { if(savedMissions[i] is mission) { found = true; break; } } if(!found) { storeMission(file, mission); savedMissions.insertLast(mission); } } } Mission@ loadMission(SaveFile& file) { if(!file.readBit()) return null; int id = 0; file >> id; if(id == -1) { Mission@ miss = createMission(file); miss.id = id; return miss; } else { for(uint i = 0, cnt = loadedMissions.length; i < cnt; ++i) { if(loadedMissions[i].id == id) return loadedMissions[i]; } Mission@ miss = createMission(file); miss.id = id; loadedMissions.insertLast(miss); return miss; } } void storeMission(SaveFile& file, Mission@ mission) { auto@ cls = getClass(mission); auto@ mod = cls.module; file << mod.name; file << cls.name; mission._save(this, file); } Mission@ createMission(SaveFile& file) { string modName; string clsName; file >> modName; file >> clsName; auto@ mod = getScriptModule(modName); if(mod is null) { error("ERROR: AI Load could not find module for mission "+modName+"::"+clsName); return null; } auto@ cls = mod.getClass(clsName); if(cls is null) { error("ERROR: AI Load could not find class for mission "+modName+"::"+clsName); return null; } auto@ miss = cast<Mission>(cls.create()); if(miss is null) { error("ERROR: AI Load could not create class instance for mission "+modName+"::"+clsName); return null; } miss._load(this, file); return miss; } void checkForFleets() { auto@ data = ai.empire.getFlagships(); Object@ obj; while(receive(data, obj)) { if(obj !is null) register(obj); } @data = ai.empire.getStations(); while(receive(data, obj)) { if(obj !is null) register(obj); } } bool haveCombatReadyFleets() { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) continue; return true; } return false; } uint countCombatReadyFleets() { uint count = 0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) continue; count += 1; } return count; } bool allFleetsCombatReady() { bool have = false; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) return false; have = true; } return have; } uint prevFleetCount = 0; double checkTimer = 0; void focusTick(double time) override { //Check for any newly obtained fleets uint curFleetCount = ai.empire.fleetCount; checkTimer += time; if(curFleetCount != prevFleetCount || checkTimer > 60.0) { checkForFleets(); prevFleetCount = curFleetCount; checkTimer = 0; } //Calculate our current strengths totalStrength = 0; totalMaxStrength = 0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { totalStrength += sqrt(fleets[i].obj.getFleetStrength()); totalMaxStrength += sqrt(fleets[i].obj.getFleetMaxStrength()); } totalStrength = sqr(totalStrength); totalMaxStrength = sqr(totalMaxStrength); } double getTotalStrength(uint checkClass, bool idleOnly = false, bool readyOnly = false) { double str = 0.0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if((flAI.fleetClass == checkClass || checkClass == FC_ALL) && (!idleOnly || flAI.mission is null) && (!readyOnly || flAI.readyForAction)) str += sqrt(fleets[i].obj.getFleetStrength()); } return str*str; } void tick(double time) override { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(!fleets[i].tick(ai, this, time)) { fleets.removeAt(i); --i; --cnt; continue; } Region@ reg = fleets[i].obj.region; if(reg !is null) systems.focus(reg); } } MoveOrder@ returnToBase(FleetAI@ fleet, uint priority = MP_Normal) { if(fleet.stationed !is null) return movement.move(fleet.obj, fleet.stationed, priority, spread=true); return null; } FleetAI@ register(Object@ obj) { FleetAI@ flAI = getAI(obj); if(flAI is null) { @flAI = FleetAI(); @flAI.obj = obj; @flAI.stationed = obj.region; obj.setHoldPosition(true); uint designClass = designs.classify(obj); if(designClass == DP_Scout) flAI.fleetClass = FC_Scout; else if(designClass == DP_Slipstream) flAI.fleetClass = FC_Slipstream; else if(designClass == DP_Mothership) flAI.fleetClass = FC_Mothership; else if(designClass == DP_Defense) flAI.fleetClass = FC_Defense; else flAI.fleetClass = FC_Combat; fleets.insertLast(flAI); } return flAI; } void register(Mission@ mission) { if(mission.id == -1) mission.id = nextMissionId++; } FleetAI@ getAI(Object@ obj) { if(obj is null) return null; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].obj is obj) return fleets[i]; } return null; } uint count(uint checkClass) { uint amount = 0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if(flAI.fleetClass == checkClass || checkClass == FC_ALL) amount += 1; } return amount; } bool haveIdle(uint checkClass) { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if((flAI.fleetClass == checkClass || checkClass == FC_ALL) && flAI.mission is null) return true; } return false; } double closestIdleTo(uint checkClass, const vec3d& position) { double closest = INFINITY; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if((flAI.fleetClass != checkClass && checkClass != FC_ALL) || flAI.mission !is null) continue; double d = flAI.obj.position.distanceTo(position); if(d < closest) closest = d; } return closest; } FleetAI@ performMission(Mission@ mission) { FleetAI@ perform; double bestWeight = 0.0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flAI = fleets[i]; if(flAI.mission !is null) continue; double w = mission.getPerformWeight(ai, flAI); if(w > bestWeight) { bestWeight = w; @perform = flAI; } } if(perform !is null) { @perform.mission = mission; register(mission); mission.start(ai, perform); } return perform; } FleetAI@ performMission(FleetAI@ fleet, Mission@ mission) { if(fleet.mission !is null) { fleet.mission.cancel(ai, fleet); fleet.mission.canceled = true; } @fleet.mission = mission; register(mission); mission.start(ai, fleet); return fleet; } }; AIComponent@ createFleets() { return Fleets(); } |
Added scripts/server/empire_ai/weasel/ImportData.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
import resources; import tile_resources; import saving; export ResourceSpecType; export ResourceSpec; export implementSpec; export ImportData; export ExportData; enum ResourceSpecType { RST_Specific, RST_Level_Specific, RST_Level_Minimum, RST_Pressure_Type, RST_Pressure_Level0, RST_Class, }; tidy final class ResourceSpec : Savable { uint type = RST_Specific; const ResourceType@ resource; const ResourceClass@ cls; uint level = 0; uint pressureType = 0; bool isLevelRequirement = false; bool isForImport = true; bool allowUniversal = true; void save(SaveFile& file) { file << type; if(resource !is null) { file.write1(); file.writeIdentifier(SI_Resource, resource.id); } else { file.write0(); } if(cls !is null) { file.write1(); file << cls.ident; } else { file.write0(); } file << level; file << pressureType; file << isLevelRequirement; file << isForImport; file << allowUniversal; } void load(SaveFile& file) { file >> type; if(file.readBit()) @resource = getResource(file.readIdentifier(SI_Resource)); if(file.readBit()) { string clsName; file >> clsName; @cls = getResourceClass(clsName); } file >> level; file >> pressureType; file >> isLevelRequirement; file >> isForImport; file >> allowUniversal; } bool opEquals(const ResourceSpec& other) const { if(type != other.type) return false; if(isLevelRequirement != other.isLevelRequirement) return false; switch(type) { case RST_Specific: return other.resource is resource; case RST_Level_Specific: case RST_Level_Minimum: return other.level == level; case RST_Pressure_Type: case RST_Pressure_Level0: return other.pressureType == pressureType; case RST_Class: return other.cls is cls; } return true; } bool meets(const ResourceType@ check, Object@ fromObj = null, Object@ toObj = null) const { if(check is null) return false; if(allowUniversal && isLevelRequirement) { if(check.mode == RM_UniversalUnique || check.mode == RM_Universal) { //HACK: The AI shouldn't use drugs for food and water switch(type) { case RST_Level_Specific: case RST_Level_Minimum: return level >= 2; } return false; } } if(isForImport && !check.exportable && (fromObj is null || fromObj !is toObj)) return false; if(isLevelRequirement && check.mode == RM_NonRequirement) return false; switch(type) { case RST_Specific: return check is resource; case RST_Level_Specific: return check.level == level; case RST_Level_Minimum: return check.level >= level; case RST_Pressure_Type: return check.tilePressure[pressureType] >= max(check.totalPressure * 0.4, 1.0); case RST_Pressure_Level0: return check.level == 0 && check.tilePressure[pressureType] >= max(check.totalPressure * 0.4, 1.0); case RST_Class: return check.cls is cls; } return false; } bool implements(const ResourceRequirement& req) const { if(!isLevelRequirement) return false; switch(req.type) { case RRT_Resource: return this.type == RST_Specific && this.resource is req.resource; case RRT_Class: case RRT_Class_Types: return this.type == RST_Class && this.cls is req.cls; case RRT_Level: case RRT_Level_Types: return this.type == RST_Level_Specific && this.level == req.level; } return false; } string dump() { switch(type) { case RST_Specific: return resource.name; case RST_Level_Specific: return "Tier "+level; case RST_Level_Minimum: return "Tier "+level+"+"; case RST_Pressure_Type: return "Any "+getTileResourceIdent(pressureType); case RST_Pressure_Level0: return "Level 0 "+getTileResourceIdent(pressureType); case RST_Class: return "Of "+cls.ident; } return "??"; } int get_resourceLevel() const { switch(type) { case RST_Specific: return 0; case RST_Level_Specific: return level; case RST_Level_Minimum: return level; case RST_Pressure_Type: return 0; case RST_Pressure_Level0: return 0; case RST_Class: return 0; } return 0; } int opCmp(const ResourceSpec@ other) const { int level = this.resourceLevel; int otherLevel = other.resourceLevel; if(level > otherLevel) return 1; if(level < otherLevel) return -1; return 0; } }; ResourceSpec@ implementSpec(const ResourceRequirement& req) { ResourceSpec spec; spec.isLevelRequirement = true; switch(req.type) { case RRT_Resource: spec.type = RST_Specific; @spec.resource = req.resource; break; case RRT_Class: case RRT_Class_Types: spec.type = RST_Class; @spec.cls = req.cls; break; case RRT_Level: case RRT_Level_Types: spec.type = RST_Level_Specific; spec.level = req.level; break; } return spec; } tidy final class ImportData : Savable { int id = -1; Object@ obj; ResourceSpec@ spec; const ResourceType@ resource; Object@ fromObject; int resourceId = -1; bool beingMet = false; bool forLevel = false; bool cycled = false; bool isColonizing = false; bool claimedFor = false; double idleSince = 0.0; void save(SaveFile& file) { file << obj; file << spec; if(resource !is null) { file.write1(); file.writeIdentifier(SI_Resource, resource.id); } else { file.write0(); } file << fromObject; file << resourceId; file << beingMet; file << forLevel; file << cycled; file << isColonizing; file << claimedFor; file << idleSince; } void load(SaveFile& file) { file >> obj; @spec = ResourceSpec(); file >> spec; if(file.readBit()) @resource = getResource(file.readIdentifier(SI_Resource)); file >> fromObject; file >> resourceId; file >> beingMet; file >> forLevel; file >> cycled; file >> isColonizing; file >> claimedFor; file >> idleSince; } void set(ExportData@ source) { @fromObject = source.obj; resourceId = source.resourceId; @resource = source.resource; } int opCmp(const ImportData@ other) const { return spec.opCmp(other.spec); } bool get_isOpen() const { return !beingMet; } }; tidy final class ExportData : Savable { int id = -1; Object@ obj; const ResourceType@ resource; int resourceId = -1; ImportData@ request; Object@ developUse; bool localOnly = false; bool get_usable() const { if(obj is null) return false; if(resourceId == obj.primaryResourceId) return obj.primaryResourceUsable; else return obj.getNativeResourceUsableByID(resourceId); } bool get_isPrimary() const { return resourceId == obj.primaryResourceId; } bool isExportedTo(Object@ check) const { if(check is obj) return true; if(resourceId == obj.primaryResourceId) return obj.isPrimaryDestination(check); else return obj.getNativeResourceDestinationByID(obj.owner, resourceId) is check; } void save(SaveFile& file) { //Does not save the request link, this is done by Resources file << obj; if(resource !is null) { file.write1(); file.writeIdentifier(SI_Resource, resource.id); } else { file.write0(); } file << resourceId; file << developUse; file << localOnly; } void load(SaveFile& file) { file >> obj; if(file.readBit()) @resource = getResource(file.readIdentifier(SI_Resource)); file >> resourceId; file >> developUse; file >> localOnly; } }; |
Added scripts/server/empire_ai/weasel/Infrastructure.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 |
// Infrastructure // ------ // Manages building basic structures in newly colonized or weakened systems // to support the Military or Colonization components. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Events; import empire_ai.weasel.Colonization; import empire_ai.weasel.Development; import empire_ai.weasel.Construction; import empire_ai.weasel.Budget; import empire_ai.weasel.Orbitals; import empire_ai.weasel.Systems; import empire_ai.weasel.Planets; import empire_ai.weasel.Resources; import ai.construction; import ai.events; from ai.orbitals import RegisterForTradeUse; from statuses import getStatusID; from traits import getTraitID; enum ResourcePreference { RP_None, RP_FoodWater, RP_Level0, RP_Level1, RP_Level2, RP_Level3, RP_Scalable, }; enum SystemArea { SA_Core, SA_Tradable, }; enum SystemBuildAction { BA_BuildOutpost, }; enum PlanetBuildAction { BA_BuildMoonBase, }; enum SystemBuildLocation { BL_InSystem, BL_AtSystemEdge, BL_AtBestPlanet, }; enum FocusType { FT_None, FT_Outpost, } int moonBaseStatusId = -1; final class OwnedSystemEvents : IOwnedSystemEvents { Infrastructure@ infrastructure; OwnedSystemEvents(Infrastructure& infrastructure) { @this.infrastructure = infrastructure; } void onOwnedSystemAdded(ref& sender, EventArgs& args) { SystemAI@ ai = cast<SystemAI>(sender); if (ai !is null) infrastructure.registerOwnedSystemAdded(ai); } void onOwnedSystemRemoved(ref& sender, EventArgs& args) { SystemAI@ ai = cast<SystemAI>(sender); if (ai !is null) infrastructure.registerOwnedSystemRemoved(ai); } }; final class OutsideBorderSystemEvents : IOutsideBorderSystemEvents { Infrastructure@ infrastructure; OutsideBorderSystemEvents(Infrastructure& infrastructure) { @this.infrastructure = infrastructure; } void onOutsideBorderSystemAdded(ref& sender, EventArgs& args) { SystemAI@ ai = cast<SystemAI>(sender); if (ai !is null) infrastructure.registerOutsideBorderSystemAdded(ai); } void onOutsideBorderSystemRemoved(ref& sender, EventArgs& args) { SystemAI@ ai = cast<SystemAI>(sender); if (ai !is null) infrastructure.registerOutsideBorderSystemRemoved(ai); } }; final class PlanetEvents : IPlanetEvents { Infrastructure@ infrastructure; PlanetEvents(Infrastructure& infrastructure) { @this.infrastructure = infrastructure; } void onPlanetAdded(ref& sender, EventArgs& args) { PlanetAI@ ai = cast<PlanetAI>(sender); if (ai !is null) infrastructure.registerPlanetAdded(ai); } void onPlanetRemoved(ref& sender, EventArgs& args) { PlanetAI@ ai = cast<PlanetAI>(sender); if (ai !is null) infrastructure.registerPlanetRemoved(ai); } }; final class TradeRouteEvents : ITradeRouteEvents { Infrastructure@ infrastructure; TradeRouteEvents(Infrastructure& infrastructure) { @this.infrastructure = infrastructure; } void onTradeRouteNeeded(ref& sender, EventArgs& args) { TradeRouteNeededEventArgs@ specs = cast<TradeRouteNeededEventArgs>(args); if (specs !is null) infrastructure.establishTradeRoute(specs.territoryA, specs.territoryB); } } final class OrbitalRequestEvents : IOrbitalRequestEvents { Infrastructure@ infrastructure; OrbitalRequestEvents(Infrastructure& infrastructure) { @this.infrastructure = infrastructure; } void onOrbitalRequested(ref& sender, EventArgs& args) { OrbitalRequestedEventArgs@ specs = cast<OrbitalRequestedEventArgs>(args); if (specs !is null) infrastructure.requestOrbital(specs.region, specs.module, specs.priority, specs.expires, specs.moneyType); } } final class SystemOrder { private IConstruction@ _construction; double expires = INFINITY; SystemOrder() {} SystemOrder(IConstruction@ construction) { @_construction = (construction); } bool get_isValid() const { return _construction !is null; } bool get_isInProgress() const { return _construction.started; } bool get_isComplete() const { return _construction.completed; } IConstruction@ get_info() const { return _construction; } void save(Infrastructure& infrastructure, SaveFile& file) { file << _construction.id; file << expires; } void load(Infrastructure& infrastructure, SaveFile& file) { int id = - 1; file >> id; if (id != -1) { for (uint i = 0, cnt = infrastructure.construction.allocations.length; i < cnt; ++i) { if (infrastructure.construction.allocations[i].id == id) { @_construction = infrastructure.construction.allocations[i]; } } } file >> expires; } }; final class PlanetOrder { private IConstruction@ _construction; double expires = INFINITY; PlanetOrder() {} PlanetOrder(IConstruction@ construction) { @_construction = construction; } bool get_isValid() const { return _construction !is null; } bool get_isInProgress() const { return _construction.started; } bool get_isComplete() const { return _construction.completed; } IConstruction@ get_info() const { return _construction; } void save(Infrastructure& infrastructure, SaveFile& file) { file << _construction.id; file << expires; } void load(Infrastructure& infrastructure, SaveFile& file) { int id = - 1; file >> id; if (id != -1) { for (uint i = 0, cnt = infrastructure.planets.constructionRequests.length; i < cnt; ++i) { if (infrastructure.planets.constructionRequests[i].id == id) { @_construction = infrastructure.planets.constructionRequests[i]; } } } file >> expires; } }; abstract class NextAction { double priority = 1.0; bool force = false; bool critical = false; }; final class SystemAction : NextAction { private SystemCheck@ _sys; private SystemBuildAction _action; private SystemBuildLocation _loc; SystemAction(SystemCheck& sys, SystemBuildAction action, SystemBuildLocation loc) { @_sys = sys; _action = action; _loc = loc; } SystemCheck@ get_sys() const { return _sys; } SystemBuildAction get_action() const { return _action; } SystemBuildLocation get_loc() const { return _loc; } }; final class PlanetAction : NextAction { private PlanetCheck@ _pl; private PlanetBuildAction _action; PlanetAction(PlanetCheck& pl, PlanetBuildAction action) { @_pl = pl; _action = action; } PlanetCheck@ get_pl() const { return _pl; } PlanetBuildAction get_action() const { return _action; } }; abstract class Check { protected double _checkInTime = 0.0; Check() { _checkInTime = gameTime; } double get_checkInTime() const { return _checkInTime; } } namespace SystemCheck { array<SystemOrder@> allOrders; } final class SystemCheck : Check { SystemAI@ ai; array<SystemOrder@> orders; private double _weight = 0.0; private bool _isUnderAttack = false; SystemCheck() {} SystemCheck(Infrastructure& infrastructure, SystemAI& ai) { super(); @this.ai = ai; } double get_weight() const { return _weight; } bool get_isUnderAttack() const { return _isUnderAttack; } bool get_isBuilding() const { return orders.length > 0; } void save(Infrastructure& infrastructure, SaveFile& file) { infrastructure.systems.saveAI(file, ai); uint cnt = orders.length; file << cnt; for(uint i = 0; i < cnt; ++i) orders[i].save(infrastructure, file); file << _checkInTime; file << _weight; file << _isUnderAttack; } void load(Infrastructure& infrastructure, SaveFile& file) { @ai = infrastructure.systems.loadAI(file); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ order = SystemOrder(); order.load(infrastructure, file); if (order.isValid) addOrder(order); } file >> _checkInTime; file >> _weight; file >> _isUnderAttack; } void tick(AI& ai, Infrastructure& infrastructure, double time) { OrbitalAI@ orbital; //Update hostile status _isUnderAttack = this.ai.obj.ContestedMask & ai.mask != 0; //Cancel all orders if attacked /*if (isUnderAttack && isBuilding) { for (uint i = 0, cnt = orders.length; i < cnt; ++i) { auto@ order = orders[i]; //SoI - TODO: Cancel not fully implemented, see Construction.as infrastructure.construction.cancel(order.info); removeOrder(order); --i; --cnt; } }*/ if (isBuilding) { for (uint i = 0, cnt = orders.length; i < cnt; ++i) { auto@ order = orders[i]; if (!order.isValid) { removeOrder(order); --i; --cnt; } else if (order.isComplete) { if (infrastructure.log) ai.print("order complete"); removeOrder(order); --i; --cnt; } else if (!order.isInProgress && order.expires < gameTime) { if (infrastructure.log) ai.print("order expired, gameTime = " + gameTime); removeOrder(order); --i; --cnt; } } } } void focusTick(AI& ai, Infrastructure& infrastructure, double time) { } double check(AI& ai) { _weight = 0.0; //Systems under attack are bottom priority for now if (isUnderAttack) return weight; //Hostile systems are bottom priority until cleared if (this.ai.seenPresent & ai.enemyMask != 0) return weight; //Start weighting double sysWeight = 1.0; //Oldest systems come first sysWeight /= (checkInTime + 60.0) / 60.0; //The home system is a priority if (this.ai.obj is ai.empire.HomeSystem) sysWeight *= 2.0; _weight = 1.0 * sysWeight; return weight; } SystemOrder@ buildInSystem(Infrastructure& infrastructure, const OrbitalModule@ module, double priority = 1.0, bool force = false, double delay = 600.0, uint moneyType = BT_Infrastructure) { vec3d pos = ai.obj.position; vec2d offset = random2d(ai.obj.radius * 0.4, ai.obj.radius * 0.7); pos.x += offset.x; pos.z += offset.y; BuildOrbital@ orbital = infrastructure.construction.buildOrbital(module, pos, priority, force, moneyType); auto@ order = SystemOrder(orbital); order.expires = gameTime + delay; addOrder(order); return order; } SystemOrder@ buildAtSystemEdge(Infrastructure& infrastructure, const OrbitalModule@ module, double priority = 1.0, bool force = false, double delay = 600.0, uint moneyType = BT_Infrastructure) { vec3d pos = ai.obj.position; vec2d offset = random2d(ai.obj.radius * 0.8, ai.obj.radius * 0.9); pos.x += offset.x; pos.z += offset.y; BuildOrbital@ orbital = infrastructure.construction.buildOrbital(module, pos, priority, force, moneyType); auto@ order = SystemOrder(orbital); order.expires = gameTime + delay; addOrder(order); return order; } SystemOrder@ buildAtPlanet(Infrastructure& infrastructure, Planet& planet, const OrbitalModule@ module, double priority = 1.0, bool force = false, double delay = 600.0, uint moneyType = BT_Infrastructure) { BuildOrbital@ orbital = infrastructure.construction.buildLocalOrbital(module, planet, priority, force, moneyType); auto@ order = SystemOrder(orbital); order.expires = gameTime + delay; addOrder(order); return order; } void addOrder(SystemOrder@ order) { orders.insertLast(order); SystemCheck::allOrders.insertLast(order); } void removeOrder(SystemOrder@ order) { orders.remove(order); SystemCheck::allOrders.remove(order); @order = null; } }; namespace PlanetCheck { array<PlanetOrder@> allOrders; } final class PlanetCheck : Check { PlanetAI@ ai; array<PlanetOrder@> orders; private double _weight = 0.0; private bool _isSystemUnderAttack = false; PlanetCheck() {} PlanetCheck(Infrastructure& infrastructure, PlanetAI& ai) { super(); @this.ai = ai; } double get_weight() const { return _weight; } bool get_isSystemUnderAttack() const { return _isSystemUnderAttack; } bool get_isBuilding() const { return orders.length > 0; } void save(Infrastructure& infrastructure, SaveFile& file) { infrastructure.planets.saveAI(file, ai); uint cnt = orders.length; file << cnt; for(uint i = 0; i < cnt; ++i) orders[i].save(infrastructure, file); file << _checkInTime; file << _weight; file << _isSystemUnderAttack; } void load(Infrastructure& infrastructure, SaveFile& file) { @ai = infrastructure.planets.loadAI(file); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ order = PlanetOrder(); order.load(infrastructure, file); if (order.isValid) addOrder(order); } file >> _checkInTime; file >> _weight; file >> _isSystemUnderAttack; } void tick(AI& ai, Infrastructure& infrastructure, double time) { auto@ sysAI = infrastructure.systems.getAI(this.ai.obj.region); if (sysAI !is null) _isSystemUnderAttack = sysAI.obj.ContestedMask & ai.mask != 0; if (isBuilding) { for (uint i = 0, cnt = orders.length; i < cnt; ++i) { auto@ order = orders[i]; if (!order.isValid) { removeOrder(order); --i; --cnt; } else if (order.isComplete) { if (infrastructure.log) ai.print("planet order complete"); removeOrder(order); --i; --cnt; } else if (!order.isInProgress && order.expires < gameTime) { if (infrastructure.log) ai.print("planet order expired, gameTime = " + gameTime); removeOrder(order); --i; --cnt; } } } } void focusTick(AI& ai, Infrastructure& infrastructure, double time) { } double check(AI& ai) { _weight = 0.0; //Planets in systems under attack are bottom priority for now if (isSystemUnderAttack) return _weight; //Start weighting double plWeight = 1.0; //Oldest planets come first plWeight /= (checkInTime + 60.0) / 60.0; //The homeworld is a priority if (this.ai.obj is ai.empire.Homeworld) plWeight *= 2.0; _weight = 1.0 * plWeight; return _weight; } PlanetOrder@ build(Infrastructure& infrastructure, const ConstructionType@ consType, double priority = 1.0, bool force = false, bool critical = false, double delay = 600.0, uint moneyType = BT_Infrastructure) { ConstructionRequest@ request = infrastructure.planets.requestConstruction(ai, ai.obj, consType, priority, gameTime + delay, moneyType); auto@ order = PlanetOrder(request); order.expires = gameTime + delay; addOrder(order); return order; } void addOrder(PlanetOrder@ order) { orders.insertLast(order); PlanetCheck::allOrders.insertLast(order); } void removeOrder(PlanetOrder@ order) { orders.remove(order); PlanetCheck::allOrders.remove(order); @order = null; } }; final class TradeRoute { private Territory@ _territoryA; private Territory@ _territoryB; private Region@ _endpointA; private Region@ _endpointB; private SystemOrder@ _orderA; private SystemOrder@ _orderB; private bool _isEstablishing; private bool _isWaitingForLabor; private double _delay; private double _sleep; TradeRoute() {} TradeRoute(Territory& territoryA, Territory& territoryB) { @_territoryA = territoryA; @_territoryB = territoryB; _isEstablishing = false; _isWaitingForLabor = false; _delay = 0.0; _sleep = 0.0; } Territory@ get_territoryA() const { return _territoryA; } Territory@ get_territoryB() const { return _territoryB; } Region@ get_endpointA() const { return _endpointA; } Region@ get_endpointB() const { return _endpointB; } SystemOrder@ get_orderA() const { return _orderA; } SystemOrder@ get_orderB() const { return _orderB; } bool get_isEstablishing() const { return _isEstablishing; } bool get_isWaitingForLabor() const { return _isWaitingForLabor; } void save(Infrastructure& infrastructure, SaveFile& file) { } void load(Infrastructure& infrastructure, SaveFile& file) { } void tick(AI& ai, Infrastructure& infrastructure, double time) { if (_delay > 0.0 && _delay < gameTime) { _isWaitingForLabor = false; _delay = 0.0; } } void focusTick(AI& ai, Infrastructure& infrastructure, double time) { } bool canEstablish(Infrastructure& infrastructure, bool&out buildAtA, bool&out canBuildAtA, bool&out buildAtB, bool&out canBuildAtB) { //We're still sleeping if (_sleep > gameTime) return false; //At least one building order is still pending if (orderA !is null || orderB !is null) return false; buildAtA = true; buildAtB = true; canBuildAtA = false; canBuildAtB = false; for (uint i = 0, cnt = infrastructure.checkedPlanets.length; i < cnt; ++i) { Planet@ pl = infrastructure.checkedPlanets[i].ai.obj; if (pl.region !is null) { Territory@ t = pl.region.getTerritory(infrastructure.ai.empire); if (t is territoryA) { //Is there a global trade node here already if (pl.region.GateMask & ~pl.owner.mask != 0) { buildAtA = false; @_endpointA = pl.region; } if (!canBuildAtA) { //Is there a labor source in this territory if (pl.laborIncome > 0 && pl.canBuildOrbitals) canBuildAtA = true; } } else if (t is territoryB) { //Is there a global trade node here already if (pl.region.GateMask & ~pl.owner.mask != 0) { buildAtB = false; @_endpointB = pl.region; } if (!canBuildAtB) { //Is there a labor source in this territory if (pl.laborIncome > 0 && pl.canBuildOrbitals) canBuildAtB = true; } } if (!buildAtA && !buildAtB) { //Should not normally happen, except if trade if somehow disrupted despite global trade nodes return false; } if (canBuildAtA && canBuildAtB) { _isWaitingForLabor = false; return true; } } } //These checks are expensive and don't need to be run frequently, so let's sleep for some time _sleep = gameTime + 10.0; return false; } void establish(Infrastructure& infrastructure, Region@ regionA, Region@ regionB) { SystemOrder@ orderA = null; SystemOrder@ orderB = null; if (regionA !is null) { @orderA = infrastructure.requestOrbital(regionA, infrastructure.ai.defs.TradeStation); @_endpointA = regionA; } if (regionB !is null) { @orderB = infrastructure.requestOrbital(regionB, infrastructure.ai.defs.TradeStation); @_endpointB = regionB; } if (orderA is null || orderB is null) { infrastructure.ai.print("ERROR: could not establish trade route between " + regionA.name + " and " + regionB.name); return; } @_orderA = orderA; @_orderB = orderB; _isEstablishing = true; } void waitForLabor(double expires) { _isWaitingForLabor = true; _delay = gameTime + expires; } } final class Infrastructure : AIComponent { const ResourceClass@ foodClass, waterClass, scalableClass; //Current focus private uint _focus = FT_None; Events@ events; Colonization@ colonization; Development@ development; Construction@ construction; Orbitals@ orbitals; Planets@ planets; Systems@ systems; Budget@ budget; Resources@ resources; array<SystemCheck@> checkedOwnedSystems; //Includes border systems array<SystemCheck@> checkedOutsideSystems; array<PlanetCheck@> checkedPlanets; array<TradeRoute@> pendingRoutes; SystemCheck@ homeSystem; NextAction@ nextAction; //Unlock tracking bool canBuildGate = false; bool canBuildMoonBase = true; void create() { @events = cast<Events>(ai.events); @colonization = cast<Colonization>(ai.colonization); @development = cast<Development>(ai.development); @construction = cast<Construction>(ai.construction); @orbitals = cast<Orbitals>(ai.orbitals); @planets = cast<Planets>(ai.planets); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); @resources = cast<Resources>(ai.resources); //Cache expensive lookups @foodClass = getResourceClass("Food"); @waterClass = getResourceClass("WaterType"); @scalableClass = getResourceClass("Scalable"); moonBaseStatusId = getStatusID("MoonBase"); events += OwnedSystemEvents(this); events += OutsideBorderSystemEvents(this); events += PlanetEvents(this); events += TradeRouteEvents(this); if (ai.empire.hasTrait(getTraitID("Gate"))) canBuildGate = true; if (ai.empire.hasTrait(getTraitID("StarChildren"))) canBuildMoonBase = false; } void save(SaveFile& file) { file << _focus; uint cnt = checkedOwnedSystems.length; file << cnt; for(uint i = 0; i < cnt; ++i) checkedOwnedSystems[i].save(this, file); cnt = checkedOutsideSystems.length; file << cnt; for(uint i = 0; i < cnt; ++i) checkedOutsideSystems[i].save(this, file); cnt = checkedPlanets.length; file << cnt; for(uint i = 0; i < cnt; ++i) checkedPlanets[i].save(this, file); } void load(SaveFile& file) { file >> _focus; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { SystemCheck@ sys = SystemCheck(); sys.load(this, file); checkedOwnedSystems.insertLast(sys); } cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { SystemCheck@ sys = SystemCheck(); sys.load(this, file); checkedOutsideSystems.insertLast(sys); } cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { PlanetCheck@ pl = PlanetCheck(); pl.load(this, file); checkedPlanets.insertLast(pl); } } void start() { } void turn() { if(log) { ai.print("=============="); ai.print("Current owned systems checked: " + checkedOwnedSystems.length); for (uint i = 0, cnt = checkedOwnedSystems.length; i < cnt; ++i) ai.print(checkedOwnedSystems[i].ai.obj.name); ai.print("=============="); ai.print("Current outside border systems checked: " + checkedOutsideSystems.length); for (uint i = 0, cnt = checkedOutsideSystems.length; i < cnt; ++i) ai.print(checkedOutsideSystems[i].ai.obj.name); ai.print("=============="); ai.print("Current owned planets checked: " + checkedPlanets.length); for (uint i = 0, cnt = checkedPlanets.length; i < cnt; ++i) ai.print(checkedPlanets[i].ai.obj.name); ai.print("=============="); } //Reset any focus _focus = FT_None; //If colonization is somehow blocked, force territory expansion by focusing on building outposts if (colonization.needsMoreTerritory){ if (budget.canFocus()) { budget.focus(BT_Infrastructure); _focus = FT_Outpost; } } } void tick(double time) override { SystemCheck@ sys; PlanetCheck@ pl; TradeRoute@ route; //Perform routine duties for (uint i = 0, cnt = checkedOwnedSystems.length; i < cnt; ++i) { @sys = checkedOwnedSystems[i]; sys.tick(ai, this, time); } for (uint i = 0, cnt = checkedOutsideSystems.length; i < cnt; ++i) { @sys = checkedOutsideSystems[i]; sys.tick(ai, this, time); } for (uint i = 0, cnt = checkedPlanets.length; i < cnt; ++i) { @pl = checkedPlanets[i]; pl.tick(ai, this, time); } for (uint i = 0, cnt = pendingRoutes.length; i < cnt; ++i) { @route = pendingRoutes[i]; route.tick(ai, this, time); } } void focusTick(double time) override { SystemCheck@ sys; PlanetCheck@ pl; SystemBuildLocation loc; bool critical = false; double w; double bestWeight = 0.0; if(ai.behavior.forbidConstruction) return; //Check if owned systems need anything for (uint i = 0, cnt = checkedOwnedSystems.length; i < cnt; ++i) { @sys = checkedOwnedSystems[i]; //Only consider anything if no critical action is underway if (!critical) { //Evaluate current weight w = sys.check(ai); if (w > bestWeight) { if (_focus == FT_None || _focus == FT_Outpost) { //Check if an outpost is needed if (shouldHaveOutpost(sys, SA_Core, loc)) { @nextAction = SystemAction(sys, BA_BuildOutpost, loc); bestWeight = w; if (log) ai.print("outpost considered for owned system with weight: " + w, sys.ai.obj); } } } } //Perform routine duties sys.focusTick(ai, this, time); } //Check if systems in tradable area need anything for (uint i = 0, cnt = checkedOutsideSystems.length; i < cnt; ++i) { @sys = checkedOutsideSystems[i]; //Skip unexplored systems if (sys.ai.explored) { //Only consider anything if no critical action is underway if (!critical) { //Evaluate current weight w = sys.check(ai); if (w > bestWeight) { if (_focus == FT_None || _focus == FT_Outpost) { //Check if an outpost is needed if (shouldHaveOutpost(sys, SA_Tradable, loc)) { @nextAction = SystemAction(sys, BA_BuildOutpost, loc); bestWeight = w; if (log) ai.print("outpost considered for outside system with weight: " + w, sys.ai.obj); } } } } } //Perform routine duties sys.focusTick(ai, this, time); } //Check if owned planets need anything for (uint i = 0, cnt = checkedPlanets.length; i < cnt; ++i) { @pl = checkedPlanets[i]; //Only consider anything if no critical action is underway if (!critical) { //Planets are their own 'factory' and can only build one construction at a time if (!pl.isBuilding) { //Evaluate current weight w = pl.check(ai); if (w > bestWeight) { //Check if a moon base is needed if (canBuildMoonBase && shouldHaveMoonBase(pl)) { @nextAction = PlanetAction(pl, BA_BuildMoonBase); bestWeight = w; if (log) ai.print("moon base considered with weight: " + w, pl.ai.obj); } } } } //Perform routine duties pl.focusTick(ai, this, time); } //Execute our next action if there is one if (nextAction !is null) { Object@ obj; auto@ next = cast<SystemAction>(nextAction); if (next !is null) { @sys = next.sys; switch (next.action) { case BA_BuildOutpost: switch (next.loc) { case BL_InSystem: sys.buildInSystem(this, ai.defs.TradeOutpost, next.priority, next.force); break; case BL_AtSystemEdge: sys.buildAtSystemEdge(this, ai.defs.TradeOutpost, next.priority, next.force); break; case BL_AtBestPlanet: @obj = getBestPlanet(sys); if (obj !is null) { sys.buildAtPlanet(this, cast<Planet>(obj), ai.defs.TradeOutpost, next.priority, next.force); } break; default: ai.print("ERROR: undefined infrastructure building location for outpost"); } if (log) ai.print("outpost ordered", sys.ai.obj); break; default: ai.print("ERROR: undefined infrastructure building action for system"); } } else { auto@ next = cast<PlanetAction>(nextAction); if (next !is null) { @pl = next.pl; switch (next.action) { case BA_BuildMoonBase: pl.build(this, ai.defs.MoonBase, next.priority, next.force, next.critical); if (log) ai.print("moon base ordered", pl.ai.obj); break; default: ai.print("ERROR: undefined infrastructure building action for planet"); } } } @nextAction = null; } //Manage any pending trading routes TradeRoute@ route; for (uint i = 0, cnt = pendingRoutes.length; i < cnt; ++i) { @route = pendingRoutes[i]; if (route.territoryA is null || route.territoryB is null) { pendingRoutes.remove(route); --i; --cnt; if (log) ai.print("invalid territory for pending route, route canceled"); } bool buildAtA = true; bool canBuildAtA = false; bool buildAtB = true; bool canBuildAtB = false; if (route.canEstablish(this, buildAtA, canBuildAtA, buildAtB, canBuildAtB)) { Region@ regionA = null; Region@ regionB = null; if (buildAtA) @regionA = getRouteEndpoint(route.territoryA); if (buildAtB) @regionB = getRouteEndpoint(route.territoryB); if (regionA !is null && regionB !is null) { route.establish(this, regionA, regionB); if (log) ai.print("trade route establishing between " + regionA.name + " and " + regionB.name); } } else if (!route.isEstablishing && !route.isWaitingForLabor) { Region@ regionA = null; Region@ regionB = null; double expires = 0.0; if (!canBuildAtA) @regionA = getLaborAt(route.territoryA, expires); if (!canBuildAtB) @regionB = getLaborAt(route.territoryB, expires); route.waitForLabor(expires); if (log) { string location = ""; if (!canBuildAtA && regionA !is null) location += " " + regionA.name; if (!canBuildAtB && regionB !is null) { if (location != "") location += ", "; location += " " + regionB.name; } if (location == "") ai.print("trade route unable to get labor"); else ai.print("trade route waiting for labor at:" + location); } } if (route.endpointA !is null && route.endpointB !is null && resources.canTradeBetween(route.endpointA, route.endpointB)) { pendingRoutes.remove(route); --i; --cnt; if (log) ai.print("trade route established between " + addrstr(route.territoryA) + " and " + addrstr(route.territoryB)); } //Perform routine duties route.focusTick(ai, this, time); } } void registerOwnedSystemAdded(SystemAI& sysAI) { auto@ sys = SystemCheck(this, sysAI); checkedOwnedSystems.insertLast(sys); if (log) ai.print("adding owned system: " + sysAI.obj.name); } void registerOwnedSystemRemoved(SystemAI& sysAI) { for (uint i = 0, cnt = checkedOwnedSystems.length; i < cnt; ++i) { if (sysAI is checkedOwnedSystems[i].ai) { checkedOwnedSystems.removeAt(i); break; } } if (log) ai.print("removing owned system: " + sysAI.obj.name); } void registerOutsideBorderSystemAdded(SystemAI& sysAI) { auto@ sys = SystemCheck(this, sysAI); checkedOutsideSystems.insertLast(sys); if (log) ai.print("adding outside system: " + sysAI.obj.name); } void registerOutsideBorderSystemRemoved(SystemAI& sysAI) { for (uint i = 0, cnt = checkedOutsideSystems.length; i < cnt; ++i) { if (sysAI is checkedOutsideSystems[i].ai) { checkedOutsideSystems.removeAt(i); break; } } if (log) ai.print("removing outside system: " + sysAI.obj.name); } void registerPlanetAdded(PlanetAI& plAI) { auto@ pl = PlanetCheck(this, plAI); checkedPlanets.insertLast(pl); if (log) ai.print("adding planet: " + plAI.obj.name); } void registerPlanetRemoved(PlanetAI& plAI) { for (uint i = 0, cnt = checkedPlanets.length; i < cnt; ++i) { if (plAI is checkedPlanets[i].ai) { checkedPlanets.removeAt(i); break; } } if (log) ai.print("removing planet: " + plAI.obj.name); } void establishTradeRoute(Territory@ territoryA, Territory@ territoryB) { if (canBuildGate) return; if (hasPendingTradeRoute(territoryA, territoryB)) { if (log) ai.print("pending route detected between " + addrstr(territoryA) + " and " + addrstr(territoryB) + ", establishment canceled"); return; } if (territoryA is null || territoryB is null) { if (log) ai.print("invalid territory for pending route, establishment canceled"); return; } pendingRoutes.insertLast(TradeRoute(territoryA, territoryB)); if (log) ai.print("establishing trade route between " + addrstr(territoryA) + " and " + addrstr(territoryB)); } SystemOrder@ requestOrbital(Region@ region, const OrbitalModule@ module, double priority = 1.0, double expires = INFINITY, uint moneyType = BT_Infrastructure) { SystemAI@ sysAI = systems.getAI(region); if (sysAI !is null) { for (uint i = 0, cnt = checkedOwnedSystems.length; i < cnt; ++i) { if (sysAI is checkedOwnedSystems[i].ai) return checkedOwnedSystems[i].buildInSystem(this, module, priority, false, expires, moneyType); } ai.print("ERROR: requestOrbital: owned system not found: " + region.name); return null; } return null; } bool shouldHaveOutpost(SystemCheck& sys, SystemArea area, SystemBuildLocation&out loc) const { loc = BL_InSystem; uint presentMask = sys.ai.seenPresent; //Make sure we did not previously built an outpost here if (orbitals.haveInSystem(ai.defs.TradeOutpost, sys.ai.obj)) return false; //Make sure we are not already building an outpost here if (isBuilding(sys, ai.defs.TradeOutpost)) return false; //Hostile systems should be ignored until cleared if (presentMask & ai.enemyMask != 0) return false; //Inhabited systems should be ignored if we're not aggressively expanding if(!ai.behavior.colonizeNeutralOwnedSystems && (presentMask & ai.neutralMask) != 0) return false; if(!ai.behavior.colonizeAllySystems && (presentMask & ai.allyMask) != 0) return false; else { Planet@ planet; ResourceType@ type; switch(area) { //Owned systems should have an outpost case SA_Core: if (sys.ai.planets.length > 0) loc = BL_AtBestPlanet; return true; //Outside systems might have an outpost if they are of some interest case SA_Tradable: @planet = getBestPlanet(sys, type); if (planet is null) break; loc = BL_AtBestPlanet; //The best planet is barren, the system needs an outpost to allow expansion if (int(planet.primaryResourceType) == -1) return true; //The best planet has either a scalable or level 3 or 2 resource, the system should have an outpost to dissuade other empires from colonizing it if (type !is null && (type.cls is scalableClass || type.level == 3 || type.level == 2)) return true; return false; default: return false; } } return false; } bool shouldHaveMoonBase(PlanetCheck& pl) const { if (pl.ai.obj.moonCount == 0) return false; //If the planet is at least level 2 and short on empty developed tiles, it should have a moon base else if (pl.ai.obj.resourceLevel > 1 && pl.ai.obj.emptyDevelopedTiles < 9) return true; return false; } Region@ getRouteEndpoint(Territory@ territory) { const OrbitalModule@ module = ai.defs.TradeStation; Region@ region = null; for (uint i = 0, cnt = module.ai.length; i < cnt; ++i) { auto@ hook = cast<RegisterForTradeUse>(module.ai[i]); if (hook !is null) { Object@ obj = hook.considerBuild(orbitals, module, territory); if (obj !is null) { @region = cast<Region>(obj); break; } } } return region; } Region@ getLaborAt(Territory@ territory, double&out expires) { expires = 600.0; if (territory is null) { if (log) ai.print("invalid territory to get labor at"); return null; } //SoI - TODO: Handle more complex cases //Fallback solution: build a labor generation building Planet@ pl = development.getLaborAt(territory, expires); if (pl !is null) return pl.region; return null; } bool isBuilding(const OrbitalModule@ module) { for (uint i = 0, cnt = SystemCheck::allOrders.length; i < cnt; ++i) { auto@ orbital = cast<IOrbitalConstruction>(SystemCheck::allOrders[i].info); if (orbital !is null) { if (orbital.module is module) return true; } } return false; } bool isBuilding(SystemCheck& sys, const OrbitalModule@ module) { for (uint i = 0, cnt = sys.orders.length; i < cnt; ++i) { auto@ orbital = cast<IOrbitalConstruction>(sys.orders[i].info); if (orbital !is null) { if (orbital.module is module) return true; } } return false; } bool isBuilding(const ConstructionType@ consType) { for (uint i = 0, cnt = PlanetCheck::allOrders.length; i < cnt; ++i) { auto@ generic = cast<IGenericConstruction>(PlanetCheck::allOrders[i].info); if (generic !is null) { if (generic.construction is consType) return true; } } return false; } bool isBuilding(PlanetCheck& pl, const ConstructionType@ consType) { for (uint i = 0, cnt = pl.orders.length; i < cnt; ++i) { auto@ generic = cast<IGenericConstruction>(pl.orders[i].info); if (generic !is null) { if (generic.construction is consType) return true; } } return false; } bool hasPendingTradeRoute(Territory@ territoryA, Territory@ territoryB) { for (uint i = 0, cnt = pendingRoutes.length; i < cnt; ++i) { if (pendingRoutes[i].territoryA is territoryA && pendingRoutes[i].territoryB is territoryB) return true; } return false; } Planet@ getBestPlanet(SystemCheck sys) { ResourceType@ type; return getBestPlanet(sys, type); } Planet@ getBestPlanet(SystemCheck sys, const ResourceType@ resourceType) { Planet@ bestPlanet, planet; ResourcePreference bestResource = RP_None; if (sys.ai.obj is ai.empire.HomeSystem) { //The homeworld if there is one @planet = ai.empire.Homeworld; if (planet !is null) return planet; } for (uint i = 0, cnt = sys.ai.planets.length; i < cnt; ++i) { @planet = sys.ai.planets[i]; int resId = planet.primaryResourceType; if (resId == -1) continue; const ResourceType@ type = getResource(resId); //The first scalable resource if (type.cls is scalableClass) { @resourceType = type; return planet; } //The first level 3 resource if (type.level == 3) { bestResource = RP_Level3; @resourceType = type; @bestPlanet = planet; } //The first level 2 resource else if (type.level == 2 && RP_Level2 > bestResource) { bestResource = RP_Level2; @resourceType = type; @bestPlanet = planet; } //The first level 1 resource else if (type.level == 1 && RP_Level1 > bestResource) { bestResource = RP_Level1; @resourceType = type; @bestPlanet = planet; } //The first level 0 resource except food and water else if (type.level == 0 && type.cls !is foodClass && type.cls !is waterClass && RP_Level0 > bestResource) { bestResource = RP_Level0; @resourceType = type; @bestPlanet = planet; } //The first food or water resource else if ((type.cls is foodClass || type.cls is waterClass) && RP_Level0 > bestResource) { bestResource = RP_FoodWater; @resourceType = type; @bestPlanet = planet; } else if (i == sys.ai.planets.length - 1 && bestPlanet is null) { @resourceType = type; @bestPlanet = planet; } } if (bestPlanet is null) return planet; return bestPlanet; } }; AIComponent@ createInfrastructure() { return Infrastructure(); } |
Added scripts/server/empire_ai/weasel/Intelligence.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
// Intelligence // ------------ // Keeps track of the existence and movement of enemy fleets and other assets. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import regions.regions; final class FleetIntel { Object@ obj; bool known = false; bool visible = false; double lastSeen = 0; double seenStrength = 0; double predictStrength = 0; vec3d seenPosition; Region@ seenRegion; vec3d seenDestination; Region@ seenTarget; void save(SaveFile& file) { file << obj; file << known; file << visible; file << lastSeen; file << seenStrength; file << predictStrength; file << seenPosition; file << seenRegion; file << seenDestination; file << seenTarget; } void load(SaveFile& file) { file >> obj; file >> known; file >> visible; file >> lastSeen; file >> seenStrength; file >> predictStrength; file >> seenPosition; file >> seenRegion; file >> seenDestination; file >> seenTarget; } bool get_isSignificant() { return obj.getFleetStrength() > 0.1; } bool tick(AI& ai, Intelligence& intelligence, Intel& intel) { if(visible) { if(!obj.valid || obj.owner !is intel.empire) return false; } else { if(!obj.valid || obj.owner !is intel.empire) { if(!known || lastSeen < gameTime - 300.0) return false; } } if(obj.isVisibleTo(ai.empire)) { known = true; visible = true; lastSeen = gameTime; seenStrength = obj.getFleetStrength(); predictStrength = obj.getFleetMaxStrength(); int supCap = obj.SupplyCapacity; double fillPct = 1.0; if(supCap != 0) { double fillPct = double(obj.SupplyUsed) / double(supCap); if(fillPct > 0.5) predictStrength /= fillPct; else predictStrength *= 2.0; } seenPosition = obj.position; @seenRegion = obj.region; if(obj.isMoving) { seenDestination = obj.computedDestination; if(seenRegion !is null && inRegion(seenRegion, seenDestination)) @seenTarget = seenRegion; else if(seenTarget !is null && inRegion(seenTarget, seenDestination)) @seenTarget = seenTarget; else @seenTarget = getRegion(seenDestination); } else { seenDestination = seenPosition; @seenTarget = seenRegion; } } else { visible = false; } return true; } }; final class Intel { Empire@ empire; uint borderedTo = 0; array<FleetIntel@> fleets; array<SystemAI@> shared; array<SystemAI@> theirBorder; array<SystemAI@> theirOwned; void save(Intelligence& intelligence, SaveFile& file) { uint cnt = fleets.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets[i].save(file); cnt = shared.length; file << cnt; for(uint i = 0; i < cnt; ++i) intelligence.systems.saveAI(file, shared[i]); cnt = theirBorder.length; file << cnt; for(uint i = 0; i < cnt; ++i) intelligence.systems.saveAI(file, theirBorder[i]); cnt = theirOwned.length; file << cnt; for(uint i = 0; i < cnt; ++i) intelligence.systems.saveAI(file, theirOwned[i]); file << borderedTo; } void load(Intelligence& intelligence, SaveFile& file) { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { FleetIntel flIntel; flIntel.load(file); if(flIntel.obj !is null) fleets.insertLast(flIntel); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ sys = intelligence.systems.loadAI(file); if(sys !is null) shared.insertLast(sys); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ sys = intelligence.systems.loadAI(file); if(sys !is null) theirBorder.insertLast(sys); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ sys = intelligence.systems.loadAI(file); if(sys !is null) theirOwned.insertLast(sys); } file >> borderedTo; } //TODO: If a fleet is going to drop out of cutoff range soon, // queue up a scouting mission to its last known position so we // can try to regain intel on it. double getSeenStrength(double cutOff = 600.0) { double total = 0.0; cutOff = gameTime - cutOff; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flIntel = fleets[i]; if(!flIntel.known) continue; if(flIntel.lastSeen < cutOff) continue; total += sqrt(fleets[i].seenStrength); } return total * total; } double getPredictiveStrength(double cutOff = 600.0) { double total = 0.0; cutOff = gameTime - cutOff; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flIntel = fleets[i]; if(!flIntel.known) continue; if(flIntel.lastSeen < cutOff) continue; total += sqrt(fleets[i].predictStrength); } return total * total; } double accuracy(AI& ai, Intelligence& intelligence, double cutOff = 600.0) { uint total = 0; uint known = 0; cutOff = gameTime - cutOff; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flIntel = fleets[i]; if(!flIntel.isSignificant) continue; total += 1; if(flIntel.known && flIntel.lastSeen >= cutOff) known += 1; } if(total == 0) return 1.0; return double(known) / double(total); } double defeatability(AI& ai, Intelligence& intelligence, double cutOff = 600.0) { double acc = accuracy(ai, intelligence, cutOff); double ourStrength = 0, theirStrength = 0; if(acc < 0.6) { //In low-accuracy situations, base it on the empire overall strength metric theirStrength = empire.TotalMilitary; ourStrength = ai.empire.TotalMilitary; } else { theirStrength = getPredictiveStrength(cutOff * 10.0); ourStrength = intelligence.fleets.totalStrength; } if(theirStrength == 0) return 10.0; return ourStrength / theirStrength; } void tick(AI& ai, Intelligence& intelligence) { //Keep known fleets updated for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(!fleets[i].tick(ai, intelligence, this)) { fleets.removeAt(i); --i; --cnt; } } } bool isShared(AI& ai, SystemAI@ sys) { return sys.seenPresent & ai.empire.mask != 0 && sys.seenPresent & empire.mask != 0; } bool isBorder(AI& ai, SystemAI@ sys) { return sys.outsideBorder && sys.seenPresent & empire.mask != 0; } void focusTick(AI& ai, Intelligence& intelligence) { //Detect newly created fleets auto@ data = empire.getFlagships(); Object@ obj; while(receive(data, obj)) { if(obj is null) continue; bool found = false; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].obj is obj) { found = true; break; } } if(!found) { FleetIntel flIntel; @flIntel.obj = obj; fleets.insertLast(flIntel); } } //Remove no longer shared and border systems for(uint i = 0, cnt = shared.length; i < cnt; ++i) { if(!isShared(ai, shared[i])) { shared.removeAt(i); --i; --cnt; } } for(uint i = 0, cnt = theirBorder.length; i < cnt; ++i) { if(!isBorder(ai, theirBorder[i])) { theirBorder.removeAt(i); --i; --cnt; } } borderedTo = 0; for(uint i = 0, cnt = theirOwned.length; i < cnt; ++i) { auto@ sys = theirOwned[i]; uint seen = sys.seenPresent; if(seen & empire.mask == 0) { theirOwned.removeAt(i); --i; --cnt; continue; } for(uint n = 0, ncnt = sys.desc.adjacent.length; n < ncnt; ++n) { auto@ other = intelligence.systems.getAI(sys.desc.adjacent[n]); if(other !is null) borderedTo |= other.seenPresent & ~empire.mask; } for(uint n = 0, ncnt = sys.desc.wormholes.length; n < ncnt; ++n) { auto@ other = intelligence.systems.getAI(sys.desc.wormholes[n]); if(other !is null) borderedTo |= other.seenPresent & ~empire.mask; } } //Detect shared and border systems for(uint i = 0, cnt = intelligence.systems.owned.length; i < cnt; ++i) { auto@ sys = intelligence.systems.owned[i]; if(isShared(ai, sys)) { if(shared.find(sys) == -1) shared.insertLast(sys); } } for(uint i = 0, cnt = intelligence.systems.outsideBorder.length; i < cnt; ++i) { auto@ sys = intelligence.systems.outsideBorder[i]; if(isBorder(ai, sys)) { if(theirBorder.find(sys) == -1) theirBorder.insertLast(sys); } } for(uint i = 0, cnt = intelligence.systems.all.length; i < cnt; ++i) { auto@ sys = intelligence.systems.all[i]; if(sys.seenPresent & empire.mask != 0) { if(theirOwned.find(sys) == -1) theirOwned.insertLast(sys); } } //Try to update some stuff SystemAI@ check; double lru = 0; for(uint i = 0, cnt = shared.length; i < cnt; ++i) { auto@ sys = shared[i]; double update = sys.lastStrengthCheck; if(update < lru && sys.visible) { @check = sys; lru = update; } } for(uint i = 0, cnt = theirBorder.length; i < cnt; ++i) { auto@ sys = theirBorder[i]; double update = sys.lastStrengthCheck; if(update < lru && sys.visible) { @check = sys; lru = update; } } if(check !is null) check.strengthCheck(ai); } }; class Intelligence : AIComponent { Fleets@ fleets; Systems@ systems; array<Intel@> intel; void create() { @fleets = cast<Fleets>(ai.fleets); @systems = cast<Systems>(ai.systems); } void start() { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(emp is ai.empire) continue; if(!emp.major) continue; Intel empIntel; @empIntel.empire = emp; if(intel.length <= uint(emp.index)) intel.length = uint(emp.index)+1; @intel[emp.index] = empIntel; } } void save(SaveFile& file) { uint cnt = intel.length; file << cnt; for(uint i = 0; i < cnt; ++i) { if(intel[i] is null) { file.write0(); continue; } file.write1(); intel[i].save(this, file); } } void load(SaveFile& file) { uint cnt = 0; file >> cnt; intel.length = cnt; for(uint i = 0; i < cnt; ++i) { if(!file.readBit()) continue; @intel[i] = Intel(); @intel[i].empire = getEmpire(i); intel[i].load(this, file); } } Intel@ get(Empire@ emp) { if(emp is null) return null; if(!emp.major) return null; if(uint(emp.index) >= intel.length) return null; return intel[emp.index]; } uint ind = 0; void tick(double time) override { if(intel.length == 0) return; ind = (ind+1)%intel.length; if(intel[ind] !is null) intel[ind].tick(ai, this); } uint fInd = 0; void focusTick(double time) override { if(intel.length == 0) return; fInd = (fInd+1)%intel.length; if(intel[fInd] !is null) intel[fInd].focusTick(ai, this); } string strdisplay(double str) { return standardize(str * 0.001, true); } double defeatability(Empire@ emp) { auto@ empIntel = get(emp); if(empIntel is null) return 0.0; return empIntel.defeatability(ai, this); } double defeatability(uint theirMask, uint myMask = 0, double cutOff = 600.0) { if(myMask == 0) myMask = ai.empire.mask; double minAcc = 1.0; for(uint i = 0, cnt = intel.length; i < cnt; ++i) { auto@ itl = intel[i]; if(itl is null || itl.empire is null) continue; if((theirMask | myMask) & itl.empire.mask == 0) continue; minAcc = min(itl.accuracy(ai, this, cutOff), minAcc); } double ourStrength = 0, theirStrength = 0; for(uint i = 0, cnt = intel.length; i < cnt; ++i) { auto@ itl = intel[i]; if(itl is null || itl.empire is null) continue; if((theirMask | myMask) & itl.empire.mask == 0) continue; double str = 0.0; if(minAcc < 0.6) str = itl.empire.TotalMilitary; else str = itl.getPredictiveStrength(cutOff * 10.0); if(itl.empire.mask & theirMask != 0) theirStrength += str; if(itl.empire.mask & myMask != 0) ourStrength += str; } if(myMask & ai.empire.mask != 0) { if(minAcc < 0.6) ourStrength += ai.empire.TotalMilitary; else ourStrength += fleets.totalStrength; } if(theirMask & ai.empire.mask != 0) { if(minAcc < 0.6) theirStrength += ai.empire.TotalMilitary; else theirStrength += fleets.totalStrength; } if(theirStrength == 0) return 10.0; return ourStrength / theirStrength; } void turn() override { if(log) { ai.print("Intelligence Report on Empires:"); ai.print(ai.pad(" Our strength: ", 18)+strdisplay(fleets.totalStrength)+" / "+strdisplay(fleets.totalMaxStrength)); for(uint i = 0, cnt = intel.length; i < cnt; ++i) { auto@ empIntel = intel[i]; if(empIntel is null) continue; ai.print(" "+ai.pad(empIntel.empire.name, 16) +ai.pad(" "+strdisplay(empIntel.getSeenStrength()) +" / "+strdisplay(empIntel.getPredictiveStrength()), 20) +" defeatability "+toString(empIntel.defeatability(ai, this), 2) +" accuracy "+toString(empIntel.accuracy(ai, this), 2)); } } } }; AIComponent@ createIntelligence() { return Intelligence(); } |
Added scripts/server/empire_ai/weasel/Military.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 |
// Military // -------- // Military construction logic. Builds and restores fleets and defensive stations, // but does not deal with actually using those fleets to fight - that is the purview of // the War component. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Designs; import empire_ai.weasel.Construction; import empire_ai.weasel.Budget; import empire_ai.weasel.Fleets; import empire_ai.weasel.Development; import empire_ai.weasel.Movement; import empire_ai.weasel.Systems; import empire_ai.weasel.Orbitals; import resources; class SupportOrder { DesignTarget@ design; Object@ onObject; AllocateBudget@ alloc; bool isGhostOrder = false; double expires = INFINITY; uint count = 0; void save(Military& military, SaveFile& file) { military.designs.saveDesign(file, design); file << onObject; military.budget.saveAlloc(file, alloc); file << isGhostOrder; file << expires; file << count; } void load(Military& military, SaveFile& file) { @design = military.designs.loadDesign(file); file >> onObject; @alloc = military.budget.loadAlloc(file); file >> isGhostOrder; file >> expires; file >> count; } bool tick(AI& ai, Military& military, double time) { if(alloc !is null) { if(alloc.allocated) { if(isGhostOrder) onObject.rebuildAllGhosts(); else onObject.orderSupports(design.active.mostUpdated(), count); if(military.log && design !is null) ai.print("Support order completed for "+count+"x "+design.active.name+" ("+design.active.size+")", onObject); return false; } } else if(design !is null) { if(design.active !is null) @alloc = military.budget.allocate(BT_Military, getBuildCost(design.active.mostUpdated()) * count); } if(expires < gameTime) { if(alloc !is null && !alloc.allocated) military.budget.remove(alloc); if(isGhostOrder) onObject.clearAllGhosts(); if(military.log) ai.print("Support order expired", onObject); return false; } return true; } }; class StagingBase { Region@ region; array<FleetAI@> fleets; double idleTime = 0.0; double occupiedTime = 0.0; OrbitalAI@ shipyard; BuildOrbital@ shipyardBuild; Factory@ factory; bool isUnderAttack = false; void save(Military& military, SaveFile& file) { file << region; file << idleTime; file << occupiedTime; file << isUnderAttack; military.orbitals.saveAI(file, shipyard); military.construction.saveConstruction(file, shipyardBuild); uint cnt = fleets.length; file << cnt; for(uint i = 0; i < cnt; ++i) military.fleets.saveAI(file, fleets[i]); } void load(Military& military, SaveFile& file) { file >> region; file >> idleTime; file >> occupiedTime; file >> isUnderAttack; @shipyard = military.orbitals.loadAI(file); @shipyardBuild = cast<BuildOrbital>(military.construction.loadConstruction(file)); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { if(i > 200 && file < SV_0158) { //Something went preeeetty wrong in an old save if(file.readBit()) { Object@ obj; file >> obj; } } else { auto@ fleet = military.fleets.loadAI(file); if(fleet !is null) fleets.insertLast(fleet); } } } bool tick(AI& ai, Military& military, double time) { if(fleets.length == 0) { occupiedTime = 0.0; idleTime += time; } else { occupiedTime += time; idleTime = 0.0; } isUnderAttack = region.ContestedMask & ai.mask != 0; //Manage building our shipyard if(shipyardBuild !is null) { if(shipyardBuild.completed) { @shipyard = military.orbitals.getInSystem(ai.defs.Shipyard, region); if(shipyard !is null) @shipyardBuild = null; } } if(shipyard !is null) { if(!shipyard.obj.valid) { @shipyard = null; @shipyardBuild = null; } } if(factory !is null && (!factory.valid || factory.obj.region !is region)) @factory = null; if(factory is null) @factory = military.construction.getFactory(region); if(factory !is null) { factory.needsSupportLabor = false; factory.waitingSupportLabor = 0.0; if(factory.obj.hasOrderedSupports) { factory.needsSupportLabor = true; factory.waitingSupportLabor += double(factory.obj.SupplyOrdered) * ai.behavior.estSizeSupportLabor; } for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].isHome && fleets[i].obj.hasOrderedSupports) { factory.needsSupportLabor = true; factory.waitingSupportLabor += double(fleets[i].obj.SupplyOrdered) * ai.behavior.estSizeSupportLabor; break; } } if(factory.waitingSupportLabor > 0) factory.aimForLabor(factory.waitingSupportLabor / ai.behavior.constructionMaxTime); } bool isFactorySufficient = false; if(factory !is null) { if(factory.waitingSupportLabor <= factory.laborIncome * ai.behavior.constructionMaxTime || factory.obj.canImportLabor || factory !is military.construction.primaryFactory) isFactorySufficient = true; } for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { Object@ obj = fleets[i].obj; if(obj is null || !obj.valid) { fleets.removeAt(i); --i; --cnt; continue; } fleets[i].stationedFactory = isFactorySufficient; } if(occupiedTime >= 3.0 * 60.0 && ai.defs.Shipyard !is null && shipyard is null && shipyardBuild is null && !isUnderAttack && (!isFactorySufficient && factory !is military.construction.primaryFactory)) { //If any fleets need construction try to queue up a shipyard bool needYard = false; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ flt = fleets[i]; if(flt.obj.hasOrderedSupports || flt.filled < 0.8) { needYard = true; break; } } if(needYard) { @shipyard = military.orbitals.getInSystem(ai.defs.Shipyard, region); if(shipyard is null) { vec3d pos = region.position; vec2d offset = random2d(region.radius * 0.4, region.radius * 0.8); pos.x += offset.x; pos.z += offset.y; @shipyardBuild = military.construction.buildOrbital(ai.defs.Shipyard, pos); } } } if((idleTime >= 10.0 * 60.0 || region.PlanetsMask & ai.mask == 0) && (shipyardBuild is null || shipyard !is null) && (factory is null || (shipyard !is null && factory.obj is shipyard.obj)) && military.stagingBases.length >= 2) { if(shipyard !is null && !ai.behavior.forbidScuttle) { cast<Orbital>(shipyard.obj).scuttle(); } else { if(factory !is null) { factory.needsSupportLabor = false; @factory = null; } return false; } } return true; } }; class Military : AIComponent { Fleets@ fleets; Development@ development; Designs@ designs; Construction@ construction; Budget@ budget; Systems@ systems; Orbitals@ orbitals; array<SupportOrder@> supportOrders; array<StagingBase@> stagingBases; AllocateConstruction@ mainWait; bool spentMoney = true; void create() { @fleets = cast<Fleets>(ai.fleets); @development = cast<Development>(ai.development); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @budget = cast<Budget>(ai.budget); @systems = cast<Systems>(ai.systems); @orbitals = cast<Orbitals>(ai.orbitals); } void save(SaveFile& file) { uint cnt = supportOrders.length; file << cnt; for(uint i = 0; i < cnt; ++i) supportOrders[i].save(this, file); construction.saveConstruction(file, mainWait); file << spentMoney; cnt = stagingBases.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveStaging(file, stagingBases[i]); stagingBases[i].save(this, file); } } void load(SaveFile& file) { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { SupportOrder ord; ord.load(this, file); if(ord.onObject !is null) supportOrders.insertLast(ord); } @mainWait = construction.loadConstruction(file); file >> spentMoney; file >> cnt; for(uint i = 0; i < cnt; ++i) { StagingBase@ base = loadStaging(file); if(base !is null) { base.load(this, file); if(stagingBases.find(base) == -1) stagingBases.insertLast(base); } else { StagingBase().load(this, file); } } } void loadFinalize(AI& ai) override { for(uint i = 0, cnt = stagingBases.length; i < cnt; ++i) { auto@ base = stagingBases[i]; for(uint n = 0, ncnt = base.fleets.length; n < ncnt; ++n) { Object@ obj = base.fleets[n].obj; if(obj is null || !obj.valid || !obj.initialized) { base.fleets.removeAt(n); --n; --ncnt; } } } } StagingBase@ loadStaging(SaveFile& file) { Region@ reg; file >> reg; if(reg is null) return null; StagingBase@ base = getBase(reg); if(base is null) { @base = StagingBase(); @base.region = reg; stagingBases.insertLast(base); } return base; } void saveStaging(SaveFile& file, StagingBase@ base) { Region@ reg; if(base !is null) @reg = base.region; file << reg; } Region@ getClosestStaging(Region& targetRegion) { //Check if we have anything close enough StagingBase@ best; int minHops = INT_MAX; for(uint i = 0, cnt = stagingBases.length; i < cnt; ++i) { int d = systems.hopDistance(stagingBases[i].region, targetRegion); if(d < minHops) { minHops = d; @best = stagingBases[i]; } } if(best !is null) return best.region; return null; } Region@ getStagingFor(Region& targetRegion) { //Check if we have anything close enough StagingBase@ best; int minHops = INT_MAX; for(uint i = 0, cnt = stagingBases.length; i < cnt; ++i) { int d = systems.hopDistance(stagingBases[i].region, targetRegion); if(d < minHops) { minHops = d; @best = stagingBases[i]; } } if(minHops < ai.behavior.stagingMaxHops) return best.region; //Create a new staging base for this Region@ bestNew; minHops = INT_MAX; for(uint i = 0, cnt = systems.border.length; i < cnt; ++i) { auto@ sys = systems.border[i].obj; int d = systems.hopDistance(sys, targetRegion); if(d < minHops) { minHops = d; @bestNew = sys; } } if(minHops > ai.behavior.stagingMaxHops && best !is null) return best.region; auto@ base = getBase(bestNew); if(base !is null) return base.region; else return createStaging(bestNew).region; } StagingBase@ createStaging(Region@ region) { if(region is null) return null; if(log) ai.print("Create new staging base.", region); StagingBase newBase; @newBase.region = region; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { if(fleets.fleets[i].stationed is region) newBase.fleets.insertLast(fleets.fleets[i]); } stagingBases.insertLast(newBase); return newBase; } StagingBase@ getBase(Region@ inRegion) { if(inRegion is null) return null; for(uint i = 0, cnt = stagingBases.length; i < cnt; ++i) { if(stagingBases[i].region is inRegion) return stagingBases[i]; } return null; } vec3d getStationPosition(Region& inRegion, double distance = 100.0) { auto@ base = getBase(inRegion); if(base !is null) { if(base.shipyard !is null) { vec3d pos = base.shipyard.obj.position; vec2d offset = random2d(distance * 0.5, distance * 1.5); pos.x += offset.x; pos.z += offset.y; return pos; } } vec3d pos = inRegion.position; vec2d offset = random2d(inRegion.radius * 0.4, inRegion.radius * 0.8); pos.x += offset.x; pos.z += offset.y; return pos; } void stationFleet(FleetAI@ fleet, Region@ inRegion) { if(inRegion is null || fleet.stationed is inRegion) return; auto@ prevBase = getBase(fleet.stationed); if(prevBase !is null) prevBase.fleets.remove(fleet); auto@ base = getBase(inRegion); if(base !is null) base.fleets.insertLast(fleet); @fleet.stationed = inRegion; fleet.stationedFactory = construction.getFactory(inRegion) !is null; if(fleet.mission is null) fleets.returnToBase(fleet); } void orderSupportsOn(Object& obj, double expire = 60.0) { if(obj.SupplyGhost > 0) { if(ai.behavior.fleetsRebuildGhosts) { //Try to rebuild the fleet's ghosts SupportOrder ord; @ord.onObject = obj; @ord.alloc = budget.allocate(BT_Military, obj.rebuildGhostsCost()); ord.expires = gameTime + expire; ord.isGhostOrder = true; supportOrders.insertLast(ord); if(log) ai.print("Attempting to rebuild ghosts", obj); return; } else { obj.clearAllGhosts(); } } int supCap = obj.SupplyCapacity; int supHave = obj.SupplyUsed - obj.SupplyGhost; //Build some supports int supSize = pow(2, round(::log(double(supCap) * randomd(0.005, 0.03))/::log(2.0))); supSize = max(min(supSize, supCap - supHave), 1); SupportOrder ord; @ord.onObject = obj; @ord.design = designs.design(DP_Support, supSize); ord.expires = gameTime + expire; ord.count = clamp((supCap - supHave)/supSize, 1, int(ceil((randomd(0.01, 0.1)*supCap)/double(supSize)))); if(log) ai.print("Attempting to build supports: "+ord.count+"x size "+supSize, obj); supportOrders.insertLast(ord); } void findSomethingToDo() { //See if we should retrofit anything if(mainWait is null && !spentMoney && gameTime > ai.behavior.flagshipBuildMinGameTime) { int availMoney = budget.spendable(BT_Military); int moneyTargetSize = floor(double(availMoney) * ai.behavior.shipSizePerMoney); //See if one of our fleets is old enough that we can retrofit it for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { FleetAI@ fleet = fleets.fleets[i]; if(fleet.mission !is null && fleet.mission.isActive) continue; if(fleet.fleetClass != FC_Combat) continue; if(fleet.obj.hasOrders) continue; Ship@ ship = cast<Ship>(fleet.obj); if(ship is null) continue; //Don't retrofit free fleets if(ship.isFree && !ai.behavior.retrofitFreeFleets) continue; //Find the factory assigned to this Factory@ factory; if(fleet.isHome) { Region@ reg = fleet.obj.region; @factory = construction.getFactory(reg); } if(factory is null) continue; if(factory.busy) continue; //Find how large we can make this flagship const Design@ dsg = ship.blueprint.design; int targetSize = min(int(moneyTargetSize * 1.2), int(factory.laborToBear(ai) * 1.3 * ai.behavior.shipSizePerLabor)); targetSize = 5 * floor(double(targetSize) / 5.0); //See if we should retrofit this int size = ship.blueprint.design.size; if(size > targetSize) continue; double pctDiff = (double(targetSize) / double(size)) - 1.0; if(pctDiff < ai.behavior.shipRetrofitThreshold) continue; DesignTarget@ newDesign = designs.scale(dsg, targetSize); spentMoney = true; auto@ retrofit = construction.retrofit(ship); @mainWait = construction.buildNow(retrofit, factory); if(log) ai.print("Retrofitting to size "+targetSize, fleet.obj); //TODO: This should mark the fleet as occupied for missions while we retrofit return; } //See if we should build a new fleet Factory@ factory = construction.primaryFactory; if(factory !is null && !factory.busy) { //Figure out how large our flagship would be if we built one factory.aimForLabor((double(moneyTargetSize) / ai.behavior.shipSizePerLabor) / ai.behavior.constructionMaxTime); int targetSize = min(moneyTargetSize, int(factory.laborToBear(ai) * ai.behavior.shipSizePerLabor)); targetSize = 5 * floor(double(targetSize) / 5.0); int expMaint = double(targetSize) * ai.behavior.maintenancePerShipSize; int expCost = double(targetSize) / ai.behavior.shipSizePerMoney; if(budget.canSpend(BT_Military, expCost, expMaint)) { //Make sure we're building an adequately sized flagship uint count = 0; double avgSize = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { FleetAI@ fleet = fleets.fleets[i]; Ship@ ship = cast<Ship>(fleet.obj); if(ship !is null && fleet.fleetClass == FC_Combat) { avgSize += ship.blueprint.design.size; count += 1; } } if(count != 0) avgSize /= double(count); if(count < ai.behavior.maxActiveFleets && targetSize >= avgSize * ai.behavior.flagshipBuildMinAvgSize) { //Build the flagship DesignTarget@ design = designs.design(DP_Combat, targetSize, availMoney, budget.maintainable(BT_Military), factory.laborToBear(ai), findSize=true); @mainWait = construction.buildFlagship(design); mainWait.maxTime *= 1.5; spentMoney = true; if(log) ai.print("Ordering a new fleet at size "+targetSize); return; } } } } //See if any of our fleets need refilling //TODO: Aim for labor on the factory so that the supports are built in reasonable time for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { FleetAI@ fleet = fleets.fleets[i]; if(fleet.mission !is null && fleet.mission.isActive) continue; if(fleet.fleetClass != FC_Combat) continue; if(fleet.obj.hasOrders) continue; if(fleet.filled >= 1.0) continue; if(hasSupportOrderFor(fleet.obj)) continue; if(!fleet.isHome) continue; //Re-station to our factory if we're idle and need refill without being near a factory Factory@ f = construction.getFactory(fleet.obj.region); if(f is null) { if(fleet.filled < ai.behavior.stagingToFactoryFill && construction.primaryFactory !is null) stationFleet(fleet, construction.primaryFactory.obj.region); continue; } //Don't order if the factory has support orders, it'll just make everything take longer if(f !is null && ai.behavior.supportOrderWaitOnFactory && fleet.filled < 0.9 && fleet.obj.SupplyGhost == 0) { if(f.obj.hasOrderedSupports && f.obj.SupplyUsed < f.obj.SupplyCapacity) continue; } int supCap = fleet.obj.SupplyCapacity; int supHave = fleet.obj.SupplyUsed - fleet.obj.SupplyGhost; if(supHave < supCap) { orderSupportsOn(fleet.obj); spentMoney = true; return; } } budget.checkedMilitarySpending = spentMoney; //TODO: Build defense stations } bool hasSupportOrderFor(Object& obj) { for(uint i = 0, cnt = supportOrders.length; i < cnt; ++i) { if(supportOrders[i].onObject is obj) return true; } return false; } void tick(double time) override { //Manage our orders for support ships for(uint i = 0, cnt = supportOrders.length; i < cnt; ++i) { if(!supportOrders[i].tick(ai, this, time)) { supportOrders.removeAt(i); --i; --cnt; } } } void focusTick(double time) override { //Find something for us to do findSomethingToDo(); //If we're far into the budget, spend our money on building supports at our factories if(budget.Progress > 0.9 && budget.canSpend(BT_Military, 10)) { for(uint i = 0, cnt = construction.factories.length; i < cnt; ++i) { //TODO: Build on planets in the system if this is full auto@ f = construction.factories[i]; if(f.obj.SupplyUsed < f.obj.SupplyCapacity && !hasSupportOrderFor(f.obj)) { orderSupportsOn(f.obj, expire=budget.RemainingTime); break; } } } //Check if we should re-station any of our fleets for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.stationed is null) { Region@ reg = flAI.obj.region; if(reg !is null && reg.PlanetsMask & ai.mask != 0) stationFleet(flAI, reg); } } //Make sure all our major factories are considered staging bases for(uint i = 0, cnt = construction.factories.length; i < cnt; ++i) { auto@ f = construction.factories[i]; if(f.obj.isShip) continue; Region@ reg = f.obj.region; if(reg is null) continue; auto@ base = getBase(reg); if(base is null) createStaging(reg); } //If we don't have any staging bases, make one at a focus if(stagingBases.length == 0 && development.focuses.length != 0) { Region@ reg = development.focuses[0].obj.region; if(reg !is null) createStaging(reg); } //Update our staging bases for(uint i = 0, cnt = stagingBases.length; i < cnt; ++i) { auto@ base = stagingBases[i]; if(!base.tick(ai, this, time)) { stagingBases.removeAt(i); --i; --cnt; } } } void turn() override { //Fleet construction happens in the beginning of the turn, because we want //to use our entire military budget on it. if(mainWait !is null) { if(mainWait.completed) { @mainWait = null; } else if(!mainWait.started) { if(log) ai.print("Failed current main construction wait."); construction.cancel(mainWait); @mainWait = null; } } spentMoney = false; } }; AIComponent@ createMilitary() { return Military(); } |
Added scripts/server/empire_ai/weasel/Movement.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
// Movement // -------- // Manages FTL travel modes, expenditure of FTL energy, and general movement patterns. // import empire_ai.weasel.WeaselAI; import oddity_navigation; import ftl; enum FTLReturn { F_Pass, F_Continue, F_Done, F_Kill, }; class FTL : AIComponent { uint order(MoveOrder& order) { return F_Pass; } uint tick(MoveOrder& order, double time) { return F_Pass; } }; bool getNearPosition(Object& obj, Object& target, vec3d& pos, bool spread = false) { if(target !is null) { if(target.isPlanet) { Planet@ toPl = cast<Planet>(target); vec3d dir = obj.position - toPl.position; dir = dir.normalized(toPl.OrbitSize * 0.9); if(spread) dir = quaterniond_fromAxisAngle(vec3d_up(), randomd(-0.15,0.15) * pi) * dir; pos = toPl.position + dir; return true; } else if(obj.hasLeaderAI && (target.isShip || target.isOrbital)) { vec3d dir = obj.position - target.position; dir = dir.normalized(obj.getEngagementRange()); pos = target.position + dir; return true; } else { Region@ reg = cast<Region>(target); if(reg is null) @reg = target.region; if(reg !is null) { vec3d dir = obj.position - reg.position; dir = dir.normalized(reg.radius * 0.85); if(spread) dir = quaterniond_fromAxisAngle(vec3d_up(), randomd(-0.15,0.15) * pi) * dir; pos = reg.position + dir; return true; } } } return false; } bool targetPosition(MoveOrder& ord, vec3d& toPosition) { if(ord.target !is null) { return getNearPosition(ord.obj, ord.target, toPosition); } else { toPosition = ord.position; return true; } } double usableFTL(AI& ai, MoveOrder& ord) { double storage = ai.empire.FTLCapacity; double avail = ai.empire.FTLStored; double reserved = 0.0; if(ord.priority < MP_Critical) reserved += ai.behavior.ftlReservePctCritical; if(ord.priority < MP_Normal) reserved += ai.behavior.ftlReservePctNormal; avail -= reserved * storage; return avail; } enum MovePriority { MP_Background, MP_Normal, MP_Critical }; class MoveOrder { int id = -1; uint priority = MP_Normal; Object@ obj; Object@ target; vec3d position; bool completed = false; bool failed = false; void save(Movement& movement, SaveFile& file) { file << priority; file << obj; file << target; file << position; file << completed; file << failed; } void load(Movement& movement, SaveFile& file) { file >> priority; file >> obj; file >> target; file >> position; file >> completed; file >> failed; } void cancel() { failed = true; obj.clearOrders(); } bool tick(AI& ai, Movement& movement, double time) { //Check if we still exist if(obj is null || !obj.valid || obj.owner !is ai.empire) { failed = true; return false; } uint ftlMode = F_Pass; if(movement.ftl !is null) { ftlMode = movement.ftl.tick(this, time); if(ftlMode == F_Kill) return false; if(ftlMode == F_Done) return true; } //Check if we've arrived if(target !is null) { if(!target.valid) { failed = true; return false; } double targDist = target.radius + 45.0 + obj.radius; if(target.isRegion) targDist = target.radius * 0.86 + obj.radius; if(target.position.distanceTo(obj.position) < targDist) { completed = true; return false; } } else { double targDist = obj.radius * 2.0; if(obj.position.distanceTo(position) < targDist) { completed = true; return false; } } //Fail out if our order failed if(ftlMode == F_Pass) { if(!obj.hasOrders) { failed = true; return false; } } return true; } }; class Movement : AIComponent { int nextMoveOrderId = 0; array<MoveOrder@> moveOrders; array<Oddity@> oddities; FTL@ ftl; void create() { @ftl = cast<FTL>(ai.ftl); } void save(SaveFile& file) { file << nextMoveOrderId; uint cnt = moveOrders.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveMoveOrder(file, moveOrders[i]); moveOrders[i].save(this, file); } } void load(SaveFile& file) { file >> nextMoveOrderId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ ord = loadMoveOrder(file); if(ord !is null) { ord.load(this, file); if(ord.obj !is null) moveOrders.insertLast(ord); } else { MoveOrder().load(this, file); } } } array<MoveOrder@> loadIds; MoveOrder@ loadMoveOrder(SaveFile& file) { int id = -1; file >> id; bool failed = false, completed = false; file >> failed; file >> completed; if(id == -1) { return null; } else { for(uint i = 0, cnt = loadIds.length; i < cnt; ++i) { if(loadIds[i].id == id) return loadIds[i]; } MoveOrder data; data.id = id; data.failed = failed; data.completed = completed; loadIds.insertLast(data); return data; } } void saveMoveOrder(SaveFile& file, MoveOrder@ data) { int id = -1; bool failed = false, completed = false; if(data !is null) { id = data.id; failed = data.failed; completed = data.completed; } file << id; file << failed; file << completed; } void postLoad(AI& ai) { loadIds.length = 0; getOddityGates(oddities); } array<PathNode@> path; double getPathDistance(const vec3d& fromPosition, const vec3d& toPosition, double accel = 1.0) { pathOddityGates(oddities, ai.empire, path, fromPosition, toPosition, accel); return ::getPathDistance(fromPosition, toPosition, path); } double eta(Object& obj, Object& toObject, uint priority = MP_Normal) { return eta(obj, toObject.position, priority); } double eta(Object& obj, const vec3d& position, uint priority = MP_Normal) { //TODO: Use FTL //TODO: Path through gates/wormholes return newtonArrivalTime(obj.maxAcceleration, position - obj.position, obj.velocity); } void order(MoveOrder& ord) { if(ord.target !is null && ord.target is ord.obj.region) return; bool madeOrder = false; if(ftl !is null) { uint mode = ftl.order(ord); if(mode == F_Kill || mode == F_Done) return; madeOrder = (mode == F_Continue); } if(ord.target !is null) { ord.obj.addGotoOrder(ord.target, append=madeOrder); ord.position = ord.target.position; } else ord.obj.addMoveOrder(ord.position, append=madeOrder); } void add(MoveOrder& ord) { for(uint i = 0, cnt = moveOrders.length; i < cnt; ++i) { if(moveOrders[i].obj is ord.obj) { moveOrders[i].failed = true; moveOrders.removeAt(i); --i; --cnt; } } moveOrders.insertLast(ord); order(ord); } MoveOrder@ move(Object& obj, Object& toObject, uint priority = MP_Normal, bool spread = false, bool nearOnly = false) { if(toObject.isRegion) { if(obj.region is toObject) nearOnly = false; else nearOnly = true; } if(nearOnly) { vec3d pos; bool canNear = getNearPosition(obj, toObject, pos, spread); if(canNear) return move(obj, pos, priority); } MoveOrder ord; ord.id = nextMoveOrderId++; @ord.obj = obj; @ord.target = toObject; ord.priority = priority; add(ord); return ord; } MoveOrder@ move(Object& obj, const vec3d& position, uint priority = MP_Normal, bool spread = false) { MoveOrder ord; ord.id = nextMoveOrderId++; @ord.obj = obj; ord.position = position; ord.priority = priority; add(ord); return ord; } void tick(double time) override { for(uint i = 0, cnt = moveOrders.length; i < cnt; ++i) { if(!moveOrders[i].tick(ai, this, time)) { moveOrders.removeAt(i); --i; --cnt; } } } void focusTick(double time) override { //Update our gate navigation list getOddityGates(oddities); } }; AIComponent@ createMovement() { return Movement(); } |
Added scripts/server/empire_ai/weasel/Orbitals.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Budget; import empire_ai.weasel.Systems; from ai.orbitals import AIOrbitals, OrbitalAIHook, OrbitalUse; import ai.consider; import ai.construction; import orbitals; import saving; final class OrbitalAI { Object@ obj; const OrbitalModule@ type; double prevTick = 0; Object@ around; void init(AI& ai, Orbitals& orbitals) { if(obj.isOrbital) @type = getOrbitalModule(cast<Orbital>(obj).coreModule); } void save(Orbitals& orbitals, SaveFile& file) { file << obj; } void load(Orbitals& orbitals, SaveFile& file) { file >> obj; } void remove(AI& ai, Orbitals& orbitals) { } void tick(AI& ai, Orbitals& orbitals, double time) { //Deal with losing planet ownership if(obj is null || !obj.valid || obj.owner !is ai.empire) { orbitals.remove(this); return; } //Record what we're orbiting around if(around !is null) { if(!obj.isOrbitingAround(around)) @around = obj.getOrbitingAround(); } else { if(obj.hasOrbitCenter) @around = obj.getOrbitingAround(); } } }; class Orbitals : AIComponent, AIOrbitals { Budget@ budget; Systems@ systems; array<OrbitalAI@> orbitals; uint orbIdx = 0; array<IOrbitalConstruction@> genericBuilds; bool buildOrbitals = true; void create() { @budget = cast<Budget>(ai.budget); @systems = cast<Systems>(ai.systems); //Register specialized orbital types for(uint i = 0, cnt = getOrbitalModuleCount(); i < cnt; ++i) { auto@ type = getOrbitalModule(i); for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) { auto@ hook = cast<OrbitalAIHook>(type.ai[n]); if(hook !is null) hook.register(this, type); } } } Empire@ get_empire() { return ai.empire; } Considerer@ get_consider() { return cast<Considerer>(ai.consider); } OrbitalAI@ getInSystem(const OrbitalModule@ module, Region@ reg) { if(module is null) return null; for(uint i = 0, cnt = orbitals.length; i < cnt; ++i) { if(orbitals[i].type is module) { if(orbitals[i].obj.region is reg) return orbitals[i]; } } return null; } bool haveInSystem(const OrbitalModule@ module, Region@ reg) { if(module is null) return false; for(uint i = 0, cnt = orbitals.length; i < cnt; ++i) { if(orbitals[i].type is module) { if(orbitals[i].obj.region is reg) return true; } } return false; } bool haveAround(const OrbitalModule@ module, Object@ around) { if(module is null) return false; for(uint i = 0, cnt = orbitals.length; i < cnt; ++i) { if(orbitals[i].type is module) { if(orbitals[i].around is around) return true; } } return false; } void registerUse(OrbitalUse use, const OrbitalModule& type) { switch(use) { case OU_Shipyard: @ai.defs.Shipyard = type; break; case OU_TradeOutpost: @ai.defs.TradeOutpost = type; break; case OU_TradeStation: @ai.defs.TradeStation = type; break; } } void save(SaveFile& file) { uint cnt = orbitals.length; file << cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = orbitals[i]; saveAI(file, data); data.save(this, file); } } void load(SaveFile& file) { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadAI(file); if(data !is null) data.load(this, file); else OrbitalAI().load(this, file); } } OrbitalAI@ loadAI(SaveFile& file) { Object@ obj; file >> obj; if(obj is null) return null; OrbitalAI@ data = getAI(obj); if(data is null) { @data = OrbitalAI(); @data.obj = obj; data.prevTick = gameTime; orbitals.insertLast(data); data.init(ai, this); } return data; } void saveAI(SaveFile& file, OrbitalAI@ ai) { Object@ obj; if(ai !is null) @obj = ai.obj; file << obj; } void start() { checkForOrbitals(); } void checkForOrbitals() { auto@ data = ai.empire.getOrbitals(); Object@ obj; while(receive(data, obj)) { if(obj !is null) register(obj); } } bool isBuilding(const OrbitalModule& type) { for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) { if(genericBuilds[i].module is type) return true; } return false; } void tick(double time) { double curTime = gameTime; if(orbitals.length != 0) { orbIdx = (orbIdx+1) % orbitals.length; auto@ data = orbitals[orbIdx]; data.tick(ai, this, curTime - data.prevTick); data.prevTick = curTime; } } uint prevCount = 0; double checkTimer = 0; void focusTick(double time) override { //Check for any newly obtained planets uint curCount = ai.empire.orbitalCount; checkTimer += time; if(curCount != prevCount || checkTimer > 60.0) { checkForOrbitals(); prevCount = curCount; checkTimer = 0; } //Deal with building AI hints } OrbitalAI@ getAI(Object& obj) { for(uint i = 0, cnt = orbitals.length; i < cnt; ++i) { if(orbitals[i].obj is obj) return orbitals[i]; } return null; } OrbitalAI@ register(Object& obj) { OrbitalAI@ data = getAI(obj); if(data is null) { @data = OrbitalAI(); @data.obj = obj; data.prevTick = gameTime; orbitals.insertLast(data); data.init(ai, this); } return data; } void remove(OrbitalAI@ data) { data.remove(ai, this); orbitals.remove(data); } }; AIComponent@ createOrbitals() { return Orbitals(); } |
Added scripts/server/empire_ai/weasel/Planets.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Events; import empire_ai.weasel.Resources; import empire_ai.weasel.Budget; import empire_ai.weasel.Systems; import ai.construction; import ai.events; import planets.PlanetSurface; import void relationRecordLost(AI& ai, Empire& emp, Object@ obj) from "empire_ai.weasel.Relations"; from constructions import ConstructionType, getConstructionTypeCount, getConstructionType; from ai.constructions import AIConstructions, ConstructionAIHook, ConstructionUse; import ai.consider; import buildings; import saving; final class BuildingRequest : IBuildingConstruction { protected int _id = -1; PlanetAI@ plAI; AllocateBudget@ alloc; const BuildingType@ type; double expires = INFINITY; bool built = false; bool canceled = false; bool scatter = false; vec2i builtAt; BuildingRequest(Budget& budget, const BuildingType@ type, double priority, uint moneyType) { @this.type = type; @alloc = budget.allocate(moneyType, type.buildCostEst, type.maintainCostEst, priority=priority); } BuildingRequest() { } int id { get const { return _id; } set { _id = value; } } bool get_started() const { return built; } bool completed { get const { return getProgress() >= 1.0; } set { } } const BuildingType@ get_building() const { return type; } void save(Planets& planets, SaveFile& file) { planets.saveAI(file, plAI); planets.budget.saveAlloc(file, alloc); file.writeIdentifier(SI_Building, type.id); file << expires; file << built; file << canceled; file << builtAt; file << scatter; } void load(Planets& planets, SaveFile& file) { @plAI = planets.loadAI(file); @alloc = planets.budget.loadAlloc(file); @type = getBuildingType(file.readIdentifier(SI_Building)); file >> expires; file >> built; file >> canceled; file >> builtAt; if(file >= SV_0153) file >> scatter; } double cachedProgress = 0.0; double nextProgressCache = 0.0; double getProgress() { if(!built) return 0.0; if(gameTime < nextProgressCache) return cachedProgress; cachedProgress = plAI.obj.getBuildingProgressAt(builtAt.x, builtAt.y); if(cachedProgress > 0.95) nextProgressCache = gameTime + 1.0; else if(cachedProgress < 0.5) nextProgressCache = gameTime + 30.0; else nextProgressCache = gameTime + 10.0; return cachedProgress; } bool tick(AI& ai, Planets& planets, double time) { if(expires < gameTime) { if(planets.log) ai.print(type.name+" build request expired", plAI.obj); canceled = true; return false; } if(alloc is null || alloc.allocated) { builtAt = plAI.buildBuilding(ai, planets, type, scatter=scatter); if(builtAt == vec2i(-1,-1)) { planets.budget.remove(alloc); canceled = true; } else built = true; return false; } return true; } }; final class ConstructionRequest : IGenericConstruction { protected int _id = -1; PlanetAI@ plAI; AllocateBudget@ alloc; const ConstructionType@ type; double expires = INFINITY; bool built = false; bool canceled = false; vec2i builtAt; ConstructionRequest(Budget& budget, Object@ buildAt, const ConstructionType@ type, double priority, uint moneyType) { @this.type = type; @alloc = budget.allocate(moneyType, type.getBuildCost(buildAt), type.getMaintainCost(buildAt), priority=priority); } ConstructionRequest() { } int id { get const { return _id; } set { _id = value; } } bool get_started() const { return built; } bool completed { get const { double progress = getProgress(); return progress == -1.0 || progress >= 1.0; } set { } } const ConstructionType@ get_construction() const { return type; } void save(Planets& planets, SaveFile& file) { planets.saveAI(file, plAI); planets.budget.saveAlloc(file, alloc); file.writeIdentifier(SI_ConstructionType, type.id); file << expires; file << built; file << canceled; file << builtAt; } void load(Planets& planets, SaveFile& file) { @plAI = planets.loadAI(file); @alloc = planets.budget.loadAlloc(file); @type = getConstructionType(file.readIdentifier(SI_ConstructionType)); file >> expires; file >> built; file >> canceled; file >> builtAt; } double cachedProgress = 0.0; double nextProgressCache = 0.0; double getProgress() { if(!built) return 0.0; if(gameTime < nextProgressCache) return cachedProgress; cachedProgress = plAI.obj.get_constructionProgress(); if(cachedProgress > 0.95) nextProgressCache = gameTime + 1.0; else if(cachedProgress < 0.5) nextProgressCache = gameTime + 30.0; else nextProgressCache = gameTime + 10.0; return cachedProgress; } bool tick(AI& ai, Planets& planets, double time) { if(expires < gameTime) { if(planets.log) ai.print(type.name+" construction request expired", plAI.obj); canceled = true; return false; } if(alloc is null || alloc.allocated) { if(!plAI.buildConstruction(ai, planets, type)) { planets.budget.remove(alloc); canceled = true; } else built = true; return false; } return true; } }; final class PlanetAI { Planet@ obj; int targetLevel = 0; int requestedLevel = 0; double prevTick = 0; array<ExportData@>@ resources; ImportData@ claimedChain; void init(AI& ai, Planets& planets) { @resources = planets.resources.availableResource(obj); planets.events.notifyPlanetAdded(this, EventArgs()); } void save(Planets& planets, SaveFile& file) { file << obj; file << targetLevel; file << requestedLevel; file << prevTick; uint cnt = 0; if(resources !is null) cnt = resources.length; file << cnt; for(uint i = 0; i < cnt; ++i) planets.resources.saveExport(file, resources[i]); planets.resources.saveImport(file, claimedChain); } void load(Planets& planets, SaveFile& file) { file >> obj; file >> targetLevel; file >> requestedLevel; file >> prevTick; uint cnt = 0; file >> cnt; @resources = array<ExportData@>(); for(uint i = 0; i < cnt; ++i) { auto@ data = planets.resources.loadExport(file); if(data !is null) resources.insertLast(data); } @claimedChain = planets.resources.loadImport(file); } void remove(AI& ai, Planets& planets) { if(claimedChain !is null) { claimedChain.claimedFor = false; @claimedChain = null; } @resources = null; planets.events.notifyPlanetRemoved(this, EventArgs()); } void tick(AI& ai, Planets& planets, double time) { //Deal with losing planet ownership if(obj is null || !obj.valid || obj.owner !is ai.empire) { if(obj.owner !is ai.empire) relationRecordLost(ai, obj.owner, obj); planets.remove(this); return; } //Handle when the planet's native resources change if(obj.nativeResourceCount != resources.length || (resources.length != 0 && obj.primaryResourceId != resources[0].resourceId)) planets.updateResourceList(obj, resources); //Level up resources if we need them if(resources.length != 0 && claimedChain is null) { int resLevel = resources[0].resource.level; if(resLevel > 0 && !resources[0].resource.exportable) resLevel += 1; if(targetLevel < resLevel) { //See if we need it for anything first @claimedChain = planets.resources.findUnclaimed(resources[0]); if(claimedChain !is null) claimedChain.claimedFor = true; //Chain the levelup before what needs it planets.requestLevel(this, resLevel, before=claimedChain); } } //Request imports if the planet needs to level up if(targetLevel > requestedLevel) { int nextLevel = min(targetLevel, min(obj.resourceLevel, requestedLevel)+1); if(nextLevel != requestedLevel) { planets.resources.organizeImports(obj, nextLevel); requestedLevel = nextLevel; } } else if(targetLevel < requestedLevel) { planets.resources.organizeImports(obj, targetLevel); requestedLevel = targetLevel; } } double get_colonizeWeight() { if(obj.isColonizing) return 0.0; if(obj.level == 0) return 0.0; if(!obj.canSafelyColonize) return 0.0; double w = 1.0; double pop = obj.population; double maxPop = obj.maxPopulation; if(pop < maxPop-0.1) { if(obj.resourceLevel > 1 && pop/maxPop < 0.9) return 0.0; w *= 0.01 * (pop / maxPop); } return w; } vec2i buildBuilding(AI& ai, Planets& planets, const BuildingType@ type, bool scatter = true) { if(type is null || !type.canBuildOn(obj)) return vec2i(-1,-1); if(planets.log) ai.print("Attempt to construct "+type.name, obj); PlanetSurface@ surface = planets.surface; receive(obj.getPlanetSurface(), surface); //Find the best place to build this building int bestPenalty = INT_MAX; int possibs = 0; vec2i best; vec2i center = vec2i(type.getCenter()); for(int x = 0, w = surface.size.x; x < w; ++x) { for(int y = 0, h = surface.size.y; y < h; ++y) { vec2i pos(x, y); bool valid = true; int penalty = 0; for(int xoff = 0; xoff < int(type.size.x); ++xoff) { for(int yoff = 0; yoff < int(type.size.y); ++yoff) { vec2i rpos = pos - center + vec2i(xoff, yoff); if(rpos.x < 0 || rpos.y < 0 || rpos.x >= w || rpos.y >= h) { valid = false; break; } auto@ biome = surface.getBiome(rpos.x, rpos.y); if(biome is null || !biome.buildable) { valid = false; break; } uint flags = surface.getFlags(rpos.x, rpos.y); if(flags & SuF_Usable == 0) { bool affinity = false; if(type.buildAffinities.length != 0) { for(uint i = 0, cnt = type.buildAffinities.length; i < cnt; ++i) { if(biome is type.buildAffinities[i].biome) { affinity = true; break; } } } if(!affinity && type.tileBuildCost > 0) { penalty += 1; if(biome.buildCost > 1.0) penalty += ceil((biome.buildCost - 1.0) / 0.1); } affinity = false; if(type.maintainAffinities.length != 0) { for(uint i = 0, cnt = type.maintainAffinities.length; i < cnt; ++i) { if(biome is type.maintainAffinities[i].biome) { affinity = true; break; } } } if(!affinity && type.tileMaintainCost > 0) penalty += 2; } auto@ bld = surface.getBuilding(rpos.x, rpos.y); if(bld !is null) { if(bld.type.civilian) { penalty += 2; } else { valid = false; break; } } } if(!valid) break; } if(valid) { if(penalty < bestPenalty) { possibs = 1; bestPenalty = penalty; best = pos; } else if(penalty == bestPenalty && scatter) { possibs += 1; if(randomd() < 1.0 / double(possibs)) best = pos; } } } } if(bestPenalty != INT_MAX) { if(planets.log) ai.print("Construct "+type.name+" at "+best+" with penalty "+bestPenalty, obj); obj.buildBuilding(type.id, best); return best; } if(planets.log) ai.print("Could not find place to construct "+type.name, obj); return vec2i(-1,-1); } bool buildConstruction(AI& ai, Planets& planets, const ConstructionType@ type) { if(type is null || !type.canBuild(obj)) return false; if(planets.log) ai.print("Construct "+type.name); obj.buildConstruction(type.id); return true; } } final class PotentialSource { Planet@ pl; double weight = 0; }; final class AsteroidData { Asteroid@ asteroid; array<ExportData@>@ resources; void save(Planets& planets, SaveFile& file) { file << asteroid; uint cnt = 0; if(resources !is null) cnt = resources.length; file << cnt; for(uint i = 0; i < cnt; ++i) planets.resources.saveExport(file, resources[i]); } void load(Planets& planets, SaveFile& file) { file >> asteroid; uint cnt = 0; file >> cnt; if(cnt != 0) @resources = array<ExportData@>(); for(uint i = 0; i < cnt; ++i) { auto@ res = planets.resources.loadExport(file); if(res !is null) resources.insertLast(res); } } bool tick(AI& ai, Planets& planets) { if(asteroid is null || !asteroid.valid || asteroid.owner !is ai.empire) { planets.resources.killImportsTo(asteroid); planets.resources.killResourcesFrom(asteroid); return false; } if(resources is null) { @resources = planets.resources.availableResource(asteroid); } else { if(asteroid.nativeResourceCount != resources.length || (resources.length != 0 && asteroid.primaryResourceId != resources[0].resourceId)) planets.updateResourceList(asteroid, resources); } return true; } }; class Planets : AIComponent, AIConstructions { Events@ events; Resources@ resources; Budget@ budget; Systems@ systems; PlanetSurface surface; array<AsteroidData@> ownedAsteroids; array<PlanetAI@> planets; array<PlanetAI@> bumped; uint planetIdx = 0; array<BuildingRequest@> building; int nextBuildingRequestId = 0; array<ConstructionRequest@> constructionRequests; int nextConstructionRequestId = 0; void create() { @events = cast<Events>(ai.events); @resources = cast<Resources>(ai.resources); @budget = cast<Budget>(ai.budget); @systems = cast<Systems>(ai.systems); //Register specialized construction types for(uint i = 0, cnt = getConstructionTypeCount(); i < cnt; ++i) { auto@ type = getConstructionType(i); for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) { auto@ hook = cast<ConstructionAIHook>(type.ai[n]); if(hook !is null) hook.register(this, type); } } } Empire@ get_empire() { return ai.empire; } Considerer@ get_consider() { return cast<Considerer>(ai.consider); } void registerUse(ConstructionUse use, const ConstructionType& type) { switch(use) { case CU_MoonBase: @ai.defs.MoonBase = type; break; } } void save(SaveFile& file) { file << nextBuildingRequestId; file << nextConstructionRequestId; uint cnt = planets.length; file << cnt; for(uint i = 0; i < cnt; ++i) { auto@ plAI = planets[i]; saveAI(file, plAI); plAI.save(this, file); } cnt = building.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveBuildingRequest(file, building[i]); building[i].save(this, file); } cnt = constructionRequests.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveConstructionRequest(file, constructionRequests[i]); constructionRequests[i].save(this, file); } cnt = ownedAsteroids.length; file << cnt; for(uint i = 0; i < cnt; ++i) ownedAsteroids[i].save(this, file); } void load(SaveFile& file) { file >> nextBuildingRequestId; file >> nextConstructionRequestId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ plAI = loadAI(file); if(plAI !is null) plAI.load(this, file); else PlanetAI().load(this, file); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ req = loadBuildingRequest(file); if(req !is null) { req.load(this, file); building.insertLast(req); } } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ req = loadConstructionRequest(file); if (req !is null) { req.load(this, file); constructionRequests.insertLast(req); } } file >> cnt; for(uint i = 0; i < cnt; ++i) { AsteroidData data; data.load(this, file); if(data.asteroid !is null) ownedAsteroids.insertLast(data); } } PlanetAI@ loadAI(SaveFile& file) { Planet@ obj; file >> obj; if(obj is null) return null; PlanetAI@ plAI = getAI(obj); if(plAI is null) { @plAI = PlanetAI(); @plAI.obj = obj; plAI.prevTick = gameTime; planets.insertLast(plAI); } return plAI; } void saveAI(SaveFile& file, PlanetAI@ ai) { Planet@ pl; if(ai !is null) @pl = ai.obj; file << pl; } array<BuildingRequest@> buildingLoadIds; BuildingRequest@ loadBuildingRequest(int id) { if(id == -1) return null; for(uint i = 0, cnt = buildingLoadIds.length; i < cnt; ++i) { if(buildingLoadIds[i].id == id) return buildingLoadIds[i]; } BuildingRequest data; data.id = id; buildingLoadIds.insertLast(data); return data; } BuildingRequest@ loadBuildingRequest(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadBuildingRequest(id); } void saveBuildingRequest(SaveFile& file, BuildingRequest@ data) { int id = -1; if(data !is null) id = data.id; file << id; } array<ConstructionRequest@> constructionLoadIds; ConstructionRequest@ loadConstructionRequest(int id) { if(id == -1) return null; for(uint i = 0, cnt = constructionLoadIds.length; i < cnt; ++i) { if(constructionLoadIds[i].id == id) return constructionLoadIds[i]; } ConstructionRequest data; data.id = id; constructionLoadIds.insertLast(data); return data; } ConstructionRequest@ loadConstructionRequest(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadConstructionRequest(id); } void saveConstructionRequest(SaveFile& file, ConstructionRequest@ data) { int id = -1; if(data !is null) id = data.id; file << id; } void postLoad(AI& ai) { buildingLoadIds.length = 0; constructionLoadIds.length= 0; } void start() { checkForPlanets(); } void checkForPlanets() { auto@ data = ai.empire.getPlanets(); Object@ obj; while(receive(data, obj)) { Planet@ pl = cast<Planet>(obj); if(pl !is null) register(cast<Planet>(obj)); } } uint roidIdx = 0; void tick(double time) { double curTime = gameTime; if(planets.length != 0) { planetIdx = (planetIdx+1) % planets.length; auto@ plAI = planets[planetIdx]; plAI.tick(ai, this, curTime - plAI.prevTick); plAI.prevTick = curTime; } for(int i = bumped.length-1; i >= 0; --i) { auto@ plAI = bumped[i]; double tickTime = curTime - plAI.prevTick; if(tickTime != 0) { plAI.tick(ai, this, tickTime); plAI.prevTick = curTime; } } bumped.length = 0; if(ownedAsteroids.length != 0) { roidIdx = (roidIdx+1) % ownedAsteroids.length; if(!ownedAsteroids[roidIdx].tick(ai, this)) ownedAsteroids.removeAt(roidIdx); } //Construct any buildings we are waiting on for(uint i = 0, cnt = building.length; i < cnt; ++i) { if(!building[i].tick(ai, this, time)) { building.removeAt(i); --i; --cnt; break; } } //Construct any constructions we are waiting on for(uint i = 0, cnt = constructionRequests.length; i < cnt; ++i) { if(!constructionRequests[i].tick(ai, this, time)) { constructionRequests.removeAt(i); --i; --cnt; break; } } } uint prevCount = 0; double checkTimer = 0; uint sysIdx = 0, ownedIdx = 0; void focusTick(double time) override { //Check for any newly obtained planets uint curCount = ai.empire.planetCount; checkTimer += time; if(curCount != prevCount || checkTimer > 60.0) { checkForPlanets(); prevCount = curCount; checkTimer = 0; } //Find any asteroids we've gained if(systems.all.length != 0) { sysIdx = (sysIdx+1) % systems.all.length; auto@ sys = systems.all[sysIdx]; for(uint i = 0, cnt = sys.asteroids.length; i < cnt; ++i) register(sys.asteroids[i]); } if(systems.owned.length != 0) { ownedIdx = (ownedIdx+1) % systems.owned.length; auto@ sys = systems.owned[ownedIdx]; for(uint i = 0, cnt = sys.asteroids.length; i < cnt; ++i) register(sys.asteroids[i]); } } void bump(Planet@ pl) { if(pl !is null) bump(getAI(pl)); } void bump(PlanetAI@ plAI) { if(plAI !is null) bumped.insertLast(plAI); } PlanetAI@ getAI(Planet& obj) { for(uint i = 0, cnt = planets.length; i < cnt; ++i) { if(planets[i].obj is obj) return planets[i]; } return null; } PlanetAI@ register(Planet& obj) { PlanetAI@ plAI = getAI(obj); if(plAI is null) { @plAI = PlanetAI(); @plAI.obj = obj; plAI.prevTick = gameTime; planets.insertLast(plAI); plAI.init(ai, this); } return plAI; } AsteroidData@ register(Asteroid@ obj) { if(obj is null || !obj.valid || obj.owner !is ai.empire) return null; for(uint i = 0, cnt = ownedAsteroids.length; i < cnt; ++i) { if(ownedAsteroids[i].asteroid is obj) return ownedAsteroids[i]; } AsteroidData data; @data.asteroid = obj; ownedAsteroids.insertLast(data); if(log) ai.print("Detected asteroid: "+obj.name, obj.region); return data; } void remove(PlanetAI@ plAI) { resources.killImportsTo(plAI.obj); resources.killResourcesFrom(plAI.obj); plAI.remove(ai, this); planets.remove(plAI); bumped.remove(plAI); } void requestLevel(PlanetAI@ plAI, int toLevel, ImportData@ before = null) { if(plAI is null) return; plAI.targetLevel = toLevel; if(before !is null) { for(int lv = max(plAI.requestedLevel, 1); lv <= toLevel; ++lv) resources.organizeImports(plAI.obj, lv, before); plAI.requestedLevel = toLevel; } else { bump(plAI); } } BuildingRequest@ requestBuilding(PlanetAI@ plAI, const BuildingType@ type, double priority = 1.0, double expire = INFINITY, bool scatter = true, uint moneyType = BT_Development) { if(plAI is null) return null; if(log) ai.print("Requested building of type "+type.name, plAI.obj); BuildingRequest req(budget, type, priority, moneyType); req.scatter = scatter; req.id = nextBuildingRequestId++; req.expires = gameTime + expire; @req.plAI = plAI; building.insertLast(req); return req; } ConstructionRequest@ requestConstruction(PlanetAI@ plAI, Object@ buildAt, const ConstructionType@ type, double priority = 1.0, double expire = INFINITY, uint moneyType = BT_Development) { if(plAI is null) return null; if(log) ai.print("Requested construction of type "+type.name, plAI.obj); ConstructionRequest req(budget, buildAt, type, priority, moneyType); req.id = nextConstructionRequestId++; req.expires = gameTime + expire; @req.plAI = plAI; constructionRequests.insertLast(req); return req; } bool isBuilding(Planet@ planet, const BuildingType@ type) { for(uint i = 0, cnt = building.length; i < cnt; ++i) { if(building[i].type is type && building[i].plAI.obj is planet) return true; } return false; } bool isBuilding(Planet@ planet, const ConstructionType@ type) { for(uint i = 0, cnt = constructionRequests.length; i < cnt; ++i) { if(constructionRequests[i].type is type && constructionRequests[i].plAI.obj is planet) return true; } return false; } void getColonizeSources(array<PotentialSource@>& sources) { sources.length = 0; for(uint i = 0, cnt = planets.length; i < cnt; ++i) { auto@ plAI = planets[i]; if(!plAI.obj.valid) continue; double w = plAI.colonizeWeight; if(w == 0) continue; if(plAI.obj.owner !is ai.empire) continue; PotentialSource src; @src.pl = planets[i].obj; src.weight = w; sources.insertLast(src); } } array<ExportData@> newResources; array<ExportData@> removedResources; array<Resource> checkResources; void updateResourceList(Object@ obj, array<ExportData@>& resList) { newResources.length = 0; removedResources = resList; checkResources.syncFrom(obj.getNativeResources()); uint nativeCnt = checkResources.length; for(uint i = 0; i < nativeCnt; ++i) { int id = checkResources[i].id; bool found = false; for(uint n = 0, ncnt = removedResources.length; n < ncnt; ++n) { if(removedResources[n].resourceId == id) { removedResources.removeAt(n); found = true; break; } } if(!found) { auto@ type = checkResources[i].type; auto@ res = resources.availableResource(obj, type, id); if(i == 0) resList.insertAt(0, res); else resList.insertLast(res); newResources.insertLast(res); } else if(i == 0 && resList.length > 1 && resList[0].resourceId != id) { for(uint n = 0, ncnt = resList.length; n < ncnt; ++n) { if(resList[n].resourceId == id) { auto@ res = resList[n]; resList.removeAt(n); resList.insertAt(0, res); break; } } } } //Get rid of resources we no longer have for(uint i = 0, cnt = removedResources.length; i < cnt; ++i) { resources.removeResource(removedResources[i]); resList.remove(removedResources[i]); } //Tell the resources component to try to immediately use the new resources for(uint i = 0, cnt = newResources.length; i < cnt; ++i) resources.checkReplaceCurrent(newResources[i]); } }; AIComponent@ createPlanets() { return Planets(); } |
Added scripts/server/empire_ai/weasel/Relations.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 |
// Relations // --------- // Manages the relationships we have with other empires, including treaties, hatred, and wars. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Intelligence; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import empire_ai.weasel.Planets; import warandpeace; import influence; from influence_global import activeTreaties, influenceLock, joinTreaty, leaveTreaty, declineTreaty, Treaty, sendPeaceOffer, createTreaty, offerSurrender, demandSurrender, leaveTreatiesWith; enum HateType { HT_SystemPresence, HT_FleetPresence, HT_COUNT }; class Hate { uint type; double amount = 0.0; Object@ obj; SystemAI@ sys; void save(Relations& relations, SaveFile& file) { file << type; file << amount; file << obj; relations.systems.saveAI(file, sys); } void load(Relations& relations, SaveFile& file) { file >> type; file >> amount; file >> obj; @sys = relations.systems.loadAI(file); } bool get_valid() { if(type == HT_SystemPresence) return sys !is null; if(type == HT_FleetPresence) return obj !is null && sys !is null; return true; } bool update(AI& ai, Relations& relations, Relation& rel, double time) { if(type == HT_SystemPresence) { amount = 0.25; if(sys.seenPresent & rel.empire.mask == 0) return false; if(sys.seenPresent & ai.empire.mask == 0) return false; } else if(type == HT_FleetPresence) { if(!obj.valid || obj.owner !is rel.empire) return false; if(sys.seenPresent & ai.empire.mask == 0) return false; if(obj.region !is sys.obj) return false; if(obj.getFleetStrength() < 1000.0) amount = 0.1; else amount = 0.5; } rel.hate += amount * time; return true; } string dump() { switch(type) { case HT_SystemPresence: return "system presence in "+sys.obj.name; case HT_FleetPresence: return "fleet presence "+obj.name+" in "+sys.obj.name; } return "unknown"; } }; final class Relation { Empire@ empire; //Whether we've met this empire bool contacted = false; //Whether we're currently at war bool atWar = false; //Last time we tried to make peace double lastPeaceTry = 0; //Whether this is our war of aggression bool aggressive = false; //Whether this is our ally bool allied = false; //Our relationship data double hate = 0.0; array<Hate@> hates; //Masks uint borderedTo = 0; uint alliedTo = 0; //Whether we consider this empire a threat to us bool isThreat = false; //How much we would value having this empire as an ally double allyValue = 0.0; //How much we think we can beat this empire and all its allies double defeatable = 0.0; //Relative strength of this empire to us in a vacuum double relStrength = 0.0; //How much we've lost to them in this recent war double warLost = 0.0; //How much we've taken from them in this recent war double warTaken = 0.0; void save(Relations& relations, SaveFile& file) { file << contacted; file << atWar; file << aggressive; file << allied; file << hate; uint cnt = hates.length; file << cnt; for(uint i = 0; i < cnt; ++i) hates[i].save(relations, file); file << borderedTo; file << alliedTo; file << isThreat; file << allyValue; file << defeatable; file << relStrength; file << warTaken; file << warLost; file << lastPeaceTry; } void load(Relations& relations, SaveFile& file) { file >> contacted; file >> atWar; file >> aggressive; file >> allied; file >> hate; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { Hate ht; ht.load(relations, file); if(ht.valid) hates.insertLast(ht); } file >> borderedTo; file >> alliedTo; file >> isThreat; file >> allyValue; file >> defeatable; file >> relStrength; file >> warTaken; file >> warLost; file >> lastPeaceTry; } void trackSystem(AI& ai, Relations& relations, SystemAI@ sys) { for(uint i = 0, cnt = hates.length; i < cnt; ++i) { auto@ ht = hates[i]; if(ht.type != HT_SystemPresence) continue; if(ht.sys is sys) return; } Hate ht; ht.type = HT_SystemPresence; @ht.sys = sys; hates.insertLast(ht); if(relations.log) ai.print("Gain hate of "+empire.name+": "+ht.dump()); } void trackFleet(AI& ai, Relations& relations, FleetIntel@ intel, SystemAI@ sys) { for(uint i = 0, cnt = hates.length; i < cnt; ++i) { auto@ ht = hates[i]; if(ht.type != HT_FleetPresence) continue; if(ht.obj is intel.obj && ht.sys is sys) return; } Hate ht; ht.type = HT_FleetPresence; @ht.sys = sys; @ht.obj = intel.obj; hates.insertLast(ht); if(relations.log) ai.print("Gain hate of "+empire.name+": "+ht.dump()); } void tick(AI& ai, Relations& relations, double time) { if(!contacted) { if(ai.empire.ContactMask & empire.mask != 0) contacted = true; } bool curWar = ai.empire.isHostile(empire); if(curWar != atWar) atWar = curWar; if(!atWar) { aggressive = false; warLost = 0.0; warTaken = 0.0; lastPeaceTry = 0.0; } borderedTo = relations.intelligence.get(empire).borderedTo; alliedTo = empire.mask | empire.mutualDefenseMask | empire.ForcedPeaceMask.value; defeatable = relations.intelligence.defeatability(alliedTo, ai.mask | ai.allyMask); relStrength = relations.intelligence.defeatability(ai.mask, empire.mask); isThreat = defeatable < 0.8 && (borderedTo & ai.empire.mask) != 0; //Check how valuable of an ally this empire would make allyValue = 1.0; for(uint i = 0, cnt = relations.relations.length; i < cnt; ++i) { auto@ other = relations.relations[i]; if(other is null || other is this || other.empire is null) continue; if(other.borderedTo & empire.mask == 0) continue; if(alliedTo & empire.mask != 0) continue; if(other.atWar) allyValue *= 3.0; else if(other.isThreat) allyValue *= 1.5; } //Become aggressive here if we're aggressive against one of its allies if(atWar && !aggressive) { for(uint i = 0, cnt = relations.relations.length; i < cnt; ++i) { auto@ other = relations.relations[i]; if(other is null || other is this || other.empire is null) continue; if(other.aggressive && this.alliedTo & other.empire.mask != 0) { aggressive = true; break; } } } //Update our hatred of them for(uint i = 0, cnt = hates.length; i < cnt; ++i) { if(!hates[i].update(ai, relations, this, time)) { if(relations.log) ai.print("Hate with "+empire.name+" expired: "+hates[i].dump()); hates.removeAt(i); --i; --cnt; } } hate *= pow(ai.behavior.hateDecayRate, time / 60.0); if(ai.behavior.biased && !empire.isAI) hate += 1.0; if(ai.behavior.forbidDiplomacy) return; //If we really really hate them, declare war if(!atWar || !aggressive) { double reqHate = 100.0; if(defeatable < 1.0) reqHate *= sqr(1.0 / defeatable); reqHate *= pow(2.0, relations.warCount()); if(hate > reqHate && (!ai.behavior.passive || atWar) && defeatable >= ai.behavior.hatredWarOverkill) { //Make sure our other requirements for war are met if(relations.fleets.haveCombatReadyFleets()) { if(canDeclareWar(ai)) { if(relations.log) ai.print("Declaring hatred war on "+empire.name+": "+hate+" / "+reqHate); if(atWar) aggressive = true; else relations.declareWar(empire, aggressive=true); } } } } } bool isAllied(AI& ai) { return alliedTo & ai.empire.mask != 0; } bool canDeclareWar(AI& ai) { if(empire.SubjugatedBy !is null) return false; if(ai.empire.SubjugatedBy !is null) return false; if(!contacted) return false; if(ai.empire.ForcedPeaceMask & empire.mask != 0) return false; return true; } }; class Relations : AIComponent { Intelligence@ intelligence; Systems@ systems; Fleets@ fleets; Planets@ planets; array<Relation@> relations; bool expansionLocked = false; double treatyRespond = 0; double treatyConsider = 0; double warPoints = 0.0; void create() { @intelligence = cast<Intelligence>(ai.intelligence); @fleets = cast<Fleets>(ai.fleets); @systems = cast<Systems>(ai.systems); @planets = cast<Planets>(ai.planets); } void start() { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(emp is ai.empire) continue; if(!emp.major) continue; Relation r; @r.empire = emp; if(relations.length <= uint(emp.index)) relations.length = uint(emp.index)+1; @relations[emp.index] = r; } } void save(SaveFile& file) { uint cnt = relations.length; file << cnt; for(uint i = 0; i < cnt; ++i) { if(relations[i] is null) { file.write0(); continue; } file.write1(); relations[i].save(this, file); } file << expansionLocked; file << treatyRespond; file << treatyConsider; } void load(SaveFile& file) { uint cnt = 0; file >> cnt; relations.length = cnt; for(uint i = 0; i < cnt; ++i) { if(!file.readBit()) continue; @relations[i] = Relation(); @relations[i].empire = getEmpire(i); relations[i].load(this, file); } file >> expansionLocked; file >> treatyRespond; file >> treatyConsider; } double getPointValue(Object@ obj) { if(obj is null) return 0.0; if(obj.isShip) { auto@ dsg = cast<Ship>(obj).blueprint.design; if(dsg !is null) return dsg.size; } else if(obj.isPlanet) { return 10.0 * pow(3.0, double(obj.level)); } return 0.0; } void recordTakenFrom(Empire& emp, double amount) { if(!emp.valid) return; if(log) ai.print("Taken value "+amount+" from "+emp.name); auto@ rel = get(emp); if(rel !is null) rel.warTaken += amount; } void recordLostTo(Empire& emp, double amount) { if(!emp.valid) return; if(log) ai.print("Lost value "+amount+" to "+emp.name); auto@ rel = get(emp); if(rel !is null) rel.warLost += amount; } void recordLostTo(Empire& emp, Object@ obj) { recordLostTo(emp, getPointValue(obj)); } void recordTakenFrom(Empire& emp, Object@ obj) { recordTakenFrom(emp, getPointValue(obj)); } Relation@ get(Empire@ emp) { if(emp is null) return null; if(!emp.major) return null; if(uint(emp.index) >= relations.length) return null; return relations[emp.index]; } bool isFightingWar(bool aggressive = false) { for(uint i = 0, cnt = relations.length; i < cnt; ++i) { if(relations[i] is null) continue; if(relations[i].atWar) { if(!aggressive || relations[i].aggressive) return true; } } return false; } uint warCount() { uint count = 0; for(uint i = 0, cnt = relations.length; i < cnt; ++i) { if(relations[i] is null) continue; if(relations[i].atWar) count += 1; } return count; } void declareWar(Empire@ onEmpire, bool aggressive = true) { //Break all treaties leaveTreatiesWith(ai.empire, onEmpire.mask); //Declare actual war auto@ rel = get(onEmpire); rel.aggressive = aggressive; ::declareWar(ai.empire, onEmpire); } uint sysIdx = 0; uint relIdx = 0; void tick(double time) override { //Find new ways to hate other empires if(systems.all.length != 0) { sysIdx = (sysIdx+1) % systems.all.length; auto@ sys = systems.all[sysIdx]; if(sys.owned && sys.seenPresent & ~ai.mask != 0) { for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ rel = relations[i]; if(rel is null) continue; if(sys.seenPresent & rel.empire.mask != 0) rel.trackSystem(ai, this, sys); } } } if(relations.length != 0) { relIdx = (relIdx+1) % relations.length; auto@ rel = relations[relIdx]; auto@ itl = intelligence.intel[relIdx]; if(rel !is null && itl !is null) { for(uint i = 0, cnt = itl.fleets.length; i < cnt; ++i) { if(!itl.fleets[i].visible) continue; auto@ inSys = systems.getAI(itl.fleets[i].obj.region); if(inSys !is null && inSys.owned) rel.trackFleet(ai, this, itl.fleets[i], inSys); } } } } uint relInd = 0; void focusTick(double time) override { //Update our current relations if(relations.length != 0) { relInd = (relInd+1) % relations.length; if(relations[relInd] !is null) relations[relInd].tick(ai, this, time); } //Compute how many points we have in total that can be taken warPoints = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { Ship@ ship = cast<Ship>(fleets.fleets[i].obj); if(ship !is null && ship.valid && ship.owner is ai.empire) warPoints += getPointValue(ship); } for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) { Planet@ pl = cast<Planet>(planets.planets[i].obj); if(pl !is null && pl.valid && pl.owner is ai.empire) warPoints += getPointValue(pl); } //Become aggressive if we cannot expand anywhere anymore expansionLocked = true; for(uint i = 0, cnt = systems.outsideBorder.length; i < cnt; ++i) { auto@ sys = systems.outsideBorder[i]; if(sys.seenPresent == 0) { bool havePlanets = false; for(uint n = 0, ncnt = sys.planets.length; n < ncnt; ++n) { if(sys.planets[n].quarantined) continue; havePlanets = true; break; } if(havePlanets) { expansionLocked = false; break; } } } if(ai.behavior.forbidDiplomacy) return; //Deal with our AI's aggressive behavior if(ai.behavior.aggressive || (expansionLocked && ai.behavior.aggressiveWhenBoxedIn && !ai.behavior.passive)) { //Try to make sure we're always fighting at least one aggressive war bool atWar = false, aggro = false; for(uint i = 0, cnt = relations.length; i < cnt; ++i) { if(relations[i] is null) continue; if(relations[i].atWar) { atWar = true; if(relations[i].aggressive) aggro = true; } } if(!atWar) { if(fleets.haveCombatReadyFleets()) { //Declare war on people who share our border and are defeatable Empire@ best; double bestWeight = 0; for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ rel = relations[i]; if(rel is null) continue; auto@ intel = intelligence.get(rel.empire); if(intel.shared.length == 0 && intel.theirBorder.length == 0) continue; if(!rel.canDeclareWar(ai)) continue; if(!ai.behavior.biased || rel.empire.isAI) { if(rel.defeatable < ai.behavior.aggressiveWarOverkill) continue; } double w = rel.defeatable * rel.hate; if(rel.isAllied(ai)) w *= 0.01; if(w > bestWeight) { bestWeight = w; @best = rel.empire; } } if(best !is null) { if(log) ai.print("Declare aggressive war against "+best.name); declareWar(best, aggressive=true); } } } else if(!aggro) { //Start going aggressive on someone defeatable we are already at war with Empire@ best; double bestWeight = 0; for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ rel = relations[i]; if(rel is null) continue; if(!rel.atWar) continue; if(rel.defeatable < ai.behavior.aggressiveWarOverkill) continue; double w = rel.defeatable * rel.hate; if(w > bestWeight) { bestWeight = w; @best = rel.empire; } } if(best !is null) { //Go aggressive then! if(log) ai.print("Become aggressive against "+best.name); get(best).aggressive = true; } } } //Respond to treaties if(gameTime > treatyRespond) { treatyRespond = gameTime + randomd(8.0, 20.0); Treaty@ respondTreaty; { Lock lck(influenceLock); for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) { auto@ trty = activeTreaties[i]; if(trty.inviteMask & ai.mask != 0 && trty.presentMask & ai.mask == 0) { Message msg; trty.write(msg); @respondTreaty = Treaty(); respondTreaty.read(msg); break; } } } if(respondTreaty !is null) { bool accept = false; Empire@ invitedBy = respondTreaty.leader; if(invitedBy is null) @invitedBy = respondTreaty.joinedEmpires[0]; Relation@ other = get(invitedBy); if(respondTreaty.hasClause("SubjugateClause")) { //This is a surrender offer or demand if(respondTreaty.leader is null) { //An offer accept = true; } else if(respondTreaty.joinedEmpires.length != 0) { //A demand auto@ other = get(respondTreaty.joinedEmpires[0]); if(other.defeatable < ai.behavior.surrenderMinStrength) { if(warPoints / (other.warLost + warPoints) < ai.behavior.acceptSurrenderRatio) { accept = true; } } } } else if(respondTreaty.hasClause("MutualDefenseClause") || respondTreaty.hasClause("AllianceClause")) { //This is an alliance treaty if(other.atWar) { //Need to be at peace first accept = false; } else { //See if this empire can help us defeat someone if(other.allyValue >= 3.0 && other.relStrength >= 0.5) accept = true; } } else if(respondTreaty.hasClause("PeaceClause")) { //This is a peace offering accept = shouldPeace(other); } else if(respondTreaty.hasClause("VisionClause")) { //This is a vision sharing treaty if(other !is null) accept = !other.isThreat && !other.atWar && other.hate <= 50.0; } else if(respondTreaty.hasClause("TradeClause")) { //This is a trade sharing treaty if(other !is null) accept = !other.isThreat && !other.atWar && other.hate <= 10.0; } if(accept) { if(log) ai.print("Accept treaty: "+respondTreaty.name, emp=invitedBy); joinTreaty(ai.empire, respondTreaty.id); } else { if(log) ai.print("Reject treaty: "+respondTreaty.name, emp=invitedBy); declineTreaty(ai.empire, respondTreaty.id); } } } //See if we should send a treaty over to someone if(gameTime > treatyConsider) { treatyConsider = gameTime + randomd(100.0, 300.0); uint offset = randomi(0, relations.length-1); for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ other = relations[(i+offset) % cnt]; if(other is null) continue; //Check if we should make peace with them if(other.atWar) { if(other.lastPeaceTry < gameTime - 600.0 && shouldPeace(other, isOffer=true)) { if(other.aggressive) other.aggressive = false; if(log) ai.print("Send peace offer.", emp=other.empire); other.lastPeaceTry = gameTime; sendPeaceOffer(ai.empire, other.empire); break; } } if(other.atWar) { //Check if we should try to surrender to them if(other.defeatable < ai.behavior.surrenderMinStrength) { if(warPoints / (other.warLost + warPoints) < ai.behavior.offerSurrenderRatio) { if(log) ai.print("Send surrender offer.", emp=other.empire); offerSurrender(ai.empire, other.empire); break; } } //Check if we should try to demand their surrender if(other.defeatable >= 1.0 / ai.behavior.surrenderMinStrength && other.warTaken >= warPoints * 0.1) { if(log) ai.print("Demand surrender.", emp=other.empire); demandSurrender(ai.empire, other.empire); break; } } //Check if we should try to ally with them if(!other.atWar && !other.isThreat && other.allyValue >= 3.0) { Treaty treaty; treaty.addClause(getInfluenceClauseType("AllianceClause")); treaty.addClause(getInfluenceClauseType("VisionClause")); treaty.addClause(getInfluenceClauseType("MutualDefenseClause")); if(treaty.canInvite(ai.empire, other.empire)) { treaty.inviteMask = other.empire.mask; //Generate treaty name string genName; uint genCount = 0; for(uint i = 0, cnt = systemCount; i < cnt; ++i) { auto@ reg = getSystem(i).object; if(reg.TradeMask & (ai.mask | other.empire.mask) != 0) { genCount += 1; if(randomd() < 1.0 / double(genCount)) genName = reg.name; } } treaty.name = format(locale::TREATY_NAME_GEN, genName); if(log) ai.print("Send alliance offer.", emp=other.empire); createTreaty(ai.empire, treaty); } } } } } bool shouldPeace(Relation@ other, bool isOffer = false) { bool accept = false; if(other.aggressive) { //We're trying to conquer these people, don't accept peace unless //we're fighting someone scarier or we're losing double otherWar = 0.0; uint otherInd = uint(-1); for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ rel = relations[i]; if(rel is null || rel is other) continue; if(rel.empire.mask & other.alliedTo != 0) continue; if(!rel.atWar) continue; otherWar = max(otherWar, rel.defeatable); otherInd = i; } if(otherInd != uint(-1) && otherWar < other.defeatable) { accept = true; if(!relations[otherInd].aggressive) relations[otherInd].aggressive = otherWar >= ai.behavior.aggressiveWarOverkill; } else if(other.defeatable < 0.25) { accept = true; } } else { //We don't have any ~particular qualms with these people, peace should be good if(!isOffer) { if(other.defeatable < 0.5 || other.hate < 50.0) accept = true; } } return accept; } void turn() override { if(log) { ai.print("Relations Report on Empires:"); ai.print(" war points: "+warPoints); for(uint i = 0, cnt = relations.length; i < cnt; ++i) { auto@ rel = relations[i]; if(rel is null) continue; ai.print(" "+ai.pad(rel.empire.name, 15) +" war: "+ai.pad(rel.atWar+" / "+rel.aggressive, 15) +" threat: "+ai.pad(""+rel.isThreat, 8) +" defeatable: "+ai.pad(toString(rel.defeatable,2), 8) +" hate: "+ai.pad(toString(rel.hate,0), 8) +" ally value: "+ai.pad(toString(rel.allyValue,1), 8) +" taken: "+ai.pad(toString(rel.warTaken,1), 8) +" lost: "+ai.pad(toString(rel.warLost,1), 8) ); } } } }; AIComponent@ createRelations() { return Relations(); } void relationRecordLost(AI& ai, Empire& emp, Object@ obj) { cast<Relations>(ai.relations).recordLostTo(emp, obj); } |
Added scripts/server/empire_ai/weasel/Research.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
// Research // -------- // Spends research points to unlock and improve things in the research grid. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Development; import research; const double baseAimResearchRate = 2.0; class Research : AIComponent { Development@ development; TechnologyGrid grid; array<TechnologyNode@> immediateQueue; void create() { @development = cast<Development>(ai.development); } void save(SaveFile& file) { uint cnt = immediateQueue.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << immediateQueue[i].id; } void load(SaveFile& file) { updateGrid(); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { int id = 0; file >> id; for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) { if(grid.nodes[i].id == id) { immediateQueue.insertLast(grid.nodes[i]); break; } } } } void updateGrid() { //Receive the full grid from the empire to path on grid.nodes.length = 0; DataList@ recvData = ai.empire.getTechnologyNodes(); TechnologyNode@ node = TechnologyNode(); while(receive(recvData, node)) { grid.nodes.insertLast(node); @node = TechnologyNode(); } grid.regenBounds(); } double getEndPointWeight(const TechnologyType& tech) { //TODO: Might want to make this configurable by data file return 1.0; } bool isEndPoint(const TechnologyType& tech) { return tech.cls >= Tech_BigUpgrade; } double findResearch(int atIndex, array<TechnologyNode@>& path, array<bool>& visited, bool initial = false) { if(visited[atIndex]) return 0.0; visited[atIndex] = true; auto@ node = grid.nodes[atIndex]; if(!initial) { if(node.bought) return 0.0; if(!node.hasRequirements(ai.empire)) return 0.0; path.insertLast(node); if(isEndPoint(node.type)) return getEndPointWeight(node.type); } vec2i startPos = node.position; double totalWeight = 0.0; array<TechnologyNode@> tmp; array<TechnologyNode@> chosen; tmp.reserve(20); chosen.reserve(20); for(uint d = 0; d < 6; ++d) { vec2i otherPos = startPos; if(grid.doAdvance(otherPos, HexGridAdjacency(d))) { int otherIndex = grid.getIndex(otherPos); if(otherIndex != -1) { tmp.length = 0; double w = findResearch(otherIndex, tmp, visited); if(w != 0.0) { totalWeight += w; if(randomd() < w / totalWeight) { chosen = tmp; } } } } } for(uint i = 0, cnt = chosen.length; i < cnt; ++i) path.insertLast(chosen[i]); return max(totalWeight, 0.01); } void queueNewResearch() { if(log) ai.print("Attempted to find new research to queue"); //Update our grid representation updateGrid(); //Find a good path to do array<bool> visited(grid.nodes.length, false); double totalWeight = 0.0; auto@ path = array<TechnologyNode@>(); auto@ tmp = array<TechnologyNode@>(); path.reserve(20); tmp.reserve(20); for(int i = 0, cnt = grid.nodes.length; i < cnt; ++i) { if(grid.nodes[i].bought) { tmp.length = 0; double weight = findResearch(i, tmp, visited, initial=true); if(weight != 0.0) { totalWeight += weight; if(randomd() < weight / totalWeight) { auto@ swp = path; @path = tmp; @tmp = swp; } } } } if(path.length != 0) { for(uint i = 0, cnt = path.length; i < cnt; ++i) { if(log) ai.print("Queue research: "+path[i].type.name+" at "+path[i].position); immediateQueue.insertLast(path[i]); } } } double immTimer = randomd(10.0, 60.0); void focusTick(double time) override { if (ai.behavior.forbidResearch) return; //Queue some new research if we have to if(immediateQueue.length == 0) { immTimer -= time; if(immTimer <= 0.0) { immTimer = 60.0; queueNewResearch(); } } else { immTimer = 0.0; } //Deal with current queued research if(immediateQueue.length != 0) { auto@ node = immediateQueue[0]; if(!receive(ai.empire.getTechnologyNode(node.id), node)) { immediateQueue.removeAt(0); } else if(!node.available || node.bought) { immediateQueue.removeAt(0); } else { double cost = node.getPointCost(ai.empire); if(cost == 0) { //Try it once and then give up ai.empire.research(node.id, secondary=true); immediateQueue.removeAt(0); if(log) ai.print("Attempt secondary research: "+node.type.name+" at "+node.position); } else if(cost <= ai.empire.ResearchPoints) { //If we have enough to buy it, buy it ai.empire.research(node.id); immediateQueue.removeAt(0); if(log) ai.print("Purchase research: "+node.type.name+" at "+node.position); } } } //Update research generation rate goal development.aimResearchRate = clamp(gameTime / (20.0 * 60.0) - 0.5, 0.0, baseAimResearchRate); } }; AIComponent@ createResearch() { return Research(); } |
Added scripts/server/empire_ai/weasel/Resources.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Events; import empire_ai.weasel.ImportData; import ai.events; import resources; import planet_levels; import system_pathing; import systems; from orbitals import OrbitalModule; interface RaceResources { void levelRequirements(Object& obj, int targetLevel, array<ResourceSpec@>& specs); }; final class Resources : AIComponent { Events@ events; RaceResources@ race; array<ImportData@> requested; array<ImportData@> active; int nextImportId = 0; array<ExportData@> available; array<ExportData@> used; int nextExportId = 0; void create() { @events = cast<Events>(ai.events); @race = cast<RaceResources>(ai.race); } void save(SaveFile& file) { file << nextImportId; file << nextExportId; uint cnt = 0; cnt = requested.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveImport(file, requested[i]); file << requested[i]; } cnt = active.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveImport(file, active[i]); file << active[i]; } cnt = available.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveExport(file, available[i]); file << available[i]; saveImport(file, available[i].request); } cnt = used.length; file << cnt; for(uint i = 0; i < cnt; ++i) { saveExport(file, used[i]); file << used[i]; saveImport(file, used[i].request); } } void load(SaveFile& file) { file >> nextImportId; file >> nextExportId; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadImport(file); file >> data; requested.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadImport(file); file >> data; active.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadExport(file); file >> data; @data.request = loadImport(file); available.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadExport(file); file >> data; @data.request = loadImport(file); used.insertLast(data); } } array<ImportData@> importIds; ImportData@ loadImport(int id) { if(id == -1) return null; for(uint i = 0, cnt = importIds.length; i < cnt; ++i) { if(importIds[i].id == id) return importIds[i]; } ImportData data; data.id = id; importIds.insertLast(data); return data; } ImportData@ loadImport(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadImport(id); } void saveImport(SaveFile& file, ImportData@ data) { int id = -1; if(data !is null) id = data.id; file << id; } array<ExportData@> exportIds; ExportData@ loadExport(int id) { if(id == -1) return null; for(uint i = 0, cnt = exportIds.length; i < cnt; ++i) { if(exportIds[i].id == id) return exportIds[i]; } ExportData data; data.id = id; exportIds.insertLast(data); return data; } ExportData@ loadExport(SaveFile& file) { int id = -1; file >> id; if(id == -1) return null; else return loadExport(id); } void saveExport(SaveFile& file, ExportData@ data) { int id = -1; if(data !is null) id = data.id; file << id; } void postLoad(AI& ai) { importIds.length = 0; exportIds.length = 0; } void start() { focusTick(0); } void tick(double time) { } uint checkIdx = 0; void focusTick(double time) { //Do a check to make sure our resource export setup is still correct if(used.length != 0) { checkIdx = (checkIdx+1) % used.length; ExportData@ res = used[checkIdx]; if(res.request !is null && res.request.obj !is null && !res.isExportedTo(res.request.obj)) { if(log) ai.print("Break export to "+res.request.obj.name+": link changed underfoot", res.obj); breakImport(res); } else { bool valid = true; if(res.obj is null || res.obj.owner !is ai.empire || !res.obj.valid) valid = false; //Don't break these imports, we want to wait for the decay to happen else if((res.request is null || !res.request.obj.hasSurfaceComponent || res.request.obj.decayTime <= 0) && !res.obj.isAsteroid && !res.usable) { valid = false; } else if(res.request !is null) { if(res.request.obj.owner !is ai.empire || !res.request.obj.valid) valid = false; } if(!valid) breakImport(res); } } //TODO: Make sure universal unique only applies once per planet //Match requested with available for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ req = requested[i]; req.cycled = true; if(req.obj is null) continue; if(req.beingMet) { ai.print("Error: Requested is being met", req.obj); continue; } ExportData@ source; double sourceWeight = 0.0; for(uint j = 0, jcnt = available.length; j < jcnt; ++j) { auto@ av = available[j]; if(av.request !is null) { ai.print("Error: Available is being used", av.obj); continue; } if(!req.spec.meets(av.resource, av.obj, req.obj)) continue; if(!av.usable || av.obj is null || !av.obj.valid || av.obj.owner !is ai.empire) continue; //Check if a trade route exists between the two locations if(!canTradeBetween(av.obj, req.obj) && av.obj.region !is null && req.obj.region !is null) { auto@ territoryA = av.obj.region.getTerritory(ai.empire); auto@ territoryB = req.obj.region.getTerritory(ai.empire); if (territoryA !is territoryB) { if (log) ai.print("trade route requested between " + addrstr(territoryA) + " and " + addrstr(territoryB)); events.notifyTradeRouteNeeded(this, TradeRouteNeededEventArgs(territoryA, territoryB)); } continue; } if(av.localOnly && av.obj !is req.obj) continue; double weight = 1.0; if(req.obj is av.obj) weight = INFINITY; if(weight > sourceWeight) { sourceWeight = weight; @source = av; } } if(source !is null) { link(req, source); --i; --cnt; } } } void turn() { } bool get_hasOpenRequests() { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ req = requested[i]; if(req.isOpen) return true; } return false; } TradePath tradePather; int tradeDistance(Region& fromRegion, Region& toRegion) { @tradePather.forEmpire = ai.empire; tradePather.generate(getSystem(fromRegion), getSystem(toRegion), keepCache=true); if(!tradePather.valid) return -1; return tradePather.pathSize - 1; } bool canTradeBetween(Object& fromObj, Object& toObj) { Region@ fromRegion = fromObj.region; if(fromRegion is null) return false; Region@ toRegion = toObj.region; if(toRegion is null) return false; return canTradeBetween(fromRegion, toRegion); } bool canTradeBetween(Region& fromRegion, Region& toRegion) { if(fromRegion.sharesTerritory(ai.empire, toRegion)) return true; int dist = tradeDistance(fromRegion, toRegion); if(dist < 0) return false; return true; } void link(ImportData@ req, ExportData@ source) { //Manage the data @source.request = req; @source.developUse = null; req.set(source); requested.remove(req); active.insertLast(req); req.beingMet = true; available.remove(source); used.insertLast(source); if(log) ai.print("link "+source.resource.name+" from "+source.obj.name+" to "+req.obj.name); //Perform the actual export if(source.obj !is req.obj) source.obj.exportResourceByID(source.resourceId, req.obj); else source.obj.exportResourceByID(source.resourceId, null); } ImportData@ requestResource(Object& toObject, ResourceSpec& spec, bool forLevel = false, bool activate = true, bool prioritize = false) { ImportData data; data.idleSince = gameTime; data.id = nextImportId++; @data.obj = toObject; @data.spec = spec; data.forLevel = forLevel; if(log) ai.print("requested resource: "+spec.dump(), toObject); if(activate) { if(prioritize) requested.insertAt(0, data); else requested.insertLast(data); } return data; } ExportData@ availableResource(Object& fromObject, const ResourceType& resource, int id) { ExportData data; data.id = nextExportId++; @data.obj = fromObject; @data.resource = resource; data.resourceId = id; if(log) ai.print("available resource: "+resource.name, fromObject); available.insertLast(data); return data; } void checkReplaceCurrent(ExportData@ res) { //If the planet that this resource is on is currently importing this same resource, switch it around if(res.request !is null) return; for(uint i = 0, cnt = used.length; i < cnt; ++i) { auto@ other = used[i]; auto@ request = other.request; if(request is null) continue; if(request.obj !is res.obj) continue; if(request.spec.meets(res.resource, res.obj, res.obj)) { //Swap the import with using the local resource if(other.resource.exportable) { breakImport(other); link(request, res); return; } } } } array<Resource> checkResources; array<ExportData@>@ availableResource(Object& fromObject) { array<ExportData@> list; checkResources.syncFrom(fromObject.getNativeResources()); uint nativeCount = checkResources.length; for(uint i = 0; i < nativeCount; ++i) { auto@ r = checkResources[i].type; if(r !is null) list.insertLast(availableResource(fromObject, r, checkResources[i].id)); } return list; } ExportData@ findResource(Object@ obj, int resourceId) { for(uint i = 0, cnt = available.length; i < cnt; ++i) { if(available[i].obj is obj && available[i].resourceId == resourceId) return available[i]; } for(uint i = 0, cnt = used.length; i < cnt; ++i) { if(used[i].obj is obj && used[i].resourceId == resourceId) return used[i]; } return null; } ImportData@ findUnclaimed(ExportData@ forResource) { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ req = requested[i]; if(req.claimedFor) continue; if(req.beingMet) continue; if(!req.spec.meets(forResource.resource, forResource.obj, req.obj)) continue; if(!canTradeBetween(req.obj, forResource.obj)) continue; return req; } return null; } void breakImport(ImportData@ data) { if(data.fromObject !is null) { auto@ source = findResource(data.fromObject, data.resourceId); if(source !is null) { breakImport(source); return; } } @data.fromObject = null; data.resourceId = -1; data.beingMet = false; data.idleSince = gameTime; active.remove(data); requested.insertAt(0, data); } void breakImport(ExportData@ data) { if(data.request !is null) { if(data.request.obj !is data.obj) data.obj.exportResource(data.resourceId, null); data.request.beingMet = false; @data.request.fromObject = null; data.request.resourceId = -1; data.request.idleSince = gameTime; active.remove(data.request); requested.insertAt(0, data.request); @data.request = null; } used.remove(data); available.insertLast(data); } void cancelRequest(ImportData@ data) { if(data.beingMet) { breakImport(data); active.remove(data); } else { requested.remove(data); } } void removeResource(ExportData@ data) { if(data.request !is null) { breakImport(data); used.remove(data); @data.obj = null; } else { available.remove(data); @data.obj = null; } } ImportData@ claimImport(ImportData@ data) { data.beingMet = true; requested.remove(data); active.insertLast(data); return data; } void relinquishImport(ImportData@ data) { data.beingMet = false; active.remove(data); requested.insertLast(data); } void organizeImports(Object& obj, int targetLevel, ImportData@ before = null) { //Organize any imports for this object so it tries to get to a particular target level if(log) ai.print("Organizing imports for level", obj, targetLevel); //Get the requirement list const PlanetLevel@ lvl = getPlanetLevel(obj, targetLevel); if(lvl is null) { ai.print("Error: could not find planet level", obj, targetLevel); return; //Welp, can't do nothing here } //Collect all the requests this planet currently has outstanding array<ImportData@> activeRequests; for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ req = requested[i]; if(req.obj !is obj) continue; if(!req.forLevel) continue; activeRequests.insertLast(req); } for(uint i = 0, cnt = active.length; i < cnt; ++i) { auto@ req = active[i]; if(req.obj !is obj) continue; if(!req.forLevel) continue; activeRequests.insertLast(req); } //TODO: This needs to be able to deal with dummy resources //Match import requests with level requirements array<ResourceSpec@> addSpecs; const ResourceRequirements@ reqs = lvl.reqs; for(uint i = 0, cnt = reqs.reqs.length; i < cnt; ++i) { auto@ need = reqs.reqs[i]; for(uint n = 0; n < need.amount; ++n) addSpecs.insertLast(implementSpec(need)); } if(race !is null) race.levelRequirements(obj, targetLevel, addSpecs); for(uint i = 0, cnt = addSpecs.length; i < cnt; ++i) { auto@ spec = addSpecs[i]; bool foundMatch = false; for(uint j = 0, jcnt = activeRequests.length; j < jcnt; ++j) { if(activeRequests[j].spec == spec) { foundMatch = true; activeRequests.removeAt(j); break; } } if(foundMatch) { addSpecs.removeAt(i); --i; --cnt; } } //Cancel any import requests that we don't need anymore for(uint i = 0, cnt = activeRequests.length; i < cnt; ++i) cancelRequest(activeRequests[i]); //Insert any imports above any imports of the planet we're exporting to int place = -1; if(before !is null) { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { if(requested[i] is before) { place = int(i); break; } } } //Insert everything we need to add addSpecs.sortDesc(); for(uint i = 0, cnt = addSpecs.length; i < cnt; ++i) { ImportData@ req = requestResource(obj, addSpecs[i], forLevel=true, activate=false); if(place == -1) { requested.insertLast(req); } else { requested.insertAt(place, req); place += 1; } } } void killImportsTo(Object& obj) { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { if(requested[i].obj is obj) { cancelRequest(requested[i]); --i; --cnt; } } for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].obj is obj) { cancelRequest(active[i]); --i; --cnt; } } } void killResourcesFrom(Object& obj) { for(uint i = 0, cnt = available.length; i < cnt; ++i) { if(available[i].obj is obj) { removeResource(available[i]); --i; --cnt; } } for(uint i = 0, cnt = used.length; i < cnt; ++i) { if(used[i].obj is obj) { removeResource(used[i]); --i; --cnt; } } } ImportData@ getImport(const string& fromName, uint index = 0) { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { if(requested[i].obj.name.equals_nocase(fromName)) { if(index == 0) return requested[i]; else index -= 1; } } for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].obj.name.equals_nocase(fromName)) { if(index == 0) return active[i]; else index -= 1; } } return null; } ExportData@ getExport(const string& fromName, uint index = 0) { for(uint i = 0, cnt = available.length; i < cnt; ++i) { if(available[i].obj.name.equals_nocase(fromName)) { if(index == 0) return available[i]; else index -= 1; } } for(uint i = 0, cnt = used.length; i < cnt; ++i) { if(used[i].obj.name.equals_nocase(fromName)) { if(index == 0) return used[i]; else index -= 1; } } return null; } void getImportsOf(array<ImportData@>& output, uint resType, Planet@ toPlanet = null) { for(uint i = 0, cnt = active.length; i < cnt; ++i) { auto@ req = active[i]; if(req.spec.type != RST_Specific) continue; if(req.spec.resource.id != resType) continue; if(toPlanet !is null && req.obj !is toPlanet) continue; output.insertLast(req); } for(uint i = 0, cnt = requested.length; i < cnt; ++i) { auto@ req = requested[i]; if(req.spec.type != RST_Specific) continue; if(req.spec.resource.id != resType) continue; if(toPlanet !is null && req.obj !is toPlanet) continue; output.insertLast(req); } } void dumpRequests(Object@ forObject = null) { for(uint i = 0, cnt = requested.length; i < cnt; ++i) { if(forObject !is null && requested[i].obj !is forObject) continue; print(requested[i].obj.name+" requests "+requested[i].spec.dump()); } if(forObject !is null) { for(uint i = 0, cnt = used.length; i < cnt; ++i) { if(used[i].request is null || used[i].request.obj !is forObject) continue; print(used[i].request.obj.name+" is getting "+used[i].request.spec.dump()+" from "+used[i].obj.name); } } } }; AIComponent@ createResources() { return Resources(); } |
Added scripts/server/empire_ai/weasel/Scouting.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
// Scouting // -------- // Orders the construction of scouts, explores the galaxy with them and makes // sure we have vision where we need vision, as well as scanning anomalies. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import empire_ai.weasel.Designs; import empire_ai.weasel.Construction; import empire_ai.weasel.Movement; import empire_ai.weasel.Creeping; final class ScoutingMission : Mission { Region@ region; MoveOrder@ move; void save(Fleets& fleets, SaveFile& file) override { file << region; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) override { file >> region; @move = fleets.movement.loadMoveOrder(file); } double getPerformWeight(AI& ai, FleetAI& fleet) { if(fleet.fleetClass != FC_Scout) { if(fleet.fleetClass == FC_Mothership) return 0.0; if(gameTime > ai.behavior.scoutAllTimer) return 0.0; } return 1.0 / region.position.distanceTo(fleet.obj.position); } void start(AI& ai, FleetAI& fleet) override { uint mprior = MP_Background; if(gameTime < 6.0 * 60.0) mprior = MP_Critical; else if(priority > MiP_Normal) mprior = MP_Normal; @move = cast<Movement>(ai.movement).move(fleet.obj, region, mprior); } void tick(AI& ai, FleetAI& fleet, double time) { if(move.failed) canceled = true; if(move.completed) { //We managed to scout this system if(fleet.obj.region !is region) { @move = cast<Movement>(ai.movement).move(fleet.obj, region.position + random3d(400.0)); return; } completed = true; //Detect any anomalies and put them into the scanning queue //TODO: Detect newly created anomalies in systems we already have vision over? if(region.anomalyCount != 0) { auto@ list = region.getAnomalies(); Object@ obj; while(receive(list, obj)) { Anomaly@ anom = cast<Anomaly>(obj); if(anom !is null) cast<Scouting>(ai.scouting).recordAnomaly(anom); } } } } }; final class ScanningMission : Mission { Anomaly@ anomaly; MoveOrder@ move; void save(Fleets& fleets, SaveFile& file) override { file << anomaly; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) override { file >> anomaly; @move = fleets.movement.loadMoveOrder(file); } double getPerformWeight(AI& ai, FleetAI& fleet) { if(fleet.fleetClass != FC_Scout) { if(gameTime > ai.behavior.scoutAllTimer) return 0.0; } return 1.0 / anomaly.position.distanceTo(fleet.obj.position); } void start(AI& ai, FleetAI& fleet) override { uint mprior = MP_Background; if(priority > MiP_Normal) mprior = MP_Normal; @move = cast<Movement>(ai.movement).move(fleet.obj, anomaly, mprior); } void tick(AI& ai, FleetAI& fleet, double time) { if(move !is null) { if(move.failed) { canceled = true; return; } if(move.completed) @move = null; } if(move is null) { if(anomaly is null || !anomaly.valid) { completed = true; return; } if(anomaly.getEmpireProgress(ai.empire) >= 1.f) { uint choose = 0; uint possibs = 0; uint optCnt = anomaly.getOptionCount(); for(uint i = 0; i < optCnt; ++i) { if(anomaly.isOptionSafe[i]) { possibs += 1; if(randomd() < 1.0 / double(possibs)) choose = i; } } if(!ai.behavior.forbidAnomalyChoice && possibs != 0) { anomaly.choose(ai.empire, choose); } else { completed = true; } } else { if(!fleet.obj.hasOrders) fleet.obj.addScanOrder(anomaly); } } } }; class Scouting : AIComponent { Fleets@ fleets; Systems@ systems; Designs@ designs; Construction@ construction; Movement@ movement; Creeping@ creeping; DesignTarget@ scoutDesign; array<ScoutingMission@> queue; array<ScoutingMission@> active; array<Anomaly@> anomalies; array<ScanningMission@> scanQueue; array<ScanningMission@> scanActive; array<BuildFlagship@> constructing; int exploreHops = 0; bool buildScouts = true; void create() { @fleets = cast<Fleets>(ai.fleets); @systems = cast<Systems>(ai.systems); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @movement = cast<Movement>(ai.movement); @creeping = cast<Creeping>(ai.creeping); } void save(SaveFile& file) { designs.saveDesign(file, scoutDesign); file << exploreHops; uint cnt = queue.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, queue[i]); cnt = active.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, active[i]); cnt = anomalies.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << anomalies[i]; cnt = scanQueue.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, scanQueue[i]); cnt = scanActive.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, scanActive[i]); cnt = constructing.length; file << cnt; for(uint i = 0; i < cnt; ++i) construction.saveConstruction(file, constructing[i]); } void load(SaveFile& file) { @scoutDesign = designs.loadDesign(file); file >> exploreHops; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<ScoutingMission>(fleets.loadMission(file)); if(miss !is null) queue.insertLast(miss); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<ScoutingMission>(fleets.loadMission(file)); if(miss !is null) active.insertLast(miss); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Anomaly@ anom; file >> anom; if(anom !is null) anomalies.insertLast(anom); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<ScanningMission>(fleets.loadMission(file)); if(miss !is null) scanQueue.insertLast(miss); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<ScanningMission>(fleets.loadMission(file)); if(miss !is null) scanActive.insertLast(miss); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ cons = cast<BuildFlagship>(construction.loadConstruction(file)); if(cons !is null) constructing.insertLast(cons); } } void start() { @scoutDesign = DesignTarget(DP_Scout, 16); scoutDesign.targetMaintenance = 40; designs.design(scoutDesign); } bool isScouting(Region@ region) { for(uint i = 0, cnt = queue.length; i < cnt; ++i) { if(queue[i].region is region) return true; } for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].region is region) return true; } return false; } ScoutingMission@ scout(Region@ region, uint priority = MiP_High) { for(uint i = 0, cnt = queue.length; i < cnt; ++i) { if(queue[i].region is region) return queue[i]; } for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].region is region) return active[i]; } if(log) ai.print("Queue scouting mission", region); ScoutingMission mission; @mission.region = region; mission.priority = priority; fleets.register(mission); queue.insertLast(mission); return mission; } void recordAnomaly(Anomaly@ anom) { for(uint i = 0, cnt = scanActive.length; i < cnt; ++i) { if(scanActive[i].anomaly is anom) return; } if(anomalies.find(anom) == -1) anomalies.insertLast(anom); } ScanningMission@ scan(Anomaly& anomaly, uint priority = MiP_Normal) { for(uint i = 0, cnt = scanActive.length; i < cnt; ++i) { if(scanActive[i].anomaly is anomaly) return scanActive[i]; } if(log) ai.print("Queue scanning mission on "+anomaly.name, anomaly.region); ScanningMission mission; @mission.anomaly = anomaly; mission.priority = priority; fleets.register(mission); anomalies.remove(anomaly); scanQueue.insertLast(mission); return mission; } void focusTick(double time) { //Remove completed scouting missions for(uint i = 0, cnt = active.length; i < cnt; ++i) { if(active[i].completed || active[i].canceled) { active.removeAt(i); --i; --cnt; } } //Remove completed scanning missions for(uint i = 0, cnt = scanActive.length; i < cnt; ++i) { if(scanActive[i].completed || scanActive[i].canceled) { scanActive.removeAt(i); --i; --cnt; } } if(ai.behavior.forbidScouting) return; //Make sure we have enough scouts active and scouting if(fleets.count(FC_Scout) + constructing.length < ai.behavior.scoutsActive && buildScouts) constructing.insertLast(construction.buildFlagship(scoutDesign)); for(uint i = 0, cnt = constructing.length; i < cnt; ++i) { if(constructing[i].completed && constructing[i].completedAt + 30.0 < gameTime) { constructing.removeAt(i); --i; --cnt; } } //See if we can fill the scouting queue with something nice uint scoutClass = FC_Scout; if(gameTime < ai.behavior.scoutAllTimer) scoutClass = FC_ALL; bool haveIdle = fleets.haveIdle(scoutClass); //See if we should queue up a new anomaly scan if(scanQueue.length == 0 && anomalies.length != 0 && scanActive.length < ai.behavior.maxScanningMissions && haveIdle && (!ai.behavior.prioritizeScoutOverScan || active.length > 0)) { Anomaly@ best; double bestDist = INFINITY; for(uint i = 0, cnt = anomalies.length; i < cnt; ++i) { auto@ anom = anomalies[i]; if(anom is null || !anom.valid) { anomalies.removeAt(i); --i; --cnt; continue; } if(creeping.isQuarantined(anom.region)) continue; double d = fleets.closestIdleTo(scoutClass, anom.position); if(d < bestDist) { @best = anom; bestDist = d; } } if(best !is null) scan(best); } //Scan anomalies in our scan queue if(scanQueue.length != 0) { auto@ mission = scanQueue[0]; if(mission.anomaly is null || !mission.anomaly.valid) { scanQueue.removeAt(0); } else { auto@ flAI = fleets.performMission(mission); if(flAI !is null) { if(log) ai.print("Perform scanning mission with "+flAI.obj.name, mission.anomaly.region); scanQueue.remove(mission); scanActive.insertLast(mission); } } } //TODO: In large maps we should probably devote scouts to scouting enemies even before the map is fully explored if(queue.length == 0 && active.length < ai.behavior.maxScoutingMissions && haveIdle) { //Explore systems from the inside out if(exploreHops != -1) { double bestDist = INFINITY; bool remainingHops = false; bool emptyHops = true; Region@ best; for(uint i = 0, cnt = systems.all.length; i < cnt; ++i) { auto@ sys = systems.all[i]; if(sys.hopDistance == exploreHops) emptyHops = false; if(sys.explored || isScouting(sys.obj)) continue; if(sys.hopDistance == exploreHops) remainingHops = true; double d = fleets.closestIdleTo(scoutClass, sys.obj.position); if(sys.hopDistance != exploreHops) d *= pow(ai.behavior.exploreBorderWeight, double(sys.hopDistance - exploreHops)); if(d < bestDist) { bestDist = d; @best = sys.obj; } } if(best !is null) scout(best, priority=MiP_Normal); if(emptyHops) exploreHops = -1; else if(!remainingHops) exploreHops += 1; } else { //Gain vision over systems we haven't recently seen Region@ best; double bestWeight = 0; double curTime = gameTime; for(uint i = 0, cnt = systems.all.length; i < cnt; ++i) { auto@ sys = systems.all[i]; if(sys.visible || sys.visibleNow(ai)) continue; if(isScouting(sys.obj)) continue; double timer = curTime - sys.lastVisible; if(timer < ai.behavior.minScoutingInterval) continue; double w = 1.0; w *= timer / ai.behavior.minScoutingInterval; w /= fleets.closestIdleTo(scoutClass, sys.obj.position); if(!sys.explored) w *= 10.0; if(sys.seenPresent & ~ai.visionMask != 0) w *= 2.0; if(sys.seenPresent & ai.enemyMask != 0) { if(sys.hopDistance < 2) w *= 4.0; w *= 4.0; } if(w > bestWeight) { bestWeight = w; @best = sys.obj; } } if(best !is null) scout(best, priority=MiP_Normal); } } //Try to find a scout to perform our top scouting mission from the queue if(queue.length != 0) { auto@ mission = queue[0]; auto@ flAI = fleets.performMission(mission); if(flAI !is null) { if(log) ai.print("Perform scouting mission with "+flAI.obj.name, mission.region); active.insertLast(mission); queue.removeAt(0); } } } }; AIComponent@ createScouting() { return Scouting(); } |
Added scripts/server/empire_ai/weasel/Systems.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Events; import empire_ai.weasel.searches; import ai.events; import systems; import system_pathing; final class SystemAI { const SystemDesc@ desc; Region@ obj; double prevTick = 0.0; array<Planet@> planets; array<Pickup@> pickups; array<Object@> pickupProtectors; array<Artifact@> artifacts; array<Asteroid@> asteroids; bool explored = false; bool owned = false; bool visible = false; int hopDistance = 0; bool visited = false; bool border = false; bool bordersEmpires = false; bool outsideBorder = false; double lastVisible = 0; uint seenPresent = 0; double focusDuration = 0; double enemyStrength = 0; double lastStrengthCheck = 0; double nextDetailed = 0; SystemAI() { } SystemAI(const SystemDesc@ sys) { @desc = sys; @obj = desc.object; } void save(SaveFile& file) { file << obj; file << prevTick; uint cnt = planets.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << planets[i]; cnt = pickups.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << pickups[i]; cnt = pickupProtectors.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << pickupProtectors[i]; cnt = artifacts.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << artifacts[i]; cnt = asteroids.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << asteroids[i]; file << explored; file << owned; file << visible; file << hopDistance; file << border; file << bordersEmpires; file << outsideBorder; file << lastVisible; file << seenPresent; file << enemyStrength; file << lastStrengthCheck; } void load(SaveFile& file) { file >> obj; file >> prevTick; uint cnt = 0; file >> cnt; planets.length = cnt; for(uint i = 0; i < cnt; ++i) file >> planets[i]; file >> cnt; pickups.length = cnt; for(uint i = 0; i < cnt; ++i) file >> pickups[i]; file >> cnt; pickupProtectors.length = cnt; for(uint i = 0; i < cnt; ++i) file >> pickupProtectors[i]; file >> cnt; for(uint i = 0; i < cnt; ++i) { Artifact@ artif; file >> artif; if(artif !is null) artifacts.insertLast(artif); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Asteroid@ roid; file >> roid; if(roid !is null) asteroids.insertLast(roid); } file >> explored; file >> owned; file >> visible; file >> hopDistance; file >> border; file >> bordersEmpires; file >> outsideBorder; file >> lastVisible; file >> seenPresent; file >> enemyStrength; file >> lastStrengthCheck; } bool visibleNow(AI& ai) { return obj.VisionMask & ai.visionMask != 0; } void strengthCheck(AI& ai, double minInterval = 30.0) { if(lastStrengthCheck + minInterval > gameTime) return; if(!visible && lastVisible < gameTime - 30.0) return; lastStrengthCheck = gameTime; enemyStrength = getTotalFleetStrength(obj, ai.enemyMask); } void tick(AI& ai, Systems& systems, double time) { //Check if we should be visible bool shouldVisible = obj.VisionMask & ai.visionMask != 0; if(visible != shouldVisible) { if(visible) lastVisible = gameTime; visible = shouldVisible; } //Check if we should be owned bool shouldOwned = obj.TradeMask & ai.mask != 0; if(owned != shouldOwned) { if(shouldOwned) { systems.owned.insertLast(this); systems.hopsChanged = true; hopDistance = 0; systems.events.notifyOwnedSystemAdded(this, EventArgs()); } else { hopDistance = 1; systems.owned.remove(this); systems.hopsChanged = true; systems.events.notifyOwnedSystemRemoved(this, EventArgs()); } owned = shouldOwned; } //Check if we should be border bool shouldBorder = false; bordersEmpires = false; if(owned) { for(uint i = 0, cnt = desc.adjacent.length; i < cnt; ++i) { auto@ other = systems.getAI(desc.adjacent[i]); if(other !is null && !other.owned) { if(other.seenPresent & ~ai.teamMask != 0) bordersEmpires = true; shouldBorder = true; break; } } for(uint i = 0, cnt = desc.wormholes.length; i < cnt; ++i) { auto@ other = systems.getAI(desc.wormholes[i]); if(other !is null && !other.owned) { if(other.seenPresent & ~ai.teamMask != 0) bordersEmpires = true; shouldBorder = true; break; } } } if(border != shouldBorder) { if(shouldBorder) { systems.border.insertLast(this); systems.events.notifyBorderSystemAdded(this, EventArgs()); } else { systems.border.remove(this); systems.events.notifyBorderSystemRemoved(this, EventArgs()); } border = shouldBorder; } //Check if we should be outsideBorder bool shouldOutsideBorder = !owned && hopDistance == 1; if(outsideBorder != shouldOutsideBorder) { if(shouldOutsideBorder) { systems.outsideBorder.insertLast(this); systems.events.notifyOutsideBorderSystemAdded(this, EventArgs()); } else { systems.outsideBorder.remove(this); systems.events.notifyOutsideBorderSystemRemoved(this, EventArgs()); } outsideBorder = shouldOutsideBorder; } //Check if we've been explored if(visible && !explored) { //Find all remnants in this system auto@ objs = findType(obj, null, OT_Pickup); for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Pickup@ p = cast<Pickup>(objs[i]); if(p !is null) { pickups.insertLast(p); pickupProtectors.insertLast(p.getProtector()); } } explored = true; } //Deal with recording new data on this system if(explored) { uint plCnt = obj.planetCount; if(plCnt != planets.length) { auto@ objs = findType(obj, null, OT_Planet); planets.length = 0; planets.reserve(objs.length); for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Planet@ pl = cast<Planet>(objs[i]); if(pl !is null) planets.insertLast(pl); } } } if(visible) { seenPresent = obj.PlanetsMask; uint astrCount = obj.asteroidCount; if(astrCount != asteroids.length) { auto@ objs = findType(obj, null, OT_Asteroid); asteroids.length = 0; asteroids.reserve(objs.length); for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Asteroid@ a = cast<Asteroid>(objs[i]); if(a !is null) asteroids.insertLast(a); } } for(uint i = 0, cnt = pickups.length; i < cnt; ++i) { if(!pickups[i].valid) { pickups.removeAt(i); pickupProtectors.removeAt(i); break; } } if(nextDetailed < gameTime) { nextDetailed = gameTime + randomd(40.0, 100.0); auto@ objs = findType(obj, null, OT_Artifact); artifacts.length = 0; artifacts.reserve(objs.length); for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Artifact@ a = cast<Artifact>(objs[i]); if(a !is null) artifacts.insertLast(a); } } } } }; class Systems : AIComponent { Events@ events; //All owned systems array<SystemAI@> owned; //All owned systems that are considered our empire's border array<SystemAI@> border; //All systems just outside our border array<SystemAI@> outsideBorder; //All systems array<SystemAI@> all; //Systems that need to be processed soon array<SystemAI@> bumped; array<SystemAI@> focused; uint sysIdx = 0; bool hopsChanged = false; void create() { @events = cast<Events>(ai.events); } void save(SaveFile& file) { uint cnt = all.length; file << cnt; for(uint i = 0; i < cnt; ++i) all[i].save(file); cnt = owned.length; file << cnt; for(uint i = 0; i < cnt; ++i) saveAI(file, owned[i]); cnt = border.length; file << cnt; for(uint i = 0; i < cnt; ++i) saveAI(file, border[i]); cnt = outsideBorder.length; file << cnt; for(uint i = 0; i < cnt; ++i) saveAI(file, outsideBorder[i]); } void load(SaveFile& file) { uint cnt = 0; file >> cnt; all.length = max(all.length, cnt); for(uint i = 0; i < cnt; ++i) { if(all[i] is null) @all[i] = SystemAI(); all[i].load(file); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadAI(file); if(data !is null) owned.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadAI(file); if(data !is null) border.insertLast(data); } file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ data = loadAI(file); if(data !is null) outsideBorder.insertLast(data); } } void loadFinalize(AI& ai) override { for(uint i = 0, cnt = all.length; i < cnt; ++i) { auto@ sys = getSystem(i); @all[i].desc = sys; @all[i].obj = sys.object; } } void saveAI(SaveFile& file, SystemAI@ ai) { Region@ reg; if(ai !is null) @reg = ai.obj; file << reg; } SystemAI@ loadAI(SaveFile& file) { Region@ reg; file >> reg; if(reg is null) return null; uint id = reg.SystemId; if(id >= all.length) { all.length = id+1; @all[id] = SystemAI(); @all[id].obj = reg; } return all[id]; } void focusTick(double time) { if(all.length != systemCount) { uint prevCount = all.length; all.length = systemCount; for(uint i = prevCount, cnt = all.length; i < cnt; ++i) @all[i] = SystemAI(getSystem(i)); } if(hopsChanged) calculateHops(); } void tick(double time) override { double curTime = gameTime; if(all.length != 0) { uint tcount = max(ceil(time / 0.2), double(all.length)/20.0); for(uint n = 0; n < tcount; ++n) { sysIdx = (sysIdx+1) % all.length; auto@ sys = all[sysIdx]; sys.tick(ai, this, curTime - sys.prevTick); sys.prevTick = curTime; } } for(uint i = 0, cnt = bumped.length; i < cnt; ++i) { auto@ sys = bumped[i]; double tickTime = curTime - sys.prevTick; if(tickTime != 0) { sys.tick(ai, this, tickTime); sys.prevTick = curTime; } } bumped.length = 0; for(uint i = 0, cnt = focused.length; i < cnt; ++i) { auto@ sys = focused[i]; sys.focusDuration -= time; double tickTime = curTime - sys.prevTick; if(tickTime != 0) { sys.tick(ai, this, tickTime); sys.prevTick = curTime; } if(sys.focusDuration <= 0) { focused.removeAt(i); --i; --cnt; } } } void calculateHops() { if(!hopsChanged) return; hopsChanged = false; priority_queue q; for(uint i = 0, cnt = all.length; i < cnt; ++i) { auto@ sys = all[i]; sys.visited = false; if(sys.owned) { sys.hopDistance = 0; q.push(int(i), 0); } else sys.hopDistance = INT_MAX; } while(!q.empty()) { uint index = uint(q.top()); q.pop(); auto@ sys = all[index]; if(sys.visited) continue; int dist = sys.hopDistance; sys.visited = true; for(uint i = 0, cnt = sys.desc.adjacent.length; i < cnt; ++i) { uint otherInd = sys.desc.adjacent[i]; if(otherInd < all.length) { auto@ other = all[otherInd]; if(other.hopDistance > dist+1) { other.hopDistance = dist+1; q.push(otherInd, -other.hopDistance); } } } for(uint i = 0, cnt = sys.desc.wormholes.length; i < cnt; ++i) { uint otherInd = sys.desc.wormholes[i]; if(otherInd < all.length) { auto@ other = all[otherInd]; if(other.hopDistance > dist+1) { other.hopDistance = dist+1; q.push(otherInd, -other.hopDistance); } } } } } void focus(Region@ reg, double duration = 30.0) { bool found = false; for(uint i = 0, cnt = focused.length; i < cnt; ++i) { if(focused[i].obj is reg) { focused[i].focusDuration = max(focused[i].focusDuration, duration); found = true; break; } } if(!found) { auto@ sys = getAI(reg); if(sys !is null) { sys.focusDuration = duration; focused.insertLast(sys); } } } void bump(Region@ sys) { if(sys !is null) bump(getAI(sys)); } void bump(SystemAI@ sys) { if(sys !is null) bumped.insertLast(sys); } SystemAI@ getAI(uint idx) { if(idx < all.length) return all[idx]; return null; } SystemAI@ getAI(Region@ region) { if(region is null) return null; uint idx = region.SystemId; if(idx < all.length) return all[idx]; return null; } SystemPath pather; int hopDistance(Region& fromRegion, Region& toRegion){ pather.generate(getSystem(fromRegion), getSystem(toRegion), keepCache=true); if(!pather.valid) return INT_MAX; return pather.pathSize - 1; } TradePath tradePather; int tradeDistance(Region& fromRegion, Region& toRegion) { @tradePather.forEmpire = ai.empire; tradePather.generate(getSystem(fromRegion), getSystem(toRegion), keepCache=true); if(!tradePather.valid) return -1; return tradePather.pathSize - 1; } bool canTrade(Region& fromRegion, Region& toRegion) { if(fromRegion.sharesTerritory(ai.empire, toRegion)) return true; int dist = tradeDistance(fromRegion, toRegion); if(dist < 0) return false; return true; } SystemAI@ getAI(const string& name) { for(uint i = 0, cnt = all.length; i < cnt; ++i) { if(all[i].obj.name.equals_nocase(name)) return all[i]; } return null; } uint index(const string& name) { for(uint i = 0, cnt = all.length; i < cnt; ++i) { if(all[i].obj.name.equals_nocase(name)) return i; } return uint(-1); } }; AIComponent@ createSystems() { return Systems(); } |
Added scripts/server/empire_ai/weasel/War.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 |
// War // --- // Attacks and defends from enemy attacks during situations of war. // import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Intelligence; import empire_ai.weasel.Relations; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import empire_ai.weasel.Movement; import empire_ai.weasel.Scouting; import empire_ai.weasel.Military; import empire_ai.weasel.searches; import regions.regions; class BattleMission : Mission { Region@ battleIn; FleetAI@ fleet; MoveOrder@ move; Object@ defending; Planet@ capturing; Empire@ captureFrom; bool arrived = false; void save(Fleets& fleets, SaveFile& file) override { file << battleIn; fleets.saveAI(file, fleet); fleets.movement.saveMoveOrder(file, move); file << defending; file << capturing; file << captureFrom; file << arrived; } void load(Fleets& fleets, SaveFile& file) override { file >> battleIn; @fleet = fleets.loadAI(file); @move = fleets.movement.loadMoveOrder(file); file >> defending; file >> capturing; file >> captureFrom; file >> arrived; } }; double captureSupply(Empire& emp, Object& check) { double loy = check.getLoyaltyFacing(emp); double cost = config::SIEGE_LOYALTY_SUPPLY_COST * loy; cost *= emp.CaptureSupplyFactor; cost *= check.owner.CaptureSupplyDifficulty; return cost; } class Battle { SystemAI@ system; Region@ staging; array<BattleMission@> fleets; uint curPriority = MiP_Critical; bool isAttack = false; double enemyStrength; double ourStrength; double lastCombat = 0; double bestCapturePct; double lastHadFleets = 0; bool inCombat = false; bool isUnderSiege = false; Planet@ defendPlanet; Object@ eliminate; Battle() { lastHadFleets = gameTime; lastCombat = gameTime; } void save(War& war, SaveFile& file) { war.systems.saveAI(file, system); uint cnt = fleets.length; file << cnt; for(uint i = 0; i < cnt; ++i) war.fleets.saveMission(file, fleets[i]); file << curPriority; file << isAttack; file << lastCombat; file << inCombat; file << defendPlanet; file << eliminate; file << isUnderSiege; file << bestCapturePct; } void load(War& war, SaveFile& file) { @system = war.systems.loadAI(file); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ miss = cast<BattleMission>(war.fleets.loadMission(file)); if(miss !is null) fleets.insertLast(miss); } file >> curPriority; file >> isAttack; file >> lastCombat; file >> inCombat; file >> defendPlanet; file >> eliminate; file >> isUnderSiege; file >> bestCapturePct; } BattleMission@ join(AI& ai, War& war, FleetAI@ flAI) { BattleMission mission; @mission.fleet = flAI; @mission.battleIn = system.obj; mission.priority = curPriority; Object@ moveTo = system.obj; if(defendPlanet !is null) @moveTo = defendPlanet; else if(eliminate !is null && eliminate.isShip) @moveTo = eliminate; @mission.move = war.movement.move(flAI.obj, moveTo, MP_Critical, spread=true, nearOnly=true); //Station this fleet nearby after the battle is over if(staging !is null) war.military.stationFleet(flAI, staging); if(war.log) ai.print("Assign to battle at "+system.obj.name +" for strength "+standardize(ourStrength * 0.001, true) + " vs their "+standardize(enemyStrength * 0.001, true), flAI.obj); fleets.insertLast(mission); war.fleets.performMission(flAI, mission); return mission; } bool stayingHere(Object@ other) { if(other is null || !other.hasMover) return true; if(!inRegion(system.obj, other.position)) return false; double acc = other.maxAcceleration; if(acc <= 0.0001) return true; vec3d compDest = other.computedDestination; if(inRegion(system.obj, compDest)) return true; if(inRegion(system.obj, other.position + other.velocity * 10.0)) return true; return false; } bool tick(AI& ai, War& war, double time) { //Compute strength values enemyStrength = getTotalFleetStrength(system.obj, ai.enemyMask, planets=true); ourStrength = 0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) ourStrength += sqrt(fleets[i].fleet.strength); ourStrength *= ourStrength; inCombat = false; bool ourPlanetsPresent = system.obj.PlanetsMask & (ai.allyMask | ai.mask) != 0; if((enemyStrength < 0.01 || !ourPlanetsPresent) && defendPlanet is null) isUnderSiege = false; //Remove lost fleets bool anyArrived = false; bestCapturePct = 0.0; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ miss = fleets[i]; miss.priority = curPriority; if(!miss.fleet.obj.valid || miss.canceled) { if(!miss.fleet.obj.valid) { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ other = getEmpire(i); if(!other.major || !ai.empire.isHostile(other)) continue; if(system.obj.ContestedMask & other.mask != 0) war.relations.recordLostTo(other, miss.fleet.obj); } } miss.canceled = true; if(war.log) ai.print("BATTLE: lost fleet "+miss.fleet.obj.name, system.obj); fleets.removeAt(i); --i; --cnt; if(fleets.length == 0) lastHadFleets = gameTime; continue; } if(miss.move !is null) { if(miss.move.failed) { miss.canceled = true; if(war.log) ai.print("BATTLE: move failed on lost fleet "+miss.fleet.obj.name, system.obj); fleets.removeAt(i); --i; --cnt; if(fleets.length == 0) lastHadFleets = gameTime; continue; } if(miss.move.completed) { miss.arrived = true; @miss.move = null; } } if(miss.arrived) { anyArrived = true; bool shouldRetreat = false; if(miss.fleet.supplies < 0.05) { if(isCapturingAny && eliminate is null) shouldRetreat = true; else if(ourStrength < enemyStrength * 0.75) shouldRetreat = true; } if(miss.fleet.fleetHealth < 0.25) { if(ourStrength < enemyStrength * 0.5) shouldRetreat = true; } //DOF - Adding some leader based checks if(miss.fleet.flagshipHealth < 0.5) { if(ourStrength < enemyStrength * 0.75) shouldRetreat = true; } if(shouldRetreat) { war.fleets.returnToBase(miss.fleet); fleets.removeAt(i); miss.canceled = true; --i; --cnt; if(fleets.length == 0) lastHadFleets = gameTime; continue; } } if(miss.capturing !is null) bestCapturePct = max(bestCapturePct, miss.capturing.capturePct); } //Defend our planets if(defendPlanet is null) { Planet@ defPl; double bestWeight = 0.0; for(uint i = 0, cnt = system.planets.length; i < cnt; ++i) { Planet@ pl = system.planets[i]; double w = 1.0; if(pl.owner is ai.empire) w *= 2.0; else if(pl.owner.mask & ai.allyMask != 0) w *= 0.5; else continue; double capt = pl.capturePct; if(capt <= 0.01) continue; w *= capt; if(!pl.enemiesInOrbit) continue; if(w > bestWeight) { bestWeight = w; @defPl = pl; } } if(defPl !is null) { if(war.log) ai.print("BATTLE: protect planet "+defPl.name, system.obj); @defendPlanet = defPl; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) moveTo(fleets[i], defendPlanet, force=true); } } else { //Check if there are still enemies in orbit if(!defendPlanet.enemiesInOrbit || !defendPlanet.valid || defendPlanet.owner.isHostile(ai.empire)) @defendPlanet = null; } if(defendPlanet !is null) { //Make sure one fleet is in orbit inCombat = true; isUnderSiege = true; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) moveTo(fleets[i], defendPlanet); } //Eliminate any remaining threats if(!inCombat) { //Eliminate any hostile targets in the system if(eliminate !is null) { //Make sure this is still a valid target to eliminate bool valid = true; if(!eliminate.valid) { valid = false; war.relations.recordTakenFrom(eliminate.owner, eliminate); } else if(!stayingHere(eliminate)) valid = false; else if(!eliminate.isVisibleTo(ai.empire)) valid = false; else if(!ai.empire.isHostile(eliminate.owner)) valid = false; if(!valid) { @eliminate = null; clearOrders(); } else { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) attack(fleets[i], eliminate); inCombat = true; } } else { //Find a new target to eliminate Object@ check = findEnemy(system.obj, ai.empire, ai.enemyMask); if(check !is null) { if(stayingHere(check)) { @eliminate = check; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ obj = fleets[i].fleet.obj; if(!fleets[i].arrived) continue; obj.addAttackOrder(eliminate); } if(war.log) ai.print("BATTLE: Eliminate "+eliminate.name, system.obj); inCombat = true; for(uint i = 0, cnt = fleets.length; i < cnt; ++i) attack(fleets[i], eliminate, force=true); } } } } else { @eliminate = null; } //Capture enemy planets if possible //TODO: Respond to defense by abandoning all but 1 capture and swarming around the best one if(!inCombat) { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ miss = fleets[i]; if(miss.capturing !is null) { if(canCapture(ai, miss, miss.capturing) && miss.fleet.remainingSupplies > captureSupply(ai.empire, miss.capturing) && miss.fleet.obj.hasOrders) { inCombat = true; continue; } else { if(miss.capturing.owner is ai.empire && miss.captureFrom !is null) war.relations.recordTakenFrom(miss.captureFrom, miss.capturing); @miss.capturing = null; @miss.captureFrom = null; } } if(!miss.arrived) continue; Planet@ bestCapture; double totalWeight = 0; for(uint i = 0, cnt = system.planets.length; i < cnt; ++i) { Planet@ check = system.planets[i]; if(!canCapture(ai, miss, check)) continue; //Don't send two fleets to the same thing if(isCapturing(check)) continue; //Make sure we have the supplies remaining to capture if(miss.fleet.remainingSupplies < captureSupply(ai.empire, check) * ai.behavior.captureSupplyEstimate) continue; double str = check.getFleetStrength(); double w = 1.0; w *= check.getLoyaltyFacing(ai.empire); if(str != 0) w /= str; totalWeight += w; if(randomd() < w / totalWeight) @bestCapture = check; } if(bestCapture !is null) { if(war.log) ai.print("BATTLE: Capture "+bestCapture.name+" with "+miss.fleet.obj.name, system.obj); @miss.capturing = bestCapture; @miss.captureFrom = bestCapture.owner; miss.fleet.obj.addCaptureOrder(bestCapture); inCombat = true; } } } else { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ miss = fleets[i]; @miss.capturing = null; @miss.captureFrom = null; } } //Keep fleets here in non-critical mode for a few minutes if(!inCombat && (anyArrived || !isAttack)) { //TODO: Don't start this countdown until we've actually arrived if(gameTime > lastCombat + 90.0) { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) fleets[i].completed = true; if(war.log) ai.print("BATTLE: ended", system.obj); return false; } else if(gameTime > lastCombat + 30.0) { curPriority = MiP_Normal; } else { curPriority = MiP_High; } } else { if(ourPlanetsPresent && isUnderSiege) { curPriority = MiP_Critical; if(war.winnability(this) < 0.5) curPriority = MiP_High; } else if(bestCapturePct > 0.75) curPriority = MiP_Critical; else curPriority = MiP_High; lastCombat = gameTime; } //If needed, claim fleets if(ourStrength < enemyStrength * ai.behavior.battleStrengthOverkill) { FleetAI@ claim; double bestWeight = 0; for(uint i = 0, cnt = war.fleets.fleets.length; i < cnt; ++i) { auto@ fleet = war.fleets.fleets[i]; double w = war.assignable(this, fleet); if(w > bestWeight) { bestWeight = w; @claim = fleet; } } if(claim !is null) join(ai, war, claim); } //Give up the battle when we should if(fleets.length == 0) { if(!ourPlanetsPresent && !isAttack) { //We lost all our planets before we could respond with anything. // We might be able to use an attack to claim it back later, but for now we just give up on it. if(war.log) ai.print("BATTLE: aborted defense, no fleets and no planets left", system.obj); for(uint i = 0, cnt = fleets.length; i < cnt; ++i) fleets[i].canceled = true; return false; } if(isAttack) { //We haven't been able to find any fleets to assign here for a while, //so just abort the attack if(gameTime - lastHadFleets > 60.0) { if(war.log) ai.print("BATTLE: aborted attack, no fleets available", system.obj); for(uint i = 0, cnt = fleets.length; i < cnt; ++i) fleets[i].canceled = true; return false; } } } return true; } void clearOrders() { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { auto@ obj = fleets[i].fleet.obj; if(!fleets[i].arrived) continue; if(obj.hasOrders) obj.clearOrders(); } } bool canCapture(AI& ai, BattleMission@ miss, Planet@ check) { if(!ai.empire.isHostile(check.owner)) return false; //TODO: Wait around a while maybe? if(check.isProtected(ai.empire)) return false; return true; } void moveTo(BattleMission@ miss, Planet@ defPl, bool force = false) { if(!miss.arrived) return; if(!force) { if(miss.fleet.obj.hasOrders) return; double dist = miss.fleet.obj.position.distanceTo(defPl.position); if(dist < defPl.OrbitSize) return; } vec3d pos = defPl.position; vec2d offset = random2d(defPl.OrbitSize * 0.85); pos.x += offset.x; pos.z += offset.y; miss.fleet.obj.addMoveOrder(pos); } void attack(BattleMission@ miss, Object@ target, bool force = false) { //TODO: make this not chase stuff out of the system like a madman? // (in attack logic as well) if(!miss.arrived) return; if(!force) { if(miss.fleet.obj.hasOrders) return; } miss.fleet.obj.addAttackOrder(target); } bool isCapturing(Planet@ pl) { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].capturing is pl) return true; } return false; } bool get_isCapturingAny() { for(uint i = 0, cnt = fleets.length; i < cnt; ++i) { if(fleets[i].capturing !is null) return true; } return false; } }; class War : AIComponent { Fleets@ fleets; Intelligence@ intelligence; Relations@ relations; Movement@ movement; Scouting@ scouting; Systems@ systems; Military@ military; array<Battle@> battles; ScoutingMission@ currentScout; void create() { @fleets = cast<Fleets>(ai.fleets); @intelligence = cast<Intelligence>(ai.intelligence); @relations = cast<Relations>(ai.relations); @movement = cast<Movement>(ai.movement); @scouting = cast<Scouting>(ai.scouting); @systems = cast<Systems>(ai.systems); @military = cast<Military>(ai.military); } void save(SaveFile& file) { uint cnt = battles.length; file << cnt; for(uint i = 0; i < cnt; ++i) battles[i].save(this, file); fleets.saveMission(file, currentScout); } void load(SaveFile& file) { uint cnt = 0; file >> cnt; battles.length = cnt; for(uint i = 0; i < cnt; ++i) { @battles[i] = Battle(); battles[i].load(this, file); } @currentScout = cast<ScoutingMission>(fleets.loadMission(file)); ai.behavior.remnantAllowArbitraryClear = false; } double getCombatReadyStrength() { double str = 0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) continue; str += flAI.strength; } return str * str; } Battle@ attack(SystemAI@ sys) { Battle atk; @atk.system = sys; atk.isAttack = true; atk.curPriority = MiP_High; @atk.staging = military.getStagingFor(sys.obj); if(log) ai.print("Organizing an attack against "+sys.obj.name); claimFleetsFor(atk); battles.insertLast(atk); return atk; } Battle@ defend(SystemAI@ sys) { Battle def; @def.system = sys; @def.staging = military.getClosestStaging(sys.obj); if(log) ai.print("Organizing defense for "+sys.obj.name); battles.insertLast(def); return def; } void claimFleetsFor(Battle@ atk) { //TODO: This currently claims everything not in use, should it //leave some reserves for defense? Is that good? for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) continue; atk.join(ai, this, flAI); } } void sendFleetToJoin(Battle@ atk) { for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!flAI.readyForAction) continue; atk.join(ai, this, flAI); break; } } bool isFightingIn(Region@ reg) { for(uint i = 0, cnt = battles.length; i < cnt; ++i) { if(battles[i].system.obj is reg) return true; } return false; } void tick(double time) override { for(uint i = 0, cnt = battles.length; i < cnt; ++i) { if(!battles[i].tick(ai, this, time)) { battles.removeAt(i); --i; --cnt; } } } double ourStrength; double curTime; SystemAI@ best; double totalWeight; SystemAI@ scout; uint scoutCount; void check(SystemAI@ sys, double baseWeight) { if(isFightingIn(sys.obj)) return; if(!sys.visible) { sys.strengthCheck(ai, minInterval=5*60.0); if(sys.lastStrengthCheck < curTime - 5 * 60.0) { scoutCount += 1; if(randomd() < 1.0 / double(scoutCount)) @scout = sys; return; } } else { sys.strengthCheck(ai, minInterval=60.0); } double theirStrength = sys.enemyStrength; if(ourStrength < theirStrength * ai.behavior.attackStrengthOverkill) return; double w = baseWeight; //Try to capture less important systems at first //TODO: This should flip when we go from border skirmishes to subjugation war uint capturable = 0; for(uint i = 0, cnt = sys.planets.length; i < cnt; ++i) { Planet@ pl = sys.planets[i]; if(!ai.empire.isHostile(pl.owner)) continue; if(pl.isProtected(ai.empire)) continue; w /= 1.0 + double(pl.level); capturable += 1; } //Ignore protected systems if(capturable == 0) return; //See where their defenses are low if(theirStrength != 0) { double defRatio = ourStrength / theirStrength; if(defRatio > 4.0) { //We prefer destroying some minor assets over fighting an entirely undefended system, //because it hurts more to lose stuff. w *= 6.0; } } else { w *= 2.0; } totalWeight += w; if(randomd() < w / totalWeight) @best = sys; } int totalEnemySize(SystemAI@ sys) { int size = 0; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ other = getEmpire(i); if(other.major && ai.empire.isHostile(other)) size += sys.obj.getStrength(other); } return size; } bool isUnderAttack(SystemAI@ sys) { if(sys.obj.ContestedMask & ai.mask == 0) return false; if(totalEnemySize(sys) < 100) { if(sys.obj.SiegedMask & ai.mask == 0) return false; } return true; } double assignable(Battle& battle, FleetAI& fleet) { if(fleet.fleetClass != FC_Combat) return 0.0; double w = 1.0; if(fleet.mission !is null) { w *= 0.1; if(fleet.mission.priority >= MiP_High) w *= 0.1; if(fleet.mission.priority >= battle.curPriority) return 0.0; auto@ miss = cast<BattleMission>(fleet.mission); if(miss !is null && miss.battleIn is battle.system.obj) return 0.0; } else if(fleet.isHome && fleet.stationed is battle.system.obj) { //This should be allowed to fight always } else if(battle.curPriority >= MiP_Critical) { if(fleet.supplies < 0.25) return 0.0; if(fleet.fleetHealth < 0.25) return 0.0; if(fleet.filled < 0.2) return 0.0; //DOF - Do not send badly damaged flagships if(fleet.flagshipHealth < 0.5) return 0.0; if(fleet.obj.isMoving) { if(fleet.obj.velocity.length / fleet.obj.maxAcceleration > 16.0) w *= 0.1; } } else { if(!fleet.readyForAction) return 0.0; } double fleetStrength = fleet.strength; if(battle.ourStrength + fleetStrength < battle.enemyStrength * ai.behavior.battleStrengthOverkill) w *= 0.25; return w; } double winnability(Battle& battle) { double ours = sqrt(battle.ourStrength); double theirs = battle.enemyStrength; if(theirs <= 0.0) return 10.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ fleet = fleets.fleets[i]; double w = assignable(battle, fleet); if(w != 0.0) ours += sqrt(fleet.strength); } ours *= ours; return ours / theirs; } void focusTick(double time) override { if(currentScout !is null) { if(currentScout.canceled || currentScout.completed) @currentScout = null; } //Change our behavior a little depending on the state ai.behavior.remnantAllowArbitraryClear = !relations.isFightingWar(aggressive=true) && battles.length == 0; //Find any systems that need defending //TODO: Defend allies at lowered priority if(ai.behavior.forbidDefense) { for(uint i = 0, cnt = systems.owned.length; i < cnt; ++i) { SystemAI@ sys = systems.owned[i]; if(!isUnderAttack(sys)) continue; if(isFightingIn(sys.obj)) continue; defend(sys); return; } } if(ai.behavior.forbidAttack) return; //Do attacks uint ready = fleets.countCombatReadyFleets(); if(ready != 0) { //See if we can start a new attack if(battles.length < ai.behavior.maxBattles && relations.isFightingWar(aggressive=true) && (battles.length == 0 || ready > ai.behavior.battleReserveFleets)) { //Determine our own strength ourStrength = getCombatReadyStrength(); //Evaluate systems to attack in our aggressive war @best = null; totalWeight = 0; curTime = gameTime; @scout = null; scoutCount = 0; //TODO: Consider aggressive wars against an empire to also be against that empire's vassals for(uint i = 0, cnt = intelligence.intel.length; i < cnt; ++i) { auto@ intel = intelligence.intel[i]; if(intel is null) continue; auto@ relation = relations.get(intel.empire); if(!relation.atWar || !relation.aggressive) continue; for(uint n = 0, ncnt = intel.shared.length; n < ncnt; ++n) { auto@ sys = intel.shared[n]; check(sys, 20.0); } for(uint n = 0, ncnt = intel.theirBorder.length; n < ncnt; ++n) { auto@ sys = intel.theirBorder[n]; check(sys, 1.0); } } //Make the attack with our fleets if(best !is null) attack(best); else if(scout !is null && currentScout is null) { if(log) ai.print("War requests scout to flyby "+scout.obj.name); @currentScout = scouting.scout(scout.obj); } } } } }; AIComponent@ createWar() { return War(); } |
Added scripts/server/empire_ai/weasel/WeaselAI.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
import settings.game_settings; from empire_ai.EmpireAI import AIController; import AIComponent@ createEvents() from "empire_ai.weasel.Events"; import AIComponent@ createColonization() from "empire_ai.weasel.Colonization"; import AIComponent@ createResources() from "empire_ai.weasel.Resources"; import AIComponent@ createPlanets() from "empire_ai.weasel.Planets"; import AIComponent@ createSystems() from "empire_ai.weasel.Systems"; import AIComponent@ createFleets() from "empire_ai.weasel.Fleets"; import AIComponent@ createScouting() from "empire_ai.weasel.Scouting"; import AIComponent@ createDevelopment() from "empire_ai.weasel.Development"; import AIComponent@ createDesigns() from "empire_ai.weasel.Designs"; import AIComponent@ createBudget() from "empire_ai.weasel.Budget"; import AIComponent@ createConstruction() from "empire_ai.weasel.Construction"; import AIComponent@ createMilitary() from "empire_ai.weasel.Military"; import AIComponent@ createMovement() from "empire_ai.weasel.Movement"; import AIComponent@ createCreeping() from "empire_ai.weasel.Creeping"; import AIComponent@ createRelations() from "empire_ai.weasel.Relations"; import AIComponent@ createIntelligence() from "empire_ai.weasel.Intelligence"; import AIComponent@ createWar() from "empire_ai.weasel.War"; import AIComponent@ createResearch() from "empire_ai.weasel.Research"; import AIComponent@ createEnergy() from "empire_ai.weasel.Energy"; import IAIComponent@ createDiplomacy() from "empire_ai.weasel.Diplomacy"; import AIComponent@ createConsider() from "empire_ai.weasel.Consider"; import AIComponent@ createOrbitals() from "empire_ai.weasel.Orbitals"; import AIComponent@ createInfrastructure() from "empire_ai.weasel.Infrastructure"; import AIComponent@ createHyperdrive() from "empire_ai.weasel.ftl.Hyperdrive"; import AIComponent@ createGate() from "empire_ai.weasel.ftl.Gate"; import AIComponent@ createFling() from "empire_ai.weasel.ftl.Fling"; import AIComponent@ createSlipstream() from "empire_ai.weasel.ftl.Slipstream"; import AIComponent@ createJumpdrive() from "empire_ai.weasel.ftl.Jumpdrive"; import AIComponent@ createVerdant() from "empire_ai.weasel.race.Verdant"; import AIComponent@ createMechanoid() from "empire_ai.weasel.race.Mechanoid"; import AIComponent@ createStarChildren() from "empire_ai.weasel.race.StarChildren"; import AIComponent@ createExtragalactic() from "empire_ai.weasel.race.Extragalactic"; import AIComponent@ createLinked() from "empire_ai.weasel.race.Linked"; import AIComponent@ createDevout() from "empire_ai.weasel.race.Devout"; import AIComponent@ createAncient() from "empire_ai.weasel.race.Ancient"; import AIComponent@ createInvasion() from "empire_ai.weasel.misc.Invasion"; import bool hasInvasionMap() from "Invasion.InvasionMap"; from buildings import BuildingType; from orbitals import OrbitalModule; from constructions import ConstructionType; import util.formatting; from empire import ai_full_speed; from traits import getTraitID; export IAIComponent, AIComponent, AI; uint GUARD = 0xDEADBEEF; enum AddedComponent { AC_Invasion = 0x1, }; interface IAIComponent : Savable { void set(AI& ai); void setLog(); void setLogCritical(); double getPrevFocus(); void setPrevFocus(double value); void create(); void start(); void tick(double time); void focusTick(double time); void turn(); void save(SaveFile& file); void load(SaveFile& file); void postLoad(AI& ai); void postSave(AI& ai); void loadFinalize(AI& ai); }; class AIComponent : IAIComponent, Savable { AI@ ai; double prevFocus = 0; bool log = false; bool logCritical = false; bool logErrors = true; double getPrevFocus() { return prevFocus; } void setPrevFocus(double value) { prevFocus = value; } void setLog() { log = true; } void setLogCritical() { logCritical = true; } void set(AI& ai) { @this.ai = ai; } void create() {} void start() {} void tick(double time) {} void focusTick(double time) {} void turn() {} void save(SaveFile& file) {} void load(SaveFile& file) {} void postLoad(AI& ai) {} void postSave(AI& ai) {} void loadFinalize(AI& ai) {} }; class ProfileData { double tickPeak = 0.0; double tickAvg = 0.0; double tickCount = 0.0; double focusPeak = 0.0; double focusAvg = 0.0; double focusCount = 0.0; }; final class AIBehavior { //AIEmpire controls bool forbidDiplomacy = false; bool forbidColonization = false; bool forbidCreeping = false; bool forbidResearch = false; bool forbidDefense = false; bool forbidAttack = false; bool forbidConstruction = false; bool forbidScouting = false; bool forbidAnomalyChoice = false; bool forbidArtifact = false; bool forbidScuttle = false; //How many focuses we can manage in a tick uint focusPerTick = 2; //The maximum colonizations this AI can do in one turn uint maxColonizations = UINT_MAX; //How many colonizations we're guaranteed to be able to do in one turn regardless of finances uint guaranteeColonizations = 2; //How many colonizations at most we can be doing at once uint maxConcurrentColonizations = UINT_MAX; //Whether this AI will colonize planets in systems owned by someone else // TODO: This should be partially ignored for border systems, so it can try to aggressively expand into the border bool colonizeEnemySystems = false; bool colonizeNeutralOwnedSystems = false; bool colonizeAllySystems = false; //How much this AI values claiming new systems instead of colonizing stuff in its existing ones double weightOutwardExpand = 2.0; //How much money this AI considers a colonization event to cost out of the budget int colonizeBudgetCost = 80; //Whether to do any generic expansion beyond any requests bool colonizeGenericExpand = true; //Latest percentage into a budget cycle that we still allow colonization double colonizeMaxBudgetProgress = 0.66; //Time after initial ownership change that an incomplete colonization is canceled double colonizeFailGraceTime = 100.0; //Time a planet that we failed to colonize is disregarded for colonization double colonizePenalizeTime = 9.0 * 60.0; //Maximum amount of scouting missions that can be performed simultaneously uint maxScoutingMissions = UINT_MAX; //Minimum time after losing vision over a system that we can scout it again double minScoutingInterval = 3.0 * 60.0; //Weight that it gives to exploring things near our empire instead of greedily exploring nearby things double exploreBorderWeight = 2.0; //How long we consider all fleets viable for scouting with double scoutAllTimer = 3.0 * 60.0; //How many scouts we want to have active uint scoutsActive = 2; //How many scanning missions we can do at once uint maxScanningMissions = 1; //Whether to prioritize scouting over scanning if we only have one scout bool prioritizeScoutOverScan = true; //Weights for what to do in generic planet development // Leveling up an existing development focus double focusDevelopWeight = 1.0; // Colonizing a new scalable or high tier to focus on double focusColonizeNewWeight = 4.0; // Colonizing a new high tier resource to import to one of our focuses double focusColonizeHighTierWeight = 1.0; //How many potential designs are evaluated before choosing the best one uint designEvaluateCount = 10; //How long a fleet has to be fully idle before it returns to its stationed system double fleetIdleReturnStationedTime = 60.0; //How long we try to have a fleet be capable of firing before running out of supplies double fleetAimSupplyDuration = 2.0 * 60.0; //How long a potential construction can take at most before we consider it unreasonable double constructionMaxTime = 10.0 * 60.0; //How long a factory has to have been idle for us to consider constructing labor storage double laborStoreIdleTimer = 60.0; //Maximum amount of time worth of labor we want to store in our warehouses double laborStoreMaxFillTime = 60.0 * 10.0; //Whether to use labor to build asteroids in the background bool backgroundBuildAsteroids = true; //Whether to choose the best resource on an asteroid, instead of doing it randomly bool chooseAsteroidResource = true; //Whether to distribute labor to shipyards when planets are idle bool distributeLaborExports = true; //Whether to build a shipyard to consolidate multiple planets of labor where possible bool consolidateLaborExports = true; //Estimate amount of labor spent per point of support ship size double estSizeSupportLabor = 0.25; //Maximum combat fleets we can have in service at once (counts starting fleet(s)) uint maxActiveFleets = UINT_MAX; //How much flagship size we try to make per available money double shipSizePerMoney = 1.0 / 3.5; //How much flagship size we try to make per available labor double shipSizePerLabor = 1.0 / 0.33; //How much maintenance we expect per ship size double maintenancePerShipSize = 2.0; //Minimum percentage increase in size before we decide to retrofit a flagship to be bigger double shipRetrofitThreshold = 0.5; //Whether to retrofit our free starting fleet if appropriate bool retrofitFreeFleets = false; //Minimum percentage of average current flagship size new fleets should be double flagshipBuildMinAvgSize = 1.00; //Minimum game time before we consider constructing new flagships double flagshipBuildMinGameTime = 4.0 * 60.0; //Whether to build factories when we need labor bool buildFactoryForLabor = true; //Whether to build warehouses when we're not using labor bool buildLaborStorage = true; //Whether factories can queue labor resource imports when needed bool allowRequestLaborImports = true; //Whether fleets with ghosted supports attempt to rebuild the ghosts or just clear them bool fleetsRebuildGhosts = true; //When trying to order supports on a fleet, wait for the planet to construct its supports so we can claim them bool supportOrderWaitOnFactory = true; //How much stronger we need to be than a remnant fleet to clear it double remnantOverkillFactor = 1.5; //Whether to allow idle fleets to be sent to clear remnants // Modified by Relations bool remnantAllowArbitraryClear = true; //Whether we should aggressively try to take out enemies bool aggressive = false; //Whether to become aggressive after we get boxed in and can no longer expand anywhere bool aggressiveWhenBoxedIn = false; //Whether we should never declare war ourselves bool passive = false; //Whether to hate human players the most bool biased = false; //How much stronger we need to be than someone to declare war out of hatred double hatredWarOverkill = 0.5; //How much stronger we need to be than someone to try to take them out in an aggressive war double aggressiveWarOverkill = 1.5; //How much stronger we want to be before we attack a system double attackStrengthOverkill = 1.5; //How many battles we can be performing at once uint maxBattles = UINT_MAX; //How much we try to overkill while fighting double battleStrengthOverkill = 1.5; //How many fleets we don't commit to attacks when we're already currently fighting uint battleReserveFleets = 1; //How much extra supply we try to have before starting a capture, to make sure we can actually do it double captureSupplyEstimate = 1.5; //Maximum hop distance we use as staging areas for our attacks int stagingMaxHops = 5; //If our fleet fill is less than this, immediately move back to factory from staging double stagingToFactoryFill = 0.6; //How much ftl is reserved for critical applications double ftlReservePctCritical = 0.25; //How much ftl is reserved to not be used for background applications double ftlReservePctNormal = 0.25; //How many artifacts we consider where to use per focus turn uint artifactFocusConsiderCount = 2; //How long after trying to build a generically requested building we give up double genericBuildExpire = 3.0 * 60.0; //How much the hate in a relationship decays to every minute double hateDecayRate = 0.9; //How much weaker we need to be to even consider surrender double surrenderMinStrength = 0.5; //How many of our total war points have to be taken by an empire for us to surrender double acceptSurrenderRatio = 0.75; double offerSurrenderRatio = 0.5; void setDifficulty(int diff, uint flags) { //This changes the behavior values based on difficulty and flags if(flags & AIF_Aggressive != 0) aggressive = true; if(flags & AIF_Passive != 0) passive = true; if(flags & AIF_Biased != 0) biased = true; //Low difficulties can't colonize as many things at once if(diff <= 0) { maxConcurrentColonizations = 1; guaranteeColonizations = 1; weightOutwardExpand = 0.5; } else if(diff <= 1) { maxConcurrentColonizations = 2; weightOutwardExpand = 1.0; } //Hard AI becomes aggressive when it gets boxed in aggressiveWhenBoxedIn = diff >= 2; //Easy difficulty can't attack and defend at the same time if(diff <= 0) maxBattles = 1; //Low difficulties aren't as good at managing labor if(diff <= 0) { distributeLaborExports = false; consolidateLaborExports = false; buildLaborStorage = false; } else if(diff <= 1) { consolidateLaborExports = false; } //Low difficulties aren't as good at managing fleets if(diff <= 0) { maxActiveFleets = 2; retrofitFreeFleets = true; } //Low difficulties aren't as good at scouting if(diff <= 1) scoutAllTimer = 0.0; //Low difficulties are worse at designing if(diff <= 0) designEvaluateCount = 3; else if(diff <= 1) designEvaluateCount = 8; else designEvaluateCount = 12; //Easy is a bit slow if(diff <= 0) focusPerTick = 1; else if(diff >= 2) focusPerTick = 3; } }; final class AIDefs { const BuildingType@ Factory; const BuildingType@ LaborStorage; const ConstructionType@ MoonBase; const OrbitalModule@ Shipyard; const OrbitalModule@ TradeOutpost; const OrbitalModule@ TradeStation; }; final class AI : AIController, Savable { Empire@ empire; AIBehavior behavior; AIDefs defs; int cycleId = -1; uint componentCycle = 0; uint addedComponents = 0; uint majorMask = 0; uint difficulty = 0; uint flags = 0; bool isLoading = false; array<IAIComponent@> components; array<ProfileData> profileData; IAIComponent@ events; IAIComponent@ fleets; IAIComponent@ budget; IAIComponent@ colonization; IAIComponent@ resources; IAIComponent@ planets; IAIComponent@ systems; IAIComponent@ scouting; IAIComponent@ development; IAIComponent@ designs; IAIComponent@ construction; IAIComponent@ military; IAIComponent@ movement; IAIComponent@ creeping; IAIComponent@ relations; IAIComponent@ intelligence; IAIComponent@ war; IAIComponent@ research; IAIComponent@ energy; IAIComponent@ diplomacy; IAIComponent@ consider; IAIComponent@ orbitals; IAIComponent@ infrastructure; IAIComponent@ ftl; IAIComponent@ race; IAIComponent@ invasion; void createComponents() { //NOTE: This is also save/load order, so //make sure to add loading logic when changing this list @events = add(createEvents()); @budget = add(createBudget()); @planets = add(createPlanets()); @resources = add(createResources()); @systems = add(createSystems()); @colonization = add(createColonization()); @fleets = add(createFleets()); @scouting = add(createScouting()); @development = add(createDevelopment()); @designs = add(createDesigns()); @construction = add(createConstruction()); @military = add(createMilitary()); @movement = add(createMovement()); @creeping = add(createCreeping()); @relations = add(createRelations()); @intelligence = add(createIntelligence()); @war = add(createWar()); @research = add(createResearch()); @energy = add(createEnergy()); @diplomacy = add(createDiplomacy()); @consider = add(createConsider()); @orbitals = add(createOrbitals()); @infrastructure = add(createInfrastructure()); //Make FTL component if(empire.hasTrait(getTraitID("Hyperdrive"))) @ftl = add(createHyperdrive()); else if(empire.hasTrait(getTraitID("Gate"))) @ftl = add(createGate()); else if(empire.hasTrait(getTraitID("Fling"))) @ftl = add(createFling()); else if(empire.hasTrait(getTraitID("Slipstream"))) @ftl = add(createSlipstream()); else if(empire.hasTrait(getTraitID("Jumpdrive"))) @ftl = add(createJumpdrive()); /* Not implemented yet. else if(empire.hasTrait(getTraitID("Flux"))) @ftl = add(createFlux()); */ //Make racial component if(empire.hasTrait(getTraitID("Verdant"))) @race = add(createVerdant()); else if(empire.hasTrait(getTraitID("Mechanoid"))) @race = add(createMechanoid()); else if(empire.hasTrait(getTraitID("StarChildren"))) @race = add(createStarChildren()); else if(empire.hasTrait(getTraitID("Extragalactic"))) @race = add(createExtragalactic()); else if(empire.hasTrait(getTraitID("Linked"))) @race = add(createLinked()); else if(empire.hasTrait(getTraitID("Devout"))) @race = add(createDevout()); else if(empire.hasTrait(getTraitID("Ancient"))) @race = add(createAncient()); /* Not implemented yet. else if(empire.hasTrait(getTraitID("Technicists"))) @race = add(createResearchers()); else if(empire.hasTrait(getTraitID("Progenitors"))) @race = add(createProgenitors()); else if(empire.hasTrait(getTraitID("Berserkers"))) @race = add(createBerserkers()); else if(empire.hasTrait(getTraitID("Pacifists"))) @race = add(createPacifists()); */ //Misc components if(hasInvasionMap() || addedComponents & AC_Invasion != 0) { @invasion = add(createInvasion()); addedComponents |= AC_Invasion; } //if(empire is playerEmpire) { //log(race); // log(colonization); // log(resources); // log(construction); //} //log(intelligence); //logAll(); logCritical(); profileData.length = components.length; for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].create(); } void createGeneral() { } void init(Empire& emp, EmpireSettings& settings) { @this.empire = emp; flags = settings.aiFlags; difficulty = settings.difficulty; behavior.setDifficulty(difficulty, flags); createComponents(); } int getDifficultyLevel() { return difficulty; } void load(SaveFile& file) { file >> empire; file >> cycleId; file >> majorMask; file >> difficulty; file >> flags; if(file >= SV_0153) file >> addedComponents; behavior.setDifficulty(difficulty, flags); createComponents(); createGeneral(); uint loadCnt = 0; file >> loadCnt; loadCnt = loadCnt; for(uint i = 0; i < loadCnt; ++i) { double prevFocus = 0; file >> prevFocus; components[i].setPrevFocus(prevFocus); file >> components[i]; uint check = 0; file >> check; if(check != GUARD) error("ERROR: AI Load error detected in component "+addrstr(components[i])+" of "+empire.name); } for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].postLoad(this); isLoading = true; } void save(SaveFile& file) { file << empire; file << cycleId; file << majorMask; file << difficulty; file << flags; file << addedComponents; uint saveCnt = components.length; file << saveCnt; for(uint i = 0; i < saveCnt; ++i) { file << components[i].getPrevFocus(); file << components[i]; file << GUARD; } for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].postSave(this); } void log(IAIComponent@ comp) { if(comp is null) return; comp.setLog(); comp.setLogCritical(); } void logCritical() { for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].setLogCritical(); } void logAll() { for(uint i = 0, cnt = components.length; i < cnt; ++i) { components[i].setLog(); components[i].setLogCritical(); } } IAIComponent@ add(IAIComponent& component) { component.set(this); components.insertLast(component); return component; } void init(Empire& emp) { majorMask = 0; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(emp.major) majorMask |= emp.mask; } createGeneral(); } bool hasStarted = false; void tick(Empire& emp, double time) { if(isLoading) { for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].loadFinalize(this); isLoading = false; hasStarted = true; } else if(!hasStarted) { for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].start(); hasStarted = true; } else if(emp.Victory == -1) { //Don't do anything when actually defeated return; } //Manage gametime-specific behaviors behavior.colonizeGenericExpand = gameTime >= 6.0 * 60.0; //Find cycled turns int curCycle = emp.BudgetCycleId; if(curCycle != cycleId) { for(uint i = 0, cnt = components.length; i < cnt; ++i) components[i].turn(); cycleId = curCycle; } //Generic ticks double startTime = getExactTime(); for(uint i = 0, cnt = components.length; i < cnt; ++i) { auto@ comp = components[i]; comp.tick(time); double endTime = getExactTime(); //double ms = 1000.0 * (endTime - startTime); startTime = endTime; //auto@ dat = profileData[i]; //dat.tickPeak = max(dat.tickPeak, ms); //dat.tickAvg += ms; //dat.tickCount += 1.0; } //Do focuseds tick on components uint focusCount = behavior.focusPerTick; if(ai_full_speed.value == 1.0) focusCount = max(uint(round((time / 0.25) * behavior.focusPerTick)), behavior.focusPerTick); double allocStart = startTime; for(uint n = 0; n < focusCount; ++n) { componentCycle = (componentCycle+1) % components.length; auto@ focusComp = components[componentCycle]; focusComp.focusTick(gameTime - focusComp.getPrevFocus()); focusComp.setPrevFocus(gameTime); double endTime = getExactTime(); //double ms = 1000.0 * (endTime - startTime); startTime = endTime; if(endTime - allocStart > 4000.0) break; //auto@ dat = profileData[componentCycle]; //dat.focusPeak = max(dat.focusPeak, ms); //dat.focusAvg += ms; //dat.focusCount += 1.0; } } void dumpProfile() { for(uint i = 0, cnt = components.length; i < cnt; ++i) { auto@ c = profileData[i]; print(pad(addrstr(components[i]), 40)+" tick peak "+toString(c.tickPeak,2)+" tick avg "+toString(c.tickAvg/c.tickCount, 2) +" focus peak "+toString(c.focusPeak,2)+" focus avg "+toString(c.focusAvg/c.focusCount, 2)); } } void resetProfile() { for(uint i = 0, cnt = profileData.length; i < cnt; ++i) { auto@ c = profileData[i]; c.tickPeak = 0.0; c.tickAvg = 0.0; c.tickCount = 0.0; c.focusPeak = 0.0; c.focusAvg = 0.0; c.focusCount = 0.0; } } uint get_mask() { return empire.mask; } uint get_teamMask() { //TODO return empire.mask; } uint get_visionMask() { return majorMask & empire.visionMask; } uint get_allyMask() { return empire.mutualDefenseMask | empire.ForcedPeaceMask.value; } uint get_enemyMask() { return empire.hostileMask & majorMask; } uint get_neutralMask() { return majorMask & ~allyMask & ~mask & ~enemyMask; } uint get_otherMask() { return majorMask & ~mask; } string pad(const string& input, uint width) { string str = input; while(str.length < width) str += " "; return str; } void print(const string& info, Object@ related = null, double value = INFINITY, bool flag = false, Empire@ emp = null) { string str = info; if(related !is null) str = pad(related.name, 16)+" | "+str; str = pad("["+empire.index+": "+empire.name+" AI] ", 20)+str; str = formatGameTime(gameTime) + " " + str; if(value != INFINITY) str += " | Value = "+standardize(value, true); if(flag) str += " | FLAGGED On"; if(emp !is null) str += " | Target = "+emp.name; ::print(str); } void debugAI() {} void commandAI(string cmd) { if (cmd == "forbid all") { behavior.forbidDiplomacy = true; behavior.forbidColonization = true; behavior.forbidCreeping = true; behavior.forbidResearch = true; behavior.forbidDefense = true; behavior.forbidAttack = true; behavior.forbidConstruction = true; behavior.forbidScouting = true; behavior.forbidAnomalyChoice = true; behavior.forbidArtifact = true; behavior.forbidScuttle = true; } else if (cmd == "allow all") { behavior.forbidDiplomacy = false; behavior.forbidColonization = false; behavior.forbidCreeping = false; behavior.forbidResearch = false; behavior.forbidDefense = false; behavior.forbidAttack = false; behavior.forbidConstruction = false; behavior.forbidScouting = false; behavior.forbidAnomalyChoice = false; behavior.forbidArtifact = false; behavior.forbidScuttle = false; } else if (cmd == "forbid Diplomacy") { behavior.forbidDiplomacy = true; } else if (cmd == "allow Diplomacy") { behavior.forbidDiplomacy = false; } else if (cmd == "forbid Colonization") { behavior.forbidColonization = true; } else if (cmd == "allow Colonization") { behavior.forbidColonization = false; } else if (cmd == "forbid Creeping") { behavior.forbidCreeping = true; } else if (cmd == "allow Creeping") { behavior.forbidCreeping = false; } else if (cmd == "forbid Research") { behavior.forbidResearch = true; } else if (cmd == "allow Research") { behavior.forbidResearch = false; } else if (cmd == "forbid Defense") { behavior.forbidDefense = true; } else if (cmd == "allow Defense") { behavior.forbidDefense = false; } else if (cmd == "forbid Attack") { behavior.forbidAttack = true; } else if (cmd == "allow Attack") { behavior.forbidAttack = false; } else if (cmd == "forbid Construction") { behavior.forbidConstruction = true; } else if (cmd == "allow Construction") { behavior.forbidConstruction = false; } else if (cmd == "forbid Scouting") { behavior.forbidScouting = true; } else if (cmd == "allow Scouting") { behavior.forbidScouting = false; } else if (cmd == "forbid AnomalyChoice") { behavior.forbidAnomalyChoice = true; } else if (cmd == "allow AnomalyChoice") { behavior.forbidAnomalyChoice = false; } else if (cmd == "forbid Artifact") { behavior.forbidArtifact = true; } else if (cmd == "allow Artifact") { behavior.forbidArtifact = false; } else if (cmd == "forbid Scuttle") { behavior.forbidScuttle = true; } else if (cmd == "allow Scuttle") { behavior.forbidScuttle = false; } else { print("WeaselAI: got unhandled AI command: " + cmd); } } void aiPing(Empire@ fromEmpire, vec3d position, uint type) {} void pause(Empire& emp) {} void resume(Empire& emp) {} vec3d get_aiFocus() { return vec3d(); } string getOpinionOf(Empire& emp, Empire@ other) { return ""; } int getStandingTo(Empire& emp, Empire@ other) { return 0; } }; AIController@ createWeaselAI() { return AI(); } |
Added scripts/server/empire_ai/weasel/debug.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Colonization; import empire_ai.weasel.Construction; import empire_ai.weasel.Budget; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Fleets; import empire_ai.weasel.Military; import empire_ai.weasel.Planets; import empire_ai.weasel.Resources; import empire_ai.weasel.Scouting; import empire_ai.weasel.Systems; import empire_ai.weasel.Creeping; import empire_ai.weasel.Movement; import empire_ai.weasel.Relations; import empire_ai.weasel.Intelligence; import empire_ai.weasel.War; import empire_ai.weasel.Research; import empire_ai.weasel.Energy; import empire_ai.weasel.Diplomacy; import empire_ai.weasel.ftl.Gate; import empire_ai.weasel.ftl.Hyperdrive; import empire_ai.weasel.ftl.Fling; import empire_ai.weasel.ftl.Slipstream; import empire_ai.weasel.ftl.Jumpdrive; import empire_ai.weasel.race.Verdant; import empire_ai.weasel.race.Mechanoid; import empire_ai.weasel.race.StarChildren; import empire_ai.weasel.race.Extragalactic; import empire_ai.weasel.race.Linked; import empire_ai.weasel.race.Devout; import empire_ai.weasel.race.Ancient; import empire_ai.weasel.misc.Invasion; import empire_ai.EmpireAI; AI@ ai(uint index) { Empire@ emp = getEmpire(index); return cast<AI>(cast<EmpireAI>(emp.EmpireAI).ctrl); } Colonization@ colonization(uint index) { return cast<Colonization>(ai(index).colonization); } Construction@ construction(uint index) { return cast<Construction>(ai(index).construction); } Budget@ budget(uint index) { return cast<Budget>(ai(index).budget); } Designs@ designs(uint index) { return cast<Designs>(ai(index).designs); } Development@ development(uint index) { return cast<Development>(ai(index).development); } Fleets@ fleets(uint index) { return cast<Fleets>(ai(index).fleets); } Military@ military(uint index) { return cast<Military>(ai(index).military); } Planets@ planets(uint index) { return cast<Planets>(ai(index).planets); } Resources@ resources(uint index) { return cast<Resources>(ai(index).resources); } Scouting@ scouting(uint index) { return cast<Scouting>(ai(index).scouting); } Systems@ systems(uint index) { return cast<Systems>(ai(index).systems); } Movement@ movement(uint index) { return cast<Movement>(ai(index).movement); } Creeping@ creeping(uint index) { return cast<Creeping>(ai(index).creeping); } Relations@ relations(uint index) { return cast<Relations>(ai(index).relations); } Intelligence@ intelligence(uint index) { return cast<Intelligence>(ai(index).intelligence); } War@ war(uint index) { return cast<War>(ai(index).war); } Research@ research(uint index) { return cast<Research>(ai(index).research); } Energy@ energy(uint index) { return cast<Energy>(ai(index).energy); } Diplomacy@ diplomacy(uint index) { return cast<Diplomacy>(ai(index).diplomacy); } Gate@ gate(uint index) { return cast<Gate>(ai(index).ftl); } Hyperdrive@ hyperdrive(uint index) { return cast<Hyperdrive>(ai(index).ftl); } Fling@ fling(uint index) { return cast<Fling>(ai(index).ftl); } Slipstream@ slipstream(uint index) { return cast<Slipstream>(ai(index).ftl); } Jumpdrive@ jumpdrive(uint index) { return cast<Jumpdrive>(ai(index).ftl); } Mechanoid@ mechanoid(uint index) { return cast<Mechanoid>(ai(index).race); } Verdant@ verdant(uint index) { return cast<Verdant>(ai(index).race); } StarChildren@ starchildren(uint index) { return cast<StarChildren>(ai(index).race); } Extragalactic@ extragalactic(uint index) { return cast<Extragalactic>(ai(index).race); } Linked@ linked(uint index) { return cast<Linked>(ai(index).race); } Devout@ devout(uint index) { return cast<Devout>(ai(index).race); } Ancient@ ancient(uint index) { return cast<Ancient>(ai(index).race); } Invasion@ invasion(uint index) { return cast<Invasion>(ai(index).invasion); } |
Added scripts/server/empire_ai/weasel/ftl/Fling.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Military; import empire_ai.weasel.Construction; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Systems; import empire_ai.weasel.Budget; import empire_ai.weasel.Fleets; import ftl; from orbitals import getOrbitalModuleID; const double FLING_MIN_DISTANCE_STAGE = 10000; const double FLING_MIN_DISTANCE_DEVELOP = 20000; const double FLING_MIN_TIMER = 3.0 * 60.0; int flingModule = -1; void init() { flingModule = getOrbitalModuleID("FlingCore"); } class FlingRegion : Savable { Region@ region; Object@ obj; bool installed = false; vec3d destination; void save(SaveFile& file) { file << region; file << obj; file << installed; file << destination; } void load(SaveFile& file) { file >> region; file >> obj; file >> installed; file >> destination; } }; class Fling : FTL { Military@ military; Designs@ designs; Construction@ construction; Development@ development; Systems@ systems; Budget@ budget; Fleets@ fleets; array<FlingRegion@> tracked; array<Object@> unused; BuildOrbital@ buildFling; double nextBuildTry = 15.0 * 60.0; bool wantToBuild = false; void create() override { @military = cast<Military>(ai.military); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @development = cast<Development>(ai.development); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); @fleets = cast<Fleets>(ai.fleets); } void save(SaveFile& file) override { construction.saveConstruction(file, buildFling); file << nextBuildTry; file << wantToBuild; uint cnt = tracked.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << tracked[i]; cnt = unused.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << unused[i]; } void load(SaveFile& file) override { @buildFling = cast<BuildOrbital>(construction.loadConstruction(file)); file >> nextBuildTry; file >> wantToBuild; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { FlingRegion fr; file >> fr; tracked.insertLast(fr); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Object@ obj; file >> obj; if(obj !is null) unused.insertLast(obj); } } uint order(MoveOrder& ord) override { if(!canFling(ord.obj)) return F_Pass; //Find the position to fling to vec3d toPosition; if(!targetPosition(ord, toPosition)) return F_Pass; //Don't fling if we're saving our FTL for a new beacon double avail = usableFTL(ai, ord); if((buildFling !is null && !buildFling.started) || wantToBuild) avail = min(avail, ai.empire.FTLStored - 250.0); //Make sure we have the ftl to fling if(flingCost(ord.obj, toPosition) > avail) return F_Pass; //Make sure we're in range of a beacon Object@ beacon = getClosest(ord.obj.position); if(beacon is null || beacon.position.distanceTo(ord.obj.position) > FLING_BEACON_RANGE) return F_Pass; ord.obj.addFlingOrder(beacon, toPosition); return F_Continue; } FlingRegion@ get(Region@ reg) { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].region is reg) return tracked[i]; } return null; } void remove(FlingRegion@ gt) { if(gt.obj !is null && gt.obj.valid && gt.obj.owner is ai.empire) unused.insertLast(gt.obj); tracked.remove(gt); } Object@ getClosest(const vec3d& position) { Object@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Object@ obj = tracked[i].obj; if(obj is null) continue; double d = obj.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = obj; } } for(uint i = 0, cnt = unused.length; i < cnt; ++i) { Object@ obj = unused[i]; if(obj is null) continue; double d = obj.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = obj; } } return closest; } FlingRegion@ getClosestRegion(const vec3d& position) { FlingRegion@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { double d = tracked[i].region.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = tracked[i]; } } return closest; } void assignTo(FlingRegion@ track, Object@ closest) { unused.remove(closest); @track.obj = closest; } bool trackingBeacon(Object@ obj) { for(uint i = 0, cnt = unused.length; i < cnt; ++i) { if(unused[i] is obj) return true; } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].obj is obj) return true; } return false; } bool shouldHaveBeacon(Region@ reg, bool always = false) { if(military.getBase(reg) !is null) return true; if(development.isDevelopingIn(reg)) return true; return false; } void focusTick(double time) override { //Manage unused beacons list for(uint i = 0, cnt = unused.length; i < cnt; ++i) { Object@ obj = unused[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { unused.removeAt(i); --i; --cnt; } } if(ai.behavior.forbidConstruction) return; //Detect new beacons auto@ data = ai.empire.getFlingBeacons(); Object@ obj; while(receive(data, obj)) { if(obj is null) continue; if(!trackingBeacon(obj)) unused.insertLast(obj); } //Update existing beacons for staging bases for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ reg = tracked[i]; bool checkAlways = false; if(reg.obj !is null) { if(!reg.obj.valid || reg.obj.owner !is ai.empire || reg.obj.region !is reg.region) { @reg.obj = null; checkAlways = true; } } if(!shouldHaveBeacon(reg.region, checkAlways)) { remove(tracked[i]); --i; --cnt; } } //Detect new staging bases to build beacons at for(uint i = 0, cnt = military.stagingBases.length; i < cnt; ++i) { auto@ base = military.stagingBases[i]; if(base.occupiedTime < FLING_MIN_TIMER) continue; if(get(base.region) is null) { FlingRegion@ closest = getClosestRegion(base.region.position); if(closest !is null && closest.region.position.distanceTo(base.region.position) < FLING_MIN_DISTANCE_STAGE) continue; FlingRegion gt; @gt.region = base.region; tracked.insertLast(gt); break; } } //Detect new important planets to build beacons at for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { auto@ focus = development.focuses[i]; Region@ reg = focus.obj.region; if(reg is null) continue; if(get(reg) is null) { FlingRegion@ closest = getClosestRegion(reg.position); if(closest !is null && closest.region.position.distanceTo(reg.position) < FLING_MIN_DISTANCE_DEVELOP) continue; FlingRegion gt; @gt.region = reg; tracked.insertLast(gt); break; } } //Destroy beacons if we're having ftl trouble if(ai.empire.FTLShortage && !ai.behavior.forbidScuttle) { Orbital@ leastImportant; double leastWeight = INFINITY; for(uint i = 0, cnt = unused.length; i < cnt; ++i) { Orbital@ obj = cast<Orbital>(unused[i]); if(obj is null || !obj.valid) continue; @leastImportant = obj; leastWeight = 0.0; break; } if(leastImportant !is null) { if(log) ai.print("Scuttle unused beacon for ftl", leastImportant.region); leastImportant.scuttle(); } else { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Orbital@ obj = cast<Orbital>(tracked[i].obj); if(obj is null || !obj.valid) continue; double weight = 1.0; auto@ base = military.getBase(tracked[i].region); if(base is null) { weight *= 5.0; } else if(base.idleTime >= 1) { weight *= 1.0 + (base.idleTime / 60.0); } else { weight /= 2.0; } if(weight < leastWeight) { @leastImportant = obj; leastWeight = weight; } } if(leastImportant !is null) { if(log) ai.print("Scuttle unimportant beacon for ftl", leastImportant.region); leastImportant.scuttle(); } } } //See if we should build a new gate if(buildFling !is null) { if(buildFling.completed) { @buildFling = null; nextBuildTry = gameTime + 60.0; } } wantToBuild = false; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; if(gt.obj is null && gt.region.ContestedMask & ai.mask == 0 && gt.region.BlockFTLMask & ai.mask == 0) { Object@ found; for(uint n = 0, ncnt = unused.length; n < ncnt; ++n) { Object@ obj = unused[n]; if(obj.region is gt.region) { @found = obj; break; } } if(found !is null) { if(log) ai.print("Assign beacon to => "+gt.region.name, found.region); assignTo(gt, found); } else if(buildFling is null && gameTime > nextBuildTry && !ai.empire.isFTLShortage(0.15)) { if(ai.empire.FTLStored >= 250) { if(log) ai.print("Build beacon for this system", gt.region); @buildFling = construction.buildOrbital(getOrbitalModule(flingModule), military.getStationPosition(gt.region)); } else { wantToBuild = true; } } } } //Scuttle anything unused if we don't need beacons in those regions for(uint i = 0, cnt = unused.length; i < cnt; ++i) { if(get(unused[i].region) is null && unused[i].isOrbital) { cast<Orbital>(unused[i]).scuttle(); unused.removeAt(i); --i; --cnt; } } //Try to get enough ftl storage that we can fling our largest fleet and have some remaining double highestCost = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; highestCost = max(highestCost, double(flingCost(flAI.obj, vec3d()))); } development.aimFTLStorage = highestCost / (1.0 - ai.behavior.ftlReservePctCritical - ai.behavior.ftlReservePctNormal); } }; AIComponent@ createFling() { return Fling(); } |
Added scripts/server/empire_ai/weasel/ftl/Gate.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Military; import empire_ai.weasel.Construction; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Systems; import empire_ai.weasel.Budget; from statuses import getStatusID; from abilities import getAbilityID; const double GATE_MIN_DISTANCE_STAGE = 10000; const double GATE_MIN_DISTANCE_DEVELOP = 20000; const double GATE_MIN_DISTANCE_BORDER = 30000; const double GATE_MIN_TIMER = 3.0 * 60.0; const int GATE_BUILD_MOVE_HOPS = 5; int packAbility = -1; int unpackAbility = -1; int packedStatus = -1; int unpackedStatus = -1; void init() { packAbility = getAbilityID("GatePack"); unpackAbility = getAbilityID("GateUnpack"); packedStatus = getStatusID("GatePacked"); unpackedStatus = getStatusID("GateUnpacked"); } class GateRegion : Savable { Region@ region; Object@ gate; bool installed = false; vec3d destination; void save(SaveFile& file) { file << region; file << gate; file << installed; file << destination; } void load(SaveFile& file) { file >> region; file >> gate; file >> installed; file >> destination; } }; class Gate : FTL { Military@ military; Designs@ designs; Construction@ construction; Development@ development; Systems@ systems; Budget@ budget; DesignTarget@ gateDesign; array<GateRegion@> tracked; array<Object@> unassigned; BuildStation@ buildGate; double nextBuildTry = 15.0 * 60.0; void create() override { @military = cast<Military>(ai.military); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @development = cast<Development>(ai.development); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); } void save(SaveFile& file) override { designs.saveDesign(file, gateDesign); construction.saveConstruction(file, buildGate); file << nextBuildTry; uint cnt = tracked.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << tracked[i]; cnt = unassigned.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << unassigned[i]; } void load(SaveFile& file) override { @gateDesign = designs.loadDesign(file); @buildGate = cast<BuildStation>(construction.loadConstruction(file)); file >> nextBuildTry; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { GateRegion gt; file >> gt; tracked.insertLast(gt); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Object@ obj; file >> obj; if(obj !is null) unassigned.insertLast(obj); } } GateRegion@ get(Region@ reg) { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].region is reg) return tracked[i]; } return null; } void remove(GateRegion@ gt) { if(gt.gate !is null && gt.gate.valid && gt.gate.owner is ai.empire) unassigned.insertLast(gt.gate); tracked.remove(gt); } Object@ getClosestGate(const vec3d& position) { Object@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Object@ gate = tracked[i].gate; if(gate is null) continue; if(!tracked[i].installed) continue; double d = gate.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = gate; } } return closest; } GateRegion@ getClosestGateRegion(const vec3d& position) { GateRegion@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { double d = tracked[i].region.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = tracked[i]; } } return closest; } void assignTo(GateRegion@ gt, Object@ closest) { unassigned.remove(closest); @gt.gate = closest; gt.installed = false; if(closest.region is gt.region) { if(closest.hasStatusEffect(unpackedStatus)) { gt.installed = true; } } if(!gt.installed) { gt.destination = military.getStationPosition(gt.region); closest.activateAbilityTypeFor(ai.empire, packAbility); closest.addMoveOrder(gt.destination); } } bool trackingGate(Object@ obj) { for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { if(unassigned[i] is obj) return true; } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].gate is obj) return true; } return false; } bool shouldHaveGate(Region@ reg, bool always = false) { if(military.getBase(reg) !is null) return true; if(development.isDevelopingIn(reg)) return true; if(!always) { auto@ sys = systems.getAI(reg); if(sys !is null) { if(sys.border && sys.bordersEmpires) return true; } } return false; } void turn() override { if(gateDesign !is null && gateDesign.active !is null) { int newSize = round(double(budget.spendable(BT_Military)) * 0.5 * ai.behavior.shipSizePerMoney / 64.0) * 64; if(newSize < 128) newSize = 128; if(newSize != gateDesign.targetSize) { @gateDesign = designs.design(DP_Gate, newSize); gateDesign.customName = "Gate"; } } } void focusTick(double time) override { if(ai.behavior.forbidConstruction) return; //Design a gate if(gateDesign is null) { @gateDesign = designs.design(DP_Gate, 128); gateDesign.customName = "Gate"; } //Manage unassigned gates list for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { Object@ obj = unassigned[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { unassigned.removeAt(i); --i; --cnt; } } //Detect new gates auto@ data = ai.empire.getStargates(); Object@ obj; while(receive(data, obj)) { if(obj is null) continue; if(!trackingGate(obj)) unassigned.insertLast(obj); } //Update existing gates for staging bases for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; bool checkAlways = false; if(gt.gate !is null) { if(!gt.gate.valid || gt.gate.owner !is ai.empire || (gt.installed && gt.gate.region !is gt.region)) { @gt.gate = null; gt.installed = false; checkAlways = true; } else if(!gt.installed && !gt.gate.hasOrders) { if(gt.destination.distanceTo(gt.gate.position) < 10.0) { gt.gate.activateAbilityTypeFor(ai.empire, unpackAbility, gt.destination); gt.installed = true; } else { gt.gate.activateAbilityTypeFor(ai.empire, packAbility); gt.gate.addMoveOrder(gt.destination); } } } if(!shouldHaveGate(gt.region, checkAlways)) { remove(tracked[i]); --i; --cnt; } } //Detect new staging bases to build gates at for(uint i = 0, cnt = military.stagingBases.length; i < cnt; ++i) { auto@ base = military.stagingBases[i]; if(base.occupiedTime < GATE_MIN_TIMER) continue; if(get(base.region) is null) { GateRegion@ closest = getClosestGateRegion(base.region.position); if(closest !is null && closest.region.position.distanceTo(base.region.position) < GATE_MIN_DISTANCE_STAGE) continue; GateRegion gt; @gt.region = base.region; tracked.insertLast(gt); break; } } //Detect new important planets to build gates at for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { auto@ focus = development.focuses[i]; Region@ reg = focus.obj.region; if(reg is null) continue; if(get(reg) is null) { GateRegion@ closest = getClosestGateRegion(reg.position); if(closest !is null && closest.region.position.distanceTo(reg.position) < GATE_MIN_DISTANCE_DEVELOP) continue; GateRegion gt; @gt.region = reg; tracked.insertLast(gt); break; } } //Detect new border systems to build gates at uint offset = randomi(0, systems.border.length-1); for(uint i = 0, cnt = systems.border.length; i < cnt; ++i) { auto@ sys = systems.border[(i+offset)%cnt]; Region@ reg = sys.obj; if(reg is null) continue; if(!sys.bordersEmpires) continue; if(get(reg) is null) { GateRegion@ closest = getClosestGateRegion(reg.position); if(closest !is null && closest.region.position.distanceTo(reg.position) < GATE_MIN_DISTANCE_DEVELOP) continue; GateRegion gt; @gt.region = reg; tracked.insertLast(gt); break; } } //Destroy gates if we're having ftl trouble if(ai.empire.FTLShortage && !ai.behavior.forbidScuttle) { Ship@ leastImportant; double leastWeight = INFINITY; for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { Ship@ ship = cast<Ship>(unassigned[i]); if(ship is null || !ship.valid) continue; double weight = ship.blueprint.design.size; weight *= 10.0; if(weight < leastWeight) { @leastImportant = ship; leastWeight = weight; } } if(leastImportant !is null) { if(log) ai.print("Scuttle unassigned gate for ftl", leastImportant.region); leastImportant.scuttle(); } else { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Ship@ ship = cast<Ship>(tracked[i].gate); if(ship is null || !ship.valid) continue; double weight = ship.blueprint.design.size; auto@ base = military.getBase(tracked[i].region); if(base is null) { weight *= 5.0; } else if(base.idleTime >= 1) { weight *= 1.0 + (base.idleTime / 60.0); } else { weight /= 2.0; } if(weight < leastWeight) { @leastImportant = ship; leastWeight = weight; } } if(leastImportant !is null) { if(log) ai.print("Scuttle unimportant gate for ftl", leastImportant.region); leastImportant.scuttle(); } } } //See if we should build a new gate if(buildGate !is null) { if(buildGate.completed) { @buildGate = null; nextBuildTry = gameTime + 60.0; } } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; if(gt.gate is null && gt.region.ContestedMask & ai.mask == 0 && gt.region.BlockFTLMask & ai.mask == 0) { Object@ closest; double closestDist = INFINITY; for(uint n = 0, ncnt = unassigned.length; n < ncnt; ++n) { Object@ obj = unassigned[n]; if(obj.region is gt.region) { @closest = obj; break; } if(!obj.hasMover) continue; if(buildGate is null && gameTime > nextBuildTry) { double d = obj.position.distanceTo(gt.region.position); if(d < closestDist) { closestDist = d; @closest = obj; } } } if(closest !is null) { if(log) ai.print("Assign gate to => "+gt.region.name, closest.region); assignTo(gt, closest); } else if(buildGate is null && gameTime > nextBuildTry && !ai.empire.isFTLShortage(0.15)) { if(log) ai.print("Build gate for this system", gt.region); bool buildLocal = true; auto@ factory = construction.primaryFactory; if(factory !is null) { Region@ factRegion = factory.obj.region; if(factRegion !is null && systems.hopDistance(gt.region, factRegion) < GATE_BUILD_MOVE_HOPS) buildLocal = false; } if(buildLocal) @buildGate = construction.buildLocalStation(gateDesign); else @buildGate = construction.buildStation(gateDesign, military.getStationPosition(gt.region)); } } } } }; AIComponent@ createGate() { return Gate(); } |
Added scripts/server/empire_ai/weasel/ftl/Hyperdrive.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Development; import empire_ai.weasel.Fleets; import ftl; from orders import OrderType; const double REJUMP_MIN_DIST = 8000.0; const double STORAGE_AIM_DISTANCE = 40000; class Hyperdrive : FTL { Development@ development; Fleets@ fleets; void create() override { @development = cast<Development>(ai.development); @fleets = cast<Fleets>(ai.fleets); } double hdETA(Object& obj, const vec3d& position) { double charge = HYPERDRIVE_CHARGE_TIME; if(obj.owner.HyperdriveNeedCharge == 0) charge = 0.0; double dist = position.distanceTo(obj.position); double speed = hyperdriveMaxSpeed(obj); return charge + dist / speed; } double subETA(Object& obj, const vec3d& position) { return newtonArrivalTime(obj.maxAcceleration, position - obj.position, vec3d()); } bool shouldHD(Object& obj, const vec3d& position, uint priority) { //This makes me sad if(position.distanceTo(obj.position) < 3000) return false; double pathDist = cast<Movement>(ai.movement).getPathDistance(obj.position, position, obj.maxAcceleration); double straightDist = position.distanceTo(obj.position); return pathDist >= straightDist * 0.6; } uint order(MoveOrder& ord) override { if(!canHyperdrive(ord.obj)) return F_Pass; double avail = usableFTL(ai, ord); if(avail > 0) { vec3d toPosition; if(targetPosition(ord, toPosition)) { if(shouldHD(ord.obj, toPosition, ord.priority)) { double cost = hyperdriveCost(ord.obj, toPosition); if(avail >= cost) { ord.obj.addHyperdriveOrder(toPosition); return F_Continue; } } } } return F_Pass; } uint tick(MoveOrder& ord, double time) { if(ord.priority == MP_Critical && canHyperdrive(ord.obj) && ord.obj.firstOrderType != OT_Hyperdrive) { vec3d toPosition; if(targetPosition(ord, toPosition)) { double dist = ord.obj.position.distanceToSQ(toPosition); if(dist > REJUMP_MIN_DIST * REJUMP_MIN_DIST) { double avail = usableFTL(ai, ord); double cost = hyperdriveCost(ord.obj, toPosition); if(avail >= cost && shouldHD(ord.obj, toPosition, ord.priority)) { cast<Movement>(ai.movement).order(ord); return F_Continue; } } } } return F_Pass; } void focusTick(double time) override { //Try to get enough ftl storage that we can ftl our largest fleet a fair distance and have some remaining double highestCost = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; vec3d toPosition = flAI.obj.position + vec3d(0, 0, STORAGE_AIM_DISTANCE); highestCost = max(highestCost, double(hyperdriveCost(flAI.obj, toPosition))); } development.aimFTLStorage = highestCost / (1.0 - ai.behavior.ftlReservePctCritical - ai.behavior.ftlReservePctNormal); } }; AIComponent@ createHyperdrive() { return Hyperdrive(); } |
Added scripts/server/empire_ai/weasel/ftl/Jumpdrive.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Development; import empire_ai.weasel.Fleets; import ftl; import system_flags; import regions.regions; import systems; from orders import OrderType; const double REJUMP_MIN_DIST = 8000.0; class Jumpdrive : FTL { Development@ development; Fleets@ fleets; int safetyFlag = -1; array<Region@> safeRegions; void create() override { @development = cast<Development>(ai.development); @fleets = cast<Fleets>(ai.fleets); safetyFlag = getSystemFlag("JumpdriveSafety"); } void save(SaveFile& file) { uint cnt = safeRegions.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << safeRegions[i]; } void load(SaveFile& file) { uint cnt = 0; file >> cnt; safeRegions.length = cnt; for(uint i = 0; i < cnt; ++i) file >> safeRegions[i]; } double jdETA(Object& obj, const vec3d& position) { double charge = JUMPDRIVE_CHARGE_TIME; return charge; } double subETA(Object& obj, const vec3d& position) { return newtonArrivalTime(obj.maxAcceleration, position - obj.position, vec3d()); } bool shouldJD(Object& obj, const vec3d& position, uint priority) { //This makes me sad if(position.distanceTo(obj.position) < 3000) return false; return true; /*double factor = 0.8;*/ /*if(priority == MP_Critical)*/ /* factor = 1.0;*/ /*return jdETA(obj, position) <= factor * subETA(obj, position);*/ } uint order(MoveOrder& ord) override { return order(ord, ord.obj.position, false); } uint order(MoveOrder& ord, const vec3d& fromPos, bool secondary) { if(!canJumpdrive(ord.obj)) return F_Pass; double avail = usableFTL(ai, ord); if(avail > 0) { vec3d toPosition; if(targetPosition(ord, toPosition)) { double maxRange = jumpdriveRange(ord.obj); double dist = toPosition.distanceTo(fromPos); bool isSafe = false; Region@ reg = getRegion(toPosition); if(reg !is null) isSafe = reg.getSystemFlag(ai.empire, safetyFlag); if(dist > maxRange && !isSafe) { //See if we should jump to a safe region first if(!secondary) { double bestHop = INFINITY; Region@ hopRegion; vec3d bestPos; for(uint i = 0, cnt = safeRegions.length; i < cnt; ++i) { if(!safeRegions[i].getSystemFlag(ai.empire, safetyFlag)) continue; vec3d hopPos = safeRegions[i].position; hopPos = hopPos + (fromPos-hopPos).normalized(safeRegions[i].radius * 0.85); double d = hopPos.distanceTo(toPosition); if(d < bestHop) { bestHop = d; @hopRegion = safeRegions[i]; bestPos = hopPos; } } if(bestHop < dist * 0.8) { double cost = jumpdriveCost(ord.obj, fromPos, bestPos); if(avail >= cost) { ord.obj.addJumpdriveOrder(bestPos); order(ord, bestPos, true); return F_Continue; } } } //Shorten our jump if(ord.priority < MP_Normal) return F_Pass; toPosition = fromPos + (toPosition - fromPos).normalized(maxRange); } if(shouldJD(ord.obj, toPosition, ord.priority)) { double cost = jumpdriveCost(ord.obj, toPosition); if(avail >= cost) { ord.obj.addJumpdriveOrder(toPosition, append=secondary); return F_Continue; } } } } return F_Pass; } uint tick(MoveOrder& ord, double time) { if(ord.priority == MP_Critical && canJumpdrive(ord.obj) && ord.obj.firstOrderType != OT_Jumpdrive) { vec3d toPosition; if(targetPosition(ord, toPosition)) { double dist = ord.obj.position.distanceToSQ(toPosition); if(dist > REJUMP_MIN_DIST * REJUMP_MIN_DIST) { double maxRange = jumpdriveRange(ord.obj); dist = sqrt(dist); bool isSafe = false; Region@ reg = getRegion(toPosition); if(reg !is null) isSafe = reg.getSystemFlag(ai.empire, safetyFlag); if(dist > maxRange && !isSafe) toPosition = ord.obj.position + (toPosition - ord.obj.position).normalized(maxRange); if(shouldJD(ord.obj, toPosition, ord.priority)) { double avail = usableFTL(ai, ord); double cost = jumpdriveCost(ord.obj, toPosition); if(avail >= cost) { cast<Movement>(ai.movement).order(ord); return F_Continue; } } } } } return F_Pass; } uint sysChk = 0; void start() { for(uint i = 0, cnt = systemCount; i < cnt; ++i) { Region@ reg = getSystem(i).object; if(reg.getSystemFlag(ai.empire, safetyFlag)) safeRegions.insertLast(reg); } } void focusTick(double time) override { //Try to get enough ftl storage that we can ftl our largest fleet a fair distance and have some remaining double highestCost = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; double dist = jumpdriveRange(flAI.obj); vec3d toPosition = flAI.obj.position + vec3d(0, 0, dist); highestCost = max(highestCost, double(jumpdriveCost(flAI.obj, toPosition))); } development.aimFTLStorage = highestCost / (1.0 - ai.behavior.ftlReservePctCritical - ai.behavior.ftlReservePctNormal); //Disable systems that are no longer safe for(uint i = 0, cnt = safeRegions.length; i < cnt; ++i) { if(!safeRegions[i].getSystemFlag(ai.empire, safetyFlag)) { safeRegions.removeAt(i); --i; --cnt; } } //Try to find regions that are safe for us { sysChk = (sysChk+1) % systemCount; auto@ reg = getSystem(sysChk).object; if(reg.getSystemFlag(ai.empire, safetyFlag)) { if(safeRegions.find(reg) == -1) safeRegions.insertLast(reg); } } } }; AIComponent@ createJumpdrive() { return Jumpdrive(); } |
Added scripts/server/empire_ai/weasel/ftl/Slipstream.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Military; import empire_ai.weasel.Construction; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Systems; import empire_ai.weasel.Budget; import empire_ai.weasel.Fleets; from statuses import getStatusID; from abilities import getAbilityID; from oddity_navigation import hasOddityLink; const double SS_MIN_DISTANCE_STAGE = 0; const double SS_MIN_DISTANCE_DEVELOP = 10000; const double SS_MIN_TIMER = 3.0 * 60.0; const double SS_MAX_DISTANCE = 3000.0; class SSRegion : Savable { Region@ region; Object@ obj; bool arrived = false; vec3d destination; void save(SaveFile& file) { file << region; file << obj; file << arrived; file << destination; } void load(SaveFile& file) { file >> region; file >> obj; file >> arrived; file >> destination; } }; class Slipstream : FTL { Military@ military; Designs@ designs; Construction@ construction; Development@ development; Systems@ systems; Budget@ budget; Fleets@ fleets; DesignTarget@ ssDesign; array<SSRegion@> tracked; array<Object@> unassigned; BuildFlagship@ buildSS; double nextBuildTry = 15.0 * 60.0; void create() override { @military = cast<Military>(ai.military); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @development = cast<Development>(ai.development); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); @fleets = cast<Fleets>(ai.fleets); } void save(SaveFile& file) override { designs.saveDesign(file, ssDesign); construction.saveConstruction(file, buildSS); file << nextBuildTry; uint cnt = tracked.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << tracked[i]; cnt = unassigned.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << unassigned[i]; } void load(SaveFile& file) override { @ssDesign = designs.loadDesign(file); @buildSS = cast<BuildFlagship>(construction.loadConstruction(file)); file >> nextBuildTry; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { SSRegion gt; file >> gt; tracked.insertLast(gt); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Object@ obj; file >> obj; if(obj !is null) unassigned.insertLast(obj); } } uint order(MoveOrder& ord) override { //Find the position to fling to vec3d toPosition; if(!targetPosition(ord, toPosition)) return F_Pass; //Check if we have a slipstream generator in this region auto@ gt = get(ord.obj.region); if(gt is null || gt.obj is null || !gt.arrived) return F_Pass; //Make sure our generator is usable Object@ ssGen = gt.obj; if(!canSlipstream(ssGen)) return F_Pass; //Check if we already have a link if(hasOddityLink(gt.region, toPosition, SS_MAX_DISTANCE, minDuration=60.0)) return F_Pass; //See if we have the FTL to make a link double avail = usableFTL(ai, ord); if(!canSlipstreamTo(ssGen, toPosition)) return F_Pass; if(slipstreamCost(ssGen, 0, toPosition.distanceTo(ssGen.position)) >= avail) return F_Pass; ssGen.addSlipstreamOrder(toPosition, append=true); if(ssGen !is ord.obj) { ord.obj.addWaitOrder(ssGen, moveTo=true); ssGen.addSecondaryToSlipstream(ord.obj); } else { ord.obj.addMoveOrder(toPosition, append=true); } return F_Continue; } SSRegion@ get(Region@ reg) { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].region is reg) return tracked[i]; } return null; } void remove(SSRegion@ gt) { if(gt.obj !is null && gt.obj.valid && gt.obj.owner is ai.empire) unassigned.insertLast(gt.obj); tracked.remove(gt); } Object@ getClosest(const vec3d& position) { Object@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Object@ obj = tracked[i].obj; if(obj is null) continue; if(!tracked[i].arrived) continue; double d = obj.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = obj; } } return closest; } SSRegion@ getClosestRegion(const vec3d& position) { SSRegion@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { double d = tracked[i].region.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = tracked[i]; } } return closest; } void assignTo(SSRegion@ gt, Object@ closest) { unassigned.remove(closest); @gt.obj = closest; gt.arrived = false; military.stationFleet(fleets.getAI(closest), gt.region); if(closest.region is gt.region) gt.arrived = true; if(!gt.arrived) { gt.destination = military.getStationPosition(gt.region); closest.addMoveOrder(gt.destination); } } bool trackingGen(Object@ obj) { for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { if(unassigned[i] is obj) return true; } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].obj is obj) return true; } return false; } bool shouldHaveGen(Region@ reg, bool always = false) { if(military.getBase(reg) !is null) return true; if(development.isDevelopingIn(reg)) return true; return false; } void turn() override { if(ssDesign !is null && ssDesign.active !is null) { int newSize = round(double(budget.spendable(BT_Military)) * 0.2 * ai.behavior.shipSizePerMoney / 64.0) * 64; if(newSize < 128) newSize = 128; if(newSize != ssDesign.targetSize) { @ssDesign = designs.design(DP_Slipstream, newSize); ssDesign.customName = "Slipstream"; } } } void focusTick(double time) override { if(ai.behavior.forbidConstruction) return; //Design a generator if(ssDesign is null) { @ssDesign = designs.design(DP_Slipstream, 128); ssDesign.customName = "Slipstream"; } //Manage unassigned gens list for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { Object@ obj = unassigned[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { unassigned.removeAt(i); --i; --cnt; } } //Detect new gens for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Slipstream) continue; if(!trackingGen(flAI.obj)) unassigned.insertLast(flAI.obj); } //Update existing gens for staging bases for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; bool checkAlways = false; if(gt.obj !is null) { if(!gt.obj.valid || gt.obj.owner !is ai.empire || (gt.arrived && gt.obj.region !is gt.region)) { @gt.obj = null; gt.arrived = false; checkAlways = true; } else if(!gt.arrived && !gt.obj.hasOrders) { if(gt.destination.distanceTo(gt.obj.position) < 10.0) gt.arrived = true; else assignTo(gt, gt.obj); } } if(!shouldHaveGen(gt.region, checkAlways)) { remove(tracked[i]); --i; --cnt; } } //Detect new staging bases to build gens at for(uint i = 0, cnt = military.stagingBases.length; i < cnt; ++i) { auto@ base = military.stagingBases[i]; if(base.occupiedTime < SS_MIN_TIMER) continue; if(get(base.region) is null) { SSRegion@ closest = getClosestRegion(base.region.position); if(closest !is null && closest.region.position.distanceTo(base.region.position) < SS_MIN_DISTANCE_STAGE) continue; SSRegion gt; @gt.region = base.region; tracked.insertLast(gt); break; } } //Detect new important planets to build generator at for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { auto@ focus = development.focuses[i]; Region@ reg = focus.obj.region; if(reg is null) continue; if(get(reg) is null) { SSRegion@ closest = getClosestRegion(reg.position); if(closest !is null && closest.region.position.distanceTo(reg.position) < SS_MIN_DISTANCE_DEVELOP) continue; SSRegion gt; @gt.region = reg; tracked.insertLast(gt); break; } } //See if we should build a new generator if(buildSS !is null) { if(buildSS.completed) { @buildSS = null; nextBuildTry = gameTime + 60.0; } } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; if(gt.obj is null && gt.region.ContestedMask & ai.mask == 0 && gt.region.BlockFTLMask & ai.mask == 0) { Object@ closest; double closestDist = INFINITY; for(uint n = 0, ncnt = unassigned.length; n < ncnt; ++n) { Object@ obj = unassigned[n]; if(obj.region is gt.region) { @closest = obj; break; } if(!obj.hasMover) continue; if(buildSS is null && gameTime > nextBuildTry) { double d = obj.position.distanceTo(gt.region.position); if(d < closestDist) { closestDist = d; @closest = obj; } } } if(closest !is null) { if(log) ai.print("Assign slipstream gen to => "+gt.region.name, closest.region); assignTo(gt, closest); } else if(buildSS is null && gameTime > nextBuildTry) { if(log) ai.print("Build slipstream gen for this system", gt.region); @buildSS = construction.buildFlagship(ssDesign); } } } //Try to get enough ftl storage that we can permanently open a slipstream with each of generators double mostCost = 0.0; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Ship@ obj = cast<Ship>(tracked[i].obj); if(obj is null) continue; double baseCost = obj.blueprint.design.average(SV_SlipstreamCost); double duration = obj.blueprint.design.average(SV_SlipstreamDuration); mostCost += baseCost / duration; } development.aimFTLStorage = mostCost; } }; AIComponent@ createSlipstream() { return Slipstream(); } |
Added scripts/server/empire_ai/weasel/misc/Invasion.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Fleets; import empire_ai.weasel.Systems; import empire_ai.weasel.Movement; import empire_ai.weasel.searches; import systems; from empire import Pirates; class InvasionDefendMission : Mission { FleetAI@ fleet; Region@ targRegion; MoveOrder@ move; bool pending = false; Object@ eliminate; void save(Fleets& fleets, SaveFile& file) override { fleets.saveAI(file, fleet); file << targRegion; fleets.movement.saveMoveOrder(file, move); file << pending; } void load(Fleets& fleets, SaveFile& file) override { @fleet = fleets.loadAI(file); file >> targRegion; @move = fleets.movement.loadMoveOrder(file); file >> pending; } bool get_isActive() override { return targRegion !is null; } void tick(AI& ai, FleetAI& fleet, double time) override { if(targRegion is null) return; if(move !is null) return; //Find stuff to fight if(eliminate is null) @eliminate = findEnemy(targRegion, null, ai.empire.hostileMask); if(eliminate !is null) { if(!eliminate.valid) { @eliminate = null; } else { if(!fleet.obj.hasOrders) fleet.obj.addAttackOrder(eliminate); if((fleet.filled < 0.3 || fleet.supplies < 0.3 || fleet.flagshipHealth < 0.5) && eliminate.getFleetStrength() * 2.0 > fleet.strength && !pending) { @targRegion = null; @eliminate = null; @move = cast<Fleets>(ai.fleets).returnToBase(fleet, MP_Critical); } } } } void update(AI& ai, Invasion& invasion) { //Manage movement if(move !is null) { if(move.failed || move.completed) @move = null; } //Find new regions to go to if(targRegion is null || (!pending && move is null && !invasion.isFighting(targRegion))) { bool ready = fleet.actionableState && move is null; DefendSystem@ bestDef; double bestWeight = 0.0; for(uint i = 0, cnt = invasion.defending.length; i < cnt; ++i) { auto@ def = invasion.defending[i]; double w = randomd(0.9, 1.1); if(!def.fighting) { if(!ready) continue; else w *= 0.1; } if(!def.winning) { w *= 10.0; } else { if(!ready) continue; } if(def.obj is targRegion) w *= 1.5; if(w > bestWeight) { bestWeight = w; @bestDef = def; } } if(bestDef !is null && fleet.supplies >= 0.25 && fleet.filled >= 0.2 && fleet.fleetHealth >= 0.2) { @targRegion = bestDef.obj; invasion.pend(targRegion, fleet); pending = true; } } //Move to the region we want to go to if(targRegion !is null) { if(move is null) { if(fleet.obj.region !is targRegion) { @eliminate = findEnemy(targRegion, null, ai.empire.hostileMask); if(eliminate is null) { vec3d targPos = targRegion.position; targPos += (targRegion.position - ai.empire.HomeSystem.position).normalized(targRegion.radius * 0.85); @move = invasion.movement.move(fleet.obj, targPos, MP_Critical); } else { @move = invasion.movement.move(fleet.obj, eliminate, MP_Critical, nearOnly=true); } } else { //Remove from pending list if(pending) { invasion.unpend(targRegion, fleet); pending = false; } //See if we should return to base if(!invasion.isFighting(targRegion) && (fleet.supplies < 0.25 || fleet.filled < 0.5)) { @targRegion = null; @move = invasion.fleets.returnToBase(fleet, MP_Critical); } } } } } }; class DefendSystem { Region@ obj; array<FleetAI@> pending; double enemyStrength = 0.0; double ourStrength = 0.0; double remnantStrength = 0.0; double pendingStrength = 0.0; void save(Invasion& invasion, SaveFile& file) { file << obj; uint cnt = pending.length; file << cnt; for(uint i = 0; i < cnt; ++i) invasion.fleets.saveAI(file, pending[i]); file << enemyStrength; file << ourStrength; file << remnantStrength; file << pendingStrength; } void load(Invasion& invasion, SaveFile& file) { file >> obj; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ fleet = invasion.fleets.loadAI(file); if(fleet !is null && fleet.obj !is null) pending.insertLast(fleet); } file >> enemyStrength; file >> ourStrength; file >> remnantStrength; file >> pendingStrength; } void update(AI& ai, Invasion& invasion) { enemyStrength = getTotalFleetStrength(obj, ai.empire.hostileMask); ourStrength = getTotalFleetStrength(obj, ai.mask); remnantStrength = getTotalFleetStrength(obj, Pirates.mask); if(gameTime < 10.0 * 60.0) ourStrength += remnantStrength; else if(gameTime < 30.0 * 60.0) ourStrength += remnantStrength * 0.5; pendingStrength = 0.0; for(uint i = 0, cnt = pending.length; i < cnt; ++i) pendingStrength += sqrt(pending[i].strength); pendingStrength *= pendingStrength; if(obj.PlanetsMask & ai.empire.mask != 0) ai.empire.setDefending(obj, true); } bool get_fighting() { return enemyStrength > 0; } bool get_winning() { return ourStrength + pendingStrength > enemyStrength; } }; class Invasion : AIComponent { Fleets@ fleets; Movement@ movement; array<DefendSystem@> defending; array<InvasionDefendMission@> tracked; void create() { @fleets = cast<Fleets>(ai.fleets); @movement = cast<Movement>(ai.movement); ai.behavior.maintenancePerShipSize = 0.0; } void save(SaveFile& file) { uint cnt = defending.length; file << cnt; for(uint i = 0; i < cnt; ++i) defending[i].save(this, file); cnt = tracked.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveMission(file, tracked[i]); } void load(SaveFile& file) { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { DefendSystem def; def.load(this, file); defending.insertLast(def); } file >> cnt; for(uint i = 0; i < cnt; ++i) { InvasionDefendMission@ miss = cast<InvasionDefendMission>(fleets.loadMission(file)); if(miss !is null) tracked.insertLast(miss); } } void start() { //Find systems to defend Region@ home = ai.empire.HomeSystem; const SystemDesc@ sys = getSystem(home); for(uint i = 0, cnt = sys.adjacent.length; i < cnt; ++i) { auto@ otherSys = getSystem(sys.adjacent[i]); if(findEnemy(otherSys.object, null, Pirates.mask, fleets=false, stations=true) !is null) { DefendSystem def; @def.obj = otherSys.object; defending.insertLast(def); } } } bool isManaging(FleetAI& fleet) { if(fleet.mission is null) return false; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i] is fleet.mission) return true; } return false; } void manage(FleetAI& fleet) { InvasionDefendMission miss; @miss.fleet = fleet; fleets.performMission(fleet, miss); tracked.insertLast(miss); } void pend(Region@ region, FleetAI& fleet) { for(uint i = 0, cnt = defending.length; i < cnt; ++i ){ if(defending[i].obj is region) { defending[i].pending.insertLast(fleet); break; } } } void unpend(Region@ region, FleetAI& fleet) { for(uint i = 0, cnt = defending.length; i < cnt; ++i ){ if(defending[i].obj is region) { defending[i].pending.remove(fleet); break; } } } DefendSystem@ getDefending(Region@ region) { for(uint i = 0, cnt = defending.length; i < cnt; ++i ){ if(defending[i].obj is region) return defending[i]; } return null; } bool isFighting(Region@ region) { for(uint i = 0, cnt = defending.length; i < cnt; ++i ){ if(defending[i].obj is region) return defending[i].fighting; } return false; } uint sysUpd = 0; void focusTick(double time) { //All your fleets are belong to us for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; if(!isManaging(flAI)) manage(flAI); } //Update systems we're defending if(defending.length != 0) { sysUpd = (sysUpd+1) % defending.length; defending[sysUpd].update(ai, this); } //Make sure our fleets are in the right places for(uint i = 0, cnt = tracked.length; i < cnt; ++i) tracked[i].update(ai, this); } }; AIComponent@ createInvasion() { return Invasion(); } |
Added scripts/server/empire_ai/weasel/race/Ancient.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Colonization; import empire_ai.weasel.Construction; import empire_ai.weasel.Resources; import empire_ai.weasel.Development; import empire_ai.weasel.Movement; import empire_ai.weasel.Planets; import empire_ai.weasel.Orbitals; from orbitals import getOrbitalModule, OrbitalModule; from buildings import getBuildingType, BuildingType; from resources import ResourceType, getResource, getResourceID; from statuses import getStatusID; from biomes import getBiomeID; enum PlanetClass { PC_Empty, PC_Core, PC_Mine, PC_Transmute, } class TrackReplicator { Object@ obj; Planet@ target; bool arrived = false; MoveOrder@ move; BuildingRequest@ build; uint intention = PC_Empty; bool get_busy() { if(target is null) return false; if(!arrived || move !is null || build !is null) return true; return false; } void save(Ancient& ancient, SaveFile& file) { file << obj; file << target; file << arrived; ancient.movement.saveMoveOrder(file, move); ancient.planets.saveBuildingRequest(file, build); file << intention; } void load(Ancient& ancient, SaveFile& file) { file >> obj; file >> target; file >> arrived; @move = ancient.movement.loadMoveOrder(file); @build = ancient.planets.loadBuildingRequest(file); file >> intention; } }; class Ancient : Race, RaceResources, RaceColonization { Colonization@ colonization; Construction@ construction; Resources@ resources; Planets@ planets; Development@ development; Movement@ movement; Orbitals@ orbitals; array<TrackReplicator@> replicators; const OrbitalModule@ replicatorMod; const BuildingType@ core; const BuildingType@ miner; const BuildingType@ transmuter; const BuildingType@ foundry; const BuildingType@ depot; const BuildingType@ refinery; const BuildingType@ reinforcer; const BuildingType@ developer; const BuildingType@ compressor; int claimStatus = -1; int replicatorStatus = -1; int mountainsBiome = -1; int oreResource = -1; int baseMatResource = -1; bool foundFirstT2 = false; void create() { @colonization = cast<Colonization>(ai.colonization); colonization.performColonization = false; @resources = cast<Resources>(ai.resources); @construction = cast<Construction>(ai.construction); @movement = cast<Movement>(ai.movement); @planets = cast<Planets>(ai.planets); @orbitals = cast<Orbitals>(ai.orbitals); @planets = cast<Planets>(ai.planets); @development = cast<Development>(ai.development); development.managePlanetPressure = false; development.buildBuildings = false; development.colonizeResources = false; @replicatorMod = getOrbitalModule("AncientReplicator"); @transmuter = getBuildingType("AncientTransmuter"); @miner = getBuildingType("AncientMiner"); @core = getBuildingType("AncientCore"); @foundry = getBuildingType("AncientFoundry"); @depot = getBuildingType("AncientDepot"); @refinery = getBuildingType("AncientRefinery"); @reinforcer = getBuildingType("AncientReinforcer"); @developer = getBuildingType("AncientDeveloper"); @compressor = getBuildingType("Compressor"); claimStatus = getStatusID("AncientClaim"); replicatorStatus = getStatusID("AncientReplicator"); mountainsBiome = getBiomeID("Mountains"); oreResource = getResourceID("OreRate"); baseMatResource = getResourceID("BaseMaterial"); @ai.defs.Factory = null; @ai.defs.LaborStorage = null; } void save(SaveFile& file) override { file << foundFirstT2; uint cnt = replicators.length; file << cnt; for(uint i = 0; i < cnt; ++i) replicators[i].save(this, file); } void load(SaveFile& file) override { file >> foundFirstT2; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { TrackReplicator t; t.load(this, file); if(t.obj !is null) replicators.insertLast(t); } } void levelRequirements(Object& obj, int targetLevel, array<ResourceSpec@>& specs) { //YOLO specs.length = 0; } bool orderColonization(ColonizeData& data, Planet@ sourcePlanet) { return true; } double getGenericUsefulness(const ResourceType@ type) { return 1.0; } bool hasReplicator(Planet& pl) { for(uint i = 0, cnt = replicators.length; i < cnt; ++i) { if(replicators[i].target is pl) return true; } return false; } bool isTracking(Object& obj) { for(uint i = 0, cnt = replicators.length; i < cnt; ++i) { if(replicators[i].obj is obj) return true; } return false; } void trackReplicator(Object& obj) { TrackReplicator t; @t.obj = obj; replicators.insertLast(t); } void updateRequests(Planet& pl) { //Handle requests for base materials uint baseMatReqs = 0; baseMatReqs += pl.getBuildingCount(depot.id); baseMatReqs += pl.getBuildingCount(refinery.id); baseMatReqs += pl.getBuildingCount(reinforcer.id); baseMatReqs += pl.getBuildingCount(developer.id); baseMatReqs += pl.getBuildingCount(compressor.id); array<ImportData@> curBaseMat; resources.getImportsOf(curBaseMat, baseMatResource, pl); if(curBaseMat.length < baseMatReqs) { for(uint i = curBaseMat.length, cnt = baseMatReqs; i < cnt; ++i) { ResourceSpec spec; spec.type = RST_Specific; @spec.resource = getResource(baseMatResource); resources.requestResource(pl, spec); } } else if(curBaseMat.length > baseMatReqs) { for(uint i = baseMatReqs, cnt = curBaseMat.length; i < cnt; ++i) resources.cancelRequest(curBaseMat[i]); } //Handle requests for ore uint oreReqs = 0; oreReqs += pl.getBuildingCount(foundry.id); array<ImportData@> curOre; resources.getImportsOf(curOre, oreResource, pl); if(curOre.length < oreReqs) { for(uint i = curOre.length, cnt = oreReqs; i < cnt; ++i) { ResourceSpec spec; spec.type = RST_Specific; @spec.resource = getResource(oreResource); resources.requestResource(pl, spec); } } else if(curOre.length > oreReqs) { for(uint i = oreReqs, cnt = curOre.length; i < cnt; ++i) resources.cancelRequest(curOre[i]); } } uint plInd = 0; void focusTick(double time) { if(ai.behavior.forbidColonization) return; //Find new replicators for(uint i = 0, cnt = orbitals.orbitals.length; i < cnt; ++i) { auto@ orb = cast<Orbital>(orbitals.orbitals[i].obj); if(orb.coreModule == replicatorMod.id) { if(!isTracking(orb)) trackReplicator(orb); } } //Update requests for planets if(planets.planets.length != 0) { for(uint n = 0, ncnt = min(planets.planets.length, 10); n < ncnt; ++n) { plInd = (plInd+1) % planets.planets.length; Planet@ pl = planets.planets[plInd].obj; if(classify(pl) == PC_Core) updateRequests(pl); } } //Manage existing replicators for(uint i = 0, cnt = replicators.length; i < cnt; ++i) { auto@ t = replicators[i]; if(t.obj is null || !t.obj.valid || t.obj.owner !is ai.empire) { replicators.removeAt(i); --i; --cnt; continue; } if(t.target !is null) { if(!t.target.valid) { @t.target = null; if(!t.arrived) t.obj.stopMoving(); t.arrived = false; } else if(t.target.owner !is ai.empire && t.target.owner.valid) { @t.target = null; if(!t.arrived) t.obj.stopMoving(); t.arrived = false; } } if(t.move !is null) { if(t.move.failed) { @t.move = null; t.arrived = false; } else if(t.move.completed) { if(t.obj.isOrbitingAround(t.target)) { @t.move = null; t.arrived = true; } else if(t.obj.inOrbit) { @t.move = null; t.arrived = false; @t.target = null; } } } else if(t.target !is null && !t.arrived) { @t.move = movement.move(t.obj, t.target); } if(t.build !is null) { if(t.build.canceled) { //A build failed, give up on this planet if(log) ai.print("Failed building build", t.target); @t.target = null; @t.build = null; t.arrived = false; } else if(t.build.built) { float progress = t.build.getProgress(); if(progress >= 1.f) { if(log) ai.print("Completed building build", t.target); @t.build = null; } else if(progress < -0.5f) { if(log) ai.print("Failed building build location "+t.build.builtAt, t.target); @t.build = null; @t.target = null; t.arrived = false; } } } if(t.arrived || t.target is null) { if(!t.busy) useReplicator(t); } } } uint classify(Planet& pl) { int resType = pl.primaryResourceType; if(resType == oreResource) return PC_Mine; if(resType == baseMatResource) return PC_Transmute; uint claims = pl.getStatusStackCountAny(claimStatus); if(claims <= 1) return PC_Empty; if(pl.getBuildingCount(core.id) >= 1) return PC_Core; if(pl.getBuildingCount(transmuter.id) >= 1) return PC_Transmute; if(pl.getBuildingCount(miner.id) >= 1) return PC_Mine; return PC_Empty; } bool shouldBeCore(const ResourceType@ type) { if(type.level >= 1) return true; if(type.totalPressure >= 8) return true; return false; } int openOreRequests(TrackReplicator@ discount = null) { int reqs = 0; for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) { auto@ req = resources.requested[i]; if(req.beingMet) continue; if(req.spec.type != RST_Specific) continue; if(req.spec.resource.id != uint(oreResource)) continue; reqs += 1; } for(uint i = 0, cnt = replicators.length; i < cnt; ++i) { auto@ t = replicators[i]; if(t is discount) continue; if(t.target is null) continue; if(t.intention == PC_Mine && (t.build is null || t.build.type is miner)) reqs -= 1; } return reqs; } int openBaseMatRequests(TrackReplicator@ discount = null) { int reqs = 0; for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) { auto@ req = resources.requested[i]; if(req.beingMet) continue; if(req.spec.type != RST_Specific) continue; if(req.spec.resource.id != uint(baseMatResource)) continue; reqs += 1; } for(uint i = 0, cnt = replicators.length; i < cnt; ++i) { auto@ t = replicators[i]; if(t is discount) continue; if(t.target is null) continue; if(t.intention == PC_Transmute && (t.build is null || t.build.type is transmuter)) reqs -= 1; } return reqs; } void build(TrackReplicator& t, const BuildingType@ building) { auto@ plAI = planets.getAI(t.target); if(plAI is null) return; if(!t.target.hasStatusEffect(replicatorStatus)) return; //bool scatter = building is miner || building is transmuter; bool scatter = false; @t.build = planets.requestBuilding(plAI, building, scatter=scatter, moneyType=BT_Colonization); if(log) ai.print("Build "+building.name, t.target); } void useReplicator(TrackReplicator& t) { if(t.target !is null) { uint type = classify(t.target); switch(type) { case PC_Empty: { const ResourceType@ res = getResource(t.target.primaryResourceType); if(res is null) { @t.target = null; t.arrived = false; return; } if(shouldBeCore(res)) { build(t, core); } else if(openBaseMatRequests(t) >= openOreRequests(t) || gameTime < 6.0 * 60.0 || !t.target.hasBiome(mountainsBiome)) { build(t, transmuter); } else { build(t, miner); } return; } case PC_Transmute: @t.target = null; t.arrived = false; break; case PC_Mine: @t.target = null; t.arrived = false; break; case PC_Core: build(t, refinery); return; } } //Find a new planet to colonize PotentialColonize@ best; double bestWeight = 0.0; uint getType = PC_Core; if(openBaseMatRequests() >= 1) getType = PC_Transmute; else if(openOreRequests() >= 1 && gameTime > 6.0 * 60.0) getType = PC_Mine; auto@ potentials = colonization.getPotentialColonize(); for(uint i = 0, cnt = potentials.length; i < cnt; ++i) { PotentialColonize@ p = potentials[i]; if(hasReplicator(p.pl)) continue; double w = p.weight; if(!foundFirstT2 && p.resource.level >= 2) w *= 100.0; else if((getType == PC_Core) != shouldBeCore(p.resource)) w *= 0.6; if(getType == PC_Core && p.resource.level >= 2) w *= 4.0; if(getType == PC_Core && p.resource.level >= 3) w *= 6.0; if(getType == PC_Mine && !p.pl.hasBiome(mountainsBiome)) w *= 0.1; if(getType == PC_Core) w *= double(p.pl.totalSurfaceTiles) / 100.0; w /= p.pl.position.distanceTo(t.obj.position)/1000.0; if(w > bestWeight) { bestWeight = w; @best = p; } } if(best !is null) { @t.target = best.pl; t.intention = shouldBeCore(best.resource) ? uint(PC_Core) : getType; t.arrived = false; if(!foundFirstT2) { if(best.resource.level == 2) foundFirstT2 = true; } } } }; AIComponent@ createAncient() { return Ancient(); } |
Added scripts/server/empire_ai/weasel/race/Devout.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Development; import empire_ai.weasel.Planets; import empire_ai.weasel.Budget; import resources; import buildings; import attributes; class Devout : Race, RaceDevelopment { Development@ development; Planets@ planets; Budget@ budget; const ResourceType@ altarResource; const BuildingType@ altar; int coverAttrib = -1; BuildingRequest@ altarBuild; Planet@ focusAltar; double considerTimer = 0.0; void save(SaveFile& file) { planets.saveBuildingRequest(file, altarBuild); file << focusAltar; file << considerTimer; } void load(SaveFile& file) { @altarBuild = planets.loadBuildingRequest(file); file >> focusAltar; file >> considerTimer; } void create() { @planets = cast<Planets>(ai.planets); @development = cast<Development>(ai.development); @budget = cast<Budget>(ai.budget); @altarResource = getResource("Altar"); @altar = getBuildingType("Altar"); coverAttrib = getEmpAttribute("AltarSupportedPopulation"); } void start() { auto@ data = ai.empire.getPlanets(); Object@ obj; while(receive(data, obj)) { Planet@ pl = cast<Planet>(obj); if(pl !is null){ if(pl.primaryResourceType == altarResource.id) { @focusAltar = pl; break; } } } } bool shouldBeFocus(Planet& pl, const ResourceType@ resource) override { if(resource is altarResource) return true; return false; } void focusTick(double time) override { if(ai.behavior.forbidConstruction) return; //Handle our current altar build if(altarBuild !is null) { if(altarBuild.built) { @focusAltar = altarBuild.plAI.obj; @altarBuild = null; } else if(altarBuild.canceled) { @altarBuild = null; } } //Handle our focused altar if(focusAltar !is null) { if(!focusAltar.valid || focusAltar.owner !is ai.empire || focusAltar.primaryResourceType != altarResource.id) { @focusAltar = null; } } //If we aren't covering our entire population, find new planets to make into altars double coverage = ai.empire.getAttribute(coverAttrib); double population = ai.empire.TotalPopulation; if(coverage >= population || altarBuild !is null) return; bool makeNewAltar = true; if(focusAltar !is null) { auto@ foc = development.getFocus(focusAltar); if(foc !is null && int(foc.obj.level) >= foc.targetLevel) { foc.targetLevel += 1; considerTimer = gameTime + 180.0; makeNewAltar = false; } else { makeNewAltar = gameTime > considerTimer; } } if(makeNewAltar) { if(budget.canSpend(BT_Development, 300)) { //Turn our most suitable planet into an altar PlanetAI@ bestBuild; double bestWeight = 0.0; for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) { auto@ plAI = planets.planets[i]; double w = randomd(0.9, 1.1); if(plAI.resources !is null && plAI.resources.length != 0) { auto@ res = plAI.resources[0].resource; if(res.level == 0 && !res.limitlessLevel) w *= 5.0; if(res.cls !is null) w *= 0.5; if(res.level > 0) w /= pow(2.0, res.level); } else { w *= 100.0; } if(w > bestWeight) { bestWeight = w; @bestBuild = plAI; } } if(bestBuild !is null) { @altarBuild = planets.requestBuilding(bestBuild, altar, expire=60.0); considerTimer = gameTime + 120.0; } } } } }; AIComponent@ createDevout() { return Devout(); } |
Added scripts/server/empire_ai/weasel/race/Extragalactic.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Colonization; import empire_ai.weasel.Construction; import empire_ai.weasel.Resources; import empire_ai.weasel.Scouting; import empire_ai.weasel.Orbitals; import empire_ai.weasel.Budget; from orbitals import getOrbitalModuleID; from constructions import ConstructionType, getConstructionType; class Extragalactic : Race { Colonization@ colonization; Construction@ construction; Scouting@ scouting; Orbitals@ orbitals; Resources@ resources; Budget@ budget; array<OrbitalAI@> beacons; OrbitalAI@ masterBeacon; int beaconMod = -1; array<ImportData@> imports; array<const ConstructionType@> beaconBuilds; void create() { @colonization = cast<Colonization>(ai.colonization); colonization.performColonization = false; colonization.queueColonization = false; @scouting = cast<Scouting>(ai.scouting); scouting.buildScouts = false; @orbitals = cast<Orbitals>(ai.orbitals); beaconMod = getOrbitalModuleID("Beacon"); @construction = cast<Construction>(ai.construction); @resources = cast<Resources>(ai.resources); @budget = cast<Budget>(ai.budget); beaconBuilds.insertLast(getConstructionType("BeaconHealth")); beaconBuilds.insertLast(getConstructionType("BeaconWeapons")); beaconBuilds.insertLast(getConstructionType("BeaconLabor")); } void save(SaveFile& file) override { uint cnt = beacons.length; file << cnt; for(uint i = 0; i < cnt; ++i) orbitals.saveAI(file, beacons[i]); orbitals.saveAI(file, masterBeacon); cnt = imports.length; file << cnt; for(uint i = 0; i < cnt; ++i) resources.saveImport(file, imports[i]); } void load(SaveFile& file) override { uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ b = orbitals.loadAI(file); if(b !is null && b.obj !is null) beacons.insertLast(b); } @masterBeacon = orbitals.loadAI(file); file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ imp = resources.loadImport(file); if(imp !is null) imports.insertLast(imp); } } uint prevBeacons = 0; void focusTick(double time) { if(ai.behavior.forbidConstruction) return; //Find our beacons for(uint i = 0, cnt = beacons.length; i < cnt; ++i) { auto@ b = beacons[i]; if(b is null || b.obj is null || !b.obj.valid || b.obj.owner !is ai.empire) { if(b.obj !is null) resources.killImportsTo(b.obj); beacons.removeAt(i); --i; --cnt; } } for(uint i = 0, cnt = orbitals.orbitals.length; i < cnt; ++i) { auto@ orb = orbitals.orbitals[i]; Orbital@ obj = cast<Orbital>(orb.obj); if(obj !is null && obj.coreModule == uint(beaconMod)) { if(beacons.find(orb) == -1) beacons.insertLast(orb); } } //Find our master beacon if(masterBeacon !is null) { Orbital@ obj = cast<Orbital>(masterBeacon.obj); if(obj is null || !obj.valid || obj.owner !is ai.empire || obj.hasMaster()) @masterBeacon = null; } else { for(uint i = 0, cnt = beacons.length; i < cnt; ++i) { auto@ b = beacons[i]; Orbital@ obj = cast<Orbital>(b.obj); if(!obj.hasMaster()) { @masterBeacon = b; ai.empire.setDefending(obj, true); break; } } } scouting.buildScouts = gameTime > 5.0 * 60.0; if(prevBeacons < beacons.length && masterBeacon !is null && gameTime > 10.0) { for(int i = beacons.length-1; i >= int(prevBeacons); --i) { //Make sure we order a scout at each beacon if(!scouting.buildScouts) { BuildFlagshipSourced build(scouting.scoutDesign); build.moneyType = BT_Military; @build.buildAt = masterBeacon.obj; if(beacons[i] !is masterBeacon) @build.buildFrom = beacons[i].obj; construction.build(build, force=true); } //Set the beacon to fill up other stuff beacons[i].obj.allowFillFrom = true; } prevBeacons = beacons.length; } //Handle with importing labor and defense to our master beacon if(masterBeacon !is null) { if(imports.length == 0) { //Request labor and defense at our beacon { ResourceSpec spec; spec.type = RST_Pressure_Type; spec.pressureType = TR_Labor; imports.insertLast(resources.requestResource(masterBeacon.obj, spec)); } { ResourceSpec spec; spec.type = RST_Pressure_Type; spec.pressureType = TR_Defense; imports.insertLast(resources.requestResource(masterBeacon.obj, spec)); } { ResourceSpec spec; spec.type = RST_Pressure_Level0; spec.pressureType = TR_Research; imports.insertLast(resources.requestResource(masterBeacon.obj, spec)); } } else { //When our requests are met, make more requests! for(uint i = 0, cnt = imports.length; i < cnt; ++i) { if(imports[i].beingMet || imports[i].obj !is masterBeacon.obj) { ResourceSpec spec; spec = imports[i].spec; @imports[i] = resources.requestResource(masterBeacon.obj, spec); } } } //Build stuff on our beacon if we have enough stuff if(budget.canSpend(BT_Development, 300)) { uint offset = randomi(0, beaconBuilds.length-1); for(uint i = 0, cnt = beaconBuilds.length; i < cnt; ++i) { uint ind = (i+offset) % cnt; auto@ type = beaconBuilds[ind]; if(type is null) continue; if(type.canBuild(masterBeacon.obj, ignoreCost=false)) { masterBeacon.obj.buildConstruction(type.id); break; } } } } } }; AIComponent@ createExtragalactic() { return Extragalactic(); } |
Added scripts/server/empire_ai/weasel/race/Linked.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Movement; import empire_ai.weasel.Military; import empire_ai.weasel.Construction; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Systems; import empire_ai.weasel.Budget; from orbitals import getOrbitalModuleID; const double MAINFRAME_MIN_DISTANCE_STAGE = 15000; const double MAINFRAME_MIN_DISTANCE_DEVELOP = 20000; const double MAINFRAME_MIN_TIMER = 3.0 * 60.0; const int MAINFRAME_BUILD_MOVE_HOPS = 5; class LinkRegion : Savable { Region@ region; Object@ obj; bool arrived = false; vec3d destination; void save(SaveFile& file) { file << region; file << obj; file << arrived; file << destination; } void load(SaveFile& file) { file >> region; file >> obj; file >> arrived; file >> destination; } }; class Linked : Race { Military@ military; Designs@ designs; Construction@ construction; Development@ development; Systems@ systems; Budget@ budget; array<LinkRegion@> tracked; array<Object@> unassigned; BuildOrbital@ buildMainframe; int mainframeId = -1; double nextBuildTry = 15.0 * 60.0; void create() override { @military = cast<Military>(ai.military); @designs = cast<Designs>(ai.designs); @construction = cast<Construction>(ai.construction); @development = cast<Development>(ai.development); @systems = cast<Systems>(ai.systems); @budget = cast<Budget>(ai.budget); mainframeId = getOrbitalModuleID("Mainframe"); } void save(SaveFile& file) override { construction.saveConstruction(file, buildMainframe); file << nextBuildTry; uint cnt = tracked.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << tracked[i]; cnt = unassigned.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << unassigned[i]; } void load(SaveFile& file) override { @buildMainframe = cast<BuildOrbital>(construction.loadConstruction(file)); file >> nextBuildTry; uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { LinkRegion gt; file >> gt; tracked.insertLast(gt); } file >> cnt; for(uint i = 0; i < cnt; ++i) { Object@ obj; file >> obj; if(obj !is null) unassigned.insertLast(obj); } } LinkRegion@ get(Region@ reg) { for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].region is reg) return tracked[i]; } return null; } void remove(LinkRegion@ gt) { if(gt.obj !is null && gt.obj.valid && gt.obj.owner is ai.empire) unassigned.insertLast(gt.obj); tracked.remove(gt); } Object@ getClosestMainframe(const vec3d& position) { Object@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { Object@ obj = tracked[i].obj; if(obj is null) continue; if(!tracked[i].arrived) continue; double d = obj.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = obj; } } return closest; } LinkRegion@ getClosestLinkRegion(const vec3d& position) { LinkRegion@ closest; double minDist = INFINITY; for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { double d = tracked[i].region.position.distanceTo(position); if(d < minDist) { minDist = d; @closest = tracked[i]; } } return closest; } void assignTo(LinkRegion@ gt, Object@ closest) { unassigned.remove(closest); @gt.obj = closest; gt.arrived = false; if(closest.region is gt.region) gt.arrived = true; if(!gt.arrived) { gt.destination = military.getStationPosition(gt.region); closest.addMoveOrder(gt.destination); } } bool trackingMainframe(Object@ obj) { for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { if(unassigned[i] is obj) return true; } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { if(tracked[i].obj is obj) return true; } return false; } bool shouldHaveMainframe(Region@ reg, bool always = false) { if(military.getBase(reg) !is null) return true; if(development.isDevelopingIn(reg)) return true; return false; } void focusTick(double time) override { if(ai.behavior.forbidConstruction) return; //Manage unassigned mainframes list for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) { Object@ obj = unassigned[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { unassigned.removeAt(i); --i; --cnt; } } //Detect new gates auto@ data = ai.empire.getOrbitals(); Object@ obj; while(receive(data, obj)) { if(obj is null) continue; Orbital@ orb = cast<Orbital>(obj); if(orb is null || orb.coreModule != uint(mainframeId)) continue; if(!trackingMainframe(obj)) unassigned.insertLast(obj); } //Update existing gates for staging bases for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; bool checkAlways = false; if(gt.obj !is null) { if(!gt.obj.valid || gt.obj.owner !is ai.empire || (gt.arrived && gt.obj.region !is gt.region)) { @gt.obj = null; gt.arrived = false; checkAlways = true; } else if(!gt.arrived && !gt.obj.hasOrders) { if(gt.destination.distanceTo(gt.obj.position) < 10.0) gt.arrived = true; else gt.obj.addMoveOrder(gt.destination); } } if(!shouldHaveMainframe(gt.region, checkAlways)) { remove(tracked[i]); --i; --cnt; } } //Detect new staging bases to build mainframes at for(uint i = 0, cnt = military.stagingBases.length; i < cnt; ++i) { auto@ base = military.stagingBases[i]; if(base.occupiedTime < MAINFRAME_MIN_TIMER) continue; if(get(base.region) is null) { LinkRegion@ closest = getClosestLinkRegion(base.region.position); if(closest !is null && closest.region.position.distanceTo(base.region.position) < MAINFRAME_MIN_DISTANCE_STAGE) continue; LinkRegion gt; @gt.region = base.region; tracked.insertLast(gt); break; } } //Detect new important planets to build mainframes at for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) { auto@ focus = development.focuses[i]; Region@ reg = focus.obj.region; if(reg is null) continue; if(get(reg) is null) { LinkRegion@ closest = getClosestLinkRegion(reg.position); if(closest !is null && closest.region.position.distanceTo(reg.position) < MAINFRAME_MIN_DISTANCE_DEVELOP) continue; LinkRegion gt; @gt.region = reg; tracked.insertLast(gt); break; } } //See if we should build a new mainframe if(buildMainframe !is null) { if(buildMainframe.completed) { @buildMainframe = null; nextBuildTry = gameTime + 60.0; } } for(uint i = 0, cnt = tracked.length; i < cnt; ++i) { auto@ gt = tracked[i]; if(gt.obj is null) { Object@ closest; double closestDist = INFINITY; for(uint n = 0, ncnt = unassigned.length; n < ncnt; ++n) { Object@ obj = unassigned[n]; if(obj.region is gt.region) { @closest = obj; break; } if(!obj.hasMover) continue; if(buildMainframe is null && gameTime > nextBuildTry) { double d = obj.position.distanceTo(gt.region.position); if(d < closestDist) { closestDist = d; @closest = obj; } } } if(closest !is null) { if(log) ai.print("Assign mainframe to => "+gt.region.name, closest.region); assignTo(gt, closest); } else if(buildMainframe is null && gameTime > nextBuildTry) { if(log) ai.print("Build mainframe for this system", gt.region); bool buildLocal = true; auto@ factory = construction.primaryFactory; if(factory !is null) { Region@ factRegion = factory.obj.region; if(factRegion !is null && systems.hopDistance(gt.region, factRegion) < MAINFRAME_BUILD_MOVE_HOPS) buildLocal = false; } if(buildLocal) @buildMainframe = construction.buildLocalOrbital(getOrbitalModule(mainframeId)); else @buildMainframe = construction.buildOrbital(getOrbitalModule(mainframeId), military.getStationPosition(gt.region)); } } } } }; AIComponent@ createLinked() { return Linked(); } |
Added scripts/server/empire_ai/weasel/race/Mechanoid.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Resources; import empire_ai.weasel.Colonization; import empire_ai.weasel.Construction; import empire_ai.weasel.Movement; import empire_ai.weasel.Planets; import empire_ai.weasel.Budget; import resources; import abilities; import planet_levels; from constructions import getConstructionType, ConstructionType; from abilities import getAbilityID; import oddity_navigation; const double MAX_POP_BUILDTIME = 3.0 * 60.0; class Mechanoid : Race, RaceResources, RaceColonization { Colonization@ colonization; Construction@ construction; Movement@ movement; Budget@ budget; Planets@ planets; const ResourceType@ unobtanium; const ResourceType@ crystals; int unobtaniumAbl = -1; const ResourceClass@ foodClass; const ResourceClass@ waterClass; const ResourceClass@ scalableClass; const ConstructionType@ buildPop; int colonizeAbl = -1; array<Planet@> popRequests; array<Planet@> popSources; array<Planet@> popFactories; void create() { @colonization = cast<Colonization>(ai.colonization); @construction = cast<Construction>(ai.construction); @movement = cast<Movement>(ai.movement); @planets = cast<Planets>(ai.planets); @budget = cast<Budget>(ai.budget); @ai.defs.Shipyard = null; @crystals = getResource("FTL"); @unobtanium = getResource("Unobtanium"); unobtaniumAbl = getAbilityID("UnobtaniumMorph"); @foodClass = getResourceClass("Food"); @waterClass = getResourceClass("WaterType"); @scalableClass = getResourceClass("Scalable"); colonizeAbl = getAbilityID("MechanoidColonize"); colonization.performColonization = false; @buildPop = getConstructionType("MechanoidPopulation"); } void start() { //Oh yes please can we have some ftl crystals sir if(crystals !is null) { ResourceSpec spec; spec.type = RST_Specific; @spec.resource = crystals; spec.isLevelRequirement = false; spec.isForImport = false; colonization.queueColonize(spec); } } void levelRequirements(Object& obj, int targetLevel, array<ResourceSpec@>& specs) override { //Remove all food and water resources if(obj.levelChain != baseLevelChain.id) return; for(int i = specs.length-1; i >= 0; --i) { auto@ spec = specs[i]; if(spec.type == RST_Class && (spec.cls is foodClass || spec.cls is waterClass)) specs.removeAt(i); } } double transferCost(double dist) { return 20 + dist * 0.002; } bool orderColonization(ColonizeData& data, Planet@ sourcePlanet) { return false; } double getGenericUsefulness(const ResourceType@ type) override { if(type.cls is foodClass || type.cls is waterClass) return 0.00001; if(type.level == 1) return 100.0; return 1.0; } bool canBuildPopulation(Planet& pl, double factor=1.0) { if(buildPop is null) return false; if(!buildPop.canBuild(pl, ignoreCost=true)) return false; auto@ primFact = construction.primaryFactory; if(primFact !is null && pl is primFact.obj) return true; double laborCost = buildPop.getLaborCost(pl); double laborIncome = pl.laborIncome; return laborCost < laborIncome * MAX_POP_BUILDTIME * factor; } bool requiresPopulation(Planet& pl, double mod = 0.0) { double curPop = pl.population + mod; double maxPop = pl.maxPopulation; return curPop < maxPop; } bool canSendPopulation(Planet& pl, double mod = 0.0) { double curPop = pl.population + mod; double maxPop = pl.maxPopulation; if(curPop >= maxPop + 1) return true; //auto@ primFact = construction.primaryFactory; //if(primFact !is null && pl is primFact.obj) { // uint minFacts = 2; // if(popFactories.find(pl) == -1) // minFacts -= 1; // if(popFactories.length >= minFacts) // return false; //} //if(canBuildPopulation(pl)) { // if(curPop >= maxPop) // return true; //} return false; } uint chkInd = 0; array<Planet@> availSources; void focusTick(double time) override { if(ai.behavior.forbidColonization) return; //Check existing lists for(uint i = 0, cnt = popFactories.length; i < cnt; ++i) { auto@ obj = popFactories[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { popFactories.removeAt(i); --i; --cnt; continue; } if(!canBuildPopulation(popFactories[i])) { popFactories.removeAt(i); --i; --cnt; continue; } } for(uint i = 0, cnt = popSources.length; i < cnt; ++i) { auto@ obj = popSources[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { popSources.removeAt(i); --i; --cnt; continue; } if(!canSendPopulation(popSources[i])) { popSources.removeAt(i); --i; --cnt; continue; } } for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) { auto@ obj = popRequests[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { popRequests.removeAt(i); --i; --cnt; continue; } if(!requiresPopulation(popRequests[i])) { popRequests.removeAt(i); --i; --cnt; continue; } } //Find new planets to add to our lists bool checkMorph = false; Planet@ hw = ai.empire.Homeworld; if(hw !is null && hw.valid && hw.owner is ai.empire && unobtanium !is null) { if(hw.primaryResourceType == unobtanium.id) checkMorph = true; } uint plCnt = planets.planets.length; for(uint n = 0, cnt = min(15, plCnt); n < cnt; ++n) { chkInd = (chkInd+1) % plCnt; auto@ plAI = planets.planets[chkInd]; //Find planets that can build population reliably if(canBuildPopulation(plAI.obj)) { if(popFactories.find(plAI.obj) == -1) popFactories.insertLast(plAI.obj); } //Find planets that need population if(requiresPopulation(plAI.obj)) { if(popRequests.find(plAI.obj) == -1) popRequests.insertLast(plAI.obj); } //Find planets that have extra population if(canSendPopulation(plAI.obj)) { if(popSources.find(plAI.obj) == -1) popSources.insertLast(plAI.obj); } if(plAI.resources !is null && plAI.resources.length != 0) { auto@ res = plAI.resources[0]; //Get rid of food and water we don't need if(res.resource.cls is foodClass || res.resource.cls is waterClass) { if(res.request is null && !ai.behavior.forbidScuttle) { Region@ reg = res.obj.region; if(reg !is null && reg.getPlanetCount(ai.empire) >= 2) { plAI.obj.abandon(); } } } //See if we have anything useful to morph our homeworld too if(checkMorph) { bool morph = false; if(res.resource is crystals) morph = true; else if(res.resource.level >= 2 && res.resource.tilePressure[TR_Labor] >= 5) morph = true; else if(res.resource.level >= 3 && res.resource.totalPressure > 10) morph = true; else if(res.resource.cls is scalableClass && gameTime > 30.0 * 60.0) morph = true; else if(res.resource.level >= 2 && res.resource.totalPressure >= 5 && gameTime > 60.0 * 60.0) morph = true; if(morph) { if(log) ai.print("Morph homeworld to "+res.resource.name+" from "+res.obj.name, hw); hw.activateAbilityTypeFor(ai.empire, unobtaniumAbl, plAI.obj); } } } } //See if we can find something to send population to availSources = popSources; for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) { Planet@ dest = popRequests[i]; if(canBuildPopulation(dest, factor=(availSources.length == 0 ? 2.5 : 1.5))) { Factory@ f = construction.get(dest); if(f !is null) { if(f.active is null) { auto@ build = construction.buildConstruction(buildPop); construction.buildNow(build, f); if(log) ai.print("Build population", f.obj); continue; } else { auto@ cons = cast<BuildConstruction>(f.active); if(cons !is null && cons.consType is buildPop) { if(double(dest.maxPopulation) <= dest.population + 0.0) continue; } } } } transferBest(dest, availSources); } if(availSources.length != 0) { //If we have any population left, do stuff from our colonization queue for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt && availSources.length != 0; ++i) { Planet@ dest = colonization.awaitingSource[i].target; Planet@ source = transferBest(dest, availSources); if(source !is null) { @colonization.awaitingSource[i].colonizeFrom = source; colonization.awaitingSource.removeAt(i); --i; --cnt; } } } //Build population on idle planets if(budget.canSpend(BT_Development, 100)) { for(int i = popFactories.length-1; i >= 0; --i) { Planet@ dest = popFactories[i]; Factory@ f = construction.get(dest); if(f is null || f.active !is null) continue; if(dest.population >= double(dest.maxPopulation) + 1.0) continue; auto@ build = construction.buildConstruction(buildPop); construction.buildNow(build, f); if(log) ai.print("Build population for idle", f.obj); break; } } } Planet@ transferBest(Planet& dest, array<Planet@>& availSources) { //Find closest source Planet@ bestSource; double bestDist = INFINITY; for(uint j = 0, jcnt = availSources.length; j < jcnt; ++j) { double d = movement.getPathDistance(availSources[j].position, dest.position); if(d < bestDist) { bestDist = d; @bestSource = availSources[j]; } } if(bestSource !is null) { double cost = transferCost(bestDist); if(cost <= ai.empire.FTLStored) { if(log) ai.print("Transfering population to "+dest.name, bestSource); availSources.remove(bestSource); bestSource.activateAbilityTypeFor(ai.empire, colonizeAbl, dest); return bestSource; } } return null; } void tick(double time) override { } }; AIComponent@ createMechanoid() { return Mechanoid(); } |
Added scripts/server/empire_ai/weasel/race/Race.as.
> > > > |
1 2 3 4 |
import empire_ai.weasel.WeaselAI; class Race : AIComponent { }; |
Added scripts/server/empire_ai/weasel/race/StarChildren.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Colonization; import empire_ai.weasel.Resources; import empire_ai.weasel.Construction; import empire_ai.weasel.Development; import empire_ai.weasel.Fleets; import empire_ai.weasel.Movement; import empire_ai.weasel.Planets; import empire_ai.weasel.Designs; import oddity_navigation; from abilities import getAbilityID; from statuses import getStatusID; class HabitatMission : Mission { Planet@ target; MoveOrder@ move; double timer = 0.0; void save(Fleets& fleets, SaveFile& file) override { file << target; file << timer; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) override { file >> target; file >> timer; @move = fleets.movement.loadMoveOrder(file); } void start(AI& ai, FleetAI& fleet) override { uint prior = MP_Normal; if(gameTime < 30.0 * 60.0) prior = MP_Critical; @move = cast<Movement>(ai.movement).move(fleet.obj, target, prior); } void tick(AI& ai, FleetAI& fleet, double time) override { if(move !is null) { if(move.failed) { canceled = true; return; } if(move.completed) { int ablId = cast<StarChildren>(ai.race).habitatAbl; fleet.obj.activateAbilityTypeFor(ai.empire, ablId, target); @move = null; timer = gameTime + 60.0; } } else { if(target is null || !target.valid || target.quarantined || (target.owner !is ai.empire && target.owner.valid) || target.inCombat) { canceled = true; return; } double maxPop = max(double(target.maxPopulation), double(getPlanetLevel(target, target.primaryResourceLevel).population)); double curPop = target.population; if(curPop >= maxPop) { completed = true; return; } if(gameTime >= timer) { int popStatus = cast<StarChildren>(ai.race).popStatus; if(target.getStatusStackCountAny(popStatus) >= 5) { canceled = true; return; } } } } }; class LaborMission : Mission { Planet@ target; MoveOrder@ move; double timer = 0.0; void save(Fleets& fleets, SaveFile& file) override { file << target; file << timer; fleets.movement.saveMoveOrder(file, move); } void load(Fleets& fleets, SaveFile& file) override { file >> target; file >> timer; @move = fleets.movement.loadMoveOrder(file); } void start(AI& ai, FleetAI& fleet) override { @move = cast<Movement>(ai.movement).move(fleet.obj, target); } void tick(AI& ai, FleetAI& fleet, double time) override { if(move !is null) { if(move.failed) { canceled = true; return; } if(move.completed) { @move = null; timer = gameTime + 10.0; } } else { if(target is null || !target.valid || target.quarantined || target.owner !is ai.empire) { canceled = true; return; } if(gameTime >= timer) { int popStatus = cast<StarChildren>(ai.race).popStatus; timer = gameTime + 10.0; if(target.getStatusStackCountAny(popStatus) >= 10) { completed = true; return; } } } } }; class StarChildren : Race { Colonization@ colonization; Construction@ construction; Development@ development; Movement@ movement; Planets@ planets; Fleets@ fleets; Designs@ designs; DesignTarget@ mothershipDesign; double idleSince = 0; array<FleetAI@> motherships; int habitatAbl = -1; int popStatus = -1; array<Planet@> popRequests; array<Planet@> laborPlanets; BuildFlagship@ mcBuild; BuildOrbital@ yardBuild; void save(SaveFile& file) override { designs.saveDesign(file, mothershipDesign); file << idleSince; construction.saveConstruction(file, mcBuild); construction.saveConstruction(file, yardBuild); uint cnt = motherships.length; file << cnt; for(uint i = 0; i < cnt; ++i) fleets.saveAI(file, motherships[i]); } void load(SaveFile& file) override { @mothershipDesign = designs.loadDesign(file); file >> idleSince; @mcBuild = cast<BuildFlagship>(construction.loadConstruction(file)); @yardBuild = cast<BuildOrbital>(construction.loadConstruction(file)); uint cnt = 0; file >> cnt; for(uint i = 0; i < cnt; ++i) { auto@ flAI = fleets.loadAI(file); if(flAI !is null) motherships.insertLast(flAI); } } void create() override { @colonization = cast<Colonization>(ai.colonization); colonization.performColonization = false; @development = cast<Development>(ai.development); development.managePlanetPressure = false; development.buildBuildings = false; @fleets = cast<Fleets>(ai.fleets); @construction = cast<Construction>(ai.construction); @planets = cast<Planets>(ai.planets); @designs = cast<Designs>(ai.designs); @movement = cast<Movement>(ai.movement); @ai.defs.Factory = null; @ai.defs.LaborStorage = null; habitatAbl = getAbilityID("MothershipColonize"); popStatus = getStatusID("MothershipPopulation"); } void start() override { //Get the Tier 1 in our home system { ResourceSpec spec; spec.type = RST_Level_Specific; spec.level = 1; spec.isForImport = false; spec.isLevelRequirement = false; colonization.queueColonize(spec); } //Then find a Tier 2 to get { ResourceSpec spec; spec.type = RST_Level_Specific; spec.level = 2; spec.isForImport = false; spec.isLevelRequirement = false; colonization.queueColonize(spec); } //Design a mothership @mothershipDesign = designs.design(DP_Mothership, 500); mothershipDesign.targetMaintenance = 300; mothershipDesign.targetLaborCost = 110; mothershipDesign.customName = "Mothership"; } bool requiresPopulation(Planet& target) { double maxPop = max(double(target.maxPopulation), double(getPlanetLevel(target, target.primaryResourceLevel).population)); double curPop = target.population; return curPop < maxPop; } uint chkInd = 0; void focusTick(double time) override { if(ai.behavior.forbidColonization) return; //Detect motherships for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Mothership) continue; if(motherships.find(flAI) == -1) { //Add to our tracking list flAI.obj.autoFillSupports = false; flAI.obj.allowFillFrom = true; motherships.insertLast(flAI); //Add as a factory construction.registerFactory(flAI.obj); } } for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { Object@ obj = motherships[i].obj; if(obj is null || !obj.valid || obj.owner !is ai.empire) { motherships.removeAt(i); --i; --cnt; } } //Detect planets that require more population for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) { auto@ obj = popRequests[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { popRequests.removeAt(i); --i; --cnt; continue; } if(!requiresPopulation(obj)) { popRequests.removeAt(i); --i; --cnt; continue; } } for(uint i = 0, cnt = laborPlanets.length; i < cnt; ++i) { auto@ obj = laborPlanets[i]; if(obj is null || !obj.valid || obj.owner !is ai.empire) { laborPlanets.removeAt(i); --i; --cnt; continue; } if(obj.laborIncome < 3.0/60.0) { laborPlanets.removeAt(i); --i; --cnt; continue; } } uint plCnt = planets.planets.length; for(uint n = 0, cnt = min(15, plCnt); n < cnt; ++n) { chkInd = (chkInd+1) % plCnt; auto@ plAI = planets.planets[chkInd]; //Find planets that need population if(requiresPopulation(plAI.obj)) { if(popRequests.find(plAI.obj) == -1) popRequests.insertLast(plAI.obj); } //Find planets that have labor if(plAI.obj.laborIncome >= 3.0/60.0) { if(laborPlanets.find(plAI.obj) == -1) laborPlanets.insertLast(plAI.obj); } } //Send motherships to do colonization uint totalCount = popRequests.length + colonization.awaitingSource.length; uint motherCount = idleMothershipCount(); /*if(motherCount > totalCount) {*/ for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) { Planet@ dest = popRequests[i]; if(isColonizing(dest)) continue; if(dest.inCombat) continue; colonizeBest(dest); } for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt; ++i) { Planet@ dest = colonization.awaitingSource[i].target; if(isColonizing(dest)) continue; colonizeBest(dest); } /*}*/ /*else {*/ /* for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {*/ /* auto@ flAI = motherships[i];*/ /* if(flAI.mission !is null)*/ /* continue;*/ /* if(isBuildingWithLabor(flAI))*/ /* continue;*/ /* colonizeBest(flAI);*/ /* }*/ /*}*/ if(totalCount != 0) idleSince = gameTime; //See if we should build new motherships uint haveMC = motherships.length; uint wantMC = 1; if(gameTime > 20.0 * 60.0) wantMC += 1; wantMC = max(wantMC, min(laborPlanets.length, uint(gameTime/(30.0*60.0)))); if(mcBuild !is null && mcBuild.completed) @mcBuild = null; if(wantMC > haveMC && mcBuild is null) @mcBuild = construction.buildFlagship(mothershipDesign, force=true); if(yardBuild is null && haveMC > 0 && gameTime > 60 && gameTime < 180 && ai.defs.Shipyard !is null) { Region@ reg = motherships[0].obj.region; if(reg !is null) { vec3d pos = reg.position; vec2d offset = random2d(reg.radius * 0.4, reg.radius * 0.8); pos.x += offset.x; pos.z += offset.y; @yardBuild = construction.buildOrbital(ai.defs.Shipyard, pos); } } if(motherships.length == 1) @colonization.colonizeWeightObj = motherships[0].obj; else @colonization.colonizeWeightObj = null; //Idle motherships should be sent to go collect labor from labor planets if(laborPlanets.length != 0) { for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { auto@ flAI = motherships[i]; if(flAI.mission !is null) continue; if(isAtLaborPlanet(flAI)) continue; if(i == 0 && idleSince < gameTime-60.0) continue; double bestDist = INFINITY; Planet@ best; for(uint n = 0, ncnt = laborPlanets.length; n < ncnt; ++n) { Planet@ check = laborPlanets[n]; if(hasMothershipAt(check)) continue; double d = movement.getPathDistance(flAI.obj.position, check.position); if(d < bestDist) { @best = check; bestDist = d; } } if(best !is null) { LaborMission miss; @miss.target = best; fleets.performMission(flAI, miss); } } } } bool isAtLaborPlanet(FleetAI& flAI) { auto@ miss = cast<LaborMission>(flAI); if(miss !is null) return true; for(uint i = 0, cnt = laborPlanets.length; i < cnt; ++i) { if(flAI.obj.isLockedOrbit(laborPlanets[i])) return true; } return false; } bool isBuildingWithLabor(FleetAI& flAI) { auto@ f = construction.get(flAI.obj); if(f !is null && f.active !is null) return false; if(isAtLaborPlanet(flAI)) return true; return false; } bool hasMothershipAt(Planet& pl) { for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { auto@ flAI = motherships[i]; auto@ miss = cast<LaborMission>(flAI); if(miss !is null && miss.target is pl) return true; if(flAI.obj.isLockedOrbit(pl)) return true; } return false; } uint idleMothershipCount() { uint count = 0; for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { if(motherships[i].mission is null) count += 1; } return count; } bool isColonizing(Planet& dest) { for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { auto@ flAI = motherships[i]; auto@ miss = cast<HabitatMission>(flAI.mission); if(miss !is null && miss.target is dest) return true; } return false; } Planet@ colonizeBest(FleetAI& flAI) { Planet@ best; double bestDist = INFINITY; for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) { Planet@ dest = popRequests[i]; if(isColonizing(dest)) continue; if(dest.inCombat) continue; double d = movement.getPathDistance(flAI.obj.position, dest.position); if(d < bestDist) { @best = dest; bestDist = d; } } if(best is null) { for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt; ++i) { Planet@ dest = colonization.awaitingSource[i].target; if(isColonizing(dest)) continue; double d = movement.getPathDistance(flAI.obj.position, dest.position); if(d < bestDist) { @best = dest; bestDist = d; } } } if(best !is null) { HabitatMission miss; @miss.target = best; fleets.performMission(flAI, miss); } return best; } FleetAI@ colonizeBest(Planet& dest) { FleetAI@ best; double bestDist = INFINITY; for(uint i = 0, cnt = motherships.length; i < cnt; ++i) { auto@ flAI = motherships[i]; if(flAI.mission !is null) continue; if(isBuildingWithLabor(flAI)) continue; double d = movement.getPathDistance(flAI.obj.position, dest.position); if(d < bestDist) { @best = flAI; bestDist = d; } } if(best !is null) { HabitatMission miss; @miss.target = dest; fleets.performMission(best, miss); } return best; } }; AIComponent@ createStarChildren() { return StarChildren(); } |
Added scripts/server/empire_ai/weasel/race/Verdant.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
import empire_ai.weasel.WeaselAI; import empire_ai.weasel.race.Race; import empire_ai.weasel.Designs; import empire_ai.weasel.Development; import empire_ai.weasel.Planets; import buildings; class Verdant : Race, RaceDesigns { Designs@ designs; Development@ development; Planets@ planets; array<const Design@> defaultDesigns; array<uint> defaultGoals; const SubsystemDef@ sinewSubsystem; const SubsystemDef@ supportSinewSubsystem; const BuildingType@ stalk; void create() override { @designs = cast<Designs>(ai.designs); @development = cast<Development>(ai.development); @planets = cast<Planets>(ai.planets); @sinewSubsystem = getSubsystemDef("VerdantSinew"); @supportSinewSubsystem = getSubsystemDef("VerdantSupportSinew"); @stalk = getBuildingType("Stalk"); } void start() override { ReadLock lock(ai.empire.designMutex); for(uint i = 0, cnt = ai.empire.designCount; i < cnt; ++i) { const Design@ dsg = ai.empire.getDesign(i); if(dsg.newer !is null) continue; if(dsg.updated !is null) continue; uint goal = designs.classify(dsg, DP_Unknown); if(goal == DP_Unknown) continue; defaultDesigns.insertLast(dsg); defaultGoals.insertLast(goal); } } void save(SaveFile& file) override { uint cnt = defaultDesigns.length; file << cnt; for(uint i = 0; i < cnt; ++i) { file << defaultDesigns[i]; file << defaultGoals[i]; } } void load(SaveFile& file) override { uint cnt = 0; file >> cnt; defaultDesigns.length = cnt; defaultGoals.length = cnt; for(uint i = 0; i < cnt; ++i) { file >> defaultDesigns[i]; file >> defaultGoals[i]; } } uint plCheck = 0; void focusTick(double time) override { if(ai.behavior.forbidConstruction) return; //Check if we need to build stalks anywhere for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) checkForStalk(development.focuses[i].plAI); uint plCnt = planets.planets.length; if(plCnt != 0) { for(uint n = 0; n < min(plCnt, 5); ++n) { plCheck = (plCheck+1) % plCnt; auto@ plAI = planets.planets[plCheck]; checkForStalk(plAI); } } } void checkForStalk(PlanetAI@ plAI) { if(plAI is null) return; Planet@ pl = plAI.obj; if(pl.pressureCap <= 0 && pl.totalPressure >= 1) { if(planets.isBuilding(plAI.obj, stalk)) return; if(pl.getBuildingCount(stalk.id) != 0) return; planets.requestBuilding(plAI, stalk, expire=180.0); } } bool preCompose(DesignTarget@ target) override { return false; } bool postCompose(DesignTarget@ target) override { // auto@ d = target.designer; // //Add an extra engine // if(target.purpose == DP_Combat) // d.composition.insertAt(0, Exhaust(tag("Engine") & tag("GivesThrust"), 0.25, 0.35)); // //Remove armor layers we don't need // for(uint i = 0, cnt = d.composition.length; i < cnt; ++i) { // if(cast<ArmorLayer>(d.composition[i]) !is null) { // d.composition.removeAt(i); // --i; --cnt; // } // } return false; } bool design(DesignTarget@ target, int size, const Design@& output) { //All designs are rescales of default designs const Design@ baseDesign; uint possible = 0; for(uint i = 0, cnt = defaultDesigns.length; i < cnt; ++i) { if(defaultGoals[i] == target.purpose) { possible += 1; if(randomd() < 1.0 / double(possible)) @baseDesign = defaultDesigns[i]; } } if(baseDesign is null) return false; //if(target.designer !is null) { // @target.designer.baseOffDesign = baseDesign; // if(target.purpose != DP_Support) // @target.designer.baseOffSubsystem = sinewSubsystem; // else // @target.designer.baseOffSubsystem = supportSinewSubsystem; // @output = target.designer.design(); //} if(output is null) @output = scaleDesign(baseDesign, size); return true; } }; AIComponent@ createVerdant() { return Verdant(); } |
Added scripts/server/empire_ai/weasel/searches.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
Object@ findEnemy(Region@ region, Empire@ emp, uint empireMask, bool fleets = true, bool stations = true, bool planets = false) { array<Object@>@ objs = findInBox(region.position - vec3d(region.radius), region.position + vec3d(region.radius), empireMask); uint offset = randomi(0, objs.length-1); uint cnt = objs.length; for(uint i = 0; i < cnt; ++i) { Object@ obj = objs[(i+offset)%cnt]; Empire@ owner = obj.owner; if(!obj.valid) { continue; } else if(owner is null || owner.mask & empireMask == 0) { continue; } else if(emp !is null && !obj.isVisibleTo(emp)) { continue; } else if(obj.region !is region) { continue; } else { uint type = obj.type; switch(type) { case OT_Ship: if(!obj.hasLeaderAI) continue; if(cast<Ship>(obj).isStation) { if(!stations) continue; } else { if(!fleets) continue; } if(obj.getFleetStrength() < 100.0) continue; break; case OT_Orbital: if(!stations) continue; break; case OT_Planet: if(!planets) continue; break; default: continue; } } return obj; } return null; } array<Object@>@ findEnemies(Region@ region, Empire@ emp, uint empireMask, bool fleets = true, bool stations = true, bool planets = false) { array<Object@>@ objs = findInBox(region.position - vec3d(region.radius), region.position + vec3d(region.radius), empireMask); array<Object@> outObjs; for(int i = objs.length-1; i >= 0; --i) { Object@ obj = objs[i]; Empire@ owner = obj.owner; bool remove = false; if(!obj.valid) { remove = true; } else if(owner is null || owner.mask & empireMask == 0) { remove = true; } else if(emp !is null && !obj.isVisibleTo(emp)) { remove = true; } else if(obj.region !is region) { remove = true; } else { uint type = obj.type; switch(type) { case OT_Ship: if(!obj.hasLeaderAI) remove = true; if(cast<Ship>(obj).isStation) { if(!stations) remove = true; } else { if(!fleets) remove = true; } if(obj.getFleetStrength() < 100.0) remove = true; break; case OT_Orbital: if(!stations) remove = true; break; case OT_Planet: if(!planets) remove = true; break; default: remove = true; } } if(!remove) outObjs.insertLast(obj); } return outObjs; } array<Object@>@ findType(Region@ region, Empire@ emp, uint objectType, uint empireMask = ~0) { // Specialized for safe object buckets array<Object@>@ objs; DataList@ data; switch(objectType) { case OT_Planet: @data = region.getPlanets(); break; case OT_Pickup: @data = region.getPickups(); break; case OT_Anomaly: @data = region.getAnomalies(); break; case OT_Artifact: @data = region.getArtifacts(); break; case OT_Asteroid: @data = region.getAsteroids(); break; } if(data !is null) { @objs = array<Object@>(); Object@ obj; while(receive(data, obj)) { if(obj !is null) objs.insertLast(obj); } } else { // No object bucket retrieval mechanism, do a full physics search @objs = findInBox(region.position - vec3d(region.radius), region.position + vec3d(region.radius), empireMask); } // Generic search using physics system array<Object@> outObjs; for(int i = objs.length-1; i >= 0; --i) { Object@ obj = objs[i]; Empire@ owner = obj.owner; bool remove = false; if(!obj.valid) { remove = true; } else if(owner is null || owner.mask & empireMask == 0) { remove = true; } else if(emp !is null && !obj.isVisibleTo(emp)) { remove = true; } else if(obj.region !is region) { remove = true; } else { uint type = obj.type; if(type != objectType) remove = true; } if(!remove) outObjs.insertLast(obj); } return outObjs; } array<Object@>@ findAll(Region@ region, uint empireMask = ~0) { return findInBox(region.position - vec3d(region.radius), region.position + vec3d(region.radius), empireMask); } double getTotalFleetStrength(Region@ region, uint empireMask, bool fleets = true, bool stations = true, bool planets = true) { auto@ objs = findAll(region, empireMask); double str = 0.0; for(uint i = 0, cnt = objs.length; i < cnt; ++i) { Object@ obj = objs[i]; Empire@ owner = obj.owner; if(!obj.valid) continue; if(owner is null || owner.mask & empireMask == 0) continue; if(obj.region !is region) continue; uint type = obj.type; switch(type) { case OT_Ship: if(!obj.hasLeaderAI) continue; if(cast<Ship>(obj).isStation) { if(!stations) continue; } else { if(!fleets) continue; } if(obj.getFleetStrength() < 100.0) continue; break; case OT_Orbital: if(!stations) continue; break; case OT_Planet: if(!planets) continue; break; default: continue; } str += sqrt(obj.getFleetStrength()); } return str * str; } |
Added scripts/server/game_start.as.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
#priority init 1000 #priority sync 10 import empire_ai.EmpireAI; import settings.map_lib; import settings.game_settings; import maps; import map_systems; import regions.regions; import artifacts; from map_generation import generatedSystems, generatedGalaxyGas, GasData; from empire import Creeps, majorEmpireCount, initEmpireDesigns, sendChatMessage; import void createWormhole(SystemDesc@ from, SystemDesc@ to) from "objects.Oddity"; import Artifact@ makeArtifact(SystemDesc@ system, uint type = uint(-1)) from "map_effects"; //Galaxy positioning Map@[] galaxies; vec3d mapLeft; vec3d mapRight; double galaxyRadius = 0; const double GALAXY_MIN_SPACING = 60000.0; const double GALAXY_MAX_SPACING = 120000.0; const double GALAXY_HEIGHT_MARGIN = 50000.0; bool overlaps(Map@ from, vec3d point, Map@ to) { return point.distanceTo(from.origin) < GALAXY_MIN_SPACING + from.radius + to.radius; } //Homeworld searches class HomeworldSearch { ScriptThread@ thread; vec3d goal; SystemData@ result; Map@ map; Empire@ emp; }; double findHomeworld(double time, ScriptThread& thread) { HomeworldSearch@ search; thread.getObject(@search); @search.result = search.map.findHomeworld(search.emp, search.goal); thread.stop(); return 0; } class QualityCalculation { array<Map@> galaxies; array<SystemData@>@ homeworlds; } void calculateQuality(QualityCalculation@ data) { uint homeworldCount = data.homeworlds.length; array<double> dists(homeworldCount); for(uint g = 0, gcnt = data.galaxies.length; g < gcnt; ++g) { Map@ mp = data.galaxies[g]; mp.calculateHomeworldDistances(); for(uint i = 0, end = mp.systemData.length; i < end; ++i) { SystemData@ system = mp.systemData[i]; mp.calculateQuality(system, data.homeworlds, dists); } } } void init() { soundScale = 500.f; if(isLoadedSave) return; double start = getExactTime(), end = start; uint hwGalaxies = 0; //Create galaxy map instances for(uint i = 0, cnt = gameSettings.galaxies.length; i < cnt; ++i) { Map@ desc = getMap(gameSettings.galaxies[i].map_id); if(desc !is null) { for(uint n = 0; n < gameSettings.galaxies[i].galaxyCount; ++n) { Map@ mp = cast<Map>(desc.create()); @mp.settings = gameSettings.galaxies[i]; mp.allowHomeworlds = gameSettings.galaxies[i].allowHomeworlds; if(mp.allowHomeworlds) hwGalaxies += 1; galaxies.insertLast(mp); } } else { error("Error: Could not find map "+gameSettings.galaxies[i].map_id); } } if(galaxies.length == 0) { auto@ _map = cast<Map>(getMap("Spiral.SpiralMap").create()); @_map.settings = MapSettings(); galaxies.insertLast(_map); } if(hwGalaxies == 0) { hwGalaxies += 1; galaxies[0].allowHomeworlds = true; } //Place all the systems for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { galaxies[i].preInit(); if(galaxies[i].allowHomeworlds) galaxies[i].estPlayerCount = ceil(double(majorEmpireCount) / double(hwGalaxies)); else galaxies[i].estPlayerCount = 0; galaxies[i].universePlayerCount = majorEmpireCount; galaxies[i].preGenerate(); } //Place the galaxies for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { vec3d origin; if(i != 0) { double startRad = galaxies[0].radius + galaxies[i].radius + GALAXY_MIN_SPACING; double endRad = startRad - GALAXY_MIN_SPACING + GALAXY_MAX_SPACING; bool overlap = false; do { vec2d pos = random2d(startRad, endRad); origin = vec3d(pos.x, randomd(-GALAXY_HEIGHT_MARGIN, GALAXY_HEIGHT_MARGIN), pos.y); overlap = false; for(uint j = 0; j < i; ++j) { if(overlaps(galaxies[j], origin, galaxies[i])) { overlap = true; endRad += GALAXY_MIN_SPACING; break; } } } while(overlap); } galaxies[i].setOrigin(origin); galaxyRadius = max(galaxyRadius, origin.length + galaxies[i].radius * 1.4); } //Search for homeworld starting positions in multiple threads (one per empire) array<SystemData@> globalHomeworlds; { array<TeamSorter> sortedEmps; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; sortedEmps.insertLast(TeamSorter(emp)); } sortedEmps.sortAsc(); array<HomeworldSearch> homeworlds(sortedEmps.length); uint mapCnt = galaxies.length; uint mapN = randomi(0, mapCnt - 1), mapC = 0; for(uint i = 0; i < homeworlds.length; ++i) { HomeworldSearch@ search = homeworlds[i]; Empire@ emp = sortedEmps[i].emp; //Find a galaxy willing to host this empire uint j = 0; do { @search.map = galaxies[(mapN + mapC) % mapCnt]; ++mapC; ++j; } while((!search.map.allowHomeworlds || !search.map.canHaveHomeworld(emp)) && j < mapCnt); if(mapC >= mapCnt) { mapN = randomi(0, mapCnt - 1); mapC = 0; } //Suggested place for this empire double angle = double(i) * twopi / double(majorEmpireCount); double rad = search.map.radius * 0.8; search.goal = vec3d(rad * cos(angle), 0, rad * sin(angle)); search.goal += search.map.origin; //Start the search @search.emp = emp; if(search.map.possibleHomeworlds.length == 0) @search.thread = ScriptThread("game_start::findHomeworld", @search); else @search.result = search.map.findHomeworld(search.emp, search.goal); } for(uint i = 0; i < homeworlds.length; ++i) { HomeworldSearch@ search = homeworlds[i]; while(search.thread !is null && search.thread.running) sleep(0); if(search.result !is null) { search.result.addHomeworld(search.emp); search.map.markHomeworld(search.result); } globalHomeworlds.insertLast(search.result); } } //Calculate system quality in threads { array<QualityCalculation> calcs(6); for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].calculateGalaxyQuality(globalHomeworlds); uint n = 0, step = int(ceil(double(galaxies.length) / double(calcs.length))); for(uint i = 0; i < calcs.length; ++i) { QualityCalculation@ calc = calcs[i]; @calc.homeworlds = @globalHomeworlds; for(uint j = 0; j < step && n < galaxies.length; ++j) { calc.galaxies.insertLast(galaxies[n]); n += 1; } calculateQuality(calc); } } //Generate physics double gridSize = max(modSpacing(7500.0), (galaxyRadius * 2.0) / 150.0); int gridAmount = (galaxyRadius * 2.0) / gridSize; setupPhysics(gridSize, gridSize / 8.0, gridAmount); //Generate region objects for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].generateRegions(); for(uint i = 0, cnt = generatedSystems.length; i < cnt; ++i) generatedSystems[i].object.finalizeCreation(); //Actually generate maps for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].generate(); //Regenerate the region lookup tree with the actual sizes regenerateRegionGroups(); //Generate wormholes in case of multiple galaxies if(galaxies.length > 1) { uint totalSystems = 0; for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) totalSystems += galaxies[i].systems.length; uint wormholes = max(config::GALAXY_MIN_WORMHOLES * galaxies.length, totalSystems / config::SYSTEMS_PER_WORMHOLE); if(wormholes % 2 != 0) wormholes += 1; uint generated = 0; for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { auto@ glx = galaxies[i]; //Figure out how many wormhole endpoints this galaxy should have double pct = double(glx.systems.length) / double(totalSystems); uint amount = max(uint(config::GALAXY_MIN_WORMHOLES), uint(round(pct * wormholes))); //Tell the galaxy to distribute them glx.placeWormholes(amount); generated += amount; } //Make a circle of wormhole endpoints for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { auto@ glx = galaxies[i]; auto@ nextGlx = galaxies[(i+1)%cnt]; auto@ from = glx.getWormhole(); auto@ to = nextGlx.getWormhole(); if(from is null || to is null) continue; createWormhole(from, to); glx.addWormhole(from, to); nextGlx.addWormhole(to, from); } //Randomly spread the remaining wormholes for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) { auto@ glx = galaxies[i], otherGlx; SystemDesc@ hole = glx.getWormhole(); SystemDesc@ other; while(hole !is null) { uint index = randomi(0, cnt - 1); for(uint n = 0; n < cnt; ++n) { @otherGlx = galaxies[n]; @other = otherGlx.getWormhole(); if(other !is null) break; } if(other !is null) { createWormhole(hole, other); glx.addWormhole(hole, other); otherGlx.addWormhole(other, hole); } @hole = glx.getWormhole(); @other = null; @otherGlx = null; } } } end = getExactTime(); info("Map generation: "+toString((end - start)*1000,1)+"ms"); start = end; end = getExactTime(); info("Link generation: "+toString((end - start)*1000,1)+"ms"); start = end; //Deal with generating unique spread artifacts if(generatedSystems.length > 1 && config::ENABLE_UNIQUE_SPREADS != 0) { for(uint i = 0, cnt = getArtifactTypeCount(); i < cnt; ++i) { auto@ type = getArtifactType(i); if(type.spreadVariable.length == 0) continue; if(config::get(type.spreadVariable) <= 0.0) continue; SystemDesc@ sys; if(type.requireContestation > 0) @sys = getRandomSystemAboveContestation(type.requireContestation); if(sys is null) @sys = getRandomSystem(); if(sys !is null) makeArtifact(sys, type.id); } } //Initialization for map code for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].initDefs(); for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].init(); //Explore entire map if indicated if(config::START_EXPLORED_MAP != 0.0) { for(uint i = 0, cnt = systemCount; i < cnt; ++i) getSystem(i).object.ExploredMask = int(~0); } //Assign already connected players to empires { if(playerEmpire !is null && playerEmpire.valid) CURRENT_PLAYER.linkEmpire(playerEmpire); uint empInd = 0, empCnt = getEmpireCount(); array<Player@>@ players = getPlayers(); //First pass: players into player empires for(uint i = 0, plCnt = players.length; i < plCnt && empInd < empCnt; ++i) { Player@ pl = players[i]; connectedPlayers.insertLast(pl); connectedSet.insert(pl.id); if(pl.emp is null) { for(; empInd < empCnt; ++empInd) { Empire@ emp = getEmpire(empInd); if(!emp.major) continue; if(emp.player !is null) continue; //if(emp.getAIType() != ET_Player) // continue; pl.linkEmpire(emp); ++empInd; break; } } } //Second pass: take over AIs empInd = 0; for(uint i = 0, plCnt = players.length; i < plCnt && empInd < empCnt; ++i) { Player@ pl = players[i]; if(pl.emp is null) { for(; empInd < empCnt; ++empInd) { Empire@ emp = getEmpire(empInd); if(!emp.major) continue; if(emp.player !is null) continue; pl.linkEmpire(emp); if(pl.name.length != 0) emp.name = pl.name; ++empInd; break; } } } } } class TeamSorter { Empire@ emp; TeamSorter() {} TeamSorter(Empire@ empire) { @emp = empire; } int opCmp(const TeamSorter& other) const { if(emp.team == -1) { if(other.emp.team == -1) return 0; return 1; } if(other.emp.team == -1) return -1; if(emp.team > other.emp.team) return 1; if(emp.team < other.emp.team) return 1; return 0; } }; uint get_systemCount() { return generatedSystems.length; } SystemDesc@ getSystem(uint index) { if(index >= generatedSystems.length) return null; return generatedSystems[index]; } SystemDesc@ getSystem(Region@ region) { if(region is null || region.SystemId == -1) return null; return generatedSystems[region.SystemId]; } SystemDesc@ getSystem(const string& name) { //TODO: Use dictionary uint cnt = systemCount; for(uint i = 0; i < cnt; ++i) { if(getSystem(i).name == name) return getSystem(i); } return null; } SystemDesc@ getRandomSystem() { return generatedSystems[randomi(0, generatedSystems.length-1)]; } SystemDesc@ getRandomSystemAboveContestation(double contest) { double roll = randomd(); double total = 0.0; SystemDesc@ chosen; for(uint i = 0, cnt = generatedSystems.length; i < cnt; ++i) { auto@ sys = generatedSystems[i]; if(sys.contestation < contest) continue; total += 1.0; double chance = 1.0 / total; if(roll < chance) { @chosen = sys; roll /= chance; } else { roll = (roll - chance) / (1.0 - chance); } } return chosen; } SystemDesc@ getClosestSystem(const vec3d& point) { SystemDesc@ closest; double dist = INFINITY; for(uint i = 0, cnt = generatedSystems.length; i < cnt; ++i) { double d = generatedSystems[i].position.distanceToSQ(point); if(d < dist) { dist = d; @closest = generatedSystems[i]; } } return closest; } void syncInitial(Message& msg) { uint cnt = generatedSystems.length; msg << cnt; for(uint i = 0; i < cnt; ++i) generatedSystems[i].write(msg); cnt = galaxies.length; msg << cnt; for(uint i = 0; i < cnt; ++i) msg << galaxies[i].id; cnt = generatedGalaxyGas.length; msg << cnt; for(uint i = 0; i < cnt; ++i) { GasData@ gas = generatedGalaxyGas[i]; msg.writeSmallVec3(gas.position); msg << float(gas.scale); if(gas.gdat.cullingNode !is null) { msg.write1(); msg.writeSmallVec3(gas.gdat.cullingNode.position); msg << float(gas.gdat.cullingNode.scale); } else { msg.write0(); } uint sCnt = gas.sprites.length; msg.writeSmall(sCnt); for(uint s = 0; s < sCnt; ++s) { GasSprite@ sprite = gas.sprites[s]; msg.writeSmallVec3(sprite.pos); msg << float(sprite.scale); msg << sprite.color; msg.writeBit(sprite.structured); } } } bool doSystemSync = false; bool sendPeriodic(Message& msg) { if(!doSystemSync) return false; doSystemSync = false; uint cnt = generatedSystems.length; msg << cnt; for(uint i = 0; i < cnt; ++i) generatedSystems[i].write(msg); return true; } array<Player@> connectedPlayers; set_int connectedSet; double timer = 0.0; void tick(double time) { for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) galaxies[i].tick(time); timer += time; if(timer >= 1.0) { timer = 0.0; array<Player@>@ players = getPlayers(); //Send connect events for(uint i = 0, cnt = players.length; i < cnt; ++i) { Player@ pl = players[i]; string name = pl.name; if(name.length == 0) continue; if(!connectedSet.contains(pl.id)) { string msg = format("[color=#aaa]"+locale::MP_CONNECT_EVENT+"[/color]", format("[b]$1[/b]", bbescape(name))); sendChatMessage(msg, offset=30); connectedPlayers.insertLast(pl); connectedSet.insert(pl.id); } } connectedSet.clear(); for(uint i = 0, cnt = players.length; i < cnt; ++i) connectedSet.insert(players[i].id); //Send disconnect events for(uint i = 0, cnt = connectedPlayers.length; i < cnt; ++i) { if(!connectedSet.contains(connectedPlayers[i].id)) { Color color; string name = connectedPlayers[i].name; Empire@ emp = connectedPlayers[i].emp; if(emp !is null) color = emp.color; string msg = format("[color=#aaa]"+locale::MP_DISCONNECT_EVENT+"[/color]", format("[b][color=$1]$2[/color][/b]", toString(color), bbescape(name))); sendChatMessage(msg, offset=30); connectedPlayers.removeAt(i); --i; --cnt; } } } } void getSystems() { uint cnt = generatedSystems.length; for(uint i = 0; i < cnt; ++i) yield(generatedSystems[i]); } void generateNewSystem(const vec3d& pos, double radius, const string& name = "", bool makeLinks = true) { generateNewSystem(pos, radius, null, name, makeLinks); } void generateNewSystem(const vec3d& pos, double radius, SystemGenerateHook@ hook, const string& name = "", bool makeLinks = true, const string& type = "") { //Because things access the generated systems list from outside of a locked context for performance, and //creating new systems is a very very rare thing, we just use an isolation hook here, which pauses the //execution of the entire game, runs the hook, then resumes. SystemGenerator sys; sys.position = pos; sys.radius = radius; sys.makeLinks = makeLinks; sys.makeType = type; sys.name = name; @sys.hook = hook; isolate_run(sys); } interface SystemGenerateHook { void call(SystemDesc@ desc); } class SystemGenerator : IsolateHook { vec3d position; double radius; string name; SystemGenerateHook@ hook; bool makeLinks = true; string makeType; void call() { if(name.length == 0) { NameGenerator sysNames; sysNames.read("data/system_names.txt"); name = sysNames.generate(); } ObjectDesc sysDesc; sysDesc.type = OT_Region; sysDesc.name = name; sysDesc.flags |= objNoPhysics; sysDesc.flags |= objNoDamage; sysDesc.delayedCreation = true; sysDesc.position = position; Region@ region = cast<Region>(makeObject(sysDesc)); region.alwaysVisible = true; region.InnerRadius = radius / 1.5; region.OuterRadius = radius; region.radius = region.OuterRadius; SystemData dat; dat.index = generatedSystems.length; dat.position = position; dat.quality = 100; @dat.systemCode = SystemCode(); SystemDesc desc; desc.index = generatedSystems.length; region.SystemId = desc.index; desc.name = region.name; desc.position = position; desc.radius = region.OuterRadius; @desc.object = region; generatedSystems.insertLast(desc); addRegion(desc.object); region.finalizeCreation(); //Run the type auto@ sysType = getSystemType(makeType); if(sysType !is null) { dat.systemType = sysType.id; sysType.generate(dat, desc); region.InnerRadius = desc.radius; region.OuterRadius = desc.radius * 1.5; region.radius = region.OuterRadius; desc.radius = region.OuterRadius; sysType.postGenerate(dat, desc); MapGeneration gen; gen.finalizeSystem(dat, desc); } //Make trade lines to nearby systems if(makeLinks) { SystemDesc@ closest; array<SystemDesc@> nearby; double closestDist = INFINITY; for(uint i = 0, cnt = generatedSystems.length; i < cnt; ++i) { double d = generatedSystems[i].position.distanceTo(desc.position); if(generatedSystems[i] is desc) continue; if(d < 13000.0) nearby.insertLast(generatedSystems[i]); if(d < closestDist) { closestDist = d; @closest = generatedSystems[i]; } } if(nearby.length == 0) { closest.adjacent.insertLast(desc.index); closest.adjacentDist.insertLast(closest.position.distanceTo(desc.position)); desc.adjacent.insertLast(closest.index); desc.adjacentDist.insertLast(closest.position.distanceTo(desc.position)); } else { for(uint i = 0, cnt = nearby.length; i < cnt; ++i) { nearby[i].adjacent.insertLast(desc.index); nearby[i].adjacentDist.insertLast(nearby[i].position.distanceTo(desc.position)); desc.adjacent.insertLast(nearby[i].index); desc.adjacentDist.insertLast(nearby[i].position.distanceTo(desc.position)); } } if(desc.adjacent.length == 0 || config::START_EXPLORED_MAP != 0.0) { desc.object.ExploredMask.value = int(~0); } else { for(uint i = 0, cnt = desc.adjacent.length; i < cnt; ++i) desc.object.ExploredMask |= getSystem(desc.adjacent[i]).object.SeenMask; } } //Create the system node Node@ snode = bindCullingNode(region, desc.position, 1000.0); snode.scale = region.radius + 128.0; snode.rebuildTransform(); calcGalaxyExtents(); if(hook !is null) hook.call(desc); //Notify clients of changes refreshClientSystems(CURRENT_PLAYER); doSystemSync = true; } }; void save(SaveFile& data) { data << uint(generatedSystems.length); for(uint i = 0; i < generatedSystems.length; ++i) generatedSystems[i].save(data); data << uint(generatedGalaxies.length); for(uint i = 0; i < generatedGalaxies.length; ++i) generatedGalaxies[i].save(data); data << uint(generatedGalaxyGas.length); for(uint i = 0; i < generatedGalaxyGas.length; ++i) generatedGalaxyGas[i].save(data); data << uint(galaxies.length); for(uint i = 0; i < galaxies.length; ++i) { data << galaxies[i].id; galaxies[i].save(data); } } void load(SaveFile& data) { uint count = 0; data >> count; generatedSystems.length = count; for(uint i = 0; i < generatedSystems.length; ++i) { SystemDesc desc; desc.load(data); @generatedSystems[i] = desc; } data >> count; generatedGalaxies.length = count; for(uint i = 0; i < count; ++i) { @generatedGalaxies[i] = GalaxyData(); generatedGalaxies[i].load(data); } data >> count; generatedGalaxyGas.length = count; for(uint i = 0; i < count; ++i) { @generatedGalaxyGas[i] = GasData(); generatedGalaxyGas[i].load(data); } if(data >= SV_0040) { data >> count; galaxies.length = count; for(uint i = 0; i < galaxies.length; ++i) { string ident; data >> ident; @galaxies[i] = getMap(ident).create(); galaxies[i].load(data); } } } |