## Description:
I hope we can get this into v30?
The nation count is configurable now, just like the bot count.
Replaced the "Disable Nations" toggle with a nations slider (0–400) in
SinglePlayer and Host Lobby modals.
<img width="710" height="121" alt="Screenshot 2026-03-03 021952"
src="https://github.com/user-attachments/assets/c8d0f0c3-db51-4303-95fa-dbc770460ec2"
/>
Public games are staying exactly the same, this is just for singleplayer
and private lobby fun.
Youtubers could play HvN against 400 nations, for example.
Singleplayer enjoyers no longer have to play against 1 nation in HvN,
they can freely choose.
`GameConfig.disableNations: boolean` got replaced by `nations: number
(0-400, optional)`
`undefined` = map default,
`0` = disabled,
number = custom count
Nations slider defaults to the map's nation count, shows "(MAP DEFAULT)"
label when unchanged
Compact map toggle reduces nations to 25% when at default, restores when
toggled off (just like we already do with bots)
The nation count for HvN no longer automatically matches the human count
in singleplayer and private games, only in public games.
**What if there aren't enough nations configured for the map?**
We just use the HvN logic (Generate random nations)
### Warning
**This infra PR also needs to get merged:
https://github.com/openfrontio/infra/pull/263
Otherwise players can set 0 nations and get achievements.**
## 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
## Description:
Introduces Passage, an island filled thin map (like Amazon River but
inverted) which is a long, thin stretch of islands that makes for some
really fun gameplay. I playtested with 3 friends a number of times and
it's great in pretty much all the modes. Naval gameplay on this map is
particularly fun since some islands in the center basically hold the
choke points on trade ships passing from one side to another, making
them hotly contested territory.
<img width="6000" height="400" alt="long map"
src="https://github.com/user-attachments/assets/7904d6f8-e7b8-437d-852e-68a2f006d200"
/>
Describe the PR.
## 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
## Description:
instead of just killing the server, lets save 10m before, then kill the
server at 3hr mark, so at least we have a proper savegame.
## 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
## Description:
Add Nile Delta as a new regional map. Features 11 nations across the
delta region (Alexandria, Damietta, Faraskur, Sheremsah, El
Senbellawein, Aga, Mit Ghamr, Cairo, Heliopolis, Memphis, El Mansoura).
1.36M land tiles at 1556x1280, terrain generated from real relief data.
Includes the Suez Canal. Playlist frequency: 4.

## 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
Source: https://commons.wikimedia.org/wiki/File:Niledelta_33.svg
Discord: gabigabogabu
## Description:
Adds two new public game modifiers for variety and improves compact map
eligibility for team games.
### New Modifiers
**Hard Nations (`isHardNations`)**
- We need this modifier for HvN, because medium nations are easier now
(will result in a much higher human winrate)
- In a discord discussion we concluded that HvN should generally be
easier (higher winrate than 50%, so players are less frustated)
- Thats why only 20% of HvN games have the hard nations modifier (for
now)
- For PvPvE enjoyers, the modifier is also active in FFA games => (Only
2.5% chance, and 1 ticket in `SPECIAL_MODIFIER_POOL`)
**25M Starting Gold (`startingGoldHigh`)**
- Some people in the main discord wanted this modifier, and it will
result in crazy games
- Rare special-only modifier (1 ticket in pool); mutually exclusive with
5M starting gold via `MUTUALLY_EXCLUSIVE_MODIFIERS`
- Disables nations (they lack PVP immunity, so 25M gold doesn't work
well with them)
- Excluded from HumansVsNations games (since it disables nations)
- Spawn immunity set to **2 minutes 30 seconds** (vs 30s for 5M gold),
so people can spend the gold and prepare
### Other Changes
- **Improved `supportsCompactMapForTeams`**: Replaced the hard `smallest
>= 50` land-tile cutoff with a per-team-config calculation that
simulates worst-case compact player count and checks every team gets at
least 2 players.
- **HvN spawn immunity**: Always 5 seconds in both regular and special
lobbies (to get rid of a confusing PVP immunity HeadsUpMessage in 5M
starting gold games)
- **Regular public lobby random spawn modifier probabilty**: Reduced
from 10% to 5% (Because of the new modifier, so there aren't too many
modifiers in non-special-lobbies, should only occur sometimes there)
- Rebalanced `SPECIAL_MODIFIER_POOL` a bit
## 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
## Description:
This PR fixes an exploit that allows the player to request alliances to
Nations, mostly in impossible mode, during spawn phase, with high
chances for it to be accepted due to troop count parity.
Nations now reject alliance requests during the spawn phase.
`GameImpl.executeNextTick()` initializes ALL pending `unInitExecs` in
one batch on the first post-spawn tick ( `numSpawnPhaseTurns() + 1` ).
So every alliance request submitted during spawn phase is guaranteed to
be created with `createdAt = numSpawnPhaseTurns() + 1` on the very first
post-spawn tick.
Therefore, we check for alliance requests created on the very first
post-spawn tick and reject those.
## 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:
deshack_82603
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Description:
Reverts openfrontio/OpenFrontIO#3312
## 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
## Description:
This PR fixes an exploit that allows the player to request alliances to
Nations, mostly in impossible mode, during spawn phase, with high
chances for it to be accepted due to troop count parity.
Nations now reject alliance requests during the spawn 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:
deshack_82603
## Description:
Adds a new Bosphorus Map (Turkey). One of the key strategic locations in
the world and control access to the Black Sea. Smaller map that most to
facilitate smaller FFA and Team games. Added are a selection of nations
that correspond to the location.
<img width="1000" height="612" alt="image"
src="https://github.com/user-attachments/assets/27a6debc-a33b-4b54-b522-69ab814c39f0"
/>


## 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:
TSProphet
---------
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
## Description:
PR to add Bering Strait map. Produced using TOPO data from real world
location, rivers and lakes correct to real world location. The map is
slightly smaller than most so facilitates smaller FFA games or 2 team
game modes.
The centre island has been increased in size to enable better warfare to
capture the strategic location.
<img width="1500" height="918" alt="image"
src="https://github.com/user-attachments/assets/bc9b2e69-cef1-4f21-92b5-4ffdce5812e1"
/>

## 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:
TSProphet
---------
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
## Description:
Removes the client-driven heartbeat loop and switches worker tick
execution to a worker-owned drain scheduler with batched game update
delivery.
## Why
The previous flow required the client to send a `heartbeat` every
animation frame just to keep the worker progressing turns. That had two
costs:
1. Simulation progress was coupled to browser frame cadence.
2. Catch-up periods produced many single `game_update` messages,
increasing message overhead and main-thread wakeups.
## What Changed
### 1) Remove heartbeat protocol
- Deleted `heartbeat` from `WorkerMessageType`.
- Removed `HeartbeatMessage` from `MainThreadMessage`.
- Removed `sendHeartbeat()` from `WorkerClient`.
- Removed the `requestAnimationFrame` keep-alive loop in
`ClientGameRunner`.
Files:
- `src/client/ClientGameRunner.ts`
- `src/core/worker/WorkerClient.ts`
- `src/core/worker/WorkerMessages.ts`
- `src/core/worker/Worker.worker.ts`
### 2) Add batched worker-to-client updates
- Added `game_update_batch` message type and `GameUpdateBatchMessage`.
- Worker now emits one batch message containing multiple tick updates.
- `WorkerClient` handles `game_update_batch` by replaying updates to the
existing callback in order.
Files:
- `src/core/worker/WorkerMessages.ts`
- `src/core/worker/WorkerClient.ts`
### 3) Move tick draining into worker
- Added a scheduler (`scheduleDrain`) and drain loop (`drain`) in
`Worker.worker.ts`.
- On each `turn` message, worker enqueues turn and schedules drain.
- Drain executes up to `MAX_TICKS_BEFORE_YIELD = 4` ticks per cycle,
then yields with `setTimeout(..., 0)`.
- Tick updates are collected into a batch and sent once with
transferables:
- `packedTileUpdates.buffer`
- `packedMotionPlans.buffer` (when present)
- If backlog remains, drain reschedules itself.
File:
- `src/core/worker/Worker.worker.ts`
## Behavioral Notes
- No server protocol changes.
- Ggame update callback contract remains the same (still receives one
`GameUpdateViewData` at a time in order).
- Ordering is preserved: `WorkerClient` iterates batch entries in
sequence.
- Error updates are still filtered from update delivery in the worker
batch path (same effective behavior as before for normal update flow).
## Expected Impact
- Fewer `postMessage` calls during backlog and burst turn delivery.
- Lower message overhead and fewer main-thread interrupts.
- Less dependence on UI frame timing for worker progress.
- Better catch-up stability due to explicit periodic yielding.
## Risk Areas
- Drain scheduling edge cases (re-entrancy / lost wake-ups).
- Mitigated with `drainScheduled`, `draining`, and `drainRequested`
flags.
- Larger per-message payloads due to batching.
- Bounded by `MAX_TICKS_BEFORE_YIELD`.
- Any assumptions in downstream code about receiving only `game_update`.
- Handled by adding `game_update_batch` support in `WorkerClient`.
## 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
## Description:
Reduce per-step `Unit` update traffic by shipping packed motion plans
and letting the client advance plan-driven units locally.
Changes:
- Add packed motion plan records (`packedMotionPlans?: Uint32Array`) to
game updates and transfer the buffer worker -> main.
- Introduce `src/core/game/MotionPlans.ts` (schema + pack/unpack) for
grid + train motion plans.
- Extend `Game` with `recordMotionPlan(...)` and
`drainPackedMotionPlans()`, and implement buffering/packing in
`GameImpl`.
- Treat units with motion plans as “plan-driven”: suppress per-tile
`Unit` updates on `move()` and advance positions client-side.
- Emit motion plans from executions:
- `TradeShipExecution`: record/update grid motion plans and `touch()`
when changing target after capture.
- `TransportShipExecution`: record initial plan and update it when
destination changes.
- `TrainExecution`: record a train plan on init (engine + cars).
- Client: apply motion plans in `GameView` and ensure `UnitLayer`
updates sprites for motion-planned units even when no `Unit` updates
arrived.
## 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
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves#2822
## Description:
Adds an attack ratio keybind increment setting with a new dropdown UI,
wires keybinds to use the configured step, updates the attack ratio
adjustment logic, and makes the select reflect stored settings.
<img width="806" height="165" alt="スクリーンショット 2026-01-12 9 11 12"
src="https://github.com/user-attachments/assets/c6eaa96d-e147-4927-b3ed-964e832ecc36"
/>
## 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
---------
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves#1139
## Description:
New version of the #2789 PR that is cleaner after changes made to old
pathfinding logic.
Adds logic to troop transport retreat behaviour which retreats a
transport to the closest owned tile instead of the source. Now if no
shores are detected (you lost all your shoreline while the transport was
out) we handle the return case same as if the original source was no
longer your territory.
<img width="2541" height="1593" alt="image"
src="https://github.com/user-attachments/assets/4d2ff5e7-d10d-40f4-80e0-9f029cff61a2"
/>
## Video example from previous PR (works the exact same way in this PR):
https://github.com/user-attachments/assets/e43a3b10-e8b0-4f23-87f3-2dc4739de880
## 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
## Description:
Skin trials has been a failure, very low fill rate and cause a major
drop in sales.
reverts
https://github.com/openfrontio/OpenFrontIO/commit/97d0a05d58e926e3de4ba46d8dd14a04d60d6698
## 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
## Description:
Preparation for nuke wars, for v30.
Next PR will be adding the nuke wars modifier for public games, but
Wonders https://github.com/openfrontio/OpenFrontIO/pull/3224 needs to be
merged first to avoid merge conflicts.
### 1. Disable boats setting
It's possible to disable `UnitType.TransportShip` now. Because they are
not needed in nuke wars and can even be annoying.
<img width="720" height="320" alt="image"
src="https://github.com/user-attachments/assets/661bc10d-b204-4b4f-b876-ee7c9b92de8c"
/>
### 2. Team spawn zones for random spawn
Maps can have `teamGameSpawnAreas` in their json file now.
Spawn areas are currently active if
- a supported map is chosen (Baikal Nuke Wars or Four Islands)
- a supported team size is chosen (2 teams on Baikal Nuke Wars or 2/4
teams on Four Islands)
- random spawn is enabled
## 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
## Description:
updates the Performance Overlay to be more usable
(draggable/resizable/scrollable), adds tick-level metrics (TPS +
per-layer tick timings), and reduces overhead when the overlay is
hidden.
### UI/UX
- Overlay layout updated to a fixed, pixel-positioned panel (default
near top-left) with a dedicated drag handle.
- Overlay is touch-draggable (pointer events) and remains usable on
small viewports via internal scrolling.
- Overlay width is resizable with a right-edge handle; width is clamped
to viewport bounds.
- Render/tick layer breakdown sections are collapsible, with headers and
“last tick” summaries.
### New metrics
- Adds TPS reporting:
- Current TPS (ticks in the last 1s).
- Average TPS over the last ~60s, computed using elapsed time so it’s
accurate before a full 60s passes.
- Adds per-layer tick profiling (“Tick Layers”) alongside render
profiling (“Render Layers”).
- Adds “render-per-tick” metrics so render-layer costs can be understood
per simulation tick (frames + per-layer totals).
### Performance / overhead
- Avoids profiling overhead when the overlay is hidden:
- `GameRenderer` only calls `FrameProfiler.clear()/consume()` and
per-layer `start/end` when profiling is enabled.
- Tick-layer duration tracking is only collected when profiling is
enabled.
### Settings plumbing
- `UserSettings` now dispatches a `user-settings-changed` `CustomEvent`
on `set()` / `setFloat()`.
- The overlay listens for `settings.performanceOverlay` changes so
visibility stays in sync even when toggled outside the overlay.
## Implementation notes (by file)
- `src/client/graphics/layers/PerformanceOverlay.ts`
- Adds TPS tracking using a timestamp ring + moving heads (1s / 60s).
- Adds UI state for collapsibles, drag + resize pointer tracking, and
new breakdown models:
- Render layers: EMA avg/max + per-tick render aggregation.
- Tick layers: EMA avg/max + last-tick durations.
- Copy-to-clipboard snapshot now includes TPS, tick layers, and
render-per-tick last-tick details.
- `src/client/graphics/GameRenderer.ts`
- Gates render-layer profiling behind `FrameProfiler.isEnabled()`.
- Accumulates per-render-layer timings across frames and publishes them
once per tick via `updateRenderPerTickMetrics(...)`.
- Measures tick-layer durations (per layer `tick()` call) and publishes
them via `updateTickLayerMetrics(...)`.
- `src/core/game/UserSettings.ts`
- Adds `emitChange(key, value)` to dispatch `user-settings-changed` to
`globalThis` (best-effort).
- `resources/lang/en.json`
- Adds/updates `performance_overlay.*` strings for TPS and the new
render/tick layer sections.
## 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
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Description:
Array.from was performed on this.player.alliances(), which already
returns an array. Also it was saved in a const which isn't strictly
necessary, same goes for the array in the loop below it.
## 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
## Description:
Please merge for v30 if possible.
Use .find instead of .filter for tradeShipSpawn since we're only looking
for the first (if any) port found at the given tile anyway.
Also just return targetTile instead of getting porr.tile() because
targetTile is tile we found the port on.
Also use no intermediate const, just return right away based on outcome
of units.find.
Found when working on PR #3220. But tradeShipSpawn is out of 3220's
scope since it won't be called by playerImpl buildableUnits() anymore,
it should and will be only ever used by TradeShipExecution via
playerImpl 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
## Description:
Can we maintain a one-minute game creation rate with three times as many
lobbies?
I don’t think so - we should allow more time for players to join.
It still shouldn’t bore players, since they can also join the special
mix lobby. That lobby includes both FFA and team games and fills more
quickly due to the higher frequency of compact-map matches (the Wonder
PR https://github.com/openfrontio/OpenFrontIO/pull/3224 needs to be
merged for that).
## 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
## Description:
Only need find() instead of filter() in orderRetreat and executeRetreat,
since we just need the first hit. Small perf win.
## 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
## Description:
Because spawning is prohibited on tiles that have an owner, this created
a problem when a person tried to spawn near the center of their previous
spawn. This was resolved by relinquishing all the tiles conquered by the
previous spawn.
## 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:
nikolaj_mykola
## Description:
Fixing
https://discord.com/channels/1359946986937258015/1360078040222142564/1463898386854973642
Now, if not all tiles on the spawn circle can be owned, the algorithm
tries to select another random spawn tile.
## 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:
nikolaj_mykola
---------
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
## Description:
Introduces a dedicated `factoryValue()` scoring function for AI factory
placement, replacing the generic `interiorStructureValue()` previously
shared with cities and missile silos.
Scoring criteria:
- High elevation and spacing from other factories (unchanged from
city/silo logic)
- Rail connectivity: bonus per distinct rail cluster reachable within
`trainStationMaxRange`, weighted by trade gold potential — allied
clusters score highest (1.0), team/neutral clusters score ~0.71, own
clusters ~0.29 (based on `config.tradeGold()` values). Based on
difficulty
- Cluster deduplication: connecting to the same cluster multiple times
does not inflate the score
- Embargoed and bot neighbors are excluded; all other non-embargoed
neighbors are included
The result is that the AI tends to place factories where they can bridge
separate rail networks or connect to high-value trade partners, rather
than deep in its own interior.
### EDIT
Added a dedicated `cityValue()` scoring function that takes into account
the connectivity score. This allows placement of cities in a
"factory-aware" way, while also enforcing spreading structures (we want
the network to grow, not a cluster of cities and factories all
together).
## 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:
deshack_82603
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Description:
The master set lobby start times on creation, which caused an issue if
the previous lobby filled up and started before its timer ran out, the
next lobby would have its timer set too far back. For example, if lobby
time is 60 seconds, and the first lobby fills up after 10s, the
subsequent lobby would have its timer set for 110 seconds (60+50).
Instead we have the master set the lobby start time only when it is next
up in rotation. So all lobbies behind it don't have a start time,
because we don't actually know what it should be.
## 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
## Description:
### SAM Overwhelming (`NationNukeBehavior.ts`)
On Impossible difficulty, nations can now destroy enemy SAMs by
overwhelming them with coordinated atom bomb salvos. When no good nuke
target is found (all trajectories intercepted by SAMs), the nations
will:
- Identify the easiest enemy SAM to destroy (lowest level first)
- Calculate the total interception capacity of all covering SAMs and
send enough bombs to overwhelm them (+1 extra per 5 needed to account
for enemy building more SAMs during flight)
- Plan launches in NukeExecution's Manhattan-distance silo order,
tracking which silos have interceptable trajectories (wasted bombs)
- Use a sliding window over parabolic flight times to find the best
cluster of bombs that can arrive within half the SAM cooldown window
- Compute per-bomb wait ticks to synchronize arrivals from silos at
different distances
- Skip launching if a salvo is already in flight
- Upgrade the best SAM-protected silo when silo capacity is
insufficient; wait and save gold when only gold is lacking
https://github.com/user-attachments/assets/14fa592f-2902-4604-8e37-1eba2b2f0b85
### 2-Player Endgame Handling (`NationNukeBehavior.ts`)
- On Hard/Impossible with only 2 players remaining,
`findBestNukeTarget()` directly targets the other player (bypasses all
priority logic)
- `getPerceivedNukeCost()` returns actual cost (no MIRV saving
inflation) when only 2 players are left
### SAM Build Rate (`NationStructureBehavior.ts`)
- Reduced SAM perceived cost increase per owned from 1.0 to 0.5, so
nations build more SAMs
### Island Attack Variety (`AiAttackBehavior.ts`)
- `findNearestIslandEnemy()` now collects up to 2 reachable candidates
and has a 33% chance to pick the second-nearest, adding variety to boat
attack targeting
## 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
## Description
Reduces CPU + GC pressure from tile update serialization.
**What changed**
- Switched `packedTileUpdates` from `BigUint64Array` (BigInt packing) to
`Uint32Array` `[tileRef, state]` pairs, updating `GameView` ingestion.
- Updated tile state to use `GameMap.tileState(tile)` and
`GameMap.updateTile(tile, state)`.
- Removed per-tile `GameUpdateType.Tile` wrapper allocations by
recording raw `(tile, state)` pairs in `GameImpl` and draining them via
`drainPackedTileUpdates()` in `GameRunner`.
**Why it’s faster**
- Avoids BigInt and pack/unpack.
- Avoids per-tile object allocations.
**Compatibility**
- Wire format change: `packedTileUpdates` is now `Uint32Array` pairs
instead of `BigUint64Array`.
## 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
## Description:
Noticed this in two singleplayer games:
When a nation's nuke destroys its own transport/trade ship in the blast
radius, `NationWarshipBehavior` incorrectly tries to retaliate against
itself, calling `updateRelation(self)` which throws (GameRunner tick
error).
Added a self-check in `maybeRetaliateWithWarship` to skip retaliation
when the destroyer is the player itself.
## 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
## Description:
The following PR replaces the (disabled) alliance request button with an
alliance extension/renewal button when the alliance with the target
player is expiring.
Agreeing to renewal via radial menu also hides the message in the
EventsDisplay.
<img width="369" height="364" alt="image"
src="https://github.com/user-attachments/assets/d8040f5c-ad7b-47d0-852f-925ecbf273a8"
/>
https://github.com/user-attachments/assets/aa589edf-6505-46bf-88a3-aa4c2df9137f
Icon size adjusted:
<img width="294" height="252" alt="image"
src="https://github.com/user-attachments/assets/7ca63500-b1fb-427b-965c-cf121a5213da"
/>
## 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:
deshack_82603
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)
## Description:
## PR Title
perf(core): reduce hot-path allocations & safe optimizations
This PR brings in a set of allocation-focused optimizations in core hot
paths
### Scope
- `src/core/execution/NukeExecution.ts`
- `src/core/execution/WarshipExecution.ts`
- `src/core/game/UnitGrid.ts`
- `src/core/game/PlayerImpl.ts`
- `src/core/configuration/DefaultConfig.ts`
- `src/core/execution/SAMLauncherExecution.ts`
### What Changed
- `NukeExecution.detonate`: reduced call overhead/allocations by caching
`mg`/`config`, avoiding repeated lookups, and using allocation-free
loops (no `forEach` closures) in the diminishing-effect pass.
- `WarshipExecution.findTargetUnit`: replaced allocate+sort flow with
single-pass best-target selection.
- `UnitGrid.nearbyUnits`: reduced call overhead and allocations via
single-type fast path and cached query coordinates.
- `PlayerImpl.units`: added fast paths for common small-arity type
queries (1-3 unit types).
- `DefaultConfig.unitInfo`: cached `UnitInfo` objects per `UnitType` to
avoid repeated object/closure creation.
- `SAMLauncherExecution` targeting: removed sort churn and streamlined
target selection with single-pass hydrogen prioritization.
### Rebase
- One conflict was resolved in `NukeExecution.detonate` by keeping
`main`'s diminishing-effect-per-impacted-tile behavior, while retaining
the allocation-reduction refactors.
## 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
## Description:
Some literals were present that could/should have been enums. Replaced
them.
For Util.ts > createRandomName, also changed type of parameter
playerType from string to PlayerType. All callers already send it this
type.
## 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
## Description:
- Build packedTileUpdates directly into a BigUint64Array (avoid
intermediate JS array + copy).
- Transfer packedTileUpdates.buffer in worker postMessage to avoid
structured-clone.
- Avoids large short-lived allocations during tile-update (lower peak
heap and GC pressure).
- Prevents duplicating buffers across thread boundaries when the main
thread is behind. (one buffer per queued update, not two)
**Notes**
- after postMessage(..., [packedTileUpdates.buffer]) transfer the
worker’s packedTileUpdates becomes unusable (its .buffer is detached).
- [ ] 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
## Description:
Update UI
check https://homepageupdate.openfront.dev/
Improved mobile UI (now fills whole screen for all modals) e.g.:
<img width="432" height="852" alt="image"
src="https://github.com/user-attachments/assets/56de40af-4137-4c57-96b7-3910c9a665b8"
/>
Converted PublicLobby to be "GameModeSelector" to get a nicer 4x4 grid
div, where <GameModeSelector> now handles all the username validation
now (removed redundant code from modals such as matchmaking) also fixed
a bug where someone could have "[XX] X" as thier username (when the
minimum should be 3 chars for their name)
Now visually displays the 3 lobbies ffa/team/special (which is a
continuation from the work done in: #3196 )
<img width="818" height="563" alt="image"
src="https://github.com/user-attachments/assets/a15cd31b-6061-4fb8-83ee-ffde6225cfa7"
/>
updated the background:
<img width="1919" height="807" alt="image"
src="https://github.com/user-attachments/assets/358a7434-51b8-4540-baf2-d1be05053c44"
/>
slightly updated the glassy-look to be less glassy:
<img width="825" height="729" alt="image"
src="https://github.com/user-attachments/assets/1801871b-bbf8-43db-ac53-489337ae80a5"
/>
## 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
## Description:
PR 7/x in effort to break up PR #3220. Follows on already merged #3238.
Please see if these can be merged for v30.
-**RadialMenuElements**:
- _getAllEnabledUnits_: use camelCase instead of PascalCase so change
Units into units.
- _getAllEnabledUnits_: use StructureTypes to loop through instead of
having 6 individual lines of code to check if a structure is enabled.
StructureTypes contains and will keep containing the same structures as
those that are checked here. PR 3220 will later on also replace the
individual lines for the attack type units into a loop with a newly
introduced Types array, in the same way as we do in this PR with
StructureTypes.
- _getAllEnabledUnits_: rename the long named const
addStructureIfEnabled to just addIfEnabled, which is clear enough from
the context it is used in.
-**PlayerImpl**
- _buildableUnits_: removed unnecessary "as BuildableUnit" after the
in-loop return; the function itself already says it returns
BuildableUnit[].
## 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
## Description:
When a nuke detonates, the explosion is looping over each tiles. Among
other things, it filters the owner units to find the `TransportShips`
units.
This operation is cpu-intensive and repeated over each tiles can make
the tick noticably slower.
On this screenshot the detonation function took 100ms:
<img width="1617" height="488" alt="image"
src="https://github.com/user-attachments/assets/07009b18-4342-4caf-9e82-9ae5147b63f8"
/>
<img width="1645" height="375" alt="image"
src="https://github.com/user-attachments/assets/fe9ead87-550a-4166-96ab-092d0c08be82"
/>
I suspect it led to a few frame freeze when I MIRVed too.
Suggestion: loop over the impacted players rather than the tiles.
With this suggestion, the same nuke takes between 4ms to 20ms:
<img width="989" height="365" alt="image"
src="https://github.com/user-attachments/assets/25c0faf0-cc34-41b7-8091-b14bde6db595"
/>
However this changes the death formula used, as they were repeated over
each tiles with ever-smaller values, and with this change the operation
is done all-at-once. This will result in a different outcome.
In my opinion the performance gain is seductive enough to maybe tweak
the formula to make it work with this revised strategy.
What do you think?
## 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:
IngloriousTom
## Description:
PR 6/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220. Follows on already
merged https://github.com/openfrontio/OpenFrontIO/pull/3237.
Please see if these can be merged for v30.
- **PlayerImpl**: validStructureSpawnTiles did a filter on unit types to
get isTerroritoryBound units, on every call again. It read this from
unit info in DefaultConfig. While having it centrally in DefaultConfig
unitInfo is good for maintainability, other code uses hardcoded
StructureTypes and isisStructureType from Game.ts. Which has the same
purpose and thus contains the same unit types. StructureTypes and
isisStructureType do need manual maintainance outside of DefaultConfig.
And are more bug prone/less type safe. But, using them gives more speed
compared to getting these unit types out of DefaultConfig unitInfo
centrally with some cached function in GameImpl for example (tested with
buildableUnits and MIRVPerf.ts). So I went with StructureTypes in
validStructureSpawnTiles too.
- **PlayerExecution**: now validStructureSpawnTiles no longer needs
isTerritoryBound (see the point above), PlayerExecution is the last
place where it was used. Replaced it for isStructureType here too (since
it has the same meaning and outcome).
- **Game.ts** and **DefaultConfig** unitInfo: remove the now unused
_territoryBound_. As it was only used in validStructureSpawnTiles and
PlayerExecution and has been replaced in both (see the two points
above).
## 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
## Description:
PR 5/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220. Follows on already
merged https://github.com/openfrontio/OpenFrontIO/pull/3236.
Please see if these can be merged for v30.
**NationStructureBehavior**:
- maybeSpawnStructure: cache this.game to be used twice.
- maybeSpawnStructure: instead of hardcoded ruling out Defense Post for
upgrade check, check dynamically if type is upgradable. That way if
defense posts ever do become upgradable, we don't run into a bug right
away.
- maybeUpgradeStructure: removed canUpgradeUnit check. Since it already
checked this right before in findBestStructureToUpgrade, so only
upgradable units are returned. And canUpgradeUnit is also checked right
after in UpgradeStructureExecution. So we're going from 3 times to 2
times canUpgradeUnit, small perf win too.
- findBestStructureToUpgrade: cache this.game to be used thrice.
- shouldBuildStructure: cache this.game.config() to be used twice.
- getTotalStructureDensity: this.player.units can handle an array of
unit types to count. Input StructureTypes like this so we don't need a
loop and count, and only have to get an array length once.
getTotalStructureDensity needs to ignore unit levels so we can't make
use of other pre-defined functions in PlayerImpl (which were created to
avoid array length calls), but at least this saves a few.
## 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
## Description:
PR 4/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220. Follows on already
merged https://github.com/openfrontio/OpenFrontIO/pull/3235.
Please see if these can be merged for v30.
- **Game**/**GameImpl**/**GameView**: nearbyUnits required "UnitType |
UnitType[]" for tiles, but calls UnitGrid nearbyUnits which requires
"UnitType | readonly UnitType[]". Made the requirement the same for
Game/GameImpl/GameView nearbyUnits. This way, we don't have make a
shallow copy of the StructureTypes array everytime we want to send it as
an argument. Other callers than listNukeBreakAlliance in Util.ts are
unaffected.
- **Util.ts**: listNukeBreakAlliance needs no shallow copy of
StructureTypes anymore as argument for NearbyUnits
## 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
## Description:
Based on [this suggestion on
Discord](https://discord.com/channels/1284581928254701718/1447110257196138577)
and feedback gathered in [this
thread](https://discord.com/channels/1359946986937258015/1469598906173227184).
Supersedes #3143
This PR introduces "ghost railways": when you are going to place a city
or port, previews railway connections that will be made when actually
building the structure.
Ghost railways are skipped if the structure is going to be snapped to
existing railways (as in railway snapping functionality introduced in
#3156 ).
### Video
https://github.com/user-attachments/assets/ff8cf325-6501-4df8-801d-c8ae3ced3d0e
### Ghost rails color revisited
black with 40% opacity
<img width="695" height="430" alt="image"
src="https://github.com/user-attachments/assets/272efbcc-4185-426a-921c-7fae61f6c462"
/>
## 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:
deshack_82603
## Description:
PR 1/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220.
Please see if these can be merged for v30.
- **BuildMenu**: remove one redundant comment about replacing an icon
(which has been done long ago already). And fix typo in one other
comment.
- **Worker.worker**: correct some existing error messages.
## 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
## Description:
PR 2/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220.
Removes unused code and properties.
- **Game.ts** and **DefaultConfig** unitInfo: removed
_canBuildTrainStation_ and _expirimental_ properties, as they weren't
used anywhere anymore.
- **PlayerActionHandler**: remove unused getPlayerActions, the only
potential caller MainRadialMenu already just calls myPlayer.actions via
GameView directly.
- **StructureIconsLayer**: remove unused PlayerActions
## 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
## Description:
Now that pathfinding is much more efficient with hpa*, we can add more
trade ships.
This PR does the following:
1. No gold bonus for additional ports, keeps the meta simple
2. cut the gold per trade ship roughly in half.
3. Use a "pity bonus", the more times a port has failed to spawn a
tradeship, the higher the likelyhood it will spawn one
4. Increase the sigmoid so the mid-point is 200, with a half life of 50.
In tests about ~400 trade ships max.
It's pretty difficult to balance on singleplayer so I'm sure the values
will be adjusted after playtests.
## 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
## Description:
Bug (before): In multi-nuke scenarios (e.g., 6 incoming nukes), SAMs,
when stacked, could behave like they only engage one nuke at a time,
letting other nukes slip through and wipe the SAM stack. This was most
noticeable when nukes entered from certain angles/entry sides (from what
I’ve noticed, it happens more often in the 2 o’clock to 6 o’clock
direction).
<img width="734" height="743" alt="asadasada"
src="https://github.com/user-attachments/assets/7ca6c9ef-b2b4-47ea-bed2-249a84c8f5ed"
/>
What was fixed:
1- Removed permanent "unreachable" caching: When a nuke was out of SAM
range on first evaluation, it was cached as null (permanently
unreachable) and never re-evaluated. Since nukes move each tick, they
could later enter interception range but would be ignored. The fix
removes this permanent cache, so nukes are re-evaluated every tick.
2 - Moved targetedBySAM filter into the targeting system: The
targetedBySAM() check was at the launch decision, if the
highest-priority nuke was already claimed by another SAM, the launcher
skipped firing entirely instead of falling back to the next best target.
The fix moves the filter inside getSingleTarget() so claimed nukes are
excluded before ranking.
3 - Cleared targetedBySAM flag on SAM missile abort: When a SAM missile
aborted (for example target became allied), the targetedBySAM flag was
never cleared. This permanently prevented all other SAMs from targeting
that nuke. The fix calls setTargetedBySAM(false) on abort so the nuke
becomes available for re-targeting.
**Videos:**
I uploaded a before/after clip showing SAM performance intercepting 6
nukes, before the fix, nukes could break through, but after the fix,
SAMs consistently intercept as expected.
Before:
https://github.com/user-attachments/assets/d5a85354-f35c-4aca-82f8-902f5966312b
after:
https://github.com/user-attachments/assets/54074c09-fbdf-44d5-a88c-b1d54b20fee2
- Deployed for further testing
## 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:
abodcraft1
---------
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
## Description:
This PR fixes (at least partially) the attack loss imbalance. Would
recommend adjusting the weighting towards 100% in the long run and then
removing redundant code. I haven't tested it at 100% yet, but it might
actually be fine to change it now. I don't notice any substantial
differences in how the game feels at 50%.
## 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:
1brucben
## Description:
Fixes closed PR and adds the new “Alps” map and register it in the
game/map playlist. With 7 geographically accurate nations. Pure large
land map for land warfare only.
<img width="2564" height="1400" alt="Untitled"
src="https://github.com/user-attachments/assets/2c51fad3-7345-46a8-9256-2874d1c4211b"
/>
<img width="2000" height="1837"
alt="551068953-fe4445cd-c0b2-4a5a-b652-1e480e38cdb6"
src="https://github.com/user-attachments/assets/1c466262-e20b-46bc-b782-944cbf6ee62a"
/>

## 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:
TsProphet
## Description:
As far as I can remember, in v28 the spawn immunity applied to both
humans and nations.
With the configurable spawn immunity (added for v29) the spawn immunity
no longer applies to nations... Because its called PVP immunity now.
So right now it's possible to spawnkill nations. This is a big problem
for the 5M gold modifier games... And you can "cheat" in singleplayer.
This PR changes two things:
- Nations always have 5 seconds spawn immunity now, no matter whats
configured for the PVP immunity
- Nations attack TerraNullius earlier (Otherwise the easy nations would
sometimes do their first attack after the 5 seconds are over, spawnkills
would still be possible)
## 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: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
## Description:
- `AiAttackBehavior`: Because bots delete stolen structures now, nations
prioritize attacking bots with structures
- `NationMIRVBehavior`: Nations no longer MIRV enemies who already got
MIRVed in the last 30 seconds. Some humans complained about getting
double-MIRVed by nations. And in games with very high starting gold, ALL
nations MIRVed the same player (stop steamroll logic).
- `NationAllianceBehavior`: Fixes a comparison logic bug (Thanks to
Deshack)
- `NationNukeBehavior.ts`: Little atom bomb perceived cost balance
change
- `MIRVExecution`: To make sure the MIRVing nations are attacking the
MIRVed nations (even if they don't share a border), the relation gets
updated in both directions now.
- `SinglePlayerModal` & `HostLobbyModal`: Update the default difficulty
to "Medium" (to synchronize the defaults with the public game default)
## 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
Resolves#3154
## Description:
#2716 introduced nuke cancellation logic on alliance acceptance via
`AllianceRequestReplyExecution`. The radial menu action, though, calls
`AllianceRequestExecution` instead, which accepts the alliance if a
request has already been made by the other player.
This PR moves the nuke cancellation logic to `GameImpl`, hooking into
the `acceptAllianceRequest` method, therefore accounting for every
alliance acceptance, regardless of the specific action that brought to
that.
## 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:
deshack_82603