Created
June 9, 2023 04:32
-
-
Save snuffyDev/a5e970708c7a1c2fbbead92fcbc858de to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // adapted from: https://github.com/vpoupet/wolfenstein/blob/master/js/files.js | |
| class GameData { | |
| private declare VSWAP: DataView; | |
| private declare MAPHEAD: ArrayBuffer; | |
| private declare GAMEMAPS: ArrayBuffer; | |
| private declare wallTexturesOffset: number; | |
| private declare plane0: Uint16Array; | |
| private declare plane1: Uint16Array; | |
| private declare plane2: boolean[][]; | |
| constructor() { | |
| this.VSWAP = new DataView(new ArrayBuffer(0)); | |
| this.MAPHEAD = new Uint8Array(this.VSWAP.buffer); | |
| this.GAMEMAPS = new Uint8Array(this.VSWAP.buffer); | |
| this.wallTexturesOffset = 0; | |
| this.plane0 = new Uint16Array(this.VSWAP.buffer); | |
| this.plane1 = new Uint16Array(this.VSWAP.buffer); | |
| this.plane2 = []; | |
| } | |
| get() { | |
| return [ | |
| this.VSWAP, | |
| this.MAPHEAD, | |
| this.GAMEMAPS, | |
| this.wallTexturesOffset, | |
| this.plane0, | |
| this.plane1, | |
| this.plane2 | |
| ] as const; | |
| } | |
| private rlewDecode(inView: Uint16Array): Uint16Array { | |
| const mapHeadView = new Uint16Array(this.MAPHEAD); | |
| const rlewTag = mapHeadView[0]; | |
| const size = inView[0]; | |
| const outView = new Uint16Array(size); | |
| let inOffset = 2; | |
| let outOffset = 0; | |
| for (; inOffset < inView.length; inOffset++) { | |
| const w = inView[inOffset]; | |
| if (w === rlewTag) { | |
| const n = inView[++inOffset]; | |
| const x = inView[++inOffset]; | |
| for (let i = 0; i < n; i++) { | |
| outView[outOffset++] = x; | |
| } | |
| } else { | |
| outView[outOffset++] = w; | |
| } | |
| } | |
| return outView; | |
| } | |
| private carmackDecode(inView: Uint8Array): Uint16Array { | |
| const size = inView[0] | (inView[1] << 8); | |
| const outView = new Uint16Array(size); | |
| let inOffset = 2; | |
| let outOffset = 0; | |
| while (inOffset < inView.length) { | |
| const x = inView[inOffset + 1]; | |
| if (x === 0xa7 || x === 0xa8) { | |
| const n = inView[inOffset]; | |
| if (n === 0) { | |
| outView[outOffset++] = inView[inOffset + 2] | (x << 8); | |
| inOffset += 3; | |
| } else if (x === 0xa7) { | |
| const offset = 2 * inView[inOffset + 2]; | |
| for (let i = 0; i < n; i++) { | |
| outView[outOffset++] = outView[outOffset - offset]; | |
| } | |
| inOffset += 3; | |
| } else { | |
| const offset = 2 * (inView[inOffset + 2] | (inView[inOffset + 3] << 8)); | |
| for (let i = 0; i < n; i++) { | |
| outView[outOffset++] = outView[offset + 2 * i]; | |
| } | |
| inOffset += 4; | |
| } | |
| } else { | |
| outView[outOffset++] = inView[inOffset] | (inView[inOffset + 1] << 8); | |
| inOffset += 2; | |
| } | |
| } | |
| return outView; | |
| } | |
| loadBytes(url: string): Promise<ArrayBuffer> { | |
| return fetch(url).then((response) => { | |
| if (!response.ok) { | |
| throw new Error(`Failed to load ${url}: ${response.status} ${response.statusText}`); | |
| } | |
| return response.arrayBuffer(); | |
| }); | |
| } | |
| loadResources(): Promise<void | void[]> { | |
| const splashPromise = new Promise<void>((resolve) => setTimeout(resolve, 1000)); | |
| const gamemapsPromise = this.loadBytes("data/GAMEMAPS.WL6"); | |
| const mapheadPromise = this.loadBytes("data/MAPHEAD.WL6"); | |
| const vswapPromise = this.loadBytes("data/VSWAP.WL6"); | |
| return Promise.all([splashPromise, gamemapsPromise, mapheadPromise, vswapPromise]).then( | |
| ([_, gamemaps, maphead, vswap]) => { | |
| this.GAMEMAPS = gamemaps; | |
| this.MAPHEAD = maphead; | |
| this.VSWAP = new DataView(vswap); | |
| this.wallTexturesOffset = this.VSWAP.getUint32(6, true); | |
| } | |
| ); | |
| } | |
| loadLevel(level: number): void { | |
| const mapHeadView = new DataView(this.MAPHEAD); | |
| const offset = mapHeadView.getUint32(2 + 4 * level, true); | |
| const mapHeader = new DataView(this.GAMEMAPS, offset, 42); | |
| const plane0Offset = mapHeader.getUint32(0, true); | |
| const plane0Size = mapHeader.getUint16(12, true); | |
| const plane1Offset = mapHeader.getUint32(4, true); | |
| const plane1Size = mapHeader.getUint16(14, true); | |
| this.plane0 = this.rlewDecode( | |
| this.carmackDecode(new Uint8Array(this.GAMEMAPS, plane0Offset, plane0Size)) | |
| ); | |
| this.plane1 = this.rlewDecode( | |
| this.carmackDecode(new Uint8Array(this.GAMEMAPS, plane1Offset, plane1Size)) | |
| ); | |
| this.plane2 = Array.from({ length: 64 }, () => Array(64).fill(false)); | |
| const plane2View = new DataView(this.GAMEMAPS, offset + 6, (64 * 64) / 8); | |
| for (let i = 0; i < (64 * 64) / 8; i++) { | |
| const byte = plane2View.getUint8(i); | |
| for (let j = 0; j < 8; j++) { | |
| const index = i * 8 + j; | |
| const row = Math.floor(index / 64); | |
| const col = index % 64; | |
| this.plane2[row][col] = (byte & (1 << (7 - j))) !== 0; | |
| } | |
| } | |
| } | |
| getMap0(x: number, y: number): number { | |
| return this.plane0[y * 64 + x]; | |
| } | |
| setMap0(x: number, y: number, value: number): void { | |
| this.plane0[y * 64 + x] = value; | |
| } | |
| getMap1(x: number, y: number): number { | |
| return this.plane1[y * 64 + x]; | |
| } | |
| setMap1(x: number, y: number, value: number): void { | |
| this.plane1[y * 64 + x] = value; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment