sakura-gozen/js/plugins/iii/iiiBattleStateHelp.js
2025-01-14 14:06:54 -06:00

459 lines
14 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.

//=============================================================================
// iiiBattleStateHelp.js
// ----------------------------------------------------------------------------
// (C) 2024 iii
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
/*:ja
* @target MZ
* @plugindesc ステートアイコン上にマウスホバーでヘルプバルーン表示
* @author iii
* @help
* ■ マウスホバーでのバルーン表示
* ステートのメモ欄に
* <ShowHoverState:バルーン上で表示する文字列>
* と記載したステートのアイコンにマウスカーソルを合わせると説明文が表示されます。
*
* ■ ヘルプ画像表示コマンドの追加(パーティコマンド)
* プラグインパラメータのhelpImagesに表示したい画像を指定してください。
* (指定が無ければコマンド自体表示されません)
*
* -----
* @param helpCommandName
* @text バトル中ヘルプのコマンド名
* @desc 空白の場合、ヘルプコマンド自体が表示されなくなります
* @type string
* @default ヘルプ
*
* @param helpImages
* @text バトル中ヘルプ画像
* @desc 設定されていない場合、ヘルプ画像コマンド自体が表示されなくなります
* @type file[]
*
*/
(() => {
"use strict";
const script = document.currentScript;
const pluginParam = PluginManagerEx.createParameter(script);
const needShowHelp =
pluginParam.helpCommandName && pluginParam.helpImages.length > 0;
/*
=============================================================================
以下、バトル中のマウスホバーでのヘルプバルーン表示対応
=============================================================================
*/
/*******************************************************
* Scene_Battle
*/
const _Scene_Battle_prototype_createDisplayObjects =
Scene_Battle.prototype.createDisplayObjects;
Scene_Battle.prototype.createDisplayObjects = function () {
_Scene_Battle_prototype_createDisplayObjects.call(this);
this._helpBalloon = new StateHelpBalloonSprite();
this._helpBalloon.x = Graphics.width / 2;
this._helpBalloon.y = Graphics.height / 2;
this.addChild(this._helpBalloon);
this._helpBalloon.hide();
if (needShowHelp) {
this._helpSprite = new Sprite();
this._helpSprite.bitmap = new Bitmap(Graphics.width, Graphics.height);
this._helpSprite.hide();
this.addChild(this._helpSprite);
}
};
// 毎フレーム更新
const _Scene_Battle_prototype_update = Scene_Battle.prototype.update;
Scene_Battle.prototype.update = function () {
_Scene_Battle_prototype_update.call(this);
this.updateStateIconHelpBalloon();
};
// バルーン表示判定
Scene_Battle.prototype.updateStateIconHelpBalloon = function () {
let battlers = this._spriteset.battlerSprites();
let targetBattler = null;
// Sprite_Battlerにくっついているアイコンのマウスホバーをチェック
var battlerSprite = battlers.find((bs) => {
const battler = bs._battler;
if (!battler) return false;
const stateSprite = bs._stateIconSprite;
if (!stateSprite) return false;
const icons = bs._battler.allIcons();
if (!icons || icons.length <= 0) return false;
if (!stateSprite._hovered) return false;
return true;
});
targetBattler = battlerSprite?._battler;
// ボス用UIにくっついているアイコンのマウスホバーをチェック
if (!targetBattler && this._currentEnemyInfoWindow) {
const gameEnemy = this._currentEnemyInfoWindow._gameEnemy;
const stateSprite = this._currentEnemyInfoWindow._stateIconSprite;
if (gameEnemy && stateSprite) {
const icons = gameEnemy.allIcons();
if (icons?.length > 0 && stateSprite._hovered) {
targetBattler = gameEnemy;
}
}
}
// ↑の対象がいなければ、Window_BattleStatusにくっついているアイコンのマウスホバーをチェック
if (!targetBattler) {
for (let i = 0; i < this._statusWindow.maxItems(); i++) {
const actor = this._statusWindow.actor(i);
if (!actor) continue;
// Window_StatusBase.prototype.placeStateIcon 参照
const key = "actor%1-stateIcon".format(actor.actorId());
const dict = this._statusWindow._additionalSprites;
if (!dict[key]) continue;
const stateSprite = dict[key];
if (!stateSprite._hovered) continue;
targetBattler = actor;
}
}
// マウスホバー中のBattler存在しないので終了
if (!targetBattler) {
this._helpBalloon.hide();
return;
}
// ステートアイコン番号からはステートIDが逆引きできないので、メタデータ設定されているステートをすべて表示
const states = targetBattler
.traitObjects()
.filter((sData) => sData.meta.ShowHoverState);
if (states.length <= 0) {
this._helpBalloon.hide();
return;
}
this._helpBalloon.ShowBalloon(states);
};
/*******************************************************
* Sprite_StateIconの拡張
* マウスホバー反応するようにしたもの
*/
const Sprite_StateIcon_prototype_initialize =
Sprite_StateIcon.prototype.initialize;
Sprite_StateIcon.prototype.initialize = function () {
Sprite_StateIcon_prototype_initialize.call(this);
this._hovered = false;
};
const Sprite_StateIcon_prototype_update = Sprite_StateIcon.prototype.update;
Sprite_StateIcon.prototype.update = function () {
Sprite_StateIcon_prototype_update.call(this);
this.processTouch();
};
Sprite_StateIcon.prototype.processTouch = function () {
if (this.isClickEnabled()) {
if (this.isBeingTouched()) {
if (!this._hovered && TouchInput.isHovered()) {
this._hovered = true;
}
} else {
this._hovered = false;
}
} else {
this._hovered = false;
}
};
Sprite_StateIcon.prototype.isClickEnabled = function () {
return this.worldVisible;
};
Sprite_StateIcon.prototype.isBeingTouched = function () {
const touchPos = new Point(TouchInput.x, TouchInput.y);
const localPos = this.worldTransform.applyInverse(touchPos);
return this.hitTest(localPos.x, localPos.y);
};
Sprite_StateIcon.prototype.hitTest = function (x, y) {
const rect = new Rectangle(
-this.anchor.x * this.width,
-this.anchor.y * this.height,
this.width,
this.height
);
return rect.contains(x, y);
};
/*******************************************************
* 状態異常ヘルプ表示用バルーンスプライト
*/
class StateHelpBalloonSprite extends Sprite_Clickable {
#mergin = 8;
#stateCache = null;
constructor() {
super();
this.bitmap = new Bitmap(300, 24); // 幅は固定。高さは動的に変わる
}
ShowBalloon(states) {
const lineHei = 24;
let totalHei = this.#mergin * 2;
states.forEach((sData) => {
if (!sData.meta.ShowHoverState) return;
totalHei += lineHei;
});
this.bitmap.resize(this.width, totalHei);
this.height = totalHei;
this.bitmap.clear();
this.bitmap.fillAll("#00000088");
states.forEach((sData, idx) => {
if (!sData.meta.ShowHoverState) return;
this.#drawTextAndIcon(
sData.iconIndex,
sData.meta.ShowHoverState,
this.#mergin,
this.#mergin + idx * lineHei,
this.width,
lineHei
);
}, this);
this.x = TouchInput.x + this.#mergin;
this.y = TouchInput.y + this.#mergin;
this.show();
}
#drawTextAndIcon(iconIndex, text, x, y, w, h) {
const iconW = ImageManager.iconWidth + 4;
this.#drawIcon(iconIndex, x, y);
this.bitmap.drawText(
this.#convertEscapeCharacters(text),
x + iconW,
y,
w - iconW,
h
);
}
#drawIcon = function (iconIndex, x, y, w, h) {
const bitmap = ImageManager.loadSystem("IconSet");
const pw = ImageManager.iconWidth;
const ph = ImageManager.iconHeight;
const sx = (iconIndex % 16) * pw;
const sy = Math.floor(iconIndex / 16) * ph;
const dw = w || 24;
const dh = h || 24;
this.bitmap.blt(bitmap, sx, sy, pw, ph, x, y, dw, dh);
};
#convertEscapeCharacters = function (text) {
/* eslint no-control-regex: 0 */
text = text.replace(/\\/g, "\x1b");
text = text.replace(/\x1b\x1b/g, "\\");
text = text.replace(/\x1bV\[(\d+)\]/gi, (_, p1) =>
$gameVariables.value(parseInt(p1))
);
text = text.replace(/\x1bN\[(\d+)\]/gi, (_, p1) =>
this.actorName(parseInt(p1))
);
text = text.replace(/\x1bP\[(\d+)\]/gi, (_, p1) =>
this.partyMemberName(parseInt(p1))
);
text = text.replace(/\x1bG/gi, TextManager.currencyUnit);
return text;
};
}
/*
=============================================================================
以下、バトル中にヘルプ画像表示するための対応
=============================================================================
*/
const PartyCommandSymbol_Help = "battle_help";
const _Scene_Battle_prototype_createAllWindows =
Scene_Battle.prototype.createAllWindows;
Scene_Battle.prototype.createAllWindows = function () {
_Scene_Battle_prototype_createAllWindows.call(this);
this.createHelpImageCommandWindow();
};
const _Scene_Battle_prototype_createPartyCommandWindow =
Scene_Battle.prototype.createPartyCommandWindow;
Scene_Battle.prototype.createPartyCommandWindow = function () {
_Scene_Battle_prototype_createPartyCommandWindow.call(this);
const commandWindow = this._partyCommandWindow;
commandWindow.setHandler(
PartyCommandSymbol_Help,
this.commandHelp.bind(this)
);
};
Scene_Battle.prototype.createHelpImageCommandWindow = function () {
//
const windowWidth = 300;
const windowHeight = 96;
const rect = new Rectangle(
this._partyCommandWindow.x + this._partyCommandWindow.width,
this._partyCommandWindow.y + this._partyCommandWindow.height / 2,
windowWidth,
windowHeight
);
const helpImageWindow = new Window_HelpImageCommand(rect);
helpImageWindow.deselect();
helpImageWindow.setHandler(
Window_HelpImageCommand.CloseCommandSymbol,
this.helpCancel.bind(this)
);
this.addWindow(helpImageWindow);
this._helpImageCommandWindow = helpImageWindow;
};
Scene_Battle.prototype.commandHelp = function () {
this._helpImageCommandWindow.openHelp(this._helpSprite);
};
Scene_Battle.prototype.helpCancel = function () {
this._helpImageCommandWindow.closeHelp();
this._partyCommandWindow.refresh();
this._partyCommandWindow.activate();
};
const _Window_PartyCommand_makeCommandList =
Window_PartyCommand.prototype.makeCommandList;
Window_PartyCommand.prototype.makeCommandList = function () {
_Window_PartyCommand_makeCommandList.apply(this, arguments);
if (needShowHelp) {
this.addCommand(
pluginParam.helpCommandName,
PartyCommandSymbol_Help,
true
);
}
};
class Window_HelpImageCommand extends Window_HorzCommand {
static CloseCommandSymbol = "cancel";
static PrevCommandSymbol = "prev_page";
static NextCommandSymbol = "next_page";
_helpSprite = null;
_helpIndex = 0;
constructor(rect) {
super(rect);
this.setHandler(
Window_HelpImageCommand.PrevCommandSymbol,
(() => {
this.#changePrevPage();
}).bind(this)
);
this.setHandler(
Window_HelpImageCommand.NextCommandSymbol,
(() => {
this.#changeNextPage();
}).bind(this)
);
this.openness = 0;
}
openHelp(helpSprite) {
this._helpIndex = 0;
this._helpSprite = helpSprite;
this.refresh();
this.activate();
this.open();
}
closeHelp() {
if (this._helpSprite) {
this._helpSprite.hide();
this._helpSprite = null;
}
this.close();
this.deactivate();
}
refresh() {
super.refresh();
if (this._helpSprite) {
const imagePath = pluginParam.helpImages[this._helpIndex];
const paths = imagePath.split("/");
const fileName = paths.pop();
const dirName = paths.join("/");
const helpTex = ImageManager.loadBitmap(dirName + "/", fileName);
this._helpSprite.bitmap = helpTex;
helpTex.addLoadListener(() => {
this._helpSprite.anchor.x = 0.5;
this._helpSprite.anchor.y = 0.5;
this._helpSprite.width = helpTex.width;
this._helpSprite.height = helpTex.height;
this._helpSprite.x = Graphics.width / 2;
this._helpSprite.y = Graphics.height / 2;
this._helpSprite.show();
}, this);
}
}
#changeNextPage() {
this._helpIndex = Math.max(
this._helpIndex + 1,
pluginParam.helpImages.length - 1
);
this.refresh();
this.activate();
}
#changePrevPage() {
this._helpIndex = Math.max(this._helpIndex - 1, 0);
this.refresh();
this.activate();
}
maxCols() {
return 3;
}
makeCommandList() {
super.makeCommandList();
if (pluginParam.helpImages.length >= 2) {
this.addCommand(
"←",
Window_HelpImageCommand.PrevCommandSymbol,
this._helpIndex > 0
);
this.addCommand(
"閉じる",
Window_HelpImageCommand.CloseCommandSymbol,
true
);
this.addCommand(
"→",
Window_HelpImageCommand.NextCommandSymbol,
this._helpIndex < pluginParam.helpImages.length - 1
);
} else {
this.addCommand(
"閉じる",
Window_HelpImageCommand.CloseCommandSymbol,
true
);
}
}
}
})();