mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:20:47 +00:00
simplify attack overlay to reduce visual clutter (#3848)
## Description: Simplifies the attacking-troops overlay: removes the soldier icon and strength bar, dropping each label down to just the troop number in cyan (outgoing) or red (incoming) with a soft dark text-shadow halo and no background fill so territory borders show through cleanly. Also splits the label into outer (transitioned position) and inner (instant scale) divs so zoom changes no longer get smeared by the 0.25s cluster-move transition, retunes the zoom→size curve, and skips incoming labels from bot tribes to cut clutter. <img width="374" height="307" alt="Screenshot 2026-05-04 at 5 53 17 PM" src="https://github.com/user-attachments/assets/a7044221-06cc-4027-b19a-6ff4ca8f542a" /> ## 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: evan
This commit is contained in:
@@ -1,66 +1,37 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
alignClusterOrder,
|
||||
computeBarStrength,
|
||||
computeLabelScale,
|
||||
} from "../../../../src/client/graphics/layers/AttackingTroopsOverlay";
|
||||
import { Cell } from "../../../../src/core/game/Game";
|
||||
|
||||
describe("computeLabelScale", () => {
|
||||
test("counter-scales the zoom when above the full-size threshold", () => {
|
||||
// zoom = 2 → label rendered at 1/2 to stay at full screen size.
|
||||
expect(computeLabelScale(2)).toBeCloseTo(0.5);
|
||||
// LABEL_FULL_SIZE_ZOOM = 4, LABEL_MIN_RENDERED_SIZE = 0.63,
|
||||
// LABEL_SIZE_MULTIPLIER = 1.0. Rendered size at zoom z:
|
||||
// 1.0 * (0.63 + 0.37 * min(1, z/4)).
|
||||
test("at the full-size threshold, rendered size is capped at the multiplier", () => {
|
||||
// zoom = 4 → rendered = 1.0 → scale = 1.0 / 4.
|
||||
expect(computeLabelScale(4)).toBeCloseTo(1.0 / 4);
|
||||
});
|
||||
|
||||
test("counter-scales exactly at the full-size threshold", () => {
|
||||
// zoom = 1.5 → label rendered at 1/1.5 ≈ 0.6667.
|
||||
expect(computeLabelScale(1.5)).toBeCloseTo(1 / 1.5);
|
||||
test("above the threshold, rendered size stays capped (counter-scales zoom)", () => {
|
||||
// zoom = 8 → rendered still 1.0 → scale = 1.0 / 8.
|
||||
expect(computeLabelScale(8)).toBeCloseTo(1.0 / 8);
|
||||
});
|
||||
|
||||
test("rides the world transform between the floor and the threshold", () => {
|
||||
// Below the threshold, netScale = zoom / 1.5, so the factor is constant 1/1.5.
|
||||
expect(computeLabelScale(1)).toBeCloseTo(1 / 1.5);
|
||||
expect(computeLabelScale(0.9)).toBeCloseTo(1 / 1.5);
|
||||
test("at zoom = 0+, rendered size approaches the floor", () => {
|
||||
// As zoom→0, t→0, rendered → 1.0 * 0.63 (the floor).
|
||||
// At zoom = 0.001, rendered ≈ floor, so scale ≈ floor / zoom = huge.
|
||||
const scale = computeLabelScale(0.001);
|
||||
const floorRendered = 1.0 * 0.63;
|
||||
// Within 1% of the floor-divided-by-zoom value.
|
||||
expect(scale).toBeGreaterThan((floorRendered / 0.001) * 0.99);
|
||||
expect(scale).toBeLessThan((floorRendered / 0.001) * 1.01);
|
||||
});
|
||||
|
||||
test("floor engages exactly at zoom = 0.75 (LABEL_MIN_SCREEN_SCALE * LABEL_FULL_SIZE_ZOOM)", () => {
|
||||
expect(computeLabelScale(0.75)).toBeCloseTo(1 / 1.5);
|
||||
});
|
||||
|
||||
test("grows in screen space when zoomed out past the floor", () => {
|
||||
// zoom = 0.5 → netScale clamped to 0.5, factor = 0.5 / 0.5 = 1.
|
||||
expect(computeLabelScale(0.5)).toBeCloseTo(1);
|
||||
// zoom = 0.25 → factor = 0.5 / 0.25 = 2.
|
||||
expect(computeLabelScale(0.25)).toBeCloseTo(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("computeBarStrength", () => {
|
||||
test("equal troops sit at the midpoint", () => {
|
||||
// 1000 vs 1000 → ratio 1, divided by full-height ratio of 2 → 0.5.
|
||||
expect(computeBarStrength(1000, 1000)).toBeCloseTo(0.5);
|
||||
});
|
||||
|
||||
test("attacker with no troops yields a zero-height bar", () => {
|
||||
expect(computeBarStrength(0, 1000)).toBe(0);
|
||||
});
|
||||
|
||||
test("scales linearly between zero and the full-height threshold", () => {
|
||||
// 500 vs 1000 → ratio 0.5 → 0.25.
|
||||
expect(computeBarStrength(500, 1000)).toBeCloseTo(0.25);
|
||||
// 1500 vs 1000 → ratio 1.5 → 0.75.
|
||||
expect(computeBarStrength(1500, 1000)).toBeCloseTo(0.75);
|
||||
});
|
||||
|
||||
test("clamps at full height when attacker has 2× the opposition", () => {
|
||||
expect(computeBarStrength(2000, 1000)).toBeCloseTo(1);
|
||||
expect(computeBarStrength(10_000, 1000)).toBeCloseTo(1);
|
||||
});
|
||||
|
||||
test("returns full height when the opposing side has no troops", () => {
|
||||
// Avoids division-by-zero: an undefended target is maximum strength.
|
||||
expect(computeBarStrength(500, 0)).toBe(1);
|
||||
expect(computeBarStrength(0, 0)).toBe(1);
|
||||
test("interpolates linearly between floor and full-size threshold", () => {
|
||||
// zoom = 2 → t = 0.5 → rendered = 1.0 * (0.63 + 0.185) = 0.815.
|
||||
expect(computeLabelScale(2)).toBeCloseTo(0.815 / 2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user