exorcist-evangeline/www/js/plugins/BattleVoice.js
2025-10-08 13:42:52 -05:00

804 lines
26 KiB
JavaScript

//=============================================================================
// Plugin for RPG Maker MV
// BattleVoice.js
//=============================================================================
// [Update History]
// - BattleVoice.js
// 2015.Nov Ver1.0.0 First Release
// 2016.Aug Ver1.1.0 Strict Option Input
// 2019.Feb.27 Ver1.2.0 Random Play
// - BattkeVoiceMZ
// 2020.Jan Ver1.0.0 First release: Add plugin commands
// 2020.Oct.06 Ver1.1.0 Add situations: on counter attack and on reflect magic
// 2020.Nov.09 Ver1.2.0 Add situations: on evade attack and on battle starts
// 2021.Feb.21 Ver1.3.0 Add situations: on receive recover magic from ally
// - BattleVoice.js again
// 2021.Feb.21 Ver1.4.0 forcedly adapt MV version(closed version)
// 2021.Feb.24 Ver2.0.0 Ver.up based on MZ Ver1.3.0
// 2021.Mar.25 Ver2.1.0 enables to set delay when recovery received
/*:
* @target MV
* @plugindesc [Ver2.1.0]Play voice SE at battle when actor does spcified action
* @author Sasuke KANNAZUKI
*
* @param ON switch ID
* @desc play se only when the switch is ON.
* This setting interlocks with option Battle Voice.
* @type switch
* @default 1
*
* @param volume
* @desc volume of SEs. this setting is common among all voice SEs.
* (Default:90)
* @type number
* @min 0
* @max 100000
* @default 90
*
* @param pitch
* @desc pitch of SEs. this setting is common among all voice SEs.
* (Default:100)
* @type number
* @min 10
* @max 100000
* @default 100
*
* @param pan
* @desc pan of SEs. this setting is common among all voice SEs.
* 0:center, <0:left, >0:right (Default:0)
* @type number
* @min -100
* @max 100
* @default 0
*
* @param Battle Voice Name at Option
* @desc display name at option
* @type text
* @default Battle Voice
*
* @param waitForReceive
* @text Delay Frames Of Receiver
* @desc Set the frames from recover invoke to receive
* @type number
* @default 30
*
* @noteParam attackVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam recoverVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam friendMagicVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam magicVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam skillVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam damageVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam evadeVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam defeatedVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam firstVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam victoryVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam counterVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam reflectVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam fromAllyVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @help
* This plugin runs under RPG Maker MV.
*
* This plugin enables to play SE (assumed battle voice) at
* various situations.
*
* [Summary]
* Player can change voice ON/OFF by Option Scene (except Title).
* This setting interlocks switch ID set at plugin parameter.
*
* [note specification]
* write down each actor's note at following format to set SE filename.
* [[Voices when an actor perform something]]
* <attackVoice:filename> plays when actor does normal attack.
* <recoverVoice:filename> plays when actor uses HP recovering magic.
* <friendMagicVoice:filename> plays when actor spells magic for friend
* except HP recovering. if this is not set but <magicVoice:filename> is set,
* it plays <magicVoice:filename> setting file.
* <magicVoice:filename> plays when actor spells magic(except for friend).
* <skillVoice:filename> plays when actor uses special skill except magic.
* [[Voices when an actor affected from any battler]]
* <damageVoice:filename> plays when actor takes damage.
* <evadeVoice:filename> plays when actor evades enemy attack.
* <defeatedVoice:filename> plays when actor is died.
* <counterVoice:filename> plays when counter attack invokes.
* <reflectVoice:filename> plays when actor reflects magic.
* <fromAllyVoice:filename> plays when actor received HP recover magic.
* It doesn't play when magic user is the same as receiver.
* It assumes the phrase 'Thank you' and so on.
* [[Voices when battle exceeds]]
* if plural actors attend the battle, randomly selected actor's SE is adopted. * <firstVoice:filename> plays when battle starts except surprised.
* <victoryVoice:filename> plays when battle finishes.
*
* *NOTE* Here 'magic' skill means its 'Skill Type' is included in
* '[SV]Magic Skills' on 'System '.
*
* [Advanced option 1]
* If you want to play one of several voices randomly,
* write filenames with colon as follows:
* <attackVoice:atk1,atk2,atk3>
* in this case, at attack, plays atk1 atk2, or atk3 randomly.
*
* If set no SE one of filenames,
* <attackVoice:atk1,atk2,$>
* in this case, at attack, plays atk1 atk2, or doesn't play SE.
*
* You can set the same filename twice or more than.
* <attackVoice:atk1,atk2,atk2,$>
* in this case, 25% atk1, 50% atk2, 25% don't play.
*
* *NOTE* When set SEs at this notation, these files might be excluded at
* deployment with option 'Exclude unused files'.
* To prevent this, I recommend to make dummy event and set each SE to
* 'Play SE' on the Contents.
*
* [Plugin Commands]
*
* **Set voice on each situation**
* BattleVoice set arg1 arg2 arg3
* - arg1 must be actor id.
* - arg2 must be a situation.
* attack , recover, friendMagic, magic, skill, damage, evaded,
* dead, counter, reflect, fromAlly, first or victory
* -arg3 must be voice file name. the same as note, by split comma,
* plural setting enables.
* ex.
* BattleVoice 1 attack attackVoice
* set actor whose id is 1 voice for attack.
*
* **Reset voice on each situation**
* BattleVoice reset arg1 arg2
* - arg1 must be actor id.
* - arg2 must be a situation.
* ex.
* BattleVoice reset 2 attack
* reset actor whose id is 2 voice for attack.
*
* **Reset all situations' voice to default**
* BattleVoice allReset arg1
* - arg1 must be actor id to reset all situation
* (note: voice for actor's each skill is not reset)
*
* **Assign actor voice to each skill**
* BattleVoice skillSet arg1 arg2 arg3
* - arg1 must be actor id
* - arg2 must be skill id to set original voice
* - arg3 must be voice file name. the same as note, by split comma,
* plural setting enables.
* ex.
* BattleVoice skillSet 3 8 fire1
* sets fire1 when an actor whose id is 3 uses skill whose id is 8
*
* **Reset actor voice to each skill**
* BattleVoice skillReset arg1 arg2
* - arg1 must be actor id
* - arg2 must be skill id to reset original voice
* ex.
* BattleVoice skillReset 2 15
* reset voice for an actor whose id is 2's skill whose id is 15.
*
* **Reset all voices assigned to skills**
* BattleVoice skillAllReset arg1
* - arg1 must be actor id to reset all skill's voice.
* (note: voice for situations are not reset)
*
* [License]
* this plugin is released under MIT license.
* http://opensource.org/licenses/mit-license.php
*/
/*:ja
* @target MV
* @plugindesc [Ver2.1.0]アクターの戦闘時の行動にボイスSEを設定します。
* @author 神無月サスケ
*
* @param ON switch ID
* @text ボイス演奏スイッチID
* @desc このスイッチが ON の時のみ、ボイスSEを演奏します。
* オプション「バトルボイス」と連動します。
* @type switch
* @default 1
*
* @param volume
* @text 共通ボリューム
* @desc ボイスSEのボリュームです。この設定が全てのボイスSEの
* 共通となります。(既定値:90)
* @type number
* @min 0
* @max 100000
* @default 90
*
* @param pitch
* @text 共通ピッチ
* @desc ボイスSEのピッチです。この設定が全てのボイスSEの
* 共通となります。(既定値:100)
* @type number
* @min 10
* @max 100000
* @default 100
*
* @param pan
* @text 共通位相
* @desc ボイスSEの位相。この設定が全てのボイスSE共通になります。
* 0:中央, 負数:左寄り, 正数:右寄り (既定値:0)
* @type number
* @min -100
* @max 100
* @default 0
*
* @param Battle Voice Name at Option
* @text バトルボイス表示名
* @desc オプション画面での表示名です。
* @type string
* @default バトルボイス
*
* @param waitForReceive
* @text 被回復時ウェイト
* @desc 回復魔法が使われてから受け取ったアクターが発声するまでのフレーム数
* @type number
* @default 30
*
* @noteParam attackVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam recoverVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam friendMagicVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam magicVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam skillVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam damageVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam evadeVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam defeatedVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam firstVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam victoryVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam counterVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam reflectVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
* @noteParam fromAllyVoice
* @noteRequire 1
* @noteDir audio/se/
* @noteType file
* @noteData actors
*
@help
* このプラグインは、RPGツクールMVに対応しています。
*
* 戦闘中のシチュエーションに応じてにバトルボイスを演奏可能にします。
*
* ■概要
* ゲーム中のオプション画面(タイトル画面以外)でON/OFFが可能です。
* この設定は、このプラグインのパラメータで指定したスイッチと連動しています。
* デフォルトではOFFになっています。
*
* ■メモ設定方法
* それぞれのアクターのメモに以下の書式で書いてください。
* filename はボイスSEのファイル名にしてください。
* ◆アクター行動時
* <attackVoice:filename> 通常攻撃の時に再生されるボイスです。
* <recoverVoice:filename> HP回復魔法を使用した時に再生されるボイスです。
* <friendMagicVoice:filename> HP回復以外の味方向け魔法を使用した時に
* 再生されるボイスです。省略された場合で<magicVoice:filename>が
* 設定されている場合は、そちらが再生されます。
* <magicVoice:filename> 味方向け以外の魔法を使用した時に再生されるボイスです。
* <skillVoice:filename> 必殺技を使用した時に再生されるボイスです。
* ◆アクターが対象になった時
* <damageVoice:filename> ダメージを受けた時に再生されるボイスです。
* <evadeVoice:filename> 攻撃を回避した時に再生されるボイスです。
* <defeatedVoice:filename> 戦闘不能になった時に再生されるボイスです。
* <counterVoice:filename> カウンター攻撃発動時に再生されるボイスです。
* <reflectVoice:filename> 魔法を反射する時に再生されるボイスです。
* <fromAllyVoice:filename> HP回復魔法を受けた時に再生されるボイスです。
* 自分自身に使った場合は再生されません
* 「ありがとう」など感謝の言葉を想定しています。
* ◆バトルの進捗に応じて
* アクターが複数いる場合、生きているアクターの中からランダムで再生されます。
* <firstVoice:filename> 戦闘開始時に再生されるボイスです。
* ただし、不意打ちの際は再生されません。
* <victoryVoice:filename> 戦闘勝利時に再生されるボイスです。
*
* 注意:ここでいう「魔法」の定義は、そのスキルのスキルタイプが、
* 「システム2」タブの「[SV]魔法スキル」に含まれているものです。
*
* ■拡張機能1
* 上記のメモのfilename を、コロンで複数指定すると、その中からランダムで
* 再生されます。例えば、以下のように指定した場合、
* <attackVoice:atk1,atk2,atk3>
* atk1 atk2 atk3 のいずれかのボイスがランダムで再生されます。
*
* 無音を指定したい場合は、$ を入れてください。
* <attackVoice:atk1,atk2,$>
* この場合、atk1, atk2, 無音の中から選ばれます。
*
* 同じファイル名を複数回指定可能です。
* <attackVoice:atk1,atk2,atk2,$>
* この場合、25%でatk1、50%でatk2、25%で演奏なしになります。
*
* 注意:この形式で設定を行った場合、デプロイメントの「不要ファイルの削除」で
* 削除される可能性があります。例えばダミーイベントを作り、これらのSEを
* 演奏するなどして、適宜対処してください。
*
* ■プラグインコマンド
* ◆各シチュエーションでのボイスの変更
* BattleVoice set arg1 arg2 arg3
* - arg1 は対象のアクターID
* - arg2 はシチュエーション文字列。以下から選んでください
* attack (通常攻撃時), recover (回復魔法使用時),
* friendMagic (味方対象魔法使用時) magic (通常魔法使用時)
* skill (非魔法スキル使用時), damage (被ダメージ時), evaded (攻撃回避時),
* dead (戦闘不能時), counter (カウンター発動時), reflect (魔法反射発動時),
* fromAlly (回復魔法を受けた時),
* first (戦闘開始時) or victory (戦闘勝利時).
* -arg3 はボイスSE名。セミコロンで複数指定可能
* 例.
* BattleVoice 1 attack attackVoice
* アクターID1の攻撃時(attack)にattackVoiceというSEを再生します。
*
* ◆各シチュエーションでのボイスのリセット
* BattleVoice reset arg1 arg2
* - arg1 は対象のアクターID
* - arg2 はシチュエーション文字列。上記を参考に。
* 例.
* BattleVoice reset 2 attack
* アクターID2の攻撃時ボイスをリセットしメモ設定に戻します。
*
* ◆全シチュエーションのボイスの一括リセット
* BattleVoice allReset arg1
* - arg1 は対象のアクターID
* (注: スキル毎に設定されたボイスはリセットされません)
*
* ◆スキル番号にボイスを割り当てる
* BattleVoice skillSet arg1 arg2 arg3
* - arg1 は対象のアクターID
* - arg2 はスキルID
* - arg3 は演奏するSE名
* 例.
* BattleVoice skillSet 3 8 fire1
* アクターID3がスキルID8を使用した時にfire1を演奏します
*
* ◆スキル番号に割り当てたボイスを解除
* BattleVoice skillReset arg1 arg2
* - arg1 は対象のアクターID
* - arg2 はスキルID
* 例.
* BattleVoice skillReset 2 15
* アクターID2がスキルID15を使用した時のボイスをなくします。
*
* ◆スキル番号に割り当てられた全ボイスを一括リセット
* BattleVoice skillAllReset arg1
* - arg1 は対象のアクターID
* (注意: シチュエーションに割り当てられたボイスはリセットされません。)
*
* ■ライセンス表記
* このプラグインは MIT ライセンスで配布されます。
* ご自由にお使いください。
* http://opensource.org/licenses/mit-license.php
*/
(function() {
var pluginName = 'BattleVoice';
//
// process parameters
//
var parameters = PluginManager.parameters(pluginName);
var pitch = Number(parameters['pitch'] || 100);
var volume = Number(parameters['volume'] || 90);
var pan = Number(parameters['pan'] || 0);
var playSwitchId = Number(parameters['ON switch ID'] || 1);
var strBattleVoice = parameters['Battle Voice Name at Option'] ||
'Battle Voice';
var waitForReceive = Number(parameters['waitForReceive'] || 30);
//
// process plugin commands
//
var _Game_Interpreter_pluginCommand =
Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
_Game_Interpreter_pluginCommand.call(this, command, args);
if (command === pluginName) {
var actor = $gameActors.actor(+args[1]);
if (!actor) {
return;
}
actor.battleVoices = actor.battleVoices || {};
actor.skillVoices = actor.skillVoices || {};
switch (args[0]) {
case 'set':
actor.battleVoices[args[2]] = args[3];
break;
case 'reset':
actor.battleVoices[args[2]] = null;
break;
case 'allReset':
actor.battleVoices = {};
break;
case 'skillSet':
actor.skillVoices[+args[2]] = args[3];
break;
case 'skillReset':
actor.skillVoices[+args[2]] = null;
break;
case 'skillAllReset':
actor.skillVoices = {};
break;
}
}
};
//
// set play options (interlock with switch)
//
var doesDisplaySpecialOptions = function() {
return !SceneManager.isPreviousScene(Scene_Title);
};
var _Window_Options_makeCommandList =
Window_Options.prototype.makeCommandList;
Window_Options.prototype.makeCommandList = function() {
if (doesDisplaySpecialOptions()) {
this.addCommand(strBattleVoice, 'battleVoice');
}
_Window_Options_makeCommandList.call(this);
};
var _Window_Options_getConfigValue =
Window_Options.prototype.getConfigValue;
Window_Options.prototype.getConfigValue = function(symbol) {
switch (symbol) {
case 'battleVoice':
return $gameSwitches.value(playSwitchId);
default:
return _Window_Options_getConfigValue.call(this, symbol);
}
};
var _Window_Options_setConfigValue =
Window_Options.prototype.setConfigValue;
Window_Options.prototype.setConfigValue = function(symbol, volume) {
switch (symbol) {
case 'battleVoice':
return $gameSwitches.setValue(playSwitchId, volume);
default:
return _Window_Options_setConfigValue.call(this, symbol, volume);
}
};
var _Scene_Options_maxCommands = Scene_Options.prototype.maxCommands;
Scene_Options.prototype.maxCommands = function() {
var rowNum = _Scene_Options_maxCommands.call(this);
return doesDisplaySpecialOptions() ? rowNum + 1 : rowNum;
};
//
// play actor voice
//
var canPlayActorVoice = function() {
return $gameSwitches.value(playSwitchId);
};
var split = function(name) {
if (!name) {
return name;
}
var names = name.split(',');
return names[Math.randomInt(names.length)];
};
var createAudioByFileName = function(name) {
var audio = {};
audio.name = name;
audio.pitch = pitch;
audio.volume = volume;
audio.pan = pan
return audio;
};
var playActorVoice = function(actor, type) {
if (!canPlayActorVoice()) {
return;
}
var name = '';
var a = actor.battleVoices || {};
var m = actor.actor().meta;
switch(type){
case 'attack':
name = split(a.attack || m.attackVoice);
break;
case 'recover':
name = split(a.recover || m.recoverVoice);
break;
case 'friendmagic':
name = split(a.friendMagic || m.friendMagicVoice || m.magicVoice);
break;
case 'magic':
name = split(a.magic || m.magicVoice);
break;
case 'skill':
name = split(a.skill || m.skillVoice);
break;
case 'damage':
name = split(a.damage || m.damageVoice);
break;
case 'evade':
name = split(a.evade || m.evadeVoice);
break;
case 'dead':
name = split(a.dead || m.defeatedVoice);
break;
case 'counter':
name = split(a.counter || m.counterVoice);
break;
case 'reflect':
name = split(a.reflect || m.reflectVoice);
break;
case 'fromAlly':
name = split(a.fromAlly || m.fromAllyVoice);
break;
case 'first':
name = split(a.first || m.firstVoice);
break;
case 'victory':
name = split(a.victory || m.victoryVoice);
break;
}
if (name && name !=="$") {
var audio = createAudioByFileName(name);
AudioManager.playSe(audio);
}
};
var isSkillVoice = function(actor, action) {
if (!actor.skillVoices || !action.isSkill()) {
return false;
}
return !!actor.skillVoices[action._item.itemId()];
};
var playSkillVoice = function(actor, action) {
if (!canPlayActorVoice()) {
return;
}
var name = split(actor.skillVoices[action._item.itemId()]);
if (name && name !=="$") {
var audio = createAudioByFileName(name);
AudioManager.playSe(audio);
}
};
//
// functions for call actor voice.
//
var _Game_Actor_performAction = Game_Actor.prototype.performAction;
Game_Actor.prototype.performAction = function(action) {
_Game_Actor_performAction.call(this, action);
if (isSkillVoice(this, action)) {
playSkillVoice(this, action);
} else if (action.isAttack()) {
playActorVoice(this, 'attack');
} else if (action.isMagicSkill() && action.isHpRecover()) {
playActorVoice(this, 'recover');
} else if (action.isMagicSkill() && action.isForFriend()) {
playActorVoice(this, 'friendmagic');
} else if (action.isMagicSkill()) {
playActorVoice(this, 'magic');
} else if (action.isSkill() && !action.isGuard()) {
playActorVoice(this, 'skill');
}
};
var _Game_Actor_performDamage = Game_Actor.prototype.performDamage;
Game_Actor.prototype.performDamage = function() {
_Game_Actor_performDamage.call(this);
playActorVoice(this, 'damage');
};
var _Game_Actor_performEvasion = Game_Actor.prototype.performEvasion;
Game_Actor.prototype.performEvasion = function() {
_Game_Actor_performEvasion.call(this);
playActorVoice(this, 'evade');
};
var _Game_Actor_performCollapse = Game_Actor.prototype.performCollapse;
Game_Actor.prototype.performCollapse = function() {
_Game_Actor_performCollapse.call(this);
if ($gameParty.inBattle()) {
playActorVoice(this, 'dead');
}
};
var _BattleManager_invokeCounterAttack = BattleManager.invokeCounterAttack;
BattleManager.invokeCounterAttack = function(subject, target) {1
if (target.isActor()) {
playActorVoice(target, 'counter');
}
_BattleManager_invokeCounterAttack.call(this, subject, target);
};
var _BattleManager_invokeMagicReflection =
BattleManager.invokeMagicReflection;
BattleManager.invokeMagicReflection = function(subject, target) {
if (target.isActor()) {
playActorVoice(target, 'reflect');
}
_BattleManager_invokeMagicReflection.call(this, subject, target);
};
var _Game_System_onBattleStart = Game_System.prototype.onBattleStart;
Game_System.prototype.onBattleStart = function() {
_Game_System_onBattleStart.call(this);
var candidates = $gameParty.aliveMembers().filter(function(actor) {
return actor.actor().meta.firstVoice ||
(actor.battleVoices && actor.battleVoices.first);
});
if (candidates.length > 0) {
var index = Math.randomInt(candidates.length);
var actor = candidates[index];
if (!BattleManager._surprise) {
playActorVoice(actor, 'first');
}
}
};
var _BattleManager_processVictory = BattleManager.processVictory;
BattleManager.processVictory = function() {
var candidates = $gameParty.aliveMembers().filter(function(actor) {
return actor.actor().meta.victoryVoice ||
(actor.battleVoices && actor.battleVoices.victory);
});
if (candidates.length > 0) {
var index = Math.randomInt(candidates.length);
var actor = candidates[index];
playActorVoice(actor, 'victory');
}
_BattleManager_processVictory.call(this);
};
//
// When recover received, wait some frames until play.
//
Game_Battler.prototype.doesPlayFromAlly = function() {
return false;
};
Game_Actor.prototype.doesPlayFromAlly = function() {
// not play voice if target is the same as magic user
return this !== BattleManager._subject;
};
Window_BattleLog.prototype.waitAlly = function() {
this._waitCount = waitForReceive;
};
Window_BattleLog.prototype.playReceiveVoice = function(target) {
playActorVoice(target, 'fromAlly');
};
var _Window_BattleLog_performRecovery =
Window_BattleLog.prototype.performRecovery;
Window_BattleLog.prototype.performRecovery = function(target) {
if (target.doesPlayFromAlly()) {
this.push('waitAlly');
this.push('playReceiveVoice', target);
}
_Window_BattleLog_performRecovery.call(this, target);
};
})();