Skip to content
Snippets Groups Projects
main.js 12.08 KiB
import * as Phaser from "phaser";
import axios from "axios";
import path from "path";

const BACKEND_URL = new URL(process.env.BACKEND_URL);
const backendEndpointURL = (endpoint) =>
  new URL(path.join(BACKEND_URL.pathname, endpoint), BACKEND_URL);

let config = {
  type: Phaser.AUTO,
  width: 1200,
  height: 800,
  pixelArt: true,
  autoCenter: Phaser.Scale.CENTER_BOTH,
  scaleMode: Phaser.Scale.FIT,
  physics: {
    default: "arcade",
    arcade: {
      gravity: { y: 200 },
      debug: false,
    },
  },
  scene: {
    preload: preload,
    create: create, // update: updateScene
  },
};

let game = new Phaser.Game(config);

function preload() {
  this.scale.scaleMode = Phaser.ScaleModes.LINEAR;
  this.load.image("b0", "assets/background_layers/Layer_0011_0.png");
  this.load.image("b1", "assets/background_layers/Layer_0010_1.png");
  this.load.image("b2", "assets/background_layers/Layer_0009_2.png");
  this.load.image("b3", "assets/background_layers/Layer_0008_3.png");
  this.load.image("b4", "assets/background_layers/Layer_0007_Lights.png");
  this.load.image("b5", "assets/background_layers/Layer_0006_4.png");
  this.load.image("b6", "assets/background_layers/Layer_0005_5.png");
  this.load.image("b7", "assets/background_layers/Layer_0004_Lights.png");
  this.load.image("b8", "assets/background_layers/Layer_0003_6.png");
  this.load.image("b9", "assets/background_layers/Layer_0002_7.png");
  this.load.image("b10", "assets/background_layers/Layer_0001_8.png");
  this.load.image("b11", "assets/background_layers/Layer_0000_9.png");
  this.load.image("logo", "assets/background_layers/Logo.png");
  this.load.image("ground", "assets/background_layers/ground.png");

  this.load.spritesheet("oezi", "assets/sprites/player/oezi.png", {
    frameWidth: 27,
    frameHeight: 35,
  });
  this.load.spritesheet("deer", "assets/sprites/player/deer.png", {
    frameWidth: 72,
    frameHeight: 52,
  });
  this.load.spritesheet("boar", "assets/sprites/player/boar.png", {
    frameWidth: 52,
    frameHeight: 28,
  });
  this.load.spritesheet("wolf", "assets/sprites/player/wolf.png", {
    frameWidth: 54,
    frameHeight: 35,
  });
  this.load.spritesheet("bear", "assets/sprites/player/bear.png", {
    frameWidth: 60,
    frameHeight: 31,
  });
  this.load.spritesheet("spear", "assets/sprites/player/spear.png", {
    frameWidth: 31,
    frameHeight: 7,
  });
  this.load.spritesheet("spearhit", "assets/sprites/player/spearhit.png", {
    frameWidth: 14,
    frameHeight: 33,
  });
  this.load.spritesheet("hit", "assets/sprites/player/hit-sheet.png", {
    frameWidth: 469,
    frameHeight: 79,
  });
  this.load.spritesheet("miss", "assets/sprites/player/misssing-sheet.png", {
    frameWidth: 466,
    frameHeight: 76,
  });
}

let gameRunning = false;
let player;
let scene;
let imageInUse = [];
let enemyNumber = 0;
let ground;
let consecutiveMissing = 0;
let onscreenEnemies = [];

/* game params */
let enemies = ["bear", "wolf", "deer", "boar"];
let maxEnemyNumber = 4;
let enemiesSpeed = 50000;
/**************/

function create() {
  scene = this;

  // Draw background forest
  ["b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"].forEach(
    (textureKey) => {
      this.add
        .tileSprite(
          0,
          0,
          this.cameras.main.width,
          this.cameras.main.height,
          textureKey,
        )
        .setOrigin(0, 0.2)
        .setScale(1.3);
    },
  );

  // Draw foreground grass
  this.add
    .tileSprite(0, 0, this.cameras.main.width, this.cameras.main.height, "b11")
    .setAlpha(0.6)
    .setOrigin(0, -0.03);

  createAnim("player_idle", "oezi", 1, 5);
  createAnim("player_run", "oezi", 6, 13);
  createAnim("deer_run", "deer", 0, 5);
  createAnim("deer_idle", "deer", 6, 15);
  createAnim("deer_walk", "deer", 16, 23);
  createAnim("boar_run", "boar", 0, 5);
  createAnim("boar_idle", "boar", 6, 13);
  createAnim("boar_walk", "boar", 14, 22);
  createAnim("wolf_run", "wolf", 0, 5);
  createAnim("wolf_idle", "wolf", 6, 15);
  createAnim("wolf_walk", "wolf", 16, 23);
  createAnim("bear_run", "bear", 12, 16);
  createAnim("bear_idle", "bear", 0, 11);
  createAnim("bear_walk", "bear", 17, 24);
  createAnim("spearAni", "spear", 0, 3);
  createAnim("spearHitAni", "spearhit", 0, 8);
  createAnim("hit", "hit", 0, 9);
  createAnim("missing", "miss", 0, 6);

  ground = this.physics.add
    .staticImage(0, this.cameras.main.height - 25, "ground")
    .setScale(1)
    .refreshBody();
  ground.setImmovable(true);
  ground.body.allowGravity = false;

  player = this.physics.add
    .sprite(
      this.cameras.main.width + 300,
      this.cameras.main.height - 100,
      "oezi",
    )
    .setScale(3)
    .setInteractive();

  player.flipX = true;

  player.play({ key: "player_run" });

  this.physics.add.collider(player, ground);

  let tween = this.tweens.add({
    targets: player,
    x: this.cameras.main.width - 80,
    ease: "Power2",
    duration: 2000,
    onComplete: function () {
      setAnimation(player, "player_idle");
      gameRunning = true;
      gameStart(this.parent.scene);
    },
  });

  this.scale.displaySize.setAspectRatio(
    this.cameras.main.width / this.cameras.main.height,
  );
  this.scale.refresh();
  initAndBindGuessPreview(this);
}

function createAnim(key, refKey, from, to) {
  scene.anims.create({
    key: key,
    frames: scene.anims.generateFrameNumbers(refKey, { start: from, end: to }),
    frameRate: 10,
    repeat: -1,
  });
}

function gameStart(scene) {
  let waitTime = 200;
  for (let i = 0; i < maxEnemyNumber; i++) {
    setTimeout(() => {
      dispatchEnemy(scene);
    }, waitTime);
    waitTime += Math.floor(Math.random() * 4000 + 4000);
  }
}

function dispatchEnemy(scene) {
  let e = new enemy();
  onscreenEnemies.push(e);

  axios
    .post(backendEndpointURL("GetImage"), {
      sessionImages: imageInUse,
    })
    .then(function (response) {
      e.refData = response.data;
      imageInUse.push(e.refData.id);

      const imageData = `data:image/png;base64,${response.data.image}`;
      scene.textures.addBase64(`WORD-${response.data.id}`, imageData);

      scene.textures.once(
        "addtexture",
        function () {
          e.run((v) => {
            imageInUse.splice(imageInUse.indexOf(e.refData.id), 1);
            if (gameRunning) {
              setTimeout(() => {
                dispatchEnemy(scene);
              }, Math.floor(Math.random() * 10000 + 3000));
            }
          });
        },
        scene,
      );
    });
}

class enemy {
  refData = null;

  run(callback) {
    let me = this;
    enemyNumber++;
    let randomEnemyType = enemies[Math.floor(Math.random() * 4 + 0)];

    let scale = 2;
    if (randomEnemyType === "deer") {
      scale = 2.5;
    } else if (randomEnemyType === "bear") {
      scale = 3;
    }

    let flag = scene.add.sprite(400, 300, `WORD-${this.refData.id}`);
    let enemy = scene.physics.add
      .sprite(-100, scene.cameras.main.height - 100, randomEnemyType)
      .setScale(scale)
      .setInteractive();

    this.sprite = enemy;

    enemy.typeName = randomEnemyType;
    scene.physics.add.collider(enemy, ground);
    enemy.flipX = true;

    setAnimation(enemy, enemy.typeName + "_walk");
    // TODO: bring animal below grass

    enemy.movement = scene.tweens.add({
      targets: enemy,
      x: scene.cameras.main.width + 300,
      duration: enemiesSpeed,
      onComplete: function () {
        enemy.destroy();
        callback(me);
      },
      onStop: function () {},
    });

    // here to implement health
    scene.physics.add.overlap(player, enemy, (p, nemico) => {
      nemico.disableInteractive();
      nemico.body.enable = false;
      nemico.movement.pause();
      nemico.play(nemico.typeName + "_idle");
      setTimeout(() => {
        nemico.play(nemico.typeName + "_run");
        nemico.movement.stop();
        scene.tweens.add({
          targets: nemico,
          x: scene.cameras.main.width + 100,
          duration: 2000,
          onComplete: function () {
            nemico.destroy();
            callback(me);
          },
        });
      }, 3000);
    });
  }
}

function setAnimation(obj, idleKey) {
  obj.play({ key: idleKey, repeat: -1 });
}

function initAndBindGuessPreview(scene) {
  var textEntry = scene.add.text(10, scene.cameras.main.height / 2 - 32, "", {
    font: "bold 64px Courier",
    fill: "#ffffff",
  });
  scene.input.keyboard.on("keydown", function (event) {
    if (
      event.keyCode === Phaser.Input.Keyboard.KeyCodes.BACKSPACE &&
      textEntry.text.length > 0
    ) {
      textEntry.text = textEntry.text.substr(0, textEntry.text.length - 1);
    } else if (
      event.keyCode === Phaser.Input.Keyboard.KeyCodes.SPACE ||
      (event.keyCode >= Phaser.Input.Keyboard.KeyCodes.A &&
        event.keyCode <= Phaser.Input.Keyboard.KeyCodes.Z)
    ) {
      textEntry.text += event.key;
    } else if (event.keyCode === Phaser.Input.Keyboard.KeyCodes.ENTER) {
      submitTranscription(textEntry.text);
      textEntry.text = textEntry.text.substr(0, 0);
    }
  });
}

function shootSpear(enemy, hit) {
  let startPoint = new Phaser.Math.Vector2(player.x, player.y);
  let controlPoint1 = new Phaser.Math.Vector2(
    enemy.x + (player.x - enemy.x) / 2,
    scene.cameras.main.height - (player.x - enemy.x),
  );

  let endPoint = new Phaser.Math.Vector2(
    enemy.x + enemy.width * 1.6,
    scene.cameras.main.height - 60,
  );
  let message = scene.add
    .sprite(scene.cameras.main.width / 2, scene.cameras.main.height / 2)
    .setScale(1);

  // hit / missing <--- message
  if (!hit) {
    endPoint = new Phaser.Math.Vector2(-500, scene.cameras.main.height - 60);
    message.play({ key: "missing", repeat: 1 });
    message.on("animationcomplete", () => {
      message.anims.remove("miss");
      message.destroy();
    });
  } else {
    let message = scene.add
      .sprite(scene.cameras.main.width / 2, scene.cameras.main.height / 2)
      .setScale(1);
    message.play({ key: "hit", repeat: 1 });
    message.on("animationcomplete", () => {
      message.anims.remove("hit");
      message.destroy();
    });
  }

  let curve = new Phaser.Curves.QuadraticBezier(
    startPoint,
    controlPoint1,
    endPoint,
  );

  let graphics = scene.add.graphics();
  graphics.lineStyle(1, 0xff00ff, 1);

  // curve.draw(graphics); // decomment to see the trajectory

  let spear = scene.add.follower(curve);

  // console.log(player.x + " " + enemy.x);
  if (hit) {
    spear.startFollow({
      positionOnPath: true,
      duration: (scene.cameras.main.width - enemy.x) * 3,
      rotateToPath: true,
      verticalAdjust: true,
      scale: 3,
      onComplete: (x, y) => {
        console.log("done");
        let spearHitObj = scene.add
          .sprite(
            enemy.x + enemy.width * 1.6 - 10,
            scene.cameras.main.height - 60,
          )
          .setScale(2);
        setAnimation(spearHitObj, "spearHitAni");
        spear.anims.stop("spearAni");
        spear.anims.remove("spearAni");
        spear.destroy();
        setTimeout(() => {
          enemy.flipX = false;
          setAnimation(enemy, enemy.typeName + "_run");
          enemy.movement.stop();
          scene.tweens.add({
            targets: enemy,
            x: -500,
            duration: 5000,
            onComplete: function () {
              enemy.destroy();
              //callback(enemy); // ???
            },
          });
        }, 3000);
        setTimeout(() => {
          spearHitObj.destroy();
        }, 4000);
      },
    });
  } else {
    enemy.setInteractive();
    enemy.movement.resume();
    setAnimation(enemy, enemy.typeName + "_walk");
    spear.startFollow({
      positionOnPath: true,
      duration: 3000,
      rotateToPath: true,
      verticalAdjust: true,
      scale: 3,
    });
  }

  spear.scale = 2;
  spear.anims.play("spearAni");
}

function submitTranscription(transcription) {
  // TODO: guess the enemy if any
  const enemy = onscreenEnemies[0].sprite;

  let hit = true;
  enemy.disableInteractive();
  // here invoke image
  enemy.movement.pause();
  setAnimation(enemy, enemy.typeName + "_idle");

  // let beginTime = new Date().getTime();
  // let endTime = new Date().getTime();
  // let deltaTime = endTime - beginTime;

  hit = transcription == "fuffa";

  shootSpear(enemy, hit);
}