Source: ItemManager.js

/**
 * @typedef {object} ItemManager.itemObject
 * @property {string} ID - itemID
 * @property {number} Expiration - How many days until this item expires. Only decrements if it is being used
 * @property {boolean|string} Used - The ID of the girl using this item or false
 */

/**
 * The config object used for creating new items
 * @typedef {object} ItemManager.itemConfig
 * @property {string} ID - The ID needs to be the same as the texture key
 * @property {string} Name - The display name of the item
 * @property {string} Description - The description of the item put in the dialogue boxes of item menus
 * @property {number} [Cost] - How much it costs to buy this item
 * @property {number} [Sell] - How much it costs to sell this tiem
 * @property {boolean} Quest - If true, the player can not use/buy/sell this item
 * @property {boolean} Sellable - If true, the player can sell this item
 * @property {boolean} [Consumable] - If true, the item disappears after it expires or after using it if OnlyOne is false
 * @property {boolean} [OnlyOne] - If true, the item can only be used if there are no consumables in use or if the current consumable is removed
 * @property {number|'Never'} [Expiration] - How many days until the item expires, set to "Never" if it never expires
 * @property {object} [Stats] - The bonus attributes gained
 * @property {number} Stats.Recovery - The amount of stamina a girl recovers in a boss battle
 * @property {number} Stats.Stamina - The amount of extra stamina a girl will have with this item
 * @property {number} Stats.Hands - The girl's hands level increased by
 * @property {number} Stats.Feet - The girl's feet level increased by
 * @property {number} Stats.Throat - The girl's throat level increased by
 * @property {number} Stats.Tits - The girl's tits level increased by
 * @property {number} Stats.Pussy - The girl's pussy level increased by
 * @property {number} Stats.Anal - The girl's anal level increased by
 */

/**
 * @class ItemManager
 */
class ItemManager {
    constructor() {
        this._items = {
            "Lube": {
                "Name": "Lube",
                "ID": "Lube",
                "Description": "Why wait? Let those boys start fucking you faster with some lube!\nPussy level increased by 3 for 3 days.",
                "Cost": 600,
                "Sell": 200,
                "Quest": false,
                "Sellable": true,
                "Consumable": true,
                "OnlyOne": true,
                "Expiration": 3,
                "Stats": {
                    "Pussy": 3
                }
            },
            "Gloves": {
                "Name": "Latex Gloves",
                "ID": "Gloves",
                "Description": "Gloves to really grip that dick hard. Plus you won't get cum on your hands!\nHand level increased by 3 for 3 days.",
                "Cost": 600,
                "Sell": 200,
                "Quest": false,
                "Sellable": true,
                "Consumable": true,
                "OnlyOne": true,
                "Expiration": 3,
                "Stats": {
                    "Hands": 3
                }
            },
            "Lollipop": {
                "Name": "Lollipop",
                "ID": "Lollipop",
                "Description": "You know what tastes better than dick? Strawberry dick!\nThroat level increased by 3 for 3 days.",
                "Cost": 600,
                "Sell": 200,
                "Quest": false,
                "Sellable": true,
                "Consumable": true,
                "OnlyOne": true,
                "Expiration": 3,
                "Stats": {
                    "Throat": 3
                }
            },
            "AnalBeads": {
                "Name": "Anal Beads",
                "ID": "AnalBeads",
                "Description": "Keep your ass prepared for big cocks using these anal beads!!\nAnal level increased by 3 for 3 days.",
                "Cost": 600,
                "Sell": 200,
                "Quest": false,
                "Sellable": true,
                "Consumable": true,
                "OnlyOne": true,
                "Expiration": 3,
                "Stats": {
                    "Anal": 3
                }
            },
            "Alcohol": {
                "Name": "Alcohol",
                "ID": "Alcohol",
                "Description": "Tastes a little funny.\nStamina increased by 2.",
                "Cost": 2500,
                "Sell": 1000,
                "Quest": false,
                "Sellable": true,
                "Consumable": true,
                "OnlyOne": false,
                "Expiration": 1,
                "Stats": {
                    "Stamina": 2
                }
            },
            "Cum": {
                "Name": "Spurt of Cum",
                "ID": "Cum",
                "Description": "Cum harvested from clients at the brothel.\nCan be sold for 50 gold.",
                "Sell": 50,
                "Quest": true,
                "Sellable": true
            },
            "ForestDildo": {
                "Name": "Forest Dildo",
                "ID": "ForestDildo",
                "Description": "The forest beast's cock turned into a dildo after he was defeated.\nAll stats increased by 1.",
                "Quest": false,
                "Sellable": false,
                "Consumable": false,
                "Expiration": "Never",
                "Stats": {
                    "Hands": 1,
                    "Feet": 1,
                    "Throat": 1,
                    "Tits": 1,
                    "Pussy": 1,
                    "Anal": 1
                }
            },
            "MorassDildo": {
                "Name": "Morass Dildo",
                "ID": "MorassDildo",
                "Description": "The morass goblin's cock turned into a dildo after he was defeated.\nRecovery increased by 0.5.",
                "Quest": false,
                "Sellable": false,
                "Consumable": false,
                "Expiration": "Never",
                "Stats": {
                    "Recovery": 0.5
                }
            }
        }
    }

    /**
     * Returns the object file for all of the items in the game
     * @method getAllItems
     * @memberOf ItemManager
     * @instance
     * @returns {Object}
     */
    getAllItems() {
        return this._items;
    }

    /**
     * Pushes an item to the player's inventory
     * @method pushItem
     * @memberOf ItemManager
     * @instance
     * @param {string} itemID
     * @param {boolean} [silent] - If you don't want the game to notify the player
     */
    pushItem(itemID, silent) {
        gameData.items.push(new GAME.item.item(itemID));
        if (silent !== true) {
            GAME.notify('You received ' + this._items[itemID].Name + '!')
        }
    }

    /**
     * @method removeItem
     * @memberOf ItemManager
     * @instance
     * @param {string} girl - id of girl
     * @param {String} item - id of item
     * @returns Promise
     */
    removeItem(girl, item) {
        return new Promise((resolve) => {
            let usedItem = GAME.item.getUsedItem(girl, item);
            let itemConst = GAME.item.getAllItems()[item];

            if (itemConst.OnlyOne === false && itemConst.Consumable === true) {
                gameData.items.splice(gameData.items.indexOf(usedItem), 1);

                globalEvents.emit('refreshItems');

                resolve();
                return null;
            }

            for (let skill in skills) {
                if (itemConst.hasOwnProperty('Stats') === true && itemConst.Stats.hasOwnProperty(skills[skill])) {
                    gameData.character[girl].Bonus[skills[skill]] -= itemConst.Stats[skills[skill]];
                }
            }
            if (itemConst.hasOwnProperty('Stats') === true && itemConst.Stats.hasOwnProperty('Stamina')) {
                gameData.character[girl].Bonus.Stamina -= itemConst.Stats.Stamina;
                GAME.girl.loseStamina(girl, itemConst.Stats.Stamina);
            }
            if (itemConst.hasOwnProperty('Stats') === true && itemConst.Stats.hasOwnProperty('Recovery')) {
                gameData.character[girl].Bonus.Recovery -= itemConst.Stats.Recovery;
            }
            if (usedItem.Expiration === 'Never') {
                gameData.items[gameData.items.indexOf(usedItem)].Used = false;
            } else {
                gameData.items.splice(gameData.items.indexOf(usedItem), 1);
            }

            if (itemConst.Quest === true) {
                gameData.items.splice(gameData.items.indexOf(usedItem), 1);
            }

            globalEvents.emit('refreshItems');
            globalEvents.emit('refresh');
            resolve();
        });
    }

    /**
     * Does not remove items that are being used
     * @method massRemoveItem
     * @memberOf ItemManager
     * @instance
     * @param {string} itemID
     * @param {number}amount
     */
    massRemoveItem(itemID, amount) {
        amount = amount || 0;
        let itemCount = 0;

        for (let i = gameData.items.length - 1; i >= 0; i--) {
            if (itemCount >= amount) {
                break;
            }

            if (gameData.items[i].ID === itemID && gameData.items[i].Used === false) {
                gameData.items.splice(i, 1);
                itemCount += 1;
            }
        }
    }

    /**
     * @method useItem
     * @memberOf ItemManager
     * @instance
     * @param {string} girl - girlID
     * @param {string} item - itemID
     * @return {Promise<any>}
     */
    useItem(girl, item) {
        return new Promise((resolve) => {
            let unusedItem = GAME.item.getUnusedItem(item);
            let itemConst = GAME.item.getAllItems()[item];
            unusedItem.Used = girl;

            // Equip item
            for (let skill in skills) {
                if (itemConst.Stats.hasOwnProperty(skills[skill])) {
                    gameData.character[girl].Bonus[skills[skill]]
                        += itemConst.Stats[skills[skill]];
                }
            }

            // If has Stamina Bonus
            if (itemConst.Stats.hasOwnProperty('Stamina')) {
                if (itemConst.OnlyOne === true) {
                    gameData.character[girl].Bonus.Stamina += itemConst.Stats.Stamina;
                }
                GAME.girl.gainStamina(girl, itemConst.Stats.Stamina);
            }

            // If has Recovery Bonus
            if (itemConst.Stats.hasOwnProperty('Recovery')) {
                gameData.character[girl].Bonus.Recovery += itemConst.Stats.Recovery;
            }

            // Spammable items
            if (itemConst.Consumable === true && itemConst.OnlyOne === false) {
                gameData.items.splice(gameData.items.indexOf(unusedItem), 1);
            }

            globalEvents.emit('refreshItems');
            globalEvents.emit('refresh');

            resolve();
        });
    }

    /**
     * Returns the number of items with the itemID in your inventory
     * @method getNumItems
     * @memberOf ItemManager
     * @instance
     * @param {string} itemID
     * @return {number}
     */
    getNumItems(itemID) {
        let quantity = 0;
        for (let i in gameData.items) {
            if (gameData.items[i].ID === itemID && gameData.items[i].Used === false) {
                quantity += 1;
            }
        }
        return quantity;
    }

    /**
     * @method buyItem
     * @memberOf ItemManager
     * @instance
     * @param {string} shopID
     * @param {string} item
     * @param {number} quantity
     * @returns Promise<any>
     */
    buyItem(shopID, item, quantity) {
        return new Promise((resolve) => {
            for (let i = 1; i <= quantity; i++) {
                GAME.removeGold(GAME.item.getAllItems()[item].Cost);
                GAME.item.pushItem(item, true);
                gameData.shopStock[shopID][item] -= 1;
            }

            globalEvents.emit('refreshItems');
            resolve();
        })
    }

    /**
     * @method sellItem
     * @memberOf ItemManager
     * @instance
     * @param {string} item
     * @param {number} quantity
     * @returns Promise<any>
     */
    sellItem(item, quantity) {
        return new Promise((resolve) => {
            GAME.addGold(GAME.item.getAllItems()[item].Sell * quantity);
            GAME.item.massRemoveItem(item, quantity);

            globalEvents.emit('refreshItems');
            resolve();
        })
    }

    /**
     * Gets an unused item with the itemID
     * @method getUnusedItem
     * @memberOf ItemManager
     * @instance
     * @param itemID
     * @return {ItemManager.itemObject}
     */
    getUnusedItem(itemID) {
        for (let i in gameData.items) {
            if (gameData.items[i].ID === itemID && gameData.items[i].Used === false) {
                return gameData.items[i];
            }
        }
    }

    /**
     * Gets an item that is being used by a girl
     * @method getUsedItem
     * @memberOf ItemManager
     * @instance
     * @param {string} girl
     * @param {string} itemID
     * @return {ItemManager.itemObject}
     */
    getUsedItem(girl, itemID) {
        let items = GAME.item.getItems(girl);

        for (let i in items) {
            if (gameData.items[items[i]].ID === itemID) {
                return gameData.items[items[i]];
            }
        }
    }

    /**
     * Returns an array of gameData.items indices
     * @method getItems
     * @memberOf ItemManager
     * @instance
     * @param {string} [girl]
     * @return {Array<number>}
     */
    getItems(girl) {
        let array = [];
        girl = girl || false;

        for (let item in gameData.items) {
            if (gameData.items[item].Used === girl) {
                array.push(item);
            }
        }
        return array;
    }

    /**
     * Use this to create new items
     * @method newItem
     * @memberOf ItemManager
     * @instance
     * @param {ItemManager.itemConfig} config
     */
    newItem(config) {
        this._items[config.ID] = config;
    }
}

/**
 * @hideconstructor
 * @memberOf ItemManager
 * @param {string} id
 * @returns {ItemManager.itemObject}
 * @example
 * let item = new GAME.item.item('Lollipop');
 */
ItemManager.prototype.item = function (id) {
    let obj = GAME.item._items[id];
    let item = {};
    item.ID = obj.ID;

    if (obj.hasOwnProperty('Expiration')) {
        item.Expiration = obj.Expiration;
    }
    item.Used = false;

    return item;
};