/*============================================================================= IconDescription.js ---------------------------------------------------------------------------- (C)2023 Triacontane This software is released under the MIT License. http://opensource.org/licenses/mit-license.php ---------------------------------------------------------------------------- Version 1.1.0 2025/09/30 マウスオーバーでアイコン説明を表示できる機能を追加 画面左上にアイコン説明が誤って表示される場合がある問題を修正 1.0.3 2024/12/26 ヘルプテキストやメッセージウィンドウにアイコンを表示したとき、切り替わり後も判定が残ってしまう問題を修正 スクロールするウィンドウでアイコン判定の位置がずれる問題を修正 1.0.2 2023/08/20 アイコンが一度表示され消去された後もクリック判定残ってしまう場合がある問題を修正 1.0.1 2023/07/29 表示位置の微調整 1.0.0 2023/07/29 初版 ---------------------------------------------------------------------------- [Blog] : https://triacontane.blogspot.jp/ [Twitter]: https://twitter.com/triacontane/ [GitHub] : https://github.com/triacontane/ =============================================================================*/ /*: @plugindesc Icon Description Plugin @author Triacontane @url https://github.com/triacontane/RPGMakerMV/tree/mz_master/IconDescription.js @license MIT License @help English Help Translator: munokura This is an unofficial English translation of the plugin help, created to support global RPG Maker users. Feedback is welcome to improve translation quality (see: https://github.com/munokura/triacontane-MV-plugins ). Original plugin by Triacontane. Please check the latest official version at: https://triacontane.blogspot.com ----- You can display a popup with the description text for an icon. Click or hold down an icon to display the popup. Any icon displayed by default, such as a message window or status window, will be eligible for the popup, but icons displayed by other plugins may not be displayed. Terms of Use: You may modify and redistribute this plugin without permission from the author, and there are no restrictions on its use (commercial, 18+, etc.). This plugin is now yours. @param list @text Icon Description List @desc A list of icon descriptions. @default [] @type struct[] @param fontSize @text Font size @desc Font size for the icon description window. Specify 0 for the default size. @default 22 @type number @param padding @text margin @desc Margins for the icon description window. Specify 0 for the default margins. @default 8 @type number @param backOpacity @text background opacity @desc The background opacity of the icon description window. Specify 0 for the default opacity. @default 0 @type number @param triggerType @text Trigger Type @desc This is a trigger to display the icon explanation window. Mouse over only works in a PC environment. @default click @type select @option click @value click @option Keep pressing @value press @option Mouseover @value hover @param se @text sound effects @desc This is the sound effect that plays when the icon description window is displayed. @type struct */ /*~struct~Description: @param iconIndex @text icon @desc The index of the icon to display the description for. @default 1 @type icon @param caption @text Explanatory text @desc This is the description of the icon. Control characters can be used. @type multiline_string */ /*~struct~SE: @param name @text SE file name @desc The file name of the SE. @type file @require 1 @dir audio/se/ @param volume @text SE Volume @desc This is the volume of the SE. @default 90 @type number @min 0 @max 100 @param pitch @text SE Pitch @desc This is the SE pitch. @default 100 @type number @min 50 @max 150 @param pan @text SE Balance @desc This is the left and right balance of the SE. @default 0 @type number @min -100 @max 100 */ /*:ja @plugindesc アイコン説明プラグイン @author トリアコンタン @url https://github.com/triacontane/RPGMakerMV/tree/mz_master/IconDescription.js @help アイコンの説明テキストをポップアップ表示できます。 アイコンをクリックするか、押し続けることでポップアップが表示されます。 メッセージウィンドウやステータスウィンドウなど、デフォルトの方法で 表示されたあらゆるアイコンがポップアップ対象になりますが 他のプラグインによって表示されたアイコンは表示できるとは限りません。 利用規約: 作者に無断で改変、再配布が可能で、利用形態(商用、18禁利用等) についても制限はありません。 このプラグインはもうあなたのものです。 @param list @text アイコン説明リスト @desc アイコン説明のリストです。 @default [] @type struct[] @param fontSize @text フォントサイズ @desc アイコン説明ウィンドウのフォントサイズです。0を指定するとデフォルトのサイズになります。 @default 22 @type number @param padding @text 余白 @desc アイコン説明ウィンドウの余白です。0を指定するとデフォルトの余白になります。 @default 8 @type number @param backOpacity @text 背景不透明度 @desc アイコン説明ウィンドウの背景不透明度です。0を指定するとデフォルトの不透明度になります。 @default 0 @type number @param triggerType @text トリガータイプ @desc アイコン説明ウィンドウを表示するトリガーです。マウスオーバーはPC環境でのみ有効に動作します。 @default click @type select @option クリック @value click @option 押し続け @value press @option マウスオーバー @value hover @param se @text 効果音 @desc アイコン説明ウィンドウを表示する際に演奏する効果音です。 @type struct */ /*~struct~Description:ja @param iconIndex @text アイコン @desc 説明を表示するアイコンのインデックスです。 @default 1 @type icon @param caption @text 説明テキスト @desc アイコンの説明文です。制御文字が使えます。 @type multiline_string */ /*~struct~SE:ja @param name @text SEファイル名 @desc SEのファイル名です。 @type file @require 1 @dir audio/se/ @param volume @text SEボリューム @desc SEのボリュームです。 @default 90 @type number @min 0 @max 100 @param pitch @text SEピッチ @desc SEのピッチです。 @default 100 @type number @min 50 @max 150 @param pan @text SEバランス @desc SEの左右バランスです。 @default 0 @type number @min -100 @max 100 */ (() => { 'use strict'; const script = document.currentScript; const param = PluginManagerEx.createParameter(script); function findIconCaptionParam(iconIndex) { return param.list.find(desc => desc.iconIndex === iconIndex); } const _Window_Base_drawIcon = Window_Base.prototype.drawIcon; Window_Base.prototype.drawIcon = function (iconIndex, x, y) { _Window_Base_drawIcon.apply(this, arguments); this.appendIconMap(iconIndex, x, y); }; Window_Base.prototype.clearIconMap = function () { if (this._iconMap) { this._iconMap.clear(); } }; const _Window_Help_refresh = Window_Help.prototype.refresh; Window_Help.prototype.refresh = function (text) { this.clearIconMap(); _Window_Help_refresh.apply(this, arguments); }; const _Window_Message_startMessage = Window_Message.prototype.startMessage; Window_Message.prototype.startMessage = function () { this.clearIconMap(); _Window_Message_startMessage.apply(this, arguments); }; const _Window_Selectable_refresh = Window_Selectable.prototype.refresh; Window_Selectable.prototype.refresh = function () { this.clearIconMap(); _Window_Selectable_refresh.apply(this, arguments); }; Window_Base.prototype.appendIconMap = function (iconIndex, x, y) { if (!findIconCaptionParam(iconIndex)) { return; } if (!this._iconMap) { this._iconMap = new Map(); } this._iconMap.set(`${x}:${y}`, { index: iconIndex, x: x + this.padding, y: y + this.padding }); }; const _Window_Base_update = Window_Base.prototype.update; Window_Base.prototype.update = function () { _Window_Base_update.apply(this, arguments); if (this._iconMap && this.isOpen() && this.visible) { this.updateIconCaption(); } }; Window_Base.prototype.updateIconCaption = function () { if (SceneManager.isTriggeredIconCaption()) { const touchPos = new Point(TouchInput.x, TouchInput.y); const localPos = this.worldTransform.applyInverse(touchPos); const rect = new Rectangle(0, 0, ImageManager.iconWidth, ImageManager.iconHeight); const icon = Array.from(this._iconMap.values()).find(icon => { rect.x = icon.x; rect.y = icon.y - (this.origin.y || 0); return rect.contains(localPos.x, localPos.y); }); if (icon) { const worldPos = this.worldTransform.apply(new Point(0, 0)); const x = worldPos.x + rect.x; const y = worldPos.y + rect.y; SceneManager.addIconCaption(icon.index, x, y); } } }; const _Sprite_StateIcon_update = Sprite_StateIcon.prototype.update; Sprite_StateIcon.prototype.update = function () { _Sprite_StateIcon_update.apply(this, arguments); this.updateIconCaption(); }; Sprite_StateIcon.prototype.updateIconCaption = function () { if (SceneManager.isTriggeredIconCaption() && this.isBeingTouched()) { const localPos = new Point(-this.anchor.x * this.width, -this.anchor.y * this.height); const worldPos = this.worldTransform.apply(localPos); SceneManager.addIconCaption(this._iconIndex, worldPos.x, worldPos.y); } }; Sprite_StateIcon.prototype.isBeingTouched = function () { const touchPos = new Point(TouchInput.x, TouchInput.y); const localPos = this.worldTransform.applyInverse(touchPos); // applyInverseがtouchPosと同じ値を返すケースがあり原因不明のため対症療法 if (touchPos.x === localPos.x && touchPos.y === localPos.y) { const win = this.parent.parent; if (win.x !== 0 || win.y !== 0) { return false; } } 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); }; SceneManager.isTriggeredIconCaption = function () { if (param.triggerType === 'press') { return TouchInput.isPressed(); } else if (param.triggerType === 'hover') { return true; } else { return TouchInput.isTriggered(); } }; SceneManager.isCanceledIconCaption = function () { if (!this.isExistIconCaption()) { return false; } if (param.triggerType === 'press') { return !TouchInput.isPressed(); } else if (param.triggerType === 'hover') { return this._iconDescFrame + 10 < Graphics.frameCount; } else { return TouchInput.isTriggered(); } }; SceneManager.isExistIconCaption = function () { return !!this._iconCaption; } const _SceneManager_updateMain = SceneManager.updateMain; SceneManager.updateMain = function () { _SceneManager_updateMain.apply(this, arguments); if (this.isCanceledIconCaption()) { this.removeIconCaption(); } }; SceneManager.addIconCaption = function (index, x, y) { if (!findIconCaptionParam(index)) { return; } this._iconDescFrame = Graphics.frameCount; if (this._iconCaption?.isSameCaption(index, x, y)) { return; } this.removeIconCaption(); if (param.se) { AudioManager.playSe(param.se); } this._iconCaption = new Window_IconCaption(index, x, y); this._scene.addChild(this._iconCaption); TouchInput.update(); }; SceneManager.removeIconCaption = function () { if (!this._iconCaption) { return; } this._scene.removeChild(this._iconCaption); this._iconCaption = null; TouchInput.update(); }; const _SceneManager_onSceneTerminate = SceneManager.onSceneTerminate; SceneManager.onSceneTerminate = function () { _SceneManager_onSceneTerminate.apply(this, arguments); this.removeIconCaption(); }; const _Window_Selectable_processTouch = Window_Selectable.prototype.processTouch; Window_Selectable.prototype.processTouch = function () { if (SceneManager.isExistIconCaption()) { return; } _Window_Selectable_processTouch.apply(this, arguments); }; const _Window_Message_isTriggered = Window_Message.prototype.isTriggered; Window_Message.prototype.isTriggered = function () { if (SceneManager.isExistIconCaption()) { return; } return _Window_Message_isTriggered.apply(this, arguments); }; class Window_IconCaption extends Window_Base { constructor(index, x, y) { super(new Rectangle(x, y + ImageManager.iconHeight, 1, 1)); this._index = index; this._x = x; this._y = y; this.setup(); } isSameCaption(index, x, y) { return this._index === index && this._x === x && this._y === y; } setup() { const text = findIconCaptionParam(this._index).caption; const rect = this.textSizeEx(text); this.updatePadding(); this.width = rect.width + this.padding * 2; this.height = rect.height + this.padding * 2; this.createContents(); this.drawTextEx(text, 0, 0); if (this.y + this.height > Graphics.height) { this.y -= this.height + ImageManager.iconHeight; } if (this.x + this.width > Graphics.width) { this.x = Math.max(0, Graphics.width - this.width); } this.hide(); } update() { super.update(); if (this.windowskin.isReady()) { this.show(); } } resetFontSettings() { super.resetFontSettings(); if (param.fontSize) { this.contents.fontSize = param.fontSize; } } updatePadding() { super.updatePadding(); if (param.padding) { this.padding = param.padding; } } updateBackOpacity() { super.updateBackOpacity(); if (param.backOpacity) { this.backOpacity = param.backOpacity; } } updateIconCaption() { } } window.Window_IconCaption = Window_IconCaption; })();