Skip to content
Snippets Groups Projects
hud.ts 4.47 KiB
const ICONS = {
  SCORE: "️⭐️",
  CLOCK: "⏲️",
  HEALTH: "❤️️",
};

const STATS_BASE_TEXT_STYLE = {
  fontFamily: "Courier",
  fontStyle: "bold",
  color: "white",
  testString: `${ICONS.CLOCK}${ICONS.HEALTH}${ICONS.SCORE}1234567890:.`,
  stroke: "black",
  strokeThickness: 4,
} as Phaser.Types.GameObjects.Text.TextStyle;

const INPUT_BASE_TEXT_STYLE = {
  fontFamily: "Courier",
  fontStyle: "bold",
  color: "white",
  testString: `ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖẞabcdefghijklmnopqrstuvwxyzäüöß `,
} as Phaser.Types.GameObjects.Text.TextStyle;

interface HudOptions {
  statsPadding: number;
  statsFontSize: string;
  inputPadding: number;
  inputFontSize: string;
  inputPosition: number;
}

export default class HUD {
  scene: Phaser.Scene;
  options: HudOptions;
  input: Phaser.GameObjects.Text;
  score: Phaser.GameObjects.Text;
  clock: Phaser.GameObjects.Text;
  health: Phaser.GameObjects.Text;

  constructor(scene: Phaser.Scene, options?: HudOptions) {
    this.scene = scene;
    this.options = options || {
      statsPadding: 10,
      statsFontSize: "22px",
      inputPadding: 4,
      inputFontSize: "60px",
      inputPosition: 0.5,
    };
    this.input = this.initInput(scene);
    this.score = this.initScore(scene);
    this.health = this.initHealth(scene);
    this.clock = this.initClock(scene);
  }

  initInput(scene: Phaser.Scene) {
    return scene.add
      .text(
        scene.cameras.main.width / 2,
        scene.cameras.main.height * this.options.inputPosition,
        "",
        this.inputTextStyle(),
      )
      .setOrigin(0.5, 0.5);
  }

  inputTextStyle(): Phaser.Types.GameObjects.Text.TextStyle {
    return {
      ...INPUT_BASE_TEXT_STYLE,
      fontSize: this.options.inputFontSize,
      padding: {
        x: this.options.inputPadding,
        y: this.options.inputPadding,
      },
    };
  }

  statsTextStyle(): Phaser.Types.GameObjects.Text.TextStyle {
    return {
      ...STATS_BASE_TEXT_STYLE,
      fontSize: this.options.statsFontSize,
      padding: {
        x: this.options.statsPadding,
        y: this.options.statsPadding,
      },
    };
  }

  initScore(scene: Phaser.Scene) {
    return scene.add.text(0, 0, "", this.statsTextStyle()).setOrigin(0, 0);
  }

  initHealth(scene: Phaser.Scene) {
    return scene.add
      .text(scene.cameras.main.width, 0, "", this.statsTextStyle())
      .setOrigin(1, 0);
  }

  initClock(scene: Phaser.Scene) {
    return scene.add
      .text(
        scene.cameras.main.width * 0.5,
        0,
        `${ICONS.CLOCK}1:23.32`,
        this.statsTextStyle(),
      )
      .setOrigin(0.5, 0);
  }

  setInput(input: string) {
    this.input.text = input;
  }

  setScore(score: number) {
    this.score.text = `${ICONS.SCORE}\u2009${score}`;
  }

  setHealth(health: number) {
    this.health.text = `${health}\u2009${ICONS.HEALTH}`;
  }

  setClock(milliseconds: number) {
    const minutes = Math.floor(milliseconds / 60000);
    const seconds = Math.floor((milliseconds % 60000) / 1000)
      .toString()
      .padStart(2, "0");
    const hundredths = Math.floor((milliseconds % 1000) / 10)
      .toString()
      .padStart(2, "0");
    const formatted = `${minutes}:${seconds}.${hundredths}`;
    // TODO: we can probably do away with the clock icon
    this.clock.text = `${formatted}\u2009${ICONS.CLOCK}`;
  }

  showSubmitFeedback(color: string, input: string) {
    const text = this.scene.add
      .text(
        this.scene.cameras.main.width / 2,
        this.scene.cameras.main.height * this.options.inputPosition,
        input,
        {
          ...this.inputTextStyle(),
          color: color,
        },
      )
      .setOrigin(0.5, 0.5);
    this.scene.tweens.add({
      targets: text,
      scaleX: 5,
      scaleY: 5,
      alpha: 0,
      ease: "Power2",
      duration: 500,
      onComplete: (_tween, [target]) => target.destroy(),
    });
  }

  announceWave(input: string) {
    const text = this.scene.add
      .text(
        this.scene.cameras.main.width / 2,
        this.scene.cameras.main.height / 4,
        input,
        {
          ...this.inputTextStyle(),
          color: "white",
        },
      )
      .setOrigin(0.5, 0.5)
      .setAlpha(0)
      .setScale(0);
    this.scene.tweens.add({
      targets: text,
      scaleX: 1,
      scaleY: 1,
      alpha: 1,
      ease: "Expo",
      yoyo: true,
      duration: 500,
    });
  }

  changeFlash(object: Phaser.GameObjects.Text, color: number) {
    object.setTintFill(color);
    this.scene.time.delayedCall(100, () => object.clearTint());
  }
}