mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-23 21:22:21 +00:00
1cd36325ac29dddba15daf577de87f929fac1f17
738
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
32a254b375 |
add Baja California map 🏜️ (#3759)
## Description: Adds map of the Baja California peninsula in Mexico. Majority of this map is brown terrain to simulate the (Sonora) desert Mexican and American states as NPCs. The peninsula while having little landmass is balanced by the fact it will be very easy to pirate from it <img width="503" height="583" alt="Captura de pantalla 2026-04-18 151337" src="https://github.com/user-attachments/assets/b1acd93a-fbe4-4d17-95b6-5f4c86ec872f" /> Relief data from OpenTopography, already credited ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
37079e6a05 |
2661 PR 1/3 Warship Retreat Core, Blue UI Signal, and Transport-First Target Priority (#3498)
Part of #2661 (split into 3 PRs so they are not too large..) ## Description: Part 1/3 of #2661. This PR adds warship retreat basics, a blue retreating UI state, and updates target priority. Added: - Retreat state handling - Blue visual for retreating warships - Target priority: transport > warship > trade - Tests for retreat and target priority Example video: https://youtu.be/2hE2qeOeY48 Ship retreating: <img width="630" height="488" alt="image" src="https://github.com/user-attachments/assets/56d3e6d5-08af-453d-afe5-ee21dd6f3414" /> Ship healing: <img width="483" height="311" alt="image" src="https://github.com/user-attachments/assets/aeaf2239-bb81-444f-84ef-62dbcb48fddf" /> Back to being deployed: <img width="585" height="358" alt="image" src="https://github.com/user-attachments/assets/875828a2-8a24-4593-ac76-26426bb81057" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: zixer._ |
||
|
|
aa70d6742a |
add Archipelago Sea map ⚓ (#3749)
## Description: Adds Archipelago Sea map. The island map to end all island maps. This is a real location in Southwest Finland, famous for being the archipelago with the most islands in the world. This map is made for the very loud playerbase who enjoys naval maps. This is probably the most heavy naval map we are gonna get. Given this is a very experimental and unorthodox map like amazon or 4islands, it has a rotation of 3 , low but still should pop in for people who prefer island maps. https://github.com/user-attachments/assets/19b32a4f-825b-4b82-aec3-b24c387b9cdd ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
ec6e14051a |
Add an Antarctica map 🐧 (#3740)
## Description: (re-submission of older PR because the old one had too many merge conflicts) Re-adds and reworks the previously April Fools "Reglaciated Antarctica" as a new map, this time with proper elevation data: Map of Antarctica centered in the South Pole. The "appeal" of this a map is that it has no green terrain. (As such it has a really low rotation number (of 1). ) This also completes the Continental map category (antarctica was the only continent out of the traditional 7 missing). https://github.com/user-attachments/assets/28302464-c533-483e-8a1b-2699093921ff The base map image is a composite of 2 relief maps from 2 different sources, both properly credited ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 Co-authored-by: iamlewis <lewismmmm@gmail.com> |
||
|
|
0801798fbd |
Feat: Alliance and betrayal hotkeys (#3110)
Original Feature request by @FloPinguin Resolves #3077 ## Description: Adds hotkeys for Requesting alliances and breaking alliances. This allows for players to send or break alliances whose tile is under the cursor, without opening the radial menu. Keybinds: New "Ally Keybinds" section in Settings -> Keybinds Request alliance: Default: K - sends an alliance request to the player/bot/nation under the cursor Break alliance: Default: L - breaks the alliance with the player at the cursor Behavior: - Cursor must be over a tile owned by the target player. The action runs only when the game allows it, following the same logic as the radial menu. (canSendAllianceRequest and canBreakAlliance) - When an alliance request is sent, the events log shows: "Alliance request sent to [target]" for confirmation. No extra message for breaking an alliance (betrayal/debuff message already exists and is sent upon breaking an alliance) ## Screenshots: Keybind menu: <img width="739" height="595" alt="image" src="https://github.com/user-attachments/assets/ee958eab-fd50-4971-85c5-dfd49c6f0bdc" /> In game logs: <img width="373" height="232" alt="image" src="https://github.com/user-attachments/assets/2cf6bb07-5f0d-425a-82d3-65a44fef99c5" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced Discord username: _Dave9595_ |
||
|
|
29a1e8dfda |
feat: multi-warship selection with Shift+drag box (#3677)
Resolves #3666 ## Description: Adds RTS-style box selection for warships. Hold Shift and drag (desktop) or long-press and drag (touch/mobile) to draw a selection rectangle — all player-owned warships inside get selected at once. A subsequent click/tap on water sends them all to that location. - `SelectionBoxLayer` — pixel-dashed rectangle in world-space, player territory color; shared between desktop and touch - `UILayer` — same pulsing selection outline on each box-selected warship; clears correctly when switching between single/multi selection - `UnitLayer` — finds warships in screen rect, filters inactive ships before sending; touch support included - `InputHandler` — Shift+drag and touch long-press+drag both emit selection box events; cursor becomes crosshair on Shift; discards active ghost structure on Shift press; configurable via `shiftKey` keybind - `Transport` — single atomic `move_multiple_warships` intent (no split on socket drop) - `Schemas` + `ExecutionManager` + `MoveMultipleWarshipsExecution` — server fans out atomic intent into individual `MoveWarshipExecution` per ship - `DynamicUILayer` — `MoveIndicatorUI` chevron animation on target tile for both single and multi move - `UnitDisplay` — warship tooltip Shift hint via `translateText` - `HelpModal` — new hotkey row: Shift + drag → select multiple warships ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## UI update ### Mouse + Keyboard https://github.com/user-attachments/assets/3f35ab5e-1f3c-4c5d-bc4f-aabccf64dc60 ### Touch https://github.com/user-attachments/assets/0d6aec3f-44fa-4fee-b5c6-b267b9b14d79 ## ## Please put your Discord username so you can be contacted if a bug or regression is found: fghjk_60845 |
||
|
|
ec8884410c |
Add new Bering Sea map, reduce Strait rotation (#3706)
## Description: Re-submission of older PR: https://github.com/openfrontio/OpenFrontIO/pull/3617 <img width="612" height="396" alt="image" src="https://github.com/user-attachments/assets/955a0b2e-1df9-4fa3-a389-235f46a90f69" /> Regarding Bering Sea and Bering strait maps: <img width="576" height="87" alt="image" src="https://github.com/user-attachments/assets/f57031eb-f9c0-4e1e-83ff-204df51bb6c2" /> <img width="589" height="93" alt="image" src="https://github.com/user-attachments/assets/629e71ba-489f-41cf-b67e-2476af8704f2" /> Because of mixed opinions about replacing the maps, i propose at least this temporary compromise: Bering Sea gets added as a new map and Bering Strait gets its rotation lowered to a small number. Since the classic maps are probably being taken off rotation, we should have some small breathing space for the 2 maps. In the near future we should discuss between mapmakers in the dev server whether we keep, replace or completely take Bering Strait off rotation, and/or a community poll. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
bcd9cd6af5 |
Cache shared-water computation for nation port placement 💧 (#3696)
## Description: - Cache `sharedWaterComponents` globally with a 30-tick (~3s) TTL so all nations share one `O(total_border_tiles)` pass instead of each nation re-scanning every other player's border on every call. - Treat ocean as always-shared: any ocean neighbor short-circuits as a valid port site, skipping the `getWaterComponent` lookup in both the build pass and the per-tile port check. - Exclude bots and mutually-embargoed players from the trade-partner candidate set, so nations no longer avoid port sites that only "share" water with a player they can never trade with. Port placement is not time-critical, so the 3-second staleness is acceptable and lets the expensive build amortize across many attack cycles. ### Performance Benchmarked on World map (2000×1000, 61 nations) with the realistic call pattern of ~3 nations invoking `sharedWaterComponents` per tick: - **Before (main):** ~414 μs per tick - **After:** ~8 μs per tick amortized (29/30 ticks hit the warm cache; 1/30 rebuilds) - **~50× faster** on this AI hot path ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
0801cad0b5 |
Fix NaN coordinates in Warship patrol logic 🚢 (#3697)
## Description: This PR fixes the `Invalid coordinates: NaN,NaN` crash during Warship patrol execution. ### Root cause `WarshipExecution.randomTile` picks a patrol destination inside `warshipPatrolRange / 2` of the current patrol tile. When a search fails to find a valid tile, the range expands by 50% per retry (`100 → 150 → 225 → 337`) and becomes odd. Once odd, `warshipPatrolRange / 2` is a float (e.g. `112.5`), which is handed straight to `PseudoRandom.nextInt`: ```ts Math.floor(this.rng() * (max - min)) + min; ``` With a float `min`, this returns `integer + float` - a float. Despite its name, `nextInt` was silently returning a non-integer. From there: - `x = mg.x(patrolTile) + floatOffset` → float - `mg.isValidCoord(floatX, floatY)` → `true` (only bounds were checked) - `mg.ref(floatX, floatY)` → `yToRef[floatY] + floatX` → `undefined + float` → `NaN` - `hasWaterComponent(NaN, …)` → `miniMap.ref(NaN, NaN)` → **throw** ### Why this only started crashing recently The float‑leaking `nextInt` bug has been latent since at least the pathfinding refactor (#2866, January), which introduced the `hasWaterComponent` check. It was invisible because the guard directly above it short‑circuited on `NaN`: ```ts if (!this.mg.isOcean(tile) || (!allowShoreline && this.mg.isShoreline(tile))) continue; ``` For a `NaN` tile ref, `terrain[NaN]` is `undefined`, so: - `isOcean(NaN)` → `Boolean(undefined & OCEAN_BIT)` → **`false`** - `isLand(NaN)` → **`false`** - `isWater(NaN)` → `!isLand(NaN)` → **`true`** Before: `!isOcean(NaN)` was `true`, execution hit `continue`, and the poisoned ref never reached `hasWaterComponent`. The "Trading in lakes" PR (#3653) relaxed that single line to allow patrol on lakes: ```diff - if (!this.mg.isOcean(tile) || ...) continue; + if (!this.mg.isWater(tile) || ...) continue; ``` Because `isWater(NaN)` is `true`, `!isWater(NaN)` is now `false` - execution falls through to `hasWaterComponent(NaN, …)` and crashes. #3653 didn't introduce the bug; it just happened to remove the accidental NaN filter that was hiding it. ### Changes - **`PseudoRandom.nextInt`** - root‑cause fix. Floors both `min` and `max` so `nextInt` always returns an integer regardless of what callers pass. Future callers can't re‑trip this trap. - **`WarshipExecution.randomTile`** - replaced the unsafe `this.warship.patrolTile()!` non‑null assertion with a proper `undefined` guard that returns early. - **`GameMap.isValidCoord`** - defense in depth: also requires `Number.isInteger(x)` and `Number.isInteger(y)`. Non‑integer coords can still be produced outside `nextInt` (trig, arithmetic); this makes `ref()` fail loudly at the boundary instead of silently producing `NaN` refs. ### Original stacktrace Please paste the following in your bug report in Discord: Game crashed! game id: gGicMpDh client id: wXE5SpT2 Error: Invalid coordinates: NaN,NaN Message: Error: Invalid coordinates: NaN,NaN at at.ref (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:64853) at r_.hasWaterComponent (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:243326) at l_.hasWaterComponent (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:260740) at b1.randomTile (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:92634) at b1.patrol (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:91728) at b1.tick (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:89996) at https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:251463 at Array.forEach (<anonymous>) at l_.executeNextTick (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:251383) at p_.executeNextTick (https://nightly.openfront.dev/assets/Worker.worker-DL_guV2P.js:31:271256) Discord: https://discord.com/channels/1284581928254701718/1494336024740888667 ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
ae96eb7e98 |
Fix nation nuke crash when attacker has no remaining tiles 🛡️ (#3703)
## Description: Fixes a game crash (`Error: array must not be empty`) thrown from `PseudoRandom.randElement` when a nation tries to pick a nuke target whose territory no longer exists. ## Root cause `NationNukeBehavior.findBestNukeTarget` calls `findIncomingAttackPlayer`, which iterates the player's `_incomingAttacks`. `AttackImpl` instances can linger in this array past the point where the attacker has lost all tiles — the attack is only removed on explicit `delete()` and `removeOnDeath` cleanup isn't guaranteed to run before other executions tick within the same turn. A dead attacker gets returned as the nuke target, `randTerritoryTileArray` samples their (empty) territory, and `randElement` throws on the empty array. ## Fix - `Player.incomingAttacks()` now filters out attacks whose attacker is no longer alive, so consumers can't observe stale references from mid-tick deaths. - `randTerritoryTile` guards against `numTilesOwned() === 0` before falling back to `randElement(p.tiles())` as a defense-in-depth safeguard at the util level. - `PlayerImpl.toUpdate()` now uses the `incomingAttacks()` / `outgoingAttacks()` accessors (rather than the raw `_` arrays) so serialized client state stays consistent with the server-side view. `outgoingAttacks()` is intentionally left unfiltered, the engine relies on seeing in-flight attacks during their retreat phase after a target is conquered (attack merging/cancellation in `AttackExecution.init`). ### Bug report from discord Please paste the following in your bug report in Discord: Game crashed! game id: LQDSWbh6 client id: JjwysSLN Error: array must not be empty Message: Error: array must not be empty at sa.randElement (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:28:39166) at H1 (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:116429) at Jc (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:116135) at G1.maybeSendNuke (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:117597) at n5.tick (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:171764) at https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:251463 at Array.forEach (<anonymous>) at c.executeNextTick (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:251383) at b.executeNextTick (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:271256) at S_ (https://main.openfront.dev/assets/Worker.worker-DoPM94lr.js:31:366356) ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
f1f63ec9b4 |
Partial Pathfinding Rebuild 💧 (#3689)
## Description: Another big water nukes performance improvement. Performance measurements (water-nuke-detonation on GWM): <img width="203" height="103" alt="image" src="https://github.com/user-attachments/assets/b3d62575-d4bd-43c9-a5af-af127d73a9c5" /> I did a lot of testing with throwing water nukes and sending boats in the area, paths are looking clean & correct! ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
9821e8e041 |
Add host cheats for streamers (Specifically Enzo) ⭐ (#3671)
## Description: - Adds a "Host Cheats" toggle in the private lobby options section that reveals a dedicated section with four host-only cheats: infinite gold, infinite troops, gold multiplier, and starting gold - Only the lobby creator receives the cheat effects in-game (checked via `isLobbyCreator` in DefaultConfig) - Joining players see active host cheats displayed as yellow badges in the lobby UI - Adds `hostCheats` optional object to `GameConfigSchema` and wires it through the server config update whitelist - Raises the intent size limit for `update_game_config` messages (lobby-only, not stored in turn history) to prevent rate-limiter kicks (I always got too-much-data-kicked after selecting "host cheats" lol) <img width="861" height="525" alt="image" src="https://github.com/user-attachments/assets/51e51ec4-c2e8-46ca-b258-11a93487964f" /> <img width="933" height="825" alt="image" src="https://github.com/user-attachments/assets/5acbd38d-2097-42e1-ba78-0fb17d6afe82" /> ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
d05f5e748c |
Fix external train trade attribution in match results (#3589)
## Description: A player reported that income received from opponents' trains was missing from match result logs. Fix external train trade attribution to the station owner and add regression tests for train income stats. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: aotumuri |
||
|
|
35a64fa0d9 |
Big Water-Nukes Performance Improvements 💧 (#3668)
## Description: ### 1. Water Magnitude Calculation Optimization (WaterManager.ts) * Boxed BFS Approach: Refactored the water magnitude recomputation to use "Dirty" and "Seed" boxes. Instead of a global update, the system now only recalculates magnitudes within a specific radius of the affected area, significantly reducing CPU load after water-nuke-explosions * Shoreline Bit Optimization: Narrowed the scope for updating shoreline bits to a 2-ring neighborhood around converted tiles, avoiding unnecessary checks across the entire map. Performance test on the world map: - AtomBomb (r=30): 24ms (was 344ms with global BFS), 2,993 changed tiles (was 630k) - Massive (r=200): 178ms (was 378ms), 130k changed tiles (was 654k) ### 2. Pathfinding Rebuild Staggering (PathFinder.ts, TradeShipExecution.ts, TransportShipExecution.ts) * Distributed Rebuilds: Introduced a staggering mechanism in WaterPathFinder. Ship pathfinders now wait a randomized/distributed number of ticks (0 - 5 seconds) before rebuilding after a water graph change. * CPU Spike Mitigation: By spreading out these expensive A* rebuilds over time, we prevent lag when hundreds of ships attempt to re-path simultaneously * Like Mole said it: "Pretty realistic I;d say the capitan needs a second to realize the big nuke on the left opened a new path" From a performance test on the big new Luna map: Graph rebuild: 256.4ms Pathfinder-Rebuild of 329 ships (Including other Executions): 1564.4ms (No longer noticeable, spread over 5s) ### 3. Performance Refinements * Simplified deep ocean magnitude logic within the optimized BFS flow. * Improved memory efficiency by utilizing clipped BFS wavefronts. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
897a3a3566 |
adds map of the Caucasus (#3661)
## Description: Adds map of the Caucasus. This map is made especifically because of the new update that allows ports to be placed on smaller bodies of water, rather than just the largest one. This map has 2 disconnected, large bodies of water of similar size (Black and Caspian seas), which would create 2 independent trade systems, creating a more dynamic economy. <img width="667" height="541" alt="Captura de pantalla 2026-04-13 195810" src="https://github.com/user-attachments/assets/d980b831-d920-4a2e-9d92-40a7be96ead6" /> <img width="539" height="439" alt="Captura de pantalla 2026-04-13 200005" src="https://github.com/user-attachments/assets/e973a66a-8796-40dc-b8b3-39b49f687609" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
318d1e2c44 |
Refactor/Fix(UserSettings): last localstorage calls now use UserSettings, and Keybinds fixes (#3619)
## Description: 1) Have last localstorage calls for keybinds and attack ratio also use UserSettings cache instead, after #3481. Remaining calls to localstorage are for different things than user settings, so they are left as is. 2) Consolidate and centralize keybinds logic. And three fixes for it. - **UnitDisplay** and **UserSettingsModal**: _parsedUserKeybinds_ is introduced in **UserSettings** to centralize their logic. It is also used by _normalizedUserKeybinds_, see point below. - **UserSettingsModal** -- replaced unwanted cast `as SettingKeybind` by a typed QuerySelector. -- renamed this.keybinds to this.userKeybinds for more clarity, and distinction from defaultKeybinds. -- state private _userKeybinds_: remove type string[] since loadKeybindsFromStorage replaces a value array by its first string element, so it can not contain string[] anymore. -- _handleKeybindChange_ and _getKeyValue_: no need to check for Array.isArray anymore, see above reason. -- **Fix**: checks after calling _parsedUserKeybinds_ are improved a bit: don't delete all keybinds and print a console warning when finding just one invalid keybind and (i think i have seen people complaining about things being removed). Instead it now migrates or throws out the invalid ones but keeps the valid ones. Also works with the "Null" value expected and removed within **UserSettingsModal**._handleyKeybindChange_() and in **HelpModal**. When legacy value is an array and key is empty, don't put value as key but get first array element or empty string as key name. So that check on line 68 is true. - **HelpModal** and **InputHandler**: Also centralize/consolidate their logic more, by having __keybinds()_ from **UserSettings** perform fetching _getDefaultKeybinds_ and _normalizedUserKeybinds_. -- Functionality in _normalizedUserKeybinds_ is the same: Where HelpModal did return [k, v.value] if typeof (v as any).value === "string", this is now handled by lines 309-310 of normalizedKeybinds still the same but with less lines. Same for old HelplModal if (typeof v === "string") return [k, v], this is stil returned by line 112 of normalizedKeybinds. And return [k, undefined] when (typeof val !== "string") as was done in InputHandler, isn't needed as values that weren't strings were already filtered out right after which we still do on line 314 of normalizedKeybinds. -- **Fix** in _normalizedUserKeybinds_: added one extra thing that was a discrepancy between **HelpModal**/**InputHandler** and **UserSettingsModal** before: **UserSettingsModal** would handle array values, and normalize them by picking only the first value if it is a string. Now have _normalizedKeybinds_ do the same. Otherwise it would have thrown those values out while **UserSettingsModal** would have kept the first value. This may still help a returning player who hasn't played in the last version (i think i have seen people complaining about things being removed, but that may not have been about this). And makes the logic more consistent between **UserSettingsModal** and **HelpModal**/**InputHandler**. - **UserSettings**: -- _getDefaultKeybinds_: centralized/consolidated logic, accepts Platform.isMac parameter. In **HelpModal**, **InputHandler** and **UserSettingsModal** the same list with default keybinds was hardcoded. Now they all read from _getDefaultKeybinds_. The list of default keybinds in **HelpModal** was a little shorter, but that doesn't matter since its _render_() function has hardcoded which of the hotkeys **HelpModal** shows. Have thought about putting default keybinds in **DefaultConfig** but with all the logic handled through **UserSettings**, this seemed the better place in the current refactor. -- _removeCached_: make public, now that **InputHandler.test.ts** needs to be able to call it. We could instead make a public function like removeKeybinds() and keep removeCached() private, but went with this for now. -- _parsedUserKeybinds_: centralized/consolidated logic for **UserSettingsModal**/**UserDisplay**. Always returns an object, even an empty one if the JSON wasn't parsable. -- _normalizedKeybinds_: centralized/consolidated logic. Used by _keybinds_() which is now called by **HelpModal**/**InputHandler**. -- _keybinds_: now uses getDefaultKeybinds() and normalizedKeybinds() to get the default and user changed keybinds. -- **Fix** in _keybinds_: it now removes a key if it is Unbound by the user in **UserSettingsModal**. Instead of first loading the parsedUserKeybinds, removing "Null" keys from it, and then merging that with defaultKeybinds (so default key would overwrite an unbound key), we now merge parsedUserKeybinds with defaultKeybinds and after that remove "Null" keys from it (so that unbound key stays removed). For example if Boat Attack Up is set to "None" ("Null") by clicking Unbind, there is now no hotkey working for it anymore. Even when the default is "B". Why? This prevents the user from being confused, they have deliberately Unbound it, they don't understand why it still works (have seen bug reports and game feedback about this)? Also more importantly: they used to now be able to bind "B" to another action. Effectively making key "B" bound to two actions: the user choosen one and Boat Attack. This also makes the logic more consistent. Because building hotkeys in **UnitDisplay** already didn't work when unbound, eg. when Build Missile Silo was Unbound, the "5" key did not do anything anymore (there is a fallback in **UnitDisplay** in case the key is actually null, but it does respect "Null" as it should). -- _setKeybinds_: have it accept an object, it stringifies it itself. Callers UserSettingsModal and InputHandler.test.ts now just send either a string or an object. - **InputHandler.test.ts**: -- use **UserSettings** functions instead of localStorage for more real-world testing. -- change test "ignores non-string values and preserves defaults, removes 'Null' for unbound keys". As explained above, as a fix we no longer preserve unbound ("Null") keys within InputHandler. UserSettings.keybinds() now removes "Null" keys as explained above. - ControlPanel: use UserSettings to fetch initial attack ratio. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 |
||
|
|
3704914676 |
Add map conakry (#3656)
## Description: Add Map - Conakry Dataset from OpenTopography. Map rotated 45 degrees from north. This is a 'long' map in a similar catagory as Amazon River with public rotation adjusted to match. Different than Amazon in that its stubbier, one sided, and has various terrain obstacles. Also its a really cool looking piece of geography. https://www.youtube.com/watch?v=OsMDbnnOOko https://discord.com/channels/1284581928254701718/1481689305960288477/1481689305960288477 I removed an additional bot from the far left to help balance the map. Some rivers were extended past realism to help section off the map in areas. Size of map kept below average intentionally. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username PlaysBadly |
||
|
|
17c1a6300f |
Trading in lakes 🚤 (#3653)
## Description: - Widened port placement and warship spawn/patrol checks from `isOcean`/`isOceanShore` to `isWater`/`isShore`, so ports can be built on lake shores and ships can operate on lakes, we discussed it here: <img width="996" height="423" alt="image" src="https://github.com/user-attachments/assets/acf1e970-9631-4848-a0ed-6d0470616e1d" /> - Filtered `tradingPorts()` by water component so ports only attempt trades with reachable ports - prevents silent path-not-found failures across disconnected water bodies - Applied the same water component filter when a captured trade ship reroutes to its new owner's nearest port - Removed the `WaterManager` fallback that force-marked isolated water-nuked-tiles as ocean (no longer needed since lakes are now navigable) - Added a check to prevent nations from building ports on water bodies that aren't accessible to other players ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin --------- Co-authored-by: Evan <evanpelle@gmail.com> |
||
|
|
51716e9dc4 |
Add map luna (the moon) (#3635)
## Description: Add map Luna (The Moon). https://youtu.be/6L6vS9VvD8k https://discord.com/channels/1284581928254701718/1490394299785805944/1490394299785805944 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Discord username PlaysBadly --------- Co-authored-by: FloPinguin <25036848+FloPinguin@users.noreply.github.com> |
||
|
|
f0b3c490b1 |
Fix: flag not visually removed when selecting default/none flag + should not update flag on clearFlag (#3626)
## Description: https://github.com/openfrontio/OpenFrontIO/pull/3479 put setFlag and clearFlag in UserSettings. https://github.com/openfrontio/OpenFrontIO/pull/3481 did some updates to incorporate them into UserSettings cache. But made two misjudgments. Which led to: when default/none flag was selected, FlagInput would still display the previously selected flag. And on clearFlag call, unnecessary updates where send to FlagInput > updateFlag. This PR fixes it. - Don't send update event on clearFlag by default, just like PR 3479 didn't. Although i could imagine potential cases where we would want to update the displayed flag in FlagInput. Not when clearFlag is called from Auth > logOut. But maybe in some, but not all, cases when it is called from Cosmetics > getPlayerCosmeticsRefs. That is for future investigations by another contributor. - Do sent update event when clearFlag is called from setFlag, if the "country:xx" (default/none) flag is set. Previously, it would have sent event this.emitChange(FLAG_KEY, **"country:xx"**). Now, via clearFlag, it sends this.emitChange(FLAG_KEY, **null**) - Have FlagInput > updateFlag handle null. Previously it expected to always recieve a string, even for default/none flag "country:xx". Now it will also set this.flag to null if it recieves an event with null. It being able to handle event value null actually hardens the code so seems better to me either way. FlagInput > isDefaultFlagValue() already did handle null values to determine the default flag, so no changes needed there. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 |
||
|
|
7f7cbba12f |
Water-Nukes 💧 (#3604)
## Description: Adds a new `waterNukes` game config option that causes nuclear detonations to convert land tiles into water instead of just leaving fallout. When enabled, nuked land tiles are batched and converted to water each tick, with full terrain metadata updates including: - Ocean bit propagation from adjacent ocean tiles (BFS flood fill) - Magnitude recomputation via BFS from remaining coastlines - Shoreline bit fix-up in a 2-ring neighborhood around converted tiles - Minimap terrain sync (majority-rule downsampling) - Throttled water navigation graph rebuild (every 20 ticks) for ship pathfinding - Ship executions detect graph rebuilds and refresh their pathfinders - TransportShips auto-retreat if their destination becomes water - Water nuke craters use a smoothed angular noise ring with a bounding-box scan instead of the regular per-tile random coin flip with BFS, producing clean blob-shaped craters without scattered land pixels that players would have to boat to individually The `TerrainLayer` now incrementally repaints tiles that changed terrain type, and tile update packets encode the terrain byte alongside tile state so clients can reflect water conversions in real time. When `waterNukes` is disabled, behavior is unchanged (fallout only). Includes a new test suite (WaterNukes.test.ts) covering the conversion pipeline, ocean propagation, magnitude recalculation, shoreline updates, and minimap sync. Also adds a new public game modifier for the special rotation. ### The only problem A bit of lag on impact. But otherwise it works great and is fun. Maybe needs some followup improvements if it gets merged. I think its very cool in baikal / four islands team games. Chip away the territory of your opponents. Its also fun to turn The Box / Alps into a water map (its actually possible to boat-trade then) ### Media Video does not show the updated craters https://github.com/user-attachments/assets/aed8bf08-0e94-4484-b997-4de11ae313d9 Updated craters (no tiny islands after impact): <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e896870b-bc9d-493d-8bc8-b3a5427d69d3" /> <img width="1472" height="920" alt="image" src="https://github.com/user-attachments/assets/677065aa-0159-48cd-af44-a91b0f57adfc" /> <img width="1296" height="892" alt="image" src="https://github.com/user-attachments/assets/886ffaba-541f-4e46-97c6-ce963f632fe0" /> ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
f3cbca059f |
Remove dead color code (#3613)
## Description: Remove dead code relating to colors. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: DISCORD_USERNAME babyboucher |
||
|
|
38222bfe34 |
Strait of Malacca map adition (#3599)
## Description: Adition of Strait of Malacca map It adds a new map, requested by Coolson awildcoolson Describe the PR. The map added is a map inspired by the strait of Malacca, it contains two large landmasses and some islands ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: lerithmercano --------- Co-authored-by: Ricky G.P. <realtacoco@gmail.com> Co-authored-by: VariableVince <24507472+VariableVince@users.noreply.github.com> |
||
|
|
341f344ce5 |
Perf/Refactor(UserSettings): caching makes it 10-20x faster (#3481)
## Description: Skip slow and blocking LocalStorage reads, replace by a Map. Also some refactoring. ### Contains - No out-of-sync issue between main and worker thread: Earlier PRs got a comment from evan about main & worker.worker thread having their own version of usersettings and possibly getting out-of-sync (see https://github.com/openfrontio/OpenFrontIO/pull/760#pullrequestreview-2845155737, https://github.com/openfrontio/OpenFrontIO/pull/896#pullrequestreview-2871836979 and https://github.com/openfrontio/OpenFrontIO/pull/1266. But userSettings is not used in files ran by worker.worker, not even 10 months after evan's first comment about it. In GameRunner, createGameRunner sends NULL to getConfig as argument for userSettings. And DefaultConfig guards against userSettings being null by throwing an error, but it has never been thrown which points to worker.worker thread not using userSettings. So we do not need to worry about syncing between the threads currently. (If needed in the future after all, we could quite easily sync it, by loading the userSettings cache on worker.worker and listening to the "user-settings-changed" event @scamiv to keep it synced (changes in WorkerMessages and WorkerClient etc would be needed to handle this). - Went with cache in UserSettings, not with listening to "user-settings-changed" event: "user-settings-changed" was added by @scamiv and is used in PerformanceOverlay. Which is great for single files that need the very best performance. But having to add that same system to any file reading settings, scales poorly and would lead to messy code. Also, a developer could make the mistake of not listening to the event and it would end up just reading LocalStorage again just like now. Also a developer might forget removing the listener or so etc. The cache is a central solution and fast, without changes to other files needed and future-proof. - Make sure each setting is cached: UserSettingsModal was using LocalStorage directly by itself for some things. Made it use the central UserSettings methods instead so we avoid LocalStorage reads as much as possible. For this, changed get() and set() in UserSettings to getBool() and setBool(), to introduce a getString() and setString() for use in UserSettingsModal while keeping getCached() and setCached() private within UserSettings. - Remove unused 'focusLocked' and 'toggleFocusLocked' from UserSettings: was last changed 11 months ago to just return false. Since then we've moved to different ways of highlighting and this setting isn't used anymore. No existing references or callers are left. - Other files: -- Have callers call the renamed functions (see point above) -- Remove userSettings from UILayer and Territorylayer: the variable is unused in those files. Also remove from GameRenderer when it calls TerritoryLayer. -- Cache calls to defaultconfig Theme (which in turn calls dark mode setting)/Config better in: GameView and Terrainlayer. ### Update on Contents later on It wasn't really in scope of this PR but further consolidation was called for. These changes could also pave the way for UserSettingsModal (main menu) perhaps being partly mergable with SettingsModal (in-game) one day as it begins to look more like it. Even though UserSettingsModal still does things its own way, and does console.log where SettingsModal doesn't, etc. They both have partially different content and settings but also have a large overlap. - UserSettings: Removed localStorage call from clearFlag() and setFlag() which were added after creation of this PR, and were neatly merged in silence without merge conflicts so i wasn't aware of them yet until now. - UserSettings: added key constants, exported to use both inside UserSettings and in files that listen to its events. - UserSettings 'emitChange': now done from setCached, removed from setBool, setFlag etc. Also removed from the new setFlag. And from setPattern even though it emitted "pattern" instead of key name "territoryPattern"; now it emits the default "territoryPattern" from PATTERN_KEY which is re-used in Store, TerritoryPatternsModal and PatternInput. - UserSettingsModal: made UserSettingsModal call existing toggle functions in UserSettings, or new or existing getter or setter. We do not need CustomEvent: checked anymore. In UserSettingsModal, its toggle functions did not all actually toggle, some like toggleLeftClickOpensMenu actually just set a value. Based on the 'checked' value of the CustomEvent. But we don't need that 'checked' value anymore and none of the checks for it inside the toggle functions in UserSettingsModal, now that we just directly call toggleLeftClickOpensMenu and others in UserSettings. - SettingToggle: continuing about not needing CustomEvent anymore: the old way actually fired two events. The native change event from <input> and our own CustomEvent from handleChange in SettingToggle. It prevented handling both events by checking e.detail?.checked === undefined. But now, the native <input> event is all we need to show the visual toggle change and trigger @changed in UserSettingsModal which calls the toggle function. - Use the toggle functions too from CopyButton and PerformanceOverlay.ts. In PerformanceOverlay, change in onUserSettingsChanged was needed because of how setBool works. - UserSettingsModal 'toggleDarkMode': in UserSettingsModal, removed the event from toggleDarkMode in UserSettingsModal; nothing is listening to this event anymore after DarkModeButton.ts was removed some time ago. Also both UserSettingsModal an UserSettings added/removed "dark" from the document element. Now that UserSettingsModal calls toggleDarkMode in UserSettings, we could centralize that. But UserSettings is in core, not in client like UserSettingsModal. But now that we emit "user-settings-changed", we could handle it even more centralized and not have UserSettingsModal or UserSettings touch the element directly. Instead have Main.ts listen to the event and change it dark mode from there. - UserSettings: added claryfing comment to attackRatioIncrement and the new attackRatio setters/getters, to explain their difference. Noticed a small omitment in its description and fixed that right away in en.json: you can change attack ratio increment by shift+mouse wheel scroll or by hotkey. So made "How much the attack ratio keybinds change per press" also mention "/scroll." **BEFORE** (with getDisplayName added back to NameLayer as a fix i will do soon) get > getItem in UserSettings  renderLayer in NameLayer (with getDisplayName added back to NameLayer as a fix i will do soon)  **AFTER** (with getDisplayName added back to NameLayer as a fix i will do soon) getCached in UserSettings  renderLayer in NameLayer (with getDisplayName added back to NameLayer as a fix i will do soon)  ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 |
||
|
|
b5ca0f9d8f |
Perf/refactor/fix(NameLayer): about 10% extra improvement (#3540)
## Description: NameLayer perf part 2 after https://github.com/openfrontio/OpenFrontIO/pull/3475 with thanks to @scamiv. Shaves off another 10% or thereabouts, even doing something extra for a fix (see below). Also refactor/fixes around NameLayer and PlayerIcons, which is used by both NameLayer and PlayerInfoOverlay, and underlying function in GameView. This would go well with other PR https://github.com/openfrontio/OpenFrontIO/pull/3481, since this layer reads multiple settings. Reasoning to not use events and instead rely on fast caching is explained in that PR. ### Contents - Fixes: -- Fixes bug on .dev introduced by wrong assumption by me in previous PR https://github.com/openfrontio/OpenFrontIO/pull/3475. displayName CAN change during game, when Hidden Names is toggled from settings, so needs to be put back in renderPlayerInfo. -- Fixes longer existing bug: it was assumed Dark Mode didn't change after creation of icon element. Now it also sets Dark Mode attribute when updating icons elements. -- Fixes target mark icons not being shown to team members, while the icons were shown to normal allies. And EventsDisplay displayed message "XX requests you attack XX" to both team members and allies already. So why is the icon not shown to both if the message already is. While we improve performance of GameView > PlayerView > transitiveTargets (which is only used by NameLayer/PlayerIcons so only in this context). We can add team members' targets to it in one go. So previously transitiveTargets returned: your own targets and allies' targets. Now transitiveTargets is faster and returns: your own targets and allies' targets and team members' targets. - NameLayer: -- renderLayer: for target icons, getPlayerIcons used to fetch myPlayer.transitiveTargets each time. While that doesn't change per player we're rendering for. So now, we fetch myPlayer.transitiveTargets once per call to renderPlayerInfo, which passes it on to getPlayerIcons. So now we check it 1x each 100ms (renderCheckRate) inside of renderLayer. Instead of up to 100s of times each 500ms (renderRefreshRate) inside of getPlayerIcons inside of renderPlayerInfo loop. -- createBasePlayerElement and renderPlayerInfo: use cloneNode where possible with templates -- createPlayerElement: only find the elements and set font and flag. Leave the rest to renderLayer > renderPlayerInfo which fills displayName and troops and font color very soon after anyway. I haven't noticed a difference in testing. -- cache game.config() and others -- renderPlayerInfo: remove check if render.flagDiv exists, we know it exists. Check if fontColor changed before assigning it (it never changes, currently, be it dark mode or light mode). Don't check if troops or size changes, that happens so often that the overhead for checking would be smaller than the win, probably. -- We don't require nameLocation to be changed to change scale (see previous Namelayer perf PR for the reason). But it seems good to check if the transform changed before 'overwriting' it, so do that now instead. -- Remove Alliance icon DOM traversals. Only do it once, for each time an alliance icon is displayed. To this end, also made NameLayer more agnostic on Alliance icon stuff. By moving more code to PlayerIcons. See below. -- Use cached allianceDuration instead of fetching this static value every time -- Re-use from PlayerIcons: ALLIANCE_ICON_ID, TRAITOR_ICON_ID etc -- create more sub-functions to make the icons loop in renderLayer more readable: handleEmojiIcon, handleAllianceIcons, createOrUpdateIconElement (createIconElement already existed, now combined), handleTraitorIcon. For Alliance icons, this was already done in PlayerIcons.ts through createAllianceProgressIcon (now createAllianceProgressIconRefs), and more now to skip some DOM traversals. But most of this belongs in NameLayer itself when it comes to seperation of concern. - cache dark mode (as boolean and as string) - use dark mode to update (alliance) icons too, not only on create, since the setting can change after icon element creation and before it is removed -- for getPlayerIcons, add this.alliancesDisabled. If disabled, getPlayerIcons won't fetch Alliance icon and Alliance Request icon. - PlayerIcons: -- use cloneNode where possible -- added check for alliances disabled: then skip alliance (request) icon checks -- See point under NameLayer about the move of Alliance icon code to PlayerIcons. To make NameLayer even more agnostic on it and keep it in one place. -- getPlayerIcons: skip creating a new Set from myPlayer.transitiveTargets() each time getPlayerIcons is called. One allocation less. Just do .includes on the returned array. Probably just as fast in this case, also because not many Targets are present many times anyway. -- getPlayerIcons: on outgoingEmojis(), use .find() instead of .filter() since we only use the first result anyway and it saves us another allocation. -- getPlayerIcons: for nukes, only fetch the ones from the player we're rendering for, not all game nukes. Also don't use .filter() and just a normal loop to skip an allocation. Logic outcome is the same. -- getPlayerIcons: for target icons, it used to fetch myPlayer.transitiveTargets each time. While that doesn't change per player we're rendering for. So now, NameLayer fetches myPlayer.transitiveTargets once per call to renderPlayerInfo, which passes it on to getPlayerIcons. -- Remove the need for querySelector and getElementsByTagName("img") alltogether. Since this would be done for every time an alliance was (re-)created, here in createAllianceProgressIconRefs in PlayerIcons it makes more sense to not do DOM traversal than in createPlayerElement in NameLayer where we only do it once per player per game anyway. We assume updateAllianceProgressIconRefs just knows of all image names in createAllianceProgressIconRefs. This is a bit less dynamic and maintainable maybe, but i think worth the win. And the functions are all one-purpose and not meant to be used dynamically by another caller anyway. -- So instead of updateAllianceProgressIconRefs looping through refs.images, now just update the different images each. See point above. - PlayerInfoOverlay: also re-use the new exported consts from PlayerIcons. Since we put those in PlayerIcons anyway, need to be consistent. Even though PlayerInfoOverlay is outside of the scope of this PR otherwise. -- for getPlayerIcons, add this.alliancesDisabled here too. If disabled, getPlayerIcons won't fetch Alliance icon and Alliance Request icon. We also send includeAllianceIcon = false, which means Alliance icon will already be excluded but Alliance Request icon is normally still fetched and shown. - GameView > PlayerView: for transitiveTargets (only used in NameLayer/PlayerIcons so only in this context), improve performance. It did several allocations. Now it loops directly over the arrays we need. Also (as mentioned under Fixes) previously transitiveTargets returned: your own targets and allies' targets. Now transitiveTargets is faster and returns: your own targets and allies' targets and team members' targets. **BEFORE**  **AFTER** (including now getting team members' targets for myPlayer.transitiveTargets)  ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 |
||
|
|
e7029564a2 |
adds map of the Great Lakes (#3588)
## Description: Highly requested map. Map of the 5 Great Lakes (Superior, Michigan, Huron, Erie and Ontario) with adjacent rivers and lakes. Local cities and towns as nations / NPCs. https://github.com/user-attachments/assets/157346fa-e3e6-4d63-9f51-46bae002d529 (Kinda obvious we needed this location at some point) ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
6467007fd7 |
Add map dyslexdria (#3568)
## Description: Re Add map Dyslexdria. I adjusted the rivers to make boat traffic go through main channels isntead of shortcuts. Flags adjusted. Correct thumbnail added ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Discord username Plays Badly |
||
|
|
6ca5cb9701 |
Revert "Add multiple funny maps april fools day (#3538)"
This reverts commit
|
||
|
|
8b9f714d6f |
Revert "Fix WorldRotated enum value to match lang key 🌐 (#3553)"
This reverts commit
|
||
|
|
ff7f459825 |
Revert "Add Map - Dyslexdria - April Fools (#3413)"
This reverts commit
|
||
|
|
85f09c1c98 |
Revert "Make april fool maps have the same name as normal maps (#3555)"
This reverts commit
|
||
|
|
38be51a2d6 |
Make april fool maps have the same name as normal maps (#3555)
## Description: Make the april fool maps have the same name as the normal maps, in order to make the prank better. NOTE: In order for this to work, it re-adds the normal maps into rotation. These normal maps have been given a lower rotation however. In custom matches the maps will appear with the april fools names. This change will be reverted after April Fools. Check this for proof of it working: https://canary.discord.com/channels/1359946986937258015/1450487807242932336/1488682300924231882 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: DISCORD_USERNAME |
||
|
|
2bf96db464 |
Add Map - Dyslexdria - April Fools (#3413)
Re-submission, previous request was not branched properly. ## Description: Add custom map Dyslexdria. Based off of large_world_map_recolor.tif and rivers from the XL World map. Map is intended for long term use. Suggesting that map be used as an April Fools gag by replacing thumbnail and title with 'World' during first run. <img width="995" height="721" alt="dyslexdria screenshot" src="https://github.com/user-attachments/assets/8826839f-b4e0-431d-af9c-0b0e43dc601d" /> - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Following: - Build log, screen shots, and test videos. https://discord.com/channels/1284581928254701718/1456890656147374080 ## Discord username: PlaysBadly --------- Co-authored-by: Duwibi <86431918+Duwibi@users.noreply.github.com> Co-authored-by: VariableVince <24507472+VariableVince@users.noreply.github.com> |
||
|
|
4dda807b03 |
Fix WorldRotated enum value to match lang key 🌐 (#3553)
## Description: Fixes this display bug, says "WORLD ROTATED" now <img width="235" height="140" alt="image" src="https://github.com/user-attachments/assets/dc8bb97d-350a-4ced-a63d-f3564479ba41" /> ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
a7d9652886 |
Add multiple funny maps april fools day (#3538)
## Description: Requesting that these 4 maps be added temporarily for the April Fools holiday. Included are: - A wider version of the Amazon River to allow for larger ships. - An edit of The Box with more varied terrain. - A modification to The World which optimizes play for mobile users. - A custom remix to Antarctica which re-adds ice to the map. All maps are edits of existing work. All maps have been added as their own individual map files rather than edits to existing files for ease of management. <img width="912" height="1067" alt="temp" src="https://github.com/user-attachments/assets/089ef59f-8bdf-4c86-b479-620b8c220c3a" /> https://discord.com/channels/1284581928254701718/1483786939332169830 - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Discord username PlaysBadly |
||
|
|
6fa9bf692b |
Add Mediterranean map (#3523)
## Description: Adds Mediterranean sea map, from Iberia to Asia. Map contains ancient Roman Empire provinces and its rivals as Nations. This map was requested by the dev. elevation data from Opentopography <img width="2850" height="1450" alt="image" src="https://github.com/user-attachments/assets/6aa5ba12-f4f7-414d-a712-b90323f1d796" /> <img width="590" height="304" alt="Captura de pantalla 2026-03-27 010038" src="https://github.com/user-attachments/assets/efd1deea-bd88-4ae2-92a0-47a6626a0c0f" /> <img width="585" height="302" alt="Captura de pantalla 2026-03-27 005758" src="https://github.com/user-attachments/assets/a127696e-fe34-424c-a88d-b86b99a5f414" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
217a2c4548 |
Adds Milky Way Map (#3519)
## Description: Adds Milky Way galaxy map based on real reconstruction by NASA. Star density simulated as terrain. Best played in Dark Mode. Also adds credits to JPL NASA <img width="532" height="533" alt="Captura de pantalla 2026-03-26 142938" src="https://github.com/user-attachments/assets/87bb19bb-4e2d-4383-a3e9-6e14b714b84c" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tri.star1011 |
||
|
|
7fdda33fb9 | Merge branch 'v30' | ||
|
|
cf0cf14a1f |
Add new public game modifiers 🙂 (#3500)
## Description: Adds 5 new public game modifiers to the Special game mode modifier pool: - **Ports Disabled** - disables port construction, focus on factories - **Nukes Disabled** - disables atom bombs, hydrogen bombs, MIRVs, missile silos and SAM launchers - **SAMs Disabled** - disables SAM launchers (thats funny, you cant protect against nukes, have to space out your stuff) (mutually exclusive with nukes disabled) - **1M Starting Gold** - gives all players 1M starting gold (was requested by people, new chill tier alongside existing 5M and 25M) - **4min Peace Time** - grants 4 minutes of PVP spawn immunity All `PublicGameModifiers` boolean fields are now optional - inactive modifiers are omitted from game JSON instead of being serialized as `false` (stop bloating the JSON size) I think we have enough modifiers now :) Good variety ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
39ad547c04 |
support for unlockable flags (#3479)
## Description: Add support for purchasable/gated flags. * Create a new "Store" modal that renders both skins & flags * move all store related logic out of TerritoryPatternsModal * use nation:code for existing nation flags & flag:key for gated flags * check if user has the appropriate flags before purchasing ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan |
||
|
|
eb51853b05 |
Perf/Fix: spawn and other functions that need closest by unit (#3243)
## Description: Performance improvements. - **PlayerImpl**: for _nukeSpawn_, cache config to const. - **Other files**: for nukeSpawn and other functions doing the same, introduce findClosestBy function. - for **TradeShipExecution**, with the move from _distSortUnit_ to _findClosestBy_, also add check if port isActive, !_isMarkedForDeletion_ and !_isUnderConstruction_. These checks should have been there already, so now do it in one go to make use of the predicate isCandidate in findClosestBy. - for **TradeShipExectution.test.ts**, add mock functions for _isMarkedForDeletion_ and _isUnderConstruction_ because of the above. Also, set Unit tiles and Pathfinding node to actual valid TileRefs for the testing map. This prevents NaN as return value from manhattanDist. This problem was already present with the use of distSortUnit, but that function just did NaN - NaN, returned the first and only port unit in the array and called it a day. For findClosestBy we have to make sure the predicate manhattanDist actually returns a number instead of NaN so we need actually valid tiles. We now have a working test instead of a test that actually silently failed like before. - **PlayerImpl**: _warshipSpawn_ and _nukeSpawn_: Make use of the isCandidate predicate of findClosestBy to have warshipSpawn not return ports under construction or (smaller change) inactive. This fixes a bug i have seen right away (where Warship spawns from under construction Port). Same for _nukeSpawn_ silos, don't return inactive silo just to be sure now that we can easily add it to isCandidate predicate anyway. This costs no performance in the _nukeSpawn_ benchmarks actually. This should as a by-effecft fix an edge case bug i have seen, where a nuke is sent from a phantom silo. Some of this goes along with PR #3220 since playerImpl buildableUnits makes use of the underlying spawn functions via canBuild. Just like ConstructionExecution does. But i didn't want to add more to PR 3220 since there's already a lot in there. The new function _findClosestBy_ could also be applied to some other parts of code to benefit of it being faster, so i did that. _findClosestBy_ uses _findMinimumBy_, which is a little more generic in name. I think _findMinimumBy_ could be used by other parts of code, while _findClosestBy_ is more clear naming for what it does now. But we could ditch _findMinimumBy_ and just leave findClosestBy? Examples of synthetic benchmarks (not included in this PR): **BEFORE CHANGES (before Scamiv's PR #3241)** <img width="705" height="91" alt="image" src="https://github.com/user-attachments/assets/d6d91c08-39f1-4387-9ccc-e51951caa539" /> <img width="751" height="101" alt="image" src="https://github.com/user-attachments/assets/80d400ac-3408-4107-aa58-6d2a847311e9" /> **AFTER CHANGES (before Scamiv's PR #3241)**   **BEFORE CHANGES (after Scamiv's PR #3241)**   **AFTER CHANGES (after Scamiv's PR #3241)** <img width="717" height="96" alt="image" src="https://github.com/user-attachments/assets/5b106843-bf6e-4448-a8e8-94448fb30ced" /> <img width="767" height="92" alt="image" src="https://github.com/user-attachments/assets/e6714c7b-26c1-455b-adae-f0060f1cbc7b" /> _Also see more **BEFORE** and **AFTER** in this comment:_ https://github.com/openfrontio/OpenFrontIO/pull/3243#issuecomment-3949060395 _And here a comparison in the flame charts:_ - based on the same replay and tried to get the performance recording going at the same speed and length but always end up with small differences - because of a bug in replays currently, it puts you in with the same clientID/persistantID currently. This means we can also record part of what is normally only recordable with live human input (the playerActions/playerBuildables). **BEFORE** flame chart with nukeSpawn (human player) and maybeSendNuke (Nation players, uses nukeSpawn via canBuild):    **AFTER** flame chart with nukeSpawn (human player) and maybeSendNuke (Nation players, uses nukeSpawn via canBuild):      ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33 |
||
|
|
05e2bc9f0a |
Improve cacheability with content-hashed public assets and a cacheable app shell (#3494)
## Description: This reworks asset delivery and cacheability across the app and moves non-bundled public resources onto immutable, content-hashed URLs. Vite bundle outputs continue to live under `/assets/**` and remain content-hashed by Vite. Public resources that were previously fetched from stable paths in `resources/` now go through a custom hashed namespace under `/_assets/**`, backed by a generated asset manifest that is available to the server, browser, and worker runtime. In parallel, the root app shell is now cacheable shared HTML instead of request-time `no-store` HTML. Dynamic and live routes remain explicitly uncached. ## Why - Improve browser and Cloudflare cacheability for static assets. - Remove query-string and release-version cache busting for runtime-fetched assets. - Allow unchanged public assets to keep the same URL across releases. - Reduce avoidable work on `/` by serving a shared app shell instead of rendering HTML on every request. - Make cache behavior explicit instead of relying on mixed framework defaults and file-extension heuristics. ## What Changed ### 1. Content-hashed public asset pipeline - Added a build-time public asset manifest and hashing pipeline for non-Vite resources. - Production now emits hashed public assets under `/_assets/**`. - Added runtime manifest loading for Node so server-rendered paths resolve against built hashed files instead of rebuilding from source at runtime. - Emitted the runtime asset manifest as an ESM module for server consumption. Result: - `/assets/**` = Vite-managed hashed bundle outputs - `/_assets/**` = custom content-hashed public resources ### 2. Runtime asset URL migration - Added a shared `assetUrl(...)` resolution path. - Migrated runtime references away from query-string versioning and stable source paths. - Updated browser, worker, and server-side rendering paths to resolve through the asset manifest. - Moved map manifests, map binaries, thumbnails, sprites, sounds, fonts, flags, icons, screenshots, and other runtime-fetched resources onto hashed URLs. ### 3. Map and preview fixes - Fixed directory and per-file map asset resolution so map manifest and binary fetches resolve to the correct hashed URLs. - Updated preview metadata and map thumbnail paths to use the hashed asset namespace. - Fixed runtime manifest loading in prod after deployment. ### 4. Explicit cache policies - Added explicit immutable cache headers for: - `/assets/**` - `/_assets/**` - worker-prefixed equivalents under `/wN/...` - Added explicit `no-store` headers for live and dynamic APIs. - Removed the old `/api/env` bootstrap request and baked `gameEnv` into the HTML bootstrap instead. ### 5. Cacheable root app shell - Refactored the root HTML path to serve a shared app shell with: - `Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=86400` - `/` and the SPA fallback now serve shared cacheable HTML instead of request-time `no-store` rendering. - `/game/:id` remains dynamic and `no-store`, but now reuses the shared shell before injecting preview tags. ### 6. Matchmaking instance handling - Because the app shell is now cacheable, `INSTANCE_ID` was removed from shared HTML. - Added `/api/instance` as a temporary `no-store` runtime lookup used only by matchmaking. - This preserves correctness with the current random-per-boot `INSTANCE_ID` model while keeping `/` cacheable, but it is not the intended long-term design. ## Behavior Changes ### Asset URL contract Production URLs for non-Vite public resources now change from stable paths such as: - `/maps/...` - `/images/...` - `/manifest.json` to content-hashed paths under: - `/_assets/...` Examples: - `/_assets/maps/<map>/manifest.<hash>.json` - `/_assets/images/Favicon.<hash>.svg` ### Bootstrap/config - `/api/env` is removed. - `gameEnv` is now bootstrapped from HTML. ### HTML caching - `/` and the SPA fallback are now cacheable shared HTML. - `/game/:id` remains dynamic. ## Cache Matrix After This Branch - `/_assets/**`: `public, max-age=31536000, immutable` - `/assets/**`: `public, max-age=31536000, immutable` - live `/api/**`: explicit `no-store` - `/api/health`: explicit `no-store` - `/api/instance`: explicit `no-store` - `/game/:id`: explicit `no-store` - `/` and SPA fallback: `public, max-age=0, s-maxage=300, stale-while-revalidate=86400` ## Notes / Tradeoffs - `/api/instance` is a temporary compromise. It exists because `INSTANCE_ID` is currently random per boot, which is not safe to embed into cacheable shared HTML. - The current matchmaking flow still asks the client to provide `instance_id` during `matchmaking/join`. That is functional, but it is the wrong ownership boundary: instance selection should be handled by the matchmaking service, not by the browser. - The cleaner end-state would be: - make `matchmaking/join` stop requiring `instance_id` from the client, and let the matchmaking service select a healthy instance from worker check-ins - This branch makes the origin behavior edge-cache-friendly, but Cloudflare still needs matching cache rules if HTML itself should be cached at the edge. ## Validation Verified during development with: - `npx tsc --noEmit` - `node node_modules\\vite\\bin\\vite.js build` - `node node_modules\\vitest\\vitest.mjs run tests/server/RenderHtml.test.ts tests/server/NoStoreHeaders.test.ts tests/server/StaticAssetCache.test.ts tests/core/configuration/ConfigLoader.test.ts` Additional targeted tests added: - `tests/AssetUrls.test.ts` - `tests/core/game/FetchGameMapLoader.test.ts` - `tests/core/configuration/ConfigLoader.test.ts` - `tests/server/NoStoreHeaders.test.ts` - `tests/server/StaticAssetCache.test.ts` - `tests/server/RenderHtml.test.ts` ## Known Existing Warnings The production build still reports pre-existing warnings that are not addressed by this branch: - inconsistent JSON import attributes for `resources/countries.json` - inconsistent JSON import attributes for `resources/QuickChat.json` - large chunk warnings from Vite ## Rollout Notes - Cache rules should treat `/_assets/**` and `/assets/**` as immutable. - Cloudflare will still classify HTML as dynamic after deploy unless matching edge cache rules are configured for it. ## Follow-ups - Remove `/api/instance` by changing `matchmaking/join` so the server selects the target instance, or by making `INSTANCE_ID` deploy-stable if the current contract must remain. ## Please complete the following: - [ ] I have added screenshots for all UI updates - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: DISCORD_USERNAME |
||
|
|
95cdd3b4b9 |
More random nation names 😄 (#3486)
## Description: Extend NationCreation for a bit more variety :D ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
837458c369 |
Feat: Tournament Maps (#3483)
## Description: Introduces 4 new private match maps for the OFM tournament in May. Includes 2, 3, 4, and 8 player maps. Playtested, and include as many default nations as the map expects players, named after compass points. Give it a try at https://tourney-maps.openfront.dev/ <img width="1500" height="1500" alt="image" src="https://github.com/user-attachments/assets/9138b636-5dd7-4118-82e2-50a5125a7963" /> The base images were created from scratch based on prototype designs, and they were converted from vector versions I made. I haven't decided on what the names should be for the maps themselves, so for now they're just `Tourney Map 1`, `Tourney Map 2`, etc. Ideas welcome. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: bijx |
||
|
|
015e3c7d19 |
feat: Attacking Troops Overlay (#3427)
## Description: https://troop-advantage-layer.openfront.dev/ Hey OpenFront dev team, I've been really enjoying the game, and the v0.30 changes have felt great so far. Happy to start contributing! This PR introduces `AttackingTroopsOverlay`, a layer that renders live attacker vs. defender troop counts directly on active front lines. Players can immediately gauge combat strength without leaving the map view.  A recent change updates the layer to just the # of attackers and a symbol for attack/defence:  Left: Perspective of Anon 667 (Blue) | Right: Perspective of Anon332 (Red)  **How it works:** - Attacker count shown for ground invasions. When attacking, your troop count will display amber for disadvantageous, and green for advantageous battles. When defending, the enemy troop count will switch to red if you are at a severe disadvantage. - Label position recalculates every tick at 200ms, tracking the front line as it moves. - Automatically hidden during Terrain view (spacebar) - Labels clean up when an attack ends or its target becomes invalid **Settings:** An "Attacking Troops Overlay" toggle is added to Settings, enabled by default. --> the screenshot is old, but the text has been updated <img width="448" height="410" alt="Settings toggle" src="https://github.com/user-attachments/assets/2df8ec7a-3f77-48b7-a9b5-ee4a6eed0412" /> ## Checklist - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Discord Radyus |
||
|
|
1049b7e7dc |
Clan System Part 1 (#3276)
## Description: Properly split out clantags and usernames, a clantag should not be part of a username. <img width="285" height="286" alt="image" src="https://github.com/user-attachments/assets/8ac56e82-b12c-4fc0-9774-e445252a6e61" /> https://api.openfront.dev/game/ojkqZFb2 <img width="296" height="596" alt="image" src="https://github.com/user-attachments/assets/85152f80-c111-4f87-b85b-8516c9c6137b" /> https://api.openfront.dev/game/MF32BkVc requires; https://github.com/openfrontio/infra/pull/264 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: w.o.n |
||
|
|
3013133d08 |
Embrace the aftergame! 😄 (#3410)
## Description: In v30 we have the following change to prevent teammates from destroying your structures: **Block nuking teammate structures** - Nukes blocked if they'd hit a teammate's structure (that was possible by nuking oceans / rivers) (by @FloPinguin) Original idea was from Wonder. I think it makes sense, but it has a side effect: The aftergame, which many players love, will be dead because of this change. <img width="835" height="103" alt="image" src="https://github.com/user-attachments/assets/521b7915-be28-4d83-8d45-65835e7385ab" /> <img width="1101" height="105" alt="image" src="https://github.com/user-attachments/assets/db74a9c6-da12-44a2-aa06-f042b8e58b8a" /> I think a lot of complaints will follow after v30 is live. So why not add a little bit of logic for the aftergame? After a team wins/loses, players can nuke their teammates. No longer need to aim for water. SAMs also intercept teammate nukes in this phase. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
3e65d08942 |
reduce train gold after each city (#3400)
## Description: Now that cities snap to existing rails, it's possible to line up dozens of cities in a row, producing way too much gold. This PR reduces the gold after each stop to prevent that. Gold only starts decreasing after the 3rd city. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan |
||
|
|
be9ea14fe9 |
refactor: rename Bot to Tribe in internal execution code (#3372)
## Description Follows up on #3290 which renamed the user-facing "Bots" to "Tribes". This renames the internal implementation to match: - `BotExecution` → `TribeExecution` - `BotSpawner` → `TribeSpawner` - `BotNames` → `TribeNames` (`BOT_NAME_*` → `TRIBE_NAME_*`) All callers updated accordingly. `PlayerType.Bot` and `ColoredTeams.Bot` are intentionally left unchanged as they are serialised wire-format values. Closes #3335 ## Please complete the following: - [x] My changes do not break existing functionality - [x] I have tested my changes ## Please put your Discord username so you can be contacted if a bug or regression is found: Just reply here --------- Co-authored-by: PGray <PGrayCS@users.noreply.github.com> |
||
|
|
3838de1d30 |
Option to disable alliances + 2 new modifiers for variety 😄 (#3392)
## Description:
Rex had this idea: "It would be funny to have an option in private
lobbies to disable alliances."
I added it as an option.
Now people can choose to live in constant fear of their neighbors 😆
Also added two new public game modifiers for variety (only for the
special rotation):
- Alliances disabled (low probability)
- x2 gold multiplier (low probability)
Would be nice to squeeze this into v30, last minute?
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
|