Source: ClientManager.js

/**
 * @typedef {object} ClientManager.clientObject
 * @property {string} ID - client id
 * @property {number} expire - How many days left until this client expires
 * @property {boolean} fucked - If a girl has fucked this client. Fucked clients do not appear in the brothel and are removed after sleeping
 */

/**
 * @class ClientManager
 */
class ClientManager {
    constructor() {
        this._clients = {
            "EasthollowResident": {
                "Tint": 0xFFFFCC,
                "Special": false,
                "Sort": 0,
                "Name": "Easthollow Resident",
                "ID": "EasthollowResident",
                "Region": "Easthollow",
                "Level": 1,
                "EXP": 10,
                "Gold": 12,
                "Stamina": 1,
                "Length": 2,
                "MaxLength": 8
            },
            "GreenhavenResident": {
                "Tint": 0x86583b,
                "Special": false,
                "Sort": 3,
                "Name": "Greenhaven Resident",
                "ID": "GreenhavenResident",
                "Region": "Greenhaven",
                "Level": 5,
                "EXP": 15,
                "Gold": 15,
                "Stamina": 1,
                "Length": 2,
                "MaxLength": 6
            },
            "Goblin": {
                "Tint": 0x337829,
                "Special": false,
                "Sort": 6,
                "Name": "Goblin",
                "ID": "Goblin",
                "Region": "MoaningMorass",
                "Level": 10,
                "EXP": 30,
                "Gold": 20,
                "Stamina": 1,
                "Length": 2,
                "MaxLength": 6
            },
            "AviaResident": {
                "Tint": 0xa2ffec,
                "Special": false,
                "Sort": 7,
                "Name": "Avia Resident",
                "ID": "AviaResident",
                "Region": "Avia",
                "Level": 15,
                "EXP": 40,
                "Gold": 30,
                "Stamina": 1,
                "Length": 2,
                "MaxLength": 6
            },
            "Leon": {
                "Tint": 0xffa150,
                "Special": true,
                "Sort": 1,
                "Name": "Leon",
                "ID": "Leon",
                "Region": "Easthollow",
                "Level": 3,
                "EXP": 25,
                "Gold": 20,
                "Stamina": 1,
                "Chance": 30
            },
            "Geoff": {
                "Tint": 0x35F26D,
                "Special": true,
                "Sort": 2,
                "Name": "Geoff",
                "ID": "Geoff",
                "Region": "Easthollow",
                "Level": 5,
                "EXP": 40,
                "Gold": 25,
                "Stamina": 1,
                "Chance": 25
            },
            "Principal": {
                "Tint": 0xCC77A0,
                "Special": true,
                "Sort": 4,
                "Name": "Principal",
                "ID": "Principal",
                "Region": "Easthollow",
                "Level": 7,
                "EXP": 64,
                "Gold": 30,
                "Stamina": 1,
                "Chance": 20
            },
            "Darrak": {
                "Tint": 0xAB3F20,
                "Special": true,
                "Sort": 5,
                "Name": "Darrak",
                "ID": "Darrak",
                "Region": "Greenhaven",
                "Level": 12,
                "EXP": 100,
                "Gold": 33,
                "Stamina": 2,
                "Chance": 10
            }
        }
    }

    /**
     * Returns the object list of all clients
     * @method getClientObj
     * @memberOf ClientManager
     * @instance
     * @returns {Object<client>}
     */
    getClientObj() {
        return this._clients;
    }

    /**
     * Creates and pushes a new client to the game data
     * @method pushClient
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     */
    pushClient(clientID) {
        gameData.clients.push(new GAME.client.client(clientID));
    }

    /**
     * @method doClient
     * @memberOf ClientManager
     * @instance
     * @param {string} girl - id of girl
     * @param {string} client - id of client
     * @param {GirlManager.bodyPart} bodyPart
     * @return {Promise<any>}
     */
    doClient(girl, client, bodyPart) {
        return new Promise((resolve) => {
            if (chance.bool({likelihood: GAME.client.getClientChance(girl, client, bodyPart)})) {
                gameData.clients[GAME.client.getClientsByID(client)[0]].fucked = true;

                gameData.character[girl].GuysFucked += 1;
                GAME.girl.loseStamina(girl, GAME.client.getClientObj()[client].Stamina);
                GAME.addGold(GAME.client.getClientObj()[client].Gold);
                GAME.girl.gainExp(girl, bodyPart, GAME.client.getClientExp(client));
                GAME.girl.girlPassive(girl, client, bodyPart);
                GAME.client.guyDrops(client);

                if (GAME.client.getClientObj()[client].Special === false) {
                    gameData.client[client].ClientToday = true;
                    gameData.client[client].TimesDone += 1;
                } else {
                    gameData.client[client].TimesDone += 1;
                }

                globalEvents.emit('doClient', client);

                if (GAME.client.getClientsByID(client).length > 0) {
                    selectedClient = client;
                } else {
                    selectedClient = null;
                }

                resolve();
            } else {
                GAME.girl.loseStamina(girl, GAME.client.getClientObj()[client].Stamina);
                resolve(girl + " couldn't satisfy " + GAME.client.getClientObj()[client].Name);
            }
        });
    }

    /**
     * @method getClientsByID
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     * @returns {Array}
     */
    getClientsByID(clientID) {
        let clientArray = [];
        for (let i in gameData.clients) {
            if (gameData.clients[i].fucked === false && gameData.clients[i].ID === clientID) {
                clientArray.push(i);
            }
        }
        return clientArray;
    }

    /**
     * @method getClientChance
     * @memberOf ClientManager
     * @instance
     * @param {string} girl
     * @param {string} client
     * @param {GirlManager.bodyPart} bodyPart
     */
    getClientChance(girl, client, bodyPart) {
        if (GAME.girl.getGirlLevel(girl, bodyPart) >= GAME.client.getClientObj()[client].Level) {
            return 100;
        }
        return Math.floor(GAME.Sigmoid(GAME.girl.getGirlLevel(girl, bodyPart), GAME.client.getClientObj()[client].Level, -12, 5) * 100);
    }

    /**
     * @method guyDrops
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     */
    guyDrops(clientID) {
        switch (clientID) {
            case 'Leon':
                if (chance.bool({likelihood: 1}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves']));
                }
                break;
            case 'GreenhavenResident':
                if (chance.bool({likelihood: 1}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves']));
                }
                break;
            case 'Principal':
                if (chance.bool({likelihood: 2}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves']));
                }
                break;
            case 'Goblin':
                if (chance.bool({likelihood: 2}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves', 'Alcohol']));
                }
                break;
            case 'Geoff':
                if (chance.bool({likelihood: 5}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves']));
                }
                break;
            case 'Darrak':
                if (chance.bool({likelihood: 5}) === true) {
                    dropItem(chance.pickone(['Lube', 'Lollipop', 'Gloves', 'Alcohol']));
                }
                break;
            default:
                break;
        }

        if (chance.bool({likelihood: 10}) === true) {
            dropItem('Cum');
        }

        function dropItem(itemID) {
            GAME.item.pushItem(itemID, true);
            GAME.notify(GAME.client.getClientObj()[clientID].Name + " dropped " + GAME.item.getAllItems()[itemID].Name + "!");
        }
    }

    /**
     * Returns an array of available client indexes
     * @method getClients
     * @memberOf ClientManager
     * @instance
     * @return {Array}
     */
    getClients() {
        let clientArray = [];
        for (let i in gameData.clients) {
            if (gameData.clients[i].fucked === false) {
                // Check Special
                if (currentGuySort.Special === false && GAME.client.getClientObj()[gameData.clients[i].ID].Special === true) {
                    continue;
                }

                // Check Region
                if (currentGuySort[GAME.client.getClientObj()[gameData.clients[i].ID].Region] === true) {
                    clientArray.push(i);
                }
            }
        }

        return clientArray;
    }

    /**
     * Sorts the clients by their sort number
     * @method sortClients
     * @memberOf ClientManager
     * @instance
     * @return {Promise<any>}
     */
    sortClients() {
        return new Promise((resolve) => {
            gameData.clients.sort(function (a, b) {
                let nameA = GAME.client.getClientObj()[a.ID].Sort;
                let nameB = GAME.client.getClientObj()[b.ID].Sort;

                if (nameA > nameB) {
                    return -1;
                }
                if (nameA < nameB) {
                    return 1;
                }

            });
            resolve();
        })
    }


    /**
     * Unlocks the client and pushes the client once.
     * @method unlockClient
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     */
    unlockClient(clientID) {
        gameData.regions[GAME.client.getClientObj()[clientID].Region] = true;
        gameData.client[clientID].Unlocked = true;
        GAME.client.pushClient(clientID);
    }

    /**
     * Returns the amount of exp a client will give
     * @method getClientExp
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     * @return {number}
     */
    getClientExp(clientID) {
        let expBoost = 1;
        switch (GAME.client.getClientObj()[clientID].Region) {
            case 'Easthollow':
                expBoost = GAME.buildingStats('Gloryhole').EXP / 100;
                break;
            case 'Greenhaven':
                expBoost = GAME.buildingStats('TreeHouse').EXP / 100;
                break;
            case 'MoaningMorass':
                expBoost = GAME.buildingStats('Mudpot').EXP / 100;
                break;
            case 'Avia':
                expBoost = GAME.buildingStats('Bukkake').EXP / 100;
                break;
            default:
                expBoost = 1;
        }
        return Math.floor(GAME.client.getClientObj()[clientID].EXP * expBoost);
    }

    /**
     * Increases the amount of times a client has been fucked by the given amount. All clients have a counter for how many times they have been fucked in the brothel. If you need to increment it manually, you can use this method.
     * @method increaseClientFucks
     * @memberOf ClientManager
     * @instance
     * @param {string} clientID
     * @param {number} amount
     */
    increaseClientFucks(clientID, amount) {
        gameData.client[clientID].TimesDone += amount;
    }
}


/**
 * Constructor for creating clients.
 * @hideconstructor
 * @memberOf ClientManager
 * @param {string} clientID
 * @returns {ClientManager.clientObject}
 * @example
 * let client = new GAME.client.client('Leon');
 */
ClientManager.prototype.client = function (clientID) {
    let tempClient = GAME.client._clients[clientID];

    if (tempClient.hasOwnProperty('Expiration')) {
        this.expire = tempClient.Expiration;
    } else {
        this.expire = 1;
    }

    this.ID = GAME.client._clients[clientID].ID;

    this.fucked = false;

    return {
        expire: this.expire,
        ID: this.ID,
        fucked: this.fucked
    };
};