File size: 3,043 Bytes
c670567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
 * ============================================================================
 * FLOATING TEXT - Animated popup text (damage numbers, score, etc.)
 * ============================================================================
 *
 * This is a STATIC utility class. It is NOT instantiated with `new`.
 *
 * Creates text that floats upward and fades out. Used for:
 * - Damage numbers ("25 DMG")
 * - Score popups ("+100")
 * - Status effects ("SHIELDED!", "MISS!")
 * - Combo indicators ("x1.5 COMBO!")
 *
 * Auto-destroys after animation completes.
 *
 * CORRECT USAGE:
 *   // From a scene (static method):
 *   FloatingText.show(scene, 400, 300, '-25', { color: '#ff0000' });
 *
 *   // From a BaseBattleScene subclass (inherited helper):
 *   this.showFloatingText('-25', 400, 300, { color: '#ff0000' });
 *
 * !! WRONG - WILL CAUSE RUNTIME ERROR !!
 *   new FloatingText(scene, ...)  // NOT a constructor!
 *   new FloatingText(...)         // NOT a constructor!
 */

import Phaser from 'phaser';

export interface FloatingTextConfig {
  /** Text color (CSS format, default: '#ffffff') */
  color?: string;
  /** Font size (CSS format, default: '24px') */
  fontSize?: string;
  /** Font family (default: 'Arial') */
  fontFamily?: string;
  /** Bold text (default: true) */
  bold?: boolean;
  /** Stroke color (default: '#000000') */
  strokeColor?: string;
  /** Stroke width (default: 4) */
  strokeWidth?: number;
  /** Float distance in pixels (default: 60) */
  floatDistance?: number;
  /** Animation duration in ms (default: 1000) */
  duration?: number;
  /** Start with a scale bounce (default: false) */
  bounce?: boolean;
  /** Horizontal drift (pixels, default: 0) */
  driftX?: number;
}

export class FloatingText {
  /**
   * Create and animate a floating text.
   * Static factory method -- auto-destroys on completion.
   */
  static show(
    scene: Phaser.Scene,
    x: number,
    y: number,
    text: string,
    config?: FloatingTextConfig,
  ): Phaser.GameObjects.Text {
    const c = config ?? {};
    const textObj = scene.add
      .text(x, y, text, {
        fontSize: c.fontSize ?? '24px',
        fontFamily: c.fontFamily ?? 'Arial',
        color: c.color ?? '#ffffff',
        fontStyle: c.bold !== false ? 'bold' : '',
        stroke: c.strokeColor ?? '#000000',
        strokeThickness: c.strokeWidth ?? 4,
      })
      .setOrigin(0.5)
      .setDepth(999);

    const duration = c.duration ?? 1000;
    const floatDist = c.floatDistance ?? 60;

    // Optional bounce
    if (c.bounce) {
      textObj.setScale(0.5);
      scene.tweens.add({
        targets: textObj,
        scaleX: 1.2,
        scaleY: 1.2,
        duration: 100,
        yoyo: true,
        onComplete: () => textObj.setScale(1),
      });
    }

    // Float up + fade out
    scene.tweens.add({
      targets: textObj,
      y: y - floatDist,
      x: x + (c.driftX ?? 0),
      alpha: 0,
      duration: duration,
      ease: 'Cubic.easeOut',
      onComplete: () => textObj.destroy(),
    });

    return textObj;
  }
}