Commit Graph

1382 Commits

Author SHA1 Message Date
FloPinguin eb6b2a9948 Rebalance nation difficulty 📊 More oriented towards beginners now (#3184)
## Description:

**3 problems:**

* Nation difficulty steps in `DefaultConfig` don't look good (max
troops: 0.5 → 1 → 1.125 → 1.25).
* We previously reduced the difficulty of easy nations for singleplayer,
but now they are too easy for public FFAs.
* In Discord we discussed HvN difficulty and concluded that a 50% human
win rate (my previous target) is too low; nations should be easier.

This PR addresses all of them:

* Difficulty steps in `DefaultConfig` are now cleaner (max troops: 0.5 →
0.75 → 1 → 1.25).
* Nation difficulty in public-game FFAs is restored to *medium* (0.75
max troops - still weaker than humans, but not too weak).
* HvN difficulty (medium) is now easier.

Regarding singleplayer:
These rebalances make nation difficulties more beginner-friendly, while
experts still have their challenge at *Impossible*.


## 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
2026-02-11 14:42:26 -08:00
FloPinguin c44130cf32 New map! "Traders Dream" 🏝️ (#3177)
## Description:

A redditor posted this, got quite a few upvotes

<img width="753" height="667" alt="image"
src="https://github.com/user-attachments/assets/9cd6664f-6afa-428a-b85e-4c335dbe1699"
/>


https://www.reddit.com/r/Openfront/comments/1qlrqro/these_maps_are_so_fun_i_want_more/

So I thought why not make another one, this time a bit focused on
traders (tiny islands to trademaxx)
And two big islands (about 50% of the map land tiles). So its big
islands vs small islands.

<img width="1098" height="959" alt="Screenshot 2026-02-10 231024"
src="https://github.com/user-attachments/assets/32368223-bef4-4ba1-a203-29d9afd5b762"
/>

13 nations :)
2200 × 1920

## 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
2026-02-10 16:33:09 -08:00
Evan 900cc89067 Better username censoring (#3122)
## Description:

Many inapropriate names bypass the current filter. This PR does the
following:

1. Moves name censoring to server side so inappropriate names are
scrubbed before being sent to the client
2. Requests a list of profane words from the api, this allows us to
quickly add new profane words in the admin panel without having to
redeploy.

## 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
2026-02-09 21:05:59 -08:00
FloPinguin f7da20ddfd Nation build order improvements + Nation structure upgrading 🏠 (#3152)
Resolves #2997

## Description:

### New stuff

- Nations can upgrade structures now. They do it if they have too many
structures compared to their territory size
- They prefer to upgrade stuff thats protected by SAMs (based on
difficulty)
- Updated the build order, it also depends a bit on the difficulty now
(easy nations build less SAMs)
- Nations can handle extreme amounts of gold now. 500M starting gold? no
problem. Previously they only built cities
- They stop saving up for MIRV if they can afford it (in some old Enzo
"impossible difficulty experiment" videos you could see nations with
like 300M gold...)
- The save-up-target changes when bombs / hydros / MIRVs are disabled
- Added many checks for disabled units. For example: Don't build SAMs
when missile silos are disabled, focus on factories when ports are
disabled
- Updated the `structureSpawnTileValue` method, SAM-placement depends a
bit on the difficulty now

### Refactor

- Moved all structure related nation code into
`NationStructureBehavior.ts`
- Split up the good old `structureSpawnTileValue` method to make it more
readable
- Cleaned up NationExecution a bit

### A screenshot

<img width="1681" height="755" alt="Screenshot 2026-02-08 001108"
src="https://github.com/user-attachments/assets/c9b3df01-41ca-4c68-b450-b20e7d7d910a"
/>

## 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
2026-02-09 16:18:13 -08:00
Ryan e93cab3392 sam missile immunity (#3167)
## Description:

added sam missile immunity (was missing from the immunity list)

## 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
2026-02-09 16:09:48 -08:00
DevelopingTom c6c793f6b3 Highlight hovering railroad (#3156)
## Description:


![rail_snap](https://github.com/user-attachments/assets/1dc66dc8-5df8-4826-8a8e-521d72a1f8aa)

The `RailroadLayer` simply displays tiles as instructed by the core
worker. While it's practical for the layer to only care about the tiles,
it also means it has no understanding of railroads as entities (their
paths, connections, or identities).

It also means that the core worker is responsible for rendering tasks
such as tile orientation and construction animation, which is not
expected.

To support ID-based events and better separation of concerns, the
rendering layer needs to be aware of complete railroads. With this
change, the core worker can send the tiles once and subsequently
reference railroads only by ID for all other events.

#### Changes:
- `RailroadLayer` now stores full railroad data instead of only
individual tiles
- `RailroadLayer` is responsible for animating newly built railroads
- Add a new `RailroadSnapUpdate` sent when a new structure is built over
an existing railroad. This event is used by `RailroadLayer` to keep
railroad ID in sync.

- When hovering over a railroad, the render worker is querying the core
worker about overlapping railroads.
Alternatively, RailroadLayer could compute overlaps itself now that it
has full railroad knowledge, but this logic would need to be duplicated
and kept in sync across workers. Keeping a single source of truth in the
core worker is preferred.


#### Edgecases:
- When a structure snaps over a railroad, the original railroad is split
into two new railroads. If the construction animation is still in
progress, instead of resuming the animation at the correct point on the
new railroads, all remaining tiles are rendered immediately
- Previously, `RailroadUpdate` handled both construction and
destruction. This no longer works with `RailroadSnapUpdate`, as event
ordering is now pretty important and IDs may be lost before they are
consumed.
To address this, RailroadUpdate is split in two:
`RailroadConstructionUpdate` and `RailroadDestructionUpdate`.


## 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:

IngloriousTom

---------

Co-authored-by: jrouillard <jon@rouillard.org>
2026-02-09 13:37:27 -08:00
Ryan 8dcc7cfb9a Fix client reconnection after page refresh (#3117)
## Description:

- Removed all code related to generating a client ID on the client. The
server now assigns the client ID and sends it to the client in lobby
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:

w.o.n
2026-02-09 01:10:11 +00:00
rubenperezrial 1ef0cf28a1 feat: improve team colors with LCH color space (#3146)
## Summary

Refactor `generateTeamColors()` to use LCH (Lightness-Chroma-Hue) color
space instead of HSL for perceptually uniform team color variations.

## Changes

- **`Colors.ts`**: Rewrite `generateTeamColors()` to use LCH color space
- Golden angle hue distribution clamped to ±12° to preserve team
identity
- Chroma oscillates ±10% around the base to add variety without washing
out
- Lightness alternates ±18 around the base to keep teammates
recognizable

## Why LCH?

LCH is a perceptually uniform color space, meaning equal numeric
differences correspond to equal perceived differences. This produces
team color variations that look more consistent and distinguishable
compared to HSL-based generation.

## Notes

- The "skip ally attack confirmation" feature that was previously in
this PR has been split into a separate PR as requested by @evanpelle.
2026-02-07 19:56:06 -08:00
Duwibi e97f4650b7 Readd Yenisei (#3145)
## Description:
This PR readds the Yenisei map, with it being about 3 times smaller than
before. I haven't updated the en.json file, as it still has the string
for the map, which was probably forgotten when the map got removed from
the repo.
## 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
Nikola123
2026-02-07 12:55:58 -08:00
VariableVince 0255a6e5a8 Fix: don't stop seperation of MIRV warheads after launching player died (#3134)
## Description:
Like Atom and H-bombs, MIRV warheads should still land after the
launching player died. PlayerExecution already makes sure to skip nukes
when deleting a dead player's units. But MIRVexecution separate()
creates NukeExecutions which check canBuild, which returns false for a
dead player.

Fix: Skip alive and cost check for MIRV warheads. canBuild is only
applicable when creating the MIRV itself, not at the seperation stage
mid-flight. There's no cost involved either.

Note: this bug has been there since MIRVs were added to the game. But
the bug wasn't noticed often in all that time. Still the fix may have
some impact on gameplay.

## 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

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-06 19:57:34 +00:00
scamiv 8cc6c2c2aa Perf spawn train (#3130)
## Description:

Train spawning hot-path optimization (trade destination selection)

## Summary
This PR reduces per-tick overhead in train spawning by removing
temporary allocations and reducing work in the
destination-selection path.

The change focuses on `Cluster` trade destination lookup and how
`TrainStationExecution` picks a destination.

## What changed
### 1) Maintain a “trade-capable” station subset per cluster
`src/core/game/TrainStation.ts`

- `Cluster` now maintains:
  - `stations`: all stations in the cluster (unchanged)
- `tradeStations`: maintained subset of stations that can act as trade
endpoints (`City` or `Port`)
- `tradeStations` is kept in sync in:
  - `addStation()`
  - `removeStation()`
  - `clear()`

Impact:
- Trade queries no longer scan every station in the cluster; they only
scan `tradeStations`.

### 2) Add cheap eligibility helpers
`src/core/game/TrainStation.ts`

- `hasAnyTradeDestination(player)`:
- Fast early-exit check: returns as soon as it finds any eligible trade
destination.
- `randomTradeDestination(player, random)`:
- Picks a random eligible trade destination directly without
materializing an intermediate `Set`.

### 3) Use reservoir sampling for single-pass random choice
`src/core/game/TrainStation.ts`

`Cluster.randomTradeDestination()` uses reservoir sampling:
- Iterates `tradeStations` once.
- Maintains a running count of eligible stations (`eligibleSeen`).
- Replaces the selected station with probability `1/eligibleSeen`.

Properties:
- Uniform selection among eligible stations.
- One pass instead of “count then pick by index” (two pass).
- Allocation-free.
- Returns `null` when no eligible destination exists.

### 4) Update train spawning to avoid temporary sets
`src/core/execution/TrainStationExecution.ts`

- Previously: `spawnTrain()` called `cluster.availableForTrade()` and
then `random.randFromSet(...)`.
  - This built a new `Set` on the hot path.
- Now:
  - Early-exit via `cluster.hasAnyTradeDestination(owner)`.
  - Destination via `cluster.randomTradeDestination(owner, random)`.

Net effect:
- Less per-tick work and no per-spawn temporary `Set` allocations.

## Why this helps
Train spawning happens frequently and can become a hot path in large
games / large rail clusters.
Avoiding repeated allocations and reducing work inside `tick()` helps
keep frame/update time predictable.

## notes
- Trade rules are unchanged (`tradeAvailable(player)` still gates
eligibility).
- Destination selection remains random-uniform over eligible
`City`/`Port` stations that satisfy `tradeAvailable(player)`.
- `TrainStationExecution` now avoids calling `spawnTrain()` entirely
when `spawnTrains` is falsy (it was already guarded inside).




## 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
2026-02-05 12:16:59 -08:00
scamiv b68de96c6e Perf clusters (#3127)
## Description:

This PR reduces server/client tick CPU spent on territory cluster
maintenance by:
- Cutting redundant work in `PlayerExecution.removeClusters()`
(largest-cluster bounding box reuse, fewer allocations in
`isSurrounded()` and `removeCluster()`).
- Making `calculateBoundingBox()` allocation-free per tile by switching
from `gm.cell(tile)` to `gm.x(tile)`/`gm.y(tile)` (it now only allocates
the two result `Cell`s, instead of 1 per tile).

## Commits
- `51de0a1b` core: reduce PlayerExecution cluster overhead
- `6d9d85c5` core: avoid Cell allocations in PlayerExecution
isSurrounded
- `346f6a8c` core(util): speed up calculateBoundingBox by avoiding Cell
allocations


## Notes
- This PR is intended to be behavior-preserving; changes are limited to
hot-path micro-optimizations.
- Follow-up opportunity: `calculateClusters`/flood-fill is now the top
hotspot; further wins likely come from reducing traversal work or
caching.


## 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
2026-02-05 10:07:17 -08:00
scamiv d40923fc18 perf(game): optimize border tile updates (#3124)
## Description

Optimize border-tile maintenance in `GameImpl` to reduce per-conquest
overhead.

Border tiles are updated whenever ownership changes; this PR trims
allocations and avoids unnecessary iteration in the hot path.

## Changes

- `src/core/game/GameImpl.ts`
- `updateBorders(tile)` no longer allocates an array of tiles; it
updates the changed tile and its 4-neighbors directly via
`forEachNeighbor`.
- `calcIsBorder(tile)` no longer calls `neighbors(tile)` / loops an
array; it checks the four cardinal neighbors via `x/y` bounds and
`ownerID`.

## Affected Functions

- `GameImpl.updateBorders(tile: TileRef)`
- `GameImpl.calcIsBorder(tile: TileRef): boolean`
- Call sites impacted by behavior/perf:
  - `GameImpl.conquer(owner, tile)`
  - `GameImpl.relinquish(tile)`

## 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
2026-02-04 20:19:50 -08:00
FloPinguin c2663944e5 Stop getting gold from conquering inactive players 🔧 (#3020)
## Description:

Maybe for v29. 

In the 5M starting gold modifier games you can conquer a inactive player
(spawned but didn't do anything) and get their 5M gold.
Huge unfair advantage.
I think that even without the starting gold modifier you should not get
the gold of inactive players because its unfair.

I identify inactive players (spawned but didn't do anything) by checking
the attack stats.
I added a translation for the displayMessage "Conquered {name}, received
{gold} gold". Why was that not translated?
I added a new message "Conquered {name} (Inactive player, received no
gold)".

## 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>
2026-02-04 22:12:30 +00:00
FloPinguin a8836f76d3 Reduce bot farming problem (#2895)
## Description:

Explanation of the bot farming strategy in the discord:
https://discord.com/channels/1359946986937258015/1359949371956789289/1460928540575928478
"the result is that a player can build unlimited factories for 125 000
gold discount, trade with themselves with each train being worth 50 000
gold. First the 25 000 for neutral trade and then another 25 000 when
the bot is harvested."
"If you have a minute and ally people around you it should be trivial to
get 10 of both cities and factories for 1.25mil"

It's debatable if we want to let people do that (close this PR) or see
it as an abusive mechanic.

Here is the fix, bots try to delete all structures now. You can simply
retake them to stop the deletion:


https://github.com/user-attachments/assets/ac1ca846-50bd-42fa-8e25-5ac25a6d627e

## 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>
2026-02-04 11:39:35 +00:00
FloPinguin 904425cab0 Prevent players from nuking their teammates structures 💥 (#3105)
## Description:

It is possible to hit your teammates while throwing a nuke onto water or
enemies.
This PR blocks the nuking entirely if you would hit a teammates
structure. Because they are valuable.
Feature requested by Wonder :)


https://github.com/user-attachments/assets/448a3444-cc3d-4e76-acaf-595decab1634

## 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>
2026-02-04 11:38:49 +00:00
Evan 294a1b4784 move lobby websockets to worker (#2974)
## Description:

Currently only the master process sends public lobby updates to clients.
This is not scalable since it could overload the master process.

In this PR, the master uses IPC to send public lobby info to all
workers. Then clients connect to a random worker to get public lobby
updates via websocket. This way clients never connect directly to the
master websocket.

The flow looks like this:

Every 100ms:
1. Master schedules a public game on a random worker if new games are
needed
2. Master broadcasts public lobby info to all workers (all public games
& num clients connected to each game)
3. Each worker responds to that update with the number of clients
connected to its own public games
4. Master then updates its public lobby state so it knows how many
clients are connected to each public game

## 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
2026-02-03 18:26:38 -08:00
Ryan 2baaebfef3 JoinLobbyModal for public and private lobbies (#3097)
## Description:

Replaced the src/client/JoinPrivateLobbyModal.ts with a new
src/client/JoinLobbyModal.ts which handles both public + private
lobbies.

<img width="771" height="714" alt="image"
src="https://github.com/user-attachments/assets/7ac55d91-3f0c-4f99-b960-cea9e617538d"
/>

also made a "connecting" to the lobby 
<img width="772" height="708" alt="image"
src="https://github.com/user-attachments/assets/a2812462-c5f4-459a-b63a-49d93bb2a6a2"
/>


It also needed to be updated to address the issue with the modal using
both polling + websockets

## 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
2026-02-02 21:02:20 -08:00
Mattia Migliorini 6b80337aa9 Allow accepting alliance via radial menu during alliance request cooldown (#3092)
## Description:

Currently, when you send an alliance request to another player and it
gets rejected, but the same player sends you an alliance request back
during your alliance request cooldown, you cannot accept it via the
radial menu, you need to do that via the notification on the bottom
right.

This is not consistent with when you receive an alliance request outside
of the cooldown.

This PR checks for incoming alliance request for the same player before
checking the outgoing request cooldown, therefore allowing you to accept
incoming alliance requests via the radial menu button even during
cooldown.

## 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
2026-02-02 08:30:23 -08:00
FloPinguin 8ab9cef65b Various little nation improvements 🤖 (#3082)
## Description:

- Maybe greet nearby players with a 👋 emoji in the earlygame (make
nations feel a little bit more lively)
- Destroying a transport ship / capturing a trade ship of a nation now
updates the relation (mainly on higher difficulties)
- On hard difficulty: Only lift embargos if we have a friendly relation.
On impossible difficulty: Don't lift embargos
- Improve totalPlayers calculation and return value of
`hasTooManyAlliances` in `NationAllianceBehavior`

## 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
2026-02-01 20:12:19 -08:00
Ryan e4280c28e1 Add Ranked 1v1 Leaderboard (#3008)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)

## Description:

@wraith4081 's pr

updates the stats modal to show both 1v1 and clan 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:

w.o.n

---------

Co-authored-by: Wraith <54374743+wraith4081@users.noreply.github.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-01 14:58:54 -08:00
FloPinguin 19cdc3fe5f Make easy difficulty easier 📊 (#3072)
## Description:


https://www.reddit.com/r/Openfront/comments/1qquvdg/difficulty_scaling_in_single_player/
People seem to feel that easy is not easy enough, even after the various
nerfs in multiple places.
So let's touch DefaultConfig.

## 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
2026-01-30 13:45:00 -08:00
Sardi 13117251af Add new map “The Box” (#3054)
<img width="1670" height="200" alt="banner"
src="https://github.com/user-attachments/assets/b6561231-71dc-46dd-a521-453d4e97931f"
/>

## Description:

This proposal adds a new, completely flat map called "The Box," where
players will find themselves on a completely flat landmass and will only
be able to trade by land using trains and factories.

- Map size: 2048×2048 pixels
- Binaries generated with MapGenerator (map.bin, map4x.bin, map16x.bin)
- Integrated into arcade category and multiplayer playlist
- English and Spanish translations added
- 13 nations/bots with unique and thematic names:
 
> - **Middle Defender (located in the center)**
> - **King of the Corner (located in the top-left corner)**
> - Suspicious Ally
> - Punch Merchant
> - Nuke Thrower
> - Fullsender
> - Factory Builder
> - Front Manager
> - Box Fighter
> - Cage Liberator
> - Train Trader
> - Non-peaceful Bot
> - **Evan The Dev** (special mention)

All other bots are distributed to cover the map evenly.

<img width="838" height="835" alt="Screenshot 2026-01-28 at 17 39 45"
src="https://github.com/user-attachments/assets/4665088d-cb94-4db3-8602-8a22b63bfee2"
/>

## 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:

sardidefcon
2026-01-29 19:49:10 -08:00
evanpelle c54c73d157 Merge branch 'v29' 2026-01-28 16:06:32 -08:00
FloPinguin f20028a2a1 Show troop count of troop transport boats (#3056)
## Description:

Troop count display for naval invasion message:

<img width="398" height="131" alt="Screenshot 2026-01-28 204213"
src="https://github.com/user-attachments/assets/d7ccf2a8-9974-4f12-9901-e603426a8e56"
/>

On hover, PlayerInfoOverlay shows the troop count now:

<img width="504" height="99" alt="Screenshot 2026-01-28 202916"
src="https://github.com/user-attachments/assets/46f7685f-8c0e-4156-8d02-8a68dbcffde0"
/>


## 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
2026-01-28 12:39:30 -08:00
Evan cb3128f390 Better CrazyGames integration (#3055)
## Description:

Better integration with CrazyGames:

* Don't show login because accounts have not been integrated with
CrazyGames yet
* Integrate CG invite links & usernames
* Refactor match making logic to Matchmaking.ts
* Allow periods to support crazy game usernames
* Create a no-crazygames class that disabled elements when on crazygames

## 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
2026-01-28 11:29:27 -08:00
Ryan da4b8aa5e1 Spectate catchup (#3012)
## Description:


https://github.com/user-attachments/assets/dc118d5f-3b7f-4ccb-8579-5b0d8c73fe8e

Catchup mechanic for live games and changes replays to have a backlog
for more "max" speed

## 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
2026-01-27 15:15:35 -08:00
FloPinguin 7942990037 Crowded modifier 😄 (#3023)
## Description:

To increase variety a bit more I present: The "crowded" public game
modifier :)
It basically simulates a crazy youtuber lobby. Cramp a lot of players on
a small map 😄
I think its fun, exciting and you actually need skill to manage the
chaos.
5% of public games get this modifier, but because we remove the modifier
for big maps its more like 2.5% (should be something special)

| <img width="321" height="269" alt="Screenshot 2026-01-25 200427"
src="https://github.com/user-attachments/assets/7d2e90c1-e6bc-40a8-a19e-a0849612f472"
/> | <img width="317" height="264" alt="Screenshot 2026-01-25 200554"
src="https://github.com/user-attachments/assets/8b4bd5da-bed1-4743-a107-9ce07fce3040"
/> | <img width="317" height="244" alt="Screenshot 2026-01-25 200521"
src="https://github.com/user-attachments/assets/16293de3-0fc4-431f-8151-31b4e11040fe"
/> |
|---|---|---|






## 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
2026-01-27 00:29:52 +00:00
FloPinguin 3f95a45eaf Nations donate troops now 💀 (In team games) (#2984)
## Description:

For v29, balances the HvN winrate.

In team games, nations now donate troops to their weakest team members
(if they have no attack options available).
How often they donate depends on the difficulty.

This PR also has some other little fixes:
- For HvN games, always return true in `shouldAttack()` (make nations a
bit more aggressive).
- Early exit in `attackWithRandomBoat()` for performance
- Early exit in `findNearestIslandEnemy()` for performance AND to make
sure nations which are encircled by friends don't run into this method
(=> no donation happening!)

## 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
2026-01-25 20:40:13 -08:00
FloPinguin f7d3c2e0bc Nations donate troops now 💀 (In team games) (#2984)
## Description:

For v29, balances the HvN winrate.

In team games, nations now donate troops to their weakest team members
(if they have no attack options available).
How often they donate depends on the difficulty.

This PR also has some other little fixes:
- For HvN games, always return true in `shouldAttack()` (make nations a
bit more aggressive).
- Early exit in `attackWithRandomBoat()` for performance
- Early exit in `findNearestIslandEnemy()` for performance AND to make
sure nations which are encircled by friends don't run into this method
(=> no donation happening!)

## 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
2026-01-23 12:33:04 -08:00
DevelopingTom 9415162f51 Split railroads when placing overlapping structures (#3003)
## Description:
Players wrongly assume that building a structure over an existing
railroad will connect it properly. What actually happens is that the
structure will connect on the network with its own railroad, even if the
new railroads are overlapping over the existing network.

To address this issue, this PR splits the overlapping railroad into two
segments when a structure is built over it, and inserts the structure as
a new node in the rail graph. It does not alter the rail network
visually because the same railroad tiles are used for the new segments.

Railroad tiles are not stored directly in the map, they exist only as
edges in the rail graph, so looking for nearby rails would be terribly
inefficient. To address that, this PR introduces a new `RailSpatialGrid`
class which indexes rails on a 4×4 grid, allowing fast spatial queries.

Alternative considered: removing overlapping rails and rebuilding them
from the new structure. It would visually modify the rail network, which
may be unexpected for the player.

It's still missing a visual indicator so the player knows that the
structures has been connected properly.

### Line placement:


![snap_line](https://github.com/user-attachments/assets/f24ddd36-1594-4316-91ff-093a5cebd576)

### Multi-railroad overlap:


![snap_cross](https://github.com/user-attachments/assets/b2cc962e-6dce-4444-b689-7e04a09de603)


## 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:

IngloriousTom
2026-01-22 19:19:51 -08:00
FloPinguin e9e2f06d69 Humans are severely skill issued ⚠️ Change HvN difficulty to Medium (#2971)
## Description:

For v29

HvN winrate is between 10 and 15%, but should be around 50%.

1. Change HvN difficulty to Medium
2. Little balance change in `NationAllianceBehavior`

## 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
2026-01-20 10:44:06 -08:00
FloPinguin e5c91945af Humans are severely skill issued ⚠️ Change HvN difficulty to Medium (#2971)
## Description:

For v29

HvN winrate is between 10 and 15%, but should be around 50%.

1. Change HvN difficulty to Medium
2. Little balance change in `NationAllianceBehavior`

## 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
2026-01-20 10:43:48 -08:00
Arkadiusz Sygulski f6454963b2 Pathfinding refinements (#2959)
## Description:

### Short path for multi-source HPA*

Math was not mathing, increased the bounds to 260x260, it is a bit
slower but should work better. The short path was breaking when player
owned a lot of shores. This is because the bounding box of tiles with
less than 120 distance + 10 padding could be as big as 260x260 and the
optimized array was set to 140x140. I made mistake of calculating it as
`2 * (60 + 10)` instead of `2 * (120 + 10)`.

### LoS path refinement

Previously, we ran 2 passes of LoS smoothing on the path. However, since
we are effectively tracing the same path, the line of sight is
essentially the same. This PR makes second line of sight stop on water
tiles with magnitude `n + 1` compared to first path. Practically, this
means it'll attempt LoS exactly 1 tile after previous corner. See
screenshot.

<img width="1299" height="1151" alt="image"
src="https://github.com/user-attachments/assets/726be236-1ff8-406c-896a-02902a762ab0"
/>

### SendBoatAttackIntentEvent

The flow of sending transport ships is currently strange. This PR makes
the flow more sane.

**Old flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client asks Worker for the best START tile to TARGET tile
- Worker answers `false`, since the tile is inland
- Client sends BoatAttackIntent with START=false and TARGET tiles set
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker re-computes best START to DESTINATION
- Worker sends boat from START to DESTINATION
```

**New flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client sends BoatAttackIntent with TARGET
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker computes START as the best tile to DESTINATION
- Worker sends boat from START to DESTINATION
```

## 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:

moleole
2026-01-19 19:28:45 -08:00
Arkadiusz Sygulski 18fb513326 Pathfinding refinements (#2959)
## Description:

### Short path for multi-source HPA*

Math was not mathing, increased the bounds to 260x260, it is a bit
slower but should work better. The short path was breaking when player
owned a lot of shores. This is because the bounding box of tiles with
less than 120 distance + 10 padding could be as big as 260x260 and the
optimized array was set to 140x140. I made mistake of calculating it as
`2 * (60 + 10)` instead of `2 * (120 + 10)`.

### LoS path refinement

Previously, we ran 2 passes of LoS smoothing on the path. However, since
we are effectively tracing the same path, the line of sight is
essentially the same. This PR makes second line of sight stop on water
tiles with magnitude `n + 1` compared to first path. Practically, this
means it'll attempt LoS exactly 1 tile after previous corner. See
screenshot.

<img width="1299" height="1151" alt="image"
src="https://github.com/user-attachments/assets/726be236-1ff8-406c-896a-02902a762ab0"
/>

### SendBoatAttackIntentEvent

The flow of sending transport ships is currently strange. This PR makes
the flow more sane.

**Old flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client asks Worker for the best START tile to TARGET tile
- Worker answers `false`, since the tile is inland
- Client sends BoatAttackIntent with START=false and TARGET tiles set
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker re-computes best START to DESTINATION
- Worker sends boat from START to DESTINATION
```

**New flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client sends BoatAttackIntent with TARGET
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker computes START as the best tile to DESTINATION
- Worker sends boat from START to DESTINATION
```

## 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:

moleole
2026-01-19 19:28:28 -08:00
evanpelle 697a346c86 Increase worker initailization timeout from 5=>20s to prevent worker timeout, add duration logging to some longer operations 2026-01-19 19:26:09 -08:00
FloPinguin 21a035cdb4 Fix random spawn (#2958)
## Description:

"You can pick your spawn in random spawn games in v29. You need to open
the menu and click on the attack button. That's it."

Thats the fix for this problem.
Radial menu no longer allows to attack (pick a spawn) while random spawn
is enabled.
And SpawnExecution got a check so you cannot send malicious intents.

## 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
2026-01-19 16:28:14 -08:00
FloPinguin f8156c550b Fix random spawn (#2958)
## Description:

"You can pick your spawn in random spawn games in v29. You need to open
the menu and click on the attack button. That's it."

Thats the fix for this problem.
Radial menu no longer allows to attack (pick a spawn) while random spawn
is enabled.
And SpawnExecution got a check so you cannot send malicious intents.

## 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
2026-01-19 23:49:10 +00:00
Arkadiusz Sygulski ac56fccd8e Fix warship pathfinding (#2955)
## Description:

As reported on Discord, warship could get stuck. This PR fixes the
issue.

## 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:

moleole
2026-01-19 09:16:47 -08:00
FloPinguin c71af0e602 Fix map name formatting for Baikal Nuke Wars 🔧 (#2922)
## Description:

Fixes this little i18n problem:

<img width="732" height="172" alt="Screenshot 2026-01-16 050833"
src="https://github.com/user-attachments/assets/65fe27a6-f77a-49d9-94a9-145b4e719a88"
/>

## 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>
2026-01-19 09:16:44 -08:00
Arkadiusz Sygulski 566113c4de Fix warship pathfinding (#2955)
## Description:

As reported on Discord, warship could get stuck. This PR fixes the
issue.

## 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:

moleole
2026-01-19 16:09:17 +00:00
FloPinguin a4a41ac9f4 Fix map name formatting for Baikal Nuke Wars 🔧 (#2922)
## Description:

Fixes this little i18n problem:

<img width="732" height="172" alt="Screenshot 2026-01-16 050833"
src="https://github.com/user-attachments/assets/65fe27a6-f77a-49d9-94a9-145b4e719a88"
/>

## 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>
2026-01-19 09:55:06 +00:00
bibizu 3dadfbd23d feat: Nuke trajectory prediction now accounts for alliance breakage. (#2912)
## Description:

Nuke trajectory prediction now will show interception with allied SAMs
if the alliance will break on nuke launch.

Code was also refactored to be shared a bit more. 

In addition, if an incoming alliance would break if accepted, the nuke
launch will break the alliance.

<img width="1199" height="1002" alt="nukepr"
src="https://github.com/user-attachments/assets/c31066d9-66cf-4eaa-be3c-e2fbcfe7965a"
/>

## 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:

bibizu
2026-01-19 04:56:43 +00:00
DevelopingTom f367ea1940 Record human/nation/bot conquests (#2949)
## Description:

Conquests are currently mixing all player types.

This is not ideal as people wonders why a 50 player game can lead to
hundred of kills.
Having separate records can also help with achievements and better
balancing.

This PR splits the conquests record into 3 categories: human, nations
and bots.

It is linked to this infra PR:
https://github.com/openfrontio/infra/pull/246

<img width="895" height="497" alt="image"
src="https://github.com/user-attachments/assets/66e49100-8114-4406-84ab-d9627355956d"
/>

While the recorded data make a distinction between bots/nations, it's
only displayed here as a single "bot" category.

## 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:

IngloriousTom
2026-01-18 20:51:12 -08:00
VariableVince 969b301aac Fix: Ally won't conquer last tiles of so-called dead defender (#2954)
## Description:

When a player is conquered (has less than 100 tiles left) their gold is
transfered to the conqueror. And after that the conqueror gets the last
tiles. But if some of those last tiles are not bordered by the
conqueror, they are given to their neighbour player. However that
neighbour player can be an ally. It is percieved as a bug if an ally
conquers/annexes tiles.

This PR fixes that by adding an isFriendly check to `handleDeadDefender`
in `AttackExecution`.

Now, there are already scenarios possible currently, where a player
survives being conquered. If they have some tiles on a small island for
example. Going from that, there should be no unexpected bugs following
this change. A player can be conquered twice in a game already in the
stats too.
https://discord.com/channels/1359946986937258015/1359946989046989063/1462595261204533248

Example of this happening in an Enzo vid (with his surprised reaction)
and explanation posted here:
https://discord.com/channels/1359946986937258015/1359946989046989063/1460483209308536925

## 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
2026-01-19 00:03:13 +00:00
WillTHomeGit be4cabdde9 fix (pathfinding): prioritize best connected water neighbor in ShoreCoercingTransformer (#2937)
## Description:

**Describe the PR.**

This PR improves how pathfinding finds a starting water tile when
launching a transport ship from a shore.

Previously, the code simply picked the first water neighbor it found.
This caused issues where, if a boat were traveling east, it might launch
out of a northern tile from a shore.

<img width="896" height="353" alt="image"
src="https://github.com/user-attachments/assets/69d83012-3397-43b3-8ab0-9ebde6ffea97"
/>

<img width="342" height="219" alt="image"
src="https://github.com/user-attachments/assets/a191f5cf-97da-4e34-a191-55ce14c794f0"
/>

The new logic checks all water neighbors and picks the "best" one by
counting how many water tiles surround it. This ensures transport ships
launch into the main body of water instead of suboptimal positions.

If two tiles have water neighbors with the same score, they are
tie-broken through a euclidean distance check.

## 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:

Scisyph

---------

Co-authored-by: WilliamT-byte <williamt2023@tamu.edu>
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-01-18 13:05:53 -08:00
Arkadiusz Sygulski b75df821cd Fix rail pathfinding (#2950)
## Description:

This PR resolves a crash related to rail pathfinding reported on
Discord.

```
git checkout c179249cdd
npm run dev:staging
Replay id: kEbHPSP3
```

## 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:

moleole

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-01-18 13:04:38 -08:00
Arkadiusz Sygulski 216e8ca29f Fix rail pathfinding (#2950)
## Description:

This PR resolves a crash related to rail pathfinding reported on
Discord.

```
git checkout c179249cdd
npm run dev:staging
Replay id: kEbHPSP3
```

## 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:

moleole

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-01-18 20:51:22 +00:00
WillTHomeGit c123adc0ef fix (pathfinding): prioritize best connected water neighbor in ShoreCoercingTransformer (#2937)
## Description:

**Describe the PR.**

This PR improves how pathfinding finds a starting water tile when
launching a transport ship from a shore.

Previously, the code simply picked the first water neighbor it found.
This caused issues where, if a boat were traveling east, it might launch
out of a northern tile from a shore.

<img width="896" height="353" alt="image"
src="https://github.com/user-attachments/assets/69d83012-3397-43b3-8ab0-9ebde6ffea97"
/>

<img width="342" height="219" alt="image"
src="https://github.com/user-attachments/assets/a191f5cf-97da-4e34-a191-55ce14c794f0"
/>

The new logic checks all water neighbors and picks the "best" one by
counting how many water tiles surround it. This ensures transport ships
launch into the main body of water instead of suboptimal positions.

If two tiles have water neighbors with the same score, they are
tie-broken through a euclidean distance check.

## 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:

Scisyph

---------

Co-authored-by: WilliamT-byte <williamt2023@tamu.edu>
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-01-18 15:19:55 +00:00
Arkadiusz Sygulski 2fcca8ee26 Pathfinding - optimize naval invasions (#2932)
# Pathfinding pt. 4

https://pf-pt-4.openfront.dev/

## Description:

Hello again! Pathfinding. It's fast, but inaccurate. This PR makes it
more accurate and actually faster. Sadly it is _faster_ because of a
blunder in previous PR (using BucketQueue where MinHeap would be
better), not because of a new tech. More importantly, it is more
accurate. And that's what people apparently want.

## What changed?

Most of the functional changes relate to `SpatialQuery` module. This is
the thingy that answers "we know the target, which tile of my territory
is the best to launch an invasion". To make it compute a path from South
America to the deep inland China river, it has to work on a coerced map,
one with a very small resolution, so small in fact, that every 4096 map
tiles gets compressed to just one pixel. I hope you see where this is
going.

Previously we selected a random coastal tile within this big pixel
(honestly it wasn't random at all, but could very well be for the
illustrative purposes). Now, we try to be a bit more deliberate. Since
we already know the rough location of the probably best tile, we can
exclude all other tiles from the computation. Imagine a player's
territory spans both Americas on global map - that's a lot of shores.
But since we already know the best tile is somewhere close to Miami, the
problem space was greatly reduced, no need to consider all other shores.
But pathing to the target in China from Miami is still crazy expensive.

This is where second trick comes to play - instead of pathing all the
way to China, we select a _waypoint_ in the rough direction of China,
about 100 to 200 tiles away. This way we fairly cheaply select best tile
to launch an invasion towards this abstract point. And chances are, this
point is far enough, the newly computed path is very close to being
optimal. When you throw a dart from far away, the difference between
scoring 10 and missing is very small. This is why aiming in the general
direction of the board - as opposed to the ceiling - is usually good
enough.

## Okay, but what about the crazy paths when I send invasion to the
opposed bank of a river?!

Well, pathing from America to China is cool, but most players wouldn't
notice the difference on such long paths, what about the short ones? We
now try more accurate pathing first and defer to hierarchy only if it
fails. This produces much better paths for short invasions. While the
fix described above ensures the accuracy is improved also on
medium-to-long routes.

## Playground

Yes.


https://github.com/user-attachments/assets/9cf9586f-c99a-416d-b856-8cf0a21c35ed

## CodeRabbit

Grab a 🥕. Remember `tests/pathfinding/playground` is mostly generated
code and go easy on it. It's enough for it to work and do it's job of
visualizing the paths. No need for throughout review of these files.

## 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:

moleole
2026-01-16 16:27:55 -08:00