remove debug prints, axe testbed-only APIs

This commit is contained in:
Lily Tsuru 2024-07-09 23:50:16 -04:00
parent f720acc738
commit b6b069450d
3 changed files with 103 additions and 119 deletions

View file

@ -13,39 +13,40 @@ function dwAlign(off: number): number {
return ul; return ul;
} }
// animation state (used during animation playback)
class AgentAnimationState { class AgentAnimationState {
char: Agent; char: Agent;
anim: AcsAnimation; anim: AcsAnimation;
finishCallback: () => void; finishCallback: () => void;
frameIndex = 0; frameIndex = 0;
interval = 0; interval = 0;
constructor(char: Agent, anim: AcsAnimation, finishCallback: () => void) { constructor(char: Agent, anim: AcsAnimation, finishCallback: () => void) {
this.char = char; this.char = char;
this.anim = anim; this.anim = anim;
this.finishCallback = finishCallback; this.finishCallback = finishCallback;
} }
// start playing the animation // start playing the animation
play() { play() {
this.nextFrame(); this.nextFrame();
} }
nextFrame() { nextFrame() {
this.char.renderFrame(this.anim.frameInfo[this.frameIndex++]); this.char.renderFrame(this.anim.frameInfo[this.frameIndex++]);
if(this.frameIndex >= this.anim.frameInfo.length) { if (this.frameIndex >= this.anim.frameInfo.length) {
this.finishCallback(); this.finishCallback();
return; return;
} }
//@ts-ignore
this.interval = setTimeout(() => {
this.nextFrame();
}, this.anim.frameInfo[this.frameIndex].frameDuration * 10)
}
//@ts-ignore
this.interval = setTimeout(() => {
this.nextFrame();
}, this.anim.frameInfo[this.frameIndex].frameDuration * 10);
}
} }
export class Agent { export class Agent {
@ -53,11 +54,11 @@ export class Agent {
private cnv: HTMLCanvasElement; private cnv: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D; private ctx: CanvasRenderingContext2D;
private dragging: boolean; private dragging: boolean;
private x: number; private x: number;
private y: number; private y: number;
private animState: AgentAnimationState | null = null; private animState: AgentAnimationState | null = null;
constructor(data: AcsData) { constructor(data: AcsData) {
this.data = data; this.data = data;
@ -68,44 +69,48 @@ export class Agent {
this.cnv.style.position = 'fixed'; this.cnv.style.position = 'fixed';
this.cnv.style.display = 'none'; this.cnv.style.display = 'none';
this.dragging = false; this.dragging = false;
this.x = 0; this.x = 0;
this.y = 0; this.y = 0;
this.setLoc(); this.setLoc();
this.cnv.addEventListener('mousedown', () => { this.cnv.addEventListener('mousedown', () => {
this.dragging = true; this.dragging = true;
document.addEventListener('mouseup', () => { document.addEventListener(
this.dragging = false; 'mouseup',
}, {once: true}); () => {
}); this.dragging = false;
document.addEventListener('mousemove', e => { },
if (!this.dragging) return; { once: true }
this.x += e.movementX; );
this.y += e.movementY; });
this.setLoc(); document.addEventListener('mousemove', (e) => {
}); if (!this.dragging) return;
window.addEventListener('resize', () => { this.x += e.movementX;
this.setLoc(); this.y += e.movementY;
}); this.setLoc();
});
window.addEventListener('resize', () => {
this.setLoc();
});
} }
private setLoc() { private setLoc() {
if (this.x < 0) this.x = 0; if (this.x < 0) this.x = 0;
if (this.y < 0) this.y = 0; if (this.y < 0) this.y = 0;
if (this.x > document.documentElement.clientWidth - this.cnv.width) this.x = document.documentElement.clientWidth - this.cnv.width; if (this.x > document.documentElement.clientWidth - this.cnv.width) this.x = document.documentElement.clientWidth - this.cnv.width;
if (this.y > document.documentElement.clientHeight - this.cnv.height) this.y = document.documentElement.clientHeight - this.cnv.height; if (this.y > document.documentElement.clientHeight - this.cnv.height) this.y = document.documentElement.clientHeight - this.cnv.height;
this.cnv.style.top = this.y + "px"; this.cnv.style.top = this.y + 'px';
this.cnv.style.left = this.x + "px"; this.cnv.style.left = this.x + 'px';
} }
renderFrame(frame: AcsAnimationFrameInfo) { renderFrame(frame: AcsAnimationFrameInfo) {
this.ctx.clearRect(0, 0, this.cnv.width, this.cnv.height); this.ctx.clearRect(0, 0, this.cnv.width, this.cnv.height);
for (const mimg of frame.images) { for (const mimg of frame.images) {
this.drawImage(this.data.images[mimg.imageIndex], mimg.xOffset, mimg.yOffset); this.drawImage(this.data.images[mimg.imageIndex], mimg.xOffset, mimg.yOffset);
} }
} }
// Draw a single image from the agent's image table. // Draw a single image from the agent's image table.
drawImage(imageEntry: AcsImageEntry, xOffset: number, yOffset: number) { drawImage(imageEntry: AcsImageEntry, xOffset: number, yOffset: number) {
let rgbaBuffer = new Uint32Array(imageEntry.image.width * imageEntry.image.height); let rgbaBuffer = new Uint32Array(imageEntry.image.width * imageEntry.image.height);
@ -114,20 +119,20 @@ export class Agent {
let rows = new Array<Uint8Array>(imageEntry.image.height - 1); let rows = new Array<Uint8Array>(imageEntry.image.height - 1);
// Read all the rows bottom-up first. This idiosyncracy is due to the fact // Read all the rows bottom-up first. This idiosyncracy is due to the fact
// that the bitmap data is actually formatted to be used as a GDI DIB // that the bitmap data is actually formatted to be used as a GDI DIB
// (device-independent bitmap), so it inherits all the strange baggage from that. // (device-independent bitmap), so it inherits all the strange baggage from that.
for (let y = imageEntry.image.height - 1; y >= 0; --y) { for (let y = imageEntry.image.height - 1; y >= 0; --y) {
let row = bufStream.subBuffer(imageEntry.image.width).raw(); let row = bufStream.subBuffer(imageEntry.image.width).raw();
let rowResized = row.slice(0, imageEntry.image.width); let rowResized = row.slice(0, imageEntry.image.width);
rows[y] = rowResized; rows[y] = rowResized;
// Seek to the next DWORD aligned spot to get to the next row. // Seek to the next DWORD aligned spot to get to the next row.
// For most images this may mean not seeking at all. // For most images this may mean not seeking at all.
bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG); bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG);
} }
// Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect) // Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect)
for (let y = 0; y < imageEntry.image.height - 1; ++y) { for (let y = 0; y < imageEntry.image.height - 1; ++y) {
let row = rows[y]; let row = rows[y];
for (let x = 0; x < imageEntry.image.width; ++x) { for (let x = 0; x < imageEntry.image.width; ++x) {
@ -143,46 +148,41 @@ export class Agent {
parent.appendChild(this.cnv); parent.appendChild(this.cnv);
} }
remove() { remove() {
this.cnv.parentElement?.removeChild(this.cnv); this.cnv.parentElement?.removeChild(this.cnv);
} }
// add promise versions later. // add promise versions later.
playAnimation(index: number, finishCallback: () => void) { playAnimation(index: number, finishCallback: () => void) {
if(this.animState != null) if (this.animState != null) throw new Error('Cannot play multiple animations at once.');
throw new Error('Cannot play multiple animations at once.'); let animInfo = this.data.animInfo[index];
let animInfo = this.data.animInfo[index];
// Create and start the animation state // Create and start the animation state
this.animState = new AgentAnimationState(this, animInfo.animationData, () => { this.animState = new AgentAnimationState(this, animInfo.animationData, () => {
this.animationFinished(); this.animationFinished();
finishCallback(); finishCallback();
}); });
this.animState.play(); this.animState.play();
} }
playAnimationByName(name: String, finishCallback: () => void) { playAnimationByName(name: String, finishCallback: () => void) {
let index = this.data.animInfo.findIndex((n) => n.name == name); let index = this.data.animInfo.findIndex((n) => n.name == name);
if(index !== -1) if (index !== -1) this.playAnimation(index, finishCallback);
this.playAnimation(index, finishCallback); }
}
animationFinished() { animationFinished() {
this.animState = null; this.animState = null;
} }
show() { show() {
this.cnv.style.display = 'block'; this.cnv.style.display = 'block';
this.playAnimationByName("Show", () => {}); this.playAnimationByName('Show', () => {});
} }
hide(remove: boolean = false) { hide(remove: boolean = false) {
this.playAnimationByName("Hide", () => { this.playAnimationByName('Hide', () => {
if(remove) if (remove) this.remove();
this.remove(); else this.cnv.style.display = 'none';
else });
this.cnv.style.display = 'none';
});
} }
} }

View file

@ -6,19 +6,14 @@ import { AcsAnimationEntry } from './structs/animation.js';
import { AcsImageEntry } from './structs/image.js'; import { AcsImageEntry } from './structs/image.js';
import { Agent } from './agent.js'; import { Agent } from './agent.js';
// Experiment for storing parsed data // Data
export class AcsData { export class AcsData {
characterInfo = new AcsCharacterInfo(); characterInfo = new AcsCharacterInfo();
animInfo: AcsAnimationEntry[] = []; animInfo: AcsAnimationEntry[] = [];
images: AcsImageEntry[] = []; images: AcsImageEntry[] = [];
} }
function logOffset(o: number, name: string) { function agentCharacterParseACS(buffer: BufferStream): AcsData {
let n = o >>> 0;
console.log(name, 'offset:', '0x' + n.toString(16));
}
function agentCharacterParseACS(buffer: BufferStream) {
// Make sure the magic is correct for the ACS file. // Make sure the magic is correct for the ACS file.
if (buffer.readU32LE() != 0xabcdabc3) { if (buffer.readU32LE() != 0xabcdabc3) {
throw new Error('The provided data buffer does not contain valid ACS data.'); throw new Error('The provided data buffer does not contain valid ACS data.');
@ -32,10 +27,6 @@ function agentCharacterParseACS(buffer: BufferStream) {
let imageInfoLocation = LOCATION.read(buffer); let imageInfoLocation = LOCATION.read(buffer);
let audioInfoLocation = LOCATION.read(buffer); let audioInfoLocation = LOCATION.read(buffer);
logOffset(characterInfoLocation.offset, 'character info');
logOffset(animationInfoLocation.offset, 'animation info');
logOffset(imageInfoLocation.offset, 'image info');
logOffset(audioInfoLocation.offset, 'audio info');
buffer.withOffset(characterInfoLocation.offset, () => { buffer.withOffset(characterInfoLocation.offset, () => {
acsData.characterInfo = AcsCharacterInfo.read(buffer); acsData.characterInfo = AcsCharacterInfo.read(buffer);
@ -53,19 +44,12 @@ function agentCharacterParseACS(buffer: BufferStream) {
}); });
}); });
console.log(acsData);
return acsData; return acsData;
} }
// For the testbed code only, remove when that gets axed
// (or don't, I'm not your dad)
export function agentParseCharacterTestbed(buffer: Uint8Array) {
return new Agent(agentCharacterParseACS(new BufferStream(buffer)));
}
// TODO this will be the public API // TODO this will be the public API
// Dunno about maintaining canvases. We can pass a div into agentInit and add a characterInit() which recieves it // Dunno about maintaining canvases. We can pass a div into agentInit and add a characterInit() which recieves it
// (which we then mount characters and their wordballoons into?) // (which we then mount characters and their wordballoons into?)
export function agentCreateCharacter(data: Uint8Array): Promise<void> { export function agentCreateCharacter(data: Uint8Array): Agent {
throw new Error('Not implemented yet'); return new Agent(agentCharacterParseACS(new BufferStream(data)));
} }

View file

@ -8,8 +8,8 @@ let input = document.getElementById("testbed-input") as HTMLInputElement;
input.addEventListener("change", async () => { input.addEventListener("change", async () => {
let buffer = await input.files![0].arrayBuffer(); let buffer = await input.files![0].arrayBuffer();
console.log("About to parse character"); console.log("Creating agent");
let agent = msagent.agentParseCharacterTestbed(new Uint8Array(buffer)); let agent = msagent.agentCreateCharacter(new Uint8Array(buffer));
// destroy the previous agent // destroy the previous agent
if(w.agent != null) { if(w.agent != null) {
@ -20,7 +20,7 @@ input.addEventListener("change", async () => {
agent.addToDom(document.body); agent.addToDom(document.body);
agent.show(); agent.show();
console.log("parsed character"); console.log("Agent created");
}) })
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {