1920 lines
60 KiB
JavaScript
1920 lines
60 KiB
JavaScript
//=============================================================================
|
||
// MPP_EncounterEffect.js
|
||
//=============================================================================
|
||
// Copyright (c) 2021 - 2022 Mokusei Penguin
|
||
// Released under the MIT license
|
||
// http://opensource.org/licenses/mit-license.php
|
||
//=============================================================================
|
||
|
||
/*:
|
||
* @target MZ
|
||
* @plugindesc Change the effect at the time of encounter to a special effect.
|
||
* @author Mokusei Penguin
|
||
* @url
|
||
*
|
||
* @help [version 4.1.0]
|
||
* This plugin is for RPG Maker MZ.
|
||
*
|
||
* ▼ Plugin command
|
||
* - In the item to enter a numerical value,
|
||
* select the text and write 'v[N]' to refer to the variable N.
|
||
*
|
||
* 〇 setType
|
||
* - The effect type will be changed to the specified number
|
||
* only once next.
|
||
*
|
||
* 〇 setCharacter
|
||
* - The effect will be executed around the specified character
|
||
* only once next.
|
||
* - This setting is valid only for effect type 1.
|
||
*
|
||
* 〇 setColor
|
||
* - The effect will change to the specified color only once next.
|
||
*
|
||
* ▼ Effect type
|
||
* -1 : No SE & effects
|
||
* 0 : Default.
|
||
* 1 : Breaks around the character.
|
||
* 2 : The screen splits from the left and scatters to the left.
|
||
* 3 : The entire screen scatters to the front.
|
||
* 4 : The screen splits from the left and scatters to the right.
|
||
* 5 : Divide into squares and expand to the front.
|
||
*
|
||
* ▼ Other
|
||
* - In effect type 1, if the main character is not specified,
|
||
* [Player] will be the center for random encounters,
|
||
* and [This Event] will be the center for
|
||
* the event command [Battle Processing].
|
||
*
|
||
* ================================
|
||
* Mail : wood_penguin@yahoo.co.jp (@ is half-width)
|
||
* Blog : http://woodpenguin.blog.fc2.com/
|
||
* License : MIT license
|
||
*
|
||
* @command setType
|
||
* @desc Change the effect type only in the next battle.
|
||
* @arg type
|
||
* @desc
|
||
* @type number
|
||
* @min -1
|
||
* @max 5
|
||
* @default 1
|
||
*
|
||
*
|
||
* @command setCharacter
|
||
* @desc Specify the main character for the next effect.
|
||
* This setting is valid only for effect type 1.
|
||
* @arg character
|
||
* @desc -1:Player, 0:This event, 1-:Event with specified ID
|
||
* @type number
|
||
* @min -1
|
||
* @max 999
|
||
* @default 0
|
||
*
|
||
* @command setColor
|
||
* @desc Specifies the color of the next effect.
|
||
* @arg color
|
||
* @desc R,G,B
|
||
* @default 255,255,255
|
||
*
|
||
*
|
||
* @param Effect Type
|
||
* @desc
|
||
* @type number
|
||
* @min 0
|
||
* @max 5
|
||
* @default 1
|
||
*
|
||
* @param Effect Color
|
||
* @desc R,G,B
|
||
* @default 255,255,255
|
||
*
|
||
*/
|
||
|
||
/*:ja
|
||
* @target MZ
|
||
* @plugindesc エンカウント時の演出を特殊なエフェクトに変更します。
|
||
* @author 木星ペンギン
|
||
* @url
|
||
*
|
||
* @help [version 4.1.0]
|
||
* このプラグインはRPGツクールMZ用です。
|
||
*
|
||
* ▼ プラグインコマンド
|
||
* - 数値を入力する項目で、テキストを選択して v[N] と記述することで
|
||
* 変数N番を参照します。
|
||
*
|
||
* 〇 エフェクトタイプ変更
|
||
* - 次の1回のみ、エフェクトタイプが指定した番号に変更されます。
|
||
*
|
||
* 〇 中心キャラ指定
|
||
* - 次の1回のみ、エフェクトが指定したキャラクターを中心に実行されます。
|
||
* - この設定はエフェクトタイプ1のみ有効です。
|
||
*
|
||
* 〇 エフェクト色指定
|
||
* - 次の1回のみ、エフェクトが指定した色に変更されます。
|
||
*
|
||
* ▼ エフェクトタイプ
|
||
* -1: SE & エフェクトなし
|
||
* 0 : デフォルト
|
||
* 1 : キャラクターを中心に割れる
|
||
* 2 : 画面が左から割れて左に散る
|
||
* 3 : 画面全体が前面に飛び散る
|
||
* 4 : 画面が左から割れて右に散る
|
||
* 5 : 四角く区切り、手前に拡大する
|
||
*
|
||
* ▼ その他
|
||
* - エフェクトタイプ1にて、中心となるキャラクターを指定していない場合、
|
||
* ランダムエンカウントでは[プレイヤー]、
|
||
* イベントコマンドの【戦闘の処理】では[このイベント]が中心となります。
|
||
*
|
||
* ================================
|
||
* Mail : wood_penguin@yahoo.co.jp (@は半角)
|
||
* Blog : http://woodpenguin.blog.fc2.com/
|
||
* License : MIT license
|
||
*
|
||
* @command setType
|
||
* @text エフェクトタイプ変更
|
||
* @desc 次に行う戦闘のみエフェクトタイプを変更します。
|
||
* @arg type
|
||
* @text タイプ
|
||
* @desc エフェクトタイプ
|
||
* @type number
|
||
* @min -1
|
||
* @max 5
|
||
* @default 1
|
||
*
|
||
* @command setCharacter
|
||
* @text 中心キャラ指定
|
||
* @desc 次に行うエフェクトで中心となるキャラクターを指定します。
|
||
* この設定はエフェクトタイプ1でのみ有効です。
|
||
* @arg character
|
||
* @text キャラクター
|
||
* @desc 中心となるキャラクター
|
||
* -1:プレイヤー, 0:このイベント, 1~:指定したIDのイベント
|
||
* @type number
|
||
* @min -1
|
||
* @max 999
|
||
* @default 0
|
||
*
|
||
* @command setColor
|
||
* @text エフェクト色指定
|
||
* @desc 次に行うエフェクトの色を指定します。
|
||
* @arg color
|
||
* @text 色(RGB)
|
||
* @desc
|
||
* @default 255,255,255
|
||
*
|
||
*
|
||
* @param Effect Type
|
||
* @text エフェクトタイプ
|
||
* @desc 画面割れエフェクトのタイプ
|
||
* @type number
|
||
* @min 0
|
||
* @max 5
|
||
* @default 1
|
||
*
|
||
* @param Effect Color
|
||
* @text エフェクト色
|
||
* @desc エフェクトのデフォルト色
|
||
* (r,g,bで指定)
|
||
* @default 255,255,255
|
||
*
|
||
*
|
||
*/
|
||
|
||
(() => {
|
||
'use strict';
|
||
|
||
const pluginName = 'MPP_EncounterEffect';
|
||
|
||
// Plugin Parameters
|
||
const parameters = PluginManager.parameters(pluginName);
|
||
const param_EffectType = Number(parameters['Effect Type'] || 1);
|
||
const param_DefaultColor = parameters['Effect Color'].split(',').map(Number);
|
||
|
||
const _importedPlugin = (...names) => {
|
||
return names.some(name => PluginManager._scripts.includes(name));
|
||
};
|
||
const pluginOptions = [
|
||
'MPP_EncounterEffect_Op1',
|
||
'MPP_EncounterEffect_Op2'
|
||
];
|
||
|
||
// JsExtensions 代替
|
||
const MathExt = (() => {
|
||
// Number.prototype.clamp と違い、下限優先
|
||
const clamp = (x, min, max) => Math.max(Math.min(x, max), min);
|
||
const mod = (x, n) => ((x % n) + n) % n;
|
||
const randomInt = (x) => Math.floor(x * Math.random());
|
||
return { clamp, mod, randomInt };
|
||
})();
|
||
|
||
// 各タイプのパラメータ
|
||
const baseTypeParameters = [
|
||
null,
|
||
// Type 1
|
||
{
|
||
'Shape Type': 'square',
|
||
'Center': 'character',
|
||
'Break Direction': 'none',
|
||
'Break Duration': 45,
|
||
'Interval Duration': 45,
|
||
'Scatter Duration': 0,
|
||
'Move Duration': 60,
|
||
'Split Radial': 8,
|
||
'Radial Random Rate': 90,
|
||
'Circle Radius': 96,
|
||
'Circle Increase Rate': 150,
|
||
'Circle Random Rate': 40
|
||
},
|
||
// Type 2
|
||
{
|
||
'Shape Type': 'random',
|
||
'Break Direction': 'left',
|
||
'Break Duration': 30,
|
||
'Interval Duration': 45,
|
||
'Scatter Direction': 'left',
|
||
'Scatter Duration': 24,
|
||
'Move Duration': 60,
|
||
'Split X': 7,
|
||
'X Random Rate': 80,
|
||
'Split Y': 5,
|
||
'Y Random Rate': 80
|
||
},
|
||
// Type 3
|
||
{
|
||
'Shape Type': 'triangle',
|
||
'Break Direction': 'inside',
|
||
'Break Duration': 35,
|
||
'Interval Duration': 40,
|
||
'Scatter Direction': 'inside',
|
||
'Scatter Duration': 16,
|
||
'Move Duration': 100,
|
||
'Split X': 8,
|
||
'X Random Rate': 80,
|
||
'Split Y': 6,
|
||
'Y Random Rate': 80,
|
||
'Jump': true
|
||
},
|
||
// Type 4
|
||
{
|
||
'Shape Type': 'random',
|
||
'Break Direction': 'left',
|
||
'Break Duration': 30,
|
||
'Interval Duration': 45,
|
||
'Scatter Direction': 'right',
|
||
'Scatter Duration': 6,
|
||
'Move Duration': 45,
|
||
'Split X': 9,
|
||
'X Random Rate': 70,
|
||
'Split Y': 7,
|
||
'Y Random Rate': 70
|
||
},
|
||
// Type 5
|
||
{
|
||
'Shape Type': 'square',
|
||
'Break Direction': 'outside',
|
||
'Break Duration': 30,
|
||
'Interval Duration': 45,
|
||
'Scatter Direction': 'left',
|
||
'Scatter Duration': 50,
|
||
'Move Duration': 80,
|
||
'Split X': 15,
|
||
'X Random Rate': 0,
|
||
'Split Y': 11,
|
||
'Y Random Rate': 0
|
||
},
|
||
];
|
||
|
||
//-------------------------------------------------------------------------
|
||
// EncounterEffect
|
||
|
||
function EncounterEffect() {
|
||
throw new Error('This is a static class');
|
||
}
|
||
|
||
if (_importedPlugin(...pluginOptions)) {
|
||
window.EncounterEffect = EncounterEffect;
|
||
}
|
||
|
||
EncounterEffect.centerPoint = new Point();
|
||
EncounterEffect.windowBounds = null;
|
||
|
||
EncounterEffect.clear = function() {
|
||
this._snapTexture = null;
|
||
this._opacity = 0;
|
||
this._fragments = null;
|
||
this._type = param_EffectType;
|
||
this._character = null;
|
||
this._color = param_DefaultColor;
|
||
this._angle = 0;
|
||
};
|
||
|
||
EncounterEffect.destroy = function() {
|
||
if (this._snapTexture) {
|
||
this._snapTexture.destroy();
|
||
this._snapTexture = null;
|
||
}
|
||
};
|
||
|
||
EncounterEffect.params = function() {
|
||
return this._type >= 0 ? baseTypeParameters[this._type] : null;
|
||
};
|
||
|
||
EncounterEffect.type = function() {
|
||
return this._type;
|
||
};
|
||
|
||
EncounterEffect.setup = function(snapTexture) {
|
||
this._snapTexture = snapTexture;
|
||
this._opacity = 255;
|
||
this._opacityDelay = 0;
|
||
this._opacityDuration = 0;
|
||
this._params = this.params();
|
||
this.setupWindowBounds();
|
||
this.setupCenterPoint();
|
||
this.createFragments();
|
||
this.sortFragment(this.currentParam('Break Direction'));
|
||
this.setupAngle();
|
||
};
|
||
|
||
EncounterEffect.setupWindowBounds = function() {
|
||
const { width, height } = Graphics;
|
||
this.windowBounds = { minX: 0, maxX: width, minY: 0, maxY: height };
|
||
};
|
||
|
||
EncounterEffect.setupCenterPoint = function() {
|
||
if (this.currentParam('Center') === 'character') {
|
||
const { minX, maxX, minY, maxY } = this.windowBounds;
|
||
const character = this.centerCharacter();
|
||
const charX = character.screenX();
|
||
const charY = character.screenY();
|
||
const cx = Math.round(MathExt.clamp(charX, minX, maxX));
|
||
const cy = Math.round(MathExt.clamp(charY - 24, minY, maxY));
|
||
this.centerPoint.set(cx, cy);
|
||
} else {
|
||
const cx = Graphics.width / 2;
|
||
const cy = Graphics.height / 2;
|
||
this.centerPoint.set(cx, cy);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.setupAngle = function() {
|
||
this._angle = 0;
|
||
this._targetAngle = 0;
|
||
this._angleEasing = new Easing();
|
||
if (this._type === 5) {
|
||
const duration = this.effectSpeed();
|
||
this._targetAngle = -3;
|
||
this._angleEasing.start('Slow end', duration);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.snapTexture = function() {
|
||
return this._snapTexture;
|
||
};
|
||
|
||
EncounterEffect.setType = function(type) {
|
||
this._type = type;
|
||
};
|
||
|
||
EncounterEffect.opacity = function() {
|
||
return this._opacity;
|
||
};
|
||
|
||
EncounterEffect.setColor = function(...color) {
|
||
this._color = color;
|
||
};
|
||
|
||
EncounterEffect.color = function() {
|
||
return this._color;
|
||
};
|
||
|
||
EncounterEffect.setCharacter = function(character) {
|
||
this._character = character;
|
||
};
|
||
|
||
EncounterEffect.centerCharacter = function() {
|
||
return (
|
||
this._character ||
|
||
$gameMap.getInterpreterCharacter(0) ||
|
||
$gamePlayer
|
||
);
|
||
};
|
||
|
||
EncounterEffect.createFragments = function() {
|
||
this._fragments = [];
|
||
switch (this._type) {
|
||
case 1:
|
||
this.createFragmentsT1();
|
||
break;
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
case 5:
|
||
this.createFragmentsT2();
|
||
break;
|
||
}
|
||
};
|
||
|
||
EncounterEffect.createFragmentsT1 = function() {
|
||
const { minX, maxX, minY, maxY } = this.windowBounds;
|
||
const allPoints = this.createAllPointsT1();
|
||
for (let points of this.generaterPointsT1(allPoints)) {
|
||
points = this.cutProtrusionX(points, minX, false);
|
||
points = this.cutProtrusionX(points, maxX, true);
|
||
points = this.cutProtrusionY(points, minY, false);
|
||
points = this.cutProtrusionY(points, maxY, true);
|
||
points = this.removeOuterPoint(points);
|
||
if (points.length >= 3) {
|
||
this.addFragment(this.uniqPoints(points));
|
||
}
|
||
}
|
||
};
|
||
|
||
EncounterEffect.generaterPointsT1 = function*(allPoints) {
|
||
const square = this.currentParam('Shape Type', 'square') === 'square';
|
||
const max = allPoints[0] ? allPoints[0].length : 1;
|
||
for (const [i, outPoints] of allPoints.entries()) {
|
||
const inPoints = i > 0 ? allPoints[i - 1] : null;
|
||
for (const [j, point2] of outPoints.entries()) {
|
||
const point3 = outPoints[(j + 1) % max];
|
||
if (inPoints) {
|
||
const point4 = inPoints[(j + 1) % max];
|
||
const point1 = inPoints[j];
|
||
if (square) {
|
||
yield [point1, point2, point3, point4];
|
||
} else {
|
||
yield [point1, point2, point4];
|
||
yield [point2, point3, point4];
|
||
}
|
||
} else {
|
||
yield [this.centerPoint, point2, point3];
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
EncounterEffect.cutProtrusionX = function(points, x, cutAbove) {
|
||
const size = points.length;
|
||
for (const p1 of points) {
|
||
const i = points.indexOf(p1);
|
||
const p2 = points[(i + 1) % points.length];
|
||
const p3 = this.verticalIntersectionPoint(p1, p2, x);
|
||
if (p3) {
|
||
points.splice(i + 1, 0, p3);
|
||
}
|
||
}
|
||
if (size < points.length) {
|
||
return points.filter(p => cutAbove ? p.x <= x : p.x >= x);
|
||
}
|
||
return points;
|
||
};
|
||
|
||
EncounterEffect.verticalIntersectionPoint = function(point1, point2, x) {
|
||
const x1 = point1.x;
|
||
const y1 = point1.y;
|
||
const x2 = point2.x;
|
||
const y2 = point2.y;
|
||
if ((x1 < x && x2 > x) || (x1 > x && x2 < x)) {
|
||
const y = y1 + Math.round((y2 - y1) * (x - x1) / (x2 - x1));
|
||
return new Point(x, y);
|
||
}
|
||
return null;
|
||
};
|
||
|
||
EncounterEffect.cutProtrusionY = function(points, y, cutAbove) {
|
||
const size = points.length;
|
||
for (const p1 of points) {
|
||
const i = points.indexOf(p1);
|
||
const p2 = points[(i + 1) % points.length];
|
||
const p3 = this.horizontalIntersectionPoint(p1, p2, y);
|
||
if (p3) {
|
||
points.splice(i + 1, 0, p3);
|
||
}
|
||
}
|
||
if (size < points.length) {
|
||
return points.filter(p => cutAbove ? p.y <= y : p.y >= y);
|
||
}
|
||
return points;
|
||
};
|
||
|
||
EncounterEffect.horizontalIntersectionPoint = function(point1, point2, y) {
|
||
const x1 = point1.x;
|
||
const y1 = point1.y;
|
||
const x2 = point2.x;
|
||
const y2 = point2.y;
|
||
if ((y1 < y && y2 > y) || (y1 > y && y2 < y)) {
|
||
const x = x1 + Math.round((x2 - x1) * (y - y1) / (y2 - y1));
|
||
return new Point(x, y);
|
||
}
|
||
return null;
|
||
};
|
||
|
||
EncounterEffect.removeOuterPoint = function(points) {
|
||
const { minX, maxX, minY, maxY } = this.windowBounds;
|
||
return points.filter(
|
||
({ x, y }) => x >= minX && x <= maxX && y >= minY && y <= maxY
|
||
);
|
||
};
|
||
|
||
EncounterEffect.uniqPoints = function(points) {
|
||
return points.filter(
|
||
(p1, i) => points.findIndex(p2 => p2.equals(p1)) === i
|
||
);
|
||
};
|
||
|
||
EncounterEffect.createAllPointsT1 = function() {
|
||
const {
|
||
'Shape Type': sType = 'square',
|
||
'Split Radial': splitX = 8,
|
||
'Circle Radius': baseRadius = 96,
|
||
'Circle Increase Rate': circleRate = 150
|
||
} = this._params;
|
||
const maxRadius = this.maxRadius();
|
||
|
||
const allPoints = [];
|
||
let startAngle = 360 * Math.random();
|
||
let innerR = 0;
|
||
let currentR = baseRadius;
|
||
for (let i = 0; i < 8; i++) {
|
||
if (sType === 'triangle') startAngle += 180 / splitX;
|
||
if (i === 7) innerR = 1000000;
|
||
allPoints.push(this.createRoundPoints(startAngle, innerR, currentR));
|
||
if (innerR > maxRadius) break;
|
||
innerR += currentR;
|
||
currentR *= circleRate / 100;
|
||
}
|
||
return allPoints;
|
||
};
|
||
|
||
EncounterEffect.createRoundPoints = function(startAngle, innerR, currentR) {
|
||
const {
|
||
'Split Radial': splitX = 8,
|
||
'Radial Random Rate': rRate = 90,
|
||
'Circle Random Rate': cRate = 40
|
||
} = this._params;
|
||
const baseAngle = 360 / splitX;
|
||
|
||
const randomAngle = (n) => {
|
||
return (startAngle + baseAngle * n +
|
||
baseAngle * rRate * Math.random() / 100) % 360;
|
||
};
|
||
const randomRadius = () => {
|
||
return innerR +
|
||
currentR * (1 - cRate * (0.75 - Math.random()) / 100);
|
||
};
|
||
|
||
return [...Array(splitX).keys()].map(
|
||
i => this.calculatePoint(randomAngle(i), randomRadius())
|
||
);
|
||
};
|
||
|
||
EncounterEffect.calculatePoint = function(angle, radius) {
|
||
const { x, y } = this.centerPoint;
|
||
const sx = Math.round(radius * Math.cos(angle * Math.PI / 180));
|
||
const sy = Math.round(radius * Math.sin(angle * Math.PI / 180));
|
||
return new Point(x + sx, y + sy);
|
||
};
|
||
|
||
EncounterEffect.maxRadius = function() {
|
||
const { width, height } = Graphics;
|
||
const { x, y } = this.centerPoint;
|
||
const deltaX = x < width / 2 ? width - x : x;
|
||
const deltaY = y < height / 2 ? height - y : y;
|
||
return Math.hypot(deltaX, deltaY);
|
||
};
|
||
|
||
EncounterEffect.createFragmentsT2 = function() {
|
||
switch (this.currentParam('Shape Type', 'square')) {
|
||
case 'square':
|
||
this.createFragmentsT2Square();
|
||
break;
|
||
case 'triangle':
|
||
this.createFragmentsT2Triangle();
|
||
break;
|
||
case 'random':
|
||
this.createFragmentsT2Random();
|
||
break;
|
||
}
|
||
};
|
||
|
||
EncounterEffect.createFragmentsT2Square = function() {
|
||
const allPoints = this.createAllPointsT2Square();
|
||
for (const points of this.generaterPointsT2(allPoints)) {
|
||
this.addFragment(points);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.generaterPointsT2 = function*(allPoints) {
|
||
const {
|
||
'Shape Type': sType = 'square',
|
||
'Split X': splitX = 12,
|
||
'Split Y': splitY = 9
|
||
} = this._params;
|
||
for (let i = 0; i < splitX; i++) {
|
||
const leftPoints = allPoints[i];
|
||
const rightPoints = allPoints[i + 1];
|
||
for (let j = 0; j < splitY; j++) {
|
||
const point1 = leftPoints[j];
|
||
const point2 = rightPoints[j];
|
||
const point3 = rightPoints[j + 1];
|
||
const point4 = leftPoints[j + 1];
|
||
if (sType === 'triangle') {
|
||
yield [point1, point2, point4];
|
||
yield [point2, point3, point4];
|
||
} else {
|
||
yield [point1, point2, point3, point4];
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
EncounterEffect.createAllPointsT2Square = function() {
|
||
const {
|
||
'Split X': splitX = 12,
|
||
'Split Y': splitY = 9,
|
||
'X Random Rate': xRate = 80,
|
||
'Y Random Rate': yRate = 80
|
||
} = this._params;
|
||
const { minX, maxX, minY, maxY } = this.windowBounds;
|
||
const width = Graphics.width / Math.max(splitX - xRate / 100, 1);
|
||
const height = Graphics.height / Math.max(splitY - yRate / 100, 1);
|
||
|
||
const pointX = (i) => {
|
||
if (i === 0) {
|
||
return minX;
|
||
} else if (i === splitX) {
|
||
return maxX;
|
||
}
|
||
return this.randomValue(width, i, xRate);
|
||
};
|
||
const pointY = (j) => {
|
||
if (j === 0) {
|
||
return minY;
|
||
} else if (j === splitY) {
|
||
return maxY;
|
||
}
|
||
return this.randomValue(height, j, yRate);
|
||
};
|
||
|
||
return [...Array(splitX + 1).keys()].map(i => {
|
||
return [...Array(splitY + 1).keys()].map(j => {
|
||
return new Point(pointX(i), pointY(j));
|
||
});
|
||
});
|
||
};
|
||
|
||
EncounterEffect.randomValue = function(value, index, rate) {
|
||
return Math.round(value * (index - rate * Math.random() / 100));
|
||
};
|
||
|
||
EncounterEffect.createFragmentsT2Triangle = function() {
|
||
const allPoints = this.createAllPointsT2Triangle();
|
||
for (const points of this.generaterPointsT2(allPoints)) {
|
||
this.addFragment(points);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.createAllPointsT2Triangle = function() {
|
||
const {
|
||
'Split X': splitX = 12,
|
||
'Split Y': splitY = 9,
|
||
'X Random Rate': xRate = 80,
|
||
'Y Random Rate': yRate = 80
|
||
} = this._params;
|
||
const { minX, maxX, minY, maxY } = this.windowBounds;
|
||
const width = Graphics.width / Math.max(splitX - xRate / 100, 1);
|
||
const height = Graphics.height / Math.max(splitY - 0.5, 1);
|
||
const bottomY = height / 2 * yRate / 100;
|
||
|
||
const pointX = (i) =>{
|
||
if (i === 0) {
|
||
return minX;
|
||
} else if (i === splitX) {
|
||
return maxX;
|
||
}
|
||
return this.randomValue(width, i, xRate);
|
||
};
|
||
const pointY = (i, j) => {
|
||
if (j === 0) {
|
||
return minY;
|
||
} else if (j === splitY) {
|
||
return maxY;
|
||
}
|
||
if (i % 2 === 1 && j === 1) {
|
||
return this.randomValue(height / 2, j, yRate);
|
||
}
|
||
const py = bottomY - (i % 2 === 0 ? 0 : height / 2);
|
||
return py + this.randomValue(height, j, yRate);
|
||
};
|
||
|
||
return [...Array(splitX + 1).keys()].map(i => {
|
||
return [...Array(splitY + 1).keys()].map(j => {
|
||
return new Point(pointX(i), pointY(i, j));
|
||
});
|
||
});
|
||
};
|
||
|
||
EncounterEffect.createFragmentsT2Random = function() {
|
||
const allPolygons = this.createPolygonsT2Random();
|
||
|
||
const sidePolygon = (i, j, pos) => {
|
||
const si = i + (pos === 0 || pos === 1 ? -1 : 1);
|
||
const subPolygons = allPolygons[si];
|
||
if (!subPolygons) {
|
||
return null;
|
||
}
|
||
const sj = Math.floor(j / 2) * 2;
|
||
const polygon2 = subPolygons[sj];
|
||
if (Math.floor(pos / 2) === Math.floor(polygon2.pos / 2)) {
|
||
return subPolygons[sj + 1];
|
||
}
|
||
return polygon2;
|
||
};
|
||
|
||
for (const [i, mainPolygons] of allPolygons.entries()) {
|
||
for (const [j, polygon1] of mainPolygons.entries()) {
|
||
if (polygon1.state === 'none') {
|
||
const d = MathExt.randomInt(3);
|
||
let polygon2;
|
||
if (d === 0) {
|
||
// 上
|
||
polygon2 = mainPolygons[j - 1];
|
||
} else if (d === 1) {
|
||
// 下
|
||
polygon2 = mainPolygons[j + 1];
|
||
} else {
|
||
// 横
|
||
polygon2 = sidePolygon(i, j, polygon1.pos);
|
||
}
|
||
if (polygon2 && polygon2.state === 'none') {
|
||
this.joinPolygon(polygon1, polygon2, d);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
for (const polygon of allPolygons.flat()) {
|
||
if (polygon.state === 'none' || polygon.state === 'received') {
|
||
this.addFragment(polygon.points);
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
*
|
||
* @returns {array} 二次元配列。
|
||
* pos: 分割した際の位置。0=左上, 1=左下, 2=右上, 3=右下。
|
||
*/
|
||
EncounterEffect.createPolygonsT2Random = function() {
|
||
const allPoints = this.createAllPointsT2Square();
|
||
const polygons = [];
|
||
for (const [p1, p2, p3, p4] of this.generaterPointsT2(allPoints)) {
|
||
if (Math.random() < 0.5) {
|
||
polygons.push(
|
||
{ points:[p4, p1, p2], pos:0, state:'none' },
|
||
{ points:[p4, p2, p3], pos:3, state:'none' }
|
||
);
|
||
} else {
|
||
polygons.push(
|
||
{ points:[p1, p2, p3], pos:2, state:'none' },
|
||
{ points:[p4, p1, p3], pos:1, state:'none' }
|
||
);
|
||
}
|
||
}
|
||
|
||
const splitY = this.currentParam('Split Y', 9);
|
||
const allPolygons = [];
|
||
while (polygons.length > 0) {
|
||
allPolygons.push(polygons.splice(0, splitY * 2));
|
||
}
|
||
return allPolygons;
|
||
};
|
||
|
||
EncounterEffect.joinPolygon = function(polygon1, polygon2, d) {
|
||
const pos1 = polygon1.pos;
|
||
const pos2 = polygon2.pos;
|
||
let start = 0, index = 0;
|
||
if (d === 0) {
|
||
start = pos1 === 0 || pos1 === 1 ? 2 : 1;
|
||
index = 1;
|
||
} else if (d === 1) {
|
||
start = 0;
|
||
index = pos1 === 0 ? 2 : pos1 === 2 ? 0 : pos2 === 0 ? 0 : 2;
|
||
} else {
|
||
start = pos1 === 0 || pos1 === 1 ? 1 : 2;
|
||
index = pos2 === 0 || pos2 === 1 ? 2 : 0;
|
||
}
|
||
polygon1.points.splice(start, 0, polygon2.points[index]);
|
||
polygon1.state = 'received';
|
||
polygon2.state = 'joined';
|
||
};
|
||
|
||
EncounterEffect.addFragment = function(points) {
|
||
const fragment = new Encounter_Fragment(points);
|
||
if (fragment.isValid()) {
|
||
fragment.setup();
|
||
this._fragments.push(fragment);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.sortFragment = function(direction) {
|
||
const formula = this.compareFormula(direction);
|
||
if (formula) {
|
||
this._fragments.sort((a, b) => formula(a) - formula(b));
|
||
}
|
||
};
|
||
|
||
EncounterEffect.compareFormula = function(direction) {
|
||
const { x, y } = this.centerPoint;
|
||
switch (direction) {
|
||
case 'left':
|
||
return (f) => {
|
||
return f._x;
|
||
};
|
||
case 'center':
|
||
return (f) => {
|
||
return Math.abs(x - f._x);
|
||
};
|
||
case 'right':
|
||
return (f) => {
|
||
return -f._x;
|
||
};
|
||
case 'inside':
|
||
return (f) => {
|
||
return Math.abs(x - f._x) + Math.abs(y - f._y) * y / x;
|
||
};
|
||
case 'outside':
|
||
return (f) => {
|
||
return -Math.abs(x - f._x) - Math.abs(y - f._y) * y / x;
|
||
};
|
||
}
|
||
return null;
|
||
};
|
||
|
||
EncounterEffect.breakFragments = function(start, end, duration) {
|
||
for (let i = start; i < end; i++) {
|
||
this._fragments[i].onBreak(duration, i);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.onBattleStart = function() {
|
||
const {
|
||
'Move Duration': duration = 60,
|
||
'Scatter Direction': direction = 'none',
|
||
'Scatter Duration': delay = 0
|
||
} = this._params;
|
||
this.sortFragment(direction);
|
||
this.startFragments(duration, delay);
|
||
this.startOpacity(duration);
|
||
if (this._type === 5) {
|
||
this._targetAngle = -4;
|
||
this._angleEasing.start('Straight', duration);
|
||
}
|
||
};
|
||
|
||
EncounterEffect.startFragments = function(duration, delay) {
|
||
const maxItems = this.maxItems();
|
||
for (const [i, fragment] of this._fragments.entries()) {
|
||
fragment.onBattleStart(duration, delay);
|
||
fragment.setDelay(Math.floor(delay * i / maxItems));
|
||
}
|
||
};
|
||
|
||
EncounterEffect.startOpacity = function(duration) {
|
||
if (this._type === 5) {
|
||
this._opacityDuration = 1;
|
||
this._opacityDelay = duration - 1;
|
||
} else {
|
||
this._opacityDelay = Math.floor(duration / 2);
|
||
this._opacityDuration = duration - this._opacityDelay;
|
||
}
|
||
};
|
||
|
||
EncounterEffect.update = function() {
|
||
if (this.isRunning()) {
|
||
this._fragments.forEach(fragment => fragment.update());
|
||
this.updateOpacity();
|
||
this.updateAngle();
|
||
if (this._opacity === 0) {
|
||
this.destroy();
|
||
this.clear();
|
||
}
|
||
}
|
||
};
|
||
|
||
EncounterEffect.updateOpacity = function() {
|
||
if (this._opacityDuration > 0 && this.updateOpacityDelay()) {
|
||
const d = this._opacityDuration--;
|
||
this._opacity *= (d - 1) / d;
|
||
}
|
||
};
|
||
|
||
EncounterEffect.updateOpacityDelay = function() {
|
||
if (this._opacityDelay > 0) {
|
||
this._opacityDelay--;
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
EncounterEffect.updateAngle = function() {
|
||
const easing = this._angleEasing;
|
||
if (easing.isMoving()) {
|
||
this._angle = easing.apply(this._angle, this._targetAngle);
|
||
easing.update();
|
||
}
|
||
};
|
||
|
||
EncounterEffect.maxItems = function() {
|
||
return this._fragments ? this._fragments.length : 0;
|
||
};
|
||
|
||
EncounterEffect.fragments = function() {
|
||
return this._fragments || [];
|
||
};
|
||
|
||
EncounterEffect.isValid = function() {
|
||
return !!baseTypeParameters[this._type];
|
||
};
|
||
|
||
EncounterEffect.isRunning = function() {
|
||
return !!this._fragments;
|
||
};
|
||
|
||
EncounterEffect.isReady = function() {
|
||
return !this.isRunning() || this._opacityDuration < 45;
|
||
};
|
||
|
||
EncounterEffect.breakDuration = function() {
|
||
return this.currentParam('Break Duration', 45);
|
||
};
|
||
|
||
EncounterEffect.effectSpeed = function() {
|
||
const d = this.currentParam('Interval Duration', 45);
|
||
return this.breakDuration() + d + 2;
|
||
};
|
||
|
||
EncounterEffect.fadeSpeed = function() {
|
||
return this.currentParam('Move Duration', 60);
|
||
};
|
||
|
||
EncounterEffect.currentParam = function(name, def = null) {
|
||
return this._params ? this._params[name] || def : def;
|
||
};
|
||
|
||
EncounterEffect.rotation = function() {
|
||
return this._angle * Math.PI / 180;
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// SceneManager
|
||
|
||
SceneManager.snapTexture = function() {
|
||
const stage = this._scene
|
||
const width = Graphics.width;
|
||
const height = Graphics.height;
|
||
const renderTexture = PIXI.RenderTexture.create(width, height);
|
||
if (stage) {
|
||
const renderer = Graphics.app.renderer;
|
||
renderer.render(stage, renderTexture);
|
||
stage.worldTransform.identity();
|
||
}
|
||
return renderTexture;
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// SoundManager
|
||
|
||
const _SoundManager_playBattleStart = SoundManager.playBattleStart;
|
||
SoundManager.playBattleStart = function() {
|
||
if (EncounterEffect.type() >= 0) {
|
||
_SoundManager_playBattleStart.apply(this, arguments);
|
||
}
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Encounter_Fragment
|
||
|
||
class Encounter_Fragment {
|
||
constructor(points) {
|
||
this._points = points;
|
||
this.initRect(points);
|
||
this.initMembers();
|
||
}
|
||
|
||
initMembers() {
|
||
const rect = this._rect;
|
||
this._x = rect.x + Math.floor(rect.width / 2);
|
||
this._y = rect.y + Math.floor(rect.height / 2);
|
||
this._targetX = 0;
|
||
this._targetY = 0;
|
||
this._jumpHeight = 0;
|
||
this._scale = 1;
|
||
this._targetScale = 1;
|
||
this._moveEasing = new Easing();
|
||
this._opacity = 255;
|
||
this._opacityDuration = 0;
|
||
this._delay = 0;
|
||
this._flash = false;
|
||
this._break = false;
|
||
this._slideX = 0;
|
||
this._slideY = 0;
|
||
this.visible = true;
|
||
}
|
||
|
||
initRect(points) {
|
||
const allX = points.map( point => point.x );
|
||
const allY = points.map( point => point.y );
|
||
const x = Math.min(...allX);
|
||
const y = Math.min(...allY);
|
||
const width = Math.max(...allX) - x;
|
||
const height = Math.max(...allY) - y;
|
||
this._rect = new Rectangle(x, y, width, height);
|
||
}
|
||
|
||
isValid() {
|
||
return this._rect.width > 4 && this._rect.height > 4;
|
||
}
|
||
|
||
setDelay(delay) {
|
||
this._delay = delay;
|
||
}
|
||
|
||
clearFlash() {
|
||
this._flash = false;
|
||
}
|
||
|
||
isFlashRequested() {
|
||
return this._flash;
|
||
}
|
||
|
||
rect() {
|
||
return this._rect;
|
||
}
|
||
|
||
isBreak() {
|
||
return this._break;
|
||
}
|
||
|
||
lineWidth() {
|
||
return 4;
|
||
}
|
||
|
||
flashOpacity() {
|
||
return 255;
|
||
}
|
||
|
||
breakRate() {
|
||
return 1;
|
||
}
|
||
|
||
slideRate() {
|
||
return 1;
|
||
}
|
||
|
||
rotationRate() {
|
||
return 1;
|
||
}
|
||
|
||
screenX() {
|
||
return this._x;
|
||
}
|
||
|
||
screenY() {
|
||
return this._y - this._jumpHeight;
|
||
}
|
||
|
||
flashColor() {
|
||
const color = [...EncounterEffect.color()];
|
||
color[3] = this.flashOpacity();
|
||
return color;
|
||
}
|
||
|
||
points() {
|
||
return this._points;
|
||
}
|
||
|
||
setup() {
|
||
this.setupRotation();
|
||
}
|
||
|
||
setupRotation() {
|
||
this._speed = 0;
|
||
this._pace = 0;
|
||
this._rotationX = 0;
|
||
this._rotationY = 0;
|
||
this._rotationZ = 0;
|
||
const rate = this.rotationRate();
|
||
if (Math.random() < 0.5) {
|
||
this._rotationSpeedX = (Math.random() + 0.5) * 2 * rate;
|
||
this._rotationSpeedY = 0;
|
||
} else {
|
||
this._rotationSpeedX = 0;
|
||
this._rotationSpeedY = (Math.random() + 0.5) * 2 * rate;
|
||
}
|
||
this._rotationSpeedZ = (Math.random() - 0.5) * 3 * rate;
|
||
}
|
||
|
||
textureMatrix() {
|
||
const tx = this._break ? this._slideX : 0;
|
||
const ty = this._break ? this._slideY : 0;
|
||
return new PIXI.Matrix(1, 0, 0, 1, tx, ty);
|
||
}
|
||
|
||
onBreak(duration, index) {
|
||
this.startEncounter(duration, index);
|
||
this._flash = true;
|
||
this._break = true;
|
||
}
|
||
|
||
startEncounter(duration, index) {
|
||
switch (EncounterEffect.type()) {
|
||
case 1:
|
||
this.setupMoveT1(duration);
|
||
this.setupSlideT1()
|
||
break;
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
this.setupMoveT2();
|
||
this.setupSlideT2();
|
||
break;
|
||
case 5:
|
||
this.setupMoveT5(duration);
|
||
break;
|
||
}
|
||
}
|
||
|
||
setupMoveT1(duration) {
|
||
const breakRate = this.breakRate();
|
||
this._moveEasing.start('Slow end', duration);
|
||
this._speed = this.hypotSpeed(1.5) * breakRate;
|
||
this._pace = -1;
|
||
this.setMoveT1(breakRate / 100, breakRate / 100);
|
||
}
|
||
|
||
setupMoveT2() {
|
||
const breakRate = this.breakRate();
|
||
this._speed = 0.125 * breakRate;
|
||
this._pace = -1;
|
||
}
|
||
|
||
setupSlideT1() {
|
||
const slideRate = this.slideRate();
|
||
const { x, y } = EncounterEffect.centerPoint;
|
||
const radian = Math.atan2(this._y - y, this._x - x);
|
||
this._slideX = 20 * Math.cos(radian) * slideRate;
|
||
this._slideY = 20 * Math.sin(radian) * slideRate;
|
||
}
|
||
|
||
setupSlideT2() {
|
||
const slideRate = this.slideRate();
|
||
const { x, y, width, height } = this._rect;
|
||
const mx = (MathExt.randomInt(21) - 10) * slideRate;
|
||
const my = (MathExt.randomInt(21) - 10) * slideRate;
|
||
const minX = x + width - Graphics.width;
|
||
const minY = y + height - Graphics.height;
|
||
this._slideX = MathExt.clamp(mx, minX, x);
|
||
this._slideY = MathExt.clamp(my, minY, y);
|
||
}
|
||
|
||
setupMoveT5(duration) {
|
||
this._moveEasing.start('Slow end', duration);
|
||
this._speed = 0;
|
||
this._pace = 0;
|
||
this.setMoveAway('outside', 1.1);
|
||
}
|
||
|
||
update() {
|
||
if (this.visible && this.updateDelay()) {
|
||
this.updateMove();
|
||
this.updateOpacity();
|
||
this.updateRotation();
|
||
}
|
||
}
|
||
|
||
updateDelay() {
|
||
if (this._delay > 0) {
|
||
this._delay--;
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
updateMove() {
|
||
const easing = this._moveEasing;
|
||
if (easing.isMoving()) {
|
||
this._x = easing.apply(this._x, this._targetX);
|
||
this._y = easing.apply(this._y, this._targetY);
|
||
this._scale = easing.apply(this._scale, this._targetScale);
|
||
this.visible = this._scale < 8;
|
||
if (EncounterEffect.currentParam('Jump')) {
|
||
const d = easing._duration;
|
||
const wd = easing._wholeDuration;
|
||
const m = 4 * (d - 1) / wd;
|
||
this._jumpHeight = (2 * 2 - Math.pow(m - 2, 2)) * 96;
|
||
}
|
||
easing.update();
|
||
}
|
||
}
|
||
|
||
updateOpacity() {
|
||
if (this._opacityDuration > 0) {
|
||
const d = this._opacityDuration--;
|
||
this._opacity *= (d - 1) / d;
|
||
}
|
||
}
|
||
|
||
updateRotation() {
|
||
if (this._speed > 0) {
|
||
this._speed *= 1 + 0.04 * this._pace;
|
||
this._rotationX += this._rotationSpeedX * this._speed;
|
||
this._rotationY += this._rotationSpeedY * this._speed;
|
||
this._rotationZ += this._rotationSpeedZ * this._speed;
|
||
}
|
||
}
|
||
|
||
onBattleStart(duration, delay) {
|
||
switch (EncounterEffect.type()) {
|
||
case 1:
|
||
this.onBattleStartT1(duration);
|
||
break;
|
||
case 2:
|
||
this.onBattleStartT2(Math.max(duration - delay, 1));
|
||
break;
|
||
case 3:
|
||
this.onBattleStartT3(duration);
|
||
break;
|
||
case 4:
|
||
this.onBattleStartT4(duration);
|
||
break;
|
||
case 5:
|
||
this.onBattleStartT5(Math.max(duration - delay, 1));
|
||
break;
|
||
}
|
||
}
|
||
|
||
onBattleStartT1(duration) {
|
||
this._moveEasing.start('Slow end', duration);
|
||
this._speed = this.hypotSpeed(40);
|
||
this._pace = -1;
|
||
this.setMoveT1(0.8 + Math.random() / 2, 0.8 + Math.random() / 2);
|
||
}
|
||
|
||
hypotSpeed(baseSpeed) {
|
||
const { x, y } = EncounterEffect.centerPoint;
|
||
return baseSpeed / Math.sqrt(Math.hypot(this._x - x, this._y - y));
|
||
}
|
||
|
||
setMoveT1(rateX, rateY) {
|
||
const { minX, maxX, minY, maxY } = EncounterEffect.windowBounds;
|
||
const { x: cx, y: cy } = EncounterEffect.centerPoint;
|
||
const sx = this._x - cx;
|
||
const sy = this._y - cy;
|
||
let ox = sx;
|
||
let oy = sy;
|
||
if (ox < 0) {
|
||
oy *= (minX - cx) / ox;
|
||
ox = minX - cx;
|
||
} else if (ox > 0) {
|
||
oy *= (maxX - cx) / ox;
|
||
ox = maxX - cx;
|
||
} else {
|
||
oy = oy < cy ? minY - cy : maxY - cy;
|
||
}
|
||
if (oy < minY - cy) {
|
||
ox *= (minY - cy) / oy;
|
||
oy = minY - cy;
|
||
} else if (oy > maxY - cy) {
|
||
ox *= (maxY - cy) / oy;
|
||
oy = maxY - cy;
|
||
}
|
||
this._targetX = this._x + (ox - sx) * rateX;
|
||
this._targetY = this._y + (oy - sy) * rateY;
|
||
}
|
||
|
||
onBattleStartT2(duration) {
|
||
this._moveEasing.start('Slow start', duration);
|
||
this._speed = 1;
|
||
this._pace = 1;
|
||
const dir = EncounterEffect.currentParam('Scatter Direction');
|
||
this.setMoveT2(dir);
|
||
}
|
||
|
||
setMoveT2(direction) {
|
||
const { x: cx, y: cy } = EncounterEffect.centerPoint;
|
||
switch (direction) {
|
||
case 'left':
|
||
this._targetX = this._x - Graphics.width;
|
||
this._targetY = this._y + (this._y - cy);
|
||
break;
|
||
case 'right':
|
||
this._targetX = this._x + Graphics.width;
|
||
this._targetY = this._y + (this._y - cy);
|
||
break;
|
||
case 'outside': {
|
||
const radian = Math.atan2(this._y - cy, this._x - cx);
|
||
const d = (cx + cy) / 2;
|
||
this._targetX = this._x + d * Math.cos(radian);
|
||
this._targetY = this._y + d * Math.sin(radian);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
onBattleStartT3(duration) {
|
||
this._moveEasing.start('Slow end', duration);
|
||
this._speed = 2;
|
||
this._pace = 0;
|
||
this.setMoveT3();
|
||
}
|
||
|
||
setMoveT3() {
|
||
const { x: cx, y: cy } = EncounterEffect.centerPoint;
|
||
const sx = this._x - cx;
|
||
const sy = this._y - cy;
|
||
|
||
const rate = 3;
|
||
this._targetX = (sx + 80 * (Math.random() - 0.5)) * rate + cx;
|
||
this._targetY = (sy + 32 * (Math.random() - 0.5)) * rate + cy * 3;
|
||
|
||
const delta = Math.abs(sx) + Math.abs(sy);
|
||
this._targetScale = delta < 128 && Math.random() < 0.4 ? 12 : 1;
|
||
this._targetScale += 2.5 * Math.random();
|
||
}
|
||
|
||
onBattleStartT4(duration) {
|
||
this._moveEasing.start('Slow end', duration);
|
||
this._speed = 2;
|
||
this._pace = -1;
|
||
const dir = EncounterEffect.currentParam('Scatter Direction');
|
||
this.setMoveAway(dir, 2);
|
||
}
|
||
|
||
onBattleStartT5(duration) {
|
||
this._moveEasing.start('Straight', duration);
|
||
this._speed = 0;
|
||
this._pace = 0;
|
||
this.setMoveAway('outside', 2.25);
|
||
this._targetScale = 1.75;
|
||
this._opacityDuration = duration;
|
||
}
|
||
|
||
setMoveAway(direction, rate) {
|
||
let { x, y } = EncounterEffect.centerPoint;
|
||
switch (direction) {
|
||
case 'left':
|
||
x = Graphics.width;
|
||
break;
|
||
case 'right':
|
||
x = 0;
|
||
break;
|
||
}
|
||
this._targetX = x + (this._x - x) * rate;
|
||
this._targetY = y + (this._y - y) * rate;
|
||
}
|
||
|
||
opacity() {
|
||
return this._opacity;
|
||
}
|
||
|
||
scaleX() {
|
||
return Math.cos(this._rotationX * Math.PI / 180) * this._scale;
|
||
}
|
||
|
||
scaleY() {
|
||
return Math.cos(this._rotationY * Math.PI / 180) * this._scale;
|
||
}
|
||
|
||
rotation() {
|
||
return this._rotationZ * Math.PI / 180;
|
||
}
|
||
|
||
}
|
||
|
||
if (_importedPlugin(...pluginOptions)) {
|
||
window.Encounter_Fragment = Encounter_Fragment;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Easing
|
||
|
||
class Easing {
|
||
constructor(type = '', duration = 0) {
|
||
this.setType(type);
|
||
this.setDuration(duration);
|
||
}
|
||
|
||
start(type, duration) {
|
||
this.setType(type);
|
||
this.setDuration(duration);
|
||
}
|
||
|
||
setType(type) {
|
||
this._type = type || 'Slow end';
|
||
}
|
||
|
||
setDuration(duration) {
|
||
this._duration = duration;
|
||
this._wholeDuration = duration;
|
||
}
|
||
|
||
clear() {
|
||
this._duration = 0;
|
||
}
|
||
|
||
isMoving() {
|
||
return this._duration > 0;
|
||
}
|
||
|
||
update() {
|
||
if (this._duration > 0) {
|
||
this._duration--;
|
||
}
|
||
}
|
||
|
||
apply(current, target) {
|
||
const d = this._duration;
|
||
const wd = this._wholeDuration;
|
||
const lt = this.calc((wd - d) / wd);
|
||
const t = this.calc((wd - d + 1) / wd);
|
||
const start = (current - target * lt) / (1 - lt);
|
||
return start + (target - start) * t;
|
||
}
|
||
|
||
calc(t) {
|
||
switch (this._type) {
|
||
case 'Slow start':
|
||
return this.easeIn(t);
|
||
case 'Slow end':
|
||
return this.easeOut(t);
|
||
case 'Slow start and end':
|
||
return this.easeInOut(t);
|
||
default:
|
||
return t;
|
||
}
|
||
}
|
||
|
||
easeIn(t) {
|
||
return Math.pow(t, 2);
|
||
}
|
||
|
||
easeOut(t) {
|
||
return 1 - Math.pow(1 - t, 2);
|
||
}
|
||
|
||
easeInOut(t) {
|
||
if (t < 0.5) {
|
||
return this.easeIn(t * 2) / 2;
|
||
} else {
|
||
return this.easeOut(t * 2 - 1) / 2 + 0.5;
|
||
}
|
||
}
|
||
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// PluginManager
|
||
|
||
PluginManager.registerCommand(pluginName, 'setType', args => {
|
||
EncounterEffect.setType(PluginManager.mppValue(args.type));
|
||
});
|
||
|
||
PluginManager.registerCommand(pluginName, 'setCharacter', function(args) {
|
||
const eId = PluginManager.mppValue(args.character);
|
||
EncounterEffect.setCharacter(this.character(eId));
|
||
});
|
||
|
||
PluginManager.registerCommand(pluginName, 'setColor', args => {
|
||
const color = args.color.split(',');
|
||
const r = PluginManager.mppValue(color[0]);
|
||
const g = PluginManager.mppValue(color[1]);
|
||
const b = PluginManager.mppValue(color[2]);
|
||
EncounterEffect.setColor(r, g, b);
|
||
});
|
||
|
||
PluginManager.mppValue = function(value) {
|
||
const match = /^V\[(\d+)\]$/i.exec(value);
|
||
return match ? $gameVariables.value(+match[1]) : +value;
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Game_Map
|
||
|
||
Game_Map.prototype.getInterpreterCharacter = function(param) {
|
||
const interpreter = this._interpreter;
|
||
return interpreter.isRunning() ? interpreter.character(param) : null;
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Graphics_Fragment
|
||
|
||
class Graphics_Fragment extends PIXI.Graphics {
|
||
constructor(fragment) {
|
||
super();
|
||
this._fragment = fragment;
|
||
this._blendColor = [0, 0, 0, 0];
|
||
this._break = fragment.isBreak();
|
||
this._flashDuration = 0;
|
||
this.initPivot();
|
||
this.refresh();
|
||
this.update();
|
||
}
|
||
|
||
initPivot() {
|
||
const rect = this._fragment.rect();
|
||
const x = rect.x + Math.floor(rect.width / 2);
|
||
const y = rect.y + Math.floor(rect.height / 2);
|
||
this.pivot.set(x, y);
|
||
}
|
||
|
||
refreshIfNeeded() {
|
||
if (!this._break && this._fragment.isBreak()) {
|
||
this._break = true;
|
||
this.refresh();
|
||
}
|
||
}
|
||
|
||
refresh() {
|
||
this.clear();
|
||
const options = {
|
||
texture: EncounterEffect.snapTexture(),
|
||
matrix: this._fragment.textureMatrix()
|
||
};
|
||
this.beginTextureFill(options);
|
||
this.drawPolygon();
|
||
this.endFill();
|
||
const lw = this._fragment.lineWidth() / 2;
|
||
if (this._break && lw > 0) {
|
||
const c = EncounterEffect.color();
|
||
const color = c[0] * 256 * 256 + c[1] * 256 + c[2];
|
||
this.lineStyle(lw + 0.5, color, 0.25, 0.3);
|
||
this.drawPolygon();
|
||
this.lineStyle(lw, color, 0.5, 0.3);
|
||
this.drawPolygon();
|
||
}
|
||
}
|
||
|
||
drawPolygon() {
|
||
for (const [i, point] of this._fragment.points().entries()) {
|
||
i === 0
|
||
? this.moveTo(point.x, point.y)
|
||
: this.lineTo(point.x, point.y);
|
||
}
|
||
this.closePath();
|
||
}
|
||
|
||
update() {
|
||
this.refreshIfNeeded();
|
||
this.updatePosition();
|
||
this.updateOpacity();
|
||
this.updateFlash();
|
||
this.updateVisibility();
|
||
}
|
||
|
||
updatePosition() {
|
||
const fragment = this._fragment;
|
||
this.x = fragment.screenX();
|
||
this.y = fragment.screenY();
|
||
this.scale.x = fragment.scaleX();
|
||
this.scale.y = fragment.scaleY();
|
||
this.rotation = fragment.rotation();
|
||
}
|
||
|
||
updateOpacity() {
|
||
this.alpha = this._fragment.opacity() / 255;
|
||
}
|
||
|
||
updateFlash() {
|
||
this.setupFlash();
|
||
if (this._flashDuration > 0) {
|
||
const d = this._flashDuration--;
|
||
this._blendColor[3] *= (d - 1) / d;
|
||
if (this._flashDuration % 8 === 0) {
|
||
this._updateColorFilter();
|
||
}
|
||
}
|
||
}
|
||
|
||
setupFlash() {
|
||
if (this._fragment.isFlashRequested()) {
|
||
this._flashDuration = 25;
|
||
this._blendColor = this._fragment.flashColor();
|
||
this._fragment.clearFlash();
|
||
}
|
||
}
|
||
|
||
_createColorFilter() {
|
||
this._colorFilter = new BlendColorFilter();
|
||
this.filters = [this._colorFilter];
|
||
}
|
||
|
||
_removeColorFilter() {
|
||
this.filters = null;
|
||
this._colorFilter = null;
|
||
}
|
||
|
||
_updateColorFilter() {
|
||
if (this._colorFilter && this._blendColor[3] === 0) {
|
||
this._removeColorFilter();
|
||
return;
|
||
}
|
||
if (!this._colorFilter) {
|
||
this._createColorFilter();
|
||
}
|
||
this._colorFilter.setColor(this._blendColor);
|
||
}
|
||
|
||
updateVisibility() {
|
||
this.visible = this._fragment.visible;
|
||
}
|
||
|
||
}
|
||
|
||
//-------------------------------------------------------------------------
|
||
// BlendColorFilter
|
||
|
||
class BlendColorFilter extends PIXI.Filter {
|
||
constructor() {
|
||
super(null, BlendColorFilter._fragmentSrc);
|
||
this.uniforms.blendColor = [0, 0, 0, 0];
|
||
}
|
||
|
||
static _fragmentSrc = (
|
||
'varying vec2 vTextureCoord;' +
|
||
'uniform sampler2D uSampler;' +
|
||
'uniform vec4 blendColor;' +
|
||
'void main() {' +
|
||
' vec4 sample = texture2D(uSampler, vTextureCoord);' +
|
||
' float a = sample.a;' +
|
||
' vec3 rgb = sample.rgb;' +
|
||
' float r = rgb.r;' +
|
||
' float g = rgb.g;' +
|
||
' float b = rgb.b;' +
|
||
' float r3 = blendColor.r / 255.0;' +
|
||
' float g3 = blendColor.g / 255.0;' +
|
||
' float b3 = blendColor.b / 255.0;' +
|
||
' float i3 = blendColor.a / 255.0;' +
|
||
' float i1 = 1.0 - i3;' +
|
||
' r = clamp(r * i1 + r3 * i3 * a, 0.0, 1.0);' +
|
||
' g = clamp(g * i1 + g3 * i3 * a, 0.0, 1.0);' +
|
||
' b = clamp(b * i1 + b3 * i3 * a, 0.0, 1.0);' +
|
||
' gl_FragColor = vec4(r, g, b, a);' +
|
||
'}'
|
||
);
|
||
|
||
setColor(color) {
|
||
if (!(color instanceof Array)) {
|
||
throw new Error('Argument must be an array');
|
||
}
|
||
this.uniforms.blendColor = [...color];
|
||
}
|
||
|
||
}
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Sprite_EncounterEffect
|
||
//
|
||
// スプライトクラスではないですが、
|
||
// RPGツクールのSpriteクラスのような扱いをするという意味でのクラス名です。
|
||
|
||
class Sprite_EncounterEffect extends PIXI.Container {
|
||
constructor() {
|
||
super();
|
||
this.createBlackBack();
|
||
this.createFragments();
|
||
this._fadeDelay = 0;
|
||
this._fadeDuration = 0;
|
||
this.initPosition();
|
||
this.updateRotation();
|
||
}
|
||
|
||
destroy() {
|
||
const options = { children: true };
|
||
super.destroy(options);
|
||
}
|
||
|
||
createBlackBack() {
|
||
const p = 40;
|
||
const width = Graphics.width + p * 2;
|
||
const height = Graphics.height + p * 2;
|
||
this._backGraphics = new PIXI.Graphics();
|
||
this._backGraphics.beginFill(0);
|
||
this._backGraphics.drawRect(-p, -p, width, height);
|
||
this._backGraphics.endFill();
|
||
this.addChild(this._backGraphics);
|
||
}
|
||
|
||
createFragments() {
|
||
for (const fragment of EncounterEffect.fragments()) {
|
||
this.addChild(new Graphics_Fragment(fragment));
|
||
}
|
||
}
|
||
|
||
initPosition() {
|
||
const { x, y } = EncounterEffect.centerPoint;
|
||
this.x = x;
|
||
this.y = y;
|
||
this.pivot.set(x, y);
|
||
}
|
||
|
||
startFadeIn(duration) {
|
||
const rate = 3;
|
||
this._fadeDelay = Math.floor(duration / rate);
|
||
this._fadeDuration = duration - this._fadeDelay;
|
||
}
|
||
|
||
update() {
|
||
this.updateChildren();
|
||
this.updateRotation();
|
||
this.updateOpacity();
|
||
this.updateFadeIn();
|
||
}
|
||
|
||
updateChildren() {
|
||
for (const child of this.children) {
|
||
if (child.update) {
|
||
child.update();
|
||
}
|
||
}
|
||
}
|
||
|
||
updateRotation() {
|
||
this.rotation = EncounterEffect.rotation();
|
||
}
|
||
|
||
updateOpacity() {
|
||
this.alpha = EncounterEffect.opacity() / 255;
|
||
}
|
||
|
||
updateFadeIn() {
|
||
if (this._fadeDuration > 0 && this.updateFadeDelay()) {
|
||
const d = this._fadeDuration;
|
||
this._backGraphics.alpha -= this._backGraphics.alpha / d;
|
||
this._fadeDuration--;
|
||
}
|
||
}
|
||
|
||
updateFadeDelay() {
|
||
if (this._fadeDelay > 0) {
|
||
this._fadeDelay--;
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
isSpritesetVisible() {
|
||
return this.alpha < 1 || this._backGraphics.alpha < 1;
|
||
}
|
||
|
||
}
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Sprite_Actor
|
||
|
||
const _Sprite_Actor_updateMove = Sprite_Actor.prototype.updateMove;
|
||
Sprite_Actor.prototype.updateMove = function() {
|
||
if (EncounterEffect.isReady()) {
|
||
_Sprite_Actor_updateMove.apply(this, arguments);
|
||
}
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Spriteset_Map
|
||
|
||
Spriteset_Map.prototype.temporaryHideDestination = function() {
|
||
this._destinationSprite.visible = false;
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Scene_Boot
|
||
|
||
const _Scene_Boot_create = Scene_Boot.prototype.create;
|
||
Scene_Boot.prototype.create = function() {
|
||
_Scene_Boot_create.apply(this, arguments);
|
||
EncounterEffect.clear();
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Scene_Base
|
||
|
||
Scene_Base.prototype.createEncEffSprite = function() {
|
||
this._encEffSprite = new Sprite_EncounterEffect();
|
||
this.addChild(this._encEffSprite);
|
||
};
|
||
|
||
Scene_Base.prototype.destroyEncEffSprite = function() {
|
||
this.removeChild(this._encEffSprite);
|
||
this._encEffSprite.destroy();
|
||
delete this._encEffSprite;
|
||
};
|
||
|
||
Scene_Base.prototype.clearFade = function() {
|
||
this._fadeSign = 0;
|
||
this._fadeDuration = 0;
|
||
this._fadeWhite = 0;
|
||
this._fadeOpacity = 0;
|
||
this.updateColorFilter();
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Scene_Map
|
||
|
||
const _Scene_Map_startEncounterEffect = Scene_Map.prototype.startEncounterEffect;
|
||
Scene_Map.prototype.startEncounterEffect = function() {
|
||
if (EncounterEffect.isValid()) {
|
||
this.snapForBattleBackground();
|
||
this.snapEncounterTexture();
|
||
_Scene_Map_startEncounterEffect.apply(this, arguments);
|
||
this.createEncEffSprite();
|
||
this._spriteset.visible = false;
|
||
return;
|
||
} else if (EncounterEffect.type() < 0) {
|
||
EncounterEffect.clear();
|
||
this.startFadeOut(this.fadeSpeed(), false);
|
||
} else {
|
||
EncounterEffect.clear();
|
||
_Scene_Map_startEncounterEffect.apply(this, arguments);
|
||
}
|
||
};
|
||
|
||
const _Scene_Map_updateEncounterEffect = Scene_Map.prototype.updateEncounterEffect
|
||
Scene_Map.prototype.updateEncounterEffect = function() {
|
||
if (EncounterEffect.isRunning()) {
|
||
this.updateMppEncounterEffect();
|
||
} else {
|
||
_Scene_Map_updateEncounterEffect.apply(this, arguments);
|
||
}
|
||
};
|
||
|
||
const _Scene_Map_encounterEffectSpeed = Scene_Map.prototype.encounterEffectSpeed;
|
||
Scene_Map.prototype.encounterEffectSpeed = function() {
|
||
if (EncounterEffect.isRunning()) {
|
||
return EncounterEffect.effectSpeed();
|
||
} else {
|
||
return _Scene_Map_encounterEffectSpeed.apply(this, arguments);
|
||
}
|
||
};
|
||
|
||
Scene_Map.prototype.snapEncounterTexture = function() {
|
||
this._windowLayer.visible = false;
|
||
this._spriteset.temporaryHideDestination();
|
||
EncounterEffect.setup(SceneManager.snapTexture());
|
||
};
|
||
|
||
Scene_Map.prototype.updateMppEncounterEffect = function() {
|
||
EncounterEffect.update();
|
||
if (this._encounterEffectDuration > 0) {
|
||
this._encounterEffectDuration--;
|
||
const speed = this.encounterEffectSpeed();
|
||
const n = speed - this._encounterEffectDuration;
|
||
const breakEnd = EncounterEffect.breakDuration();
|
||
if (n > 2 && n <= breakEnd + 2) {
|
||
const maxItems = EncounterEffect.maxItems();
|
||
const start = Math.floor(maxItems * (n - 3) / breakEnd);
|
||
const end = Math.floor(maxItems * (n - 2) / breakEnd);
|
||
EncounterEffect.breakFragments(start, end, speed);
|
||
}
|
||
if (n === Math.floor(speed / 4)) {
|
||
BattleManager.playBattleBgm();
|
||
}
|
||
}
|
||
};
|
||
|
||
//-------------------------------------------------------------------------
|
||
// Scene_Battle
|
||
|
||
const _Scene_Battle_start = Scene_Battle.prototype.start;
|
||
Scene_Battle.prototype.start = function() {
|
||
_Scene_Battle_start.apply(this, arguments);
|
||
this.startEncounterEffect();
|
||
};
|
||
|
||
const _Scene_Battle_update = Scene_Battle.prototype.update;
|
||
Scene_Battle.prototype.update = function() {
|
||
_Scene_Battle_update.apply(this, arguments);
|
||
this.updateEncounterEffect();
|
||
};
|
||
|
||
Scene_Battle.prototype.startEncounterEffect = function() {
|
||
if (EncounterEffect.isRunning()) {
|
||
this.clearFade();
|
||
EncounterEffect.onBattleStart();
|
||
this.createEncEffSprite();
|
||
this._encEffSprite.startFadeIn(EncounterEffect.fadeSpeed());
|
||
}
|
||
};
|
||
|
||
Scene_Battle.prototype.updateEncounterEffect = function() {
|
||
if (EncounterEffect.isRunning()) {
|
||
EncounterEffect.update();
|
||
this._spriteset.visible = this._encEffSprite.isSpritesetVisible();
|
||
if (!EncounterEffect.isRunning()) {
|
||
this.destroyEncEffSprite();
|
||
}
|
||
}
|
||
};
|
||
|
||
})();
|