magical-girls-runa-and-nanami/js/plugins/DarkPlasma_CharacterAfterimage.js
2026-02-28 12:33:13 -06:00

356 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.

// 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<ColorTone>
* @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;
})();