Leon.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { Anim, Character, type CharacterContext } from './Character';
  2. import { aimDir } from './Mamie';
  3. /**
  4. * Léon le Caméléon.
  5. * - Attack: shoots his long sticky tongue toward the aim. It licks a groove through the cake,
  6. * and whatever it catches at the tip is pulled back to him: players (who drop the cherry),
  7. * items, falling chunks.
  8. * - Special: blends in and turns invisible for the other players for a while (press again to
  9. * show up early).
  10. * - Flaw: attacking or being hit shows him at once, and a cherry he holds floats in plain sight.
  11. */
  12. export class Leon extends Character {
  13. private tongueTimer = 0;
  14. private hidden = 0;
  15. onAttack(ctx: CharacterContext): void {
  16. const p = this.stats.params;
  17. this.reveal(ctx);
  18. const [dx, dy] = aimDir(ctx);
  19. // The groove licked through the cake on the way.
  20. for (let d = this.size; d <= this.size + p.tongueReach; d += p.tongueStep) {
  21. ctx.emitOp({ kind: 'carve', x: Math.round(ctx.x + dx * d), y: Math.round(ctx.y + dy * d), r: Math.round(p.tongueCarve), power: Math.round(p.tonguePower) });
  22. }
  23. // Caught at the tip: stuck, then pulled back to him.
  24. const tx = ctx.x + dx * (this.size + p.tongueReach);
  25. const ty = ctx.y + dy * (this.size + p.tongueReach);
  26. ctx.hit(tx, ty, p.tongueRadius, 0, p.tongueStun);
  27. ctx.shove(tx, ty, p.tongueRadius, -dx * p.tonguePull, -dy * p.tonguePull - p.tongueLift, 1);
  28. ctx.cue('tongue');
  29. this.tongueTimer = p.tongueAnim;
  30. }
  31. onSpecial(ctx: CharacterContext): void {
  32. if (this.hidden > 0) {
  33. this.reveal(ctx);
  34. return;
  35. }
  36. this.hidden = this.stats.params.invisibleTime;
  37. ctx.cue('vanish');
  38. }
  39. override onHit(ctx: CharacterContext): void {
  40. this.reveal(ctx);
  41. }
  42. override onTick(ctx: CharacterContext, dt: number): void {
  43. this.tongueTimer = Math.max(0, this.tongueTimer - dt);
  44. if (this.hidden <= 0) return;
  45. this.hidden -= dt;
  46. if (this.hidden <= 0) this.reveal(ctx);
  47. }
  48. override anim(): number {
  49. return (this.tongueTimer > 0 ? Anim.Attack : 0) | (this.hidden > 0 ? Anim.Invisible : 0);
  50. }
  51. private reveal(ctx: CharacterContext): void {
  52. if (this.hidden === 0) return;
  53. this.hidden = 0;
  54. ctx.cue('appear');
  55. }
  56. }