// DarkPlasma_FallImages 1.1.1 // Copyright (c) 2025 DarkPlasma // This software is released under the MIT license. // http://opensource.org/licenses/mit-license.php /** * 2026/04/13 1.1.1 イベントテストでエラーが発生する不具合を修正 * 2025/11/15 1.1.0 自動で降らせるマップメモタグを追加 * 1.0.6 configをTypeScript移行 * 2024/02/08 1.0.5 TypeScript移行 * 画像が降っていない状態で画像を消すコマンドを実行するとエラーになる不具合を修正 * 2021/07/05 1.0.4 MZ 1.3.2に対応 * 2021/06/22 1.0.3 サブフォルダからの読み込みに対応 * 2020/12/16 1.0.2 ゲーム終了時に正しく状態を初期化しない不具合を修正 * 2020/10/25 1.0.1 ヘルプ追記 * 2020/10/24 1.0.0 公開 */ /*: * @plugindesc 画面内に画像を降らせる * @author DarkPlasma * @license MIT * * @target MZ * @url https://github.com/elleonard/DarkPlasma-MZ-Plugins/tree/release * * @param images * @desc 降らせる画像の設定 * @text 画像設定 * @type struct[] * @default [] * * @command startFall * @text 画像を降らせる * @desc 画像を画面内に降らせます。 * @arg id * @desc 降らせる画像設定のIDです。 * @text 降らせる画像設定ID * @type number * @default 0 * * @command stopFall * @text 画像を消す * @desc 降らせている画像を消し、止ませます。 * * @command fadeOutFall * @text 画像をフェードアウトする * @desc 振らせている画像をフェードアウトさせ、止ませます。 * * @help * version: 1.1.1 * 何らかの画像を降らせる画面演出を提供します。 * * プラグインパラメータにIDと画像ファイルを設定し、 * プラグインコマンドでそのIDを指定してください。 * * マップのメモ欄に以下のように記述することで * そのマップに入った際にIDnの設定で自動で降らせはじめ、 * 出た際に自動で止めるようにできます。 * * * 本プラグインはセーブデータを拡張します。 * 画像を降らせるための状態をセーブします。 * */ /*~struct~FallImage: * @param id * @desc 降らせる画像設定のIDです。降らせるプラグインコマンドで指定します。 * @text 画像設定ID * @type number * @default 1 * * @param file * @desc 降らせるための画像ファイルを指定します。 * @text 画像ファイル * @type file * @dir / * * @param rows * @desc 降らせる画像の行数を指定します。 * @text 画像の行数 * @type number * @default 5 * * @param cols * @desc 降らせる画像の列数を指定します。 * @text 画像の列数 * @type number * @default 18 * * @param count * @desc 画面内に一度に表示する数を指定します。 * @text 表示数 * @type number * @default 40 * * @param waveringFrequency * @desc 降る過程で揺れる頻度を指定します。最大10で、多いほど頻繁に揺れます。 * @text 揺れ頻度 * @type number * @max 10 * @default 7 * * @param minimumLifeTime * @desc 1枚を降らせ続ける最短の時間(フレーム単位)を指定します。 * @text 最短表示時間 * @type number * @default 150 * * @param lifeTimeRange * @desc 1枚を降らせ続ける時間の範囲(フレーム単位)を指定します。最短表示時間とこの値の和が最長表示時間になります。 * @text 表示時間の範囲 * @type number * @default 500 * * @param animationSpeed * @desc アニメーションする速さを指定します。小さいほど速くアニメーションします。 * @text アニメーション速度 * @type number * @default 2 * * @param moveSpeedX * @desc 横方向の移動速度を指定します。大きいほど速く移動します。 * @text 横移動速度 * @type number * @default 4 * * @param moveSpeedY * @desc 落下速度を指定します。大きいほど速く落下します。 * @text 縦移動速度 * @type number * @default 6 */ (() => { 'use strict'; const pluginName = document.currentScript.src.replace(/^.*\/(.*).js$/, function () { return arguments[1]; }); /** * マップのメタデータを取得できるか * @return {boolean} */ function isMapMetaDataAvailable() { return $dataMap && $dataMap.meta; } const command_startFall = 'startFall'; const command_stopFall = 'stopFall'; const command_fadeOutFall = 'fadeOutFall'; const pluginParametersOf = (pluginName) => PluginManager.parameters(pluginName); const pluginParameters = pluginParametersOf(pluginName); const settings = { images: pluginParameters.images ? JSON.parse(pluginParameters.images).map((e) => { return e ? ((parameter) => { const parsed = JSON.parse(parameter); return { id: Number(parsed.id || 1), file: String(parsed.file || ``), rows: Number(parsed.rows || 5), cols: Number(parsed.cols || 18), count: Number(parsed.count || 40), waveringFrequency: Number(parsed.waveringFrequency || 7), minimumLifeTime: Number(parsed.minimumLifeTime || 150), lifeTimeRange: Number(parsed.lifeTimeRange || 500), animationSpeed: Number(parsed.animationSpeed || 2), moveSpeedX: Number(parsed.moveSpeedX || 4), moveSpeedY: Number(parsed.moveSpeedY || 6), }; })(e) : { id: 1, file: '', rows: 5, cols: 18, count: 40, waveringFrequency: 7, minimumLifeTime: 150, lifeTimeRange: 500, animationSpeed: 2, moveSpeedX: 4, moveSpeedY: 6, }; }) : [], }; const START_Y_OFFSET = -100; PluginManager.registerCommand(pluginName, command_startFall, function (args) { fallImageStatus?.requestStart(Number(args.id)); }); PluginManager.registerCommand(pluginName, command_stopFall, function () { fallImageStatus?.requestStop(); }); PluginManager.registerCommand(pluginName, command_fadeOutFall, function () { fallImageStatus?.requestFadeOut(); }); class FallImageStatus { constructor(startRequested, requestedImageId, stopRequested, fadeOutRequested, isFalling, startOptions) { this._startRequested = startRequested; this._requestedImageId = requestedImageId; this._stopRequested = stopRequested; this._fadeOutRequested = fadeOutRequested; this._isFalling = isFalling; this._startOptions = startOptions; } static newInstance() { return new FallImageStatus(false, 0, false, false, false, []); } toSave() { return { startRequested: this.startRequested, requestedImageId: this.requestedImageId, stopRequested: this.stopRequested, fadeOutRequested: this.fadeOutRequested, isFalling: this.isFalling, startOptions: this.startOptions || [], }; } static fromSave(saveObject) { return new FallImageStatus( saveObject.startRequested, saveObject.requestedImageId, saveObject.stopRequested, saveObject.fadeOutRequested, saveObject.isFalling, saveObject.startOptions, ); } get startRequested() { return this._startRequested; } get requestedImageId() { return this._requestedImageId; } get stopRequested() { return this._stopRequested; } get fadeOutRequested() { return this._fadeOutRequested; } get isFalling() { return this._isFalling; } get startOptions() { return this._startOptions; } startWithOverride() { return this._startOptions?.includes('override') || false; } requestedImageSetting() { return settings.images.find((image) => image.id === this._requestedImageId); } requestStart(fallSettingId, options) { this._startRequested = true; this._requestedImageId = fallSettingId; this._isFalling = true; this._startOptions = options; } requestStop() { this._stopRequested = true; this._isFalling = false; } requestFadeOut() { this._fadeOutRequested = true; this._isFalling = false; } clearStartRequest() { this._startRequested = false; this._startOptions = []; } clearStopRequest() { this._stopRequested = false; } clearFadeOutRequest() { this._fadeOutRequested = false; } } let fallImageStatus = null; function Game_System_FallImageMixIn(gameSystem) { const _initialize = gameSystem.initialize; gameSystem.initialize = function () { _initialize.call(this); fallImageStatus = FallImageStatus.newInstance(); }; const _onBeforeSave = gameSystem.onBeforeSave; gameSystem.onBeforeSave = function () { _onBeforeSave.call(this); if (fallImageStatus) { this._fallImageStatus = fallImageStatus.toSave(); } }; const _onAfterLoad = gameSystem.onAfterLoad; gameSystem.onAfterLoad = function () { _onAfterLoad.call(this); if (this._fallImageStatus) { fallImageStatus = FallImageStatus.fromSave(this._fallImageStatus); } else { fallImageStatus = FallImageStatus.newInstance(); } }; } Game_System_FallImageMixIn(Game_System.prototype); function Game_Map_FallImageMixIn(gameMap) { gameMap.autoFallImageId = function () { return isMapMetaDataAvailable() ? Number($dataMap.meta.autoFallImage) : undefined; }; const _setup = gameMap.setup; gameMap.setup = function (mapId) { _setup.call(this, mapId); const afterAutoFall = this.autoFallImageId(); if (afterAutoFall) { fallImageStatus?.requestStart(afterAutoFall, ['override']); this._autoFallingImage = true; } else if (this._autoFallingImage) { fallImageStatus?.requestStop(); this._autoFallingImage = false; } }; } Game_Map_FallImageMixIn(Game_Map.prototype); class Sprite_Falling extends Sprite { initialize(fallSettingId) { super.initialize(); this._fallSetting = settings.images.find((image) => image.id === fallSettingId); if (!this._fallSetting) { throw new Error('Invalid fallSettingId.'); } this.bitmap = ImageManager.loadBitmap('', this._fallSetting.file); } fallSetting() { if (!this._fallSetting) { throw new Error(`設定値が存在しません`); } return this._fallSetting; } setup() { this.opacity = 255; this.x = Math.floor(Math.random() * Graphics.boxWidth); this.y = Math.floor(Math.random() * Graphics.boxHeight + START_Y_OFFSET); this._lifeTime = this.initialLifeTime(); const scale = this.calcScale(); this.scale.set(scale, scale); this._row = Math.randomInt(this.fallSetting().rows); this._animationFrame = Math.randomInt(this.fallSetting().cols * this.fallSetting().animationSpeed); this.updateFrame(); } update() { if (!this._fallSetting) { throw new Error(`設定値が存在しません`); } this._lifeTime--; this._animationFrame = (this._animationFrame + 1) % (this._fallSetting.cols * this._fallSetting.animationSpeed); this.x += this.moveSpeedX(); this.y += this.moveSpeedY(); this.updateFrame(); this.updateLifeTime(); } wavering() { return Math.randomInt(10 - this.fallSetting().waveringFrequency + 1) === 0; } moveSpeedX() { return (Math.randomInt(this.fallSetting().moveSpeedX) + 1) * (this.wavering() ? -1 : 1); } moveSpeedY() { return Math.randomInt(this.fallSetting().moveSpeedY) + 1; } updateLifeTime() { this._lifeTime--; if (this._lifeTime <= 50) { this.opacity -= 255 / 50; } if (this._lifeTime <= 0 && fallImageStatus?.isFalling) { this.setup(); } } updateFrame() { const width = this.frameWidth(); const height = this.frameHeight(); this.setFrame( Math.floor(this._animationFrame / this.fallSetting().animationSpeed) * width, this._row * height, width, height, ); } frameHeight() { return Math.floor((this.bitmap?.height || 0) / this.fallSetting().rows); } frameWidth() { return Math.floor((this.bitmap?.width || 0) / this.fallSetting().cols); } initialLifeTime() { return this.fallSetting().minimumLifeTime + Math.floor(Math.random() * this.fallSetting().lifeTimeRange); } calcScale() { return Math.randomInt(2) === 0 ? 0.5 : 1.5; } } function Spriteset_Map_FallImagesMixIn(spritesetMap) { const _initialize = spritesetMap.initialize; spritesetMap.initialize = function () { this._fallImageSprites = []; _initialize.call(this); }; const _createLowerLayer = spritesetMap.createLowerLayer; spritesetMap.createLowerLayer = function () { _createLowerLayer.call(this); this.createFallImage(); }; spritesetMap.needCreateFallImage = function () { return !!fallImageStatus?.startRequested || (!!fallImageStatus?.isFalling && this._fallImageSprites.length === 0); }; spritesetMap.createFallImage = function () { const image = fallImageStatus?.requestedImageSetting(); if (!image || !this.needCreateFallImage()) { return; } fallImageStatus?.clearStartRequest(); this._fallImageSprites = [...Array(image.count).keys()].map((_) => new Sprite_Falling(image.id)); this._fallImageSprites.forEach((sprite) => { this.addChild(sprite); sprite.setup(); }); }; const _update = spritesetMap.update; spritesetMap.update = function () { _update.call(this); this.updateFallImage(); }; spritesetMap.updateFallImage = function () { if (fallImageStatus?.startRequested) { if (fallImageStatus.startWithOverride()) { this.destroyFallImages(); } this.createFallImage(); } else if (fallImageStatus?.stopRequested) { this.destroyFallImages(); } }; spritesetMap.destroyFallImages = function () { this._fallImageSprites.forEach((sprite) => sprite.destroy()); this._fallImageSprites = []; fallImageStatus?.clearStopRequest(); }; } Spriteset_Map_FallImagesMixIn(Spriteset_Map.prototype); })();