/*---------------------------------------------------------------------------* * TorigoyaMZ_NotifyMessage.js v.1.6.0 *---------------------------------------------------------------------------* * Build Date: 2024/03/19 01:26:00 (JST) *---------------------------------------------------------------------------* * Ruたん ( @ru_shalm ) * https://torigoya-plugin.rutan.dev *---------------------------------------------------------------------------*/ /*: * @target MZ * @plugindesc 通知メッセージプラグイン (v.1.6.0) * @version 1.6.0 * @author Ruたん(ru_shalm) * @license public domain * @url https://raw.githubusercontent.com/rutan/torigoya-rpg-maker-plugin/gh-pages/TorigoyaMZ_NotifyMessage.js * * @base TorigoyaMZ_FrameTween * @orderAfter TorigoyaMZ_FrameTween * * @help 通知メッセージプラグイン (v.1.6.0) * https://torigoya-plugin.rutan.dev * * 画面の左下から通知メッセージを表示する機能を追加します。 * 通知メッセージはプラグインコマンドで表示できます。 * * ------------------------------------------------------------ * ■ メモ欄を使ったカスタマイズ * ------------------------------------------------------------ * プラグインコマンドのメモ欄に以下の記述を行うことで * 通知ごとに一部動作をカスタマイズすることができます。 * * ▼ 表示時の効果音を無しにする * * * ------------------------------------------------------------ * ■ 上級者設定:UIエリア余白 * ------------------------------------------------------------ * 通知メッセージの表示位置を調整するための設定です。 * 指定した値の分だけ、上下に余白を追加することができます。 * * 例えば、上端に100、下端に100のような値を設定すると、 * 画面の真ん中の部分だけに通知が表示されるようになります。 * * なお、上端と下端の合計値が画面の高さを超えてしまうと、 * 通知が正常に表示されなくなってしまうためご注意ください。 * * @param base * @text ■ 基本設定 * @type string * * @param baseAnimationDirection * @text 通知の表示方向 * @desc 通知の表示方向を指定します。 * @parent base * @type select * @option 下から上へ * @value bottomToTop * @option 上から下へ * @value topToBottom * @default bottomToTop * * @param baseAppearTime * @text 登場/退場時間 * @desc 通知が画面にスクロールイン/アウトする時間(フレーム数/60=1秒)を指定します。 * @parent base * @type number * @min 0 * @decimals 0 * @default 15 * * @param baseViewTime * @text 表示時間 * @desc 通知の表示時間(フレーム数/60=1秒)を指定します。0にすると画面外に押し出されるまで消えなくなります。 * @parent base * @type number * @min 0 * @decimals 0 * @default 90 * * @param baseFontSize * @text 文字サイズ * @desc 通知メッセージの文字サイズを指定します * @parent base * @type number * @min 1 * @decimals 0 * @default 22 * * @param basePadding * @text 余白サイズ * @desc 通知メッセージの全体的な余白サイズを指定します * @parent base * @type number * @min 0 * @decimals 0 * @default 4 * * @param baseLeftPadding * @text 左側の追加余白サイズ * @desc 通知メッセージの左側の余白サイズを指定します。 * 全体の余白サイズに加え、この分が余白に追加されます。 * @parent base * @type number * @min 0 * @decimals 0 * @default 0 * * @param baseRightPadding * @text 右側の追加余白サイズ * @desc 通知メッセージの右側の余白サイズを指定します。 * 全体の余白サイズに加え、この分が余白に追加されます。 * @parent base * @type number * @min 0 * @decimals 0 * @default 30 * * @param baseItemPadding * @text アイコンと文章の余白 * @desc 通知アイコンとメッセージの間の余白サイズを指定します。 * @parent base * @type number * @min 0 * @decimals 0 * @default 5 * * @param baseSound * @text 効果音 * @desc 通知表示時の効果音を指定します。 * @parent base * @type struct * @default {"name":"Item1","volume":"90","pitch":"100","pan":"0"} * * @param advanced * @text ■ 上級者設定 * @type string * * @param advancedVisibleSwitch * @text 有効スイッチ * @desc このスイッチがONのときのみ画面に通知を表示するようにします。「なし」の場合は常に表示されます。 * @parent advanced * @type switch * @default 0 * * @param advancedBackgroundColor1 * @text 背景グラデーション:左端 * @desc 背景グラデーションの左端の色を指定します。 * 色はCSSの記法で指定できます。 * @parent advanced * @type string * @default rgba(0, 0, 64, 0.6) * * @param advancedBackgroundColor2 * @text 背景グラデーション:右端 * @desc 背景グラデーションの右端の色を指定します。 * 色はCSSの記法で指定できます。 * @parent advanced * @type string * @default rgba(0, 32, 64, 0) * * @param advancedUiPaddingTop * @text UIエリア余白: 上端 * @desc 通知メッセージ表示位置の上側の余白を指定します。 * @parent advanced * @type number * @decimals 0 * @default 5 * * @param advancedUiPaddingBottom * @text UIエリア余白: 下端 * @desc 通知メッセージ表示位置の下側の余白を指定します。 * @parent advanced * @type number * @decimals 0 * @default 5 * * @param advancedAppendScenes * @text 通知を表示するシーンを追加 * @desc マップ以外のシーンを通知表示シーンの対象にします。シーン名を指定してください(例: Scene_Battle ) * @parent advanced * @type string[] * @default [] * * @param advancedKeepMessage * @text シーン遷移しても表示をキープ * @desc 【危険:環境によってはONにすると不具合の原因となります】シーン遷移してもメッセージを全消ししないようにします。 * @parent advanced * @type boolean * @on キープする(危険) * @off キープしない(安全) * @default false * * @command notify * @text 通知の表示 * @desc 指定内容の通知を表示します * * @arg message * @text メッセージ * @desc 通知に表示する文章を指定します。「文章の表示」のコマンドが一部利用できます。 * @type multiline_string * * @arg icon * @text アイコンID * @desc 通知に表示するアイコンのIDを指定します。 * 0の場合は表示しません。 * @type number * @decimals 0 * @default 0 * * @arg note * @text メモ欄 * @desc メモ欄です。 * ツクールのメモ欄と同様に利用できます。 * @type note * * @command notifyWithVariableIcon * @text 通知の表示(アイコン変数指定) * @desc 指定内容の通知を表示します。アイコンのIDを変数で指定します。 * * @arg message * @text メッセージ * @desc 通知に表示する文章を指定します。「文章の表示」のコマンドが一部利用できます。 * @type multiline_string * * @arg iconVariable * @text アイコンID(変数指定) * @desc 通知に表示するアイコンIDが設定された変数を指定します。 * 変数の中身が0またはマイナスの場合は表示しません。 * @type variable * @default 0 * * @arg note * @text メモ欄 * @desc メモ欄です。 * ツクールのメモ欄と同様に利用できます。 * @type note * * @command forceExitNotifications * @text 表示中の全通知を強制終了 * @desc 現在表示中の全通知を強制終了します。 */ /*~struct~Sound: * @param name * @text 効果音ファイル名 * @desc 通知表示時に再生する効果音ファイル * 空っぽの場合は効果音なしになります * @type file * @dir audio/se * * @param volume * @text 効果音の音量 * @desc 通知表示時に再生する効果音の音量(%) * @type number * @min 0 * @max 100 * @decimals 0 * @default 90 * * @param pitch * @text 効果音のピッチ * @desc 通知表示時に再生する効果音のピッチ(%) * @type number * @min 0 * @max 200 * @decimals 0 * @default 100 * * @param pan * @text 効果音の位相 * @desc 通知表示時に再生する効果音の位相 * @type number * @min -100 * @max 100 * @decimals 0 * @default 0 */ (function () { 'use strict'; /** * 指定名のグローバルなオブジェクトを探索する * 例: findGlobalObject('Torigoya.Item') // => window.Torigoya.Item * @param objName */ function findGlobalObject(objName) { if (!objName) return null; const arr = objName.split('.'); let scope = window; for (const name of arr) { scope = scope[name]; if (!scope) return null; } return scope; } /** * プラグインが存在するかチェックする * @param pluginObject * @param errorMessage */ function checkExistPlugin(pluginObject, errorMessage) { if (typeof pluginObject !== 'undefined') return; alert(errorMessage); throw new Error(errorMessage); } /** * プラグインのバージョンをチェックする * @param version * @param requireVersion * @param errorMessage */ function checkPluginVersion(version, requireVersion, errorMessage) { if (isVersionString(version) && isGreaterThanOrEqualVersion(requireVersion, version)) return; alert(errorMessage); throw new Error(errorMessage); } /** * x.y.z 形式のバージョン文字列かどうか判定する * @param version */ function isVersionString(version) { if (typeof version !== 'string') return false; return /^\d+\.\d+\.\d+$/.test(version); } /** * x.y.z 形式のバージョン文字列を分解する * @param version */ function parseVersion(version) { if (!isVersionString(version)) throw new Error(`invalid version: ${version}`); return version.split('.', 3).map((n) => parseInt(n || '0', 10)); } /** * b のバージョンが a 以上であるか判定する(a <= b) * @param a * @param b */ function isGreaterThanOrEqualVersion(a, b) { if (a === b) return true; const version1 = parseVersion(a); const version2 = parseVersion(b); if (version1[0] !== version2[0]) return version1[0] < version2[0]; if (version1[1] !== version2[1]) return version1[1] < version2[1]; return version1[2] < version2[2]; } /** * プラグインのファイル名を取得 */ function getPluginName() { const cs = document.currentScript; return cs ? cs.src.split('/').pop().replace(/\.js$/, '') : 'TorigoyaMZ_NotifyMessage'; } function parseBooleanParam(value, defaultValue) { if (value === undefined) return defaultValue; return String(value).toLowerCase() === 'true'; } function parseIntegerParam(value, defaultValue) { if (value === undefined || value === '') return defaultValue; const intValue = Number.parseInt(String(value), 10); return isNaN(intValue) ? defaultValue : intValue; } function parseStringParam(value, defaultValue) { if (value === undefined) return defaultValue; return String(value); } function parseStringParamList(value, defaultValue) { if (value === undefined || value === '') return defaultValue; return (Array.isArray(value) ? value : JSON.parse(String(value))).map((n) => String(n)); } function parseStructObjectParam(value, defaultValue) { if (value === undefined || value === '') return defaultValue; if (typeof value === 'string') return JSON.parse(value); return value; } const Torigoya = (window.Torigoya = window.Torigoya || {}); function readStructSound(parameters) { parameters = typeof parameters === 'string' ? JSON.parse(parameters) : parameters; return { name: parseStringParam(parameters['name'], ''), volume: parseIntegerParam(parameters['volume'], 90), pitch: parseIntegerParam(parameters['pitch'], 100), pan: parseIntegerParam(parameters['pan'], 0), }; } function readParameter() { const parameters = PluginManager.parameters(getPluginName()); return { version: '1.6.0', base: parseStringParam(parameters['base'], ''), baseAnimationDirection: parseStringParam(parameters['baseAnimationDirection'], 'bottomToTop'), baseAppearTime: parseIntegerParam(parameters['baseAppearTime'], 15), baseViewTime: parseIntegerParam(parameters['baseViewTime'], 90), baseFontSize: parseIntegerParam(parameters['baseFontSize'], 22), basePadding: parseIntegerParam(parameters['basePadding'], 4), baseLeftPadding: parseIntegerParam(parameters['baseLeftPadding'], 0), baseRightPadding: parseIntegerParam(parameters['baseRightPadding'], 30), baseItemPadding: parseIntegerParam(parameters['baseItemPadding'], 5), baseSound: readStructSound( parseStructObjectParam(parameters['baseSound'], { name: 'Item1', volume: 90, pitch: 100, pan: 0 }), ), advanced: parseStringParam(parameters['advanced'], ''), advancedVisibleSwitch: parseIntegerParam(parameters['advancedVisibleSwitch'], 0), advancedBackgroundColor1: parseStringParam(parameters['advancedBackgroundColor1'], 'rgba(0, 0, 64, 0.6)'), advancedBackgroundColor2: parseStringParam(parameters['advancedBackgroundColor2'], 'rgba(0, 32, 64, 0)'), advancedUiPaddingTop: parseIntegerParam(parameters['advancedUiPaddingTop'], 5), advancedUiPaddingBottom: parseIntegerParam(parameters['advancedUiPaddingBottom'], 5), advancedAppendScenes: parseStringParamList(parameters['advancedAppendScenes'], []), advancedKeepMessage: parseBooleanParam(parameters['advancedKeepMessage'], false), }; } checkExistPlugin( Torigoya.FrameTween, '「通知メッセージプラグイン」より上に「[鳥小屋.txt ベースプラグイン] Tweenアニメーション」が導入されていません。', ); checkPluginVersion( Torigoya.FrameTween.parameter.version, '2.1.0', '「[鳥小屋.txt ベースプラグイン] Tweenアニメーション」のバージョンが古いです。アップデートをしてください。', ); Torigoya.NotifyMessage = { name: getPluginName(), parameter: readParameter(), }; (() => { // ------------------------------------------------------------------------- // NotifyItem class NotifyItem { /** * 初期化 * @param {{ message?: string; icon: number; note?: string; }} params */ constructor(params) { this.initialize(params); } initialize(params) { this.message = params.message || ''; this.icon = params.icon; this.note = params.note || ''; this._openness = 0; DataManager.extractMetadata(this); } /** * @return {number} */ get openness() { return this._openness; } /** * @param {number} value */ set openness(value) { if (value < 0) value = 0; if (value > 255) value = 255; this._openness = value; } /** * 表示時に効果音再生が必要であるか? * @returns {boolean} */ isRequirePlaySound() { return !this.meta['noSound']; } /** * 表示時の効果音を再生 * @return {{volume: number, name: string, pitch: number, pan: number}} */ getDisplaySe() { return Torigoya.NotifyMessage.parameter.baseSound; } } Torigoya.NotifyMessage.NotifyItem = NotifyItem; // ------------------------------------------------------------------------- // Window_NotifyMessage /** * 通知メッセージウィンドウ */ class Window_NotifyMessage extends Window_Base { constructor() { const rect = new Rectangle(0, 0, 1, 1); super(rect); this._notifyItem = new NotifyItem({}); this.opacity = 0; this.createBackCoverSprite(); } /** * @return {number} */ get contentsOpacity() { return super.contentsOpacity; } /** * @param {number} value */ set contentsOpacity(value) { super.contentsOpacity = value; this._backCoverSprite.opacity = value; } /** * 背景用スプライトの生成 */ createBackCoverSprite() { this._backCoverSprite = new Sprite(new Bitmap(1, 1)); this.addChildAt(this._backCoverSprite, 0); } /** * 通知メッセージの設定 * @param {NotifyItem} notifyItem */ setup(notifyItem) { this._notifyItem = notifyItem; this.refresh(); } /** * 更新 */ update() { this.contentsOpacity = this._notifyItem.openness; super.update(); } /** * 画面の再描画 */ refresh() { if (!this._notifyItem) return; this.refreshContents(); this.drawItem(); } /** * ウィンドウの再生成 */ refreshContents() { const { width, height } = this.itemSize(); this.width = width + this.padding * 2; this.height = height + this.padding * 2; this.createContents(); this._refreshBackCoverSprite(); } /** * アイテムの描画 */ drawItem() { const { message, icon } = this._notifyItem; const messageRect = this.messageRect(); this.drawTextEx(message, messageRect.x, messageRect.y, messageRect.width); if (icon) this.drawIcon(icon, this.leftPadding(), (this.innerHeight - ImageManager.iconHeight) / 2); } /** * アイテムの描画領域サイズ * @returns {{width: number, height: number}} */ itemSize() { const { icon } = this._notifyItem; const messageRect = this.messageRect(); return { width: Math.min(Graphics.width, messageRect.x + messageRect.width + this.rightPadding()), height: Math.max(messageRect.y + messageRect.height, icon ? ImageManager.iconHeight : 0), }; } /** * 本文領域 * @returns {Rectangle} */ messageRect() { const { message, icon } = this._notifyItem; const { width: messageWidth, height: messageHeight } = this.textSizeEx(message); const gap = messageHeight - ImageManager.iconHeight; const x = this.leftPadding() + (icon ? ImageManager.iconWidth + this.itemPadding() : 0); return new Rectangle( x, icon ? (gap < 0 ? -gap / 2 : 0) : 0, Math.min(messageWidth, this.messageMaxWidth()), messageHeight, ); } /** * 本文領域の最大幅 * @returns {number} */ messageMaxWidth() { const { icon } = this._notifyItem; return ( Graphics.width - this.leftPadding() - this.rightPadding() - (icon ? ImageManager.iconWidth + this.padding : 0) ); } /** * テキストの高さを計算 * @note ツクールMZのコアスクリプトはフォントサイズの変更に対応していないため、自前で計算する * @param textState * @return {number} */ calcTextHeight(textState) { const lastFontSize = this.contents.fontSize; const lineSpacing = this.lineHeight() - this.baseFontSize(); const firstLine = textState.text.slice(textState.index).split('\n')[0]; const textHeight = this.maxFontSizeInLine(firstLine) + lineSpacing; this.contents.fontSize = lastFontSize; return textHeight; } /** * 左側の空きスペースのサイズ * @return {number} */ leftPadding() { return Torigoya.NotifyMessage.parameter.baseLeftPadding; } /** * 右側の空きスペースのサイズ * @return {number} */ rightPadding() { return Torigoya.NotifyMessage.parameter.baseRightPadding; } /** * 背景画像の再生成 * @private */ _refreshBackCoverSprite() { const bitmap = this._backCoverSprite.bitmap; const w = this.width; const h = this.height; const c1 = Torigoya.NotifyMessage.parameter.advancedBackgroundColor1; const c2 = Torigoya.NotifyMessage.parameter.advancedBackgroundColor2; bitmap.resize(w, h); bitmap.gradientFillRect(0, 0, w, h, c1, c2, false); this._backCoverSprite.setFrame(0, 0, w, h); this._backCoverSprite.opacity = this.contentsOpacity; } /** * Paddingの設定 */ updatePadding() { this.padding = Torigoya.NotifyMessage.parameter.basePadding; } /** * フォント設定 */ resetFontSettings() { super.resetFontSettings(); this.contents.fontSize = this.baseFontSize(); } /** * 行間 * @returns {number} */ lineHeight() { return this.contents.fontSize * 1.25; } /** * アイコンとメッセージの間の余白 * @returns {number} */ itemPadding() { return Torigoya.NotifyMessage.parameter.baseItemPadding; } /** * テキストのサイズを取得 * @return {number} */ baseFontSize() { return Torigoya.NotifyMessage.parameter.baseFontSize; } } Torigoya.NotifyMessage.Window = Window_NotifyMessage; // ------------------------------------------------------------------------- // NotifyManagerClass /** * 通知マネージャー */ class NotifyManagerClass { constructor() { this._currentScene = null; this._stacks = []; this._pools = []; this._scrollAnimations = []; this._group = new Torigoya.FrameTween.Group(); this._handlers = []; } /** * 通知登録 * @param {(notifyItem: NotifyItem) => void} handler */ on(handler) { this._handlers.push(handler); } /** * 通知登録の解除 * @param {(notifyItem: NotifyItem) => void} handler */ off(handler) { const index = this._handlers.indexOf(handler); if (index === -1) return; this._handlers.splice(index, 1); } /** * すべての通知登録を解除 */ offAll() { this._handlers.length = 0; } /** * 現在表示中のすべての通知ウィンドウを破棄 * ※シーン遷移時に呼び出される想定 */ clear() { this._currentScene = null; this._stacks.forEach((stack) => { if (!stack.window) return; if (stack.window.parent) stack.window.parent.removeChild(window); stack.window.parent = null; stack.window = null; }); if (!Torigoya.NotifyMessage.parameter.advancedKeepMessage) { this._stacks.length = 0; this._group.clear(); } this._scrollAnimations.forEach((a) => a.abort()); this._scrollAnimations.length = 0; this._pools.forEach((pool) => pool.destroy()); this._pools.length = 0; } /** * 表示先シーンの設定 * @param {Scene_Base} scene */ setScene(scene) { if (this._currentScene === scene) return; if (this._currentScene) this.clear(); this._currentScene = scene; if (!this._currentScene.torigoyaNotifyMessageContainer) { const container = new Sprite(); this._currentScene.torigoyaNotifyMessageContainer = container; this._currentScene.addChild(container); } if (Torigoya.NotifyMessage.parameter.advancedKeepMessage) { this._stacks.forEach((stack) => { stack.window = this._setupWindow(stack.notifyItem); stack.window.y = stack.y; stack.window.contentsOpacity = 255; this._appendToScene(stack.window); }); } } /** * すべての通知を強制終了 */ forceExitNotifications() { this._stacks.forEach((stack) => { this.forceStartExitAnimation(stack); }); } /** * 通知メッセージごとのマージン * @returns {number} */ itemMargin() { return 5; } /** * 更新処理 */ update() { const visible = this.isVisible(); if (visible) this._group.update(); if (this._currentScene && this._currentScene.torigoyaNotifyMessageContainer) this._currentScene.torigoyaNotifyMessageContainer.visible = visible; } /** * 表示中であるか? * @return {boolean} */ isVisible() { if (!this._currentScene) return false; const switchId = Torigoya.NotifyMessage.parameter.advancedVisibleSwitch; if (!switchId) return true; return $gameSwitches.value(switchId); } /** * 通知の発生 * @param {NotifyItem} notifyItem */ notify(notifyItem) { // 非表示中は新規の追加を行わない if (!this.isVisible()) return; const window = this._setupWindow(notifyItem); this._appendToScene(window); const stack = { notifyItem, window, y: 0, appearAnimation: null, waitAnimation: null, exitAnimation: null, }; this._stacks.unshift(stack); this.startAppearAndExitAnimation(stack); this.startScrollAnimation(stack.window.height); // 効果音再生 if (notifyItem.isRequirePlaySound()) { const se = notifyItem.getDisplaySe(); if (se && se.name) AudioManager.playSe(se); } // 通知 this._handlers.forEach((func) => func(notifyItem)); } /** * 通知メッセージの登場/退場アニメーション * @param {*} stack */ startAppearAndExitAnimation(stack) { const viewTime = Torigoya.NotifyMessage.parameter.baseViewTime; const animationDirection = Torigoya.NotifyMessage.parameter.baseAnimationDirection; const topPadding = Torigoya.NotifyMessage.parameter.advancedUiPaddingTop; const bottomPadding = Torigoya.NotifyMessage.parameter.advancedUiPaddingBottom; // スタックの状態を初期化 stack.y = animationDirection === 'topToBottom' ? -stack.window.height + topPadding : Graphics.height - bottomPadding; // ウィンドウの初期状態を設定 stack.window.x = 0; stack.window.y = stack.y; stack.window.contentsOpacity = 0; // 登場アニメーション stack.appearAnimation = this.createAppearAnimation(stack).call(() => stack.waitAnimation ? stack.waitAnimation.start() : null, ); // 停止アニメーション stack.waitAnimation = viewTime > 0 ? this.createWaitAnimation(stack).call(() => stack.exitAnimation ? stack.exitAnimation.start() : null, ) : null; // 退場アニメーション stack.exitAnimation = this.createExitAnimation(stack).call(() => this._destroyStack(stack)); stack.appearAnimation.start(); } /** * 登場時のTweenアニメーションの作成 * @param stack * @return {Tween} */ createAppearAnimation(stack) { const appearTime = Torigoya.NotifyMessage.parameter.baseAppearTime; return Torigoya.FrameTween.create(stack.notifyItem) .group(this._group) .to({ openness: 255 }, appearTime, Torigoya.FrameTween.Easing.easeInOutQuad); } /** * 待機時のTweenアニメーションの作成 * @param stack * @return {Tween} */ createWaitAnimation(stack) { const viewTime = Torigoya.NotifyMessage.parameter.baseViewTime; return Torigoya.FrameTween.create(stack.notifyItem).group(this._group).wait(viewTime); } /** * 終了時のTweenアニメーションの作成 * @param stack * @return {Tween} */ createExitAnimation(stack) { const appearTime = Torigoya.NotifyMessage.parameter.baseAppearTime; return Torigoya.FrameTween.create(stack.notifyItem) .group(this._group) .to({ openness: 0 }, appearTime, Torigoya.FrameTween.Easing.easeInOutQuad); } /** * 通知メッセージリストのスクロールアニメーション * @param {number} newWindowHeight */ startScrollAnimation(newWindowHeight) { const animationDirection = Torigoya.NotifyMessage.parameter.baseAnimationDirection; // 既に動作中のアニメーションがある場合は破棄 this._scrollAnimations.forEach((a) => a.abort()); this._scrollAnimations.length = 0; if (animationDirection === 'topToBottom') { this.applyScrollAnimationForTopToBottom(newWindowHeight); } else { this.applyScrollAnimationForBottomToTop(newWindowHeight); } } /** * 上から下に移動するアニメーションを反映 * @param {number} newWindowHeight */ applyScrollAnimationForTopToBottom(newWindowHeight) { const appearTime = Torigoya.NotifyMessage.parameter.baseAppearTime; const bottomPadding = Torigoya.NotifyMessage.parameter.advancedUiPaddingBottom; let i = 0; while (i < this._stacks.length) { const stack = this._stacks[i]; if (stack.y > Graphics.height) { this._destroyStack(stack); continue; } if (stack.y + stack.window.height > Graphics.height - bottomPadding) { this.forceStartExitAnimation(stack); } stack.y += newWindowHeight + this.itemMargin(); const animation = Torigoya.FrameTween.create(stack.window) .group(this._group) .to({ y: stack.y }, appearTime, Torigoya.FrameTween.Easing.easeInOutQuad) .start(); this._scrollAnimations.push(animation); ++i; } } /** * 下から上に移動するアニメーションを反映 * @param {number} newWindowHeight */ applyScrollAnimationForBottomToTop(newWindowHeight) { const appearTime = Torigoya.NotifyMessage.parameter.baseAppearTime; const topPadding = Torigoya.NotifyMessage.parameter.advancedUiPaddingTop; let i = 0; while (i < this._stacks.length) { const stack = this._stacks[i]; if (stack.y + stack.window.height < 0) { this._destroyStack(stack); continue; } if (stack.y < topPadding) { this.forceStartExitAnimation(stack); } stack.y -= newWindowHeight + this.itemMargin(); const animation = Torigoya.FrameTween.create(stack.window) .group(this._group) .to({ y: stack.y }, appearTime, Torigoya.FrameTween.Easing.easeInOutQuad) .start(); this._scrollAnimations.push(animation); ++i; } } /** * 指定スタックの登場/待機アニメーションを強制終了して終了アニメーションを開始 * @param {*} stack */ forceStartExitAnimation(stack) { if (!stack.appearAnimation.finished) { stack.appearAnimation.abort(); if (stack.waitAnimation) stack.waitAnimation.abort(); stack.exitAnimation.start(); } else if (stack.waitAnimation && !stack.waitAnimation.finished) { stack.waitAnimation.abort(); stack.exitAnimation.start(); } } /** * 通知メッセージをもとにウィンドウを初期化 * @param {NotifyItem} notifyItem * @returns {Window_NotifyMessage} * @private */ _setupWindow(notifyItem) { const window = this._createOrGetFromPoolWindow(); window.setup(notifyItem); window.x = 0; window.y = 0; window.contentsOpacity = 0; return window; } /** * ウィンドウをプールから取得、ない場合は生成する * @returns {Window_NotifyMessage} * @private */ _createOrGetFromPoolWindow() { const window = this._pools.pop(); if (window) return window; return new Window_NotifyMessage(); } /** * 使用済みウィンドウをプールに戻す * @param {Window_NotifyMessage} window * @private */ _releaseWindow(window) { if (window.parent) window.parent.removeChild(window); window.parent = null; this._pools.push(window); } /** * シーンへのaddChild * @param {Window_NotifyMessage} window * @private */ _appendToScene(window) { if (!this._currentScene) return; if (!this._currentScene.torigoyaNotifyMessageContainer) return; this._currentScene.torigoyaNotifyMessageContainer.addChild(window); } /** * スタックから指定の通知を削除 * @param {*} stack * @private */ _destroyStack(stack) { const index = this._stacks.indexOf(stack); if (index === -1) return; this._stacks.splice(index, 1); if (stack.window) this._releaseWindow(stack.window); stack.appearAnimation = null; stack.waitAnimation = null; stack.exitAnimation = null; } } const NotifyManager = new NotifyManagerClass(); Torigoya.NotifyMessage.Manager = NotifyManager; // ------------------------------------------------------------------------- // Scene_Map const upstream_Scene_Map_createWindowLayer = Scene_Map.prototype.createWindowLayer; Scene_Map.prototype.createWindowLayer = function () { NotifyManager.setScene(this); upstream_Scene_Map_createWindowLayer.apply(this); }; const upstream_Scene_Map_update = Scene_Map.prototype.update; Scene_Map.prototype.update = function () { upstream_Scene_Map_update.apply(this); NotifyManager.update(); }; // ------------------------------------------------------------------------- // Add Scenes if (Torigoya.NotifyMessage.parameter.advancedAppendScenes) { Torigoya.NotifyMessage.parameter.advancedAppendScenes.forEach((sceneName) => { const targetClass = findGlobalObject(sceneName); const upstreamSceneCreateWindowLayer = targetClass.prototype.createWindowLayer; const upstreamSceneUpdate = targetClass.prototype.update; targetClass.prototype.createWindowLayer = function () { NotifyManager.setScene(this); upstreamSceneCreateWindowLayer.apply(this); }; targetClass.prototype.update = function () { upstreamSceneUpdate.apply(this); NotifyManager.update(); }; }); } // ------------------------------------------------------------------------- // SceneManager const upstream_SceneManager_onSceneTerminate = SceneManager.onSceneTerminate; SceneManager.onSceneTerminate = function () { NotifyManager.clear(); upstream_SceneManager_onSceneTerminate.apply(this); }; // ------------------------------------------------------------------------- // プラグインコマンド function commandNotify({ message, icon, note }) { const item = new NotifyItem({ message, icon: parseInt(icon, 10), note }); NotifyManager.notify(item); } function commandNotifyWithVariableIcon({ message, iconVariable, note }) { const variableId = parseInt(iconVariable, 10); const icon = Math.max($gameVariables.value(variableId), 0); const item = new NotifyItem({ message, icon, note }); NotifyManager.notify(item); } function commandForceExitNotifications() { NotifyManager.forceExitNotifications(); } PluginManager.registerCommand(Torigoya.NotifyMessage.name, 'notify', commandNotify); PluginManager.registerCommand( Torigoya.NotifyMessage.name, 'notifyWithVariableIcon', commandNotifyWithVariableIcon, ); PluginManager.registerCommand( Torigoya.NotifyMessage.name, 'forceExitNotifications', commandForceExitNotifications, ); })(); })();