1
0
Fork 0
tsgames/src/games/zombies/spinner.ts

131 lines
3.8 KiB
TypeScript

import Entity from "./entity";
export enum SpinnerAction {
RUN = 1,
BITE = 2,
MELEE = 3,
SHOOT = 4,
};
export type SpinnerListener = (action: SpinnerAction) => void;
export default class Spinner extends Entity {
private readonly probabilities = [0.3, 0.3, 0.2, 0.2];
private readonly colors = ['yellow', 'green', 'blue', 'red'];
private readonly symbols = ['🏃‍♂️', '🧟‍♂️', '🔪', '🎯'];
private readonly startAngle = -Math.PI / 2 - this.probabilities[0] * 2 * Math.PI;
private angle = this.startAngle;
private speed = 0;
private friction = 0.3;
private fired = true;
private listeners = new Set<SpinnerListener>();
protected override draw(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.lineCap = 'butt';
ctx.fillStyle = 'black';
ctx.font = '0.1px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.lineWidth = 0.2;
let angle = this.startAngle;
for (let i = 0; i < this.probabilities.length; i++) {
ctx.strokeStyle = this.colors[i];
ctx.beginPath();
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
ctx.arc(0.5, 0.5, 0.4, angle, nextAngle);
ctx.stroke();
const center = (angle + nextAngle) / 2;
ctx.fillText(`${i + 1}`, 0.5 + Math.cos(center) * 0.4, 0.5 + Math.sin(center) * 0.4);
ctx.fillText(this.symbols[i], 0.5 + Math.cos(center) * 0.18, 0.5 + Math.sin(center) * 0.18);
angle = nextAngle;
}
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
ctx.lineWidth = 0.01;
angle = this.startAngle;
for (let i = 0; i < this.probabilities.length; i++) {
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
ctx.beginPath();
ctx.moveTo(0.5, 0.5);
ctx.lineTo(0.5 + Math.cos(angle) * 0.49, 0.5 + Math.sin(angle) * 0.49);
ctx.stroke();
angle = nextAngle;
}
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.3, 0, Math.PI * 2);
ctx.stroke();
ctx.lineWidth = 0.03;
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.49, 0, Math.PI * 2);
ctx.moveTo(0.5, 0.5);
ctx.lineTo(0.5 + Math.cos(this.angle) * 0.4, 0.5 + Math.sin(this.angle) * 0.4);
ctx.stroke();
}
public override update(dt: number) {
if (this.fired) return;
if (this.speed < 0.1) {
this.fire();
return;
}
this.angle += this.speed * dt;
this.speed *= 1.0 - this.friction * dt;
this.friction += 0.7 * dt;
}
public override onClick() {
if (!this.fired) return;
this.fired = false;
this.speed = 25 + Math.random() * 25;
this.friction = 0.3 + Math.random() * 0.3;
}
public addListener(listener: SpinnerListener) {
this.listeners.add(listener);
}
public removeListener(listener: SpinnerListener) {
this.listeners.delete(listener);
}
private fire() {
if (this.fired) return;
this.fired = true;
let checkAngle = this.angle % (Math.PI * 2);
for (let a = 0; a < 2; a++) {
let angle = (this.startAngle + Math.PI * 2) % (Math.PI * 2);
for (let i = 0; i < this.probabilities.length; i++) {
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
if (angle <= checkAngle && checkAngle <= nextAngle) {
this.listeners.forEach(l => l(i + 1));
return;
}
angle = nextAngle;
}
checkAngle += Math.PI * 2;
}
}
}