import { LitElement, SVGTemplateResult, TemplateResult, html, nothing, svg, } from "lit"; import { customElement, property } from "lit/decorators.js"; import { Difficulty, Duos, GameMapType, GameMode, HumansVsNations, Quads, Trios, UnitType, } from "../../core/game/Game"; import { TeamCountConfig } from "../../core/Schemas"; import { translateText } from "../Utils"; import "./Difficulties"; import "./FluentSlider"; import "./map/MapPicker"; const ACTIVE_CARD = "bg-blue-500/20 border-blue-500/50 shadow-[0_0_15px_rgba(59,130,246,0.2)]"; const INACTIVE_CARD = "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"; const DISABLED_CARD = "w-full rounded-xl border transition-all duration-200 opacity-30 grayscale cursor-not-allowed bg-white/5 border-white/5"; function cardClass(active: boolean, extra = ""): string { return `w-full rounded-xl border cursor-pointer transition-all duration-200 active:scale-95 ${extra} ${active ? ACTIVE_CARD : INACTIVE_CARD}`; } const CARD_LABEL_CLASS = "text-xs uppercase font-bold tracking-wider leading-tight break-words hyphens-auto"; const DIFFICULTY_OPTIONS = Object.entries(Difficulty).filter(([key]) => isNaN(Number(key)), ) as Array<[string, Difficulty]>; const TEAM_COUNT_OPTIONS: TeamCountConfig[] = [ 2, 3, 4, 5, 6, 7, Quads, Trios, Duos, HumansVsNations, ]; function stateTextClass(active: boolean): string { return active ? "text-white" : "text-white/60"; } function renderTextCardButton( label: string, active: boolean, onClick: () => void, cardExtraClass: string, ): TemplateResult { return html` `; } function renderSection( iconSvg: SVGTemplateResult, colorClass: string, bgClass: string, titleKey: string, content: TemplateResult | TemplateResult[], sectionClass = "space-y-6", ): TemplateResult { return html`
${renderSectionHeader(iconSvg, colorClass, bgClass, titleKey)} ${content}
`; } const unitOptions: { type: UnitType; translationKey: string }[] = [ { type: UnitType.City, translationKey: "unit_type.city" }, { type: UnitType.DefensePost, translationKey: "unit_type.defense_post" }, { type: UnitType.Port, translationKey: "unit_type.port" }, { type: UnitType.Warship, translationKey: "unit_type.warship" }, { type: UnitType.TransportShip, translationKey: "unit_type.boat" }, { type: UnitType.MissileSilo, translationKey: "unit_type.missile_silo" }, { type: UnitType.SAMLauncher, translationKey: "unit_type.sam_launcher" }, { type: UnitType.AtomBomb, translationKey: "unit_type.atom_bomb" }, { type: UnitType.HydrogenBomb, translationKey: "unit_type.hydrogen_bomb" }, { type: UnitType.MIRV, translationKey: "unit_type.mirv" }, { type: UnitType.Factory, translationKey: "unit_type.factory" }, ]; const MAP_ICON = svg``; const DIFFICULTY_ICON = svg``; const MODE_ICON = svg``; const OPTIONS_ICON = svg``; const ENABLES_ICON = svg``; function renderSectionHeader( iconSvg: SVGTemplateResult, colorClass: string, bgClass: string, titleKey: string, ): TemplateResult { return html`
${iconSvg}

${translateText(titleKey)}

`; } export interface ToggleOptionConfig { labelKey: string; checked: boolean; hidden?: boolean; } export interface GameConfigSettingsData { map: { selected: GameMapType; useRandom: boolean; randomMapDivider?: boolean; showMedals?: boolean; mapWins?: Map>; }; difficulty: { selected: Difficulty; disabled: boolean; }; gameMode: { selected: GameMode; }; teamCount: { selected: TeamCountConfig; }; options: { titleKey: string; bots: { value: number; labelKey: string; disabledKey: string; }; nations?: { value: number; defaultValue?: number; labelKey: string; disabledKey: string; hidden?: boolean; }; toggles: ToggleOptionConfig[]; inputCards: TemplateResult[]; }; unitTypes: { titleKey: string; disabledUnits: UnitType[]; }; } @customElement("game-config-settings") export class GameConfigSettings extends LitElement { @property({ attribute: false }) settings?: GameConfigSettingsData; @property({ attribute: false }) sectionGapClass = "space-y-6"; createRenderRoot() { return this; } private emit(name: string, detail: T) { this.dispatchEvent( new CustomEvent(name, { detail, bubbles: true, composed: true, }), ); } private handleSelectMap = (map: GameMapType) => { this.emit("map-selected", { map }); }; private handleSelectRandom = () => { this.emit("random-map-selected", {}); }; private handleDifficultySelect = (difficulty: Difficulty) => { this.emit("difficulty-selected", { difficulty }); }; private handleGameModeSelect = (mode: GameMode) => { this.emit("game-mode-selected", { mode }); }; private handleTeamCountSelect = (count: TeamCountConfig) => { this.emit("team-count-selected", { count }); }; private handleOptionToggle = (toggle: ToggleOptionConfig) => { this.emit("option-toggle-changed", { labelKey: toggle.labelKey, checked: !toggle.checked, }); }; private handleBotsChanged = (event: Event) => { const customEvent = event as CustomEvent<{ value: number }>; this.emit("bots-changed", customEvent.detail); }; private handleNationsChanged = (event: Event) => { const customEvent = event as CustomEvent<{ value: number }>; this.emit("nations-changed", customEvent.detail); }; private handleUnitToggle = (unit: UnitType, checked: boolean) => { this.emit("unit-toggle-changed", { unit, checked }); }; private renderOptionToggle(toggle: ToggleOptionConfig): TemplateResult { if (toggle.hidden) return html``; return renderTextCardButton( translateText(toggle.labelKey), toggle.checked, () => this.handleOptionToggle(toggle), "p-4 text-center", ); } private renderUnitTypeOptions(disabledUnits: UnitType[]): TemplateResult[] { return unitOptions.map(({ type, translationKey }) => { const isEnabled = !disabledUnits.includes(type); return html` `; }); } render() { if (!this.settings) return nothing; const settings = this.settings; return html`
${renderSection( MAP_ICON, "text-blue-400", "bg-blue-500/20", "map.map", html``, )} ${renderSection( DIFFICULTY_ICON, "text-green-400", "bg-green-500/20", "difficulty.difficulty", html`
${DIFFICULTY_OPTIONS.map(([key, value]) => { const isSelected = settings.difficulty.selected === value; const isDisabled = settings.difficulty.disabled; return html` `; })}
`, )} ${renderSection( MODE_ICON, "text-purple-400", "bg-purple-500/20", "host_modal.mode", html`
${[GameMode.FFA, GameMode.Team].map((mode) => { const isSelected = settings.gameMode.selected === mode; return html` `; })}
`, )} ${settings.gameMode.selected === GameMode.FFA ? nothing : html`
${translateText("host_modal.team_count")}
${TEAM_COUNT_OPTIONS.map((o) => { const isSelected = settings.teamCount.selected === o; return html` `; })}
`} ${renderSection( OPTIONS_ICON, "text-orange-400", "bg-orange-500/20", settings.options.titleKey, html`
${settings.options.nations && !settings.options.nations.hidden ? html`
` : nothing} ${settings.options.toggles.map((toggle) => this.renderOptionToggle(toggle), )} ${settings.options.inputCards}
`, )} ${renderSection( ENABLES_ICON, "text-teal-400", "bg-teal-500/20", settings.unitTypes.titleKey, html`
${this.renderUnitTypeOptions(settings.unitTypes.disabledUnits)}
`, "space-y-6 pb-6", )}
`; } }