mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:40:44 +00:00
Orange betrayal button for no-debuff-betrayals 🖌️ (#3161)
Resolves #1276 ## Description: Orange betrayal button if the player is a traitor or disconnected. So people can easier tell that this is a betrayal without consequences. The color changes back to red without reopening the menu (live) when the traitor debuff ends or the player reconnects. <img width="268" height="257" alt="image" src="https://github.com/user-attachments/assets/276e91ce-e49d-474c-afaa-ffa18d45a2c7" /> ## 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: FloPinguin --------- Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
This commit is contained in:
@@ -11,6 +11,16 @@ import {
|
||||
} from "./RadialMenuElements";
|
||||
import backIcon from "/images/BackIconWhite.svg?url";
|
||||
|
||||
function resolveColor(
|
||||
item: MenuElement,
|
||||
params: MenuElementParams | null,
|
||||
): string | undefined {
|
||||
if (typeof item.color === "function") {
|
||||
return params ? item.color(params) : undefined;
|
||||
}
|
||||
return item.color;
|
||||
}
|
||||
|
||||
export class CloseRadialMenuEvent implements GameEvent {
|
||||
constructor() {}
|
||||
}
|
||||
@@ -322,7 +332,7 @@ export class RadialMenu implements Layer {
|
||||
const disabled = this.params === null || d.data.disabled(this.params);
|
||||
const color = disabled
|
||||
? this.config.disabledColor
|
||||
: (d.data.color ?? "#333333");
|
||||
: (resolveColor(d.data, this.params) ?? "#333333");
|
||||
const opacity = disabled ? 0.5 : 0.7;
|
||||
|
||||
if (d.data.id === this.selectedItemId && this.currentLevel > level) {
|
||||
@@ -365,7 +375,7 @@ export class RadialMenu implements Layer {
|
||||
const color =
|
||||
this.params === null || d.data.disabled(this.params)
|
||||
? this.config.disabledColor
|
||||
: (d.data.color ?? "#333333");
|
||||
: (resolveColor(d.data, this.params) ?? "#333333");
|
||||
path.attr("fill", color);
|
||||
}
|
||||
});
|
||||
@@ -431,7 +441,7 @@ export class RadialMenu implements Layer {
|
||||
path.attr("stroke-width", "2");
|
||||
const color = disabled
|
||||
? this.config.disabledColor
|
||||
: (d.data.color ?? "#333333");
|
||||
: (resolveColor(d.data, this.params) ?? "#333333");
|
||||
const opacity = disabled ? 0.5 : 0.7;
|
||||
path.attr(
|
||||
"fill",
|
||||
@@ -848,10 +858,7 @@ export class RadialMenu implements Layer {
|
||||
|
||||
public disableAllButtons() {
|
||||
this.updateCenterButtonState("default");
|
||||
|
||||
for (const item of this.currentMenuItems) {
|
||||
item.color = this.config.disabledColor;
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public updateCenterButtonState(state: CenterButtonState) {
|
||||
@@ -1043,7 +1050,7 @@ export class RadialMenu implements Layer {
|
||||
const disabled = this.isItemDisabled(item);
|
||||
const color = disabled
|
||||
? this.config.disabledColor
|
||||
: (item.color ?? "#333333");
|
||||
: (resolveColor(item, this.params) ?? "#333333");
|
||||
const opacity = disabled ? 0.5 : 0.7;
|
||||
|
||||
// Update path appearance
|
||||
|
||||
@@ -46,7 +46,7 @@ export interface MenuElement {
|
||||
id: string;
|
||||
name: string;
|
||||
displayed?: boolean | ((params: MenuElementParams) => boolean);
|
||||
color?: string;
|
||||
color?: string | ((params: MenuElementParams) => string);
|
||||
icon?: string;
|
||||
text?: string;
|
||||
fontSize?: string;
|
||||
@@ -76,6 +76,7 @@ export const COLORS = {
|
||||
boat: "#3f6ab1",
|
||||
ally: "#53ac75",
|
||||
breakAlly: "#c74848",
|
||||
breakAllyNoDebuff: "#d4882b",
|
||||
delete: "#ff0000",
|
||||
info: "#64748B",
|
||||
target: "#ff0000",
|
||||
@@ -216,7 +217,10 @@ const allyBreakElement: MenuElement = {
|
||||
!params.playerActions?.interaction?.canBreakAlliance,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canBreakAlliance,
|
||||
color: COLORS.breakAlly,
|
||||
color: (params: MenuElementParams) =>
|
||||
params.selected?.isTraitor() || params.selected?.isDisconnected()
|
||||
? COLORS.breakAllyNoDebuff
|
||||
: COLORS.breakAlly,
|
||||
icon: traitorIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleBreakAlliance(
|
||||
|
||||
@@ -92,6 +92,8 @@ describe("RadialMenuElements", () => {
|
||||
id: () => 1,
|
||||
isAlliedWith: vi.fn(() => false),
|
||||
isPlayer: vi.fn(() => true),
|
||||
isTraitor: vi.fn(() => false),
|
||||
isDisconnected: vi.fn(() => false),
|
||||
} as unknown as PlayerView;
|
||||
|
||||
mockGame = {
|
||||
@@ -339,6 +341,8 @@ describe("RadialMenuElements", () => {
|
||||
id: () => 2,
|
||||
isAlliedWith: vi.fn(() => true),
|
||||
isPlayer: vi.fn(() => true),
|
||||
isTraitor: vi.fn(() => false),
|
||||
isDisconnected: vi.fn(() => false),
|
||||
} as unknown as PlayerView;
|
||||
mockParams.selected = allyPlayer;
|
||||
mockGame.owner = vi.fn(() => allyPlayer);
|
||||
|
||||
@@ -19,13 +19,18 @@ import {
|
||||
} from "../src/client/graphics/layers/RadialMenuElements";
|
||||
|
||||
// Minimal stubs to satisfy types used in rootMenuElement.subMenu and allyBreak actions
|
||||
const makePlayer = (id: string) =>
|
||||
const makePlayer = (
|
||||
id: string,
|
||||
opts?: { isTraitor?: boolean; isDisconnected?: boolean },
|
||||
) =>
|
||||
({
|
||||
id: () => id,
|
||||
isAlliedWith: (other: any) =>
|
||||
other && typeof other.id === "function" && other.id() !== id
|
||||
? true
|
||||
: true,
|
||||
isTraitor: () => opts?.isTraitor ?? false,
|
||||
isDisconnected: () => opts?.isDisconnected ?? false,
|
||||
}) as unknown as import("../src/core/game/GameView").PlayerView;
|
||||
|
||||
const makeParams = (opts?: Partial<MenuElementParams>): MenuElementParams => {
|
||||
@@ -82,7 +87,26 @@ describe("RadialMenuElements ally break", () => {
|
||||
const ally = findAllyBreak(items)!;
|
||||
expect(ally).toBeTruthy();
|
||||
expect(ally.name).toBe("break");
|
||||
expect(ally.color).toBe(COLORS.breakAlly);
|
||||
expect(typeof ally.color).toBe("function");
|
||||
expect(ally.color(params)).toBe(COLORS.breakAlly);
|
||||
});
|
||||
|
||||
test("shows break option with orange color when allied to traitor", () => {
|
||||
const params = makeParams({
|
||||
selected: makePlayer("p2", { isTraitor: true }),
|
||||
});
|
||||
const items = rootMenuElement.subMenu!(params);
|
||||
const ally = findAllyBreak(items)!;
|
||||
expect(ally.color(params)).toBe(COLORS.breakAllyNoDebuff);
|
||||
});
|
||||
|
||||
test("shows boat button instead of break when allied to disconnected player", () => {
|
||||
const params = makeParams({
|
||||
selected: makePlayer("p2", { isDisconnected: true }),
|
||||
});
|
||||
const items = rootMenuElement.subMenu!(params);
|
||||
expect(findAllyBreak(items)).toBeUndefined();
|
||||
expect(items.find((i) => i.id === "boat")).toBeDefined();
|
||||
});
|
||||
|
||||
test("break action calls handleBreakAlliance and closes menu", () => {
|
||||
|
||||
Reference in New Issue
Block a user