/*---------------------------------------------------------------------------* * TorigoyaMZ_Achievement2.js v.1.8.0 *---------------------------------------------------------------------------* * Build Date: 2024/01/19 01:57:12 (JST) *---------------------------------------------------------------------------* * Ruたん ( @ru_shalm ) * https://torigoya-plugin.rutan.dev *---------------------------------------------------------------------------*/ /*: * @target MZ * @plugindesc 実績プラグイン (v.1.8.0) * @version 1.8.0 * @author Ruたん(ru_shalm) * @license public domain * @url https://raw.githubusercontent.com/rutan/torigoya-rpg-maker-plugin/gh-pages/TorigoyaMZ_Achievement2.js * * @help 実績プラグイン (v.1.8.0) * https://torigoya-plugin.rutan.dev * * 実績・トロフィー的なシステムを定義します。 * 使用方法の詳細は以下の記事をご確認ください。 * https://torigoya-plugin.rutan.dev/system/achievement2/ * * ------------------------------------------------------------ * ■ 設定方法 * ------------------------------------------------------------ * * このプラグインの設定から実績を登録してください。 * ここでの並び順の順番で画面に表示されます。 * (並び順は後から並び替えても問題ありません) * * ------------------------------------------------------------ * ■ ゲーム中に実績を獲得する * ------------------------------------------------------------ * * プラグインコマンドから獲得処理を呼び出すことができます。 * * ------------------------------------------------------------ * ■ その他の使い方・アドオンについて * ------------------------------------------------------------ * 以下の記事をご確認ください。 * https://torigoya-plugin.rutan.dev/system/achievement2/ * * @param base * @text ■ 基本設定 * @type string * * @param baseAchievementData * @text 実績情報の登録 * @parent base * @type struct[] * @default [] * * @param baseSaveSlot * @text セーブデータのスロット名 * @parent base * @type string * @default achievement * * @param popup * @text ■ ポップアップ設定 * @type string * * @param popupEnable * @text ポップアップ表示のON/OFF * @desc 実績を獲得した時にポップアップ表示を行うか? * @parent popup * @type boolean * @on 表示する * @off 表示しない * @default true * * @param popupPosition * @text 表示位置 * @desc 実績獲得ポップアップの表示位置 * @parent popup * @type select * @option 左上 * @value leftUp * @option 右上 * @value rightUp * @default leftUp * * @param popupTopY * @text 表示位置: 上端 * @desc 実績獲得ポップアップ表示位置の上端 * @parent popup * @type number * @min 0 * @decimals 0 * @default 10 * * @param popupAnimationType * @text アニメーション * @desc 実績獲得ポップアップのアニメーション方法。 * 「なめらか」は Torigoya_FrameTween が必要です。 * @parent popup * @type select * @option なめらか * @value tween * @option その場に表示 * @value open * @default tween * * @param popupWait * @text 表示時間 * @desc 実績獲得ポップアップの表示時間(秒) * ※アニメーションの時間は含みません * @parent popup * @type number * @min 0 * @decimals 2 * @default 1.25 * * @param popupWidth * @text ポップアップの横幅 * @desc 実績獲得ポップアップの横幅(px) * 小さすぎると文字がはみ出します * @parent popup * @type number * @min 200 * @decimals 0 * @default 300 * * @param popupPadding * @text ポップアップの余白 * @desc 実績獲得ポップアップの余白サイズ * @parent popup * @type number * @min 0 * @decimals 0 * @default 10 * * @param popupTitleFontSize * @text 実績名の文字サイズ * @desc 実績獲得ポップアップに表示される * 取得した実績名の文字サイズ * @parent popup * @type number * @min 16 * @decimals 0 * @default 20 * * @param popupTitleColor * @text 実績名の文字の色番号 * @desc 実績名の文字表示に使用する色 * ※\c[数字] ←の数字欄に入れる数字 * @parent popup * @type number * @min 0 * @decimals 0 * @default 1 * * @param popupMessage * @text メッセージの内容 * @desc 実績獲得ポップアップに表示される * 獲得メッセージの内容 * @parent popup * @type string * @default 実績を獲得しました * * @param popupMessageFontSize * @text メッセージの文字サイズ * @desc 実績獲得ポップアップに表示される * 獲得メッセージの文字サイズ * @parent popup * @type number * @min 12 * @decimals 0 * @default 16 * * @param popupSound * @text 効果音 * @desc 実績獲得時に再生する効果音の設定 * @parent popup * @type struct * @default {"soundName":"Saint5","soundVolume":"90"} * * @param popupWindowImage * @text ウィンドウ画像 * @desc 実績獲得ポップアップのウィンドウ画像 * @parent popup * @type file * @dir img/system * @default Window * * @param popupOpacity * @text ウィンドウ背景の透明度 * @desc ウィンドウ背景の透明度(0~255) * -1の場合はデフォルトの透明度を使用します。 * @parent popup * @type number * @min -1 * @max 255 * @decimals 0 * @default -1 * * @param titleMenu * @text ■ タイトル / メニュー画面設定 * @type string * * @param titleMenuUseInTitle * @text タイトル画面に表示 * @desc タイトル画面に実績メニューを表示するか? * @parent titleMenu * @type boolean * @on 表示する * @off 表示しない * @default true * * @param titleMenuUseInMenu * @text メニュー画面に表示 * @desc メニュー画面に実績メニューを表示するか? * @parent titleMenu * @type boolean * @on 表示する * @off 表示しない * @default true * * @param titleMenuText * @text 項目名 * @desc タイトルやメニューに表示する際の * 実績メニューの項目名 * @parent titleMenu * @type string * @default 実績 * * @param achievementMenu * @text ■ 実績画面設定 * @type string * * @param achievementMenuHiddenTitle * @text 未獲得実績の表示名 * @desc 実績画面で未取得の実績の欄に * 表示する名前 * @parent achievementMenu * @type string * @default ????? * * @param achievementMenuHiddenIcon * @text 未獲得実績のアイコンID * @desc 実績画面で未取得の実績の欄に * 表示するアイコンのID * @parent achievementMenu * @type number * @decimals 0 * @default 0 * * @param advanced * @text ■ 上級者向け設定 * @type string * * @param advancedFontFace * @text ポップアップのフォント * @desc 実績獲得ポップアップ表示のフォント名を指定します。 * 空欄の場合は他のウィンドウと同じフォントを使用します。 * @parent advanced * @type string * * @param advancedOverwritable * @text 獲得済み実績の再取得 * @desc 既に獲得済みの実績でも再取得できるようにします * @parent advanced * @type boolean * @on する * @off しない * @default false * * @command gainAchievement * @text 実績の獲得 * @desc 指定した実績を獲得します。 * * @arg key * @text 実績の管理ID * @desc 獲得したい実績に設定したIDを指定 * @type string * * @command removeAchievement * @text 獲得済み実績の削除 * @desc 既に獲得済みの実績を未獲得状態にします。 * 未獲得だった場合は何もしません。 * * @arg key * @text 実績の管理ID * @desc 削除したい実績に設定したIDを指定 * @type string * * @command openSceneAchievement * @text 実績画面の表示 * @desc 獲得済み実績の一覧画面を表示します。 * * @command resetAchievement * @text 全実績の削除(注意!) * @desc すべての実績を獲得前の状態に戻します。 * 気をつけて使おう! */ /*~struct~Sound: * @param soundName * @text 効果音ファイル名 * @desc 実績獲得ポップアップ表示時に再生する効果音ファイル。 * 空っぽの場合は効果音なしになります。 * @type file * @dir audio/se * @default Saint5 * * @param soundVolume * @text 効果音の音量 * @desc 実績獲得ポップアップ表示時に再生する効果音の音量 * @type number * @min 0 * @max 100 * @decimals 0 * @default 90 */ /*~struct~Achievement: * @param key * @text 管理ID * @desc 実績獲得時に指定する名前(例: ゲームクリア) * 数字でも日本語でもOK / 他の実績と被るのはNG * @type string * * @param title * @text 表示名 * @desc 実績に画面に表示するときの実績名 * (例:激闘の果てに魔王を倒した…ッ!) * @type string * * @param description * @text 実績の説明文 * @desc 実績に画面に表示する説明文(2行程度) * @type multiline_string * * @param icon * @text 実績のアイコンID * @desc 実績に使用するアイコンの番号。 * @type number * @decimals 0 * @default 0 * * @param hint * @text 実績獲得のヒント * @desc 未取得の場合に取得方法を表示できます(2行程度) * 空欄の場合は通常の説明文を表示します * @type multiline_string * * @param isSecret * @text 秘密実績フラグ * @desc この実績の存在を秘密にします。 * 未獲得の場合に一覧に表示されなくなります。 * @type boolean * @on 秘密にする * @off 秘密にしない * @default false * * @param note * @text メモ * @desc メモ欄です。 * ツクールのメモ欄同様に使えます。 * @type multiline_string */ (function () { 'use strict'; /** * PromiseLike かどうかを判定する * @param obj */ function isThenable(obj) { return obj && typeof obj['then'] === 'function'; } /** * プラグインのファイル名を取得 */ function getPluginName() { const cs = document.currentScript; return cs ? cs.src.split('/').pop().replace(/\.js$/, '') : 'TorigoyaMZ_Achievement2'; } 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 parseNumberParam(value, defaultValue) { if (value === undefined || value === '') return defaultValue; const floatValue = Number.parseFloat(String(value)); return isNaN(floatValue) ? defaultValue : floatValue; } function parseStringParam(value, defaultValue) { if (value === undefined) return defaultValue; return String(value); } 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 { soundName: parseStringParam(parameters['soundName'], 'Saint5'), soundVolume: parseIntegerParam(parameters['soundVolume'], 90), }; } function readStructAchievement(parameters) { parameters = typeof parameters === 'string' ? JSON.parse(parameters) : parameters; return { key: parseStringParam(parameters['key'], ''), title: parseStringParam(parameters['title'], ''), description: parseStringParam(parameters['description'], ''), icon: parseIntegerParam(parameters['icon'], 0), hint: parseStringParam(parameters['hint'], ''), isSecret: parseBooleanParam(parameters['isSecret'], false), note: parseStringParam(parameters['note'], ''), }; } function readParameter() { const parameters = PluginManager.parameters(getPluginName()); return { version: '1.8.0', base: parseStringParam(parameters['base'], ''), baseAchievementData: parseStructObjectParam(parameters['baseAchievementData'], []).map( readStructAchievement, ), baseSaveSlot: parseStringParam(parameters['baseSaveSlot'], 'achievement'), popup: parseStringParam(parameters['popup'], ''), popupEnable: parseBooleanParam(parameters['popupEnable'], true), popupPosition: parseStringParam(parameters['popupPosition'], 'leftUp'), popupTopY: parseIntegerParam(parameters['popupTopY'], 10), popupAnimationType: parseStringParam(parameters['popupAnimationType'], 'tween'), popupWait: parseNumberParam(parameters['popupWait'], 1.25), popupWidth: parseIntegerParam(parameters['popupWidth'], 300), popupPadding: parseIntegerParam(parameters['popupPadding'], 10), popupTitleFontSize: parseIntegerParam(parameters['popupTitleFontSize'], 20), popupTitleColor: parseIntegerParam(parameters['popupTitleColor'], 1), popupMessage: parseStringParam(parameters['popupMessage'], '実績を獲得しました'), popupMessageFontSize: parseIntegerParam(parameters['popupMessageFontSize'], 16), popupSound: readStructSound( parseStructObjectParam(parameters['popupSound'], { soundName: 'Saint5', soundVolume: 90 }), ), popupWindowImage: parseStringParam(parameters['popupWindowImage'], 'Window'), popupOpacity: parseIntegerParam(parameters['popupOpacity'], -1), titleMenu: parseStringParam(parameters['titleMenu'], ''), titleMenuUseInTitle: parseBooleanParam(parameters['titleMenuUseInTitle'], true), titleMenuUseInMenu: parseBooleanParam(parameters['titleMenuUseInMenu'], true), titleMenuText: parseStringParam(parameters['titleMenuText'], '実績'), achievementMenu: parseStringParam(parameters['achievementMenu'], ''), achievementMenuHiddenTitle: parseStringParam(parameters['achievementMenuHiddenTitle'], '?????'), achievementMenuHiddenIcon: parseIntegerParam(parameters['achievementMenuHiddenIcon'], 0), advanced: parseStringParam(parameters['advanced'], ''), advancedFontFace: parseStringParam(parameters['advancedFontFace'], ''), advancedOverwritable: parseBooleanParam(parameters['advancedOverwritable'], false), }; } class AchievementManager { get achievements() { return this._achievements; } get unlockInfo() { return this._unlockInfo; } get options() { return this._options; } /** * 生成 * @param options */ constructor(options = {}) { this._options = options; this._achievements = []; this._unlockInfo = new Map(); this._handlers = []; this._isReady = false; } /** * 初期化処理 */ init() { if (this.options.onInit) { const result = this.options.onInit(this); if (isThenable(result)) { result.then(() => (this._isReady = true)); } else { this._isReady = true; } } else { this._isReady = true; } } /** * 初期化完了 * @returns {*} */ isReady() { return this._isReady; } /** * 実績マスター情報の登録 */ setAchievements(achievements) { this._achievements = achievements.map((achievement) => { if (achievement.note) { DataManager.extractMetadata(achievement); } else { achievement.meta = {}; } return achievement; }); } /** * 獲得済み実績の保存 * @returns {Promise} */ save() { if (this.options.onSave) { const result = this.options.onSave(this); return isThenable(result) ? result : Promise.resolve(); } return Promise.resolve(); } /** * 実績リストを取得 * @returns {{unlockInfo: any, achievement: *}[]} */ data() { return this._achievements.map((achievement) => ({ achievement, unlockInfo: this.unlockInfo.get(achievement.key) || null, })); } /** * 指定キーの実績情報を取得 * @param {string} key 取得する実績のキー * @returns {Achievement|null} */ getAchievement(key) { key = this.normalizeKey(key); return this._achievements.find((achievement) => achievement.key === key) || null; } /** * 獲得済み実績の件数を取得 * @returns {number} */ getUnlockedCount() { return this.unlockInfo.size; } /** * 実績獲得情報の取得 * @param {string} key 取得するする実績のキー * @returns {any | null} */ getUnlockInfo(key) { key = this.normalizeKey(key); return this.unlockInfo.get(`${key}`) || null; } /** * 指定キーの実績が獲得済みであるか? * @param {string} key 確認する実績のキー * @returns {boolean} */ isUnlocked() { return Array.from(arguments).every((key) => { key = this.normalizeKey(key); return this.unlockInfo.has(key); }); } /** * すべての実績が獲得済みであるか? * @returns {boolean} */ isAllUnlocked() { return this.achievements.every((achievement) => { return this.unlockInfo.has(achievement.key); }); } /** * 指定キーの実績が獲得可能であるか? * @param {string} key 確認する実績のキー * @returns {boolean} */ isUnlockable(key) { key = this.normalizeKey(key); if (!this.getAchievement(key)) return false; if (!this.options.overwritable && this.unlockInfo.has(key)) return false; return true; } /** * 指定キーの実績を獲得する * @param {string} key 獲得する実績のキー * @returns {boolean} 実績獲得処理が実行されたか */ unlock(key) { key = this.normalizeKey(key); if (!this.isUnlockable(key)) return false; this.unlockInfo.set(key, this.makeUnlockData(key)); this.notify(key); this.save(); return true; } /** * 実績獲得情報を生成する * ※アドオンプラグイン等で再定義・加工される想定 * @param {string} _key 獲得する実績のキー * @returns {{date: number}} */ makeUnlockData(_key) { return { date: Date.now(), }; } /** * 指定キーの実績獲得イベントの通知 * @param {string} key 獲得した実績のキー */ notify(key) { key = this.normalizeKey(key); const achievement = this.getAchievement(key); if (!achievement) return; const unlockInfo = this.unlockInfo.get(key); if (!unlockInfo) return; this._handlers.forEach((handler) => { handler({ achievement, unlockInfo }); }); } /** * 指定キーの実績を削除する * @param key */ remove(key) { key = `${key}`; this.unlockInfo.delete(key); } /** * 全実績を削除する * @note 削除後にセーブ処理を呼び出す */ clear() { this.resetData(); this.save(); } /** * 実績データのリセット */ resetData() { this.unlockInfo.clear(); } /** * 実績獲得通知イベントの購読開始 * @param {Handler} handler */ on(handler) { this._handlers.push(handler); } /** * 実績獲得通知イベントの購読解除 * @param {Handler} handler */ off(handler) { this._handlers = this._handlers.filter((h) => h !== handler); } /** * @callback Handler * @param {{achievement: any, unlockInfo: any}} responseCode */ /** * keyの文字列化 * @param key * @returns {string} * @private */ normalizeKey(key) { return typeof key === 'string' ? key : `${key}`; } /** * 保存したいデータの出力 */ createSaveContents() { return { unlockInfo: Array.from(this.unlockInfo.entries()), }; } /** * データのインポート * @param data */ extractSaveContents(data) { try { this.resetData(); data.unlockInfo.forEach(([key, value]) => { if (!this.getAchievement(key)) return; this.unlockInfo.set(key, value); }); } catch (e) { console.error(e); } } } class AchievementPopupManager { get options() { return this._options; } /** * 生成 * @param {AchievementManager} manager * @param {any} options */ constructor(manager, options = {}) { this._manager = manager; this._options = options; this._stacks = []; this._stackAnimations = []; this._soundAnimator = null; } /** * 初期化処理 */ init() { this._manager.on(this.onNotify.bind(this)); } /** * リセット処理 */ reset() { this._stackAnimations.forEach((tween) => { tween.abort(); }); this._stacks.forEach(this.destroyPopupWindow.bind(this)); this._stacks.length = 0; this._stackAnimations.length = 0; } /** * 通知処理 * @param {{achievement: Achievement, unlockInfo: any}} data */ onNotify(data) { const popupWindow = this._options.createPopupWindow(data); const isLeftUp = this._options.popupPosition === 'leftUp'; const x = isLeftUp ? this.leftX() : this.rightX() - popupWindow.width; const y = (() => { let y = this.topY(); for (let i = 0; i < this._stacks.length; ++i) { const target = this._stacks[i]; if (Math.abs(target.y - y) > (target.height + popupWindow.height) / 2) continue; y += popupWindow.y + popupWindow.height + 10; } return y; })(); if (this._options.popupAnimationType === 'tween' && (Torigoya.FrameTween || Torigoya.Tween)) { this._showWithTween(popupWindow, x, y); } else { this._showWithoutTween(popupWindow, x, y); } } /** * Tweenを使った表示処理 * @param popupWindow * @param x * @param y * @private */ _showWithTween(popupWindow, x, y) { const isLeftUp = this._options.popupPosition === 'leftUp'; const originalOpacity = popupWindow.opacity; const originalBackOpacity = popupWindow.backOpacity; const Easing = (Torigoya.FrameTween || Torigoya.Tween).Easing; const tween = (Torigoya.FrameTween || Torigoya.Tween) .create(popupWindow, { x: x + popupWindow.width * (isLeftUp ? -1 : 1), y, opacity: 0, backOpacity: 0, contentsOpacity: 0, }) .to( { x: x, opacity: originalOpacity, backOpacity: originalBackOpacity, contentsOpacity: 255, }, 30, Easing.easeOutCircular, ) .wait(Math.floor(this._options.popupWait * 60)) .to( { y: y - popupWindow.height, opacity: 0, backOpacity: 0, contentsOpacity: 0, }, 30, Easing.easeInCircular, ) .call(() => { this._stacks = this._stacks.filter((stack) => popupWindow !== stack); this.destroyPopupWindow(popupWindow); }); tween.start(); this._stacks.push(popupWindow); this._stacks.sort((a, b) => a.y - b.y); this._stackAnimations.push(tween); if (this._soundAnimator) { this._soundAnimator.abort(); this._soundAnimator = null; } this._soundAnimator = (Torigoya.FrameTween || Torigoya.Tween) .create({}) .wait(1) .call(() => { this._options.playSe(); }); this._soundAnimator.start(); } /** * Tweenを使わない表示処理 * @param popupWindow * @param x * @param y * @private */ _showWithoutTween(popupWindow, x, y) { popupWindow.x = x; popupWindow.y = y; popupWindow.openness = 0; popupWindow.open(); setTimeout(() => { popupWindow.close(); this._stacks = this._stacks.filter((stack) => popupWindow !== stack); setTimeout(() => { if (popupWindow.parent) popupWindow.parent.removeChild(popupWindow); }, 500); }, this._options.popupWait * 1000); this._stacks.push(popupWindow); this._stacks.sort((a, b) => a.y - b.y); this._options.playSe(); } /** * 一番左端 * @returns {number} */ leftX() { return 10; } /** * 一番右端 * @returns {number} */ rightX() { return Graphics.width - 10; } /** * 表示Y座標:上端 * @returns {number} */ topY() { return this._options.topY === undefined ? 10 : this._options.topY; } /** * ポップアップウィンドウの廃棄処理 * @param popupWindow */ destroyPopupWindow(popupWindow) { if (popupWindow.parent) popupWindow.parent.removeChild(popupWindow); if (typeof popupWindow.destroy === 'function') popupWindow.destroy(); } } function convertItemForWindow(achievementItem) { if (!achievementItem || !achievementItem.achievement) return null; if (achievementItem.unlockInfo) { return { name: achievementItem.achievement.title, iconIndex: achievementItem.achievement.icon, description: achievementItem.achievement.description, note: '', meta: {}, }; } else { return { name: Torigoya.Achievement2.parameter.achievementMenuHiddenTitle, iconIndex: Torigoya.Achievement2.parameter.achievementMenuHiddenIcon, description: achievementItem.achievement.hint || achievementItem.achievement.description || '', note: '', meta: {}, }; } } Torigoya.Achievement2 = { name: getPluginName(), parameter: readParameter(), }; // ------------------------------------------------------------------------- // Utils Torigoya.Achievement2.convertItemForWindow = convertItemForWindow; // ------------------------------------------------------------------------- // 実績マネージャ Torigoya.Achievement2.Manager = new AchievementManager({ onInit(manager) { manager.setAchievements(Torigoya.Achievement2.parameter.baseAchievementData); return StorageManager.loadObject(Torigoya.Achievement2.parameter.baseSaveSlot) .then((data) => manager.extractSaveContents(data)) .catch((e) => { console.error(e); manager.resetData(); }); }, onSave(manager) { return StorageManager.saveObject( Torigoya.Achievement2.parameter.baseSaveSlot, manager.createSaveContents(), ); }, overwritable: Torigoya.Achievement2.parameter.advancedOverwritable, }); // ------------------------------------------------------------------------- // 実績ポップアップ表示マネージャ Torigoya.Achievement2.PopupManager = new AchievementPopupManager(Torigoya.Achievement2.Manager, { popupPosition: Torigoya.Achievement2.parameter.popupPosition, popupWait: Torigoya.Achievement2.parameter.popupWait, popupAnimationType: Torigoya.Achievement2.parameter.popupAnimationType, topY: Torigoya.Achievement2.parameter.popupTopY, createPopupWindow(item) { const popupWindow = new Window_AchievementPopup(item); SceneManager._scene.addChild(popupWindow); // 行儀悪い return popupWindow; }, playSe() { const name = Torigoya.Achievement2.parameter.popupSound.soundName; if (!name) return; AudioManager.playSe({ name, pan: 0, pitch: 100, volume: Torigoya.Achievement2.parameter.popupSound.soundVolume, }); }, }); // ------------------------------------------------------------------------- // 実績ポップアップウィンドウ class Window_AchievementPopup extends Window_Base { initialize(item) { const rect = new Rectangle(0, 0, this.windowWidth(), this.windowHeight()); super.initialize(rect); this._item = item; this.refresh(); } updatePadding() { this.padding = this.standardPadding(); } windowWidth() { return Torigoya.Achievement2.parameter.popupWidth; } windowHeight() { return this.titleFontSize() + this.messageFontSize() + this.standardPadding() * 2 + 5; } standardPadding() { return Torigoya.Achievement2.parameter.popupPadding; } labelFontFace() { return Torigoya.Achievement2.parameter.advancedFontFace || $gameSystem.mainFontFace(); } titleFontSize() { return Torigoya.Achievement2.parameter.popupTitleFontSize; } messageFontSize() { return Torigoya.Achievement2.parameter.popupMessageFontSize; } lineHeight() { return this.titleFontSize(); } resetFontSettings() { super.resetFontSettings(); this.contents.fontFace = this.labelFontFace(); this.contents.fontSize = this.titleFontSize(); } refresh() { this.contents.clear(); this.drawIcon(this._item.achievement.icon, 0, 0); this.drawTitle(); this.drawMessage(); } drawTitle() { this.resetFontSettings(); this.drawTextEx( `\\c[${Torigoya.Achievement2.parameter.popupTitleColor}]` + this._item.achievement.title, 40, 0, ); } drawMessage() { const textWidth = this.windowWidth() - this.standardPadding() * 2 - 40; const y = this.titleFontSize() + 5; this.resetTextColor(); this.contents.fontSize = this.messageFontSize(); this.contents.drawText( Torigoya.Achievement2.parameter.popupMessage, 40, y, textWidth, this.messageFontSize(), 'left', ); } calcTextHeight() { return this.contents.fontSize; } loadWindowskin() { this.windowskin = ImageManager.loadSystem(Torigoya.Achievement2.parameter.popupWindowImage); } updateBackOpacity() { return Torigoya.Achievement2.parameter.popupOpacity === -1 ? super.updateBackOpacity() : Torigoya.Achievement2.parameter.popupOpacity; } } Torigoya.Achievement2.Window_AchievementPopup = Window_AchievementPopup; // ------------------------------------------------------------------------- // 実績一覧ウィンドウ class Window_AchievementList extends Window_Selectable { initialize(rect) { super.initialize(rect); this._data = []; this._isLaunchInTitle = false; this.refresh(); } isLaunchInTitle() { return this._isLaunchInTitle; } setIsLaunchInTitle(value) { this._isLaunchInTitle = !!value; } maxItems() { return this._data ? this._data.length : 0; } item() { return this._data ? this._data[this.index()] : null; } makeItemList() { this._data = Torigoya.Achievement2.Manager.data().filter((param) => this.isVisibleItem(param)); } isCurrentItemEnabled() { return !this.item(); } isVisibleItem({ achievement, unlockInfo }) { if (unlockInfo) return true; return !achievement.isSecret; } drawItem(index) { const achievementItem = this._data[index]; const item = Torigoya.Achievement2.convertItemForWindow(achievementItem); this.resetFontSettings(); if (!item) return; const rect = this.itemLineRect(index); this.resetTextColor(); this.changePaintOpacity(!!achievementItem); const name = typeof PluginManagerEx !== 'undefined' && PluginManagerEx.convertEscapeCharacters ? PluginManagerEx.convertEscapeCharacters(item.name) : item.name; const iconWidth = ImageManager.iconWidth + 8; this.drawIcon(item.iconIndex, rect.x, rect.y + (rect.height - ImageManager.iconHeight) / 2); this.drawText(name, rect.x + iconWidth, rect.y, rect.width - iconWidth, 'left'); } refresh() { this.makeItemList(); this.paint(); } updateHelp() { const item = this.item(); this.setHelpWindowItem(Torigoya.Achievement2.convertItemForWindow(item)); } playBuzzerSound() { // nothing to do } } Torigoya.Achievement2.Window_AchievementList = Window_AchievementList; // ------------------------------------------------------------------------- // 実績表示シーン class Scene_Achievement extends Scene_MenuBase { create() { super.create(); this.createHelpWindow(); this._listWindow = new Window_AchievementList(this.listWindowRect()); this._listWindow.setHandler('ok', this.onListOk.bind(this)); this._listWindow.setHandler('cancel', this.onListCancel.bind(this)); this._listWindow.setHelpWindow(this._helpWindow); this.addWindow(this._listWindow); } listWindowRect() { const wx = 0; const wy = this.mainAreaTop(); const ww = Graphics.boxWidth; const wh = this.mainAreaHeight(); return new Rectangle(wx, wy, ww, wh); } prepare(params) { this._isLaunchInTitle = !!params.isLaunchInTitle; } start() { super.start(); this._listWindow.setIsLaunchInTitle(this._isLaunchInTitle); this._listWindow.select(0); this._listWindow.activate(); } onListOk() { this.onListCancel(); } onListCancel() { this.popScene(); } isLaunchInTitle() { return this._isLaunchInTitle; } } Torigoya.Achievement2.Scene_Achievement = Scene_Achievement; (() => { // ------------------------------------------------------------------------- // タイトル画面への追加 if (Torigoya.Achievement2.parameter.titleMenuUseInTitle) { const upstream_Window_TitleCommand_makeCommandList = Window_TitleCommand.prototype.makeCommandList; Window_TitleCommand.prototype.makeCommandList = function () { upstream_Window_TitleCommand_makeCommandList.apply(this); this.addCommand(Torigoya.Achievement2.parameter.titleMenuText, 'Torigoya_Achievement', true); }; const upstream_Scene_Title_createCommandWindow = Scene_Title.prototype.createCommandWindow; Scene_Title.prototype.createCommandWindow = function () { upstream_Scene_Title_createCommandWindow.apply(this); this._commandWindow.setHandler('Torigoya_Achievement', this.commandTorigoyaAchievement.bind(this)); }; Scene_Title.prototype.commandTorigoyaAchievement = function () { this._commandWindow.close(); SceneManager.push(Torigoya.Achievement2.Scene_Achievement); SceneManager.prepareNextScene({ isLaunchInTitle: true }); }; } // ------------------------------------------------------------------------- // メニュー画面への追加 if (Torigoya.Achievement2.parameter.titleMenuUseInMenu) { const upstream_Window_MenuCommand_addOriginalCommands = Window_MenuCommand.prototype.addOriginalCommands; Window_MenuCommand.prototype.addOriginalCommands = function () { upstream_Window_MenuCommand_addOriginalCommands.apply(this); this.addCommand(Torigoya.Achievement2.parameter.titleMenuText, 'Torigoya_Achievement', true); }; const upstream_Scene_Menu_createCommandWindow = Scene_Menu.prototype.createCommandWindow; Scene_Menu.prototype.createCommandWindow = function () { upstream_Scene_Menu_createCommandWindow.apply(this); this._commandWindow.setHandler('Torigoya_Achievement', this.commandTorigoyaAchievement.bind(this)); }; Scene_Menu.prototype.commandTorigoyaAchievement = function () { SceneManager.push(Torigoya.Achievement2.Scene_Achievement); }; } // ------------------------------------------------------------------------- // シーン管理 const upstream_Scene_Boot_onSceneTerminate = SceneManager.onSceneTerminate; SceneManager.onSceneTerminate = function () { Torigoya.Achievement2.PopupManager.reset(); upstream_Scene_Boot_onSceneTerminate.apply(this); }; // ------------------------------------------------------------------------- // 起動処理 const upstream_Scene_Boot_onDatabaseLoaded = Scene_Boot.prototype.onDatabaseLoaded; Scene_Boot.prototype.onDatabaseLoaded = function () { upstream_Scene_Boot_onDatabaseLoaded.apply(this); ImageManager.loadSystem(Torigoya.Achievement2.parameter.popupWindowImage); Torigoya.Achievement2.Manager.init(); }; const upstream_Scene_Boot_start = Scene_Boot.prototype.start; Scene_Boot.prototype.start = function () { upstream_Scene_Boot_start.apply(this); if (Torigoya.Achievement2.parameter.popupEnable) { Torigoya.Achievement2.PopupManager.init(); } }; const upstream_Scene_Boot_isReady = Scene_Boot.prototype.isReady; Scene_Boot.prototype.isReady = function () { return upstream_Scene_Boot_isReady.apply(this) && Torigoya.Achievement2.Manager.isReady(); }; // ------------------------------------------------------------------------- // プラグインコマンド function commandGainAchievement({ key }) { Torigoya.Achievement2.Manager.unlock(`${key}`.trim()); } function commandRemoveAchievement({ key }) { Torigoya.Achievement2.Manager.remove(`${key}`.trim()); } function commandOpenSceneAchievement() { SceneManager.push(Torigoya.Achievement2.Scene_Achievement); } function commandResetAchievement() { Torigoya.Achievement2.Manager.clear(); } PluginManager.registerCommand(Torigoya.Achievement2.name, 'gainAchievement', commandGainAchievement); PluginManager.registerCommand(Torigoya.Achievement2.name, 'removeAchievement', commandRemoveAchievement); PluginManager.registerCommand(Torigoya.Achievement2.name, 'openSceneAchievement', commandOpenSceneAchievement); PluginManager.registerCommand(Torigoya.Achievement2.name, 'resetAchievement', commandResetAchievement); })(); })();