This commit is contained in:
Aotumuri
2025-05-29 21:37:59 +09:00
parent 70d6906e62
commit 10846cc4a9
3 changed files with 83 additions and 93 deletions
+37 -32
View File
@@ -2,13 +2,7 @@ import { z } from "zod";
import rawTerritoryPatterns from "../../resources/territory_patterns.json"; import rawTerritoryPatterns from "../../resources/territory_patterns.json";
const PatternSchema = z.object({ const PatternSchema = z.object({
tileWidth: z.number().optional(),
tileHeight: z.number().optional(),
scale: z.number().optional(),
patternBase64: z.string().optional(), patternBase64: z.string().optional(),
patternData: z
.custom<Uint8Array>((val) => val instanceof Uint8Array)
.optional(),
}); });
const TerritoryPatternsSchema = z.object({ const TerritoryPatternsSchema = z.object({
@@ -18,13 +12,14 @@ const TerritoryPatternsSchema = z.object({
export const territoryPatterns = export const territoryPatterns =
TerritoryPatternsSchema.parse(rawTerritoryPatterns); TerritoryPatternsSchema.parse(rawTerritoryPatterns);
class PatternDecoder { export class PatternDecoder {
static decodeBase64Pattern(base64: string): { private bytes: Uint8Array;
data: Uint8Array; private tileWidth: number;
tileWidth: number; private tileHeight: number;
tileHeight: number; private scale: number;
scale: number; private dataStart: number;
} {
constructor(base64: string) {
const byteString = atob(base64); const byteString = atob(base64);
const bytes = new Uint8Array(byteString.length); const bytes = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) { for (let i = 0; i < byteString.length; i++) {
@@ -35,28 +30,38 @@ class PatternDecoder {
if (version !== 1) { if (version !== 1) {
throw new Error("The pattern versions are different."); 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; this.tileWidth = (bytes[1] << 8) | bytes[2];
const totalBytes = Math.ceil(totalBits / 8); this.tileHeight = (bytes[3] << 8) | bytes[4];
const data = bytes.slice(7, 7 + totalBytes); this.scale = (bytes[5] << 8) | bytes[6];
console.log("data", data); this.dataStart = 7;
this.bytes = bytes;
return { data, tileWidth, tileHeight, scale };
} }
}
export function initTerritoryPatterns(): void { getTileWidth(): number {
for (const [key, value] of Object.entries(territoryPatterns.patterns)) { return this.tileWidth;
if (value.patternBase64) { }
const decoded = PatternDecoder.decodeBase64Pattern(value.patternBase64);
value.patternData = decoded.data; getTileHeight(): number {
value.tileWidth = decoded.tileWidth; return this.tileHeight;
value.tileHeight = decoded.tileHeight; }
value.scale = decoded.scale;
} 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;
} }
} }
+43 -44
View File
@@ -4,7 +4,7 @@ import { customElement, query, state } from "lit/decorators.js";
import "./components/Difficulties"; import "./components/Difficulties";
import "./components/Maps"; import "./components/Maps";
import { import {
initTerritoryPatterns, PatternDecoder,
territoryPatterns, territoryPatterns,
TerritoryPatternStorage, TerritoryPatternStorage,
} from "./TerritoryPatterns"; } from "./TerritoryPatterns";
@@ -50,8 +50,6 @@ export class territoryPatternsModal extends LitElement {
this.updatePreview(); this.updatePreview();
}); });
initTerritoryPatterns();
this.setLockedPatterns(["evan"], { this.setLockedPatterns(["evan"], {
evan: "This pattern is locked because it is restricted.", evan: "This pattern is locked because it is restricted.",
}); });
@@ -116,8 +114,9 @@ export class territoryPatternsModal extends LitElement {
" "
> >
${(() => { ${(() => {
const cellCountX = pattern.tileWidth ?? 1; const decoder = new PatternDecoder(pattern.patternBase64!);
const cellCountY = pattern.tileHeight ?? 1; const cellCountX = decoder.getTileWidth();
const cellCountY = decoder.getTileHeight();
const cellSize = Math.floor( const cellSize = Math.floor(
this.buttonWidth / Math.max(cellCountX, cellCountY), this.buttonWidth / Math.max(cellCountX, cellCountY),
); );
@@ -135,26 +134,25 @@ export class territoryPatternsModal extends LitElement {
> >
${(() => { ${(() => {
const tiles: TemplateResult[] = []; const tiles: TemplateResult[] = [];
const total = cellCountX * cellCountY; for (let py = 0; py < cellCountY; py++) {
for (let i = 0; i < total; i++) { for (let px = 0; px < cellCountX; px++) {
const byteIndex = Math.floor(i / 8); const x = px * decoder.getScale();
const bitIndex = 7 - (i % 8); const y = py * decoder.getScale();
const bit = const bit = decoder.isSet(x, y);
(pattern.patternData?.[byteIndex] ?? 0) & tiles.push(html`
(1 << bitIndex); <div
tiles.push(html` style="
<div background-color: ${bit
style=" ? "#000"
background-color: ${bit : "transparent"};
? "#000" border: 1px solid rgba(0, 0, 0, 0.1);
: "transparent"}; width: ${cellSize}px;
border: 1px solid rgba(0, 0, 0, 0.1); height: ${cellSize}px;
width: ${cellSize}px; border-radius: 1px;
height: ${cellSize}px; "
border-radius: 1px; ></div>
" `);
></div> }
`);
} }
return tiles; return tiles;
})()} })()}
@@ -193,8 +191,9 @@ export class territoryPatternsModal extends LitElement {
const fixedHeight = 48; const fixedHeight = 48;
const fixedWidth = 48; const fixedWidth = 48;
const cellCountX = pattern.tileWidth ?? 1; const decoder = new PatternDecoder(pattern.patternBase64!);
const cellCountY = pattern.tileHeight ?? 1; const cellCountX = decoder.getTileWidth();
const cellCountY = decoder.getTileHeight();
const cellSize = Math.min( const cellSize = Math.min(
fixedHeight / cellCountY, fixedHeight / cellCountY,
@@ -228,23 +227,23 @@ export class territoryPatternsModal extends LitElement {
> >
${(() => { ${(() => {
const tiles: TemplateResult[] = []; const tiles: TemplateResult[] = [];
const total = cellCountX * cellCountY; for (let py = 0; py < cellCountY; py++) {
for (let i = 0; i < total; i++) { for (let px = 0; px < cellCountX; px++) {
const byteIndex = Math.floor(i / 8); const x = px * decoder.getScale();
const bitIndex = 7 - (i % 8); const y = py * decoder.getScale();
const bit = const bit = decoder.isSet(x, y);
(pattern.patternData?.[byteIndex] ?? 0) & (1 << bitIndex); tiles.push(html`
tiles.push(html` <div
<div style="
style=" background-color: ${bit ? "#000" : "transparent"};
background-color: ${bit ? "#000" : "transparent"}; border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.1); width: ${cellSize}px;
width: ${cellSize}px; height: ${cellSize}px;
height: ${cellSize}px; border-radius: 1px;
border-radius: 1px; "
" ></div>
></div> `);
`); }
} }
return tiles; return tiles;
})()} })()}
+3 -17
View File
@@ -8,7 +8,7 @@ import { GameUpdateType } from "../../../core/game/GameUpdates";
import { GameView, PlayerView } from "../../../core/game/GameView"; import { GameView, PlayerView } from "../../../core/game/GameView";
import { PseudoRandom } from "../../../core/PseudoRandom"; import { PseudoRandom } from "../../../core/PseudoRandom";
import { AlternateViewEvent, DragEvent } from "../../InputHandler"; import { AlternateViewEvent, DragEvent } from "../../InputHandler";
import { territoryPatterns } from "../../TerritoryPatterns"; import { PatternDecoder, territoryPatterns } from "../../TerritoryPatterns";
import { Layer } from "./Layer"; import { Layer } from "./Layer";
export class TerritoryLayer implements Layer { export class TerritoryLayer implements Layer {
@@ -301,22 +301,8 @@ export class TerritoryLayer implements Layer {
const baseColor = this.theme.territoryColor(owner); const baseColor = this.theme.territoryColor(owner);
const patternConfig = territoryPatterns.patterns[patternName]; const patternConfig = territoryPatterns.patterns[patternName];
const { const decoder = new PatternDecoder(patternConfig.patternBase64 ?? "");
tileWidth = 1, const bit = decoder.isSet(x, y) ? 1 : 0;
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 colorToUse = bit ? baseColor.darken(0.2) : baseColor; const colorToUse = bit ? baseColor.darken(0.2) : baseColor;
this.paintCell(x, y, colorToUse, 150); this.paintCell(x, y, colorToUse, 150);
} }