diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 092ec8b42..0913182d1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,6 +2,8 @@ name: 🧪 CI
on:
merge_group:
+ types:
+ - checks_requested
pull_request:
push:
branches: [main]
diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml
index 86e1d32b3..d108aaab2 100644
--- a/.github/workflows/pr-description.yml
+++ b/.github/workflows/pr-description.yml
@@ -1,6 +1,9 @@
name: 🧼 PR
on:
+ merge_group:
+ types:
+ - checks_requested
pull_request:
types:
- demilestoned
@@ -20,6 +23,11 @@ jobs:
- uses: actions/github-script@v7
with:
script: |
+ if (context.eventName === 'merge_group') {
+ // Ignore merge_group events
+ return;
+ }
+
const body = context.payload.pull_request.body || '';
const errors = [];
@@ -60,10 +68,16 @@ jobs:
- uses: actions/github-script@v7
with:
script: |
- // Get the pull request data
- const milestone = context.payload.pull_request.milestone;
- if (!milestone) {
- core.setFailed('❌ Pull request must have a milestone assigned before merging.');
- return;
+ if (context.eventName === 'merge_group') {
+ // Ignore merge_group events
+ } else if (context.eventName === 'pull_request') {
+ // Get the pull request data
+ const milestone = context.payload.pull_request.milestone;
+ if (!milestone) {
+ core.setFailed('❌ Pull request must have a milestone assigned before merging.');
+ return;
+ }
+ console.log(`✅ Milestone found: ${milestone.title}`);
+ } else {
+ core.setFailed(`❌ Unknown event type ${context.eventName}.`);
}
- console.log(`✅ Milestone found: ${milestone.title}`);
diff --git a/.gitignore b/.gitignore
index df5f0dd28..8b3f0c0db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ resources/.DS_Store
.env*
.DS_Store
.clinic/
+CLAUDE.md
diff --git a/resources/changelog.md b/resources/changelog.md
index 76e4aeec3..ba9e16fca 100644
--- a/resources/changelog.md
+++ b/resources/changelog.md
@@ -1,3 +1,493 @@
-# header
+- This is a sample changelog based off of v0.24.0.
+- This file will be replaced with real release notes during the release build process.
+ - Indented bullets look like this
-changelog here
+📦 **OpenFront v24 Changelog**
+
+⚖️ **Balance Changes**
+
+- Trade ships are now capped at 150 (Evan)
+ → Each port you own now increases the gold per trade, counterbalancing the cap.
+- MIRVs have been nerfed
+ → Expect less devastating multi-warhead nukes. Land in-between the fallout can be more quickly conquered.
+- Warships prioritize enemy transport ships over warships. Reload instantly after shooting a transport ship. (Evan)
+- Building discounts can only be used one time.
+- AI nukes now avoid SAM launchers
+
+🚅 **Major Features**
+
+- Trains added for new movement mechanics (experimental for private lobbies and single player) (DevelopingTom)
+- Factories spawn trains and railroads (choose Factory as unit in private lobby or for single player, to use trains)
+- Railroads can form loops
+- Added Trios and Quads. Add them to public lobby rotation together with Duos. (FakeNeo)
+- Upgradable structures: Cities, Ports, SAMs, and Silos can now be improved
+- Multi-level radial menu with dynamic build options
+- Creative Commons License added to non-commercial resources
+- Factories added for private lobbies and single player games
+- Hash-based routing implemented
+- Flares system implemented
+- GitHub Releases with release notes are now supported (click the What's New button/megaphone icon)
+
+🔧 **Game Improvements**
+
+- Improved territory drawing performance
+- SAMs now only target nukes threatening nearby areas
+- Nukes are now faster (speed increased from 4 → 6)
+- Better color mixing for small player counts (Ble4Ch)
+- Unique player colors to avoid confusion (Ble4Ch)
+- Better and optimized bot behaviour and spawn logic (tryout33 & FakeNeo)
+- Boat build discounts now scale with unit ownership
+- Improved username censoring and management
+- Updated East Asia map (formerly "Japan and Neighbors")
+- Reworked and optimized leaderboard UI
+- Improved visual clarity for alliances and stacked buildings
+
+🔧 **Game Improvements (continued)**
+
+- Better handling for betrayal alerts and radial menu behavior
+- Red alert frame when betrayed (devalnor)
+- Attack hotkeys added (Engla)
+- Boat hotkey added
+- Nations can spawn cities without a port
+- Team sizes now equalized
+- MIRV warhead intercepted stats are now recorded
+- Text FX added
+- Terrain manipulation for attack advantage
+- New logo added
+- Fix Duo partner (Nation) always same in Single player (tryout33)
+- Rename Replay Speed to Game Speed for Single player (tryout33)
+- Fix Nations building more than allowed (tryout33)
+
+🧪 **UI & Quality of Life**
+
+- Fixed text overflow in UI (Diessel)
+- Fixed websocket and join bugs
+- Fixed boat-on-land issues
+- Fixed modal errors and null pointer warnings
+- Fixed input handler edge cases on Mac (proper modifier and emoji key detection) (Ble4Ch)
+- Fixed scrollbar appearing unnecessarily in small boxes on Chromium browsers
+- Fixed giant world map key
+- Leaderboards, alerts, and modals now support translation & dark mode
+- New custom flag support and pattern icons
+- Various patterns available (Sword, Shells, White Rabbit, Goat, Cats, Hand, Radiation, Cursor, QR)
+- Patterned territory support
+- More responsive scrollbar and player info panels
+- Top bar redesign (Diessel)
+- More responsive design for in-game elements
+- New icon layer/sprites for structures
+- Building/loading/HP bars improved
+- Proper alliance timer naming
+- Logout button added
+- Handle not spawned player fixes
+- Multiple patterns support
+- Fix: anonymized name isn't displayed in chat message (tryout33)
+- Fix Leaderboard: show 0% instead of NaN when all terrain is nuked (tryout33)
+- Some fixes to the new Radial menu (tryout33)
+- Fix bug/performance improvements for trade ships (tryout33)
+- News Notification Badge for new release notes (floriankilian)
+- Translation improvements
+
+🛠️ **Backend & Technical**
+
+- Stats endpoints are now available
+- Added CORS origin headers
+- Added support for mobile apps native login
+- Discord user and guild member caching
+- Improved session error handling
+- Changed server logging
+- Improved data loading and fixed various bugs
+
+🔒 **Security & Bug Fixes**
+
+- Fixed naval attack spam exploit
+- Fixed gold donation validation exploit
+- Fixed pot issue
+- Various stability improvements and bug fixes
+
+🌐 **Translations**
+
+- Bulgarian🇧🇬: Nikola123 & NewHappyRabbit
+- Japanese🇯🇵: Aotumuri, daimyo_panda2 & gafunuko
+- French🇫🇷: cldprv, gx21 & r3ms
+- Dutch🇳🇱: cldprv & tryout33
+- German🇩🇪: Pilkey, jacks0n, floriankilian, Fibig & Texxter
+- Spanish🇪🇸: 6uzm4n
+- Russian🇷🇺: Rulfam
+- Ukrainian🇺🇦: Rulfam
+- Polish🇵🇱: zibi, RinkyDinky & Rulfam
+- Serbo-Croatian🇷🇸🇭🇷🇧🇦🇲🇪: Vekser
+- Italian🇮🇹: frappa10 & Lollosean
+- Brazilian Portuguese🇧🇷: theskeleton4393 & juliosilvaqwerty5
+- Turkish🇹🇷: Toyatak
+- Arabic🇸🇦: N0ur, Moha & SyntaxPM
+- Swedish🇸🇪: Moha, theangel2 & Keevee
+- Hindi🇮🇳: sheikh
+- Bengali🇧🇩: sheikh
+- Esperanto: r3ms
+- Toki Pona: Makonede
+- Slovak🇸🇰: extraextra
+- Czech🇨🇿: Xaelor & erinthegirl
+- Hebrew🇮🇱: Goblinon
+- Finnish🇫🇮: Tanepro193
+- Korean🇰🇷: Jinyoon
+- Danish🇩🇰: NiclasWK
+- Chinese Simplified🇨🇳: Moki
+- Galician: toldinsound
+
+## What's Changed
+
+- Bugfix: don't allow other players to move warships by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/879
+- Proper alliance timer naming by @tnhnblgl in https://github.com/openfrontio/OpenFrontIO/pull/886
+- Add naval combat animations by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/858
+- Use array index access instead of .at by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/889
+- mls by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/888
+- Revert "add addinplay ads" by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/897
+- Fix Toki Pona by @Duwibi in https://github.com/openfrontio/OpenFrontIO/pull/898
+- remove player id from Schemas, fix archive bug by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/907
+- Unit menu by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/867
+- Convert stats to bigints by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/909
+- Flag fixes for Europe map and for Brittany in flag menu and Gateway To the Atlantic map by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/910
+- Add deploy concurrency configuration by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/911
+- Add Github Logo on footer by @LucasLion in https://github.com/openfrontio/OpenFrontIO/pull/875
+- Revert "Population visualization (#842)" by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/908
+- floor by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/913
+- remove known world by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/914
+- Main menu UI cleanup by @Demonessica in https://github.com/openfrontio/OpenFrontIO/pull/857
+- Improve territory drawing performances by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/696
+- bug: Clicking out of bounds throws uncaught exception by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/920
+- Removes CSS rule causing performance issues by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/925
+- Always delete tradeship on pathfinding fail by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/921
+- Fix bigint serialization error by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/916
+- Revert tradeship path caching by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/927
+- Meta Adjustments from [UN] clan test by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/932
+- fix alternate view regression by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/937
+- fix warship targetting range by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/938
+- Add instructional overlay message during spawn phase by @spicydll in https://github.com/openfrontio/OpenFrontIO/pull/934
+- Add test coverage script by @aqw42 in https://github.com/openfrontio/OpenFrontIO/pull/929
+- Added two checkboxes to the default pull request template by @aqw42 in https://github.com/openfrontio/OpenFrontIO/pull/930
+- Fix slow singleplayer timer by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/943
+- improved perfomance of PseudoRandom by @falcolnic in https://github.com/openfrontio/OpenFrontIO/pull/933
+- Change deploy concurrency group by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/944
+- Set singleplayer gitCommit in the client by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/945
+- Simplify bots retaliation logic by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/946
+- Add close label by @drillskibo in https://github.com/openfrontio/OpenFrontIO/pull/949
+- Remove ClientID from GameRenderer by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/878
+- Resolve code scanning warning about HTML injection by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/953
+- Fix invalid username popup being behind public game button by @Demonessica in https://github.com/openfrontio/OpenFrontIO/pull/951
+- Server role lookup by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/954
+- Flag fixes in several maps by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/957
+- Fix map jsons by @Duwibi in https://github.com/openfrontio/OpenFrontIO/pull/960
+- change defaults to reflect meta by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/942
+- Even more flag flair by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/959
+- Only load tiles when viewed by player by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/887
+- Hide login button by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/965
+- Fix discord user schema by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/969
+- Balance Adjustment for Attack Mechanism by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/973
+- Prevent Attack Spam by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/977
+- Update HeadsUpMessage.ts to support translations by @spicydll in https://github.com/openfrontio/OpenFrontIO/pull/981
+- Cap lobby sizes at 150 by @Duwibi in https://github.com/openfrontio/OpenFrontIO/pull/984
+- Fix Translations showing as untranslated keys by @Duwibi in https://github.com/openfrontio/OpenFrontIO/pull/983
+- Another Balance Change by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/987
+- make bots weaker by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/985
+- Remove shield icon from bots by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/986
+- Balance Update by @1brucben in https://github.com/openfrontio/OpenFrontIO/pull/996
+- Revert meta by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1002
+- Fix text overflow in instructions for longer translations by @ERHash in https://github.com/openfrontio/OpenFrontIO/pull/971
+- Add dynamic sorting to leaderboard by tiles, gold, and troops by @ERHash in https://github.com/openfrontio/OpenFrontIO/pull/961
+- Fix Player Name Monospaced Text Overflow on PlayerInfo by @ERHash in https://github.com/openfrontio/OpenFrontIO/pull/975
+- Scroll bar Behavior on Chromium Browsers, c-modal_content by @andrewNiziolek in https://github.com/openfrontio/OpenFrontIO/pull/976
+- Synced the single player and host files together, and fix issue withc… by @shaan150 in https://github.com/openfrontio/OpenFrontIO/pull/991
+- Equalize team sizes by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/992
+- Added support for dark mode icons for Alliance Request Icon and Embargo Icon by @Vermylion in https://github.com/openfrontio/OpenFrontIO/pull/993
+- Use bigint for gold by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1000
+- Fix : Donation when max pop already reached by @aqw42 in https://github.com/openfrontio/OpenFrontIO/pull/904
+- Validate incoming API data with zod by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/891
+- this is a fix for the "possibly null" error. dosent seem to cause runtime issues but does cause the compiler to throw an error by @Jerryslang in https://github.com/openfrontio/OpenFrontIO/pull/1005
+- Fixnukeboatbug by @rldtech in https://github.com/openfrontio/OpenFrontIO/pull/1011
+- added ratio controls by @falcolnic in https://github.com/openfrontio/OpenFrontIO/pull/963
+- Add a status check for the milestone field by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1029
+- Fix discord login issue by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1028
+- Changed consolex to console logging by @falcolnic in https://github.com/openfrontio/OpenFrontIO/pull/1036
+- Center map on start by @Demonessica in https://github.com/openfrontio/OpenFrontIO/pull/1013
+- Rev: Update "Japan and Neighbors" map to "East Asia" by @andrewNiziolek in https://github.com/openfrontio/OpenFrontIO/pull/1007
+- Close socket on ClientMessageSchema, improve zod error by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1003
+- SAMs should target only nukes aimed at nearby targets by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1038
+- AI nukes avoid SAM launchers by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1045
+- Show alliances on the PlayerPanel by @Maaxion in https://github.com/openfrontio/OpenFrontIO/pull/1053
+- Improve readability of alliance acceptation logic for bots and add tests by @Nephty in https://github.com/openfrontio/OpenFrontIO/pull/1049
+- [Cleanup] Pass Player into execution constructor instead of PlayerID by @LJoyL in https://github.com/openfrontio/OpenFrontIO/pull/1022
+- Monitoring client connections by @aqw42 in https://github.com/openfrontio/OpenFrontIO/pull/941
+- have master create tunnels for all workers #780 by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1042
+- Add Boat hotkey by @tnhnblgl in https://github.com/openfrontio/OpenFrontIO/pull/1060
+- bug: logout by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1073
+- fix cloudflare tunnels by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1076
+- Duo partner SP always same: randomize players before team assignment by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1051
+- Multi-level radial menu by @oleksandr-shysh in https://github.com/openfrontio/OpenFrontIO/pull/1018
+- Fix broken flag images by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1078
+- kick existing client when duplicate persistent id is found by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1077
+- Update PlayerImpl.ts by @E-EE-E in https://github.com/openfrontio/OpenFrontIO/pull/1079
+- Add back #646 - trade ship gold by travelled distance by @Maaxion in https://github.com/openfrontio/OpenFrontIO/pull/1085
+- #1086 prevent clicking on other structures than your own by @Maaxion in https://github.com/openfrontio/OpenFrontIO/pull/1087
+- rename Event interface -> GameEvent by @Maaxion in https://github.com/openfrontio/OpenFrontIO/pull/1094
+- refactor radial, fix boat on terra nullius not working fixes by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1095
+- Disable donations public ffa matches by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1097
+- Nations can spawn cities without a port by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1072
+- Ci coverage by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1099
+- Revert "Ci coverage" by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1101
+- Add filters tabs to EvensDisplay to let users filter events by @Maaxion in https://github.com/openfrontio/OpenFrontIO/pull/1080
+- Fix bug in FakeHumanExecution by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1102
+- Fix: Hide username validation error in-game by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1110
+- cloudflare fixed tunnel name by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1096
+- Remove duplicate gold accumulation in team stats calculation by @rldtech in https://github.com/openfrontio/OpenFrontIO/pull/1010
+- Optimizations for botbehaviour by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1114
+- fix: correct mac modifier and emoji key detection in input handler by @Ble4Ch in https://github.com/openfrontio/OpenFrontIO/pull/1118
+- fix duplicate websocket handler by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1124
+- Adding unit info modal translation support. by @its-sii in https://github.com/openfrontio/OpenFrontIO/pull/1122
+- increase nuke speed from 4 to 6 by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1125
+- Avoid using as to cast values by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1115
+- Fix Māori flag name by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1133
+- use newer attack, delete existing attack by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1134
+- counter attack doesn't cancel out attack by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1132
+- Move version and changelog to files by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1109
+- Fix non valid SafeString flag codes by @ghisloufou in https://github.com/openfrontio/OpenFrontIO/pull/1135
+- Add a Replay speed control feature by @ghisloufou in https://github.com/openfrontio/OpenFrontIO/pull/1106
+- Add progress bars to show loading time and healthbars by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1107
+- feat: assign unique colors for players by @Ble4Ch in https://github.com/openfrontio/OpenFrontIO/pull/1063
+- lazy loading and current data var by @falcolnic in https://github.com/openfrontio/OpenFrontIO/pull/988
+- fix(client): use the right language-modal selector by @ghisloufou in https://github.com/openfrontio/OpenFrontIO/pull/1136
+- Simple Upgradable Structures (Cities, Ports, SAMs and Silos) by @Egraveline in https://github.com/openfrontio/OpenFrontIO/pull/1012
+- Rename Replay speed to Game speed in Singleplayer by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1145
+- discriminatedUnion by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1130
+- Fixed bad translation string bug for unit info modal. by @its-sii in https://github.com/openfrontio/OpenFrontIO/pull/1143
+ - fix timer overflow by @DiesselOne in https://github.com/openfrontio/OpenFrontIO/pull/1148
+ - optimize leaderboard by @DiesselOne in https://github.com/openfrontio/OpenFrontIO/pull/1151
+- Fix regression cooldown bars by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1154
+- favor transport ships, no reload penalty by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1153
+- dynamic radial menu build options by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1152
+- Update building images and adjust border/territory radii for unit configuration by @rldtech in https://github.com/openfrontio/OpenFrontIO/pull/1037
+- Fixed quick chat text injection by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1144
+- Rework leaderboard and team stats by @DiesselOne in https://github.com/openfrontio/OpenFrontIO/pull/1164
+- Extend token lifetime to 3 days by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1172
+- Redraw stacked buildings sprites by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1170
+- Fix Nations building more than allowed by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1176
+- Set a targetable status for nukes by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1174
+- fixed giantworldmap key by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1188
+- Fix Leaderboard: convert NaN into 0% by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1190
+- Update pr-description regex by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1181
+- discriminatedUnion by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1193
+- UsernameSchema, FlagSchema by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1185
+- feat: colors are better mixed up when players count is low by @Ble4Ch in https://github.com/openfrontio/OpenFrontIO/pull/1149
+- Improve handling of HTTP 401 by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1194
+- increase worker connections by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1179
+- Fix: Handle not spawned player focus by @tnhnblgl in https://github.com/openfrontio/OpenFrontIO/pull/1186
+- Fix Radial menu undefined params error during spawn phase by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1192
+- Better handling of bad tokens by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1180
+- Hash-based routing by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1198
+- cache busting: Import version, changelog by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1201
+- REV - Improved Username Censoring by @andrewNiziolek in https://github.com/openfrontio/OpenFrontIO/pull/1119
+- Jest v30 by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1206
+- Release workflow by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1202
+- Fix unnecessary join check by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1209
+- fix websocket error by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1208
+- add playwire ads by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1128
+- Update webpack-dev-server to 5.2.2 by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1207
+- Add a 30 minute timeout to actions by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1210
+- Update release workflow by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1212
+ - update leaderboard align by @DiesselOne in https://github.com/openfrontio/OpenFrontIO/pull/1189
+- Fix gutter ads, move in-game add to bottom right corner. by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1214
+- have worker send error back to client by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1178
+- Fix build menu on water tile by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1216
+- Update default version number by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1218
+- Schema cleanup by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1219
+- ads on death screen by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1223
+- Delay win modal by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1224
+- Dependency removals and updates by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1215
+- add spawn ads by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1228
+- upgrade to zod 4 by @omrih4 in https://github.com/openfrontio/OpenFrontIO/pull/1161
+- Record MIRV warhead intercepted stats, perf improvements by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1220
+- Simplfiy LangSelector by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1226
+- Pot issue fix by @tnhnblgl in https://github.com/openfrontio/OpenFrontIO/pull/1233
+- Logout Button Fix by @tnhnblgl in https://github.com/openfrontio/OpenFrontIO/pull/1234
+- fix bad tile crash by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1237
+- fix is valid ref by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1240
+- Remove babel-jest from devDependencies by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1247
+- Refactor radial menu by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1246
+- Simplify ClientMessage handling by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1235
+- Add trains by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1159
+- Add back the trade ship send stat by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1253
+- Remove maxTokenAge by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1255
+- Patterned territory by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/786
+- Discounts can only be used one time by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/892
+- Fix singleplayer check by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1260
+- Move maps generation out of repo, new map structure by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1256
+- Show a red alert frame when the player is betrayed by @devalnor in https://github.com/openfrontio/OpenFrontIO/pull/1195
+- Allow boat discount based on number of units owned by @devalnor in https://github.com/openfrontio/OpenFrontIO/pull/1261
+- Move map metadata to map manifest by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1262
+- Refactor cosmetics.json by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1263
+- bug: StatsSchema zod validation error by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1267
+- White Rabbit pattern by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1264
+- Cleanup log spam in TerritoryPatternsModal by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1269
+- Fix pattern locking logic by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1270
+- Keybind Ground Attack by @dengh in https://github.com/openfrontio/OpenFrontIO/pull/1258
+- UrlEncode patterns in cosmetics.json by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1273
+- improve astar perf by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1268
+- Log public id by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1278
+- clarify license by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1277
+- Fix sam targetting everything by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1280
+- Add Creative Commons License to resources/non-commercial by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1284
+- Sword pattern by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1283
+- Display OFM25 ad in WinModal by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1281
+- QR code pattern by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1288
+- custom flag (1) by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1257
+- Allow railroad loops by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1274
+- patterns by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1290
+- Split build & deploy scripts by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1239
+- New icons by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1287
+- Add GitHub deployment support by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1291
+- bug: Fix version number and changelog by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1293
+- Revert "counter attack doesn't cancel out attack (#1132)" by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1301
+- Graceful handling of ping before join by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1295
+- refactor cosmetics out of PlayerInfo by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1299
+- Remove unused MON\_\* credentials by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1304
+- Add new patterns by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1294
+- Fix error-modal filling up the whole screen by @fraxxio in https://github.com/openfrontio/OpenFrontIO/pull/1298
+- Reapply "enable otel logs and metrics for staging environments" by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1310
+- Separate prod release environments by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1311
+- Change news title to release notes by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1312
+- Add localization support for leaderboard and team-related UI elements by @TomaszOleszko in https://github.com/openfrontio/OpenFrontIO/pull/1308
+- Better In Game UI by @DiesselOne in https://github.com/openfrontio/OpenFrontIO/pull/1243
+- w-320 by @PilkeySEK in https://github.com/openfrontio/OpenFrontIO/pull/1316
+- Patterns by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1318
+- Show structure levels by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1305
+- fix alliance expired message by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1323
+- Mark train stations and factories as experimental by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1309
+- allow alliance extension Fixes #491 by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1314
+- Additional patterns and subclass creation by @Sgt-lewis in https://github.com/openfrontio/OpenFrontIO/pull/1327
+- fix healthbars not being removed by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1329
+- lighten pattern by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1326
+- custom flag (2) by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1303
+- Make patterns puchasable with stripe by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1313
+- Improve icons readability by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1321
+- remove select on hover by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1330
+- Fix role lookup by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1335
+- Extend winner schema by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1333
+- mls 4.0 by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1336
+- upgrade unit when building a unit of same type by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1328
+- remove unit menu by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1338
+- unit upgrade minor improvements by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1337
+- Add gold fx when a tradeship lands by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1322
+- validate coords in construction execution by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1339
+- fix pattern and role bugs by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1343
+- Disable trains in public games by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1342
+- Add levels on structure sprites by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1346
+- Automatic train stations by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1353
+- Quads by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1347
+- Quads fix by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1356
+- Revert "enable otel logs and metrics for staging environments" by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1358
+- alliance renewal: fix request to renew when ally is dead, fix translation keys by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1359
+- Add new icon shapes and filter for filtering icons on the layer by @jrouillard in https://github.com/openfrontio/OpenFrontIO/pull/1348
+- upgrades not counting towards building discount bugfix by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1361
+- Add strait of Gibraltar and Italia maps by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1363
+- Radial menu: remove player info sub-radial by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1362
+- move unit display to bottom of screen by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1365
+- Move settings to it's own modal by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1366
+- update ui by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1368
+- Add localization support for game events, settings, and UI text elements by @TomaszOleszko in https://github.com/openfrontio/OpenFrontIO/pull/1372
+- Validate incoming parameters by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1371
+- Add domain, subdomain to GameRecord by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1370
+- bugfix: Crash during replay by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1375
+- fix top bar small screens by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1377
+- add domain and subdomain for dev env by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1379
+- fix pop delta number in TopBar by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1373
+- Add expand ratio to bot behavior class by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1376
+- bugfix: Crash by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1381
+- Don't erase patterns on page load by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1383
+- Require login to connect to staging by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1360
+- feat(news-button): highlight button when new version is available by @floriankilian in https://github.com/openfrontio/OpenFrontIO/pull/1385
+- Fix local development by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1388
+- fixed Custom Flags via Path Traversal by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1384
+- fix odd dimension maps by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1389
+- Improve unit updates & reloading by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1394
+- update meta by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1397
+- port execution bugfixes by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1400
+- Internationalization: Add i18n support for login/auth messages in main by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1406
+- Update README.md by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1407
+- Redraw existing railroads when redrawing the complete layer by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1410
+- Unit count by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1402
+- fix color allocator not selecting distinct colors by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1404
+- mls (v4.1) by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1357
+- remove levels player overview panel by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1414
+- Remove top bar & revert control panel by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1415
+- move player overview higher up by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1418
+- have mirv attack enemy units by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1419
+- fix team bar by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1422
+- fix team bar by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1425
+- Leaderboard improvements by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1424
+- radial menu attack self bugfix by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1426
+- remove radial animation, fix back button by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1427
+- Factory spawns trains by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1408
+- Followup: news-button: blue-glow; simpler localStorage by @floriankilian in https://github.com/openfrontio/OpenFrontIO/pull/1431
+- fix unit upgrade not considering cost by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1434
+- Enable @typescript eslint/prefer nullish coalescing eslint rule by @g-santos-m in https://github.com/openfrontio/OpenFrontIO/pull/1420
+- Eslint by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/998
+- Restore nation AI by @scottanderson in https://github.com/openfrontio/OpenFrontIO/pull/1440
+- fix number of land tiles fixes #1409 by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1445
+- Have radial menu refresh when open by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1437
+- make radial menu thicker by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1446
+- Fix: anonymized name isn't used in chat message by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1265
+- Revert MIRV attacks enemy units by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1452
+- Tradeship performance by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1448
+- Fix: "Game speed" not "Replay speed" during Single player game by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1457
+- Update asset license by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1458
+- Fix: attack on ally even with greyed out button by @VariableVince in https://github.com/openfrontio/OpenFrontIO/pull/1460
+- Create CLA.md by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1462
+- update pr template to have CLA checkbox. by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1465
+- Increase trade ship spawn rate by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1455
+- Increase traitor punishment by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1456
+- fix team leaderboard margin by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1469
+- leaderboard bugfix: show by default for medium to large screens. by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1470
+- fix control panel & events display scaling on mobile by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1471
+- alert on ws 1002 error by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1472
+- Fix Regex to allow Umlaute "üÜ" in username by @floriankilian in https://github.com/openfrontio/OpenFrontIO/pull/1466
+- Have port destination likelihood scale with level by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1473
+- remove spawn ad by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1482
+- fix squad allocator color palette by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1483
+- bug fix?: Hide UnitDisplay frame when all unit types are disabled by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1392
+- fix pop & gold not showing up on mobile UI by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1484
+- meta: reduce port gold multiplier & trade ship frequency by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1486
+- Fix language code mismatch during language switching by @Aotumuri in https://github.com/openfrontio/OpenFrontIO/pull/1416
+- Add close button to emoji table by @DevelopingTom in https://github.com/openfrontio/OpenFrontIO/pull/1479
+- increase MIRV to 35M by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1487
+- increase player panel z index so it is on top of spawn timer by @evanpelle in https://github.com/openfrontio/OpenFrontIO/pull/1488
+
+## New Contributors
+
+- @LucasLion made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/875
+- @spicydll made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/934
+- @falcolnic made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/933
+- @drillskibo made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/949
+- @ERHash made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/971
+- @andrewNiziolek made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/976
+- @shaan150 made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/991
+- @Vermylion made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/993
+- @Jerryslang made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1005
+- @rldtech made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1011
+- @Maaxion made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1053
+- @Nephty made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1049
+- @LJoyL made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1022
+- @oleksandr-shysh made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1018
+- @E-EE-E made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1079
+- @Ble4Ch made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1118
+- @its-sii made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1122
+- @ghisloufou made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1135
+- @Egraveline made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1012
+- @omrih4 made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1161
+- @devalnor made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1195
+- @dengh made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1258
+- @fraxxio made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1298
+- @TomaszOleszko made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1308
+- @Sgt-lewis made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1327
+- @floriankilian made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1385
+- @g-santos-m made their first contribution in https://github.com/openfrontio/OpenFrontIO/pull/1420
+
+**Full Changelog**: https://github.com/openfrontio/OpenFrontIO/compare/v0.23.19...v0.24.0
diff --git a/resources/cosmetics/roles.txt b/resources/cosmetics/roles.txt
deleted file mode 100644
index 6da05b458..000000000
--- a/resources/cosmetics/roles.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Admin 1286738076386856991
-OG 1286743849707769936
-Creator 1286745100411473930
-Bots 1286910984702791711
-Challenger 1292157381496799264
-OG100 1314802550314237952
-Contributor 1314972008362020957
-Ping 1316444187276738612
-Server Booster 1319387513206345770
-Content Creator 1320961080750637076
-Beta Tester 1327125593791397929
-Early Access Supporter 1330243292306341969
-Mod 1338654590043820148
-Support Staff 1343759662545244296
-DevChatAccess 1345831753528377425
-Member 1347621713852235808
-Active Contributor 1354828445489692692
-Retired Staff 1355753028099117147
-Head Mod 1357747869742010661
-Money Haters 1359441841371480176
-Translator 1367345579272831128
-Head Translator 1367345660852174930
-Development Stream Ping 1369340951109304340
-Core Contributor 1370238576868200488
diff --git a/resources/flags/Apartheid South Africa.svg b/resources/flags/Apartheid South Africa.svg
new file mode 100644
index 000000000..62879a59c
--- /dev/null
+++ b/resources/flags/Apartheid South Africa.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Australian Aboriginal Flag.svg b/resources/flags/Australian Aboriginal Flag.svg
new file mode 100644
index 000000000..a44011040
--- /dev/null
+++ b/resources/flags/Australian Aboriginal Flag.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Burma2.svg b/resources/flags/Burma2.svg
new file mode 100644
index 000000000..1b66bb909
--- /dev/null
+++ b/resources/flags/Burma2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Byelorussian SSR.svg b/resources/flags/Byelorussian SSR.svg
new file mode 100644
index 000000000..77cc0d4b2
--- /dev/null
+++ b/resources/flags/Byelorussian SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Communist Romania.svg b/resources/flags/Communist Romania.svg
new file mode 100644
index 000000000..50ffca157
--- /dev/null
+++ b/resources/flags/Communist Romania.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Georgian SSR.svg b/resources/flags/Georgian SSR.svg
new file mode 100644
index 000000000..0199dcc05
--- /dev/null
+++ b/resources/flags/Georgian SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Imperial Ethiopia.svg b/resources/flags/Imperial Ethiopia.svg
new file mode 100644
index 000000000..5130c7276
--- /dev/null
+++ b/resources/flags/Imperial Ethiopia.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Kazakh SSR.svg b/resources/flags/Kazakh SSR.svg
new file mode 100644
index 000000000..c45a75e01
--- /dev/null
+++ b/resources/flags/Kazakh SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Mauritania.svg b/resources/flags/Mauritania.svg
new file mode 100644
index 000000000..a31afd815
--- /dev/null
+++ b/resources/flags/Mauritania.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Newfoundland.svg b/resources/flags/Newfoundland.svg
new file mode 100644
index 000000000..afa5cdede
--- /dev/null
+++ b/resources/flags/Newfoundland.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/North yemen.svg b/resources/flags/North yemen.svg
new file mode 100644
index 000000000..4e21d655a
--- /dev/null
+++ b/resources/flags/North yemen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Nunavut.svg b/resources/flags/Nunavut.svg
new file mode 100644
index 000000000..177892518
--- /dev/null
+++ b/resources/flags/Nunavut.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Pahlavi Iran.svg b/resources/flags/Pahlavi Iran.svg
new file mode 100644
index 000000000..ab03c3a6f
--- /dev/null
+++ b/resources/flags/Pahlavi Iran.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Rhodesia.svg b/resources/flags/Rhodesia.svg
new file mode 100644
index 000000000..2cc310579
--- /dev/null
+++ b/resources/flags/Rhodesia.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Russian SSR.svg b/resources/flags/Russian SSR.svg
new file mode 100644
index 000000000..fa00bb9bf
--- /dev/null
+++ b/resources/flags/Russian SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/South Vietnam.svg b/resources/flags/South Vietnam.svg
new file mode 100644
index 000000000..d8fa5e463
--- /dev/null
+++ b/resources/flags/South Vietnam.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Turkmen SSR.svg b/resources/flags/Turkmen SSR.svg
new file mode 100644
index 000000000..27ebbe04a
--- /dev/null
+++ b/resources/flags/Turkmen SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Ukrainian SSR.svg b/resources/flags/Ukrainian SSR.svg
new file mode 100644
index 000000000..977a3a610
--- /dev/null
+++ b/resources/flags/Ukrainian SSR.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/Yukon.svg b/resources/flags/Yukon.svg
new file mode 100644
index 000000000..37e4a613b
--- /dev/null
+++ b/resources/flags/Yukon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/flags/south yemen.svg b/resources/flags/south yemen.svg
new file mode 100644
index 000000000..cb7c8e879
--- /dev/null
+++ b/resources/flags/south yemen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/images/AnchorIcon.png b/resources/images/AnchorIcon.png
index cd0582fc7..b3005c098 100644
Binary files a/resources/images/AnchorIcon.png and b/resources/images/AnchorIcon.png differ
diff --git a/resources/images/FactoryUnit.png b/resources/images/FactoryUnit.png
index ce25ced1d..7e141abbe 100644
Binary files a/resources/images/FactoryUnit.png and b/resources/images/FactoryUnit.png differ
diff --git a/resources/images/GameplayScreenshot.png b/resources/images/GameplayScreenshot.png
new file mode 100644
index 000000000..e74458c86
Binary files /dev/null and b/resources/images/GameplayScreenshot.png differ
diff --git a/resources/images/SamLauncherUnit.png b/resources/images/SamLauncherUnit.png
index ab29b9617..295051dff 100644
Binary files a/resources/images/SamLauncherUnit.png and b/resources/images/SamLauncherUnit.png differ
diff --git a/resources/images/ShieldIcon.png b/resources/images/ShieldIcon.png
index ac58abdec..847621dd0 100644
Binary files a/resources/images/ShieldIcon.png and b/resources/images/ShieldIcon.png differ
diff --git a/resources/lang/en.json b/resources/lang/en.json
index 92fad88cd..e738a8d67 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -26,7 +26,7 @@
"terms_of_service": "Terms of Service"
},
"news": {
- "full_changelog": "See the complete change log",
+ "see_all_releases": "See all releases",
"github_link": "on GitHub",
"title": "Release Notes"
},
@@ -43,6 +43,7 @@
"action_move_camera": "Move camera",
"action_ratio_change": "Decrease/Increase attack ratio",
"action_reset_gfx": "Reset graphics",
+ "action_auto_upgrade": "Auto-upgrade nearest building",
"ui_section": "Game UI",
"ui_leaderboard": "Leaderboard",
"ui_your_team": "Your team:",
@@ -69,6 +70,7 @@
"radial_title": "Radial menu",
"radial_desc": "Right clicking (or touch on mobile) opens the Radial menu. Right click outside it to close it. From the menu you can:",
"radial_build": "Open the Build menu.",
+ "radial_attack": "Open the Attack menu.",
"radial_info": "Open the Info menu.",
"radial_boat": "Send a Boat (transport ship) to attack at the selected location. Only available if you have access to water.",
"radial_close": "Close the menu.",
@@ -275,6 +277,10 @@
"special_effects_desc": "Toggle special effects. Deactivate to improve performances",
"special_effects_enabled": "Special effects enabled",
"special_effects_disabled": "Special effects disabled",
+ "structure_sprites_label": "Structure Sprites",
+ "structure_sprites_desc": "Toggle structure sprites",
+ "structure_sprites_enabled": "Structure Sprites enabled",
+ "structure_sprites_disabled": "Structure Sprites disabled",
"anonymous_names_label": "Hidden Names",
"anonymous_names_desc": "Hide real player names with random ones on your screen.",
"anonymous_names_enabled": "Anonymous names enabled",
diff --git a/resources/maps/giantworldmap/manifest.json b/resources/maps/giantworldmap/manifest.json
index 98840aa3d..236877a9c 100644
--- a/resources/maps/giantworldmap/manifest.json
+++ b/resources/maps/giantworldmap/manifest.json
@@ -12,267 +12,585 @@
"name": "Giant_World_Map",
"nations": [
{
- "coordinates": [777, 540],
- "flag": "us",
- "name": "United States",
- "strength": 3
- },
- {
- "coordinates": [770.784, 287.776],
- "flag": "ca",
- "name": "Canada",
- "strength": 2
- },
- {
- "coordinates": [777, 780],
- "flag": "mx",
- "name": "Mexico",
+ "coordinates": [2309, 535],
+ "flag": "tr",
+ "name": "Türkiye",
"strength": 1
},
{
- "coordinates": [1025, 744],
- "flag": "cu",
- "name": "Cuba",
+ "coordinates": [2030, 409],
+ "flag": "west_germany",
+ "name": "West Germany",
"strength": 1
},
{
- "coordinates": [1085.728, 990],
- "flag": "co",
- "name": "Colombia",
+ "coordinates": [2074, 382],
+ "flag": "east_germany",
+ "name": "East Germany",
"strength": 1
},
{
- "coordinates": [1228.6960000000001, 990],
- "flag": "ve",
- "name": "Venezuela",
+ "coordinates": [1966, 442],
+ "flag": "fr",
+ "name": "France",
"strength": 1
},
{
- "coordinates": [1220, 1485],
- "flag": "ar",
- "name": "Argentina",
- "strength": 1
- },
- {
- "coordinates": [1330, 1190],
- "flag": "br",
- "name": "Brazil",
- "strength": 1
- },
- {
- "coordinates": [2650, 1897],
- "flag": "aq",
- "name": "Antarctica",
- "strength": 3
- },
- {
- "coordinates": [1469.048, 120.61200000000001],
- "flag": "gl",
- "name": "Greenland",
- "strength": 2
- },
- {
- "coordinates": [1721.832, 236.99200000000002],
- "flag": "is",
- "name": "Iceland",
- "strength": 1
- },
- {
- "coordinates": [1916.6000000000001, 393.576],
- "flag": "gb",
- "name": "United Kingdom",
- "strength": 3
- },
- {
- "coordinates": [1837.864, 387.228],
- "flag": "ie",
- "name": "Ireland",
- "strength": 1
- },
- {
- "coordinates": [1885, 550],
- "flag": "es",
+ "coordinates": [1872, 528],
+ "flag": "Fascist Spain",
"name": "Spain",
"strength": 1
},
{
- "coordinates": [2080.288, 529],
+ "coordinates": [2074, 498],
"flag": "it",
"name": "Italy",
"strength": 1
},
{
- "coordinates": [1980, 455],
- "flag": "fr",
- "name": "France",
- "strength": 2
- },
- {
- "coordinates": [2060, 425],
- "flag": "de",
- "name": "Germany",
+ "coordinates": [1912, 379],
+ "flag": "gb",
+ "name": "United Kingdom",
"strength": 1
},
{
- "coordinates": [2111, 277],
- "flag": "se",
- "name": "Sweden",
+ "coordinates": [1841, 373],
+ "flag": "ie",
+ "name": "Ireland",
"strength": 1
},
{
- "coordinates": [2165, 400],
+ "coordinates": [2153, 378],
"flag": "pl",
"name": "Poland",
"strength": 1
},
{
- "coordinates": [2205, 397.808],
- "flag": "by",
- "name": "Belarus",
+ "coordinates": [2178, 539],
+ "flag": "gr",
+ "name": "Greece",
"strength": 1
},
{
- "coordinates": [2223.256, 514.188],
- "flag": "ro",
+ "coordinates": [2222, 493],
+ "flag": "bg",
+ "name": "Bulgaria",
+ "strength": 1
+ },
+ {
+ "coordinates": [2135, 481],
+ "flag": "yugoslavia",
+ "name": "Yugoslavia",
+ "strength": 1
+ },
+ {
+ "coordinates": [2242, 461],
+ "flag": "Communist Romania",
"name": "Romania",
"strength": 1
},
{
- "coordinates": [2405.592, 579.784],
- "flag": "tr",
- "name": "Turkey",
+ "coordinates": [2163, 441],
+ "flag": "hu",
+ "name": "Hungary",
"strength": 1
},
{
- "coordinates": [2007.768, 281.428],
+ "coordinates": [2272, 418],
+ "flag": "Ukrainian SSR",
+ "name": "Ukrainian SSR",
+ "strength": 1
+ },
+ {
+ "coordinates": [2093, 297],
+ "flag": "se",
+ "name": "Sweden",
+ "strength": 1
+ },
+ {
+ "coordinates": [2027, 282],
"flag": "no",
"name": "Norway",
"strength": 1
},
{
- "coordinates": [2200.464, 281.428],
+ "coordinates": [2191, 194],
+ "flag": "Sami flag",
+ "name": "Sapmi",
+ "strength": 1
+ },
+ {
+ "coordinates": [2206, 262],
"flag": "fi",
"name": "Finland",
"strength": 1
},
{
- "coordinates": [2277.128, 443],
- "flag": "ua",
- "name": "Ukraine",
+ "coordinates": [2376, 363],
+ "flag": "Russian SSR",
+ "name": "Russian SSR",
"strength": 1
},
{
- "coordinates": [2480, 311],
- "flag": "ru",
- "name": "Russia",
- "strength": 3
- },
- {
- "coordinates": [3175, 400],
- "flag": "mn",
- "name": "Mongolia",
+ "coordinates": [2222, 371],
+ "flag": "Byelorussian SSR",
+ "name": "Byelorussian SSR",
"strength": 1
},
{
- "coordinates": [3170, 680],
- "flag": "cn",
- "name": "China",
- "strength": 3
- },
- {
- "coordinates": [2834.496, 789.268],
- "flag": "in",
- "name": "India",
- "strength": 2
- },
- {
- "coordinates": [2643.8720000000003, 505.72400000000005],
- "flag": "kz",
- "name": "Kazakhstan",
+ "coordinates": [2441, 507],
+ "flag": "Georgian SSR",
+ "name": "Georgian SSR",
"strength": 1
},
{
- "coordinates": [2565.136, 653.844],
- "flag": "ir",
- "name": "Islamic Republic Of Iran",
+ "coordinates": [2402, 580],
+ "flag": "Second Republic of Iraq",
+ "name": "Iraq",
"strength": 1
},
{
- "coordinates": [2440.8160000000003, 742.716],
+ "coordinates": [2353, 595],
+ "flag": "sy",
+ "name": "Syria",
+ "strength": 1
+ },
+ {
+ "coordinates": [2414, 679],
"flag": "sa",
"name": "Saudi Arabia",
"strength": 1
},
{
- "coordinates": [3478, 1370],
+ "coordinates": [2434, 815],
+ "flag": "North yemen",
+ "name": "North Yemen",
+ "strength": 1
+ },
+ {
+ "coordinates": [2479, 824],
+ "flag": "south yemen",
+ "name": "South Yemen",
+ "strength": 1
+ },
+ {
+ "coordinates": [2554, 724],
+ "flag": "ae",
+ "name": "United Arab Emirates",
+ "strength": 1
+ },
+ {
+ "coordinates": [2532, 609],
+ "flag": "Pahlavi Iran",
+ "name": "Iran",
+ "strength": 1
+ },
+ {
+ "coordinates": [2683, 650],
+ "flag": "pk",
+ "name": "Pakistan",
+ "strength": 1
+ },
+ {
+ "coordinates": [2654, 580],
+ "flag": "af",
+ "name": "Afghanistan",
+ "strength": 1
+ },
+ {
+ "coordinates": [2727, 416],
+ "flag": "Kazakh SSR",
+ "name": "Kazakh SSR",
+ "strength": 1
+ },
+ {
+ "coordinates": [2556, 544],
+ "flag": "Turkmen SSR",
+ "name": "Turkmen SSR",
+ "strength": 1
+ },
+ {
+ "coordinates": [2947, 362],
+ "flag": "Zheleznogorsk",
+ "name": "Zheleznogorsk",
+ "strength": 1
+ },
+ {
+ "coordinates": [3252, 229],
+ "flag": "Siberia",
+ "name": "Siberia",
+ "strength": 1
+ },
+ {
+ "coordinates": [2810, 744],
+ "flag": "in",
+ "name": "India",
+ "strength": 1
+ },
+ {
+ "coordinates": [1717, 237],
+ "flag": "is",
+ "name": "Iceland",
+ "strength": 1
+ },
+ {
+ "coordinates": [2944, 709],
+ "flag": "bd",
+ "name": "Bangladesh",
+ "strength": 1
+ },
+ {
+ "coordinates": [2868, 635],
+ "flag": "np",
+ "name": "Nepal",
+ "strength": 1
+ },
+ {
+ "coordinates": [3254, 672],
+ "flag": "cn",
+ "name": "China",
+ "strength": 1
+ },
+ {
+ "coordinates": [3373, 521],
+ "flag": "kp",
+ "name": "North Korea",
+ "strength": 1
+ },
+ {
+ "coordinates": [3389, 573],
+ "flag": "kr",
+ "name": "South Korea",
+ "strength": 1
+ },
+ {
+ "coordinates": [3515, 571],
+ "flag": "jp",
+ "name": "Japan",
+ "strength": 1
+ },
+ {
+ "coordinates": [3026, 457],
+ "flag": "mn",
+ "name": "Mongolia",
+ "strength": 1
+ },
+ {
+ "coordinates": [3229, 995],
+ "flag": "id",
+ "name": "Indonesia",
+ "strength": 1
+ },
+ {
+ "coordinates": [3121, 755],
+ "flag": "vn",
+ "name": "North Vietnam",
+ "strength": 1
+ },
+ {
+ "coordinates": [3153, 833],
+ "flag": "South Vietnam",
+ "name": "South Vietnam",
+ "strength": 1
+ },
+ {
+ "coordinates": [3013, 722],
+ "flag": "Burma2",
+ "name": "Burma",
+ "strength": 1
+ },
+ {
+ "coordinates": [3095, 822],
+ "flag": "kh",
+ "name": "Cambodia",
+ "strength": 1
+ },
+ {
+ "coordinates": [3538, 1067],
+ "flag": "pg",
+ "name": "Papua New Guinea",
+ "strength": 1
+ },
+ {
+ "coordinates": [3542, 1356],
"flag": "au",
"name": "Australia",
- "strength": 2
+ "strength": 1
},
{
- "coordinates": [3880, 1516],
+ "coordinates": [3422, 1203],
+ "flag": "Australian Aboriginal Flag",
+ "name": "Nawan-mirri",
+ "strength": 1
+ },
+ {
+ "coordinates": [3880, 1521],
"flag": "nz",
"name": "New Zealand",
- "strength": 0.5
+ "strength": 1
},
{
- "coordinates": [1902.096, 700],
+ "coordinates": [2632, 1893],
+ "flag": "aq",
+ "name": "Antarctica",
+ "strength": 1
+ },
+ {
+ "coordinates": [2038, 590],
+ "flag": "tn",
+ "name": "Tunisia",
+ "strength": 1
+ },
+ {
+ "coordinates": [2116, 653],
+ "flag": "ly",
+ "name": "Libya",
+ "strength": 1
+ },
+ {
+ "coordinates": [2281, 653],
+ "flag": "United Arab Republic",
+ "name": "United Arab Republic",
+ "strength": 1
+ },
+ {
+ "coordinates": [1859, 613],
+ "flag": "ma",
+ "name": "Morocco",
+ "strength": 1
+ },
+ {
+ "coordinates": [1943, 615],
"flag": "dz",
"name": "Algeria",
"strength": 1
},
{
- "coordinates": [2134.16, 680],
- "flag": "ly",
- "name": "Libyan Arab Jamahiriya",
- "strength": 1
- },
- {
- "coordinates": [2262.6240000000003, 708.86],
- "flag": "eg",
- "name": "Egypt",
- "strength": 1
- },
- {
- "coordinates": [1995.336, 867.5600000000001],
- "flag": "ne",
- "name": "Niger",
- "strength": 1
- },
- {
- "coordinates": [2304.064, 859.096],
+ "coordinates": [2317, 754],
"flag": "sd",
"name": "Sudan",
"strength": 1
},
{
- "coordinates": [2225.328, 1074.928],
- "flag": "cd",
- "name": "The Democratic Republic of the Congo",
+ "coordinates": [2466, 918],
+ "flag": "so",
+ "name": "Somalia",
"strength": 1
},
{
- "coordinates": [2391.088, 937.388],
- "flag": "et",
+ "coordinates": [2352, 895],
+ "flag": "Imperial Ethiopia",
"name": "Ethiopia",
"strength": 1
},
{
- "coordinates": [2188, 1374.0120000000002],
- "flag": "za",
+ "coordinates": [1790, 729],
+ "flag": "Mauritania",
+ "name": "Mauritania",
+ "strength": 1
+ },
+ {
+ "coordinates": [2154, 764],
+ "flag": "td",
+ "name": "Chad",
+ "strength": 1
+ },
+ {
+ "coordinates": [2051, 745],
+ "flag": "ne",
+ "name": "Niger",
+ "strength": 1
+ },
+ {
+ "coordinates": [2040, 930],
+ "flag": "ng",
+ "name": "Nigeria",
+ "strength": 1
+ },
+ {
+ "coordinates": [1805, 907],
+ "flag": "lr",
+ "name": "Liberia",
+ "strength": 1
+ },
+ {
+ "coordinates": [2195, 918],
+ "flag": "cf",
+ "name": "Central African Republic",
+ "strength": 1
+ },
+ {
+ "coordinates": [2197, 1070],
+ "flag": "Zaire",
+ "name": "Zaire",
+ "strength": 1
+ },
+ {
+ "coordinates": [2189, 1372],
+ "flag": "Apartheid South Africa",
"name": "South Africa",
"strength": 1
},
{
- "coordinates": [2459, 1230],
+ "coordinates": [2452, 1247],
"flag": "mg",
"name": "Madagascar",
- "strength": 0.5
+ "strength": 1
},
{
- "coordinates": [2170, 880],
- "flag": "td",
- "name": "Chad",
+ "coordinates": [2356, 1165],
+ "flag": "mz",
+ "name": "Mozambique",
+ "strength": 1
+ },
+ {
+ "coordinates": [2368, 1032],
+ "flag": "tz",
+ "name": "Tanzania",
+ "strength": 1
+ },
+ {
+ "coordinates": [1934, 762],
+ "flag": "ml",
+ "name": "Mali",
+ "strength": 1
+ },
+ {
+ "coordinates": [2128, 1292],
+ "flag": "Apartheid South Africa",
+ "name": "South West Africa",
+ "strength": 1
+ },
+ {
+ "coordinates": [2099, 1178],
+ "flag": "ao",
+ "name": "Angola",
+ "strength": 1
+ },
+ {
+ "coordinates": [1375, 1121],
+ "flag": "br",
+ "name": "Brazil",
+ "strength": 1
+ },
+ {
+ "coordinates": [1203, 1059],
+ "flag": "amazonas",
+ "name": "Amazonas",
+ "strength": 1
+ },
+ {
+ "coordinates": [1210, 1395],
+ "flag": "ar",
+ "name": "Argentina",
+ "strength": 1
+ },
+ {
+ "coordinates": [1107, 1419],
+ "flag": "cl",
+ "name": "Chile",
+ "strength": 1
+ },
+ {
+ "coordinates": [1064, 1114],
+ "flag": "pe",
+ "name": "Peru",
+ "strength": 1
+ },
+ {
+ "coordinates": [1065, 938],
+ "flag": "co",
+ "name": "Colombia",
+ "strength": 1
+ },
+ {
+ "coordinates": [1192, 938],
+ "flag": "ve",
+ "name": "Venezuela",
+ "strength": 1
+ },
+ {
+ "coordinates": [913, 833],
+ "flag": "ni",
+ "name": "Nicaragua",
+ "strength": 1
+ },
+ {
+ "coordinates": [788, 744],
+ "flag": "mx",
+ "name": "Mexico",
+ "strength": 1
+ },
+ {
+ "coordinates": [1011, 555],
+ "flag": "us",
+ "name": "USA",
+ "strength": 1
+ },
+ {
+ "coordinates": [800, 624],
+ "flag": "Texas",
+ "name": "Texas",
+ "strength": 1
+ },
+ {
+ "coordinates": [551, 564],
+ "flag": "California",
+ "name": "California",
+ "strength": 1
+ },
+ {
+ "coordinates": [703, 483],
+ "flag": "Utah",
+ "name": "Utah",
+ "strength": 1
+ },
+ {
+ "coordinates": [1077, 444],
+ "flag": "Quebec",
+ "name": "Quebec",
+ "strength": 1
+ },
+ {
+ "coordinates": [1231, 395],
+ "flag": "Newfoundland",
+ "name": "Newfoundland",
+ "strength": 1
+ },
+ {
+ "coordinates": [967, 418],
+ "flag": "ca",
+ "name": "Canada",
+ "strength": 1
+ },
+ {
+ "coordinates": [170, 244],
+ "flag": "Alaska",
+ "name": "Alaska",
+ "strength": 1
+ },
+ {
+ "coordinates": [741, 234],
+ "flag": "Nunavut",
+ "name": "Nunavut",
+ "strength": 1
+ },
+ {
+ "coordinates": [484, 256],
+ "flag": "Yukon",
+ "name": "Yukon",
+ "strength": 1
+ },
+ {
+ "coordinates": [1434, 223],
+ "flag": "gl",
+ "name": "Greenland",
+ "strength": 1
+ },
+ {
+ "coordinates": [2247, 1229],
+ "flag": "Rhodesia",
+ "name": "Rhodesia",
"strength": 1
}
]
diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts
index b0c3150d8..6d073fd30 100644
--- a/src/client/ClientGameRunner.ts
+++ b/src/client/ClientGameRunner.ts
@@ -26,6 +26,7 @@ import { loadTerrainMap, TerrainMapData } from "../core/game/TerrainMapLoader";
import { UserSettings } from "../core/game/UserSettings";
import { WorkerClient } from "../core/worker/WorkerClient";
import {
+ AutoUpgradeEvent,
DoBoatAttackEvent,
DoGroundAttackEvent,
InputHandler,
@@ -40,6 +41,7 @@ import {
SendBoatAttackIntentEvent,
SendHashEvent,
SendSpawnIntentEvent,
+ SendUpgradeStructureIntentEvent,
Transport,
} from "./Transport";
import { createCanvas } from "./Utils";
@@ -248,6 +250,7 @@ export class ClientGameRunner {
}, 20000);
this.eventBus.on(MouseUpEvent, this.inputEvent.bind(this));
this.eventBus.on(MouseMoveEvent, this.onMouseMove.bind(this));
+ this.eventBus.on(AutoUpgradeEvent, this.autoUpgradeEvent.bind(this));
this.eventBus.on(
DoBoatAttackEvent,
this.doBoatAttackUnderCursor.bind(this),
@@ -424,6 +427,76 @@ export class ClientGameRunner {
});
}
+ private autoUpgradeEvent(event: AutoUpgradeEvent) {
+ if (!this.isActive) {
+ return;
+ }
+
+ const cell = this.renderer.transformHandler.screenToWorldCoordinates(
+ event.x,
+ event.y,
+ );
+ if (!this.gameView.isValidCoord(cell.x, cell.y)) {
+ return;
+ }
+
+ const tile = this.gameView.ref(cell.x, cell.y);
+
+ if (this.myPlayer === null) {
+ const myPlayer = this.gameView.playerByClientID(this.lobby.clientID);
+ if (myPlayer === null) return;
+ this.myPlayer = myPlayer;
+ }
+
+ if (this.gameView.inSpawnPhase()) {
+ return;
+ }
+
+ this.findAndUpgradeNearestBuilding(tile);
+ }
+
+ private findAndUpgradeNearestBuilding(clickedTile: TileRef) {
+ this.myPlayer!.actions(clickedTile).then((actions) => {
+ const upgradeUnits: {
+ unitId: number;
+ unitType: UnitType;
+ distance: number;
+ }[] = [];
+
+ for (const bu of actions.buildableUnits) {
+ if (bu.canUpgrade !== false) {
+ const existingUnit = this.gameView
+ .units()
+ .find((unit) => unit.id() === bu.canUpgrade);
+ if (existingUnit) {
+ const distance = this.gameView.manhattanDist(
+ clickedTile,
+ existingUnit.tile(),
+ );
+
+ upgradeUnits.push({
+ unitId: bu.canUpgrade,
+ unitType: bu.type,
+ distance: distance,
+ });
+ }
+ }
+ }
+
+ if (upgradeUnits.length > 0) {
+ upgradeUnits.sort((a, b) => a.distance - b.distance);
+ const bestUpgrade = upgradeUnits[0];
+
+ this.eventBus.emit(
+ new SendUpgradeStructureIntentEvent(
+ bestUpgrade.unitId,
+ bestUpgrade.unitType,
+ ),
+ );
+ }
+ });
+ }
+
private doBoatAttackUnderCursor(): void {
const tile = this.getTileUnderCursor();
if (tile === null) {
diff --git a/src/client/Cosmetics.ts b/src/client/Cosmetics.ts
index 7e694207f..01a4a5de0 100644
--- a/src/client/Cosmetics.ts
+++ b/src/client/Cosmetics.ts
@@ -1,149 +1,85 @@
import { UserMeResponse } from "../core/ApiSchemas";
-import { COSMETICS } from "../core/CosmeticSchemas";
+import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas";
import { getApiBase, getAuthHeader } from "./jwt";
-import { translateText } from "./Utils";
-
-interface StripeProduct {
- id: string;
- object: "product";
- active: boolean;
- created: number;
- description: string | null;
- images: string[];
- livemode: boolean;
- metadata: Record;
- name: string;
- shippable: boolean | null;
- type: "good" | "service";
- updated: number;
- url: string | null;
- price: string;
- price_id: string;
-}
-
-export interface Pattern {
- name: string;
- key: string;
- roles: string[];
- price?: string;
- priceId?: string;
- lockedReason?: string;
- notShown?: boolean;
-}
export async function patterns(
userMe: UserMeResponse | null,
): Promise {
- const patterns: Pattern[] = Object.entries(COSMETICS.patterns).map(
- ([key, patternData]) => {
- return {
- name: patternData.name,
- key,
- roles: patternData.role_group
- ? (COSMETICS.role_groups[patternData.role_group] ?? [])
- : [],
- };
- },
- );
+ const cosmetics = await getCosmetics();
- const products = await listAllProducts();
- patterns.forEach((pattern) => {
- addRestrictions(pattern, userMe, products);
- });
+ if (cosmetics === undefined) {
+ return [];
+ }
+
+ const patterns: Pattern[] = [];
+ const playerFlares = new Set(userMe?.player.flares);
+
+ for (const name in cosmetics.patterns) {
+ const patternData = cosmetics.patterns[name];
+ const hasAccess = playerFlares.has(`pattern:${name}`);
+ if (hasAccess) {
+ // Remove product info because player already has access.
+ patternData.product = null;
+ patterns.push(patternData);
+ } else if (patternData.product !== null) {
+ // Player doesn't have access, but product is available for purchase.
+ patterns.push(patternData);
+ }
+ // If player doesn't have access and product is null, don't show it.
+ }
return patterns;
}
-function addRestrictions(
- pattern: Pattern,
- userMe: UserMeResponse | null,
- products: Map,
-) {
- if (userMe === null) {
- if (products.has(`pattern:${pattern.name}`)) {
- // Purchasable (flare-gated) patterns are shown as disabled
- pattern.lockedReason = translateText("territory_patterns.blocked.login");
- } else {
- // Role-gated patterns are not shown
- pattern.notShown = true;
- }
- return;
- }
- const flares = userMe.player.flares ?? [];
- if (
- flares.includes("pattern:*") ||
- flares.includes(`pattern:${pattern.name}`)
- ) {
- // Pattern is unlocked by flare
- return;
- }
-
- const myRoles = userMe.player.roles ?? [];
- if (
- pattern.roles.some((authorizedRole) => myRoles.includes(authorizedRole))
- ) {
- // Pattern is unlocked by role
- return;
- }
-
- const product = products.get(`pattern:${pattern.name}`);
- if (product) {
- pattern.price = product.price;
- pattern.priceId = product.price_id;
- pattern.lockedReason = translateText("territory_patterns.blocked.purchase");
- return;
- }
-
- // Pattern is locked by role group and not purchasable, don't show it.
- pattern.notShown = true;
-}
-
export async function handlePurchase(priceId: string) {
- try {
- const response = await fetch(
- `${getApiBase()}/stripe/create-checkout-session`,
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- authorization: getAuthHeader(),
- },
- body: JSON.stringify({
- priceId: priceId,
- successUrl: `${window.location.href}purchase-success`,
- cancelUrl: `${window.location.href}purchase-cancel`,
- }),
+ const response = await fetch(
+ `${getApiBase()}/stripe/create-checkout-session`,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ authorization: getAuthHeader(),
},
+ body: JSON.stringify({
+ priceId: priceId,
+
+ successUrl: `${window.location.href}purchase-success`,
+ cancelUrl: `${window.location.href}purchase-cancel`,
+ }),
+ },
+ );
+
+ if (!response.ok) {
+ console.error(
+ `Error purchasing pattern:${response.status} ${response.statusText}`,
);
-
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
+ if (response.status === 401) {
+ alert("You are not logged in. Please log in to purchase a pattern.");
+ } else {
+ alert("Something went wrong. Please try again later.");
}
-
- const { url } = await response.json();
-
- // Redirect to Stripe checkout
- window.location.href = url;
- } catch (error) {
- console.error("Purchase error:", error);
- alert("Something went wrong. Please try again later.");
+ return;
}
+
+ const { url } = await response.json();
+
+ // Redirect to Stripe checkout
+ window.location.href = url;
}
-// Returns a map of flare -> product
-export async function listAllProducts(): Promise