Resolves#2464
## Description
This PR fixes a bug where the alliance renewal popup remained visible
after an alliance was broken or betrayed.
The issue occurred because renewal UI events were tied to player
identifiers instead of the unique allianceId.
When a player had multiple alliances, breaking one alliance did not
correctly remove the associated renewal popup.
This change ensures that renewal popups are correctly removed only for
the specific alliance that was broken.
### What was wrong
Alliance renewal UI events were previously associated implicitly with
players.
This caused incorrect behavior when a player had **multiple alliances**,
because **players are not unique identifiers of an alliance**.
As a result, breaking one alliance could leave stale renewal popups
visible.
### What was changed
- Alliance break logic now relies on **allianceId**, not player IDs
- Renewal popups are removed **only for the specific broken alliance**
- Alliances involving the same player but different allianceIds are
unaffected
- Added tests to ensure this bug cannot reappear
### Result
- Renewal popup disappears immediately when an alliance is
betrayed/broken
- No unintended removal of other alliance renewal popups
- Correct behavior even when a player is involved in multiple alliances
## 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:
assessin.
## 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>
## Description:
Removes the check to break alliance after nuke is launched, and alliance
breaking is now determined before tiles are randomly chosen for
destruction. (It is now slightly stricter, I believe, as a weighted
average calculation is a little tricky)
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
bibizu
## Description:
Introduces the Greek island, Lemnos, as a map. The island is both fun
and challenging to play (because of the terrain and elevation) and this
addition was inspired by Altis from the game [Arma
3](https://armedassault.fandom.com/wiki/Altis). The nation names are set
based on the real landmarks, towns, and regions.
<img width="2190" height="1791" alt="image"
src="https://github.com/user-attachments/assets/a7a6de54-f376-43ac-87da-f20aecfebbe0"
/>
<img width="1994" height="1608" alt="image"
src="https://github.com/user-attachments/assets/bc280780-298f-4342-8313-db6cc27ac188"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
bijx
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves#2491
## Description:
Adds pause/unpause functionality for private multiplayer games. Only the
lobby creator can pause the game, and all players see a pause overlay
when the game is paused.
**Key features:**
- Lobby creator sees pause/play button in control panel (alongside
existing singleplayer/replay controls)
- Server validates that only lobby creator can toggle pause
- All players see "Game paused by Lobby Creator" overlay when paused
- Game state freezes (no turn execution) while paused
- Unpause resumes normal gameplay
**Implementation details:**
- Server-side pause state (`isPaused`) prevents turn execution during
pause
- Each client receives `isLobbyCreator` flag in `GameStartInfo` to
show/hide pause button
- Added `TogglePauseIntent` that broadcasts to all clients via
`NoOpExecution`
- New `PauseOverlay` component (shows in single player also)
## 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:
furo18
<img width="1459" height="861" alt="Screenshot 2025-12-20 at 15 16 33"
src="https://github.com/user-attachments/assets/f5a3222f-f54b-473c-b0f6-104ce4c1e7a8"
/>
## Description:
* Remove unused otel creds
* Remove unused R2 creds
* remove left-over BASIC_AUTH
* Generate an admin token on startup
* Removed kick_player since lobby creators already have ability to kick
player
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
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
## Description:
Reverts #49b01d8
Move cloudflare tunnel creation back into startup.sh. This keeps the
infra outside of the main app.
## Please complete the following:
- [ ] I have added screenshots for all UI updates
- [ ] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [ ] I have added relevant tests to the test directory
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Relevant for singleplayer and HumansVsNations:
Humans sometimes try to flood the entire ocean with warships. The goal
is to dominate the trade and to block transport ships.
The already existing `trackTransportShipsAndRetaliate` and
`trackTradeShipsAndRetaliate` methods can't stop these large scale
infestations, the nations are completely helpless.
The new `counterWarshipInfestation` method checks if a nation is one of
the top 3 richest players (Enough money for warships) and if any enemy
(or enemy team) has accumulated more than 10 (for teams total 15)
warships, then builds a counter-warship targeting that threat.
This feature only activates on Hard or Impossible difficulty.
Thats how it can look, nations send out a warship every couple of
seconds, until the infestation threat is gone:
<img width="779" height="670" alt="Screenshot 2025-12-20 160600"
src="https://github.com/user-attachments/assets/25040077-e7db-4720-aea4-7c230afe05ea"
/>
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
## Description:
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
## Description:
Returned code that spawns nations around predefined coordinates from the
map manifest.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
nikolaj_mykola
## Description:
SAMs will now always hit their target instead of missing sometimes.
Describe the PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Lavodan
## Description:
Add visual indicators to territory borders that reflect diplomatic
relationships between players, making it easier to identify relations at
a glance.
### Problem Statement
Currently, players must check diplomatic status through other UI
elements. There's no immediate visual feedback on the map showing which
borders represent embargo or friendly relationships.
### Benefits
1. **Improved Gameplay Clarity**: Quickly identify diplomatic
relationships without opening menus
2. **Strategic Awareness**: Visual feedback helps make tactical
decisions about border defense
### Proposed Solution
Tint territory borders based on neighbor relationships: embargo red,
friendly green
### Implementation Details
- Border variants are based on this._borderColor (theme/style handling
unchanged) computed in the constructor and stored in userSettings
UnitView
- Apply tinting to checkerboard for defended borders
- borderColor() checks all neighbors to determine the worst relationship
status
- Embargos take priority.
<img width="1193" height="601" alt="image"
src="https://github.com/user-attachments/assets/cb516402-4f4b-473c-a31b-02397fee9203"
/>
<img width="959" height="682" alt="image"
src="https://github.com/user-attachments/assets/de01a4b9-0fd4-44b2-895d-96705b6cf30b"
/>
## 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
## Description:
SAMs will now always hit their target instead of missing sometimes.
Describe the PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Lavodan
Resolves#2602
## Description:
tldr: `npm run docs:map-generator`
Adds documentation to the `map-generator` go code.
This has no functional changes, other than the renaming of the package.
I used the github url, though this can be set to anything as long as it
contains a `.` so that the docs parse it correctly. Go doc best
practices seem a little verbose and terse, but attempted to comply
Future Facing (to get these docs viewable without running locally):
- Wait until the -http issue is sorted, then these are easy to
statically host alongside builds
- Could use the legacy `godoc`
- Could do formatting after outputting the `txt` output
## Change List:
- Add documentation to all types/fns in map-generator go code
- Ensure this outputs correctly with `go doc`
- Add `docs:map-generator` command to package.json. This runs `go doc`
in `map-generator` w/ appropriate flags to generate full documentation.
(see notes in readme)
- rename `map-generator` module to work around pkgsite assuming all
packages without a . are stdlib (this makes `-http` work at all)
- Add new sections to README and update existing sections
- Add additional references to locations in the primary code base where
things can be found
- Update documentation in the ts theme files to add output color
mappings - this ensures that everything needed to trace the input file
-> in game rendered asset is fully documented.
## 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:
tidwell
## Description:
Saw lots of `"cannot send ship to Player"` warnings in the console.
It was caused by `randomBoatTarget` not checking if the target is
reachable by boat.
It also caused the for loop in `randomBoatTarget` to exit too early (=>
no boat sent).
So I added a `canBuildTransportShip` check. Because this runs expensive
(?) pathfinding I added a `unreachablePlayers` Set to optimize
performance.
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
## Description:
This 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>
## Description:
Following the hotkey cursor price textbox addition of #2650, this
feature adds the option to enable and disable the visual feature via the
User Settings menu or the Basic Settings modal in game. Also added a
[new icon](https://thenounproject.com/icon/pay-per-click-2586454/) for
the Basic Settings modal from the Noun Project and added credit for it
to the `CREDITS.md` file.
### Video Demo
https://github.com/user-attachments/assets/1667081e-45e3-4b11-9bda-3f00c341e03c
### User Settings Menu
<img width="1029" height="1436" alt="image"
src="https://github.com/user-attachments/assets/e4e6bf6d-db59-463a-81fb-f622ef6e3931"
/>
### Basic Settings Menu
<img width="964" height="1545" alt="image"
src="https://github.com/user-attachments/assets/6b083655-b96e-4937-95d6-f3458858f03d"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
bijx
## Description:
This PR removes the nation strength. Reasoning:
- It is currently unused. The backstory can be found in #2498
- It forces map-makers to do balancing work, which is probably not a
good idea
- It increases map-making work
- It increases nation balancing complexity by a lot (we need to have all
the json files in mind)
- It makes humans avoid map areas completely because there is always
that one, same, strong nation
- The map lead Nikola123 wants to "not deal with the stupid nations and
their balancing"
If the goal of nation strength was to make them feel different I would
suggest a nation personality system. Nations that love to boat, love to
ally, love to nuke, love to fullsend, etc.
Link to a discord discussion about nation strength:
https://discord.com/channels/1359946986937258015/1360078040222142564/1450973197251117218
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
## Description:
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
## 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
## Description:
Add some christmas spirit in the emoji list
🎁🌟🎅🎄⛄
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
IngloriousTom
## Description:
Saw this in an Enzo video about beating impossible nations:
You can just donate 1% gold / 1% troops to a nation to get a friendly
relation with them.
This PR adds randomized minimum donation requirements based on the
difficulty.
Randomized in a way that there is a minimum someone has to donate to
surely get the relation improvement, but you can also gamble and send
less.
For troop donations, the minimum is calculated based on what percentage
of their troops the sender donated.
For gold donations, it's fixed values. We cannot use percentages here
because “having nearly no gold” is a usual case. Donating 100% of your
gold wouldn’t hurt if you just spent all your gold on buildings.
I tried to add tests for this but it's really horrible. Because the test
would have to wait until the relation update from the alliance accepting
is gone (we need to have an alliance to send stuff), has returned to
Neutral, and then changes back to Friendly after the donation.
## 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>
## Description:
This PR centralizes all username validation using UsernameSchema with a
set maximum, minimum, and a regex pattern,
It also removes sanitization, as all places where the username would be
sanitized on the server have been gatekept, so no unvalidated usernames
can get onto the server past the ClientMessageSchema safeParse in
GameServer's on message func.
Here is how the errors look if that happens, Note that if the client is
funtioning correctly and the user doesn't manually send a WS message,
they should never see this. The screenshots are from a debug build where
client uname validation was disabled.
<img height="300" alt="error message too short"
src="https://github.com/user-attachments/assets/1b7ac32c-2f03-40fb-8ce9-1f4ab66100bd"
/>
<img height="300" alt="error message bad regex"
src="https://github.com/user-attachments/assets/c78b4114-7e4b-4d39-a135-4cab3ad52c0b"
/>
Profanity sanitization was not changed.
Additionally, the censor tests were updated to reflect the new
expectations.
Jose was added to the jest config as an allowed transform pattern, as it
didn't make sense to me to mock a zod schema.
The UsernameSchema pattern was set to `^[a-zA-Z0-9_ \[\]üÜ]+$`, I
personally think either we should allow all latin characters (regex has
a pattern for this, `\p{L}` or `\p{sc=Latin}`) and then we'd use some
kind of library to normalize all latin characters into regular ascii for
name filtering, or we should only keep ascii letters.
## 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:
Lavodan
(I just realized sanitization isn't a word, it's supposed to be
sanitation, sorry.)
## Description:
Adds a map based on the Manicouagan Reservoir in Quebec and credits
OpenTopography
## Checklist:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
<img width="1017" height="1017" alt="Screenshot 2025-12-14 214706"
src="https://github.com/user-attachments/assets/030c4bbd-0325-4da4-bef5-71053dc8f183"
/>
## Discord username:
sehentsin
## Description:
To prevent MAD stalemates, have the price of MIRVs increase after each
launch. This will encourage players to launch a MIRV once they have
enough money for it. Also reduce the price of the first MIRV to 25
million to reduce snowballing, each subsequent MIRV cost an extra 15
million:
1. 25 million
2. 40 million
3. 60 million
4. etc
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
**Fix:**
Error box like "Username must be at least 3 characters long" sometimes
still remains after the game starts. See bug report with screenshot:
https://discord.com/channels/1284581928254701718/1449169462426079343/1449169462426079343
This is while in main.ts, element username-validation-error is already
set to hidden. Most of the time this works but apparently sometimes
there's a re-render which removes the 'hidden' tag again. Most probable
solution for this edge case: clear validationError first. Then to make
sure, still hide element username-validation-error.
**Cleanup username.ts:**
- Remove cat and clover emojis from validPattern. For single player
games, the only other validity checks are done from GameRunner calling
sanitize() from Util.ts. And from GameImpl where from addPlayer the
PlayerImpl are created, which uses sanitizeUserName also from
username.ts, which uses validPattern again. Both GameRunner and
PlayerImpl therefor allow emoji. But for muliplayer there's an extra
step where ClientJoinMessageSchema enforces UserNameSchema = SafeString.
Which does NOT allow cat or clover emoji. So for multiplayer you get an
error and can't join a game with cat or clover emoji. To align their
behavior more, just disallow the emojis from validPattern from the
start. Almost no-one ever used them anyway, they were once put in
because a developer liked to use them before the existance of
ClientJoinMessageSchema. There's more consolidation/refactoring possible
but this is an important first step.
- Remove sending arg max: MAX_USERNAME_LENGTH for translation key
invalid_chars, since the translation string doesn't contain params
**Cleanup UserNameInput.ts:**
- Remove userSettings: isn't used anywhere in the code
- Remove dispatchUsernameEvent: nowhere in the repo is event
'username-change' listened to. Also, dispatchUsernameEvent is only
called from connectedCallBack but not anywhere else. It seems like
handleChange was made for this. Also main.ts calls
usernameInput.getCurrentUsername() and doesn't listen for the event
either.
## 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
Resolves#2599
## Description:
For example in #2599 we can see people already struggling with the
higher difficulties.
With my PR #2606 (nation alliance request logic), an alliance request
towards an impossible difficulty nation will probably always get
rejected. They have `maxTroops * 2`, and because humans have much less
troops, the nation of course won't want to ally with them (unless the
human somehow manages to get a similar troop count).
This makes them even harder to beat.
So I tuned the `maxTroops`, `troopIncreaseRate` and `startManpower`
configs towards the setting for humans, following the goal to make
difficulties impact the **SMARTNESS** of nations, instead of just their
troop count configs (more varied gameplay 😊).
The biggest change is in `startManpower`. I thought it was weird that
easy nations started with much less troops than a bot (2_500 vs 10_000).
Look at this screenshot, a couple of seconds into the game. Bots were
bigger than nations. 💀 Turkey in the bottom right corner...
<img width="1536" height="1045" alt="Screenshot 2025-12-14 180823"
src="https://github.com/user-attachments/assets/d219e6ce-b579-4b7e-a8bb-effa339507bf"
/>
## 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
## 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
## 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
## Description:
Replace BFS with DFS and eliminate GC pressure in calculateClusters():
- Switch from O(N) queue.shift() to O(1) stack.pop() operations
- Replace Set.has()/Set.add() with Uint8Array bitfield
- Add reusable buffer management to avoid repeated allocations
- Implement callback-based neighbor iteration to eliminate array
allocations
- Add forEachNeighborWithDiag() method to Game interface and GameImpl
- Remove now unused GameImpl import from PlayerExecution
Describe the PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
DISCORD_USERNAME
## 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
## Description:
This PR adds the Christmas map Svalmel with 5 nations.
Describe the PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
DISCORD_USERNAME
Nikola123
## Description:
Fix for v28.
After PR #2378 changes, unit type Construction doesn't exist anymore.
Resulting in totalUnitLevels counting in construction buildings towards
total units in TeamStats, old and new Build Menu and Player Info
Overlay. To fix this we now need to filter out the construction phase.
totalUnitLevels is only used for displaying total count purposes, it
doesn't affect other logic.
Before PR 2378 changes:
https://github.com/user-attachments/assets/7748092a-8b7b-41bb-bc68-439ba922b479
After PR 2378 changes:
https://github.com/user-attachments/assets/bc77d7c2-be02-487a-b46a-c02367ae5ad8
After fix in this PR it is back to what is was before:
https://github.com/user-attachments/assets/8d7d14b6-7b6b-4ddb-96d1-17a0a45727f3
## 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
When user tries to join either a public or private multiplayer game, the
turnstile callback is triggered, and the turnstile token is passed to
the server when joining a game.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
Previously, the connection and reconnection logic were identical in
Worker.ts, so clients would need to be re-authorized for cosmetics etc
even when reconnecting. Now, on reconnect, Worker.ts only does
authentication - verifying the jwt is valid.
This will allow clients to require a valid turnstile token when first
connecting, and not when reconnecting after a broken ws connection.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Overhaul to nuke previews:
- Nuke previews now show when they become invulnerable (if
invulnerability setting enabled).
- Nuke previews now show if a SAM defense could intercept it, and where
the intercept occurs.
- SAMs range is now color-coded and outlined to be able to know if
missile will be intercepted by it.
<img width="1326" height="862" alt="image"
src="https://github.com/user-attachments/assets/4cc43a0b-ebac-4707-85bb-4d422f23da28"
/>
Colors/outlines aren't meant to be final, and there are constants to
play around with at the start of both files.
I also kept the naive implementation since the performance cost isn't
too high from my testing and any improvements I can think of are small
and require a bigger rewrite.
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
bibizu
## 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
## Description:
Fixes for v28. In https://github.com/openfrontio/OpenFrontIO/pull/2444,
lobby team preview was added. But for players with clan tags, this
doesn't work correctly. They don't get assigned to the same team in the
preview, while they do get assigned to the same team in the actual game.
Also added some small fixes and QoL improvements like showing the number
of Nations next to the number of Players. Since we needed that info
anyway. Did not choose to show and assign Nations to teams (just the
numbers), see why under CONSIDERED OPTIONS THAT I DIDN'T WENT WITH.
**BEFORE:**
https://youtu.be/AV_aDJ4PgOk
<img width="767" height="117" alt="Malformed argument because of double
accolades for remove_player"
src="https://github.com/user-attachments/assets/7de1114e-7ce1-4a8f-95cc-6b0528a61e3b"
/>
**AFTER:**
https://youtu.be/aDCKkwedqes
Cause of bug:
maxTeamSize is a number in assignTeams, only used to assign clan
players. It uses the length of the players array as input. At actual
game start, the Nations are also in the players array. But at lobby
preview the Nations aren't yet fetched. So when 2 players want to do a 3
Teams private lobby with them using clan tags to be in the same team.
maxTeamCount would do Math.ceil(2/3)=1. So only 1 clan player per team
as a result. While actually there could be 10 Nations which would result
in maxTeamCount Math.ceil(12/3)=4 so in the game they would actually be
assigned to the same team.
Fix for bug:
fetch Nations count in HostLobbyModal and pass on to LobbyTeamView. Add
it to the number of players for maxTeamCount that
assignTeamsLobbyPreview uses for its calculation. Also added nation
count to the similar teamMaxSize in LobbyTeamView itself, to display the
same and correct number of max players. For random maps, we now need to
know the random map before the game starts to get its Nation count. So
made some changes for that too in HostLobbyModal.
Also fixed:
- willUpdate ran comptuteTeamPreview every second, now checks if
properties like 'client' actually changed. PollPlayers in HostLobbyModal
'changes' the clients property every second even if there are no actual
changes. Checks if the other properties are actually changed too, to
make it more future proof.
- cache teamsList so it is only fetched once instead of first in
computeTeamPreview and then again for showTeamColors.
- don't show the "Empty Teams" header if there are no empty teams.
- console error ICU format error SyntaxError: MALFORMED ARGUMENT.
Because of double accolades around remove_player in translation value.
- remove fallback for comparing clients on clientID, which used client
name. Players may have the same names so it's not safe to compare based
on name.
Also show number of Nations next to number of Players:
now we now the nationCount since it is needed for the fix, show number
of Nations next to number of Players. It's handy and it prevents
confusion as to why it says 2/32 for two teams if there are only 2
players; it is because there are 61 Nations as well on the World map for
example.
Also determine number of teams based on Players + Nations:
now we now the nationCount since it is needed for the fix, use it to
determine the number of teams. Just like populateTeams in GameImpl does.
This means for Duos on the World map, a minimum of 31 teams will be
shown since there are 61 Nations. This is better than just show two
teams based on 1 or 2 humans in the lobby. Also it makes more clear how
many teams there'll be the game and how the players and nations are
divided over the teams. Choose to not show the Nations' team assignments
though. That could be for another PR. See explanation under CONSIDERED
OPTIONS THAT I DIDN'T WENT WITH.
Also show Nations team as pre-filled for HumansVSNations:
now we now the nationCount since it is needed for the fix, for
HumansVSNations, show the Nations team as fully assigned and non-empty.
For example for World map it shows Nations 61/61. Don't show them as
Empty Team but as Assigned Team. Although i choose not to show the
actual Nations (see CONSIDERED OPTIONS THAT I DIDN'T WENT WITH), this
makes it clear their team is pre-filled and how many Nations you're
actually up against. Whereas for other Team game types like 7 Teams or
Duos, it will display the teams that the Nations will fill up as empty.
CONSIDERED OPTIONS THAT I DIDN'T WENT WITH
- Use an optional param 'nationsCount' to assignTeams with default = 0.
And simply add nationsCount to the players.length count for maxTeamSize.
This would be error prone; 'nationsCount' should then never be assigned
a value except when called from LobbyTeamView. But in the future someone
might assign it a value even when called from somewhere else. Then you
could say, check if there are Nations in the players array and if so, do
not use Nationscount because we know they are already counted from
players.length. But if Disable Nations is enabled, there would be no
Nations in the players array but if nationsCount was somehow given a
value we still should not use it. So again, too developer error prone.
- Not only fetch the number of Nations, but actually get the Nations
themselves to show them in the team assign preview as well. They are
shuffled on team assignment but of course deterministicly (nation player
ID assigned based on Pseudorandom seeded with GameID in GameRunner, then
shuffled in TeamAssignment with Pseudorandom seeded with map's first
Nation's playerID), so we could replicate it. But then, how to
distinguish humans and Nations in the UI? This feels like something for
a follow-up PR.
FOR A FUTURE PR
- change the way Clan team overflow is handled. Now they're "kicked" as
in not assigned to a team without their knowing, are loaded into the
game but cannot spawn. This UX could need some improvement. And the
logic can be improved too, ie. don't "kick" too soon, check the number
of Clans in the lobby and the number of teams to decide if we can assign
the 'overflowing' Clan player to another team that doesn't have
rivalling Clan players. Far out of scope for this PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
tryout33
## Description:
Adds a map based on Lisbon and the surrounding area. Also credits the
ESA's Copernicus Digital Elevation Model, which was used to create this
map and the Gulf of St. Lawrence map.
<img width="1257" height="1257" alt="screenshot of the new Lisbon map"
src="https://github.com/user-attachments/assets/39fa73da-c77d-4d5c-8d00-7ee2794d0298"
/>
## Checklist:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Discord Username:
sehentsin
## Description:
Adds the Gulf of St. Lawrence map and the flags of New Brunswick
(ca_nb), Nova Scotia (ca_ns), and Prince Edward Island (ca_pe).
## 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
<img width="1380" height="1150" alt="Screenshot 2025-11-26 201008"
src="https://github.com/user-attachments/assets/77531058-b429-4c68-b645-a3e59033458b"
/>
<img width="1434" height="1195" alt="Screenshot 2025-11-26 202128"
src="https://github.com/user-attachments/assets/9c3e2bc2-882f-4662-a32b-16e17db852f2"
/>
## Discord username
sehentsin