Commit Graph
891 Commits
Author SHA1 Message Date
SkigimandGitHub 60f69a6408 perf: remove O(n) StructureIconsLayer render lookups (#3305)
Begins work on #3207

## Description:

This PR is the first optimization slice for #3207: it removes O(n)
render lookups in `StructureIconsLayer` by replacing array-first render
state with a unit-id keyed map, and tightens hot-path execution to
reduce per-tick allocations.

### What changed
- Refactored render state from array-first to `rendersByUnitId:
Map<number, StructureRenderInfo>`.
- Replaced O(n) lookup/delete paths with O(1) `Map#get` / `Map#delete`.
- Replaced `seenUnits` object-identity tracking with `seenUnitIds:
Set<number>`.
- Removed `tick()` array/closure chain (`map(...).forEach(...)`) and
switched to index-based loop.
- Reduced ghost-path allocation pressure by reusing a layer-level `Set`
for connected ally IDs instead of allocating `filter` + `map` + `new
Set` per ghost query.
- Added dirty-flag caching for structure visibility focus
(`visibilityStateDirty`) so expensive visibility-state scans recompute
only when toggles change.

### Performance validation (before/after)
Benchmark added: `tests/perf/StructureIconsLayerLookupPerf.ts`

Command:
`npm run perf`

Observed result:
- `StructureIconsLayer BEFORE (array O(n) lookup/delete) x 0.33 ops/sec
±13.28%`
- `StructureIconsLayer AFTER (unit-id map O(1) lookup/delete) x 95.65
ops/sec ±2.46%`
- Fastest implementation: AFTER (unit-id map)

#### Profiler screenshots are too noisy to be useful for such a focused
change

### Verification
- `npx tsc --noEmit` 
- `npx eslint src/client/graphics/layers/StructureIconsLayer.ts
tests/perf/StructureIconsLayerLookupPerf.ts` 
- `npm run perf` 

## Please complete the following:

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

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

skigim
2026-03-02 17:40:15 -08:00
SkigimandGitHub 17f32a590c fix: resolve drawImage scaling penalty on non-square sprite height (#3320)
## Description:

This PR resolves a performance parsing penalty in the `UnitLayer`
rendering loop by fixing two issues with non-square sprites:
1. `drawImage` was incorrectly using `sprite.width` for both width and
height, causing aspect ratio squashing and forcing the browser to
perform a scaling operation on the image instead of hitting the canvas
fast-path. It now correctly uses `sprite.height` for the vertical
dimension.
2. `clearUnitsCells` was previously configured to only clear a square
grid (`clearsize`) based solely on width, meaning taller sprites would
leave visual artifact "ghosts" on the map. The clearing bounds have been
corrected to leverage discrete `sprite.width` and `sprite.height`.
Additionally, these values are wrapped in `Math.round()` prior to offset
calculation to prevent subpixel anti-aliasing CPU penalties during
`clearRect`.

## Please complete the following:

- [x] I have added screenshots for all UI updates (not needed)
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file (no new text)
- [x] I have added relevant tests to the test directory (existing tests
suffice, change was minuscule and non-breaking)
- [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:
skigim
2026-03-02 10:56:37 -08:00
SkigimandGitHub f7598369ed refactor: consolidate platform detection across client components (#3325)
## Description:

This PR consolidates ad hoc platform/environment/viewport detection into
a single shared utility. It is scoped to this refactor only, and serves
as groundwork for the mobile-focused feature work planned for the v31
milestone.

### What changed
- Introduced a shared `Platform` utility centralising:
  - OS detection (with `userAgentData` + UA fallback)
  - Electron environment detection
- Viewport breakpoint helpers (`isMobileWidth`, `isTabletWidth`,
`isDesktopWidth`)
- Replaced duplicated inline checks across client files with the shared
API.
- Normalised Mac detection to derive from the consolidated OS logic
rather than a separate regex.

### Why
- Multiple client files each independently ran `navigator.userAgent`
regexes or copy-pasted `isElectron` logic — this unifies all of that.
- Puts a stable, tested abstraction in place before v31 mobile work
lands, so mobile feature branches have a consistent surface to build
against.

## Please complete the following:

- [x] I have added screenshots for all UI updates (N/A: refactor only,
no visible UI changes)
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file (N/A: no new user-facing strings)
- [x] I have added relevant tests to the test directory (N/A: refactor
only)
- [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:

skigim
2026-03-02 10:12:48 -08:00
scamivandGitHub 50197e7254 perf(PerformanceOverlay): reduce per-render overhead (#3295)
## Description:

Cache translated UI labels per language/translation load
Avoid per-frame layer breakdown sorting unless expanded
Use rolling sums instead of array reduce
Drop redundant requestUpdate() calls and object clones

## Please complete the following:

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

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

DISCORD_USERNAME
2026-03-01 20:15:31 -08:00
8754f5291f Feat: Alphanumeric Coordinate Grid on Alternate View (#2938)
## Description:

Adds a coordinate grid to the Alternate View (holding spacebar) using
numbers on the X-axis, and letters on the Y-axis. No more "he's
attacking you in that—well, the little peninsula thing... next to the
island! which island? uhh..." moments when playing with friends.
Optimally maps have letters A-J (just like in the Battleships board
game) but special maps like Amazon River dynamically resize to only have
2 letters so as to not have too many number columns. This feature
overall can be toggled via the settings menu.

Also saw it requested on the [official
discord](https://discord.com/channels/1359946986937258015/1457037351422263480)
a couple times, thought it was a neat idea.

### World Map
<img width="3809" height="1824" alt="image"
src="https://github.com/user-attachments/assets/dab56879-a34e-48ea-a588-2907d26feb45"
/>

### Scales correctly when zoomed in
<img width="3798" height="1874" alt="image"
src="https://github.com/user-attachments/assets/7e06a47f-d3d9-4f92-8e89-3eaf866e9b25"
/>

### Amazon River
<img width="3803" height="1595" alt="image"
src="https://github.com/user-attachments/assets/4797c576-20b2-4aa8-8b7a-107078ab6308"
/>

### Enable/Disable via settings


https://github.com/user-attachments/assets/ec9f4e07-70a1-4f9d-b137-c3c3d2a2540c

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

---------

Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-28 20:28:47 -08:00
scamivandGitHub c911bfb2d8 Packed unit updates / MotionPlans (#3292)
## Description:

Reduce per-step `Unit` update traffic by shipping packed motion plans
and letting the client advance plan-driven units locally.

Changes:
- Add packed motion plan records (`packedMotionPlans?: Uint32Array`) to
game updates and transfer the buffer worker -> main.
- Introduce `src/core/game/MotionPlans.ts` (schema + pack/unpack) for
grid + train motion plans.
- Extend `Game` with `recordMotionPlan(...)` and
`drainPackedMotionPlans()`, and implement buffering/packing in
`GameImpl`.
- Treat units with motion plans as “plan-driven”: suppress per-tile
`Unit` updates on `move()` and advance positions client-side.
- Emit motion plans from executions:
- `TradeShipExecution`: record/update grid motion plans and `touch()`
when changing target after capture.
- `TransportShipExecution`: record initial plan and update it when
destination changes.
  - `TrainExecution`: record a train plan on init (engine + cars).
- Client: apply motion plans in `GameView` and ensure `UnitLayer`
updates sprites for motion-planned units even when no `Unit` updates
arrived.

## Please complete the following:

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

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

DISCORD_USERNAME
2026-02-27 20:54:42 -08:00
evanpelle f09177f8fe Merge branch 'v29' 2026-02-26 17:40:57 -06:00
Mattia MiglioriniandGitHub 7b785ea79a Fix alliance renewal prompt incorrectly dismissed for both players (#3297)
## Description:

NOTE: Applies to current main / beta version. Needs to be included in
v30.

When a player clicked "Renew Alliance", the `AllianceExtensionUpdate`
broadcast caused both players' renewal prompts to be removed, even the
one who hadn't yet acted. This happened because
`onAllianceExtensionEvent` called `removeAllianceRenewalEvents`
unconditionally on every client.

This PR fixes the behavior by calling `removeAllianceRenewalEvents` only
for the player that executed the action.

## Please complete the following:

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

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

deshack_82603
2026-02-25 21:12:58 -06:00
EvanandGitHub 7f03072e9b revert skin trials (#3293)
## Description:

Skin trials has been a failure, very low fill rate and cause a major
drop in sales.

reverts 


https://github.com/openfrontio/OpenFrontIO/commit/97d0a05d58e926e3de4ba46d8dd14a04d60d6698

## Please complete the following:

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

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

evan
2026-02-24 15:47:21 -06:00
scamivGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
4b917c4153 Performance Overlay rework/redesign (#3274)
## Description:
updates the Performance Overlay to be more usable
(draggable/resizable/scrollable), adds tick-level metrics (TPS +
per-layer tick timings), and reduces overhead when the overlay is
hidden.

### UI/UX
- Overlay layout updated to a fixed, pixel-positioned panel (default
near top-left) with a dedicated drag handle.
- Overlay is touch-draggable (pointer events) and remains usable on
small viewports via internal scrolling.
- Overlay width is resizable with a right-edge handle; width is clamped
to viewport bounds.
- Render/tick layer breakdown sections are collapsible, with headers and
“last tick” summaries.

### New metrics
- Adds TPS reporting:
  - Current TPS (ticks in the last 1s).
- Average TPS over the last ~60s, computed using elapsed time so it’s
accurate before a full 60s passes.
- Adds per-layer tick profiling (“Tick Layers”) alongside render
profiling (“Render Layers”).
- Adds “render-per-tick” metrics so render-layer costs can be understood
per simulation tick (frames + per-layer totals).

### Performance / overhead
- Avoids profiling overhead when the overlay is hidden:
- `GameRenderer` only calls `FrameProfiler.clear()/consume()` and
per-layer `start/end` when profiling is enabled.
- Tick-layer duration tracking is only collected when profiling is
enabled.

### Settings plumbing
- `UserSettings` now dispatches a `user-settings-changed` `CustomEvent`
on `set()` / `setFloat()`.
- The overlay listens for `settings.performanceOverlay` changes so
visibility stays in sync even when toggled outside the overlay.

## Implementation notes (by file)

- `src/client/graphics/layers/PerformanceOverlay.ts`
  - Adds TPS tracking using a timestamp ring + moving heads (1s / 60s).
- Adds UI state for collapsibles, drag + resize pointer tracking, and
new breakdown models:
    - Render layers: EMA avg/max + per-tick render aggregation.
    - Tick layers: EMA avg/max + last-tick durations.
- Copy-to-clipboard snapshot now includes TPS, tick layers, and
render-per-tick last-tick details.

- `src/client/graphics/GameRenderer.ts`
  - Gates render-layer profiling behind `FrameProfiler.isEnabled()`.
- Accumulates per-render-layer timings across frames and publishes them
once per tick via `updateRenderPerTickMetrics(...)`.
- Measures tick-layer durations (per layer `tick()` call) and publishes
them via `updateTickLayerMetrics(...)`.

- `src/core/game/UserSettings.ts`
- Adds `emitChange(key, value)` to dispatch `user-settings-changed` to
`globalThis` (best-effort).

- `resources/lang/en.json`
- Adds/updates `performance_overlay.*` strings for TPS and the new
render/tick layer sections.

## Please complete the following:

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

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

DISCORD_USERNAME

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-23 14:22:56 -06:00
Mattia MiglioriniGitHubClaude Opus 4.6coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
90204f6628 Add alliance renewal action to Radial Menu (#3148)
## Description:

The following PR replaces the (disabled) alliance request button with an
alliance extension/renewal button when the alliance with the target
player is expiring.

Agreeing to renewal via radial menu also hides the message in the
EventsDisplay.

<img width="369" height="364" alt="image"
src="https://github.com/user-attachments/assets/d8040f5c-ad7b-47d0-852f-925ecbf273a8"
/>


https://github.com/user-attachments/assets/aa589edf-6505-46bf-88a3-aa4c2df9137f

Icon size adjusted:

<img width="294" height="252" alt="image"
src="https://github.com/user-attachments/assets/7ca63500-b1fb-427b-965c-cf121a5213da"
/>

## Please complete the following:

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

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

deshack_82603

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-19 19:47:57 -06:00
VariableVinceandGitHub 2a7db43db3 Small refactor/cleanup: RadialMenuElements and PlayerImpl (#3239)
## Description:

PR 7/x in effort to break up PR #3220. Follows on already merged #3238.

Please see if these can be merged for v30.

-**RadialMenuElements**: 
- _getAllEnabledUnits_: use camelCase instead of PascalCase so change
Units into units.
- _getAllEnabledUnits_: use StructureTypes to loop through instead of
having 6 individual lines of code to check if a structure is enabled.
StructureTypes contains and will keep containing the same structures as
those that are checked here. PR 3220 will later on also replace the
individual lines for the attack type units into a loop with a newly
introduced Types array, in the same way as we do in this PR with
StructureTypes.
- _getAllEnabledUnits_: rename the long named const
addStructureIfEnabled to just addIfEnabled, which is clear enough from
the context it is used in.

-**PlayerImpl**
- _buildableUnits_: removed unnecessary "as BuildableUnit" after the
in-loop return; the function itself already says it returns
BuildableUnit[].

## Please complete the following:

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

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

tryout33
2026-02-19 00:27:12 +00:00
Mattia MiglioriniandGitHub ba2a947061 Feat: Display ghost railways when building cities and ports (#3202)
## Description:

Based on [this suggestion on
Discord](https://discord.com/channels/1284581928254701718/1447110257196138577)
and feedback gathered in [this
thread](https://discord.com/channels/1359946986937258015/1469598906173227184).

Supersedes #3143 

This PR introduces "ghost railways": when you are going to place a city
or port, previews railway connections that will be made when actually
building the structure.

Ghost railways are skipped if the structure is going to be snapped to
existing railways (as in railway snapping functionality introduced in
#3156 ).

### Video


https://github.com/user-attachments/assets/ff8cf325-6501-4df8-801d-c8ae3ced3d0e


### Ghost rails color revisited

black with 40% opacity

<img width="695" height="430" alt="image"
src="https://github.com/user-attachments/assets/272efbcc-4185-426a-921c-7fae61f6c462"
/>


## Please complete the following:

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

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

deshack_82603
2026-02-18 21:44:08 +00:00
VariableVinceandGitHub f1cd478970 Cleanup/refactor: Remove some redundant checks (#3235)
## Description:

PR 3/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220. Follows on already
merged https://github.com/openfrontio/OpenFrontIO/pull/3233 and
https://github.com/openfrontio/OpenFrontIO/pull/3234.

Please see if these can be merged for v30.

- **ClientGameRunner**: removed two redundant myPlayer===null checks
since that was already done right above, instead use !.
- **BuildMenu**: just like in UnitDisplay, assign public PlayerActions
default value of null. So that in canCreateOrBuild, where we already do
a === null check on it btw, we can safely skip the assignment to const
buildableUnits and just directly loop over
this.playerActions.buildableUnits.
- **RadialMenuElements**: don't call canBuildOrUpgrade 3x in
CreateMenuElements for the .map on flattenedBuildTable, instead do it
once and re-use outcome.

## Please complete the following:

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

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

tryout33
2026-02-18 21:09:24 +00:00
VariableVinceandGitHub f4f7ae3929 Cleanup: comments in BuildMenu and error messages in worker.worker (#3233)
## Description:

PR 1/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220.
Please see if these can be merged for v30.

- **BuildMenu**: remove one redundant comment about replacing an icon
(which has been done long ago already). And fix typo in one other
comment.
- **Worker.worker**: correct some existing error messages.

## Please complete the following:

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

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

tryout33
2026-02-18 20:21:19 +00:00
VariableVinceandGitHub 52036cc20c Cleanup: remove unused code from four files (#3234)
## Description:

PR 2/x in effort to break up PR
https://github.com/openfrontio/OpenFrontIO/pull/3220.

Removes unused code and properties.

- **Game.ts** and **DefaultConfig** unitInfo: removed
_canBuildTrainStation_ and _expirimental_ properties, as they weren't
used anywhere anymore.

- **PlayerActionHandler**: remove unused getPlayerActions, the only
potential caller MainRadialMenu already just calls myPlayer.actions via
GameView directly.

- **StructureIconsLayer**: remove unused PlayerActions

## Please complete the following:

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

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

tryout33
2026-02-18 20:17:46 +00:00
f276a72635 Remove win modal animation 🖌️ (#3230)
## Description:

Win modal animation looks bugged (https://youtu.be/fmR4nZL5RLg?t=3839)
Just remove it, make the game feel snappier
Reported by Wonder

## Please complete the following:

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

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

FloPinguin

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-17 20:35:38 +00:00
FloPinguinandGitHub 18f52c01bb Improve moble UI again (#3226)
## Description:

- Fix HeadsUpMessage appearing above Settings modal
- Fix HeadsUpMessage appearing above Leaderboard
- Remove PlayerInfoOverlay show/hide animation (we need quick access to
the data!)

- Close PlayerInfoOverlay on tap outside the map (gray area)

- Fix error when tapping gray area outside the map
- Close PlayerInfoOverlay on click/tap on itself


## Please complete the following:

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

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

FloPinguin
2026-02-16 23:28:43 +00:00
FloPinguinandGitHub 4c44da4940 Adjust styling for attack ratio popup and event display button (#3225)
## Description:

Before:

<img width="445" height="501" alt="Screenshot 2026-02-16 205903"
src="https://github.com/user-attachments/assets/3add2b1c-f108-4138-9066-ce16ce9fb76d"
/>

After:

<img width="484" height="515" alt="Screenshot 2026-02-16 210819"
src="https://github.com/user-attachments/assets/cb7ba559-535e-4e87-a4b2-4bf744b98e6e"
/>

## Please complete the following:

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

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

FloPinguin
2026-02-16 20:56:50 +00:00
VariableVinceandGitHub 5e2930075a Fix: console error stemming from WinModal (#3221)
## Description:

Fix console error triggered by this.game being undefined but WinModal
render() still tries to get this.game.myPlayer?.

There is no guard needed in tick() or show() (the latter only called by
tick). Because when this.game is undefined tick() won't be called
anyway.

![undefined in WinModal reading myPlayer in console at game
start](https://github.com/user-attachments/assets/0c399516-f6a1-418d-916b-2633413eb241)

![undefined in WinModal reading myPlayer in console at game start
B](https://github.com/user-attachments/assets/28986383-596a-4a64-bc26-b00f28828bb7)

## Please complete the following:

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

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

tryout33
2026-02-16 11:19:06 -08:00
Mattia MiglioriniandGitHub f362e47413 Cancel nukes when accepting alliance via radial menu (#3155)
Resolves #3154

## Description:

#2716 introduced nuke cancellation logic on alliance acceptance via
`AllianceRequestReplyExecution`. The radial menu action, though, calls
`AllianceRequestExecution` instead, which accepts the alliance if a
request has already been made by the other player.

This PR moves the nuke cancellation logic to `GameImpl`, hooking into
the `acceptAllianceRequest` method, therefore accounting for every
alliance acceptance, regardless of the specific action that brought to
that.

## Please complete the following:

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

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

deshack_82603
2026-02-16 11:10:26 -08:00
d0bb3a016e "Catching up..." HeadsUpMessage 🏃‍♀️ (#3194)
## Description:

After an internet problem or page reload the game catches up, replaying
the ticks.

But especially new players might be confused what is happening. The game
runs fast???
And you can't easily tell when its finished catching up. You need to
spot when it stops running faster than usual.

So add a HeadsUpMessage to tell people what is happening.


https://github.com/user-attachments/assets/6fcdd85f-c58e-4549-89d0-5ba51df39339

## 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>
2026-02-16 11:08:11 -08:00
FloPinguinandGitHub 086a7e9000 Improve mobile UI (#3217)
## Description:

### Leaderboard cut off

Previous:

<img width="285" height="255" alt="Screenshot 2026-02-15 171617"
src="https://github.com/user-attachments/assets/affb7559-3885-4cc3-bc1a-f653dcb13fb2"
/>

Now: 

<img width="302" height="299" alt="Screenshot 2026-02-15 171603"
src="https://github.com/user-attachments/assets/623fe424-d744-46f7-99aa-710b010c4084"
/>

### Attack ratio popup cut off

Previous:

<img width="276" height="806" alt="Screenshot 2026-02-15 171542"
src="https://github.com/user-attachments/assets/2ac0ec19-feea-465a-b04b-323a18309d4d"
/>

Now: 

<img width="278" height="807" alt="Screenshot 2026-02-15 171533"
src="https://github.com/user-attachments/assets/7e06aa96-04ba-4454-ba0e-cdaad74f79be"
/>

Also fixed this text overlap problem on boat retreat:

<img width="359" height="192" alt="Screenshot 2026-02-15 172603"
src="https://github.com/user-attachments/assets/e4cef05c-5dc3-4960-ab21-a2f0740e3380"
/>

## Please complete the following:

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

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

FloPinguin
2026-02-16 18:00:25 +00:00
Vivacious BoxandGitHub 040766d417 Add units filter on playeractions for performance (#3213)
## Description:

The ghost structure calls player actions each frame, which is costly
since it's checking for all possible actions.
This add a unit list filter in actions so if there are units it only
checks for buildability of those units.

Before:
![WhatsApp Image 2026-02-14 at 23 25
25](https://github.com/user-attachments/assets/beda6142-9dc7-4a9c-a702-cee3b6ea043c)
Player actions takes 20-30% of the worker

After:
<img width="825" height="342" alt="image"
src="https://github.com/user-attachments/assets/36e47547-5028-4dc9-bc42-e17df4a87200"
/>
Player actions takes 1-3% of the worker


Both performances are relevant only when a ghost structure is selected

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

Mr. Box
2026-02-14 19:54:12 -08:00
FloPinguinandGitHub 0c7da790f1 Improve Ingame UI (#3212)
## Description:

- **Dynamic sidebar offset for top bars** - GameLeftSidebar,
GameRightSidebar, and PlayerInfoOverlay now shift down when SpawnTimer
and/or ImmunityTimer bars are visible (7px per bar). Implemented via
events.
- **Fixed text overflow** in HeadsUpMessage.ts (Random spawn message is
long)
- **Fixed inconsistent text sizing** in EventsDisplay 
- **Alliance icon horizontal** in PlayerInfoOverlay so the size of the
overlay doesn't change if there is an alliance
- **Nation relation coloring** - Nation player names are now colored
based on their relation
- **Background & Blur Unification**
- **Border Radius & Page Edge Gap Standardization**
- **EventsDisplay collapsed button:** Fixed badge hidden / inline-block
CSS conflict (conditional rendering), added gap-2 between text and badge
- **Right panel spacing:** Changed right container from sm:w-1/2 to
sm:flex-1 to fill remaining space
- **Leaderboard**: Rounded grid corners (rounded-lg overflow-hidden),
removed last-row border, added `willUpdate` for auto-refresh on
hide/show click, plus button styled to match toggle buttons
- Other little CSS fixes (margins etc)

Showcase:
(Note the red mexico name on betrayal)


https://github.com/user-attachments/assets/f0ed91de-3a07-4564-a209-3d7723edee55

Two progress bars at the top, mobile UI not cut off:


https://github.com/user-attachments/assets/83f1fd64-ceab-4a74-8d16-6e1eeea1709d

HeadsUpMessage text overflow fixed, SpawnTimer does not cut off the
PlayerInfoOverlay:

<img width="516" height="929" alt="Screenshot 2026-02-14 214410"
src="https://github.com/user-attachments/assets/74f0edea-8c01-4394-a3d0-a3245922e0da"
/>

Previous:

<img width="306" height="118" alt="Screenshot 2026-02-14 213705"
src="https://github.com/user-attachments/assets/a7c7e8f3-f0e8-4213-8a8f-4f3677e9fc98"
/>

Smaller event panel text:

<img width="594" height="975" alt="Screenshot 2026-02-14 215738"
src="https://github.com/user-attachments/assets/33e80570-9260-40b0-b810-c71eda4861fc"
/>

## Please complete the following:

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

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

FloPinguin
2026-02-14 19:48:43 -08:00
evanpelle 8e889fe857 Merge branch 'v29' 2026-02-14 12:14:19 -08:00
VariableVinceandGitHub 07e13b3479 Fix: remove alliances on death (#3168)
## Description:

- Remove alliances on death: after death, alliances would stay active
including countdown timers and (when dead player kept spectating) icons.
Now remove them when player becomes inActive.

- Moved code to private method within PlayerExecution + added comments
in NationExecution and BotExecution for more clarity as to where
removals are performed from at death

- Remove renewal request from Events Display when Alliance doesn't exist
anymore (after death or otherwise).

- Also cleanup this.alliancesCheckedAt when alliance doesn't exist
anymore. Before, old/broken alliance id's would accumulate in it during
a game.

- Removed now-redundant isAlive check in EventsDisplay. Both the
alliances array as the isAlive are updated in the same tick from
PlayerUpdates so now alliance is removed from alliances array on player
death, the other.isAlive() check is no longer needed. Of course we could
keep it in just to be very safe, so just let me know when you're
doubtful about this.

- Attack.test.ts: fix failing test. Player B dies because of the attack,
meaning the alliance now gets removed. Prevent this by gving both a
different, adjecent, starting tile. And to be more clear about what is
needed for the test to pass, add isAlive check for both of them after
the attacks.

## Please complete the following:

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

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

tryout33
2026-02-12 11:01:08 -08:00
6cc0ef7d14 Add PVP immunity to 5M starting gold modifier games 🔧 (#3180)
## Description:

Adds 30 seconds of PVP immunity to 5M starting gold modifier games.
So you cannot insta-nuke other players.

Because I'm sure people would be confused "I cannot attack!!!!" I added
a HeadsUpMessage which informs about the PVP immunity.
We already have a ImmunityTimer progress bar but I don't think its
enough.

<img width="1270" height="745" alt="image"
src="https://github.com/user-attachments/assets/0ee23dc4-1c7b-4d62-8b3d-8de214f03c2b"
/>

I had a second count in the HeadsUpMessage (seconds until PVP immunity
is over) but it felt too busy. So I removed it. You can tell when PVP
immunity is over by looking at the progress bar.

## 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>
2026-02-12 10:57:18 -08:00
EvanandGitHub 97d0a05d58 Rewarded videos ads to test a skin (#3120)
## Description:

Added rewarded video ads for skin trials via Playwire's
manuallyCreateRewardUi API. Users can now click "Try me" to watch a
video ad and receive a temporary skin trial. Upon completion a temporary
flare is granted to the player so they have ~5 minutes to use the skin.

added getPlayerCosmeticsRefs and getPlayerCosmetics to Cosmetics.ts to
centralize cosmetic retrieval & validation.

<img width="801" height="534" alt="Screenshot 2026-02-10 at 7 58 14 PM"
src="https://github.com/user-attachments/assets/51cc378c-2feb-4692-8cf2-20ee54cea3b8"
/>

## Please complete the following:

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

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

evan
2026-02-11 20:52:48 -08:00
EvanandGitHub 79330af2b2 attack panel (#3114)
Relates #2260

## Description:

Move outgoing & incoming boat & land attacks to a new "AttacksDisplay"
layer that sits on top of the ControlPanel. The idea is to break up
EventsDisplay so it's easier to find information. It's also more mobile
friendly.

It still needs more styling, but this just a first pass.

<img width="356" height="199" alt="Screenshot 2026-02-09 at 4 44 38 PM"
src="https://github.com/user-attachments/assets/c8e32972-be3b-469b-b7c7-982197c1d572"
/>

<img width="750" height="436" alt="Screenshot 2026-02-09 at 4 44 18 PM"
src="https://github.com/user-attachments/assets/5359459b-015e-432f-81bf-1561cc64babe"
/>

<img width="537" height="537" alt="Screenshot 2026-02-09 at 4 43 33 PM"
src="https://github.com/user-attachments/assets/edc7a07e-3589-4107-b017-38e00768c5cf"
/>

<img width="487" height="283" alt="Screenshot 2026-02-09 at 4 44 05 PM"
src="https://github.com/user-attachments/assets/1a3886c7-57e3-4247-92c5-3a13876c2a71"
/>

## Please complete the following:

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

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

evan
2026-02-09 21:06:08 -08:00
c212735f09 Orange betrayal button for no-debuff-betrayals 🖌️ (#3161)
Resolves #1276

## Description:

Orange betrayal button if the player is a traitor or disconnected.
So people can easier tell that this is a betrayal without consequences.
The color changes back to red without reopening the menu (live) when the
traitor debuff ends or the player reconnects.

<img width="268" height="257" alt="image"
src="https://github.com/user-attachments/assets/276e91ce-e49d-474c-afaa-ffa18d45a2c7"
/>

## Please complete the following:

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

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

FloPinguin

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-09 23:23:20 +00:00
Vivacious BoxandGitHub 3cd4ffff0c Fix railroads dead pixels (#3166)
## Description:

Fix railroads coordinates to remove dead pixels
<img width="281" height="248" alt="image"
src="https://github.com/user-attachments/assets/dca6a954-c28f-44c0-8a5e-a7ad147f5dd2"
/>


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

Mr. Box
2026-02-09 15:07:50 -08:00
c6c793f6b3 Highlight hovering railroad (#3156)
## Description:


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

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

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

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

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

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


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


## Please complete the following:

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

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

IngloriousTom

---------

Co-authored-by: jrouillard <jon@rouillard.org>
2026-02-09 13:37:27 -08:00
VariableVinceandevanpelle cceb7bd0fc Fix: can't boat into AFK ally from radial menu (#3165)
## Description:

Fixes issue where you can't boat into an AFK/disconnected ally from the
radial menu:
https://www.youtube.com/clip/UgkxRXy2Y9BrmCiQRSFJnhVFanR5NRsG9pzu

## Please complete the following:

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

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

tryout33
2026-02-09 13:01:35 -08:00
VariableVinceandGitHub 742cbf90f3 Fix: can't boat into AFK ally from radial menu (#3165)
## Description:

Fixes issue where you can't boat into an AFK/disconnected ally from the
radial menu:
https://www.youtube.com/clip/UgkxRXy2Y9BrmCiQRSFJnhVFanR5NRsG9pzu

## Please complete the following:

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

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

tryout33
2026-02-09 13:00:51 -08:00
evanpelle f051bd8a1b Merge branch 'v29' 2026-02-09 12:54:34 -08:00
SkigimandGitHub 32adfa2f79 Add requeue button to Ranked victory/defeat modal (#3121)
## Description:

Adds a "Play Again" requeue button to the victory/defeat modal for
Ranked 1v1 games. When clicked, it navigates the player back to the
homepage and automatically opens the matchmaking modal to queue for
another ranked match.

Changes:

- WinModal.ts: Added isRankedGame state, purple "Play Again" button
(only shown for ranked 1v1), and _handleRequeue() method
- Main.ts: Added ?requeue URL parameter handling to trigger matchmaking
modal on page load
- en.json: Added "requeue": "Play Again" translation string
- added tests to WinModal.test.ts

Note: temporarily set isRanked flag to true to get the modal to pop in a
solo match on dev server and confirmed that ?requeue URL parameter
called _handleRequeue() correctly, which opened the sign in process
since actually signing in and queuing for a ranked match isn't possible
on dev server.

<img width="771" height="364" alt="play-again"
src="https://github.com/user-attachments/assets/6e3f5a02-f1ae-465a-9b28-656126c11d3d"
/>


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

skigim
2026-02-07 12:51:02 -08:00
Martin IandGitHub 070b5060d8 fix: swap team text and buttons position; fix gap space on leaderboard (#3135)
## Description:

1. Swaps the position of the teams text and leaderboard buttons;
2. Edits the text and button margins
3. Fixes spacing bug where because of a "gap-2", if only one leaderboard
is open, there's empty space on the left or right of the leaderboard.
4. Small code refactor proposed by code rabbit / icon description

Button swap and spacing changes were suggested by:
@FloPinguin  @ryanbarlow97  

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

***
Screenshots:
Before swapping the position and editing the spacing:
<img width="242" height="151" alt="image"
src="https://github.com/user-attachments/assets/cf196842-b469-45ab-a685-0a5e56b56378"
/>

After swapping the position and editing the spacing: ✔️
<img width="233" height="149" alt="image"
src="https://github.com/user-attachments/assets/c88da4ec-0f23-4670-af5d-fce4124d4936"
/>

Before swapping the position and editing the spacing without the text:
<img width="528" height="398" alt="image"
src="https://github.com/user-attachments/assets/e1e31352-31d1-42a4-ad92-a60b0014b779"
/>

After swapping the position and editing the spacing without the text: ✔️
<img width="514" height="350" alt="image"
src="https://github.com/user-attachments/assets/6a1f2391-e2f1-478e-bada-9436b7cb2e13"
/>

Before fixing the spacing on mobile:
<img width="579" height="158" alt="image"
src="https://github.com/user-attachments/assets/8d5e225b-6dbd-4a07-afeb-97035000a09d"
/>

After fixing the spacing on mobile: ✔️
<img width="575" height="134" alt="image"
src="https://github.com/user-attachments/assets/f9016060-ac9e-47fc-8886-e3eee6359906"
/>

Before fixing the leaderboard space issue:
<img width="511" height="398" alt="image"
src="https://github.com/user-attachments/assets/0fadddcd-2c5f-4caf-b641-c7a3e19a5a14"
/>

<img width="511" height="398" alt="image"
src="https://github.com/user-attachments/assets/2a9a9f7d-e08d-4908-b2d1-f26500c4c602"
/>

<img width="585" height="204" alt="image"
src="https://github.com/user-attachments/assets/9dbb4c51-56ae-4e7a-b603-f49cd1dc2286"
/>

After fixing the leaderboard space issue: ✔️
<img width="533" height="463" alt="image"
src="https://github.com/user-attachments/assets/c0608e83-974a-4950-94cd-896bc7dd7720"
/>

##Discord username: martoi

***
Signed-off-by: MartinIvovIv <https://github.com/martinIvovIv>
2026-02-06 19:39:35 -08:00
Martin IandGitHub d16fca16e2 fix: adjust style for plus and minor leaderboard button to match other buttons (#3132)
## Description:

Changes the leaderboard + / - buttons to follow the styling of other
buttons.
Suggested by @FloPinguin 

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

***
Screenshots:

Example with previous (above) and new style (below):
<img width="539" height="186" alt="image"
src="https://github.com/user-attachments/assets/896e3fba-4dfb-4e12-a6bf-f2c363faf485"
/>

Example with previous (above) and new style (below) with onhover effect:
<img width="555" height="307" alt="image"
src="https://github.com/user-attachments/assets/b76aaa80-2839-4e6c-b244-9af6ce569a9a"
/>

##Discord username: martoi

***
Signed-off-by: MartinIvovIv <https://github.com/martinIvovIv>
2026-02-06 17:56:30 +00:00
920ae190fd fix: show Spectate instead of Keep Playing on win modal when dead Closes #3058 (#3062)
## Description:                                                        
This PR resolves issue #3058 where the "win modal" incorrectly
displayed a **"Keep Playing"** button instead of **"Spectate"** when a
player's team won but the player themselves was already dead.
### The Problem
In WinModal.ts, the button text logic only checked `this.isWin`
(whether the team won) but didn't verify if the player was still alive.
This caused dead players on winning teams to see "Keep Playing"
instead of "Spectate".
### The Solution
Updated the button rendering logic in
`src/client/graphics/layers/WinModal.ts:82` to check both the win
condition AND the player's alive status:
```typescript
// Before
${this.isWin
? translateText("win_modal.keep")
: translateText("win_modal.spectate")}
// After
${this.isWin && this.game.myPlayer()?.isAlive()
? translateText("win_modal.keep")
    : translateText("win_modal.spectate")}
  ```                               
This approach maintains clean separation of concerns:
- this.isWin continues to represent whether the player's team won
(true/false)
- The button text logic now checks both team victory and player alive
status
- This ensures the correct button appears based on the player's actual
state
Behavior After Fix
- Alive players on the winning team → "Keep Playing"
- Dead players on the winning team → "Spectate"
- Any player on the losing team → "Spectate"
Testing Performed
1. Code Audit: Verified myPlayer().isAlive() correctly reflects the
eliminated state in Teams mode.
2. Build Verification: Ran npm run build and npm run build-prod to
ensure no regressions in the UI layer.
3. Type Safety: Ran tsc --noEmit to confirm the fix is fully compliant
with the project's TypeScript strictness.
---
Closes #3058
## 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:
ghadi8097

---------

Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-06 09:43:33 -08:00
Martin IandGitHub 289e246162 fix: spacing between team leaderboard buttons (#3101)
## Description:

In team games, the UI buttons for the leaderboard and the team
leaderboard look out of place.
A bit of tailwind css fixes the look and a making the gap window
specific makes the accessibility better.

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

***
Screenshots:
Before change:
<img width="218" height="130" alt="image"
src="https://github.com/user-attachments/assets/e7366435-c3d3-44b9-af5f-05a1548bf743"
/>

After change:
<img width="393" height="277" alt="image"
src="https://github.com/user-attachments/assets/86dcb614-dd30-48f9-9048-fcb892a5dbae"
/>

Befre change with open leaderboard:
<img width="491" height="211" alt="image"
src="https://github.com/user-attachments/assets/4313fb89-8a61-4892-8c28-0c0d82bcc03b"
/>

After change with open leaderboard:
<img width="531" height="251" alt="image"
src="https://github.com/user-attachments/assets/9b4e2ed5-8427-4a7a-84dc-69169f53b031"
/>

Mobile before change:
<img width="535" height="180" alt="image"
src="https://github.com/user-attachments/assets/f6cc87be-1f7f-4bc7-bb1d-9afcce45ff6d"
/>

Mobile after change:
<img width="469" height="199" alt="image"
src="https://github.com/user-attachments/assets/ac4baa9d-4051-4938-a0a1-53d38efea7a8"
/>

Mobile before change with open leaderboard:
<img width="501" height="195" alt="image"
src="https://github.com/user-attachments/assets/a086cdd8-7898-4e13-a679-04b63be7a67b"
/>

Mobile after change with open leaderboard:
<img width="446" height="238" alt="image"
src="https://github.com/user-attachments/assets/8cc9c833-b8f9-43bb-9664-8cfb9f608cc4"
/>


Discord username: martoi

***
Signed-off-by: MartinIvovIv <https://github.com/martinIvovIv>
2026-02-06 09:42:41 -08:00
Martin IandGitHub 563ae3f90a fix; improvement proposal for the leaderboard buttons (#3107)
## Description:

Changes the leaderboard buttons to look more like other buttons per
suggestion by @FloPinguin

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

***
Screenshots:

Before change:
<img width="306" height="164" alt="image"
src="https://github.com/user-attachments/assets/3ff5648c-b088-4d3e-a608-901c2e8d0402"
/>

After change:
<img width="263" height="169" alt="image"
src="https://github.com/user-attachments/assets/983f7d50-0c40-4365-9f34-d64a18e69c68"
/>

After change hover:
<img width="315" height="221" alt="image"
src="https://github.com/user-attachments/assets/e56746ae-9e65-437a-b27c-60ac6ab52d6c"
/>

Mobile:
<img width="297" height="251" alt="image"
src="https://github.com/user-attachments/assets/66f6de72-5002-4da9-95d9-63f67b6136e3"
/>


##Discord username: martoi

***
Signed-off-by: MartinIvovIv <https://github.com/martinIvovIv>
2026-02-06 09:42:10 -08:00
scamivandGitHub ec5fb4fa22 Pr fxlayer viewport culling (#3123)
## Description:

Reduce FX layer rendering cost by:
- Updating the offscreen FX buffer only when needed (and clearing it
once when FX ends).
- Drawing only the visible portion of the FX buffer to the main canvas
(viewport culling).
- Reusing `TransformHandler.screenBoundingRect()` as the single source
of truth for viewport bounds.
- 
## Changes
- `FxLayer`:
  - Track buffered frames and skip work when there are no active FX.
  - Use `performance.now()` for refresh timing.
- Draw only the visible map rect (clamp + small pad) instead of blitting
the full map-sized FX canvas.
- Compute the visible rect via `TransformHandler.screenBoundingRect()`.
- `GameRenderer`:
  - Thread `TransformHandler` into `FxLayer` construction.


## Please complete the following:

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

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

DISCORD_USERNAME
2026-02-04 20:18:25 -08:00
evanpelle d358ef79de destroy ads properly 2026-02-03 19:52:07 -08:00
evanpelle 286b31f403 update ad frequencies 2026-02-03 16:21:34 -08:00
FloPinguinandGitHub 596bc90134 Fix the new mobile styles a bit 🖌️ (#3106)
## Description:

### 1. PlayerInfoOverlay had less margin top than the leaderboard and
the game controls. Fixed.

### 2. PlayerInfoOverlay  did overlap with HeadsUpMessage. Fixed.

Previous:

<img width="543" height="174" alt="Screenshot 2026-02-03 184835"
src="https://github.com/user-attachments/assets/8687294b-feb6-409d-995a-971986a9d406"
/>

Now:

<img width="510" height="299" alt="Screenshot 2026-02-03 185117"
src="https://github.com/user-attachments/assets/8b81f5ed-98ea-4154-b485-9de4ed974939"
/>

### 3. Fixed border radius of the lower panels

Previous:

<img width="1200" height="162" alt="Screenshot 2026-02-03 184938"
src="https://github.com/user-attachments/assets/72dc77c4-8992-4812-8b5d-e100e16fe91e"
/>

<img width="1081" height="122" alt="Screenshot 2026-02-03 185014"
src="https://github.com/user-attachments/assets/0291e305-faed-41d2-b5a9-db795b61a8d2"
/>

Now:

<img width="1237" height="151" alt="Screenshot 2026-02-03 184953"
src="https://github.com/user-attachments/assets/40565ab4-cdad-4ea6-81e5-f24d485c7199"
/>

<img width="1054" height="99" alt="Screenshot 2026-02-03 185004"
src="https://github.com/user-attachments/assets/ce091c16-74ac-4a05-8843-8493e08ba6c3"
/>

### 4. Give PlayerInfoOverlay the same gap between gold icon and gold
text as in ControlPanel

Previous:

<img width="772" height="759" alt="Screenshot 2026-02-03 191150"
src="https://github.com/user-attachments/assets/49b14025-9d28-447d-9370-64ad30532abd"
/>

Now:

<img width="803" height="748" alt="Screenshot 2026-02-03 191203"
src="https://github.com/user-attachments/assets/b4749557-bf10-4643-83b1-5e9d22f122ed"
/>

## Please complete the following:

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

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

FloPinguin
2026-02-03 15:16:17 -08:00
EvanandGitHub d04f90ec4a mobile control panel (#3096)
Relates to #2260

## Description:

Redo the control panel to be more mobile friendly and take up less space

![Uploading Screenshot 2026-02-02 at 8.09.13 PM.png…]()
<img width="584" height="236" alt="Screenshot 2026-02-02 at 8 09 34 PM"
src="https://github.com/user-attachments/assets/d48906d5-3653-499c-9b08-b661d5e7d4a4"
/>

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:

evan
2026-02-02 20:12:43 -08:00
EvanandGitHub 7d3ec0fcb8 Move player info panel to top of the screen & simplify (#3087)
related to #2260

## Description:

* Moves the player info panel from the right to the top of the screen
* Disable the header ad for now because it would cover up the player
info, we'll find a better place for it in the future
* Remove the collapsable button/functionality. It's hard to even click
the button because the panel disappears when you move away from a
player, and I think the info is too valuable to ever need to be
collapsed.
* Removed the "land" and "irradiated land" since it didn't add much
value
* Remove all alt text & translation, you can't hover over the player
overlay so it's irrelevant.
* put troop info inside the troop bar to reduce amount of text


<img width="479" height="88" alt="Screenshot 2026-02-01 at 8 57 33 PM"
src="https://github.com/user-attachments/assets/3b72eb16-2efa-4c00-a4d0-5e085548fa78"
/>

<img width="438" height="136" alt="Screenshot 2026-02-01 at 8 58 06 PM"
src="https://github.com/user-attachments/assets/285bb2c9-6deb-4ee8-bcc8-743cccd6b77e"
/>

## Please complete the following:

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

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

evan
2026-02-02 09:09:52 -08:00
FloPinguinandevanpelle 5aa023bba5 Adblockers blocked OUR OWN source files 🔧 (#3085)
## Description:

Renamed ad files because adblockers blocked them because of their name. 
When ES modules fail to import, the entire application breaks.

## Please complete the following:

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

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

FloPinguin
2026-02-01 14:50:46 -08:00
FloPinguinandGitHub 6407dc418a Adblockers blocked OUR OWN source files 🔧 (#3085)
## Description:

Renamed ad files because adblockers blocked them because of their name. 
When ES modules fail to import, the entire application breaks.

## Please complete the following:

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

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

FloPinguin
2026-02-01 16:57:33 +00:00