import type { TemplateResult } from "lit"; import { html, LitElement, render } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import "./components/Difficulties"; import "./components/Maps"; import { PatternDecoder, territoryPatterns, TerritoryPatternStorage, } from "./TerritoryPatterns"; @customElement("territory-patterns-modal") export class territoryPatternsModal extends LitElement { @query("o-modal") private modalEl!: HTMLElement & { open: () => void; close: () => void; }; @query("#territory-patterns-input-preview-button") private previewButton!: HTMLElement; @state() private selectedPattern = TerritoryPatternStorage.getSelectedPattern(); @state() private buttonWidth: number = 100; @state() private lockedPatterns: string[] = []; @state() private lockedReasons: Record = {}; @state() private hoveredPattern: string | null = null; @state() private hoverPosition = { x: 0, y: 0 }; private resizeObserver: ResizeObserver; constructor() { super(); this.resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { if (entry.target.classList.contains("preview-container")) { this.buttonWidth = entry.contentRect.width; } } }); } connectedCallback() { super.connectedCallback(); this.updateComplete.then(() => { const containers = this.renderRoot.querySelectorAll(".preview-container"); containers.forEach((container) => this.resizeObserver.observe(container)); this.updatePreview(); }); this.setLockedPatterns(["evan"], { evan: "This pattern is locked because it is restricted.", }); } disconnectedCallback() { super.disconnectedCallback(); this.resizeObserver.disconnect(); } createRenderRoot() { return this; } render() { return html` ${this.hoveredPattern && this.lockedReasons[this.hoveredPattern] ? html`
${this.lockedReasons[this.hoveredPattern]}
` : null}
${Object.entries(territoryPatterns.patterns).map(([key, pattern]) => { const isLocked = this.isPatternLocked(key); const reason = this.lockedReasons[key] || "Locked"; return html` `; })}
`; } public open() { this.modalEl?.open(); } public close() { this.modalEl?.close(); } private selectPattern(patternKey: string) { this.selectedPattern = patternKey; TerritoryPatternStorage.setSelectedPattern(patternKey); this.updatePreview(); this.close(); } private updatePreview() { if (!this.previewButton || !this.selectedPattern) return; const pattern = territoryPatterns.patterns[this.selectedPattern]; if (!pattern) return; const fixedHeight = 48; const fixedWidth = 48; const decoder = new PatternDecoder(pattern.patternBase64!); const cellCountX = decoder.getTileWidth(); const cellCountY = decoder.getTileHeight(); const cellSize = Math.min( fixedHeight / cellCountY, fixedWidth / cellCountX, ); const previewHTML = html`
${(() => { const tiles: TemplateResult[] = []; for (let py = 0; py < cellCountY; py++) { for (let px = 0; px < cellCountX; px++) { const x = px * decoder.getScale(); const y = py * decoder.getScale(); const bit = decoder.isSet(x, y); tiles.push(html`
`); } } return tiles; })()}
`; render(previewHTML, this.previewButton); } private setLockedPatterns( lockedPatterns: string[], reasons: Record, ) { this.lockedPatterns = lockedPatterns; this.lockedReasons = reasons; } private isPatternLocked(patternKey: string): boolean { return this.lockedPatterns.includes(patternKey); } private handleMouseEnter(patternKey: string, event: MouseEvent) { if (this.isPatternLocked(patternKey)) { this.hoveredPattern = patternKey; this.hoverPosition = { x: event.clientX, y: event.clientY }; } } private handleMouseMove(event: MouseEvent) { if (this.hoveredPattern) { this.hoverPosition = { x: event.clientX, y: event.clientY }; } } private handleMouseLeave() { this.hoveredPattern = null; } }