//============================================================================= // RPG Maker MZ - IM_BattleLayout // // Copyright 2025 I'm moralist/min-pub.com All rights reserved. // This source code or any portion thereof must not be // reproduced or used without licensed in any manner whatsoever. //============================================================================= /*: * @target MZ * @plugindesc [v1.0.9] バトル画面レイアウト一元管理 + シングルステータスウィンドウ。ステートウィンドウ機能は廃止しました。 * @author * * @help * ============================================================================= * ■ 概要 * ============================================================================= * - バトル画面のウィンドウ配置を一元管理するプラグインです。 * - 1) 標準ステータスウィンドウは削除せず、画面外(-9999,-9999)に移動して非表示。 * - 2) 先頭アクター用のHP/MP/TPウィンドウ(幅600)を左下に配置(名前/ステート非表示)。 * - 3) ver1.06でステート専用ウィンドウ関連機能を廃止しました。 * - 4) 「this.drawGauge is not a function」エラー回避のフォールバックを冒頭で定義。 * * ■ バージョン情報 * v1.0.9 (2025/6/05) : 全体をリファクタリング&IM_BattleLayoutMarginをマージ * v1.0.8 (2025/3/18) : プレイヤー行動時ゲージ再描画 + TPゲージ75%超過時にアニメ点滅 : プレイヤー行動時のゲージ再描画を追加 * v1.0.6 (2025/3/18) : ステートウィンドウ機能を廃止 * v1.0.5 (2025/3/18) : 左側メニューに修正 * (以下略) */ (() => { "use strict"; //======================================================================== // プラグイン定義 //======================================================================== const pluginName = "IM_BattleLayout"; const script = document.currentScript; const param = PluginManagerEx.createParameter(script); //======================================================================== // 定数定義 //======================================================================== const PARTY_CMD_ROWS = 6; const ACTOR_CMD_ROWS = 6; const CMD_WINDOW_WIDTH = 240; const SINGLE_STATUS_LINES = 3; const SINGLE_STATUS_WIDTH = 592; const SKILLITEM_ROWS = 6; const SKILLITEM_WIDTH = 960; const GAUGE_MARGIN_X = 40; const LABEL_WIDTH = 60; const LABEL_GAP = 10; const TP_GAUGE_MARGIN_X = 40; const TP_LABEL_WIDTH = 60; const TP_LABEL_GAP = 10; const GAUGE_HEIGHT = 6; const TP_GAUGE_HEIGHT = 9; // 横方向のマージン量 const LEFT_MARGIN = 50; const RIGHT_MARGIN = 50; const LARGER_FONT_SIZE = 36; //======================================================================== // 共通基盤 //======================================================================== //======================================================================== // コアスクリプト変更部 (Window_Base) //======================================================================== // drawGauge フォールバック if (typeof Window_Base.prototype.drawGauge !== "function") { if (typeof Window_Base.prototype.gaugeBackColor !== "function") { Window_Base.prototype.gaugeBackColor = function() { return "#808080"; }; } Window_Base.prototype.drawGauge = function(x, y, width, rate, color1, color2) { const gaugeH = 6; const gaugeY = y + this.lineHeight() - gaugeH - 2; this.contents.fillRect(x, gaugeY, width, gaugeH, this.gaugeBackColor()); const fillW = Math.floor(width * rate); const c1 = color1 || "#ff6060"; const c2 = color2 || "#ffc0c0"; this.contents.gradientFillRect(x, gaugeY, fillW, gaugeH, c1, c2, false); }; } //======================================================================== // コアスクリプト変更部 (Window_PartyCommand) //======================================================================== // パーティコマンド&アクターコマンドのウィンドウ変更 const _Window_PartyCommand_prototype_numVisibleRows = Window_PartyCommand.prototype.numVisibleRows; Window_PartyCommand.prototype.numVisibleRows = function() { return PARTY_CMD_ROWS; }; const _Window_PartyCommand_prototype_hide = Window_PartyCommand.prototype.hide; Window_PartyCommand.prototype.hide = function() { // do nothing }; //======================================================================== // コアスクリプト変更部 (Window_ActorCommand) //======================================================================== // パーティコマンド&アクターコマンドのウィンドウ変更 const _Scene_Battle_prototype_createActorCommandWindow = Scene_Battle.prototype.createActorCommandWindow; Scene_Battle.prototype.createActorCommandWindow = function() { const rect = this.actorCommandWindowRect(); const commandWindow = new Window_ActorCommand(rect); commandWindow.y = Graphics.boxHeight - commandWindow.height; commandWindow.setHandler("skill", this.commandSkill.bind(this)); commandWindow.setHandler("guard", this.commandGuard.bind(this)); // キャンセル禁止(戦う・逃げるに戻さない) this.addWindow(commandWindow); this._actorCommandWindow = commandWindow; }; const _Window_ActorCommand_prototype_numVisibleRows = Window_ActorCommand.prototype.numVisibleRows; Window_ActorCommand.prototype.numVisibleRows = function() { return ACTOR_CMD_ROWS; }; const _Window_ActorCommand_prototype_hide = Window_ActorCommand.prototype.hide; Window_ActorCommand.prototype.hide = function() { // do nothing }; // 通常攻撃は使わない const _Window_ActorCommand_prototype_addAttackCommand = Window_ActorCommand.prototype.addAttackCommand; Window_ActorCommand.prototype.addAttackCommand = function() { // do noghing }; // 無効になってるコマンドは表示しないように const _Window_ActorCommand_prototype_addGuardCommand = Window_ActorCommand.prototype.addGuardCommand; Window_ActorCommand.prototype.addGuardCommand = function() { if (this._actor.canGuard()) { this.addCommand(TextManager.guard, "guard", this._actor.canGuard()); } }; const _Window_ActorCommand_prototype_addItemCommand = Window_ActorCommand.prototype.addItemCommand; Window_ActorCommand.prototype.addItemCommand = function() { // アイテムは今のところ使用予定無いので非表示 // do nothing }; const _Window_ActorCommand_prototype_selectLast = Window_ActorCommand.prototype.selectLast; Window_ActorCommand.prototype.selectLast = function() { if ($gameSwitches.value(15) === false) { _Window_ActorCommand_prototype_selectLast.apply(this, arguments); } else { this.forceSelect(0); $gameSwitches.setValue(15, false); } }; //======================================================================== // コアスクリプト変更部 (Window_BattleLog) //======================================================================== const _Window_BattleLog_prototype_resetFontSettings = Window_BattleLog.prototype.resetFontSettings; Window_BattleLog.prototype.resetFontSettings = function() { this.contents.fontFace = $gameSystem.mainFontFace(); this.contents.fontSize = LARGER_FONT_SIZE; this.resetTextColor(); }; const _Window_BattleLog_prototype_lineHeight = Window_BattleLog.prototype.lineHeight; Window_BattleLog.prototype.lineHeight = function() { return 38; }; const _Window_BattleLog_prototype_itemHeight = Window_BattleLog.prototype.itemHeight; Window_BattleLog.prototype.itemHeight = function() { return this.lineHeight() + 2; }; //======================================================================== // コアスクリプト変更部 (Scene_Battle) //======================================================================== const _Scene_Battle_prototype_partyCommandWindowRect = Scene_Battle.prototype.partyCommandWindowRect; Scene_Battle.prototype.partyCommandWindowRect = function() { const ww = CMD_WINDOW_WIDTH; const wh = this.calcWindowHeight(PARTY_CMD_ROWS, true); const wx = 0; const wy = Graphics.boxHeight - wh; return new Rectangle(wx, wy, ww, wh); }; const _Scene_Battle_prototype_actorCommandWindowRect = Scene_Battle.prototype.actorCommandWindowRect; Scene_Battle.prototype.actorCommandWindowRect = function() { const ww = CMD_WINDOW_WIDTH; const wh = this.calcWindowHeight(ACTOR_CMD_ROWS, true); const wx = 0; const wy = Graphics.boxHeight - wh; return new Rectangle(wx, wy, ww, wh); }; // バトルログの修正 const _Scene_Battle_prototype_logWindowRect = Scene_Battle.prototype.logWindowRect; Scene_Battle.prototype.logWindowRect = function() { const cmdRect = this.partyCommandWindowRect(); const wx = cmdRect.x + cmdRect.width + 10; const wy = 0; const ww = Graphics.boxWidth - ((cmdRect.x + cmdRect.width + 10) * 2); const wh = this.calcWindowHeight(14, false); return new Rectangle(wx, wy, ww, wh); }; // シングルステータスウィンドウ (HP/MP/TP) const _Scene_Battle_createAllWindows = Scene_Battle.prototype.createAllWindows; Scene_Battle.prototype.createAllWindows = function() { _Scene_Battle_createAllWindows.apply(this, arguments); // メッセージウィンドウのフォントサイズ変更 this._messageWindow.contents.fontSize += 12; // 旧ステータスウィンドウを画面外へ if (this._statusWindow) { this._statusWindow.x = -9999; this._statusWindow.y = -9999; this._statusWindow.visible = false; } // 新ステータス(HP/MP/TP) this.createSingleActorWindow(); }; Scene_Battle.prototype.createSingleActorWindow = function() { const rect = this.singleActorWindowRect(); this._singleActorWindow = new Window_SingleActorStatus(rect); this.addWindow(this._singleActorWindow); }; Scene_Battle.prototype.singleActorWindowRect = function() { const ww = SINGLE_STATUS_WIDTH; const wh = this.calcWindowHeight(SINGLE_STATUS_LINES, false); const wx = Graphics.boxWidth - ww; const wy = Graphics.boxHeight - wh; return new Rectangle(wx, wy, ww, wh); }; // スキル等の説明ウィンドウ const _Scene_Battle_prototype_helpWindowRect = Scene_Battle.prototype.helpWindowRect; Scene_Battle.prototype.helpWindowRect = function() { const cmdRect = this.partyCommandWindowRect(); const ww = Graphics.boxWidth - ((cmdRect.x + cmdRect.width + 10) * 2); const wh = this.helpAreaHeight(); const wx = cmdRect.x + cmdRect.width + 10; const wy = this.helpAreaTop(); return new Rectangle(wx, wy, ww, wh); }; const _Scene_Battle_prototype_helpAreaHeight = Scene_Battle.prototype.helpAreaHeight; Scene_Battle.prototype.helpAreaHeight = function() { const numLines = 2; return numLines * (LARGER_FONT_SIZE + 4) + $gameSystem.windowPadding() * 2; }; const _Scene_Battle_prototype_createHelpWindow = Scene_Battle.prototype.createHelpWindow; Scene_Battle.prototype.createHelpWindow = function() { const rect = this.helpWindowRect(); this._helpWindow = new Window_HelpInBattle(rect); this._helpWindow.hide(); this.addWindow(this._helpWindow); }; // スキル/アイテム/敵選択ウィンドウ Scene_Battle.prototype.skillItemEnemyWindowRect = function() { const cmdRect = this.partyCommandWindowRect(); const ww = SKILLITEM_WIDTH; const wh = this.calcWindowHeight(SKILLITEM_ROWS, true); const wx = cmdRect.x + cmdRect.width + 10; const wy = cmdRect.y + cmdRect.height - wh; return new Rectangle(wx, wy, ww, wh); }; const _Scene_Battle_prototype_skillWindowRect = Scene_Battle.prototype.skillWindowRect; Scene_Battle.prototype.skillWindowRect = function() { return this.skillItemEnemyWindowRect(); }; const _Scene_Battle_prototype_itemWindowRect = Scene_Battle.prototype.itemWindowRect; Scene_Battle.prototype.itemWindowRect = function() { return this.skillItemEnemyWindowRect(); }; const _Scene_Battle_prototype_enemyWindowRect = Scene_Battle.prototype.enemyWindowRect; Scene_Battle.prototype.enemyWindowRect = function() { return this.skillItemEnemyWindowRect(); }; // バトルシーンに入ったときだけバトルウィンドウ類が生成される時点(createDisplayObjects)で // boxWidth を書き換えて左右マージンを適用する const _Scene_Battle_createDisplayObjects = Scene_Battle.prototype.createDisplayObjects; Scene_Battle.prototype.createDisplayObjects = function() { // バトルシーン開始処理(ウィンドウ生成前) // いまの boxWidth を保持 this._originalBoxWidthIM = Graphics.boxWidth; // boxWidth を狭める const totalMargin = LEFT_MARGIN + RIGHT_MARGIN; Graphics.boxWidth = Math.max(1, Graphics.boxWidth - totalMargin); // ADV バックログ抑止 if (!!$gameTemp.adv) { $gameTemp.adv.enableBackLog(false); } // 元メソッド実行 → バトルウィンドウ類は「狭めたwidth」で初期化される _Scene_Battle_createDisplayObjects.call(this); }; const _Scene_Battle_terminate = Scene_Battle.prototype.terminate; Scene_Battle.prototype.terminate = function() { // バトルシーン終了処理 // boxWidth を元に戻す if (this._originalBoxWidthIM != null) { Graphics.boxWidth = this._originalBoxWidthIM; } // ADV バックログ復帰 if (!!$gameTemp.adv) { $gameTemp.adv.enableBackLog(true); } // 元メソッド実行 _Scene_Battle_terminate.call(this); }; // タッチUI設定の再表示されるバックボタンの生成を抑止 const _Scene_Battle_prototype_createCancelButton = Scene_Battle.prototype.createCancelButton; Scene_Battle.prototype.createCancelButton = function() { // do nothing }; //======================================================================== // コアスクリプト変更部 (BattleManager) //======================================================================== // ★ ver1.07: プレイヤー行動終了時に再描画 const _BattleManager_endAction = BattleManager.endAction; BattleManager.endAction = function() { _BattleManager_endAction.apply(this, arguments); if (!!SceneManager._scene && !!SceneManager._scene._singleActorWindow) { SceneManager._scene._singleActorWindow.refresh(); } }; //======================================================================== // コアスクリプト変更部 (Game_BattlerBase) //======================================================================== // HP/MP/TP変動で先頭アクターのSingleActor再描画 const _Game_BattlerBase_setHp = Game_BattlerBase.prototype.setHp; Game_BattlerBase.prototype.setHp = function(hp) { const prevHp = this._hp; _Game_BattlerBase_setHp.apply(this, arguments); if (this === $gameParty.members()[0] && this._hp !== prevHp) { if (SceneManager._scene && SceneManager._scene._singleActorWindow) { SceneManager._scene._singleActorWindow.refresh(); } } }; const _Game_BattlerBase_setMp = Game_BattlerBase.prototype.setMp; Game_BattlerBase.prototype.setMp = function(mp) { const prevMp = this._mp; _Game_BattlerBase_setMp.apply(this, arguments); if (this === $gameParty.members()[0] && this._mp !== prevMp) { if (SceneManager._scene && SceneManager._scene._singleActorWindow) { SceneManager._scene._singleActorWindow.refresh(); } } }; const _Game_BattlerBase_setTp = Game_BattlerBase.prototype.setTp; Game_BattlerBase.prototype.setTp = function(tp) { const prevTp = this._tp; _Game_BattlerBase_setTp.apply(this, arguments); if (this === $gameParty.members()[0] && this._tp !== prevTp) { if (SceneManager._scene && SceneManager._scene._singleActorWindow) { SceneManager._scene._singleActorWindow.refresh(); } } }; //======================================================================== // コアスクリプト変更部 (Sprite_Enemy) //======================================================================== // 敵消滅時のエフェクトをモノクロ化 const _Sprite_Enemy_prototype_updateCollapse = Sprite_Enemy.prototype.updateCollapse; Sprite_Enemy.prototype.updateCollapse = function() { this.blendMode = 1; this.setBlendColor([255, 255, 255, 128]); this.opacity *= this._effectDuration / (this._effectDuration + 1); }; //======================================================================== // クラス定義 (Window_HelpInBattle) //======================================================================== // スキル説明ウィンドウの文字サイズを変更 class Window_HelpInBattle extends Window_Help { constructor() { super(...arguments); } resetFontSettings() { this.contents.fontFace = $gameSystem.mainFontFace(); this.contents.fontSize = LARGER_FONT_SIZE; this.resetTextColor(); } lineHeight() { return LARGER_FONT_SIZE + 6; } calcTextHeight(textState) { const lineSpacing = this.lineHeight() - LARGER_FONT_SIZE; const lastFontSize = this.contents.fontSize; const lines = textState.text.slice(textState.index).split("\n"); const textHeight = this.maxFontSizeInLine(lines[0]) + lineSpacing; this.contents.fontSize = lastFontSize; return textHeight; } }; //======================================================================== // クラス定義 (Window_SingleActorStatus) //======================================================================== // Window_SingleActorStatus : 先頭アクターHP/MP/TPのみ表示 // ★ アニメ用フレームカウンタを保持する拡張 // Window_SingleActorStatus に _tpBlinkCount を追加し、 // 毎フレームアップデートして再描画を行う。 // 先にWindow_SingleActorStatus拡張のため、クラス定義の後にupdateメソッドを追加 class Window_SingleActorStatus extends Window_Base { initialize(rect) { super.initialize(rect); this.openness = 0; this.open(); // ★TPゲージ点滅用カウンタ this._tpBlinkCount = 0; this.refresh(); } update() { super.update(); // ゲージを毎フレームアニメするためにTPレートを監視 const actor = $gameParty.members()[0]; if (!actor) return; const rate = actor.tpRate(); // TP 75%超えならカウンタを進め、一定間隔で再描画 if (rate >= 0.75) { this._tpBlinkCount++; // 例えば 4フレームごとに再描画 if (this._tpBlinkCount % 4 === 0) { this.refresh(); } } else { // 75%未満になったら点滅をリセット if (this._tpBlinkCount !== 0) { this._tpBlinkCount = 0; this.refresh(); } } } refresh() { this.contents.clear(); const actor = $gameParty.members()[0]; if (!actor) return; let yy = 0; const lh = this.lineHeight(); // HP this.drawParamLineEx(TextManager.hpA, actor.hp, actor.mhp, actor.hpRate(), yy); yy += lh; // MP this.drawParamLineEx(TextManager.mpA, actor.mp, actor.mmp, actor.mpRate(), yy); yy += lh; // TP this.drawParamLineEx(TextManager.tpA, actor.tp, actor.maxTp(), actor.tpRate(), yy); } drawParamLineEx(label, cur, max, rate, y) { this.contents.fontSize = this.lineHeight() - 8; this.contents.textColor = ColorManager.normalColor(); let gaugeMarginX = GAUGE_MARGIN_X; let labelWidth = LABEL_WIDTH; let labelGap = LABEL_GAP; let gaugeHeight = GAUGE_HEIGHT; let color1 = ColorManager.hpGaugeColor1(); let color2 = ColorManager.hpGaugeColor2(); // TPゲージ用の設定 if (label === TextManager.tpA) { gaugeMarginX = TP_GAUGE_MARGIN_X; labelWidth = TP_LABEL_WIDTH; labelGap = TP_LABEL_GAP; gaugeHeight = TP_GAUGE_HEIGHT; color1 = ColorManager.tpGaugeColor1(); color2 = ColorManager.tpGaugeColor2(); // ★TPゲージが75%以上ならアニメ点滅 if (rate >= 0.75) { // 例: 60フレームで心臓ドキドキ風 (灰色→白→灰色) const blinkPeriod = 40; const c = this._tpBlinkCount % blinkPeriod; // 0.0~1.0に正規化 const baseFrac = c / blinkPeriod; // サイン波(1周期)で 0→1→0 と変化 => ドキドキ感を演出 const fraction = 0.5 - 0.5 * Math.cos(2 * Math.PI * baseFrac); // 0x80(=128)~0xFF(=255)を fraction で補間 => 灰色→白 const grayVal = Math.round(0x80 + (0xFF - 0x80) * fraction); const hexStr = grayVal.toString(16).padStart(2, "0"); color1 = "#" + hexStr + hexStr + hexStr; // color2は常に白 color2 = "#FFFFFF"; } } else if (label === TextManager.mpA) { color1 = ColorManager.mpGaugeColor1(); color2 = ColorManager.mpGaugeColor2(); } const totalW = this.contentsWidth(); const xLeft = gaugeMarginX; const gaugeAll = totalW - gaugeMarginX * 2; const gaugeW = gaugeAll - (labelWidth + labelGap); this.drawText(label, xLeft, y, labelWidth, "left"); const gx = xLeft + labelWidth + labelGap; this.drawCustomGauge(gx, y, gaugeW, rate, color1, color2, gaugeHeight); const text = `${cur}/${max}`; this.drawText(text, gx, y, gaugeW, "right"); } drawCustomGauge(x, y, width, rate, color1, color2, gaugeH) { const gaugeY = y + this.lineHeight() - gaugeH - 2; this.contents.fillRect(x, gaugeY, width, gaugeH, this.gaugeBackColor()); const fillW = Math.floor(width * rate); this.contents.gradientFillRect(x, gaugeY, fillW, gaugeH, color1, color2, false); } hide() { // hide()無視 // do nothing } } window.Window_SingleActorStatus = Window_SingleActorStatus; })();