Source: Backend/game/item.mjs

/* global ml */
/** @module backend/game/Item */
import fs from 'fs';
import util from 'util';

import ItemCommon from '../../Frontend/game/common/item.mjs';
import ItemModel from '../models/item.mjs';

const readFile = util.promisify(fs.readFile);

export default class Item extends ItemCommon {
  static async load(floor) {
    let rawItems = await ItemModel.findAll({
      where: {
        floorId: floor.id
      }
    });
    floor.items = [];
    let itemDef = await Item.getItemDefinitions(true);
    for(let raw of rawItems) {
      let definition = itemDef.get(raw.spriteName);
      let item = new Item(
        floor,
        definition.spriteName,
        definition.spriteSize,
        definition.movementSpeed,
        definition.attackSpeed,
        definition.attack,
        definition.defence,
        definition.range,
        raw.id,
        definition.category,
        definition.accuracy,
        definition.attackStyle
      );
      if(raw.x || raw.y) {
        item.setCoordinates(raw.x, raw.y);
      }
      floor.items.push(item);
    }
  }

  /**
   * Saves items to the database.
   * @param {Floor} floor - The floor the items are on
   */
  static async saveAll(floor) {
    for(let item of floor.items) {
      let uniqueId = item.id;
      if(typeof (item.id) === typeof ('string')) {
        uniqueId = item.id.slice(item.id.lastIndexOf('-') + 1);
      }
      let dataToSave = {
        id: `${item.floor.id}-${uniqueId}`,
        floorId: item.floor.id,
        x: item.x,
        y: item.y,
        spriteName: item.spriteName
      };
      let itemFound = await ItemModel.findOne({
        where: {
          id: dataToSave.id
        }
      });
      if(itemFound) {
        itemFound.update(dataToSave);
      } else {
        ItemModel.create(dataToSave);
      }
    }
  }

  toJSON() {
    return {
      id: this.id,
      spriteName: this.spriteName,
      spriteSize: this.spriteSize,
      movementSpeed: this.movementSpeed,
      attackSpeed: this.attackSpeed,
      attack: this.attack,
      defence: this.defence,
      range: this.range,
      x: this.x,
      y: this.y,
      isOnFloor: this.isOnFloor,
      category: this.category,
      accuracy: this.accuracy,
      attackStyle: this.attackStyle,
      maxWearTime: this.maxWearTime,
      holder: this.holder,
      holderName: this.holderName
    };
  }

  static async spawnKey(floor, x, y) {
    let itemDefs = await Item.getItemDefinitions();
    let key = itemDefs[4];
    let newItem = new Item(
      floor,
      key.spriteName,
      key.spriteSize,
      key.movementSpeed,
      key.attackSpeed,
      key.attack,
      key.defence,
      key.range,
      floor.map.numItems,
      key.category,
      key.accuracy,
      key.attackStyle
    );
    ++floor.map.numItems;
    newItem.setCoordinates(x, y);
    newItem.isOnFloor = true;
    floor.items.push(newItem);
    ml.logger.verbose(`Spawning key ${newItem.spriteName} at (${newItem.x}, ${newItem.y})`, ml.tags.item);
  }

  static async spawnRandomItem(floor, x, y) {
    let itemDefs = await Item.getItemDefinitions();
    let randomItem = itemDefs[Math.floor(Math.random() * itemDefs.length)];

    while(randomItem === itemDefs[4]) { //Don't want regular monsters to spawn keys.
      randomItem = itemDefs[Math.floor(Math.random() * itemDefs.length)];
    }

    let newItem = new Item(
      floor,
      randomItem.spriteName,
      randomItem.spriteSize,
      randomItem.movementSpeed,
      randomItem.attackSpeed,
      randomItem.attack,
      randomItem.defence,
      randomItem.range,
      floor.map.numItems,
      randomItem.category,
      randomItem.accuracy,
      randomItem.attackStyle,
      randomItem.maxWearTime
    );
    ++floor.map.numItems;
    newItem.setCoordinates(x, y);
    newItem.isOnFloor = true;
    floor.items.push(newItem);
    ml.logger.verbose(`Spawning item ${newItem.spriteName} at (${newItem.x}, ${newItem.y})`, ml.tags.item);
  }

  static async getItemDefinitions(map = false) {
    let rawDefs = await readFile('Backend/game/item-definitions/item-definitions.json', 'utf-8');
    let jsonDefs = JSON.parse(rawDefs).itemDefinitions;
    if(map) {
      let defMap = new Map();
      for(let item of jsonDefs) {
        defMap.set(item.spriteName, item);
      }
      return defMap;
    }
    return jsonDefs;
  }

  /**
   * Puts item in a state that signifies that it has been
   * placed in an inventory
   */
  pickup(holderName) {
    this.setCoordinates(null, null);
    this.isOnFloor = false;
    this.holder = true;
    this.holderName = holderName;
    this.timeWorn = Date.now();
  }
}