Source: BattleManager.js

/**
 * The BATTLE object holds all of the information for the current battle.
 * @typedef BattleManager.BATTLE
 * @property {array<ClientManager.client>} CLIENTS - Array of {@link ClientManager.client}, the first one in the array is always the one the girl is fucking
 * @property {BattleManager.GIRLS} GIRLS - Object that holds all of the girl's stats
 * @property {array<String>} GIRLS.girlArray - All of the girlIDs that are in the battle
 * @property {string} Animation - Animation ID
 * @property {number} AnimationFPS - Animation FPS
 * @property {boolean} AnimationRepeat - If true, animation will loop. Resets to false each time a client is fucked
 * @property {'Easy'|'Normal'|'Hard'} Difficulty - The difficulty set for this battle, does not effect anything unless certain attacks utilize this variable
 */

/**
 * The GIRLS variable inside {@link BattleManager.BATTLE} is unique to each girl participating in the battle.
 * @typedef BattleManager.GIRLS
 * @property {number} Ultimate - Ultimate meter
 * @property {array<BattleManager.Trap>} Traps - All of the traps on the girl
 * @property {array<BattleManager.Status>} Status - All of the status effects on the girl
 */

/**
 * @typedef BattleManager.Combo
 * @property {function} hitCombo - What happens when the player hits a combo
 * @property {function} finishCombo - What happens when the player finishes a combo
 */

/**
 * @typedef BattleManager.Trap
 * @property {string} ID - ID of trap
 * @property {number} trapLength - How long a girl should be trapped for, decrements each turn
 */

/**
 * @typedef BattleManager.Status
 * @property {string} ID - ID of status effect
 * @property {number} statusLength - How long a girl should have this status effect. If -1 then status effect will remain effective until manually removed
 */

/**
 * Each turn, an attackData variable is created which holds information for the next attack. Utilize this variable through attacks/ultimates/status effects to change what happens
 * @typedef BattleManager.attackData
 * @property {string} ID - Girl ID
 * @property {number} damage - How much damage is going to be taken by the girl this turn
 * @property {GirlManager.bodyPart} bodyPart - Body part the girl is using
 * @property {number} recovery - How much stamina a girl will recover this turn
 * @property {number} cumAmount - How much the client's cum meter should increase by
 */

/**
 * @typedef BattleManager.attackObj
 * @property {function} onRecovery - Called when the player uses the recovery button
 * @property {function} beforeAttack - Called before the attack function
 * @property {function} attack - The main attack function
 * @property {function} afterAttack - Called after the attack/status/ultimate effects and before the recovery phase
 */

/**
 * @typedef BattleManager.statusObj
 * @property {function} effect - Called after {@link BattleManager.attackObj}'s attack function for every girl that has a status effect
 */

/**
 * @typedef BattleManager.ultimateObj
 * @property {string} Description - The description that is given to the player about this ultimate ability
 * @property {function} ultimate - Called when the ultimate is used
 */

/**
 * @typedef BattleManager.comboObj
 * @property {string} ComboID - ID of Combo
 * @property {GirlManager.bodyPart} BodyPart - Body part required to continue the combo
 * @property {string|'Any'} Girl - Girl required to continue the combo or 'Any' girl
 * @property {number} Delay - How long the player has to continue the combo
 */

/**
 * @class BattleManager
 */
class BattleManager {
    constructor() {
        this._attacks = {
            "EasthollowResident": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 1800
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 900
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 900
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 700
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 700
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 600
                    }, {
                        ComboID: "exhaust",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 600
                    }];

                    return COMBO;
                },
                exhaust: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 5;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 20;
                    }
                },
                beforeAttack: function () {
                    if (gameData.quests.firstTimeLowStamina === false && GAME.girl.getStamina('Queen') < 2) {
                        return new Promise((resolve) => {
                            GAME.popUpBoard("Queen's stamina is running low! Click the \"Recover Stamina\" button to gain some stamina back!").then(() => {
                                gameData.quests.firstTimeLowStamina = true;
                                resolve();
                            })
                        })
                    }
                },
                attack: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    return new Promise((resolve) => {
                        BATTLE.Animation = girl + bodyPart + "default";
                        BATTLE.AnimationFPS = 24;
                        resolve(attackData)
                    });
                }
            },
            "GreenhavenResident": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 800
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 400
                    }];

                    return COMBO;
                },
                burst: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 2;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 20;
                    }
                }
            },
            "Goblin": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "again",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 2000
                    }, {
                        ComboID: "again",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 2000
                    }, {
                        ComboID: "again",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 2000
                    }];

                    return COMBO;
                },
                again: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 2;
                        BATTLE.AnimationFPS += 4;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 5;
                        BATTLE.AnimationFPS += 12;
                    }
                }
            },
            "AviaResident": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: 'Tits',
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: 'Tits',
                        Girl: girl,
                        Delay: 1000
                    }];

                    return COMBO;
                },
                KO: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 2;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 50;
                    }
                }
            },
            "Leon": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 2000
                    }, {
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 1000
                    }, {
                        ComboID: "KO",
                        BodyPart: bodyPart,
                        Girl: 'Any',
                        Delay: 1000
                    }];

                    return COMBO;
                },
                KO: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 5;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 20;
                    }
                },
                beforeAttack: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    if (gameData.quests.firstTimeCombo === false && client.Cum > 0) {
                        return new Promise((resolve) => {
                            GAME.dialogue.talk('', "To hit a combo, try to fuck the client when the green square is over the body part symbol!").then(() => {
                                gameData.quests.firstTimeCombo = true;
                                resolve();
                            });
                        });
                    }
                }
            },
            "Boris": {
                onRecovery: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    return new Promise((resolve) => {
                        if (BATTLE.Difficulty === 'Normal') {
                            let borisGirl = chance.pickone(BATTLE.GIRLS.girlArray);
                            let borisPart = chance.pickone(BATTLE.BODYPARTS);

                            attackData.cumAmount += 0.5;
                            attackData[borisGirl].bodyPart = borisPart;

                            if (borisPart === 'Pussy') {
                                BATTLE.Animation = "forestBeastPussy";
                                BATTLE.AnimationFPS = 24;
                            } else if (borisPart === 'Throat') {
                                BATTLE.Animation = "forestBeastThroat";
                                BATTLE.AnimationFPS = 24;
                            }

                            GAME.dialogue.talk('', "Boris doesn't want to wait. He fucks " + borisGirl + ".").then(() => {
                                resolve(attackData);
                            })
                        } else {
                            resolve(attackData);
                        }
                    });
                },
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "burst",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }];
                    return COMBO;
                },
                burst: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 1;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 3;
                    }
                },
                attack: function (BATTLE, attackData, girl, bodyPart) {
                    return new Promise((resolve) => {
                        if (bodyPart === 'Pussy') {
                            BATTLE.Animation = "forestBeastPussy";
                            BATTLE.AnimationFPS = 24;
                        } else if (bodyPart === 'Throat') {
                            BATTLE.Animation = "forestBeastThroat";
                            BATTLE.AnimationFPS = 24;
                        }

                        resolve(attackData);
                    })
                }
            },
            "WildGoblin": {
                onRecovery: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    return new Promise((resolve) => {
                        if (BATTLE.Difficulty === 'Normal' && chance.bool({likelihood: 10})) {
                            GAME.battle.addClient(BATTLE, new GAME.client.client('WildGoblin', client.Level + 1));
                        } else {
                            resolve(attackData);
                        }
                    });
                },
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 800
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 800
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 800
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 600
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 600
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 600
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 200
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 200
                    }, {
                        ComboID: "ramp",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 200
                    }];

                    return COMBO;
                },
                ramp: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 2.5;
                        BATTLE.AnimationFPS = 24;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount += 20;
                        BATTLE.AnimationFPS = 24;
                    }
                },
                attack: function (BATTLE, attackData, girl, bodyPart, COMBO) {
                    return new Promise((resolve) => {
                        if (COMBO.length > 0) {
                            attackData[girl].damage -= (attackData[girl].damage * 0.1);
                        }
                        BATTLE.Animation = 'wildGoblinFaster' + bodyPart;
                        BATTLE.AnimationFPS = 24;

                        resolve();
                    });
                }
            },
            "Peasant": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO) {
                    COMBO = [{
                        ComboID: "random",
                        BodyPart: "Tits",
                        Girl: "Any",
                        Delay: 1000
                    }, {
                        ComboID: "random",
                        BodyPart: "Tits",
                        Girl: "Any",
                        Delay: chance.integer({min: 400, max: 1000})
                    }, {
                        ComboID: "random",
                        BodyPart: "Tits",
                        Girl: "Any",
                        Delay: chance.integer({min: 400, max: 1000})
                    }, {
                        ComboID: "random",
                        BodyPart: "Tits",
                        Girl: "Any",
                        Delay: chance.integer({min: 400, max: 1000})
                    }, {
                        ComboID: "random",
                        BodyPart: "Tits",
                        Girl: "Any",
                        Delay: chance.integer({min: 400, max: 1000})
                    }];

                    return COMBO;
                },
                random: {
                    hitCombo: function (BATTLE, attackData) {
                        attackData.cumAmount += 20;
                    },
                    finishCombo: function (BATTLE, attackData) {
                        attackData.cumAmount += 100;
                    }
                },
                attack: function (BATTLE, attackData, girl) {
                    return new Promise((resolve) => {
                        BATTLE.Animation = "kingsQuestBossFuck";
                        BATTLE.AnimationFPS = 24;

                        resolve();
                    })
                }
            },
            "AviaGuard": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 800
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "titFuck",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }];

                    return COMBO;
                },
                titFuck: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        if (COMBO.length > 11) {
                            BATTLE.Animation = "kingsQuestBossFuck";
                        } else if (COMBO.length <= 11 && COMBO.length >= 6) {
                            BATTLE.Animation = "kingsQuestBossFuckMilk";
                        } else if (COMBO.length < 6) {
                            BATTLE.Animation = "kingsQuestBossSuck";
                        }
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        BATTLE.Animation = "kingsQuestBossSuck";
                        BATTLE.AnimationRepeat = true;

                        if (BATTLE.Difficulty === 'Normal') {
                            attackData.cumAmount += 5;
                        } else {
                            attackData.cumAmount += 25;
                        }
                    }
                },
                attack: function (BATTLE, attackData, girl) {
                    return new Promise((resolve) => {
                        BATTLE.Animation = "kingsQuestBossFuck";
                        BATTLE.AnimationFPS = 24;

                        if (BATTLE.Difficulty === 'Normal') {
                            if (GAME.battle.getGirlsTrap(BATTLE, girl, 'guardTrap') === false) {
                                GAME.battle.trapGirl(BATTLE, girl, 'guardTrap', 4);
                            }
                        }

                        resolve();
                    });
                }
            },
            "AviaGuard2": {
                getCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                    COMBO = [{
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 500
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 1000
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: girl,
                        Delay: 400
                    }, {
                        ComboID: "anal",
                        BodyPart: bodyPart,
                        Girl: 'Scarlett',
                        Delay: 2500
                    }];

                    return COMBO;
                },
                anal: {
                    hitCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        attackData.cumAmount = 1;
                    },
                    finishCombo: function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                        BATTLE.Animation = "aviaGuard2Cum";
                        attackData.cumAmount += 100;
                    }
                },
                attack: function (BATTLE, attackData, girl) {
                    return new Promise((resolve) => {
                        BATTLE.Animation = "aviaGuard2Fuck";
                        BATTLE.AnimationFPS = 24;
                        attackData.cumAmount = 1;
                        resolve();
                    });
                }
            }
        };

        this._statusEffects = {
            "queenUltimate": {
                effect: function (BATTLE, attackData, girl, bodyPart, client) {
                    attackData[girl].damage = Phaser.Math.RoundTo(attackData[girl].damage / 2, -1);
                    return attackData;
                }
            },
            "sukiUltimate": {
                effect: function (BATTLE, attackData, girl, bodyPart, client) {
                    attackData.cumAmount *= 2;
                    return attackData;
                }
            },
            "esxeaUltimate": {
                effect: function (BATTLE, attackData, girl, bodyPart, client) {
                    attackData[girl].damage = 0;
                    return attackData;
                }
            },
            "scarlettUltimate": {
                effect: function (BATTLE, attackData, girl, bodyPart, client) {
                    attackData[girl].recovery *= 2;
                    return attackData;
                }
            },
            "arduraUltimate": {
                effect: function (BATTLE, attackData, girl, bodyPart, client) {
                    BATTLE.CLIENTS[0].Cum = 0;
                    GAME.girl.gainStamina('Ardura', 5);
                    return attackData;
                }
            }
        };

        this._traps = {
            "borisVines": {
                ID: "vines",
                Description: "This girl is being held down by vines!"
            },
            "goblinMushroom": {
                ID: "goblinMushroom",
                Description: "This girl can't stop having an orgasm!"
            },
            "guardTrap": {
                ID: "guardTrap",
                Description: "The guards only want to fuck this girl!"
            }
        };

        this._girlUltimate = {
            "Queen": {
                Description: "All girl's damage reduced by half for the next 10 turns.",
                ultimate: function (BATTLE) {
                    for (let girl of BATTLE.GIRLS.girlArray) {
                        GAME.battle.giveStatus(BATTLE, girl, 'queenUltimate', 10);
                    }
                }
            },
            "Suki": {
                Description: "Cum % doubled for the next 5 turns.",
                ultimate: function (BATTLE) {
                    GAME.battle.giveStatus(BATTLE, 'Suki', 'sukiUltimate', 5);
                }
            },
            "Esxea": {
                Description: "All girls take 0 damage on the next 3 attacks.",
                ultimate: function (BATTLE) {
                    for (let girl of BATTLE.GIRLS.girlArray) {
                        GAME.battle.giveStatus(BATTLE, girl, 'esxeaUltimate', 3);
                    }
                }
            },
            "Scarlett": {
                Description: "All girl's recovery doubled for the next 2 turns.",
                ultimate: function (BATTLE) {
                    for (let girl of BATTLE.GIRLS.girlArray) {
                        GAME.battle.giveStatus(BATTLE, girl, 'scarlettUltimate', 2);
                    }
                }
            },
            "Ardura": {
                Description: "Reduce the client's cum % by 100%, but Ardura gains 5 stamina.",
                ultimate: function (BATTLE) {
                    GAME.battle.giveStatus(BATTLE, 'Ardura', 'arduraUltimate', 1);
                }
            }
        }
    }

    /**
     * The function called when the player clicks the fuck button during a battle. This function is called so that (this) will be {@link BattleManager.BATTLE}
     * @method fuck
     * @memberOf BattleManager
     * @instance
     * @param {string} girl - ID of girl
     * @param {GirlManager.bodyPart} bodyPart
     * @param {boolean} recovery - If player wants to recover stamina
     * @param {boolean} [hitCombo=false] - If the player successfully aligned the rectangle into the body part. Does not check if the combo was the correct body part
     * @returns {Promise<null>}
     */
    async fuck(girl, bodyPart, recovery, hitCombo) {
        hitCombo = hitCombo || false;

        this.AnimationFPS = false;
        this.AnimationRepeat = false;
        let client = this.CLIENTS[0] || null;

        // Client null
        if (client === null) {
            GAME.notify("There are no more clients!");
            return null;
        }

        // Stamina
        if (GAME.girl.getStamina(girl) <= 0 && this.GIRLS[girl].Traps.length === 0) {
            // If all other girls are trapped, then player can't switch
            if (GAME.battle.getTrappedGirls(this).length !== this.GIRLS.girlArray.length - 1) {
                GAME.notify(girl + " does not have enough stamina!");
                return null;
            }
        }

        // Get attack id using client's getAttack() function
        let attack = GAME.battle.getClientAttack(client.ID);

        let attackData = {};
        for (let i in this.GIRLS.girlArray) {
            attackData[this.GIRLS.girlArray[i]] = {
                ID: this.GIRLS.girlArray[i],
                damage: 0.3,
                recovery: GAME.girl.getRecovery(this.GIRLS.girlArray[i])
            };
            if (recovery === true) {
                attackData[this.GIRLS.girlArray[i]].damage = 0;
            }
        }

        attackData[girl].bodyPart = bodyPart;

        if (recovery === true) {
            this.CLIENTS[0].Combo = [];
            this.Animation = 'battleNullImage';
            this.AnimationFPS = 1;

            if (attack.hasOwnProperty('onRecovery') === false) {
                attack.onRecovery = function () {
                };
            }
            attackData.cumAmount = 0;
            attackData = await attack.onRecovery(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client) || attackData;
            attackData = await GAME.battle._iterateStatusEffects(this, attackData, girl, bodyPart, client.ID) || attackData;
            GAME.battle._doDamage(this, attackData, client);
            GAME.battle._recoverGirls(this, attackData, client);

            globalEvents.emit('doClient', this, attackData);
            globalEvents.emit('updateTraps', this);
            return null;
        }

        // Before Attack
        if (attack.hasOwnProperty('beforeAttack') === false) {
            attack.beforeAttack = function () {
            };
        }

        attackData = await attack.beforeAttack(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client) || attackData;

        attackData.cumAmount = 100 / GAME.battle.fuckCount(GAME.girl.getGirlLevel(girl, bodyPart), client.Level, GAME.client.getClientObj(client.ID).Difficulty);

        // Attack
        if (attack.hasOwnProperty('attack') === false) {
            attack.attack = function (BATTLE, attackData, girl, bodyPart, COMBO, client) {
                return new Promise((resolve) => {
                    BATTLE.Animation = girl + bodyPart + "default";
                    BATTLE.AnimationFPS = 24;
                    resolve(attackData)
                });
            };
        }

        attackData = await attack.attack(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client) || attackData;

        if (attack.hasOwnProperty('getCombo') === false) {
            attack.getCombo = function () {
                return [];
            }
        }

        // Check Combo
        if (GAME.battle.isComboing(this)) {
            if (this.CLIENTS[0].Combo[0].BodyPart === bodyPart && (this.CLIENTS[0].Combo[0].Girl === girl || this.CLIENTS[0].Combo[0].Girl === 'Any') && hitCombo === true) {
                attackData[girl].damage -= (attack.getCombo(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client).length - this.CLIENTS[0].Combo.length) / 100;
                if (this.CLIENTS[0].Combo.length === 1) {
                    GAME.battle.getClientCombo(client.ID, this.CLIENTS[0].Combo[0].ComboID).finishCombo(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client);
                    globalEvents.emit('finishCombo');
                } else {
                    GAME.battle.getClientCombo(client.ID, this.CLIENTS[0].Combo[0].ComboID).hitCombo(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client);
                    globalEvents.emit('hitCombo');
                }
                this.CLIENTS[0].Combo.splice(0, 1);
            } else {
                this.CLIENTS[0].Combo = [];
                globalEvents.emit('failCombo');
            }
        } else {
            this.CLIENTS[0].Combo = attack.getCombo(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client);
            globalEvents.emit('startCombo', this.CLIENTS[0].Combo);
        }

        attackData = await GAME.battle._iterateClientStatusEffects(this, attackData, girl, bodyPart, client.ID) || attackData;

        attackData = await GAME.battle._iterateStatusEffects(this, attackData, girl, bodyPart, client.ID) || attackData;

        if (GAME.girl.getStamina(girl) <= 0) {
            attackData.cumAmount = 0
        }

        GAME.girl.gainExp(girl, bodyPart, GAME.client.getClientExp(GAME.girl.getGirlLevel(girl, bodyPart), client.Level, client.ID));

        GAME.battle._doDamage(this, attackData, client);

        await GAME.battle._iterateTraps(this);

        // After Attack
        if (attack.hasOwnProperty('afterAttack') === false) {
            attack.afterAttack = function () {
            };
        }

        attackData = await attack.afterAttack(this, attackData, girl, bodyPart, this.CLIENTS[0].Combo, client) || attackData;

        if (client.Cum >= 100) {
            GAME.girl.cumOn(girl, bodyPart);
            GAME.battle.addUltimate(this, girl, 30);
            GAME.client.guyDrops(client.ID);
            GAME.girl.fuckGuys(girl, 1);
            if (GAME.client.getClientObj(client.ID).hasOwnProperty('Gold')) {
                GAME.addGold(GAME.client.getClientGold(client.ID, client.Level));
            }
        }

        GAME.battle.addUltimate(this, girl, 5);

        // Clear finished clients
        for (let i = this.CLIENTS.length - 1; i >= 0; i--) {
            if (this.CLIENTS[i].Cum >= 100) {
                this.CLIENTS.splice(i, 1);
            }
        }

        if (this.CLIENTS.length > 0) {
            selectedClient = this.CLIENTS[0];
        } else {
            selectedClient = null;
        }

        // Check Win
        if (this.CLIENTS.length === 0) {
            globalEvents.emit('winBattle', this, attackData);
            return null;
        }

        let girlsWithStamina = [];
        for (let girl of this.GIRLS.girlArray) {
            if (GAME.girl.getStamina(girl) > 0) {
                girlsWithStamina.push(girl);
            }
        }

        if (girlsWithStamina.length === 0) {
            globalEvents.emit('loseBattle', this, attackData);
            return null;
        }

        globalEvents.emit('doClient', this, attackData);
        globalEvents.emit('updateTraps', this);
    }

    /**
     * @method _recoverGirls
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {BattleManager.attackData} attackData
     * @param {ClientManager.client} client
     * @private
     */
    _recoverGirls(BATTLE, attackData, client) {
        for (let girlID of BATTLE.GIRLS.girlArray) {
            let girl = BATTLE.GIRLS[girlID];
            if (girl.Traps.length <= 0 && GAME.girl.getStamina(girlID) > 0 && attackData[girlID].damage === 0) {
                if (GAME.girl.getStamina(girlID) < GAME.girl.getMaxStamina(girlID)) {
                    let recovery = attackData[girlID].recovery;
                    recovery = (recovery * (girl.RecoverAmount / 100));

                    girl.RecoverAmount -= 4;
                    if (girl.RecoverAmount < 0) {
                        girl.RecoverAmount = 0;
                    }

                    GAME.girl.gainStamina(girlID, recovery, false);
                }
            }
        }

        client.Cum -= (client.Cum * 0.10);
    }

    /**
     * @method _doDamage
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {BattleManager.attackData} attackData
     * @param {ClientManager.client} client
     * @private
     */
    _doDamage(BATTLE, attackData, client) {
        for (let girlID of BATTLE.GIRLS.girlArray) {
            let girlDamage = attackData[girlID].damage;
            let bodyPart = attackData[girlID].bodyPart;

            if (girlDamage <= 0) {
                continue;
            }

            if (bodyPart) {
                let girlLevel = GAME.girl.getGirlLevel(girlID, bodyPart);
                let totalDamage = girlDamage - (girlDamage * (Math.round(GAME.Sigmoid(girlLevel, client.Level) * 10) / 10));
                if (totalDamage <= 0) {
                    totalDamage = 0.1;
                }
                GAME.girl.loseStamina(girlID, totalDamage);

                GAME.battle.playSound(girlID, bodyPart, totalDamage);
            }
        }

        client.Cum += attackData.cumAmount;
    }

    /**
     * @method _iterateTraps
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @private
     */
    _iterateTraps(BATTLE) {
        return new Promise((resolve) => {
            for (let girl of BATTLE.GIRLS.girlArray) {
                for (let i = BATTLE.GIRLS[girl].Traps.length - 1; i >= 0; i--) {
                    let girlTrap = BATTLE.GIRLS[girl].Traps[i];

                    if (girlTrap.trapLength !== -1) {
                        girlTrap.trapLength -= 1;

                        if (girlTrap.trapLength <= 0) {
                            GAME.battle.untrapGirl(BATTLE, girl, girlTrap.ID);
                        }
                    }
                }
            }

            resolve();
        })
    }

    /**
     * @method _iterateStatusEffects
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {BattleManager.attackData} attackData
     * @param {string} girl
     * @param {string} bodyPart
     * @param {string} clientID
     * @private
     */
    _iterateStatusEffects(BATTLE, attackData, girl, bodyPart, clientID) {
        return new Promise((resolve) => {
            for (let girlID of BATTLE.GIRLS.girlArray) {
                for (let i = BATTLE.GIRLS[girlID].Status.length - 1; i >= 0; i--) {
                    let girlStatus = BATTLE.GIRLS[girlID].Status[i];
                    let statusEffect = GAME.battle.getStatusEffect(girlStatus.ID);

                    if (statusEffect.hasOwnProperty('effect')) {
                        attackData = statusEffect.effect(BATTLE, attackData, girlID, bodyPart, clientID);
                    }

                    if (girlStatus.statusLength !== -1) {
                        girlStatus.statusLength -= 1;

                        if (girlStatus.statusLength <= 0) {
                            GAME.battle.removeStatus(BATTLE, girlID, girlStatus.ID);
                        }
                    }
                }
            }

            resolve(attackData);
        })
    }

    _iterateClientStatusEffects(BATTLE, attackData, girl, bodyPart, clientID) {
        return new Promise((resolve) => {
            let client = BATTLE.CLIENTS[0];
            for (let i = client.Status.length - 1; i >= 0; i--) {
                let clientStatus = client.Status[i];
                let statusEffect = GAME.battle.getStatusEffect(clientStatus.ID);

                if (statusEffect.hasOwnProperty('effect')) {
                    attackData = statusEffect.effect(BATTLE, attackData, girl, bodyPart, clientID);
                }

                if (clientStatus.statusLength !== -1) {
                    clientStatus.statusLength -= 1;

                    if (clientStatus.statusLength <= 0) {
                        GAME.battle.removeClientStatus(BATTLE, client, clientStatus.ID);
                    }
                }
            }

            resolve(attackData);
        });
    }

    /**
     * Returns the amount of times a girl has to fuck the client for them to cum
     * @method fuckCount
     * @memberOf BattleManager
     * @instance
     * @param {number} girlLevel
     * @param {number} clientLevel
     * @param {number} [offset=200] - Is set to be the difficulty variable in the Client's object {@link ClientManager.clientObj} when called
     * @returns {number}
     */
    fuckCount(girlLevel, clientLevel, offset) {
        offset = offset || 200;
        return Math.ceil((Math.pow(clientLevel, 2) * (offset / 3) * Math.sqrt(clientLevel)) / (10 * Math.pow(girlLevel, 2)));
    }

    /**
     * Use this to add a trap to a girl
     * @method trapGirl
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} trapID
     * @param {number} trapLength
     */
    trapGirl(BATTLE, girlID, trapID, trapLength) {
        if (GAME.battle.getGirlsTrap(BATTLE, girlID, trapID) !== false) {
            GAME.battle.untrapGirl(BATTLE, girlID, trapID);
        }

        BATTLE.GIRLS[girlID].Traps.push({
            ID: trapID,
            trapLength: trapLength + 1
        });
    }

    /**
     * Use this to untrap a girl
     * @method untrapGirl
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} trapID
     */
    untrapGirl(BATTLE, girlID, trapID) {
        for (let i = BATTLE.GIRLS[girlID].Traps.length - 1; i >= 0; i--) {
            if (BATTLE.GIRLS[girlID].Traps[i].ID === trapID) {
                BATTLE.GIRLS[girlID].Traps.splice(i, 1);
            }
        }
    }

    /**
     * Returns the given girl's trap object
     * @method getGirlsTrap
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} trapID
     * @returns {BattleManager.Trap|boolean}
     */
    getGirlsTrap(BATTLE, girlID, trapID) {
        let trap = false;

        for (let i in BATTLE.GIRLS[girlID].Traps) {
            if (BATTLE.GIRLS[girlID].Traps[i].ID === trapID) {
                trap = BATTLE.GIRLS[girlID].Traps[i];
            }
        }

        return trap;
    }

    /**
     * @method giveStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} statusID
     * @param {number} statusLength - -1 for infinite
     */
    giveStatus(BATTLE, girlID, statusID, statusLength) {
        if (GAME.battle.getGirlStatus(BATTLE, girlID, statusID) !== false) {
            GAME.battle.removeStatus(BATTLE, girlID, statusID);
        }

        BATTLE.GIRLS[girlID].Status.push({
            ID: statusID,
            statusLength: statusLength
        });
    }

    /**
     * @method giveClientStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {ClientManager.client} client
     * @param {string} statusID
     * @param {number} statusLength - -1 for infinite
     */
    giveClientStatus(BATTLE, client, statusID, statusLength) {
        if (GAME.battle.getClientStatus(BATTLE, client, statusID) !== false) {
            GAME.battle.removeClientStatus(BATTLE, client, statusID);
        }

        client.Status.push({
            ID: statusID,
            statusLength: statusLength
        });
    }

    /**
     * Removes a status effect from a girl
     * @method removeStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} statusID
     */
    removeStatus(BATTLE, girlID, statusID) {
        for (let i = BATTLE.GIRLS[girlID].Status.length - 1; i >= 0; i--) {
            if (BATTLE.GIRLS[girlID].Status[i].ID === statusID) {
                BATTLE.GIRLS[girlID].Status.splice(i, 1);
            }
        }
    }

    /**
     * @method removeClientStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {ClientManager.client} client
     * @param {string} statusID
     */
    removeClientStatus(BATTLE, client, statusID) {
        for (let i = client.Status.length - 1; i >= 0; i--) {
            if (client.Status[i].ID === statusID) {
                client.Status.splice(i, 1);
            }
        }
    }

    /**
     * Returns the given girl's status effect object
     * @method getGirlStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girlID
     * @param {string} statusID
     * @returns {BattleManager.Status|boolean}
     */
    getGirlStatus(BATTLE, girlID, statusID) {
        let status = false;

        for (let i in BATTLE.GIRLS[girlID].Status) {
            if (BATTLE.GIRLS[girlID].Status[i].ID === statusID) {
                status = BATTLE.GIRLS[girlID].Status[i];
            }
        }

        return status;
    }

    /**
     * @method getClientStatus
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {ClientManager.client} client
     * @param {string} statusID
     * @returns {BattleManager.Status|boolean}
     */
    getClientStatus(BATTLE, client, statusID) {
        let status = false;

        for (let i in client.Status) {
            if (client.Status[i].ID === statusID) {
                status = client.Status[i];
            }
        }

        return status;
    }

    /**
     * Adds ultimate meter to the girl
     * @method addUltimate
     * @memberOf GirlManager
     * @instance
     * @param {string} girl
     * @param {number} amount
     */
    addUltimate(BATTLE, girl, amount) {
        BATTLE.GIRLS[girl].Ultimate += amount;

        if (BATTLE.GIRLS[girl].Ultimate >= 100) {
            BATTLE.GIRLS[girl].Ultimate = 100;
        }

        globalEvents.emit('updateUltimate', {girl: girl, ultimate: BATTLE.GIRLS[girl].Ultimate});
    }

    /**
     * Subtracts ultimate meter to the girl
     * @method loseUltimate
     * @memberOf GirlManager
     * @instance
     * @param {string} girl
     * @param {number} amount
     */
    loseUltimate(BATTLE, girl, amount) {
        BATTLE.GIRLS[girl].Ultimate -= amount;

        if (BATTLE.GIRLS[girl].Ultimate <= 0) {
            BATTLE.GIRLS[girl].Ultimate = 0;
        }

        globalEvents.emit('updateUltimate', {girl: girl, ultimate: BATTLE.GIRLS[girl].Ultimate});
    }

    /**
     * Activates the ultimate ability for the given girl
     * @method useUltimate
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {string} girl
     */
    useUltimate(BATTLE, girl) {
        let ultimate = GAME.battle.getBattleUltimate(girl);

        ultimate.ultimate(BATTLE);
        BATTLE.GIRLS[girl].Ultimate = 0;
        globalEvents.emit('updateUltimate', {girl: girl, ultimate: 0});
    }

    /**
     * Adds more clients to the battle
     * @method addClient
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @param {ClientManager.client} clientObj
     */
    addClient(BATTLE, clientObj) {
        BATTLE.CLIENTS.unshift(clientObj);
    }

    /**
     * @method getClientAttack
     * @memberOf BattleManager
     * @instance
     * @param {string} clientID
     * @returns {BattleManager.attackObj}
     */
    getClientAttack(clientID) {
        return GAME.battle._attacks[clientID];
    }

    /**
     * @method getClientCombo
     * @memberOf BattleManager
     * @instance
     * @param {string} clientID
     * @param {string} comboID
     * @returns {BattleManager.Combo}
     */
    getClientCombo(clientID, comboID) {
        return GAME.battle._attacks[clientID][comboID];
    }

    /**
     * @method getStatusEffect
     * @memberOf BattleManager
     * @instance
     * @param {string} statusID
     * @returns {BattleManager.statusObj}
     */
    getStatusEffect(statusID) {
        return GAME.battle._statusEffects[statusID];
    }

    /**
     * @method getBattleUltimate
     * @memberOf BattleManager
     * @instance
     * @param {string} girlID
     * @returns {BattleManager.ultimateObj}
     */
    getBattleUltimate(girlID) {
        return GAME.battle._girlUltimate[girlID];
    }

    /**
     * Gets all the girls that are trapped
     * @method trappedGirls
     * @memberOf BattleManager
     * @instance
     * @param {BattleManager.BATTLE} BATTLE
     * @returns {Array<string>} - Array of girl IDs
     */
    getTrappedGirls(BATTLE) {
        let girlArray = [];

        for (let girl of BATTLE.GIRLS.girlArray) {
            if (BATTLE.GIRLS[girl].Traps.length > 0) {
                girlArray.push(girl);
            }
        }

        return girlArray;
    }

    /**
     * @method playSound
     * @memberOf BattleManager
     * @instance
     * @param {string} girlID
     * @param {GirlManager.bodyPart} bodyPart
     * @param {number} totalDamage
     */
    playSound(girlID, bodyPart, totalDamage) {
        if (totalDamage < 0.15) {
            GAME.QBsound.playSound(girlID, bodyPart, 'Slow');
        } else if (totalDamage >= 0.15 && totalDamage <= 0.25) {
            GAME.QBsound.playSound(girlID, bodyPart, 'Medium');
        } else if (totalDamage > 0.25) {
            GAME.QBsound.playSound(girlID, bodyPart, 'Fast');
        }
    }

    /**
     * Checks if the player is currently in a combo string
     * @method isComboing
     * @param {BattleManager.BATTLE} BATTLE
     * @returns {boolean}
     */
    isComboing(BATTLE) {
        if (BATTLE.CLIENTS.length > 0) {
            return BATTLE.CLIENTS[0].Combo.length > 0;
        } else {
            return false;
        }
    }
}

/**
 * Starts a battle using the given clients, girls, and difficulty
 * @method startBattle
 * @memberOf BattleManager
 * @param {array<ClientManager.client>} clientArray - Array of {@link ClientManager.client}
 * @param {array<string>} [girlArray] - Array of girl IDs who are able to fight in this battle
 * @param {array<GirlManager.bodyPart>} [bodyPartArray] - Array of body parts that will be used in this battle
 * @param {'Easy'|'Normal'|'Hard'} [difficulty] - Difficulty string
 * @returns {Promise<any>}
 */
BattleManager.prototype.startBattle = function (clientArray, girlArray, bodyPartArray, difficulty) {
    return new Promise((resolve) => {
        girlArray = girlArray || GAME.girl.getUnlocked();
        bodyPartArray = bodyPartArray || GAME.girl.getUnlockedBodyParts();
        difficulty = difficulty || 'Easy';

        let BATTLE = {
            GIRLS: {
                girlArray: girlArray
            },
            CLIENTS: clientArray,
            BODYPARTS: bodyPartArray,
            Animation: 'nullImage',
            AnimationFPS: 1,
            AnimationRepeat: false,
            Difficulty: difficulty
        };

        for (let i in girlArray) {
            BATTLE.GIRLS[girlArray[i]] = {
                RecoverAmount: 100,
                Ultimate: 0,
                Traps: [],
                Status: []
            }
        }

        game.scene.start('Battle', {pauseAllScenes: true, BATTLE});
        game.scene.getScene('Battle').events.once('shutdown', (scene, data) => {
            resolve(data.answer);
        })
    });
};