Source: HealthMeter.js

/**
 * @license GPL-3.0-only
 *
 * @author Mark Mayes / mm-dev
 *
 *
 * @module HealthMeter
 *
 * @description
 * ## Manage and draw the health meter
 */

import { PD } from "./PD/CONST.js";
import * as GAME from "./PD/GAME.js";
import * as STRING from "./PD/STRING.js";

import { Display } from "./Display.js";
import { Game } from "./Game.js";
import { Layout } from "./Layout.js";
import { Player } from "./Player.js";
import { Text } from "./Text.js";

import { __, isEvenNumber } from "./utils.js";

class HealthMeter {}

/**
 * @function updateSizes
 * @static
 *
 * @description
 * ##### Update the dimensions of the meter
 * Size is based on text character widths or heights (depending on viewport aspect ratio), so that the meter lines up with the (conceptual) text grid.
 */
HealthMeter.updateSizes = function () {
  __("HealthMeter.updateSizes()", PD.FMT_DISPLAY);
  __("Text.drawnPixelSize: " + Text.drawnPixelSize, PD.FMT_DISPLAY);
  if (Display.isLandscapeAspect) {
    HealthMeter.width = Text.drawnCharWidth;
    HealthMeter.height = GAME.HEALTHMETER_SIZE_CHARS * Text.lineHeight;
    //
    HealthMeter.bgWidth =
      HealthMeter.width + Layout.textBgPaddingProportional * 2;
    HealthMeter.bgHeight =
      HealthMeter.height +
      Text.lineHeight +
      Layout.textBgPaddingProportional * 3;
    //
    HealthMeter.offsetH = 0 - Layout.textBgPaddingProportional;
    HealthMeter.offsetV = 0;
    //
    HealthMeter.heartOffsetH = 0 - Math.round(Text.letterSpacing / 2);
  } else {
    HealthMeter.width = GAME.HEALTHMETER_SIZE_CHARS * Text.fullCharWidth;
    HealthMeter.height = Text.drawnCharHeight;
    //
    HealthMeter.bgWidth =
      HealthMeter.width +
      Text.fullCharWidth +
      Layout.textBgPaddingProportional * 3;
    HealthMeter.bgHeight =
      HealthMeter.height + Layout.textBgPaddingProportional * 2;
    //
    HealthMeter.offsetH = Layout.textBgPaddingProportional;
    HealthMeter.offsetV = 0 - Layout.textBgPaddingProportional;
    //
    HealthMeter.heartOffsetH = 0;
    HealthMeter.heartOffsetV = 0 - Math.round(Text.letterSpacing / 2);
  }

  if (Layout.currentAspectUI.healthMeter.offsetByCharsV) {
    HealthMeter.offsetV +=
      Layout.currentAspectUI.healthMeter.offsetByCharsV * Text.lineHeight;
    HealthMeter.heartOffsetV +=
      Layout.currentAspectUI.healthMeter.offsetByCharsV * Text.lineHeight;
    HealthMeter.heartOffsetV += Layout.textBgPaddingProportional * 2;
  }

  HealthMeter.pos = Layout.getAlignedPos(
    Layout.currentAspectUI.healthMeter.alignH,
    Layout.currentAspectUI.healthMeter.alignV,
    HealthMeter.bgWidth,
    HealthMeter.bgHeight,
    HealthMeter.offsetH,
    HealthMeter.offsetV
  );

  // Used to prevent fuzzy lines when thickness is an odd number
  HealthMeter.strokeFixOffset = !isEvenNumber(Text.drawnPixelSize) ? -0.5 : 0;

  __("HealthMeter.width: " + HealthMeter.width, PD.FMT_DISPLAY);
  __("HealthMeter.height: " + HealthMeter.height, PD.FMT_DISPLAY);
  __("HealthMeter.pos.x: " + HealthMeter.pos.x, PD.FMT_DISPLAY);
  __("HealthMeter.pos.y: " + HealthMeter.pos.y, PD.FMT_DISPLAY);
  __(
    "Layout.textBgPaddingProportional: " + Layout.textBgPaddingProportional,
    PD.FMT_DISPLAY
  );
};

/**
 * @function draw
 * @static
 *
 * @description
 * ##### Call other functions to update and draw the meter
 */
HealthMeter.draw = function () {
  HealthMeter.setStrokeProperties();
  HealthMeter.drawBackground();
  HealthMeter.drawInnerBar();
  HealthMeter.drawOuterStroke();
  HealthMeter.drawHeart();
};

/**
 * @function drawInnerBar
 * @static
 *
 * @description
 * ##### The inner bar
 * This is the part that changes in size (shrinks) as the player's health diminishes.
 */
HealthMeter.drawInnerBar = function () {
  var width,
    height,
    x,
    y,
    healthRemainingDecimalFraction =
      Player.health / Game.curLevelData.startHealth;

  if (Display.isLandscapeAspect) {
    width = HealthMeter.width;
    height = HealthMeter.height * healthRemainingDecimalFraction;
    y =
      HealthMeter.pos.y +
      Layout.textBgPaddingProportional +
      HealthMeter.height -
      HealthMeter.strokeFixOffset -
      height;
    x = HealthMeter.pos.x + Layout.textBgPaddingProportional;
  } else {
    width = HealthMeter.width * healthRemainingDecimalFraction;
    height = HealthMeter.height;
    x =
      HealthMeter.pos.x +
      Layout.textBgPaddingProportional +
      HealthMeter.width -
      HealthMeter.strokeFixOffset -
      width;
    y = HealthMeter.pos.y + Layout.textBgPaddingProportional;
  }

  x = Math.floor(x);
  y = Math.floor(y);
  height = Math.floor(height);
  width = Math.floor(width);

  // inner bar (changes size with health)
  // Flash after damage
  if (
    Player.damagedFramesCounter > 0 &&
    Display.flashIsOff(GAME.TEXT_FLASH_ON_SECS, GAME.TEXT_FLASH_OFF_SECS)
  ) {
    Display.ctx.fillStyle = "transparent";
  } else {
    Display.ctx.fillStyle = Display.textColorHighlight;
  }
  Display.ctx.fillRect(x, y, width, height);

  // Cap stroke
  Display.ctx.beginPath();
  if (Display.isLandscapeAspect) {
    Display.ctx.moveTo(x, y);
    Display.ctx.lineTo(x + width - Text.drawnPixelSize, y);
  } else {
    Display.ctx.moveTo(x, y);
    Display.ctx.lineTo(x, y - Text.drawnPixelSize + height);
  }
  Display.ctx.stroke();
};

/**
 * @function drawBackground
 * @static
 *
 * @description
 * ##### The translucent padded background
 */
HealthMeter.drawBackground = function () {
  Display.ctx.fillStyle = Display.overlayBgColor;
  Display.ctx.fillRect(
    HealthMeter.pos.x,
    HealthMeter.pos.y,
    HealthMeter.bgWidth,
    HealthMeter.bgHeight
  );
};

/**
 * @function drawOuterStroke
 * @static
 *
 * @description
 * ##### The main outline
 */
HealthMeter.drawOuterStroke = function () {
  Display.ctx.strokeRect(
    HealthMeter.pos.x +
      Layout.textBgPaddingProportional +
      HealthMeter.strokeFixOffset,
    HealthMeter.pos.y +
      Layout.textBgPaddingProportional +
      HealthMeter.strokeFixOffset,
    HealthMeter.width,
    HealthMeter.height
  );
  Display.ctx.stroke();
};

/**
 * @function setStrokeProperties
 * @static
 *
 * @description
 * ##### Set the thickness and colour of the stroke for drawing the outline and cap line
 */
HealthMeter.setStrokeProperties = function () {
  Display.ctx.lineWidth = Text.drawnPixelSize;
  Display.ctx.strokeStyle = Display.shadowColor;
};

/**
 * @function drawHeart
 * @static
 *
 * @description
 * ##### The heart icon
 */
HealthMeter.drawHeart = function () {
  Text.draw({
    text: STRING.HEART,
    color: Display.textColorHighlight,
    drawBackground: false,
    alignH: Layout.currentAspectUI.healthMeter.alignH,
    alignV: Layout.currentAspectUI.healthMeter.alignV,
    offsetH: HealthMeter.heartOffsetH,
    offsetV: HealthMeter.heartOffsetV,
  });
};

export { HealthMeter };