//=============================================================================
// 特定条件で自動でスキルを発動させるプラグイン
// FTKR_AutoInvokeSkill.js
// プラグインNo : 34
// 作成者 : フトコロ
// 作成日 : 2017/05/03
// 最終更新日 : 2018/09/07
// バージョン : v1.3.2
//=============================================================================
var Imported = Imported || {};
Imported.FTKR_AIS = true;
var FTKR = FTKR || {};
FTKR.AIS = FTKR.AIS || {};
//=============================================================================
/*:
* @plugindesc v1.3.2 特定条件で自動でスキルを発動させるプラグイン
* @author フトコロ
*
* @help
*-----------------------------------------------------------------------------
* 概要
*-----------------------------------------------------------------------------
* 特定条件で自動でスキルを発動させることができます。
*
* 発動できる条件は、以下の通りです。
*
* 1. 行動後に指定した条件式を満たした時
* 2. 付与されていたステートが解除された時
*
*
*-----------------------------------------------------------------------------
* 設定方法
*-----------------------------------------------------------------------------
* 1.「プラグインマネージャー(プラグイン管理)」に、本プラグインを追加して
* ください。
*
*
* 2. 他プラグインと組み合わせる場合
* 当プラグインは以下のプラグインよりも下にしてください。
* YEP_BattleEngineCore.js
* YEP_X_BattleSysATB.js
* および、他のFTKR系プラグイン
*
*
*-----------------------------------------------------------------------------
* アクター、職業、装備、ステートに設定し、行動後に発動させる
*-----------------------------------------------------------------------------
* 以下のタグをアクターや職号、装備、ステートのメモ欄に追記すると
* 各キャラが行動する度に、設定した条件式を満たした時にスキルを自動発動します。
*
*
* 条件式
*
*
* または
*
*
* 条件式
*
*
* x : スキルID
*
*
* [条件判定するタイミング]
* 条件判定は、各キャラが行動しする度に毎回行います。
* そして、スキルを発動する相手は、そのキャラが行動するときの対象です。
*
* 例えば、味方が敵を攻撃した時の場合は、スキルが発動すると、その攻撃する敵に
* 対して発動します。
* 逆に、味方が防御するなど、自分を対象とする行動をした場合、スキルを発動する
* 対象は、そのキャラ自身になります。
*
* このため、条件式には設定したアクターと、行動したキャラ、行動の対象のキャラの
* パラメータを参照することができます。
* 判定はパーティーの先頭から順番に行い、条件を満たしたキャラのスキルを
* 実行します。
*
*
* [条件式で使用できるコード]
* 条件式には、以下のコードを使うことができます。
* a.param - 設定したアクターのパラメータを参照します。(a.hp で現在HPを参照)
* b.param - 行動の対象キャラのパラメータを参照します。
* c.param - 行動したキャラのパラメータを参照します。
* item - 使用したスキルのデータを参照します。
* s[x] - スイッチID x の状態を参照します。
* v[x] - 変数ID x の値を参照します。
*
*
* 例)武器に設定し、その武器を装備したキャラが、敵を攻撃したときのみ
* スキルID10を発動させる場合
*
*
* a !== b && a == c
*
*
* 上の条件では、a は装備したキャラ、b は攻撃する相手、
* c は攻撃したキャラを意味します。
* a !== b で装備したキャラが攻撃する相手ではない、
* a == c で装備したキャラが攻撃したキャラである
* という条件を満たす場合に、スキルID 10 が発動する、となります。
*
*
* 使用したスキルの設定で発動条件を変えることが出来ます。
* item.stypeId
* - スキルタイプのIDを参照します
* 0:なし, 1~:データベースのスキルタイプ
* item.hitType
* - 命中タイプを参照します
* 0:必中, 1:物理, 2:魔法
* item.damage.elementId
* - ダメージの属性IDを参照します
* -1:通常攻撃, 0:なし, 1~:データベースの属性
* item.damage.type
* - ダメージタイプを参照します
* 1:HPダメージ, 2:MPダメージ, 3:HP回復,
* 4:MP回復, 5:HP吸収, 6:MP吸収, 0:なし
*
* 例)物理攻撃タイプのスキルを使用した時のみ、スキルID10を発動させる場合
*
*
* a !== b && a == c
* item.hitType == 1
*
*
*
*-----------------------------------------------------------------------------
* スキルに設定し、行動後に指定した条件式を満たした時に発動
*-----------------------------------------------------------------------------
* 以下のタグをスキルのメモ欄に追記すると、設定した条件式を満たしたターンの
* 終わりに、スキルを自動発動します。
* なお、同じスキルは1ターンに1回だけ発動します。
*
*
* 条件式
*
*
* または
*
*
* 条件式
*
*
*
* [条件式(eval) の値について]
* 条件式(eval)は、ダメージ計算式のように、計算式を入力することで、
* 固定値以外の値を使用することができます。以下のコードを使用できます。
* a.param - 行動したキャラのパラメータを参照します。
* s[x] - スイッチID x の状態を参照します。
* v[x] - 変数ID x の値を参照します。
*
*
* 入力例)
* キャラのTPが 50 以上になると自動発動。
*
* a.tp >= 50
*
*
*
*-----------------------------------------------------------------------------
* 条件式を設定する上での注意点
*-----------------------------------------------------------------------------
* スキルは、条件を満たした時に発動します。
* つまり、条件を満たしている間は常に発動するようになります。
* スキルの仕様が、何度も発動してもよいものであれば問題ありませんが
* そうでない場合は、何度も発動させないように条件式を設定しましょう。
*
*
* [複数の条件を設定する場合]
* この場合、設定したすべての条件を満たした時に発動します。
* 以下の表記はすべて同じ意味になります。
*
* 1. '&&'を使用して横に並べる
*
* 条件式1 && 条件式2 && 条件式3
*
*
* 2. 縦に並べる
*
* 条件式1
* 条件式2
* 条件式3
*
*
* 3. 上記を組み合わせる
*
* 条件式1 && 条件式2
* 条件式3
*
*
*
*-----------------------------------------------------------------------------
* ステート解除時に発動
*-----------------------------------------------------------------------------
* 以下のタグをステートのメモ欄に追記すると、ステートを解除(*1)した時点(*2)で
* 指定したスキルを自動で発動(*3)させます。
*
*
* :x - スキルID
*
* (*1) ステートの解除条件に設定した内容に従います。
* (*2) ダメージで解除の場合はダメージを受けた時点で、ターン終了時なら
* ターン終了時に発動します。
* (*3) スキルの範囲が「敵単体」の場合は、「敵1体ランダム」になります。
*
*
* また、以下のタグを追記すると、解除時に最後にダメージを与えた相手(*3)を
* スキル発動のターゲットにします。
*
*
*
* (*3) ダメージ解除の場合は、ダメージを与えた相手です。
* 解除後にダメージを与えた相手は、ターゲットにしません。
*
*
* 解除時に誰にもダメージを与えられていない場合は、スキルの範囲設定に従い
* スキルを発動します。
*
*
*-----------------------------------------------------------------------------
* 本プラグインのライセンスについて(License)
*-----------------------------------------------------------------------------
* 本プラグインはMITライセンスのもとで公開しています。
* This plugin is released under the MIT License.
*
* Copyright (c) 2017 Futokoro
* http://opensource.org/licenses/mit-license.php
*
*
* フトコロのプラグイン置き場
* https://github.com/futokoro/RPGMaker/blob/master/README.md
*
*
*-----------------------------------------------------------------------------
* 変更来歴
*-----------------------------------------------------------------------------
*
* v1.3.2 - 2018/09/07 : 不具合修正
* 1. FTKR_AlternatingTurnBattle.jsと組み合わせた時に、複数回行動可能なキャラが
* 自動スキルを発動させた場合、その次の行動でエラーになる不具合を修正。
* 2. FTKR_AlternatingTurnBattle.jsと組み合わせた時に、ターン終了の条件で
* ステート解除時発動スキルを設定した場合に、次ターンに発動する不具合を修正。
*
* v1.3.1 - 2017/11/04 : ヘルプ修正
*
* v1.3.0 - 2017/11/04 : 機能追加、仕様変更
* 1. 発動条件の条件式で、行動したキャラのパラメータを参照するコードを変更。
* 2. 発動条件の条件式で、使用したスキルをitemコードで参照できる機能を追加。
*
* v1.2.0 - 2017/11/02 : 機能追加
* 1. 職業や装備、ステートにもスキル発動条件を設定する機能を追加。
*
* v1.1.0 - 2017/06/28 : 機能追加、タグの名称変更
* 1. アクターに任意のスキルの自動発動条件を設定する機能を追加。
* 2. 発動条件タグの英字名称を変更。
*
* v1.0.0 - 2017/05/03 : 初版作成
*
*-----------------------------------------------------------------------------
*/
//=============================================================================
(function() {
//=============================================================================
// プラグイン パラメータ
//=============================================================================
FTKR.AIS.parameters = PluginManager.parameters('FTKR_AutoInvokeSkill');
//=============================================================================
// 自作関数(グローバル)
//=============================================================================
FTKR.gameData = FTKR.gameData || {
user :null,
target :null,
item :null,
number :0,
owner :null,
};
FTKR.setGameData = function(user, target, item, number, owner) {
FTKR.gameData = {
user :user || null,
target :target || null,
item :item || null,
number :number || 0,
owner :owner || null
};
};
FTKR.evalFormula = function(formula) {
var datas = FTKR.gameData;
try {
var s = $gameSwitches._data;
var v = $gameVariables._data;
var a = datas.user;
var b = datas.target;
var c = datas.owner;
var item = datas.item;
var number = datas.number;
if (Imported.FTKR_ISV) {
if (a) {
var aData = a.isActor() ? a.actor() : a.enemy();
if (aData._selfVariables) var av = aData._selfVariables._data;
}
if (b) {
var result = b.result();
var bData = b.isActor() ? b.actor() : b.enemy();
if (bData._selfVariables) var bv = bData._selfVariables._data;
}
if (c) {
var cData = c.isActor() ? c.actor() : c.enemy();
if (cData._selfVariables) var cv = cData._selfVariables._data;
}
if (item && item._selfVariables) var iv = item._selfVariables._data;
} else {
if (b) var result = b.result();
}
var value = eval(formula);
if (isNaN(value)) value = 0;
return value;
} catch (e) {
console.error(e);
return 0;
}
};
//=============================================================================
// 自作関数(ローカル)
//=============================================================================
// textの形式のメタデータを読み取ってtextを返す
var readEntrapmentCodeToText = function(obj, codeTitles) {
notes = convertEntrapmentRegArray(codeTitles);
var notedata = obj.note.split(/[\r\n]+/);
var setMode = 'none';
for (var i = 0; i < notedata.length; i++) {
var line = notedata[i];
if (testRegs(line, notes, 'a')) {
var text = '';
setMode = 'read';
} else if (testRegs(line, notes, 'b')) {
setMode = 'none';
} else if (setMode === 'read') {
text += line + ';';
}
}
return text;
};
var convertEntrapmentRegArray = function(codeTitles) {
return codeTitles.map(function(codeTitle) {
return {
a:new RegExp('<' + codeTitle + '>', 'i'),
b:new RegExp('<\/' + codeTitle + '>', 'i')
};
});
};
//正規表現オブジェクトの配列とdataをテストする
var testRegs = function(data, regs, prop) {
return regs.some(function(reg) {
return prop ? reg[prop].test(data) : reg.test(data);
});
};
var readEntrapmentCodeToTextEx = function(obj, codeTitles) {
var regs = convertEntrapmentRegArrayEx(codeTitles);
if (!obj) return [];
var notedata = obj.note.split(/[\r\n]+/);
var setMode = 'none';
var results = [];
for (var i = 0; i < notedata.length; i++) {
var line = notedata[i];
if (matchRegs(line, regs, 'start')) {
var data = {
id:RegExp.$1,
text:''
};
setMode = 'read';
} else if (matchRegs(line, regs, 'end')) {
setMode = 'none';
results.push(data);
} else if (setMode === 'read') {
data.text += line + ';';
}
}
return results;
};
var convertEntrapmentRegArrayEx = function(codeTitles) {
return codeTitles.map(function(codeTitle) {
return {
start:new RegExp('<' + codeTitle + ':[ ]*(.+)>', 'i'),
end :new RegExp('<\/' + codeTitle + '>', 'i')
};
});
};
var matchRegs = function(data, regs, prop) {
return regs.some(function(reg){
return prop ? data.match(reg[prop]) : data.match(reg);
});
};
// textを条件式に使える状態に変換する
var convertTextToConditions = function(text) {
var result = '';
if (text) {
var datas = text.split(';');
datas.forEach(function(data, i) {
result += data;
if (datas[i+1]) result += ')&&(';
});
result = '(' + result + ')';
}
return result;
};
var evalCheckConditions = function(text) {
var formula = convertTextToConditions(text);
if (!formula) return false;
return FTKR.evalFormula(formula);
};
var convertRefreshConditions = function(obj) {
return convertTextToConditions(readEntrapmentCodeToText(obj, ['AIS_発動条件', 'AIS_INVOKE_CONDITIONS']));
};
var evalRefreshConditions = function(formula) {
if (!formula) return false;
return FTKR.evalFormula(formula);
};
//=============================================================================
// BattleManager
//=============================================================================
var _AIS_BattleManager_setup = BattleManager.setup;
BattleManager.setup = function(troopId, canEscape, canLose) {
_AIS_BattleManager_setup.call(this, troopId, canEscape, canLose);
this.checkAutoSkills();
};
BattleManager.checkAutoSkills = function() {
this._autoSkillList = [];
$dataSkills.forEach( function (skill) {
if (!skill) return;
var formula = convertRefreshConditions(skill);
if (formula) {
this._autoSkillList.push({id:skill.id, formula:formula});
}
},this);
};
var _AIS_BattleManager_update = BattleManager.update;
BattleManager.update = function() {
if (!this.isBusy() && !this.updateEvent()) {
switch (this._phase) {
case 'autoSkill':
this.updateAutoSkill();
break;
case 'autoSkillAction':
this.updateAutoSkillAction();
break;
default:
_AIS_BattleManager_update.call(this);
break;
}
}
};
var _AIS_BattleManager_startTurn = BattleManager.startTurn;
BattleManager.startTurn = function() {
this._autoSkills = [];
_AIS_BattleManager_startTurn.call(this);
};
var _AIS_BattleManager_endAction = BattleManager.endAction;
BattleManager.endAction = function() {
_AIS_BattleManager_endAction.call(this);
if (this._autoSkills && this._autoSkills.length) {
this._keepPhase = this._phase;
this._phase = 'autoSkill'
}
};
if (Imported.FTKR_AltTB) {
var _AIS_BattleManager_updateTurnEnd = BattleManager.updateTurnEnd;
BattleManager.updateTurnEnd = function() {
_AIS_BattleManager_updateTurnEnd.call(this);
if (this._autoSkills && this._autoSkills.length) {
this._keepPhase = this._phase;
this._phase = 'autoSkill'
}
};
} else {
var _AIS_BattleManager_endTurn = BattleManager.endTurn;
BattleManager.endTurn = function() {
_AIS_BattleManager_endTurn.call(this);
if (this._autoSkills && this._autoSkills.length) {
this._keepPhase = this._phase;
this._phase = 'autoSkill'
}
};
}
BattleManager.updateAutoSkill = function() {
$gameParty.requestMotionRefresh();
var autoSkill = this._autoSkills.shift();
if (autoSkill) {
this.startAutoSkillAction(autoSkill);
} else {
this._phase = this._keepPhase;
}
};
var _AIS_BattleManager_startAction = BattleManager.startAction;
BattleManager.startAction = function() {
this._aisPhase = false;
_AIS_BattleManager_startAction.call(this);
};
BattleManager.startAutoSkillAction = function(autoSkill) {
var subject = autoSkill.subject;
this._subject = subject;
var action = new Game_Action(subject);
action.setSkill(autoSkill.id);
var targets = !!autoSkill.target ? [autoSkill.target] : action.makeTargets();
this._phase = 'autoSkillAction';
this._aisPhase = true;
this._onAutoSkill = true;
this._action = action;
this._targets = targets;
if (Imported.YEP_BattleEngineCore) {
this.setTargets(targets);
this._allTargets = targets.slice();
this._individualTargets = targets.slice();
this._phase = 'phaseChange';
this._phaseSteps = ['setup', 'whole', 'target', 'follow', 'finish'];
this._returnPhase = '';
this._actionList = [];
}
subject.useItem(action.item());
this._action.applyGlobal();
this.refreshStatus();
this._logWindow.startAction(subject, action, targets);
};
BattleManager.updateAutoSkillAction = function() {
var target = this._targets.shift();
if (target) {
this.invokeAction(this._subject, target);
} else {
this.endAutoSkill();
}
};
BattleManager.endAutoSkill = function() {
this._logWindow.endAction(this._subject);
this._onAutoSkill = false;
this._phase = 'autoSkill';
};
var _AIS_BattleManager_refreshStatus = BattleManager.refreshStatus;
BattleManager.refreshStatus = function() {
_AIS_BattleManager_refreshStatus.call(this);
if (this._subject && !this._onAutoSkill) {
FTKR.setGameData(this._subject, null, null);
this._subject.checkAutoSkillAll();
}
};
//=============================================================================
// Game_Battler
//=============================================================================
var _AIS_Game_Battler_initMembers = Game_Battler.prototype.initMembers;
Game_Battler.prototype.initMembers = function() {
_AIS_Game_Battler_initMembers.call(this);
this.clearRevenge();
};
Game_Battler.prototype.clearRevenge = function() {
this._revenge = {
skillId :false,
stateId :false,
subject :null,
id :-1,
opponent :false,
};
};
//書き換え
Game_Battler.prototype.removeStatesAuto = function(timing) {
this.states().forEach(function(state) {
if (this.isStateExpired(state.id) && state.autoRemovalTiming === timing) {
this.setAssAutoSkill(state);
this.setRevengeData();
this.removeState(state.id);
}
}, this);
};
//書き換え
Game_Battler.prototype.removeStatesByDamage = function() {
this.states().forEach(function(state) {
if (state.removeByDamage && Math.randomInt(100) < state.chanceByDamage) {
this.setAssAutoSkill(state);
this.removeState(state.id);
}
}, this);
};
Game_Battler.prototype.setAssAutoSkill = function(obj) {
var skillId = Number(obj.meta['AIS_解除発動']);
if (skillId) {
this._revenge.skillId = skillId;
this._revenge.subject = this;
this._revenge.stateId = obj.id;
} else {
this._revenge.skillId = false;
this._revenge.subject = null;
this._revenge.stateId = false;
}
};
Game_Battler.prototype.setRevengeData = function(flag) {
var revenge = this._revenge;
if (revenge.skillId) {
flag = flag || revenge.stateId && $dataStates[revenge.stateId].meta['AIS_リベンジターゲット'];
var target = flag ? this.revengeTarget() : null;
if (!BattleManager._autoSkills) BattleManager._autoSkills = [];
var autoSkill = {id:revenge.skillId, subject:revenge.subject, target:target};
if (!this.isSettingAutoSkill(autoSkill)) {
BattleManager._autoSkills.push(autoSkill);
this.clearRevenge();
}
}
};
Game_Battler.prototype.isSettingAutoSkill = function(autoSkill) {
return BattleManager._autoSkills.some( function(_autoSkill){
return _autoSkill.id === autoSkill.id;
});
};
Game_Battler.prototype.revengeTarget = function() {
var revenge = this._revenge;
if (!revenge || revenge.id === -1) return null;
if (revenge.opponent) {
return $gameTroop.members()[revenge.id];
} else {
return $gameParty.members()[revenge.id];
}
};
var _AIS_Game_Battler_removeState = Game_Battler.prototype.removeState;
Game_Battler.prototype.removeState = function(stateId) {
if (!this._removeStates) this._removeStates = [];
this._removeStates.push(stateId);
_AIS_Game_Battler_removeState.call(this, stateId);
};
Game_Battler.prototype.checkAutoSkillAll = function() {
BattleManager._autoSkillList.forEach( function(data){
if (!data) return;
if (evalRefreshConditions(data.formula)) {
this._revenge.skillId = data.id;
this._revenge.subject = this;
this.setRevengeData();
}
},this);
};
//=============================================================================
// Game_Actor
//=============================================================================
var readEntrapmentCodeToTextExTotal = function(objs, metacodes) {
var result = [];
objs.forEach( function(obj) {
var metas = readEntrapmentCodeToTextEx(obj, metacodes);
Array.prototype.push.apply(result, metas);
});
return result;
};
Game_Actor.prototype.checkAutoSkill = function() {
var datas = [];
var metacodes = ['AIS_発動条件', 'AIS_INVOKE_CONDITIONS'];
datas = datas.concat(
readEntrapmentCodeToTextEx(this.actor(), metacodes),
readEntrapmentCodeToTextEx($dataClasses[this.actor().classId], metacodes),
readEntrapmentCodeToTextExTotal(this.equips(), metacodes),
readEntrapmentCodeToTextExTotal(this.states(), metacodes)
);
return datas.some( function(data) {
if (evalCheckConditions(data.text)) {
this._revenge.skillId = data.id;
this._revenge.stateId = data.id;
this._revenge.subject = this;
this.setRevengeData(1);
return true;
}
},this);
};
//=============================================================================
// Game_Action
//=============================================================================
var _AIS_Game_Action_apply = Game_Action.prototype.apply;
Game_Action.prototype.apply = function(target) {
_AIS_Game_Action_apply.call(this, target);
var result = target.result();
if (result.isHit() && (result.hpDamage || result.mpDamage)) {
this.setRevengeTarget(this.subject(), target);
target.setRevengeData();
}
if (!BattleManager._aisPhase) {
$gameParty.members().forEach( function(member) {
// if (member.isDead() || member === this.subject()) return;
if (member.isDead()) return;
FTKR.setGameData(member, target, this.item(), null, this.subject());
this.setRevengeTarget(target, member)
member.checkAutoSkill();
},this);
}
};
Game_Action.prototype.setRevengeTarget = function(target, user) {
var memberId = -1;
if (target.isEnemy()) {
$gameTroop.members().forEach( function(member, i){
if (member === target) memberId = i;
});
} else {
$gameParty.members().forEach( function(member, i){
if (member === target) memberId = i;
});
}
user._revenge.id = memberId,
user._revenge.opponent = target.isEnemy()
};
//=============================================================================
// YEP_BattleEngineCoreの修正
//=============================================================================
if (Imported.YEP_BattleEngineCore) {
var _AIS_Game_BattlerBase_updateStateTurnTiming = Game_BattlerBase.prototype.updateStateTurnTiming;
Game_BattlerBase.prototype.updateStateTurnTiming = function(timing) {
this._removeStates = [];
_AIS_Game_BattlerBase_updateStateTurnTiming.call(this, timing);
if (this._removeStates.length) {
this._removeStates.forEach( function(removeState){
if (!removeState) return;
this.setAssAutoSkill($dataStates[removeState]);
this.setRevengeData();
},this);
}
};
}//YEP_BattleEngineCore
}());//EOF