SOURCE / PINNED RELEASE
Made of little things.
Browser Side Quest
- Release
- b9d42f5768e4…
- Author-recorded commit
- c267690145fa…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/browser-side-quest
Archive hash verified: 6d4b90b8a5f328f7…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Port of BrowserQuest client/js/{timer,animation,transition,tile}.js (MPL-2.0).
export class Timer {
lastTime: number;
constructor(
public duration: number,
startTime = 0,
) {
this.lastTime = startTime;
}
isOver(time: number): boolean {
if (time - this.lastTime > this.duration) {
this.lastTime = time;
return true;
}
return false;
}
}
export interface Frame {
index: number;
x: number;
y: number;
}
export class Animation {
lastTime = 0;
speed = 0;
count = 0;
currentFrame: Frame = { index: 0, x: 0, y: 0 };
private endcountCallback: () => void = () => {};
constructor(
readonly name: string,
readonly length: number,
readonly row: number,
readonly width: number,
readonly height: number,
) {
this.reset();
}
tick(): void {
let i = this.currentFrame.index;
i = i < this.length - 1 ? i + 1 : 0;
if (this.count > 0 && i === 0) {
this.count -= 1;
if (this.count === 0) {
this.currentFrame.index = 0;
this.endcountCallback();
return;
}
}
this.currentFrame.x = this.width * i;
this.currentFrame.y = this.height * this.row;
this.currentFrame.index = i;
}
setSpeed(speed: number): void {
this.speed = speed;
}
setCount(count: number, onEndCount: () => void): void {
this.count = count;
this.endcountCallback = onEndCount;
}
isTimeToAnimate(time: number): boolean {
return time - this.lastTime > this.speed;
}
update(time: number): boolean {
if (this.lastTime === 0 && this.name.substring(0, 3) === 'atk') {
this.lastTime = time;
}
if (this.isTimeToAnimate(time)) {
this.lastTime = time;
this.tick();
return true;
}
return false;
}
reset(): void {
this.lastTime = 0;
this.currentFrame = { index: 0, x: 0, y: this.row * this.height };
}
}
export class Transition {
startValue = 0;
endValue = 0;
duration = 0;
inProgress = false;
startTime = 0;
count = 0;
private updateFunction: ((value: number) => void) | null = null;
private stopFunction: (() => void) | null = null;
start(
currentTime: number,
updateFunction: ((value: number) => void) | null,
stopFunction: (() => void) | null,
startValue: number,
endValue: number,
duration: number,
): void {
this.startTime = currentTime;
this.updateFunction = updateFunction;
this.stopFunction = stopFunction;
this.startValue = startValue;
this.endValue = endValue;
this.duration = duration;
this.inProgress = true;
this.count = 0;
}
step(currentTime: number): void {
if (!this.inProgress) return;
if (this.count > 0) {
this.count -= 1;
return;
}
let elapsed = currentTime - this.startTime;
if (elapsed > this.duration) elapsed = this.duration;
const diff = this.endValue - this.startValue;
const i = Math.round(this.startValue + (diff / this.duration) * elapsed);
if (elapsed === this.duration || i === this.endValue) {
this.stop();
this.stopFunction?.();
} else {
this.updateFunction?.(i);
}
}
restart(currentTime: number, startValue: number, endValue: number): void {
this.start(currentTime, this.updateFunction, this.stopFunction, startValue, endValue, this.duration);
this.step(currentTime);
}
stop(): void {
this.inProgress = false;
}
}
export class AnimatedTile {
readonly startId: number;
lastTime = 0;
x = 0;
y = 0;
isDirty = false;
constructor(
public id: number,
readonly length: number,
readonly speed: number,
readonly index: number,
) {
this.startId = id;
}
tick(): void {
if (this.id - this.startId < this.length - 1) this.id += 1;
else this.id = this.startId;
}
animate(time: number): boolean {
if (time - this.lastTime > this.speed) {
this.tick();
this.lastTime = time;
return true;
}
return false;
}
}
export class Area {
id: number | string = 0;
musicName = '';
constructor(
readonly x: number,
readonly y: number,
readonly width: number,
readonly height: number,
) {}
contains(entity: { gridX: number; gridY: number } | null): boolean {
if (!entity) return false;
return (
entity.gridX >= this.x &&
entity.gridY >= this.y &&
entity.gridX < this.x + this.width &&
entity.gridY < this.y + this.height
);
}
}
