mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 16:56:36 +00:00
094aa766ced86c5c0aea46af4a4172b691ea92b7
72 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
094aa766ce |
Improve "Better troop management for nations 🤖" (#4278)
## Description: **Allow Hard/Impossible nations to retaliate and expand freely** Previously, nations on Hard/Impossible difficulty could be stuck unable to fight back if their `troopSendCap` or `isAttackTooWeak` checks blocked them from sending enough troops. **@legan320** on the main discord noticed it. Now: - `troopSendCap` raises the cap to at least the total incoming attack troops, so nations can match the force being used against them - `isAttackTooWeak` bypasses the 20% minimum check entirely when under attack - `troopSendCap` no longer applies when attacking Terra Nullius, so nations can always expand into unowned land with full troops All checks still apply normally for unprovoked attacks against other players. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
82b68d16a1 |
Fix "Better troop management for nations 🤖" (#4265)
## Description: There was a check missing... The troop management stuff should be disabled for team games because nations can expect donations in that case, and its mainly relevant for FFAs. ## 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 ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
d96c055df1 |
Better troop management for nations 🤖 (#4239)
## Description:
When human pro players have non-allied players with similar troops next
to them, they wouldn't send out a big attack.
But nations are doing exactly that.
With this PR, they no longer do. On hard and impossible.
On easy and medium they are stupid 😀
```
1. Troop send cap: the nation must retain a minimum fraction of its
strongest non-allied neighbor's troop count (Hard: 75%, Impossible:
90%). Attacks that would drop below this floor are scaled down or
skipped entirely. Allied and same-team neighbors are ignored since
they pose no threat. The cap applies to land attacks, boat attacks,
and random boat attacks.
2. Minimum attack strength: if the capped troop count is less than 20%
of the target's troop count, the attack is skipped as too weak to be
worthwhile. Only applies on Hard and Impossible.
```
_Coded by MiMo 2.5 Pro, reviewed by MiniMax M3_
## 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
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
|
||
|
|
2789db8b96 |
Optimize core simulation hot paths (no behavior change) (#4230)
## Summary Pure performance optimizations to the attack/conquer/cluster hot paths in `src/core`, driven by the full-game perf harness from #4228. **No behavior change**: the final game-state hash is identical before/after on every config tested — world quick run (2 different seeds), giantworldmap, and the default 1800-tick run. ### Changes - **Flat-arithmetic neighbor iteration**: `forEachNeighbor` / `forEachNeighborWithDiag` / `isBorder` / `isOceanShore` are now implemented inside `GameMapImpl` using raw `ref±1` / `ref±width` index math, skipping the per-neighbor `ref()` coordinate validation (`Number.isInteger` etc.). `GameImpl` and `GameView` delegate. - **New `neighbors4(ref, out)`**: zero-allocation, callback-free neighbor query for hot loops (W, E, N, S — same order as `forEachNeighbor`). - **`AttackExecution`**: the per-tile closures in `tick()` / `addNeighbors()` are replaced with reusable neighbor buffers, a cached `GameMap` reference, and integer `smallID()` owner comparisons instead of owner-object lookups. - **`GameImpl`**: the per-conquer `updateBorders` closure is hoisted to a method with a reusable buffer; `removeInactiveExecutions` compacts the executions array in place instead of allocating a new ~4200-element array every tick. - **`PlayerExecution`**: `surroundedBySamePlayer` / `isSurrounded` / `getCapturingPlayer` de-closured (`neighbors4` + integer compares; neighbor visit order preserved, so `getCapturingPlayer`'s Map-insertion-order tie-breaking is unchanged); flood-fill visit closure hoisted out of the while loop. - **`FlatBinaryHeap.dequeue`**: returns the tile directly instead of allocating a `[tile, priority]` tuple per dequeued tile (AttackExecution is the only caller). ### Performance (`npm run perf:game`, same machine, before → after) | run | mean tick | ticks/sec | max tick | |---|---|---|---| | default (world, 400 bots, 1800 ticks) | 9.04 → **7.98 ms** | 111 → **125** | 31.7 → 35.7 ms | | giantworldmap, 600 ticks | 22.5 → **17.4 ms** | 44 → **58** | 52.8 → **36.2 ms** | The giantworldmap tail improvement (max tick −31%) is the most relevant for the 100 ms tick budget. ### Determinism verification Identical `Final hash` before and after on all configs: | config | hash | |---|---| | `--map world --ticks 200 --bots 100` | `5455008589403520` | | same + `--seed second-seed-check` | `5580840142777488` | | `--map giantworldmap --ticks 600` | `37373734953428430` | | default run | `26773450321979388` | ### Tests - New `tests/NeighborIteration.test.ts` pins the exact neighbor iteration orders (W,E,N,S cardinal; dx-major diagonal — conquest order and RNG consumption depend on them) and conquer/border-tile invariants checked mid-battle. - New `tests/FlatBinaryHeap.test.ts` covers heap ordering, clear, and growth. - Full suite passes (122 files / 1386 tests + server tests); lint and prettier clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
eca5794ebb |
Chore(deps): Update and remove dependencies (#3819)
## Description:
Only mentioning removals/major updates/notable changes below, not all
minor upgrades.
### Removed:
- "@aws-sdk/client-s3": not used anywhere (was used in Archive.ts
previously)
- chai, "@types/chai", sinon-chai: not used anywhere, probably leftover.
Vitest uses a bundled version of Chai for its expect asserations under
the hood too.
- protobufjs, "@types/google-protobuf": not used anywhere, probably left
from evan's experiment with it? Removed from vite.config.ts too.
- "@types/jquery": not used anywhere, probably leftover
- sinon, "@types/sinon": not used anywhere just like chai, probably
leftover. And Vitest provides us with the same functionality.
- "@types/systeminformation": dependency systeminformation was removed
last year, this is an unneeded, deprecated and unmaintained remainder.
- vite-tsconfig-paths: removed, and removed the import and usage in
vite.config.ts and replaced it by adding `tsconfigPaths: true` to the
`resolve` block. Because of this message displayed on running the tests:
"The plugin "vite-tsconfig-paths" is detected. Vite now supports
tsconfig paths resolution natively via the resolve.tsconfigPaths option.
You can remove the plugin and set resolve.tsconfigPaths: true in your
Vite config instead."
- vite-plugin-static-copy: removed, we don't use it anymore (was used in
our vite.config.ts once,, probably before Vite natively supported
copying static assets via its publicDir configuration)
### Updated:
- color.js: v0.5 > v0.6, no breaking change affecting us
- cross-env: v7 > v10. It's a publicly archived repo since Nov 2025. But
before that he got it up-to-date from June 2025, porting to TS, dropping
old Node versions, dependencies etc. Seems still good to use for some
amount of time to come.
- dotenv: v16 > v17, now logs an informational message by default when
it loads an environment file. Can be disabled by using
dotenv.config({quite: true}) if needed.
- ejs: v3 > v5: security patches mostly. Vite still uses v3 btw.
- eslint: v9 > v10. Newly enabled rules by default:
'no-unassigned-vars', 'no-useless-assignment' and
'preserve-caught-error'. Mostly faster and minimum support moved to
higher node versions, which shouldn't be a problem.
- "@eslint/compat": v1 > v2. Minimum supported Node versions, which
should not be a problem.
- intl-messageformat: v10 > v11 no breaking changes that affect us
- jdom: v27 > v29. Faster. Most notably minimum support moved to higher
node v22 version, which should not be a problem. Also, see types/node,
kind of expecting v24 to be installed now.
- nanoid: from v3 to v5, no breaking changes that affect us
- "@opentelemetry/sdk-logs": now that addLogRecordProcessor is removed,
changed Logger.ts to pass an (empty) provider array directly to the
LoggerProvider constructor. Follows the changes in
https://github.com/open-telemetry/opentelemetry-js/pull/5588
- "@tailwindcss/vite": supports vite v8 from 4.2.2, and a fix for it in
4.2.4
- tailwindcss: supports vite v8 from 4.2.2
-- in 4.1.15 (we were already above this version) break-words was
deprecated in favor of wrap-break-word. But break-words, which we use in
15 places, will still work as expected
(https://github.com/tailwindlabs/tailwindcss/pull/19157). Same goes for
also deprecated "order-none".
- "@types/node": from v22 to v24, assuming most now use node 24
- vite v7 > v8:
-- is now on 8.0.10 so first bugs are out of it, while v8 itself also
fixed a big number of bugs.
-- in vite.config.ts, fixed Ts error/compilation issue by changing the
manualChunks option in build.rollupOptions.output to use the function
syntax, which is required by the updated types instead of the object
syntax.
- zod: no changes that affect us
### Prettier:
Updated only because of (new because of update?) Prettier errors for
files untouched in this PR originally:
- PathFinder.Parabola.ts
- WorkerMessages.ts
- ClanModal.handlers.test.ts
- ClanModal.rendering.test.ts
- CONTRIBUTING.md
- README.md
### ESLint:
Fixes needed to silence errors coming from newly enabled recommended
rules 'no-useless-assignment' and 'preserve-caught-error':
For 'no-useless-assignment' (default assignment never used because of
unreachable code or they are guaranteed to get a value, so they can be
undefinedat the start. Exception was AttackExecution, so made the
default value of 0 the default case in the switch statement):
- ClientGameRunner
- GameModeSelector
- NameBoxCalculator
- StructureDrawingUtils
- TerritoryLayer
- Diagnostics
- GameRunner
- ColorAllocator
- DefaultConfig
- AttackExecution
- AiAttackBehavior
- Worker.worker
- GamePreviewBuilder
For 'preserve-caught-error', disabled the rule here because the possible
fix `{cause: error}` was introduced in ES2022 while we're still on
target ES2020 currently:
- GameServer
- Privilege
_Error: The value assigned to 'gameMap' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'timeDisplay' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'scalingFactor' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'radius' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'teamColor' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'gl' is not used in subsequent statements.
(no-useless-assignment)
Error: The value assigned to 'power' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'tickExecutionDuration' is not used in
subsequent statements. (no-useless-assignment)
Error: The value assigned to 'selectedIndex' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'mag' is not used in subsequent statements.
(no-useless-assignment)
Error: The value assigned to 'speed' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'matchesCriteria' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'shouldContinue' is not used in subsequent
statements. (no-useless-assignment)
Error: The value assigned to 'description' is not used in subsequent
statements. (no-useless-assignment)
Error: There is no `cause` attached to the symptom error being thrown.
(preserve-caught-error)
Error: There is no `cause` attached to the symptom error being thrown.
(preserve-caught-error)_
All tests pass. TypeScript and ESLint errors resolved.
## 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: Copilot <copilot@github.com>
|
||
|
|
58ec8b280f |
Nations: Fix city farming + reactive defense posts + fix nuked territory capture 🛡️ (#3814)
## Description: Would be very good to get these fixes last minute into v31. - **Farming nations for cities is fixed**: Here you can see city farming in action, vari farms Finland: https://www.youtube.com/watch?v=J4J0ajlnSHs&t=137s - Lots of 125k gold cities... Now nations will build defense posts instead of cities: - **Nations now build defense posts reactively** instead of through the normal structure build order. When a nation is under significant land attack (incoming/own troops >= 35%), it places a defense post near the attack front -- skipped on Easy, capped at 1 on Medium, and scaled by the incoming troop ratio on Hard/Impossible. Posts spread along the front. The old `defensePostValue()` placement path is removed. - **Nations now capture nuked territory.** `hasLandBorderWithTerraNullius()` was incorrectly filtering out tiles with fallout, causing the `nuked` attack strategy to never dispatch a attack. Big bug . - [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 |
||
|
|
7654537a00 |
Much better river handling for nations and tribes! 🏞️ (#3786)
## Description: In this example, the two nations DONT see each other as neighbors, but as ISLANDERS. Because they dont have a direct border connection, there is water in between. <img width="526" height="329" alt="image" src="https://github.com/user-attachments/assets/cf2c15b5-7793-4445-afd2-920d6cd50a2a" /> This is a big problem, because most of the logic in AiAttackBehavior gets ignored. Only the "islander" strategy runs (late, because its a not-important strat). ### Summary - `PlayerImpl.neighbors()` now includes cross-water neighbors: a new `shoreReachableNeighbors()` helper samples every 10th shore border tile and looks up to 5 tiles in each cardinal direction across water, finding land owners on the other side (covers rivers up to 4 tiles wide). - `AiAttackBehavior.maybeAttack()` extends the `hasNonNukedTerraNullius` check to also trigger on TN detected via `player.neighbors()`, so nations notice and pursue TN that is only reachable across a river. - `sendAttack()` uses a new `hasLandBorderWithTerraNullius()` land-only adjacency check to decide between a land attack and a boat attack for TN, rather than `sharesBorderWith()` which includes water tiles. - Added `sendBoatAttackToNearbyTerraNullius()`: when no TN land is directly adjacent, the AI scans its shore border tiles for unowned land across water and dispatches a transport ship. ### Also works for Tribes! Tribes can boat rivers now, really cool. https://github.com/user-attachments/assets/382e85aa-c437-4e0c-afc2-0c381432da3d ## 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 |
||
|
|
692028d033 |
Make nations more forgiving toward humans on Easy difficulty 🕊️ (#3791)
## Description: - Nations on Easy no longer betray human allies - Nations on Easy now attack humans only 25% of the time (down from 50%) - Increased Easy reaction tick range (65-100, up from 65-80) so nations respond more slowly - Raised MIRV steamroll-stop thresholds on Easy (city gap multiplier 1.5 -> 2, min leader cities 15 -> 20) and slightly tuned Medium/Hard thresholds ## 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 |
||
|
|
8099b9fad7 |
Massive nation improvement 🤖 (#3761)
## Description: - Hard / Impossible nations in team games auto-stop trading with all enemies - If there are a LOT of nations on the map (Enzo stream with 400 nation HvN private games) they no longer start with a city, they start with eco (port / factory) because they cannot gain much gold from bot-killing - Impossible nations built way too many missile silos sometimes, caused by the SAM overwhelming logic. Fixed now. - In public HvN games with 5M starting gold, nations placed their structures way too fast, which slowed down their expansion. And humans could easily cause a lot of damage with one atom bomb. Now their first structure is a SAM (on hard / impossible) and they wait between their earlygame structure placements. - Nations now spread out their port placements more evenly - Nations are now able to attack much stronger enemies in team games (They can expect donations) - Improve performance a bit by adding more early-returns (Dont run any nuking logic if nukes are disabled, no alliance logic if alliances are disabled, no boating logic if transport boats are disabled, ...) - Fix some of the "cannot send troops" messages in the console (DonateTroopExecution) - Nations build their first missile silo sooner, they should also build more SAMs - Nations spend their gold better after reaching the save-up-target (previously they stopped nuking) - Optimized save-up-targets for team games - The richest impossible nation is nuking very dense players now (lot of structure levels on a small island) ### How does a 5M gold HvN start look like now? https://github.com/user-attachments/assets/e9da89c3-c0d4-4144-a741-3101746b16da ## 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 |
||
|
|
f7716c7d42 |
Little Console Cleanup 🧹 (#3741)
## Description: Fixes these console warnings from bots: <img width="591" height="94" alt="Screenshot 2026-04-19 033624" src="https://github.com/user-attachments/assets/6ee79302-e2a7-4195-94e5-c1f455eb1799" /> Removes some spammy logs, they dont seem to be helpful? <img width="271" height="174" alt="Screenshot 2026-04-19 033739" src="https://github.com/user-attachments/assets/70122506-e8fb-4a72-b73e-08e72fe222bd" /> <img width="284" height="656" alt="Screenshot 2026-04-19 033646" src="https://github.com/user-attachments/assets/4b4ebef2-e191-4947-9615-0e26cd9bf075" /> ## 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 |
||
|
|
17c1a6300f |
Trading in lakes 🚤 (#3653)
## Description: - Widened port placement and warship spawn/patrol checks from `isOcean`/`isOceanShore` to `isWater`/`isShore`, so ports can be built on lake shores and ships can operate on lakes, we discussed it here: <img width="996" height="423" alt="image" src="https://github.com/user-attachments/assets/acf1e970-9631-4848-a0ed-6d0470616e1d" /> - Filtered `tradingPorts()` by water component so ports only attempt trades with reachable ports - prevents silent path-not-found failures across disconnected water bodies - Applied the same water component filter when a captured trade ship reroutes to its new owner's nearest port - Removed the `WaterManager` fallback that force-marked isolated water-nuked-tiles as ocean (no longer needed since lakes are now navigable) - Added a check to prevent nations from building ports on water bodies that aren't accessible to other players ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin --------- Co-authored-by: Evan <evanpelle@gmail.com> |
||
|
|
aa59dd23be |
Rebalance HvN (#3433)
## Description: For the next v30 fix version <img width="868" height="364" alt="imaege" src="https://github.com/user-attachments/assets/520a999c-67e7-4c57-8651-895ad9eeb73a" /> HvN balancing for the revamped difficulty steps of v30 sadly doesn't really work out... In medium difficulty games humans nearly always win (boring) In hard difficulty games humans usually lose It was intended differently... So lets get rid of medium difficulty HvN, always use hard difficulty and disable the donation-capability for public game nations. That will tune the human winrate towards a middle ground at about 65% I think. Which should be nice. Easier than in v29 (was frustrating sometimes) but not as easy as it's now. We can only test this in prod lol ## 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 |
||
|
|
be9ea14fe9 |
refactor: rename Bot to Tribe in internal execution code (#3372)
## Description Follows up on #3290 which renamed the user-facing "Bots" to "Tribes". This renames the internal implementation to match: - `BotExecution` → `TribeExecution` - `BotSpawner` → `TribeSpawner` - `BotNames` → `TribeNames` (`BOT_NAME_*` → `TRIBE_NAME_*`) All callers updated accordingly. `PlayerType.Bot` and `ColoredTeams.Bot` are intentionally left unchanged as they are serialised wire-format values. Closes #3335 ## Please complete the following: - [x] My changes do not break existing functionality - [x] I have tested my changes ## Please put your Discord username so you can be contacted if a bug or regression is found: Just reply here --------- Co-authored-by: PGray <PGrayCS@users.noreply.github.com> |
||
|
|
e137fcaa6c |
Fix/Perf/Refactor: playerActions and buildableUnits, their callers and related types (#3220)
## Description: TL;DR: it's faster. buildableUnits is called via PlayerView.actions from UnitDisplay (each tick without TileRef), BuildMenu (each tick when open), MainRadialMenu (each tick when open), PlayerPanel (each tick when open), StructureIconsLayer (when placing a building from build bar), NukeTrajectoryPreviewLayer (when placing nuke, on tick when tile changes), ClientGameRunner (on click to attack/auto-boat or hotkey B or G). After https://github.com/openfrontio/OpenFrontIO/pull/3213 got merged, the change with largest impact in https://github.com/openfrontio/OpenFrontIO/pull/3193 was done in such a different way that a new PR was needed The idea in 3193 was to not always ask for Transport Ship from buildableUnits. In such a way that very little extra data was send to the worker. This had the biggest impact on performance (the idea was months older btw, see https://github.com/openfrontio/OpenFrontIO/pull/2295). Now, we do it the other way around, by telling buildableUnits all unit types we want. Or we want them all (undefined). The downside is more data is send in the worker message. The upside is we have more options and can add more in this PR. This PR implements some of the leftovers in 3193 on top of 3213 and adds further improvements. (Some unrelated refactor/perf changes where moved out of this PR and into already merged https://github.com/openfrontio/OpenFrontIO/pull/3233, https://github.com/openfrontio/OpenFrontIO/pull/3234, https://github.com/openfrontio/OpenFrontIO/pull/3235, https://github.com/openfrontio/OpenFrontIO/pull/3236, https://github.com/openfrontio/OpenFrontIO/pull/3237, https://github.com/openfrontio/OpenFrontIO/pull/3238, https://github.com/openfrontio/OpenFrontIO/pull/3239) - **GameRunner**, **WorkerMessages**: _playerActions_ and _PlayerActionsMessage ._ Option to ask for no buildable units (null). It now has 3 modes: get all actions and all buildings (units undefined), get all actions and no buildings (units null), or get all actions and specific building (units contains Unit Types). - **GameRunner**: _playerActions_. fixes wrong assumption in PR 3213: that only if units was undefined, we have to know canAttack. ClientGameRunner wants to know both, in case of a click on non-bordering land, to decide if it should auto-boat using a Transport Ship. So units is not undefined (we only ask for Transport Ship now which has a positive effect on performance for each click/tap) but we need canAttack still. Solved by removing the unit === undefined check before _canAttack_ in _playerActions_. - **GameRunner**, **GameView**, **WorkerClient**, **WorkerMessages**, **Worker.worker**: added _playerBuildables_ / _buildables_ next to existing _playerActions_ / _actions_. With above solved, there was still no option to only get buildable units when the actions are not needed. While **StructureIconsLayer**, **NukeTrajectoryPreviewLayer**, **BuildMenu** and **UnitDisplay** need only that. To not make playerActions more convoluted with more params or so, i've added a new function _playerBuildables_ in **GameView** to only get buildable units (**GameRunner** _playerBuildables_). _playerBuildables_ has 2 modes: get all buildings (units undefined) or get specific buildings (units contains Unit Types). Also update some comments that mentioned .actions in **NukeTrajectoryPreviewLayer**. - **ClientGameRunner**, **PlayerPanel**, **BuildMenu**, **UnitDisplay**, **StructureIconsLayer** and **NukeTrajectoryPreviewLayer**: Since PR 3213, **StructureIconsLayer** and **NukeTrajectoryPreviewLayer** ask for specific types of units from **GameView** _actions_ (**GameRunner** playerActions). Now have the other files do the same. For example **BuildMenu** asks for the new _BuildMenuTypes_ when it calls ._buildables_ and **ClientGameRunner** asks for UnitType.TransportShip when sending a boat - **ClientGameRunner**: canBoatAttack now accepts BuildableUnit[] instead of PlayerActions so we can send it either actions.buildableUnits or just buildables. Have functions call myPlayer.buildables(tileRef, [UnitType.TransportShip]) when we only need a buildable unit and no actions. Or myPlayer.actions(tileRef, null) when we need actions but no buildable units. Or myPlayer.actions(tileRef, [UnitType.TransportShip]) when we need both actions, like canAttack, and a buildable unit. Then if needed send either actions.buildableUnits or buildables to to _canAutoBoat_ / _canBoatAttack_. - **MainRadialMenu**: needs all player buildable unit types including Transport Ship, so the _actions_ call argument for unit types can stay undefined (unchanged) there. - **MainRadialMenu**: now that **BuildMenu** uses _playerBuildables_ instead of _playerActions_, we must put data in _this.buildMenu.playerBuildables_. And since we're not putting the (unneeded) full _actions_ in there anymore, we can now put only the needed and expected _actions._buildableUnits_ in it. - **Game**, **PlayerImpl**, **StructureIconsLayer**: Typesafety and some added perf: new type _PlayerBuildableUnitType_ (see also the below point for how it is formed). So callers of _buildableUnits_ can never ask for the wrong type like e.g. UnitType.Train because it doesn't return data for that type. This type is now used in **PlayerImpl**, **BuildMenu**, **RadialMenuElements**, **StructureDrawingUtils** and **UnitDisplay** for that reason. And **InputHandler**, **StructureIconsLayer** and **UIState** (little more on that in point below). - **InputHandler**, **StructureIconsLayer**, **UIState**: In order to make type safety work for GhostUnit.buildableUnit.type too (line ~217 of StructureIconsLayer), changed type of interface _BuildableUnit_ to _PlayerBuildableType_. Which is only more accurate. Same for and this.structures and uiState.ghostStructure and with the latter, _renderUnitItem_ in **UnitDisplay** and _setGhostStructure_ in **InputHandler**. All Structures are of PlayerBuildableType (there are even some in PlayerBuildables that aren't Structures, but it is much more confined than UnitType). - **Game**: Typesafety and some added perf: added _BuildMenus_ and _BuildableAttacks_ in the same fashion that the existing StructureTypes was already used (simplified it a bit too, with it renamed _StructureTypes_ to _Structures_ and removed _isStructureType_). They can be used with .types or .has(). _BuildableAttacks_.has() is used in **RadialMenuElements**. _BuildableAttacks_ and existing _Structures_ now make up _BuildMenus_. Which is used in **BuildMenu**, **StructureIconsLayer** and **UnitDisplay**. Then _BuildMenus_ together with UnitType.TransportShip make up the _PlayerBuildables_. Which is used in **PlayerImpl** _buildableUnits_ (see point below). And with _PlayerBuildableUnits_ we get the new _PlayerBuildableUnitType_ (see above point on Game / PlayerImpl). - **RadialMenuElements**: replace non-central ATTACK_UNIT_TYPES in **RadialMenuElements** with centralized _BuildableAttackTypes_ too. Use _PlayerBuildableUnitType_ for more type safety (can't by mistake add UnitType.Train to its build menu). Make use of _BuildableAttackTypes_ instead of adding items hardcoded line by line in _getAllEnabledUnits_, just like we already did since PR 3239 with _StructureTypes_. And use _BuildableAttacks.types_ in the same fashion that existing _isStructureTypes_ (now Structures.types) was already used elsewhere. - **PlayerImpl**: _buildableUnits_ -- would do Object.values(UnitTypes) on every call. Now for better perf directly loop over player buildable units by using _PlayerBuildables_ (see above point). In this way we also exclude MIRVWarhead, TradeShip, Train, SamMissile and Shell so there are less unit types to loop through by default. Since a player doesn't build those by themselves, they are only build by Executions which use _canBuild_ directly and not _buildableUnits_. -- for more performance, do for loop instead of using .map and .filter, no intermediate array needed nor callback overhead. We just loop over the given units (which if undefined will contain _PlayerBuildables_). Also pre-allocate the results array to get the most out of it, even if V8 might already be very good at this. -- cache config, railNetwork and inSpawnPhase so they can be re-used inside the for loop. -- cache cost inside the loop -- it would check twice for tile!==null to decide to call findUnitToUpgrade and canBuild. Now once. -- eliminated double/triple checks for the same thing. It called _findUnitToUpgrade_ (and with that _canUpgradeUnit_) and then _canBuild_ which both check if player has enough gold for the cost of the unit type. And they both check if the unit type is disabled. Now we call private functions _canBuildUnitType_, _canUpgradeUnitType_ to first do checks on unit type level for early returns, and _findExistingUnitToUpgrade_ to find existing unit without doing anything extra. in a specific order to check everything only once. The public functions _findUnitToUpgrade_ and _canBuild_ have an unchanged functionality and we don't call them from _buildableUnits_ anymore. -- would get _overlappingRailRoads_ and _computeGhostRailPaths_ when canBuild was true. But this data is only meant for **StructureIconsLayer** and it logically only uses it when placing a new unit, not when upgrading one. Which is also commented on line 351 of **StructureIconsLayer**. So, we now only get overlapping railroads and ghost rails if we're not hovering to upgrade an existing unit. - **PlayerImpl**: _findUnitToUpgrade_: unchanged functionality, but have it call new private function _findExistingUnitToUpgrade_ to find existing unit. - **PlayerImpl**: _canBuild_: unchanged functionality, but have it call new private function _canBuildUnitType_ to do the checks it first did itself. And then new private function _canSpawnUnitType_ for the rest of the checks. This way we can call _canBuildUnitType_ and _canSpawnUnitType_ from _buildableUnits_ in a specific order to prevent double/triple checks. - **PlayerImpl**: _canBuildUnitType_: new private function to be shared by _buildableUnits_, _canBuild_ and _canUpgradeUnit_ to be able do unit type level checks in a specific order to prevent double/triple checks. Via parameter knownCost, _buildableUnits_ can send it the cost it already fetched so that it doesn't have to be fetched again. For caller _canUpgradeUnit_, the isAlive() check (which was previously only done in canBuild) is new but harmless, maybe even better to have also check isAlive() on upgrade now that Nations are also upgrading which might prevent some edge case bugs. - **PlayerImpl**: _canUpgradeUnitType_: new private function to be shared by _buildableUnits_ and _canUpgradeUnit_ to be able do unit type level checks in a specific order to prevent double/triple checks. - **PlayerImpl**: _canSpawnUnitType_: new private function to be shared by _buildableUnits_ and _canBuildUnit_ to be able do unit type level checks in a specific order to prevent double/triple checks. - **PlayerImpl**: _findExistingUnitToUpgrade_: new private function to be shared by _buildableUnits_ and _findUnitToUpgrade_ to be able do unit level checks in a specific order to prevent double/triple checks. - **PlayerImpl**: _isUnitValidToUpgrade_: new private function to be shared by _buildableUnits_ and _canUpgradeUnit_ to be able do unit level checks in a specific order to prevent double/triple checks. - **PlayerImpl.test.ts**: because of the isAlive() check in which is new for _canUpgradeUnit_ (see above at _canBuildUnitType_), the tests needed to have the players be alive at the start, in order to pass. - **BuildMenu**: use .find instead of .filter in canBuildOrUpgrade, a function we already needed to change. This is faster and prevents an allocation. **PERFORMANCE** As for calling ._buildables_ instead of unnecessarily getting ._actions_, there is an obvious win because there's less to send calculate and recieve. Also asking for only the needed buildings helps a lot (especially if TradeShip isn't needed, see the difference in benchmark in original #3193). But the real-world impact is hard to measure. gave it a try in #3193 and those results should be even better now. Now testing only _buildableUnits_ performance in a synthetic benchmark, we get these results. This is after other performance improvments so the base is already better than it was in original #3193: **BEFORE** (only buildableUnits itself) <img width="602" height="96" alt="image" src="https://github.com/user-attachments/assets/7770c0fa-a35e-42fc-90de-1de83242ec23" /> **AFTER** (only buildableUnits itself) <img width="603" height="91" alt="image" src="https://github.com/user-attachments/assets/a1578382-7010-4160-937c-7117bad18beb" /> ## 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: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
f09d9a3a5f |
Nations can overwhelm SAMs now 💥 (+ 3 little nation improvements) (#3246)
## Description: ### SAM Overwhelming (`NationNukeBehavior.ts`) On Impossible difficulty, nations can now destroy enemy SAMs by overwhelming them with coordinated atom bomb salvos. When no good nuke target is found (all trajectories intercepted by SAMs), the nations will: - Identify the easiest enemy SAM to destroy (lowest level first) - Calculate the total interception capacity of all covering SAMs and send enough bombs to overwhelm them (+1 extra per 5 needed to account for enemy building more SAMs during flight) - Plan launches in NukeExecution's Manhattan-distance silo order, tracking which silos have interceptable trajectories (wasted bombs) - Use a sliding window over parabolic flight times to find the best cluster of bombs that can arrive within half the SAM cooldown window - Compute per-bomb wait ticks to synchronize arrivals from silos at different distances - Skip launching if a salvo is already in flight - Upgrade the best SAM-protected silo when silo capacity is insufficient; wait and save gold when only gold is lacking https://github.com/user-attachments/assets/14fa592f-2902-4604-8e37-1eba2b2f0b85 ### 2-Player Endgame Handling (`NationNukeBehavior.ts`) - On Hard/Impossible with only 2 players remaining, `findBestNukeTarget()` directly targets the other player (bypasses all priority logic) - `getPerceivedNukeCost()` returns actual cost (no MIRV saving inflation) when only 2 players are left ### SAM Build Rate (`NationStructureBehavior.ts`) - Reduced SAM perceived cost increase per owned from 1.0 to 0.5, so nations build more SAMs ### Island Attack Variety (`AiAttackBehavior.ts`) - `findNearestIslandEnemy()` now collects up to 2 reachable candidates and has a 33% chance to pick the second-nearest, adding variety to boat attack targeting ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
4e62114ea0 |
Improve nations 🤖 (#3206)
## Description: - `AiAttackBehavior`: Because bots delete stolen structures now, nations prioritize attacking bots with structures - `NationMIRVBehavior`: Nations no longer MIRV enemies who already got MIRVed in the last 30 seconds. Some humans complained about getting double-MIRVed by nations. And in games with very high starting gold, ALL nations MIRVed the same player (stop steamroll logic). - `NationAllianceBehavior`: Fixes a comparison logic bug (Thanks to Deshack) - `NationNukeBehavior.ts`: Little atom bomb perceived cost balance change - `MIRVExecution`: To make sure the MIRVing nations are attacking the MIRVed nations (even if they don't share a border), the relation gets updated in both directions now. - `SinglePlayerModal` & `HostLobbyModal`: Update the default difficulty to "Medium" (to synchronize the defaults with the public game default) ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
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 |
||
|
|
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 |
||
|
|
96aa39a415 |
Improve nations 🤖 (#2817)
## Description: ### Refactor - Moved `maybeSpawnWarship()` from `NationExecution` to `NationWarshipBehavior` - Moved `maybeAttack()` (and sub-methods) from `NationExecution` to `AiAttackBehavior` ### Betrayal - Added nice betrayal logic in `maybeBetray()`. Previously that method was basically just a placeholder for a future implementation. ### Attacking - Added `veryWeak()` attack strategy for hard and impossible difficulty nations attack orders to target MIRVed players with higher priority - Optimized the `weakest()` attack strategy so that nations don't attack stronger players. This should make nation-attacks feel less random (humans complained in discord) - `findNearestIslandEnemy()` and `randomBoatTarget()` also no longer returns stronger players - `afk()` and `hated()` attack strategies no longer return MUCH stronger players - Several tiny refactorings, fixes and balance optimizations in `AiAttackBehavior` ### Emojis - Added some `canSendEmoji()` because I saw some "cannot send emoji" warnings in the console ## 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 |
||
|
|
f73953c240 |
☢️ Nations send much better nukes now (Part 2) ☢️ (#2779)
## Description: ### Refactor - Moved `findBestNukeTarget()` (and child methods) from `AiAttackBehavior` to `NationNukeBehavior` because it makes more sense. - `NationNukeBehavior`: Renamed `mg` to `game` (So its similar to the other nation .ts files) - Moved the `removeOldNukeEvents()` method a bit ### New features - Impossible difficulty nations are now optimized for SAM outranging! - 1 of 3 nations is now a hydro-nation. They cannot send atom bombs. They save up for hydros. This is to reduce atom-bomb-spam on the map. - On impossible difficulty, the crown nukes the second place now (in FFAs) - On hard and impossible difficulty, nations now ignore the perceived cost for nukes if they get heavily attacked. Reasoning: They should stop saving for MIRV, they should defend with all their gold! - On medium, hard and impossible difficulty, nations no longer throw nukes at places where another team member already has a nuke "in the flying process" - Optimized `lastNukeSent` a bit (to respect nuke radius) - Adjusted `maybeSendNuke()` to use 30 instead of 10 randomTiles on impossible difficulty to improve chances of finding a perfect SAM outranging spot - On impossible difficulty, nations now ignore their "most hated enemy" if the crown has 50%+ of the map (Very big danger). Instead they are nuking the crown. - Added `isFriendly` check to `findStrongestTeamTarget()` ### Media SAM outranging: https://github.com/user-attachments/assets/d1e88bb6-0060-400b-9c16-24d7399f5949 Team game nukes not hitting the same spot: <img width="1160" height="708" alt="Screenshot 2026-01-03 042236" src="https://github.com/user-attachments/assets/c017fb3c-3e3f-45fb-9d45-dd4caba7a59f" /> ## 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: iamlewis <lewismmmm@gmail.com> |
||
|
|
2f7c9eb930 | Merge branch 'v28' | ||
|
|
793efe4dd1 |
Revert "Special bot names (#2552)"
This reverts commit
|
||
|
|
e4a6e3bd20 |
Revert "Special bot names 2 (#2609)"
This reverts commit
|
||
|
|
23e4bf6725 |
☢️ Nations send much better nukes now (Part 1) ☢️ (#2756)
This is a very important PR for HumansVsNations (But also for
singleplayer).
Humans will throw lots of nukes onto nations, but nations didn't do
that. Until now :)
## Refactor
- Moved all the nuking logic to the new file `NationNukeBehavior.ts`
- Moved `randTerritoryTileArray()` and `randTerritoryTile()` to the new
file `NationUtils.ts` because we need that method in multiple places now
- Because we already have an `NationUtils.ts` (It contains the method
`createNationsForGame` for HumansVsNations) I renamed the old one to
`NationCreation.ts` to avoid confusion
## Bug fixed
- `allRelationsSorted()` in `PlayerImpl` returned dead players all the
time... Which caused nations to not attack / send nukes in some cases...
## Nuke-sending features / improvements
- On hard and impossible difficulty, nations no longer make sure that
nukes will only hit inside of their targets border. This logic very
often stopped nations from throwing nukes. Now their nukes are allowed
to hit TerraNullius (=> ocean!). And in team games, it's even allowed
that their nukes hit other non-friendly players as well! This is very
important for HumansVsNations.
- The basic check for SAMs now gets skipped if we are on easy difficulty
(easy nations are not smart enough to do that)
- I improved the basic check for SAMs (medium difficulty) a bit (nations
send less nukes into SAMs)
- On hard and impossible difficulty, we now use the new method
`isTrajectoryInterceptableBySam()` to avoid SAMs completely. It's
mirroring `NukeTrajectoryPreviewLayer.ts` logic a bit.
- I added "perceived cost" to simulate nations saving up for a MIRV
(Otherwise most hard/impossible nations will spend all their gold on
nukes). But if we are in a team game (MIRVs are not relevant) or if we
already saved up for a MIRV, the "perceived cost" gets ignored.
- Updated the "most hated player" selection in `findBestNukeTarget()` to
ignore very weak players. We don't need to throw nukes at players which
we can easily steamroll by land.
- Added `findFFACrownTarget()` to nuke the crown (based on difficulty).
- Added `findStrongestTeamTarget()` to nuke the strongest team.
- Updated `randTerritoryTile()` so that it has a higher chance of
returning the tiles of a
"leftover-nuked-to-death-player-with-some-tiles-left": `if
(p.numTilesOwned() <= 100) {return
random.randElement(Array.from(p.tiles()));}`.
- Changed `const range = nukeType === UnitType.HydrogenBomb ? 60 : 15`
to `config().nukeMagnitudes(nukeType).inner`. Should make more sense.
- Adjusted `nukeTileScore()` to search for units in
`this.mg.config().nukeMagnitudes(nukeType).inner` instead of fixed 25
- Adjusted `nukeTileScore()` to account for unit levels (levels got
ignored previously). Also increased score for ports from 10_000 to
15_000.
- I made sure that nations can nuke EVERY SINGLE TILE from an enemy,
even if the enemy has no structures ("Prefer tiles that are closer to a
silo" can no longer make the `nukeTileScore()` drop too much,
`bestValue` in `maybeSendNuke()` starts at -1 now)
- In the entire nuking logic, factories were missing. Now they are
added.
## Media
Nation team vs. nation team: They are nuking the very last pixels of
red, just like humans would do it 😀
<img width="915" height="683" alt="image"
src="https://github.com/user-attachments/assets/109c7921-b959-4aa9-a971-0d7742971686"
/>
Hard difficulty FFA game: Nations throwing much more nukes. And they are
nuking the crown.
https://github.com/user-attachments/assets/a6e43924-a6ca-4b1a-a578-4e4f8252e383
Lots of nukes flying:
https://github.com/user-attachments/assets/8fc4edad-a6e6-4476-8a86-08cdef58169e
## 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: iamlewis <lewismmmm@gmail.com>
|
||
|
|
d321c08d92 |
Nations now gang up on players and prioritize traitors/AFK players 🤖 (+ other improvements) (#2730)
## Description: - **Nations now specifically target AFK players, traitors, and players who are already being attacked (they gang up on them).** Depends on the difficulty. - Added ally assistance directly to the attack order (instead of calling it separately beforehand). - Added ally assistance to `findBestNukeTarget`. - Removed some checks from `findBestNukeTarget` (not necessary; better checks will follow in a dedicated nuking PR). - Relation updates on attack now depend on difficulty (makes Easy & Medium nations a bit easier). - On betrayal, every nation in the game previously received a –40 relation penalty toward the betrayer. That was too extreme, so now this only applies only to neighbors. - Nations send fewer alliance requests now; it felt like too many before. - In team games, nations may now reject alliance requests more often (depending on difficulty). - To ensure there are enough non-friendly players to stop the crown with nukes, nations may now reject alliance requests if the other player has too many alliances (on Hard and Impossible difficulty). - Rebalanced nation emoji usage a bit. - Nations may now send an emoji when they get MIRVed. ## 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: iamlewis <lewismmmm@gmail.com> |
||
|
|
5d52f73278 |
The clown is gone! 🤡 Nations send much better emojis now (#2696)
## Description: Previously, nations just spammed these two rather toxic emojis: 🤡😡 They now send fewer emojis while attacking, and the clown emoji is reserved for special cases. They got the ability to send emojis in much more cases: - Human didn't donate enough for relation update - Human did donate an ok amount - Human did donate a lot - Responding to emojis that they get sent from a human - Nuke sent - MIRV sent - Retaliation warship sent - Traitor tries to ally - Threat asks for / accepts an alliance request - Disliked human tries to ally - Friendly human tries to ally - They are getting attacked by very much troops - They are getting attacked by very little troops - Congratulating the winner - Bragging with their crown - Charming their allies - Clown-Emoting traitors - Easteregg: Sending a rat emoji to very small humans ## 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: iamlewis <lewismmmm@gmail.com> |
||
|
|
e1d0773672 |
Fix bots no longer attacking humans 🤖 For v28 (#2697)
## Description: A small number of people are complaining that bots no longer attack them and that "This has broken the factory farming strat". In the old #2550 I somehow added that bots on easy difficulty don't attack humans and nations anymore. And public games are on easy difficulty now. But I think the difficulty should actually only change nation behavior, not bot behavior. Their attacks are harmless anyways. So lets remove that little check. Also let `shouldAttack()` return true for bots. ## 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 |
||
|
|
6d2ac30526 |
Fix bots no longer attacking humans 🤖 (#2690)
## Description: A small number of people are complaining that bots no longer attack them and that "This has broken the factory farming strat". In the old #2550 I somehow added that bots on easy difficulty don't attack humans and nations anymore. And public games are on easy difficulty now. But I think the difficulty should actually only change nation behavior, not bot behavior. Their attacks are harmless anyways. So lets remove that little check. Also let `shouldAttack()` return true for bots. ## 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 |
||
|
|
6afaf932a5 |
Make easy and medium nations less aggressive 📊 (#2671)
## Description: 1. Players complained that they have problems allying with nations in the earlygame. So I added an `isEarlygame()` check to `AllianceBehavior`. This should make the easier difficulties much easier :) 2. The attack order of nations now depends on the difficulty. Easy and medium nations got dumbed down, they now take nuked territory before retaliating against attacks again. 3. The attack rate now depends on the difficulty. Easy nations are reacting slower than impossible nations (to make sure the number of sent alliance requests stays the same I removed the difficulty check in `maybeSendAllianceRequests()`). 4. On easy and medium difficulty nations will sometimes just skip an attack if the enemy is a human (`shouldAttack()`). But this did not apply for the nuking logic. Now it does, which makes the easier difficulties a bit easier. 5. I tuned the `getBotAttackMaxParallelism()` method a bit. The nations are doing a bit less parallel bot attacks now, which makes the easier difficulties a bit easier. 6. The settings in MIRVBehavior now depend on the difficulty. On easy difficulty, nations will only send MIRVs very rarely. 7. Unrelated MIRVBehavior Cleanup: There was a 2 second cooldown and cache logic. But it was completely useless because `considerMIRV()` is only called every 4-8 seconds by NationExecution. So I removed it. 8. Unrelated little cleanup: I made a couple of methods `private` ## 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 |
||
|
|
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> |
||
|
|
af1e05961c |
Cleanup nations (Part 2) 🧹 (#2647)
## Description: 1. Moved the currently very small betrayal logic from `AiAttackBehavior` to `NationAllianceBehavior` because it makes more sense to have it there. 3. Very small bugfix in `AiAttackBehavior::shouldAttack()`: the numbers in the two `random.chance` calls were the wrong way round. 4. `NationExecution` was quite big and a lot of it was about MIRVs. So I moved all the MIRV logic to the new `NationMIRVBehavior`. 5. `emoji()` and `maybeSendEmoji()` did not really fit in `AiAttackBehavior`. So I moved it to the new `NationEmojiBehavior` (and did some renaming for clarity). I'm planning to extend that class in a future PR. 2. Reordered methods in `AiAttackBehavior` to easily find related methods. 6. Reordered methods in `NationExecution` to easily find related methods. ## 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 |
||
|
|
4d5bb7a835 |
Cleanup nations (Part 1) 🧹 (#2637)
## Description: 1. Using the wording `"Nation"`, `"FakeHuman"` and `"NPC"` at the same time is confusing. So I renamed every mention of `"FakeHuman"` and `"NPC"` in the entire project to `"Nation"`. Just like they are called ingame. 2. `BotBehavior.ts` was originally intended for sharing the logic between nations and bots. But at the moment, the logic there isn't really shared and it's basically just about attacking. So I renamed `BotBehavior.ts` to `AiAttackBehavior.ts`. I use "Ai" to indicate that this file is used by bots AND nations. 3. Moved `execuction/utils/AllianceBehavior.ts` to `execuction/nation/NationAllianceBehavior.ts` to make sure everybody understands that this file is not about alliances in general. It's just about nations and how they handle alliances. 4. Removed `difficultyModifier` from `DefaultConfig`. It's unused and I think we usually want to finetune the difficulty instead of using that method. 5. Added `assertNever` in all `switch (difficulty)` default cases. ## 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 |
||
|
|
dfe33a05e9 |
Improved the nation alliance request logic 🤝 Massive upgrade to singleplayer fun (#2606)
## Response to alliance requests Previously the way nations responded to alliance requests was quite simple / boring / exploitable. Basically you couldn't ally them if you had a bad relation with them, or if you had too many alliances. Otherwise they would just take it. Now there is a **complete decision tree which is based on the difficulty**. The nations should also feel more human now. For example, just like humans, nations will now consider to take an alliance even if you have a bad relation with them (If you are a threat). Also, nations no longer check if YOU have too many alliances. Now they do what humans do: Check if THEY have too many alliances (they want to be able to attack somebody). Another big change is the default case: Previously it was just `return true`. Now it's `return isAlliancePartnerSimilarlyStrong`. So they do what humans do: Take a quick look at their troop count before allying them. ## Sending alliance requests Previously alliance requests were sent randomly. Quite boring. Now we use the same decision tree as for responding. ## Alliance extension requests They also use the same decision tree. ## Tests Tested it a lot in singleplayer. I have planned to add unit tests for all the nation/bot stuff in the upcoming cleanup phase. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin |
||
|
|
099337d83e |
Special bot names 2 (#2609)
## Description: Second and last changes to special bot names. Sorry to anyone who might feel left out, the list is non-exhaustive. ## 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 |
||
|
|
427e462fe5 |
Revamp nation/bot enemy selection 🗡️ (#2550)
## Description: I closed my previous PR #2533 which was already reviewed by evan (but not yet merged) because I noticed some issues. Which led me to changing the enemy selection entirely. Nations / Bots previously had a fixed enemy which they kept for 100 ticks (10 seconds). This could make them react too late and feel slow. Now they are a bit more responsive. But the main benefit: Without a fixed enemy we can do multiple sendAttack() on the same tick, which allowed me to give impossible nations extremely efficient parallel bot attacks: https://github.com/user-attachments/assets/38f65623-fbf0-4e98-a833-5fcba2ee6eee Previously nations were so slow in taking out bots that you could even encircle them on the Archiran map... Now they are like 200% faster (but only on the impossible difficulty) ## Nuke enemy selection Previously, the enemy for troop attacks and nukes was identical. Now, as we no longer have a fixed enemy in BotBehaviour, I added findBestNukeTarget() to select better nuke-targets. I will probably open a PR soon which makes nations nuke the crown :) ## Betrayal logic While revamping the attack logic I had to work on the betrayal logic, which was quite confusing, with many negations. And the betrayals were just random. So I made it easier to understand with maybeBetrayAndAttack(). Now it does betray friends if we have 10 times more troops than them. I will improve that method in a future PR, but already now it should be better than just betraying randomly. ## Attack order Previously, nations attacked in this order: - TerraNullius (Untaken land and nuked territory) - Bots - Retaliate against incoming attacks Now its in this order: - TerraNullius (Untaken land) - Retaliate against incoming attacks - Bots - TerraNullius (Nuked territory) So the changes are these: - After throwing a nuke onto a nation, they will no longer ignore incoming attacks. Previously they attacked the nuked territory first. Very common singleplayer problem. - Nations now retaliate against incoming attacks before attacking bots. Previously you could attack a nation but they did not care because there were still bots left. I also changed the attack order of bots a bit (retaliate before attacking randoms), but that isn't even noticeable. ## Big bug fixed Additionally, I fixed a big bug: selectEnemy() oftentimes returned null (because of enemySanityCheck) and therefore no attack happened. This was especially visible in games where nations are surrounded by friends (Team games and nations vs humans). This was also the reason why Enzo could play nations vs humans in singleplayer and NO NATION of the much bigger nation team would try to attack him. ## 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 |
||
|
|
327d425fd5 |
Fix obvious typos (#2585)
## Summary - fix obvious spelling typos flagged by codespell across docs, tests, comments - no functional changes ## Testing - pre-commit hooks (eslint/prettier) ran during commit |
||
|
|
8f32746bb2 |
Special bot names (#2552)
## Description: Special bot names. If the solution seems convoluted for such an easy thing, that is because: not all bots find a spawn position, so only assign a candidate name after finding a spawn. And the first few are almost always overwritten by Nation spawns so the first 20 just get a random name. Only then do we assign from the provided lists. For the random names, some might get the same name but that's not an issue as no-one will notice and they're off the map quite fast anyway. ## 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 |
||
|
|
807151b723 |
Revert "Improve random spawn (#2465)"
This reverts commit
|
||
|
|
2b2200c808 |
Improve random spawn (#2465)
## Description: After the v27 playtest, some players experienced instant death on spawn. The issue was that the human random spawn occasionally coincided with a bot’s spawn. Previously, bot spawns didn’t account for human spawn locations and could appear on the same tile, now they don’t. ## Please complete the following: - [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> |
||
|
|
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> |
||
|
|
e8a04d9a72 | Merge branch 'v26' | ||
|
|
4144628808 |
Revert "Halloween Event (#2285)"
This reverts commit
|
||
|
|
f607f6d66b |
Nations rarely sent boats, now they do! (#2161)
## Description: ### Nations rarely sent random boats. Now they are sending twice as many. It feels right now, not too many and not too few random boats. To make sure that small island nations with, for example, 10k troops don't repeatedly spam boats into an nation with 1.5M troops (that makes no sense), they no longer send boats to opponents which have more than twice the amount of troops. | my optimizations - 27s into the game - `8 Boats active` | openfront.io live - 27s into the game - `only 2 Boats active` | | :---: | :---: | | <img width="2560" height="1068" alt="27s into the game - 8 Boats active" src="https://github.com/user-attachments/assets/258e3d69-d86b-4c68-94ec-ade04c6c18b3" />|<img width="2560" height="1068" alt="27s into the game - 2 Boats active" src="https://github.com/user-attachments/assets/343603d6-e95d-402c-a660-cc54f715d148" />| | my optimizations - 1m20s into the game - `much more boats` | openfront.io live - 1m20s into the game - `only 4 boats active` | | :---: | :---: | | <img width="2560" height="1068" alt="1m20s into the game - much more boats" src="https://github.com/user-attachments/assets/bbe35603-8db4-4d82-af45-c77b8ef0dcbf" /> | <img width="2560" height="1068" alt="1m20s into the game - 4 boats" src="https://github.com/user-attachments/assets/f698794d-f94f-49fb-a09a-1a89b292456f" /> | ### There was a bug in the random boat sending. It did not check if the target is the player himself. That caused console warnings and a reduced amount of boat-sending. ### The Hiding-Strategy on small islands on the impossible difficulty is now harder! Because the random-boat-sending-method preferred large landmasses instead of small islands (it randomly selects a valid tile), human-players could easily hide on them, play a warship-infestation-strat and nearly NEVER get boat-attacked by nations. I implemented that the random boat functionality now searches for bots and untaken tiles before searching for nations / humans. That way, they will try to take some of these tiny islands before the human-player does. Also its cool to see nations playing on the entire available land-tiles instead of just the bigger landmasses. Fixes #1916 (Please check this issue for screenshots and more info) | my optimizations - 6m53s into the game - `only 5 islands not taken!` | openfront.io live - 6m53s into the game - `19 islands not taken!` | | :---: | :---: | | <img width="1078" height="876" alt="6m53s into the game - 5 islands not taken!" src="https://github.com/user-attachments/assets/6c103ca1-7d4c-4d0d-948f-3313839302a7" /> | <img width="1102" height="852" alt="6m53s into the game - 19 islands not taken!" src="https://github.com/user-attachments/assets/01cc512d-71c9-47aa-b4b1-f7cd5123a782" /> | | my optimizations - 6m53s into the game - `all islands taken!` | openfront.io live - 6m53s into the game - `8 islands not taken!` | | :---: | :---: | | <img width="2154" height="489" alt="6m53s into the game - 0 islands not taken!" src="https://github.com/user-attachments/assets/fc8000b2-de28-4d47-859d-0cc3c6d28ac3" /> | <img width="2181" height="317" alt="6m53s into the game - 8 islands not taken!" src="https://github.com/user-attachments/assets/093243de-f6dc-41ee-ab5e-7e485fe27646" />| ### Nations now boat-attack other nations more often! If there are two nations on two very large landmasses, which are divided by water, they nearly NEVER attacked each other. Most attack-functionality relies on the enemies sharing a border. If they don't have one, the only possible attack-mechanism is the random-boat-sending. But for very large landmasses (=> very large number of coastal tiles) it can take a long, long time before it randomly selects an enemy-tile. With the bug I described above ("did not check if the target is the player himself") it took even longer. And on the world-map, the nations have to go over iceland, this is also very unlikely. So I implemented the method selectNearestIslandEnemy, which specifically doesn't cares about borders. It makes sure that a nation always has someone to attack. This method only gets used as a fallback, and only if a nation has no borders with anybody. Fixes #1916 (Please check this issue for screenshots and more info) | my optimizations - 29m41s into the game - `China won and is in the process of killing the last enemy` | openfront.io live - NEARLY A HOUR into the game - `UK finally won. There are still 14 players on the map...` | | :---: | :---: | | <img width="2560" height="1068" alt="localhost_9000_" src="https://github.com/user-attachments/assets/a8107b88-19c8-4df7-a3fd-76d3d3e05d8e" /> | <img width="2560" height="1068" alt="openfront io_ (1)" src="https://github.com/user-attachments/assets/0d80e503-d5ed-41d4-a103-33dffc302001" /> | ## 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 |
||
|
|
b69adf70b3 |
Halloween Event (#2285)
## Description: Changed theme colors for an "autumn" ambiance: <img width="395" height="254" alt="image" src="https://github.com/user-attachments/assets/67bd07d3-e74b-49e2-ba0b-c87958767d45" /> Changed structures pixel art: <img width="756" height="605" alt="image" src="https://github.com/user-attachments/assets/5158ae81-0641-413c-8862-367259496a9a" /> Change existing FX with new halloween-themed ones: https://github.com/user-attachments/assets/fb99be49-43cd-4d85-ad77-8c153070edaf Added new FX playing randomly on the map: https://github.com/user-attachments/assets/16631113-77e6-4b8c-b1b6-c147f5f1d275 Added a couple of new emojis, which are used by the bots when attacked: 👻🎃 ## 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 |
||
|
|
136cfa1316 |
improve nation ai (#2172)
## Description: 1. Create forceSendAttack function so nations expand faster at the start (their reserve troop ratio was too low, causing them to skip the first attack 2. modify the perceived cost modifier to reduce the number of defense posts built. 3. Updated how random land is selected to avoid player.tiles() since that can be millions of entries. 4. Improve performance of valueFunction by using closestTile and reducing the number of tiles checked. 5. Nations now launch hydros if they have enough gold. 6. used boundBox instead of bfs because doing a large bfs for h-bombs can get expensive. 7. Modified perceived multiplayer to remove cap and scale super-linearly to discourage nations from spamming too many building. Instead they are more likely to spend that money on nukes. ## 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 |
||
|
|
b03f9778db |
Fix nations break alliance too ealry bug (#2123)
Betrayal was being considered too early (inside shouldAttack), causing alliances to break before calling attackChance. - [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 regression is found: abodcraft1 |
||
|
|
d83a66196a |
bugfix: Nations rarely launch nukes (#1860)
## Description: Simplify nation enemy selection to make nations more likely to launch nukes. Partially fixes #1855 by addressing a v24 regression in nation behavior. ## 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 |
||
|
|
8308d7f1e7 |
Nations send emoji when declining assistance requests (#1911)
## Description: Nations will now send emoji when declining assistance requests. ## 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 |
||
|
|
81bd98c8d6 |
Nations send emoji when declining assistance requests (#1911)
Nations will now send emoji when declining assistance requests. - [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 Cancel alliance requests if the recipient attacks (#1733) Problem: attacking a player right before accepting an alliance request is very effective since the requester can't fight back or reclaim his territory without canceling the alliance and being penalized with the traitor debuff. Change: - Attacking a player after he requested an alliance automatically rejects the request - No changes to existing attacks in both directions, only new attacks affect the request - [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). regression is found: IngloriousTom |
||
|
|
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> |