pastime/js/plugins/TorigoyaMZ_Achievement2.js
2026-03-15 16:45:22 -05:00

1339 lines
42 KiB
JavaScript
Raw Permalink 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.

/*---------------------------------------------------------------------------*
* 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<Achievement>[]
* @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<Sound>
* @default {"soundName":"Saint5","soundVolume":"90"}
*
* @param popupWindowImage
* @text ウィンドウ画像
* @desc 実績獲得ポップアップのウィンドウ画像
* @parent popup
* @type file
* @dir img/system
* @default Window
*
* @param popupOpacity
* @text ウィンドウ背景の透明度
* @desc ウィンドウ背景の透明度(0255)
* -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);
})();
})();