import { html, LitElement } from "lit"; import { customElement } from "lit/decorators.js"; import { assetUrl } from "../../../core/AssetUrls"; import { EventBus } from "../../../core/EventBus"; import { BuildableUnit, BuildMenus, Gold, PlayerBuildableUnitType, UnitType, } from "../../../core/game/Game"; import { UserSettings } from "../../../core/game/UserSettings"; import { Controller } from "../../Controller"; import { ToggleStructureEvent } from "../../InputHandler"; import { UIState } from "../../UIState"; import { renderNumber, translateText } from "../../Utils"; import { GameView } from "../../view"; const warshipIcon = assetUrl("images/BattleshipIconWhite.svg"); const cityIcon = assetUrl("images/CityIconWhite.svg"); const factoryIcon = assetUrl("images/FactoryIconWhite.svg"); const goldCoinIcon = assetUrl("images/GoldCoinIcon.svg"); const mirvIcon = assetUrl("images/MIRVIcon.svg"); const missileSiloIcon = assetUrl("images/MissileSiloIconWhite.svg"); const hydrogenBombIcon = assetUrl("images/MushroomCloudIconWhite.svg"); const atomBombIcon = assetUrl("images/NukeIconWhite.svg"); const portIcon = assetUrl("images/PortIcon.svg"); const samLauncherIcon = assetUrl("images/SamLauncherIconWhite.svg"); const defensePostIcon = assetUrl("images/ShieldIconWhite.svg"); @customElement("unit-display") export class UnitDisplay extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; public uiState: UIState; private playerBuildables: BuildableUnit[] | 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: PlayerBuildableUnitType | null = null; createRenderRoot() { return this; } init() { const config = this.game.config(); const userSettings = new UserSettings(); this.keybinds = userSettings.parsedUserKeybinds(); this.allDisabled = BuildMenus.types.every((u) => config.isUnitDisabled(u)); this.requestUpdate(); } private cost(item: UnitType): Gold { for (const bu of this.playerBuildables ?? []) { 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(); if (!player) return; player.buildables(undefined, BuildMenus.types).then((buildables) => { this.playerBuildables = buildables; }); 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`
${this.renderUnitItem( cityIcon, this._cities, UnitType.City, "city", this.keybinds["buildCity"]?.key ?? "1", )} ${this.renderUnitItem( factoryIcon, this._factories, UnitType.Factory, "factory", this.keybinds["buildFactory"]?.key ?? "2", )} ${this.renderUnitItem( portIcon, this._port, UnitType.Port, "port", this.keybinds["buildPort"]?.key ?? "3", )} ${this.renderUnitItem( defensePostIcon, this._defensePost, UnitType.DefensePost, "defense_post", this.keybinds["buildDefensePost"]?.key ?? "4", )} ${this.renderUnitItem( missileSiloIcon, this._missileSilo, UnitType.MissileSilo, "missile_silo", this.keybinds["buildMissileSilo"]?.key ?? "5", )} ${this.renderUnitItem( samLauncherIcon, this._samLauncher, UnitType.SAMLauncher, "sam_launcher", this.keybinds["buildSamLauncher"]?.key ?? "6", )} ${this.renderUnitItem( warshipIcon, this._warships, UnitType.Warship, "warship", this.keybinds["buildWarship"]?.key ?? "7", )} ${this.renderUnitItem( atomBombIcon, null, UnitType.AtomBomb, "atom_bomb", this.keybinds["buildAtomBomb"]?.key ?? "8", )} ${this.renderUnitItem( hydrogenBombIcon, null, UnitType.HydrogenBomb, "hydrogen_bomb", this.keybinds["buildHydrogenBomb"]?.key ?? "9", )} ${this.renderUnitItem( mirvIcon, null, UnitType.MIRV, "mirv", this.keybinds["buildMIRV"]?.key ?? "0", )}
`; } private renderUnitItem( icon: string, number: number | null, unitType: PlayerBuildableUnitType, structureKey: string, hotkey: string, ) { if (this.game.config().isUnitDisabled(unitType)) { return html``; } const selected = this.uiState.ghostStructure === unitType; const hovered = this._hoveredUnit === unitType; const displayHotkey = hotkey .replace("Digit", "") .replace("Key", "") .toUpperCase(); return html`
{ this._hoveredUnit = unitType; this.requestUpdate(); }} @mouseleave=${() => { this._hoveredUnit = null; this.requestUpdate(); }} > ${hovered ? html`
${translateText( "unit_type." + structureKey, )}${` [${displayHotkey}]`}
${translateText("build_menu.desc." + structureKey)}
${unitType === UnitType.Warship ? html`
⇧ ${translateText("build_menu.warship_shift_hint")}
` : null}
${renderNumber(this.cost(unitType))}
` : null}
{ if (selected) { this.uiState.ghostStructure = null; } else if (this.canBuild(unitType)) { this.uiState.ghostStructure = 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`
${displayHotkey}
`}
${structureKey} ${number !== null ? html`${renderNumber(number)}` : null}
`; } }