From 8010688d277edc725433e1a402b0a9dd06b9b661 Mon Sep 17 00:00:00 2001 From: yanir <100792995+Boostry123@users.noreply.github.com> Date: Fri, 5 Sep 2025 21:25:17 +0300 Subject: [PATCH] Feat/breathing animation around spawn cell (#1951) ## Description: Noticed many people are struggling to see where they choose to spawn or forget where they chose , what leads to confusion. solved it with this animation around the spawning player cell. ![giphy](https://github.com/user-attachments/assets/938f7dc8-97cb-40d0-8222-9f8ddbc2b21f) image ALSO: Added a missing translation for hebrew ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: boostry Co-authored-by: evanpelle --- src/client/graphics/layers/TerritoryLayer.ts | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 354a23df8..3ccd54f91 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -23,6 +23,7 @@ export class TerritoryLayer implements Layer { private context: CanvasRenderingContext2D; private imageData: ImageData; private alternativeImageData: ImageData; + private borderAnimTime = 0; private cachedTerritoryPatternsEnabled: boolean | undefined; @@ -195,6 +196,37 @@ export class TerritoryLayer implements Layer { } } } + // Breathing border animation + this.borderAnimTime += 1; + const minPadding = 3; + const maxPadding = 8; + // Range: [minPadding..maxPadding] + const breathingPadding = + minPadding + + (maxPadding - minPadding) * + (0.5 + 0.5 * Math.sin(this.borderAnimTime * 0.3)); + + if (focusedPlayer) { + // Clear previous animated border + if (this.highlightContext) { + this.highlightContext.clearRect( + 0, + 0, + this.game.width(), + this.game.height(), + ); + } + + const center = focusedPlayer.nameLocation(); + if (center) { + this.drawBreathingRing( + center.x, + center.y, + breathingPadding, + this.theme.spawnHighlightColor(), + ); + } + } } init() { @@ -550,4 +582,18 @@ export class TerritoryLayer implements Layer { const y = this.game.y(tile); this.highlightContext.clearRect(x, y, 1, 1); } + private drawBreathingRing( + cx: number, + cy: number, + radius: number, + color: Colord, + ) { + const ctx = this.highlightContext; + if (!ctx) return; + ctx.beginPath(); + ctx.arc(cx, cy, radius, 0, Math.PI * 2); + ctx.strokeStyle = color.toRgbString(); + ctx.lineWidth = 2; + ctx.stroke(); + } }