/*
/*
 * ==============================================================================
 * ** Victor Engine MV - Cooperation Skills
 * ------------------------------------------------------------------------------
 *  CooperationSkills.js
 * ==============================================================================
 */
 
var Imported = Imported || {};
Imported['VE - Cooperation Skills'] = '1.00';
 
var VictorEngine = VictorEngine || {};
VictorEngine.CooperationSkills = VictorEngine.CooperationSkills || {};
 
(function() {
 
    VictorEngine.CooperationSkills.loadDatabase = DataManager.loadDatabase;
    DataManager.loadDatabase = function() {
        VictorEngine.CooperationSkills.loadDatabase.call(this);
        PluginManager.requiredPlugin.call(PluginManager, 'VE - Cooperation Skills', 'VE - Basic Module', '1.22');
        PluginManager.requiredPlugin.call(PluginManager, 'VE - Cooperation Skills', 'VE - Skip Battle Log');
    };
 
    VictorEngine.CooperationSkills.requiredPlugin = PluginManager.requiredPlugin;
    PluginManager.requiredPlugin = function(name, required, version) {
        if (!VictorEngine.BasicModule) {
            var msg = '插件 ' + name + ' 需要基础插件 ' + required;
            msg += ' 版本' + version + ' 或以上才能生效。';
            msg += ' 去 [url]http://victorenginescripts.wordpress.com/[/url] 下载插件。';
            throw new Error(msg);
        } else if (Imported.YEP_BattleEngineCore) {
            var msg = '插件 ' + name + " 与";
            msg += ' 插件 YEP Battle Engine Core不兼容。';
            throw new Error(msg);
        } else {
            VictorEngine.CooperationSkills.requiredPlugin.call(this, name, required, version);
        };
    };
 
})();
 
/*:
 * ==============================================================================
 * @plugindesc v1.00 - 合作技 
 * 战斗者可以一同做出行动。
 * @author Victor Sant
 *
 * ==============================================================================
 * @help 
 * ==============================================================================
 * 角色、职业、敌人、武器、防具和状态注释:
 * ==============================================================================
 *
 * ==============================================================================
 *  联合技(技能注释)
 * ------------------------------------------------------------------------------
 *  <unite skill: X[, X...]>
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  特定的角色一同发动该技能。
 *    x: 角色ID
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  例.: <unite skill: 1, 2>
 *       <unite skill: 3, 6, 4>
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  注:
 *  - 你放置id的顺序与伤害公式相关,如果你使用插件'VE -战斗动作'的动
 *    作序列。
 * ==============================================================================
 *
 * ==============================================================================
 *  融合技 (技能注释)
 * ------------------------------------------------------------------------------
 *  <fusion skill: X[, X...]>
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  当战斗人员使用同一回合列出的特定技能时,他们将加入执行该技能。
 *    x : 技能ID
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  例: <fusion skill: 10, 12>
 *       <fusion skill: 8, 8, 8>
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  NOTES:
 *  - 你放置id的顺序与伤害公式相关,如果你使用插件'VE -战斗动作'的动
 *    作序列。
 *  - 多次使用相同的id意味着不止一名战士必须使用相同的技能。
 * ==============================================================================
 *
 * ==============================================================================
 *  组合技 (技能注释)
 * ------------------------------------------------------------------------------
 *  <combination skill: X>
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  要执行此技能,必须有多个战士在同一回合使用此技能。
 *    x : 所需的战斗者数量
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  例.: <combination skill: 3>
 * ==============================================================================
 *
 * ==============================================================================
 * 另外的信息:
 * ------------------------------------------------------------------------------
 *  
 *  合作技设置是在回合开始时完成的,即在所有输入完成之后。所以,如
 *  果作为组合的一部分的战斗人员无法使用该技能,那么该组合就会失败,
 *  所有参与其中的战斗人员都将失去该回合。
 *
 *  组合行动的时间是基于最慢的战斗者所涉及的速度。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * 
 *  - 联合技
 *  “联合类型”要求所有角色都有技能学习和可用。当您为第一个角色选择技
 *  能时,所需的所有其他角色将自动选择相同的技能。统一技能只适用于角
 *  色。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * 
 *  - 融合技
 *  “融合类型”是基于所使用的技能,如果所有所需的技能都被选中,使用这
 *  些技能的战斗人员的行动将被融合技能取代。动作是在所有命令被选择后
 *  ,在回合开始前设置的。战斗人员不需要学习技能就能使用。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  - 组合技
 *  “组合类型”是基于使用该技能的战斗人员的数量。如果达到了所需的战斗
 *  人员数量,就会使用该技能。不同于联合技能,每个战士单独选择技能。
 *
 * ==============================================================================
 *
 * ==============================================================================
 *  合作技和伤害公式:
 * ------------------------------------------------------------------------------
 *  当使用动作时,只会考虑当前激活的战斗者(即调用合作技时激活的战斗
 *  者)的参数。若想在伤害公式中使用其他战斗者的参数,你可以使用 cp[X] 
 *  来引用参数,其中X是合作注释中战斗者的位置(对于联合技来说基于
 *  角色的ID,对于融合技来说基于战斗者所使用的技能ID,对于组合技来
 *  说基于选择动作的顺序)
 *
 *  例如,设置了以下注释:
 *  <unite skill: 3, 6, 4>
 *  - 使用cp[1]来引用第一个角色(角色#3)
 *  - 使用cp[2]来引用第二个角色(角色#6)
 *  - 使用cp[3]来引用第二个角色(角色#4)
 *  
 *  例如,伤害公式可以这样写:
 *  (cp[1].atk + cp[2].atk + cp[3].atk) * 4 - b.def * 2
 *  
 * ==============================================================================
 *
 * ===============================================================================
 *  合作技与即时战斗:
 * -------------------------------------------------------------------------------
 *  如果使用插件'VE - Active Time Battle',只有联合类型的合作技会
 *  生效。还要注意的是,所有必要的战斗都必须让ATB栏是满的,如
 *  果你使用“满等待”和“半等待”的更新模式,可能会更难设置组合,
 *  因为在选择动作时ATB会被冻结。因此,建议只使用“半激活”和
 *  “完全激活”模式。
 
 * ==============================================================================
 *
 * ===============================================================================
 *  合作技和战斗动作:
 * -------------------------------------------------------------------------------
 *  If using the plugin 'VE - Battle Motions', you can use new [subject] values
 *  of the motions for cooperation skills.
 *
 * ------------------------------------------------------------------------------
 *  Here are all possible [subjects] values:
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 *   cooperation X   : the Xth battler participating on the cooperation skill.
 *   all cooperation : all battlers participating on the cooperation skill.
 * ------------------------------------------------------------------------------
 *
 * -----------------------------------------------------------------------------
 *  - Motion Subjects: Combination X
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 *  This subject value will choose one of the combination battlers, based on the
 *  type of cooperation and the order of the values on the notetag (similar to
 *  how the damage formula choose the battlers)
 *  
 *  For example. you have the following notetag:
 *  <unite skill: 3, 6, 4>
 *  if you use motion 'move: cooperation 2, close to target, 12, 80;'
 *  the second cooperation member (the actor Id 6) will move close to the target
 * ==============================================================================
 *
 * ==============================================================================
 * 兼容性:
 * ------------------------------------------------------------------------------
 * To be used together with this plugin, the following plugins must be placed
 * bellow this plugin:
 *    VE - Skip Battle Log
 * ==============================================================================
 * 
 * ==============================================================================
 *  版本历史:
 * ------------------------------------------------------------------------------
 *  v 1.00 - 2016.07.20 > First release.
 * ==============================================================================
 */
 
(function() {
 
    //=============================================================================
    // Parameters
    //=============================================================================
 
    if (Imported['VE - Basic Module']) {
        var parameters = VictorEngine.getPluginParameters();
        VictorEngine.Parameters = VictorEngine.Parameters || {};
        VictorEngine.Parameters.CooperationSkills = {};
        VictorEngine.Parameters.CooperationSkills.PluginParameter = Number(parameters["Plugin Parameter"]) || 0;
        VictorEngine.Parameters.CooperationSkills.PluginParameter = String(parameters["Plugin Parameter"]).trim();
        VictorEngine.Parameters.CooperationSkills.PluginParameter = eval(parameters["Plugin Parameter"]);
    }
 
    //=============================================================================
    // VictorEngine
    //=============================================================================
 
    VictorEngine.CooperationSkills.loadNotetagsValues = VictorEngine.loadNotetagsValues;
    VictorEngine.loadNotetagsValues = function(data, index) {
        VictorEngine.CooperationSkills.loadNotetagsValues.call(this, data, index);
        if (this.objectSelection(index, ['skill'])) {
            VictorEngine.CooperationSkills.loadNotes(data);
        }
    };
 
    VictorEngine.CooperationSkills.loadNotes = function(data) {
        data.cooperationSkill = data.cooperationSkill || {};
        this.processNotes(data);
    };
 
    VictorEngine.CooperationSkills.processNotes = function(data, type) {
        var match;
        var regex1 = new RegExp('<unite skill:[ ]*((?:\\d+[ ]*,?[ ]*)+)[ ]*>', 'gi');
        var regex2 = new RegExp('<fusion skill:[ ]*((?:\\d+[ ]*,?[ ]*)+)[ ]*>', 'gi');
        var regex3 = new RegExp('<combination skill:[ ]*(\\d+)[ ]*>', 'gi');
        while (match = regex1.exec(data.note)) {
            data.cooperationSkill.unite = this.processValues(data, match);
        };
        while (match = regex2.exec(data.note)) {
            data.cooperationSkill.fusion = this.processValues(data, match);
        };
        while (match = regex3.exec(data.note)) {
            data.cooperationSkill.combination = Number(match[1]);
        };
        var skill = data.cooperationSkill;
        var isUnite = skill.unite && skill.unite.length > 1;
        var isFusion = skill.fusion && skill.fusion.length > 1;
        var isCombination = skill.combination && skill.combination > 1;
        data.isCooperation = isUnite || isFusion || isCombination;
    };
 
    VictorEngine.CooperationSkills.processValues = function(data, match) {
        return match[1].split(/[ ]*,[ ]*/gi).map(function(value) {
            return Number(value);
        });
    };
 
    //=============================================================================
    // BattleManager
    //=============================================================================
 
    VictorEngine.CooperationSkills.startTurn = BattleManager.startTurn;
    BattleManager.startTurn = function() {
        if (!Imported['VE - Active Time Battle']) {
            this.prepareCombinationActions();
        }
        VictorEngine.CooperationSkills.startTurn.call(this);
    };
 
    VictorEngine.CooperationSkills.startAction = BattleManager.startAction;
    BattleManager.startAction = function() {
        if (Imported['VE - Active Time Battle']) {
            this.setupUnionSkills();
        }
        VictorEngine.CooperationSkills.startAction.call(this);
    };
 
    BattleManager.prepareCombinationActions = function() {
        this.setupUnionSkills();
        this.setupFusionSkills();
        this.setupCombinationSkills();
    };
 
    BattleManager.setupUnionSkills = function() {
        $gameParty.members().forEach(function(member) {
            if (member.uniteLeaderSkill()) {
                member.setupUniteBaltters();
            }
        });
    };
 
    BattleManager.setupFusionSkills = function() {
        for (var i = 1; i < $dataSkills.length; i++) {
            var skill = $dataSkills[i]
            if (skill.cooperationSkill.fusion) {
                this.setupFusionBattlers(skill, $gameParty.members());
                this.setupFusionBattlers(skill, $gameTroop.members());
            }
        };
        this.allBattleMembers().forEach(function(member) {
            if (member.fusionLeaderSkill()) {
                member.setupFusionBaltters();
            }
        });
    };
 
    BattleManager.setupCombinationSkills = function() {
        for (var i = 1; i < $dataSkills.length; i++) {
            var skill = $dataSkills[i]
            if (skill.cooperationSkill.combination) {
                this.setupCombinationBattlers(skill, $gameParty.members());
                this.setupCombinationBattlers(skill, $gameTroop.members());
            }
        };
        this.allBattleMembers().forEach(function(member) {
            if (member.combinationLeaderSkill()) {
                member.setupCombinationBaltters();
            }
        });
    };
 
    BattleManager.setupFusionBattlers = function(skill, members) {
        for (var i = 0; i < members.length; i++) {
            var fusion = this.getFusionList(skill, members);
            if (fusion.length === skill.cooperationSkill.fusion.length) {
                fusion[0].member.setFusionLeader(skill, fusion[0].id);
                for (var j = 1; j < fusion.length; j++) {
                    fusion[j].member.setFusionMember(fusion[0].member, fusion[j].id);
                };
            }
        }
    };
 
    BattleManager.getFusionList = function(skill, members) {
        var fusion = [];
        var skills = skill.cooperationSkill.fusion;
        for (var i = 0; i < skills.length; i++) {
            var id = skills[i];
            var member = this.getFusionMember(members, fusion, id);
            if (member) {
                fusion.push({
                    member: member,
                    id: id
                });
            }
        };
        return fusion;
    };
 
    BattleManager.getFusionMember = function(members, fusion, id) {
        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            if (this.notFusionMember(fusion, member) && member.isUsingFusion(id)) {
                return member;
            }
        };
        return null;
    };
 
    BattleManager.notFusionMember = function(fusions, member) {
        return fusions.every(function(fusion) {
            return fusion.member !== member;
        })
    };
 
    BattleManager.setupCombinationBattlers = function(skill, members) {
        for (var i = 0; i < members.length; i++) {
            var combination = this.getCombinationList(skill, members);
            if (combination.length === skill.cooperationSkill.combination) {
                combination[0].setCombinationLeader(skill);
                for (var j = 1; j < combination.length; j++) {
                    combination[j].setCombinationMember(combination[0]);
                };
            }
        };
    };
 
    BattleManager.getCombinationList = function(skill, members) {
        var combination = [];
        var skills = skill.cooperationSkill.combination;
        for (var i = 0; i < skills; i++) {
            var member = this.getCombinationMember(members, combination, skill.id);
            if (member) {
                combination.push(member);
            }
        };
        return combination;
    };
 
    BattleManager.getCombinationMember = function(members, combination, id) {
        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            if (!combination.contains(member) && member.isUsingCombination(id)) {
                return member;
            }
        };
        return null;
    };
 
    //=============================================================================
    // Game_Action
    //=============================================================================
 
    /* Overwritten function */
    Game_Action.prototype.evalDamageFormula = function(target) {
        try {
            var item = this.item();
            var a = this.subject();
            var b = target;
            var v = $gameVariables._data;
            var cp = this.cooperationBattlersFormula();
            var formula = VictorEngine.getDamageFormula(this);
            var sign = ([3, 4].contains(item.damage.type) ? -1 : 1);
            return (Math.max(eval(formula), 0) * sign) || 0;
        } catch (e) {
            return 0;
        }
    };
 
    VictorEngine.CooperationSkills.isValid = Game_Action.prototype.isValid;
    Game_Action.prototype.isValid = function() {
        return VictorEngine.CooperationSkills.isValid.call(this) && !this.subject().cooperationMember() &&
            this.isCombinationOk();
    };
 
    Game_Action.prototype.setCooperationBattlers = function() {
        this._cooperationBattlers = this.cooperationActionBattlers();
    };
 
    Game_Action.prototype.cooperationBattlers = function() {
        return this._cooperationBattlers || [];
    };
 
    Game_Action.prototype.cooperationBattlersFormula = function() {
        return this.isCooperation() ? [{}].concat(this.cooperationBattlers()) : [];
    };
 
    Game_Action.prototype.isCooperation = function() {
        return this.item() && this.item().isCooperation;
    };
 
    Game_Action.prototype.isUniteAction = function() {
        return this.isCooperation() && this.item().cooperationSkill.unite;
    };
 
    Game_Action.prototype.isFusionAction = function() {
        return this.isCooperation() && this.item().cooperationSkill.fusion;
    };
 
    Game_Action.prototype.isCombinationAction = function() {
        return this.isCooperation() && this.item().cooperationSkill.combination;
    };
 
    Game_Action.prototype.cooperationActionBattlers = function() {
        if (this.isUniteAction()) {
            return this.uniteActionBattlers();
        } else if (this.isFusionAction()) {
            return this.fusionActionBattlers();
        } else if (this.isCombinationAction()) {
            return this.combinationActionBattlers();
        }
        return [];
    };
 
    Game_Action.prototype.cooperationLength = function() {
        if (this.isUniteAction()) {
            return this.item().cooperationSkill.unite.length;
        } else if (this.isFusionAction()) {
            return this.item().cooperationSkill.fusion.length;
        } else if (this.isCombinationAction()) {
            return this.item().cooperationSkill.combination;
        }
        return 0;
    };
 
    Game_Action.prototype.uniteActionBattlers = function() {
        return this.item().cooperationSkill.unite.map(function(id) {
            return $gameActors.actor(id);
        });
    };
 
    Game_Action.prototype.fusionActionBattlers = function() {
        var object = this;
        var leader = this.subject();
        var skills = this.item().cooperationSkill.fusion;
        var fusion = [leader];
        for (var i = 1; i < skills.length; i++) {
            var id = skills[i];
            var member = this.getFusionBattler(leader, id, fusion);
            if (member) {
                fusion.push(member);
            }
        }
        return fusion;
    };
 
    Game_Action.prototype.getFusionBattler = function(leader, id, fusion) {
        var members = BattleManager.allBattleMembers();
        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            if (member.isOriginalFusionSkill(id) && member.fusionLeader() === leader &&
                !fusion.contains(member)) {
                return member;
            }
        }
        return null;
    };
 
    Game_Action.prototype.combinationActionBattlers = function() {
        var object = this;
        var leader = this.subject();
        var combination = [leader];
        for (var i = 1; i < this.item().cooperationSkill.combination; i++) {
            var member = this.getCombinationBattler(leader, combination);
            if (member) {
                combination.push(member);
            }
        }
        return combination;
    };
 
    Game_Action.prototype.getCombinationBattler = function(leader, combination) {
        var members = BattleManager.allBattleMembers();
        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            if (member.combinationLeader() === leader && !combination.contains(member)) {
                return member;
            }
        }
        return null;
    };
 
    Game_Action.prototype.isCombinationOk = function() {
        if (this.isCombinationAction()) {
            if (this.subject().combinationLeaderSkill()) {
                return this.combinationMembersOk();
            } else {
                return false;
            }
        } else {
            return true;
        };
    };
 
    Game_Action.prototype.combinationMembersOk = function() {
        var skill = this.item();
        return this.cooperationBattlers().every(function(member) {
            return member.canUse(skill);
        })
    };
 
    //=============================================================================
    // Game_BattlerBase
    //=============================================================================
 
    VictorEngine.CooperationSkills.paySkillCost = Game_BattlerBase.prototype.paySkillCost;
    Game_BattlerBase.prototype.paySkillCost = function(skill) {
        VictorEngine.CooperationSkills.paySkillCost.call(this, skill);
        if (skill.isCooperation && this.isCooperationSkill(skill)) {
            var user = this;
            this.cooperationBattlers(skill).forEach(function(battler) {
                if (battler !== user) {
                    battler.payCooperationSkillCost(skill);
                }
            });
        }
    };
 
    VictorEngine.CooperationSkills.canInput = Game_BattlerBase.prototype.canInput;
    Game_BattlerBase.prototype.canInput = function() {
        return VictorEngine.CooperationSkills.canInput.call(this) && !this.uniteLeader();
    };
 
    VictorEngine.CooperationSkills.meetsSkillConditions = Game_BattlerBase.prototype.meetsSkillConditions;
    Game_BattlerBase.prototype.meetsSkillConditions = function(skill) {
        return (VictorEngine.CooperationSkills.meetsSkillConditions.call(this, skill) &&
            this.isSkillCooperationOk(skill));
    };
 
    Game_BattlerBase.prototype.isSkillCooperationOk = function(skill) {
        return !skill.isCooperation || this.isCooperationOk(skill);
    };
 
    Game_BattlerBase.prototype.isCooperationOk = function(skill) {
        return this.isUniteOk(skill) || this.isFusionOk(skill) || this.isCombinationOk(skill);
    };
 
    Game_BattlerBase.prototype.isUniteOk = function(skill) {
        if (this.isEnemy() || !skill.cooperationSkill.unite) {
            return false;
        }
        return this.uniteBattlers(skill).every(function(actor) {
            return actor.isUniteMemberOk(skill) && !actor.isUsingNonUnite() &&
                (!Imported['VE - Active Time Battle'] || actor.atbFull());
        });
    };
 
    Game_BattlerBase.prototype.isFusionOk = function(skill) {
        if (!$gameParty.inBattle() || !skill.cooperationSkill.fusion) {
            return false;
        }
        return this.fusionBattlers(skill).every(function(actor) {
            return actor.isFusionMemberOk(actor.originalFusionSkill());
        });
    };
 
    Game_BattlerBase.prototype.isCombinationOk = function(skill) {
        if (!$gameParty.inBattle() || !skill.cooperationSkill.combination) {
            return false;
        } else {
            return true;
        }
    };
 
    Game_BattlerBase.prototype.isUniteMemberOk = function(skill) {
        return $gameParty.members().contains(this) && this.hasSkill(skill.id) &&
            VictorEngine.CooperationSkills.meetsSkillConditions.call(this, skill);
    };
 
    Game_BattlerBase.prototype.isFusionMemberOk = function(skill) {
        return VictorEngine.CooperationSkills.meetsSkillConditions.call(this, skill);
    };
 
    Game_BattlerBase.prototype.cooperationBattlers = function(skill) {
        if (skill.cooperationSkill.unite) {
            return this.uniteBattlers(skill);
        } else if (skill.cooperationSkill.fusion) {
            return this.fusionBattlers(skill);
        } else if (skill.cooperationSkill.combination) {
            return this.combinationBattlers(skill);
        }
        return [];
    };
 
    Game_BattlerBase.prototype.uniteBattlers = function(skill) {
        return skill.cooperationSkill.unite.map(function(id) {
            return $gameActors.actor(id);
        });
    };
 
    Game_BattlerBase.prototype.fusionBattlers = function(skill) {
        if (this.fusionLeaderSkill() === skill) {
            return this.fusionMembersList(this);
        } else if (this.fusionLeader() && this.fusionLeader().fusionLeaderSkill() === skill) {
            return this.fusionMembersList(this.fusionLeader());
        } else {
            return [];
        }
    };
 
    Game_BattlerBase.prototype.combinationBattlers = function(skill) {
        if (this.combinationLeaderSkill() === skill) {
            return this.combinationMembersList(this);
        } else if (this.combinationLeader() && this.combinationLeader().combinationLeaderSkill() === skill) {
            return this.combinationMembersList(this.combinationLeader());
        } else {
            return [];
        }
    };
 
    Game_BattlerBase.prototype.fusionMembersList = function(leader) {
        return BattleManager.allBattleMembers().filter(function(member) {
            return member.fusionLeader() === leader || member === leader;
        });
    };
 
    Game_BattlerBase.prototype.combinationMembersList = function(leader) {
        return BattleManager.allBattleMembers().filter(function(member) {
            return member.combinationLeader() === leader || member === leader;
        });
    };
 
    Game_BattlerBase.prototype.cooperationMember = function() {
        return this._uniteLeader || this._fusionLeader;
    };
 
    Game_BattlerBase.prototype.cooperationLeader = function(skill) {
        if (skill.cooperationSkill.unite) {
            return this.uniteLeader();
        } else if (skill.cooperationSkill.fusion) {
            return this.fusionLeader();
        } else if (skill.cooperationSkill.combination) {
            return this.combinationLeader();
        } else {
            return null;
        };
    };
 
    Game_BattlerBase.prototype.isCooperationSkill = function(skill) {
        return skill.cooperationSkill.unite || skill.cooperationSkill.fusion || skill.cooperationSkill.combination;
    };
 
    Game_BattlerBase.prototype.payCooperationSkillCost = function(skill) {
        if (skill.cooperationSkill.fusion) {
            if (this.originalFusionSkill()) {
                VictorEngine.CooperationSkills.paySkillCost.call(this, this.originalFusionSkill());
            }
        } else {
            VictorEngine.CooperationSkills.paySkillCost.call(this, skill);
        }
    };
 
    Game_BattlerBase.prototype.cooperationName = function(skill) {
        var text = ''
        var names = this.cooperationBattlers(skill).map(function(battler) {
            return battler.name();
        });
        for (var i = 0; i < names.length; i++) {
            var name = names[i]
            if (i === 0) {
                text = name;
            } else if (i > 0 && i < names.length - 1) {
                text += ', ' + name;
            } else if (i === names.length - 1) {
                text += ' and ' + name;
            }
        }
        return text;
    };
 
    Game_BattlerBase.prototype.uniteLeader = function() {
        return this._uniteLeader;
    };
 
    Game_BattlerBase.prototype.uniteLeaderSkill = function() {
        return this._uniteLeaderSkill;
    };
 
    Game_BattlerBase.prototype.isUsingNonUnite = function() {
        return this._isUsingNonUnite;
    };
 
    Game_BattlerBase.prototype.setUniteLeader = function(leader) {
        this._uniteLeader = leader;
    };
 
    Game_BattlerBase.prototype.setUniteLeaderSkill = function(skill) {
        this._uniteLeaderSkill = skill;
    };
 
    Game_BattlerBase.prototype.setUsingNonUnite = function() {
        if (this.notUsingUnite()) {
            this._isUsingNonUnite = true;
        }
    };
 
    Game_BattlerBase.prototype.clearUniteSetup = function() {
        this._uniteLeader = null;
        this._uniteLeaderSkill = null;
        this._isUsingNonUnite = false;
    };
 
    Game_BattlerBase.prototype.notUsingUnite = function() {
        return this._actions.every(function(action) {
            return !action.isUniteAction();
        })
    };
 
    Game_BattlerBase.prototype.setupUniteBaltters = function() {
        this._actions.forEach(function(action) {
            if (action.isUniteAction()) {
                action.setCooperationBattlers();
            }
        })
    };
 
    /* FUSION */
    Game_BattlerBase.prototype.isUsingFusion = function(id) {
        var battler = this;
        return this._actions.some(function(action) {
            return action.item() && action.isSkill() && action.item().id === id && !battler.originalFusionSkill();
        })
    };
 
    Game_BattlerBase.prototype.fusionLeader = function() {
        return this._fusionLeader;
    };
 
    Game_BattlerBase.prototype.fusionLeaderSkill = function() {
        return this._fusionLeaderSkill;
    };
 
    Game_BattlerBase.prototype.originalFusionSkill = function() {
        return this._originalFusionSkill;
    };
 
    Game_BattlerBase.prototype.isOriginalFusionSkill = function(id) {
        return this._originalFusionSkill && this._originalFusionSkill.id === id;
    };
 
    Game_BattlerBase.prototype.clearFusionSetup = function() {
        this._fusionLeader = null;
        this._fusionLeaderSkill = null;
        this._originalFusionSkill = null;
    };
 
    Game_BattlerBase.prototype.setFusionLeader = function(skill, id) {
        this._fusionLeaderSkill = skill;
        this.setFusionAction(skill, id);
    };
 
    Game_BattlerBase.prototype.setFusionMember = function(leader, id) {
        this._fusionLeader = leader;
        this.setFusionAction(leader.fusionLeaderSkill(), id);
    };
 
    Game_BattlerBase.prototype.setFusionAction = function(skill, id) {
        var index = this.setFusionActionIndex(id);
        var action = new Game_Action(this);
        action.setSkill(skill.id);
        action.setTarget(this._actions[index]._targetIndex);
        this._actions.splice(index, 1, action);
        this._originalFusionSkill = $dataSkills[id];
    };
 
    Game_BattlerBase.prototype.setFusionActionIndex = function(id) {
        for (var i = 1; i < this._actions.length; i++) {
            var action = this._actions[i];
            if (action.item() && action.isSkill() && action.item().id === id) {
                return i;
            }
        }
        return 0;
    };
 
    Game_BattlerBase.prototype.setupFusionBaltters = function() {
        this._actions.forEach(function(action) {
            if (action.isFusionAction()) {
                action.setCooperationBattlers();
            }
        })
    };
 
    /* Combination */
    Game_BattlerBase.prototype.isUsingCombination = function(id) {
        var battler = this;
        return this._actions.some(function(action) {
            return action.item() && action.isSkill() && action.item().id === id && !battler.isCombinationSkill();
        })
    };
 
    Game_BattlerBase.prototype.combinationLeader = function() {
        return this._combinationLeader;
    };
 
    Game_BattlerBase.prototype.combinationLeaderSkill = function() {
        return this._combinationLeaderSkill;
    };
 
    Game_BattlerBase.prototype.isCombinationSkill = function() {
        return this.combinationLeader() || this.combinationLeaderSkill();
    };
 
    Game_BattlerBase.prototype.clearCombinationSetup = function() {
        this._combinationLeader = null;
        this._combinationLeaderSkill = null;
    };
 
    Game_BattlerBase.prototype.setCombinationLeader = function(skill) {
        this._combinationLeaderSkill = skill;
    };
 
    Game_BattlerBase.prototype.setCombinationMember = function(leader) {
        this._combinationLeader = leader;
    };
 
    Game_BattlerBase.prototype.setupCombinationBaltters = function() {
        this._actions.forEach(function(action) {
            if (action.isCombinationAction()) {
                action.setCooperationBattlers();
            }
        })
    };
 
    //=============================================================================
    // Game_Battler
    //=============================================================================
 
    VictorEngine.CooperationSkills.performActionStart = Game_Battler.prototype.performActionStart;
    Game_Battler.prototype.performActionStart = function(action) {
        if (this.uniteLeaderSkill()) {
            this.clearUniteAction();
        }
        if (this.fusionLeaderSkill()) {
            this.clearFusionAction();
        }
        if (this.combinationLeaderSkill()) {
            this.clearCombinationAction();
        }
        VictorEngine.CooperationSkills.performActionStart.call(this, action);
    };
 
    VictorEngine.CooperationSkills.onAllActionsEnd = Game_Battler.prototype.onAllActionsEnd;
    Game_Battler.prototype.onAllActionsEnd = function() {
        VictorEngine.CooperationSkills.onAllActionsEnd.call(this);
        if (this.uniteLeaderSkill()) {
            if (Imported['VE - Active Time Battle']) {
                this.resetAtbUnion();
            }
            this.resetUniteAction(true);
        }
        if (this.fusionLeaderSkill()) {
            this.resetFusionAction(true);
        }
        if (this.combinationLeaderSkill()) {
            this.resetCombinationAction(true);
        }
    };
 
    VictorEngine.CooperationSkills.speed = Game_Battler.prototype.speed;
    Game_Battler.prototype.speed = function() {
        var battlers = this.allCooperationBattlers()
        if (this.uniteLeaderSkill() || this.fusionLeaderSkill() && battlers.length > 0) {
            return this.cooperationActionSpeed(battlers);
        } else if (this.uniteLeader() || this.fusionLeader()) {
            return -9999;
        } else {
            return VictorEngine.CooperationSkills.speed.call(this);
        }
    };
 
    Game_Battler.prototype.cooperationActionSpeed = function(battlers) {
        return Math.min.apply(null, battlers.map(function(member) {
            return VictorEngine.CooperationSkills.speed.call(member);
        })) || 0;
    };
 
    Game_Battler.prototype.allCooperationBattlers = function() {
        var battlers = []
        this._actions.forEach(function(action) {
            if (action.isCooperation()) {
                battlers = battlers.concat(action.cooperationBattlers());
            }
        })
        return battlers;
    };
 
    Game_Battler.prototype.resetUniteAction = function(turnEnd) {
        var leader = this;
        this.clearUniteSetup();
        $gameParty.members().forEach(function(member) {
            if (member.uniteLeader() === leader) {
                member.makeActions();
                member.clearUniteSetup();
            }
        })
    };
 
    Game_Battler.prototype.resetFusionAction = function(turnEnd) {
        var leader = this;
        this.clearFusionSetup();
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.fusionLeader() === leader) {
                member.makeActions();
                member.clearFusionSetup();
            }
        })
    };
 
    Game_Battler.prototype.resetCombinationAction = function(turnEnd) {
        var leader = this;
        this.clearCombinationSetup();
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.combinationLeader() === leader) {
                member.makeActions();
                member.clearCombinationSetup();
            }
        })
    };
 
    Game_Battler.prototype.clearUniteAction = function() {
        var leader = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.uniteLeader() === leader) {
                member.clearActions();
                member.setActionState('acting');
            }
        })
    };
 
    Game_Battler.prototype.clearFusionAction = function() {
        var leader = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.fusionLeader() === leader) {
                member.clearActions();
                member.setActionState('acting');
            }
        })
    };
 
    Game_Battler.prototype.clearCombinationAction = function() {
        var leader = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.combinationLeader() === leader) {
                member.clearActions();
                member.setActionState('acting');
            }
        })
    };
 
    VictorEngine.CooperationSkills.onBattleStart = Game_Battler.prototype.onBattleStart;
    Game_Battler.prototype.onBattleStart = function() {
        VictorEngine.CooperationSkills.onBattleStart.call(this);
        this.clearUniteSetup();
        this.clearFusionSetup();
        this.clearCombinationSetup();
    };
 
    VictorEngine.CooperationSkills.onTurnEnd = Game_Battler.prototype.onTurnEnd;
    Game_Battler.prototype.onTurnEnd = function() {
        VictorEngine.CooperationSkills.onTurnEnd.call(this);
        this.clearUniteSetup();
        this.clearFusionSetup();
    };
 
    VictorEngine.CooperationSkills.onBattleEnd = Game_Battler.prototype.onBattleEnd;
    Game_Battler.prototype.onBattleEnd = function() {
        VictorEngine.CooperationSkills.onBattleEnd.call(this);
        this.clearUniteSetup();
        this.clearFusionSetup();
        this.clearCombinationSetup();
    };
 
    Game_Battler.prototype.resetAtbUnion = function() {
        var leader = this;
        $gameParty.members().forEach(function(member) {
            if (member.uniteLeader() === leader) {
                member.clearAtb();
            }
        })
    };
 
    //=============================================================================
    // Scene_Battle
    //=============================================================================
 
    VictorEngine.CooperationSkills.onSelectAction = Scene_Battle.prototype.onSelectAction;
    Scene_Battle.prototype.onSelectAction = function() {
        var action = BattleManager.inputtingAction();
        VictorEngine.CooperationSkills.onSelectAction.call(this);
        if (!action.needsSelection()) {
            this.setUniteSkill();
        }
    };
 
    VictorEngine.CooperationSkills.onActorOk = Scene_Battle.prototype.onActorOk;
    Scene_Battle.prototype.onActorOk = function() {
        this.setUniteSkill();
        VictorEngine.CooperationSkills.onActorOk.call(this);
    };
 
    VictorEngine.CooperationSkills.onEnemyOk = Scene_Battle.prototype.onEnemyOk;
    Scene_Battle.prototype.onEnemyOk = function() {
        this.setUniteSkill();
        VictorEngine.CooperationSkills.onEnemyOk.call(this);
    };
 
    VictorEngine.CooperationSkills.startActorCommandSelection = Scene_Battle.prototype.startActorCommandSelection;
    Scene_Battle.prototype.startActorCommandSelection = function() {
        var actor = BattleManager.actor();
        actor.resetUniteAction();
        VictorEngine.CooperationSkills.startActorCommandSelection.call(this);
    };
 
    Scene_Battle.prototype.setUniteSkill = function() {
        var actor = BattleManager.actor();
        if (actor && this._actorCommandWindow.currentSymbol() === 'skill') {
            var skill = this._skillWindow.item();
            if (skill.cooperationSkill.unite) {
                this.setUniteLeader(skill);
            } else {
                actor.setUsingNonUnite();
            }
        } else {
            if (actor) {
                actor.setUsingNonUnite();
            }
        }
    };
 
    Scene_Battle.prototype.setUniteLeader = function(skill) {
        var leader = BattleManager.actor();
        var current = BattleManager.inputtingAction();
        return skill.cooperationSkill.unite.forEach(function(id) {
            var actor = $gameActors.actor(id);
            if (actor === leader) {
                actor.setUniteLeaderSkill(skill);
            } else {
                var action = new Game_Action(actor);
                action.setSkill(skill.id);
                action.setTarget(current._targetIndex);
                actor._actions.push(action);
                actor.setUniteLeader(leader);
            }
        });
    };
 
    //=============================================================================
    // Window_BattleLog
    //=============================================================================
 
    VictorEngine.CooperationSkills.startActionWindowBattleLog = Window_BattleLog.prototype.startAction;
    Window_BattleLog.prototype.startAction = function(subject, action, targets) {
        this._cooperationSkill = action;
        VictorEngine.CooperationSkills.startActionWindowBattleLog.call(this, subject, action, targets);
    };
 
    VictorEngine.CooperationSkills.performActionStartWindowBattleLog = Window_BattleLog.prototype.performActionStart;
    Window_BattleLog.prototype.performActionStart = function(subject, action) {
        VictorEngine.CooperationSkills.performActionStartWindowBattleLog.call(this, subject, action);
        if (this.isCooperationSkill(subject)) {
            this.performActionStartCooperation(subject, action);
        }
    };
 
    VictorEngine.CooperationSkills.performAction = Window_BattleLog.prototype.performAction;
    Window_BattleLog.prototype.performAction = function(subject, action, targets) {
        VictorEngine.CooperationSkills.performAction.call(this, subject, action, targets);
        if (this.isCooperationSkill(subject)) {
            this.performActionCooperation(subject, action, targets);
        }
    };
 
    VictorEngine.CooperationSkills.performActionEnd = Window_BattleLog.prototype.performActionEnd;
    Window_BattleLog.prototype.performActionEnd = function(subject, action) {
        VictorEngine.CooperationSkills.performActionEnd.call(this, subject, action);
        if (this.isCooperationSkill(subject)) {
            this.performActionEndCooperation(subject, this._cooperationSkill);
        }
    };
 
    VictorEngine.CooperationSkills.displayAction = Window_BattleLog.prototype.displayAction;
    Window_BattleLog.prototype.displayAction = function(subject, item) {
        if (DataManager.isSkill(item) && item.isCooperation) {
            var numMethods = this._methods.length;
            if (DataManager.isSkill(item)) {
                if (item.message1) {
                    this.push('addText', subject.cooperationName(item) + item.message1.format(item.name));
                }
                if (item.message2) {
                    this.push('addText', item.message2.format(item.name));
                }
            }
            if (this._methods.length === numMethods) {
                this.push('wait');
            }
        } else {
            VictorEngine.CooperationSkills.displayAction.call(this, subject, item);
        }
    };
 
    VictorEngine.CooperationSkills.performActionAnimation = Window_BattleLog.prototype.performActionAnimation;
    Window_BattleLog.prototype.performActionAnimation = function(subject, animationId) {
        VictorEngine.CooperationSkills.performActionAnimation.call(this, subject, animationId);
        if (this.isCooperationSkill(subject)) {
            this.performActionAnimationCooperation(subject, animationId, this._cooperationSkill);
        }
    };
 
    VictorEngine.CooperationSkills.performCastAnimation = Window_BattleLog.prototype.performCastAnimation;
    Window_BattleLog.prototype.performCastAnimation = function(subject, animationId) {
        VictorEngine.CooperationSkills.performCastAnimation.call(this, subject, animationId);
        if (this.isCooperationSkill(subject)) {
            this.performCastAnimationCooperation(subject, animationId, this._cooperationSkill);
        }
    };
 
    Window_BattleLog.prototype.isCooperationSkill = function(subject) {
        return (!Imported['VE - Battle Motions'] && this._cooperationSkill &&
            subject.isCooperationSkill(this._cooperationSkill.item()));
    };
 
    Window_BattleLog.prototype.performActionStartCooperation = function(subject, action) {
        var object = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.cooperationLeader(action.item()) === subject) {
                VictorEngine.CooperationSkills.performActionStartWindowBattleLog.call(object, member, action);
            }
        })
    };
 
    Window_BattleLog.prototype.performActionCooperation = function(subject, action, targets) {
        var object = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.cooperationLeader(action.item()) === subject) {
                VictorEngine.CooperationSkills.performAction.call(object, member, action, targets);
            }
        })
    };
 
    Window_BattleLog.prototype.performActionEndCooperation = function(subject, action) {
        var object = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.cooperationLeader(action.item()) === subject) {
                VictorEngine.CooperationSkills.performActionEndWindowBattleLog.call(object, member, action);
            }
        })
    };
 
    Window_BattleLog.prototype.performActionAnimationCooperation = function(subject, animationId, action) {
        var object = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.cooperationLeader(action.item()) === subject) {
                VictorEngine.CooperationSkills.performActionAnimation.call(object, member, animationId);
            }
        })
    };
 
    Window_BattleLog.prototype.performCastAnimationCooperation = function(subject, animationId, action) {
        var object = this;
        BattleManager.allBattleMembers().forEach(function(member) {
            if (member.cooperationLeader(action.item()) === subject) {
                VictorEngine.CooperationSkills.performCastAnimation.call(object, member, animationId);
            }
        })
    };
 
    VictorEngine.CooperationSkills.defaultMotionMovement = Window_BattleLog.prototype.defaultMotionMovement;
    Window_BattleLog.prototype.defaultMotionMovement = function(subject, action) {
        if (action.isCooperation()) {
            var motion = '';
            if (action && !action.isGuard()) {
                for (var i = 0; i < action.cooperationLength(); i++) {
                    if (action.isStepForward()) {
                        motion += 'motion: cooperation ' + String(i + 1) + ', walk;';
                        motion += 'move: cooperation ' + String(i + 1) + ', forward, 30, 48;';
                    } else {
                        motion += 'direction: cooperation ' + String(i + 1) + ', targets;';
                        motion += 'motion: cooperation ' + String(i + 1) + ', walk;';
                        motion += 'move: cooperation ' + String(i + 1) + ', close to target, 12, 80;';
                    }
                }
                for (var i = 0; i < action.cooperationLength(); i++) {
                    motion += 'wait: cooperation ' + String(i + 1) + ', move;';
                    motion += 'motion: cooperation ' + String(i + 1) + ', reset;';
                }
            }
            return motion;
        } else {
            return VictorEngine.CooperationSkills.defaultMotionMovement.call(this, subject, action);
        }
    };
 
    VictorEngine.CooperationSkills.defaultMotionExecute = Window_BattleLog.prototype.defaultMotionExecute;
    Window_BattleLog.prototype.defaultMotionExecute = function(subject, action) {
        if (action.isCooperation()) {
            var motion = '';
            motion += 'wait: all cooperation, move;';
            for (var i = 0; i < action.cooperationLength(); i++) {
                motion += 'motion: cooperation ' + String(i + 1) + ', action;';
            }
            for (var i = 0; i < action.cooperationLength(); i++) {
                motion += 'wait: cooperation ' + String(i + 1) + ', 8;';
            }
            motion += 'action: all targets, effect;';
            motion += 'wait: all targets, action;';
            return motion;
        } else {
            return VictorEngine.CooperationSkills.defaultMotionExecute.call(this, subject, action);
        }
    };
 
    VictorEngine.CooperationSkills.defaultMotionReturn = Window_BattleLog.prototype.defaultMotionReturn;
    Window_BattleLog.prototype.defaultMotionReturn = function(subject, action) {
        if (action.isCooperation()) {
            var motion = '';
            for (var i = 0; i < action.cooperationLength(); i++) {
                motion += 'motion: cooperation ' + String(i + 1) + ', return;';
                motion += 'move: cooperation ' + String(i + 1) + ', to home, 12;';
            }
            for (var i = 0; i < action.cooperationLength(); i++) {
                motion += 'wait: cooperation ' + String(i + 1) + ', move;';
                motion += 'motion: cooperation ' + String(i + 1) + ', reset;';
            }
            motion += 'wait: all cooperation, move;';
            return motion;
        } else {
            return VictorEngine.CooperationSkills.defaultMotionReturn.call(this, subject, action);
        }
    };
 
    VictorEngine.CooperationSkills.getMotionSubjects = Window_BattleLog.prototype.getMotionSubjects;
    Window_BattleLog.prototype.getMotionSubjects = function(type, user, targets, target, index) {
        if (this.isCooperation()) {
            type = type.toLowerCase().trim();
            var battlers = this._currentAction.action.cooperationBattlers();
            if (type === 'all cooperation') {
                return battlers;
            }
            var match = (/cooperation[ ]*(\d+)/gi).exec(type);
            if (match) {
                var battler = battlers[Number(match[1]) - 1]
                return battler ? [battler] : [];
            }
        }
        return VictorEngine.CooperationSkills.getMotionSubjects.call(this, type, user, targets, target, index);
    }
 
    Window_BattleLog.prototype.isCooperation = function() {
        return this._currentAction && this._currentAction.action && this._currentAction.action.isCooperation();
    }
 
})();