diff --git a/src/client/TerritoryPatterns.ts b/src/client/TerritoryPatterns.ts index 1aba8e7d2..02035873e 100644 --- a/src/client/TerritoryPatterns.ts +++ b/src/client/TerritoryPatterns.ts @@ -2,13 +2,7 @@ import { z } from "zod"; import rawTerritoryPatterns from "../../resources/territory_patterns.json"; const PatternSchema = z.object({ - tileWidth: z.number().optional(), - tileHeight: z.number().optional(), - scale: z.number().optional(), patternBase64: z.string().optional(), - patternData: z - .custom((val) => val instanceof Uint8Array) - .optional(), }); const TerritoryPatternsSchema = z.object({ @@ -18,13 +12,14 @@ const TerritoryPatternsSchema = z.object({ export const territoryPatterns = TerritoryPatternsSchema.parse(rawTerritoryPatterns); -class PatternDecoder { - static decodeBase64Pattern(base64: string): { - data: Uint8Array; - tileWidth: number; - tileHeight: number; - scale: number; - } { +export class PatternDecoder { + private bytes: Uint8Array; + private tileWidth: number; + private tileHeight: number; + private scale: number; + private dataStart: number; + + constructor(base64: string) { const byteString = atob(base64); const bytes = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; i++) { @@ -35,28 +30,38 @@ class PatternDecoder { if (version !== 1) { throw new Error("The pattern versions are different."); } - const tileWidth = (bytes[1] << 8) | bytes[2]; - const tileHeight = (bytes[3] << 8) | bytes[4]; - const scale = (bytes[5] << 8) | bytes[6]; - const totalBits = tileWidth * tileHeight; - const totalBytes = Math.ceil(totalBits / 8); - const data = bytes.slice(7, 7 + totalBytes); - console.log("data", data); - - return { data, tileWidth, tileHeight, scale }; + this.tileWidth = (bytes[1] << 8) | bytes[2]; + this.tileHeight = (bytes[3] << 8) | bytes[4]; + this.scale = (bytes[5] << 8) | bytes[6]; + this.dataStart = 7; + this.bytes = bytes; } -} -export function initTerritoryPatterns(): void { - for (const [key, value] of Object.entries(territoryPatterns.patterns)) { - if (value.patternBase64) { - const decoded = PatternDecoder.decodeBase64Pattern(value.patternBase64); - value.patternData = decoded.data; - value.tileWidth = decoded.tileWidth; - value.tileHeight = decoded.tileHeight; - value.scale = decoded.scale; - } + getTileWidth(): number { + return this.tileWidth; + } + + getTileHeight(): number { + return this.tileHeight; + } + + getScale(): number { + return this.scale; + } + + isSet(x: number, y: number): boolean { + const px = + ((Math.floor(x / this.scale) % this.tileWidth) + this.tileWidth) % + this.tileWidth; + const py = + ((Math.floor(y / this.scale) % this.tileHeight) + this.tileHeight) % + this.tileHeight; + const idx = py * this.tileWidth + px; + const byteIndex = Math.floor(idx / 8); + const bitIndex = 7 - (idx % 8); + const byte = this.bytes[this.dataStart + byteIndex] ?? 0; + return (byte & (1 << bitIndex)) !== 0; } } diff --git a/src/client/TerritoryPatternsModal.ts b/src/client/TerritoryPatternsModal.ts index a3e9e1dce..35946689c 100644 --- a/src/client/TerritoryPatternsModal.ts +++ b/src/client/TerritoryPatternsModal.ts @@ -4,7 +4,7 @@ import { customElement, query, state } from "lit/decorators.js"; import "./components/Difficulties"; import "./components/Maps"; import { - initTerritoryPatterns, + PatternDecoder, territoryPatterns, TerritoryPatternStorage, } from "./TerritoryPatterns"; @@ -50,8 +50,6 @@ export class territoryPatternsModal extends LitElement { this.updatePreview(); }); - initTerritoryPatterns(); - this.setLockedPatterns(["evan"], { evan: "This pattern is locked because it is restricted.", }); @@ -116,8 +114,9 @@ export class territoryPatternsModal extends LitElement { " > ${(() => { - const cellCountX = pattern.tileWidth ?? 1; - const cellCountY = pattern.tileHeight ?? 1; + const decoder = new PatternDecoder(pattern.patternBase64!); + const cellCountX = decoder.getTileWidth(); + const cellCountY = decoder.getTileHeight(); const cellSize = Math.floor( this.buttonWidth / Math.max(cellCountX, cellCountY), ); @@ -135,26 +134,25 @@ export class territoryPatternsModal extends LitElement { > ${(() => { const tiles: TemplateResult[] = []; - const total = cellCountX * cellCountY; - for (let i = 0; i < total; i++) { - const byteIndex = Math.floor(i / 8); - const bitIndex = 7 - (i % 8); - const bit = - (pattern.patternData?.[byteIndex] ?? 0) & - (1 << bitIndex); - tiles.push(html` -
- `); + 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; })()} @@ -193,8 +191,9 @@ export class territoryPatternsModal extends LitElement { const fixedHeight = 48; const fixedWidth = 48; - const cellCountX = pattern.tileWidth ?? 1; - const cellCountY = pattern.tileHeight ?? 1; + const decoder = new PatternDecoder(pattern.patternBase64!); + const cellCountX = decoder.getTileWidth(); + const cellCountY = decoder.getTileHeight(); const cellSize = Math.min( fixedHeight / cellCountY, @@ -228,23 +227,23 @@ export class territoryPatternsModal extends LitElement { > ${(() => { const tiles: TemplateResult[] = []; - const total = cellCountX * cellCountY; - for (let i = 0; i < total; i++) { - const byteIndex = Math.floor(i / 8); - const bitIndex = 7 - (i % 8); - const bit = - (pattern.patternData?.[byteIndex] ?? 0) & (1 << bitIndex); - tiles.push(html` -
- `); + 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; })()} diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 7235fcc8c..6403c20f7 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -8,7 +8,7 @@ import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { PseudoRandom } from "../../../core/PseudoRandom"; import { AlternateViewEvent, DragEvent } from "../../InputHandler"; -import { territoryPatterns } from "../../TerritoryPatterns"; +import { PatternDecoder, territoryPatterns } from "../../TerritoryPatterns"; import { Layer } from "./Layer"; export class TerritoryLayer implements Layer { @@ -301,22 +301,8 @@ export class TerritoryLayer implements Layer { const baseColor = this.theme.territoryColor(owner); const patternConfig = territoryPatterns.patterns[patternName]; - const { - tileWidth = 1, - tileHeight = 1, - scale = 1, - patternData, - } = patternConfig; - - const px = - ((Math.floor(x / scale) % tileWidth) + tileWidth) % tileWidth; - const py = - ((Math.floor(y / scale) % tileHeight) + tileHeight) % tileHeight; - - const bitIndex = py * tileWidth + px; - const byte = patternData?.[Math.floor(bitIndex / 8)] ?? 0; - const bit = (byte >> (7 - (bitIndex % 8))) & 1; - + const decoder = new PatternDecoder(patternConfig.patternBase64 ?? ""); + const bit = decoder.isSet(x, y) ? 1 : 0; const colorToUse = bit ? baseColor.darken(0.2) : baseColor; this.paintCell(x, y, colorToUse, 150); }