<head>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link
    href="https://fonts.googleapis.com/css2?family=Lato&family=Poppins&family=Press+Start+2P&display=swap"
    rel="stylesheet"
  />
  <style>
    * {
      font-family: "Press Start 2P", cursive;
    }

    body {
      background-color: #000000;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    h1 {
      margin: 0;
    }

    button {
      border: 0;
      cursor: pointer;
      font-size: 16px;
    }

    button:hover {
      background-color: #e0e0e0;
    }
  </style>
</head>

<div style="display: inline-block; position: relative">
  <div
    id="overlappingDiv"
    style="
      background-color: black;
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      opacity: 0;
      pointer-events: none;
      z-index: 10;
    "
  ></div>

  <canvas></canvas>

  <div id="userInterface" style="display: none">
    <!--- Health bar of Forest Spirit (opponent) -->

    <div
      style="
        background-color: white;
        width: 250px;
        position: absolute;
        top: 50px;
        left: 50px;
        border: 4px black solid;
        padding: 12px;
        border-radius: 4px;
      "
    >
      <h1 style="font-size: 16px">Forest Spirit</h1>
      <div style="position: relative">
        <div
          style="height: 5px; background-color: #ccc; margin-top: 10px"
        ></div>
        <div
          id="opponentHealthBar"
          style="
            height: 5px;
            background-color: green;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
          "
        ></div>
      </div>
    </div>

    <!--- Health bar of Raccacoonie (hobemon) -->

    <div
      style="
        background-color: white;
        width: 250px;
        position: absolute;
        top: 330px;
        right: 50px;
        border: 4px black solid;
        padding: 12px;
        border-radius: 4px;
      "
    >
      <h1 style="font-size: 16px">Raccacoonie</h1>
      <div style="position: relative">
        <div
          style="height: 5px; background-color: #ccc; margin-top: 10px"
        ></div>
        <div
          id="hobemonHealthBar"
          style="
            height: 5px;
            background-color: green;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
          "
        ></div>
      </div>
    </div>

    <div
      style="
        background-color: white;
        height: 140px;
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        border-top: 4px black solid;
        display: flex;
      "
    >
      <div
        id="dialogueBox"
        style="
          position: absolute;
          top: 0;
          right: 0;
          bottom: 0;
          left: 0;
          background-color: white;
          padding: 12px;
          display: none;
          cursor: pointer;
        "
      >
        abcd
      </div>
      <div
        id="attacksBox"
        style="
          width: 66.66%;
          display: grid;
          grid-template-columns: repeat(2, 1fr);
        "
      ></div>
      <div
        style="
          display: flex;
          align-items: center;
          justify-content: center;
          width: 33.33%;
          border-left: 4px black solid;
        "
      >
        <h1 id="attackType" style="font-size: 16px">Attack Type</h1>
      </div>
    </div>
  </div>
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js" integrity="sha512-6+YN/9o9BWrk6wSfGxQGpt3EUK6XeHi6yeHV+TYD2GR0Sj/cggRpXr1BrAQf0as6XslxomMUxXp2vIl+fv0QRA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script
    src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"
    integrity="sha512-H6cPm97FAsgIKmlBA4s774vqoN24V5gSQL4yBTDOY2su2DeXZVhQPxFK4P6GPdnZqM9fg1G3cMv5wD7e6cFLZQ=="
    crossorigin="anonymous"
    referrerpolicy="no-referrer"
  ></script>
  <script src="data/audio.js"></script>
  <script src="data/battleZonesData.js"></script>
  <script src="data/collisions.js"></script>
  <script src="data/attacks.js"></script>
  <script src="data/monsters.js"></script>
  <script src="classes.js"></script>
  <script src="index.js"></script>
  <script src="data/battleScene.js"></script>
</div>
const canvas = document.querySelector("canvas");
const c = canvas.getContext("2d");

canvas.width = 1024;
canvas.height = 576;

const collisionsMap = [];
for (let i = 0; i < collisions.length; i += 70) {
  collisionsMap.push(collisions.slice(i, 70 + i));
}

const battleZonesMap = [];
for (let i = 0; i < battleZonesData.length; i += 70) {
  battleZonesMap.push(battleZonesData.slice(i, 70 + i));
}

const boundaries = [];
const offset = {
  x: -600,
  y: -425,
};

collisionsMap.forEach((row, i) => {
  row.forEach((symbol, j) => {
    if (symbol === 1025)
      boundaries.push(
        new Boundary({
          position: {
            x: j * Boundary.width + offset.x,
            y: i * Boundary.height + offset.y,
          },
        })
      );
  });
});

const battleZones = [];

battleZonesMap.forEach((row, i) => {
  row.forEach((symbol, j) => {
    if (symbol === 1025)
      battleZones.push(
        new Boundary({
          position: {
            x: j * Boundary.width + offset.x,
            y: i * Boundary.height + offset.y,
          },
        })
      );
  });
});

const image = new Image();
image.src = "./img/Oregon City.png";

const foregroundImage = new Image();
foregroundImage.src = "./img/foregroundObjects.png";

const playerDownImage = new Image();
playerDownImage.src = "./img/playerDown.png";

const playerUpImage = new Image();
playerUpImage.src = "./img/playerUp.png";

const playerLeftImage = new Image();
playerLeftImage.src = "./img/playerLeft.png";

const playerRightImage = new Image();
playerRightImage.src = "./img/playerRight.png";

const player = new Sprite({
  position: {
    x: canvas.width / 2 - 192 / 4 / 2,
    y: canvas.height / 2 - 68 / 2,
  },
  image: playerDownImage,
  frames: {
    max: 4,
    hold: 10,
  },
  sprites: {
    up: playerUpImage,
    left: playerLeftImage,
    right: playerRightImage,
    down: playerDownImage,
  },
});

const background = new Sprite({
  position: {
    x: offset.x,
    y: offset.y,
  },
  image: image,
});

const foreground = new Sprite({
  position: {
    x: offset.x,
    y: offset.y,
  },
  image: foregroundImage,
});

const keys = {
  w: {
    pressed: false,
  },
  a: {
    pressed: false,
  },
  s: {
    pressed: false,
  },
  d: {
    pressed: false,
  },
  ArrowUp: {
    pressed: false,
  },
  ArrowLeft: {
    pressed: false,
  },
  ArrowDown: {
    pressed: false,
  },
  ArrowRight: {
    pressed: false,
  },
};

// Animate();
const movables = [background, ...boundaries, foreground, ...battleZones];

function rectangularCollision({ rectangle1, rectangle2 }) {
  return (
    rectangle1.position.x + rectangle1.width >= rectangle2.position.x &&
    rectangle1.position.x <= rectangle2.position.x + rectangle2.width &&
    rectangle1.position.y <= rectangle2.position.y + rectangle2.height &&
    rectangle1.position.y + rectangle1.height >= rectangle2.position.y
  );
}
const battle = {
  initiated: false,
};

function animate() {
  const animationId = window.requestAnimationFrame(animate);
  background.draw();
  boundaries.forEach((boundary) => {
    boundary.draw();
  });
  battleZones.forEach((battleZones) => {
    battleZones.draw();
  });
  player.draw();
  foreground.draw();

  let moving = true;
  player.animate = false;

  if (battle.initiated) return;

  // Activate a Battle

  if (
    keys.w.pressed ||
    keys.a.pressed ||
    keys.s.pressed ||
    keys.d.pressed ||
    keys.ArrowUp.pressed ||
    keys.ArrowLeft.pressed ||
    keys.ArrowDown.pressed ||
    keys.ArrowRight.pressed
  ) {
    for (let i = 0; i < battleZones.length; i++) {
      const battleZone = battleZones[i];
      const overlappingArea =
        (Math.min(
          player.position.x + player.width,
          battleZone.position.x + battleZone.width
        ) -
          Math.max(player.position.x, battleZone.position.x)) *
        (Math.min(
          player.position.y + player.height,
          battleZone.position.y + battleZone.height
        ) -
          Math.max(player.position.y, battleZone.position.y));
      if (
        rectangularCollision({
          rectangle1: player,
          rectangle2: battleZone,
        }) &&
        overlappingArea > (player.width * player.height) / 2 &&
        Math.random() < 0.01
      ) {
        // Deactivate Current Animation Loop
        window.cancelAnimationFrame(animationId);

        audio.Map.stop();
        audio.initBattle.play();
        audio.battle.play();

        battle.initiated = true;
        gsap.to("#overlappingDiv", {
          opacity: 1,
          repeat: 3,
          yoyo: true,
          duration: 0.4,
          onComplete() {
            gsap.to("#overlappingDiv", {
              opacity: 1,
              duration: 0.4,
              onComplete() {
                // Activate a new animation loop
                initBattle();
                animateBattle();
                gsap.to("#overlappingDiv", {
                  opacity: 0,
                  duration: 0.4,
                });
              },
            });
          },
        });
        break;
      }
    }
  }

  if (
    (keys.w.pressed && lastKey === "w") ||
    (keys.ArrowUp.pressed && lastKey === "ArrowUp")
  ) {
    player.animate = true;
    player.image = player.sprites.up;
    for (let i = 0; i < boundaries.length; i++) {
      const boundary = boundaries[i];
      if (
        rectangularCollision({
          rectangle1: player,
          rectangle2: {
            ...boundary,
            position: {
              x: boundary.position.x,
              y: boundary.position.y + 3,
            },
          },
        })
      ) {
        moving = false;
        break;
      }
    }
    if (moving)
      movables.forEach((movable) => {
        movable.position.y += 3;
      });
  } else if (
    (keys.a.pressed && lastKey === "a") ||
    (keys.ArrowLeft.pressed && lastKey === "ArrowLeft")
  ) {
    player.animate = true;
    player.image = player.sprites.left;
    for (let i = 0; i < boundaries.length; i++) {
      const boundary = boundaries[i];
      if (
        rectangularCollision({
          rectangle1: player,
          rectangle2: {
            ...boundary,
            position: {
              x: boundary.position.x + 3,
              y: boundary.position.y,
            },
          },
        })
      ) {
        moving = false;
        break;
      }
    }

    if (moving)
      movables.forEach((movable) => {
        movable.position.x += 3;
      });
  } else if (
    (keys.s.pressed && lastKey === "s") ||
    (keys.ArrowDown.pressed && lastKey === "ArrowDown")
  ) {
    player.animate = true;
    player.image = player.sprites.down;
    for (let i = 0; i < boundaries.length; i++) {
      const boundary = boundaries[i];
      if (
        rectangularCollision({
          rectangle1: player,
          rectangle2: {
            ...boundary,
            position: {
              x: boundary.position.x,
              y: boundary.position.y - 3,
            },
          },
        })
      ) {
        moving = false;
        break;
      }
    }
    if (moving)
      movables.forEach((movable) => {
        movable.position.y -= 3;
      });
  } else if (
    (keys.d.pressed && lastKey === "d") ||
    (keys.ArrowRight.pressed && lastKey === "ArrowRight")
  ) {
    player.animate = true;
    player.image = player.sprites.right;
    for (let i = 0; i < boundaries.length; i++) {
      const boundary = boundaries[i];
      if (
        rectangularCollision({
          rectangle1: player,
          rectangle2: {
            ...boundary,
            position: {
              x: boundary.position.x - 3,
              y: boundary.position.y,
            },
          },
        })
      ) {
        moving = false;
        break;
      }
    }
    if (moving)
      movables.forEach((movable) => {
        movable.position.x -= 3;
      });
  }
}

// Event Listener - Keydown

let lastKey = "";
window.addEventListener("keydown", (e) => {
  switch (e.key) {
    case "w": {
      keys.w.pressed = true;
      lastKey = "w";
      break;
    }
    case "a": {
      keys.a.pressed = true;
      lastKey = "a";
      break;
    }
    case "s": {
      keys.s.pressed = true;
      lastKey = "s";
      break;
    }
    case "d": {
      keys.d.pressed = true;
      lastKey = "d";
      break;
    }
    case "ArrowUp": {
      keys.ArrowUp.pressed = true;
      lastKey = "ArrowUp";
      break;
    }
    case "ArrowLeft": {
      keys.ArrowLeft.pressed = true;
      lastKey = "ArrowLeft";
      break;
    }
    case "ArrowDown": {
      keys.ArrowDown.pressed = true;
      lastKey = "ArrowDown";
      break;
    }
    case "ArrowRight": {
      keys.ArrowRight.pressed = true;
      lastKey = "ArrowRight";
      break;
    }
  }
});

// Event Listener - Keyup

window.addEventListener("keyup", (e) => {
  switch (e.key) {
    case "w": {
      keys.w.pressed = false;
      break;
    }
    case "a": {
      keys.a.pressed = false;
      break;
    }
    case "s": {
      keys.s.pressed = false;
      break;
    }
    case "d": {
      keys.d.pressed = false;
      break;
    }
    case "ArrowUp": {
      keys.ArrowUp.pressed = false;
      break;
    }
    case "ArrowLeft": {
      keys.ArrowLeft.pressed = false;
      break;
    }
    case "ArrowDown": {
      keys.ArrowDown.pressed = false;
      break;
    }
    case "ArrowRight": {
      keys.ArrowRight.pressed = false;
      break;
    }
  }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.