【解決済み】対象がランダムな技を途中で敵が倒れても全弾命中させるプラグイン…まであと一歩

ねつし

ユーザー
ツクールMZにおける対象がランダムな技って途中で敵が倒れると全弾当たらないのがもどかしく感じません?…なので #ChatGPT に相談して作成しましたが、どうも4発に設定しても5発も当たるという、たまに+1発されるみたいで完成にはまだ及びません。


上記のリンク先に実際の挙動を、下記にプラグイン本文を載せてますのでどなたか分かる方が居たらご教授お願いします。

/*:
* @target MZ
* @plugindesc RandomHits 完全固定型(+1防止・TPB安全)
*/

(() => {

const _startAction = BattleManager.startAction;
BattleManager.startAction = function() {
_startAction.call(this);

const action = this._subject?.currentAction();
const hits = Number(action?.item()?.meta?.RandomHits);
if (!hits) return;

this._randomHitsData = {
subject: this._subject,
action,
remaining: hits,
processing: false
};
};

const _updateAction = BattleManager.updateAction;
BattleManager.updateAction = function() {
const data = this._randomHitsData;

// 通常処理
if (!data) {
_updateAction.call(this);
return;
}

// ★ 同一フレーム多重実行防止
if (data.processing) return;
data.processing = true;

if (data.remaining <= 0) {
this._randomHitsData = null;
data.processing = false;
return;
}

const alive = data.action.opponentsUnit().aliveMembers();
if (!alive.length) {
this._randomHitsData = null;
data.processing = false;
return;
}

const target = alive[Math.randomInt(alive.length)];
const action = data.action;
const subject = data.subject;

// アニメ
if (action.item().animationId > 0) {
this._logWindow.showAnimation(subject, [target], action.item().animationId);
}

// ダメージ
action.apply(target);
this._logWindow.displayActionResults(subject, target);

data.remaining--;

// ★ 次フレームで次HITを許可
setTimeout(() => {
if (this._randomHitsData === data) {
data.processing = false;
}
}, 0);
};

})();
 
色々と思うところがありますが、
とりあえず私が調べたことの共有と質問者様への情報提供でお願いしたいことを箇条書きにしておきます。

【質問者様への確認事項】
Q1.実施したい処理は「ランダム攻撃の途中で敵が倒れた場合、攻撃回数が残っていれば別のエネミーにターゲットを移し替える」という認識で合っていますか?
(例)敵エネミーがA,B,Cの3体いて、ランダム4回攻撃が「A→A(ここでAが倒れる)→A→B」となっていた場合、3回目の行動をBまたはCに変更したい。
Q2.見本の動画で使用しているスキルはツクールのデータベースにある「スキル」タブの範囲を「敵単体」としていますか?
 また、メモ欄には"<RandamHits:4>"を記載していますか?

【調査した内容】
1.結論から
BattleManager.startActionで"_startAction.call(this);"を実行しているため、データベースに存在するスキル処理を実行した後に、ランダム処理を実施しています。つまり、質問Q2の通りの場合、元々のスキル処理が行われた後に、追加で<RandamHits:n>に相当する処理を行っているため、結果的に1 + 4の処理が実行されていると思われます。

2. ターゲットとアニメーション処理が一致していない件について
見たところ、攻撃技のアニメーションと実際のダメージを受けている相手が一致していないように見受けられます。
・1.のことを踏まえて"this._logWindow.showAnimation(subject, [target], action.item().animationId);"のタイミングがズレてしまっている
(_startAction.call(this);で既に1回実行されているため)
・BattleManager.updateActionの "const target = alive[Math.randomInt(alive.length)];"でターゲットが書き換わっていること
などが原因と推測されます。


言えることとしては、._logWindowからは直接いじらない方がいいと思います。
object.jsのGame_Action.~~やmanager.jsのBattleManager.processTurnsや.tartActionあたりを確認の上、
「ターゲットが戦闘不能の場合に再抽選する処理」を加えると良いでしょう。
AIはツクールの細かい仕様まで知らないので、鵜吞みにせず、自分の目で仕様を確かめることが大切です。
 
返信ありがとうございます。
Q1についてですが、再現したい挙動はそれで合っていて、リンク先のリプ欄に載せてある通り、スキルの使用範囲やメモ欄も正しく設定してあります。
Q2に関しては、ChatGPTに「メモ欄の数より一発少なくできないか」「対象の敵の位置とアニメーションがズレていないか」など何度も添削してもらったのですが改善できなかったので、自分でもう一度コードを見直して調べて見ようと思います
 
今度はGemininiに添削して貰った所、遂に理想通りの挙動となったのでコードを共有します。
こちらが検証動画でございます


/*:
* @target MZ
* @plugindesc [修正版] ランダム攻撃再抽選(エラー回避・演出同期版)
* @author AI Assistant
*/
(() => {
'use strict';
let isExtraHitProcessing = false;

const _Game_Action_makeTargets = Game_Action.prototype.makeTargets;
Game_Action.prototype.makeTargets = function() {
const item = this.item();
const hits = item ? Number(item.meta.RandomHits) : 0;
if (hits > 0) {
const aliveMembers = this.opponentsUnit().aliveMembers();
return aliveMembers.length > 0 ? [aliveMembers[Math.floor(Math.random() * aliveMembers.length)]] : [];
}
return _Game_Action_makeTargets.call(this);
};

const _BattleManager_startAction = BattleManager.startAction;
BattleManager.startAction = function() {
const subject = this._subject;
const action = subject ? subject.currentAction() : null; // 安全策
const hits = action ? Number(action.item().meta.RandomHits) : 0;

if (hits > 1 && !action._randomSplitDone) {
const item = action.item();
const splitActions = [];
for (let i = 0; i < hits; i++) {
const newAction = new Game_Action(subject);
newAction.setItemObject(item);
newAction._randomSplitDone = true;
if (i > 0) newAction._isExtraPart = true;
splitActions.push(newAction);
}
subject._actions.shift();
subject._actions.unshift(...splitActions);
isExtraHitProcessing = false;
_BattleManager_startAction.call(this);
return;
}

isExtraHitProcessing = !!(action && action._isExtraPart);
_BattleManager_startAction.call(this);
};

const _Window_BattleLog_displayAction = Window_BattleLog.prototype.displayAction;
Window_BattleLog.prototype.displayAction = function(subject, item) {
if (isExtraHitProcessing) return;
_Window_BattleLog_displayAction.call(this, subject, item);
this.push("wait");
};

const _BattleManager_endAction = BattleManager.endAction;
BattleManager.endAction = function() {
_BattleManager_endAction.call(this);
const action = this._subject ? this._subject.currentAction() : null;
if (action && action._isExtraPart) {
this._logWindow._waitCount = 0;
}
};

const _Game_BattlerBase_paySkillCost = Game_BattlerBase.prototype.paySkillCost;
Game_BattlerBase.prototype.paySkillCost = function(skill) {
if (isExtraHitProcessing) return;
_Game_BattlerBase_paySkillCost.call(this, skill);
};
})();
 

Attachments

  • RandomTargetSkillAllBullertsHit_Gemini.js
    2.7 KB · 閲覧: 2
最後に編集:
Back
トップ