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> { +async function getCosmetics(): Promise { try { - const response = await fetch(`${getApiBase()}/stripe/products`); + const response = await fetch(`${getApiBase()}/cosmetics.json`); if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + console.error(`HTTP error! status: ${response.status}`); + return; } - const products = (await response.json()) as StripeProduct[]; - const productMap = new Map(); - products.forEach((product) => { - productMap.set(product.metadata.flare, product); - }); - return productMap; + const result = CosmeticsSchema.safeParse(await response.json()); + if (!result.success) { + console.error(`Invalid cosmetics: ${result.error.message}`); + return; + } + return result.data; } catch (error) { - console.error("Failed to fetch products:", error); - return new Map(); + console.error("Error getting cosmetics:", error); } } diff --git a/src/client/FlagInput.ts b/src/client/FlagInput.ts index 8ae13ca77..584253700 100644 --- a/src/client/FlagInput.ts +++ b/src/client/FlagInput.ts @@ -1,13 +1,11 @@ import { LitElement, css, html } from "lit"; import { customElement, state } from "lit/decorators.js"; -import Countries from "./data/countries.json"; +import { renderPlayerFlag } from "../core/CustomFlag"; const flagKey: string = "flag"; @customElement("flag-input") export class FlagInput extends LitElement { - @state() private flag: string = ""; - @state() private search: string = ""; - @state() private showModal: boolean = false; + @state() public flag: string = ""; static styles = css` @media (max-width: 768px) { @@ -21,19 +19,6 @@ export class FlagInput extends LitElement { } `; - private handleSearch(e: Event) { - this.search = String((e.target as HTMLInputElement).value); - } - - private setFlag(flag: string) { - if (flag === "xx") { - flag = ""; - } - this.flag = flag; - this.showModal = false; - this.storeFlag(flag); - } - public getCurrentFlag(): string { return this.flag; } @@ -46,14 +31,6 @@ export class FlagInput extends LitElement { return ""; } - private storeFlag(flag: string) { - if (flag) { - localStorage.setItem(flagKey, flag); - } else if (flag === "") { - localStorage.removeItem(flagKey); - } - } - private dispatchFlagEvent() { this.dispatchEvent( new CustomEvent("flag-change", { @@ -68,86 +45,51 @@ export class FlagInput extends LitElement { super.connectedCallback(); this.flag = this.getStoredFlag(); this.dispatchFlagEvent(); - window.addEventListener("keydown", this.handleKeyDown); } - disconnectedCallback() { - window.removeEventListener("keydown", this.handleKeyDown); - super.disconnectedCallback(); - } - - private handleKeyDown = (e: KeyboardEvent) => { - if (e.code === "Escape") { - e.preventDefault(); - this.showModal = false; - } - }; - createRenderRoot() { return this; } render() { return html` -
(this.showModal = false)} - >
- ${this.showModal - ? html` -
- -
- ${Countries.filter( - (country) => - country.name - .toLowerCase() - .includes(this.search.toLowerCase()) || - country.code - .toLowerCase() - .includes(this.search.toLowerCase()), - ).map( - (country) => html` - - `, - )} -
-
- ` - : ""}
`; } + + updated() { + const preview = this.renderRoot.querySelector( + "#flag-preview", + ) as HTMLElement; + if (!preview) return; + + preview.innerHTML = ""; + + if (this.flag?.startsWith("!")) { + renderPlayerFlag(this.flag, preview); + } else { + const img = document.createElement("img"); + img.src = this.flag ? `/flags/${this.flag}.svg` : `/flags/xx.svg`; + img.style.width = "100%"; + img.style.height = "100%"; + img.style.objectFit = "contain"; + img.onerror = () => { + if (!img.src.endsWith("/flags/xx.svg")) { + img.src = "/flags/xx.svg"; + } + }; + preview.appendChild(img); + } + } } diff --git a/src/client/FlagInputModal.ts b/src/client/FlagInputModal.ts new file mode 100644 index 000000000..86e413677 --- /dev/null +++ b/src/client/FlagInputModal.ts @@ -0,0 +1,105 @@ +import { LitElement, html } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; +import Countries from "./data/countries.json"; + +@customElement("flag-input-modal") +export class FlagInputModal extends LitElement { + @query("o-modal") private modalEl!: HTMLElement & { + open: () => void; + close: () => void; + }; + + @state() private search: string = ""; + + createRenderRoot() { + return this; + } + + render() { + return html` + + +
+ ${Countries.filter( + (country) => + country.name.toLowerCase().includes(this.search.toLowerCase()) || + country.code.toLowerCase().includes(this.search.toLowerCase()), + ).map( + (country) => html` + + `, + )} +
+
+ `; + } + + private handleSearch(event: Event) { + this.search = (event.target as HTMLInputElement).value; + } + + private setFlag(flag: string) { + localStorage.setItem("flag", flag); + this.dispatchEvent( + new CustomEvent("flag-change", { + detail: { flag }, + bubbles: true, + composed: true, + }), + ); + } + + public open() { + this.modalEl?.open(); + } + public close() { + this.modalEl?.close(); + } + + connectedCallback() { + super.connectedCallback(); + window.addEventListener("keydown", this.handleKeyDown); + } + + disconnectedCallback() { + window.removeEventListener("keydown", this.handleKeyDown); + super.disconnectedCallback(); + } + + private handleKeyDown = (e: KeyboardEvent) => { + if (e.code === "Escape") { + e.preventDefault(); + this.close(); + } + }; +} diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index 97e74be6d..2c9e33cbb 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -138,6 +138,14 @@ export class HelpModal extends LitElement { ${translateText("help_modal.action_reset_gfx")} + + +
+
+
+ + ${translateText("help_modal.action_auto_upgrade")} + diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index f089df82f..3031858df 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -107,6 +107,13 @@ export class CenterCameraEvent implements GameEvent { constructor() {} } +export class AutoUpgradeEvent implements GameEvent { + constructor( + public readonly x: number, + public readonly y: number, + ) {} +} + export class InputHandler { private lastPointerX: number = 0; private lastPointerY: number = 0; @@ -325,6 +332,12 @@ export class InputHandler { } private onPointerDown(event: PointerEvent) { + if (event.button === 1) { + event.preventDefault(); + this.eventBus.emit(new AutoUpgradeEvent(event.clientX, event.clientY)); + return; + } + if (event.button > 0) { return; } @@ -346,6 +359,11 @@ export class InputHandler { } onPointerUp(event: PointerEvent) { + if (event.button === 1) { + event.preventDefault(); + return; + } + if (event.button > 0) { return; } @@ -398,6 +416,11 @@ export class InputHandler { } private onPointerMove(event: PointerEvent) { + if (event.button === 1) { + event.preventDefault(); + return; + } + if (event.button > 0) { return; } diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index 1b631be87..1c875a7e7 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -135,14 +135,25 @@ export class JoinPrivateLobbyModal extends LitElement { ); } - private setLobbyId(id: string) { - if (id.startsWith("http")) { - this.lobbyIdInput.value = id.split("join/")[1]; + private extractLobbyIdFromUrl(input: string): string { + if (input.startsWith("http")) { + if (input.includes("#join=")) { + const params = new URLSearchParams(input.split("#")[1]); + return params.get("join") ?? input; + } else if (input.includes("join/")) { + return input.split("join/")[1]; + } else { + return input; + } } else { - this.lobbyIdInput.value = id; + return input; } } + private setLobbyId(id: string) { + this.lobbyIdInput.value = this.extractLobbyIdFromUrl(id); + } + private handleChange(e: Event) { const value = (e.target as HTMLInputElement).value.trim(); this.setLobbyId(value); @@ -151,15 +162,7 @@ export class JoinPrivateLobbyModal extends LitElement { private async pasteFromClipboard() { try { const clipText = await navigator.clipboard.readText(); - - let lobbyId: string; - if (clipText.startsWith("http")) { - lobbyId = clipText.split("join/")[1]; - } else { - lobbyId = clipText; - } - - this.lobbyIdInput.value = lobbyId; + this.setLobbyId(clipText); } catch (err) { console.error("Failed to read clipboard contents: ", err); } diff --git a/src/client/Main.ts b/src/client/Main.ts index b908aa6dd..9fb10c059 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -1,4 +1,3 @@ -import favicon from "../../resources/images/Favicon.svg"; import version from "../../resources/version.txt"; import { UserMeResponse } from "../core/ApiSchemas"; import { EventBus } from "../core/EventBus"; @@ -12,6 +11,7 @@ import "./DarkModeButton"; import { DarkModeButton } from "./DarkModeButton"; import "./FlagInput"; import { FlagInput } from "./FlagInput"; +import { FlagInputModal } from "./FlagInputModal"; import { GameStartingModal } from "./GameStartingModal"; import "./GoogleAdElement"; import { HelpModal } from "./HelpModal"; @@ -64,6 +64,7 @@ declare global { // Extend the global interfaces to include your custom events interface DocumentEventMap { "join-lobby": CustomEvent; + "kick-player": CustomEvent; } } @@ -163,7 +164,6 @@ class Client { } }); - setFavicon(); document.addEventListener("join-lobby", this.handleJoinLobby.bind(this)); document.addEventListener("leave-lobby", this.handleLeaveLobby.bind(this)); document.addEventListener("kick-player", this.handleKickPlayer.bind(this)); @@ -195,6 +195,16 @@ class Client { hlpModal.open(); }); + const flagInputModal = document.querySelector( + "flag-input-modal", + ) as FlagInputModal; + flagInputModal instanceof FlagInputModal; + const flgInput = document.getElementById("flag-input_"); + if (flgInput === null) throw new Error("Missing flag-input_"); + flgInput.addEventListener("click", () => { + flagInputModal.open(); + }); + const territoryModal = document.querySelector( "territory-patterns-modal", ) as TerritoryPatternsModal; @@ -536,14 +546,6 @@ document.addEventListener("DOMContentLoaded", () => { new Client().initialize(); }); -function setFavicon(): void { - const link = document.createElement("link"); - link.type = "image/x-icon"; - link.rel = "shortcut icon"; - link.href = favicon; - document.head.appendChild(link); -} - // WARNING: DO NOT EXPOSE THIS ID function getPlayToken(): string { const result = isLoggedIn(); diff --git a/src/client/NewsModal.ts b/src/client/NewsModal.ts index 308c9be75..ddfa1fdc5 100644 --- a/src/client/NewsModal.ts +++ b/src/client/NewsModal.ts @@ -83,7 +83,7 @@ export class NewsModal extends LitElement {
- ${translateText("news.full_changelog")} + ${translateText("news.see_all_releases")} (response.ok ? response.text() : "Failed to load")) + .then((markdown) => + markdown + .replace( + /(? + `[#${prNumber}](https://github.com/openfrontio/OpenFrontIO/pull/${prNumber})`, + ) + .replace( + /(? + `[${comparison}](https://github.com/openfrontio/OpenFrontIO/compare/${comparison})`, + ), + ) .then((markdown) => (this.markdown = markdown)); } this.requestUpdate(); diff --git a/src/client/TerritoryPatternsModal.ts b/src/client/TerritoryPatternsModal.ts index e116ea486..74e8e6857 100644 --- a/src/client/TerritoryPatternsModal.ts +++ b/src/client/TerritoryPatternsModal.ts @@ -3,11 +3,12 @@ import type { TemplateResult } from "lit"; import { html, LitElement, render } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import { UserMeResponse } from "../core/ApiSchemas"; +import { Pattern } from "../core/CosmeticSchemas"; import { UserSettings } from "../core/game/UserSettings"; import { PatternDecoder } from "../core/PatternDecoder"; import "./components/Difficulties"; import "./components/Maps"; -import { handlePurchase, Pattern, patterns } from "./Cosmetics"; +import { handlePurchase, patterns } from "./Cosmetics"; import { translateText } from "./Utils"; @customElement("territory-patterns-modal") @@ -107,14 +108,14 @@ export class TerritoryPatternsModal extends LitElement { } private renderTooltip(): TemplateResult | null { - if (this.hoveredPattern && this.hoveredPattern.lockedReason) { + if (this.hoveredPattern && this.hoveredPattern.product !== undefined) { return html`
- ${this.hoveredPattern.lockedReason} + ${translateText("territory_patterns.blocked.purchase")}
`; } @@ -122,7 +123,7 @@ export class TerritoryPatternsModal extends LitElement { } private renderPatternButton(pattern: Pattern): TemplateResult { - const isSelected = this.selectedPattern === pattern.key; + const isSelected = this.selectedPattern === pattern.pattern; return html`
@@ -131,9 +132,9 @@ export class TerritoryPatternsModal extends LitElement { ${isSelected ? "bg-blue-500 text-white" : "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"} - ${pattern.lockedReason ? "opacity-50 cursor-not-allowed" : ""}" + ${pattern.product !== null ? "opacity-50 cursor-not-allowed" : ""}" @click=${() => - !pattern.lockedReason && this.selectPattern(pattern.key)} + pattern.product === null && this.selectPattern(pattern.pattern)} @mouseenter=${(e: MouseEvent) => this.handleMouseEnter(pattern, e)} @mousemove=${(e: MouseEvent) => this.handleMouseMove(e)} @mouseleave=${() => this.handleMouseLeave()} @@ -155,23 +156,23 @@ export class TerritoryPatternsModal extends LitElement { " > ${this.renderPatternPreview( - pattern.key, + pattern.pattern, this.buttonWidth, this.buttonWidth, )}
- ${pattern.priceId !== undefined && pattern.lockedReason + ${pattern.product !== null ? html` ` : null} @@ -183,7 +184,6 @@ export class TerritoryPatternsModal extends LitElement { const buttons: TemplateResult[] = []; for (const pattern of this.patterns) { if (!this.showChocoPattern && pattern.name === "choco") continue; - if (pattern.notShown === true) continue; const result = this.renderPatternButton(pattern); buttons.push(result); @@ -243,6 +243,7 @@ export class TerritoryPatternsModal extends LitElement { this.modalEl?.open(); window.addEventListener("keydown", this.handleKeyDown); this.isActive = true; + this.requestUpdate(); } public close() { @@ -332,7 +333,7 @@ export class TerritoryPatternsModal extends LitElement { } private handleMouseEnter(pattern: Pattern, event: MouseEvent) { - if (pattern.lockedReason) { + if (pattern.product !== null) { this.hoveredPattern = pattern; this.hoverPosition = { x: event.clientX, y: event.clientY }; } diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index 70782e83a..14f79c190 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -125,6 +125,15 @@ export class UserSettingModal extends LitElement { console.log("💥 Special effects:", enabled ? "ON" : "OFF"); } + private toggleStructureSprites(e: CustomEvent<{ checked: boolean }>) { + const enabled = e.detail?.checked; + if (typeof enabled !== "boolean") return; + + this.userSettings.set("settings.structureSprites", enabled); + + console.log("🏠 Structure sprites:", enabled ? "ON" : "OFF"); + } + private toggleAnonymousNames(e: CustomEvent<{ checked: boolean }>) { const enabled = e.detail?.checked; if (typeof enabled !== "boolean") return; @@ -291,6 +300,15 @@ export class UserSettingModal extends LitElement { @change=${this.toggleFxLayer} > + + + = 10_000_000) { const value = Math.floor(num / 100000) / 10; - return value.toFixed(1) + "M"; + return value.toFixed(fixedPoints ?? 1) + "M"; } else if (num >= 1_000_000) { const value = Math.floor(num / 10000) / 100; - return value.toFixed(2) + "M"; + return value.toFixed(fixedPoints ?? 2) + "M"; } else if (num >= 100000) { return Math.floor(num / 1000) + "K"; } else if (num >= 10000) { const value = Math.floor(num / 100) / 10; - return value.toFixed(1) + "K"; + return value.toFixed(fixedPoints ?? 1) + "K"; } else if (num >= 1000) { const value = Math.floor(num / 10) / 100; - return value.toFixed(2) + "K"; + return value.toFixed(fixedPoints ?? 2) + "K"; } else { return Math.floor(num).toString(); } diff --git a/src/client/components/baseComponents/Modal.ts b/src/client/components/baseComponents/Modal.ts index af7c37440..0841b4ba4 100644 --- a/src/client/components/baseComponents/Modal.ts +++ b/src/client/components/baseComponents/Modal.ts @@ -7,6 +7,7 @@ export class OModal extends LitElement { @state() public isModalOpen = false; @property({ type: String }) title = ""; @property({ type: String }) translationKey = ""; + @property({ type: Boolean }) alwaysMaximized = false; static styles = css` .c-modal { @@ -31,6 +32,17 @@ export class OModal extends LitElement { max-width: 860px; } + .c-modal__wrapper.always-maximized { + width: 100%; + min-width: 340px; + max-width: 860px; + min-height: 320px; + /* Fallback for older browsers */ + height: 60vh; + /* Use dvh if supported for dynamic viewport handling */ + height: 60dvh; + } + .c-modal__header { position: relative; border-top-left-radius: 4px; @@ -74,7 +86,11 @@ export class OModal extends LitElement { ${this.isModalOpen ? html`