From b1e583dd42f549ba083c3a8eed13125fedb663e4 Mon Sep 17 00:00:00 2001 From: Bruce Pascoe Date: Thu, 27 Jan 2022 01:41:19 -0500 Subject: [PATCH] Oozaru 0.5.1 Adds `Task` alias for `Thread` and removes the `.whenReady()` APIs. --- CHANGELOG.md | 9 ++- package-lock.json | 18 ++--- package.json | 4 +- src/fontso.ts | 151 +++++++++++++++++++++-------------------- src/galileo.ts | 19 +++--- src/package.ts | 24 +++---- web/oozaru.json | 2 +- web/runtime/console.js | 2 +- 8 files changed, 115 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f45865..472bedd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,12 @@ Oozaru Changelog ================ -Since Last Release ------------------- +v0.5.1 - January 27, 2022 +------------------------- * Adds support for games that use `Task` instead of `Thread`. -* Removes the background-loading support for assets and the corresponding APIs - added in the previous release. - +* Removes background-loading support for assets and the corresponding APIs + (e.g. `whenReady`) added in the previous release. v0.5.0 - January 5, 2022 ------------------------ diff --git a/package-lock.json b/package-lock.json index ef0773a..4d734bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,21 @@ { "name": "oozaru", - "version": "0.5.0+", + "version": "0.5.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "oozaru", - "version": "0.5.0+", + "version": "0.5.1", "license": "MIT", "devDependencies": { - "typescript": "^4.5.4" + "typescript": "^4.5.5" } }, "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -28,9 +28,9 @@ }, "dependencies": { "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true } } diff --git a/package.json b/package.json index 81b29a3..bc41ab8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oozaru", - "version": "0.5.0+", + "version": "0.5.1", "description": "JavaScript game engine for the Web", "main": "index.js", "scripts": { @@ -23,6 +23,6 @@ }, "homepage": "https://github.com/fatcerberus/oozaru#readme", "devDependencies": { - "typescript": "^4.5.4" + "typescript": "^4.5.5" } } diff --git a/src/fontso.ts b/src/fontso.ts index 2fb448a..516de2c 100644 --- a/src/fontso.ts +++ b/src/fontso.ts @@ -68,17 +68,17 @@ class Font const fontURL = Game.urlOf(fileName); const fileData = await Fido.fetchData(fontURL); const font = new Font(fileData); - font.fileName = Game.fullPath(fileName); + font.#fileName = Game.fullPath(fileName); return font; } - atlas!: Texture; - fileName: string | undefined; - glyphs: Glyph[] = []; - lineHeight = 0; - maxWidth = 0; - numGlyphs = 0; - stride!: number; + #atlas: Texture; + #fileName: string | undefined; + #glyphs: Glyph[] = []; + #lineHeight = 0; + #maxWidth = 0; + #numGlyphs = 0; + #stride: number; constructor(...args: | [ ArrayBuffer ] | [ string ]) @@ -88,30 +88,30 @@ class Font } else if (args[0] instanceof ArrayBuffer) { let dataStream = new DataStream(args[0]); - let rfn = dataStream.readStruct({ + let rfnHeader = dataStream.readStruct({ signature: 'string/4', version: 'uint16-le', numGlyphs: 'uint16-le', reserved: 'reserve/248', }); - if (rfn.signature !== '.rfn') + if (rfnHeader.signature !== '.rfn') throw new Error(`Unable to load RFN font file`); - if (rfn.version < 2 || rfn.version > 2) - throw new Error(`Unsupported RFN version '${rfn.version}'`) - if (rfn.numGlyphs <= 0) + if (rfnHeader.version < 2 || rfnHeader.version > 2) + throw new Error(`Unsupported RFN version '${rfnHeader.version}'`) + if (rfnHeader.numGlyphs <= 0) throw new Error(`Malformed RFN font (no glyphs)`); - const numAcross = Math.ceil(Math.sqrt(rfn.numGlyphs)); - this.stride = 1.0 / numAcross; - for (let i = 0; i < rfn.numGlyphs; ++i) { + const numAcross = Math.ceil(Math.sqrt(rfnHeader.numGlyphs)); + this.#stride = 1.0 / numAcross; + for (let i = 0; i < rfnHeader.numGlyphs; ++i) { let charInfo = dataStream.readStruct({ width: 'uint16-le', height: 'uint16-le', reserved: 'reserve/28', }); - this.lineHeight = Math.max(this.lineHeight, charInfo.height); - this.maxWidth = Math.max(this.maxWidth, charInfo.width); + this.#lineHeight = Math.max(this.#lineHeight, charInfo.height); + this.#maxWidth = Math.max(this.#maxWidth, charInfo.width); const pixelData = dataStream.readBytes(charInfo.width * charInfo.height * 4); - this.glyphs.push({ + this.#glyphs.push({ width: charInfo.width, height: charInfo.height, u: i % numAcross / numAcross, @@ -119,13 +119,13 @@ class Font pixelData, }); } - this.atlas = new Texture(numAcross * this.maxWidth, numAcross * this.lineHeight); - this.numGlyphs = rfn.numGlyphs; - for (let i = 0; i < this.numGlyphs; ++i) { - const glyph = this.glyphs[i]; - const x = i % numAcross * this.maxWidth; - const y = Math.floor(i / numAcross) * this.lineHeight; - this.atlas.upload(glyph.pixelData, x, y, glyph.width, glyph.height); + this.#atlas = new Texture(numAcross * this.#maxWidth, numAcross * this.#lineHeight); + this.#numGlyphs = rfnHeader.numGlyphs; + for (let i = 0; i < this.#numGlyphs; ++i) { + const glyph = this.#glyphs[i]; + const x = i % numAcross * this.#maxWidth; + const y = Math.floor(i / numAcross) * this.#lineHeight; + this.#atlas.upload(glyph.pixelData, x, y, glyph.width, glyph.height); } } else { @@ -133,9 +133,14 @@ class Font } } + get fileName() + { + return this.#fileName; + } + get height() { - return this.lineHeight; + return this.#lineHeight; } drawText(surface: Surface, x: number, y: number, text: string | number | boolean, color = Color.White, wrapWidth?: number) @@ -144,10 +149,10 @@ class Font if (wrapWidth !== undefined) { const lines = this.wordWrap(text, wrapWidth); for (let i = 0, len = lines.length; i < len; ++i) - this.renderString(surface, x, y, lines[i], color); + this.#renderString(surface, x, y, lines[i], color); } else { - this.renderString(surface, x, y, text, color); + this.#renderString(surface, x, y, text, color); } } @@ -158,13 +163,13 @@ class Font const lines = this.wordWrap(text, wrapWidth); return { width: wrapWidth, - height: lines.length * this.lineHeight, + height: lines.length * this.#lineHeight, }; } else { return { width: this.widthOf(text), - height: this.lineHeight, + height: this.#lineHeight, }; } } @@ -174,42 +179,6 @@ class Font return this.getTextSize(text, wrapWidth).height; } - renderString(surface: Surface, x: number, y: number, text: string, color: Color) - { - x = Math.trunc(x); - y = Math.trunc(y); - if (text === "") - return; // empty string, nothing to render - let cp: number | undefined; - let ptr = 0; - let xOffset = 0; - const vertices: Vertex[] = []; - while ((cp = text.codePointAt(ptr++)) !== undefined) { - if (cp > 0xFFFF) // surrogate pair? - ++ptr; - cp = toCP1252(cp); - if (cp >= this.numGlyphs) - cp = 0x1A; - const glyph = this.glyphs[cp]; - const x1 = x + xOffset, x2 = x1 + glyph.width; - const y1 = y, y2 = y1 + glyph.height; - const u1 = glyph.u; - const u2 = u1 + glyph.width / this.maxWidth * this.stride; - const v1 = glyph.v; - const v2 = v1 - glyph.height / this.lineHeight * this.stride; - vertices.push( - { x: x1, y: y1, u: u1, v: v1, color }, - { x: x2, y: y1, u: u2, v: v1, color }, - { x: x1, y: y2, u: u1, v: v2, color }, - { x: x2, y: y1, u: u2, v: v1, color }, - { x: x1, y: y2, u: u1, v: v2, color }, - { x: x2, y: y2, u: u2, v: v2, color }, - ); - xOffset += glyph.width; - } - Shape.drawImmediate(surface, ShapeType.Triangles, this.atlas, vertices); - } - widthOf(text: string | number | boolean) { text = text.toString(); @@ -220,9 +189,9 @@ class Font if (cp > 0xFFFF) // surrogate pair? ++ptr; cp = toCP1252(cp); - if (cp >= this.numGlyphs) + if (cp >= this.#numGlyphs) cp = 0x1A; - width += this.glyphs[cp].width; + width += this.#glyphs[cp].width; } return width; } @@ -231,7 +200,7 @@ class Font { text = text.toString(); const lines: string[] = []; - let codepoints: number[] = []; + const codepoints: number[] = []; let currentLine = ""; let lineWidth = 0; let lineFinished = false; @@ -243,9 +212,9 @@ class Font if (cp > 0xFFFF) // surrogate pair? ++ptr; cp = toCP1252(cp); - if (cp >= this.numGlyphs) + if (cp >= this.#numGlyphs) cp = 0x1A; - const glyph = this.glyphs[cp]; + const glyph = this.#glyphs[cp]; switch (cp) { case 13: case 10: // newline if (cp === 13 && text.codePointAt(ptr) == 10) @@ -254,7 +223,7 @@ class Font break; case 8: // tab codepoints.push(cp); - wordWidth += this.glyphs[32].width * 3; + wordWidth += this.#glyphs[32].width * 3; wordFinished = true; break; case 32: // space @@ -286,6 +255,42 @@ class Font lines.push(currentLine); return lines; } + + #renderString(surface: Surface, x: number, y: number, text: string, color: Color) + { + x = Math.trunc(x); + y = Math.trunc(y); + if (text === "") + return; // empty string, nothing to render + let cp: number | undefined; + let ptr = 0; + let xOffset = 0; + const vertices: Vertex[] = []; + while ((cp = text.codePointAt(ptr++)) !== undefined) { + if (cp > 0xFFFF) // surrogate pair? + ++ptr; + cp = toCP1252(cp); + if (cp >= this.#numGlyphs) + cp = 0x1A; + const glyph = this.#glyphs[cp]; + const x1 = x + xOffset, x2 = x1 + glyph.width; + const y1 = y, y2 = y1 + glyph.height; + const u1 = glyph.u; + const u2 = u1 + glyph.width / this.#maxWidth * this.#stride; + const v1 = glyph.v; + const v2 = v1 - glyph.height / this.#lineHeight * this.#stride; + vertices.push( + { x: x1, y: y1, u: u1, v: v1, color }, + { x: x2, y: y1, u: u2, v: v1, color }, + { x: x1, y: y2, u: u1, v: v2, color }, + { x: x2, y: y1, u: u2, v: v1, color }, + { x: x1, y: y2, u: u1, v: v2, color }, + { x: x2, y: y2, u: u2, v: v2, color }, + ); + xOffset += glyph.width; + } + Shape.drawImmediate(surface, ShapeType.Triangles, this.#atlas, vertices); + } } function toCP1252(codepoint: number) diff --git a/src/galileo.ts b/src/galileo.ts index 02b938c..970fddd 100644 --- a/src/galileo.ts +++ b/src/galileo.ts @@ -489,17 +489,15 @@ class Model { return this.shader_; } - - get transform() - { - return this.transform_; - } - set shader(value) { this.shader_ = value; } + get transform() + { + return this.transform_; + } set transform(value) { this.transform_ = value; @@ -1019,18 +1017,17 @@ class Surface extends Texture return screenSurface; } - static async fromFile(fileName: string) + static fromFile(fileName: string): Promise { - const url = Game.urlOf(fileName); - const image = await Fido.fetchImage(url); - return new Surface(image); + throw Error("'Surface.fromFile' is not supported in Oozaru."); } constructor(...args: | [ HTMLImageElement ] + | [ string ] | [ number, number, (BufferSource | Color)? ]) { if (typeof args[0] === 'string') - throw RangeError("new Surface() doesn't support background loading."); + throw Error("'new Surface' with filename is not supported in Oozaru."); super(...args); diff --git a/src/package.ts b/src/package.ts index bc23623..3141303 100644 --- a/src/package.ts +++ b/src/package.ts @@ -44,8 +44,8 @@ interface FileRecord export class Package { - stream: DataStream; - toc: Record = {}; + #stream: DataStream; + #toc: Record = {}; static async fromFile(url: string) { @@ -80,27 +80,27 @@ class Package if (entry.version !== 1) throw RangeError(`Unsupported SPK file record version '${entry.version}'`); const pathName = stream.readString(entry.nameLength); - this.toc[pathName] = { + this.#toc[pathName] = { byteOffset: entry.byteOffset, byteLength: entry.byteLength, fileSize: entry.fileSize, }; } - this.stream = stream; + this.#stream = stream; } dataOf(pathName: string) { - if (!(pathName in this.toc)) + if (!(pathName in this.#toc)) throw Error(`File not found in Sphere package '${pathName}'`); - const record = this.toc[pathName]; - if (record.data === undefined) { - if (record.byteLength !== record.fileSize) + const fileRecord = this.#toc[pathName]; + if (fileRecord.data === undefined) { + if (fileRecord.byteLength !== fileRecord.fileSize) throw RangeError(`Compressed packages are currently unsupported`); - this.stream.position = record.byteOffset; - const compressedData = this.stream.readBytes(record.byteLength); - record.data = compressedData.buffer; + this.#stream.position = fileRecord.byteOffset; + const compressedData = this.#stream.readBytes(fileRecord.byteLength); + fileRecord.data = compressedData.buffer; } - return record.data; + return fileRecord.data; } } diff --git a/web/oozaru.json b/web/oozaru.json index c5cfbe9..9220915 100644 --- a/web/oozaru.json +++ b/web/oozaru.json @@ -1,5 +1,5 @@ { "name": "Oozaru", "publisher": "Fat Cerberus", - "version": "0.5.0+" + "version": "0.5.1" } \ No newline at end of file diff --git a/web/runtime/console.js b/web/runtime/console.js index ddf2126..0348e74 100644 --- a/web/runtime/console.js +++ b/web/runtime/console.js @@ -130,7 +130,7 @@ class Console extends Task this.log(`initializing the Sphere Runtime Console`); this.log(` ${Sphere.Game.name} by ${Sphere.Game.author}`); - this.log(` Sphere v${Sphere.Version} API ${Sphere.APILevel} (${Sphere.Engine})`); + this.log(` Sphere v${Sphere.Version} API level ${Sphere.APILevel} (${Sphere.Engine})`); this.log(""); super.start();