Players on the same team get a distinct color based on the team color (#1297)

## Description:

Instead of all teams having the same color, they get a different color.

### 2 teams


![blue-red](https://github.com/user-attachments/assets/7ec88d25-af1d-42de-8cda-f0ffdc7029d5)

### All teams


![all-teams](https://github.com/user-attachments/assets/97f8a51b-b5a4-40ec-8a96-f749547d354c)

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

pineappleprince

---------

Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com>
Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
sidneyprins
2025-07-26 01:00:57 +02:00
committed by GitHub
parent 9b6c290ec7
commit 79c638c1ca
5 changed files with 149 additions and 34 deletions
+48 -27
View File
@@ -6,14 +6,14 @@ import { ColoredTeams, Team } from "../game/Game";
import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import {
blue,
botColor,
green,
orange,
purple,
red,
teal,
yellow,
blueTeamColors,
botTeamColors,
greenTeamColors,
orangeTeamColors,
purpleTeamColors,
redTeamColors,
tealTeamColors,
yellowTeamColors,
} from "./Colors";
extend([lchPlugin]);
extend([labPlugin]);
@@ -22,12 +22,36 @@ export class ColorAllocator {
private availableColors: Colord[];
private fallbackColors: Colord[];
private assigned = new Map<string, Colord>();
private teamPlayerColors = new Map<string, Colord>();
constructor(colors: Colord[], fallback: Colord[]) {
this.availableColors = [...colors];
this.fallbackColors = [...colors, ...fallback];
}
private getTeamColorVariations(team: Team): Colord[] {
switch (team) {
case ColoredTeams.Blue:
return blueTeamColors;
case ColoredTeams.Red:
return redTeamColors;
case ColoredTeams.Teal:
return tealTeamColors;
case ColoredTeams.Purple:
return purpleTeamColors;
case ColoredTeams.Yellow:
return yellowTeamColors;
case ColoredTeams.Orange:
return orangeTeamColors;
case ColoredTeams.Green:
return greenTeamColors;
case ColoredTeams.Bot:
return botTeamColors;
default:
throw new Error(`Unknown team color: ${team}`);
}
}
assignColor(id: string): Colord {
if (this.assigned.has(id)) {
return this.assigned.get(id)!;
@@ -58,26 +82,23 @@ export class ColorAllocator {
}
assignTeamColor(team: Team): Colord {
switch (team) {
case ColoredTeams.Blue:
return blue;
case ColoredTeams.Red:
return red;
case ColoredTeams.Teal:
return teal;
case ColoredTeams.Purple:
return purple;
case ColoredTeams.Yellow:
return yellow;
case ColoredTeams.Orange:
return orange;
case ColoredTeams.Green:
return green;
case ColoredTeams.Bot:
return botColor;
default:
return this.assignColor(team);
const teamColors = this.getTeamColorVariations(team);
return teamColors[0];
}
assignTeamPlayerColor(team: Team, playerId: string): Colord {
if (this.teamPlayerColors.has(playerId)) {
return this.teamPlayerColors.get(playerId)!;
}
const teamColors = this.getTeamColorVariations(team);
const hashValue = simpleHash(playerId);
const colorIndex = hashValue % teamColors.length;
const color = teamColors[colorIndex];
this.teamPlayerColors.set(playerId, color);
return color;
}
}
+31 -3
View File
@@ -1,17 +1,45 @@
import { colord, Colord, extend } from "colord";
import labPlugin from "colord/plugins/lab";
import lchPlugin from "colord/plugins/lch";
extend([lchPlugin]);
extend([labPlugin]);
export const red: Colord = colord({ r: 235, g: 53, b: 53 }); // Bright Red
export const blue: Colord = colord({ r: 41, g: 98, b: 255 }); // Royal Blue
export const red = colord({ h: 0, s: 82, l: 56 });
export const blue = colord({ h: 224, s: 100, l: 58 });
export const teal = colord({ h: 172, s: 66, l: 50 });
export const purple = colord({ h: 271, s: 81, l: 56 });
export const yellow = colord({ h: 45, s: 93, l: 47 });
export const orange = colord({ h: 25, s: 95, l: 53 });
export const green = colord({ h: 128, s: 49, l: 50 });
export const botColor: Colord = colord({ r: 210, g: 206, b: 200 }); // Muted Beige Gray
export const botColor = colord({ h: 36, s: 10, l: 80 });
export const redTeamColors: Colord[] = generateTeamColors(red);
export const blueTeamColors: Colord[] = generateTeamColors(blue);
export const tealTeamColors: Colord[] = generateTeamColors(teal);
export const purpleTeamColors: Colord[] = generateTeamColors(purple);
export const yellowTeamColors: Colord[] = generateTeamColors(yellow);
export const orangeTeamColors: Colord[] = generateTeamColors(orange);
export const greenTeamColors: Colord[] = generateTeamColors(green);
export const botTeamColors: Colord[] = [colord(botColor)];
function generateTeamColors(baseColor: Colord): Colord[] {
const { h: baseHue, s: baseSaturation, l: baseLightness } = baseColor.toHsl();
const colorCount = 64;
return Array.from({ length: colorCount }, (_, index) => {
const progression = index / (colorCount - 1);
const saturation = baseSaturation * (1.0 - 0.3 * progression);
const lightness = Math.min(100, baseLightness + progression * 30);
return colord({
h: baseHue,
s: saturation,
l: lightness,
});
});
}
export const nationColors: Colord[] = [
colord({ r: 230, g: 100, b: 100 }), // Bright Red
+1 -1
View File
@@ -42,7 +42,7 @@ export class PastelTheme implements Theme {
territoryColor(player: PlayerView): Colord {
const team = player.team();
if (team !== null) {
return this.teamColor(team);
return this.teamColorAllocator.assignTeamPlayerColor(team, player.id());
}
if (player.type() === PlayerType.Human) {
return this.humanColorAllocator.assignColor(player.id());
+1 -1
View File
@@ -42,7 +42,7 @@ export class PastelThemeDark implements Theme {
territoryColor(player: PlayerView): Colord {
const team = player.team();
if (team !== null) {
return this.teamColor(team);
return this.teamColorAllocator.assignTeamPlayerColor(team, player.id());
}
if (player.type() === PlayerType.Human) {
return this.humanColorAllocator.assignColor(player.id());
+68 -2
View File
@@ -3,7 +3,16 @@ import {
ColorAllocator,
selectDistinctColorIndex,
} from "../src/core/configuration/ColorAllocator";
import { blue, botColor, red, teal } from "../src/core/configuration/Colors";
import {
blue,
botColor,
green,
orange,
purple,
red,
teal,
yellow,
} from "../src/core/configuration/Colors";
import { ColoredTeams } from "../src/core/game/Game";
const mockColors: Colord[] = [
@@ -72,12 +81,69 @@ describe("ColorAllocator", () => {
expect(c2.isEqual(c2Again)).toBe(true);
});
test("assignTeamColor returns the expected static color for known teams", () => {
test("assignTeamColor returns the base color from the team", () => {
expect(allocator.assignTeamColor(ColoredTeams.Blue)).toEqual(blue);
expect(allocator.assignTeamColor(ColoredTeams.Red)).toEqual(red);
expect(allocator.assignTeamColor(ColoredTeams.Teal)).toEqual(teal);
expect(allocator.assignTeamColor(ColoredTeams.Purple)).toEqual(purple);
expect(allocator.assignTeamColor(ColoredTeams.Yellow)).toEqual(yellow);
expect(allocator.assignTeamColor(ColoredTeams.Orange)).toEqual(orange);
expect(allocator.assignTeamColor(ColoredTeams.Green)).toEqual(green);
expect(allocator.assignTeamColor(ColoredTeams.Bot)).toEqual(botColor);
});
test("assignTeamPlayerColor always returns the same color for the same playerID", () => {
const playerId = "player123";
const blueColor1 = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerId,
);
const blueColor2 = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerId,
);
expect(blueColor1.isEqual(blueColor2)).toBe(true);
const redColor1 = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerId,
);
const redColor2 = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerId,
);
expect(redColor1.isEqual(redColor2)).toBe(true);
});
test("assignTeamPlayerColor returns a different color when the playerID is different", () => {
const playerIdOne = "player1";
const playerIdTwo = "player2";
const blueColorPlayerOne = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerIdOne,
);
const blueColorPlayerTwo = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerIdTwo,
);
expect(blueColorPlayerOne.isEqual(blueColorPlayerTwo)).toBe(false);
const redColorPlayerOne = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerIdOne,
);
const redColorPlayerTwo = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerIdTwo,
);
expect(redColorPlayerOne.isEqual(redColorPlayerTwo)).toBe(false);
});
});
describe("selectDistinctColor", () => {