mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-05 19:25:08 +00:00
Add support for colored patterns (#2062)
## Description: Add support for colored territory patterns/skins * Refactored & updated territory pattern rendering to render colored skins * rename public from pattern to skin (keep pattern name internally, too difficult to rename) * Moved all territory color logic to PlayerView * Updated WinModal to show colored skins * Refactored decode logic into a separate function: decodePatternData * Refactored/updated how cosmetics are sent to server. Players now send a PlayerCosmeticRefsSchema in the ClientJoinMessage. PlayerCosmeticRefsSchema just contains names of the cosmetics, and the server replaces the names/references with actual cosmetic data * Refactored PastelThemeDark: have it extend Pastel theme so duplicate logic can be removed. * ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
+46
-30
@@ -1,3 +1,5 @@
|
||||
import { PlayerPattern } from "./Schemas";
|
||||
|
||||
export class PatternDecoder {
|
||||
private bytes: Uint8Array;
|
||||
|
||||
@@ -5,37 +7,19 @@ export class PatternDecoder {
|
||||
readonly width: number;
|
||||
readonly scale: number;
|
||||
|
||||
constructor(base64: string, base64urlDecode: (input: string) => Uint8Array) {
|
||||
this.bytes = base64urlDecode(base64);
|
||||
|
||||
if (this.bytes.length < 3) {
|
||||
throw new Error(
|
||||
"Pattern data is too short to contain required metadata.",
|
||||
);
|
||||
}
|
||||
|
||||
const version = this.bytes[0];
|
||||
if (version !== 0) {
|
||||
throw new Error(`Unrecognized pattern version ${version}.`);
|
||||
}
|
||||
|
||||
const byte1 = this.bytes[1];
|
||||
const byte2 = this.bytes[2];
|
||||
this.scale = byte1 & 0x07;
|
||||
|
||||
this.width = (((byte2 & 0x03) << 5) | ((byte1 >> 3) & 0x1f)) + 2;
|
||||
this.height = ((byte2 >> 2) & 0x3f) + 2;
|
||||
|
||||
const expectedBits = this.width * this.height;
|
||||
const expectedBytes = (expectedBits + 7) >> 3; // Equivalent to: ceil(expectedBits / 8);
|
||||
if (this.bytes.length - 3 < expectedBytes) {
|
||||
throw new Error(
|
||||
"Pattern data is too short for the specified dimensions.",
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
pattern: PlayerPattern,
|
||||
base64urlDecode: (input: string) => Uint8Array,
|
||||
) {
|
||||
({
|
||||
height: this.height,
|
||||
width: this.width,
|
||||
scale: this.scale,
|
||||
bytes: this.bytes,
|
||||
} = decodePatternData(pattern.patternData, base64urlDecode));
|
||||
}
|
||||
|
||||
isSet(x: number, y: number): boolean {
|
||||
isPrimary(x: number, y: number): boolean {
|
||||
const px = (x >> this.scale) % this.width;
|
||||
const py = (y >> this.scale) % this.height;
|
||||
const idx = py * this.width + px;
|
||||
@@ -43,7 +27,8 @@ export class PatternDecoder {
|
||||
const bitIndex = idx & 7;
|
||||
const byte = this.bytes[3 + byteIndex];
|
||||
if (byte === undefined) throw new Error("Invalid pattern");
|
||||
return (byte & (1 << bitIndex)) !== 0;
|
||||
|
||||
return (byte & (1 << bitIndex)) === 0;
|
||||
}
|
||||
|
||||
scaledHeight(): number {
|
||||
@@ -54,3 +39,34 @@ export class PatternDecoder {
|
||||
return this.width << this.scale;
|
||||
}
|
||||
}
|
||||
|
||||
export function decodePatternData(
|
||||
b64: string,
|
||||
base64urlDecode: (input: string) => Uint8Array,
|
||||
): { height: number; width: number; scale: number; bytes: Uint8Array } {
|
||||
const bytes = base64urlDecode(b64);
|
||||
|
||||
if (bytes.length < 3) {
|
||||
throw new Error("Pattern data is too short to contain required metadata.");
|
||||
}
|
||||
|
||||
const version = bytes[0];
|
||||
if (version !== 0) {
|
||||
throw new Error(`Unrecognized pattern version ${version}.`);
|
||||
}
|
||||
|
||||
const byte1 = bytes[1];
|
||||
const byte2 = bytes[2];
|
||||
const scale = byte1 & 0x07;
|
||||
|
||||
const width = (((byte2 & 0x03) << 5) | ((byte1 >> 3) & 0x1f)) + 2;
|
||||
const height = ((byte2 >> 2) & 0x3f) + 2;
|
||||
|
||||
const expectedBits = width * height;
|
||||
const expectedBytes = (expectedBits + 7) >> 3; // Equivalent to: ceil(expectedBits / 8);
|
||||
if (bytes.length - 3 < expectedBytes) {
|
||||
throw new Error("Pattern data is too short for the specified dimensions.");
|
||||
}
|
||||
|
||||
return { height, width, scale, bytes };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user