feat: assign unique colors for players (#1063)

## Description:

  * adds 100+ colors for players
  * assigns a unique color for each player
  * bot and team colors assignment unchanged


![image](https://github.com/user-attachments/assets/75061a50-7166-4c0b-8f53-b35074a85706)

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

George

---------

Co-authored-by: cmesona <christopher.mesona@ubisoft.com>
This commit is contained in:
Christopher Mesona
2025-06-10 22:55:53 +02:00
committed by GitHub
parent 6a1e34b16a
commit cd799b514c
4 changed files with 320 additions and 169 deletions
+215 -99
View File
@@ -1,4 +1,6 @@
import { colord, Colord } from "colord";
import { ColoredTeams, Team } from "../game/Game";
import { simpleHash } from "../Util";
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
@@ -9,7 +11,7 @@ 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 territoryColors: Colord[] = [
export const nationColors: Colord[] = [
colord({ r: 230, g: 100, b: 100 }), // Bright Red
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
@@ -109,110 +111,71 @@ export const territoryColors: Colord[] = [
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
];
// Bright pastel theme with 64 colors
export const humanColors: Colord[] = [
// Original set
colord({ r: 235, g: 75, b: 75 }), // Bright Red
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
colord({ r: 245, g: 158, b: 11 }), // Amber
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
colord({ r: 48, g: 178, b: 180 }), // Teal
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
colord({ r: 251, g: 191, b: 36 }), // Marigold
colord({ r: 74, g: 222, b: 128 }), // Mint
colord({ r: 239, g: 68, b: 68 }), // Crimson
colord({ r: 34, g: 197, b: 94 }), // Emerald
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
colord({ r: 249, g: 115, b: 22 }), // Tangerine
colord({ r: 192, g: 132, b: 252 }), // Lavender
colord({ r: 45, g: 212, b: 191 }), // Turquoise
colord({ r: 244, g: 114, b: 182 }), // Rose
colord({ r: 132, g: 204, b: 22 }), // Lime
colord({ r: 56, g: 189, b: 248 }), // Light Blue
colord({ r: 234, g: 179, b: 8 }), // Sunflower
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
colord({ r: 16, g: 185, b: 129 }), // Sea Green
colord({ r: 251, g: 146, b: 60 }), // Light Orange
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
colord({ r: 79, g: 70, b: 229 }), // Indigo
colord({ r: 245, g: 101, b: 101 }), // Coral
colord({ r: 134, g: 239, b: 172 }), // Light Green
colord({ r: 59, g: 130, b: 246 }), // Cerulean
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
colord({ r: 252, g: 211, b: 77 }), // Golden
colord({ r: 190, g: 92, b: 251 }), // Amethyst
colord({ r: 82, g: 183, b: 136 }), // Jade
colord({ r: 248, g: 113, b: 113 }), // Warm Red
colord({ r: 99, g: 202, b: 253 }), // Azure
colord({ r: 240, g: 171, b: 252 }), // Orchid
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
colord({ r: 251, g: 113, b: 133 }), // Watermelon
colord({ r: 34, g: 197, b: 94 }), // Emerald
colord({ r: 45, g: 212, b: 191 }), // Turquoise
colord({ r: 48, g: 178, b: 180 }), // Teal
colord({ r: 52, g: 211, b: 153 }), // Spearmint
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
colord({ r: 245, g: 158, b: 11 }), // Honey
colord({ r: 56, g: 189, b: 248 }), // Light Blue
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
colord({ r: 74, g: 222, b: 128 }), // Mint
colord({ r: 79, g: 70, b: 229 }), // Indigo
colord({ r: 82, g: 183, b: 136 }), // Jade
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
colord({ r: 99, g: 202, b: 253 }), // Azure
colord({ r: 110, g: 231, b: 183 }), // Seafoam
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
colord({ r: 220, g: 38, b: 38 }), // Ruby
colord({ r: 124, g: 58, b: 237 }), // Royal Purple
colord({ r: 45, g: 212, b: 191 }), // Ocean
colord({ r: 252, g: 165, b: 165 }), // Peach
// Additional 50 colors
colord({ r: 179, g: 136, b: 255 }), // Light Purple
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
colord({ r: 132, g: 204, b: 22 }), // Lime
colord({ r: 133, g: 77, b: 14 }), // Chocolate
colord({ r: 52, g: 211, b: 153 }), // Aquamarine
colord({ r: 234, g: 179, b: 8 }), // Mustard
colord({ r: 236, g: 72, b: 153 }), // Hot Pink
colord({ r: 147, g: 197, b: 253 }), // Sky
colord({ r: 249, g: 115, b: 22 }), // Pumpkin
colord({ r: 167, g: 139, b: 250 }), // Iris
colord({ r: 16, g: 185, b: 129 }), // Pine
colord({ r: 251, g: 146, b: 60 }), // Mango
colord({ r: 192, g: 132, b: 252 }), // Wisteria
colord({ r: 79, g: 70, b: 229 }), // Sapphire
colord({ r: 245, g: 101, b: 101 }), // Salmon
colord({ r: 134, g: 239, b: 172 }), // Spring Green
colord({ r: 59, g: 130, b: 246 }), // Ocean Blue
colord({ r: 253, g: 164, b: 175 }), // Rose Gold
colord({ r: 16, g: 185, b: 129 }), // Forest
colord({ r: 252, g: 211, b: 77 }), // Sunshine
colord({ r: 190, g: 92, b: 251 }), // Grape
colord({ r: 82, g: 183, b: 136 }), // Eucalyptus
colord({ r: 248, g: 113, b: 113 }), // Cherry
colord({ r: 99, g: 202, b: 253 }), // Arctic
colord({ r: 240, g: 171, b: 252 }), // Lilac
colord({ r: 163, g: 230, b: 53 }), // Chartreuse
colord({ r: 234, g: 88, b: 12 }), // Rust
colord({ r: 125, g: 211, b: 252 }), // Ice Blue
colord({ r: 251, g: 113, b: 133 }), // Strawberry
colord({ r: 52, g: 211, b: 153 }), // Sage
colord({ r: 167, g: 139, b: 250 }), // Violet
colord({ r: 245, g: 158, b: 11 }), // Apricot
colord({ r: 110, g: 231, b: 183 }), // Mint Green
colord({ r: 233, g: 213, b: 255 }), // Thistle
colord({ r: 202, g: 138, b: 4 }), // Bronze
colord({ r: 151, g: 255, b: 187 }), // Pistachio
colord({ r: 220, g: 38, b: 38 }), // Fire Engine
colord({ r: 124, g: 58, b: 237 }), // Electric Purple
colord({ r: 45, g: 212, b: 191 }), // Caribbean
colord({ r: 252, g: 165, b: 165 }), // Melon
colord({ r: 168, g: 85, b: 247 }), // Byzantium
colord({ r: 74, g: 222, b: 128 }), // Kelly Green
colord({ r: 239, g: 68, b: 68 }), // Cardinal
colord({ r: 34, g: 197, b: 94 }), // Shamrock
colord({ r: 96, g: 165, b: 250 }), // Marina
colord({ r: 249, g: 115, b: 22 }), // Carrot
colord({ r: 192, g: 132, b: 252 }), // Heliotrope
colord({ r: 45, g: 212, b: 191 }), // Lagoon
colord({ r: 244, g: 114, b: 182 }), // Bubble Gum
colord({ r: 132, g: 204, b: 22 }), // Apple
colord({ r: 56, g: 189, b: 248 }), // Electric Blue
colord({ r: 234, g: 179, b: 8 }), // Daffodil
colord({ r: 134, g: 239, b: 172 }), // Light Green
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
colord({ r: 179, g: 136, b: 255 }), // Light Purple
colord({ r: 186, g: 255, b: 201 }), // Pale Emerald
colord({ r: 190, g: 92, b: 251 }), // Amethyst
colord({ r: 192, g: 132, b: 252 }), // Lavender
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
colord({ r: 202, g: 225, b: 255 }), // Baby Blue
colord({ r: 204, g: 204, b: 255 }), // Soft Lavender Blue
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
colord({ r: 220, g: 38, b: 38 }), // Ruby
colord({ r: 220, g: 220, b: 255 }), // Meringue Blue
colord({ r: 220, g: 240, b: 250 }), // Ice Blue
colord({ r: 230, g: 250, b: 210 }), // Pastel Lime
colord({ r: 230, g: 255, b: 250 }), // Mint Whisper
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
colord({ r: 234, g: 179, b: 8 }), // Sunflower
colord({ r: 235, g: 75, b: 75 }), // Bright Red
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
colord({ r: 239, g: 68, b: 68 }), // Crimson
colord({ r: 240, g: 171, b: 252 }), // Orchid
colord({ r: 240, g: 240, b: 200 }), // Light Khaki
colord({ r: 244, g: 114, b: 182 }), // Rose
colord({ r: 245, g: 101, b: 101 }), // Coral
colord({ r: 245, g: 158, b: 11 }), // Amber
colord({ r: 248, g: 113, b: 113 }), // Warm Red
colord({ r: 249, g: 115, b: 22 }), // Tangerine
colord({ r: 250, g: 215, b: 225 }), // Cotton Candy
colord({ r: 250, g: 250, b: 210 }), // Pastel Lemon
colord({ r: 251, g: 113, b: 133 }), // Watermelon
colord({ r: 251, g: 146, b: 60 }), // Light Orange
colord({ r: 251, g: 191, b: 36 }), // Marigold
colord({ r: 251, g: 235, b: 245 }), // Rose Powder
colord({ r: 252, g: 165, b: 165 }), // Peach
colord({ r: 252, g: 211, b: 77 }), // Golden
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
colord({ r: 255, g: 204, b: 229 }), // Blush Pink
colord({ r: 255, g: 223, b: 186 }), // Apricot Cream
colord({ r: 255, g: 240, b: 200 }), // Vanilla
];
export const botColors: Colord[] = [
@@ -266,3 +229,156 @@ export const botColors: Colord[] = [
colord({ r: 150, g: 160, b: 140 }), // Muted Dark Olive Green
colord({ r: 150, g: 140, b: 150 }), // Muted Dusty Rose
];
// Fallback colors for when the color palette is exhausted. Currently 100 colors.
export const fallbackColors: Colord[] = [
colord({ r: 0, g: 5, b: 0 }), // Black Mint
colord({ r: 0, g: 15, b: 0 }), // Deep Forest
colord({ r: 0, g: 25, b: 0 }), // Jungle
colord({ r: 0, g: 35, b: 0 }), // Dark Emerald
colord({ r: 0, g: 45, b: 0 }), // Green Moss
colord({ r: 0, g: 55, b: 0 }), // Moss Shadow
colord({ r: 0, g: 65, b: 0 }), // Dark Meadow
colord({ r: 0, g: 75, b: 0 }), // Forest Fern
colord({ r: 0, g: 85, b: 0 }), // Pine Leaf
colord({ r: 0, g: 95, b: 0 }), // Shadow Grass
colord({ r: 0, g: 105, b: 0 }), // Classic Green
colord({ r: 0, g: 115, b: 0 }), // Deep Lime
colord({ r: 0, g: 125, b: 0 }), // Dense Leaf
colord({ r: 0, g: 135, b: 0 }), // Basil Green
colord({ r: 0, g: 145, b: 0 }), // Organic Green
colord({ r: 0, g: 155, b: 0 }), // Bitter Herb
colord({ r: 0, g: 165, b: 0 }), // Raw Spinach
colord({ r: 0, g: 175, b: 0 }), // Woodland
colord({ r: 0, g: 185, b: 0 }), // Spring Weed
colord({ r: 0, g: 195, b: 5 }), // Apple Stem
colord({ r: 0, g: 205, b: 10 }), // Crisp Lettuce
colord({ r: 0, g: 215, b: 15 }), // Vibrant Green
colord({ r: 0, g: 225, b: 20 }), // Bright Herb
colord({ r: 0, g: 235, b: 25 }), // Green Splash
colord({ r: 0, g: 245, b: 30 }), // Mint Leaf
colord({ r: 0, g: 255, b: 35 }), // Fresh Mint
colord({ r: 10, g: 255, b: 45 }), // Neon Grass
colord({ r: 20, g: 255, b: 55 }), // Lemon Balm
colord({ r: 30, g: 255, b: 65 }), // Juicy Green
colord({ r: 40, g: 255, b: 75 }), // Pear Tint
colord({ r: 50, g: 255, b: 85 }), // Avocado Pastel
colord({ r: 60, g: 255, b: 95 }), // Lime Glow
colord({ r: 70, g: 255, b: 105 }), // Light Leaf
colord({ r: 80, g: 255, b: 115 }), // Soft Fern
colord({ r: 90, g: 255, b: 125 }), // Pastel Green
colord({ r: 100, g: 255, b: 135 }), // Green Melon
colord({ r: 110, g: 255, b: 145 }), // Herbal Mist
colord({ r: 120, g: 255, b: 155 }), // Kiwi Foam
colord({ r: 130, g: 255, b: 165 }), // Aloe Fresh
colord({ r: 140, g: 255, b: 175 }), // Light Mint
colord({ r: 150, g: 200, b: 255 }), // Cornflower Mist
colord({ r: 150, g: 255, b: 185 }), // Green Sorbet
colord({ r: 160, g: 215, b: 255 }), // Powder Blue
colord({ r: 160, g: 255, b: 195 }), // Pastel Apple
colord({ r: 170, g: 190, b: 255 }), // Periwinkle Ice
colord({ r: 170, g: 225, b: 255 }), // Baby Sky
colord({ r: 170, g: 255, b: 205 }), // Aloe Breeze
colord({ r: 180, g: 180, b: 255 }), // Pale Indigo
colord({ r: 180, g: 235, b: 250 }), // Aqua Pastel
colord({ r: 180, g: 255, b: 215 }), // Pale Mint
colord({ r: 190, g: 140, b: 195 }), // Fuchsia Tint
colord({ r: 190, g: 245, b: 240 }), // Ice Mint
colord({ r: 190, g: 255, b: 225 }), // Mint Water
colord({ r: 195, g: 145, b: 200 }), // Dusky Rose
colord({ r: 200, g: 150, b: 205 }), // Plum Frost
colord({ r: 200, g: 170, b: 255 }), // Lilac Bloom
colord({ r: 200, g: 255, b: 215 }), // Cool Aloe
colord({ r: 200, g: 255, b: 235 }), // Cool Mist
colord({ r: 205, g: 155, b: 210 }), // Berry Foam
colord({ r: 210, g: 160, b: 215 }), // Grape Cloud
colord({ r: 210, g: 255, b: 245 }), // Sea Mist
colord({ r: 215, g: 165, b: 220 }), // Light Bloom
colord({ r: 215, g: 255, b: 200 }), // Fresh Mint
colord({ r: 220, g: 160, b: 255 }), // Violet Mist
colord({ r: 220, g: 170, b: 225 }), // Cherry Blossom
colord({ r: 220, g: 255, b: 255 }), // Pale Aqua
colord({ r: 225, g: 175, b: 230 }), // Faded Rose
colord({ r: 225, g: 255, b: 175 }), // Soft Lime
colord({ r: 230, g: 180, b: 235 }), // Dreamy Mauve
colord({ r: 230, g: 250, b: 255 }), // Sky Haze
colord({ r: 235, g: 150, b: 255 }), // Orchid Glow
colord({ r: 235, g: 185, b: 240 }), // Powder Violet
colord({ r: 240, g: 190, b: 245 }), // Pastel Violet
colord({ r: 240, g: 240, b: 255 }), // Frosted Lilac
colord({ r: 240, g: 250, b: 160 }), // Citrus Wash
colord({ r: 245, g: 160, b: 240 }), // Rose Lilac
colord({ r: 245, g: 195, b: 250 }), // Soft Magenta
colord({ r: 245, g: 245, b: 175 }), // Lemon Mist
colord({ r: 250, g: 200, b: 255 }), // Lilac Cream
colord({ r: 250, g: 230, b: 255 }), // Misty Mauve
colord({ r: 255, g: 170, b: 225 }), // Bubblegum Pink
colord({ r: 255, g: 185, b: 215 }), // Blush Mist
colord({ r: 255, g: 195, b: 235 }), // Faded Fuchsia
colord({ r: 255, g: 200, b: 220 }), // Cotton Rose
colord({ r: 255, g: 205, b: 245 }), // Pastel Orchid
colord({ r: 255, g: 205, b: 255 }), // Violet Bloom
colord({ r: 255, g: 210, b: 230 }), // Pastel Blush
colord({ r: 255, g: 210, b: 250 }), // Lavender Mist
colord({ r: 255, g: 210, b: 255 }), // Orchid Mist
colord({ r: 255, g: 215, b: 195 }), // Apricot Glow
colord({ r: 255, g: 215, b: 245 }), // Rose Whisper
colord({ r: 255, g: 220, b: 235 }), // Pink Mist
colord({ r: 255, g: 220, b: 250 }), // Powder Petal
colord({ r: 255, g: 225, b: 180 }), // Butter Peach
colord({ r: 255, g: 225, b: 255 }), // Petal Mist
colord({ r: 255, g: 230, b: 245 }), // Light Rose
colord({ r: 255, g: 235, b: 200 }), // Cream Peach
colord({ r: 255, g: 235, b: 235 }), // Blushed Petal
colord({ r: 255, g: 240, b: 220 }), // Pastel Sand
colord({ r: 255, g: 245, b: 210 }), // Soft Banana
];
export class ColorAllocator {
private availableColors: Colord[];
private fallbackColors: Colord[];
private assigned = new Map<string, Colord>();
constructor(colors: Colord[], fallback: Colord[]) {
this.availableColors = [...colors];
this.fallbackColors = [...fallback];
}
assignColor(id: string): Colord {
if (this.assigned.has(id)) {
return this.assigned.get(id)!;
}
if (this.availableColors.length === 0) {
this.availableColors = this.fallbackColors;
}
const index = 0;
const color = this.availableColors.splice(index, 1)[0];
this.assigned.set(id, color);
return color;
}
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.availableColors[
simpleHash(team) % this.availableColors.length
];
}
}
}
+12 -35
View File
@@ -1,21 +1,14 @@
import { Colord, colord } from "colord";
import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import { ColoredTeams, PlayerType, Team, TerrainType } from "../game/Game";
import { PlayerType, Team, TerrainType } from "../game/Game";
import { GameMap, TileRef } from "../game/GameMap";
import { PlayerView } from "../game/GameView";
import {
blue,
botColor,
botColors,
green,
ColorAllocator,
fallbackColors,
humanColors,
orange,
purple,
red,
teal,
territoryColors,
yellow,
nationColors,
} from "./Colors";
import { Theme } from "./Config";
@@ -24,9 +17,12 @@ type ColorCache = Map<string, Colord>;
export class PastelTheme implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private botColorAllocator = new ColorAllocator(botColors, botColors);
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
private background = colord({ r: 60, g: 60, b: 60 });
private land = colord({ r: 194, g: 193, b: 148 });
private shore = colord({ r: 204, g: 203, b: 158 });
private falloutColors = [
colord({ r: 120, g: 255, b: 71 }), // Original color
@@ -45,26 +41,7 @@ export class PastelTheme implements Theme {
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
teamColor(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 humanColors[simpleHash(team) % humanColors.length];
}
return this.teamColorAllocator.assignTeamColor(team);
}
territoryColor(player: PlayerView): Colord {
@@ -73,12 +50,12 @@ export class PastelTheme implements Theme {
return this.teamColor(team);
}
if (player.type() === PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
return this.humanColorAllocator.assignColor(player.id());
}
if (player.type() === PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
return this.botColorAllocator.assignColor(player.id());
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
return this.nationColorAllocator.assignColor(player.id());
}
textColor(player: PlayerView): string {
+12 -35
View File
@@ -1,21 +1,14 @@
import { Colord, colord } from "colord";
import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import { ColoredTeams, PlayerType, Team, TerrainType } from "../game/Game";
import { PlayerType, Team, TerrainType } from "../game/Game";
import { GameMap, TileRef } from "../game/GameMap";
import { PlayerView } from "../game/GameView";
import {
blue,
botColor,
botColors,
green,
ColorAllocator,
fallbackColors,
humanColors,
orange,
purple,
red,
teal,
territoryColors,
yellow,
nationColors,
} from "./Colors";
import { Theme } from "./Config";
@@ -24,9 +17,12 @@ type ColorCache = Map<string, Colord>;
export class PastelThemeDark implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private botColorAllocator = new ColorAllocator(botColors, botColors);
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
private background = colord({ r: 0, g: 0, b: 0 });
private land = colord({ r: 194, g: 193, b: 148 });
private shore = colord({ r: 134, g: 133, b: 88 });
private falloutColors = [
colord({ r: 120, g: 255, b: 71 }), // Original color
@@ -45,26 +41,7 @@ export class PastelThemeDark implements Theme {
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
teamColor(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 humanColors[simpleHash(team) % humanColors.length];
}
return this.teamColorAllocator.assignTeamColor(team);
}
territoryColor(player: PlayerView): Colord {
@@ -73,12 +50,12 @@ export class PastelThemeDark implements Theme {
return this.teamColor(team);
}
if (player.type() === PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
return this.humanColorAllocator.assignColor(player.id());
}
if (player.type() === PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
return this.botColorAllocator.assignColor(player.id());
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
return this.nationColorAllocator.assignColor(player.id());
}
textColor(player: PlayerView): string {
+81
View File
@@ -0,0 +1,81 @@
import { colord, Colord } from "colord";
import {
blue,
botColor,
ColorAllocator,
red,
teal,
} from "../src/core/configuration/Colors";
import { ColoredTeams } from "../src/core/game/Game";
const mockColors: Colord[] = [
colord({ r: 255, g: 0, b: 0 }),
colord({ r: 0, g: 255, b: 0 }),
colord({ r: 0, g: 0, b: 255 }),
];
const fallbackMockColors: Colord[] = [
colord({ r: 0, g: 0, b: 0 }),
colord({ r: 255, g: 255, b: 255 }),
];
describe("ColorAllocator", () => {
let allocator: ColorAllocator;
beforeEach(() => {
allocator = new ColorAllocator(mockColors, fallbackMockColors);
});
test("returns a unique color for each new ID", () => {
const c1 = allocator.assignColor("a");
const c2 = allocator.assignColor("b");
const c3 = allocator.assignColor("c");
expect(c1.isEqual(c2)).toBe(false);
expect(c1.isEqual(c3)).toBe(false);
expect(c2.isEqual(c3)).toBe(false);
});
test("returns the same color for the same ID", () => {
const c1 = allocator.assignColor("a");
const c2 = allocator.assignColor("a");
expect(c1.isEqual(c2)).toBe(true);
});
test("falls back when colors are exhausted", () => {
allocator.assignColor("1");
allocator.assignColor("2");
allocator.assignColor("3");
const fallback = allocator.assignColor("4");
const fallback2 = allocator.assignColor("5");
const match = fallbackMockColors.some((color) => color.isEqual(fallback));
expect(match).toBe(true);
const match2 = fallback.isEqual(fallback2);
expect(match2).toBe(false);
});
test("assignBotColor returns deterministic color from botColors", () => {
const allocator = new ColorAllocator(mockColors, mockColors);
const id1 = "bot123";
const id2 = "bot456";
const c1 = allocator.assignColor(id1);
const c2 = allocator.assignColor(id2);
const c1Again = allocator.assignColor(id1);
const c2Again = allocator.assignColor(id2);
expect(c1.isEqual(c1Again)).toBe(true);
expect(c2.isEqual(c2Again)).toBe(true);
});
test("assignTeamColor returns the expected static color for known teams", () => {
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.Bot)).toEqual(botColor);
});
});