mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-20 00:20:00 +00:00
Feat: Added cursor price option to user and basic settings (#2655)
## Description: Following the hotkey cursor price textbox addition of #2650, this feature adds the option to enable and disable the visual feature via the User Settings menu or the Basic Settings modal in game. Also added a [new icon](https://thenounproject.com/icon/pay-per-click-2586454/) for the Basic Settings modal from the Noun Project and added credit for it to the `CREDITS.md` file. ### Video Demo https://github.com/user-attachments/assets/1667081e-45e3-4b11-9bda-3f00c341e03c ### User Settings Menu <img width="1029" height="1436" alt="image" src="https://github.com/user-attachments/assets/e4e6bf6d-db59-463a-81fb-f622ef6e3931" /> ### Basic Settings Menu <img width="964" height="1545" alt="image" src="https://github.com/user-attachments/assets/6b083655-b96e-4937-95d6-f3458858f03d" /> ## 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: bijx
This commit is contained in:
@@ -143,6 +143,15 @@ export class UserSettingModal extends LitElement {
|
||||
console.log("🏠 Structure sprites:", enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
private toggleCursorCostLabel(e: CustomEvent<{ checked: boolean }>) {
|
||||
const enabled = e.detail?.checked;
|
||||
if (typeof enabled !== "boolean") return;
|
||||
|
||||
this.userSettings.set("settings.cursorCostLabel", enabled);
|
||||
|
||||
console.log("💰 Cursor build cost:", enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
private toggleAnonymousNames(e: CustomEvent<{ checked: boolean }>) {
|
||||
const enabled = e.detail?.checked;
|
||||
if (typeof enabled !== "boolean") return;
|
||||
@@ -309,6 +318,15 @@ export class UserSettingModal extends LitElement {
|
||||
@change=${this.toggleStructureSprites}
|
||||
></setting-toggle>
|
||||
|
||||
<!-- 💰 Cursor Price Pill -->
|
||||
<setting-toggle
|
||||
label="${translateText("user_setting.cursor_cost_label_label")}"
|
||||
description="${translateText("user_setting.cursor_cost_label_desc")}"
|
||||
id="cursor_cost_label-toggle"
|
||||
.checked=${this.userSettings.cursorCostLabel()}
|
||||
@change=${this.toggleCursorCostLabel}
|
||||
></setting-toggle>
|
||||
|
||||
<!-- 🖱️ Left Click Menu -->
|
||||
<setting-toggle
|
||||
label="${translateText("user_setting.left_click_label")}"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import structureIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import cursorPriceIcon from "../../../../resources/images/CursorPriceIconWhite.svg";
|
||||
import darkModeIcon from "../../../../resources/images/DarkModeIconWhite.svg";
|
||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
||||
import exitIcon from "../../../../resources/images/ExitIconWhite.svg";
|
||||
@@ -152,6 +153,11 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private onToggleCursorCostLabelButtonClick() {
|
||||
this.userSettings.toggleCursorCostLabel();
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private onTogglePerformanceOverlayButtonClick() {
|
||||
this.userSettings.togglePerformanceOverlay();
|
||||
this.requestUpdate();
|
||||
@@ -397,6 +403,31 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
|
||||
@click="${this.onToggleCursorCostLabelButtonClick}"
|
||||
>
|
||||
<img
|
||||
src=${cursorPriceIcon}
|
||||
alt="cursorCostLabel"
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("user_setting.cursor_cost_label_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("user_setting.cursor_cost_label_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings.cursorCostLabel()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
|
||||
@click="${this.onToggleRandomNameModeButtonClick}"
|
||||
|
||||
@@ -114,6 +114,7 @@ export class SpriteFactory {
|
||||
container: PIXI.Container;
|
||||
priceText: PIXI.BitmapText;
|
||||
priceBg: PIXI.Graphics;
|
||||
priceGroup: PIXI.Container;
|
||||
priceBox: { height: number; y: number; paddingX: number; minWidth: number };
|
||||
} {
|
||||
const parentContainer = new PIXI.Container();
|
||||
@@ -165,6 +166,7 @@ export class SpriteFactory {
|
||||
container: parentContainer,
|
||||
priceText,
|
||||
priceBg,
|
||||
priceGroup,
|
||||
priceBox: { height: boxHeight, y: boxY, paddingX, minWidth },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ export class StructureIconsLayer implements Layer {
|
||||
container: PIXI.Container;
|
||||
priceText: PIXI.BitmapText;
|
||||
priceBg: PIXI.Graphics;
|
||||
priceGroup: PIXI.Container;
|
||||
priceBox: { height: number; y: number; paddingX: number; minWidth: number };
|
||||
range: PIXI.Container | null;
|
||||
rangeLevel?: number;
|
||||
@@ -270,12 +271,13 @@ export class StructureIconsLayer implements Layer {
|
||||
const unit = actions.buildableUnits.find(
|
||||
(u) => u.type === this.ghostUnit!.buildableUnit.type,
|
||||
);
|
||||
const showPrice = this.game.config().userSettings().cursorCostLabel();
|
||||
if (!unit) {
|
||||
Object.assign(this.ghostUnit.buildableUnit, {
|
||||
canBuild: false,
|
||||
canUpgrade: false,
|
||||
});
|
||||
this.updateGhostPrice(0);
|
||||
this.updateGhostPrice(0, showPrice);
|
||||
this.ghostUnit.container.filters = [
|
||||
new OutlineFilter({ thickness: 2, color: "rgba(255, 0, 0, 1)" }),
|
||||
];
|
||||
@@ -283,7 +285,7 @@ export class StructureIconsLayer implements Layer {
|
||||
}
|
||||
|
||||
this.ghostUnit.buildableUnit = unit;
|
||||
this.updateGhostPrice(unit.cost ?? 0);
|
||||
this.updateGhostPrice(unit.cost ?? 0, showPrice);
|
||||
|
||||
const targetLevel = this.resolveGhostRangeLevel(unit);
|
||||
this.updateGhostRange(targetLevel);
|
||||
@@ -318,9 +320,12 @@ export class StructureIconsLayer implements Layer {
|
||||
});
|
||||
}
|
||||
|
||||
private updateGhostPrice(cost: bigint | number) {
|
||||
private updateGhostPrice(cost: bigint | number, showPrice: boolean) {
|
||||
if (!this.ghostUnit) return;
|
||||
const { priceText, priceBg, priceBox } = this.ghostUnit;
|
||||
const { priceText, priceBg, priceBox, priceGroup } = this.ghostUnit;
|
||||
priceGroup.visible = showPrice;
|
||||
if (!showPrice) return;
|
||||
|
||||
priceText.text = renderNumber(cost);
|
||||
priceText.position.set(0, priceBox.y);
|
||||
|
||||
@@ -407,11 +412,13 @@ export class StructureIconsLayer implements Layer {
|
||||
container: ghost.container,
|
||||
priceText: ghost.priceText,
|
||||
priceBg: ghost.priceBg,
|
||||
priceGroup: ghost.priceGroup,
|
||||
priceBox: ghost.priceBox,
|
||||
range: null,
|
||||
buildableUnit: { type, canBuild: false, canUpgrade: false, cost: 0n },
|
||||
};
|
||||
this.updateGhostPrice(0);
|
||||
const showPrice = this.game.config().userSettings().cursorCostLabel();
|
||||
this.updateGhostPrice(0, showPrice);
|
||||
const baseLevel = this.resolveGhostRangeLevel(this.ghostUnit.buildableUnit);
|
||||
this.updateGhostRange(baseLevel);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ export class UserSettings {
|
||||
return this.get("settings.territoryPatterns", true);
|
||||
}
|
||||
|
||||
cursorCostLabel() {
|
||||
const legacy = this.get("settings.ghostPricePill", true);
|
||||
return this.get("settings.cursorCostLabel", legacy);
|
||||
}
|
||||
|
||||
focusLocked() {
|
||||
return false;
|
||||
// TODO: re-enable when performance issues are fixed.
|
||||
@@ -115,6 +120,10 @@ export class UserSettings {
|
||||
this.set("settings.structureSprites", !this.structureSprites());
|
||||
}
|
||||
|
||||
toggleCursorCostLabel() {
|
||||
this.set("settings.cursorCostLabel", !this.cursorCostLabel());
|
||||
}
|
||||
|
||||
toggleTerritoryPatterns() {
|
||||
this.set("settings.territoryPatterns", !this.territoryPatterns());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user