import { LitElement, html } from "lit"; import { customElement } from "lit/decorators.js"; import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg"; import cityIcon from "../../../../resources/images/CityIconWhite.svg"; import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; import mirvIcon from "../../../../resources/images/MIRVIcon.svg"; import missileSiloIcon from "../../../../resources/images/MissileSiloIconWhite.svg"; import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg"; import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg"; import portIcon from "../../../../resources/images/PortIcon.svg"; import samLauncherIcon from "../../../../resources/images/SamLauncherIconWhite.svg"; import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg"; import { EventBus } from "../../../core/EventBus"; import { Gold, PlayerActions, UnitType } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { GhostStructureChangedEvent, ToggleStructureEvent, } from "../../InputHandler"; import { renderNumber, translateText } from "../../Utils"; import { UIState } from "../UIState"; import { Layer } from "./Layer"; @customElement("unit-display") export class UnitDisplay extends LitElement implements Layer { public game: GameView; public eventBus: EventBus; public uiState: UIState; private playerActions: PlayerActions | null = null; private keybinds: Record = {}; private _cities = 0; private _warships = 0; private _factories = 0; private _missileSilo = 0; private _port = 0; private _defensePost = 0; private _samLauncher = 0; private allDisabled = false; private _hoveredUnit: UnitType | null = null; createRenderRoot() { return this; } init() { const config = this.game.config(); const savedKeybinds = localStorage.getItem("settings.keybinds"); if (savedKeybinds) { try { this.keybinds = JSON.parse(savedKeybinds); } catch (e) { console.warn("Invalid keybinds JSON:", e); } } this.allDisabled = config.isUnitDisabled(UnitType.City) && config.isUnitDisabled(UnitType.Factory) && config.isUnitDisabled(UnitType.Port) && config.isUnitDisabled(UnitType.DefensePost) && config.isUnitDisabled(UnitType.MissileSilo) && config.isUnitDisabled(UnitType.SAMLauncher) && config.isUnitDisabled(UnitType.Warship) && config.isUnitDisabled(UnitType.AtomBomb) && config.isUnitDisabled(UnitType.HydrogenBomb) && config.isUnitDisabled(UnitType.MIRV); this.requestUpdate(); } private cost(item: UnitType): Gold { for (const bu of this.playerActions?.buildableUnits ?? []) { if (bu.type === item) { return bu.cost; } } return 0n; } private canBuild(item: UnitType): boolean { if (this.game?.config().isUnitDisabled(item)) return false; const player = this.game?.myPlayer(); switch (item) { case UnitType.AtomBomb: case UnitType.HydrogenBomb: case UnitType.MIRV: return ( this.cost(item) <= (player?.gold() ?? 0n) && (player?.units(UnitType.MissileSilo).length ?? 0) > 0 ); case UnitType.Warship: return ( this.cost(item) <= (player?.gold() ?? 0n) && (player?.units(UnitType.Port).length ?? 0) > 0 ); default: return this.cost(item) <= (player?.gold() ?? 0n); } } tick() { const player = this.game?.myPlayer(); player?.actions().then((actions) => { this.playerActions = actions; }); if (!player) return; this._cities = player.totalUnitLevels(UnitType.City); this._missileSilo = player.totalUnitLevels(UnitType.MissileSilo); this._port = player.totalUnitLevels(UnitType.Port); this._defensePost = player.totalUnitLevels(UnitType.DefensePost); this._samLauncher = player.totalUnitLevels(UnitType.SAMLauncher); this._factories = player.totalUnitLevels(UnitType.Factory); this._warships = player.totalUnitLevels(UnitType.Warship); this.requestUpdate(); } render() { const myPlayer = this.game?.myPlayer(); if ( !this.game || !myPlayer || this.game.inSpawnPhase() || !myPlayer.isAlive() ) { return null; } if (this.allDisabled) { return null; } return html` `; } private renderUnitItem( icon: string, number: number | null, unitType: UnitType, structureKey: string, hotkey: string, ) { if (this.game.config().isUnitDisabled(unitType)) { return html``; } const selected = this.uiState.ghostStructure === unitType; const hovered = this._hoveredUnit === unitType; return html`
{ this._hoveredUnit = unitType; this.requestUpdate(); }} @mouseleave=${() => { this._hoveredUnit = null; this.requestUpdate(); }} > ${hovered ? html`
${translateText( "unit_type." + structureKey, )}${` [${hotkey.toUpperCase()}]`}
${translateText("build_menu.desc." + structureKey)}
${renderNumber(this.cost(unitType))} ${translateText("player_info_overlay.gold")}
` : null}
{ if (selected) { this.uiState.ghostStructure = null; this.eventBus?.emit(new GhostStructureChangedEvent(null)); } else if (this.canBuild(unitType)) { this.uiState.ghostStructure = unitType; this.eventBus?.emit(new GhostStructureChangedEvent(unitType)); } this.requestUpdate(); }} @mouseenter=${() => { switch (unitType) { case UnitType.AtomBomb: case UnitType.HydrogenBomb: this.eventBus?.emit( new ToggleStructureEvent([ UnitType.MissileSilo, UnitType.SAMLauncher, ]), ); break; case UnitType.Warship: this.eventBus?.emit(new ToggleStructureEvent([UnitType.Port])); break; default: this.eventBus?.emit(new ToggleStructureEvent([unitType])); } }} @mouseleave=${() => this.eventBus?.emit(new ToggleStructureEvent(null))} > ${html`
${hotkey.toUpperCase()}
`}
${structureKey} ${number !== null ? renderNumber(number) : null}
`; } }