// DarkPlasma_CharacterAfterimage 1.0.1 // Copyright (c) 2024 DarkPlasma // This software is released under the MIT license. // http://opensource.org/licenses/mit-license.php /** * 2024/03/07 1.0.1 特定のプラグインと併用するとエラーが発生する不具合を修正 * 2024/03/01 1.0.0 公開 */ /*: * @plugindesc マップ上のキャラクターに残像を表示する * @author DarkPlasma * @license MIT * * @target MZ * @url https://github.com/elleonard/DarkPlasma-MZ-Plugins/tree/release * * @param duration * @text 表示時間(フレーム数) * @type number * @default 30 * * @param generationInterval * @text 生成間隔(フレーム数) * @type number * @default 4 * * @param opacity * @text 不透明度 * @type number * @max 255 * @default 255 * * @param colorTone * @text 色調 * @type struct * @default {"red":"0","blue":"0","green":"0","alpha":"0"} * * @param blendMode * @text 合成方法 * @type select * @option 通常 * @value 0 * @option 加算 * @value 1 * @option 乗算 * @value 2 * @option スクリーン * @value 3 * @default 0 * * @command startAfterimage * @text 残像の表示 * @desc プレイヤーまたはイベントに対して残像を表示します。 * @arg target * @desc 残像の表示対象を選択します。 * @text 対象 * @type select * @option プレイヤー * @value player * @option このイベント * @value thisEvent * @option 他のイベント * @value otherEvent * @default player * @arg eventId * @desc 対象が他のイベントの場合に、イベントIDを指定します。 * @text イベントID * @type number * @default 0 * * @command clearAfterimage * @text 残像の消去 * @desc プレイヤーまたはイベントに対して表示している残像を消去します。 * @arg target * @desc 残像の表示対象を選択します。 * @text 対象 * @type select * @option プレイヤー * @value player * @option このイベント * @value thisEvent * @option 他のイベント * @value otherEvent * @default player * @arg eventId * @desc 対象が他のイベントの場合に、イベントIDを指定します。 * @text イベントID * @type number * @default 0 * * @help * version: 1.0.1 * マップ上のキャラクターに残像を表示します。 * * 本プラグインはセーブデータを拡張します。 * マップ上の残像を表示している各キャラクターについて、 * その表示状態をセーブデータに追加します。 */ /*~struct~ColorTone: * @param red * @text 赤 * @type number * @max 255 * @default 0 * * @param green * @text 緑 * @type number * @max 255 * @default 0 * * @param blue * @text 青 * @type number * @max 255 * @default 0 * * @param alpha * @text α * @type number * @max 255 * @default 0 */ (() => { 'use strict'; const pluginName = document.currentScript.src.replace(/^.*\/(.*).js$/, function () { return arguments[1]; }); const pluginParametersOf = (pluginName) => PluginManager.parameters(pluginName); const pluginParameters = pluginParametersOf(pluginName); const settings = { duration: Number(pluginParameters.duration || 30), generationInterval: Number(pluginParameters.generationInterval || 4), opacity: Number(pluginParameters.opacity || 255), colorTone: pluginParameters.colorTone ? ((parameter) => { const parsed = JSON.parse(parameter); return { red: Number(parsed.red || 0), green: Number(parsed.green || 0), blue: Number(parsed.blue || 0), alpha: Number(parsed.alpha || 0), }; })(pluginParameters.colorTone) : { red: 0, blue: 0, green: 0, alpha: 0 }, blendMode: Number(pluginParameters.blendMode || 0), }; function parseArgs_startAfterimage(args) { return { target: String(args.target || `player`), eventId: Number(args.eventId || 0), }; } const command_startAfterimage = 'startAfterimage'; const command_clearAfterimage = 'clearAfterimage'; PluginManager.registerCommand(pluginName, command_startAfterimage, function (args) { const parsedArgs = parseArgs_startAfterimage(args); const target = (() => { switch (parsedArgs.target) { case 'player': return $gamePlayer; case 'thisEvent': return this.character(0); case 'otherEvent': return this.character(parsedArgs.eventId); default: return null; } })(); target?.requestStartAfterimage(); }); PluginManager.registerCommand(pluginName, command_clearAfterimage, function (args) { const parsedArgs = parseArgs_startAfterimage(args); const target = (() => { switch (parsedArgs.target) { case 'player': return $gamePlayer; case 'thisEvent': return this.character(0); case 'otherEvent': return this.character(parsedArgs.eventId); default: return null; } })(); target?.clearAfterimage(); }); function Game_CharacterBase_AfterimageMixIn(gameCharacterBase) { const _initMembers = gameCharacterBase.initMembers; gameCharacterBase.initMembers = function () { _initMembers.call(this); this._afterimage = undefined; }; gameCharacterBase.hasAfterimage = function () { return !!this._afterimage; }; gameCharacterBase.afterimage = function () { return this._afterimage; }; gameCharacterBase.requestStartAfterimage = function () { this._afterimage = new Game_CharacterAfterimage(); }; gameCharacterBase.clearAfterimage = function () { this._afterimage = undefined; }; const _update = gameCharacterBase.update; gameCharacterBase.update = function () { _update.call(this); this._afterimage?.update(); }; } Game_CharacterBase_AfterimageMixIn(Game_CharacterBase.prototype); class Game_CharacterAfterimage { constructor() { this._interval = settings.generationInterval; this._generateAfterimageFrameCount = settings.generationInterval; this._duration = settings.duration; this._initialOpacity = settings.opacity; this._colorTone = [ settings.colorTone.red, settings.colorTone.green, settings.colorTone.blue, settings.colorTone.alpha, ]; this._blendMode = settings.blendMode; } update() { this._generateAfterimageFrameCount++; } resetGenerationFrameCount() { this._generateAfterimageFrameCount = 0; } mustGenerateSprite() { return this._generateAfterimageFrameCount >= this._interval; } duration() { return this._duration; } initialOpacity() { return this._initialOpacity; } colorTone() { return this._colorTone; } blendMode() { return this._blendMode; } } function Spriteset_Map_AfterimageMixIn(spritesetMap) { const _update = spritesetMap.update; spritesetMap.update = function () { _update.call(this); this._characterSprites.forEach((character) => character.clearReservedAfterimages()); }; } Spriteset_Map_AfterimageMixIn(Spriteset_Map.prototype); function Sprite_Character_AfterimageMixIn(spriteCharacter) { const _initialize = spriteCharacter.initialize; spriteCharacter.initialize = function (character) { _initialize.call(this, character); this._afterimageSprites = []; this._clearReservedAfterimageSprites = []; }; spriteCharacter.createAfterimage = function () { const afterimage = new Sprite_CharacterAfterimage(this._character) .withPosition(this.x, this.y, this.z - 0.1) .withScale(this.scale) .withRotation(this.rotation) .withAnchor(this.anchor.x, this.anchor.y); this._afterimageSprites.push(afterimage); this.parent.addChild(afterimage); }; spriteCharacter.clearAfterimage = function () { this._afterimageSprites.forEach((afterimage) => this.parent.removeChild(afterimage)); this._afterimageSprites = []; }; const _update = spriteCharacter.update; spriteCharacter.update = function () { _update.call(this); this.updateAfterimage(); }; spriteCharacter.updateAfterimage = function () { /** * ここでremoveChildしてしまうとtilemapの子spriteの数が変動し、updateがスキップされてしまうspriteが現れる * そのため、removeChildの予約だけしておく */ this._clearReservedAfterimageSprites = this._afterimageSprites.filter((afterimage) => afterimage.opacity <= 0); this._afterimageSprites = this._afterimageSprites.filter((afterimage) => afterimage.opacity > 0); if (this._character.afterimage()?.mustGenerateSprite()) { this.createAfterimage(); this._character.afterimage()?.resetGenerationFrameCount(); } }; spriteCharacter.clearReservedAfterimages = function () { this._clearReservedAfterimageSprites.forEach((sprite) => this.parent.removeChild(sprite)); this._clearReservedAfterimageSprites = []; }; } Sprite_Character_AfterimageMixIn(Sprite_Character.prototype); class Sprite_CharacterAfterimage extends Sprite_Character { constructor(character) { super(character); const afterimage = character.afterimage(); if (!afterimage) { throw new Error(`対象キャラクターに残像情報が存在しません。`); } /** * この時点でのキャラクター情報をコピーしておく */ this._character = JsonEx.makeDeepCopy(character); this._afterimage = afterimage; this.setColorTone(afterimage.colorTone()); this.blendMode = afterimage.blendMode(); this.updateBitmap(); this.updateFrame(); } withPosition(x, y, z) { this.x = x; this.y = y; this.z = z; return this; } withScale(scale) { this.scale = scale; return this; } withRotation(rotation) { this.rotation = rotation; return this; } withAnchor(x, y) { this.anchor.x = x; this.anchor.y = y; return this; } update() { this.opacity -= this._afterimage.initialOpacity() / this._afterimage.duration(); this.updatePosition(); } updatePosition() { super.updatePosition(); this.z -= 0.1; } } globalThis.Game_CharacterAfterimage = Game_CharacterAfterimage; })();