import { Anim, Character, type CharacterContext } from './Character'; import { aimDir } from './Mamie'; /** * Le Chat. * - Attack: paw swipe. * - Special: shoves what is in front of him: falling chunks, loose items, and stunned players * (others only budge a little). Perfect for pushing someone off the edge. * - Flaw: now and then he stops to lick his paw for a second, whatever is going on. */ export class Chat extends Character { private pawTimer = 0; private shoveTimer = 0; private licking = 0; /** Seconds before the next lick, drawn on the first tick. */ private nextLick = -1; onAttack(ctx: CharacterContext): void { const p = this.stats.params; const [dx, dy] = aimDir(ctx); const reach = this.size + p.pawReach; const px = ctx.x + dx * reach; const py = ctx.y + dy * reach; ctx.hit(px, py, p.pawRadius, p.pawKnockback, p.pawStun); ctx.emitOp({ kind: 'carve', x: Math.round(px), y: Math.round(py), r: Math.round(p.pawCarve), power: Math.round(p.pawPower) }); this.pawTimer = p.pawAnim; } onSpecial(ctx: CharacterContext): void { const p = this.stats.params; const reach = this.size + p.shoveReach / 2; ctx.shove(ctx.x + ctx.facing * reach, ctx.y, p.shoveRadius, ctx.facing * p.shoveSpeed, -p.shoveLift, p.shoveFraction); this.shoveTimer = p.shoveAnim; } override onTick(ctx: CharacterContext, dt: number): void { const p = this.stats.params; this.pawTimer = Math.max(0, this.pawTimer - dt); this.shoveTimer = Math.max(0, this.shoveTimer - dt); if (this.nextLick < 0) this.nextLick = p.lickMin + ctx.random() * (p.lickMax - p.lickMin); if (this.licking > 0) { this.licking = Math.max(0, this.licking - dt); return; } this.nextLick -= dt; if (this.nextLick <= 0) { this.licking = p.lickTime; this.nextLick = p.lickMin + ctx.random() * (p.lickMax - p.lickMin); ctx.cue('lick'); } } override busy(): boolean { return this.licking > 0; } override anim(): number { return (this.pawTimer > 0 ? Anim.Attack : 0) | (this.shoveTimer > 0 ? Anim.Special : 0) | (this.licking > 0 ? Anim.Busy : 0); } }