//============================================================================= // MPP_EncounterEffect.js //============================================================================= // Copyright (c) 2021 Mokusei Penguin // Released under the MIT license // http://opensource.org/licenses/mit-license.php //============================================================================= /*: * @target MV MZ * @plugindesc Change the effect at the time of encounter to a special effect. * @author Mokusei Penguin * @url * * @help [version 3.3.3] * This plugin is for RPG Maker MV and MZ. * * ▼ Plugin command * In the item to enter a numerical value, the variable N is * referred to by writing v[N]. * * 〇 MV / MZ * * 〇 EncEffType type / setType * type : Effect type * - The effect type will be changed to the specified number * only once next. * * 〇 EncEffChar evId / setCharacter * evId : ID of the event that is the core of the effect * - The effect will be executed around the specified character * only once next. * (-1:Player, 0:This Event, 1-:Event with specified ID) * - This setting is valid only for effect type 1. * * 〇 EncEffColor r g b / setColor * r g b : Effect color(RGB) * - The effect will change to the specified color * only once next. * * ▼ Plugin command supplement (MZ) * In the item to enter a numerical value, select the Text and * write v[N] to refer to the variable N. * * ▼ Effect type * 0 : Default * 1 : Breaks around the character * 2 : Break from the left side of the screen * 3 : The entire screen scatters 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]. * - When executed on a mobile device, some effects will be * omitted due to weight reduction. * * ================================ * 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 0 * @max 3 * @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 3 * @default 1 * * @param Effect Color * @desc R,G,B * @default 255,255,255 * */ /*:ja * @target MV MZ * @plugindesc エンカウント時の演出を特殊なエフェクトに変更します。 * @author 木星ペンギン * @url * * @help [version 3.3.3] * このプラグインはRPGツクールMVおよびMZ用です。 * * ▼ プラグインコマンド(MV) * 数値を入力する項目では、v[N] と記述することで変数N番を参照します。 * * 〇 MV / MZ * * 〇 EncEffType type / エフェクトタイプ変更 * type : エフェクトタイプ * - 次の1回のみ、エフェクトタイプが指定した番号に変更されます。 * * 〇 EncEffChar evId / 中心キャラ指定 * evId : エフェクトの中心となるイベントのID * - 次の1回のみ、エフェクトが指定したキャラクターを中心に実行されます。 * (-1:プレイヤー, 0:このイベント, 1~:指定したIDのイベント) * - この設定はエフェクトタイプ1のみ有効です。 * * 〇 EncEffColor r g b / エフェクト色指定 * r g b : エフェクトの色(RGB) * - 次の1回のみ、エフェクトが指定した色に変更されます。 * * ▼ プラグインコマンド 補足(MZ) * 数値を入力する項目で、テキストを選択して v[N] と記述することで * 変数N番を参照します。 * * ▼ エフェクトタイプ * 0 : デフォルト * 1 : キャラクターを中心に割れる * 2 : 画面の左から割れる * 3 : 画面全体が前面に飛び散る * * ▼ その他 * - エフェクトタイプ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 0 * @max 3 * @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 3 * @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"] || 2); const param_DefaultColor = parameters["Effect Color"].split(",").map(Number); // Dealing with other plugins const _importedPlugin = (...names) => { return names.some((name) => PluginManager._scripts.includes(name)); }; const pluginOptions = ["MPP_EncounterEffect_Op1", "MPP_EncounterEffect_Op2"]; // Database const DATABASE = [ null, { // Type 1 "Shape Type": "square", "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": 35, "Move Duration": 70, "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 Duration": 16, "Move Duration": 100, "Split X": 8, "X Random Rate": 80, "Split Y": 6, "Y Random Rate": 80, }, ]; //------------------------------------------------------------------------- // Array if (!Array.prototype.flat) { Array.prototype.flat = function (depth) { const rec = (flattend, array, depth) => { for (const elem of array) { if (Array.isArray(elem) && depth > 0) { rec(flattend, elem, depth - 1); } else { flattend.push(elem); } } return flattend; }; return rec([], this, depth !== undefined ? Math.floor(depth) : 1); }; } //------------------------------------------------------------------------- // EncounterEffect function EncounterEffect() { throw new Error("This is a static class"); } if (_importedPlugin(...pluginOptions)) { window.EncounterEffect = EncounterEffect; } EncounterEffect.snap = null; EncounterEffect.center = new Point(); EncounterEffect.opacity = 0; EncounterEffect._fragments = null; EncounterEffect._type = param_EffectType; EncounterEffect._character = null; EncounterEffect._color = param_DefaultColor; EncounterEffect._effectDelay = 0; EncounterEffect.clear = function () { this.snap = null; this.opacity = 0; this._fragments = null; this._type = param_EffectType; this._character = null; this._color = param_DefaultColor; this._effectDelay = 0; }; EncounterEffect.destroy = function () { this.snap.destroy(); this._fragments.forEach((fragment) => fragment.destroy()); }; EncounterEffect.params = function () { return DATABASE[this._type]; }; EncounterEffect.zoom = function () { return Utils.isMobileDevice() ? 4 : 2; }; EncounterEffect.setup = function (snap) { this.snap = snap; this.opacity = 255; this._opacityDuration = 0; this._fragments = []; this._params = this.params(); this.setupwindowBounds(); this.setupCenter(); this.createPolygons(); this.startEffectDelay(); }; EncounterEffect.setupwindowBounds = function () { this.windowBounds = { minX: 0, maxX: Graphics.width, minY: 0, maxY: Graphics.height, }; }; EncounterEffect.setupCenter = function () { const bounds = this.windowBounds; const char = this.centerCharacter(); const cx = Math.round(char.screenX()).clamp(bounds.minX, bounds.maxX); const cy = Math.round(char.screenY() - 24).clamp(bounds.minY, bounds.maxY); this.center.set(cx, cy); }; EncounterEffect.createPolygons = function () { switch (this._type) { case 1: this.createPolygonsT1(); break; case 2: case 3: this.createPolygonsT2(); break; } }; EncounterEffect.setType = function (type) { this._type = type; }; EncounterEffect.type = function () { return this._type; }; 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 ? this._character : $gameMap.getInterpreterCharacter(0) || $gamePlayer; }; EncounterEffect.createPolygonsT1 = function () { const sType = this._params["Shape Type"] || "square"; const allPoints = this.createAllPointsT1(); this.iteratePointT1(allPoints, (p1, p2, p3, p4) => { const polygon = []; if (p2) { if (this.isOutPolygon(p1, p2)) return; if (sType === "square") { polygon.push(p1, p2); } else { this.addPolygon(this.uniqPolygon([p1, p2, p3])); polygon.push(p1); } } else { polygon.push(p1); } if (this.setCorrectPoints(polygon, p3, p4)) { this.addPolygon(this.uniqPolygon(polygon)); } }); }; EncounterEffect.iteratePointT1 = function (allPoints, callback) { const splitX = this._params["Split Radial"] || 8; this._cornerFlag = [null, null, null, null]; for (let i = 0; i < allPoints.length; i++) { const inPoints = i > 0 ? allPoints[i - 1] : null; const outPoints = allPoints[i]; for (let j = 0; j < splitX; j++) { const point3 = outPoints[j]; const point4 = outPoints[(j + 1) % splitX]; if (i > 0) { const point1 = inPoints[(j + 1) % splitX]; const point2 = inPoints[j]; callback(point1, point2, point3, point4); } else { callback(this.center, null, point3, point4); } } if (!this._cornerFlag.includes(null)) break; } }; EncounterEffect.uniqPolygon = function (polygon) { return polygon.filter((point1, i) => { return polygon.findIndex((point2) => point2.equals(point1)) === i; }); }; EncounterEffect.createAllPointsT1 = function () { const { "Shape Type": sType = "square", "Split Radial": splitX = 8, "Circle Radius": baseHeight = 96, "Circle Increase Rate": circleRate = 150, } = this._params; const maxRadius = this.maxRadius(); const allPoints = []; let startR = Math.random() * 2; let baseR = 0; let height = baseHeight; for (let i = 0; i < 8; i++) { if (sType === "triangle") startR += 1 / splitX; if (i === 7) baseR = 1000000; allPoints[i] = this.createRoundPoints(startR, baseR, height); if (baseR > maxRadius) break; baseR += height; height *= circleRate / 100; } return allPoints; }; EncounterEffect.createRoundPoints = function (startR, baseR, height) { const { "Split Radial": splitX = 8, "Radial Random Rate": rRate = 90, "Circle Random Rate": cRate = 40, } = this._params; const baseAngle = 2 / splitX; const randomAngle = (n) => { return startR + baseAngle * n + (baseAngle * rRate * Math.random()) / 100; }; const randomRadius = () => { return baseR + height * (1 - (cRate * (0.75 - Math.random())) / 100); }; const points = []; for (let i = 0; i < splitX; i++) { points[i] = this.calculatePoint(randomAngle(i) % 2, randomRadius()); } return points; }; EncounterEffect.calculatePoint = function (angle, radius) { const bounds = this.windowBounds; const { x: cx, y: cy } = this.center; let sx = radius * Math.cos(angle * Math.PI); let sy = radius * Math.sin(angle * Math.PI); if (sx !== 0 && cx + sx < bounds.minX) { sy *= (bounds.minX - cx) / sx; sx = bounds.minX - cx; } if (sx !== 0 && cx + sx > bounds.maxX) { sy *= (bounds.maxX - cx) / sx; sx = bounds.maxX - cx; } if (sy !== 0 && cy + sy < bounds.minY) { sx *= (bounds.minY - cy) / sy; sy = bounds.minY - cy; } if (sy !== 0 && cy + sy > bounds.maxY) { sx *= (bounds.maxY - cy) / sy; sy = bounds.maxY - cy; } return new Point(Math.round(cx + sx), Math.round(cy + sy)); }; EncounterEffect.maxRadius = function () { const { width, height } = Graphics; const { x: cx, y: cy } = this.center; const deltaX = cx < width / 2 ? width - cx : cx; const deltaY = cy < height / 2 ? height - cy : cy; return Math.hypot(deltaX, deltaY); }; EncounterEffect.createPolygonsT2 = function () { const { "Shape Type": sType = "square", "Break Direction": direction = "left", } = this._params; switch (sType) { case "square": this.createPolygonsT2Square(); break; case "triangle": this.createPolygonsT2Triangle(); break; case "random": this.createPolygonsT2Random(); break; } this.sortFragment(direction); }; EncounterEffect.createPolygonsT2Square = function () { const allPoints = this.createAllPointsT2Square(); this.iteratePointT2(allPoints, (...points) => { this.addPolygon(points); }); }; EncounterEffect.iteratePointT2 = function (allPoints, callback) { const { "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 + 1]; const point2 = leftPoints[j]; const point3 = rightPoints[j]; const point4 = rightPoints[j + 1]; callback(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 bounds = 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 bounds.minX; if (i === splitX) return bounds.maxX; return this.randomValue(width, i, xRate); }; const pointY = (j) => { if (j === 0) return bounds.minY; if (j === splitY) return bounds.maxY; return this.randomValue(height, j, yRate); }; const allPoints = []; for (let i = 0; i <= splitX; i++) { const points = []; for (let j = 0; j <= splitY; j++) { points[j] = new Point(pointX(i), pointY(j)); } allPoints[i] = points; } return allPoints; }; EncounterEffect.randomValue = function (value, index, rate) { return Math.round(value * (index - (rate * Math.random()) / 100)); }; EncounterEffect.createPolygonsT2Triangle = function () { const allPoints = this.createAllPointsT2Triangle(); this.iteratePointT2(allPoints, (p1, p2, p3, p4) => { if (p2.y > p3.y || (p2.y === p3.y && p1.y > p4.y)) { this.addPolygon([p1, p2, p4]); this.addPolygon([p2, p3, p4]); } else { this.addPolygon([p1, p2, p3]); this.addPolygon([p1, p3, p4]); } }); }; 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 bounds = 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 bounds.minX; if (i === splitX) return bounds.maxX; return this.randomValue(width, i, xRate); }; const pointY = (i, j) => { if (j === 0) return bounds.minY; if (j === splitY) return bounds.maxY; let py = bottomY; if (i % 2 === 1) { if (j === 1) return this.randomValue(height / 2, j, yRate); py -= height / 2; } return py + this.randomValue(height, j, yRate); }; const allPoints = []; for (let i = 0; i <= splitX; i++) { const points = []; for (let j = 0; j <= splitY; j++) { points[j] = new Point(pointX(i), pointY(i, j)); } allPoints[i] = points; } return allPoints; }; EncounterEffect.createPolygonsT2Random = function () { const allBasePolygons = this.createBasePolygonsT2Random(); const sidePolygon = (n, m, pos) => { const si = n + (pos === 0 || pos === 1 ? -1 : 1); const subPolygons = allBasePolygons[si]; if (subPolygons) { const sj = Math.floor(m / 2) * 2; const polygon2 = subPolygons[sj]; if (Math.floor(pos / 2) === Math.floor(polygon2.pos / 2)) { return subPolygons[sj + 1]; } return polygon2; } return null; }; allBasePolygons.forEach((mainPolygons, i) => { mainPolygons.forEach((polygon1, j) => { if (polygon1.state === "none") { const d = Math.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); } } }); }); allBasePolygons.flat().forEach((polygon) => { if (polygon.state === "none" || polygon.state === "received") { this.addPolygon(polygon.polygon); } }); }; EncounterEffect.createBasePolygonsT2Random = function () { const allPoints = this.createAllPointsT2Square(); const polygons = []; this.iteratePointT2(allPoints, (p1, p2, p3, p4) => { if (Math.random() < 0.5) { polygons.push( { polygon: [p1, p2, p3], pos: 0, state: "none" }, { polygon: [p1, p3, p4], pos: 3, state: "none" } ); } else { polygons.push( { polygon: [p2, p3, p4], pos: 2, state: "none" }, { polygon: [p1, p2, p4], pos: 1, state: "none" } ); } }); const splitY = this._params["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.polygon.splice(start, 0, polygon2.polygon[index]); polygon1.state = "received"; polygon2.state = "joined"; }; EncounterEffect.addPolygon = function (polygon) { const fragment = new Encounter_Fragment(polygon); if (fragment.isValid()) { fragment.setup(this.snap); this._fragments.push(fragment); } }; EncounterEffect.isOutPolygon = function (pos1, pos2) { const bounds = this.windowBounds; if (pos1.x === bounds.minX && pos2.x === bounds.minX) return true; if (pos1.x === bounds.maxX && pos2.x === bounds.maxX) return true; if (pos1.y === bounds.minY && pos2.y === bounds.minY) return true; if (pos1.y === bounds.maxY && pos2.y === bounds.maxY) return true; return false; }; EncounterEffect.setCorrectPoints = function (polygon, pos1, pos2) { const bounds = this.windowBounds; polygon.push(pos1); if (pos1.x === bounds.minX && pos2.y === bounds.minY) { if (!this.pushCorner(polygon, 0)) return false; } else if (pos1.y === bounds.maxY && pos2.x === bounds.minX) { if (!this.pushCorner(polygon, 1)) return false; } else if (pos1.y === bounds.minY && pos2.x === bounds.maxX) { if (!this.pushCorner(polygon, 2)) return false; } else if (pos1.x === bounds.maxX && pos2.y === bounds.maxY) { if (!this.pushCorner(polygon, 3)) return false; } else if (pos1.x === bounds.minX && pos2.x === bounds.maxX) { if (!this.pushCorner(polygon, 0)) return false; if (!this.pushCorner(polygon, 2)) return false; } else if (pos1.y === bounds.maxY && pos2.y === bounds.minY) { if (!this.pushCorner(polygon, 1)) return false; if (!this.pushCorner(polygon, 0)) return false; } else if (pos1.y === bounds.minY && pos2.y === bounds.maxY) { if (!this.pushCorner(polygon, 2)) return false; if (!this.pushCorner(polygon, 3)) return false; } else if (pos1.x === bounds.maxX && pos2.x === bounds.minX) { if (!this.pushCorner(polygon, 3)) return false; if (!this.pushCorner(polygon, 1)) return false; } polygon.push(pos2); return true; }; EncounterEffect.pushCorner = function (polygon, c) { if (!this._cornerFlag[c]) { polygon.push(this.getCornerPoint(c)); this._cornerFlag[c] = polygon; return true; } return false; }; EncounterEffect.getCornerPoint = function (c) { const bounds = this.windowBounds; return new Point( c === 0 || c === 1 ? bounds.minX : bounds.maxX, c === 0 || c === 2 ? bounds.minY : bounds.maxY ); }; 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 cx = Graphics.width / 2; const cy = Graphics.height / 2; switch (direction) { case "left": return (p) => { return p.x; }; case "center": return (p) => { return Math.abs(cx - p.x); }; case "right": return (p) => { return -p.x; }; case "inside": return (p) => { return Math.abs(cx - p.x) + (Math.abs(cy - p.y) * cy) / cx; }; case "outside": return (p) => { return -Math.abs(cx - p.x) - (Math.abs(cy - p.y) * cy) / cx; }; } return null; }; EncounterEffect.breakFragments = function (start, end, duration) { this._fragments .slice(start, end) .forEach((fragment) => fragment.onBreak(this._color, duration)); }; EncounterEffect.onClip = function (polygon, rect) { const context = this.snap.context; context.save(); context.beginPath(); polygon.forEach((point, i) => { i === 0 ? context.moveTo(point.x, point.y) : context.lineTo(point.x, point.y); }); context.closePath(); context.clip(); context.clearRect(rect.x, rect.y, rect.width, rect.height); context.restore(); this.snap._baseTexture.update(); }; EncounterEffect.onBreakEnd = function () { this.snap.fillAll("black"); this._fragments.forEach((fragment) => fragment.clearClip()); }; EncounterEffect.onBattleStart = function () { const { "Move Duration": duration = 60, "Scatter Duration": delay = 0 } = this._params; this.startFragments(duration); this.setFragmentsDelay(delay); this._opacityDuration = duration; }; EncounterEffect.startFragments = function (duration) { switch (this._type) { case 1: this._fragments.forEach((f) => f.onBattleStartT1(duration)); this.opacity = 300; break; case 2: { const d = this._params["Scatter Direction"] || "left"; this.sortFragment(d); this._fragments.forEach((f) => f.onBattleStartT2(duration, d)); this.opacity = 640; break; } case 3: this.sortFragment("inside"); this._fragments.forEach((f) => f.onBattleStartT3(duration)); this.opacity = 640; break; } }; EncounterEffect.setFragmentsDelay = function (delay) { const maxItems = this.maxItems(); this._fragments.forEach((fragment, i) => { fragment.setDelay(Math.floor((delay * i) / maxItems)); }); }; EncounterEffect.update = function () { if (this.isRunning()) { this._fragments.forEach((fragment) => fragment.update()); this.updateOpacity(); if (this.opacity === 0) { this.destroy(); this.clear(); } } }; EncounterEffect.updateOpacity = function () { if (this._opacityDuration > 0) { const d = this._opacityDuration; this.opacity *= (d - 1) / d; this._opacityDuration--; } }; EncounterEffect.maxItems = function () { return this._fragments ? this._fragments.length : 0; }; EncounterEffect.fragments = function () { return this._fragments || []; }; EncounterEffect.isValid = function () { return !!this.params(); }; EncounterEffect.isRunning = function () { return !!this._fragments; }; EncounterEffect.isReady = function () { return !this.isRunning() || this._opacityDuration < 45; }; EncounterEffect.breakDuration = function () { return this._params ? this._params["Break Duration"] || 45 : 45; }; EncounterEffect.effectSpeed = function () { const d = this._params ? this._params["Interval Duration"] || 45 : 45; return this.breakDuration() + d; }; EncounterEffect.fadeSpeed = function () { return this._params ? Math.min(this._params["Move Duration"] || 60, 60) : 60; }; EncounterEffect.startEffectDelay = function () { this._effectDelay = 4; }; EncounterEffect.updateEffectDelay = function () { if (this._effectDelay > 0) this._effectDelay--; }; EncounterEffect.isEffectReady = function () { return this._effectDelay === 0; }; //------------------------------------------------------------------------- // Encounter_Fragment function Encounter_Fragment() { this.initialize(...arguments); } if (_importedPlugin(...pluginOptions)) { window.Encounter_Fragment = Encounter_Fragment; } Encounter_Fragment.prototype.initialize = function (polygon) { this._polygon = polygon; this.initRect(); this._flash = false; this._clip = false; this._delay = 0; this.visible = false; }; Encounter_Fragment.prototype.initRect = function () { const polygon = this._polygon; const allX = polygon.map((point) => point.x); const allY = polygon.map((point) => point.y); const rect = new Rectangle(); rect.x = Math.min(...allX); rect.y = Math.min(...allY); rect.width = Math.max(...allX) - rect.x; rect.height = Math.max(...allY) - rect.y; this._rect = rect; }; Encounter_Fragment.prototype.createBitmap = function () { const rect = this._rect; const zoom = EncounterEffect.zoom(); const bw = Math.ceil(rect.width / zoom) + 2; const bh = Math.ceil(rect.height / zoom) + 2; this.bitmap = new Bitmap(bw, bh); this.bitmap.smooth = true; }; Encounter_Fragment.prototype.initPolygon = function () { const polygon = this._polygon; const rect = this._rect; const zoom = EncounterEffect.zoom(); const context = this.bitmap.context; context.translate(-rect.x / zoom + 1, -rect.y / zoom + 1); context.beginPath(); polygon.forEach((point, i) => { i === 0 ? context.moveTo(point.x / zoom, point.y / zoom) : context.lineTo(point.x / zoom, point.y / zoom); }); context.closePath(); }; Encounter_Fragment.prototype.initPosition = function () { const rect = this._rect; this.x = rect.x + Math.floor(rect.width / 2); this.y = rect.y + Math.floor(rect.height / 2); this.jumpHeight = 0; this._targetX = 0; this._targetY = 0; this._zoom = EncounterEffect.zoom(); this._targetZoom = this._zoom; this._moveDuration = 0; }; Encounter_Fragment.prototype.initRotation = function () { 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._rotationSpeedX *= Math.random() < 0.5 ? -1 : 1; this._rotationSpeedY = 0; } else { this._rotationSpeedX = 0; this._rotationSpeedY = (Math.random() + 0.5) * 2 * rate; this._rotationSpeedY *= Math.random() < 0.5 ? -1 : 1; } this._rotationSpeedZ = (Math.random() - 0.5) * 2 * rate; }; Encounter_Fragment.prototype.destroy = function () { this.bitmap.destroy(); }; Encounter_Fragment.prototype.isValid = function () { return this._rect.width > 4 && this._rect.height > 4; }; Encounter_Fragment.prototype.clearFlash = function () { this._flash = false; }; Encounter_Fragment.prototype.isFlashRequested = function () { return !!this._flash; }; Encounter_Fragment.prototype.needClip = function () { return !Utils.isMobileDevice(); }; Encounter_Fragment.prototype.clearClip = function () { this._clip = false; }; Encounter_Fragment.prototype.isClipRequested = function () { return !!this._clip; }; Encounter_Fragment.prototype.lineWidth = function () { return 4; }; Encounter_Fragment.prototype.flashOpacity = function () { return 255; }; Encounter_Fragment.prototype.breakRate = function () { return 1; }; Encounter_Fragment.prototype.slideRate = function () { return 1; }; Encounter_Fragment.prototype.rotationRate = function () { return 1; }; Encounter_Fragment.prototype.flashColor = function () { const color = [...EncounterEffect.color()]; color[3] = this.flashOpacity(); return color; }; Encounter_Fragment.prototype.setup = function (snap) { this.createBitmap(); this.initPolygon(); this.initPosition(); this.initRotation(); this.setupSnap(snap); }; Encounter_Fragment.prototype.setupSnap = function (snap) { const rect = this._rect; const zoom = EncounterEffect.zoom(); const context = this.bitmap.context; context.save(); context.clip(); let sx = rect.x; let sy = rect.y; switch (EncounterEffect.type()) { case 1: { const center = EncounterEffect.center; const radian = Math.atan2(this.y - center.y, this.x - center.x); sx -= 20 * Math.cos(radian) * this.slideRate(); sy -= 20 * Math.sin(radian) * this.slideRate(); break; } case 2: case 3: { const bounds = EncounterEffect.windowBounds; sx += (Math.randomInt(21) - 10) * this.slideRate(); sy += (Math.randomInt(21) - 10) * this.slideRate(); sx = sx.clamp(bounds.minX, bounds.maxX - rect.width); sy = sy.clamp(bounds.minY, bounds.maxY - rect.height); break; } } const dx = rect.x / zoom; const dy = rect.y / zoom; const dw = rect.width / zoom; const dh = rect.height / zoom; context.drawImage( snap.canvas, sx, sy, rect.width, rect.height, dx, dy, dw, dh ); context.restore(); this.bitmap.baseTexture.update(); }; Encounter_Fragment.prototype.onBreak = function (color, duration) { this.drawLine(color); this.startEncounter(duration); this._flash = true; this._clip = true; this.visible = true; }; Encounter_Fragment.prototype.onClip = function () { if (this.needClip()) EncounterEffect.onClip(this._polygon, this._rect); }; Encounter_Fragment.prototype.drawLine = function (color) { const lineWidth = this.lineWidth(); if (lineWidth > 0) { const context = this.bitmap.context; context.lineWidth = lineWidth / EncounterEffect.zoom(); context.strokeStyle = "rgb(%1,%2,%3)".format(...color); context.globalAlpha = 0.5; context.stroke(); context.globalAlpha = 1; } }; Encounter_Fragment.prototype.update = function () { if (this.visible && this.updateDelay()) { this.updateMove(); this.updateRotation(); } }; Encounter_Fragment.prototype.updateDelay = function () { if (this._delay > 0) { this._delay--; return false; } return true; }; Encounter_Fragment.prototype.updateMove = function () { if (this._moveDuration > 0) { const d = this._moveDuration; const triangular = (n) => (n * (n + 1)) / 2; switch (EncounterEffect.type()) { case 1: this.updateRateMove(d / triangular(d)); break; case 2: { const n = this._moveMax - d; const max = triangular(this._moveMax) - triangular(n); this.updateRateMove((n + 1) / max); break; } case 3: { const rate = d / triangular(d); this.updateRateMove(rate); this._zoom += (this._targetZoom - this._zoom) * rate; const m = (4 * (d - 1)) / this._moveMax; this.jumpHeight = (2 * 2 - Math.pow(m - 2, 2)) * 96; this.visible = this._zoom < 8; break; } } this._moveDuration--; } }; Encounter_Fragment.prototype.updateRateMove = function (rate) { this.x += (this._targetX - this.x) * rate; this.y += (this._targetY - this.y) * rate; }; Encounter_Fragment.prototype.updateRotation = function () { if (this._speed > 0) { if (this._pace > 0) this._speed *= 1.18; else if (this._pace < 0) this._speed *= 0.96; this._rotationX += this._rotationSpeedX * this._speed; this._rotationY += this._rotationSpeedY * this._speed; this._rotationZ += this._rotationSpeedZ * this._speed; } }; Encounter_Fragment.prototype.startEncounter = function (duration) { const breakRate = this.breakRate(); switch (EncounterEffect.type()) { case 1: this._moveDuration = duration; this._speed = this.hypotSpeed(1.5); this._pace = -1; this.setMoveT1(breakRate / 100, breakRate / 100); break; case 2: case 3: this._moveDuration = 0; this._speed = 0.2; this._pace = -1; break; } this._speed *= breakRate; }; Encounter_Fragment.prototype.onBattleStartT1 = function (d) { this._moveDuration = d; this._speed = this.hypotSpeed(40); this._pace = -1; this.setMoveT1(0.75 + Math.random() / 2, 0.75 + Math.random() / 2); }; Encounter_Fragment.prototype.hypotSpeed = function (baseSpeed) { const { x: cx, y: cy } = EncounterEffect.center; return baseSpeed / Math.sqrt(Math.hypot(this.x - cx, this.y - cy)); }; Encounter_Fragment.prototype.setMoveT1 = function (rateX, rateY) { const bounds = EncounterEffect.windowBounds; const { x: cx, y: cy } = EncounterEffect.center; const sx = this.x - cx; const sy = this.y - cy; let ox = sx; let oy = sy; if (ox < 0) { oy *= (bounds.minX - cx) / ox; ox = bounds.minX - cx; } else if (ox > 0) { oy *= (bounds.maxX - cx) / ox; ox = bounds.maxX - cx; } if (oy < bounds.minY - cy) { ox *= (bounds.minY - cy) / oy; oy = bounds.minY - cy; } else if (oy > bounds.maxY - cy) { ox *= (bounds.maxY - cy) / oy; oy = bounds.maxY - cy; } this._targetX = this.x + (ox - sx) * rateX; this._targetY = this.y + (oy - sy) * rateY; }; Encounter_Fragment.prototype.onBattleStartT2 = function ( duration, direction ) { this._moveDuration = Math.floor(duration / 2); this._moveMax = this._moveDuration; this._speed = 1.6; this._pace = -1; this.setMoveT2(direction); }; Encounter_Fragment.prototype.setMoveT2 = function (direction) { const cx = Graphics.width / 2; const cy = Graphics.height / 2; switch (direction) { case "left": this._targetX = this.x - Graphics.width; this._targetY = this.y + (this.y - cy) * 1.0; break; case "right": this._targetX = this.x + Graphics.width; this._targetY = this.y + (this.y - cy) * 1.0; 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; } } }; Encounter_Fragment.prototype.onBattleStartT3 = function (duration) { this._moveDuration = duration; this._moveMax = duration; this._speed = 2; this._pace = 0; this.setMoveT3(); }; Encounter_Fragment.prototype.setMoveT3 = function () { const cx = Graphics.width / 2; const cy = Graphics.height / 2; 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._targetZoom = delta < 128 && Math.random() < 0.4 ? 12 : 1; this._targetZoom += 3 * Math.random(); }; Encounter_Fragment.prototype.setDelay = function (delay) { this._delay = delay; }; Encounter_Fragment.prototype.scaleX = function () { return Math.cos((this._rotationX * Math.PI) / 180) * this._zoom; }; Encounter_Fragment.prototype.scaleY = function () { return Math.cos((this._rotationY * Math.PI) / 180) * this._zoom; }; Encounter_Fragment.prototype.rotation = function () { return (this._rotationZ * Math.PI) / 180; }; //----------------------------------------------------------------------------- // Bitmap if (Utils.RPGMAKER_NAME === "MV") { Bitmap.prototype.destroy = function () { if (this._baseTexture) { this._baseTexture.destroy(); this.__baseTexture = null; } this._destroyCanvas(); }; Bitmap.prototype._destroyCanvas = function () { if (this._canvas) { this._canvas.width = 0; this._canvas.height = 0; this.__canvas = null; } }; } //----------------------------------------------------------------------------- // SceneManager const _SceneManager_update = SceneManager.update; SceneManager.update = function () { _SceneManager_update.apply(this, arguments); EncounterEffect.updateEffectDelay(); }; const _SceneManager_determineRepeatNumber = SceneManager.determineRepeatNumber; SceneManager.determineRepeatNumber = function (deltaTime) { if (EncounterEffect.isRunning()) { this._smoothDeltaTime *= 0.8; this._smoothDeltaTime += Math.min(deltaTime, 5) * 0.2; //2to5 if (this._smoothDeltaTime >= 0.9) { this._elapsedTime = 0; return Math.round(this._smoothDeltaTime); } else { this._elapsedTime += deltaTime; if (this._elapsedTime >= 1) { this._elapsedTime -= 1; return 1; } return 0; } } return _SceneManager_determineRepeatNumber.apply(this, arguments); }; //------------------------------------------------------------------------- // PluginManager PluginManager._commands = PluginManager._commands || {}; if (!PluginManager.registerCommand) { PluginManager.registerCommand = function (pluginName, commandName, func) { const key = pluginName + ":" + commandName; this._commands[key] = func; }; } if (!PluginManager.callCommand) { PluginManager.callCommand = function (self, pluginName, commandName, args) { const key = pluginName + ":" + commandName; const func = this._commands[key]; if (typeof func === "function") { func.bind(self)(args); } }; } 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; }; //----------------------------------------------------------------------------- // Game_Interpreter const _mzCommands = { EncEffType: { name: "setType", keys: ["type"] }, EncEffChar: { name: "setCharacter", keys: ["character"] }, EncEffColor: { name: "setColor", keys: [], join: "color" }, }; Object.assign(_mzCommands, { エフェクトタイプ変更: _mzCommands.EncEffType, 中心キャラ指定: _mzCommands.EncEffChar, エフェクト色指定: _mzCommands.EncEffColor, }); const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function (command, args) { _Game_Interpreter_pluginCommand.apply(this, arguments); const mzCommand = _mzCommands[command]; if (mzCommand) { const args2 = Object.assign( ...mzCommand.keys.map((k, i) => ({ [k]: args[i] })) ); if (mzCommand.join) args2[mzCommand.join] = args.join(); PluginManager.callCommand(this, pluginName, mzCommand.name, args2); } }; //----------------------------------------------------------------------------- // Sprite if (Utils.RPGMAKER_NAME === "MZ" && !_importedPlugin("MPP_PatchMZ")) { Sprite.prototype._removeColorFilter = function () { if (this._colorFilter) { this.filters.remove(this._colorFilter); this._colorFilter = null; } }; const _Sprite__updateColorFilter = Sprite.prototype._updateColorFilter; Sprite.prototype._updateColorFilter = function () { if ( this._hue === 0 && this._blendColor[3] === 0 && this._colorTone.equals([0, 0, 0, 0]) ) { this._removeColorFilter(); } else { _Sprite__updateColorFilter.apply(this, arguments); } }; } //----------------------------------------------------------------------------- // Sprite_Fragment function Sprite_Fragment() { this.initialize(...arguments); } Sprite_Fragment.prototype = Object.create(Sprite.prototype); Sprite_Fragment.prototype.constructor = Sprite_Fragment; Sprite_Fragment.prototype.initialize = function (fragment) { this._fragment = fragment; Sprite.prototype.initialize.call(this, fragment.bitmap); this.anchor.x = 0.5; this.anchor.y = 0.5; this.clearFlash(); this.setupRate(); this.update(); }; Sprite_Fragment.prototype.clearFlash = function () { this._flashColor = [0, 0, 0, 0]; this._flashDuration = 0; }; Sprite_Fragment.prototype.setupRate = function () { this._rate = Utils.isMobileDevice() ? 6 : 8; }; Sprite_Fragment.prototype.update = function () { Sprite.prototype.update.call(this); this.updatePosition(); this.updateScale(); this.updateRotation(); this.updateFlash(); this.updateClip(); this.updateVisibility(); }; Sprite_Fragment.prototype.updatePosition = function () { const fragment = this._fragment; this.x = fragment.x; this.y = fragment.y - fragment.jumpHeight; }; Sprite_Fragment.prototype.updateScale = function () { const fragment = this._fragment; this.scale.x = fragment.scaleX(); this.scale.y = fragment.scaleY(); }; Sprite_Fragment.prototype.updateRotation = function () { this.rotation = this._fragment.rotation(); }; Sprite_Fragment.prototype.updateFlash = function () { this.setupFlash(); if (this._flashDuration > 0) { const d = this._flashDuration--; this._flashColor[3] *= (d - 1) / d; if (this._flashDuration % this._rate === 0) { this.setBlendColor(this._flashColor); } } }; Sprite_Fragment.prototype.setupFlash = function () { if (this._fragment.isFlashRequested()) { this._flashDuration = Utils.isMobileDevice() ? 13 : 25; this._flashColor = this._fragment.flashColor(); this._fragment.clearFlash(); } }; Sprite_Fragment.prototype.updateClip = function () { if (this._fragment.isClipRequested()) { this._fragment.onClip(); this._fragment.clearClip(); } }; Sprite_Fragment.prototype.updateVisibility = function () { this.visible = this._fragment.visible; }; //----------------------------------------------------------------------------- // Sprite_EncounterEffect function Sprite_EncounterEffect() { this.initialize(...arguments); } Sprite_EncounterEffect.prototype = Object.create(Sprite.prototype); Sprite_EncounterEffect.prototype.constructor = Sprite_EncounterEffect; Sprite_EncounterEffect.prototype.initialize = function () { Sprite.prototype.initialize.call(this); this.createBackSprite(); this.createFragments(); this._fadeDelay = 0; this._fadeDuration = 0; }; Sprite_EncounterEffect.prototype.createBackSprite = function () { this._backSprite = new Sprite(EncounterEffect.snap); this.addChild(this._backSprite); }; Sprite_EncounterEffect.prototype.createFragments = function () { this._fragmentSprites = []; const fragments = EncounterEffect.fragments(); for (let i = 0; i < fragments.length; i++) { const sprite = new Sprite_Fragment(fragments[i]); this._fragmentSprites[i] = sprite; this.addChild(sprite); } }; Sprite_EncounterEffect.prototype.startFadeIn = function (duration) { const rate = 3; //Utils.isMobileDevice() ? 2 : 3; this._fadeDelay = Math.floor(duration / rate); this._fadeDuration = duration - this._fadeDelay; }; Sprite_EncounterEffect.prototype.update = function () { Sprite.prototype.update.call(this); this.opacity = EncounterEffect.opacity; this.updateFadeIn(); }; Sprite_EncounterEffect.prototype.updateFadeIn = function () { if (this._fadeDuration > 0 && this.updateFadeDelay()) { const d = this._fadeDuration; this._backSprite.opacity -= this._backSprite.opacity / d; this._fadeDuration--; } }; Sprite_EncounterEffect.prototype.updateFadeDelay = function () { if (this._fadeDelay > 0) { this._fadeDelay--; return false; } return true; }; Sprite_EncounterEffect.prototype.isSpritesetVisible = function () { return this.opacity < 255 || this._backSprite.opacity < 255; }; //----------------------------------------------------------------------------- // 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_Base Scene_Base.prototype.createEncEffSprite = function () { this._encEffSprite = new Sprite_EncounterEffect(); this.addChild(this._encEffSprite); }; Scene_Base.prototype.destroyEncEffSprite = function () { this.removeChild(this._encEffSprite); if (Utils.RPGMAKER_NAME === "MZ") this._encEffSprite.destroy(); delete this._encEffSprite; }; Scene_Base.prototype.clearFade = function () { this._fadeSign = 0; this._fadeDuration = 0; this._fadeWhite = 0; this._fadeOpacity = 0; if (Utils.RPGMAKER_NAME === "MV") { this._fadeSprite.opacity = 0; } else { this.updateColorFilter(); } }; //----------------------------------------------------------------------------- // Scene_Map const _Scene_Map_launchBattle = Scene_Map.prototype.launchBattle; Scene_Map.prototype.launchBattle = function () { if (EncounterEffect.isValid()) { this.snapForBattleBackground(); this._windowLayer.visible = false; this._spriteset.temporaryHideDestination(); EncounterEffect.setup(SceneManager.snap()); _Scene_Map_launchBattle.apply(this, arguments); this.createEncEffSprite(); this._spriteset.visible = false; } else { EncounterEffect.clear(); _Scene_Map_launchBattle.apply(this, arguments); } }; const _Scene_Map_updateEncounterEffect = Scene_Map.prototype.updateEncounterEffect; Scene_Map.prototype.updateEncounterEffect = function () { if (EncounterEffect.isRunning()) { if (!EncounterEffect.isEffectReady()) return; if (this._encounterEffectDuration > 0) { EncounterEffect.update(); this._encounterEffectDuration--; const speed = this.encounterEffectSpeed(); const n = speed - this._encounterEffectDuration; const breakEnd = EncounterEffect.breakDuration(); if (n <= breakEnd) { const maxItems = EncounterEffect.maxItems(); const start = Math.floor((maxItems * (n - 1)) / breakEnd); const end = Math.floor((maxItems * n) / breakEnd); EncounterEffect.breakFragments(start, end, speed); } if (n === breakEnd) { EncounterEffect.onBreakEnd(); } if (n === Math.floor(speed / 5)) { BattleManager.playBattleBgm(); } if (n === speed && !ImageManager.isReady()) { this._encounterEffectDuration = 1; } } } else { _Scene_Map_updateEncounterEffect.apply(this, arguments); } }; const _Scene_Map_encounterEffectSpeed = Scene_Map.prototype.encounterEffectSpeed; Scene_Map.prototype.encounterEffectSpeed = function () { return EncounterEffect.isRunning() ? EncounterEffect.effectSpeed() : _Scene_Map_encounterEffectSpeed.apply(this, arguments); }; //----------------------------------------------------------------------------- // Scene_Battle const _Scene_Battle_create = Scene_Battle.prototype.create; Scene_Battle.prototype.create = function () { _Scene_Battle_create.apply(this, arguments); if (EncounterEffect.isRunning()) { EncounterEffect.startEffectDelay(); } }; const _Scene_Battle_start = Scene_Battle.prototype.start; Scene_Battle.prototype.start = function () { _Scene_Battle_start.apply(this, arguments); if (EncounterEffect.isRunning()) { this.clearFade(); EncounterEffect.onBattleStart(); this.createEncEffSprite(); this._encEffSprite.startFadeIn(EncounterEffect.fadeSpeed()); } }; const _Scene_Battle_update = Scene_Battle.prototype.update; Scene_Battle.prototype.update = function () { _Scene_Battle_update.apply(this, arguments); if (EncounterEffect.isRunning() && EncounterEffect.isEffectReady()) { EncounterEffect.update(); this._spriteset.visible = this._encEffSprite.isSpritesetVisible(); if (!EncounterEffect.isRunning()) { this.destroyEncEffSprite(); } } }; })();