prison-brave/js/plugins/IM_BattleLayout.js
2025-08-28 10:38:20 -05:00

566 lines
24 KiB
JavaScript
Raw 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 - 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.01.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;
})();