nyacronomicon/js/plugins/KN_MapBattle.js

554 lines
No EOL
21 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=============================================================================
// RPG Maker MZ - KN_MapBattle.js
//=============================================================================
/*:ja
* @target MZ
* @author kurogoma knights
* @base PluginCommonBase
* @plugindesc マップバトル
*
* @command test
*
* @command SETUP_MAP_BATTLE
* @desc マップバトルの初期化 イベント名をキーにしてエネミーデータを検索する
*
* @command SHOW_ITEM_MENU
* @desc アイテムメニュー表示 スキルもスキルアイテムとして扱ってるので注意。
* @arg itemType @type select @option item @option skill @default item
* @desc 選択ウィンドウに表示するアイテムのタイプ
*
* @command USE_ITEM
* @desc 選択したアイテムを使用する。アイテム名と紐づいて処理が実行されるので注意。
*
* @command SIMULATE_BATTLE
* @arg resultSwitchId @text 勝利フラグを入れるスイッチ @type switch @default 0
*
* @command SHOW_SIMULATE_RESULT
* @arg variableId @text カーソル位置の変数 @type variable @default 0
* @arg victorySE @type file @dir audio/se/
* @arg defeatSE @type file @dir audio/se/
*
* @command RUN_BATTLE
*
* @command CHECK_BATTLE_END
*
* @command PROCESS_WIN_EVENT
*
* @command PROCESS_LOSE_EVENT
*
* @command ADD_STATE_TO_ENEMIES
* @desc 指定した名前の敵全てにステートを付与する
* @arg enemyName
* @text 敵の名前
* @type string
* @desc 対象となる敵の名前(この名前の敵全てに付与)
*
* @arg stateId
* @text ステートID
* @type state
* @desc 付与するステートのID
*
*/
(() => {
'use strict';
const script = document.currentScript;
const param = PluginManagerEx.createParameter(script);
//
// Window_BattleSimResult - シミュレーション結果表示用ウィンドウ
//
class Window_BattleSimResult extends Window_Base {
constructor(rect) {
super(rect);
this.openness = 0; // 初期状態で非表示
this._baseContent = "";
this._battler = null;
this._rotationTimer = 0;
}
setBattler(battler) {
this._battler = battler;
if (battler && !battler._stateIconRotationIndex) {
battler._stateIconRotationIndex = 0;
}
}
refresh(content) {
this._baseContent = content;
this.contents.clear();
this.drawTextEx(content, 0, 0);
this.open();
}
clear() {
this._baseContent = "";
this._battler = null;
this._rotationTimer = 0;
this.contents.clear();
this.close();
}
update() {
super.update();
if (this._battler && this._baseContent && this.isOpen() && this._battler.states) {
const stateList = this._battler.states().filter(state => state && state.iconIndex > 0);
if (stateList.length > 0) {
this._rotationTimer++;
if (this._rotationTimer >= 60) {
this._rotationTimer = 0;
this._battler._stateIconRotationIndex = (this._battler._stateIconRotationIndex + 1) % stateList.length;
this.refreshContent();
}
}
}
}
refreshContent() {
if (this._baseContent && this._battler && this._battler.states) {
const stateList = this._battler.states().filter(state => state && state.iconIndex > 0);
if (stateList.length > 0) {
const currentState = stateList[this._battler._stateIconRotationIndex];
const newIconCode = `\\i[${currentState.iconIndex}]`;
const fixedIcons = [421, 84, 82, 1];
const updatedContent = this._baseContent.replace(
/\\i\[(\d+)\]/g,
(match, iconIndex) => {
const num = parseInt(iconIndex);
return fixedIcons.includes(num) ? match : newIconCode;
}
);
this.contents.clear();
this.drawTextEx(updatedContent, 0, 0);
}
}
}
}
//
// Scene_Map - 分割ウィンドウの追加
//
const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
Scene_Map.prototype.createAllWindows = function() {
_Scene_Map_createAllWindows.call(this);
const rect = this.messageWindowRect();
const wh = this.calcWindowHeight(4, false) + 8;
const rectLeft = new Rectangle(0, 0, rect.width / 2, wh);
const rectRight = new Rectangle(rect.width / 2, 0, rect.width / 2, wh);
this._leftSimWindow = new Window_BattleSimResult(rectLeft);
this.addWindow(this._leftSimWindow);
this._rightSimWindow = new Window_BattleSimResult(rectRight);
this.addWindow(this._rightSimWindow);
};
const _Window_Message_close = Window_Message.prototype.close;
Window_Message.prototype.close = function() {
_Window_Message_close.call(this);
const scene = SceneManager._scene;
if (scene instanceof Scene_Map) {
if (scene._leftSimWindow) scene._leftSimWindow.clear();
if (scene._rightSimWindow) scene._rightSimWindow.clear();
}
};
//
// Plugin Commands
//
// テストメソッド
PluginManagerEx.registerCommand(script, "test", args => {
$gameMap.knTest(args);
});
Game_Map.prototype.knTest = function() {
//
};
// マップバトル初期化!
PluginManagerEx.registerCommand(script, "SETUP_MAP_BATTLE", args => {
$gameMap.knMapBattleInit(args);
});
Game_Map.prototype.knMapBattleInit = function(args) {
const actor = $gameParty.leader(); // パーティのリーダーGame_Actorインスタンス
const interpreter = this.getInterpreter();
const eventId = interpreter.eventId();
const eventName = $dataMap.events[eventId]?.name;
// Look up via $dataTroops first: troop names (Japanese) match Map002 event names
// even when $dataEnemies names have been translated. Troop IDs align with enemy IDs.
const troop = $dataTroops.find(t => t && t.name === eventName);
const dataEnemy = troop ? $dataEnemies[troop.id] : $dataEnemies.find(e => e && e.name === eventName);
if (!dataEnemy) {
console.warn(`[KN_MapBattle] データが見つかりません: ${eventName} (eventId=${eventId})`);
return;
}
// Game_Actorを使ってプレイヤーデータを管理
this._knPlayer = actor;// 直接actorGame_Actorインスタンスを代入
// _knEnemies で敵ごとの状態を管理
if (!this._knEnemies) {
this._knEnemies = {}; // 敵ごとの状態を保持するオブジェクト
}
// すでに敵が初期化されていない場合のみ初期化
if (!this._knEnemies[eventId]) {
// Game_Enemyを使って敵データを管理
const knEnemy = new Game_Enemy(dataEnemy.id); // Game_Enemyインスタンスを生成
knEnemy.isBoss = Kurogoma.RaceEffect.isEnemyBoss(dataEnemy); // RaceEffectManager経由で判定
knEnemy._skillUsageCount = 0; // スキル使用回数
knEnemy._itemUsageCount = 0; // アイテム使用回数
knEnemy._skillUsageLimit = Number(dataEnemy.meta.SkillUsageLimit) || 1; // デフォルト1回
knEnemy._itemUsageLimit = Number(dataEnemy.meta.ItemUsageLimit) || 1; // デフォルト1回
// 敵の状態をイベントIDで管理
this._knEnemies[eventId] = knEnemy; // eventIdをキーにしてknEnemyを格納
}
// 現在の敵データを取得
this._knEnemy = this._knEnemies[eventId]; // 既に初期化されている敵データを取得
// 辞書登録
const enemyName = this._knEnemy.name();
const hiddenItem = $dataItems.find(item =>
item &&
item.name === enemyName &&
(item.itypeId === 3 || item.itypeId === 4) &&
!$gameParty.hasItem(item) // すでに持っている場合は除外
);
if (hiddenItem) {
$gameParty.gainItem(hiddenItem, 1); // アイテムを1つ入手
console.log(`"${hiddenItem.name}" を登録しました`);
}
};
// アイテム(スキル)選択画面を開く
PluginManagerEx.registerCommand(script, "SHOW_ITEM_MENU", args => {
$gameMap.knShowItemMenu(args.itemType);
});
Game_Map.prototype.knShowItemMenu = function(itemType) {
const args = { categoryType: itemType };
PluginManager.callCommand($gameMap.getInterpreter(), "KN_EventItem", "SET_CATEGORY", args);
$gameMessage.setItemChoice(1, 1); // $gameMessage.setItemChoice(代入する変数,アイテムタイプ[1通常アイテム/2:大事なもの...])
this.getInterpreter().setWaitMode('message');
}
// アイテム(スキル)を使用する
PluginManagerEx.registerCommand(script, "USE_ITEM", args => {
$gameMap.knUseItem(args);
});
Game_Map.prototype.knUseItem = function(args) {
const item = $dataItems[$gameVariables.value(1)]; // 選択されたアイテム
if (!item)
return;
// 実行時に必要な情報をまとめて渡す
const context = {
subject: $gameParty.leader(),
target: this._knEnemy,
interpreter: this.getInterpreter(),
item: item,
};
Kurogoma.MapBattleActionManager.executeAction(item.name, context);
}
// シミュレートバトル
PluginManagerEx.registerCommand(script, "SIMULATE_BATTLE", args => {
$gameMap.knSimulateBattle(args);
});
Game_Map.prototype.knSimulateBattle = function(args) {
const actor = this._knPlayer;
const enemy = this._knEnemy;
if (!actor || !enemy) {
console.error('[KN_MapBattle] SIMULATE_BATTLE: _knPlayer or _knEnemy is undefined. SETUP_MAP_BATTLE may have failed.');
$gameSwitches.setValue(args.resultSwitchId, false);
return;
}
const result = Kurogoma.MapBattleSimulator.simulateBattle(actor, enemy);
this._lastSimResult = result;
$gameSwitches.setValue(args.resultSwitchId, result.winner == "actor");
}
// シミュレーション結果表示コマンド(先にシミュレートバトルを実行している想定)
PluginManagerEx.registerCommand(script, "SHOW_SIMULATE_RESULT", args => {
$gameMap.knShowSimResult(args);
});
Game_Map.prototype.knShowSimResult = function(args) {
const result = this._lastSimResult;
if (!result) {
console.warn('[KN_MapBattle] 直前のシミュレーション結果がありません');
return false;
}
// 変数設定(カーソル位置)
let selectIndex = 0;
if (Kurogoma.Option.isBattleSafeMode() || Kurogoma.Option.isBattleSkipMode()) {
selectIndex = result.winner == "actor" ? 0 : -1; // 敗北時は外す
}
$gameVariables.setValue(args.variableId, selectIndex);
const face = result.winner == "actor" ? "nyahuhun" : "bikkuri2";
$gameVariables.setValue(Kurogoma.VariableConst.Face, face); // 表情変数を指す
// SE再生
if (args.victorySE && result.winner == "actor") {
const victorySE = args.victorySE.split("/").pop();
playSe(victorySE);
} else if (args.defeatSE && result.winner != "actor") {
const defeatSE = args.defeatSE.split("/").pop();
playSe(defeatSE);
}
// 分割ウィンドウ表示
const scene = SceneManager._scene;
if (scene instanceof Scene_Map) {
const rect = scene.messageWindowRect();
const wh = scene.calcWindowHeight(4, false) + 8;
const positionType = 2; // 下
const y = (positionType * (Graphics.boxHeight - wh)) / 2 + rect.y;
scene._leftSimWindow.move(0, y, rect.width / 2, wh);
scene._rightSimWindow.move(rect.width / 2, y, rect.width / 2, wh);
scene._leftSimWindow.setBattler(result.enemy);
scene._rightSimWindow.setBattler(result.actor);
scene._leftSimWindow.refresh(result.enemyLog);
scene._rightSimWindow.refresh(result.actorLog);
}
return true;
}
// バトル実行
PluginManagerEx.registerCommand(script, "RUN_BATTLE", args => {
$gameMap.knRunBattle(args);
});
Game_Map.prototype.knRunBattle = function(args) {
const actor = this._knPlayer;
const enemy = this._knEnemy;
const context = {
subject: actor,
target: enemy,
item: { name: "通常攻撃", meta: { "倍率": 1, "TP消費": 0 } },
interpreter: $gameMap.getInterpreter(),
};
Kurogoma.MapBattleActionManager.executeAction("攻撃", context);
}
// バトル終了処理
PluginManagerEx.registerCommand(script, "CHECK_BATTLE_END", args => {
$gameMap.knCheckBattleEnd(args);
});
Game_Map.prototype.knCheckBattleEnd = function(args) {
this._knBattleResultType = "";
if (this._knEnemy.hp <= 0)
this._knBattleResultType = "win";
else if (this._knPlayer.hp <= 0)
this._knBattleResultType = "lose";
else
this._knBattleResultType = "draw";
}
// バトル終了処理(win)
PluginManagerEx.registerCommand(script, "PROCESS_WIN_EVENT", args => {
$gameMap.knActionWin(args);
});
Game_Map.prototype.knActionWin = function(args) {
const context = {
subject: this._knPlayer,
target: this._knEnemy,
interpreter: $gameMap.getInterpreter(),
};
Kurogoma.MapBattleActionManager.executeAction("勝利", context);
}
// バトル終了処理(lose)
PluginManagerEx.registerCommand(script, "PROCESS_LOSE_EVENT", args => {
$gameMap.knActionLose(args);
});
Game_Map.prototype.knActionLose = function(args) {
const context = {
subject: this._knPlayer,
target: this._knEnemy,
interpreter: $gameMap.getInterpreter(),
};
Kurogoma.MapBattleActionManager.executeAction("敗北", context);
}
// 指定した名前の敵全てにステートを付与
PluginManagerEx.registerCommand(script, "ADD_STATE_TO_ENEMIES", args => {
$gameMap.knAddStateToEnemies(args);
});
Game_Map.prototype.knAddStateToEnemies = function(args) {
const enemyName = args.enemyName;
const stateId = Number(args.stateId);
if (!enemyName || !stateId) {
console.warn('[KN_MapBattle] ADD_STATE_TO_ENEMIES: 敵の名前またはステートIDが指定されていません');
return;
}
// _knEnemiesの初期化
if (!this._knEnemies) {
this._knEnemies = {};
}
let count = 0;
// まず既に初期化されている敵をチェック
for (const eventId in this._knEnemies) {
const enemy = this._knEnemies[eventId];
if (enemy && enemy.name() === enemyName) {
count++;
}
}
// 見つからなければマップイベントから探して初期化
if (count === 0 && $dataMap && $dataMap.events) {
const dataEnemy = $dataEnemies.find(e => e && e.name === enemyName);
if (dataEnemy) {
for (let i = 1; i < $dataMap.events.length; i++) {
const event = $dataMap.events[i];
if (event && event.name === enemyName) {
// 敵を初期化
const knEnemy = new Game_Enemy(dataEnemy.id);
knEnemy.isBoss = Kurogoma.RaceEffect.isEnemyBoss(dataEnemy); // RaceEffectManager経由で判定
knEnemy._skillUsageCount = 0;
knEnemy._itemUsageCount = 0;
knEnemy._skillUsageLimit = Number(dataEnemy.meta.SkillUsageLimit) || 1;
knEnemy._itemUsageLimit = Number(dataEnemy.meta.ItemUsageLimit) || 1;
this._knEnemies[i] = knEnemy;
console.log(`[KN_MapBattle] "${enemyName}" (イベント${i}) を初期化しました`);
}
}
}
}
// ステート付与
count = 0;
for (const eventId in this._knEnemies) {
const enemy = this._knEnemies[eventId];
if (enemy && enemy.name() === enemyName) {
enemy.addState(stateId);
count++;
console.log(`[KN_MapBattle] "${enemyName}" (イベント${eventId}) にステート${stateId}を付与しました`);
}
}
if (count === 0) {
console.warn(`[KN_MapBattle] ADD_STATE_TO_ENEMIES: "${enemyName}" という名前の敵が見つかりませんでした`);
} else {
const stateName = $dataStates[stateId]?.name || `State ${stateId}`;
notify(`Applied ${stateName} to ${enemyName}...!`);
console.log(`[KN_MapBattle] ADD_STATE_TO_ENEMIES: ${count}体の敵にステートを付与しました`);
}
}
//
// Game_Map
//
// バトル結果(主にイベントコマンドから参照する用)
Game_Map.prototype.isMapBattleResultWin = function(args) {
return this._knBattleResultType == "win";
}
Game_Map.prototype.isMapBattleResultLose = function(args) {
return this._knBattleResultType == "lose";
}
Game_Map.prototype.isMapBattleResultDraw = function(args) {
return this._knBattleResultType == "draw";
}
// 移動時に敵情報をクリアする
var _Game_Map_setup = Game_Map.prototype.setup;
Game_Map.prototype.setup = function(mapId) {
_Game_Map_setup.apply(this, arguments);
// _knEnemies をクリア(マップ移動時)
this._knEnemies = {};
}
// 現在の敵に対するスキル残り使用回数を取得(コマンドウィンドウ表示用)
Game_Map.prototype.getEnemyRemainingSkillCount = function() {
if (!this._knEnemy) return 0;
const actor = $gameParty.leader();
return Kurogoma.MapBattleActionManager.getRemainingUsageCount(this._knEnemy, actor, 'skill');
}
// 現在の敵に対するアイテム残り使用回数を取得(コマンドウィンドウ表示用)
Game_Map.prototype.getEnemyRemainingItemCount = function() {
if (!this._knEnemy) return 0;
const actor = $gameParty.leader();
return Kurogoma.MapBattleActionManager.getRemainingUsageCount(this._knEnemy, actor, 'item');
}
//
// Game_System
//
Game_System.prototype.isBossBattle = function() {
return $gameMap._knEnemy?.isBoss ?? false;
};
//
// Game_Actor
//
// レベルアップ時にメッセージ出す
const _Game_Actor_levelUp = Game_Actor.prototype.levelUp;
Game_Actor.prototype.levelUp = function() {
_Game_Actor_levelUp.call(this);
const se = { name: "Saint9", volume: 90, pitch: 100, pan: 0 };
AudioManager.playSe(se);
notify("Shiro leveled up!");
const args = {
character: "-1",
value: "Level UP!!",
setting: '{"reverse":"true", "type":"HP", "critical":"false"}',
};
PluginManager.callCommand($gameMap.getInterpreter(), "CharacterPopupDamage", "POPUP_DAMAGE", args);
// ボス撃破演出中はレベルアップフラッシュをスキップ
if (!$gameTemp._skipLevelUpFlash) {
$gameScreen.startFlash([255, 255, 255, 64], 20);
}
};
//
// Window_ChoiceList
//
// 連続でボタンを受け付けないようにした
Window_ChoiceList.prototype.isCancelTriggered = function() {
return Input.isTriggered('cancel');
};
//
// Utils
//
// メッセージぽい!
function notify(msg) {
Torigoya.NotifyMessage.Manager.notify(
new Torigoya.NotifyMessage.NotifyItem({ message: msg, icon: 0 })
);
}
// SE
function playSe(name) {
const se = { name: name, volume: 90, pitch: 100, pan: 0 };
AudioManager.playSe(se);
}
})();