29 Commits

Author SHA1 Message Date
Mattia Migliorini 2dada6f516 Handle Nation win condition (#2824)
Resolves #2823

## Description:

When playing in single-player mode, if an NPC reaches 80% land control
before the player, the game enters a broken state where:

- The game clock stops
- Win checking stops permanently
- Even if the player later conquers 100% of land, victory is never
awarded
- The game becomes "stuck" in a zombie state.

This PR addresses this allowing Nations to be set as winners in single
mode, and in this case showing a "Nation {nation} has won" modal to the
user. This WinModal is the same as the "{player} has won", with the only
change being the title.

Nation wins in FFA, from the human player perspective:

<img width="1457" height="837" alt="image"
src="https://github.com/user-attachments/assets/1ce569bd-6616-4a23-b4a4-afedad2c64f8"
/>

## 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-01-08 13:51:23 -08:00
Arkadiusz Sygulski b090f2f624 HPA* Pathfinding (#2815)
## Pathfinding with HPA*

Hi! The primary objective of this PR is to replace per-tile A* with
hierarchical pathfinding - HPA*. In practice, this means we create an
abstract graph on top of the actual map with far fewer points and use it
to decide on general path structure. Only then we go back to tile-level
and build path between selected waypoints. This speeds up long distance
pathfinding by over 1000x in some cases. To make the review easier, it
comes with a benchmark and visual playground.

## PREPROCESSING

H part of HPA* means "hierarchical" and requires preprocessing.

This PR includes pre-processing as part inside `new Game()` constructor.
It takes about 135ms for `giantworldmap` on my machine, which increases
the effective initialization from ~95ms to ~230ms. This time could be
reduced in different ways, which are **out of scope** for this PR.

After confirming the initialization time is bearable on low-end devices,
I argue merging this PR as-is is acceptable tradeoff. It creates small
lag at the beginning of a round but pays for itself in the first minute
of the match.

## Nerdy details

**Architecture**
- HPA*-style hierarchical pathfinding
- 32×32 sectors on minimap with gateway nodes on borders
- Gateway graph built via BFS during preprocessing
- Water component optimization skips unreachable gateway pairs
- A* on gateway graph → local A* within sectors → Bresenham path
smoothing
- Minimap upscaling identical to currently used in MiniAStar

**Key Optimizations**
- Typed arrays instead of high-level primitives
- Stamp-based visited tracking (no need to recreate buffers, O(1)
clearing)
- Optional - enabled by default - caching of tile paths between gateways
- Line of sight smoothing for the final path

## Review Focus

Play with included tools, benchmark and visualization. Pathfinding
should be safe to merge as a black box - you do not need to understand
the details. Outcomes can be tested empirically in-game. Visualize (and
share!) edge cases with included playground. Confirm the 100x speedup is
real with benchmark.

If you plan to dive into the code, I suggest the following order:
- Pathfinding abstraction in `src/core/pathfinding/`
- Pathfinding tests in `tests/core/pathfinding/`
- NavMesh in `src/core/pathfinding/navmesh/` + integration with
`Game.ts`
- Benchmark in `tests/pathfinding/benchmark/`

Do not look at playground's code, it has been created with a clanker.
The design is 100% mine and I spent way too long polishing it, but I
haven't even once edited the code manually. There is probably no
abstraction whatsoever, just do not look at the code, let it play.

## Core Changes

#### Pathfinding (`src/core/pathfinding/navmesh/`)
- HPA* + refinement -> three phased pathfinding: A* over the graph ->
naive path -> refinement
- comes with A* and BFS optimized for for specific needs

#### Pre-Processing (`src/core/pathfinding/navmesh/`)
- identify water bodies to avoid pathfinding between disconnected nodes
- create high-level graph of gateways on top of tile map

#### Abstraction (`src/core/pathfinding/`)

- common `PathFinder` interface that can return full path and also act
as state machine (`.next()`)
- adapters for both new and legacy algorithm with fallback to legacy if
navigation mesh not available

#### Benchmark (`tests/pathfinding/benchmark/`)

- `npx tsx tests/pathfinding/benchmark/run.ts` - no guesswork, numbers
- `npx tsx tests/pathfinding/benchmark/run.ts --synthetic` - 1000s of
synthetic paths
- `npx tsc tests/pathfinding/benchmark/generate.ts` - generate more as
needed, test new maps
- includes ONE synthetic scenario to avoid PR bloat, generate more
locally / later

#### Playground (`tests/pathfinding/playground/`)

- `npx tsx tests/pathfinding/playground/server.ts` - visualize paths
with both new and legacy algorithm

## Benchmarks

### Compared with legacy in default - hand picked - scenario:
```
Initialization: 95.95ms -> 227.29ms
Pathfinding: 3038.43ms -> 6.45ms
Distance: 26972 -> 26810 tiles
```

### 42,000 synthetic routes across all maps
```
Running 42 synthetic scenarios with hpa.cached adapter...

 synthetic/achiran                   | Init:    93.42ms | Path:    139.07ms | Dist: 1481630 tiles | Routes: 1000/1000
 synthetic/africa                    | Init:    87.14ms | Path:    155.08ms | Dist: 1829414 tiles | Routes: 1000/1000
 synthetic/asia                      | Init:    57.60ms | Path:    112.55ms | Dist: 1204082 tiles | Routes: 1000/1000
 synthetic/australia                 | Init:    78.18ms | Path:     77.12ms | Dist:  978375 tiles | Routes: 1000/1000
 synthetic/baikal                    | Init:    78.26ms | Path:    152.14ms | Dist: 1600016 tiles | Routes: 1000/1000
 synthetic/baikalnukewars            | Init:    81.44ms | Path:    165.90ms | Dist: 1699283 tiles | Routes: 1000/1000
 synthetic/betweentwoseas            | Init:    29.29ms | Path:    114.99ms | Dist: 1338075 tiles | Routes: 1000/1000
 synthetic/blacksea                  | Init:    30.66ms | Path:     93.14ms | Dist:  949217 tiles | Routes: 1000/1000
 synthetic/britannia                 | Init:    74.12ms | Path:     85.62ms | Dist:  866752 tiles | Routes: 1000/1000
 synthetic/deglaciatedantarctica     | Init:   105.49ms | Path:    192.93ms | Dist: 1574684 tiles | Routes: 1000/1000
 synthetic/didier                    | Init:    81.51ms | Path:    153.70ms | Dist: 1734876 tiles | Routes: 1000/1000
 synthetic/eastasia                  | Init:    49.29ms | Path:    128.63ms | Dist: 1410270 tiles | Routes: 1000/1000
 synthetic/europe                    | Init:    92.55ms | Path:    178.35ms | Dist: 1525216 tiles | Routes: 1000/1000
 synthetic/europeclassic             | Init:    33.50ms | Path:    104.40ms | Dist: 1209759 tiles | Routes: 1000/1000
 synthetic/falklandislands           | Init:    63.00ms | Path:    107.41ms | Dist: 1080251 tiles | Routes: 1000/1000
 synthetic/faroeislands              | Init:    71.91ms | Path:     49.52ms | Dist:  604613 tiles | Routes: 1000/1000
 synthetic/fourislands               | Init:    45.75ms | Path:     78.91ms | Dist:  937439 tiles | Routes: 1000/1000
 synthetic/gatewaytotheatlantic      | Init:    81.00ms | Path:    257.06ms | Dist: 2555551 tiles | Routes: 1000/1000
 synthetic/giantworldmap             | Init:   214.25ms | Path:    220.42ms | Dist: 1976693 tiles | Routes: 1000/1000
 synthetic/gulfofstlawrence          | Init:    45.16ms | Path:     96.05ms | Dist: 1014604 tiles | Routes: 1000/1000
 synthetic/halkidiki                 | Init:    74.68ms | Path:    149.39ms | Dist: 1546781 tiles | Routes: 1000/1000
 synthetic/iceland                   | Init:    58.72ms | Path:     78.16ms | Dist: 1001554 tiles | Routes: 1000/1000
 synthetic/italia                    | Init:    29.78ms | Path:    139.93ms | Dist: 1412024 tiles | Routes: 1000/1000
 synthetic/japan                     | Init:   161.07ms | Path:    118.65ms | Dist: 1154393 tiles | Routes: 1000/1000
 synthetic/lemnos                    | Init:    52.59ms | Path:    136.69ms | Dist: 1481101 tiles | Routes: 1000/1000
 synthetic/lisbon                    | Init:    49.27ms | Path:     86.53ms | Dist: 1032011 tiles | Routes: 1000/1000
 synthetic/manicouagan               | Init:    53.74ms | Path:    110.52ms | Dist: 1307630 tiles | Routes: 1000/1000
 synthetic/mars                      | Init:    29.39ms | Path:     80.55ms | Dist: 1091702 tiles | Routes: 1000/1000
 synthetic/mena                      | Init:    26.37ms | Path:    120.09ms | Dist: 1272751 tiles | Routes: 1000/1000
 synthetic/montreal                  | Init:    26.08ms | Path:    106.77ms | Dist: 1187736 tiles | Routes: 1000/1000
 synthetic/newyorkcity               | Init:    56.60ms | Path:    181.19ms | Dist: 1753875 tiles | Routes: 1000/1000
 synthetic/northamerica              | Init:    96.29ms | Path:    123.02ms | Dist: 1217221 tiles | Routes: 1000/1000
 synthetic/oceania                   | Init:    52.81ms | Path:     51.96ms | Dist:  482373 tiles | Routes: 1000/1000
 synthetic/pangaea                   | Init:    21.29ms | Path:     56.58ms | Dist:  716189 tiles | Routes: 1000/1000
 synthetic/pluto                     | Init:    53.89ms | Path:    141.62ms | Dist: 1304362 tiles | Routes: 1000/1000
 synthetic/southamerica              | Init:    85.19ms | Path:    123.03ms | Dist: 1301403 tiles | Routes: 1000/1000
 synthetic/straitofgibraltar         | Init:    76.68ms | Path:    108.30ms | Dist: 1304592 tiles | Routes: 1000/1000
 synthetic/straitofhormuz            | Init:    38.97ms | Path:     67.78ms | Dist:  754920 tiles | Routes: 1000/1000
 synthetic/surrounded                | Init:    95.35ms | Path:     90.18ms | Dist: 1017142 tiles | Routes: 1000/1000
 synthetic/svalmel                   | Init:    60.58ms | Path:    104.75ms | Dist: 1235501 tiles | Routes: 1000/1000
 synthetic/twolakes                  | Init:    62.05ms | Path:     94.54ms | Dist: 1140807 tiles | Routes: 1000/1000
 synthetic/world                     | Init:    41.43ms | Path:     93.42ms | Dist:  873406 tiles | Routes: 1000/1000

Completed 42 scenarios
Total Initialization Time: 2796.32ms
Total Pathfinding Time: 5026.64ms
Total Distance: 53160274 tiles
```

## Playground

**That's the fun part**. Watch NavMesh running circles around legacy
`PathFinder.Mini` in real time. Debug inner workings, test edge cases,
share URLs for debugging.


https://github.com/user-attachments/assets/34e2e3f5-fbc1-4b1f-917d-820766e98d5d

## Discord Tag
`moleole`
2026-01-08 13:34:18 -08:00
Arkadiusz Sygulski d7bcbf54f3 Add tests for MIRV execution (#2767)
## Description:

This is a companion PR to
https://github.com/openfrontio/OpenFrontIO/pull/2765. It implements
tests and a performance benchmark for MIRV.

```bash
$ npm test -- tests/core/executions/MIRVExecution.test.ts

 ✓ tests/core/executions/MIRVExecution.test.ts (9 tests) 71ms
   ✓ MIRVExecution (9)
     ✓ MIRV should launch successfully 10ms
     ✓ MIRV should break alliances on launch 5ms
     ✓ MIRV should separate into warheads 20ms
     ✓ MIRV warheads should only target tiles owned by target player 15ms
     ✓ MIRV warheads should be distributed with minimum spacing 12ms
     ✓ MIRV should display warning message on launch 2ms
     ✓ MIRV should not launch if player cannot build it 2ms
     ✓ MIRV should not launch when targeting terra nullius 2ms
     ✓ MIRV should launch when targeting own territory without breaking alliances 2ms
```

```bash
$ npm tsx tests/perf/MIRVPerf.ts

...

=== MIRV Performance Benchmark Results ===
MIRV target selection - sparse territory x 53.53 ops/sec ±0.48% (71 runs sampled)
MIRV target selection - dense territory x 53.39 ops/sec ±0.57% (70 runs sampled)
MIRV target selection - giant world map (350 targets) x 1,129 ops/sec ±0.98% (90 runs sampled)
```

## 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
- [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-02 10:36:02 -08:00
Wraith 26f5d40819 build: migrate build system to Vite and test runner to Vitest & Remove depracated husky usage (#2703)
- Replace Webpack with Vite for faster client bundling and HMR.
- Migrate tests from Jest to Vitest and update configuration.
- Update Web Worker instantiation to standard ESM syntax.
- Implement Env utility in `src/core` for safe, hybrid environment
variable access (Vite vs Node).
- Refactor configuration loaders to remove direct `process.env`
dependencies in shared code.
- Update TypeScript environment definitions and project scripts for the
new toolchain.
- Remove the [depracated usage of the
husky](https://github.com/typicode/husky/releases/tag/v9.0.1).

## Description:

migrate build system to Vite and test runner to Vitest & Remove
depracated husky usage

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

wraith4081

---------

Co-authored-by: evanpelle <evanpelle@gmail.com>
2025-12-28 22:10:26 -08:00
Mykola 6112547273 Improve random spawn (#2503)
## Description:

This is a previously approved PR with an additional commit that fixes
case when nations change spawn & jump around, their previous territory
wasn't getting deleted.

## 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: Evan <evanpelle@gmail.com>
2025-12-20 13:35:30 -08:00
CrackeRR11 8f53785a80 BUG FIX: Gold double deduction + Rmoval of UnitType.Construction (#2378)
## Description:

- Removed the temporary UnitType.Construction and embedded construction
state into real units via isUnderConstruction().
- Centralized non-structure spawning to perform a single validation
right before unit creation/launch.
- Updated UI layers to render construction state without relying on the
removed enum.
- Adjusted and created tests to match the new flow and to cover the
no-refundscenarios.

# Tests updated 
- tests/economy/ConstructionGold.test.ts: covers structure cost
deduction and income, tolerant of passive income; ensures no refunds
during construction.
- tests/nukes/HydrogenAndMirv.test.ts: accounts for single-check launch
flow; MIRV test targets a player-owned tile; ensures launch after
payment.
- tests/client/graphics/UILayer.test.ts: mocks now provide
isUnderConstruction and real type strings;

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

CrackeRR1

---------

Co-authored-by: Evan <evanpelle@gmail.com>
2025-11-26 14:45:14 -08:00
Mykola 25ea11114e Random spawn (#2375)
## Description:

https://github.com/openfrontio/OpenFrontIO/issues/2352

This is my first PR in this project, and I’ll continue refining it based
on feedback.

<img width="1088" height="859" alt="image"
src="https://github.com/user-attachments/assets/07f4f8b1-52fa-4136-add4-19b00aefd963"
/>

<img width="1157" height="783" alt="image"
src="https://github.com/user-attachments/assets/1c5be80d-72f8-4ead-8d4b-706a3a04fd73"
/>

<img width="1488" height="777" alt="image"
src="https://github.com/user-attachments/assets/4d743548-f0c3-4579-963b-43676f68fab1"
/>

<img width="1499" height="778" alt="image"
src="https://github.com/user-attachments/assets/f808e44f-ef97-467f-9e41-812e2857c36e"
/>


## 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: Evan <evanpelle@gmail.com>
2025-11-06 15:49:37 -08:00
DevelopingTom d3c4cd6620 Record missing stats (#2407)
## Description:

Some stats are missing from the recorded game stats:
- Unit upgrade
- Gold from trade and from steal

The gold from trade/steal was introduced with [PR
784](https://github.com/openfrontio/OpenFrontIO/pull/784) but was
quickly reverted with [PR
927](https://github.com/openfrontio/OpenFrontIO/pull/927), probably
involuntarily.

## 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
2025-11-06 15:38:15 -08:00
Vivacious Box f161c94ff4 Max timer (#1289)
## Description:

Adds a max timer setting
The timer starts at max timer and goes down, becoming red if reaching <
1 min
The player with the biggest territory wins at the end of the timer


![image](https://github.com/user-attachments/assets/888099fc-95ae-4303-8c80-c850e58d36e2)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

Vivacious Box

---------

Co-authored-by: Loymdayddaud <145969603+TheGiraffe3@users.noreply.github.com>
2025-10-17 17:09:10 -07:00
Jeff 187ef1f2dd feat(PlayerExecution): downgrade defense posts on capture (#1957)
## Description:

Closes https://github.com/openfrontio/OpenFrontIO/issues/1619.

On capture, defense posts will be downgraded.

On the live version this means defense posts will be destroyed, as
defense posts can only be level 1.

Misc. changes:
- added `decreaserLevel` helper
- cleaned up if/else in tick unit loop for clarity to avoid yet another
nested layer

Continuation of the stale PR,
https://github.com/openfrontio/OpenFrontIO/pull/1622.

## 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: `seekerreturns`
2025-10-08 17:31:56 -07:00
Tiago Santos Da Silva fa7b7fceb3 Enable the @typescript-eslint/no-unused-vars eslint rule (#2130)
## Description:

###  Summary of Changes

This PR enables the ESLint rule **`@typescript-eslint/no-unused-vars`**
as requested in the issue and applies the necessary code adjustments
across the project.

#### 🔧 What was done:
- Activated the rule `@typescript-eslint/no-unused-vars` in the ESLint
config.
- Updated ~70 files to comply with the rule:
  - Replaced unused variables with a `_` prefix where appropriate.
- Added inline ESLint disable comments (`eslint-disable-next-line`) for
specific cases where the variable or code block seemed important for
context, readability, or future use.
- Ensured no linting errors remain related to this rule.

---

###  Clarification

Some cases were handled with inline disable comments instead of removing
the variable entirely, to avoid accidental breaking changes or loss of
intent.
If a different approach is preferred (e.g., stricter removal or
alternative handling), I’m happy to adjust the implementation
accordingly — just let me know!

---

### 🙌 Next Steps

Please review and let me know if:
- Any file should be handled differently.
- You prefer removal instead of disabling in certain areas.
- Additional rules should be enforced or reverted.

I’m available to make any follow-up improvements needed.

---

### 🎃 Hacktoberfest Note

I'm participating in **Hacktoberfest**, so if this PR is accepted,
please add the label:

`hacktoberfest-accepted`

Thank you!

#1784 

## 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
2025-10-06 13:26:43 -07:00
Abdallah Bahrawi 0a6ab07d2e fix: traitor bug when attacking immediately after initiating an alliance (#2044)
## Description:

This PR fixes a critical race condition bug where players could
unintentionally receive the traitor debuff when alliance requests were
accepted mid-attack.


Critical Bug Fixes #1866

**Root Cause:** 
Players could bypass UI alliance checks ( isFriendly() ) by accepting
alliances and immediately attacking after that, causing the server to
treat the attack as betrayal
Solution: Added server-side alliance validation in
AttackExecution.init()
This ensures attacks on allies are blocked at the server level.

- Once Bots and Nations decide to attack, they breaks the alliance. I
added maybeConsiderBetrayal(), which currently always returns true. I’ll
add proper logic for alliance-breaking soon on another PR; this didn’t
exist in the code before.

## 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: evanpelle <evanpelle@gmail.com>
2025-09-13 09:21:21 -07:00
evanpelle 3e880a34ef v25 meta update (#1872)
## Description:

v25 meta update:

- Trade ship spawn rate is determined by number of ports players has and
total number of tradeships on the map
- Train spawn rate scales hyperbolically with number of factories owned
by player
- Factory & Port share the same early unit discount (eg building a port
makes the factory more expensive), this is to encourage more
specialization: become a naval economy or land based economy.
- Trains spawn from factories and arrive on cities
- Trains only give gold on cities
- Trains give 50k gold for allies, 25k for nonallies, and 10k for self
- Large players are given a 30% speed/attack debuff in sigmoid curve
- Reduced attack bonus for large players
- Nerf bot gold production from 1k/s => 500/s
- Nerf bot max troops

## 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
2025-08-19 18:53:42 -07:00
evanpelle 754db60a00 Have trains produce more gold (#1759)
## Description:

During the play test, trains did not produce enough gold. So increase
gold, and give 4x bonus for allies.

## 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
- [x] I have read and accepted the CLA agreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
2025-08-08 18:38:16 -07:00
DevelopingTom a05814a7fb Rework trains to encourage alliances (#1697)
## Description:

This update encourages alliances by modifying how train-based trading
works. Trading is now restricted to allied networks, and allied trades
offer better rewards than self-trades, incentivizing cooperation.

Changes:
- Train destinations are now limited to stations owned by the player or
their allies.
    - Delivering a train to one’s own station grants 4,000 gold.
- Delivering a train to an ally’s station grants 5,000 gold to both the
sender and the receiver.

This system encourages players to form alliances, especially with
factory-focused players.


## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
2025-08-03 23:13:27 +00:00
DevelopingTom eeeb7e4b4e SAM smart targeting (#1618)
## Description:

Current SAM behavior is to shoot a missile as soon as a nuke is in
range.
Players can exploit it by overshooting behind the SAM, so the SAM
missile will take way longer to reach the nuke, usually too late to
prevent its explosion.

This PR introduces a "smart" targeting system that allows SAM to
calculate an optimal interception tile along the nuke's trajectory. They
can also preshot before the nuke becomes vulnerable, as long as the
interception tile will be within the vulnerable window.

This change makes SAM range enforcement much more strict.

Changes:
- Nukes now precompute their full trajectory on creation and update
their current position index every tick.
- SAMs use this trajectory data and their own missile speed to calculate
the ideal interception tile.
- SAM missiles now aim directly at that interception point rather than
chasing the nuke.

Small changes on the fly:
- `BezierCurve` now uses a provided increment so the curve LUT is the
optimal size
- Increased nuke opacity when untargetable: 0.4 → 0.5
- Slightly extended nuke vulnerability range to SAMs: 120 → 150


===

Preshot an incoming nuke still in the unfocusable state. Notice how the
nuke is destroyed as soon as becomes focusable:



https://github.com/user-attachments/assets/9fbf1ae4-33b4-4fa0-9b53-cb53f3adc17b


Shooting right at the range limit:


https://github.com/user-attachments/assets/d68793ac-b249-45fe-88bf-e20f70758449


Shooting behind the SAM:


https://github.com/user-attachments/assets/800cd7ff-d9d9-40f3-aba8-fa3ab526b3b2




## 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
- [x] I have read and accepted the CLA agreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
2025-08-02 22:03:29 +00:00
Tyler Hanavan 0726449b6e Don't become traitor when betraying inactive player (#1610)
## Description:

Don't become traitor when betraying inactive player

This PR makes the following changes
- Do not mark the attacking player as a traitor, if they attack/betray
an ally who is disconnected from the game
- Do not send the attacking player the betrayal message (traitor debuff
applied message), if they attack/betray an ally who is disconnected from
the game
- Add test case for traitor debuff being applied if the attacking player
attacks an ally who is still connected to the game
- Add test case for traitor debuff NOT being applied if the attacking
player attacks an ally who is disconnected from the game


I also tested this manually with nuking an allied player who is
connected and nuking an allied player who is disconnected. The logic
worked as expected.

This PR was made in regards to the following issue: #1599 

## 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
- [x] I have read and accepted the CLA agreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

slyty
2025-07-28 11:22:07 -07:00
evanpelle 65937b7e69 bugfix: when nuking nearby water, the attacker was not marked as a traitor on launch (#1574)
## Description:

There was a bug where we only checked for betrayals if the target tile
was owned.

## 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
- [x] I have read and accepted the CLA aggreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
2025-07-25 18:39:28 -04:00
VariableVince b850a2f93d Tradeship performance (#1448)
## Description:

Small performance improvements for tradeship execution. With the new cap
of 100 tradeships in total, the impact is smaller but still there.

Also added jlrouillard's (Vivacious Box) test with permission from PR
#1449. Tests 100% passed.

- Save tradeship owner, destination port owner and tradeship current
tile in constants for re-use.
- Added check if wasCaptured was already set to true, before comparing
origOwner and tradeship owner and setting wasCaptured to true.
Currently, wasCaptured is set to true once and never back to false
again. This PR keeps it that way but a but faster.
- One less call to pathfinder needed: before calling pathfinder, check
if current tile is destination port tile, and if so call complete. This
is basically the same as pathfinder would do, but less complex without
need for e.g. manhattan distance.
- case PathFindResultType.Completed: isn't needed anymore because of the
above. Kept as fallback. But moved it down in the switch statement, as
there's a low(er) chance the case will ever be true.
- Test if we need to find a new destination port for captured ship:
currently, if this.wasCaptured is true, it will look for a new
destination port each tick again and again. With this PR, only do this
if destination port isn't owned by capturer, or if that port has become
inactive. In tests with thousands of tradeships, went down from 300K+
times setting 'new' port, to only 1250 calls.

**Q:** Will this code work for ships that are captured again, after
being captured already?
**A:** Yes. After wasCaptured is set to true once (after first capture),
we only need to check each tick if the destination port is owned by the
current tradeship owner, to know if we need to assign a new destination
port again.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tryout33
2025-07-16 19:27:28 -04:00
Scott Anderson be01b90b25 Eslint (#998)
## Description:

Enable a few eslint rules:
- `@typescript-eslint/no-empty-object-type`
- `@typescript-eslint/no-require-imports`
- `no-useless-escape`

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

---------

Co-authored-by: Scott Anderson <scottanderson@users.noreply.github.com>
2025-07-15 00:41:24 -04:00
DevelopingTom 6353a5d6f7 Automatic train stations (#1353)
## Description:

Train stations are now built automatically when a factory is
constructed.

Changes:
- When a factory is built, nearby structures are connected to the rail
network
- When a city is built near a factory, it is connected to the rail
network
    - All structures behave the same when a train stops: to be defined
    - Removed station badge
    - Gold income is now related to the structure's level

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
2025-07-06 13:15:49 -07:00
DevelopingTom 29170321e4 Add gold fx when a tradeship lands (#1322)
## Description:

Add a text popup FX when a tradeship lands in your port.
They are only displayed for the current player.


https://github.com/user-attachments/assets/586ded57-5311-4394-a322-903ecea89bd6


## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom

---------

Co-authored-by: Tom Rouillard <trouilla@mathworks.com>
Co-authored-by: evanpelle <evanpelle@gmail.com>
2025-07-03 19:26:36 -07:00
evanpelle ca522a5937 refactor cosmetics out of PlayerInfo (#1299)
## Description:

Remove Cosmetics from PlayerInfo. The game engine should have no
knowledge of cosmetics since they shouldn't affect game play at all.
Instead pass player cosmetics into the GameView.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
2025-06-28 12:33:19 -07:00
Vivacious Box ad42dc0ee8 Fix sam targetting everything (#1280)
## Description:

There was a regression on how sam targets nukes.
This fixes 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

Vivacious Box
2025-06-26 16:47:16 -07:00
evanpelle b48770faf0 Move maps generation out of repo, new map structure (#1256)
## Description:

Move map generation outside of main repo, it has been rewritten in Go
and is much faster. Also refactor how maps are stored, one dir per map.
The map binaries are basically identical to before. Some maps like
Africa have 1% difference in bytes, but playing it looks exactly the
same.

Use lazy loading for map data access so only needed files are accessed.

Unit tests now load map binary instead of regenerating it from scratch,
speeding them up.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
2025-06-23 11:23:56 -07:00
Aotumuri b71acdc993 Patterned territory (#786)
## Description:
This is meant to give players more customization options.
Permission handling hasn’t really been implemented yet.
## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

aotumuri
2025-06-22 23:57:24 -04:00
DevelopingTom 43397779fa Add trains (#1159)
## Description:

Add a rail network to handle train stations/railroad between structures.

Changes:
- `RailNetwork` is responsible for the train station graph. Use it to
connect new `TrainStations`
- A `RailRoad` connects two `TrainStation`
- No loop possible in the rail network
- Train stations handles its railroads
- Added a layer to draw the railroads under the structures

#### Clusters
- To speed up computations, each `TrainStation` references its own
cluster
- A cluster is a list of `TrainStation` connected with each other,
created by the `RailNetwork` when connecting the station
- Train stations spawn trains randomly depending on its current cluster
size
- A `TrainStation` decides randomly of the train destination by picking
one from the cluster

#### Production building:
- Added a factory which has no gameplay impact currently. _To be
discussed._

#### Train stops:
- When a train reaches a factory, it's filled with a "cargo". The loaded
trains has no impact currently. _To be discussed._
- When a train reaches a city, the player earn 10k gold
- When a train reaches a port, it sends a new tradeship if possible
- If a destination/source is destroyed, the train & railroad are deleted
too


https://github.com/user-attachments/assets/42375c17-9e04-4a42-98d0-708c81ffd609


https://github.com/user-attachments/assets/fbecdb53-a516-4df8-87fb-1f9a62c4efa0



## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom

---------

Co-authored-by: Scott Anderson <scottanderson@users.noreply.github.com>
2025-06-22 08:14:08 -07:00
Vivacious Box 7fa11ed035 Set a targetable status for nukes (#1174)
## Description:

Set a targetable status for units (specifically atom bomb and hydro)
A nuke is targetable near launch and target but is untargetable mid air.
An untargetable unit is half transparent to show that it cannot be
destroyed.


![targetable](https://github.com/user-attachments/assets/cc6769ff-95ab-4294-9a8e-10f909711f68)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

Vivacious Box
2025-06-15 06:23:13 +00:00
Vivacious Box 52ecaea06c Redraw stacked buildings sprites (#1170)
## Description:

When buildings are stacked on each other, the buildings under are not
redrawn when the stacked building is destroyed.
A simple outer range + sprite radius check triggers a touch which
redraws the buildings.
Sprite radius is set to 16 (dont forget to change it if it ever changes)

### Before:

![image](https://github.com/user-attachments/assets/08acd6e2-a45f-4cc1-b263-89be1d994a12)

### After:

![nukeredraw](https://github.com/user-attachments/assets/c972cb26-03bd-4f74-a960-63f6145fb85d)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

Vivacious Box
2025-06-14 05:48:15 +00:00