Advertisement

← tower-of-time-game

src/ui/EnergyBar.ts

6.2 KB Download file
/**
 * Energy Bar UI Component
 *
 * Displays the player's current energy as a visual bar with text
 * Features:
 * - Animated energy bar that smoothly transitions between values
 * - Text display showing current/max energy
 * - Color changes based on energy level (red when low, green when high)
 * - Positioned in the UI layer with high depth
 */

import EnergySystem, { EnergyChangeEvent } from "../systems/EnergySystem";

export default class EnergyBar extends Phaser.GameObjects.Container {
  private energySystem: EnergySystem;
  private background: Phaser.GameObjects.Graphics;
  private energyFill: Phaser.GameObjects.Graphics;
  private energyText: Phaser.GameObjects.Text;

  // Bar dimensions and styling
  private readonly BAR_WIDTH = 120;
  private readonly BAR_HEIGHT = 16;
  private readonly BAR_BORDER = 2;

  // Animation properties
  private currentDisplayEnergy: number = 0;
  private targetEnergy: number = 0;
  private animationSpeed: number = 5; // Energy units per frame for smooth animation

  // UI depth constant
  public static readonly ENERGY_BAR_DEPTH = 1000;

  constructor(
    scene: Phaser.Scene,
    x: number,
    y: number,
    energySystem: EnergySystem
  ) {
    super(scene, x, y);

    this.energySystem = energySystem;
    this.currentDisplayEnergy = energySystem.getCurrentEnergy();
    this.targetEnergy = this.currentDisplayEnergy;

    this.setDepth(EnergyBar.ENERGY_BAR_DEPTH);
    this.setScrollFactor(0); // Keep fixed on screen

    this.createBarElements();
    this.updateDisplay();

    // Subscribe to energy changes
    this.energySystem.onEnergyChange(this.onEnergyChange.bind(this));

    console.log("EnergyBar created at", x, y);
  }

  /**
   * Create the visual elements of the energy bar
   */
  private createBarElements(): void {
    // Create background
    this.background = this.scene.add.graphics();
    this.background.fillStyle(0x000000, 0.7); // Dark background
    this.background.fillRoundedRect(0, 0, this.BAR_WIDTH, this.BAR_HEIGHT, 4);
    this.background.lineStyle(this.BAR_BORDER, 0x666666, 1);
    this.background.strokeRoundedRect(0, 0, this.BAR_WIDTH, this.BAR_HEIGHT, 4);
    this.add(this.background);

    // Create energy fill
    this.energyFill = this.scene.add.graphics();
    this.add(this.energyFill);

    // Create energy text
    this.energyText = this.scene.add.text(
      this.BAR_WIDTH / 2,
      this.BAR_HEIGHT / 2,
      "",
      {
        fontSize: "10px",
        color: "#ffffff",
        fontFamily: "Arial",
        align: "center",
        stroke: "#000000",
        strokeThickness: 1,
      }
    );
    this.energyText.setOrigin(0.5, 0.5);
    this.add(this.energyText);
  }

  /**
   * Handle energy change events from the energy system
   */
  private onEnergyChange(event: EnergyChangeEvent): void {
    this.targetEnergy = event.currentEnergy;

    // For instant changes (like building placement), update immediately
    if (event.reason.includes("building") || event.reason === "reset") {
      this.currentDisplayEnergy = this.targetEnergy;
      this.updateDisplay();
    }
  }

  /**
   * Update the visual display of the energy bar
   */
  private updateDisplay(): void {
    const maxEnergy = this.energySystem.getMaxEnergy();
    const energyPercentage =
      maxEnergy > 0 ? this.currentDisplayEnergy / maxEnergy : 0;

    // Clear and redraw the energy fill
    this.energyFill.clear();

    if (energyPercentage > 0) {
      // Calculate fill width
      const fillWidth =
        (this.BAR_WIDTH - this.BAR_BORDER * 2) * energyPercentage;

      // Choose color based on energy level
      let fillColor: number;
      if (energyPercentage > 0.6) {
        fillColor = 0x00ff00; // Green when high
      } else if (energyPercentage > 0.3) {
        fillColor = 0xffff00; // Yellow when medium
      } else {
        fillColor = 0xff0000; // Red when low
      }

      // Draw the energy fill
      this.energyFill.fillStyle(fillColor, 0.8);
      this.energyFill.fillRoundedRect(
        this.BAR_BORDER,
        this.BAR_BORDER,
        fillWidth,
        this.BAR_HEIGHT - this.BAR_BORDER * 2,
        2
      );
    }

    // Update text
    const currentEnergyDisplay = Math.floor(this.currentDisplayEnergy);
    this.energyText.setText(`${currentEnergyDisplay}/${maxEnergy}`);
  }

  /**
   * Update method - should be called every frame for smooth animations
   */
  public update(): void {
    // Animate towards target energy
    if (Math.abs(this.currentDisplayEnergy - this.targetEnergy) > 0.1) {
      const diff = this.targetEnergy - this.currentDisplayEnergy;
      const step =
        Math.sign(diff) * Math.min(Math.abs(diff), this.animationSpeed);
      this.currentDisplayEnergy += step;
      this.updateDisplay();
    } else if (this.currentDisplayEnergy !== this.targetEnergy) {
      // Snap to target when very close
      this.currentDisplayEnergy = this.targetEnergy;
      this.updateDisplay();
    }
  }

  /**
   * Set the position of the energy bar
   */
  public setBarPosition(x: number, y: number): void {
    this.setPosition(x, y);
  }

  /**
   * Get the width of the energy bar for positioning calculations
   */
  public getBarWidth(): number {
    return this.BAR_WIDTH;
  }

  /**
   * Get the height of the energy bar for positioning calculations
   */
  public getBarHeight(): number {
    return this.BAR_HEIGHT;
  }

  /**
   * Set animation speed for energy changes
   */
  public setAnimationSpeed(speed: number): void {
    this.animationSpeed = Math.max(0.1, speed);
  }

  /**
   * Show or hide the energy bar
   */
  public setBarVisible(visible: boolean): void {
    this.setVisible(visible);
  }

  /**
   * Clean up when destroying the energy bar
   */
  public destroy(fromScene?: boolean): void {
    // Unsubscribe from energy changes
    if (this.energySystem) {
      this.energySystem.offEnergyChange(this.onEnergyChange.bind(this));
    }

    super.destroy(fromScene);
  }

  /**
   * Get debug information about the energy bar
   */
  public getDebugInfo(): string {
    return `EnergyBar: Display=${this.currentDisplayEnergy.toFixed(
      1
    )}, Target=${this.targetEnergy}, Visible=${this.visible}`;
  }
}