Merge branch 'main' into keys-wrongly-displayed
@@ -138,7 +138,6 @@ jobs:
|
||||
CDN_BASE: ${{ vars.CDN_BASE }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }}
|
||||
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
SERVER_HOST_MASTERS: ${{ secrets.SERVER_HOST_MASTERS }}
|
||||
SERVER_HOST_FALK2: ${{ secrets.SERVER_HOST_FALK2 }}
|
||||
|
||||
@@ -13,4 +13,4 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: toshimaru/auto-author-assign@4d585cc37690897bd9015942ed6e766aa7cdb97f
|
||||
- uses: toshimaru/auto-author-assign@bdd7688cbf9e6d5683f02f8c7d8ae4062a254b6d
|
||||
|
||||
@@ -71,7 +71,6 @@ jobs:
|
||||
IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }}
|
||||
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
SERVER_HOST_STAGING: ${{ secrets.SERVER_HOST_STAGING }}
|
||||
SSH_KEY: ~/.ssh/id_rsa
|
||||
@@ -122,7 +121,6 @@ jobs:
|
||||
IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }}
|
||||
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
SERVER_HOST_FALK2: ${{ secrets.SERVER_HOST_FALK2 }}
|
||||
SSH_KEY: ~/.ssh/id_rsa
|
||||
@@ -173,7 +171,6 @@ jobs:
|
||||
IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }}
|
||||
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
SERVER_HOST_FALK2: ${{ secrets.SERVER_HOST_FALK2 }}
|
||||
SSH_KEY: ~/.ssh/id_rsa
|
||||
@@ -224,7 +221,6 @@ jobs:
|
||||
IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }}
|
||||
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
SERVER_HOST_FALK2: ${{ secrets.SERVER_HOST_FALK2 }}
|
||||
SSH_KEY: ~/.ssh/id_rsa
|
||||
|
||||
@@ -101,7 +101,6 @@ All new features and bug fixes should include relevant tests. We use **Vitest**.
|
||||
## Submitting a Pull Request
|
||||
|
||||
1. **Commit your changes**:
|
||||
|
||||
- Write clear, concise commit messages.
|
||||
- Use the present tense ("Add feature" not "Added feature").
|
||||
|
||||
|
||||
@@ -181,7 +181,6 @@ Feel free to ask questions in the translation Discord server!
|
||||
To ensure code quality and project stability, we use a progressive contribution system:
|
||||
|
||||
1. **New Contributors**: Limited to UI improvements and small bug fixes only
|
||||
|
||||
- This helps you become familiar with the codebase
|
||||
- UI changes are easier to review and less likely to break core functionality
|
||||
- Small, focused PRs have a higher chance of being accepted
|
||||
@@ -193,20 +192,17 @@ To ensure code quality and project stability, we use a progressive contribution
|
||||
### How to Contribute Successfully
|
||||
|
||||
1. **Before Starting Work**:
|
||||
|
||||
- Open an issue describing what you want to contribute
|
||||
- Wait for maintainer feedback before investing significant time
|
||||
- Small improvements can proceed directly to PR stage
|
||||
|
||||
2. **Code Quality Requirements**:
|
||||
|
||||
- All code must be well-commented and follow existing style patterns
|
||||
- New features should not break existing functionality
|
||||
- Code should be thoroughly tested before submission
|
||||
- All code changes in src/core _MUST_ be tested.
|
||||
|
||||
3. **Pull Request Process**:
|
||||
|
||||
- Keep PRs focused on a single feature or bug fix
|
||||
- Include screenshots for UI changes
|
||||
- Describe what testing you've performed
|
||||
|
||||
@@ -134,7 +134,6 @@ ENV=$ENV
|
||||
HOST=$HOST
|
||||
GHCR_IMAGE=$GHCR_IMAGE
|
||||
GHCR_TOKEN=$GHCR_TOKEN
|
||||
TURNSTILE_SECRET_KEY=$TURNSTILE_SECRET_KEY
|
||||
API_KEY=$API_KEY
|
||||
DOMAIN=$DOMAIN
|
||||
SUBDOMAIN=$SUBDOMAIN
|
||||
|
||||
@@ -238,6 +238,11 @@
|
||||
class="hidden w-full h-full page-content relative z-50"
|
||||
></troubleshooting-modal>
|
||||
|
||||
<clan-modal
|
||||
id="page-clan"
|
||||
inline
|
||||
class="hidden w-full h-full page-content relative z-50"
|
||||
></clan-modal>
|
||||
<account-modal
|
||||
id="page-account"
|
||||
inline
|
||||
|
||||
|
Before Width: | Height: | Size: 314 KiB After Width: | Height: | Size: 221 KiB |
@@ -2,64 +2,114 @@
|
||||
"name": "Bosphorus Straits",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [520, 300],
|
||||
"name": "Istanbul",
|
||||
"coordinates": [564, 245],
|
||||
"name": "Beykoz",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [360, 280],
|
||||
"name": "Thrace",
|
||||
"coordinates": [820, 209],
|
||||
"name": "Sile",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [220, 260],
|
||||
"name": "Edirne",
|
||||
"coordinates": [700, 316],
|
||||
"name": "Çekmeköy",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [650, 360],
|
||||
"name": "Bursa",
|
||||
"coordinates": [800, 438],
|
||||
"name": "Pendik",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [690, 290],
|
||||
"name": "Izmit",
|
||||
"coordinates": [797, 566],
|
||||
"name": "Tuzla",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [430, 430],
|
||||
"name": "Canakkale",
|
||||
"coordinates": [486, 381],
|
||||
"name": "Üsküdar",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [320, 330],
|
||||
"name": "Tekirdag",
|
||||
"coordinates": [534, 425],
|
||||
"name": "Kadıköy",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [610, 320],
|
||||
"name": "Yalova",
|
||||
"coordinates": [559, 568],
|
||||
"name": "Adalar",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [720, 260],
|
||||
"coordinates": [635, 500],
|
||||
"name": "Maltepe",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [701, 423],
|
||||
"name": "Sancaktepe",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [424, 394],
|
||||
"name": "Fatih",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [128, 46],
|
||||
"name": "Arnavutköy",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [965, 544],
|
||||
"name": "Kocaeli",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [160, 120],
|
||||
"name": "Varna",
|
||||
"flag": "bg"
|
||||
"coordinates": [42, 325],
|
||||
"name": "Büyükçekmece",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [220, 150],
|
||||
"name": "Burgas",
|
||||
"flag": "bg"
|
||||
"coordinates": [336, 175],
|
||||
"name": "Eyüpsultan",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [820, 470],
|
||||
"name": "Aegean Isles",
|
||||
"flag": "gr"
|
||||
"coordinates": [459, 157],
|
||||
"name": "Sarıyer",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [477, 297],
|
||||
"name": "Beşiktaş",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [171, 379],
|
||||
"name": "Avcılar",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [308, 412],
|
||||
"name": "Bakırköy",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [263, 283],
|
||||
"name": "Başakşehir",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [402, 272],
|
||||
"name": "Sultangazi",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [130, 270],
|
||||
"name": "Esenyurt",
|
||||
"flag": "tr"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [2260, 400],
|
||||
"flag": "west_germany",
|
||||
"flag": "de",
|
||||
"name": "Jermaine"
|
||||
},
|
||||
{
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2050, 460],
|
||||
"flag": "Fascist Spain",
|
||||
"flag": "es",
|
||||
"name": "Splain"
|
||||
},
|
||||
{
|
||||
@@ -33,12 +33,12 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2342, 436],
|
||||
"flag": "Communist Romania",
|
||||
"flag": "ro",
|
||||
"name": "Rollandia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1222, 310],
|
||||
"flag": "Ukrainian SSR",
|
||||
"flag": "ua",
|
||||
"name": "Ucryin"
|
||||
},
|
||||
{
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2555, 475],
|
||||
"flag": "Georgian SSR",
|
||||
"flag": "ge",
|
||||
"name": "Georgia"
|
||||
},
|
||||
{
|
||||
@@ -93,7 +93,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [1260, 400],
|
||||
"flag": "Kazakh SSR",
|
||||
"flag": "kz",
|
||||
"name": "Azakah"
|
||||
},
|
||||
{
|
||||
@@ -233,7 +233,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2950, 1150],
|
||||
"flag": "Apartheid South Africa",
|
||||
"flag": "za",
|
||||
"name": "Southern African State"
|
||||
},
|
||||
{
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2860, 1050],
|
||||
"flag": "Apartheid South Africa",
|
||||
"flag": "za",
|
||||
"name": "Southern South West Africa"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [634, 781],
|
||||
"name": "Republic of Ireland",
|
||||
"name": "Ireland",
|
||||
"flag": "ie"
|
||||
},
|
||||
{
|
||||
@@ -12,78 +12,78 @@
|
||||
"flag": "gb-eng"
|
||||
},
|
||||
{
|
||||
"coordinates": [935, 1289],
|
||||
"name": "Kingdom of Spain",
|
||||
"coordinates": [874, 1286],
|
||||
"name": "Spain",
|
||||
"flag": "es"
|
||||
},
|
||||
{
|
||||
"coordinates": [1087, 931],
|
||||
"name": "French Republic",
|
||||
"coordinates": [1087, 983],
|
||||
"name": "France",
|
||||
"flag": "fr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1541, 1180],
|
||||
"name": "Italian Republic",
|
||||
"name": "Italy",
|
||||
"flag": "it"
|
||||
},
|
||||
{
|
||||
"coordinates": [1339, 983],
|
||||
"name": "Swiss Confederation",
|
||||
"coordinates": [1339, 997],
|
||||
"name": "Switzerland",
|
||||
"flag": "ch"
|
||||
},
|
||||
{
|
||||
"coordinates": [1360, 428],
|
||||
"name": "Kingdom of Norway",
|
||||
"name": "Norway",
|
||||
"flag": "no"
|
||||
},
|
||||
{
|
||||
"coordinates": [1605, 573],
|
||||
"name": "Kingdom of Sweden",
|
||||
"coordinates": [1609, 477],
|
||||
"name": "Sweden",
|
||||
"flag": "se"
|
||||
},
|
||||
{
|
||||
"coordinates": [2007, 309],
|
||||
"name": "Republic of Finland",
|
||||
"coordinates": [2032, 346],
|
||||
"name": "Finland",
|
||||
"flag": "fi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1200, 830],
|
||||
"name": "Kingdom of Belgium",
|
||||
"coordinates": [1182, 836],
|
||||
"name": "Belgium",
|
||||
"flag": "be"
|
||||
},
|
||||
{
|
||||
"coordinates": [1264, 752],
|
||||
"name": "Kingdom of the Netherlands",
|
||||
"coordinates": [1268, 764],
|
||||
"name": "Netherlands",
|
||||
"flag": "nl"
|
||||
},
|
||||
{
|
||||
"coordinates": [1443, 798],
|
||||
"name": "Federal Republic of Germany",
|
||||
"coordinates": [1445, 821],
|
||||
"name": "Germany",
|
||||
"flag": "de"
|
||||
},
|
||||
{
|
||||
"coordinates": [1444, 969],
|
||||
"name": "Republic of Austria",
|
||||
"coordinates": [1507, 959],
|
||||
"name": "Austria",
|
||||
"flag": "at"
|
||||
},
|
||||
{
|
||||
"coordinates": [1850, 810],
|
||||
"name": "Republic of Poland",
|
||||
"coordinates": [1751, 772],
|
||||
"name": "Poland",
|
||||
"flag": "pl"
|
||||
},
|
||||
{
|
||||
"coordinates": [1630, 909],
|
||||
"name": "Czech Republic",
|
||||
"coordinates": [1618, 874],
|
||||
"name": "Czechia",
|
||||
"flag": "cz"
|
||||
},
|
||||
{
|
||||
"coordinates": [2342, 936],
|
||||
"coordinates": [2231, 885],
|
||||
"name": "Ukraine",
|
||||
"flag": "ua"
|
||||
},
|
||||
{
|
||||
"coordinates": [2167, 708],
|
||||
"name": "Republic of Belarus",
|
||||
"coordinates": [2206, 676],
|
||||
"name": "Belarus",
|
||||
"flag": "by"
|
||||
},
|
||||
{
|
||||
@@ -92,28 +92,28 @@
|
||||
"flag": "ro"
|
||||
},
|
||||
{
|
||||
"coordinates": [2432, 1265],
|
||||
"name": "Republic of Turkiye",
|
||||
"coordinates": [2334, 1313],
|
||||
"name": "Türkiye",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [769, 1535],
|
||||
"name": "Kingdom of Morocco",
|
||||
"name": "Morocco",
|
||||
"flag": "ma"
|
||||
},
|
||||
{
|
||||
"coordinates": [2535, 720],
|
||||
"name": "Russian Federation",
|
||||
"coordinates": [2591, 610],
|
||||
"name": "Russia",
|
||||
"flag": "ru"
|
||||
},
|
||||
{
|
||||
"coordinates": [2539, 1455],
|
||||
"name": "Syrian Arab Republic",
|
||||
"coordinates": [2538, 1418],
|
||||
"name": "Syria",
|
||||
"flag": "sy"
|
||||
},
|
||||
{
|
||||
"coordinates": [2689, 1441],
|
||||
"name": "Republic of Iraq",
|
||||
"name": "Iraq",
|
||||
"flag": "iq"
|
||||
},
|
||||
{
|
||||
@@ -122,8 +122,8 @@
|
||||
"flag": "ge"
|
||||
},
|
||||
{
|
||||
"coordinates": [1389, 1473],
|
||||
"name": "Republic of Tunisia",
|
||||
"coordinates": [1359, 1473],
|
||||
"name": "Tunisia",
|
||||
"flag": "tn"
|
||||
},
|
||||
{
|
||||
@@ -133,62 +133,62 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [680, 1254],
|
||||
"name": "Portuguese Republic",
|
||||
"name": "Portugal",
|
||||
"flag": "pt"
|
||||
},
|
||||
{
|
||||
"coordinates": [1891, 1299],
|
||||
"name": "Hellenic Republic",
|
||||
"name": "Greece",
|
||||
"flag": "gr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1906, 1113],
|
||||
"name": "Republic of Serbia",
|
||||
"name": "Serbia",
|
||||
"flag": "rs"
|
||||
},
|
||||
{
|
||||
"coordinates": [1751, 983],
|
||||
"name": "Republic of Hungary",
|
||||
"name": "Hungary",
|
||||
"flag": "hu"
|
||||
},
|
||||
{
|
||||
"coordinates": [1784, 908],
|
||||
"name": "Slovak Republic",
|
||||
"coordinates": [1826, 902],
|
||||
"name": "Slovakia",
|
||||
"flag": "sk"
|
||||
},
|
||||
{
|
||||
"coordinates": [1624, 1038],
|
||||
"name": "Republic of Croatia",
|
||||
"name": "Croatia",
|
||||
"flag": "hr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1734, 1094],
|
||||
"coordinates": [1738, 1107],
|
||||
"name": "Bosnia and Herzegovina",
|
||||
"flag": "ba"
|
||||
},
|
||||
{
|
||||
"coordinates": [1817, 1213],
|
||||
"name": "Republic of Albania",
|
||||
"name": "Albania",
|
||||
"flag": "al"
|
||||
},
|
||||
{
|
||||
"coordinates": [2092, 1158],
|
||||
"name": "Republic of Bulgaria",
|
||||
"name": "Bulgaria",
|
||||
"flag": "bg"
|
||||
},
|
||||
{
|
||||
"coordinates": [1939, 702],
|
||||
"name": "Republic of Lithuania",
|
||||
"coordinates": [1958, 704],
|
||||
"name": "Lithuania",
|
||||
"flag": "lt"
|
||||
},
|
||||
{
|
||||
"coordinates": [2014, 618],
|
||||
"name": "Republic of Latvia",
|
||||
"coordinates": [2030, 617],
|
||||
"name": "Latvia",
|
||||
"flag": "lv"
|
||||
},
|
||||
{
|
||||
"coordinates": [2033, 504],
|
||||
"name": "Republic of Estonia",
|
||||
"coordinates": [2052, 516],
|
||||
"name": "Estonia",
|
||||
"flag": "ee"
|
||||
},
|
||||
{
|
||||
@@ -197,14 +197,14 @@
|
||||
"flag": "gb-wls"
|
||||
},
|
||||
{
|
||||
"coordinates": [863, 573],
|
||||
"coordinates": [841, 603],
|
||||
"name": "Scotland",
|
||||
"flag": "gb-sct"
|
||||
},
|
||||
{
|
||||
"coordinates": [2688, 427],
|
||||
"name": "USSR",
|
||||
"flag": "ussr"
|
||||
"coordinates": [2890, 857],
|
||||
"name": "Kazakhstan",
|
||||
"flag": "kz"
|
||||
},
|
||||
{
|
||||
"coordinates": [719, 685],
|
||||
@@ -212,44 +212,54 @@
|
||||
"flag": "northern_ireland"
|
||||
},
|
||||
{
|
||||
"coordinates": [2011, 103],
|
||||
"name": "Polar Bears",
|
||||
"flag": "polar_bears"
|
||||
"coordinates": [1900, 132],
|
||||
"name": "Sápmi",
|
||||
"flag": "Sami flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1369, 628],
|
||||
"name": "Kingdom of Denmark",
|
||||
"coordinates": [1377, 625],
|
||||
"name": "Denmark",
|
||||
"flag": "dk"
|
||||
},
|
||||
{
|
||||
"coordinates": [2406, 1638],
|
||||
"name": "State of Israel",
|
||||
"name": "Israel",
|
||||
"flag": "il"
|
||||
},
|
||||
{
|
||||
"coordinates": [2226, 1661],
|
||||
"name": "Arab Republic of Egypt",
|
||||
"name": "Egypt",
|
||||
"flag": "eg"
|
||||
},
|
||||
{
|
||||
"coordinates": [1847, 1652],
|
||||
"name": "State of Libya",
|
||||
"name": "Libya",
|
||||
"flag": "ly"
|
||||
},
|
||||
{
|
||||
"coordinates": [2571, 1601],
|
||||
"name": "Hashemite Kingdom of Jordan",
|
||||
"coordinates": [2535, 1609],
|
||||
"name": "Jordan",
|
||||
"flag": "jo"
|
||||
},
|
||||
{
|
||||
"coordinates": [2473, 1528],
|
||||
"name": "Lebanese Republic",
|
||||
"name": "Lebanon",
|
||||
"flag": "lb"
|
||||
},
|
||||
{
|
||||
"coordinates": [254, 274],
|
||||
"coordinates": [266, 265],
|
||||
"name": "Iceland",
|
||||
"flag": "is"
|
||||
},
|
||||
{
|
||||
"coordinates": [1045, 1188],
|
||||
"name": "Andorra",
|
||||
"flag": "ad"
|
||||
},
|
||||
{
|
||||
"coordinates": [1290, 1121],
|
||||
"name": "Monaco",
|
||||
"flag": "mc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
"flag": "ie"
|
||||
},
|
||||
{
|
||||
"coordinates": [650, 500],
|
||||
"coordinates": [650, 477],
|
||||
"name": "United Kingdom",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [560, 800],
|
||||
"coordinates": [612, 809],
|
||||
"name": "Spain",
|
||||
"flag": "es"
|
||||
},
|
||||
{
|
||||
"coordinates": [726, 616],
|
||||
"coordinates": [729, 648],
|
||||
"name": "France",
|
||||
"flag": "fr"
|
||||
},
|
||||
@@ -32,32 +32,32 @@
|
||||
"flag": "it"
|
||||
},
|
||||
{
|
||||
"coordinates": [872, 634],
|
||||
"coordinates": [895, 641],
|
||||
"name": "Switzerland",
|
||||
"flag": "ch"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 271],
|
||||
"coordinates": [935, 259],
|
||||
"name": "Norway",
|
||||
"flag": "no"
|
||||
},
|
||||
{
|
||||
"coordinates": [1095, 336],
|
||||
"coordinates": [1105, 286],
|
||||
"name": "Sweden",
|
||||
"flag": "se"
|
||||
},
|
||||
{
|
||||
"coordinates": [1403, 235],
|
||||
"coordinates": [1438, 209],
|
||||
"name": "Finland",
|
||||
"flag": "fi"
|
||||
},
|
||||
{
|
||||
"coordinates": [775, 541],
|
||||
"coordinates": [819, 534],
|
||||
"name": "Belgium",
|
||||
"flag": "be"
|
||||
},
|
||||
{
|
||||
"coordinates": [868, 487],
|
||||
"coordinates": [868, 485],
|
||||
"name": "Netherlands",
|
||||
"flag": "nl"
|
||||
},
|
||||
@@ -72,13 +72,13 @@
|
||||
"flag": "at"
|
||||
},
|
||||
{
|
||||
"coordinates": [1120, 477],
|
||||
"coordinates": [1220, 491],
|
||||
"name": "Poland",
|
||||
"flag": "pl"
|
||||
},
|
||||
{
|
||||
"coordinates": [1060, 530],
|
||||
"name": "Czech Republic",
|
||||
"coordinates": [1078, 564],
|
||||
"name": "Czechia",
|
||||
"flag": "cz"
|
||||
},
|
||||
{
|
||||
@@ -87,18 +87,18 @@
|
||||
"flag": "ua"
|
||||
},
|
||||
{
|
||||
"coordinates": [1500, 440],
|
||||
"coordinates": [1517, 424],
|
||||
"name": "Belarus",
|
||||
"flag": "by"
|
||||
},
|
||||
{
|
||||
"coordinates": [1400, 670],
|
||||
"coordinates": [1414, 667],
|
||||
"name": "Romania",
|
||||
"flag": "ro"
|
||||
},
|
||||
{
|
||||
"coordinates": [1580, 834],
|
||||
"name": "Turkey",
|
||||
"coordinates": [1614, 834],
|
||||
"name": "Türkiye",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
@@ -107,13 +107,13 @@
|
||||
"flag": "ma"
|
||||
},
|
||||
{
|
||||
"coordinates": [1674, 449],
|
||||
"coordinates": [1771, 413],
|
||||
"name": "Russia",
|
||||
"flag": "ru"
|
||||
},
|
||||
{
|
||||
"coordinates": [1750, 950],
|
||||
"name": "Syrian Arab Republic",
|
||||
"name": "Syria",
|
||||
"flag": "sy"
|
||||
},
|
||||
{
|
||||
@@ -152,9 +152,39 @@
|
||||
"flag": "rs"
|
||||
},
|
||||
{
|
||||
"coordinates": [1200, 630],
|
||||
"coordinates": [1218, 602],
|
||||
"name": "Hungary",
|
||||
"flag": "hu"
|
||||
},
|
||||
{
|
||||
"coordinates": [1277, 90],
|
||||
"name": "Sápmi",
|
||||
"flag": "Sami flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1406, 324],
|
||||
"name": "Estonia",
|
||||
"flag": "ee"
|
||||
},
|
||||
{
|
||||
"coordinates": [1380, 384],
|
||||
"name": "Latvia",
|
||||
"flag": "lv"
|
||||
},
|
||||
{
|
||||
"coordinates": [1355, 444],
|
||||
"name": "Lithuania",
|
||||
"flag": "lt"
|
||||
},
|
||||
{
|
||||
"coordinates": [1121, 668],
|
||||
"name": "Croatia",
|
||||
"flag": "hr"
|
||||
},
|
||||
{
|
||||
"coordinates": [959, 394],
|
||||
"name": "Denmark",
|
||||
"flag": "dk"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,154 +2,139 @@
|
||||
"name": "GatewayToTheAtlantic",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [2144, 344],
|
||||
"coordinates": [2161, 420],
|
||||
"name": "Swiss Confederation",
|
||||
"flag": "ch"
|
||||
},
|
||||
{
|
||||
"coordinates": [1964, 371],
|
||||
"name": "Kingdom of Burgundy",
|
||||
"coordinates": [1938, 263],
|
||||
"name": "Duchy of Burgundy",
|
||||
"flag": "burgundy"
|
||||
},
|
||||
{
|
||||
"coordinates": [1334, 537],
|
||||
"coordinates": [1388, 485],
|
||||
"name": "Duchy of Aquitaine",
|
||||
"flag": "aquitaine"
|
||||
},
|
||||
{
|
||||
"coordinates": [2115, 684],
|
||||
"coordinates": [2137, 636],
|
||||
"name": "County of Provence",
|
||||
"flag": "provence"
|
||||
},
|
||||
{
|
||||
"coordinates": [1207, 763],
|
||||
"name": "The Basque",
|
||||
"flag": "es-pv"
|
||||
"coordinates": [1266, 748],
|
||||
"name": "Kingdom of Navarre",
|
||||
"flag": "navarre"
|
||||
},
|
||||
{
|
||||
"coordinates": [1281, 1142],
|
||||
"coordinates": [1375, 1190],
|
||||
"name": "Kingdom of Valencia",
|
||||
"flag": "valencia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1660, 891],
|
||||
"coordinates": [1696, 858],
|
||||
"name": "Catalonia",
|
||||
"flag": "catalonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [561, 764],
|
||||
"coordinates": [543, 807],
|
||||
"name": "Kingdom of Galicia",
|
||||
"flag": "es-ga"
|
||||
},
|
||||
{
|
||||
"coordinates": [1004, 1436],
|
||||
"coordinates": [1128, 1388],
|
||||
"name": "Emirate of Granada",
|
||||
"flag": "granada"
|
||||
},
|
||||
{
|
||||
"coordinates": [431, 1197],
|
||||
"name": "Portuguese Republic",
|
||||
"flag": "pt"
|
||||
"coordinates": [523, 1156],
|
||||
"name": "Kingdom of Portugal",
|
||||
"flag": "kingdom_of_portugal"
|
||||
},
|
||||
{
|
||||
"coordinates": [560, 1894],
|
||||
"name": "Kingdom of Morocco",
|
||||
"flag": "ma"
|
||||
"coordinates": [851, 1805],
|
||||
"name": "Marinid Sultanate",
|
||||
"flag": "marinid"
|
||||
},
|
||||
{
|
||||
"coordinates": [1609, 1837],
|
||||
"name": "Algeria",
|
||||
"flag": "dz"
|
||||
"coordinates": [1424, 1725],
|
||||
"name": "Zayyanid Sultanate",
|
||||
"flag": "zayyanid"
|
||||
},
|
||||
{
|
||||
"coordinates": [1733, 622],
|
||||
"coordinates": [1604, 641],
|
||||
"name": "County of Armagnac",
|
||||
"flag": "armagnac"
|
||||
},
|
||||
{
|
||||
"coordinates": [896, 1240],
|
||||
"coordinates": [946, 1300],
|
||||
"name": "City of Cordoba",
|
||||
"flag": "cordoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [636, 1781],
|
||||
"name": "City of Seville",
|
||||
"flag": "seville"
|
||||
},
|
||||
{
|
||||
"coordinates": [750, 873],
|
||||
"coordinates": [740, 1021],
|
||||
"name": "Kingdom of Leon",
|
||||
"flag": "leon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1001, 882],
|
||||
"name": "Kingdom of Castille",
|
||||
"coordinates": [1040, 1036],
|
||||
"name": "Crown of Castile",
|
||||
"flag": "castille"
|
||||
},
|
||||
{
|
||||
"coordinates": [775, 724],
|
||||
"coordinates": [847, 767],
|
||||
"name": "Principality of Asturias",
|
||||
"flag": "asturias"
|
||||
},
|
||||
{
|
||||
"coordinates": [1755, 1130],
|
||||
"name": "The Old Ones",
|
||||
"flag": "neuragic_empire"
|
||||
"name": "Kingdom of Majorca",
|
||||
"flag": "majorca"
|
||||
},
|
||||
{
|
||||
"coordinates": [2097, 1670],
|
||||
"name": "Tamazgha",
|
||||
"flag": "Amazigh flag"
|
||||
"coordinates": [2004, 1630],
|
||||
"name": "Hafsid Sultanate",
|
||||
"flag": "hafsid"
|
||||
},
|
||||
{
|
||||
"coordinates": [979, 1013],
|
||||
"name": "Kingdom of Spain",
|
||||
"flag": "es"
|
||||
"coordinates": [1374, 926],
|
||||
"name": "Crown of Aragon",
|
||||
"flag": "catalonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [468, 930],
|
||||
"name": "Sardines",
|
||||
"flag": "sardines"
|
||||
},
|
||||
{
|
||||
"coordinates": [1667, 96],
|
||||
"coordinates": [1695, 119],
|
||||
"name": "City of Paris",
|
||||
"flag": "paris"
|
||||
},
|
||||
{
|
||||
"coordinates": [1716, 296],
|
||||
"name": "Baguettes",
|
||||
"flag": "baguette"
|
||||
},
|
||||
{
|
||||
"coordinates": [1017, 180],
|
||||
"coordinates": [1121, 221],
|
||||
"name": "Kingdom of Brittany",
|
||||
"flag": "brittany"
|
||||
},
|
||||
{
|
||||
"coordinates": [2072, 567],
|
||||
"name": "An Anti-Pope",
|
||||
"flag": "antipope"
|
||||
"coordinates": [1933, 614],
|
||||
"name": "City of Avignon",
|
||||
"flag": "avignon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1355, 76],
|
||||
"coordinates": [1434, 129],
|
||||
"name": "Duchy of Normandy",
|
||||
"flag": "normandy"
|
||||
},
|
||||
{
|
||||
"coordinates": [1402, 529],
|
||||
"name": "Wine",
|
||||
"flag": ""
|
||||
"coordinates": [1644, 383],
|
||||
"name": "Kingdom of France",
|
||||
"flag": "Franks"
|
||||
},
|
||||
{
|
||||
"coordinates": [1475, 1657],
|
||||
"name": "French Foreign Legion",
|
||||
"flag": "French foreign legion"
|
||||
"coordinates": [772, 1399],
|
||||
"name": "City of Seville",
|
||||
"flag": "seville"
|
||||
},
|
||||
{
|
||||
"coordinates": [1685, 417],
|
||||
"name": "French Republic",
|
||||
"flag": "fr"
|
||||
"coordinates": [2147, 90],
|
||||
"name": "Holy Roman Empire",
|
||||
"flag": "Holy Roman Empire"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,488 +3,538 @@
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [2309, 535],
|
||||
"flag": "tr",
|
||||
"name": "Türkiye"
|
||||
"name": "Türkiye",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [2030, 409],
|
||||
"flag": "west_germany",
|
||||
"name": "West Germany"
|
||||
},
|
||||
{
|
||||
"coordinates": [2074, 382],
|
||||
"flag": "east_germany",
|
||||
"name": "East Germany"
|
||||
"coordinates": [2050, 395],
|
||||
"name": "Germany",
|
||||
"flag": "de"
|
||||
},
|
||||
{
|
||||
"coordinates": [1966, 442],
|
||||
"flag": "fr",
|
||||
"name": "France"
|
||||
"name": "France",
|
||||
"flag": "fr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1872, 528],
|
||||
"flag": "Fascist Spain",
|
||||
"name": "Spain"
|
||||
"name": "Spain",
|
||||
"flag": "es"
|
||||
},
|
||||
{
|
||||
"coordinates": [2074, 498],
|
||||
"flag": "it",
|
||||
"name": "Italy"
|
||||
"name": "Italy",
|
||||
"flag": "it"
|
||||
},
|
||||
{
|
||||
"coordinates": [1912, 379],
|
||||
"flag": "gb",
|
||||
"name": "United Kingdom"
|
||||
"name": "United Kingdom",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [1841, 373],
|
||||
"flag": "ie",
|
||||
"name": "Ireland"
|
||||
"name": "Ireland",
|
||||
"flag": "ie"
|
||||
},
|
||||
{
|
||||
"coordinates": [2153, 378],
|
||||
"flag": "pl",
|
||||
"name": "Poland"
|
||||
"name": "Poland",
|
||||
"flag": "pl"
|
||||
},
|
||||
{
|
||||
"coordinates": [2178, 539],
|
||||
"flag": "gr",
|
||||
"name": "Greece"
|
||||
"name": "Greece",
|
||||
"flag": "gr"
|
||||
},
|
||||
{
|
||||
"coordinates": [2222, 493],
|
||||
"flag": "bg",
|
||||
"name": "Bulgaria"
|
||||
"name": "Bulgaria",
|
||||
"flag": "bg"
|
||||
},
|
||||
{
|
||||
"coordinates": [2135, 481],
|
||||
"flag": "yugoslavia",
|
||||
"name": "Yugoslavia"
|
||||
"name": "Serbia",
|
||||
"flag": "rs"
|
||||
},
|
||||
{
|
||||
"coordinates": [2242, 461],
|
||||
"flag": "Communist Romania",
|
||||
"name": "Romania"
|
||||
"name": "Romania",
|
||||
"flag": "ro"
|
||||
},
|
||||
{
|
||||
"coordinates": [2163, 441],
|
||||
"flag": "hu",
|
||||
"name": "Hungary"
|
||||
"name": "Hungary",
|
||||
"flag": "hu"
|
||||
},
|
||||
{
|
||||
"coordinates": [2272, 418],
|
||||
"flag": "Ukrainian SSR",
|
||||
"name": "Ukrainian SSR"
|
||||
"name": "Ukraine",
|
||||
"flag": "ua"
|
||||
},
|
||||
{
|
||||
"coordinates": [2093, 297],
|
||||
"flag": "se",
|
||||
"name": "Sweden"
|
||||
"name": "Sweden",
|
||||
"flag": "se"
|
||||
},
|
||||
{
|
||||
"coordinates": [2027, 282],
|
||||
"flag": "no",
|
||||
"name": "Norway"
|
||||
"name": "Norway",
|
||||
"flag": "no"
|
||||
},
|
||||
{
|
||||
"coordinates": [2191, 194],
|
||||
"flag": "Sami flag",
|
||||
"name": "Sapmi"
|
||||
"name": "Sápmi",
|
||||
"flag": "Sami flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [2206, 262],
|
||||
"flag": "fi",
|
||||
"name": "Finland"
|
||||
"name": "Finland",
|
||||
"flag": "fi"
|
||||
},
|
||||
{
|
||||
"coordinates": [2376, 363],
|
||||
"flag": "Russian SSR",
|
||||
"name": "Russian SSR"
|
||||
"name": "Russia",
|
||||
"flag": "ru"
|
||||
},
|
||||
{
|
||||
"coordinates": [2222, 371],
|
||||
"flag": "Byelorussian SSR",
|
||||
"name": "Byelorussian SSR"
|
||||
"name": "Belarus",
|
||||
"flag": "by"
|
||||
},
|
||||
{
|
||||
"coordinates": [2441, 507],
|
||||
"flag": "Georgian SSR",
|
||||
"name": "Georgian SSR"
|
||||
"name": "Georgia",
|
||||
"flag": "ge"
|
||||
},
|
||||
{
|
||||
"coordinates": [2402, 580],
|
||||
"flag": "Second Republic of Iraq",
|
||||
"name": "Iraq"
|
||||
"name": "Iraq",
|
||||
"flag": "iq"
|
||||
},
|
||||
{
|
||||
"coordinates": [2353, 595],
|
||||
"flag": "sy",
|
||||
"name": "Syria"
|
||||
"name": "Syria",
|
||||
"flag": "sy"
|
||||
},
|
||||
{
|
||||
"coordinates": [2414, 679],
|
||||
"flag": "sa",
|
||||
"name": "Saudi Arabia"
|
||||
"name": "Saudi Arabia",
|
||||
"flag": "sa"
|
||||
},
|
||||
{
|
||||
"coordinates": [2434, 815],
|
||||
"flag": "North yemen",
|
||||
"name": "North Yemen"
|
||||
},
|
||||
{
|
||||
"coordinates": [2479, 824],
|
||||
"flag": "south yemen",
|
||||
"name": "South Yemen"
|
||||
"coordinates": [2456, 820],
|
||||
"name": "Yemen",
|
||||
"flag": "ye"
|
||||
},
|
||||
{
|
||||
"coordinates": [2554, 724],
|
||||
"flag": "ae",
|
||||
"name": "United Arab Emirates"
|
||||
"name": "United Arab Emirates",
|
||||
"flag": "ae"
|
||||
},
|
||||
{
|
||||
"coordinates": [2532, 609],
|
||||
"flag": "Pahlavi Iran",
|
||||
"name": "Iran"
|
||||
"name": "Iran",
|
||||
"flag": "ir"
|
||||
},
|
||||
{
|
||||
"coordinates": [2683, 650],
|
||||
"flag": "pk",
|
||||
"name": "Pakistan"
|
||||
"name": "Pakistan",
|
||||
"flag": "pk"
|
||||
},
|
||||
{
|
||||
"coordinates": [2654, 580],
|
||||
"flag": "af",
|
||||
"name": "Afghanistan"
|
||||
"name": "Afghanistan",
|
||||
"flag": "af"
|
||||
},
|
||||
{
|
||||
"coordinates": [2727, 416],
|
||||
"flag": "Kazakh SSR",
|
||||
"name": "Kazakh SSR"
|
||||
"name": "Kazakhstan",
|
||||
"flag": "kz"
|
||||
},
|
||||
{
|
||||
"coordinates": [2556, 544],
|
||||
"flag": "Turkmen SSR",
|
||||
"name": "Turkmen SSR"
|
||||
"name": "Turkmenistan",
|
||||
"flag": "tm"
|
||||
},
|
||||
{
|
||||
"coordinates": [2947, 362],
|
||||
"flag": "Zheleznogorsk",
|
||||
"name": "Zheleznogorsk"
|
||||
},
|
||||
{
|
||||
"coordinates": [3252, 229],
|
||||
"flag": "Siberia",
|
||||
"name": "Siberia"
|
||||
"coordinates": [3255, 278],
|
||||
"name": "Siberia",
|
||||
"flag": "Siberia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2810, 744],
|
||||
"flag": "in",
|
||||
"name": "India"
|
||||
"name": "India",
|
||||
"flag": "in"
|
||||
},
|
||||
{
|
||||
"coordinates": [1717, 237],
|
||||
"flag": "is",
|
||||
"name": "Iceland"
|
||||
"name": "Iceland",
|
||||
"flag": "is"
|
||||
},
|
||||
{
|
||||
"coordinates": [2944, 709],
|
||||
"flag": "bd",
|
||||
"name": "Bangladesh"
|
||||
"name": "Bangladesh",
|
||||
"flag": "bd"
|
||||
},
|
||||
{
|
||||
"coordinates": [2868, 635],
|
||||
"flag": "np",
|
||||
"name": "Nepal"
|
||||
"coordinates": [2870, 656],
|
||||
"name": "Nepal",
|
||||
"flag": "np"
|
||||
},
|
||||
{
|
||||
"coordinates": [3254, 672],
|
||||
"flag": "cn",
|
||||
"name": "China"
|
||||
"coordinates": [3172, 624],
|
||||
"name": "China",
|
||||
"flag": "cn"
|
||||
},
|
||||
{
|
||||
"coordinates": [3373, 521],
|
||||
"flag": "kp",
|
||||
"name": "North Korea"
|
||||
"name": "North Korea",
|
||||
"flag": "kp"
|
||||
},
|
||||
{
|
||||
"coordinates": [3389, 573],
|
||||
"flag": "kr",
|
||||
"name": "South Korea"
|
||||
"name": "South Korea",
|
||||
"flag": "kr"
|
||||
},
|
||||
{
|
||||
"coordinates": [3515, 571],
|
||||
"flag": "jp",
|
||||
"name": "Japan"
|
||||
"name": "Japan",
|
||||
"flag": "jp"
|
||||
},
|
||||
{
|
||||
"coordinates": [3026, 457],
|
||||
"flag": "mn",
|
||||
"name": "Mongolia"
|
||||
"name": "Mongolia",
|
||||
"flag": "mn"
|
||||
},
|
||||
{
|
||||
"coordinates": [3229, 995],
|
||||
"flag": "id",
|
||||
"name": "Indonesia"
|
||||
"name": "Indonesia",
|
||||
"flag": "id"
|
||||
},
|
||||
{
|
||||
"coordinates": [3121, 755],
|
||||
"flag": "vn",
|
||||
"name": "North Vietnam"
|
||||
"coordinates": [3145, 802],
|
||||
"name": "Vietnam",
|
||||
"flag": "vn"
|
||||
},
|
||||
{
|
||||
"coordinates": [3153, 833],
|
||||
"flag": "South Vietnam",
|
||||
"name": "South Vietnam"
|
||||
"coordinates": [3020, 750],
|
||||
"name": "Myanmar",
|
||||
"flag": "mm"
|
||||
},
|
||||
{
|
||||
"coordinates": [3013, 722],
|
||||
"flag": "Burma2",
|
||||
"name": "Burma"
|
||||
},
|
||||
{
|
||||
"coordinates": [3095, 822],
|
||||
"flag": "kh",
|
||||
"name": "Cambodia"
|
||||
"coordinates": [3082, 822],
|
||||
"name": "Thailand",
|
||||
"flag": "th"
|
||||
},
|
||||
{
|
||||
"coordinates": [3538, 1067],
|
||||
"flag": "pg",
|
||||
"name": "Papua New Guinea"
|
||||
"name": "Papua New Guinea",
|
||||
"flag": "pg"
|
||||
},
|
||||
{
|
||||
"coordinates": [3542, 1356],
|
||||
"flag": "au",
|
||||
"name": "Australia"
|
||||
"coordinates": [3570, 1392],
|
||||
"name": "Australia",
|
||||
"flag": "au"
|
||||
},
|
||||
{
|
||||
"coordinates": [3422, 1203],
|
||||
"flag": "Australian Aboriginal Flag",
|
||||
"name": "Nawan-mirri"
|
||||
"coordinates": [3416, 1213],
|
||||
"name": "Nawan-mirri",
|
||||
"flag": "Australian Aboriginal Flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [3880, 1521],
|
||||
"flag": "nz",
|
||||
"name": "New Zealand"
|
||||
"name": "New Zealand",
|
||||
"flag": "nz"
|
||||
},
|
||||
{
|
||||
"coordinates": [2632, 1893],
|
||||
"flag": "aq",
|
||||
"name": "Antarctica"
|
||||
"name": "Antarctica",
|
||||
"flag": "aq"
|
||||
},
|
||||
{
|
||||
"coordinates": [2038, 590],
|
||||
"flag": "tn",
|
||||
"name": "Tunisia"
|
||||
"name": "Tunisia",
|
||||
"flag": "tn"
|
||||
},
|
||||
{
|
||||
"coordinates": [2116, 653],
|
||||
"flag": "ly",
|
||||
"name": "Libya"
|
||||
"coordinates": [2138, 675],
|
||||
"name": "Libya",
|
||||
"flag": "ly"
|
||||
},
|
||||
{
|
||||
"coordinates": [2281, 653],
|
||||
"flag": "United Arab Republic",
|
||||
"name": "United Arab Republic"
|
||||
"name": "Egypt",
|
||||
"flag": "eg"
|
||||
},
|
||||
{
|
||||
"coordinates": [1859, 613],
|
||||
"flag": "ma",
|
||||
"name": "Morocco"
|
||||
"name": "Morocco",
|
||||
"flag": "ma"
|
||||
},
|
||||
{
|
||||
"coordinates": [1943, 615],
|
||||
"flag": "dz",
|
||||
"name": "Algeria"
|
||||
"coordinates": [1952, 671],
|
||||
"name": "Algeria",
|
||||
"flag": "dz"
|
||||
},
|
||||
{
|
||||
"coordinates": [2317, 754],
|
||||
"flag": "sd",
|
||||
"name": "Sudan"
|
||||
"coordinates": [2271, 788],
|
||||
"name": "Sudan",
|
||||
"flag": "sd"
|
||||
},
|
||||
{
|
||||
"coordinates": [2466, 918],
|
||||
"flag": "so",
|
||||
"name": "Somalia"
|
||||
"name": "Somalia",
|
||||
"flag": "so"
|
||||
},
|
||||
{
|
||||
"coordinates": [2352, 895],
|
||||
"flag": "Imperial Ethiopia",
|
||||
"name": "Ethiopia"
|
||||
"name": "Ethiopia",
|
||||
"flag": "et"
|
||||
},
|
||||
{
|
||||
"coordinates": [1790, 729],
|
||||
"flag": "Mauritania",
|
||||
"name": "Mauritania"
|
||||
"name": "Mauritania",
|
||||
"flag": "mr"
|
||||
},
|
||||
{
|
||||
"coordinates": [2154, 764],
|
||||
"flag": "td",
|
||||
"name": "Chad"
|
||||
"coordinates": [2118, 768],
|
||||
"name": "Chad",
|
||||
"flag": "td"
|
||||
},
|
||||
{
|
||||
"coordinates": [2051, 745],
|
||||
"flag": "ne",
|
||||
"name": "Niger"
|
||||
"coordinates": [2009, 810],
|
||||
"name": "Niger",
|
||||
"flag": "ne"
|
||||
},
|
||||
{
|
||||
"coordinates": [2040, 930],
|
||||
"flag": "ng",
|
||||
"name": "Nigeria"
|
||||
"coordinates": [1996, 909],
|
||||
"name": "Nigeria",
|
||||
"flag": "ng"
|
||||
},
|
||||
{
|
||||
"coordinates": [1805, 907],
|
||||
"flag": "lr",
|
||||
"name": "Liberia"
|
||||
"name": "Liberia",
|
||||
"flag": "lr"
|
||||
},
|
||||
{
|
||||
"coordinates": [2195, 918],
|
||||
"flag": "cf",
|
||||
"name": "Central African Republic"
|
||||
"name": "Central African Republic",
|
||||
"flag": "cf"
|
||||
},
|
||||
{
|
||||
"coordinates": [2197, 1070],
|
||||
"flag": "Zaire",
|
||||
"name": "Zaire"
|
||||
"name": "DR Congo",
|
||||
"flag": "cd"
|
||||
},
|
||||
{
|
||||
"coordinates": [2189, 1372],
|
||||
"flag": "Apartheid South Africa",
|
||||
"name": "South Africa"
|
||||
"coordinates": [2211, 1389],
|
||||
"name": "South Africa",
|
||||
"flag": "za"
|
||||
},
|
||||
{
|
||||
"coordinates": [2452, 1247],
|
||||
"flag": "mg",
|
||||
"name": "Madagascar"
|
||||
"name": "Madagascar",
|
||||
"flag": "mg"
|
||||
},
|
||||
{
|
||||
"coordinates": [2356, 1165],
|
||||
"flag": "mz",
|
||||
"name": "Mozambique"
|
||||
"name": "Mozambique",
|
||||
"flag": "mz"
|
||||
},
|
||||
{
|
||||
"coordinates": [2368, 1032],
|
||||
"flag": "tz",
|
||||
"name": "Tanzania"
|
||||
"name": "Tanzania",
|
||||
"flag": "tz"
|
||||
},
|
||||
{
|
||||
"coordinates": [1934, 762],
|
||||
"flag": "ml",
|
||||
"name": "Mali"
|
||||
"coordinates": [1894, 789],
|
||||
"name": "Mali",
|
||||
"flag": "ml"
|
||||
},
|
||||
{
|
||||
"coordinates": [2128, 1292],
|
||||
"flag": "Apartheid South Africa",
|
||||
"name": "South West Africa"
|
||||
"name": "Namibia",
|
||||
"flag": "na"
|
||||
},
|
||||
{
|
||||
"coordinates": [2099, 1178],
|
||||
"flag": "ao",
|
||||
"name": "Angola"
|
||||
"name": "Angola",
|
||||
"flag": "ao"
|
||||
},
|
||||
{
|
||||
"coordinates": [1375, 1121],
|
||||
"flag": "br",
|
||||
"name": "Brazil"
|
||||
"coordinates": [1418, 1167],
|
||||
"name": "Brazil",
|
||||
"flag": "br"
|
||||
},
|
||||
{
|
||||
"coordinates": [1203, 1059],
|
||||
"flag": "amazonas",
|
||||
"name": "Amazonas"
|
||||
"coordinates": [1225, 1065],
|
||||
"name": "Amazonas",
|
||||
"flag": "amazonas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1210, 1395],
|
||||
"flag": "ar",
|
||||
"name": "Argentina"
|
||||
"coordinates": [1243, 1428],
|
||||
"name": "Argentina",
|
||||
"flag": "ar"
|
||||
},
|
||||
{
|
||||
"coordinates": [1107, 1419],
|
||||
"flag": "cl",
|
||||
"name": "Chile"
|
||||
"coordinates": [1125, 1356],
|
||||
"name": "Chile",
|
||||
"flag": "cl"
|
||||
},
|
||||
{
|
||||
"coordinates": [1064, 1114],
|
||||
"flag": "pe",
|
||||
"name": "Peru"
|
||||
"coordinates": [1064, 1111],
|
||||
"name": "Peru",
|
||||
"flag": "pe"
|
||||
},
|
||||
{
|
||||
"coordinates": [1065, 938],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
"coordinates": [1083, 938],
|
||||
"name": "Colombia",
|
||||
"flag": "co"
|
||||
},
|
||||
{
|
||||
"coordinates": [1192, 938],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
"coordinates": [1183, 921],
|
||||
"name": "Venezuela",
|
||||
"flag": "ve"
|
||||
},
|
||||
{
|
||||
"coordinates": [913, 833],
|
||||
"flag": "ni",
|
||||
"name": "Nicaragua"
|
||||
"name": "Nicaragua",
|
||||
"flag": "ni"
|
||||
},
|
||||
{
|
||||
"coordinates": [788, 744],
|
||||
"flag": "mx",
|
||||
"name": "Mexico"
|
||||
"coordinates": [764, 743],
|
||||
"name": "Mexico",
|
||||
"flag": "mx"
|
||||
},
|
||||
{
|
||||
"coordinates": [1011, 555],
|
||||
"flag": "us",
|
||||
"name": "USA"
|
||||
"coordinates": [1034, 556],
|
||||
"name": "United States",
|
||||
"flag": "us"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 624],
|
||||
"flag": "Texas",
|
||||
"name": "Texas"
|
||||
"coordinates": [766, 623],
|
||||
"name": "Texas",
|
||||
"flag": "Texas"
|
||||
},
|
||||
{
|
||||
"coordinates": [551, 564],
|
||||
"flag": "California",
|
||||
"name": "California"
|
||||
"name": "California",
|
||||
"flag": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [703, 483],
|
||||
"flag": "Utah",
|
||||
"name": "Utah"
|
||||
"coordinates": [654, 530],
|
||||
"name": "Utah",
|
||||
"flag": "Utah"
|
||||
},
|
||||
{
|
||||
"coordinates": [1077, 444],
|
||||
"flag": "Quebec",
|
||||
"name": "Quebec"
|
||||
"coordinates": [1079, 385],
|
||||
"name": "Quebec",
|
||||
"flag": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [1231, 395],
|
||||
"flag": "Newfoundland",
|
||||
"name": "Newfoundland"
|
||||
"coordinates": [1211, 364],
|
||||
"name": "Newfoundland and Labrador",
|
||||
"flag": "newfoundlandandlabrador"
|
||||
},
|
||||
{
|
||||
"coordinates": [967, 418],
|
||||
"flag": "ca",
|
||||
"name": "Canada"
|
||||
"coordinates": [957, 406],
|
||||
"name": "Canada",
|
||||
"flag": "ca"
|
||||
},
|
||||
{
|
||||
"coordinates": [170, 244],
|
||||
"flag": "Alaska",
|
||||
"name": "Alaska"
|
||||
"name": "Alaska",
|
||||
"flag": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [741, 234],
|
||||
"flag": "Nunavut",
|
||||
"name": "Nunavut"
|
||||
"coordinates": [857, 232],
|
||||
"name": "Nunavut",
|
||||
"flag": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [484, 256],
|
||||
"flag": "Yukon",
|
||||
"name": "Yukon"
|
||||
"coordinates": [475, 254],
|
||||
"name": "Yukon",
|
||||
"flag": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1434, 223],
|
||||
"flag": "gl",
|
||||
"name": "Greenland"
|
||||
"coordinates": [1448, 137],
|
||||
"name": "Greenland",
|
||||
"flag": "gl"
|
||||
},
|
||||
{
|
||||
"coordinates": [2247, 1229],
|
||||
"flag": "Rhodesia",
|
||||
"name": "Rhodesia"
|
||||
"name": "Zimbabwe",
|
||||
"flag": "zw"
|
||||
},
|
||||
{
|
||||
"coordinates": [550, 438],
|
||||
"name": "Washington",
|
||||
"flag": "Washington"
|
||||
},
|
||||
{
|
||||
"coordinates": [778, 518],
|
||||
"name": "Kansas",
|
||||
"flag": "Kansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [912, 613],
|
||||
"name": "Mississippi",
|
||||
"flag": "Mississippi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1208, 1219],
|
||||
"name": "Bolivia",
|
||||
"flag": "bo"
|
||||
},
|
||||
{
|
||||
"coordinates": [2059, 1015],
|
||||
"name": "Gabon",
|
||||
"flag": "ga"
|
||||
},
|
||||
{
|
||||
"coordinates": [689, 437],
|
||||
"name": "Montana",
|
||||
"flag": "Montana"
|
||||
},
|
||||
{
|
||||
"coordinates": [517, 357],
|
||||
"name": "British Columbia",
|
||||
"flag": "britishcolumbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [618, 358],
|
||||
"name": "Alberta",
|
||||
"flag": "alberta"
|
||||
},
|
||||
{
|
||||
"coordinates": [732, 360],
|
||||
"name": "Saskatchewan",
|
||||
"flag": "saskatchewan"
|
||||
},
|
||||
{
|
||||
"coordinates": [834, 367],
|
||||
"name": "Manitoba",
|
||||
"flag": "manitoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [1108, 500],
|
||||
"name": "Massachusetts",
|
||||
"flag": "Massachusetts"
|
||||
},
|
||||
{
|
||||
"coordinates": [845, 465],
|
||||
"name": "Minnesota",
|
||||
"flag": "Minnesota"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 500],
|
||||
"name": "Michigan",
|
||||
"flag": "Michigan"
|
||||
},
|
||||
{
|
||||
"coordinates": [655, 247],
|
||||
"name": "Northwest Territories",
|
||||
"flag": "northwestterritories"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 5.0 MiB |
@@ -82,10 +82,15 @@
|
||||
"name": "Skid Row"
|
||||
},
|
||||
{
|
||||
"coordinates": [925, 935],
|
||||
"coordinates": [965, 865],
|
||||
"flag": "California",
|
||||
"name": "Inglewood"
|
||||
},
|
||||
{
|
||||
"coordinates": [815, 945],
|
||||
"flag": "California",
|
||||
"name": "L.A.X."
|
||||
},
|
||||
{
|
||||
"coordinates": [1180, 1010],
|
||||
"flag": "California",
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
@@ -12,12 +12,12 @@
|
||||
"name": "Apollo 14"
|
||||
},
|
||||
{
|
||||
"coordinates": [780, 345],
|
||||
"coordinates": [780, 340],
|
||||
"flag": "us",
|
||||
"name": "Apollo 15"
|
||||
},
|
||||
{
|
||||
"coordinates": [825, 735],
|
||||
"coordinates": [825, 705],
|
||||
"flag": "us",
|
||||
"name": "Apollo 11"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"name": "Surveyor 3"
|
||||
},
|
||||
{
|
||||
"coordinates": [256, 148],
|
||||
"coordinates": [250, 148],
|
||||
"flag": "us",
|
||||
"name": "Apollo 13"
|
||||
},
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
|
||||
{
|
||||
"coordinates": [510, 170],
|
||||
"coordinates": [515, 170],
|
||||
"flag": "Russian SSR",
|
||||
"name": "Luna 17"
|
||||
},
|
||||
@@ -95,7 +95,7 @@
|
||||
"name": "Chang'e 4"
|
||||
},
|
||||
{
|
||||
"coordinates": [270, 2690],
|
||||
"coordinates": [260, 268],
|
||||
"flag": "cn",
|
||||
"name": "Chang'e 5"
|
||||
},
|
||||
@@ -106,7 +106,7 @@
|
||||
},
|
||||
|
||||
{
|
||||
"coordinates": [830, 735],
|
||||
"coordinates": [830, 745],
|
||||
"flag": "jp",
|
||||
"name": "S.L.I.M."
|
||||
},
|
||||
@@ -117,7 +117,7 @@
|
||||
"name": "Chandrayaan 3"
|
||||
},
|
||||
{
|
||||
"coordinates": [732, 3490],
|
||||
"coordinates": [732, 3493],
|
||||
"flag": "in",
|
||||
"name": "Chandrayaan 1"
|
||||
},
|
||||
@@ -125,18 +125,18 @@
|
||||
{
|
||||
"coordinates": [755, 3035],
|
||||
"flag": "",
|
||||
"name": "T▅▚░S▅cr▅▟░M▅l▅t▅r▅░B▅s▅"
|
||||
"name": "T▆p░S▅cr▅t░M▊l▊t▅r▆░B▅s▅"
|
||||
},
|
||||
{
|
||||
"coordinates": [628, 921],
|
||||
"flag": "",
|
||||
"name": "[]"
|
||||
"name": "▊"
|
||||
}
|
||||
],
|
||||
"teamGameSpawnAreas": {
|
||||
"2": [
|
||||
{ "x": 0, "y": 0, "width": 1308, "height": 1750 },
|
||||
{ "x": 0, "y": 1750, "width": 1308, "height": 1750 }
|
||||
{ "x": 0, "y": 0, "width": 1308, "height": 1754 },
|
||||
{ "x": 0, "y": 1754, "width": 1308, "height": 1754 }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"name": "Middle East",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [300, 65],
|
||||
"name": "Ottoman Empire",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1639, 558],
|
||||
"name": "Qajar Dynasty",
|
||||
"flag": "Persia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1141, 797],
|
||||
"name": "Emirate of Kuwait",
|
||||
"flag": "Socialist_flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1880, 1353],
|
||||
"name": "Sultanate of Muscat",
|
||||
"flag": "Socialist_flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1703, 1402],
|
||||
"name": "Imamate of Oman",
|
||||
"flag": "White Flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1592, 1239],
|
||||
"name": "Trucial States",
|
||||
"flag": "Socialist_flag"
|
||||
},
|
||||
{
|
||||
"coordinates": [1129, 1875],
|
||||
"name": "Aden Protectorate",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [964, 1744],
|
||||
"name": "Kingdom of Yemen",
|
||||
"flag": "Kingdom of Yemen"
|
||||
},
|
||||
{
|
||||
"coordinates": [844, 1655],
|
||||
"name": "Emirate of Asir",
|
||||
"flag": "Emirate of Asir"
|
||||
},
|
||||
{
|
||||
"coordinates": [579, 1173],
|
||||
"name": "Kingdom of Hejaz",
|
||||
"flag": "Arabia"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 1052],
|
||||
"name": "Rashidi Emirate",
|
||||
"flag": "Rashidi Emirate"
|
||||
},
|
||||
{
|
||||
"coordinates": [1092, 1336],
|
||||
"name": "Sultanate of Nejd",
|
||||
"flag": "Sultanate of Nejd"
|
||||
},
|
||||
{
|
||||
"coordinates": [1397, 1128],
|
||||
"name": "Qatar",
|
||||
"flag": "qa"
|
||||
},
|
||||
{
|
||||
"coordinates": [973, 296],
|
||||
"name": "Kingdom of Iraq",
|
||||
"flag": "Kingdom of Iraq"
|
||||
},
|
||||
{
|
||||
"coordinates": [554, 364],
|
||||
"name": "Kingdom of Syria",
|
||||
"flag": "Kingdom of Syria"
|
||||
},
|
||||
{
|
||||
"coordinates": [423, 647],
|
||||
"name": "Palestine Mandate",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [100, 781],
|
||||
"name": "Kingdom of Egypt",
|
||||
"flag": "Kingdom of Egypt"
|
||||
},
|
||||
{
|
||||
"coordinates": [159, 1530],
|
||||
"name": "Anglo-Egyptian Sudan",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [578, 1766],
|
||||
"name": "Italian Eritrea",
|
||||
"flag": "italy"
|
||||
},
|
||||
{
|
||||
"coordinates": [401, 2005],
|
||||
"name": "Ethiopian Empire",
|
||||
"flag": "Ethiopian Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [826, 2044],
|
||||
"name": "French Somaliland",
|
||||
"flag": "fr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1455, 902],
|
||||
"name": "British Bushehr",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [185, 375],
|
||||
"name": "British Cyprus",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [2127, 373],
|
||||
"name": "Emirate of Afghanistan",
|
||||
"flag": "Emirate of Afghanistan"
|
||||
},
|
||||
{
|
||||
"coordinates": [2087, 925],
|
||||
"name": "Baluchistan Agency",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [932, 15],
|
||||
"name": "Republic of Armenia",
|
||||
"flag": "am"
|
||||
},
|
||||
{
|
||||
"coordinates": [1671, 71],
|
||||
"name": "Russian State",
|
||||
"flag": "ru"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,11 +6,6 @@
|
||||
"name": "Florida",
|
||||
"flag": "Florida"
|
||||
},
|
||||
{
|
||||
"coordinates": [1010, 435],
|
||||
"name": "Canada",
|
||||
"flag": "ca"
|
||||
},
|
||||
{
|
||||
"coordinates": [1250, 1130],
|
||||
"name": "Mexico",
|
||||
@@ -32,47 +27,47 @@
|
||||
"flag": "ni"
|
||||
},
|
||||
{
|
||||
"coordinates": [1734, 1403],
|
||||
"coordinates": [1651, 1399],
|
||||
"name": "Panama",
|
||||
"flag": "pa"
|
||||
},
|
||||
{
|
||||
"coordinates": [1821, 1395],
|
||||
"coordinates": [1760, 1410],
|
||||
"name": "Colombia",
|
||||
"flag": "co"
|
||||
},
|
||||
{
|
||||
"coordinates": [1896, 1379],
|
||||
"coordinates": [1883, 1395],
|
||||
"name": "Venezuela",
|
||||
"flag": "ve"
|
||||
},
|
||||
{
|
||||
"coordinates": [1725, 1180],
|
||||
"coordinates": [1692, 1161],
|
||||
"name": "Cuba",
|
||||
"flag": "cu"
|
||||
},
|
||||
{
|
||||
"coordinates": [467, 333],
|
||||
"coordinates": [331, 341],
|
||||
"name": "Alaska",
|
||||
"flag": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [1154, 914],
|
||||
"coordinates": [1062, 932],
|
||||
"name": "Arizona",
|
||||
"flag": "Arizona"
|
||||
},
|
||||
{
|
||||
"coordinates": [1010, 865],
|
||||
"coordinates": [940, 905],
|
||||
"name": "California",
|
||||
"flag": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [1307, 863],
|
||||
"coordinates": [1147, 843],
|
||||
"name": "Colorado",
|
||||
"flag": "Colorado"
|
||||
},
|
||||
{
|
||||
"coordinates": [1673, 965],
|
||||
"coordinates": [1593, 974],
|
||||
"name": "Georgia",
|
||||
"flag": "Georgia_US"
|
||||
},
|
||||
@@ -82,169 +77,274 @@
|
||||
"flag": "Hawaii"
|
||||
},
|
||||
{
|
||||
"coordinates": [1120, 760],
|
||||
"coordinates": [1000, 710],
|
||||
"name": "Idaho",
|
||||
"flag": "Idaho"
|
||||
},
|
||||
{
|
||||
"coordinates": [1551, 813],
|
||||
"coordinates": [1490, 789],
|
||||
"name": "Illinois",
|
||||
"flag": "Illinois"
|
||||
},
|
||||
{
|
||||
"coordinates": [1412, 873],
|
||||
"coordinates": [1335, 835],
|
||||
"name": "Kansas",
|
||||
"flag": "Kansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1651, 880],
|
||||
"coordinates": [1525, 872],
|
||||
"name": "Kentucky",
|
||||
"flag": "Kentucky"
|
||||
},
|
||||
{
|
||||
"coordinates": [1514, 1007],
|
||||
"coordinates": [1410, 984],
|
||||
"name": "Louisiana",
|
||||
"flag": "Louisiana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1884, 735],
|
||||
"coordinates": [1854, 720],
|
||||
"name": "Maine",
|
||||
"flag": "Maine"
|
||||
},
|
||||
{
|
||||
"coordinates": [1634, 764],
|
||||
"coordinates": [1572, 762],
|
||||
"name": "Michigan",
|
||||
"flag": "Michigan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1556, 965],
|
||||
"coordinates": [1472, 940],
|
||||
"name": "Mississippi",
|
||||
"flag": "Mississippi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1527, 728],
|
||||
"coordinates": [1390, 688],
|
||||
"name": "Minnesota",
|
||||
"flag": "Minnesota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1529, 880],
|
||||
"coordinates": [1427, 829],
|
||||
"name": "Missouri",
|
||||
"flag": "Missouri"
|
||||
},
|
||||
{
|
||||
"coordinates": [1225, 754],
|
||||
"coordinates": [1100, 677],
|
||||
"name": "Montana",
|
||||
"flag": "Montana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1413, 789],
|
||||
"coordinates": [1263, 787],
|
||||
"name": "Nebraska",
|
||||
"flag": "Nebraska"
|
||||
},
|
||||
{
|
||||
"coordinates": [1090, 852],
|
||||
"coordinates": [946, 818],
|
||||
"name": "Nevada",
|
||||
"flag": "Nevada"
|
||||
},
|
||||
{
|
||||
"coordinates": [1253, 933],
|
||||
"coordinates": [1169, 932],
|
||||
"name": "New Mexico",
|
||||
"flag": "New_Mexico"
|
||||
},
|
||||
{
|
||||
"coordinates": [1833, 792],
|
||||
"coordinates": [1737, 768],
|
||||
"name": "New York",
|
||||
"flag": "New_York"
|
||||
},
|
||||
{
|
||||
"coordinates": [1444, 716],
|
||||
"coordinates": [1239, 670],
|
||||
"name": "North Dakota",
|
||||
"flag": "North_Dakota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1704, 812],
|
||||
"coordinates": [1612, 816],
|
||||
"name": "Ohio",
|
||||
"flag": "Ohio"
|
||||
},
|
||||
{
|
||||
"coordinates": [1397, 921],
|
||||
"coordinates": [1296, 902],
|
||||
"name": "Oklahoma",
|
||||
"flag": "Oklahoma"
|
||||
},
|
||||
{
|
||||
"coordinates": [976, 754],
|
||||
"coordinates": [873, 757],
|
||||
"name": "Oregon",
|
||||
"flag": "Oregon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1752, 716],
|
||||
"coordinates": [1686, 796],
|
||||
"name": "Pennsylvania",
|
||||
"flag": "Pennsylvania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1716, 937],
|
||||
"coordinates": [1636, 934],
|
||||
"name": "South Carolina",
|
||||
"flag": "South_Carolina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1419, 753],
|
||||
"coordinates": [1302, 721],
|
||||
"name": "South Dakota",
|
||||
"flag": "South_Dakota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1648, 981],
|
||||
"coordinates": [1579, 900],
|
||||
"name": "Tennessee",
|
||||
"flag": "Tennessee"
|
||||
},
|
||||
{
|
||||
"coordinates": [1407, 1005],
|
||||
"coordinates": [1279, 1013],
|
||||
"name": "Texas",
|
||||
"flag": "Texas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1827, 742],
|
||||
"coordinates": [1785, 727],
|
||||
"name": "Vermont",
|
||||
"flag": "Vermont"
|
||||
},
|
||||
{
|
||||
"coordinates": [1767, 857],
|
||||
"coordinates": [1652, 864],
|
||||
"name": "Virginia",
|
||||
"flag": "Virginia"
|
||||
},
|
||||
{
|
||||
"coordinates": [994, 700],
|
||||
"coordinates": [897, 673],
|
||||
"name": "Washington",
|
||||
"flag": "Washington"
|
||||
},
|
||||
{
|
||||
"coordinates": [1261, 759],
|
||||
"coordinates": [1118, 764],
|
||||
"name": "Wyoming",
|
||||
"flag": "Wyoming"
|
||||
},
|
||||
{
|
||||
"coordinates": [1867, 561],
|
||||
"coordinates": [1798, 592],
|
||||
"name": "Quebec",
|
||||
"flag": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [1738, 80],
|
||||
"name": "Santa Claus",
|
||||
"flag": "santa_claus"
|
||||
"coordinates": [1080, 367],
|
||||
"name": "Northwest Territories",
|
||||
"flag": "northwestterritories"
|
||||
},
|
||||
{
|
||||
"coordinates": [1189, 240],
|
||||
"name": "Polar Bears",
|
||||
"flag": "polar_bears"
|
||||
},
|
||||
{
|
||||
"coordinates": [1480, 350],
|
||||
"name": "Frost Giants",
|
||||
"flag": "frost_giant"
|
||||
"coordinates": [1440, 319],
|
||||
"name": "Nunavut",
|
||||
"flag": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [2399, 171],
|
||||
"name": "Greenland",
|
||||
"flag": "gl"
|
||||
},
|
||||
{
|
||||
"coordinates": [777, 382],
|
||||
"name": "Yukon",
|
||||
"flag": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [787, 561],
|
||||
"name": "British Columbia",
|
||||
"flag": "britishcolumbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [966, 521],
|
||||
"name": "Alberta",
|
||||
"flag": "alberta"
|
||||
},
|
||||
{
|
||||
"coordinates": [1172, 569],
|
||||
"name": "Saskatchewan",
|
||||
"flag": "saskatchewan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1343, 507],
|
||||
"name": "Manitoba",
|
||||
"flag": "manitoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [1575, 621],
|
||||
"name": "Ontario",
|
||||
"flag": "ontario"
|
||||
},
|
||||
{
|
||||
"coordinates": [1903, 671],
|
||||
"name": "New Brunswick",
|
||||
"flag": "newbrunswick"
|
||||
},
|
||||
{
|
||||
"coordinates": [1924, 748],
|
||||
"name": "Nova Scotia",
|
||||
"flag": "novascotia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1983, 547],
|
||||
"name": "Newfoundland and Labrador",
|
||||
"flag": "newfoundlandandlabrador"
|
||||
},
|
||||
{
|
||||
"coordinates": [1793, 1209],
|
||||
"name": "Haiti",
|
||||
"flag": "ht"
|
||||
},
|
||||
{
|
||||
"coordinates": [1851, 1215],
|
||||
"name": "Dominican Republic",
|
||||
"flag": "do"
|
||||
},
|
||||
{
|
||||
"coordinates": [1912, 1224],
|
||||
"name": "Puerto Rico",
|
||||
"flag": "pr"
|
||||
},
|
||||
{
|
||||
"coordinates": [2020, 1422],
|
||||
"name": "Guyana",
|
||||
"flag": "gy"
|
||||
},
|
||||
{
|
||||
"coordinates": [2785, 358],
|
||||
"name": "Iceland",
|
||||
"flag": "is"
|
||||
},
|
||||
{
|
||||
"coordinates": [1975, 702],
|
||||
"name": "Prince Edward Island",
|
||||
"flag": "princeedwardisland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1037, 838],
|
||||
"name": "Utah",
|
||||
"flag": "Utah"
|
||||
},
|
||||
{
|
||||
"coordinates": [1402, 772],
|
||||
"name": "Iowa",
|
||||
"flag": "Iowa"
|
||||
},
|
||||
{
|
||||
"coordinates": [1685, 902],
|
||||
"name": "North Carolina",
|
||||
"flag": "North_Carolina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1417, 904],
|
||||
"name": "Arkansas",
|
||||
"flag": "Arkansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1547, 819],
|
||||
"name": "Indiana",
|
||||
"flag": "Indiana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1528, 963],
|
||||
"name": "Alabama",
|
||||
"flag": "Alabama"
|
||||
},
|
||||
{
|
||||
"coordinates": [1811, 782],
|
||||
"name": "Massachusetts",
|
||||
"flag": "Massachusetts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
"name": "Oceania",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [718, 738],
|
||||
"coordinates": [515, 690],
|
||||
"name": "Australia",
|
||||
"flag": "au"
|
||||
},
|
||||
{
|
||||
"coordinates": [1050, 809],
|
||||
"coordinates": [1010, 836],
|
||||
"name": "New Zealand",
|
||||
"flag": "nz"
|
||||
},
|
||||
{
|
||||
"coordinates": [436, 407],
|
||||
"name": "Timor-Leste",
|
||||
"name": "Timor Leste",
|
||||
"flag": "tl"
|
||||
},
|
||||
{
|
||||
"coordinates": [182, 378],
|
||||
"coordinates": [236, 336],
|
||||
"name": "Indonesia",
|
||||
"flag": "id"
|
||||
},
|
||||
{
|
||||
"coordinates": [292, 243],
|
||||
"name": "Brunei Darussalam",
|
||||
"name": "Brunei",
|
||||
"flag": "bn"
|
||||
},
|
||||
{
|
||||
@@ -32,33 +32,33 @@
|
||||
"flag": "sg"
|
||||
},
|
||||
{
|
||||
"coordinates": [120, 261],
|
||||
"coordinates": [115, 228],
|
||||
"name": "Malaysia",
|
||||
"flag": "my"
|
||||
},
|
||||
{
|
||||
"coordinates": [106, 129],
|
||||
"coordinates": [112, 113],
|
||||
"name": "Thailand",
|
||||
"flag": "th"
|
||||
},
|
||||
{
|
||||
"coordinates": [51, 42],
|
||||
"coordinates": [34, 23],
|
||||
"name": "Myanmar",
|
||||
"flag": "mm"
|
||||
},
|
||||
{
|
||||
"coordinates": [158, 162],
|
||||
"coordinates": [171, 153],
|
||||
"name": "Cambodia",
|
||||
"flag": "kh"
|
||||
},
|
||||
{
|
||||
"coordinates": [182, 43],
|
||||
"coordinates": [203, 105],
|
||||
"name": "Vietnam",
|
||||
"flag": "vn"
|
||||
},
|
||||
{
|
||||
"coordinates": [143, 37],
|
||||
"name": "Lao PDR",
|
||||
"coordinates": [127, 42],
|
||||
"name": "Laos",
|
||||
"flag": "la"
|
||||
},
|
||||
{
|
||||
@@ -67,70 +67,35 @@
|
||||
"flag": "hk"
|
||||
},
|
||||
{
|
||||
"coordinates": [359, 1],
|
||||
"name": "Taiwan, Province of China",
|
||||
"coordinates": [362, 9],
|
||||
"name": "Taiwan",
|
||||
"flag": "tw"
|
||||
},
|
||||
{
|
||||
"coordinates": [366, 119],
|
||||
"coordinates": [425, 194],
|
||||
"name": "Philippines",
|
||||
"flag": "ph"
|
||||
},
|
||||
{
|
||||
"coordinates": [536, 207],
|
||||
"name": "Palau",
|
||||
"flag": "pw"
|
||||
},
|
||||
{
|
||||
"coordinates": [834, 215],
|
||||
"name": "Micronesia",
|
||||
"flag": "fm"
|
||||
},
|
||||
{
|
||||
"coordinates": [664, 113],
|
||||
"name": "Guam",
|
||||
"flag": "gu"
|
||||
},
|
||||
{
|
||||
"coordinates": [1042, 317],
|
||||
"name": "Marshall Islands",
|
||||
"flag": "mh"
|
||||
},
|
||||
{
|
||||
"coordinates": [799, 385],
|
||||
"coordinates": [630, 384],
|
||||
"name": "Papua New Guinea",
|
||||
"flag": "pg"
|
||||
},
|
||||
{
|
||||
"coordinates": [862, 442],
|
||||
"coordinates": [855, 424],
|
||||
"name": "Solomon Islands",
|
||||
"flag": "sb"
|
||||
},
|
||||
{
|
||||
"coordinates": [945, 497],
|
||||
"name": "Vanuatu",
|
||||
"flag": "vu"
|
||||
},
|
||||
{
|
||||
"coordinates": [930, 574],
|
||||
"coordinates": [925, 574],
|
||||
"name": "New Caledonia",
|
||||
"flag": "nc"
|
||||
},
|
||||
{
|
||||
"coordinates": [1085, 526],
|
||||
"coordinates": [1080, 528],
|
||||
"name": "Fiji",
|
||||
"flag": "fj"
|
||||
},
|
||||
{
|
||||
"coordinates": [1169, 568],
|
||||
"name": "Tonga",
|
||||
"flag": "to"
|
||||
},
|
||||
{
|
||||
"coordinates": [1236, 541],
|
||||
"name": "Niue",
|
||||
"flag": "nu"
|
||||
},
|
||||
{
|
||||
"coordinates": [1204, 473],
|
||||
"name": "Samoa",
|
||||
@@ -142,24 +107,9 @@
|
||||
"flag": "ck"
|
||||
},
|
||||
{
|
||||
"coordinates": [1623, 424],
|
||||
"name": "French Polynesia",
|
||||
"flag": "pf"
|
||||
},
|
||||
{
|
||||
"coordinates": [1393, 278],
|
||||
"name": "Kiribati",
|
||||
"flag": "ki"
|
||||
},
|
||||
{
|
||||
"coordinates": [1420, 56],
|
||||
"name": "United States",
|
||||
"flag": "us"
|
||||
},
|
||||
{
|
||||
"coordinates": [1996, 644],
|
||||
"name": "Chile",
|
||||
"flag": "cl"
|
||||
"coordinates": [1413, 56],
|
||||
"name": "Hawaii",
|
||||
"flag": "Hawaii"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
"flag": "ht"
|
||||
},
|
||||
{
|
||||
"coordinates": [112, 209],
|
||||
"coordinates": [122, 143],
|
||||
"name": "Belize",
|
||||
"flag": "bz"
|
||||
},
|
||||
{
|
||||
"coordinates": [71, 282],
|
||||
"coordinates": [68, 257],
|
||||
"name": "Guatemala",
|
||||
"flag": "gt"
|
||||
},
|
||||
@@ -42,84 +42,89 @@
|
||||
"flag": "pa"
|
||||
},
|
||||
{
|
||||
"coordinates": [740, 1180],
|
||||
"coordinates": [847, 1209],
|
||||
"name": "Bolivia",
|
||||
"flag": "bo"
|
||||
},
|
||||
{
|
||||
"coordinates": [849, 1770],
|
||||
"coordinates": [861, 1851],
|
||||
"name": "Argentina",
|
||||
"flag": "ar"
|
||||
},
|
||||
{
|
||||
"coordinates": [1394, 1309],
|
||||
"coordinates": [1153, 1095],
|
||||
"name": "Brazil",
|
||||
"flag": "br"
|
||||
},
|
||||
{
|
||||
"coordinates": [691, 1371],
|
||||
"coordinates": [687, 1605],
|
||||
"name": "Chile",
|
||||
"flag": "cl"
|
||||
},
|
||||
{
|
||||
"coordinates": [527, 503],
|
||||
"coordinates": [582, 543],
|
||||
"name": "Colombia",
|
||||
"flag": "co"
|
||||
},
|
||||
{
|
||||
"coordinates": [384, 746],
|
||||
"coordinates": [444, 750],
|
||||
"name": "Ecuador",
|
||||
"flag": "ec"
|
||||
},
|
||||
{
|
||||
"coordinates": [933, 423],
|
||||
"name": "French Guyana",
|
||||
"coordinates": [1204, 615],
|
||||
"name": "French Guiana",
|
||||
"flag": "gf"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 410],
|
||||
"coordinates": [981, 546],
|
||||
"name": "Guyana",
|
||||
"flag": "gy"
|
||||
},
|
||||
{
|
||||
"coordinates": [541, 1092],
|
||||
"coordinates": [537, 1017],
|
||||
"name": "Peru",
|
||||
"flag": "pe"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 1496],
|
||||
"coordinates": [1028, 1404],
|
||||
"name": "Paraguay",
|
||||
"flag": "py"
|
||||
},
|
||||
{
|
||||
"coordinates": [890, 610],
|
||||
"coordinates": [1097, 586],
|
||||
"name": "Suriname",
|
||||
"flag": "sr"
|
||||
},
|
||||
{
|
||||
"coordinates": [1091, 1635],
|
||||
"coordinates": [1118, 1695],
|
||||
"name": "Uruguay",
|
||||
"flag": "uy"
|
||||
},
|
||||
{
|
||||
"coordinates": [678, 904],
|
||||
"coordinates": [770, 465],
|
||||
"name": "Venezuela",
|
||||
"flag": "ve"
|
||||
},
|
||||
{
|
||||
"coordinates": [1270, 1035],
|
||||
"name": "The Biggest Snakes",
|
||||
"flag": "Aztec Empire"
|
||||
"coordinates": [795, 808],
|
||||
"name": "Amazonas",
|
||||
"flag": "amazonas"
|
||||
},
|
||||
{
|
||||
"coordinates": [894, 693],
|
||||
"name": "Normal Capybaras",
|
||||
"flag": ""
|
||||
"coordinates": [1267, 794],
|
||||
"name": "Pará",
|
||||
"flag": "Para"
|
||||
},
|
||||
{
|
||||
"coordinates": [884, 832],
|
||||
"name": "Just Otters",
|
||||
"flag": ""
|
||||
"coordinates": [1550, 1066],
|
||||
"name": "Bahia",
|
||||
"flag": "bahia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1374, 1404],
|
||||
"name": "São Paulo",
|
||||
"flag": "Sao Paulo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,309 +2,364 @@
|
||||
"name": "World",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [375, 272],
|
||||
"flag": "us",
|
||||
"name": "United States"
|
||||
"coordinates": [484, 284],
|
||||
"name": "United States",
|
||||
"flag": "us"
|
||||
},
|
||||
{
|
||||
"coordinates": [372, 136],
|
||||
"flag": "ca",
|
||||
"name": "Canada"
|
||||
"coordinates": [374, 186],
|
||||
"name": "Canada",
|
||||
"flag": "ca"
|
||||
},
|
||||
{
|
||||
"coordinates": [375, 374],
|
||||
"flag": "mx",
|
||||
"name": "Mexico"
|
||||
"name": "Mexico",
|
||||
"flag": "mx"
|
||||
},
|
||||
{
|
||||
"coordinates": [500, 378],
|
||||
"flag": "cu",
|
||||
"name": "Cuba"
|
||||
"name": "Cuba",
|
||||
"flag": "cu"
|
||||
},
|
||||
{
|
||||
"coordinates": [524, 474],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
"coordinates": [527, 487],
|
||||
"name": "Colombia",
|
||||
"flag": "co"
|
||||
},
|
||||
{
|
||||
"coordinates": [593, 473],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
"coordinates": [576, 466],
|
||||
"name": "Venezuela",
|
||||
"flag": "ve"
|
||||
},
|
||||
{
|
||||
"coordinates": [596, 705],
|
||||
"flag": "ar",
|
||||
"name": "Argentina"
|
||||
"coordinates": [565, 737],
|
||||
"name": "Argentina",
|
||||
"flag": "ar"
|
||||
},
|
||||
{
|
||||
"coordinates": [637, 567],
|
||||
"flag": "br",
|
||||
"name": "Brazil"
|
||||
"coordinates": [684, 574],
|
||||
"name": "Brazil",
|
||||
"flag": "br"
|
||||
},
|
||||
{
|
||||
"coordinates": [1280, 975],
|
||||
"flag": "aq",
|
||||
"name": "Antarctica"
|
||||
"name": "Antarctica",
|
||||
"flag": "aq"
|
||||
},
|
||||
{
|
||||
"coordinates": [709, 57],
|
||||
"flag": "gl",
|
||||
"name": "Greenland"
|
||||
"name": "Greenland",
|
||||
"flag": "gl"
|
||||
},
|
||||
{
|
||||
"coordinates": [831, 112],
|
||||
"flag": "is",
|
||||
"name": "Iceland"
|
||||
"name": "Iceland",
|
||||
"flag": "is"
|
||||
},
|
||||
{
|
||||
"coordinates": [925, 186],
|
||||
"flag": "gb",
|
||||
"name": "United Kingdom"
|
||||
"name": "United Kingdom",
|
||||
"flag": "gb"
|
||||
},
|
||||
{
|
||||
"coordinates": [887, 183],
|
||||
"flag": "ie",
|
||||
"name": "Ireland"
|
||||
"name": "Ireland",
|
||||
"flag": "ie"
|
||||
},
|
||||
{
|
||||
"coordinates": [908, 264],
|
||||
"flag": "es",
|
||||
"name": "Spain"
|
||||
"name": "Spain",
|
||||
"flag": "es"
|
||||
},
|
||||
{
|
||||
"coordinates": [1004, 250],
|
||||
"flag": "it",
|
||||
"name": "Italy"
|
||||
"name": "Italy",
|
||||
"flag": "it"
|
||||
},
|
||||
{
|
||||
"coordinates": [958, 220],
|
||||
"flag": "fr",
|
||||
"name": "France"
|
||||
"coordinates": [948, 221],
|
||||
"name": "France",
|
||||
"flag": "fr"
|
||||
},
|
||||
{
|
||||
"coordinates": [997, 205],
|
||||
"flag": "de",
|
||||
"name": "Germany"
|
||||
"coordinates": [990, 195],
|
||||
"name": "Germany",
|
||||
"flag": "de"
|
||||
},
|
||||
{
|
||||
"coordinates": [1064, 101],
|
||||
"flag": "se",
|
||||
"name": "Sweden"
|
||||
"coordinates": [1014, 137],
|
||||
"name": "Sweden",
|
||||
"flag": "se"
|
||||
},
|
||||
{
|
||||
"coordinates": [1046, 193],
|
||||
"flag": "pl",
|
||||
"name": "Poland"
|
||||
"coordinates": [1031, 193],
|
||||
"name": "Poland",
|
||||
"flag": "pl"
|
||||
},
|
||||
{
|
||||
"coordinates": [1061, 188],
|
||||
"flag": "by",
|
||||
"name": "Belarus"
|
||||
"coordinates": [1102, 183],
|
||||
"name": "Belarus",
|
||||
"flag": "by"
|
||||
},
|
||||
{
|
||||
"coordinates": [1073, 243],
|
||||
"flag": "ro",
|
||||
"name": "Romania"
|
||||
"coordinates": [1073, 230],
|
||||
"name": "Romania",
|
||||
"flag": "ro"
|
||||
},
|
||||
{
|
||||
"coordinates": [1161, 274],
|
||||
"flag": "tr",
|
||||
"name": "Turkey"
|
||||
"coordinates": [1123, 272],
|
||||
"name": "Türkiye",
|
||||
"flag": "tr"
|
||||
},
|
||||
{
|
||||
"coordinates": [969, 133],
|
||||
"flag": "no",
|
||||
"name": "Norway"
|
||||
"name": "Norway",
|
||||
"flag": "no"
|
||||
},
|
||||
{
|
||||
"coordinates": [1062, 133],
|
||||
"flag": "fi",
|
||||
"name": "Finland"
|
||||
"coordinates": [1082, 126],
|
||||
"name": "Finland",
|
||||
"flag": "fi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1099, 211],
|
||||
"flag": "ua",
|
||||
"name": "Ukraine"
|
||||
"coordinates": [1135, 210],
|
||||
"name": "Ukraine",
|
||||
"flag": "ua"
|
||||
},
|
||||
{
|
||||
"coordinates": [1344, 136],
|
||||
"flag": "ru",
|
||||
"name": "Russia"
|
||||
"coordinates": [1351, 134],
|
||||
"name": "Russia",
|
||||
"flag": "ru"
|
||||
},
|
||||
{
|
||||
"coordinates": [1537, 186],
|
||||
"flag": "mn",
|
||||
"name": "Mongolia"
|
||||
"coordinates": [1500, 203],
|
||||
"name": "Mongolia",
|
||||
"flag": "mn"
|
||||
},
|
||||
{
|
||||
"coordinates": [1524, 328],
|
||||
"flag": "cn",
|
||||
"name": "China"
|
||||
"coordinates": [1527, 303],
|
||||
"name": "China",
|
||||
"flag": "cn"
|
||||
},
|
||||
{
|
||||
"coordinates": [1368, 373],
|
||||
"flag": "in",
|
||||
"name": "India"
|
||||
"coordinates": [1331, 353],
|
||||
"name": "India",
|
||||
"flag": "in"
|
||||
},
|
||||
{
|
||||
"coordinates": [1276, 239],
|
||||
"flag": "kz",
|
||||
"name": "Kazakhstan"
|
||||
"coordinates": [1279, 207],
|
||||
"name": "Kazakhstan",
|
||||
"flag": "kz"
|
||||
},
|
||||
{
|
||||
"coordinates": [1238, 309],
|
||||
"flag": "ir",
|
||||
"name": "Islamic Republic Of Iran"
|
||||
"name": "Iran",
|
||||
"flag": "ir"
|
||||
},
|
||||
{
|
||||
"coordinates": [1178, 351],
|
||||
"flag": "sa",
|
||||
"name": "Saudi Arabia"
|
||||
"name": "Saudi Arabia",
|
||||
"flag": "sa"
|
||||
},
|
||||
{
|
||||
"coordinates": [1679, 657],
|
||||
"flag": "au",
|
||||
"name": "Australia"
|
||||
"name": "Australia",
|
||||
"flag": "au"
|
||||
},
|
||||
{
|
||||
"coordinates": [1890, 775],
|
||||
"flag": "nz",
|
||||
"name": "New Zealand"
|
||||
"name": "New Zealand",
|
||||
"flag": "nz"
|
||||
},
|
||||
{
|
||||
"coordinates": [918, 342],
|
||||
"flag": "dz",
|
||||
"name": "Algeria"
|
||||
"coordinates": [957, 295],
|
||||
"name": "Algeria",
|
||||
"flag": "dz"
|
||||
},
|
||||
{
|
||||
"coordinates": [1030, 332],
|
||||
"flag": "ly",
|
||||
"name": "Libyan Arab Jamahiriya"
|
||||
"coordinates": [1024, 340],
|
||||
"name": "Libya",
|
||||
"flag": "ly"
|
||||
},
|
||||
{
|
||||
"coordinates": [1092, 335],
|
||||
"flag": "eg",
|
||||
"name": "Egypt"
|
||||
"coordinates": [1094, 340],
|
||||
"name": "Egypt",
|
||||
"flag": "eg"
|
||||
},
|
||||
{
|
||||
"coordinates": [963, 410],
|
||||
"flag": "ne",
|
||||
"name": "Niger"
|
||||
"coordinates": [956, 389],
|
||||
"name": "Niger",
|
||||
"flag": "ne"
|
||||
},
|
||||
{
|
||||
"coordinates": [1112, 406],
|
||||
"flag": "sd",
|
||||
"name": "Sudan"
|
||||
"coordinates": [1101, 400],
|
||||
"name": "Sudan",
|
||||
"flag": "sd"
|
||||
},
|
||||
{
|
||||
"coordinates": [1074, 508],
|
||||
"flag": "cd",
|
||||
"name": "DR Congo"
|
||||
"coordinates": [1051, 513],
|
||||
"name": "DR Congo",
|
||||
"flag": "cd"
|
||||
},
|
||||
{
|
||||
"coordinates": [1154, 443],
|
||||
"flag": "et",
|
||||
"name": "Ethiopia"
|
||||
"coordinates": [1153, 451],
|
||||
"name": "Ethiopia",
|
||||
"flag": "et"
|
||||
},
|
||||
{
|
||||
"coordinates": [1075, 707],
|
||||
"flag": "za",
|
||||
"name": "South Africa"
|
||||
"name": "South Africa",
|
||||
"flag": "za"
|
||||
},
|
||||
{
|
||||
"coordinates": [1194, 627],
|
||||
"flag": "mg",
|
||||
"name": "Madagascar"
|
||||
"name": "Madagascar",
|
||||
"flag": "mg"
|
||||
},
|
||||
{
|
||||
"coordinates": [1052, 420],
|
||||
"flag": "td",
|
||||
"name": "Chad"
|
||||
"coordinates": [1036, 420],
|
||||
"name": "Chad",
|
||||
"flag": "td"
|
||||
},
|
||||
{
|
||||
"coordinates": [1030, 665],
|
||||
"flag": "na",
|
||||
"name": "Namibia"
|
||||
"coordinates": [1039, 646],
|
||||
"name": "Namibia",
|
||||
"flag": "na"
|
||||
},
|
||||
{
|
||||
"coordinates": [1632, 465],
|
||||
"flag": "ph",
|
||||
"name": "Philippines"
|
||||
"name": "Philippines",
|
||||
"flag": "ph"
|
||||
},
|
||||
{
|
||||
"coordinates": [1537, 426],
|
||||
"flag": "th",
|
||||
"name": "Thailand"
|
||||
"coordinates": [1502, 415],
|
||||
"name": "Thailand",
|
||||
"flag": "th"
|
||||
},
|
||||
{
|
||||
"coordinates": [1610, 364],
|
||||
"flag": "tw",
|
||||
"name": "Taiwan"
|
||||
"name": "Taiwan",
|
||||
"flag": "tw"
|
||||
},
|
||||
{
|
||||
"coordinates": [1710, 290],
|
||||
"flag": "jp",
|
||||
"name": "Japan"
|
||||
"name": "Japan",
|
||||
"flag": "jp"
|
||||
},
|
||||
{
|
||||
"coordinates": [1869, 119],
|
||||
"flag": "ru",
|
||||
"name": "Siberia"
|
||||
"coordinates": [1626, 118],
|
||||
"name": "Siberia",
|
||||
"flag": "Siberia"
|
||||
},
|
||||
{
|
||||
"coordinates": [74, 117],
|
||||
"flag": "polar_bears",
|
||||
"name": "Polar Bears"
|
||||
"coordinates": [422, 95],
|
||||
"name": "Nunavut",
|
||||
"flag": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [419, 975],
|
||||
"flag": "aq",
|
||||
"name": "West Antarctica"
|
||||
"name": "West Antarctica",
|
||||
"flag": "aq"
|
||||
},
|
||||
{
|
||||
"coordinates": [542, 603],
|
||||
"flag": "pe",
|
||||
"name": "Peru"
|
||||
"coordinates": [516, 567],
|
||||
"name": "Peru",
|
||||
"flag": "pe"
|
||||
},
|
||||
{
|
||||
"coordinates": [1075, 615],
|
||||
"flag": "zm",
|
||||
"name": "Zambia"
|
||||
"coordinates": [1097, 598],
|
||||
"name": "Zambia",
|
||||
"flag": "zm"
|
||||
},
|
||||
{
|
||||
"coordinates": [1099, 165],
|
||||
"flag": "lv",
|
||||
"name": "Latvia"
|
||||
"coordinates": [1067, 168],
|
||||
"name": "Latvia",
|
||||
"flag": "lv"
|
||||
},
|
||||
{
|
||||
"coordinates": [1427, 336],
|
||||
"flag": "bt",
|
||||
"name": "Bhutan"
|
||||
"coordinates": [1419, 338],
|
||||
"name": "Bhutan",
|
||||
"flag": "bt"
|
||||
},
|
||||
{
|
||||
"coordinates": [1511, 524],
|
||||
"flag": "id",
|
||||
"name": "Indonesia"
|
||||
"name": "Indonesia",
|
||||
"flag": "id"
|
||||
},
|
||||
{
|
||||
"coordinates": [1809, 977],
|
||||
"flag": "aq",
|
||||
"name": "East Antarctica"
|
||||
"name": "East Antarctica",
|
||||
"flag": "aq"
|
||||
},
|
||||
{
|
||||
"coordinates": [1255, 382],
|
||||
"flag": "om",
|
||||
"name": "Oman"
|
||||
"name": "Oman",
|
||||
"flag": "om"
|
||||
},
|
||||
{
|
||||
"coordinates": [853, 373],
|
||||
"flag": "ma",
|
||||
"name": "Morocco"
|
||||
"coordinates": [886, 327],
|
||||
"name": "Morocco",
|
||||
"flag": "ma"
|
||||
},
|
||||
{
|
||||
"coordinates": [656, 678],
|
||||
"flag": "uy",
|
||||
"name": "Uruguay"
|
||||
"coordinates": [626, 704],
|
||||
"name": "Uruguay",
|
||||
"flag": "uy"
|
||||
},
|
||||
{
|
||||
"coordinates": [581, 620],
|
||||
"name": "Bolivia",
|
||||
"flag": "bo"
|
||||
},
|
||||
{
|
||||
"coordinates": [95, 115],
|
||||
"name": "Alaska",
|
||||
"flag": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [243, 131],
|
||||
"name": "Yukon",
|
||||
"flag": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [264, 276],
|
||||
"name": "California",
|
||||
"flag": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [371, 299],
|
||||
"name": "Texas",
|
||||
"flag": "Texas"
|
||||
},
|
||||
{
|
||||
"coordinates": [532, 191],
|
||||
"name": "Quebec",
|
||||
"flag": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [932, 476],
|
||||
"name": "Benin",
|
||||
"flag": "bj"
|
||||
},
|
||||
{
|
||||
"coordinates": [845, 425],
|
||||
"name": "Senegal",
|
||||
"flag": "sn"
|
||||
},
|
||||
{
|
||||
"coordinates": [1152, 522],
|
||||
"name": "Kenya",
|
||||
"flag": "ke"
|
||||
},
|
||||
{
|
||||
"coordinates": [1320, 282],
|
||||
"name": "Pakistan",
|
||||
"flag": "pk"
|
||||
},
|
||||
{
|
||||
"coordinates": [1384, 466],
|
||||
"name": "Sri Lanka",
|
||||
"flag": "lk"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ var maps = []struct {
|
||||
{Name: "straitofmalacca"},
|
||||
{Name: "mars"},
|
||||
{Name: "mena"},
|
||||
{Name: "middleeast"},
|
||||
{Name: "montreal"},
|
||||
{Name: "newyorkcity"},
|
||||
{Name: "northamerica"},
|
||||
|
||||
@@ -29,104 +29,91 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@datastructures-js/priority-queue": "^6.3.3",
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@datastructures-js/priority-queue": "^6.3.5",
|
||||
"@eslint/compat": "^2.0.5",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/chai": "^4.3.17",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/google-protobuf": "^3.15.12",
|
||||
"@types/hammerjs": "^2.0.46",
|
||||
"@types/howler": "^2.2.12",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/msgpack5": "^3.4.6",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@types/node": "^24.12.0",
|
||||
"@types/pg": "^8.20.0",
|
||||
"@types/seedrandom": "^3.0.8",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/systeminformation": "^3.23.1",
|
||||
"@types/ws": "^8.5.11",
|
||||
"@vitest/coverage-v8": "^4.0.16",
|
||||
"@vitest/ui": "^4.0.16",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitest/coverage-v8": "^4.1.5",
|
||||
"@vitest/ui": "^4.1.5",
|
||||
"autoprefixer": "^10.5.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"canvas": "^3.2.1",
|
||||
"chai": "^5.1.1",
|
||||
"canvas": "^3.2.3",
|
||||
"concurrently": "^9.2.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"cross-env": "^10.1.0",
|
||||
"d3": "^7.9.0",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-formatter-gha": "^1.5.2",
|
||||
"glob": "^13.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"eslint": "^10.3.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-formatter-gha": "^2.0.1",
|
||||
"glob": "^13.0.6",
|
||||
"globals": "^17.6.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^27.4.0",
|
||||
"lint-staged": "^16.1.2",
|
||||
"lit": "^3.3.1",
|
||||
"jsdom": "^29.1.1",
|
||||
"lint-staged": "^16.4.0",
|
||||
"lit": "^3.3.2",
|
||||
"lit-markdown": "^1.3.2",
|
||||
"mrmime": "^2.0.0",
|
||||
"mrmime": "^2.0.1",
|
||||
"pixi-filters": "^6.1.5",
|
||||
"pixi.js": "^8.17.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-sh": "^0.17.4",
|
||||
"protobufjs": "^7.5.5",
|
||||
"sinon": "^21.0.1",
|
||||
"sinon-chai": "^4.0.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"pixi.js": "^8.18.1",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-organize-imports": "^4.3.0",
|
||||
"prettier-plugin-sh": "^0.18.1",
|
||||
"tailwindcss": "^4.2.4",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.1",
|
||||
"vite": "^7.3.2",
|
||||
"vite": "^8.0.10",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-static-copy": "^3.1.4",
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16",
|
||||
"vitest-canvas-mock": "^1.1.3"
|
||||
"vitest": "^4.1.5",
|
||||
"vitest-canvas-mock": "^1.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.758.0",
|
||||
"@lit-labs/virtualizer": "^2.1.1",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.200.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.200.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.200.0",
|
||||
"@opentelemetry/resources": "^2.0.0",
|
||||
"@opentelemetry/sdk-logs": "^0.200.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.32.0",
|
||||
"@opentelemetry/winston-transport": "^0.11.0",
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/api-logs": "^0.216.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.216.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.216.0",
|
||||
"@opentelemetry/resources": "^2.7.1",
|
||||
"@opentelemetry/sdk-logs": "^0.216.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.7.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"@opentelemetry/winston-transport": "^0.26.0",
|
||||
"@types/compression": "^1.8.1",
|
||||
"colord": "^2.9.3",
|
||||
"colorjs.io": "^0.5.2",
|
||||
"colorjs.io": "^0.6.1",
|
||||
"compression": "^1.8.1",
|
||||
"dompurify": "^3.4.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"ejs": "^3.1.10",
|
||||
"dompurify": "^3.4.2",
|
||||
"dotenv": "^17.4.2",
|
||||
"ejs": "^5.0.2",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.3.2",
|
||||
"fastpriorityqueue": "^0.7.5",
|
||||
"express-rate-limit": "^8.4.1",
|
||||
"fastpriorityqueue": "^0.8.0",
|
||||
"howler": "^2.2.4",
|
||||
"intl-messageformat": "^10.7.16",
|
||||
"intl-messageformat": "^11.2.3",
|
||||
"ip-anonymize": "^0.1.0",
|
||||
"jose": "^6.0.10",
|
||||
"jose": "^6.2.3",
|
||||
"js-yaml": "^4.1.1",
|
||||
"limiter": "^3.0.0",
|
||||
"nanoid": "^3.3.6",
|
||||
"node-html-parser": "^7.0.2",
|
||||
"obscenity": "^0.4.3",
|
||||
"nanoid": "^5.1.11",
|
||||
"node-html-parser": "^7.1.0",
|
||||
"obscenity": "^0.4.6",
|
||||
"seedrandom": "^3.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.17.0",
|
||||
"uuid": "^14.0.0",
|
||||
"winston": "^3.17.0",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^4.0.5"
|
||||
"tsx": "^4.21.0",
|
||||
"winston": "^3.19.0",
|
||||
"ws": "^8.20.0",
|
||||
"zod": "^4.4.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -665,6 +665,16 @@
|
||||
"continent": "North America",
|
||||
"name": "El Salvador"
|
||||
},
|
||||
{
|
||||
"code": "Emirate of Afghanistan",
|
||||
"continent": "Asia",
|
||||
"name": "Emirate of Afghanistan"
|
||||
},
|
||||
{
|
||||
"code": "Emirate of Asir",
|
||||
"continent": "Asia",
|
||||
"name": "Emirate of Asir"
|
||||
},
|
||||
{
|
||||
"code": "granada",
|
||||
"continent": "Europe",
|
||||
@@ -721,6 +731,11 @@
|
||||
"continent": "Africa",
|
||||
"name": "Ethiopia"
|
||||
},
|
||||
{
|
||||
"code": "Ethiopian Empire",
|
||||
"continent": "Africa",
|
||||
"name": "Ethiopian Empire"
|
||||
},
|
||||
{
|
||||
"code": "eu",
|
||||
"continent": "Europe",
|
||||
@@ -1050,11 +1065,6 @@
|
||||
"continent": "Europe",
|
||||
"name": "Italy"
|
||||
},
|
||||
{
|
||||
"code": "italy",
|
||||
"continent": "Europe",
|
||||
"name": "Kingdom of Italy"
|
||||
},
|
||||
{
|
||||
"code": "jm",
|
||||
"continent": "North America",
|
||||
@@ -1130,6 +1140,11 @@
|
||||
"continent": "Asia",
|
||||
"name": "Kingdom of Iraq"
|
||||
},
|
||||
{
|
||||
"code": "italy",
|
||||
"continent": "Europe",
|
||||
"name": "Kingdom of Italy"
|
||||
},
|
||||
{
|
||||
"code": "Kingdom of Jerusalem",
|
||||
"continent": "Asia",
|
||||
@@ -1140,6 +1155,16 @@
|
||||
"continent": "Asia",
|
||||
"name": "Kingdom of Judah"
|
||||
},
|
||||
{
|
||||
"code": "Kingdom of Syria",
|
||||
"continent": "Asia",
|
||||
"name": "Kingdom of Syria"
|
||||
},
|
||||
{
|
||||
"code": "Kingdom of Yemen",
|
||||
"continent": "Asia",
|
||||
"name": "Kingdom of Yemen"
|
||||
},
|
||||
{
|
||||
"code": "Kirghiz SSR",
|
||||
"continent": "Asia",
|
||||
@@ -1808,6 +1833,11 @@
|
||||
"continent": "North America",
|
||||
"name": "Quebec"
|
||||
},
|
||||
{
|
||||
"code": "Rashidi Emirate",
|
||||
"continent": "Asia",
|
||||
"name": "Rashidi Emirate"
|
||||
},
|
||||
{
|
||||
"code": "Republic of China",
|
||||
"continent": "Asia",
|
||||
@@ -2057,7 +2087,7 @@
|
||||
},
|
||||
{
|
||||
"code": "Socialist_flag",
|
||||
"name": "Socialist Flag"
|
||||
"name": "Red Flag"
|
||||
},
|
||||
{
|
||||
"code": "sb",
|
||||
@@ -2463,6 +2493,10 @@
|
||||
"continent": "Africa",
|
||||
"name": "Western Sahara"
|
||||
},
|
||||
{
|
||||
"code": "White Flag",
|
||||
"name": "White Flag"
|
||||
},
|
||||
{
|
||||
"code": "Wisconsin",
|
||||
"continent": "North America",
|
||||
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" viewBox="0 0 150 150">
|
||||
<image href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAADICAYAAACZBDirAAAKlklEQVR4AezZsW5VRx7A4QGEREcBEn0a6KAECwjd7ivQUKy0GB4ATINoMZQ0mIUt8hS7HQEKJArcw6ZHgo4CISH2jCMnFnbsc+6dM2dmzhf5Ark+d+Y/3zg/JcrhsPuvn7u3nnWv/3Wv716BQWDgn4OqfwZiy2LTYtu6q/zz68cAPum+9bx7/aN7/dS9fBEgQKB2gdiy2LTYtti4P86zM4D/6d79Z/fyRYAAgVYFYuNi67bOtx3AWMW/bb0zo18clQCBWQrE1sXmhRjA+N/FsYqzlHBoAgRmKRCb93MM4LVZHt+hCRCYu8C1GMArc1eY5fkdmgCBKzGA8f+QoCBAgMDcBH6KAZzboZ2XAAECWwICuMXgl3kJOC2B3wUWDuD58+fDxsZGePHiRfj48WP4/v27FwM/A34GsvwMxObE9jx+/DjEFv2es+G/Dg7gsWPHwvr6enj16lW4fv16uHTpUjhx4sTwnX2CAAECCwrE5sT2rK6ubrXo/v374ejRo4NXGxTAs2fPhs3NzXD79u1w5MiRwZv5AAECkws0N0Bs0draWnjz5k2IjRpywEEBvHr1ajh9+vSQ9T1LgACBLAIxfrFRQzbrHcCVlZVw69at4C8CBAiUKhAbFUPYd77eAbx27Vo4fLj343339xwBAgSSCcRG3bx58y/X+/EbvYt27ty5Hz/r7wkQIFCcwJkzZ3rP1DuAQxbtvbsHCRAgkFhgSKt6B/D48eOJx7QcAQIE0gucOnWq96K9A9h7xYIfNBoBAgR2CgjgTg1/JkBgVgICOKvrdlgCBHYKCOBOjZb/7GwECOwSEMBdJN4gQGAuAgI4l5t2TgIEdgkI4C4Sb7Qn4EQE9hYQwL1dvEuAwAwEBHAGl+yIBAjsLSCAe7t4l0ArAs6xj4AA7oPjWwQItC0ggG3fr9MRILCPgADug+NbBAjULXDQ9AJ4kJDvEyDQrIAANnu1DkaAwEECAniQkO8TINCsQNMBbPbWHIwAgSQCApiE0SIECNQoIIA13pqZCRBIIiCASRgLXMRIBAgcKCCABxJ5gACBVgUEsNWbdS4CBA4UEMADiTxQn4CJCfQTEMB+Tp4iQKBBAQFs8FIdiQCBfgIC2M/JUwRqETDnAAEBHIDlUQIE2hIQwLbu02kIEBggIIADsDxKgEDZAkOnE8ChYp4nQKAZAQFs5iodhACBoQICOFTM8wQINCPQVACbuRUHIUAgi4AAZmG2CQECJQoIYIm3YiYCBLIICGAW5gyb2IIAgcECAjiYzAcIEGhFQABbuUnnIEBgsIAADibzgfIETERgMQEBXMzNpwgQaEBAABu4REcgQGAxAQFczM2nCJQiYI4lBARwCTwfJUCgbgEBrPv+TE+AwBICArgEno8SIDCtwLK7C+Cygj5PgEC1AgJY7dUZnACBZQUEcFlBnydAoFqBqgNYrbrBCRAoQkAAi7gGQxAgMIWAAE6hbk8CBIoQEMAirmGBIXyEAIGlBQRwaUILECBQq4AA1npz5iZAYGkBAVya0AL5BexIII2AAKZxtAoBAhUKCGCFl2ZkAgTSCAhgGkerEMglYJ+EAgKYENNSBAjUJSCAdd2XaQkQSCgggAkxLUWAwLgCqVcXwNSi1iNAoBoBAazmqgxKgEBqAQFMLWo9AgSqEagqgNWoGpQAgSoEBLCKazIkAQJjCAjgGKrWJECgCgEBrOKaQgjmJEAguYAAJie1IAECtQgIYC03ZU4CBJILCGByUgumF7AigXEEBHAcV6sSIFCBgABWcElGJEBgHAEBHMfVqgRSCVhnRAEBHBHX0gQIlC0ggGXfj+kIEBhRQABHxLU0AQLLCYz9aQEcW9j6BAgUKyCAxV6NwQgQGFtAAMcWtj4BAsUKFB3AYtUMRoBAEwIC2MQ1OgQBAosICOAiaj5DgEATAgJY6jWaiwCB0QUEcHRiGxAgUKqAAJZ6M+YiQGB0AQEcndgGwwV8gkAeAQHM42wXAgQKFBDAAi/FSAQI5BEQwDzOdiHQV8BzGQUEMCO2rQgQKEtAAMu6D9MQIJBRQAAzYtuKAIH9BXJ/VwBzi9uPAIFiBASwmKswCAECuQUEMLe4/QgQKEagqAAWo2IQAgRmISCAs7hmhyRAYC8BAdxLxXsECMxCQABLuWZzECCQXUAAs5PbkACBUgQEsJSbMAcBAtkFBDA7uQ13C3iHwDQCAjiNu10JEChAQAALuAQjECAwjYAATuNuVwLbAn6fUEAAJ8S3NQEC0woI4LT+didAYEIBAZwQ39YE5i4w9fkFcOobsD8BApMJCOBk9DYmQGBqAQGc+gbsT4DAZAKTBnCyU9uYAAECnYAAdgi+CBCYp4AAzvPenZoAgU5AADuESb5sSoDA5AICOPkVGIAAgakEBHAqefsSIDC5gABOfgVzHMCZCZQhIIBl3IMpCBCYQEAAJ0C3JQECZQgIYBn3YIr5CDhpQQICWNBlGIUAgbwCApjX224ECBQkIIAFXYZRCLQuUNr5BLC0GzEPAQLZBAQwG7WNCBAoTUAAS7sR8xAgkE0gawCzncpGBAgQ6CEggD2QPEKAQJsCAtjmvToVAQI9BASwB1KSRyxCgEBxAgJY3JUYiACBXAICmEvaPgQIFCcggMVdSYsDOROBMgUEsMx7MRUBAhkEBDADsi0IEChTQADLvBdTtSPgJAULCGDBl2M0AgTGFRDAcX2tToBAwQICWPDlGI1A7QKlzy+Apd+Q+QgQGE1AAEejtTABAqULCGDpN2Q+AgRGExg1gKNNbWECBAgkEBDABIiWIECgTgEBrPPeTE2AQAIBAUyAuOcS3iRAoHgBASz+igxIgMBYAgI4lqx1CRAoXkAAi7+iGgc0M4E6BASwjnsyJQECIwgI4AioliRAoA4BAazjnkxZj4BJKxIQwIouy6gECKQVEMC0nlYjQKAiAQGs6LKMSqB0gdrmE8Dabsy8BAgkExDAZJQWIkCgNgEBrO3GzEuAQDKBpAFMNpWFCBAgkEFAADMg24IAgTIFBLDMezEVAQIZBHoH8MOHDxnGqXgLoxMgUITAkFb1DuDm5mYRhzMEAQIE9hMY0ioB3E/S9wgQqE5glAA+e/YsfP36tToMA+cQsAeBMgQ+f/4cnj592nuY3v8G+O7du3Dv3r3eC3uQAAECuQXu3r0b3r9/33vb3gGMKz58+DC8ffs2/tGLAAECRQnE//R99OjRoJkGBfDbt29hZWUlrK+vh/jnQTt5mECbAk41sUBs0YMHD8KFCxcGd2lQAOM5v3z5Eu7cuRMuXrwYNjY2wsuXL8OnT5/it7wIECCQRSA2J7bnyZMnWy1aW1sLsU1DNx8cwO0NXr9+HW7cuBEuX74cTp48GQ4dOuTFwM+An4EsPwOxObE9q6urIbZou0tDf184gEM38jwBAu0J1H4iAaz9Bs1PgMDCAjGAvy38aR8kQIBAvQK/xQA+r3d+kxMgQGBhgecxgL8s+nGfI0CAQMUCv8QA/tod4F/dyxcBAgTmIhCb92sMYDzw9e6X/3YvXwQIEGhdILYuNi9sBzAe+O/dL7GK3W++DhTwAAECNQrExsXWbc2+M4DxjVjFK90f/t29/N/hDsEXAQLVC8SWxabFtsXG/XGg/wMAAP//p4HOyAAAAAZJREFUAwA3Oy4c7Kb0ZAAAAABJRU5ErkJggg==" x="7.500" y="32.813" width="135.000" height="84.375" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 248 B |
@@ -1,87 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 100 100"
|
||||
fill="none"
|
||||
x="0px"
|
||||
y="0px"
|
||||
version="1.1"
|
||||
id="svg67"
|
||||
sodipodi:docname="noun-medal-4567887.svg"
|
||||
width="100"
|
||||
height="100"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs71">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Invert"
|
||||
id="filter203"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1">
|
||||
<feColorMatrix
|
||||
type="hueRotate"
|
||||
values="180"
|
||||
result="color1"
|
||||
id="feColorMatrix199" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
|
||||
result="color2"
|
||||
id="feColorMatrix201" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Invert"
|
||||
id="filter209"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1">
|
||||
<feColorMatrix
|
||||
type="hueRotate"
|
||||
values="180"
|
||||
result="color1"
|
||||
id="feColorMatrix205" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
|
||||
result="color2"
|
||||
id="feColorMatrix207" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview69"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.456"
|
||||
inkscape:cx="49.953532"
|
||||
inkscape:cy="37.716853"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1010"
|
||||
inkscape:window-x="1913"
|
||||
inkscape:window-y="-6"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg67" />
|
||||
<path
|
||||
d="m 59.903346,13.687732 v 6.602231 h 6.60223 v 19.806691 h -6.60223 v 6.602231 H 40.096654 v -6.602231 h -6.60223 V 20.289963 h 6.60223 v -6.602231 z"
|
||||
fill="#000000"
|
||||
id="path59"
|
||||
style="stroke-width:1.65056;filter:url(#filter209)" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M 33.494424,0.48327138 V 7.0855019 h -6.602231 v 6.6022301 h -6.60223 v 33.011153 h 6.60223 v 6.60223 h 6.602231 v 6.602231 h -6.602231 v 39.613383 h 6.602231 v -6.602231 h 6.60223 v -6.60223 h 4.951673 v -6.602231 h 6.602231 v 6.602231 h 6.60223 v 6.60223 h 6.602231 v 6.602231 h 6.60223 V 59.903346 h -4.951673 v -6.602231 h 6.602231 v -6.60223 h 6.60223 V 13.687732 h -6.60223 V 7.0855019 H 66.505576 V 0.48327138 Z M 58.252788,86.312268 v -6.602231 h -6.60223 v -6.60223 h -6.602231 v 6.60223 h -6.60223 v 6.602231 H 33.494424 V 59.903346 H 64.855019 V 86.312268 Z M 59.903346,7.0855019 H 40.096654 v 6.6022301 h -6.60223 v 6.602231 h -6.602231 v 19.806691 h 6.602231 v 6.602231 h 6.60223 v 6.60223 h 19.806692 v -6.60223 h 6.60223 v -6.602231 h 6.602231 V 20.289963 h -6.602231 v -6.602231 h -6.60223 z"
|
||||
fill="#000000"
|
||||
id="path61"
|
||||
style="stroke-width:1.65056;filter:url(#filter203)" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="m59.903346 13.687732v6.602231h6.60223v19.806691h-6.60223v6.602231H40.096654v-6.602231h-6.60223V20.289963h6.60223v-6.602231z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.494424.48327138V7.0855019h-6.602231v6.6022301h-6.60223v33.011153h6.60223v6.60223h6.602231v6.602231h-6.602231v39.613383h6.602231v-6.602231h6.60223v-6.60223h4.951673v-6.602231h6.602231v6.602231h6.60223v6.60223h6.602231v6.602231h6.60223V59.903346h-4.951673v-6.602231h6.602231v-6.60223h6.60223V13.687732h-6.60223V7.0855019H66.505576V.48327138ZM58.252788 86.312268v-6.602231h-6.60223v-6.60223h-6.602231v6.60223h-6.60223v6.602231H33.494424V59.903346H64.855019V86.312268ZM59.903346 7.0855019H40.096654v6.6022301h-6.60223v6.602231h-6.602231v19.806691h6.602231v6.602231h6.60223v6.60223h19.806692v-6.60223h6.60223v-6.602231h6.602231V20.289963h-6.602231v-6.602231h-6.60223z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 955 B |
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 243 B |
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
@@ -16,6 +16,7 @@
|
||||
"summary_send": "Send",
|
||||
"summary_keep": "Keep",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"send": "Send",
|
||||
"cap_label": "Cap",
|
||||
"cap_tooltip": "Recipient’s remaining capacity",
|
||||
@@ -49,6 +50,7 @@
|
||||
"leaderboard": "Leaderboard",
|
||||
"account": "Account",
|
||||
"help": "Help",
|
||||
"clans": "Clans",
|
||||
"menu": "Menu",
|
||||
"troubleshooting": "Troubleshooting",
|
||||
"go_to_troubleshooting": "Go to our troubleshooting page"
|
||||
@@ -222,6 +224,115 @@
|
||||
"logging_in": "Logging in...",
|
||||
"success": "Successfully logged in as {email}!"
|
||||
},
|
||||
"clan_modal": {
|
||||
"title": "Clans",
|
||||
"my_clans": "My Clans",
|
||||
"browse": "Browse",
|
||||
"no_clans": "You're not in any clans yet.",
|
||||
"sign_in_for_clans": "Sign in to join and manage clans",
|
||||
"request_pending": "Request Pending",
|
||||
"search_placeholder": "Search by clan tag...",
|
||||
"no_results": "No clans found.",
|
||||
"invite_only": "Invite Only",
|
||||
"members": "Members",
|
||||
"status": "Status",
|
||||
"open": "Open",
|
||||
"join_clan": "Join Clan",
|
||||
"request_invite": "Request Invite",
|
||||
"leave_clan": "Leave Clan",
|
||||
"manage_clan": "Manage",
|
||||
"transfer_leadership": "Transfer Leadership",
|
||||
"clan_name": "Clan Name",
|
||||
"description": "Description",
|
||||
"open_clan": "Open Clan",
|
||||
"open_clan_desc": "Anyone can join without an invite",
|
||||
"clan_settings": "Clan Settings",
|
||||
"save_changes": "Save Changes",
|
||||
"promote": "Promote",
|
||||
"demote": "Demote",
|
||||
"kick": "Kick",
|
||||
"danger_zone": "Danger Zone",
|
||||
"disband_clan": "Disband Clan",
|
||||
"transfer_warning": "This will make the selected member the new leader. You will become a regular member. This action cannot be undone.",
|
||||
"confirm_transfer": "Transfer leadership to {name}",
|
||||
"select_new_leader": "Select a new leader",
|
||||
"search_members_placeholder": "Filter current page by ID or role...",
|
||||
"search_requests_placeholder": "Search by player public ID...",
|
||||
"per_page": "Per page",
|
||||
"sort_by": "Sort by",
|
||||
"sort_default": "Role",
|
||||
"sort_total_wins": "Total Wins",
|
||||
"sort_total_losses": "Total Losses",
|
||||
"sort_ffa_wins": "FFA Wins",
|
||||
"sort_ffa_losses": "FFA Losses",
|
||||
"sort_team_wins": "Team Wins",
|
||||
"sort_team_losses": "Team Losses",
|
||||
"sort_hvn_wins": "HvN Wins",
|
||||
"sort_hvn_losses": "HvN Losses",
|
||||
"sort_ranked_wins": "Ranked Wins",
|
||||
"sort_ranked_losses": "Ranked Losses",
|
||||
"sort_1v1_wins": "1v1 Wins",
|
||||
"sort_1v1_losses": "1v1 Losses",
|
||||
"sort_order_asc": "Ascending",
|
||||
"sort_order_desc": "Descending",
|
||||
"join_requests": "Join Requests",
|
||||
"no_requests": "No pending join requests.",
|
||||
"pending_requests_count": "{count, plural, one {# pending request} other {# pending requests}}",
|
||||
"approve": "Approve",
|
||||
"deny": "Deny",
|
||||
"requested_on": "Requested to join [{tag}] on {date}.",
|
||||
"pending_applications": "Pending Applications",
|
||||
"no_pending_applications": "No pending applications.",
|
||||
"applied": "Applied",
|
||||
"cancel_request": "Cancel",
|
||||
"statistics": "Statistics",
|
||||
"stats_total": "Total",
|
||||
"stats_ffa": "FFA",
|
||||
"stats_team": "Teams",
|
||||
"stats_hvn": "HvN",
|
||||
"stats_ranked": "Ranked",
|
||||
"stats_1v1": "1v1",
|
||||
"no_description": "No description",
|
||||
"saving": "Saving...",
|
||||
"join_request_cancelled": "Join request cancelled.",
|
||||
"failed_to_load_clan": "Failed to load clan",
|
||||
"join_request_sent": "Join request sent! Waiting for approval.",
|
||||
"left_clan": "You left the clan.",
|
||||
"settings_saved": "Clan settings saved!",
|
||||
"clan_disbanded": "Clan disbanded.",
|
||||
"member_promoted": "Member promoted!",
|
||||
"member_demoted": "Member demoted.",
|
||||
"member_kicked": "Member kicked.",
|
||||
"leadership_transferred": "Leadership transferred!",
|
||||
"failed_to_load_requests": "Failed to load requests",
|
||||
"request_approved": "Request approved!",
|
||||
"request_denied": "Request denied.",
|
||||
"ban": "Ban",
|
||||
"unban": "Unban",
|
||||
"banned_players": "Banned Players",
|
||||
"no_bans": "No banned players.",
|
||||
"ban_reason_prompt": "Ban reason (optional, max 200 characters):",
|
||||
"confirm_ban": "Are you sure you want to ban this player? They will be removed from the clan and unable to rejoin.",
|
||||
"member_banned": "Player banned.",
|
||||
"member_unbanned": "Player unbanned.",
|
||||
"banned_by_label": "by",
|
||||
"ban_reason": "Reason: {reason}",
|
||||
"error_banned": "You are banned from this clan.",
|
||||
"error_banned_reason": "You are banned from this clan. Reason: {reason}",
|
||||
"confirm_kick": "Are you sure you want to kick this member?",
|
||||
"confirm_disband": "Are you sure you want to disband [{tag}] {name}? This cannot be undone.",
|
||||
"joined_date": "Member since {date}.",
|
||||
"member_count": "{count, plural, one {# member} other {# members}}",
|
||||
"role_leader": "Leader",
|
||||
"role_officer": "Officer",
|
||||
"role_member": "Member",
|
||||
"error_already_member": "Already a member",
|
||||
"error_request_pending": "Join request already pending",
|
||||
"error_rate_limited_generic": "Please wait before joining another clan",
|
||||
"error_network": "Network error",
|
||||
"error_failed": "Action failed",
|
||||
"error_loading": "Failed to load"
|
||||
},
|
||||
"account_modal": {
|
||||
"title": "Account",
|
||||
"connected_as": "Connected as",
|
||||
@@ -298,6 +409,7 @@
|
||||
"giantworldmap": "Giant World Map",
|
||||
"europe": "Europe",
|
||||
"mena": "MENA",
|
||||
"middleeast": "Middle East",
|
||||
"northamerica": "North America",
|
||||
"oceania": "Oceania",
|
||||
"blacksea": "Black Sea",
|
||||
@@ -575,9 +687,9 @@
|
||||
"left_click_label": "Left Click to Open Menu",
|
||||
"left_click_desc": "When ON, left-click opens menu and sword button attacks. When OFF, left-click attacks directly.",
|
||||
"left_click_menu": "Left Click Menu",
|
||||
"attack_ratio_label": "⚔️ Attack Ratio",
|
||||
"attack_ratio_label": "Attack Ratio",
|
||||
"attack_ratio_desc": "What percentage of your troops to send in an attack (1–100%)",
|
||||
"territory_patterns_label": "🏳️ Territory Skins",
|
||||
"territory_patterns_label": "Territory Skins",
|
||||
"territory_patterns_desc": "Choose whether to display territory skin designs in game",
|
||||
"coordinate_grid_label": "Coordinate Grid",
|
||||
"coordinate_grid_desc": "Toggle the alphanumeric grid overlay",
|
||||
@@ -637,6 +749,8 @@
|
||||
"boat_attack_desc": "Send a boat attack to the tile under your cursor.",
|
||||
"ground_attack": "Ground Attack",
|
||||
"ground_attack_desc": "Send a ground attack to the tile under your cursor.",
|
||||
"retaliate_attack": "Retaliate",
|
||||
"retaliate_attack_desc": "Send a retaliation attack to blunt/negate the force of the most recent active attacker. Only available when you are being attacked.",
|
||||
"ally_keybinds": "Ally Keybinds",
|
||||
"request_alliance": "Request Alliance",
|
||||
"request_alliance_desc": "Send an alliance request to the player whose tile is under your cursor.",
|
||||
|
||||
@@ -1,80 +1,130 @@
|
||||
{
|
||||
"map": {
|
||||
"height": 612,
|
||||
"num_land_tiles": 387974,
|
||||
"num_land_tiles": 382332,
|
||||
"width": 1000
|
||||
},
|
||||
"map16x": {
|
||||
"height": 153,
|
||||
"num_land_tiles": 22991,
|
||||
"num_land_tiles": 22523,
|
||||
"width": 250
|
||||
},
|
||||
"map4x": {
|
||||
"height": 306,
|
||||
"num_land_tiles": 95304,
|
||||
"num_land_tiles": 93711,
|
||||
"width": 500
|
||||
},
|
||||
"name": "Bosphorus Straits",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [520, 300],
|
||||
"coordinates": [564, 245],
|
||||
"flag": "tr",
|
||||
"name": "Istanbul"
|
||||
"name": "Beykoz"
|
||||
},
|
||||
{
|
||||
"coordinates": [360, 280],
|
||||
"coordinates": [820, 209],
|
||||
"flag": "tr",
|
||||
"name": "Thrace"
|
||||
"name": "Sile"
|
||||
},
|
||||
{
|
||||
"coordinates": [220, 260],
|
||||
"coordinates": [700, 316],
|
||||
"flag": "tr",
|
||||
"name": "Edirne"
|
||||
"name": "Çekmeköy"
|
||||
},
|
||||
{
|
||||
"coordinates": [650, 360],
|
||||
"coordinates": [800, 438],
|
||||
"flag": "tr",
|
||||
"name": "Bursa"
|
||||
"name": "Pendik"
|
||||
},
|
||||
{
|
||||
"coordinates": [690, 290],
|
||||
"coordinates": [797, 566],
|
||||
"flag": "tr",
|
||||
"name": "Izmit"
|
||||
"name": "Tuzla"
|
||||
},
|
||||
{
|
||||
"coordinates": [430, 430],
|
||||
"coordinates": [486, 381],
|
||||
"flag": "tr",
|
||||
"name": "Canakkale"
|
||||
"name": "Üsküdar"
|
||||
},
|
||||
{
|
||||
"coordinates": [320, 330],
|
||||
"coordinates": [534, 425],
|
||||
"flag": "tr",
|
||||
"name": "Tekirdag"
|
||||
"name": "Kadıköy"
|
||||
},
|
||||
{
|
||||
"coordinates": [610, 320],
|
||||
"coordinates": [559, 568],
|
||||
"flag": "tr",
|
||||
"name": "Yalova"
|
||||
"name": "Adalar"
|
||||
},
|
||||
{
|
||||
"coordinates": [720, 260],
|
||||
"coordinates": [635, 500],
|
||||
"flag": "tr",
|
||||
"name": "Maltepe"
|
||||
},
|
||||
{
|
||||
"coordinates": [701, 423],
|
||||
"flag": "tr",
|
||||
"name": "Sancaktepe"
|
||||
},
|
||||
{
|
||||
"coordinates": [424, 394],
|
||||
"flag": "tr",
|
||||
"name": "Fatih"
|
||||
},
|
||||
{
|
||||
"coordinates": [128, 46],
|
||||
"flag": "tr",
|
||||
"name": "Arnavutköy"
|
||||
},
|
||||
{
|
||||
"coordinates": [965, 544],
|
||||
"flag": "tr",
|
||||
"name": "Kocaeli"
|
||||
},
|
||||
{
|
||||
"coordinates": [160, 120],
|
||||
"flag": "bg",
|
||||
"name": "Varna"
|
||||
"coordinates": [42, 325],
|
||||
"flag": "tr",
|
||||
"name": "Büyükçekmece"
|
||||
},
|
||||
{
|
||||
"coordinates": [220, 150],
|
||||
"flag": "bg",
|
||||
"name": "Burgas"
|
||||
"coordinates": [336, 175],
|
||||
"flag": "tr",
|
||||
"name": "Eyüpsultan"
|
||||
},
|
||||
{
|
||||
"coordinates": [820, 470],
|
||||
"flag": "gr",
|
||||
"name": "Aegean Isles"
|
||||
"coordinates": [459, 157],
|
||||
"flag": "tr",
|
||||
"name": "Sarıyer"
|
||||
},
|
||||
{
|
||||
"coordinates": [477, 297],
|
||||
"flag": "tr",
|
||||
"name": "Beşiktaş"
|
||||
},
|
||||
{
|
||||
"coordinates": [171, 379],
|
||||
"flag": "tr",
|
||||
"name": "Avcılar"
|
||||
},
|
||||
{
|
||||
"coordinates": [308, 412],
|
||||
"flag": "tr",
|
||||
"name": "Bakırköy"
|
||||
},
|
||||
{
|
||||
"coordinates": [263, 283],
|
||||
"flag": "tr",
|
||||
"name": "Başakşehir"
|
||||
},
|
||||
{
|
||||
"coordinates": [402, 272],
|
||||
"flag": "tr",
|
||||
"name": "Sultangazi"
|
||||
},
|
||||
{
|
||||
"coordinates": [130, 270],
|
||||
"flag": "tr",
|
||||
"name": "Esenyurt"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -6,19 +6,19 @@
|
||||
},
|
||||
"map16x": {
|
||||
"height": 336,
|
||||
"num_land_tiles": 108584,
|
||||
"num_land_tiles": 108613,
|
||||
"width": 770
|
||||
},
|
||||
"map4x": {
|
||||
"height": 672,
|
||||
"num_land_tiles": 458859,
|
||||
"num_land_tiles": 459895,
|
||||
"width": 1540
|
||||
},
|
||||
"name": "Dyslexdria",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [2260, 400],
|
||||
"flag": "west_germany",
|
||||
"flag": "de",
|
||||
"name": "Jermaine"
|
||||
},
|
||||
{
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2050, 460],
|
||||
"flag": "Fascist Spain",
|
||||
"flag": "es",
|
||||
"name": "Splain"
|
||||
},
|
||||
{
|
||||
@@ -48,12 +48,12 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2342, 436],
|
||||
"flag": "Communist Romania",
|
||||
"flag": "ro",
|
||||
"name": "Rollandia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1222, 310],
|
||||
"flag": "Ukrainian SSR",
|
||||
"flag": "ua",
|
||||
"name": "Ucryin"
|
||||
},
|
||||
{
|
||||
@@ -78,7 +78,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2555, 475],
|
||||
"flag": "Georgian SSR",
|
||||
"flag": "ge",
|
||||
"name": "Georgia"
|
||||
},
|
||||
{
|
||||
@@ -108,7 +108,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [1260, 400],
|
||||
"flag": "Kazakh SSR",
|
||||
"flag": "kz",
|
||||
"name": "Azakah"
|
||||
},
|
||||
{
|
||||
@@ -248,7 +248,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2950, 1150],
|
||||
"flag": "Apartheid South Africa",
|
||||
"flag": "za",
|
||||
"name": "Southern African State"
|
||||
},
|
||||
{
|
||||
@@ -258,7 +258,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2860, 1050],
|
||||
"flag": "Apartheid South Africa",
|
||||
"flag": "za",
|
||||
"name": "Southern South West Africa"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{
|
||||
"coordinates": [634, 781],
|
||||
"flag": "ie",
|
||||
"name": "Republic of Ireland"
|
||||
"name": "Ireland"
|
||||
},
|
||||
{
|
||||
"coordinates": [962, 780],
|
||||
@@ -27,79 +27,79 @@
|
||||
"name": "England"
|
||||
},
|
||||
{
|
||||
"coordinates": [935, 1289],
|
||||
"coordinates": [874, 1286],
|
||||
"flag": "es",
|
||||
"name": "Kingdom of Spain"
|
||||
"name": "Spain"
|
||||
},
|
||||
{
|
||||
"coordinates": [1087, 931],
|
||||
"coordinates": [1087, 983],
|
||||
"flag": "fr",
|
||||
"name": "French Republic"
|
||||
"name": "France"
|
||||
},
|
||||
{
|
||||
"coordinates": [1541, 1180],
|
||||
"flag": "it",
|
||||
"name": "Italian Republic"
|
||||
"name": "Italy"
|
||||
},
|
||||
{
|
||||
"coordinates": [1339, 983],
|
||||
"coordinates": [1339, 997],
|
||||
"flag": "ch",
|
||||
"name": "Swiss Confederation"
|
||||
"name": "Switzerland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1360, 428],
|
||||
"flag": "no",
|
||||
"name": "Kingdom of Norway"
|
||||
"name": "Norway"
|
||||
},
|
||||
{
|
||||
"coordinates": [1605, 573],
|
||||
"coordinates": [1609, 477],
|
||||
"flag": "se",
|
||||
"name": "Kingdom of Sweden"
|
||||
"name": "Sweden"
|
||||
},
|
||||
{
|
||||
"coordinates": [2007, 309],
|
||||
"coordinates": [2032, 346],
|
||||
"flag": "fi",
|
||||
"name": "Republic of Finland"
|
||||
"name": "Finland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1200, 830],
|
||||
"coordinates": [1182, 836],
|
||||
"flag": "be",
|
||||
"name": "Kingdom of Belgium"
|
||||
"name": "Belgium"
|
||||
},
|
||||
{
|
||||
"coordinates": [1264, 752],
|
||||
"coordinates": [1268, 764],
|
||||
"flag": "nl",
|
||||
"name": "Kingdom of the Netherlands"
|
||||
"name": "Netherlands"
|
||||
},
|
||||
{
|
||||
"coordinates": [1443, 798],
|
||||
"coordinates": [1445, 821],
|
||||
"flag": "de",
|
||||
"name": "Federal Republic of Germany"
|
||||
"name": "Germany"
|
||||
},
|
||||
{
|
||||
"coordinates": [1444, 969],
|
||||
"coordinates": [1507, 959],
|
||||
"flag": "at",
|
||||
"name": "Republic of Austria"
|
||||
"name": "Austria"
|
||||
},
|
||||
{
|
||||
"coordinates": [1850, 810],
|
||||
"coordinates": [1751, 772],
|
||||
"flag": "pl",
|
||||
"name": "Republic of Poland"
|
||||
"name": "Poland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1630, 909],
|
||||
"coordinates": [1618, 874],
|
||||
"flag": "cz",
|
||||
"name": "Czech Republic"
|
||||
"name": "Czechia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2342, 936],
|
||||
"coordinates": [2231, 885],
|
||||
"flag": "ua",
|
||||
"name": "Ukraine"
|
||||
},
|
||||
{
|
||||
"coordinates": [2167, 708],
|
||||
"coordinates": [2206, 676],
|
||||
"flag": "by",
|
||||
"name": "Republic of Belarus"
|
||||
"name": "Belarus"
|
||||
},
|
||||
{
|
||||
"coordinates": [2046, 990],
|
||||
@@ -107,29 +107,29 @@
|
||||
"name": "Romania"
|
||||
},
|
||||
{
|
||||
"coordinates": [2432, 1265],
|
||||
"coordinates": [2334, 1313],
|
||||
"flag": "tr",
|
||||
"name": "Republic of Turkiye"
|
||||
"name": "Türkiye"
|
||||
},
|
||||
{
|
||||
"coordinates": [769, 1535],
|
||||
"flag": "ma",
|
||||
"name": "Kingdom of Morocco"
|
||||
"name": "Morocco"
|
||||
},
|
||||
{
|
||||
"coordinates": [2535, 720],
|
||||
"coordinates": [2591, 610],
|
||||
"flag": "ru",
|
||||
"name": "Russian Federation"
|
||||
"name": "Russia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2539, 1455],
|
||||
"coordinates": [2538, 1418],
|
||||
"flag": "sy",
|
||||
"name": "Syrian Arab Republic"
|
||||
"name": "Syria"
|
||||
},
|
||||
{
|
||||
"coordinates": [2689, 1441],
|
||||
"flag": "iq",
|
||||
"name": "Republic of Iraq"
|
||||
"name": "Iraq"
|
||||
},
|
||||
{
|
||||
"coordinates": [2748, 1138],
|
||||
@@ -137,9 +137,9 @@
|
||||
"name": "Georgia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1389, 1473],
|
||||
"coordinates": [1359, 1473],
|
||||
"flag": "tn",
|
||||
"name": "Republic of Tunisia"
|
||||
"name": "Tunisia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1066, 1506],
|
||||
@@ -149,62 +149,62 @@
|
||||
{
|
||||
"coordinates": [680, 1254],
|
||||
"flag": "pt",
|
||||
"name": "Portuguese Republic"
|
||||
"name": "Portugal"
|
||||
},
|
||||
{
|
||||
"coordinates": [1891, 1299],
|
||||
"flag": "gr",
|
||||
"name": "Hellenic Republic"
|
||||
"name": "Greece"
|
||||
},
|
||||
{
|
||||
"coordinates": [1906, 1113],
|
||||
"flag": "rs",
|
||||
"name": "Republic of Serbia"
|
||||
"name": "Serbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1751, 983],
|
||||
"flag": "hu",
|
||||
"name": "Republic of Hungary"
|
||||
"name": "Hungary"
|
||||
},
|
||||
{
|
||||
"coordinates": [1784, 908],
|
||||
"coordinates": [1826, 902],
|
||||
"flag": "sk",
|
||||
"name": "Slovak Republic"
|
||||
"name": "Slovakia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1624, 1038],
|
||||
"flag": "hr",
|
||||
"name": "Republic of Croatia"
|
||||
"name": "Croatia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1734, 1094],
|
||||
"coordinates": [1738, 1107],
|
||||
"flag": "ba",
|
||||
"name": "Bosnia and Herzegovina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1817, 1213],
|
||||
"flag": "al",
|
||||
"name": "Republic of Albania"
|
||||
"name": "Albania"
|
||||
},
|
||||
{
|
||||
"coordinates": [2092, 1158],
|
||||
"flag": "bg",
|
||||
"name": "Republic of Bulgaria"
|
||||
"name": "Bulgaria"
|
||||
},
|
||||
{
|
||||
"coordinates": [1939, 702],
|
||||
"coordinates": [1958, 704],
|
||||
"flag": "lt",
|
||||
"name": "Republic of Lithuania"
|
||||
"name": "Lithuania"
|
||||
},
|
||||
{
|
||||
"coordinates": [2014, 618],
|
||||
"coordinates": [2030, 617],
|
||||
"flag": "lv",
|
||||
"name": "Republic of Latvia"
|
||||
"name": "Latvia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2033, 504],
|
||||
"coordinates": [2052, 516],
|
||||
"flag": "ee",
|
||||
"name": "Republic of Estonia"
|
||||
"name": "Estonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [863, 775],
|
||||
@@ -212,14 +212,14 @@
|
||||
"name": "Wales"
|
||||
},
|
||||
{
|
||||
"coordinates": [863, 573],
|
||||
"coordinates": [841, 603],
|
||||
"flag": "gb-sct",
|
||||
"name": "Scotland"
|
||||
},
|
||||
{
|
||||
"coordinates": [2688, 427],
|
||||
"flag": "ussr",
|
||||
"name": "USSR"
|
||||
"coordinates": [2890, 857],
|
||||
"flag": "kz",
|
||||
"name": "Kazakhstan"
|
||||
},
|
||||
{
|
||||
"coordinates": [719, 685],
|
||||
@@ -227,44 +227,54 @@
|
||||
"name": "Northern Ireland"
|
||||
},
|
||||
{
|
||||
"coordinates": [2011, 103],
|
||||
"flag": "polar_bears",
|
||||
"name": "Polar Bears"
|
||||
"coordinates": [1900, 132],
|
||||
"flag": "Sami flag",
|
||||
"name": "Sápmi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1369, 628],
|
||||
"coordinates": [1377, 625],
|
||||
"flag": "dk",
|
||||
"name": "Kingdom of Denmark"
|
||||
"name": "Denmark"
|
||||
},
|
||||
{
|
||||
"coordinates": [2406, 1638],
|
||||
"flag": "il",
|
||||
"name": "State of Israel"
|
||||
"name": "Israel"
|
||||
},
|
||||
{
|
||||
"coordinates": [2226, 1661],
|
||||
"flag": "eg",
|
||||
"name": "Arab Republic of Egypt"
|
||||
"name": "Egypt"
|
||||
},
|
||||
{
|
||||
"coordinates": [1847, 1652],
|
||||
"flag": "ly",
|
||||
"name": "State of Libya"
|
||||
"name": "Libya"
|
||||
},
|
||||
{
|
||||
"coordinates": [2571, 1601],
|
||||
"coordinates": [2535, 1609],
|
||||
"flag": "jo",
|
||||
"name": "Hashemite Kingdom of Jordan"
|
||||
"name": "Jordan"
|
||||
},
|
||||
{
|
||||
"coordinates": [2473, 1528],
|
||||
"flag": "lb",
|
||||
"name": "Lebanese Republic"
|
||||
"name": "Lebanon"
|
||||
},
|
||||
{
|
||||
"coordinates": [254, 274],
|
||||
"coordinates": [266, 265],
|
||||
"flag": "is",
|
||||
"name": "Iceland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1045, 1188],
|
||||
"flag": "ad",
|
||||
"name": "Andorra"
|
||||
},
|
||||
{
|
||||
"coordinates": [1290, 1121],
|
||||
"flag": "mc",
|
||||
"name": "Monaco"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -27,17 +27,17 @@
|
||||
"name": "Ireland"
|
||||
},
|
||||
{
|
||||
"coordinates": [650, 500],
|
||||
"coordinates": [650, 477],
|
||||
"flag": "gb",
|
||||
"name": "United Kingdom"
|
||||
},
|
||||
{
|
||||
"coordinates": [560, 800],
|
||||
"coordinates": [612, 809],
|
||||
"flag": "es",
|
||||
"name": "Spain"
|
||||
},
|
||||
{
|
||||
"coordinates": [726, 616],
|
||||
"coordinates": [729, 648],
|
||||
"flag": "fr",
|
||||
"name": "France"
|
||||
},
|
||||
@@ -47,32 +47,32 @@
|
||||
"name": "Italy"
|
||||
},
|
||||
{
|
||||
"coordinates": [872, 634],
|
||||
"coordinates": [895, 641],
|
||||
"flag": "ch",
|
||||
"name": "Switzerland"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 271],
|
||||
"coordinates": [935, 259],
|
||||
"flag": "no",
|
||||
"name": "Norway"
|
||||
},
|
||||
{
|
||||
"coordinates": [1095, 336],
|
||||
"coordinates": [1105, 286],
|
||||
"flag": "se",
|
||||
"name": "Sweden"
|
||||
},
|
||||
{
|
||||
"coordinates": [1403, 235],
|
||||
"coordinates": [1438, 209],
|
||||
"flag": "fi",
|
||||
"name": "Finland"
|
||||
},
|
||||
{
|
||||
"coordinates": [775, 541],
|
||||
"coordinates": [819, 534],
|
||||
"flag": "be",
|
||||
"name": "Belgium"
|
||||
},
|
||||
{
|
||||
"coordinates": [868, 487],
|
||||
"coordinates": [868, 485],
|
||||
"flag": "nl",
|
||||
"name": "Netherlands"
|
||||
},
|
||||
@@ -87,14 +87,14 @@
|
||||
"name": "Austria"
|
||||
},
|
||||
{
|
||||
"coordinates": [1120, 477],
|
||||
"coordinates": [1220, 491],
|
||||
"flag": "pl",
|
||||
"name": "Poland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1060, 530],
|
||||
"coordinates": [1078, 564],
|
||||
"flag": "cz",
|
||||
"name": "Czech Republic"
|
||||
"name": "Czechia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1540, 602],
|
||||
@@ -102,19 +102,19 @@
|
||||
"name": "Ukraine"
|
||||
},
|
||||
{
|
||||
"coordinates": [1500, 440],
|
||||
"coordinates": [1517, 424],
|
||||
"flag": "by",
|
||||
"name": "Belarus"
|
||||
},
|
||||
{
|
||||
"coordinates": [1400, 670],
|
||||
"coordinates": [1414, 667],
|
||||
"flag": "ro",
|
||||
"name": "Romania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1580, 834],
|
||||
"coordinates": [1614, 834],
|
||||
"flag": "tr",
|
||||
"name": "Turkey"
|
||||
"name": "Türkiye"
|
||||
},
|
||||
{
|
||||
"coordinates": [525, 955],
|
||||
@@ -122,14 +122,14 @@
|
||||
"name": "Morocco"
|
||||
},
|
||||
{
|
||||
"coordinates": [1674, 449],
|
||||
"coordinates": [1771, 413],
|
||||
"flag": "ru",
|
||||
"name": "Russia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1750, 950],
|
||||
"flag": "sy",
|
||||
"name": "Syrian Arab Republic"
|
||||
"name": "Syria"
|
||||
},
|
||||
{
|
||||
"coordinates": [1930, 950],
|
||||
@@ -167,9 +167,39 @@
|
||||
"name": "Serbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1200, 630],
|
||||
"coordinates": [1218, 602],
|
||||
"flag": "hu",
|
||||
"name": "Hungary"
|
||||
},
|
||||
{
|
||||
"coordinates": [1277, 90],
|
||||
"flag": "Sami flag",
|
||||
"name": "Sápmi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1406, 324],
|
||||
"flag": "ee",
|
||||
"name": "Estonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1380, 384],
|
||||
"flag": "lv",
|
||||
"name": "Latvia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1355, 444],
|
||||
"flag": "lt",
|
||||
"name": "Lithuania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1121, 668],
|
||||
"flag": "hr",
|
||||
"name": "Croatia"
|
||||
},
|
||||
{
|
||||
"coordinates": [959, 394],
|
||||
"flag": "dk",
|
||||
"name": "Denmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,154 +17,139 @@
|
||||
"name": "GatewayToTheAtlantic",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [2144, 344],
|
||||
"coordinates": [2161, 420],
|
||||
"flag": "ch",
|
||||
"name": "Swiss Confederation"
|
||||
},
|
||||
{
|
||||
"coordinates": [1964, 371],
|
||||
"coordinates": [1938, 263],
|
||||
"flag": "burgundy",
|
||||
"name": "Kingdom of Burgundy"
|
||||
"name": "Duchy of Burgundy"
|
||||
},
|
||||
{
|
||||
"coordinates": [1334, 537],
|
||||
"coordinates": [1388, 485],
|
||||
"flag": "aquitaine",
|
||||
"name": "Duchy of Aquitaine"
|
||||
},
|
||||
{
|
||||
"coordinates": [2115, 684],
|
||||
"coordinates": [2137, 636],
|
||||
"flag": "provence",
|
||||
"name": "County of Provence"
|
||||
},
|
||||
{
|
||||
"coordinates": [1207, 763],
|
||||
"flag": "es-pv",
|
||||
"name": "The Basque"
|
||||
"coordinates": [1266, 748],
|
||||
"flag": "navarre",
|
||||
"name": "Kingdom of Navarre"
|
||||
},
|
||||
{
|
||||
"coordinates": [1281, 1142],
|
||||
"coordinates": [1375, 1190],
|
||||
"flag": "valencia",
|
||||
"name": "Kingdom of Valencia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1660, 891],
|
||||
"coordinates": [1696, 858],
|
||||
"flag": "catalonia",
|
||||
"name": "Catalonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [561, 764],
|
||||
"coordinates": [543, 807],
|
||||
"flag": "es-ga",
|
||||
"name": "Kingdom of Galicia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1004, 1436],
|
||||
"coordinates": [1128, 1388],
|
||||
"flag": "granada",
|
||||
"name": "Emirate of Granada"
|
||||
},
|
||||
{
|
||||
"coordinates": [431, 1197],
|
||||
"flag": "pt",
|
||||
"name": "Portuguese Republic"
|
||||
"coordinates": [523, 1156],
|
||||
"flag": "kingdom_of_portugal",
|
||||
"name": "Kingdom of Portugal"
|
||||
},
|
||||
{
|
||||
"coordinates": [560, 1894],
|
||||
"flag": "ma",
|
||||
"name": "Kingdom of Morocco"
|
||||
"coordinates": [851, 1805],
|
||||
"flag": "marinid",
|
||||
"name": "Marinid Sultanate"
|
||||
},
|
||||
{
|
||||
"coordinates": [1609, 1837],
|
||||
"flag": "dz",
|
||||
"name": "Algeria"
|
||||
"coordinates": [1424, 1725],
|
||||
"flag": "zayyanid",
|
||||
"name": "Zayyanid Sultanate"
|
||||
},
|
||||
{
|
||||
"coordinates": [1733, 622],
|
||||
"coordinates": [1604, 641],
|
||||
"flag": "armagnac",
|
||||
"name": "County of Armagnac"
|
||||
},
|
||||
{
|
||||
"coordinates": [896, 1240],
|
||||
"coordinates": [946, 1300],
|
||||
"flag": "cordoba",
|
||||
"name": "City of Cordoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [636, 1781],
|
||||
"flag": "seville",
|
||||
"name": "City of Seville"
|
||||
},
|
||||
{
|
||||
"coordinates": [750, 873],
|
||||
"coordinates": [740, 1021],
|
||||
"flag": "leon",
|
||||
"name": "Kingdom of Leon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1001, 882],
|
||||
"coordinates": [1040, 1036],
|
||||
"flag": "castille",
|
||||
"name": "Kingdom of Castille"
|
||||
"name": "Crown of Castile"
|
||||
},
|
||||
{
|
||||
"coordinates": [775, 724],
|
||||
"coordinates": [847, 767],
|
||||
"flag": "asturias",
|
||||
"name": "Principality of Asturias"
|
||||
},
|
||||
{
|
||||
"coordinates": [1755, 1130],
|
||||
"flag": "neuragic_empire",
|
||||
"name": "The Old Ones"
|
||||
"flag": "majorca",
|
||||
"name": "Kingdom of Majorca"
|
||||
},
|
||||
{
|
||||
"coordinates": [2097, 1670],
|
||||
"flag": "Amazigh flag",
|
||||
"name": "Tamazgha"
|
||||
"coordinates": [2004, 1630],
|
||||
"flag": "hafsid",
|
||||
"name": "Hafsid Sultanate"
|
||||
},
|
||||
{
|
||||
"coordinates": [979, 1013],
|
||||
"flag": "es",
|
||||
"name": "Kingdom of Spain"
|
||||
"coordinates": [1374, 926],
|
||||
"flag": "catalonia",
|
||||
"name": "Crown of Aragon"
|
||||
},
|
||||
{
|
||||
"coordinates": [468, 930],
|
||||
"flag": "sardines",
|
||||
"name": "Sardines"
|
||||
},
|
||||
{
|
||||
"coordinates": [1667, 96],
|
||||
"coordinates": [1695, 119],
|
||||
"flag": "paris",
|
||||
"name": "City of Paris"
|
||||
},
|
||||
{
|
||||
"coordinates": [1716, 296],
|
||||
"flag": "baguette",
|
||||
"name": "Baguettes"
|
||||
},
|
||||
{
|
||||
"coordinates": [1017, 180],
|
||||
"coordinates": [1121, 221],
|
||||
"flag": "brittany",
|
||||
"name": "Kingdom of Brittany"
|
||||
},
|
||||
{
|
||||
"coordinates": [2072, 567],
|
||||
"flag": "antipope",
|
||||
"name": "An Anti-Pope"
|
||||
"coordinates": [1933, 614],
|
||||
"flag": "avignon",
|
||||
"name": "City of Avignon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1355, 76],
|
||||
"coordinates": [1434, 129],
|
||||
"flag": "normandy",
|
||||
"name": "Duchy of Normandy"
|
||||
},
|
||||
{
|
||||
"coordinates": [1402, 529],
|
||||
"flag": "",
|
||||
"name": "Wine"
|
||||
"coordinates": [1644, 383],
|
||||
"flag": "Franks",
|
||||
"name": "Kingdom of France"
|
||||
},
|
||||
{
|
||||
"coordinates": [1475, 1657],
|
||||
"flag": "French foreign legion",
|
||||
"name": "French Foreign Legion"
|
||||
"coordinates": [772, 1399],
|
||||
"flag": "seville",
|
||||
"name": "City of Seville"
|
||||
},
|
||||
{
|
||||
"coordinates": [1685, 417],
|
||||
"flag": "fr",
|
||||
"name": "French Republic"
|
||||
"coordinates": [2147, 90],
|
||||
"flag": "Holy Roman Empire",
|
||||
"name": "Holy Roman Empire"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,14 +22,9 @@
|
||||
"name": "Türkiye"
|
||||
},
|
||||
{
|
||||
"coordinates": [2030, 409],
|
||||
"flag": "west_germany",
|
||||
"name": "West Germany"
|
||||
},
|
||||
{
|
||||
"coordinates": [2074, 382],
|
||||
"flag": "east_germany",
|
||||
"name": "East Germany"
|
||||
"coordinates": [2050, 395],
|
||||
"flag": "de",
|
||||
"name": "Germany"
|
||||
},
|
||||
{
|
||||
"coordinates": [1966, 442],
|
||||
@@ -38,7 +33,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [1872, 528],
|
||||
"flag": "Fascist Spain",
|
||||
"flag": "es",
|
||||
"name": "Spain"
|
||||
},
|
||||
{
|
||||
@@ -73,12 +68,12 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2135, 481],
|
||||
"flag": "yugoslavia",
|
||||
"name": "Yugoslavia"
|
||||
"flag": "rs",
|
||||
"name": "Serbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2242, 461],
|
||||
"flag": "Communist Romania",
|
||||
"flag": "ro",
|
||||
"name": "Romania"
|
||||
},
|
||||
{
|
||||
@@ -88,8 +83,8 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2272, 418],
|
||||
"flag": "Ukrainian SSR",
|
||||
"name": "Ukrainian SSR"
|
||||
"flag": "ua",
|
||||
"name": "Ukraine"
|
||||
},
|
||||
{
|
||||
"coordinates": [2093, 297],
|
||||
@@ -104,7 +99,7 @@
|
||||
{
|
||||
"coordinates": [2191, 194],
|
||||
"flag": "Sami flag",
|
||||
"name": "Sapmi"
|
||||
"name": "Sápmi"
|
||||
},
|
||||
{
|
||||
"coordinates": [2206, 262],
|
||||
@@ -113,22 +108,22 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2376, 363],
|
||||
"flag": "Russian SSR",
|
||||
"name": "Russian SSR"
|
||||
"flag": "ru",
|
||||
"name": "Russia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2222, 371],
|
||||
"flag": "Byelorussian SSR",
|
||||
"name": "Byelorussian SSR"
|
||||
"flag": "by",
|
||||
"name": "Belarus"
|
||||
},
|
||||
{
|
||||
"coordinates": [2441, 507],
|
||||
"flag": "Georgian SSR",
|
||||
"name": "Georgian SSR"
|
||||
"flag": "ge",
|
||||
"name": "Georgia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2402, 580],
|
||||
"flag": "Second Republic of Iraq",
|
||||
"flag": "iq",
|
||||
"name": "Iraq"
|
||||
},
|
||||
{
|
||||
@@ -142,14 +137,9 @@
|
||||
"name": "Saudi Arabia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2434, 815],
|
||||
"flag": "North yemen",
|
||||
"name": "North Yemen"
|
||||
},
|
||||
{
|
||||
"coordinates": [2479, 824],
|
||||
"flag": "south yemen",
|
||||
"name": "South Yemen"
|
||||
"coordinates": [2456, 820],
|
||||
"flag": "ye",
|
||||
"name": "Yemen"
|
||||
},
|
||||
{
|
||||
"coordinates": [2554, 724],
|
||||
@@ -158,7 +148,7 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2532, 609],
|
||||
"flag": "Pahlavi Iran",
|
||||
"flag": "ir",
|
||||
"name": "Iran"
|
||||
},
|
||||
{
|
||||
@@ -173,21 +163,16 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2727, 416],
|
||||
"flag": "Kazakh SSR",
|
||||
"name": "Kazakh SSR"
|
||||
"flag": "kz",
|
||||
"name": "Kazakhstan"
|
||||
},
|
||||
{
|
||||
"coordinates": [2556, 544],
|
||||
"flag": "Turkmen SSR",
|
||||
"name": "Turkmen SSR"
|
||||
"flag": "tm",
|
||||
"name": "Turkmenistan"
|
||||
},
|
||||
{
|
||||
"coordinates": [2947, 362],
|
||||
"flag": "Zheleznogorsk",
|
||||
"name": "Zheleznogorsk"
|
||||
},
|
||||
{
|
||||
"coordinates": [3252, 229],
|
||||
"coordinates": [3255, 278],
|
||||
"flag": "Siberia",
|
||||
"name": "Siberia"
|
||||
},
|
||||
@@ -207,12 +192,12 @@
|
||||
"name": "Bangladesh"
|
||||
},
|
||||
{
|
||||
"coordinates": [2868, 635],
|
||||
"coordinates": [2870, 656],
|
||||
"flag": "np",
|
||||
"name": "Nepal"
|
||||
},
|
||||
{
|
||||
"coordinates": [3254, 672],
|
||||
"coordinates": [3172, 624],
|
||||
"flag": "cn",
|
||||
"name": "China"
|
||||
},
|
||||
@@ -242,24 +227,19 @@
|
||||
"name": "Indonesia"
|
||||
},
|
||||
{
|
||||
"coordinates": [3121, 755],
|
||||
"coordinates": [3145, 802],
|
||||
"flag": "vn",
|
||||
"name": "North Vietnam"
|
||||
"name": "Vietnam"
|
||||
},
|
||||
{
|
||||
"coordinates": [3153, 833],
|
||||
"flag": "South Vietnam",
|
||||
"name": "South Vietnam"
|
||||
"coordinates": [3020, 750],
|
||||
"flag": "mm",
|
||||
"name": "Myanmar"
|
||||
},
|
||||
{
|
||||
"coordinates": [3013, 722],
|
||||
"flag": "Burma2",
|
||||
"name": "Burma"
|
||||
},
|
||||
{
|
||||
"coordinates": [3095, 822],
|
||||
"flag": "kh",
|
||||
"name": "Cambodia"
|
||||
"coordinates": [3082, 822],
|
||||
"flag": "th",
|
||||
"name": "Thailand"
|
||||
},
|
||||
{
|
||||
"coordinates": [3538, 1067],
|
||||
@@ -267,12 +247,12 @@
|
||||
"name": "Papua New Guinea"
|
||||
},
|
||||
{
|
||||
"coordinates": [3542, 1356],
|
||||
"coordinates": [3570, 1392],
|
||||
"flag": "au",
|
||||
"name": "Australia"
|
||||
},
|
||||
{
|
||||
"coordinates": [3422, 1203],
|
||||
"coordinates": [3416, 1213],
|
||||
"flag": "Australian Aboriginal Flag",
|
||||
"name": "Nawan-mirri"
|
||||
},
|
||||
@@ -292,14 +272,14 @@
|
||||
"name": "Tunisia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2116, 653],
|
||||
"coordinates": [2138, 675],
|
||||
"flag": "ly",
|
||||
"name": "Libya"
|
||||
},
|
||||
{
|
||||
"coordinates": [2281, 653],
|
||||
"flag": "United Arab Republic",
|
||||
"name": "United Arab Republic"
|
||||
"flag": "eg",
|
||||
"name": "Egypt"
|
||||
},
|
||||
{
|
||||
"coordinates": [1859, 613],
|
||||
@@ -307,12 +287,12 @@
|
||||
"name": "Morocco"
|
||||
},
|
||||
{
|
||||
"coordinates": [1943, 615],
|
||||
"coordinates": [1952, 671],
|
||||
"flag": "dz",
|
||||
"name": "Algeria"
|
||||
},
|
||||
{
|
||||
"coordinates": [2317, 754],
|
||||
"coordinates": [2271, 788],
|
||||
"flag": "sd",
|
||||
"name": "Sudan"
|
||||
},
|
||||
@@ -323,26 +303,26 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2352, 895],
|
||||
"flag": "Imperial Ethiopia",
|
||||
"flag": "et",
|
||||
"name": "Ethiopia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1790, 729],
|
||||
"flag": "Mauritania",
|
||||
"flag": "mr",
|
||||
"name": "Mauritania"
|
||||
},
|
||||
{
|
||||
"coordinates": [2154, 764],
|
||||
"coordinates": [2118, 768],
|
||||
"flag": "td",
|
||||
"name": "Chad"
|
||||
},
|
||||
{
|
||||
"coordinates": [2051, 745],
|
||||
"coordinates": [2009, 810],
|
||||
"flag": "ne",
|
||||
"name": "Niger"
|
||||
},
|
||||
{
|
||||
"coordinates": [2040, 930],
|
||||
"coordinates": [1996, 909],
|
||||
"flag": "ng",
|
||||
"name": "Nigeria"
|
||||
},
|
||||
@@ -358,12 +338,12 @@
|
||||
},
|
||||
{
|
||||
"coordinates": [2197, 1070],
|
||||
"flag": "Zaire",
|
||||
"name": "Zaire"
|
||||
"flag": "cd",
|
||||
"name": "DR Congo"
|
||||
},
|
||||
{
|
||||
"coordinates": [2189, 1372],
|
||||
"flag": "Apartheid South Africa",
|
||||
"coordinates": [2211, 1389],
|
||||
"flag": "za",
|
||||
"name": "South Africa"
|
||||
},
|
||||
{
|
||||
@@ -382,14 +362,14 @@
|
||||
"name": "Tanzania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1934, 762],
|
||||
"coordinates": [1894, 789],
|
||||
"flag": "ml",
|
||||
"name": "Mali"
|
||||
},
|
||||
{
|
||||
"coordinates": [2128, 1292],
|
||||
"flag": "Apartheid South Africa",
|
||||
"name": "South West Africa"
|
||||
"flag": "na",
|
||||
"name": "Namibia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2099, 1178],
|
||||
@@ -397,37 +377,37 @@
|
||||
"name": "Angola"
|
||||
},
|
||||
{
|
||||
"coordinates": [1375, 1121],
|
||||
"coordinates": [1418, 1167],
|
||||
"flag": "br",
|
||||
"name": "Brazil"
|
||||
},
|
||||
{
|
||||
"coordinates": [1203, 1059],
|
||||
"coordinates": [1225, 1065],
|
||||
"flag": "amazonas",
|
||||
"name": "Amazonas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1210, 1395],
|
||||
"coordinates": [1243, 1428],
|
||||
"flag": "ar",
|
||||
"name": "Argentina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1107, 1419],
|
||||
"coordinates": [1125, 1356],
|
||||
"flag": "cl",
|
||||
"name": "Chile"
|
||||
},
|
||||
{
|
||||
"coordinates": [1064, 1114],
|
||||
"coordinates": [1064, 1111],
|
||||
"flag": "pe",
|
||||
"name": "Peru"
|
||||
},
|
||||
{
|
||||
"coordinates": [1065, 938],
|
||||
"coordinates": [1083, 938],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1192, 938],
|
||||
"coordinates": [1183, 921],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
},
|
||||
@@ -437,17 +417,17 @@
|
||||
"name": "Nicaragua"
|
||||
},
|
||||
{
|
||||
"coordinates": [788, 744],
|
||||
"coordinates": [764, 743],
|
||||
"flag": "mx",
|
||||
"name": "Mexico"
|
||||
},
|
||||
{
|
||||
"coordinates": [1011, 555],
|
||||
"coordinates": [1034, 556],
|
||||
"flag": "us",
|
||||
"name": "USA"
|
||||
"name": "United States"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 624],
|
||||
"coordinates": [766, 623],
|
||||
"flag": "Texas",
|
||||
"name": "Texas"
|
||||
},
|
||||
@@ -457,22 +437,22 @@
|
||||
"name": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [703, 483],
|
||||
"coordinates": [654, 530],
|
||||
"flag": "Utah",
|
||||
"name": "Utah"
|
||||
},
|
||||
{
|
||||
"coordinates": [1077, 444],
|
||||
"coordinates": [1079, 385],
|
||||
"flag": "Quebec",
|
||||
"name": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [1231, 395],
|
||||
"flag": "Newfoundland",
|
||||
"name": "Newfoundland"
|
||||
"coordinates": [1211, 364],
|
||||
"flag": "newfoundlandandlabrador",
|
||||
"name": "Newfoundland and Labrador"
|
||||
},
|
||||
{
|
||||
"coordinates": [967, 418],
|
||||
"coordinates": [957, 406],
|
||||
"flag": "ca",
|
||||
"name": "Canada"
|
||||
},
|
||||
@@ -482,24 +462,94 @@
|
||||
"name": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [741, 234],
|
||||
"coordinates": [857, 232],
|
||||
"flag": "Nunavut",
|
||||
"name": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [484, 256],
|
||||
"coordinates": [475, 254],
|
||||
"flag": "Yukon",
|
||||
"name": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1434, 223],
|
||||
"coordinates": [1448, 137],
|
||||
"flag": "gl",
|
||||
"name": "Greenland"
|
||||
},
|
||||
{
|
||||
"coordinates": [2247, 1229],
|
||||
"flag": "Rhodesia",
|
||||
"name": "Rhodesia"
|
||||
"flag": "zw",
|
||||
"name": "Zimbabwe"
|
||||
},
|
||||
{
|
||||
"coordinates": [550, 438],
|
||||
"flag": "Washington",
|
||||
"name": "Washington"
|
||||
},
|
||||
{
|
||||
"coordinates": [778, 518],
|
||||
"flag": "Kansas",
|
||||
"name": "Kansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [912, 613],
|
||||
"flag": "Mississippi",
|
||||
"name": "Mississippi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1208, 1219],
|
||||
"flag": "bo",
|
||||
"name": "Bolivia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2059, 1015],
|
||||
"flag": "ga",
|
||||
"name": "Gabon"
|
||||
},
|
||||
{
|
||||
"coordinates": [689, 437],
|
||||
"flag": "Montana",
|
||||
"name": "Montana"
|
||||
},
|
||||
{
|
||||
"coordinates": [517, 357],
|
||||
"flag": "britishcolumbia",
|
||||
"name": "British Columbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [618, 358],
|
||||
"flag": "alberta",
|
||||
"name": "Alberta"
|
||||
},
|
||||
{
|
||||
"coordinates": [732, 360],
|
||||
"flag": "saskatchewan",
|
||||
"name": "Saskatchewan"
|
||||
},
|
||||
{
|
||||
"coordinates": [834, 367],
|
||||
"flag": "manitoba",
|
||||
"name": "Manitoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [1108, 500],
|
||||
"flag": "Massachusetts",
|
||||
"name": "Massachusetts"
|
||||
},
|
||||
{
|
||||
"coordinates": [845, 465],
|
||||
"flag": "Minnesota",
|
||||
"name": "Minnesota"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 500],
|
||||
"flag": "Michigan",
|
||||
"name": "Michigan"
|
||||
},
|
||||
{
|
||||
"coordinates": [655, 247],
|
||||
"flag": "northwestterritories",
|
||||
"name": "Northwest Territories"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"map": {
|
||||
"height": 2320,
|
||||
"num_land_tiles": 2069756,
|
||||
"height": 2276,
|
||||
"num_land_tiles": 2066156,
|
||||
"width": 1800
|
||||
},
|
||||
"map16x": {
|
||||
"height": 580,
|
||||
"height": 569,
|
||||
"num_land_tiles": 123168,
|
||||
"width": 450
|
||||
},
|
||||
"map4x": {
|
||||
"height": 1160,
|
||||
"num_land_tiles": 509370,
|
||||
"height": 1138,
|
||||
"num_land_tiles": 508470,
|
||||
"width": 900
|
||||
},
|
||||
"name": "Los Angeles",
|
||||
@@ -97,10 +97,15 @@
|
||||
"name": "Skid Row"
|
||||
},
|
||||
{
|
||||
"coordinates": [925, 935],
|
||||
"coordinates": [965, 865],
|
||||
"flag": "California",
|
||||
"name": "Inglewood"
|
||||
},
|
||||
{
|
||||
"coordinates": [815, 945],
|
||||
"flag": "California",
|
||||
"name": "L.A.X."
|
||||
},
|
||||
{
|
||||
"coordinates": [1180, 1010],
|
||||
"flag": "California",
|
||||
|
||||
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"map": {
|
||||
"height": 3500,
|
||||
"num_land_tiles": 1646031,
|
||||
"height": 3508,
|
||||
"num_land_tiles": 1517614,
|
||||
"width": 1308
|
||||
},
|
||||
"map16x": {
|
||||
"height": 875,
|
||||
"num_land_tiles": 93490,
|
||||
"height": 877,
|
||||
"num_land_tiles": 84692,
|
||||
"width": 327
|
||||
},
|
||||
"map4x": {
|
||||
"height": 1750,
|
||||
"num_land_tiles": 396787,
|
||||
"height": 1754,
|
||||
"num_land_tiles": 364566,
|
||||
"width": 654
|
||||
},
|
||||
"name": "Luna",
|
||||
@@ -27,12 +27,12 @@
|
||||
"name": "Apollo 14"
|
||||
},
|
||||
{
|
||||
"coordinates": [780, 345],
|
||||
"coordinates": [780, 340],
|
||||
"flag": "us",
|
||||
"name": "Apollo 15"
|
||||
},
|
||||
{
|
||||
"coordinates": [825, 735],
|
||||
"coordinates": [825, 705],
|
||||
"flag": "us",
|
||||
"name": "Apollo 11"
|
||||
},
|
||||
@@ -52,7 +52,7 @@
|
||||
"name": "Surveyor 3"
|
||||
},
|
||||
{
|
||||
"coordinates": [256, 148],
|
||||
"coordinates": [250, 148],
|
||||
"flag": "us",
|
||||
"name": "Apollo 13"
|
||||
},
|
||||
@@ -62,7 +62,7 @@
|
||||
"name": "Artemis II"
|
||||
},
|
||||
{
|
||||
"coordinates": [510, 170],
|
||||
"coordinates": [515, 170],
|
||||
"flag": "Russian SSR",
|
||||
"name": "Luna 17"
|
||||
},
|
||||
@@ -107,7 +107,7 @@
|
||||
"name": "Chang'e 4"
|
||||
},
|
||||
{
|
||||
"coordinates": [270, 2690],
|
||||
"coordinates": [260, 268],
|
||||
"flag": "cn",
|
||||
"name": "Chang'e 5"
|
||||
},
|
||||
@@ -117,7 +117,7 @@
|
||||
"name": "Chang'e 6"
|
||||
},
|
||||
{
|
||||
"coordinates": [830, 735],
|
||||
"coordinates": [830, 745],
|
||||
"flag": "jp",
|
||||
"name": "S.L.I.M."
|
||||
},
|
||||
@@ -127,34 +127,34 @@
|
||||
"name": "Chandrayaan 3"
|
||||
},
|
||||
{
|
||||
"coordinates": [732, 3490],
|
||||
"coordinates": [732, 3493],
|
||||
"flag": "in",
|
||||
"name": "Chandrayaan 1"
|
||||
},
|
||||
{
|
||||
"coordinates": [755, 3035],
|
||||
"flag": "",
|
||||
"name": "T▅▚░S▅cr▅▟░M▅l▅t▅r▅░B▅s▅"
|
||||
"name": "T▆p░S▅cr▅t░M▊l▊t▅r▆░B▅s▅"
|
||||
},
|
||||
{
|
||||
"coordinates": [628, 921],
|
||||
"flag": "",
|
||||
"name": "[]"
|
||||
"name": "▊"
|
||||
}
|
||||
],
|
||||
"teamGameSpawnAreas": {
|
||||
"2": [
|
||||
{
|
||||
"height": 1750,
|
||||
"height": 1754,
|
||||
"width": 1308,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"height": 1750,
|
||||
"height": 1754,
|
||||
"width": 1308,
|
||||
"x": 0,
|
||||
"y": 1750
|
||||
"y": 1754
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"map": {
|
||||
"height": 2060,
|
||||
"num_land_tiles": 3449078,
|
||||
"width": 2200
|
||||
},
|
||||
"map16x": {
|
||||
"height": 515,
|
||||
"num_land_tiles": 211600,
|
||||
"width": 550
|
||||
},
|
||||
"map4x": {
|
||||
"height": 1030,
|
||||
"num_land_tiles": 856603,
|
||||
"width": 1100
|
||||
},
|
||||
"name": "Middle East",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [300, 65],
|
||||
"flag": "tr",
|
||||
"name": "Ottoman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1639, 558],
|
||||
"flag": "Persia",
|
||||
"name": "Qajar Dynasty"
|
||||
},
|
||||
{
|
||||
"coordinates": [1141, 797],
|
||||
"flag": "Socialist_flag",
|
||||
"name": "Emirate of Kuwait"
|
||||
},
|
||||
{
|
||||
"coordinates": [1880, 1353],
|
||||
"flag": "Socialist_flag",
|
||||
"name": "Sultanate of Muscat"
|
||||
},
|
||||
{
|
||||
"coordinates": [1703, 1402],
|
||||
"flag": "White Flag",
|
||||
"name": "Imamate of Oman"
|
||||
},
|
||||
{
|
||||
"coordinates": [1592, 1239],
|
||||
"flag": "Socialist_flag",
|
||||
"name": "Trucial States"
|
||||
},
|
||||
{
|
||||
"coordinates": [1129, 1875],
|
||||
"flag": "gb",
|
||||
"name": "Aden Protectorate"
|
||||
},
|
||||
{
|
||||
"coordinates": [964, 1744],
|
||||
"flag": "Kingdom of Yemen",
|
||||
"name": "Kingdom of Yemen"
|
||||
},
|
||||
{
|
||||
"coordinates": [844, 1655],
|
||||
"flag": "Emirate of Asir",
|
||||
"name": "Emirate of Asir"
|
||||
},
|
||||
{
|
||||
"coordinates": [579, 1173],
|
||||
"flag": "Arabia",
|
||||
"name": "Kingdom of Hejaz"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 1052],
|
||||
"flag": "Rashidi Emirate",
|
||||
"name": "Rashidi Emirate"
|
||||
},
|
||||
{
|
||||
"coordinates": [1092, 1336],
|
||||
"flag": "Sultanate of Nejd",
|
||||
"name": "Sultanate of Nejd"
|
||||
},
|
||||
{
|
||||
"coordinates": [1397, 1128],
|
||||
"flag": "qa",
|
||||
"name": "Qatar"
|
||||
},
|
||||
{
|
||||
"coordinates": [973, 296],
|
||||
"flag": "Kingdom of Iraq",
|
||||
"name": "Kingdom of Iraq"
|
||||
},
|
||||
{
|
||||
"coordinates": [554, 364],
|
||||
"flag": "Kingdom of Syria",
|
||||
"name": "Kingdom of Syria"
|
||||
},
|
||||
{
|
||||
"coordinates": [423, 647],
|
||||
"flag": "gb",
|
||||
"name": "Palestine Mandate"
|
||||
},
|
||||
{
|
||||
"coordinates": [100, 781],
|
||||
"flag": "Kingdom of Egypt",
|
||||
"name": "Kingdom of Egypt"
|
||||
},
|
||||
{
|
||||
"coordinates": [159, 1530],
|
||||
"flag": "gb",
|
||||
"name": "Anglo-Egyptian Sudan"
|
||||
},
|
||||
{
|
||||
"coordinates": [578, 1766],
|
||||
"flag": "italy",
|
||||
"name": "Italian Eritrea"
|
||||
},
|
||||
{
|
||||
"coordinates": [401, 2005],
|
||||
"flag": "Ethiopian Empire",
|
||||
"name": "Ethiopian Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [826, 2044],
|
||||
"flag": "fr",
|
||||
"name": "French Somaliland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1455, 902],
|
||||
"flag": "gb",
|
||||
"name": "British Bushehr"
|
||||
},
|
||||
{
|
||||
"coordinates": [185, 375],
|
||||
"flag": "gb",
|
||||
"name": "British Cyprus"
|
||||
},
|
||||
{
|
||||
"coordinates": [2127, 373],
|
||||
"flag": "Emirate of Afghanistan",
|
||||
"name": "Emirate of Afghanistan"
|
||||
},
|
||||
{
|
||||
"coordinates": [2087, 925],
|
||||
"flag": "gb",
|
||||
"name": "Baluchistan Agency"
|
||||
},
|
||||
{
|
||||
"coordinates": [932, 15],
|
||||
"flag": "am",
|
||||
"name": "Republic of Armenia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1671, 71],
|
||||
"flag": "ru",
|
||||
"name": "Russian State"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 10 KiB |
@@ -21,11 +21,6 @@
|
||||
"flag": "Florida",
|
||||
"name": "Florida"
|
||||
},
|
||||
{
|
||||
"coordinates": [1010, 435],
|
||||
"flag": "ca",
|
||||
"name": "Canada"
|
||||
},
|
||||
{
|
||||
"coordinates": [1250, 1130],
|
||||
"flag": "mx",
|
||||
@@ -47,47 +42,47 @@
|
||||
"name": "Nicaragua"
|
||||
},
|
||||
{
|
||||
"coordinates": [1734, 1403],
|
||||
"coordinates": [1651, 1399],
|
||||
"flag": "pa",
|
||||
"name": "Panama"
|
||||
},
|
||||
{
|
||||
"coordinates": [1821, 1395],
|
||||
"coordinates": [1760, 1410],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1896, 1379],
|
||||
"coordinates": [1883, 1395],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
},
|
||||
{
|
||||
"coordinates": [1725, 1180],
|
||||
"coordinates": [1692, 1161],
|
||||
"flag": "cu",
|
||||
"name": "Cuba"
|
||||
},
|
||||
{
|
||||
"coordinates": [467, 333],
|
||||
"coordinates": [331, 341],
|
||||
"flag": "Alaska",
|
||||
"name": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [1154, 914],
|
||||
"coordinates": [1062, 932],
|
||||
"flag": "Arizona",
|
||||
"name": "Arizona"
|
||||
},
|
||||
{
|
||||
"coordinates": [1010, 865],
|
||||
"coordinates": [940, 905],
|
||||
"flag": "California",
|
||||
"name": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [1307, 863],
|
||||
"coordinates": [1147, 843],
|
||||
"flag": "Colorado",
|
||||
"name": "Colorado"
|
||||
},
|
||||
{
|
||||
"coordinates": [1673, 965],
|
||||
"coordinates": [1593, 974],
|
||||
"flag": "Georgia_US",
|
||||
"name": "Georgia"
|
||||
},
|
||||
@@ -97,169 +92,274 @@
|
||||
"name": "Hawaii"
|
||||
},
|
||||
{
|
||||
"coordinates": [1120, 760],
|
||||
"coordinates": [1000, 710],
|
||||
"flag": "Idaho",
|
||||
"name": "Idaho"
|
||||
},
|
||||
{
|
||||
"coordinates": [1551, 813],
|
||||
"coordinates": [1490, 789],
|
||||
"flag": "Illinois",
|
||||
"name": "Illinois"
|
||||
},
|
||||
{
|
||||
"coordinates": [1412, 873],
|
||||
"coordinates": [1335, 835],
|
||||
"flag": "Kansas",
|
||||
"name": "Kansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1651, 880],
|
||||
"coordinates": [1525, 872],
|
||||
"flag": "Kentucky",
|
||||
"name": "Kentucky"
|
||||
},
|
||||
{
|
||||
"coordinates": [1514, 1007],
|
||||
"coordinates": [1410, 984],
|
||||
"flag": "Louisiana",
|
||||
"name": "Louisiana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1884, 735],
|
||||
"coordinates": [1854, 720],
|
||||
"flag": "Maine",
|
||||
"name": "Maine"
|
||||
},
|
||||
{
|
||||
"coordinates": [1634, 764],
|
||||
"coordinates": [1572, 762],
|
||||
"flag": "Michigan",
|
||||
"name": "Michigan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1556, 965],
|
||||
"coordinates": [1472, 940],
|
||||
"flag": "Mississippi",
|
||||
"name": "Mississippi"
|
||||
},
|
||||
{
|
||||
"coordinates": [1527, 728],
|
||||
"coordinates": [1390, 688],
|
||||
"flag": "Minnesota",
|
||||
"name": "Minnesota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1529, 880],
|
||||
"coordinates": [1427, 829],
|
||||
"flag": "Missouri",
|
||||
"name": "Missouri"
|
||||
},
|
||||
{
|
||||
"coordinates": [1225, 754],
|
||||
"coordinates": [1100, 677],
|
||||
"flag": "Montana",
|
||||
"name": "Montana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1413, 789],
|
||||
"coordinates": [1263, 787],
|
||||
"flag": "Nebraska",
|
||||
"name": "Nebraska"
|
||||
},
|
||||
{
|
||||
"coordinates": [1090, 852],
|
||||
"coordinates": [946, 818],
|
||||
"flag": "Nevada",
|
||||
"name": "Nevada"
|
||||
},
|
||||
{
|
||||
"coordinates": [1253, 933],
|
||||
"coordinates": [1169, 932],
|
||||
"flag": "New_Mexico",
|
||||
"name": "New Mexico"
|
||||
},
|
||||
{
|
||||
"coordinates": [1833, 792],
|
||||
"coordinates": [1737, 768],
|
||||
"flag": "New_York",
|
||||
"name": "New York"
|
||||
},
|
||||
{
|
||||
"coordinates": [1444, 716],
|
||||
"coordinates": [1239, 670],
|
||||
"flag": "North_Dakota",
|
||||
"name": "North Dakota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1704, 812],
|
||||
"coordinates": [1612, 816],
|
||||
"flag": "Ohio",
|
||||
"name": "Ohio"
|
||||
},
|
||||
{
|
||||
"coordinates": [1397, 921],
|
||||
"coordinates": [1296, 902],
|
||||
"flag": "Oklahoma",
|
||||
"name": "Oklahoma"
|
||||
},
|
||||
{
|
||||
"coordinates": [976, 754],
|
||||
"coordinates": [873, 757],
|
||||
"flag": "Oregon",
|
||||
"name": "Oregon"
|
||||
},
|
||||
{
|
||||
"coordinates": [1752, 716],
|
||||
"coordinates": [1686, 796],
|
||||
"flag": "Pennsylvania",
|
||||
"name": "Pennsylvania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1716, 937],
|
||||
"coordinates": [1636, 934],
|
||||
"flag": "South_Carolina",
|
||||
"name": "South Carolina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1419, 753],
|
||||
"coordinates": [1302, 721],
|
||||
"flag": "South_Dakota",
|
||||
"name": "South Dakota"
|
||||
},
|
||||
{
|
||||
"coordinates": [1648, 981],
|
||||
"coordinates": [1579, 900],
|
||||
"flag": "Tennessee",
|
||||
"name": "Tennessee"
|
||||
},
|
||||
{
|
||||
"coordinates": [1407, 1005],
|
||||
"coordinates": [1279, 1013],
|
||||
"flag": "Texas",
|
||||
"name": "Texas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1827, 742],
|
||||
"coordinates": [1785, 727],
|
||||
"flag": "Vermont",
|
||||
"name": "Vermont"
|
||||
},
|
||||
{
|
||||
"coordinates": [1767, 857],
|
||||
"coordinates": [1652, 864],
|
||||
"flag": "Virginia",
|
||||
"name": "Virginia"
|
||||
},
|
||||
{
|
||||
"coordinates": [994, 700],
|
||||
"coordinates": [897, 673],
|
||||
"flag": "Washington",
|
||||
"name": "Washington"
|
||||
},
|
||||
{
|
||||
"coordinates": [1261, 759],
|
||||
"coordinates": [1118, 764],
|
||||
"flag": "Wyoming",
|
||||
"name": "Wyoming"
|
||||
},
|
||||
{
|
||||
"coordinates": [1867, 561],
|
||||
"coordinates": [1798, 592],
|
||||
"flag": "Quebec",
|
||||
"name": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [1738, 80],
|
||||
"flag": "santa_claus",
|
||||
"name": "Santa Claus"
|
||||
"coordinates": [1080, 367],
|
||||
"flag": "northwestterritories",
|
||||
"name": "Northwest Territories"
|
||||
},
|
||||
{
|
||||
"coordinates": [1189, 240],
|
||||
"flag": "polar_bears",
|
||||
"name": "Polar Bears"
|
||||
},
|
||||
{
|
||||
"coordinates": [1480, 350],
|
||||
"flag": "frost_giant",
|
||||
"name": "Frost Giants"
|
||||
"coordinates": [1440, 319],
|
||||
"flag": "Nunavut",
|
||||
"name": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [2399, 171],
|
||||
"flag": "gl",
|
||||
"name": "Greenland"
|
||||
},
|
||||
{
|
||||
"coordinates": [777, 382],
|
||||
"flag": "Yukon",
|
||||
"name": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [787, 561],
|
||||
"flag": "britishcolumbia",
|
||||
"name": "British Columbia"
|
||||
},
|
||||
{
|
||||
"coordinates": [966, 521],
|
||||
"flag": "alberta",
|
||||
"name": "Alberta"
|
||||
},
|
||||
{
|
||||
"coordinates": [1172, 569],
|
||||
"flag": "saskatchewan",
|
||||
"name": "Saskatchewan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1343, 507],
|
||||
"flag": "manitoba",
|
||||
"name": "Manitoba"
|
||||
},
|
||||
{
|
||||
"coordinates": [1575, 621],
|
||||
"flag": "ontario",
|
||||
"name": "Ontario"
|
||||
},
|
||||
{
|
||||
"coordinates": [1903, 671],
|
||||
"flag": "newbrunswick",
|
||||
"name": "New Brunswick"
|
||||
},
|
||||
{
|
||||
"coordinates": [1924, 748],
|
||||
"flag": "novascotia",
|
||||
"name": "Nova Scotia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1983, 547],
|
||||
"flag": "newfoundlandandlabrador",
|
||||
"name": "Newfoundland and Labrador"
|
||||
},
|
||||
{
|
||||
"coordinates": [1793, 1209],
|
||||
"flag": "ht",
|
||||
"name": "Haiti"
|
||||
},
|
||||
{
|
||||
"coordinates": [1851, 1215],
|
||||
"flag": "do",
|
||||
"name": "Dominican Republic"
|
||||
},
|
||||
{
|
||||
"coordinates": [1912, 1224],
|
||||
"flag": "pr",
|
||||
"name": "Puerto Rico"
|
||||
},
|
||||
{
|
||||
"coordinates": [2020, 1422],
|
||||
"flag": "gy",
|
||||
"name": "Guyana"
|
||||
},
|
||||
{
|
||||
"coordinates": [2785, 358],
|
||||
"flag": "is",
|
||||
"name": "Iceland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1975, 702],
|
||||
"flag": "princeedwardisland",
|
||||
"name": "Prince Edward Island"
|
||||
},
|
||||
{
|
||||
"coordinates": [1037, 838],
|
||||
"flag": "Utah",
|
||||
"name": "Utah"
|
||||
},
|
||||
{
|
||||
"coordinates": [1402, 772],
|
||||
"flag": "Iowa",
|
||||
"name": "Iowa"
|
||||
},
|
||||
{
|
||||
"coordinates": [1685, 902],
|
||||
"flag": "North_Carolina",
|
||||
"name": "North Carolina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1417, 904],
|
||||
"flag": "Arkansas",
|
||||
"name": "Arkansas"
|
||||
},
|
||||
{
|
||||
"coordinates": [1547, 819],
|
||||
"flag": "Indiana",
|
||||
"name": "Indiana"
|
||||
},
|
||||
{
|
||||
"coordinates": [1528, 963],
|
||||
"flag": "Alabama",
|
||||
"name": "Alabama"
|
||||
},
|
||||
{
|
||||
"coordinates": [1811, 782],
|
||||
"flag": "Massachusetts",
|
||||
"name": "Massachusetts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,29 +17,29 @@
|
||||
"name": "Oceania",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [718, 738],
|
||||
"coordinates": [515, 690],
|
||||
"flag": "au",
|
||||
"name": "Australia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1050, 809],
|
||||
"coordinates": [1010, 836],
|
||||
"flag": "nz",
|
||||
"name": "New Zealand"
|
||||
},
|
||||
{
|
||||
"coordinates": [436, 407],
|
||||
"flag": "tl",
|
||||
"name": "Timor-Leste"
|
||||
"name": "Timor Leste"
|
||||
},
|
||||
{
|
||||
"coordinates": [182, 378],
|
||||
"coordinates": [236, 336],
|
||||
"flag": "id",
|
||||
"name": "Indonesia"
|
||||
},
|
||||
{
|
||||
"coordinates": [292, 243],
|
||||
"flag": "bn",
|
||||
"name": "Brunei Darussalam"
|
||||
"name": "Brunei"
|
||||
},
|
||||
{
|
||||
"coordinates": [152, 282],
|
||||
@@ -47,34 +47,34 @@
|
||||
"name": "Singapore"
|
||||
},
|
||||
{
|
||||
"coordinates": [120, 261],
|
||||
"coordinates": [115, 228],
|
||||
"flag": "my",
|
||||
"name": "Malaysia"
|
||||
},
|
||||
{
|
||||
"coordinates": [106, 129],
|
||||
"coordinates": [112, 113],
|
||||
"flag": "th",
|
||||
"name": "Thailand"
|
||||
},
|
||||
{
|
||||
"coordinates": [51, 42],
|
||||
"coordinates": [34, 23],
|
||||
"flag": "mm",
|
||||
"name": "Myanmar"
|
||||
},
|
||||
{
|
||||
"coordinates": [158, 162],
|
||||
"coordinates": [171, 153],
|
||||
"flag": "kh",
|
||||
"name": "Cambodia"
|
||||
},
|
||||
{
|
||||
"coordinates": [182, 43],
|
||||
"coordinates": [203, 105],
|
||||
"flag": "vn",
|
||||
"name": "Vietnam"
|
||||
},
|
||||
{
|
||||
"coordinates": [143, 37],
|
||||
"coordinates": [127, 42],
|
||||
"flag": "la",
|
||||
"name": "Lao PDR"
|
||||
"name": "Laos"
|
||||
},
|
||||
{
|
||||
"coordinates": [278, 18],
|
||||
@@ -82,70 +82,35 @@
|
||||
"name": "Hong Kong"
|
||||
},
|
||||
{
|
||||
"coordinates": [359, 1],
|
||||
"coordinates": [362, 9],
|
||||
"flag": "tw",
|
||||
"name": "Taiwan, Province of China"
|
||||
"name": "Taiwan"
|
||||
},
|
||||
{
|
||||
"coordinates": [366, 119],
|
||||
"coordinates": [425, 194],
|
||||
"flag": "ph",
|
||||
"name": "Philippines"
|
||||
},
|
||||
{
|
||||
"coordinates": [536, 207],
|
||||
"flag": "pw",
|
||||
"name": "Palau"
|
||||
},
|
||||
{
|
||||
"coordinates": [834, 215],
|
||||
"flag": "fm",
|
||||
"name": "Micronesia"
|
||||
},
|
||||
{
|
||||
"coordinates": [664, 113],
|
||||
"flag": "gu",
|
||||
"name": "Guam"
|
||||
},
|
||||
{
|
||||
"coordinates": [1042, 317],
|
||||
"flag": "mh",
|
||||
"name": "Marshall Islands"
|
||||
},
|
||||
{
|
||||
"coordinates": [799, 385],
|
||||
"coordinates": [630, 384],
|
||||
"flag": "pg",
|
||||
"name": "Papua New Guinea"
|
||||
},
|
||||
{
|
||||
"coordinates": [862, 442],
|
||||
"coordinates": [855, 424],
|
||||
"flag": "sb",
|
||||
"name": "Solomon Islands"
|
||||
},
|
||||
{
|
||||
"coordinates": [945, 497],
|
||||
"flag": "vu",
|
||||
"name": "Vanuatu"
|
||||
},
|
||||
{
|
||||
"coordinates": [930, 574],
|
||||
"coordinates": [925, 574],
|
||||
"flag": "nc",
|
||||
"name": "New Caledonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1085, 526],
|
||||
"coordinates": [1080, 528],
|
||||
"flag": "fj",
|
||||
"name": "Fiji"
|
||||
},
|
||||
{
|
||||
"coordinates": [1169, 568],
|
||||
"flag": "to",
|
||||
"name": "Tonga"
|
||||
},
|
||||
{
|
||||
"coordinates": [1236, 541],
|
||||
"flag": "nu",
|
||||
"name": "Niue"
|
||||
},
|
||||
{
|
||||
"coordinates": [1204, 473],
|
||||
"flag": "ws",
|
||||
@@ -157,24 +122,9 @@
|
||||
"name": "Cook Islands"
|
||||
},
|
||||
{
|
||||
"coordinates": [1623, 424],
|
||||
"flag": "pf",
|
||||
"name": "French Polynesia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1393, 278],
|
||||
"flag": "ki",
|
||||
"name": "Kiribati"
|
||||
},
|
||||
{
|
||||
"coordinates": [1420, 56],
|
||||
"flag": "us",
|
||||
"name": "United States"
|
||||
},
|
||||
{
|
||||
"coordinates": [1996, 644],
|
||||
"flag": "cl",
|
||||
"name": "Chile"
|
||||
"coordinates": [1413, 56],
|
||||
"flag": "Hawaii",
|
||||
"name": "Hawaii"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
"name": "Haiti"
|
||||
},
|
||||
{
|
||||
"coordinates": [112, 209],
|
||||
"coordinates": [122, 143],
|
||||
"flag": "bz",
|
||||
"name": "Belize"
|
||||
},
|
||||
{
|
||||
"coordinates": [71, 282],
|
||||
"coordinates": [68, 257],
|
||||
"flag": "gt",
|
||||
"name": "Guatemala"
|
||||
},
|
||||
@@ -57,84 +57,89 @@
|
||||
"name": "Panama"
|
||||
},
|
||||
{
|
||||
"coordinates": [740, 1180],
|
||||
"coordinates": [847, 1209],
|
||||
"flag": "bo",
|
||||
"name": "Bolivia"
|
||||
},
|
||||
{
|
||||
"coordinates": [849, 1770],
|
||||
"coordinates": [861, 1851],
|
||||
"flag": "ar",
|
||||
"name": "Argentina"
|
||||
},
|
||||
{
|
||||
"coordinates": [1394, 1309],
|
||||
"coordinates": [1153, 1095],
|
||||
"flag": "br",
|
||||
"name": "Brazil"
|
||||
},
|
||||
{
|
||||
"coordinates": [691, 1371],
|
||||
"coordinates": [687, 1605],
|
||||
"flag": "cl",
|
||||
"name": "Chile"
|
||||
},
|
||||
{
|
||||
"coordinates": [527, 503],
|
||||
"coordinates": [582, 543],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
},
|
||||
{
|
||||
"coordinates": [384, 746],
|
||||
"coordinates": [444, 750],
|
||||
"flag": "ec",
|
||||
"name": "Ecuador"
|
||||
},
|
||||
{
|
||||
"coordinates": [933, 423],
|
||||
"coordinates": [1204, 615],
|
||||
"flag": "gf",
|
||||
"name": "French Guyana"
|
||||
"name": "French Guiana"
|
||||
},
|
||||
{
|
||||
"coordinates": [800, 410],
|
||||
"coordinates": [981, 546],
|
||||
"flag": "gy",
|
||||
"name": "Guyana"
|
||||
},
|
||||
{
|
||||
"coordinates": [541, 1092],
|
||||
"coordinates": [537, 1017],
|
||||
"flag": "pe",
|
||||
"name": "Peru"
|
||||
},
|
||||
{
|
||||
"coordinates": [960, 1496],
|
||||
"coordinates": [1028, 1404],
|
||||
"flag": "py",
|
||||
"name": "Paraguay"
|
||||
},
|
||||
{
|
||||
"coordinates": [890, 610],
|
||||
"coordinates": [1097, 586],
|
||||
"flag": "sr",
|
||||
"name": "Suriname"
|
||||
},
|
||||
{
|
||||
"coordinates": [1091, 1635],
|
||||
"coordinates": [1118, 1695],
|
||||
"flag": "uy",
|
||||
"name": "Uruguay"
|
||||
},
|
||||
{
|
||||
"coordinates": [678, 904],
|
||||
"coordinates": [770, 465],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
},
|
||||
{
|
||||
"coordinates": [1270, 1035],
|
||||
"flag": "Aztec Empire",
|
||||
"name": "The Biggest Snakes"
|
||||
"coordinates": [795, 808],
|
||||
"flag": "amazonas",
|
||||
"name": "Amazonas"
|
||||
},
|
||||
{
|
||||
"coordinates": [894, 693],
|
||||
"flag": "",
|
||||
"name": "Normal Capybaras"
|
||||
"coordinates": [1267, 794],
|
||||
"flag": "Para",
|
||||
"name": "Pará"
|
||||
},
|
||||
{
|
||||
"coordinates": [884, 832],
|
||||
"flag": "",
|
||||
"name": "Just Otters"
|
||||
"coordinates": [1550, 1066],
|
||||
"flag": "bahia",
|
||||
"name": "Bahia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1374, 1404],
|
||||
"flag": "Sao Paulo",
|
||||
"name": "São Paulo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
"name": "World",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [375, 272],
|
||||
"coordinates": [484, 284],
|
||||
"flag": "us",
|
||||
"name": "United States"
|
||||
},
|
||||
{
|
||||
"coordinates": [372, 136],
|
||||
"coordinates": [374, 186],
|
||||
"flag": "ca",
|
||||
"name": "Canada"
|
||||
},
|
||||
@@ -37,22 +37,22 @@
|
||||
"name": "Cuba"
|
||||
},
|
||||
{
|
||||
"coordinates": [524, 474],
|
||||
"coordinates": [527, 487],
|
||||
"flag": "co",
|
||||
"name": "Colombia"
|
||||
},
|
||||
{
|
||||
"coordinates": [593, 473],
|
||||
"coordinates": [576, 466],
|
||||
"flag": "ve",
|
||||
"name": "Venezuela"
|
||||
},
|
||||
{
|
||||
"coordinates": [596, 705],
|
||||
"coordinates": [565, 737],
|
||||
"flag": "ar",
|
||||
"name": "Argentina"
|
||||
},
|
||||
{
|
||||
"coordinates": [637, 567],
|
||||
"coordinates": [684, 574],
|
||||
"flag": "br",
|
||||
"name": "Brazil"
|
||||
},
|
||||
@@ -92,39 +92,39 @@
|
||||
"name": "Italy"
|
||||
},
|
||||
{
|
||||
"coordinates": [958, 220],
|
||||
"coordinates": [948, 221],
|
||||
"flag": "fr",
|
||||
"name": "France"
|
||||
},
|
||||
{
|
||||
"coordinates": [997, 205],
|
||||
"coordinates": [990, 195],
|
||||
"flag": "de",
|
||||
"name": "Germany"
|
||||
},
|
||||
{
|
||||
"coordinates": [1064, 101],
|
||||
"coordinates": [1014, 137],
|
||||
"flag": "se",
|
||||
"name": "Sweden"
|
||||
},
|
||||
{
|
||||
"coordinates": [1046, 193],
|
||||
"coordinates": [1031, 193],
|
||||
"flag": "pl",
|
||||
"name": "Poland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1061, 188],
|
||||
"coordinates": [1102, 183],
|
||||
"flag": "by",
|
||||
"name": "Belarus"
|
||||
},
|
||||
{
|
||||
"coordinates": [1073, 243],
|
||||
"coordinates": [1073, 230],
|
||||
"flag": "ro",
|
||||
"name": "Romania"
|
||||
},
|
||||
{
|
||||
"coordinates": [1161, 274],
|
||||
"coordinates": [1123, 272],
|
||||
"flag": "tr",
|
||||
"name": "Turkey"
|
||||
"name": "Türkiye"
|
||||
},
|
||||
{
|
||||
"coordinates": [969, 133],
|
||||
@@ -132,44 +132,44 @@
|
||||
"name": "Norway"
|
||||
},
|
||||
{
|
||||
"coordinates": [1062, 133],
|
||||
"coordinates": [1082, 126],
|
||||
"flag": "fi",
|
||||
"name": "Finland"
|
||||
},
|
||||
{
|
||||
"coordinates": [1099, 211],
|
||||
"coordinates": [1135, 210],
|
||||
"flag": "ua",
|
||||
"name": "Ukraine"
|
||||
},
|
||||
{
|
||||
"coordinates": [1344, 136],
|
||||
"coordinates": [1351, 134],
|
||||
"flag": "ru",
|
||||
"name": "Russia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1537, 186],
|
||||
"coordinates": [1500, 203],
|
||||
"flag": "mn",
|
||||
"name": "Mongolia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1524, 328],
|
||||
"coordinates": [1527, 303],
|
||||
"flag": "cn",
|
||||
"name": "China"
|
||||
},
|
||||
{
|
||||
"coordinates": [1368, 373],
|
||||
"coordinates": [1331, 353],
|
||||
"flag": "in",
|
||||
"name": "India"
|
||||
},
|
||||
{
|
||||
"coordinates": [1276, 239],
|
||||
"coordinates": [1279, 207],
|
||||
"flag": "kz",
|
||||
"name": "Kazakhstan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1238, 309],
|
||||
"flag": "ir",
|
||||
"name": "Islamic Republic Of Iran"
|
||||
"name": "Iran"
|
||||
},
|
||||
{
|
||||
"coordinates": [1178, 351],
|
||||
@@ -187,37 +187,37 @@
|
||||
"name": "New Zealand"
|
||||
},
|
||||
{
|
||||
"coordinates": [918, 342],
|
||||
"coordinates": [957, 295],
|
||||
"flag": "dz",
|
||||
"name": "Algeria"
|
||||
},
|
||||
{
|
||||
"coordinates": [1030, 332],
|
||||
"coordinates": [1024, 340],
|
||||
"flag": "ly",
|
||||
"name": "Libyan Arab Jamahiriya"
|
||||
"name": "Libya"
|
||||
},
|
||||
{
|
||||
"coordinates": [1092, 335],
|
||||
"coordinates": [1094, 340],
|
||||
"flag": "eg",
|
||||
"name": "Egypt"
|
||||
},
|
||||
{
|
||||
"coordinates": [963, 410],
|
||||
"coordinates": [956, 389],
|
||||
"flag": "ne",
|
||||
"name": "Niger"
|
||||
},
|
||||
{
|
||||
"coordinates": [1112, 406],
|
||||
"coordinates": [1101, 400],
|
||||
"flag": "sd",
|
||||
"name": "Sudan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1074, 508],
|
||||
"coordinates": [1051, 513],
|
||||
"flag": "cd",
|
||||
"name": "DR Congo"
|
||||
},
|
||||
{
|
||||
"coordinates": [1154, 443],
|
||||
"coordinates": [1153, 451],
|
||||
"flag": "et",
|
||||
"name": "Ethiopia"
|
||||
},
|
||||
@@ -232,12 +232,12 @@
|
||||
"name": "Madagascar"
|
||||
},
|
||||
{
|
||||
"coordinates": [1052, 420],
|
||||
"coordinates": [1036, 420],
|
||||
"flag": "td",
|
||||
"name": "Chad"
|
||||
},
|
||||
{
|
||||
"coordinates": [1030, 665],
|
||||
"coordinates": [1039, 646],
|
||||
"flag": "na",
|
||||
"name": "Namibia"
|
||||
},
|
||||
@@ -247,7 +247,7 @@
|
||||
"name": "Philippines"
|
||||
},
|
||||
{
|
||||
"coordinates": [1537, 426],
|
||||
"coordinates": [1502, 415],
|
||||
"flag": "th",
|
||||
"name": "Thailand"
|
||||
},
|
||||
@@ -262,14 +262,14 @@
|
||||
"name": "Japan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1869, 119],
|
||||
"flag": "ru",
|
||||
"coordinates": [1626, 118],
|
||||
"flag": "Siberia",
|
||||
"name": "Siberia"
|
||||
},
|
||||
{
|
||||
"coordinates": [74, 117],
|
||||
"flag": "polar_bears",
|
||||
"name": "Polar Bears"
|
||||
"coordinates": [422, 95],
|
||||
"flag": "Nunavut",
|
||||
"name": "Nunavut"
|
||||
},
|
||||
{
|
||||
"coordinates": [419, 975],
|
||||
@@ -277,22 +277,22 @@
|
||||
"name": "West Antarctica"
|
||||
},
|
||||
{
|
||||
"coordinates": [542, 603],
|
||||
"coordinates": [516, 567],
|
||||
"flag": "pe",
|
||||
"name": "Peru"
|
||||
},
|
||||
{
|
||||
"coordinates": [1075, 615],
|
||||
"coordinates": [1097, 598],
|
||||
"flag": "zm",
|
||||
"name": "Zambia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1099, 165],
|
||||
"coordinates": [1067, 168],
|
||||
"flag": "lv",
|
||||
"name": "Latvia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1427, 336],
|
||||
"coordinates": [1419, 338],
|
||||
"flag": "bt",
|
||||
"name": "Bhutan"
|
||||
},
|
||||
@@ -312,14 +312,69 @@
|
||||
"name": "Oman"
|
||||
},
|
||||
{
|
||||
"coordinates": [853, 373],
|
||||
"coordinates": [886, 327],
|
||||
"flag": "ma",
|
||||
"name": "Morocco"
|
||||
},
|
||||
{
|
||||
"coordinates": [656, 678],
|
||||
"coordinates": [626, 704],
|
||||
"flag": "uy",
|
||||
"name": "Uruguay"
|
||||
},
|
||||
{
|
||||
"coordinates": [581, 620],
|
||||
"flag": "bo",
|
||||
"name": "Bolivia"
|
||||
},
|
||||
{
|
||||
"coordinates": [95, 115],
|
||||
"flag": "Alaska",
|
||||
"name": "Alaska"
|
||||
},
|
||||
{
|
||||
"coordinates": [243, 131],
|
||||
"flag": "Yukon",
|
||||
"name": "Yukon"
|
||||
},
|
||||
{
|
||||
"coordinates": [264, 276],
|
||||
"flag": "California",
|
||||
"name": "California"
|
||||
},
|
||||
{
|
||||
"coordinates": [371, 299],
|
||||
"flag": "Texas",
|
||||
"name": "Texas"
|
||||
},
|
||||
{
|
||||
"coordinates": [532, 191],
|
||||
"flag": "Quebec",
|
||||
"name": "Quebec"
|
||||
},
|
||||
{
|
||||
"coordinates": [932, 476],
|
||||
"flag": "bj",
|
||||
"name": "Benin"
|
||||
},
|
||||
{
|
||||
"coordinates": [845, 425],
|
||||
"flag": "sn",
|
||||
"name": "Senegal"
|
||||
},
|
||||
{
|
||||
"coordinates": [1152, 522],
|
||||
"flag": "ke",
|
||||
"name": "Kenya"
|
||||
},
|
||||
{
|
||||
"coordinates": [1320, 282],
|
||||
"flag": "pk",
|
||||
"name": "Pakistan"
|
||||
},
|
||||
{
|
||||
"coordinates": [1384, 466],
|
||||
"flag": "lk",
|
||||
"name": "Sri Lanka"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ import newsItemsFallback from "resources/news.json";
|
||||
import { z } from "zod";
|
||||
import type { NewsItem } from "../core/ApiSchemas";
|
||||
import {
|
||||
ClanLeaderboardResponse,
|
||||
ClanLeaderboardResponseSchema,
|
||||
NewsItemSchema,
|
||||
PlayerProfile,
|
||||
PlayerProfileSchema,
|
||||
@@ -236,40 +234,6 @@ export async function fetchGameById(
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchClanLeaderboard(): Promise<
|
||||
ClanLeaderboardResponse | false
|
||||
> {
|
||||
try {
|
||||
const res = await fetch(`${getApiBase()}/public/clans/leaderboard`, {
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.warn(
|
||||
"fetchClanLeaderboard: unexpected status",
|
||||
res.status,
|
||||
res.statusText,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
const parsed = ClanLeaderboardResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn(
|
||||
"fetchClanLeaderboard: Zod validation failed",
|
||||
parsed.error.toString(),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.data;
|
||||
} catch (err) {
|
||||
console.warn("fetchClanLeaderboard: request failed", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchPlayerLeaderboard(
|
||||
page: number,
|
||||
): Promise<RankedLeaderboardResponse | "reached_limit" | false> {
|
||||
|
||||
@@ -0,0 +1,494 @@
|
||||
import {
|
||||
type ClanBansResponse,
|
||||
ClanBansResponseSchema,
|
||||
type ClanBrowseResponse,
|
||||
ClanBrowseResponseSchema,
|
||||
type ClanInfo,
|
||||
ClanInfoSchema,
|
||||
type ClanLeaderboardResponse,
|
||||
ClanLeaderboardResponseSchema,
|
||||
type ClanMembersResponse,
|
||||
ClanMembersResponseSchema,
|
||||
type ClanRequestsResponse,
|
||||
ClanRequestsResponseSchema,
|
||||
type ClanStats,
|
||||
ClanStatsSchema,
|
||||
JoinClanResponseSchema,
|
||||
} from "../core/ClanApiSchemas";
|
||||
import { getApiBase } from "./Api";
|
||||
import { getAuthHeader } from "./Auth";
|
||||
export type {
|
||||
ClanBan,
|
||||
ClanBansResponse,
|
||||
ClanBrowseResponse,
|
||||
ClanInfo,
|
||||
ClanJoinRequest,
|
||||
ClanMember,
|
||||
ClanMembersResponse,
|
||||
ClanMemberStats,
|
||||
ClanMemberWL,
|
||||
ClanRequestsResponse,
|
||||
ClanStats,
|
||||
} from "../core/ClanApiSchemas";
|
||||
|
||||
async function clanFetch(
|
||||
path: string,
|
||||
options?: RequestInit,
|
||||
): Promise<Response> {
|
||||
const url = `${getApiBase()}${path}`;
|
||||
return fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...options?.headers,
|
||||
Authorization: await getAuthHeader(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchClanLeaderboard(): Promise<
|
||||
ClanLeaderboardResponse | false
|
||||
> {
|
||||
try {
|
||||
const res = await fetch(`${getApiBase()}/public/clans/leaderboard`, {
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.warn(
|
||||
"fetchClanLeaderboard: unexpected status",
|
||||
res.status,
|
||||
res.statusText,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
const parsed = ClanLeaderboardResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn(
|
||||
"fetchClanLeaderboard: Zod validation failed",
|
||||
parsed.error.toString(),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.data;
|
||||
} catch (err) {
|
||||
console.warn("fetchClanLeaderboard: request failed", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchClanStats(tag: string): Promise<ClanStats | false> {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${getApiBase()}/public/clan/${encodeURIComponent(tag)}`,
|
||||
{ headers: { Accept: "application/json" } },
|
||||
);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanStatsSchema.safeParse(json?.clan);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClanStats: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch (err) {
|
||||
console.warn("fetchClanStats: request failed", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchClans(
|
||||
search?: string,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
): Promise<ClanBrowseResponse | false> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(limit));
|
||||
if (search && search.length >= 2) params.set("search", search);
|
||||
const res = await clanFetch(`/clans?${params}`);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanBrowseResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClans: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchClanDetail(tag: string): Promise<ClanInfo | false> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}`);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanInfoSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClanDetail: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export type ClanMemberSort =
|
||||
| "default"
|
||||
| "winsTotal"
|
||||
| "lossesTotal"
|
||||
| "winsFfa"
|
||||
| "lossesFfa"
|
||||
| "winsTeam"
|
||||
| "lossesTeam"
|
||||
| "winsHvn"
|
||||
| "lossesHvn"
|
||||
| "winsRanked"
|
||||
| "lossesRanked"
|
||||
| "wins1v1"
|
||||
| "losses1v1";
|
||||
export type ClanMemberOrder = "asc" | "desc";
|
||||
|
||||
export async function fetchClanMembers(
|
||||
tag: string,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
sort: ClanMemberSort = "default",
|
||||
order?: ClanMemberOrder,
|
||||
): Promise<ClanMembersResponse | false> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(limit));
|
||||
if (sort !== "default") params.set("sort", sort);
|
||||
if (order) params.set("order", order);
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/members?${params}`,
|
||||
);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanMembersResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClanMembers: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function joinClan(
|
||||
tag: string,
|
||||
): Promise<
|
||||
{ status: "joined" | "requested" } | { error: string; reason?: string }
|
||||
> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}/join`, {
|
||||
method: "POST",
|
||||
});
|
||||
if (res.status === 409) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
const msg = (body as { message?: string }).message ?? "";
|
||||
return {
|
||||
error: msg.toLowerCase().includes("request")
|
||||
? "clan_modal.error_request_pending"
|
||||
: "clan_modal.error_already_member",
|
||||
};
|
||||
}
|
||||
if (res.status === 429) {
|
||||
return { error: "clan_modal.error_rate_limited_generic" };
|
||||
}
|
||||
if (res.status === 403) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
const b = body as { code?: string; reason?: string | null };
|
||||
if (b.code === "BANNED") {
|
||||
return {
|
||||
error: b.reason
|
||||
? "clan_modal.error_banned_reason"
|
||||
: "clan_modal.error_banned",
|
||||
...(b.reason ? { reason: b.reason } : {}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
const json = await res.json();
|
||||
const parsed = JoinClanResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("joinClan: Zod validation failed", parsed.error);
|
||||
return { error: "clan_modal.error_failed" };
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function leaveClan(
|
||||
tag: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}/leave`, {
|
||||
method: "POST",
|
||||
});
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateClan(
|
||||
tag: string,
|
||||
patch: { name?: string; description?: string; isOpen?: boolean },
|
||||
): Promise<ClanInfo | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(patch),
|
||||
});
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
const json = await res.json();
|
||||
const parsed = ClanInfoSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("updateClan: Zod validation failed", parsed.error);
|
||||
return { error: "clan_modal.error_failed" };
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function disbandClan(
|
||||
tag: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
async function memberAction(
|
||||
tag: string,
|
||||
targetPublicId: string,
|
||||
action: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}/${action}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ targetPublicId }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
return { error: "clan_modal.error_failed" };
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export const kickMember = (tag: string, targetPublicId: string) =>
|
||||
memberAction(tag, targetPublicId, "kick");
|
||||
|
||||
export const promoteMember = (tag: string, targetPublicId: string) =>
|
||||
memberAction(tag, targetPublicId, "promote");
|
||||
|
||||
export const demoteMember = (tag: string, targetPublicId: string) =>
|
||||
memberAction(tag, targetPublicId, "demote");
|
||||
|
||||
export const transferLeadership = (tag: string, targetPublicId: string) =>
|
||||
memberAction(tag, targetPublicId, "transfer");
|
||||
|
||||
export async function fetchClanRequests(
|
||||
tag: string,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
): Promise<ClanRequestsResponse | false> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(limit));
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/requests?${params}`,
|
||||
);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanRequestsResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClanRequests: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function approveClanRequest(
|
||||
tag: string,
|
||||
targetPublicId: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/requests/approve`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ targetPublicId }),
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function denyClanRequest(
|
||||
tag: string,
|
||||
targetPublicId: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/requests/deny`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ targetPublicId }),
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function withdrawClanRequest(
|
||||
tag: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/requests/withdraw`,
|
||||
{ method: "POST" },
|
||||
);
|
||||
if (!res.ok) {
|
||||
return {
|
||||
error: "clan_modal.error_failed",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function banClanMember(
|
||||
tag: string,
|
||||
targetPublicId: string,
|
||||
reason?: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const body: { targetPublicId: string; reason?: string } = {
|
||||
targetPublicId,
|
||||
};
|
||||
if (reason) body.reason = reason;
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}/ban`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
return { error: "clan_modal.error_failed" };
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function unbanClanMember(
|
||||
tag: string,
|
||||
targetPublicId: string,
|
||||
): Promise<true | { error: string }> {
|
||||
try {
|
||||
const res = await clanFetch(`/clans/${encodeURIComponent(tag)}/unban`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ targetPublicId }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
return { error: "clan_modal.error_failed" };
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return { error: "clan_modal.error_network" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchClanBans(
|
||||
tag: string,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
): Promise<ClanBansResponse | false> {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(limit));
|
||||
const res = await clanFetch(
|
||||
`/clans/${encodeURIComponent(tag)}/bans?${params}`,
|
||||
);
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
const parsed = ClanBansResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
console.warn("fetchClanBans: Zod validation failed", parsed.error);
|
||||
return false;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
import { html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { getUserMe, invalidateUserMe } from "./Api";
|
||||
import { type ClanInfo, type ClanMember, type ClanStats } from "./ClanApi";
|
||||
import { BaseModal } from "./components/BaseModal";
|
||||
import "./components/clan/ClanBansView";
|
||||
import "./components/clan/ClanBrowseView";
|
||||
import type { BrowseState } from "./components/clan/ClanBrowseView";
|
||||
import "./components/clan/ClanCard";
|
||||
import "./components/clan/ClanDetailView";
|
||||
import "./components/clan/ClanManageView";
|
||||
import "./components/clan/ClanMyRequestsView";
|
||||
import "./components/clan/ClanRequestsView";
|
||||
import type { ClanRole } from "./components/clan/ClanShared";
|
||||
import "./components/clan/ClanTransferView";
|
||||
import "./components/ConfirmDialog";
|
||||
import "./components/CopyButton";
|
||||
import { modalHeader } from "./components/ui/ModalHeader";
|
||||
import { translateText } from "./Utils";
|
||||
|
||||
type Tab = "my-clans" | "browse";
|
||||
type View =
|
||||
| "list"
|
||||
| "detail"
|
||||
| "manage"
|
||||
| "transfer"
|
||||
| "requests"
|
||||
| "bans"
|
||||
| "my-requests";
|
||||
|
||||
@customElement("clan-modal")
|
||||
export class ClanModal extends BaseModal {
|
||||
@state() private activeTab: Tab = "my-clans";
|
||||
@state() private view: View = "list";
|
||||
@state() private loading = false;
|
||||
|
||||
@state() private myClans: ClanInfo[] = [];
|
||||
@state() private myPendingRequests: {
|
||||
tag: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
}[] = [];
|
||||
|
||||
@state() private selectedClanTag = "";
|
||||
@state() private selectedClan: ClanInfo | null = null;
|
||||
@state() private myRole: ClanRole | null = null;
|
||||
private myPublicId: string | null = null;
|
||||
@state() private myClanRoles = new Map<string, ClanRole>();
|
||||
|
||||
// Lifted browse state — survives tab switches
|
||||
private browseCache: BrowseState | null = null;
|
||||
|
||||
// Lifted detail cache — survives sub-view navigation
|
||||
private detailCache: {
|
||||
tag: string;
|
||||
members: ClanMember[];
|
||||
membersTotal: number;
|
||||
pendingRequestCount: number;
|
||||
stats: ClanStats | null;
|
||||
} | null = null;
|
||||
|
||||
render() {
|
||||
const content = this.renderInner();
|
||||
if (this.inline) return content;
|
||||
return html`
|
||||
<o-modal
|
||||
id="clan-modal"
|
||||
title=""
|
||||
?hideCloseButton=${true}
|
||||
?inline=${this.inline}
|
||||
hideHeader
|
||||
>
|
||||
${content}
|
||||
</o-modal>
|
||||
`;
|
||||
}
|
||||
|
||||
protected onOpen(): void {
|
||||
this.loadMyClans();
|
||||
}
|
||||
|
||||
protected onClose(): void {
|
||||
this.activeTab = "my-clans";
|
||||
this.view = "list";
|
||||
this.selectedClan = null;
|
||||
this.selectedClanTag = "";
|
||||
this.myRole = null;
|
||||
this.browseCache = null;
|
||||
this.detailCache = null;
|
||||
}
|
||||
|
||||
private async loadMyClans() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const me = await getUserMe();
|
||||
if (!this.isModalOpen) return;
|
||||
if (!me || Object.keys(me.user).length === 0) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("show-message", {
|
||||
detail: {
|
||||
message: translateText("clan_modal.sign_in_for_clans"),
|
||||
color: "red",
|
||||
duration: 3000,
|
||||
},
|
||||
}),
|
||||
);
|
||||
this.close();
|
||||
window.showPage?.("page-account");
|
||||
return;
|
||||
}
|
||||
this.myPublicId = me.player.publicId;
|
||||
this.myPendingRequests = me.player.clanRequests ?? [];
|
||||
const roles = new Map<string, ClanRole>();
|
||||
const clans: ClanInfo[] = [];
|
||||
for (const c of me.player.clans ?? []) {
|
||||
roles.set(c.tag, c.role);
|
||||
clans.push({
|
||||
tag: c.tag,
|
||||
name: c.name,
|
||||
description: "",
|
||||
isOpen: false,
|
||||
memberCount: c.memberCount,
|
||||
});
|
||||
}
|
||||
this.myClanRoles = roles;
|
||||
this.myClans = clans;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private renderInner() {
|
||||
if (this.loading) {
|
||||
return html`
|
||||
<div class="${this.modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.title"),
|
||||
onBack: () => this.close(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}
|
||||
${this.renderLoadingSpinner()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (this.view === "my-requests") {
|
||||
return html`<clan-my-requests-view
|
||||
.myPendingRequests=${this.myPendingRequests}
|
||||
@navigate-back=${() => (this.view = "list")}
|
||||
@request-withdrawn=${(e: CustomEvent<{ tag: string }>) => {
|
||||
this.myPendingRequests = this.myPendingRequests.filter(
|
||||
(r) => r.tag !== e.detail.tag,
|
||||
);
|
||||
if (this.myPendingRequests.length === 0) this.view = "list";
|
||||
}}
|
||||
></clan-my-requests-view>`;
|
||||
}
|
||||
|
||||
if (this.selectedClanTag) {
|
||||
if (this.view === "manage") {
|
||||
return html`<clan-manage-view
|
||||
.clanTag=${this.selectedClanTag}
|
||||
.selectedClan=${this.selectedClan}
|
||||
.myPublicId=${this.myPublicId}
|
||||
.myRole=${this.myRole}
|
||||
@navigate-detail=${() => (this.view = "detail")}
|
||||
@navigate-bans=${() => (this.view = "bans")}
|
||||
@navigate-transfer=${() => (this.view = "transfer")}
|
||||
@clan-updated=${(e: CustomEvent<Partial<ClanInfo>>) => {
|
||||
if (this.selectedClan) {
|
||||
this.selectedClan = { ...this.selectedClan, ...e.detail };
|
||||
}
|
||||
this.detailCache = null;
|
||||
invalidateUserMe();
|
||||
}}
|
||||
@clan-disbanded=${(e: CustomEvent<{ tag: string }>) => {
|
||||
const roles = new Map(this.myClanRoles);
|
||||
roles.delete(e.detail.tag);
|
||||
this.myClanRoles = roles;
|
||||
this.myClans = this.myClans.filter((c) => c.tag !== e.detail.tag);
|
||||
this.selectedClan = null;
|
||||
this.selectedClanTag = "";
|
||||
this.myRole = null;
|
||||
this.view = "list";
|
||||
this.loadMyClans();
|
||||
}}
|
||||
></clan-manage-view>`;
|
||||
}
|
||||
if (this.view === "transfer") {
|
||||
return html`<clan-transfer-view
|
||||
.clanTag=${this.selectedClanTag}
|
||||
.selectedClan=${this.selectedClan}
|
||||
@navigate-back=${() => (this.view = "manage")}
|
||||
@leadership-transferred=${() => {
|
||||
this.loadMyClans().then(() =>
|
||||
this.openDetail(this.selectedClanTag),
|
||||
);
|
||||
}}
|
||||
></clan-transfer-view>`;
|
||||
}
|
||||
if (this.view === "requests") {
|
||||
return html`<clan-requests-view
|
||||
.clanTag=${this.selectedClanTag}
|
||||
.selectedClan=${this.selectedClan}
|
||||
@navigate-back=${() => (this.view = "detail")}
|
||||
@request-approved=${() => {
|
||||
if (this.selectedClan) {
|
||||
this.selectedClan = {
|
||||
...this.selectedClan,
|
||||
memberCount: (this.selectedClan.memberCount ?? 0) + 1,
|
||||
};
|
||||
}
|
||||
this.detailCache = null;
|
||||
}}
|
||||
></clan-requests-view>`;
|
||||
}
|
||||
if (this.view === "bans") {
|
||||
return html`<clan-bans-view
|
||||
.clanTag=${this.selectedClanTag}
|
||||
@navigate-back=${() => (this.view = "manage")}
|
||||
></clan-bans-view>`;
|
||||
}
|
||||
// Default: detail view
|
||||
return html`<clan-detail-view
|
||||
.clanTag=${this.selectedClanTag}
|
||||
.cachedClan=${this.selectedClan}
|
||||
.myPublicId=${this.myPublicId}
|
||||
.myClanRoles=${this.myClanRoles}
|
||||
.myPendingRequests=${this.myPendingRequests}
|
||||
.cachedDetail=${this.detailCache?.tag === this.selectedClanTag
|
||||
? this.detailCache
|
||||
: null}
|
||||
@navigate-back=${() => {
|
||||
this.view = "list";
|
||||
this.selectedClan = null;
|
||||
this.selectedClanTag = "";
|
||||
this.myRole = null;
|
||||
this.detailCache = null;
|
||||
}}
|
||||
@detail-loaded=${(
|
||||
e: CustomEvent<{
|
||||
clan: ClanInfo;
|
||||
myRole: ClanRole | null;
|
||||
members: ClanMember[];
|
||||
membersTotal: number;
|
||||
pendingRequestCount: number;
|
||||
stats: ClanStats | null;
|
||||
}>,
|
||||
) => {
|
||||
this.selectedClan = e.detail.clan;
|
||||
this.myRole = e.detail.myRole;
|
||||
this.detailCache = {
|
||||
tag: e.detail.clan.tag,
|
||||
members: e.detail.members,
|
||||
membersTotal: e.detail.membersTotal,
|
||||
pendingRequestCount: e.detail.pendingRequestCount,
|
||||
stats: e.detail.stats,
|
||||
};
|
||||
}}
|
||||
@navigate-manage=${() => (this.view = "manage")}
|
||||
@navigate-requests=${() => (this.view = "requests")}
|
||||
@clan-joined=${(e: CustomEvent<{ tag: string }>) => {
|
||||
this.myClanRoles = new Map([
|
||||
...this.myClanRoles,
|
||||
[e.detail.tag, "member" as ClanRole],
|
||||
]);
|
||||
this.openDetail(e.detail.tag);
|
||||
}}
|
||||
@clan-left=${(e: CustomEvent<{ tag: string }>) => {
|
||||
const roles = new Map(this.myClanRoles);
|
||||
roles.delete(e.detail.tag);
|
||||
this.myClanRoles = roles;
|
||||
this.selectedClan = null;
|
||||
this.selectedClanTag = "";
|
||||
this.myRole = null;
|
||||
this.view = "list";
|
||||
this.loadMyClans();
|
||||
}}
|
||||
@request-sent=${(e: CustomEvent<{ tag: string; name: string }>) => {
|
||||
this.myPendingRequests = [
|
||||
...this.myPendingRequests,
|
||||
{
|
||||
tag: e.detail.tag,
|
||||
name: e.detail.name,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
}}
|
||||
></clan-detail-view>`;
|
||||
}
|
||||
|
||||
// List view (tabs + my clans / browse)
|
||||
return html`
|
||||
<div class="${this.modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.title"),
|
||||
onBack: () => this.close(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}
|
||||
${this.renderTabs()}
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar mr-1">
|
||||
${this.activeTab === "my-clans"
|
||||
? this.renderMyClans()
|
||||
: html`<clan-browse-view
|
||||
.myClanRoles=${this.myClanRoles}
|
||||
.myPendingRequests=${this.myPendingRequests}
|
||||
.cachedState=${this.browseCache}
|
||||
@browse-updated=${(e: CustomEvent<BrowseState>) => {
|
||||
this.browseCache = e.detail;
|
||||
}}
|
||||
@clan-select=${(e: CustomEvent<{ tag: string }>) =>
|
||||
this.openDetail(e.detail.tag)}
|
||||
></clan-browse-view>`}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private openDetail(tag: string) {
|
||||
this.selectedClanTag = tag;
|
||||
this.view = "detail";
|
||||
}
|
||||
|
||||
private renderTabs() {
|
||||
const tabs: { key: Tab; label: string }[] = [
|
||||
{ key: "my-clans", label: translateText("clan_modal.my_clans") },
|
||||
{ key: "browse", label: translateText("clan_modal.browse") },
|
||||
];
|
||||
|
||||
return html`
|
||||
<div class="flex border-b border-white/10 px-4 lg:px-6 gap-1">
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<button
|
||||
@click=${() => {
|
||||
this.activeTab = tab.key;
|
||||
this.view = "list";
|
||||
this.selectedClan = null;
|
||||
this.selectedClanTag = "";
|
||||
if (tab.key === "my-clans") {
|
||||
this.loadMyClans();
|
||||
}
|
||||
}}
|
||||
class="px-4 py-3 text-sm font-bold uppercase tracking-wider transition-all relative
|
||||
${this.activeTab === tab.key
|
||||
? "text-aquarius"
|
||||
: "text-white/40 hover:text-white/70"}"
|
||||
>
|
||||
${tab.label}
|
||||
${this.activeTab === tab.key
|
||||
? html`<div
|
||||
class="absolute bottom-0 left-0 right-0 h-0.5 bg-malibu-blue"
|
||||
></div>`
|
||||
: ""}
|
||||
</button>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderMyClans() {
|
||||
const hasClans = this.myClans.length > 0;
|
||||
const hasRequests = this.myPendingRequests.length > 0;
|
||||
|
||||
if (!hasClans && !hasRequests) {
|
||||
return html`
|
||||
<div class="flex flex-col items-center justify-center p-12 text-center">
|
||||
<p class="text-white/40 text-sm mb-4">
|
||||
${translateText("clan_modal.no_clans")}
|
||||
</p>
|
||||
<button
|
||||
@click=${() => (this.activeTab = "browse")}
|
||||
class="px-6 py-2 text-sm font-bold text-white uppercase tracking-wider bg-malibu-blue hover:bg-aquarius active:bg-malibu-blue/80 rounded-lg transition-all"
|
||||
>
|
||||
${translateText("clan_modal.browse")}
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="p-4 lg:p-6 space-y-3">
|
||||
${hasRequests ? this.renderPendingRequestsButton() : ""}
|
||||
${this.myClans.map(
|
||||
(clan) => html`
|
||||
<clan-card
|
||||
.clan=${clan}
|
||||
.clanRole=${this.myClanRoles.get(clan.tag)}
|
||||
@clan-select=${(e: CustomEvent<{ tag: string }>) =>
|
||||
this.openDetail(e.detail.tag)}
|
||||
></clan-card>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderPendingRequestsButton() {
|
||||
const count = this.myPendingRequests.length;
|
||||
return html`
|
||||
<button
|
||||
@click=${() => (this.view = "my-requests")}
|
||||
class="w-full flex items-center justify-between bg-amber-500/10 hover:bg-amber-500/15 rounded-xl border border-amber-500/20 p-4 transition-all cursor-pointer group"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-amber-500/20 flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5 text-amber-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<span class="text-amber-400 text-sm font-bold">
|
||||
${translateText("clan_modal.pending_applications")}
|
||||
</span>
|
||||
<span class="text-amber-400/60 text-xs block">
|
||||
${translateText("clan_modal.pending_requests_count", {
|
||||
count,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="px-2.5 py-1 text-xs font-bold rounded-full bg-amber-500/20 text-amber-400 border border-amber-500/30"
|
||||
>
|
||||
${count}
|
||||
</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5 text-amber-400/40 group-hover:text-amber-400/70 transition-colors"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,12 @@ import {
|
||||
import { createPartialGameRecord, findClosestBy, replacer } from "../core/Util";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { getGameLogicConfig } from "../core/configuration/ConfigLoader";
|
||||
import { BuildableUnit, Structures, UnitType } from "../core/game/Game";
|
||||
import {
|
||||
BuildableUnit,
|
||||
PlayerType,
|
||||
Structures,
|
||||
UnitType,
|
||||
} from "../core/game/Game";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import { GameMapLoader } from "../core/game/GameMapLoader";
|
||||
import {
|
||||
@@ -34,6 +39,7 @@ import {
|
||||
DoBreakAllianceEvent,
|
||||
DoGroundAttackEvent,
|
||||
DoRequestAllianceEvent,
|
||||
DoRetaliateAttackEvent,
|
||||
InputHandler,
|
||||
MouseMoveEvent,
|
||||
MouseUpEvent,
|
||||
@@ -237,7 +243,7 @@ async function createClientGame(
|
||||
userSettings,
|
||||
lobbyConfig.gameRecord !== undefined,
|
||||
);
|
||||
let gameMap: TerrainMapData | null = null;
|
||||
let gameMap: TerrainMapData;
|
||||
|
||||
if (terrainLoad) {
|
||||
gameMap = await terrainLoad;
|
||||
@@ -391,6 +397,10 @@ export class ClientGameRunner {
|
||||
DoGroundAttackEvent,
|
||||
this.doGroundAttackUnderCursor.bind(this),
|
||||
);
|
||||
this.eventBus.on(
|
||||
DoRetaliateAttackEvent,
|
||||
this.doRetaliateAttackMostRecent.bind(this),
|
||||
);
|
||||
this.eventBus.on(
|
||||
DoRequestAllianceEvent,
|
||||
this.doRequestAllianceUnderCursor.bind(this),
|
||||
@@ -783,6 +793,41 @@ export class ClientGameRunner {
|
||||
});
|
||||
}
|
||||
|
||||
private doRetaliateAttackMostRecent(): void {
|
||||
if (!this.isActive || this.gameView.inSpawnPhase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.myPlayer === null) {
|
||||
if (!this.clientID) return;
|
||||
const myPlayer = this.gameView.playerByClientID(this.clientID);
|
||||
if (myPlayer === null) return;
|
||||
this.myPlayer = myPlayer;
|
||||
}
|
||||
|
||||
const incomingAttacks = this.myPlayer.incomingAttacks().filter((a) => {
|
||||
const t = (
|
||||
this.gameView.playerBySmallID(a.attackerID) as PlayerView
|
||||
).type();
|
||||
return t !== PlayerType.Bot;
|
||||
});
|
||||
|
||||
if (incomingAttacks.length === 0) return;
|
||||
|
||||
const mostRecentAttack = incomingAttacks[incomingAttacks.length - 1];
|
||||
|
||||
const attacker = this.gameView.playerBySmallID(
|
||||
mostRecentAttack.attackerID,
|
||||
) as PlayerView;
|
||||
if (!attacker) return;
|
||||
|
||||
const counterTroops = Math.min(
|
||||
mostRecentAttack.troops,
|
||||
this.renderer.uiState.attackRatio * this.myPlayer.troops(),
|
||||
);
|
||||
this.eventBus.emit(new SendAttackIntentEvent(attacker.id(), counterTroops));
|
||||
}
|
||||
|
||||
private doRequestAllianceUnderCursor(): void {
|
||||
const tile = this.getTileUnderCursor();
|
||||
if (tile === null) return;
|
||||
|
||||
@@ -128,19 +128,19 @@ export class GameModeSelector extends LitElement {
|
||||
${this.renderSmallActionCard(
|
||||
translateText("main.create"),
|
||||
this.openHostLobby,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)}
|
||||
${!crazyGamesSDK.isOnCrazyGames()
|
||||
? this.renderSmallActionCard(
|
||||
translateText("mode_selector.ranked_title"),
|
||||
this.openRankedMenu,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)
|
||||
: html`<div class="invisible"></div>`}
|
||||
${this.renderSmallActionCard(
|
||||
translateText("main.join"),
|
||||
this.openJoinLobby,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)}
|
||||
</div>
|
||||
<!-- iOS Add to Home Screen banner -->
|
||||
@@ -200,19 +200,19 @@ export class GameModeSelector extends LitElement {
|
||||
${this.renderSmallActionCard(
|
||||
translateText("main.create"),
|
||||
this.openHostLobby,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)}
|
||||
${!crazyGamesSDK.isOnCrazyGames()
|
||||
? this.renderSmallActionCard(
|
||||
translateText("mode_selector.ranked_title"),
|
||||
this.openRankedMenu,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)
|
||||
: html`<div class="invisible"></div>`}
|
||||
${this.renderSmallActionCard(
|
||||
translateText("main.join"),
|
||||
this.openJoinLobby,
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105",
|
||||
"bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:scale-105 hover:shadow-[var(--shadow-lobby-card-hover)]",
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,7 +275,7 @@ export class GameModeSelector extends LitElement {
|
||||
? getSecondsUntilServerTimestamp(lobby.startsAt, this.serverTimeOffset)
|
||||
: undefined;
|
||||
|
||||
let timeDisplay: string = "";
|
||||
let timeDisplay: string;
|
||||
let timeDisplayUppercase = false;
|
||||
if (timeRemaining === undefined) {
|
||||
timeDisplay = renderDuration(this.defaultLobbyTime);
|
||||
|
||||
@@ -153,6 +153,8 @@ export class DoBoatAttackEvent implements GameEvent {}
|
||||
|
||||
export class DoGroundAttackEvent implements GameEvent {}
|
||||
|
||||
export class DoRetaliateAttackEvent implements GameEvent {}
|
||||
|
||||
export class DoRequestAllianceEvent implements GameEvent {}
|
||||
|
||||
export class DoBreakAllianceEvent implements GameEvent {}
|
||||
@@ -497,6 +499,11 @@ export class InputHandler {
|
||||
this.eventBus.emit(new DoGroundAttackEvent());
|
||||
}
|
||||
|
||||
if (this.keybindMatchesEvent(e, this.keybinds.retaliateAttack)) {
|
||||
e.preventDefault();
|
||||
this.eventBus.emit(new DoRetaliateAttackEvent());
|
||||
}
|
||||
|
||||
if (this.keybindMatchesEvent(e, this.keybinds.attackRatioDown)) {
|
||||
e.preventDefault();
|
||||
const increment = this.userSettings.attackRatioIncrement();
|
||||
@@ -520,10 +527,12 @@ export class InputHandler {
|
||||
}
|
||||
|
||||
// Two-phase build keybind matching: exact code match first, then digit/Numpad alias.
|
||||
const matchedBuild = this.resolveBuildKeybind(e.code, e.shiftKey);
|
||||
if (matchedBuild !== null) {
|
||||
e.preventDefault();
|
||||
this.setGhostStructure(matchedBuild);
|
||||
if (this.canUseBuildKeybinds()) {
|
||||
const matchedBuild = this.resolveBuildKeybind(e.code, e.shiftKey);
|
||||
if (matchedBuild !== null) {
|
||||
e.preventDefault();
|
||||
this.setGhostStructure(matchedBuild);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.keybindMatchesEvent(e, this.keybinds.requestAlliance)) {
|
||||
@@ -944,6 +953,11 @@ export class InputHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
private canUseBuildKeybinds(): boolean {
|
||||
const myPlayer = this.gameView.myPlayer?.();
|
||||
return !this.gameView.inSpawnPhase() && myPlayer?.isAlive() === true;
|
||||
}
|
||||
|
||||
private getPinchDistance(): number {
|
||||
const pointerEvents = Array.from(this.pointers.values());
|
||||
const dx = pointerEvents[0].clientX - pointerEvents[1].clientX;
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import "./AccountModal";
|
||||
import { getUserMe } from "./Api";
|
||||
import { userAuth } from "./Auth";
|
||||
import "./ClanModal";
|
||||
import { joinLobby, type JoinLobbyResult } from "./ClientGameRunner";
|
||||
import { getPlayerCosmeticsRefs } from "./Cosmetics";
|
||||
import { crazyGamesSDK } from "./CrazyGamesSDK";
|
||||
@@ -440,11 +441,9 @@ class Client {
|
||||
|
||||
const onUserMe = async (userMeResponse: UserMeResponse | false) => {
|
||||
updateAccountNavButton(userMeResponse);
|
||||
const hasLinkedAccount =
|
||||
!crazyGamesSDK.isOnCrazyGames() &&
|
||||
((userMeResponse || null)?.player?.flares?.length ?? 0) > 0;
|
||||
console.log("ads enabled: ", hasLinkedAccount);
|
||||
window.adsEnabled = !hasLinkedAccount && !crazyGamesSDK.isOnCrazyGames();
|
||||
const isAdFree =
|
||||
userMeResponse !== false && userMeResponse.player?.adfree === true;
|
||||
window.adsEnabled = !isAdFree && !crazyGamesSDK.isOnCrazyGames();
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("userMeResponse", {
|
||||
detail: userMeResponse,
|
||||
@@ -829,6 +828,7 @@ class Client {
|
||||
"leaderboard-button",
|
||||
"token-login",
|
||||
"matchmaking-modal",
|
||||
"clan-modal",
|
||||
"lang-selector",
|
||||
"homepage-promos",
|
||||
].forEach((tag) => {
|
||||
|
||||
@@ -664,7 +664,17 @@ export class UserSettingModal extends BaseModal {
|
||||
></setting-keybind>
|
||||
|
||||
<setting-keybind
|
||||
action=${KeybindAction.swapDirection}
|
||||
action==${KeybindAction.retaliateAttack}
|
||||
label=${translateText("user_setting.retaliate_attack")}
|
||||
description=${translateText("user_setting.retaliate_attack_desc")}
|
||||
defaultKey=${this.defaultKeybinds.retaliateAttack}
|
||||
.value=${this.getKeyValue("retaliateAttack")}
|
||||
.display=${this.getKeyChar("retaliateAttack")}
|
||||
@change=${this.handleKeybindChange}
|
||||
></setting-keybind>
|
||||
|
||||
<setting-keybind
|
||||
action="swapDirection"
|
||||
label=${translateText("user_setting.swap_direction")}
|
||||
description=${translateText("user_setting.swap_direction_desc")}
|
||||
.defaultKey=${this.defaultKeybinds.swapDirection}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { generateCryptoRandomUUID, translateText } from "../client/Utils";
|
||||
import { sanitizeClanTag } from "../core/Util";
|
||||
import {
|
||||
MAX_CLAN_TAG_LENGTH,
|
||||
@@ -224,7 +223,7 @@ export class UsernameInput extends LitElement {
|
||||
}
|
||||
|
||||
export function genAnonUsername(): string {
|
||||
const uuid = uuidv4();
|
||||
const uuid = generateCryptoRandomUUID();
|
||||
const cleanUuid = uuid.replace(/-/g, "").toLowerCase();
|
||||
const decimal = BigInt(`0x${cleanUuid}`);
|
||||
const threeDigits = decimal % 1000n;
|
||||
|
||||
@@ -755,6 +755,18 @@ export function getServerNow(
|
||||
return localNowMs + serverTimeOffsetMs;
|
||||
}
|
||||
|
||||
export function showToast(
|
||||
message: string,
|
||||
color: "red" | "green",
|
||||
duration = 3500,
|
||||
): void {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("show-message", {
|
||||
detail: { message, color, duration },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getSecondsUntilServerTimestamp(
|
||||
targetServerTimestampMs: number,
|
||||
serverTimeOffsetMs: number,
|
||||
|
||||
@@ -154,42 +154,43 @@ export abstract class BaseModal extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a standardized loading spinner with optional custom message.
|
||||
* Use this for consistent loading states across all modals.
|
||||
*
|
||||
* @param message - Optional loading message text. Defaults to no message.
|
||||
* @param spinnerColor - Optional spinner color. Defaults to 'blue'.
|
||||
* @returns TemplateResult of the loading UI
|
||||
*/
|
||||
protected renderLoadingSpinner(
|
||||
message?: string,
|
||||
spinnerColor: "blue" | "green" | "yellow" | "white" = "blue",
|
||||
): TemplateResult {
|
||||
const colorClasses = {
|
||||
blue: "border-blue-500/30 border-t-blue-500",
|
||||
green: "border-green-500/30 border-t-green-500",
|
||||
yellow: "border-yellow-500/30 border-t-yellow-500",
|
||||
white: "border-white/20 border-t-white",
|
||||
};
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="flex flex-col items-center justify-center p-12 text-white h-full min-h-[400px]"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 border-4 ${colorClasses[
|
||||
spinnerColor
|
||||
]} rounded-full animate-spin mb-4"
|
||||
></div>
|
||||
${message
|
||||
? html`<p
|
||||
class="text-white/60 font-medium tracking-wide animate-pulse"
|
||||
>
|
||||
${message}
|
||||
</p>`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
return renderLoadingSpinner(message, spinnerColor);
|
||||
}
|
||||
}
|
||||
|
||||
const spinnerColorClasses: Record<string, string> = {
|
||||
blue: "border-blue-500/30 border-t-blue-500",
|
||||
green: "border-green-500/30 border-t-green-500",
|
||||
yellow: "border-yellow-500/30 border-t-yellow-500",
|
||||
white: "border-white/20 border-t-white",
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a standardized loading spinner with optional custom message.
|
||||
* Use this for consistent loading states across all modals.
|
||||
*/
|
||||
export function renderLoadingSpinner(
|
||||
message?: string,
|
||||
spinnerColor: "blue" | "green" | "yellow" | "white" = "blue",
|
||||
): TemplateResult {
|
||||
return html`
|
||||
<div
|
||||
class="flex flex-col items-center justify-center p-12 text-white h-full min-h-[400px]"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 border-4 ${spinnerColorClasses[
|
||||
spinnerColor
|
||||
]} rounded-full animate-spin mb-4"
|
||||
></div>
|
||||
${message
|
||||
? html`<p class="text-white/60 font-medium tracking-wide animate-pulse">
|
||||
${message}
|
||||
</p>`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import { html, LitElement, render as litRender } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { translateText } from "../Utils";
|
||||
|
||||
/**
|
||||
* A reusable inline confirmation dialog.
|
||||
*
|
||||
* Usage:
|
||||
* ```html
|
||||
* <confirm-dialog
|
||||
* .message=${"Are you sure?"}
|
||||
* variant="danger"
|
||||
* @confirm=${() => doThing()}
|
||||
* @cancel=${() => {}}
|
||||
* ></confirm-dialog>
|
||||
* ```
|
||||
*
|
||||
* For ban-style flows, add a textarea:
|
||||
* ```html
|
||||
* <confirm-dialog
|
||||
* .message=${"Ban this player?"}
|
||||
* variant="warning"
|
||||
* textareaPlaceholder="Reason (optional)"
|
||||
* @confirm=${(e) => ban(e.detail.text)}
|
||||
* @cancel=${() => {}}
|
||||
* ></confirm-dialog>
|
||||
* ```
|
||||
*/
|
||||
@customElement("confirm-dialog")
|
||||
export class ConfirmDialog extends LitElement {
|
||||
@property() message = "";
|
||||
@property() variant: "danger" | "warning" = "danger";
|
||||
@property() textareaPlaceholder = "";
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
|
||||
@state() private text = "";
|
||||
|
||||
private portal: HTMLDivElement | null = null;
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.portal = document.createElement("div");
|
||||
document.body.appendChild(this.portal);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.portal) {
|
||||
litRender(html``, this.portal);
|
||||
this.portal.remove();
|
||||
this.portal = null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.portal) {
|
||||
litRender(this.renderOverlay(), this.portal);
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
private renderOverlay() {
|
||||
const isDanger = this.variant === "danger";
|
||||
const borderColor = isDanger ? "border-red-500/50" : "border-amber-500/50";
|
||||
const cardBg = "bg-surface";
|
||||
const textColor = isDanger ? "text-red-300" : "text-amber-300";
|
||||
const btnClass = isDanger
|
||||
? "bg-red-600 text-white hover:bg-red-700"
|
||||
: "bg-amber-600 text-white hover:bg-amber-700";
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/80"
|
||||
@click=${(e: Event) => {
|
||||
if (e.target === e.currentTarget) this.handleCancel();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="mx-4 w-full max-w-sm p-6 rounded-2xl border ${borderColor} ${cardBg} shadow-2xl"
|
||||
>
|
||||
<p class="text-sm font-medium ${textColor} mb-5">${this.message}</p>
|
||||
${this.textareaPlaceholder
|
||||
? html`<textarea
|
||||
.value=${this.text}
|
||||
@input=${(e: Event) =>
|
||||
(this.text = (e.target as HTMLTextAreaElement).value)}
|
||||
maxlength="200"
|
||||
rows="2"
|
||||
placeholder="${this.textareaPlaceholder}"
|
||||
class="w-full px-3 py-2 mb-4 bg-white/5 border border-white/10 rounded-lg text-white placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-amber-500/50 text-sm resize-none"
|
||||
></textarea>`
|
||||
: ""}
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
@click=${() => this.handleCancel()}
|
||||
?disabled=${this.disabled}
|
||||
class="flex-1 px-4 py-2.5 text-xs font-bold uppercase tracking-wider rounded-xl bg-white/5 text-white/60 border border-white/10 hover:bg-white/10 hover:text-white/80 transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("common.cancel")}
|
||||
</button>
|
||||
<button
|
||||
@click=${() => this.handleConfirm()}
|
||||
?disabled=${this.disabled}
|
||||
class="flex-1 px-4 py-2.5 text-xs font-bold uppercase tracking-wider rounded-xl ${btnClass} transition-all disabled:opacity-50 disabled:pointer-events-none border-0"
|
||||
>
|
||||
${translateText("common.confirm")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private handleConfirm() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("confirm", { detail: { text: this.text } }),
|
||||
);
|
||||
this.text = "";
|
||||
}
|
||||
|
||||
private handleCancel() {
|
||||
this.dispatchEvent(new CustomEvent("cancel"));
|
||||
this.text = "";
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ export class CosmeticContainer extends LitElement {
|
||||
if (this.name) {
|
||||
this._nameEl ??= document.createElement("div");
|
||||
const cfg = rarityConfig[this.rarity] ?? fallback;
|
||||
this._nameEl.className = `text-xs font-bold uppercase tracking-wider text-center truncate w-full`;
|
||||
this._nameEl.className = `text-xs font-bold uppercase tracking-wider text-center whitespace-normal break-words w-full`;
|
||||
this._nameEl.style.color = cfg.nameColor;
|
||||
this._nameEl.title = this.name;
|
||||
this._nameEl.textContent = this.name;
|
||||
|
||||
@@ -123,6 +123,11 @@ export class DesktopNavBar extends LitElement {
|
||||
data-page="page-leaderboard"
|
||||
data-i18n="main.leaderboard"
|
||||
></button>
|
||||
<button
|
||||
class="nav-menu-item text-white/70 hover:text-blue-500 font-medium tracking-wider uppercase cursor-pointer transition-colors [&.active]:text-blue-500"
|
||||
data-page="page-clan"
|
||||
data-i18n="main.clans"
|
||||
></button>
|
||||
<div class="relative">
|
||||
<button
|
||||
class="nav-menu-item text-white/70 hover:text-malibu-blue font-medium tracking-wider uppercase cursor-pointer transition-colors [&.active]:text-malibu-blue "
|
||||
|
||||
@@ -69,17 +69,17 @@ export class MobileNavBar extends LitElement {
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="flex-1 w-full flex flex-col justify-start overflow-y-auto lg:pt-[clamp(1rem,3vh,4rem)] lg:pb-[clamp(0.5rem,2vh,2rem)] lg:px-[clamp(1rem,1.5vw,2rem)] p-5 gap-[clamp(1rem,3vh,3rem)]"
|
||||
class="flex-1 w-full flex flex-col justify-start overflow-y-auto lg:pt-[clamp(1rem,3vh,4rem)] lg:pb-[clamp(0.5rem,2vh,2rem)] lg:px-[clamp(1rem,1.5vw,2rem)] pt-4 pb-4 px-5 gap-4 lg:gap-[clamp(1rem,3vh,3rem)]"
|
||||
>
|
||||
<!-- Logo + Menu -->
|
||||
<div
|
||||
class="flex flex-col text-malibu-blue mb-[clamp(1rem,2vh,2rem)] ml-[clamp(0.2rem,0.4vw,0.4vh)]"
|
||||
class="flex flex-col text-malibu-blue mb-4 ml-[clamp(0.2rem,0.4vw,0.4vh)]"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<img
|
||||
src=${assetUrl("images/OpenFrontLogo.svg")}
|
||||
alt="OpenFront"
|
||||
class="h-full w-auto"
|
||||
class="w-auto h-auto max-w-[220px] max-h-[4.5rem]"
|
||||
/>
|
||||
<div
|
||||
id="game-version"
|
||||
@@ -114,6 +114,11 @@ export class MobileNavBar extends LitElement {
|
||||
data-page="page-leaderboard"
|
||||
data-i18n="main.leaderboard"
|
||||
></button>
|
||||
<button
|
||||
class="nav-menu-item block w-full text-left font-bold uppercase tracking-[0.05em] text-white/70 transition-all duration-200 cursor-pointer hover:text-blue-600 hover:translate-x-2.5 hover:drop-shadow-[0_0_20px_rgba(37,99,235,0.5)] [&.active]:text-blue-600 [&.active]:translate-x-2.5 [&.active]:drop-shadow-[0_0_20px_rgba(37,99,235,0.5)] text-[clamp(18px,2.8vh,32px)] py-[clamp(0.2rem,0.8vh,0.75rem)]"
|
||||
data-page="page-clan"
|
||||
data-i18n="main.clans"
|
||||
></button>
|
||||
<div
|
||||
class="no-crazygames nav-menu-item flex items-center w-full cursor-pointer"
|
||||
data-page="page-item-store"
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { type ClanBan, fetchClanBans, unbanClanMember } from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import "../CopyButton";
|
||||
import { modalHeader } from "../ui/ModalHeader";
|
||||
import {
|
||||
formatClanDate,
|
||||
modalContainerClass,
|
||||
renderLoadingSpinner,
|
||||
renderMemberSearchInput,
|
||||
renderServerPagination,
|
||||
showToast,
|
||||
} from "./ClanShared";
|
||||
|
||||
@customElement("clan-bans-view")
|
||||
export class ClanBansView extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property() clanTag = "";
|
||||
|
||||
@state() private bans: ClanBan[] = [];
|
||||
@state() private bansTotal = 0;
|
||||
@state() private bansPage = 1;
|
||||
@state() private bansLimit = 20;
|
||||
@state() private memberActionPending = false;
|
||||
@state() private loading = false;
|
||||
private memberSearch = "";
|
||||
private memberSearchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.loadBans(1);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
private async loadBans(page: number, showLoading = true) {
|
||||
if (showLoading) this.loading = true;
|
||||
else this.memberActionPending = true;
|
||||
try {
|
||||
const data = await fetchClanBans(this.clanTag, page);
|
||||
if (!data) {
|
||||
showToast(translateText("clan_modal.error_failed"), "red");
|
||||
return;
|
||||
}
|
||||
if (data.results.length === 0 && page > 1) {
|
||||
await this.loadBans(1, false);
|
||||
return;
|
||||
}
|
||||
this.bans = data.results;
|
||||
this.bansTotal = data.total;
|
||||
this.bansLimit = data.limit;
|
||||
this.bansPage = data.page;
|
||||
} finally {
|
||||
if (showLoading) this.loading = false;
|
||||
else this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleUnban(publicId: string) {
|
||||
if (this.memberActionPending) return;
|
||||
this.memberActionPending = true;
|
||||
try {
|
||||
const result = await unbanClanMember(this.clanTag, publicId);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
this.bans = this.bans.filter((b) => b.publicId !== publicId);
|
||||
this.bansTotal--;
|
||||
showToast(translateText("clan_modal.member_unbanned"), "green");
|
||||
if (this.bans.length === 0 && this.bansPage > 1) {
|
||||
await this.loadBans(this.bansPage - 1, false);
|
||||
}
|
||||
} finally {
|
||||
this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private onSearchInput(e: Event) {
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
this.memberSearchDebounce = setTimeout(() => {
|
||||
this.memberSearch = (e.target as HTMLInputElement).value;
|
||||
this.requestUpdate();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.loading)
|
||||
return html`<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.banned_players"),
|
||||
onBack: () =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-back", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
),
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}${renderLoadingSpinner()}
|
||||
</div>`;
|
||||
|
||||
const totalPages = Math.ceil(this.bansTotal / this.bansLimit);
|
||||
const filtered = this.memberSearch
|
||||
? this.bans.filter((b) =>
|
||||
b.publicId.toLowerCase().includes(this.memberSearch.toLowerCase()),
|
||||
)
|
||||
: this.bans;
|
||||
|
||||
return html`
|
||||
<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.banned_players"),
|
||||
onBack: () =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-back", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
),
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: html`<span
|
||||
class="text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full bg-white/10 text-white/50 border border-white/10"
|
||||
>${this.bansTotal}</span
|
||||
>`,
|
||||
})}
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar mr-1 p-4 lg:p-6">
|
||||
${renderMemberSearchInput(
|
||||
(e) => this.onSearchInput(e),
|
||||
"clan_modal.search_members_placeholder",
|
||||
)}
|
||||
${filtered.length === 0
|
||||
? html`<div
|
||||
class="flex flex-col items-center justify-center p-12 text-center"
|
||||
>
|
||||
<p class="text-white/40 text-sm">
|
||||
${translateText("clan_modal.no_bans")}
|
||||
</p>
|
||||
</div>`
|
||||
: html`
|
||||
<div class="space-y-3">
|
||||
${filtered.map(
|
||||
(ban) => html`
|
||||
<div
|
||||
class="bg-white/5 rounded-xl border border-white/10 p-4 space-y-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-red-500/20 flex items-center justify-center shrink-0"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-red-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<copy-button
|
||||
compact
|
||||
.copyText=${ban.publicId}
|
||||
.displayText=${ban.publicId}
|
||||
.showVisibilityToggle=${false}
|
||||
.showCopyIcon=${false}
|
||||
></copy-button>
|
||||
<span class="text-white/30 text-xs shrink-0"
|
||||
>${translateText(
|
||||
"clan_modal.banned_by_label",
|
||||
)}</span
|
||||
>
|
||||
<copy-button
|
||||
compact
|
||||
.copyText=${ban.bannedBy}
|
||||
.displayText=${ban.bannedBy}
|
||||
.showVisibilityToggle=${false}
|
||||
.showCopyIcon=${false}
|
||||
></copy-button>
|
||||
<span class="text-white/30 text-xs shrink-0"
|
||||
>${formatClanDate(ban.createdAt)}</span
|
||||
>
|
||||
<div class="flex-1"></div>
|
||||
<button
|
||||
@click=${() => this.handleUnban(ban.publicId)}
|
||||
?disabled=${this.memberActionPending}
|
||||
class="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-lg bg-green-500/20 text-green-400 border border-green-500/30 hover:bg-green-500/30 transition-all disabled:opacity-50 disabled:pointer-events-none shrink-0"
|
||||
>
|
||||
${translateText("clan_modal.unban")}
|
||||
</button>
|
||||
</div>
|
||||
${ban.reason
|
||||
? html`<div class="text-white/50 text-xs pl-10">
|
||||
${translateText("clan_modal.ban_reason", {
|
||||
reason: ban.reason,
|
||||
})}
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
${totalPages > 1
|
||||
? renderServerPagination(this.bansPage, totalPages, (p) =>
|
||||
this.loadBans(p, false),
|
||||
)
|
||||
: ""}
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { type ClanBrowseResponse, fetchClans } from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import "./ClanCard";
|
||||
import { type ClanRole, renderLoadingSpinner } from "./ClanShared";
|
||||
|
||||
export interface BrowseState {
|
||||
data: ClanBrowseResponse | null;
|
||||
page: number;
|
||||
query: string;
|
||||
}
|
||||
|
||||
@customElement("clan-browse-view")
|
||||
export class ClanBrowseView extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property({ type: Object }) myClanRoles: Map<string, ClanRole> = new Map();
|
||||
@property({ type: Array }) myPendingRequests: { tag: string }[] = [];
|
||||
@property({ type: Object }) cachedState: BrowseState | null = null;
|
||||
|
||||
@state() private searchQuery = "";
|
||||
@state() private browseData: ClanBrowseResponse | null = null;
|
||||
@state() private browsePage = 1;
|
||||
@state() private loading = false;
|
||||
@state() private errorMsg = "";
|
||||
private searchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
private asyncGeneration = 0;
|
||||
|
||||
private emitState() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("browse-updated", {
|
||||
detail: {
|
||||
data: this.browseData,
|
||||
page: this.browsePage,
|
||||
query: this.searchQuery,
|
||||
} satisfies BrowseState,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async loadBrowse() {
|
||||
const gen = ++this.asyncGeneration;
|
||||
this.loading = true;
|
||||
this.errorMsg = "";
|
||||
try {
|
||||
const data = await fetchClans(
|
||||
this.searchQuery || undefined,
|
||||
this.browsePage,
|
||||
);
|
||||
if (gen !== this.asyncGeneration) return;
|
||||
if (data === false) throw new Error("fetch failed");
|
||||
this.browseData = data;
|
||||
this.emitState();
|
||||
} catch {
|
||||
if (gen !== this.asyncGeneration) return;
|
||||
this.errorMsg = translateText("clan_modal.error_loading");
|
||||
} finally {
|
||||
if (gen === this.asyncGeneration) this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private onSearchInput(e: Event) {
|
||||
this.searchQuery = (e.target as HTMLInputElement).value;
|
||||
if (this.searchDebounce) clearTimeout(this.searchDebounce);
|
||||
this.searchDebounce = setTimeout(() => {
|
||||
this.browsePage = 1;
|
||||
this.loadBrowse();
|
||||
}, 400);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.cachedState?.data) {
|
||||
this.browseData = this.cachedState.data;
|
||||
this.browsePage = this.cachedState.page;
|
||||
this.searchQuery = this.cachedState.query;
|
||||
} else {
|
||||
this.loadBrowse();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.searchDebounce) clearTimeout(this.searchDebounce);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.loading && !this.browseData)
|
||||
return html`<div class="p-4 lg:p-6">${renderLoadingSpinner()}</div>`;
|
||||
|
||||
const totalPages = this.browseData
|
||||
? Math.ceil(this.browseData.total / this.browseData.limit)
|
||||
: 0;
|
||||
const pendingTags = new Set(this.myPendingRequests.map((r) => r.tag));
|
||||
const filtered = (this.browseData?.results ?? []).filter(
|
||||
(clan) => !this.myClanRoles.has(clan.tag),
|
||||
);
|
||||
|
||||
return html`
|
||||
<div class="p-4 lg:p-6 space-y-4">
|
||||
<div class="relative">
|
||||
<input
|
||||
type="text"
|
||||
.value=${this.searchQuery}
|
||||
@input=${(e: Event) => this.onSearchInput(e)}
|
||||
class="w-full pl-10 pr-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/20 focus:outline-none focus:ring-2 focus:ring-malibu-blue/50 focus:border-malibu-blue/50 transition-all font-medium hover:bg-white/10 text-sm"
|
||||
placeholder="${translateText("clan_modal.search_placeholder")}"
|
||||
/>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-white/30 absolute left-3 top-1/2 -translate-y-1/2"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
${this.errorMsg
|
||||
? html`<p class="text-red-400 text-sm text-center py-4">
|
||||
${this.errorMsg}
|
||||
</p>`
|
||||
: ""}
|
||||
|
||||
<div class="space-y-3">
|
||||
${filtered.length === 0 && this.browseData
|
||||
? html`<p class="text-white/40 text-sm text-center py-8">
|
||||
${translateText("clan_modal.no_results")}
|
||||
</p>`
|
||||
: filtered.map(
|
||||
(clan) =>
|
||||
html`<clan-card
|
||||
.clan=${clan}
|
||||
?pending=${pendingTags.has(clan.tag)}
|
||||
></clan-card>`,
|
||||
)}
|
||||
</div>
|
||||
|
||||
${totalPages > 1
|
||||
? html`
|
||||
<div class="flex items-center justify-center gap-2 pt-2">
|
||||
<button
|
||||
@click=${() => {
|
||||
this.browsePage = Math.max(1, this.browsePage - 1);
|
||||
this.loadBrowse();
|
||||
}}
|
||||
?disabled=${this.browsePage <= 1}
|
||||
class="px-2 py-1 text-xs font-bold rounded-lg transition-all ${this
|
||||
.browsePage <= 1
|
||||
? "text-white/20 cursor-not-allowed"
|
||||
: "text-white/60 hover:text-white hover:bg-white/10"}"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<span class="text-xs text-white/50 font-medium">
|
||||
${this.browsePage} / ${totalPages}
|
||||
</span>
|
||||
<button
|
||||
@click=${() => {
|
||||
this.browsePage = Math.min(totalPages, this.browsePage + 1);
|
||||
this.loadBrowse();
|
||||
}}
|
||||
?disabled=${this.browsePage >= totalPages}
|
||||
class="px-2 py-1 text-xs font-bold rounded-lg transition-all ${this
|
||||
.browsePage >= totalPages
|
||||
? "text-white/20 cursor-not-allowed"
|
||||
: "text-white/60 hover:text-white hover:bg-white/10"}"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import type { ClanInfo } from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import { translateClanRole } from "./ClanShared";
|
||||
|
||||
@customElement("clan-card")
|
||||
export class ClanCard extends LitElement {
|
||||
@property({ type: Object }) clan!: ClanInfo;
|
||||
@property() clanRole?: string;
|
||||
@property({ type: Boolean }) pending = false;
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
}
|
||||
|
||||
private onClick() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-select", {
|
||||
detail: { tag: this.clan.tag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private renderBadge() {
|
||||
const base =
|
||||
"text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full shrink-0";
|
||||
if (this.clanRole) {
|
||||
const colors =
|
||||
this.clanRole === "leader"
|
||||
? "bg-amber-500/20 text-amber-400 border border-amber-500/30"
|
||||
: "bg-malibu-blue/15 text-aquarius border border-malibu-blue/30";
|
||||
return html`<span class="${base} ${colors}"
|
||||
>${translateClanRole(this.clanRole)}</span
|
||||
>`;
|
||||
}
|
||||
if (this.pending) {
|
||||
return html`<span
|
||||
class="${base} bg-amber-500/20 text-amber-400 border border-amber-500/30"
|
||||
>${translateText("clan_modal.request_pending")}</span
|
||||
>`;
|
||||
}
|
||||
if (this.clan.isOpen) {
|
||||
return html`<span
|
||||
class="${base} bg-green-500/20 text-green-400 border border-green-500/30"
|
||||
>${translateText("clan_modal.open")}</span
|
||||
>`;
|
||||
}
|
||||
return html`<span
|
||||
class="${base} bg-red-500/20 text-red-400 border border-red-500/30"
|
||||
>${translateText("clan_modal.invite_only")}</span
|
||||
>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
const clan = this.clan;
|
||||
return html`
|
||||
<button
|
||||
@click=${() => this.onClick()}
|
||||
class="w-full text-left bg-white/5 hover:bg-white/10 rounded-xl border border-white/10 hover:border-white/20 p-4 transition-all cursor-pointer group"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-12 h-12 rounded-xl bg-gradient-to-br ${clan.isOpen
|
||||
? "from-malibu-blue/20 to-aquarius/20"
|
||||
: "from-amber-500/20 to-orange-500/20"} flex items-center justify-center border border-white/10 shrink-0"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">${clan.tag}</span>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<span class="text-white font-bold truncate block"
|
||||
>${clan.name}</span
|
||||
>
|
||||
<div class="flex items-center gap-4 mt-1 text-xs text-white/40">
|
||||
<span
|
||||
>${translateText("clan_modal.member_count", {
|
||||
count: clan.memberCount ?? 0,
|
||||
})}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
${this.renderBadge()}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5 text-white/20 group-hover:text-white/50 transition-colors shrink-0"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { invalidateUserMe } from "../../Api";
|
||||
import {
|
||||
type ClanInfo,
|
||||
type ClanMember,
|
||||
type ClanMemberOrder,
|
||||
type ClanMemberSort,
|
||||
type ClanStats,
|
||||
fetchClanDetail,
|
||||
fetchClanMembers,
|
||||
fetchClanStats,
|
||||
joinClan,
|
||||
leaveClan,
|
||||
} from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import "../ConfirmDialog";
|
||||
import "../CopyButton";
|
||||
import { modalHeader } from "../ui/ModalHeader";
|
||||
import {
|
||||
type ClanRole,
|
||||
defaultOrderForSort,
|
||||
filterMembersBySearch,
|
||||
modalContainerClass,
|
||||
renderClanWL,
|
||||
renderLoadingSpinner,
|
||||
renderMemberPagination,
|
||||
renderMemberRow,
|
||||
renderMemberSearchInput,
|
||||
renderMemberSortControl,
|
||||
renderStat,
|
||||
showToast,
|
||||
} from "./ClanShared";
|
||||
|
||||
@customElement("clan-detail-view")
|
||||
export class ClanDetailView extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property() clanTag = "";
|
||||
@property() myPublicId: string | null = null;
|
||||
@property({ type: Object }) myClanRoles: Map<string, ClanRole> = new Map();
|
||||
@property({ type: Array }) myPendingRequests: {
|
||||
tag: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
}[] = [];
|
||||
@property({ type: Object }) cachedDetail: {
|
||||
tag: string;
|
||||
members: ClanMember[];
|
||||
membersTotal: number;
|
||||
pendingRequestCount: number;
|
||||
stats: ClanStats | null;
|
||||
} | null = null;
|
||||
|
||||
@property({ type: Object }) cachedClan: ClanInfo | null = null;
|
||||
@state() private selectedClan: ClanInfo | null = null;
|
||||
@state() private myRole: ClanRole | null = null;
|
||||
@state() private members: ClanMember[] = [];
|
||||
@state() private membersTotal = 0;
|
||||
@state() private memberPage = 1;
|
||||
@state() private membersPerPage = 10;
|
||||
@state() private memberSort: ClanMemberSort = "default";
|
||||
@state() private memberOrder: ClanMemberOrder = "asc";
|
||||
@state() private pendingRequestCount = 0;
|
||||
@state() private clanStats: ClanStats | null = null;
|
||||
@state() private loading = false;
|
||||
@state() private actionPending = false;
|
||||
private memberSearch = "";
|
||||
private memberSearchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
private asyncGeneration = 0;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.cachedDetail && this.cachedDetail.tag === this.clanTag) {
|
||||
this.restoreFromCache(this.cachedDetail);
|
||||
} else if (this.clanTag) {
|
||||
this.loadDetail();
|
||||
}
|
||||
}
|
||||
|
||||
private restoreFromCache(cache: NonNullable<typeof this.cachedDetail>) {
|
||||
this.selectedClan = this.cachedClan;
|
||||
this.members = cache.members;
|
||||
this.membersTotal = cache.membersTotal;
|
||||
this.pendingRequestCount = cache.pendingRequestCount;
|
||||
this.clanStats = cache.stats;
|
||||
this.memberPage = 1;
|
||||
const knownRole = this.myClanRoles.get(this.clanTag);
|
||||
this.myRole = knownRole ?? null;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
private async loadDetail() {
|
||||
const gen = ++this.asyncGeneration;
|
||||
this.loading = true;
|
||||
this.myRole = null;
|
||||
this.pendingRequestCount = 0;
|
||||
this.memberSearch = "";
|
||||
|
||||
const isMember = this.myClanRoles.has(this.clanTag);
|
||||
const [detail, membersRes, stats] = await Promise.all([
|
||||
fetchClanDetail(this.clanTag),
|
||||
isMember
|
||||
? fetchClanMembers(
|
||||
this.clanTag,
|
||||
1,
|
||||
this.membersPerPage,
|
||||
this.memberSort,
|
||||
this.memberOrder,
|
||||
)
|
||||
: Promise.resolve(false as const),
|
||||
fetchClanStats(this.clanTag),
|
||||
]);
|
||||
|
||||
if (gen !== this.asyncGeneration) return;
|
||||
this.clanStats = stats || null;
|
||||
this.loading = false;
|
||||
|
||||
if (!detail) {
|
||||
showToast(translateText("clan_modal.failed_to_load_clan"), "red");
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-back", { bubbles: true, composed: true }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedClan = detail;
|
||||
this.memberPage = 1;
|
||||
|
||||
if (membersRes) {
|
||||
this.members = membersRes.results;
|
||||
this.membersTotal = membersRes.total;
|
||||
this.pendingRequestCount = membersRes.pendingRequests ?? 0;
|
||||
const knownRole = this.myClanRoles.get(this.clanTag);
|
||||
if (knownRole) {
|
||||
this.myRole = knownRole;
|
||||
} else {
|
||||
const me = this.myPublicId
|
||||
? membersRes.results.find((m) => m.publicId === this.myPublicId)
|
||||
: null;
|
||||
this.myRole = me ? me.role : null;
|
||||
}
|
||||
} else {
|
||||
this.members = [];
|
||||
this.membersTotal = 0;
|
||||
this.myRole = null;
|
||||
}
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("detail-loaded", {
|
||||
detail: {
|
||||
clan: detail,
|
||||
myRole: this.myRole,
|
||||
members: this.members,
|
||||
membersTotal: this.membersTotal,
|
||||
pendingRequestCount: this.pendingRequestCount,
|
||||
stats: this.clanStats,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async loadMemberPage(page: number) {
|
||||
if (!this.selectedClan) return;
|
||||
const res = await fetchClanMembers(
|
||||
this.selectedClan.tag,
|
||||
page,
|
||||
this.membersPerPage,
|
||||
this.memberSort,
|
||||
this.memberOrder,
|
||||
);
|
||||
if (!res) return;
|
||||
if (res.results.length === 0 && page > 1) {
|
||||
await this.loadMemberPage(1);
|
||||
return;
|
||||
}
|
||||
this.members = res.results;
|
||||
this.membersTotal = res.total;
|
||||
this.memberPage = page;
|
||||
this.pendingRequestCount = res.pendingRequests ?? 0;
|
||||
if (this.selectedClan.memberCount !== res.total) {
|
||||
this.selectedClan = { ...this.selectedClan, memberCount: res.total };
|
||||
}
|
||||
}
|
||||
|
||||
private onSortChange(sort: ClanMemberSort) {
|
||||
if (sort === this.memberSort) return;
|
||||
this.memberSort = sort;
|
||||
this.memberOrder = defaultOrderForSort(sort);
|
||||
this.loadMemberPage(1);
|
||||
}
|
||||
|
||||
private onOrderToggle() {
|
||||
this.memberOrder = this.memberOrder === "asc" ? "desc" : "asc";
|
||||
this.loadMemberPage(1);
|
||||
}
|
||||
|
||||
private async handleJoin() {
|
||||
if (!this.selectedClan || this.actionPending) return;
|
||||
this.actionPending = true;
|
||||
try {
|
||||
const result = await joinClan(this.selectedClan.tag);
|
||||
if ("error" in result) {
|
||||
showToast(
|
||||
result.reason
|
||||
? translateText(result.error, { reason: result.reason })
|
||||
: translateText(result.error),
|
||||
"red",
|
||||
);
|
||||
return;
|
||||
}
|
||||
invalidateUserMe();
|
||||
if (result.status === "joined") {
|
||||
// Joining an open clan should immediately switch this detail page into
|
||||
// member mode and refresh member-only data without requiring remount.
|
||||
this.myRole = "member";
|
||||
await this.loadMemberPage(1);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-joined", {
|
||||
detail: { tag: this.selectedClan.tag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("request-sent", {
|
||||
detail: {
|
||||
tag: this.selectedClan.tag,
|
||||
name: this.selectedClan.name,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
showToast(translateText("clan_modal.join_request_sent"), "green");
|
||||
}
|
||||
} finally {
|
||||
this.actionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleLeave() {
|
||||
if (!this.selectedClan || this.actionPending) return;
|
||||
this.actionPending = true;
|
||||
try {
|
||||
const result = await leaveClan(this.selectedClan.tag);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
invalidateUserMe();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-left", {
|
||||
detail: { tag: this.selectedClan.tag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
showToast(translateText("clan_modal.left_clan"), "green");
|
||||
} finally {
|
||||
this.actionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private onSearchInput(e: Event) {
|
||||
const val = (e.target as HTMLInputElement).value;
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
this.memberSearchDebounce = setTimeout(() => {
|
||||
this.memberSearch = val;
|
||||
this.requestUpdate();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.loading) {
|
||||
return html`
|
||||
<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.title"),
|
||||
onBack: () => this.back(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}
|
||||
${renderLoadingSpinner()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const clan = this.selectedClan;
|
||||
if (!clan) return "";
|
||||
|
||||
const isMember = this.myRole !== null;
|
||||
const isLeader = this.myRole === "leader";
|
||||
const isOfficer = this.myRole === "officer";
|
||||
const canManageRequests = isLeader || isOfficer;
|
||||
const hasPendingRequest = this.myPendingRequests.some(
|
||||
(r) => r.tag === clan.tag,
|
||||
);
|
||||
|
||||
return html`
|
||||
<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: clan.name,
|
||||
onBack: () => this.back(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: html`
|
||||
<span
|
||||
class="text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full bg-white/10 text-white/50 border border-white/10"
|
||||
>
|
||||
[${clan.tag}]
|
||||
</span>
|
||||
`,
|
||||
})}
|
||||
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar mr-1 p-4 lg:p-6">
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-5">
|
||||
<p class="text-white/70 text-sm">
|
||||
${clan.description ||
|
||||
translateText("clan_modal.no_description")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
${renderStat(
|
||||
translateText("clan_modal.members"),
|
||||
`${clan.memberCount ?? 0}`,
|
||||
)}
|
||||
${renderStat(
|
||||
translateText("clan_modal.status"),
|
||||
clan.isOpen
|
||||
? translateText("clan_modal.open")
|
||||
: translateText("clan_modal.invite_only"),
|
||||
)}
|
||||
</div>
|
||||
|
||||
${this.clanStats ? renderClanWL(this.clanStats) : ""}
|
||||
${canManageRequests && this.pendingRequestCount > 0
|
||||
? this.renderRequestsButton()
|
||||
: ""}
|
||||
${isMember ? this.renderMembersList() : ""}
|
||||
|
||||
<div class="flex flex-wrap gap-3">
|
||||
${this.renderActionButtons(
|
||||
isMember,
|
||||
isLeader,
|
||||
isOfficer,
|
||||
hasPendingRequest,
|
||||
clan,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderRequestsButton() {
|
||||
return html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-requests", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)}
|
||||
class="w-full flex items-center justify-between bg-amber-500/10 hover:bg-amber-500/15 rounded-xl border border-amber-500/20 p-4 transition-all cursor-pointer group"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-amber-500/20 flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5 text-amber-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<span class="text-amber-400 text-sm font-bold">
|
||||
${translateText("clan_modal.join_requests")}
|
||||
</span>
|
||||
<span class="text-amber-400/60 text-xs block">
|
||||
${translateText("clan_modal.pending_requests_count", {
|
||||
count: this.pendingRequestCount,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="px-2.5 py-1 text-xs font-bold rounded-full bg-amber-500/20 text-amber-400 border border-amber-500/30"
|
||||
>
|
||||
${this.pendingRequestCount}
|
||||
</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5 text-amber-400/40 group-hover:text-amber-400/70 transition-colors"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderMembersList() {
|
||||
const filtered = filterMembersBySearch(this.members, this.memberSearch);
|
||||
return html`
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-5 space-y-3">
|
||||
<h3 class="text-sm font-bold text-white/60 uppercase tracking-wider">
|
||||
${translateText("clan_modal.members")}
|
||||
</h3>
|
||||
${renderMemberSearchInput(
|
||||
(e: Event) => this.onSearchInput(e),
|
||||
undefined,
|
||||
renderMemberSortControl(
|
||||
this.memberSort,
|
||||
this.memberOrder,
|
||||
(s) => this.onSortChange(s),
|
||||
() => this.onOrderToggle(),
|
||||
),
|
||||
)}
|
||||
<div class="space-y-2">
|
||||
${filtered.map((m) => renderMemberRow(m, this.myPublicId))}
|
||||
</div>
|
||||
${renderMemberPagination(
|
||||
this.memberPage,
|
||||
this.membersTotal,
|
||||
this.membersPerPage,
|
||||
(p) => this.loadMemberPage(p),
|
||||
(pp) => {
|
||||
this.membersPerPage = pp;
|
||||
this.loadMemberPage(1);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderActionButtons(
|
||||
isMember: boolean,
|
||||
isLeader: boolean,
|
||||
isOfficer: boolean,
|
||||
hasPendingRequest: boolean,
|
||||
clan: ClanInfo,
|
||||
) {
|
||||
const buttons: ReturnType<typeof html>[] = [];
|
||||
if (!isMember && hasPendingRequest) {
|
||||
buttons.push(html`
|
||||
<button
|
||||
disabled
|
||||
class="flex-1 px-6 py-3 text-sm font-bold text-white/40 uppercase tracking-wider bg-white/5 rounded-xl border border-white/10 cursor-not-allowed"
|
||||
>
|
||||
${translateText("clan_modal.request_pending")}
|
||||
</button>
|
||||
`);
|
||||
} else if (!isMember && clan.isOpen) {
|
||||
buttons.push(html`
|
||||
<button
|
||||
@click=${() => this.handleJoin()}
|
||||
?disabled=${this.actionPending}
|
||||
class="flex-1 px-6 py-3 text-sm font-bold text-white uppercase tracking-wider bg-malibu-blue hover:bg-aquarius active:bg-malibu-blue/80 rounded-xl transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.join_clan")}
|
||||
</button>
|
||||
`);
|
||||
} else if (!isMember && !clan.isOpen) {
|
||||
buttons.push(html`
|
||||
<button
|
||||
@click=${() => this.handleJoin()}
|
||||
?disabled=${this.actionPending}
|
||||
class="flex-1 px-6 py-3 text-sm font-bold text-white uppercase tracking-wider bg-gradient-to-r from-amber-600 to-amber-700 hover:from-amber-500 hover:to-amber-600 rounded-xl transition-all shadow-lg hover:shadow-amber-900/40 border border-white/5 disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.request_invite")}
|
||||
</button>
|
||||
`);
|
||||
}
|
||||
if (isMember && !isLeader) {
|
||||
buttons.push(html`
|
||||
<button
|
||||
@click=${() => this.handleLeave()}
|
||||
?disabled=${this.actionPending}
|
||||
class="flex-1 px-6 py-3 text-sm font-bold text-white/70 uppercase tracking-wider bg-red-600/30 hover:bg-red-600/50 rounded-xl transition-all border border-red-500/30 disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.leave_clan")}
|
||||
</button>
|
||||
`);
|
||||
}
|
||||
if (isLeader || isOfficer) {
|
||||
buttons.push(html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-manage", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)}
|
||||
class="flex-1 px-6 py-3 text-sm font-bold text-white uppercase tracking-wider bg-white/10 hover:bg-white/15 rounded-xl transition-all border border-white/10"
|
||||
>
|
||||
${translateText("clan_modal.manage_clan")}
|
||||
</button>
|
||||
`);
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private back() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-back", { bubbles: true, composed: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,615 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { invalidateUserMe } from "../../Api";
|
||||
import {
|
||||
banClanMember,
|
||||
type ClanInfo,
|
||||
type ClanMember,
|
||||
type ClanMemberOrder,
|
||||
type ClanMemberSort,
|
||||
demoteMember,
|
||||
disbandClan,
|
||||
fetchClanMembers,
|
||||
kickMember,
|
||||
promoteMember,
|
||||
updateClan,
|
||||
} from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import "../ConfirmDialog";
|
||||
import "../CopyButton";
|
||||
import { modalHeader } from "../ui/ModalHeader";
|
||||
import {
|
||||
type ClanRole,
|
||||
defaultOrderForSort,
|
||||
filterMembersBySearch,
|
||||
formatClanDate,
|
||||
modalContainerClass,
|
||||
renderLoadingSpinner,
|
||||
renderMemberPagination,
|
||||
renderMemberSearchInput,
|
||||
renderMemberSortControl,
|
||||
renderRoleIcon,
|
||||
showToast,
|
||||
} from "./ClanShared";
|
||||
|
||||
@customElement("clan-manage-view")
|
||||
export class ClanManageView extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property() clanTag = "";
|
||||
@property({ type: Object }) selectedClan: ClanInfo | null = null;
|
||||
@property() myPublicId: string | null = null;
|
||||
@property() myRole: ClanRole | null = null;
|
||||
|
||||
@state() private manageName = "";
|
||||
@state() private manageDescription = "";
|
||||
@state() private manageIsOpen = true;
|
||||
@state() private saving = false;
|
||||
@state() private members: ClanMember[] = [];
|
||||
@state() private membersTotal = 0;
|
||||
@state() private memberPage = 1;
|
||||
@state() private membersPerPage = 10;
|
||||
@state() private memberSort: ClanMemberSort = "default";
|
||||
@state() private memberOrder: ClanMemberOrder = "asc";
|
||||
@state() private memberActionPending = false;
|
||||
@state() private loading = false;
|
||||
@state() private confirmAction: "disband" | "kick" | "ban" | null = null;
|
||||
@state() private confirmTargetId: string | null = null;
|
||||
@state() private pendingRequestCount = 0;
|
||||
@state() private actionPending = false;
|
||||
private memberSearch = "";
|
||||
private memberSearchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.selectedClan) {
|
||||
this.manageName = this.selectedClan.name;
|
||||
this.manageDescription = this.selectedClan.description ?? "";
|
||||
this.manageIsOpen = this.selectedClan.isOpen ?? true;
|
||||
}
|
||||
this.loadMembers(1);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
private async loadMembers(page: number) {
|
||||
if (this.members.length === 0) this.loading = true;
|
||||
const res = await fetchClanMembers(
|
||||
this.clanTag,
|
||||
page,
|
||||
this.membersPerPage,
|
||||
this.memberSort,
|
||||
this.memberOrder,
|
||||
);
|
||||
if (!res) {
|
||||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
if (res.results.length === 0 && page > 1) {
|
||||
await this.loadMembers(1);
|
||||
return;
|
||||
}
|
||||
this.members = res.results;
|
||||
this.membersTotal = res.total;
|
||||
this.memberPage = page;
|
||||
this.pendingRequestCount = res.pendingRequests ?? 0;
|
||||
if (this.selectedClan && this.selectedClan.memberCount !== res.total) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-updated", {
|
||||
detail: { memberCount: res.total },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
private async handleSaveSettings() {
|
||||
const clan = this.selectedClan;
|
||||
if (!clan) return;
|
||||
const patch: { name?: string; description?: string; isOpen?: boolean } = {};
|
||||
if (this.manageName !== clan.name) patch.name = this.manageName;
|
||||
if ((this.manageDescription ?? "") !== (clan.description ?? ""))
|
||||
patch.description = this.manageDescription;
|
||||
if (this.manageIsOpen !== (clan.isOpen ?? true))
|
||||
patch.isOpen = this.manageIsOpen;
|
||||
if (Object.keys(patch).length === 0) return;
|
||||
|
||||
this.saving = true;
|
||||
const result = await updateClan(this.clanTag, patch);
|
||||
if ("error" in result) {
|
||||
showToast(translateText(result.error), "red");
|
||||
this.saving = false;
|
||||
return;
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-updated", {
|
||||
detail: {
|
||||
name: result.name,
|
||||
description: result.description,
|
||||
isOpen: result.isOpen,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
this.saving = false;
|
||||
showToast(translateText("clan_modal.settings_saved"), "green");
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-detail", { bubbles: true, composed: true }),
|
||||
);
|
||||
}
|
||||
|
||||
private async handlePromote(publicId: string) {
|
||||
if (this.memberActionPending) return;
|
||||
this.memberActionPending = true;
|
||||
try {
|
||||
const result = await promoteMember(this.clanTag, publicId);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
await this.loadMembers(this.memberPage);
|
||||
showToast(translateText("clan_modal.member_promoted"), "green");
|
||||
} finally {
|
||||
this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleDemote(publicId: string) {
|
||||
if (this.memberActionPending) return;
|
||||
this.memberActionPending = true;
|
||||
try {
|
||||
const result = await demoteMember(this.clanTag, publicId);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
await this.loadMembers(this.memberPage);
|
||||
showToast(translateText("clan_modal.member_demoted"), "green");
|
||||
} finally {
|
||||
this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleKick(publicId: string) {
|
||||
if (this.memberActionPending) return;
|
||||
this.memberActionPending = true;
|
||||
try {
|
||||
const result = await kickMember(this.clanTag, publicId);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
await this.loadMembers(this.memberPage);
|
||||
showToast(translateText("clan_modal.member_kicked"), "green");
|
||||
} finally {
|
||||
this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleBan(publicId: string, reason: string) {
|
||||
if (this.memberActionPending) return;
|
||||
this.memberActionPending = true;
|
||||
try {
|
||||
const result = await banClanMember(
|
||||
this.clanTag,
|
||||
publicId,
|
||||
reason.trim().slice(0, 200) || undefined,
|
||||
);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
await this.loadMembers(this.memberPage);
|
||||
showToast(translateText("clan_modal.member_banned"), "green");
|
||||
} finally {
|
||||
this.memberActionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleDisband() {
|
||||
if (this.actionPending) return;
|
||||
this.actionPending = true;
|
||||
try {
|
||||
const result = await disbandClan(this.clanTag);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
invalidateUserMe();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("clan-disbanded", {
|
||||
detail: { tag: this.clanTag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
showToast(translateText("clan_modal.clan_disbanded"), "green");
|
||||
} finally {
|
||||
this.actionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private clearConfirm() {
|
||||
this.confirmAction = null;
|
||||
this.confirmTargetId = null;
|
||||
}
|
||||
|
||||
private onSearchInput(e: Event) {
|
||||
if (this.memberSearchDebounce) clearTimeout(this.memberSearchDebounce);
|
||||
this.memberSearchDebounce = setTimeout(() => {
|
||||
this.memberSearch = (e.target as HTMLInputElement).value;
|
||||
this.requestUpdate();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
private onSortChange(sort: ClanMemberSort) {
|
||||
if (sort === this.memberSort) return;
|
||||
this.memberSort = sort;
|
||||
this.memberOrder = defaultOrderForSort(sort);
|
||||
this.loadMembers(1);
|
||||
}
|
||||
|
||||
private onOrderToggle() {
|
||||
this.memberOrder = this.memberOrder === "asc" ? "desc" : "asc";
|
||||
this.loadMembers(1);
|
||||
}
|
||||
|
||||
private navigateDetail = () =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-detail", { bubbles: true, composed: true }),
|
||||
);
|
||||
|
||||
render() {
|
||||
if (this.loading)
|
||||
return html`<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.manage_clan"),
|
||||
onBack: this.navigateDetail,
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}
|
||||
${renderLoadingSpinner()}
|
||||
</div>`;
|
||||
|
||||
const clan = this.selectedClan;
|
||||
if (!clan) return "";
|
||||
|
||||
return html`${this.renderManageContent(clan)}${this.renderConfirmOverlay()}`;
|
||||
}
|
||||
|
||||
private renderConfirmOverlay() {
|
||||
if (!this.confirmAction) return "";
|
||||
|
||||
if (this.confirmAction === "disband") {
|
||||
return html`<confirm-dialog
|
||||
.message=${translateText("clan_modal.confirm_disband", {
|
||||
tag: this.selectedClan?.tag ?? "",
|
||||
name: this.selectedClan?.name ?? "",
|
||||
})}
|
||||
variant="danger"
|
||||
?disabled=${this.actionPending}
|
||||
@confirm=${() => {
|
||||
this.clearConfirm();
|
||||
this.handleDisband();
|
||||
}}
|
||||
@cancel=${() => this.clearConfirm()}
|
||||
></confirm-dialog>`;
|
||||
}
|
||||
if (this.confirmAction === "kick" && this.confirmTargetId) {
|
||||
return html`<confirm-dialog
|
||||
.message=${translateText("clan_modal.confirm_kick")}
|
||||
variant="warning"
|
||||
?disabled=${this.memberActionPending}
|
||||
@confirm=${() => {
|
||||
const id = this.confirmTargetId!;
|
||||
this.clearConfirm();
|
||||
this.handleKick(id);
|
||||
}}
|
||||
@cancel=${() => this.clearConfirm()}
|
||||
></confirm-dialog>`;
|
||||
}
|
||||
if (this.confirmAction === "ban" && this.confirmTargetId) {
|
||||
return html`<confirm-dialog
|
||||
.message=${translateText("clan_modal.confirm_ban")}
|
||||
variant="warning"
|
||||
textareaPlaceholder=${translateText("clan_modal.ban_reason_prompt")}
|
||||
?disabled=${this.memberActionPending}
|
||||
@confirm=${(e: CustomEvent<{ text: string }>) => {
|
||||
const id = this.confirmTargetId!;
|
||||
const reason = e.detail.text;
|
||||
this.clearConfirm();
|
||||
this.handleBan(id, reason);
|
||||
}}
|
||||
@cancel=${() => this.clearConfirm()}
|
||||
></confirm-dialog>`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private renderManageContent(clan: ClanInfo) {
|
||||
return html`
|
||||
<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.manage_clan"),
|
||||
onBack: this.navigateDetail,
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: html`<span
|
||||
class="text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full bg-white/10 text-white/50 border border-white/10"
|
||||
>[${clan.tag}]</span
|
||||
>`,
|
||||
})}
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar mr-1 p-4 lg:p-6">
|
||||
<div class="space-y-6">
|
||||
<!-- Edit Settings -->
|
||||
<div
|
||||
class="bg-white/5 rounded-2xl border border-white/10 p-6 space-y-5"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-bold text-white/60 uppercase tracking-wider"
|
||||
>
|
||||
${translateText("clan_modal.clan_settings")}
|
||||
</h3>
|
||||
<div>
|
||||
<label
|
||||
class="block text-[10px] font-bold text-white/40 uppercase tracking-wider mb-2"
|
||||
>${translateText("clan_modal.clan_name")}</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
.value=${this.manageName}
|
||||
@input=${(e: Event) =>
|
||||
(this.manageName = (e.target as HTMLInputElement).value)}
|
||||
maxlength="35"
|
||||
class="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/20 focus:outline-none focus:ring-2 focus:ring-malibu-blue/50 focus:border-malibu-blue/50 transition-all font-medium hover:bg-white/10 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-[10px] font-bold text-white/40 uppercase tracking-wider mb-2"
|
||||
>${translateText("clan_modal.description")}</label
|
||||
>
|
||||
<textarea
|
||||
.value=${this.manageDescription}
|
||||
@input=${(e: Event) =>
|
||||
(this.manageDescription = (
|
||||
e.target as HTMLTextAreaElement
|
||||
).value)}
|
||||
maxlength="200"
|
||||
rows="3"
|
||||
class="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/20 focus:outline-none focus:ring-2 focus:ring-malibu-blue/50 focus:border-malibu-blue/50 transition-all font-medium hover:bg-white/10 text-sm resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-white text-sm font-bold">
|
||||
${translateText("clan_modal.open_clan")}
|
||||
</div>
|
||||
<div class="text-white/40 text-xs">
|
||||
${translateText("clan_modal.open_clan_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked="${this.manageIsOpen}"
|
||||
aria-label="${translateText("clan_modal.open_clan")}"
|
||||
@click=${() => (this.manageIsOpen = !this.manageIsOpen)}
|
||||
class="relative w-12 h-7 rounded-full transition-all ${this
|
||||
.manageIsOpen
|
||||
? "bg-malibu-blue"
|
||||
: "bg-white/20"}"
|
||||
>
|
||||
<div
|
||||
class="absolute top-1 w-5 h-5 rounded-full bg-white shadow transition-all ${this
|
||||
.manageIsOpen
|
||||
? "left-6"
|
||||
: "left-1"}"
|
||||
></div>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
@click=${() => this.handleSaveSettings()}
|
||||
?disabled=${this.saving}
|
||||
class="w-full px-6 py-3 text-sm font-bold text-white uppercase tracking-wider bg-malibu-blue hover:bg-aquarius active:bg-malibu-blue/80 rounded-xl transition-all disabled:opacity-50"
|
||||
>
|
||||
${this.saving
|
||||
? translateText("clan_modal.saving")
|
||||
: translateText("clan_modal.save_changes")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Member Management -->
|
||||
<div
|
||||
class="bg-white/5 rounded-2xl border border-white/10 p-6 space-y-4"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-bold text-white/60 uppercase tracking-wider"
|
||||
>
|
||||
${translateText("clan_modal.members")}
|
||||
(${clan.memberCount ?? 0})
|
||||
</h3>
|
||||
${renderMemberSearchInput(
|
||||
(e) => this.onSearchInput(e),
|
||||
undefined,
|
||||
renderMemberSortControl(
|
||||
this.memberSort,
|
||||
this.memberOrder,
|
||||
(s) => this.onSortChange(s),
|
||||
() => this.onOrderToggle(),
|
||||
),
|
||||
)}
|
||||
${(() => {
|
||||
const filtered = filterMembersBySearch(
|
||||
this.members,
|
||||
this.memberSearch,
|
||||
);
|
||||
return html`
|
||||
<div class="space-y-2">
|
||||
${filtered.map((m) => this.renderManageMemberRow(m))}
|
||||
</div>
|
||||
${renderMemberPagination(
|
||||
this.memberPage,
|
||||
this.membersTotal,
|
||||
this.membersPerPage,
|
||||
(p) => this.loadMembers(p),
|
||||
(pp) => {
|
||||
this.membersPerPage = pp;
|
||||
this.loadMembers(1);
|
||||
},
|
||||
)}
|
||||
`;
|
||||
})()}
|
||||
</div>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
<div
|
||||
class="bg-red-500/5 rounded-2xl border border-red-500/20 p-6 space-y-4"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-bold text-red-400/80 uppercase tracking-wider"
|
||||
>
|
||||
${translateText("clan_modal.danger_zone")}
|
||||
</h3>
|
||||
<button
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-bans", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)}
|
||||
class="w-full px-6 py-3 text-sm font-bold text-red-400 uppercase tracking-wider bg-red-600/20 hover:bg-red-600/30 rounded-xl transition-all border border-red-500/30"
|
||||
>
|
||||
${translateText("clan_modal.banned_players")}
|
||||
</button>
|
||||
${this.myRole === "leader"
|
||||
? html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-transfer", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)}
|
||||
class="w-full px-6 py-3 text-sm font-bold text-amber-400 uppercase tracking-wider bg-amber-600/20 hover:bg-amber-600/30 rounded-xl transition-all border border-amber-500/30"
|
||||
>
|
||||
${translateText("clan_modal.transfer_leadership")}
|
||||
</button>
|
||||
<button
|
||||
@click=${() => {
|
||||
this.confirmAction = "disband";
|
||||
this.confirmTargetId = null;
|
||||
}}
|
||||
?disabled=${this.confirmAction === "disband"}
|
||||
class="w-full px-6 py-3 text-sm font-bold text-red-400 uppercase tracking-wider bg-red-600/20 hover:bg-red-600/30 rounded-xl transition-all border border-red-500/30 disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.disband_clan")}
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderManageMemberRow(member: ClanMember) {
|
||||
const isLeader = member.role === "leader";
|
||||
const isMe = member.publicId === this.myPublicId;
|
||||
const canModerate =
|
||||
!isMe &&
|
||||
!isLeader &&
|
||||
(this.myRole === "leader" ||
|
||||
(this.myRole === "officer" && member.role === "member"));
|
||||
const canPromote =
|
||||
!isMe && this.myRole === "leader" && member.role === "member";
|
||||
const canDemote =
|
||||
!isMe && this.myRole === "leader" && member.role === "officer";
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="flex flex-col py-2.5 px-3 rounded-xl border
|
||||
${isMe
|
||||
? "bg-malibu-blue/10 border-malibu-blue/20"
|
||||
: "bg-white/5 border-white/10"}"
|
||||
>
|
||||
<div class="flex items-center flex-wrap gap-1.5">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold shrink-0
|
||||
${isMe
|
||||
? "bg-malibu-blue/20 text-aquarius"
|
||||
: "bg-white/10 text-white/50"}"
|
||||
>
|
||||
${renderRoleIcon(member.role)}
|
||||
</div>
|
||||
<copy-button
|
||||
compact
|
||||
.copyText=${member.publicId}
|
||||
.displayText=${member.publicId}
|
||||
.showVisibilityToggle=${false}
|
||||
.showCopyIcon=${false}
|
||||
></copy-button>
|
||||
<span class="text-white/30 text-[10px] whitespace-nowrap">
|
||||
${translateText("clan_modal.joined_date", {
|
||||
date: formatClanDate(member.joinedAt),
|
||||
})}
|
||||
</span>
|
||||
<div class="flex items-center gap-1.5 ml-auto flex-wrap justify-end">
|
||||
${canPromote
|
||||
? html`<button
|
||||
@click=${() => this.handlePromote(member.publicId)}
|
||||
?disabled=${this.memberActionPending}
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full bg-purple-500/10 text-purple-400/70 border border-purple-500/20 hover:bg-purple-500/20 hover:text-purple-400 transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.promote")}
|
||||
</button>`
|
||||
: ""}
|
||||
${canDemote
|
||||
? html`<button
|
||||
@click=${() => this.handleDemote(member.publicId)}
|
||||
?disabled=${this.memberActionPending}
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full bg-white/5 text-white/40 border border-white/10 hover:bg-white/10 hover:text-white/60 transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.demote")}
|
||||
</button>`
|
||||
: ""}
|
||||
${canModerate
|
||||
? html`
|
||||
<button
|
||||
@click=${() => {
|
||||
this.confirmAction = "kick";
|
||||
this.confirmTargetId = member.publicId;
|
||||
}}
|
||||
?disabled=${this.memberActionPending ||
|
||||
this.confirmAction !== null}
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full bg-red-500/10 text-red-400/70 border border-red-500/20 hover:bg-red-500/20 hover:text-red-400 transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.kick")}
|
||||
</button>
|
||||
<button
|
||||
@click=${() => {
|
||||
this.confirmAction = "ban";
|
||||
this.confirmTargetId = member.publicId;
|
||||
}}
|
||||
?disabled=${this.memberActionPending ||
|
||||
this.confirmAction !== null}
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full bg-red-500/10 text-red-400/70 border border-red-500/20 hover:bg-red-500/20 hover:text-red-400 transition-all disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.ban")}
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { invalidateUserMe } from "../../Api";
|
||||
import { withdrawClanRequest } from "../../ClanApi";
|
||||
import { translateText } from "../../Utils";
|
||||
import { modalHeader } from "../ui/ModalHeader";
|
||||
import { formatClanDate, modalContainerClass, showToast } from "./ClanShared";
|
||||
|
||||
@customElement("clan-my-requests-view")
|
||||
export class ClanMyRequestsView extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property({ type: Array }) myPendingRequests: {
|
||||
tag: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
}[] = [];
|
||||
|
||||
@state() private actionPending = false;
|
||||
|
||||
async handleWithdrawRequest(tag: string) {
|
||||
if (this.actionPending) return;
|
||||
this.actionPending = true;
|
||||
try {
|
||||
const result = await withdrawClanRequest(tag);
|
||||
if (result !== true) {
|
||||
showToast(translateText(result.error), "red");
|
||||
return;
|
||||
}
|
||||
invalidateUserMe();
|
||||
showToast(translateText("clan_modal.join_request_cancelled"), "green");
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("request-withdrawn", {
|
||||
detail: { tag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
this.actionPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="${modalContainerClass}">
|
||||
${modalHeader({
|
||||
title: translateText("clan_modal.pending_applications"),
|
||||
onBack: () =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate-back", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
),
|
||||
ariaLabel: translateText("common.back"),
|
||||
})}
|
||||
<div class="flex-1 overflow-y-auto custom-scrollbar mr-1 p-4 lg:p-6">
|
||||
${this.myPendingRequests.length === 0
|
||||
? html`<p class="text-white/40 text-sm text-center py-8">
|
||||
${translateText("clan_modal.no_pending_applications")}
|
||||
</p>`
|
||||
: html`
|
||||
<div class="space-y-3">
|
||||
${this.myPendingRequests.map(
|
||||
(req) => html`
|
||||
<div
|
||||
class="flex items-center gap-3 bg-white/5 rounded-xl border border-white/10 p-4"
|
||||
>
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-amber-500/10 flex items-center justify-center border border-amber-500/20 shrink-0"
|
||||
>
|
||||
<span class="text-amber-400 font-bold text-xs"
|
||||
>${req.tag}</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<span
|
||||
class="text-white font-bold text-sm truncate block"
|
||||
>${req.name}</span
|
||||
>
|
||||
<span class="text-white/30 text-xs">
|
||||
${translateText("clan_modal.applied")}
|
||||
${formatClanDate(req.createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
@click=${() => this.handleWithdrawRequest(req.tag)}
|
||||
?disabled=${this.actionPending}
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-3 py-1 rounded-full bg-red-500/15 text-red-400 border border-red-500/20 hover:bg-red-500/25 transition-all cursor-pointer disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
${translateText("clan_modal.cancel_request")}
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||