Skip to content

Conversation

@GlacialDrift
Copy link
Contributor

@GlacialDrift GlacialDrift commented Oct 23, 2025

This PR improves spawn highlighting to identify self and friend/foe.
Suggested Label: Feature
Suggested Milestone: v26 or v27

Description:

This PR changes the behavior for spawn color highlighting and addresses issue #2270.

Currently, the user's spawn highlight color is the same as all other players in FFA and the same as all enemies in Team games. Although "breathing" rings were added recently for the user's spawn highlight, this can still be difficult to find on the map, especially with large player counts.

This PR modifies how spawn highlights are drawn for the user and for allies and enemies in Team games.

User Spawn Color Updates

First, an additional spawn highlight color was added for the user in src/core/configuration/PastelTheme.ts: _spawnHighlightSelfColor. This is defined to be white (0xFFFFFF).

Second, the breathing ring was modified to improve visibility (all in src/client/graphics/layers/TerritoryLayer.ts) :

  • A radial-gradient transparent ring in the spawn color (white) is drawn at all times
    • The ring is transparent for radius = 8 pixels from the player's center point
    • The gradient extends from 8 to 24 pixels from the player's center point
    • The first 10% of the ring (~ 1 pixel) is solid
    • The remaining ring fades to transparent at 24 pixels
  • A solid, "breathing" ring is drawn on top of the transparent ring
    • The solid ring is also transparent for radius = 8 pixels from the center point
    • The outer radius of the solid ring is a function of "time" in the same way that the current breathing ring is implemented.
    • The solid ring therefore grows and shrinks to cover the transparent ring

Other Player Spawn Color Updates

In FFA games, no change is made to the spawn color highlight of other players. It remains rgb(255,213,79).

In team games, the spawn color highlight of other players is updated to use their team colors. For example, a player on the red team will have a red spawn highlight, while a player on the purple team will have a purple spawn highlight.

Both of these changes are handled with a simple update in src/client/graphics/layers/TerritoryLayer.ts within the spawnHighlight() method:

const myPlayer = this.game.myPlayer();
  if (myPlayer !== null && myPlayer !== human && myPlayer.team() === null) {
    // In FFA games (when team === null), use default yellow spawn highlight color
    color = this.theme.spawnHighlightColor();
  } else if (myPlayer !== null && myPlayer !== human) {
    // In Team games, the spawn highlight color becomes that player's team color
    // Optionally, this could be broken down to teammate or enemy and simplified to green and red, respectively
    const team = human.team();
    if (team !== null) color = this.theme.teamColor(team);
  }

Attached Images

Three images have been attached. The three images show a progression of the "breathing" user highlight. They also show the team-specific highlights of players on other teams. (Note that Nations in the private game were assigned teams but do not have spawn highlights).

Screenshot 2025-10-22 211929 Screenshot 2025-10-22 212003 Screenshot 2025-10-22 212028

Misc.

  • I added .idea/ to .gitignore because I use JetBrains IDEs
  • I expanded the fallbackColors list. Right now it has a lot of green colors, so I added equivalents for red, blue, cyan, magenta, and yellow.

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:

GlacialDrift

…e.g. from { r: #, g: #, b: # } to 'rgb(#,#,#)'
… ally, red for enemy, and standard for enemy in FFA. Updated Theme interface with new methods. Updated color selection in spawnHighligh()
…er). Added a semi-transparent permanent ring around the starting position. This ring has a solid inner radius and gradients to transparent at the outer radius. On top of the transparent ring, draw a breathing ring with outer radius a function of 'time'. The breathing ring is solid throughout. Both rings use the 'spawnHighlightSelfColor' and both rings have a fully transparent inner circle where the starting territory is drawn.
… b: # }' to prevent significant code changes.
@GlacialDrift GlacialDrift requested a review from a team as a code owner October 23, 2025 02:52
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

Walkthrough

This PR implements improved spawn highlighting with team-based and self-specific colors. It refactors the TerritoryLayer spawn highlight invocation timing and breathing ring rendering geometry, expands the fallback color palette, and adds new spawn highlight color properties to the Theme interface and PastelTheme implementation.

Changes

Cohort / File(s) Change Summary
Build configuration
.gitignore
Adds .idea/ directory to ignore list for IDE project metadata.
Color palette expansion
src/core/configuration/Colors.ts
Replaces substantial portions of fallbackColors with new color sequences including deep reds, blues, violets, pinks, and pastel tones; reorganizes color entries and removes previous green-toned and neutral entries.
Theme interface extension
src/core/configuration/Config.ts
Adds three new Colord-returning getter methods to Theme interface: spawnHighlightSelfColor(), spawnHighlightTeamColor(), and spawnHighlightEnemyColor().
Theme implementation
src/core/configuration/PastelTheme.ts
Adds private color fields (_selfColor, _allyColor, _neutralColor, _enemyColor) and new spawn highlight color fields (_spawnHighlightSelfColor, _spawnHighlightTeamColor, _spawnHighlightEnemyColor); introduces public accessors for the three new spawn highlight colors.
Layer rendering logic
src/client/graphics/layers/TerritoryLayer.ts
Moves spawn highlight invocation to spawn phase in tick(); reworks spawn highlight color logic for FFA vs team games; refactors breathing ring rendering with updated signature (minRad, maxRad, radius parameters), reduced animation timing (3 → 0.5), dual-layer gradient drawing, and team-based color application.

Sequence Diagram(s)

sequenceDiagram
    participant tick as tick()
    participant spawn as Spawn Phase
    participant highlight as Spawn Highlight
    participant render as Render

    tick->>spawn: Check if spawn phase
    alt Spawn Phase Active
        spawn->>highlight: Invoke spawn highlight
        alt FFA (team() === null)
            highlight->>highlight: Use default spawn color
        else Team Game
            highlight->>highlight: Use highlighted player's team color
        end
        highlight->>render: Draw breathing ring with team color
    else Not Spawn Phase
        tick->>render: Standard rendering
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The changes span multiple files with mixed complexity: the breathing ring refactor requires careful verification of timing and geometry logic, color logic branches need validation across FFA and team scenarios, and the palette changes demand review for visual coherence. However, the changes follow consistent patterns and are primarily additive with clear separation of concerns.

Possibly related issues

  • Issue Improved Spawn Highlighting #2270: PR directly implements "Improved Spawn Highlighting" with team- and self-specific spawn highlight colors and updates TerritoryLayer rendering to use them, including breathing ring and self highlight behavior.

Possibly related PRs

Suggested labels

Feature - Frontend, UI/UX

Suggested reviewers

  • scottanderson
  • evanpelle
  • drillskibo

Poem

🌟 Spawns now glow with team-bound pride,
Breathing rings pulse far and wide,
Self and foe in colors bright,
Team games shine in focused light,
Palette blooms—the canvas right! 🎨

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Feature: Improve Spawn Color Highlighting" directly and accurately describes the primary objective of the changeset. The title captures the main focus across the modifications, which center on improving how spawn highlights are rendered with different colors to distinguish self, teammates, and enemies. While the PR includes secondary changes such as .gitignore updates and color palette expansion, the title appropriately emphasizes the core feature being implemented, which aligns with the substantial modifications to TerritoryLayer.ts and the new spawn highlight color methods added to the Theme interface and PastelTheme implementation.
Description Check ✅ Passed The pull request description is directly related to the changeset and provides substantial context about the modifications. It clearly states the issue being addressed (#2270), explains the current problem with spawn highlighting, and details the specific changes made to TerritoryLayer.ts, PastelTheme.ts, and Config.ts to implement the new spawn color highlighting system. The description includes code examples, references attached screenshots showing the feature in action, and notes secondary changes like .gitignore and color palette expansion. This level of detail and relevance makes the description highly related to the actual changes in the PR.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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

Caution

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

⚠️ Outside diff range comments (1)
src/core/configuration/PastelTheme.ts (1)

54-66: Probable method mismatch: assignTeamPlayerColor may not exist

territoryColor calls this.teamColorAllocator.assignTeamPlayerColor(team, player.id()). Per past learnings, ColorAllocator exposes assignColor(id: string) and assignTeamColor(team: Team), not assignTeamPlayerColor. This would cause a compile error unless you added that method in this PR.

If per‑player variation inside a team is not required, return the team color:

-    if (team !== null) {
-      return this.teamColorAllocator.assignTeamPlayerColor(team, player.id());
-    }
+    if (team !== null) {
+      return this.teamColor(team);
+    }

If you do want per‑player team shades, either add assignTeamPlayerColor to ColorAllocator or derive a shade deterministically from player.id().

Based on learnings

🧹 Nitpick comments (4)
src/core/configuration/PastelTheme.ts (1)

41-48: New spawn highlight getters are added but currently unused outside self

spawnHighlightTeamColor() and spawnHighlightEnemyColor() are exposed but not used in TerritoryLayer (team mode uses theme.teamColor(team) instead). Either wire these in the layer logic or drop them for now to keep the interface lean. If you keep them, consider a single function with a typed union parameter:

  • spawnHighlightColorFor(role: "self" | "team" | "enemy"): Colord

Also applies to: 158-169

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

187-195: Team color choice is fine; consider using theme’s new spawn getters or keep API minimal

Current logic uses theme.teamColor(team) in team games, which matches the PR summary. If you intend to keep the new Theme getters, consider using them here (friendly vs enemy) to justify the API:

-      } else if (myPlayer !== null && myPlayer !== human) {
-        // In Team games, the spawn highlight color becomes that player's team color
-        const team = human.team();
-        if (team !== null) color = this.theme.teamColor(team);
+      } else if (myPlayer !== null && myPlayer !== human) {
+        const team = human.team();
+        if (team !== null && myPlayer.team() !== null) {
+          color = human.isFriendly(myPlayer)
+            ? this.theme.spawnHighlightTeamColor()
+            : this.theme.spawnHighlightEnemyColor();
+        }
       }

Optional, non‑blocking.


219-233: Naming nit: “self” color used for focused player, which may not be “me”

drawFocusedPlayerHighlight uses spawnHighlightSelfColor() for whichever player is focused. Consider renaming to spawnHighlightFocusedColor() or pass myPlayer check if you want “self” to mean my player only. Behavior is OK as is; this is just naming clarity.


563-609: Canvas gradient transparency: prefer rgba(...) over 8‑digit hex for broader compatibility

Some canvases don’t accept #RRGGBBAA everywhere. Use rgba strings from colord to avoid browser quirks.

-    const transparent = color.toHex() + "00";
-    const c = color.toHex();
+    const transparent = color.alpha(0).toRgbString();
+    const c = color.toRgbString();

Optional: make the “~1px solid” part independent of ring thickness:

-    radGrad.addColorStop(0.01, c);
-    radGrad.addColorStop(0.1, c);
+    const solidFrac = Math.max(1 / Math.max(1, maxRad - minRad), 0.05); // ~1px or 5%
+    radGrad.addColorStop(solidFrac, c);
+    radGrad.addColorStop(Math.min(0.1, solidFrac * 2), c);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e278a5 and 46d3e7a.

📒 Files selected for processing (5)
  • .gitignore (1 hunks)
  • src/client/graphics/layers/TerritoryLayer.ts (4 hunks)
  • src/core/configuration/Colors.ts (1 hunks)
  • src/core/configuration/Config.ts (1 hunks)
  • src/core/configuration/PastelTheme.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-10T09:56:44.473Z
Learnt from: Ble4Ch
PR: openfrontio/OpenFrontIO#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/configuration/Config.ts
  • src/core/configuration/PastelTheme.ts
📚 Learning: 2025-06-06T15:42:21.903Z
Learnt from: Ble4Ch
PR: openfrontio/OpenFrontIO#1063
File: src/core/configuration/PastelThemeDark.ts:20-21
Timestamp: 2025-06-06T15:42:21.903Z
Learning: In ColorAllocator class, all methods (assignBotColor, assignPlayerColor, assignTeamColor) use the constructor colors from this.availableColors. Only the predefined team cases in assignTeamColor use hardcoded global colors, while the default case uses constructor colors.

Applied to files:

  • src/core/configuration/PastelTheme.ts
🧬 Code graph analysis (1)
src/client/graphics/layers/TerritoryLayer.ts (2)
src/core/game/GameView.ts (2)
  • myPlayer (585-587)
  • team (333-335)
src/core/game/PlayerImpl.ts (1)
  • team (740-742)
🔇 Additional comments (4)
.gitignore (1)

13-13: LGTM!

Adding .idea/ to gitignore is a standard best practice for JetBrains IDE projects. This prevents IDE-specific metadata from being committed to the repository.

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

76-80: LGTM: move spawnHighlight to start of tick()

Clearer control flow and only runs in spawn phase.

src/core/configuration/Config.ts (1)

201-203: No breaking change concern—methods already implemented and in use

The review comment's premise is incorrect. Only one class implements Theme (PastelTheme), and it already has all three methods implemented (lines 159, 163, 167 in PastelTheme.ts). Additionally, spawnHighlightSelfColor() is actively used in TerritoryLayer.ts:232, so the API addition already pays off.

The suggested refactor to a typed union (spawnHighlightColorFor(role: "self" | "team" | "enemy"): Colord) is a valid optional improvement for cleaner API design, but not required.

Likely an incorrect or invalid review comment.

src/core/configuration/Colors.ts (1)

263-570: Update comment count: 523 colors, not 100

The comment at line 263 states "Currently 100 colors" but the array contains 523 colord() elements. Update this count or remove the number to keep documentation accurate.

Verify contrast handling for very dark fallback colors

The code uses fixed dark text colors (black for humans, dark gray otherwise) in PastelTheme.textColor(). If fallbackColors entries with very low RGB values (like {r: 35, g: 0, b: 0}) are applied as backgrounds, they may create poor contrast with the dark text. Confirm how these fallback colors are used in the UI and whether contrast validation is needed.

Consider programmatic generation (optional)

The large hardcoded array could be generated from ranges and steps, then exported as a frozen constant. This would reduce file size and maintenance burden, though it is optional.

@TheGiraffe3 TheGiraffe3 added this to the v27 milestone Oct 23, 2025
@TheGiraffe3 TheGiraffe3 linked an issue Oct 23, 2025 that may be closed by this pull request
@TheGiraffe3 TheGiraffe3 added the UI/UX UI/UX changes including assets, menus, QoL, etc. label Oct 23, 2025
Copy link
Collaborator

@evanpelle evanpelle left a comment

Choose a reason for hiding this comment

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

Thanks!

@evanpelle evanpelle added this pull request to the merge queue Oct 24, 2025
Merged via the queue into openfrontio:main with commit 69373e2 Oct 24, 2025
11 of 13 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 26, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

UI/UX UI/UX changes including assets, menus, QoL, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improved Spawn Highlighting

3 participants