diff --git a/android/Images/OtherIcons/Swap.png b/android/Images/OtherIcons/Swap.png new file mode 100644 index 0000000000..443719e23f Binary files /dev/null and b/android/Images/OtherIcons/Swap.png differ diff --git a/android/assets/game.atlas b/android/assets/game.atlas index 04f3d4b5fd..03ebc0c949 100644 --- a/android/assets/game.atlas +++ b/android/assets/game.atlas @@ -13,7 +13,7 @@ EmojiIcons/Production index: -1 EmojiIcons/Turn rotate: false - xy: 1940, 1318 + xy: 1940, 1214 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -167,14 +167,14 @@ ImprovementIcons/Road index: -1 ImprovementIcons/Terrace farm rotate: false - xy: 1734, 1322 + xy: 1836, 1424 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 ImprovementIcons/Trading post rotate: false - xy: 1632, 1118 + xy: 1734, 1220 size: 100, 100 orig: 100, 100 offset: 0, 0 @@ -356,14 +356,14 @@ NationIcons/Spain index: -1 NationIcons/The Ottomans rotate: false - xy: 1836, 1424 + xy: 1428, 914 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 OtherIcons/Aircraft rotate: false - xy: 1428, 760 + xy: 1530, 868 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -440,7 +440,7 @@ OtherIcons/DisbandUnit index: -1 OtherIcons/Down rotate: false - xy: 1582, 816 + xy: 1888, 1168 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -585,6 +585,13 @@ OtherIcons/Stop orig: 100, 100 offset: 0, 0 index: -1 +OtherIcons/Swap + rotate: false + xy: 1734, 1322 + size: 100, 100 + orig: 100, 100 + offset: 0, 0 + index: -1 OtherIcons/Triangle rotate: false xy: 1342, 1856 @@ -594,7 +601,7 @@ OtherIcons/Triangle index: -1 OtherIcons/Up rotate: false - xy: 1940, 1214 + xy: 1940, 1162 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -615,175 +622,175 @@ OtherIcons/whiteDot index: -1 PolicyIcons/Aristocracy rotate: false - xy: 1734, 1066 + xy: 1836, 1168 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Citizenship rotate: false - xy: 1888, 1270 + xy: 1480, 760 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Civil Society rotate: false - xy: 1888, 1218 + xy: 1480, 708 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Collective Rule rotate: false - xy: 1480, 760 + xy: 1582, 868 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Constitution rotate: false - xy: 1480, 708 + xy: 1582, 816 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Democracy rotate: false - xy: 1582, 868 + xy: 1786, 1066 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Entrepreneurship rotate: false - xy: 1786, 1066 + xy: 1532, 764 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Fascism rotate: false - xy: 1532, 712 + xy: 1584, 764 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Free Religion rotate: false - xy: 1584, 712 + xy: 206, 530 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Free Speech rotate: false - xy: 1584, 660 + xy: 206, 478 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Free Thought rotate: false - xy: 206, 530 + xy: 206, 426 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Humanism rotate: false - xy: 206, 322 + xy: 206, 218 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Landed Elite rotate: false - xy: 206, 114 + xy: 258, 530 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Legalism rotate: false - xy: 206, 62 + xy: 258, 478 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Mandate Of Heaven rotate: false - xy: 258, 478 + xy: 258, 374 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Mercantilism rotate: false - xy: 258, 322 + xy: 258, 218 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Meritocracy rotate: false - xy: 258, 270 + xy: 258, 166 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Militarism rotate: false - xy: 258, 218 + xy: 258, 114 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Military Caste rotate: false - xy: 258, 166 + xy: 258, 62 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Military Tradition rotate: false - xy: 258, 114 + xy: 1428, 604 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Monarchy rotate: false - xy: 1428, 604 + xy: 1532, 608 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Oligarchy rotate: false - xy: 1584, 608 + xy: 1990, 1422 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Organized Religion rotate: false - xy: 1990, 1474 + xy: 1990, 1370 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Patronage rotate: false - xy: 1990, 1422 + xy: 1990, 1318 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Police State rotate: false - xy: 1990, 1370 + xy: 1990, 1266 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -874,14 +881,14 @@ PolicyIcons/Trade Unions index: -1 PolicyIcons/Universal Suffrage rotate: false - xy: 1940, 1266 + xy: 1992, 1214 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Warrior Code rotate: false - xy: 1992, 1266 + xy: 1532, 556 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -1056,28 +1063,28 @@ ResourceIcons/Sugar index: -1 ResourceIcons/Uranium rotate: false - xy: 1836, 1322 + xy: 1428, 812 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 ResourceIcons/Whales rotate: false - xy: 1428, 812 + xy: 1530, 920 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 ResourceIcons/Wheat rotate: false - xy: 1530, 920 + xy: 1632, 1016 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 ResourceIcons/Wine rotate: false - xy: 1632, 1016 + xy: 1734, 1118 size: 100, 100 orig: 100, 100 offset: 0, 0 @@ -1133,7 +1140,7 @@ StatIcons/Happiness index: -1 StatIcons/InterceptRange rotate: false - xy: 206, 218 + xy: 206, 114 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -1147,7 +1154,7 @@ StatIcons/Malcontent index: -1 StatIcons/Movement rotate: false - xy: 1532, 608 + xy: 1990, 1474 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -1707,28 +1714,28 @@ TechIcons/Steel index: -1 TechIcons/The Wheel rotate: false - xy: 1428, 914 + xy: 1530, 1022 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 TechIcons/Theology rotate: false - xy: 1530, 1022 + xy: 1632, 1118 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 TechIcons/Trapping rotate: false - xy: 1734, 1220 + xy: 1836, 1322 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 TechIcons/Writing rotate: false - xy: 1734, 1118 + xy: 1836, 1220 size: 100, 100 orig: 100, 100 offset: 0, 0 @@ -1868,42 +1875,42 @@ ImprovementIcons/Railroad index: -1 TileSets/Default/Tiles/River-Bottom rotate: false - xy: 1270, 490 + xy: 1270, 426 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/River-Bottom rotate: false - xy: 1270, 490 + xy: 1270, 426 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/Default/Tiles/River-BottomLeft rotate: false - xy: 1304, 482 + xy: 1338, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/River-BottomLeft rotate: false - xy: 1304, 482 + xy: 1338, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/Default/Tiles/River-BottomRight rotate: false - xy: 1338, 460 + xy: 1304, 456 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/River-BottomRight rotate: false - xy: 1338, 460 + xy: 1304, 456 size: 32, 28 orig: 32, 28 offset: 0, 0 @@ -1945,21 +1952,21 @@ TileSets/FantasyHex/Tiles/Academy-Snow index: -1 TileSets/FantasyHex/Tiles/Aluminum rotate: false - xy: 1836, 1136 + xy: 1836, 1138 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ancient ruins rotate: false - xy: 1992, 1184 + xy: 1688, 570 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ancient ruins-Jungle rotate: false - xy: 1870, 1132 + xy: 1870, 1134 size: 32, 32 orig: 32, 32 offset: 0, 0 @@ -1973,7 +1980,7 @@ TileSets/FantasyHex/Tiles/Ancient ruins-Sand index: -1 TileSets/FantasyHex/Tiles/Ancient ruins-Snow rotate: false - xy: 1904, 1136 + xy: 1904, 1138 size: 32, 28 orig: 32, 28 offset: 0, 0 @@ -1987,924 +1994,924 @@ TileSets/FantasyHex/Tiles/Ancient ruins2 index: -1 TileSets/FantasyHex/Tiles/Atoll rotate: false - xy: 1738, 908 + xy: 1736, 968 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Bananas rotate: false - xy: 1770, 976 + xy: 1738, 878 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Barbarian encampment rotate: false - xy: 1804, 1006 + xy: 1770, 1006 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Barbarian encampment-Snow rotate: false - xy: 1804, 975 + xy: 1770, 975 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Barringer Crater rotate: false - xy: 2013, 1802 + xy: 1804, 1006 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Cattle rotate: false - xy: 1550, 578 + xy: 2013, 1591 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Cattle+Pasture rotate: false - xy: 1584, 574 + xy: 2013, 1557 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Cerro de Potosi rotate: false - xy: 1740, 818 + xy: 1380, 565 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Citadel rotate: false - xy: 1740, 721 + xy: 1482, 567 size: 32, 35 orig: 32, 35 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Citadel-Snow rotate: false - xy: 1740, 689 + xy: 1722, 568 size: 32, 30 orig: 32, 30 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center rotate: false - xy: 1740, 652 + xy: 1722, 531 size: 32, 35 orig: 32, 35 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Ancient era rotate: false - xy: 1740, 618 + xy: 1740, 844 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Classical era rotate: false - xy: 1740, 584 + xy: 1740, 810 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Future era rotate: false - xy: 284, 642 + xy: 1740, 774 size: 32, 34 orig: 32, 34 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Industrial era rotate: false - xy: 318, 643 + xy: 1740, 739 size: 32, 33 orig: 32, 33 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Information era rotate: false - xy: 352, 640 + xy: 1740, 701 size: 32, 36 orig: 32, 36 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Medieval era rotate: false - xy: 288, 608 + xy: 1740, 667 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Modern era rotate: false - xy: 386, 640 + xy: 1740, 631 size: 32, 34 orig: 32, 34 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City center-Renaissance era rotate: false - xy: 420, 642 + xy: 1774, 851 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/City ruins rotate: false - xy: 454, 646 + xy: 1740, 601 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Coal rotate: false - xy: 1448, 544 + xy: 1774, 791 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Coast rotate: false - xy: 1482, 544 + xy: 1774, 761 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Cotton rotate: false - xy: 1686, 570 + xy: 1808, 856 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Customs house rotate: false - xy: 1686, 533 + xy: 1808, 761 size: 32, 35 orig: 32, 35 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Deer rotate: false - xy: 1652, 512 + xy: 1808, 731 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Deer+Camp rotate: false - xy: 1686, 503 + xy: 1808, 701 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Desert rotate: false - xy: 1720, 554 + xy: 1808, 671 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Desert+Farm rotate: false - xy: 1720, 524 + xy: 1808, 641 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Wheat+Farm rotate: false - xy: 1720, 524 + xy: 1808, 641 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Desert+Flood plains+Farm rotate: false - xy: 1720, 494 + xy: 1808, 611 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Dyes rotate: false - xy: 1754, 494 + xy: 1904, 1108 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Dyes+Plantation rotate: false - xy: 1904, 1106 + xy: 1938, 1102 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/El Dorado rotate: false - xy: 1938, 1101 + xy: 1972, 1101 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Fallout rotate: false - xy: 1972, 1117 + xy: 2006, 1095 size: 32, 35 orig: 32, 35 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Fish rotate: false - xy: 1972, 1087 + xy: 1756, 511 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Fishing Boats rotate: false - xy: 2006, 1124 + xy: 1414, 544 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Flood plains rotate: false - xy: 2006, 1094 + xy: 1448, 544 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Forest rotate: false - xy: 1584, 510 + xy: 284, 644 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Fort rotate: false - xy: 1618, 505 + xy: 318, 643 size: 32, 33 orig: 32, 33 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Fountain of Youth rotate: false - xy: 1652, 478 + xy: 352, 644 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Furs rotate: false - xy: 1720, 464 + xy: 420, 646 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Furs+Camp rotate: false - xy: 1754, 464 + xy: 454, 646 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Gems rotate: false - xy: 454, 586 + xy: 1824, 581 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Gold Ore rotate: false - xy: 488, 580 + xy: 1824, 551 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grand Mesa rotate: false - xy: 522, 576 + xy: 1824, 517 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland rotate: false - xy: 556, 580 + xy: 1790, 489 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Farm rotate: false - xy: 590, 580 + xy: 1824, 487 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Forest+Camp rotate: false - xy: 624, 577 + xy: 1516, 523 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Forest+Deer+Camp rotate: false - xy: 658, 577 + xy: 1550, 523 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Forest+Furs+Camp rotate: false - xy: 692, 577 + xy: 1584, 523 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Forest+Lumber mill rotate: false - xy: 726, 577 + xy: 1618, 515 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Hill+Farm rotate: false - xy: 760, 580 + xy: 1652, 518 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Hill+Forest+Camp rotate: false - xy: 794, 580 + xy: 1686, 510 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Hill+Forest+Lumber mill rotate: false - xy: 828, 580 + xy: 1720, 501 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Hill+Forest+Trading post rotate: false - xy: 862, 580 + xy: 1652, 488 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Grassland+Jungle+Trading post rotate: false - xy: 896, 576 + xy: 1686, 476 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/GrasslandForest rotate: false - xy: 930, 577 + xy: 1720, 468 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Great Barrier Reef rotate: false - xy: 1032, 580 + xy: 1788, 459 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Hill rotate: false - xy: 794, 546 + xy: 522, 576 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/HillForest+Lumber mill rotate: false - xy: 828, 550 + xy: 556, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/HillMarbleQuarry rotate: false - xy: 862, 550 + xy: 590, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/HillMine rotate: false - xy: 896, 546 + xy: 624, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/HillStoneQuarry rotate: false - xy: 930, 547 + xy: 658, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Horses rotate: false - xy: 1066, 550 + xy: 794, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Horses+Pasture rotate: false - xy: 1100, 546 + xy: 828, 576 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ice rotate: false - xy: 1202, 550 + xy: 930, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Incense rotate: false - xy: 1304, 550 + xy: 1032, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Incense+Plantation rotate: false - xy: 1338, 550 + xy: 1066, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Iron rotate: false - xy: 794, 516 + xy: 1134, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ivory rotate: false - xy: 862, 520 + xy: 556, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ivory+Camp rotate: false - xy: 896, 516 + xy: 590, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Jungle rotate: false - xy: 998, 516 + xy: 692, 546 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Krakatoa rotate: false - xy: 1134, 514 + xy: 828, 544 size: 32, 30 orig: 32, 30 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Lakes rotate: false - xy: 1202, 520 + xy: 896, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Landmark rotate: false - xy: 1304, 512 + xy: 998, 542 size: 32, 36 orig: 32, 36 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Manufactory rotate: false - xy: 1508, 502 + xy: 862, 508 size: 32, 39 orig: 32, 39 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Marble rotate: false - xy: 1406, 484 + xy: 930, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Marsh rotate: false - xy: 1474, 483 + xy: 998, 511 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Mine rotate: false - xy: 1542, 457 + xy: 1134, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Moai rotate: false - xy: 1610, 444 + xy: 930, 489 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Mount Fuji rotate: false - xy: 1746, 432 + xy: 1066, 488 size: 32, 30 orig: 32, 30 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Mountain rotate: false - xy: 1406, 446 + xy: 1100, 482 size: 32, 36 orig: 32, 36 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Oasis rotate: false - xy: 1610, 414 + xy: 1134, 460 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Ocean rotate: false - xy: 1644, 418 + xy: 1168, 460 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Offshore Platform rotate: false - xy: 1678, 413 + xy: 964, 460 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Oil rotate: false - xy: 1712, 404 + xy: 998, 451 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Oil well rotate: false - xy: 1746, 402 + xy: 1032, 430 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Old Faithful rotate: false - xy: 1440, 420 + xy: 1066, 424 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Pasture rotate: false - xy: 1542, 393 + xy: 1168, 426 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Pearls rotate: false - xy: 1610, 384 + xy: 1168, 396 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plains rotate: false - xy: 1746, 372 + xy: 1270, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plains+Farm rotate: false - xy: 1474, 393 + xy: 1202, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plains+Forest+Camp rotate: false - xy: 1508, 378 + xy: 1236, 516 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plains+Forest+Lumber mill rotate: false - xy: 1542, 359 + xy: 1202, 456 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plains+Jungle+Trading post rotate: false - xy: 1576, 356 + xy: 1236, 482 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/PlainsForest rotate: false - xy: 1610, 350 + xy: 1270, 516 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/PlainsJungle rotate: false - xy: 1644, 354 + xy: 1304, 546 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plantation rotate: false - xy: 1678, 353 + xy: 1202, 426 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plantation+Bananas rotate: false - xy: 1712, 344 + xy: 1236, 452 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Plantation+Cotton rotate: false - xy: 1746, 342 + xy: 1270, 486 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Quarry rotate: false - xy: 1100, 486 + xy: 1202, 396 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Quarry+Marble rotate: false - xy: 1134, 484 + xy: 1236, 422 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Quarry+Stone rotate: false - xy: 1168, 490 + xy: 1270, 456 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Rock of Gibraltar rotate: false - xy: 1372, 441 + xy: 1338, 456 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Sheep rotate: false - xy: 1576, 326 + xy: 1338, 396 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Sheep+Pasture rotate: false - xy: 1610, 316 + xy: 1372, 531 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Silk rotate: false - xy: 1712, 314 + xy: 1372, 440 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Silk+Plantation rotate: false - xy: 1746, 312 + xy: 1372, 410 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Silver rotate: false - xy: 1644, 293 + xy: 1406, 514 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Snow rotate: false - xy: 1746, 282 + xy: 1406, 452 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Snow+Farm rotate: false - xy: 1168, 460 + xy: 1440, 484 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Spices rotate: false - xy: 1236, 460 + xy: 1440, 454 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Spices+Plantation rotate: false - xy: 1270, 460 + xy: 1440, 424 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Stone rotate: false - xy: 1304, 452 + xy: 1474, 507 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Sugar rotate: false - xy: 1372, 411 + xy: 1474, 449 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Sugar+Plantation rotate: false - xy: 1406, 386 + xy: 1474, 419 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Terrace farm rotate: false - xy: 1542, 298 + xy: 1576, 493 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Trading post rotate: false - xy: 1610, 286 + xy: 1542, 463 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra rotate: false - xy: 1712, 254 + xy: 1576, 433 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra+Farm rotate: false - xy: 1746, 252 + xy: 1610, 485 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra+Forest+Camp rotate: false - xy: 1780, 430 + xy: 1610, 451 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra+Forest+Camp+Furs rotate: false - xy: 1780, 396 + xy: 1644, 454 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra+Forest+Deer+Camp rotate: false - xy: 1780, 362 + xy: 1610, 417 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Tundra+Forest+Lumber mill rotate: false - xy: 1780, 328 + xy: 1644, 420 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/TundraForest rotate: false - xy: 1780, 294 + xy: 1678, 442 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Uranium rotate: false - xy: 1780, 234 + xy: 1712, 438 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Whales rotate: false - xy: 964, 487 + xy: 1576, 403 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Whales+Fishing Boats rotate: false - xy: 998, 486 + xy: 1372, 380 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Wheat rotate: false - xy: 1032, 460 + xy: 1406, 392 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Wine rotate: false - xy: 1066, 457 + xy: 1440, 394 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Tiles/Wine+Plantation rotate: false - xy: 1100, 456 + xy: 1474, 389 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/TopBorder rotate: false - xy: 1576, 296 + xy: 1508, 433 size: 32, 28 orig: 32, 28 offset: 0, 0 @@ -2918,875 +2925,875 @@ TileSets/FantasyHex/Units/African Forest Elephant index: -1 TileSets/FantasyHex/Units/Anti-Aircraft Gun rotate: false - xy: 1938, 1132 + xy: 1688, 540 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Anti-Tank Gun rotate: false - xy: 1992, 1154 + xy: 1938, 1132 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Archer rotate: false - xy: 1736, 998 + xy: 1972, 1132 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Artillery rotate: false - xy: 1736, 968 + xy: 2006, 1132 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Atlatlist rotate: false - xy: 1738, 938 + xy: 1736, 998 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Axe Thrower rotate: false - xy: 1738, 878 + xy: 1738, 938 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Ballista rotate: false - xy: 1770, 1006 + xy: 1738, 908 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Battering Ram rotate: false - xy: 2013, 1772 + xy: 1804, 976 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Battleship rotate: false - xy: 2013, 1742 + xy: 1772, 945 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Bazooka rotate: false - xy: 2013, 1712 + xy: 1772, 915 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Berber Cavalry rotate: false - xy: 2013, 1682 + xy: 1772, 885 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Berserker rotate: false - xy: 2013, 1652 + xy: 1806, 946 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Bowman rotate: false - xy: 2013, 1622 + xy: 1806, 916 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Brute rotate: false - xy: 2013, 1592 + xy: 1806, 886 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Camel Archer rotate: false - xy: 2013, 1561 + xy: 2013, 1801 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Cannon rotate: false - xy: 2013, 1531 + xy: 2013, 1771 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Caravel rotate: false - xy: 1380, 565 + xy: 2013, 1741 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Carolean rotate: false - xy: 1414, 574 + xy: 2013, 1711 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Carrier rotate: false - xy: 1448, 574 + xy: 2013, 1681 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Cataphract rotate: false - xy: 1482, 574 + xy: 2013, 1651 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Catapult rotate: false - xy: 1516, 574 + xy: 2013, 1621 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Cavalry rotate: false - xy: 1740, 848 + xy: 2013, 1527 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Chariot Archer rotate: false - xy: 1740, 788 + xy: 1414, 574 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Chu-Ko-Nu rotate: false - xy: 1740, 758 + xy: 1448, 574 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/CivilianLandUnit rotate: false - xy: 1414, 544 + xy: 1774, 821 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Comanche Rider rotate: false - xy: 1516, 543 + xy: 1774, 730 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Companion Cavalry rotate: false - xy: 1550, 547 + xy: 1774, 699 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Composite Bowman rotate: false - xy: 1584, 544 + xy: 1774, 669 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Conquistador rotate: false - xy: 1618, 570 + xy: 1774, 639 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Cossack rotate: false - xy: 1652, 570 + xy: 1774, 609 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Crossbowman rotate: false - xy: 1618, 540 + xy: 1808, 826 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Cruiser rotate: false - xy: 1652, 542 + xy: 1808, 798 size: 32, 26 orig: 32, 26 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Destroyer rotate: false - xy: 1754, 554 + xy: 1756, 571 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Dromon rotate: false - xy: 1754, 524 + xy: 1756, 541 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Foreign Legion rotate: false - xy: 1550, 517 + xy: 1482, 537 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Frigate rotate: false - xy: 1686, 473 + xy: 386, 646 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Galleass rotate: false - xy: 2006, 1064 + xy: 1790, 579 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Galley rotate: false - xy: 454, 616 + xy: 1790, 549 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Gatling Gun rotate: false - xy: 420, 612 + xy: 1790, 519 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Admiral rotate: false - xy: 964, 577 + xy: 1754, 478 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Artist rotate: false - xy: 998, 580 + xy: 1754, 448 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Engineer rotate: false - xy: 1066, 580 + xy: 1822, 457 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Galleass rotate: false - xy: 1100, 580 + xy: 1788, 429 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great General rotate: false - xy: 1134, 577 + xy: 1822, 424 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Merchant rotate: false - xy: 1168, 580 + xy: 386, 616 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Musician rotate: false - xy: 556, 550 + xy: 420, 616 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Prophet rotate: false - xy: 590, 550 + xy: 454, 616 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Scientist rotate: false - xy: 624, 547 + xy: 352, 614 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great War Infantry rotate: false - xy: 658, 547 + xy: 386, 586 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Great Writer rotate: false - xy: 692, 547 + xy: 420, 586 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Hakkapeliitta rotate: false - xy: 726, 547 + xy: 454, 586 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Helicopter Gunship rotate: false - xy: 760, 550 + xy: 488, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Hoplite rotate: false - xy: 964, 547 + xy: 692, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Horse Archer rotate: false - xy: 998, 550 + xy: 726, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Horseman rotate: false - xy: 1032, 550 + xy: 760, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Hussar rotate: false - xy: 1134, 546 + xy: 862, 579 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Hwach'a rotate: false - xy: 1168, 550 + xy: 896, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Immortal rotate: false - xy: 1236, 550 + xy: 964, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Impi rotate: false - xy: 1270, 550 + xy: 998, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Infantry rotate: false - xy: 760, 520 + xy: 1100, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Ironclad rotate: false - xy: 828, 520 + xy: 1168, 580 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Jaguar rotate: false - xy: 930, 517 + xy: 624, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Janissary rotate: false - xy: 964, 517 + xy: 658, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Keshik rotate: false - xy: 1032, 520 + xy: 726, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Khan rotate: false - xy: 1066, 517 + xy: 760, 547 size: 32, 31 orig: 32, 31 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Knight rotate: false - xy: 1100, 516 + xy: 794, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Kris Swordsman rotate: false - xy: 1168, 520 + xy: 862, 549 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Lancer rotate: false - xy: 1236, 520 + xy: 930, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/LandUnit rotate: false - xy: 1270, 520 + xy: 964, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Landship rotate: false - xy: 1338, 520 + xy: 1032, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Landsknecht rotate: false - xy: 1372, 535 + xy: 1066, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Legion rotate: false - xy: 1372, 505 + xy: 1100, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Longbowman rotate: false - xy: 1338, 490 + xy: 1134, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Longswordsman rotate: false - xy: 1406, 514 + xy: 1168, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Machine Gun rotate: false - xy: 1440, 514 + xy: 794, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Mandekalu Cavalry rotate: false - xy: 1474, 514 + xy: 828, 514 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Maori Warrior rotate: false - xy: 1372, 475 + xy: 896, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Marine rotate: false - xy: 1440, 484 + xy: 964, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Mechanized Infantry rotate: false - xy: 1508, 472 + xy: 1032, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Mehal Sefari rotate: false - xy: 1542, 487 + xy: 1066, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Merchant Of Venice rotate: false - xy: 1576, 480 + xy: 1100, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Minuteman rotate: false - xy: 1576, 450 + xy: 1168, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Missile Cruiser rotate: false - xy: 1610, 475 + xy: 896, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Mobile SAM rotate: false - xy: 1644, 448 + xy: 964, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Modern Armor rotate: false - xy: 1678, 443 + xy: 998, 481 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Mohawk Warrior rotate: false - xy: 1712, 434 + xy: 1032, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Musketeer rotate: false - xy: 1440, 454 + xy: 1134, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Musketman rotate: false - xy: 1474, 453 + xy: 1168, 490 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Naresuan's Elephant rotate: false - xy: 1508, 442 + xy: 1032, 460 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Nau rotate: false - xy: 1542, 427 + xy: 1066, 458 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Norwegian Ski Infantry rotate: false - xy: 1576, 420 + xy: 1100, 452 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Panzer rotate: false - xy: 1474, 423 + xy: 1100, 422 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Paratrooper rotate: false - xy: 1508, 412 + xy: 1134, 430 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Pathfinder rotate: false - xy: 1576, 390 + xy: 1134, 400 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Persian Immortal rotate: false - xy: 1644, 388 + xy: 1202, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Pictish Warrior rotate: false - xy: 1678, 383 + xy: 1236, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Pikeman rotate: false - xy: 1712, 374 + xy: 1202, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Pracinha rotate: false - xy: 1032, 490 + xy: 1338, 550 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Privateer rotate: false - xy: 1066, 487 + xy: 1304, 516 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Quinquereme rotate: false - xy: 1202, 490 + xy: 1338, 520 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Rifleman rotate: false - xy: 1236, 490 + xy: 1304, 486 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Rocket Artillery rotate: false - xy: 1406, 416 + xy: 1304, 426 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Samurai rotate: false - xy: 1440, 390 + xy: 1338, 426 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Scout rotate: false - xy: 1474, 363 + xy: 1236, 392 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Sea Beggar rotate: false - xy: 1508, 348 + xy: 1270, 396 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Settler rotate: false - xy: 1542, 328 + xy: 1304, 395 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Ship of the Line rotate: false - xy: 1644, 323 + xy: 1372, 500 size: 32, 29 orig: 32, 29 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Siege Tower rotate: false - xy: 1678, 323 + xy: 1372, 470 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Sipahi rotate: false - xy: 1678, 291 + xy: 1406, 482 size: 32, 30 orig: 32, 30 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Slinger rotate: false - xy: 1712, 284 + xy: 1440, 514 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Spearman rotate: false - xy: 1202, 460 + xy: 1406, 422 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Submarine rotate: false - xy: 1338, 432 + xy: 1474, 479 size: 32, 26 orig: 32, 26 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Swordsman rotate: false - xy: 1440, 360 + xy: 1508, 493 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Tank rotate: false - xy: 1474, 333 + xy: 1542, 493 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Tercio rotate: false - xy: 1508, 318 + xy: 1508, 463 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Trebuchet rotate: false - xy: 1644, 263 + xy: 1542, 433 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Trireme rotate: false - xy: 1678, 261 + xy: 1576, 463 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Turtle Ship rotate: false - xy: 1780, 264 + xy: 1678, 412 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/War Chariot rotate: false - xy: 828, 490 + xy: 1712, 408 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/War Elephant rotate: false - xy: 862, 490 + xy: 1746, 418 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Warrior rotate: false - xy: 896, 486 + xy: 1508, 403 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/WaterUnit rotate: false - xy: 930, 489 + xy: 1542, 405 size: 32, 26 orig: 32, 26 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Winged Hussar rotate: false - xy: 1134, 454 + xy: 1338, 366 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Work Boats rotate: false - xy: 1168, 430 + xy: 1610, 387 size: 32, 28 orig: 32, 28 offset: 0, 0 index: -1 TileSets/FantasyHex/Units/Worker rotate: false - xy: 1202, 430 + xy: 1644, 390 size: 32, 28 orig: 32, 28 offset: 0, 0 @@ -3800,21 +3807,21 @@ UnitPromotionIcons/Accuracy index: -1 UnitPromotionIcons/Air Repair rotate: false - xy: 1836, 1270 + xy: 1428, 760 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Ambush rotate: false - xy: 1530, 868 + xy: 1632, 964 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Amphibious rotate: false - xy: 1632, 964 + xy: 1734, 1066 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -3828,189 +3835,189 @@ UnitPromotionIcons/Armor Plating index: -1 UnitPromotionIcons/Barrage rotate: false - xy: 1836, 1218 + xy: 1428, 708 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Besiege rotate: false - xy: 1428, 708 + xy: 1530, 816 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Siege rotate: false - xy: 1428, 708 + xy: 1530, 816 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Blitz rotate: false - xy: 1530, 816 + xy: 1938, 1370 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Boarding Party rotate: false - xy: 1938, 1370 + xy: 1428, 656 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Bombardment rotate: false - xy: 1836, 1166 + xy: 1938, 1318 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Charge rotate: false - xy: 1428, 656 + xy: 1938, 1266 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Coastal Raider rotate: false - xy: 1888, 1166 + xy: 1480, 656 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Cover rotate: false - xy: 1480, 656 + xy: 1684, 964 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 PolicyIcons/Discipline rotate: false - xy: 1480, 656 + xy: 1684, 964 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Drill rotate: false - xy: 1684, 964 + xy: 236, 582 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Evasion rotate: false - xy: 236, 582 + xy: 1532, 712 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Extended Range rotate: false - xy: 1532, 764 + xy: 1532, 660 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Operational Range rotate: false - xy: 1532, 764 + xy: 1532, 660 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Flight Deck rotate: false - xy: 1532, 660 + xy: 1584, 712 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Formation rotate: false - xy: 1584, 764 + xy: 1584, 660 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Great Generals rotate: false - xy: 206, 478 + xy: 206, 374 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Quick Study rotate: false - xy: 206, 478 + xy: 206, 374 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Haka War Dance rotate: false - xy: 206, 426 + xy: 206, 322 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Heal Instantly rotate: false - xy: 206, 374 + xy: 206, 270 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Indirect Fire rotate: false - xy: 206, 270 + xy: 206, 166 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Interception rotate: false - xy: 206, 166 + xy: 206, 62 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Logistics rotate: false - xy: 258, 530 + xy: 258, 426 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/March rotate: false - xy: 258, 426 + xy: 258, 322 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Medic rotate: false - xy: 258, 374 + xy: 258, 270 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Mobility rotate: false - xy: 258, 62 + xy: 1480, 604 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Morale rotate: false - xy: 1480, 604 + xy: 1584, 608 size: 50, 50 orig: 50, 50 offset: 0, 0 @@ -4073,21 +4080,21 @@ UnitPromotionIcons/Targeting I (air) index: -1 UnitPromotionIcons/Volley rotate: false - xy: 1992, 1318 + xy: 1992, 1162 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Wolfpack rotate: false - xy: 1992, 1214 + xy: 1584, 556 size: 50, 50 orig: 50, 50 offset: 0, 0 index: -1 UnitPromotionIcons/Woodsman rotate: false - xy: 1940, 1162 + xy: 1636, 548 size: 50, 50 orig: 50, 50 offset: 0, 0 diff --git a/android/assets/game.png b/android/assets/game.png index d6c1f2f54a..51dcb6b3b7 100644 Binary files a/android/assets/game.png and b/android/assets/game.png differ diff --git a/android/assets/jsons/translations/Dutch.properties b/android/assets/jsons/translations/Dutch.properties index 53470f3786..a5398d65a0 100644 --- a/android/assets/jsons/translations/Dutch.properties +++ b/android/assets/jsons/translations/Dutch.properties @@ -508,6 +508,7 @@ Bombard strength = Bombard sterkte Range = Bereik Move unit = Eenheid verplaatsen Stop movement = Verplaatsen stoppen +Swap units = Verwissel eenheden Construct improvement = Verbetering bouwen Automate = Automatiseren Stop automation = Stop automatiseren diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 0dc4f2f662..4c876bcc45 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -493,6 +493,7 @@ Bombard strength = Range = Move unit = Stop movement = +Swap units = Construct improvement = Automate = Stop automation = diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 7e6e0aa681..9df5e4b67b 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -61,6 +61,10 @@ class UnitMovementAlgorithms(val unit:MapUnit) { fun isUnknownTileWeShouldAssumeToBePassable(tileInfo: TileInfo) = !unit.civInfo.exploredTiles.contains(tileInfo.position) + /** + * Does not consider if tiles can actually be entered, use canMoveTo for that. + * If a tile can be reached within the turn, but it cannot be passed through, the total distance to it is set to unitMovement + */ fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn { val distanceToTiles = PathsToTilesWithinTurn() if (unitMovement == 0f) return distanceToTiles @@ -96,7 +100,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) { else totalDistanceToTile = unitMovement // In Civ V, you can always travel between adjacent tiles, even if you don't technically - // have enough movement points - it simple depletes what you have + // have enough movement points - it simply depletes what you have distanceToTiles[neighbor] = ParentTileAndTotalDistance(tileToCheck, totalDistanceToTile) } @@ -108,7 +112,10 @@ class UnitMovementAlgorithms(val unit:MapUnit) { return distanceToTiles } - /** Returns an empty list if there's no way to get there */ + /** + * Does not consider if the destination tile can actually be entered, use canMoveTo for that. + * Returns an empty list if there's no way to get to the destination. + */ fun getShortestPath(destination: TileInfo): List { val currentTile = unit.getTile() if (currentTile.position == destination) return listOf(currentTile) // edge case that's needed, so that workers will know that they can reach their own tile. *sigh* @@ -208,11 +215,73 @@ class UnitMovementAlgorithms(val unit:MapUnit) { /** This is performance-heavy - use as last resort, only after checking everything else! */ fun canReach(destination: TileInfo): Boolean { + if (unit.type.isAirUnit() || unit.action == Constants.unitActionParadrop) + return canReachInCurrentTurn(destination) + return getShortestPath(destination).any() + } + + fun canReachInCurrentTurn(destination: TileInfo): Boolean { if (unit.type.isAirUnit()) return unit.currentTile.aerialDistanceTo(destination) <= unit.getRange()*2 if (unit.action == Constants.unitActionParadrop) - return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination) - return getShortestPath(destination).any() + return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination) + return getDistanceToTiles().containsKey(destination) + } + + fun getReachableTilesInCurrentTurn(): Sequence { + return when { + unit.type.isAirUnit() -> + unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2)) + unit.action == Constants.unitActionParadrop -> + unit.getTile().getTilesInDistance(unit.paradropRange) + .filter { unit.movement.canParadropOn(it) } + else -> + unit.movement.getDistanceToTiles().keys.asSequence() + + } + } + + /** Returns whether we can perform a swap move to the specified tile */ + fun canUnitSwapTo(destination: TileInfo): Boolean { + return canReachInCurrentTurn(destination) && canUnitSwapToReachableTile(destination) + } + + /** Returns the tiles to which we can perform a swap move */ + fun getUnitSwappableTiles(): Sequence { + return getReachableTilesInCurrentTurn().filter { canUnitSwapToReachableTile(it) } + } + + /** + * Returns whether we can perform a unit swap move to the specified tile, given that it is + * reachable in the current turn + */ + private fun canUnitSwapToReachableTile(reachableTile: TileInfo): Boolean { + // Air units cannot swap + if (unit.type.isAirUnit()) return false + // We can't swap with ourself + if (reachableTile == unit.getTile()) return false + // Check whether the tile contains a unit of the same type as us that we own and that can + // also reach our tile in its current turn. + val otherUnit = ( + if (unit.type.isCivilian()) + reachableTile.civilianUnit + else + reachableTile.militaryUnit + ) ?: return false + val ourPosition = unit.getTile() + if (otherUnit.owner != unit.owner || !otherUnit.movement.canReachInCurrentTurn(ourPosition)) return false + // Check if we could enter their tile if they wouldn't be there + otherUnit.removeFromTile() + val weCanEnterTheirTile = canMoveTo(reachableTile) + otherUnit.putInTile(reachableTile) + if (!weCanEnterTheirTile) return false + // Check if they could enter our tile if we wouldn't be here + unit.removeFromTile() + val theyCanEnterOurTile = otherUnit.movement.canMoveTo(ourPosition) + unit.putInTile(ourPosition) + if (!theyCanEnterOurTile) return false + // All clear! + return true } @@ -249,7 +318,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) { unit.putInTile(destination) unit.currentMovement = 0f return - } else if (unit.action == Constants.unitActionParadrop) { // paratroopers move differently + } else if (unit.action == Constants.unitActionParadrop) { // paradropping units move differently unit.action = null unit.removeFromTile() unit.putInTile(destination) @@ -312,6 +381,29 @@ class UnitMovementAlgorithms(val unit:MapUnit) { } + /** + * Swaps this unit with the unit on the given tile + * Precondition: this unit can swap-move to the given tile, as determined by canUnitSwapTo + */ + fun swapMoveToTile(destination: TileInfo) { + val otherUnit = ( + if (unit.type.isCivilian()) + destination.civilianUnit + else + destination.militaryUnit + )!! // The precondition guarantees that there is an eligible same-type unit at the destination + + val ourOldPosition = unit.getTile() + val theirOldPosition = otherUnit.getTile() + + // Swap the units + otherUnit.removeFromTile() + unit.movement.moveToTile(destination) + unit.removeFromTile() + otherUnit.putInTile(theirOldPosition) + otherUnit.movement.moveToTile(ourOldPosition) + unit.putInTile(theirOldPosition) + } /** * Designates whether we can enter the tile - without attacking diff --git a/core/src/com/unciv/models/UnitAction.kt b/core/src/com/unciv/models/UnitAction.kt index b5103576c8..a011391e5c 100644 --- a/core/src/com/unciv/models/UnitAction.kt +++ b/core/src/com/unciv/models/UnitAction.kt @@ -9,6 +9,7 @@ data class UnitAction( ) enum class UnitActionType(val value: String) { + SwapUnits("Swap units"), Automate("Automate"), StopAutomation("Stop automation"), StopMovement("Stop movement"), diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index f4dc3a05e8..021b68020c 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -49,8 +49,12 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap continuousScrollingX = tileMap.mapParameters.worldWrap } - // Used to transfer data on the "move here" button that should be created, from the side thread to the main thread - class MoveHereButtonDto(val unitToTurnsToDestination: HashMap, val tileInfo: TileInfo) + // Interface for classes that contain the data required to draw a button + interface ButtonDto + // Contains the data required to draw a "move here" button + class MoveHereButtonDto(val unitToTurnsToDestination: HashMap, val tileInfo: TileInfo) : ButtonDto + // Contains the data required to draw a "swap with" button + class SwapWithButtonDto(val unit: MapUnit, val tileInfo: TileInfo) : ButtonDto internal fun addTiles() { val tileSetStrings = TileSetStrings() @@ -92,6 +96,14 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap thread { val tile = tileGroup.tileInfo + if (worldScreen.bottomUnitTable.selectedUnitIsSwapping) { + if (unit.movement.canUnitSwapTo(tile)) { + swapMoveUnitToTargetTile(unit, tile) + } + // If we are in unit-swapping mode, we don't want to move or attack + return@thread + } + val attackableTile = BattleHelper.getAttackableEnemies(unit, unit.movement.getDistanceToTiles()) .firstOrNull { it.tileToAttack == tileGroup.tileInfo } if (unit.canAttack() && attackableTile != null) { @@ -105,7 +117,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap moveUnitToTargetTile(listOf(unit), tile) return@thread } - } } }) @@ -128,17 +139,28 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap val unitTable = worldScreen.bottomUnitTable val previousSelectedUnits = unitTable.selectedUnits.toList() // create copy val previousSelectedCity = unitTable.selectedCity + val previousSelectedUnitIsSwapping = unitTable.selectedUnitIsSwapping unitTable.tileSelected(tileInfo) val newSelectedUnit = unitTable.selectedUnit if (previousSelectedUnits.isNotEmpty() && previousSelectedUnits.any { it.getTile() != tileInfo } && worldScreen.isPlayersTurn - && previousSelectedUnits.any { - it.movement.canMoveTo(tileInfo) || - it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.type.isAirUnit() - }) { - // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread - addTileOverlaysWithUnitMovement(previousSelectedUnits, tileInfo) + && ( + if (previousSelectedUnitIsSwapping) + previousSelectedUnits.first().movement.canUnitSwapTo(tileInfo) + else + previousSelectedUnits.any { + it.movement.canMoveTo(tileInfo) || + it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.type.isAirUnit() + } + )) { + if (previousSelectedUnitIsSwapping) { + addTileOverlaysWithUnitSwapping(previousSelectedUnits.first(), tileInfo) + } + else { + // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread + addTileOverlaysWithUnitMovement(previousSelectedUnits, tileInfo) + } } else addTileOverlays(tileInfo) // no unit movement but display the units in the tile etc. @@ -157,7 +179,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap } - fun moveUnitToTargetTile(selectedUnits: List, targetTile: TileInfo) { + private fun moveUnitToTargetTile(selectedUnits: List, targetTile: TileInfo) { // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread // THIS PART IS REALLY ANNOYING // So lets say you have 2 units you want to move in the same direction, right @@ -204,6 +226,20 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap } } + private fun swapMoveUnitToTargetTile(selectedUnit: MapUnit, targetTile: TileInfo) { + selectedUnit.movement.swapMoveToTile(targetTile) + + if (selectedUnit.action == Constants.unitActionExplore || selectedUnit.isMoving()) + selectedUnit.action = null // remove explore on manual swap-move + + // Perhaps something like a swish-swoosh would be clearer + Sounds.play(UncivSound.Whoosh) + + if (selectedUnit.currentMovement > 0) worldScreen.bottomUnitTable.selectUnit(selectedUnit) + + worldScreen.shouldUpdate = true + removeUnitActionOverlay() + } private fun addTileOverlaysWithUnitMovement(selectedUnits: List, tileInfo: TileInfo) { thread(name = "TurnsToGetThere") { @@ -250,15 +286,37 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap } worldScreen.shouldUpdate = true } - } } - private fun addTileOverlays(tileInfo: TileInfo, moveHereDto: MoveHereButtonDto? = null) { + private fun addTileOverlaysWithUnitSwapping(selectedUnit: MapUnit, tileInfo: TileInfo) { + if (!selectedUnit.movement.canUnitSwapTo(tileInfo)) { // give the regular tile overlays with no unit swapping + addTileOverlays(tileInfo) + worldScreen.shouldUpdate = true + return + } + if (UncivGame.Current.settings.singleTapMove) { + swapMoveUnitToTargetTile(selectedUnit, tileInfo) + } + else { + // Add "swap with" button + val swapWithButtonDto = SwapWithButtonDto(selectedUnit, tileInfo) + addTileOverlays(tileInfo, swapWithButtonDto) + } + worldScreen.shouldUpdate = true + } + + private fun addTileOverlays(tileInfo: TileInfo, buttonDto: ButtonDto? = null) { for (group in tileGroups[tileInfo]!!) { val table = Table().apply { defaults().pad(10f) } - if (moveHereDto != null && worldScreen.canChangeState) - table.add(getMoveHereButton(moveHereDto)) + if (buttonDto != null && worldScreen.canChangeState) + table.add( + when (buttonDto) { + is MoveHereButtonDto -> getMoveHereButton(buttonDto) + is SwapWithButtonDto -> getSwapWithButton(buttonDto) + else -> null + } + ) val unitList = ArrayList() if (tileInfo.isCityCenter() @@ -297,7 +355,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap val numberCircle = ImageGetter.getCircle().apply { width = size / 2; height = size / 2;color = Color.BLUE } moveHereButton.addActor(numberCircle) - moveHereButton.addActor(dto.unitToTurnsToDestination.values.maxOrNull()!!.toLabel().apply { center(numberCircle) }) val firstUnit = dto.unitToTurnsToDestination.keys.first() val unitIcon = if (dto.unitToTurnsToDestination.size == 1) UnitGroup(firstUnit, size / 2) @@ -319,6 +376,27 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap return moveHereButton } + private fun getSwapWithButton(dto: SwapWithButtonDto): Group { + val size = 60f + val swapWithButton = Group().apply { width = size;height = size; } + swapWithButton.addActor(ImageGetter.getCircle().apply { width = size; height = size }) + swapWithButton.addActor(ImageGetter.getImage("OtherIcons/Swap") + .apply { color = Color.BLACK; width = size / 2; height = size / 2; center(swapWithButton) }) + + val unitIcon = UnitGroup(dto.unit, size / 2) + unitIcon.y = size - unitIcon.height + swapWithButton.addActor(unitIcon) + + swapWithButton.onClick(UncivSound.Silent) { + UncivGame.Current.settings.addCompletedTutorialTask("Move unit") + if (dto.unit.type.isAirUnit()) + UncivGame.Current.settings.addCompletedTutorialTask("Move an air unit") + swapMoveUnitToTargetTile(dto.unit, dto.tileInfo) + } + + return swapWithButton + } + private fun addOverlayOnTileGroup(group: TileGroup, actor: Actor) { @@ -396,16 +474,32 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap group.selectUnit(unit) } + // Fade out less relevant images if a military unit is selected + val fadeout = if (unit.type.isCivilian()) 1f + else 0.5f + for (tile in allWorldTileGroups) { + if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout + if (tile.icons.improvementIcon != null && tile.tileInfo.improvement != Constants.barbarianEncampment + && tile.tileInfo.improvement != Constants.ancientRuins) + tile.icons.improvementIcon!!.color.a = fadeout + if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout + } + + if (worldScreen.bottomUnitTable.selectedUnitIsSwapping) { + val unitSwappableTiles = unit.movement.getUnitSwappableTiles() + val swapUnitsTileOverlayColor = Color.PURPLE + for (tile in unitSwappableTiles) { + for (tileToColor in tileGroups[tile]!!) { + tileToColor.showCircle(swapUnitsTileOverlayColor, + if (UncivGame.Current.settings.singleTapMove) 0.7f else 0.3f) + } + } + return // We don't want to show normal movement or attack overlays in unit-swapping mode + } + val isAirUnit = unit.type.isAirUnit() val moveTileOverlayColor = if (unit.action == Constants.unitActionParadrop) Color.BLUE else Color.WHITE - val tilesInMoveRange = - if (isAirUnit) - unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2)) - else if (unit.action == Constants.unitActionParadrop) - unit.getTile().getTilesInDistance(unit.paradropRange) - .filter { unit.movement.canParadropOn(it) } - else - unit.movement.getDistanceToTiles().keys.asSequence() + val tilesInMoveRange = unit.movement.getReachableTilesInCurrentTurn() for (tile in tilesInMoveRange) { for (tileToColor in tileGroups[tile]!!) { @@ -450,18 +544,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap else Color.RED ) } - - } - - // Fade out less relevant images if a military unit is selected - val fadeout = if (unit.type.isCivilian()) 1f - else 0.5f - for (tile in allWorldTileGroups) { - if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout - if (tile.icons.improvementIcon != null && tile.tileInfo.improvement != Constants.barbarianEncampment - && tile.tileInfo.improvement != Constants.ancientRuins) - tile.icons.improvementIcon!!.color.a = fadeout - if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout } } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 6283110e9a..077b37d992 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -47,6 +47,7 @@ object UnitActions { ) } + addSwapAction(unit, actionList, worldScreen) addExplorationActions(unit, actionList) addPromoteAction(unit, actionList) addUnitUpgradeAction(unit, actionList) @@ -64,6 +65,27 @@ object UnitActions { return actionList } + private fun addSwapAction(unit: MapUnit, actionList: ArrayList, worldScreen: WorldScreen) { + // Air units cannot swap + if (unit.type.isAirUnit()) return + // Disable unit swapping if multiple units are selected. It would make little sense. + // In principle, the unit swapping mode /will/ function with multiselect: it will simply + // only consider the first selected unit, and ignore the other selections. However, it does + // have the visual bug that the tile overlays for the eligible swap locations are drawn for + // /all/ selected units instead of only the first one. This could be fixed, but again, + // swapping makes little sense for multiselect anyway. + if (worldScreen.bottomUnitTable.selectedUnits.count() > 1) return + // Only show the swap action if there is at least one possible swap movement + if (unit.movement.getUnitSwappableTiles().none()) return + actionList += UnitAction( + type = UnitActionType.SwapUnits, + isCurrentAction = worldScreen.bottomUnitTable.selectedUnitIsSwapping, + action = { + worldScreen.bottomUnitTable.selectedUnitIsSwapping = !worldScreen.bottomUnitTable.selectedUnitIsSwapping + worldScreen.shouldUpdate = true + } + ) + } private fun addDisbandAction(actionList: ArrayList, unit: MapUnit, worldScreen: WorldScreen) { actionList += UnitAction(type = UnitActionType.DisbandUnit, action = { diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt index ac9b87906e..f65451f7fe 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt @@ -40,6 +40,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() { else -> when (unitAction) { "Move unit" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement")) "Stop movement" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement").apply { color = Color.RED }, '.') + "Swap units" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Swap"), 'y') "Promote" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star").apply { color = Color.GOLD }, 'o') "Construct improvement" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.worker), 'i') "Automate" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'm') diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 04e2af0617..9108f2cfc3 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -32,12 +32,15 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ /** This is in preparation for multi-select and multi-move */ val selectedUnits = ArrayList() + // Whether the (first) selected unit is in unit-swapping mode + var selectedUnitIsSwapping = false /** Sending no unit clears the selected units entirely */ fun selectUnit(unit:MapUnit?=null, append:Boolean=false) { if (!append) selectedUnits.clear() selectedCity = null if (unit != null) selectedUnits.add(unit) + selectedUnitIsSwapping = false } var selectedCity : CityInfo? = null @@ -223,13 +226,16 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ val previouslySelectedUnit = selectedUnit val previousNumberOfSelectedUnits = selectedUnits.size + // Do not select a different unit or city center if we click on it to swap our current unit to it + if (selectedUnitIsSwapping && selectedUnit != null && selectedUnit!!.movement.canUnitSwapTo(selectedTile)) return + if (selectedTile.isCityCenter() && (selectedTile.getOwner() == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) { citySelected(selectedTile.getCity()!!) } else if (selectedTile.militaryUnit != null && (selectedTile.militaryUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator()) && selectedTile.militaryUnit!! !in selectedUnits - && (selectedTile.civilianUnit == null || selectedUnit != selectedTile.civilianUnit)) { + && (selectedTile.civilianUnit == null || selectedUnit != selectedTile.civilianUnit)) { // Only select the military unit there if we do not currently have the civilian unit selected selectUnit(selectedTile.militaryUnit!!, Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT)) } else if (selectedTile.civilianUnit != null && (selectedTile.civilianUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator()) diff --git a/docs/Credits.md b/docs/Credits.md index 7e8c9f5683..e29d5eec48 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -509,6 +509,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Circle](https://thenounproject.com/term/circle/1841891/) By Aybige * [Arrow](https://thenounproject.com/term/arrow/18123/) By Joe Mortell for movement +* [Swap](https://thenounproject.com/search/?q=swap&i=1259600) By iconomania for swapping units * [Connection](https://thenounproject.com/search/?q=connection&i=1521886) By Travis Avery * [Skull](https://thenounproject.com/search/?q=Skull&i=1030702) By Vladimir Belochkin for disbanding units * [Crosshair](https://thenounproject.com/search/?q=crosshairs&i=916030) By Bakunetsu Kaito for selecting enemies to attack