pure-full-lily/js/plugins/ZoomFadePicture.js
2025-09-16 10:12:44 -05:00

263 lines
10 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.

/*:
* @target MZ
* @plugindesc ピクチャを拡大/スライドしながらフェードさせる v1.8
* ⾃動ID割り当て (70〜80) 完全⾃動消去対応
* @author IvI
* @help
* --------------------------------------------------------------
* ▼ 概要
* pictureId 引数を廃止。ID 70〜80 の空き番号を⾃動で使います。
* 演出が終わった瞬間にその ID を `$gameScreen.erasePicture()` で解放。
*
* ▼ プラグインコマンド
* ◆ playEffect : 拡大フェード
* ◆ playSlideEffect : 上昇+横移動+軽い拡大+フェードアウト
* どちらも数式( 880+\V[30] など)をそのまま入力可能。
*
* ▼ 自動 ID 割り当て仕様
* 1. 70〜80 を上から順に検索し、null の ID を使用
* 2. 空きが無ければ「演出が終了済み」のピクチャを erase して再利用
* 3. どうしても無ければ 70 を強制上書き(理論上ほぼ発生しません)
*
* ▼ ライセンス
* MIT
* --------------------------------------------------------------
*
* @command playEffect
* @text エフェクト再生
* @arg filename @text 画像ファイル @type file @dir img/pictures
* @arg x @text X座標 @type string @default 0
* @arg y @text Y座標 @type string @default 0
* @arg origin @text 原点 @type select
* @option 左上 @value 0
* @option 中央 @value 1
* @default 1
* @arg firstDuration @text フェードインF @type string @default 20
* @arg secondDuration @text フェードアウトF @type string @default 30
* @arg blendMode @text ブレンドモード @type select
* @option 通常 @value 0
* @option 加算 @value 1
* @default 0
* @arg wait @text 終了までウェイト @type boolean @default false
* --------------------------------------------------------------
* @command playSlideEffect
* @text スライドエフェクト再生
* @arg filename @text 画像ファイル @type file @dir img/pictures
* @arg x @text X座標 @type string @default 0
* @arg y @text Y座標 @type string @default 0
* @arg origin @text 原点 @type select
* @option 左上 @value 0
* @option 中央 @value 1
* @default 1
* @arg rise @text 上昇距離(px) @type string @default -60
* @arg slideX @text 横移動(px) @type string @default 20
* @arg scaleTo @text 最終倍率(%) @type string @default 120
* @arg firstDuration @text フェードインF @type string @default 20
* @arg secondDuration @text フェードアウトF @type string @default 40
* @arg blendMode @text ブレンドモード @type select
* @option 通常 @value 0
* @option 加算 @value 1
* @default 0
* @arg wait @text 終了までウェイト @type boolean @default false
* --------------------------------------------------------------
*/
(() => {
'use strict';
const PN = 'ZoomFadePicture';
const ID_MIN = 70;
const ID_MAX = 90;
/* ------------------------------------------------------------------
* 文字列→数値変換(\V[n] 置換+四則演算)
* ---------------------------------------------------------------- */
const exprToNumber = value => {
if (typeof value === 'number') return value;
if (value == null) return 0;
let expr = String(value).replace(/\\[Vv]\[(\d+)\]/g,
(_, id) => $gameVariables.value(Number(id)) ?? 0);
expr = expr.replace(/\s+/g, '');
try { return Number(Function(`"use strict";return(${expr})`)()); }
catch (e) {
console.warn(`${PN}: 式「${value}」を評価できません → 0`);
return 0;
}
};
/* ------------------------------------------------------------------
* 自動ピクチャ ID 取得
* ---------------------------------------------------------------- */
const allocId = () => {
// 1) 空きを探す
for (let id = ID_MIN; id <= ID_MAX; id++) {
if (!$gameScreen.picture(id)) return id;
}
// 2) 終了済みのピクチャを上書き
for (let id = ID_MIN; id <= ID_MAX; id++) {
const pic = $gameScreen.picture(id);
if (pic && (pic._zfpDone || pic._szfpDone)) {
$gameScreen.erasePicture(id);
return id;
}
}
// 3) 全部使用中なら強制上書き
console.warn(`${PN}: ID 70〜80 が全て使用中です。ID 70 を上書きします`);
$gameScreen.erasePicture(ID_MIN);
return ID_MIN;
};
/* ------------------------------------------------------------------
* プラグインコマンド
* ---------------------------------------------------------------- */
PluginManager.registerCommand(PN, 'playEffect', function (args) {
const id = allocId();
const file = String(args.filename || '');
const x = exprToNumber(args.x || 0);
const y = exprToNumber(args.y || 0);
const org = exprToNumber(args.origin || 1);
const d1 = exprToNumber(args.firstDuration || 20);
const d2 = exprToNumber(args.secondDuration || 30);
const bm = exprToNumber(args.blendMode || 0);
const wt = args.wait === 'true';
$gameScreen.showPicture(id, file, org, x, y, 0, 0, 0, bm);
const pic = $gameScreen.picture(id);
if (!pic) return;
pic._autoEraseId = id; // ← 自分の ID を保存
pic.startZoomFade(d1, d2);
if (wt) {
const tag = `ZFP-${id}`;
pic._waitTag = tag;
this.setWaitMode(tag);
}
});
PluginManager.registerCommand(PN, 'playSlideEffect', function (args) {
const id = allocId();
const file = String(args.filename || '');
const x = exprToNumber(args.x || 0);
const y = exprToNumber(args.y || 0);
const org = exprToNumber(args.origin || 1);
const rise = exprToNumber(args.rise || -60);
const dx = exprToNumber(args.slideX || 20);
const scTo = Math.max(exprToNumber(args.scaleTo || 120), 100);
const d1 = exprToNumber(args.firstDuration || 20);
const d2 = exprToNumber(args.secondDuration || 40);
const bm = exprToNumber(args.blendMode || 0);
const wt = args.wait === 'true';
$gameScreen.showPicture(id, file, org, x, y, 100, 100, 0, bm);
const pic = $gameScreen.picture(id);
if (!pic) return;
pic._autoEraseId = id;
pic.startSlideZoomFade(d1, d2, dx, rise, scTo);
if (wt) {
const tag = `ZFP-${id}`;
pic._waitTag = tag;
this.setWaitMode(tag);
}
});
/* ------------------------------------------------------------------
* Game_Picture 拡張
* ---------------------------------------------------------------- */
const _Game_Picture_update = Game_Picture.prototype.update;
Game_Picture.prototype.update = function () {
_Game_Picture_update.call(this);
if (this._zfpTotal) this._updateZoomFade();
if (this._szfpTotal) this._updateSlideZoomFade();
};
/* === 拡大フェード ============================================= */
Game_Picture.prototype.startZoomFade = function (d1, d2) {
this._zfpD1 = d1;
this._zfpD2 = d2;
this._zfpTotal = d1 + d2;
this._zfpCnt = 0;
this._zfpDone = false;
};
const easeOut = t => 1 - Math.pow(1 - t, 3);
Game_Picture.prototype._updateZoomFade = function () {
if (this._zfpDone) return;
this._zfpCnt++;
const t = this._zfpCnt / this._zfpTotal;
const r = this._zfpD1 / this._zfpTotal;
const s = easeOut(t);
/* scale 0→150% */
this._scaleX = this._scaleY = 150 * s;
/* opacity */
if (t <= r) this._opacity = Math.round(255 * easeOut(t / r));
else this._opacity = Math.round(255 * (1 - easeOut((t - r) / (1 - r))));
/* 終了判定+自動消去 */
if (this._zfpCnt >= this._zfpTotal) {
this._zfpDone = true;
if (this._autoEraseId != null && !$gameMessage.isBusy()) {
$gameScreen.erasePicture(this._autoEraseId);
}
}
};
/* === スライド+拡大フェード =================================== */
Game_Picture.prototype.startSlideZoomFade = function (d1, d2, dx, dy, scaleTo) {
this._szfpD1 = d1;
this._szfpD2 = d2;
this._szfpTotal = d1 + d2;
this._szfpCnt = 0;
this._szfpDone = false;
this._szfpDX = dx;
this._szfpDY = dy;
this._szfpSFrom = 100;
this._szfpSTo = scaleTo;
this._szfpBaseX = this._x;
this._szfpBaseY = this._y;
};
Game_Picture.prototype._updateSlideZoomFade = function () {
if (this._szfpDone) return;
this._szfpCnt++;
const t = this._szfpCnt / this._szfpTotal;
const r = this._szfpD1 / this._szfpTotal;
const ez = easeOut(t);
/* position */
this._x = Math.round(this._szfpBaseX + this._szfpDX * ez);
this._y = Math.round(this._szfpBaseY + this._szfpDY * ez);
/* scale */
const sc = this._szfpSFrom + (this._szfpSTo - this._szfpSFrom) * ez;
this._scaleX = this._scaleY = sc;
/* opacity */
if (t <= r) this._opacity = Math.round(255 * easeOut(t / r));
else this._opacity = Math.round(255 * (1 - easeOut((t - r) / (1 - r))));
/* 終了判定+自動消去 */
if (this._szfpCnt >= this._szfpTotal) {
this._szfpDone = true;
if (this._autoEraseId != null && !$gameMessage.isBusy()) {
$gameScreen.erasePicture(this._autoEraseId);
}
}
};
/* ------------------------------------------------------------------
* InterpreterZFP ウェイトモード
* ---------------------------------------------------------------- */
const _Game_Interpreter_updateWaitMode = Game_Interpreter.prototype.updateWaitMode;
Game_Interpreter.prototype.updateWaitMode = function () {
if (this._waitMode && this._waitMode.startsWith('ZFP-')) {
const id = Number(this._waitMode.split('-')[1]);
const pic = $gameScreen.picture(id);
if (!pic) { this._waitMode = ''; return false; }
return true;
}
return _Game_Interpreter_updateWaitMode.call(this);
};
})();