//============================================================================= // SubstituteExtend.js // ---------------------------------------------------------------------------- // (C)2017 Triacontane // This software is released under the MIT License. // http://opensource.org/licenses/mit-license.php // ---------------------------------------------------------------------------- // Version // 1.4.2 2024/06/16 身代わりスキル発動でコモンイベントが呼ばれるよう修正 // 1.4.1 2020/06/10 1.4.0の修正でパラメータ「身代わり条件_必中以外」が消えていた問題を修正 // 1.4.0 2020/05/19 行動制約「行動できない」状態でも身代わりが発動するように既存仕様を変更する設定を追加 // 1.3.0 2020/04/14 混乱系のステート有効時は身代わりを発動しないように既存仕様を変更する設定を追加 // 1.2.0 2019/12/22 身代わりの判定仕様を変更し、身代わり後の反撃や魔法反射できる機能を追加 // 1.1.0 2018/09/09 身代わりを無効にするスキルやアイテムを直接指定できる機能を追加 // 1.0.2 2017/08/22 身代わり条件「必中以外」を無効にした場合でも必中攻撃に対する身代わりが発動しない場合がある問題を修正 // 1.0.1 2017/02/07 端末依存の記述を削除 // 1.0.0 2017/02/05 初版 // ---------------------------------------------------------------------------- // [Blog] : https://triacontane.blogspot.jp/ // [Twitter]: https://twitter.com/triacontane/ // [GitHub] : https://github.com/triacontane/ //============================================================================= /*: @url https://triacontane.blogspot.com/ @plugindesc Substitute Extension Plugin @author Triacontane @license MIT License @help English Help Translator: munokura This is an unofficial English translation of the plugin help, created to support global RPG Maker users. Feedback is welcome to improve translation quality (see: https://github.com/munokura/triacontane-MV-plugins ). Original plugin by Triacontane. Please check the latest official version at: https://triacontane.blogspot.com ----- We'll be changing the Substitute mechanics. First, let's explain the Substitute mechanics in RPG Maker MV. - The Substitute - Alive - Not subject to the "Cannot Act" action restriction - Possess the "Substitute" trait - Substituted - Critically injured (HP is 1/4 or less) - Not the same Battler as the Substitute - Skills activated by Substitute - Hit type not "Guaranteed Hit" - Substitute priority The "Substitute Battler" is determined in order, starting from the front of the party. - A commonly misunderstood feature - Substitute is possible even if subject to the "Attack XX" action restriction - The skill's "Range" and the skill's user have no effect on the Substitute determination. (Substitute will also activate for healing and defense if the Hit type is not Guaranteed Hit.) The determination is made only once, so Substitute will not be activated in the following cases: - The first and second Battlers can Substitute - The first Battler is the Substitute target 1. The following default Substitute conditions can be disabled. ・Critical (HP is 1/4 or less) ・Accuracy type is not "Guaranteed Hit" ・Not subject to the "Cannot Act" action restriction Note that if the "Accuracy type is not "Guaranteed Hit" condition is removed, Substitute will be activated indiscriminately, even for unrelated skills such as defense. Please set this carefully. 2. You can set the detailed activation conditions for Substitute in detail. Enter the following in the Note field of the database with the characteristic. Substitute will be activated when "all conditions" in the Note field are met. Generally, this is entered together with the "Substitute" feature. Note that the Note field of the database with the characteristic is the Note field for any of the following: actor, job, weapon, armor, state, or enemy character. - Activates only when the executer's HP is 50% or above. - Activates only when the target's HP is below 50%. (*1) - Restricts the substitute target to [1].(*2) - Activates only when switch [4] is ON. - Activates only when formula [f] is true. (*3) *1 This only determines if the default substitute condition, "near death," is disabled in the parameters. *2 To set the target of the tag, write the following in the Note field of the database with the feature. # Targeted by . Any actor matching the parameters specified in "SubstituteTarget" will be considered a Substitute. For example, if you enter in the Note field, Substitute will only be performed on actors with the feature in the Note field. *3 This is an advanced feature. Also, if you want to use inequality signs in a calculation formula, write it as follows: < → < > → > Example: - Triggered if variable [2] is greater than [3]. Within the formula, you can reference the Action object of the target skill using "action." By using this effectively, you can precisely limit the skills that Substitute activates. Below is an example. - Activates Substitute only for normal attacks - Activates Substitute only for physical attacks - Activates Substitute only for single targets 3. When Substitute is activated, the effect of the skill with the specified ID can be applied to the Substitute performer. - When Substitute is activated, skill [5] is applied to the performer. (*1) *1 Damage popups, animations, and other effects will not be displayed. 4. You can individually specify skills that will disable Substitute. Specify the following in the skill or item Note field. This plugin does not have any plugin commands. Terms of Use: You may modify and redistribute this plugin without permission from the author, and there are no restrictions on its use (commercial, 18+, etc.). This plugin is now yours. @param 身代わり条件_瀕死 @text Substitute Condition: Near Death @desc Enables the default condition for substitution, "dying." Turning it OFF disables it. (ON/OFF) @type boolean @default true @param 身代わり条件_必中以外 @text Substitute Conditions - Other than Guaranteed Hit @desc Enables the default substitute condition "Other than guaranteed hit". Turning it OFF disables it. (ON/OFF) @type boolean @default true @param 身代わり反撃 @text Substitute Counterattack @desc The Substitute effect has been changed, allowing counterattacks and magic reflection after the Substitute effect. @type boolean @default false @param 混乱時の身代わり無効 @text Substitute disabled when confused @desc If the enemy is in a confused state (a state where the action restriction is "attack XX"), the activation of Substitute will be disabled. @type boolean @default false @param 行動不能時の身代わり有効 @text Substitute available when unable to act @desc Even if you are unable to act (a state where the action restriction is "cannot act"), the activation of Substitute will be enabled. @type boolean @default false */ /*:ja @plugindesc 身代わり拡張プラグイン @author トリアコンタン @param 身代わり条件_瀕死 @desc デフォルトの身代わりされる条件である「瀕死」を有効にします。OFFにすると無効になります。(ON/OFF) @default true @type boolean @param 身代わり条件_必中以外 @desc デフォルトの身代わり条件である「必中以外」を有効にします。OFFにすると無効になります。(ON/OFF) @default true @type boolean @param 身代わり反撃 @desc 身代わりの判定仕様を変更し、身代わり後の反撃や魔法反射が有効になります。 @default false @type boolean @param 混乱時の身代わり無効 @desc 混乱(行動制約が「〇〇を攻撃」のステート)状態の場合、身代わりの発動を無効化します。 @default false @type boolean @param 行動不能時の身代わり有効 @desc 行動不能(行動制約が「行動できない」のステート)状態の場合でも、身代わりの発動を有効化します。 @default false @type boolean @help 身代わりの仕様を変更します。 まずRPGツクールMV本体の身代わり仕様について説明します。 ・身代わりする側 生存している 行動制約『行動できない』のステートに掛かっていない 特徴『身代わり』を保持している ・身代わりされる側 瀕死(HPが1/4以下) 身代わりする側と同一バトラーでない ・身代わりが発動するスキル 命中タイプが『必中』でない ・身代わりの優先度 パーティの先頭から順番に『身代わりするバトラー』を判定 ・勘違いされやすい仕様 行動制約『〇〇を攻撃』のステートに掛かっていても身代わり可能 スキルの『範囲』およびスキルの使用者は、身代わり判定とは一切関係ない (命中タイプが必中でないと回復や防御にも身代わりが発動する) 判定は一度だけなので、以下のケースでは身代わりは発動しない ・1番目と2番目のバトラーが身代わり可能 ・1番目のバトラーが身代わり対象 1. デフォルトの身代わり条件である以下を無効化できます。 ・瀕死(HPが1/4以下) ・命中タイプが『必中』でない ・行動制約『行動できない』のステートに掛かっていない なお「命中タイプが『必中』でない」の条件を外すと、防御など無関係の スキルに対しても無差別に身代わりが発動するようになります。 注意して設定してください。 2. 身代わりの詳細な発動条件を細かく設定できます。 特徴を有するデータベースのメモ欄に、以下の通り記述してください。 メモ欄の「全ての条件」を満たした場合に身代わりが発動します。 基本的に「特徴」の「身代わり」とセットで記述します。 なお、特徴を有するデータベースのメモ欄とは アクター、職業、武器、防具、ステート、敵キャラのいずれかのメモ欄です。 # 実行者のHPが50%以上のときのみ発動します。 # 同上 # 対象者のHPが50%以下のときのみ発動します。(※1) # 同上 # 身代わりの対象者を[1]に限定します。(※2) # 同上 # スイッチ[4]がONのときのみ発動します。 # 同上 # 計算式[f]の結果がtrueのときのみ発動します。(※3) # 同上 ※1 パラメータからデフォルトの身代わり条件である「瀕死」を 無効にした場合のみ判定します。 ※2 のタグの対象者を設定したい場合、 特徴を有するデータベースのメモ欄に以下の通り記述します。 # の対象になります。 # 同上 「身代わり対象限定」で指定したパラメータと一致する場合に身代わり対象になります。 例えば、メモ欄にと記入した場合、同じくメモ欄に と記入されている特徴を持つアクターに対してのみ 身代わりを実行します。 ※3 上級者向け機能です。 また、計算式中で不等号を使いたい場合、以下のように記述してください。 < → < > → > 例: # 変数[2]が[3]より大きい場合、発動します。 計算式中では「action」で対象スキルのActionオブジェクトを参照できます。 うまく利用すれば身代わりが発動するスキルを細かく限定できます。 以下が記入例です。 # 通常攻撃のみ身代わり発動 # 物理攻撃のみ身代わり発動 # 単体対象のみ身代わり発動 3. 身代わり発動時に、指定したIDのスキル効果を身代わり実行者に 適用させることができます。 # 身代わり発動時にスキル[5]を実行者に適用。(※1) # 同上 ※1 ダメージポップアップやアニメーション等の演出は表示されません。 4. 身代わりを無効にするスキルを個別指定できます。 スキルもしくはアイテムのメモ欄に以下の通り指定してください。 このプラグインにはプラグインコマンドはありません。 利用規約: 作者に無断で改変、再配布が可能で、利用形態(商用、18禁利用等) についても制限はありません。 このプラグインはもうあなたのものです。 */ (function () { 'use strict'; var pluginName = 'SubstituteExtend'; var metaTagPrefix = 'SE_'; //============================================================================= // ローカル関数 // プラグインパラメータやプラグインコマンドパラメータの整形やチェックをします //============================================================================= var getParamString = function (paramNames) { if (!Array.isArray(paramNames)) paramNames = [paramNames]; for (var i = 0; i < paramNames.length; i++) { var name = PluginManager.parameters(pluginName)[paramNames[i]]; if (name) return name; } return ''; }; var getParamBoolean = function (paramNames) { var value = getParamString(paramNames); return value.toUpperCase() === 'ON' || value.toUpperCase() === 'TRUE'; }; var getMetaValue = function (object, name) { var metaTagName = metaTagPrefix + name; return object.meta.hasOwnProperty(metaTagName) ? convertEscapeCharacters(object.meta[metaTagName]) : undefined; }; var getMetaValues = function (object, names) { for (var i = 0, n = names.length; i < n; i++) { var value = getMetaValue(object, names[i]); if (value !== undefined) return value; } return undefined; }; var convertEscapeCharacters = function (text) { if (text == null) { text = ''; } if (text === true) { return text; } var windowLayer = SceneManager._scene._windowLayer; return windowLayer ? windowLayer.children[0].convertEscapeCharacters(text) : text; }; var convertEscapeTags = function (text) { if (text == null || text === true) text = ''; text = text.replace(/>?/gi, '>'); text = text.replace(/<?/gi, '<'); return text; }; //============================================================================= // パラメータの取得と整形 //============================================================================= var param = {}; param.condDying = getParamBoolean(['CondDying', '身代わり条件_瀕死']); param.condNonCertainHit = getParamBoolean(['CondNonCertainHit', '身代わり条件_必中以外']); param.substituteCounter = getParamBoolean(['SubstituteCounter', '身代わり反撃']); param.invalidConfused = getParamBoolean(['InvalidConfused', '混乱時の身代わり無効']); param.validRestriction = getParamBoolean(['ValidRestriction', '行動不能時の身代わり有効']); //============================================================================= // Game_BattlerBase // 身代わりを実行するかどうかの判定を拡張します。 //============================================================================= var _Game_BattlerBase_isSubstitute = Game_BattlerBase.prototype.isSubstitute; Game_BattlerBase.prototype.isSubstitute = function () { if (param.validRestriction) { this._suppressRestriction = true; } var result = _Game_BattlerBase_isSubstitute.apply(this, arguments) && this.isSubstituteExtend(); this._suppressRestriction = false; return result; }; var _Game_BattlerBase_restriction = Game_BattlerBase.prototype.restriction; Game_BattlerBase.prototype.restriction = function () { return this._suppressRestriction ? 0 : _Game_BattlerBase_restriction.apply(this, arguments); }; Game_BattlerBase.prototype.isSubstituteExtend = function () { if (param.invalidConfused && this.isConfused()) { return false; } return this.isValidSubstituteHpRate() && (param.condDying || this.isValidSubstituteTargetHpRate()) && this.isValidSubstituteSwitch() && this.isValidSubstituteRestriction() && this.isValidSubstituteFormula() && this.isValidSubstituteSkill(); }; Game_BattlerBase.prototype.isValidSubstituteHpRate = function () { var subjectHpRate = this.getSubstituteMetaInfo(['SubjectHPRate', '実行者HP率'], true); if (subjectHpRate) { return this.hpRate() >= subjectHpRate / 100; } return true; }; Game_BattlerBase.prototype.isValidSubstituteTargetHpRate = function () { var targetHpRate = this.getSubstituteMetaInfo(['TargetHPRate', '対象者HP率'], true); if (targetHpRate) { return BattleManager.checkSubstituteTargetHpRate(targetHpRate); } return true; }; Game_BattlerBase.prototype.isValidSubstituteRestriction = function () { var restrictionId = this.getSubstituteMetaInfo(['TargetRestriction', '身代わり対象限定'], true); if (restrictionId) { return BattleManager.checkSubstituteRestriction(restrictionId); } return true; }; Game_BattlerBase.prototype.isValidSubstituteSwitch = function () { var switchId = this.getSubstituteMetaInfo(['SubstituteSwitch', '身代わりスイッチ'], true); if (switchId) { return $gameSwitches.value(switchId); } return true; }; Game_BattlerBase.prototype.isValidSubstituteFormula = function () { var formula = this.getSubstituteMetaInfo(['SubstituteFormula', '身代わり計算式'], false); if (formula) { var action = BattleManager.getSubstituteAction(); return eval(convertEscapeTags(formula)); } return true; }; Game_BattlerBase.prototype.isValidSubstituteSkill = function () { return !getMetaValues(BattleManager.getSubstituteAction().item(), ['SubstituteInvalid', '身代わり無効']); }; Game_BattlerBase.prototype.isEqualSubstituteRestrictionId = function (restrictionId) { var restrictionTargetId = this.getSubstituteMetaInfo(['SubstituteTarget', '身代わり対象者'], true); return restrictionTargetId === restrictionId; }; Game_BattlerBase.prototype.getSubstituteSkillId = function () { return this.getSubstituteMetaInfo(['SubstituteSkillId', '身代わりスキルID'], true); }; Game_BattlerBase.prototype.getSubstituteMetaInfo = function (tagNames, isNumber) { var metaValue; this.traitObjects().some(function (traitObject) { metaValue = getMetaValues(traitObject, tagNames); return !!metaValue; }); return (metaValue && isNumber) ? parseInt(metaValue) : metaValue; }; //============================================================================= // BattleManager // 身代わり対象者の情報を保持して必要に応じて評価します。 //============================================================================= var _BattleManager_invokeAction = BattleManager.invokeAction; BattleManager.invokeAction = function (subject, target) { if (param.substituteCounter) { var realTarget = this.applySubstitute(target); _BattleManager_invokeAction.call(this, subject, realTarget); } else { _BattleManager_invokeAction.apply(this, arguments); } }; var _BattleManager_applySubstitute = BattleManager.applySubstitute; BattleManager.applySubstitute = function (target) { this._substituteTarget = target; var substitute = _BattleManager_applySubstitute.apply(this, arguments); this._substituteTarget = null; if (substitute !== target) { this.applySubstituteEffect(substitute); } return substitute; }; BattleManager.applySubstituteEffect = function (substitute) { var skillId = substitute.getSubstituteSkillId(); if (!skillId) return; var action = new Game_Action(substitute); action.setSkill(skillId); action.apply(substitute); action.applyGlobal(); }; BattleManager.checkSubstituteTargetHpRate = function (hpRate) { return this._substituteTarget ? this._substituteTarget.hpRate() <= hpRate / 100 : true; }; BattleManager.checkSubstituteRestriction = function (restrictionId) { return this._substituteTarget ? this._substituteTarget.isEqualSubstituteRestrictionId(restrictionId) : true; }; BattleManager.getSubstituteAction = function () { return this._action; }; var _BattleManager_checkSubstitute = BattleManager.checkSubstitute; BattleManager.checkSubstitute = function (target) { var resultSubstitute = _BattleManager_checkSubstitute.apply(this, arguments); if (!resultSubstitute) { return this.checkSubstituteDefault(target); } return resultSubstitute; }; BattleManager.checkSubstituteDefault = function (target) { return this.isValidSubstituteDying(target) && this.isValidSubstituteCertainHit(); }; BattleManager.isValidSubstituteDying = function (target) { return !param.condDying || target.isDying(); }; BattleManager.isValidSubstituteCertainHit = function () { return !param.condNonCertainHit || !this._action.isCertainHit(); }; })();