Skip to content

Conversation

@Sashazach
Copy link
Contributor

@Sashazach Sashazach commented Dec 28, 2025

Description:

This PR introduces a Color Blind Assist mode as a setting. It is designed to make it easier for color-blind people to see where their team is spawning/clustering during team games. When enabled, this setting creates pulsing golden indicators/highlights in the inner circle of the highlights that appear (that show you where people are spawning) during the spawn-in phase for your team. It does not highlight you, only your teammates. As a moderately color-blind person myself, this would be extremely helpful. I have often accidentally spawned in the purple area as a blue team member or the red area as a green team member etc.

image

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:

Sashazach#sashazach

@Sashazach Sashazach requested review from a team as code owners December 28, 2025 20:33
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

Walkthrough

Adds a color‑blind accessibility toggle and translations, a new teammate glow renderer, and integrates a pulsing teammate glow into spawn highlighting and focused-player visuals for team games; also exposes spawnTile on player updates and views.

Changes

Cohort / File(s) Summary
Translations
resources/lang/en.json
Added user_setting.color_blind_label and user_setting.color_blind_desc translation keys.
User settings
src/core/game/UserSettings.ts
Added colorBlind() getter and toggleColorBlind() mutator to read/toggle the color‑blind setting.
Settings UI
src/client/UserSettingModal.ts, src/client/graphics/layers/SettingsModal.ts
Added UI toggle and handlers to surface the color‑blind setting and trigger updates/redraws.
Teammate glow renderer
src/client/graphics/TeammateGlow.ts
New module exporting TeammateGlowOptions and drawTeammateGlow() to render a pulsing radial glow on canvas with outerRadius and pulsePhase.
Territory layer integration
src/client/graphics/layers/TerritoryLayer.ts
Imported glow renderer; added SPAWN_HIGHLIGHT_RADIUS and PULSE_SPEED; compute spawn centers from owned tiles; draw teammate glow for teammates when color‑blind team mode is active; apply glow to focused-player highlight.
Player spawn propagation
src/core/game/GameUpdates.ts, src/core/game/GameView.ts, src/core/game/PlayerImpl.ts
Added optional spawnTile?: TileRef to PlayerUpdate, exposed spawnTile() on PlayerView, and included spawnTile in PlayerImpl.toUpdate() payload.

Sequence Diagram

sequenceDiagram
    actor User
    participant SettingsModal as Settings Modal
    participant UserSettings as User Settings
    participant TerritoryLayer as Territory Layer
    participant Canvas as Canvas Renderer

    User->>SettingsModal: Toggle "Color‑blind" setting
    SettingsModal->>UserSettings: toggleColorBlind()
    UserSettings-->>SettingsModal: state updated
    SettingsModal->>TerritoryLayer: request redraw / UI refresh

    rect rgba(120,180,240,0.12)
        Note over TerritoryLayer: Spawn highlight render pass
        TerritoryLayer->>UserSettings: colorBlind()?
        alt Color‑blind team mode ON
            TerritoryLayer->>TerritoryLayer: compute spawn center from owned tiles
            TerritoryLayer->>TerritoryLayer: compute pulse phase (time * PULSE_SPEED)
            TerritoryLayer->>Canvas: drawTeammateGlow(ctx, x, y, {outerRadius, pulsePhase})
            Canvas-->>TerritoryLayer: glow drawn
        else Normal mode
            TerritoryLayer->>Canvas: draw standard spawn highlight
        end
    end

    Canvas-->>User: Updated spawn visuals visible
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Feature - New, UI/UX, Feature - Frontend

Poem

A golden pulse around each friend,
A tiny ring so maps can mend.
Flip the switch, the spawns align,
Teammates glow — the game feels fine. ✨

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Color blind setting' directly and clearly describes the main feature being added—a color-blind accessibility setting.
Description check ✅ Passed The description is detailed and directly relates to the changeset, explaining the feature's purpose, implementation details, and user benefits.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/graphics/layers/TerritoryLayer.ts (1)

1-1: Fix Prettier formatting.

The CI pipeline reports formatting issues. Run npx prettier --write src/client/graphics/layers/TerritoryLayer.ts to fix.

♻️ Duplicate comments (5)
resources/lang/pl.json (1)

315-316: Remove non-English locale updates from this PR.

Same issue as in other locale files - non-English translations should be added in dedicated translation PRs only. Please remove these changes.

Based on learnings.

resources/lang/fr.json (1)

335-336: Remove non-English locale updates from this PR.

Same issue - please remove French translation updates and keep only en.json changes.

Based on learnings.

resources/lang/ko.json (1)

262-263: Remove non-English locale updates from this PR.

Korean translations should also be removed and added only in dedicated translation PRs.

Based on learnings.

resources/lang/hi.json (1)

188-189: Remove non-English locale updates from this PR.

Hindi translations should be removed from this PR per project translation workflow.

Based on learnings.

resources/lang/bg.json (1)

335-336: Remove non-English locale updates from this PR.

Bulgarian translations should also be removed. Only en.json should be updated with new keys in this PR.

Based on learnings.

🧹 Nitpick comments (2)
src/client/graphics/TeammateGlow.ts (1)

14-18: Redundant validation check.

Line 14 uses Math.max(1, options.outerRadius) which guarantees outerRadius >= 1. The check at line 18 for outerRadius <= 0 can never be true after that. Consider removing the redundant part.

Proposed fix
 export function drawTeammateGlow(
   ctx: CanvasRenderingContext2D,
   x: number,
   y: number,
   options: TeammateGlowOptions,
 ): void {
-  const outerRadius = Math.max(1, options.outerRadius);
   const phase = options.pulsePhase ?? 0;
 
   if (!Number.isFinite(x) || !Number.isFinite(y)) return;
-  if (!Number.isFinite(outerRadius) || outerRadius <= 0) return;
+  if (!Number.isFinite(options.outerRadius) || options.outerRadius <= 0) return;
+
+  const outerRadius = Math.max(1, options.outerRadius);
src/client/graphics/layers/TerritoryLayer.ts (1)

191-193: Consider extracting the color-blind check to avoid duplication.

The condition this.userSettings.colorBlind() && this.game.config().gameConfig().gameMode === GameMode.Team appears both in spawnHighlight (lines 191-193) and drawFocusedPlayerHighlight (lines 285-286). A small helper or property could reduce repetition.

Proposed helper
+  private isColorBlindTeamMode(): boolean {
+    return (
+      this.userSettings.colorBlind() &&
+      this.game.config().gameConfig().gameMode === GameMode.Team
+    );
+  }
+
   private spawnHighlight() {
     // ...
     const myPlayer = this.game.myPlayer();
-    const colorBlindEnabled =
-      this.userSettings.colorBlind() &&
-      this.game.config().gameConfig().gameMode === GameMode.Team;
+    const colorBlindEnabled = this.isColorBlindTeamMode();
     // ...
   }

Also applies to: 283-289

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7284ded and b5d077e.

📒 Files selected for processing (38)
  • resources/lang/ar.json
  • resources/lang/bg.json
  • resources/lang/bn.json
  • resources/lang/cs.json
  • resources/lang/da.json
  • resources/lang/de.json
  • resources/lang/el.json
  • resources/lang/en.json
  • resources/lang/eo.json
  • resources/lang/es.json
  • resources/lang/fa.json
  • resources/lang/fi.json
  • resources/lang/fr.json
  • resources/lang/gl.json
  • resources/lang/he.json
  • resources/lang/hi.json
  • resources/lang/hu.json
  • resources/lang/it.json
  • resources/lang/ja.json
  • resources/lang/ko.json
  • resources/lang/mk.json
  • resources/lang/nl.json
  • resources/lang/pl.json
  • resources/lang/pt-PT.json
  • resources/lang/ru.json
  • resources/lang/sh.json
  • resources/lang/sk.json
  • resources/lang/sl.json
  • resources/lang/sv-SE.json
  • resources/lang/tp.json
  • resources/lang/tr.json
  • resources/lang/uk.json
  • resources/lang/zh-CN.json
  • src/client/UserSettingModal.ts
  • src/client/graphics/TeammateGlow.ts
  • src/client/graphics/layers/SettingsModal.ts
  • src/client/graphics/layers/TerritoryLayer.ts
  • src/core/game/UserSettings.ts
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.
📚 Learning: 2025-06-02T14:27:37.609Z
Learnt from: andrewNiziolek
Repo: openfrontio/OpenFrontIO PR: 1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.

Applied to files:

  • resources/lang/ru.json
  • resources/lang/fa.json
  • resources/lang/pl.json
  • resources/lang/cs.json
  • resources/lang/eo.json
  • resources/lang/fi.json
  • resources/lang/bg.json
  • resources/lang/uk.json
  • resources/lang/sk.json
  • resources/lang/hi.json
  • resources/lang/mk.json
  • resources/lang/ko.json
  • resources/lang/sh.json
  • resources/lang/es.json
  • resources/lang/pt-PT.json
  • resources/lang/tp.json
📚 Learning: 2025-08-16T10:52:08.292Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 884
File: resources/lang/en.json:456-461
Timestamp: 2025-08-16T10:52:08.292Z
Learning: In OpenFrontIO, translation files in resources/lang/*.json (except en.json) should not be updated in regular PRs. Only dedicated translation PRs titled "mls" and made by Aotumori should update non-English locale files. Regular PRs should only update en.json when adding or modifying translation keys.

Applied to files:

  • resources/lang/en.json
  • resources/lang/pl.json
  • resources/lang/sh.json
  • resources/lang/ja.json
📚 Learning: 2025-06-10T09:56:44.473Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/PastelThemeDark.ts:53-53
Timestamp: 2025-06-10T09:56:44.473Z
Learning: In ColorAllocator class in src/core/configuration/Colors.ts, the correct method names are assignColor(id: string): Colord for general color assignment and assignTeamColor(team: Team): Colord for team-specific colors. There are no assignPlayerColor() or assignBotColor() methods.

Applied to files:

  • src/core/game/UserSettings.ts
  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-10-26T15:37:07.732Z
Learnt from: GlacialDrift
Repo: openfrontio/OpenFrontIO PR: 2298
File: src/client/graphics/layers/TerritoryLayer.ts:200-210
Timestamp: 2025-10-26T15:37:07.732Z
Learning: In GameImpl.ts lines 124-139, team assignment logic varies by number of teams: when numPlayerTeams < 8, teams are assigned ColoredTeams values (Red, Blue, Yellow, Green, Purple, Orange, Teal); when numPlayerTeams >= 8, teams are assigned generic string identifiers like "Team 1", "Team 2", etc., which are not members of ColoredTeams.

Applied to files:

  • src/client/graphics/TeammateGlow.ts
  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-22T21:51:14.990Z
Learnt from: devalnor
Repo: openfrontio/OpenFrontIO PR: 1248
File: src/client/graphics/layers/TerritoryInfoLayer.ts:20-20
Timestamp: 2025-06-22T21:51:14.990Z
Learning: In TerritoryInfoLayer.ts, the highlightedTerritory field uses both null and undefined intentionally: undefined represents initial state or inactive layer (Ctrl released), while null represents active layer with no territory being highlighted at cursor position. This distinction is important for proper state change detection.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-22T05:48:19.241Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 786
File: src/client/TerritoryPatternsModal.ts:337-338
Timestamp: 2025-06-22T05:48:19.241Z
Learning: In src/client/TerritoryPatternsModal.ts, the bit shifting operators (<<) used in coordinate calculations with decoder.getScale() are intentional and should not be changed to multiplication. The user scottanderson confirmed this is functioning as intended.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-06T21:43:15.706Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:398-411
Timestamp: 2025-06-06T21:43:15.706Z
Learning: In ColorAllocator class, the assignPlayerColor method intentionally allows color duplicates when the humanColors palette is exhausted by resetting availableColors to the full humanColors array. This is done on purpose as an acceptable temporary limitation, with potential palette improvements planned for later.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-06T15:36:31.739Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-12-26T22:21:15.466Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:15.466Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-11-01T00:24:33.860Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2161
File: src/core/execution/FakeHumanExecution.ts:670-678
Timestamp: 2025-11-01T00:24:33.860Z
Learning: In OpenFrontIO, PlayerType.Bot entities cannot be in teams and do not have friendliness relationships. Only PlayerType.Human and PlayerType.FakeHuman can participate in teams and alliances. Therefore, when targeting bot-owned tiles, friendliness checks like `owner.isFriendly(this.player)` are unnecessary and meaningless for bots.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
🧬 Code graph analysis (3)
src/client/graphics/layers/SettingsModal.ts (1)
src/client/LangSelector.ts (1)
  • translateText (258-278)
src/client/UserSettingModal.ts (1)
src/client/LangSelector.ts (1)
  • translateText (258-278)
src/client/graphics/layers/TerritoryLayer.ts (4)
src/client/graphics/layers/Layer.ts (1)
  • Layer (1-7)
src/core/game/GameView.ts (4)
  • myPlayer (728-730)
  • tile (110-112)
  • focusedPlayer (914-916)
  • PlayerView (185-582)
src/core/game/GameMap.ts (2)
  • euclDistFN (371-392)
  • TileRef (3-3)
src/client/graphics/TeammateGlow.ts (1)
  • drawTeammateGlow (8-50)
🪛 GitHub Actions: 🧪 CI
src/client/graphics/TeammateGlow.ts

[warning] 1-1: Prettier formatting issues found in file. Run 'npx prettier --write' to fix.

src/client/graphics/layers/TerritoryLayer.ts

[warning] 1-1: Prettier formatting issues found in file. Run 'npx prettier --write' to fix.

🔇 Additional comments (33)
resources/lang/tr.json (1)

283-284: Localization entries look good.

The new color_blind_label and color_blind_desc keys are correctly placed within the user_setting object. The Turkish translations align with the feature's purpose of helping identify teammates during spawn phase.

resources/lang/zh-CN.json (1)

335-336: Localization entries look good.

The Simplified Chinese translations for color_blind_label and color_blind_desc are correctly added. The wording aligns with the accessibility feature's purpose.

resources/lang/sl.json (1)

286-287: Localization entries look good.

The Slovenian translations for the color-blind accessibility feature are correctly added under user_setting. The structure matches the pattern used for other settings.

resources/lang/he.json (1)

258-259: Localization entries look good.

The Hebrew translations for color_blind_label and color_blind_desc are correctly added. The structure is consistent with other user settings in this file.

resources/lang/bn.json (1)

188-189: Localization entries look good.

The Bengali translations for the color-blind accessibility feature are correctly added under user_setting. The key naming and placement follow the existing patterns.

resources/lang/es.json (1)

227-228: Localization entries look good.

The Spanish translations for color_blind_label and color_blind_desc are correctly added. The term "daltonismo" appropriately describes color blindness in Spanish. Based on learnings, community translators handle translation accuracy separately from technical changes.

resources/lang/nl.json (1)

335-336: Localization entries look good.

The Dutch translations for color_blind_label and color_blind_desc are correctly added. The wording "Kleurenblind hulp" and the description about "spawn-fase" clearly convey the feature's purpose.

resources/lang/da.json (1)

269-270: Localization entries look good.

The Danish translations for color_blind_label and color_blind_desc are correctly added. The compound word "Farveblindhjælp" follows Danish naming conventions, and the description clearly explains the feature.

resources/lang/tp.json (1)

213-214: LGTM - Color-blind accessibility localization added correctly.

The new localization keys are properly structured and consistently placed within the user_setting section, following the same pattern as other settings in this file.

resources/lang/gl.json (1)

269-270: LGTM - Localization keys properly added.

The color-blind accessibility strings are correctly integrated into the user settings section with proper JSON structure.

resources/lang/fi.json (1)

283-284: LGTM - Color-blind setting localization added.

The additions follow the established pattern and are properly structured within the user settings section.

resources/lang/eo.json (1)

286-287: LGTM - Accessibility localization properly implemented.

The new keys are correctly placed and follow the same structure as other user setting entries in this file.

resources/lang/ar.json (1)

213-214: LGTM - Color-blind setting keys added correctly.

The localization strings are properly structured and positioned within the user settings section.

resources/lang/uk.json (1)

335-336: LGTM - Localization additions are properly structured.

The new color-blind accessibility keys are correctly integrated into the user settings section following the established pattern.

resources/lang/sk.json (1)

269-270: LGTM - Color-blind accessibility keys added correctly.

The localization strings are properly positioned and follow the same structure as other user settings in this file.

resources/lang/pt-PT.json (1)

269-270: LGTM - Final locale file properly updated.

The color-blind accessibility localization keys are correctly added and follow the established pattern. All 8 language files in this review show consistent, well-structured additions that properly support the new Color Blind Assist feature.

resources/lang/en.json (1)

374-375: LGTM! English locale keys added correctly.

The new localization keys follow the existing naming conventions and provide clear, concise text for the color-blind assist feature. The label and description are appropriate and user-friendly.

src/client/graphics/layers/SettingsModal.ts (2)

146-149: LGTM! Handler follows established pattern.

The onToggleColorBlindButtonClick handler correctly toggles the color-blind setting and refreshes the component. Implementation is consistent with other toggle handlers in this file.


341-364: LGTM! UI button properly implemented.

The color-blind toggle button follows the same structure and pattern as other settings buttons. The translation keys are correctly used, and the on/off state is properly displayed. The placement between dark mode and special effects settings is logical.

src/core/game/UserSettings.ts (2)

76-78: LGTM! Clean implementation following existing patterns.

The colorBlind() getter follows the same pattern as other boolean settings in this class, using localStorage with a sensible default of false.


135-137: LGTM! Toggle method is consistent with other settings.

The toggleColorBlind() method follows the established pattern used by other toggle methods in this class.

src/client/UserSettingModal.ts (2)

110-116: LGTM! Clean handler following established patterns.

The toggleColorBlind handler is well-implemented with type checking and follows the same pattern as other simple toggle handlers like toggleEmojis.


293-300: LGTM! UI toggle properly wired and positioned.

The color-blind toggle is correctly:

  • Using translateText() for localized label and description
  • Bound to the current colorBlind() state
  • Connected to the toggle handler
  • Positioned logically in the settings list
resources/lang/hu.json (1)

286-287: Localization keys added correctly.

The new color_blind_label and color_blind_desc keys follow the existing pattern used by other settings in this file. Placement is consistent.

resources/lang/de.json (1)

258-259: Localization keys added correctly.

The German translations for color_blind_label and color_blind_desc are consistent with the existing pattern. Placement looks good.

resources/lang/cs.json (1)

227-228: Localization keys added correctly.

The Czech translations for the color-blind accessibility feature are properly placed and follow the existing pattern.

resources/lang/it.json (1)

227-228: Localization keys added correctly.

The Italian translations for the color-blind accessibility feature are properly placed and follow the existing pattern.

src/client/graphics/TeammateGlow.ts (1)

20-49: Clean rendering logic.

The pulsing effect between white and gold layers is well implemented. Good use of ctx.save()/ctx.restore() to isolate canvas state changes.

src/client/graphics/layers/TerritoryLayer.ts (5)

29-30: Good use of named constants.

Extracting SPAWN_HIGHLIGHT_RADIUS and PULSE_SPEED as static constants improves readability and makes future adjustments easier.


298-348: Well-structured helper method.

spawnCenterFromOwnedTiles cleanly computes the center position from owned tiles with proper null handling for empty input. The inner radius clamping between 2 and SPAWN_HIGHLIGHT_RADIUS - 1 is sensible.


350-379: Efficient tile accumulation.

The method collects owned tiles and sums coordinates in a single pass through the BFS results. Good use of composition with spawnCenterFromOwnedTiles and drawTeammateGlow.


225-241: Correct teammate glow integration.

The conditions properly exclude the player themselves and only show glow for actual teammates. Using isOnSameTeam ensures proper team detection.


282-296: Consistent integration for focused player.

The teammate glow logic for the focused player matches the pattern used in spawnHighlight. The conditional guard ensures glow only appears for teammates in Team mode with color-blind setting enabled.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/client/graphics/layers/TerritoryLayer.ts (1)

298-377: LGTM: Solid geometric calculations.

The helper methods correctly compute spawn centers from owned tiles using centroid calculations and distance measurements. Null handling is appropriate, and the pulse animation timing is well-implemented.

Optional: Extract magic number constants

Lines 335 and 373 contain magic numbers for minimum radii:

  private static readonly SPAWN_HIGHLIGHT_RADIUS = 9;
  private static readonly PULSE_SPEED = 0.2;
+ private static readonly MIN_INNER_RADIUS = 2;
+ private static readonly MIN_OUTER_RADIUS = 3;

  private spawnCenterFromOwnedTiles(...) {
    ...
    const innerRadius = Math.max(
-     2,
+     TerritoryLayer.MIN_INNER_RADIUS,
      Math.min(TerritoryLayer.SPAWN_HIGHLIGHT_RADIUS - 1, Math.sqrt(maxDistSq)),
    );
    ...
  }

  private drawTeammateGlowForPlayer(...) {
    ...
    if (spawnCenter) {
      drawTeammateGlow(this.highlightContext, spawnCenter.x, spawnCenter.y, {
-       outerRadius: Math.max(3, spawnCenter.innerRadius),
+       outerRadius: Math.max(TerritoryLayer.MIN_OUTER_RADIUS, spawnCenter.innerRadius),
        pulsePhase: this.game.ticks() * TerritoryLayer.PULSE_SPEED,
      });
    }
  }

This is a minor improvement and can be deferred.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5d077e and 74666f0.

📒 Files selected for processing (2)
  • src/client/graphics/TeammateGlow.ts
  • src/client/graphics/layers/TerritoryLayer.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/client/graphics/TeammateGlow.ts
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.
📚 Learning: 2025-06-22T21:51:14.990Z
Learnt from: devalnor
Repo: openfrontio/OpenFrontIO PR: 1248
File: src/client/graphics/layers/TerritoryInfoLayer.ts:20-20
Timestamp: 2025-06-22T21:51:14.990Z
Learning: In TerritoryInfoLayer.ts, the highlightedTerritory field uses both null and undefined intentionally: undefined represents initial state or inactive layer (Ctrl released), while null represents active layer with no territory being highlighted at cursor position. This distinction is important for proper state change detection.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-10T09:56:44.473Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/PastelThemeDark.ts:53-53
Timestamp: 2025-06-10T09:56:44.473Z
Learning: In ColorAllocator class in src/core/configuration/Colors.ts, the correct method names are assignColor(id: string): Colord for general color assignment and assignTeamColor(team: Team): Colord for team-specific colors. There are no assignPlayerColor() or assignBotColor() methods.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-10-26T15:37:07.732Z
Learnt from: GlacialDrift
Repo: openfrontio/OpenFrontIO PR: 2298
File: src/client/graphics/layers/TerritoryLayer.ts:200-210
Timestamp: 2025-10-26T15:37:07.732Z
Learning: In GameImpl.ts lines 124-139, team assignment logic varies by number of teams: when numPlayerTeams < 8, teams are assigned ColoredTeams values (Red, Blue, Yellow, Green, Purple, Orange, Teal); when numPlayerTeams >= 8, teams are assigned generic string identifiers like "Team 1", "Team 2", etc., which are not members of ColoredTeams.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-06T21:43:15.706Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:398-411
Timestamp: 2025-06-06T21:43:15.706Z
Learning: In ColorAllocator class, the assignPlayerColor method intentionally allows color duplicates when the humanColors palette is exhausted by resetting availableColors to the full humanColors array. This is done on purpose as an acceptable temporary limitation, with potential palette improvements planned for later.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-06-06T15:36:31.739Z
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-12-26T22:21:15.466Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:15.466Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-11-01T00:24:33.860Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2161
File: src/core/execution/FakeHumanExecution.ts:670-678
Timestamp: 2025-11-01T00:24:33.860Z
Learning: In OpenFrontIO, PlayerType.Bot entities cannot be in teams and do not have friendliness relationships. Only PlayerType.Human and PlayerType.FakeHuman can participate in teams and alliances. Therefore, when targeting bot-owned tiles, friendliness checks like `owner.isFriendly(this.player)` are unnecessary and meaningless for bots.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/client/graphics/layers/TerritoryLayer.ts
🔇 Additional comments (4)
src/client/graphics/layers/TerritoryLayer.ts (4)

8-8: LGTM: Clean imports and constants.

The GameMode import and drawTeammateGlow utility are used appropriately. The new constants eliminate magic numbers and make the spawn highlight radius and pulse animation speed clear and maintainable.

Also applies to: 24-24, 29-30


190-193: LGTM: Efficient and correct feature gating.

Moving myPlayer outside the loop eliminates redundant lookups, and the colorBlindEnabled check correctly restricts the assist mode to Team games with the setting enabled.


234-241: LGTM with minor performance observation.

The teammate glow rendering logic is well-structured with proper null checks and team validation. The conditions correctly exclude self-highlighting and restrict glow to actual teammates.

Note: Each teammate triggers a BFS traversal per frame during spawn phase. While the spawn phase is brief, consider monitoring frame rates in games with many teammates (8+ team games) if performance issues arise.

Also applies to: 283-295


229-231: The spawn highlight condition checking ownerID(tile) === 0 appears intentional and correctly highlights unowned (terra nullius) tiles within the spawn radius. This aligns with the spawn feature's purpose of showing expansion potential. The git history does not show evidence of a change from human.smallID() to 0 at this line. Line 359 in spawnCenterFromOwnedTiles() correctly uses player.smallID() for its distinct purpose of finding the center of a player's owned tiles.

Likely an incorrect or invalid review comment.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 28, 2025
Duwibi
Duwibi previously approved these changes Dec 28, 2025
Copy link
Contributor

@Duwibi Duwibi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome! If this gets added, I'd most likely also be using it, as it looks much more helpful than what we have currently in game, although I'm not colour-blind myself :)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/client/graphics/layers/SettingsModal.ts (1)

341-364: Consider using a distinct icon for the color blind setting.

The color blind toggle currently uses settingsIcon, which is identical to the performance overlay icon (line 506). Using a distinct icon would improve visual scanning and help users quickly locate the accessibility option.

💡 Potential alternatives

If an accessibility or color-blind specific icon is available in the project, use it. For example:

  <img
-   src=${settingsIcon}
+   src=${accessibilityIcon}
    alt="colorBlind"
    width="20"
    height="20"
  />

If no dedicated icon exists, consider importing or creating one that visually represents accessibility or color contrast.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74666f0 and 49c04ce.

📒 Files selected for processing (2)
  • resources/lang/en.json
  • src/client/graphics/layers/SettingsModal.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • resources/lang/en.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.
🧬 Code graph analysis (1)
src/client/graphics/layers/SettingsModal.ts (1)
src/client/LangSelector.ts (1)
  • translateText (258-278)
🔇 Additional comments (1)
src/client/graphics/layers/SettingsModal.ts (1)

146-149: No RefreshGraphicsEvent is needed for the color blind toggle. The colorBlind() setting is read reactively within TerritoryLayer.spawnHighlight() during each render frame, not cached at initialization. The effect will be visible immediately on the next frame without requiring an explicit refresh event.

}
}

private spawnCenterFromOwnedTiles(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have Player.spawnTile(), just need to pass that through GameUpdate and then expose it to PlayerView

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that! I should have checked for that myself ;/

@Sashazach Sashazach dismissed stale reviews from Duwibi and coderabbitai[bot] via d9be493 December 30, 2025 21:52
Comment on lines +284 to +292
if (
this.userSettings.colorBlind() &&
this.game.config().gameConfig().gameMode === GameMode.Team &&
myPlayer !== null &&
focusedPlayer.smallID() !== myPlayer.smallID() &&
myPlayer.isOnSameTeam(focusedPlayer)
) {
this.drawTeammateGlowForPlayer(focusedPlayer);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is duplicated, can you move this check into drawTeammateGlowForPlayer, and then rename it "maybeDrawTeammateGlow"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes will do! Thanks for the feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
resources/lang/en.json (1)

380-381: LGTM! Solid accessibility addition.

The translation entries follow the established naming convention and provide clear, user-friendly text. The feature adds valuable accessibility support for team games.

Optional: Consider a slightly more descriptive explanation

The current description is good, but you could make it more specific about the visual aid:

-    "color_blind_desc": "Adds visual aid to make spotting teammates during spawn phase easier",
+    "color_blind_desc": "Adds pulsing visual indicators to teammates during spawn phase for easier identification",

This is completely optional—the current version is clear and avoids being color-specific, which may actually be preferable for accessibility.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9be493 and df92d45.

📒 Files selected for processing (2)
  • resources/lang/en.json
  • src/core/game/PlayerImpl.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: Ble4Ch
Repo: openfrontio/OpenFrontIO PR: 1063
File: src/core/configuration/Colors.ts:114-330
Timestamp: 2025-06-06T15:36:31.739Z
Learning: In the OpenFrontIO project, color improvements are implemented incrementally. The current focus for player colors is ensuring sufficient unique color availability rather than optimizing for visual distinction or accessibility features.
📚 Learning: 2025-08-16T10:52:08.292Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 884
File: resources/lang/en.json:456-461
Timestamp: 2025-08-16T10:52:08.292Z
Learning: In OpenFrontIO, translation files in resources/lang/*.json (except en.json) should not be updated in regular PRs. Only dedicated translation PRs titled "mls" and made by Aotumori should update non-English locale files. Regular PRs should only update en.json when adding or modifying translation keys.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-06-02T14:27:37.609Z
Learnt from: andrewNiziolek
Repo: openfrontio/OpenFrontIO PR: 1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/core/game/PlayerImpl.ts
📚 Learning: 2025-11-26T20:49:29.140Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2519
File: src/core/game/GameView.ts:516-525
Timestamp: 2025-11-26T20:49:29.140Z
Learning: In GameView.ts, when usesSharedTileState is true (SAB mode), packedTileUpdates contains unpacked tile references as BigInt(tileRef) only, because all tile state lives in the shared Uint16Array. In non-SAB mode, packedTileUpdates contains packed TileUpdate bigints in the format (tileRef << 16n | state), which must be decoded via updateTile(tu). Therefore, Number(tu) is correct in SAB mode and shifting right by 16 bits would be wrong.

Applied to files:

  • src/core/game/PlayerImpl.ts
📚 Learning: 2025-08-12T00:31:50.144Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 1752
File: src/core/game/Game.ts:750-752
Timestamp: 2025-08-12T00:31:50.144Z
Learning: In the OpenFrontIO codebase, changes to the PlayerInteraction interface (like adding canDonateGold and canDonateTroops flags) do not require corresponding updates to src/core/Schemas.ts or server serialization code.

Applied to files:

  • src/core/game/PlayerImpl.ts
🔇 Additional comments (1)
src/core/game/PlayerImpl.ts (1)

180-180: LGTM! Clean addition of spawn tile state to player updates.

The new field exposes the spawn tile to the PlayerUpdate payload, logically placed right after hasSpawned. The implementation follows existing patterns and maintains type consistency with the PlayerUpdate interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Peer Review

Development

Successfully merging this pull request may close these issues.

4 participants