pastime/js/plugins/MNKR_HzTimingBarMZ.js
2026-03-15 16:45:22 -05:00

478 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* --------------------------------------------------
* MNKR_HzTimingBarMZ.js
* Ver.0.1.2
* Copyright (c) 2022 Munokura
* This software is released under the MIT license.
* http://opensource.org/licenses/mit-license.php
* --------------------------------------------------
*/
/*
Copyright (c) <2016> <hiz>
MITライセンスの下で公開されています。
*/
/*:
* @target MZ
* @url https://raw.githubusercontent.com/munokura/MNKR-MZ-plugins/master/MNKR_HzTimingBarMZ.js
* @plugindesc タイミングを合わせてボタン入力するタイミングバーを実行します。
* @author hiz (改変 munokura)
*
* @help
* タイミングを合わせてボタン入力するタイミングバーを実行します。
* プラグインコマンドでタイミングバーを設定・実行します。
*
* カーソルがバー終了地点に向かって動きます。
* 何もない範囲で入力した時点で終了し、0が代入されます。
* 終了までに、ヒット(1)・クリティカル(2)のどちらかを入力したポイントの高い方が
* 変数に代入されます。
* ただし、入力必須範囲がある場合、それを入力していないと0が代入されます。
*
* バー末端に来るか、クリア条件(必須、ヒット、クリティカルの全てをヒット済)
* を満たした時点で変数が代入され終了します。
*
*
* コマンドヘルプ補足
* [hit_area]
* ヒット範囲の最小値・最大値を0から100の間で設定。
* min-max
* 例)
* 70-90
*
* [critical_area]
* クリティカル範囲の最小値・最大値を0から100の間で設定。
* min-max
* 例)
* 90-95
*
* [require_area]
* 入力必須範囲複数可の最小値・最大値を0から100の間で設定。
* min-max,min-max,...
* 入力必須範囲で全てボタン入力しないとヒット範囲・クリティカル範囲で
* ボタン入力してもミスになります。
* ※ 必ずヒット範囲・クリティカル範囲より前になるように設定して下さい。
* 例)
* 10-20 # 必須エリアは10から20の範囲
* 20-30,50-60 # 必須エリアは20から30・50から60の範囲
* 無入力 # 必須エリア無し
*
*
* このプラグインについて
* RPGツクールMV用に作成されたプラグインをMZ用に移植したものです。
* お問い合わせは改変者へお願いいたします。
*
*
* 利用規約:
* MITライセンスです。
* https://licenses.opensource.jp/MIT/MIT.html
* 作者に無断で改変、再配布が可能で、
* 利用形態商用、18禁利用等についても制限はありません。
*
*
* Ver.0.0.1 by munokura (2022/4/11)
* MZへ移植
* 変数に値が代入されない不具合を修正
*
* Ver.0.0.2 by munokura (2022/4/12)
* プラグインコマンド後に文章の表示がない場合、無限ループになる不具合を修正
* 必須エリアが他エリアの後ろにある場合、必ずミスになる不具合を修正
*
* Ver.0.0.3 by munokura (2023/5/7)
* クリティカルの後にヒットを取ると、ヒットの値を取得する不具合を修正
*
* Ver.0.0.4 by munokura (2023/5/7)
* 2回目以降に前回のスコアが影響してしまう不具合を修正
*
* Ver.0.1.0 by munokura (2023/5/7)
* ヒット時にクリア条件が揃っている場合、末端まで待たずにクリアする仕様変更
*
* @param bar width
* @type number
* @text バーの幅
* @desc バーの幅
* @default 500
*
* @param required SE
* @type file
* @reauire 1
* @dir audio/se/
* @text 必須エリアヒット時SE
* @desc 必須エリアヒット時のSE
* @default Decision2
*
* @param hit SE
* @type file
* @reauire 1
* @dir audio/se/
* @text ヒットエリアヒット時SE
* @desc ヒットエリアヒット時のSE
* @default Attack2
*
* @param critical SE
* @type file
* @reauire 1
* @dir audio/se/
* @text クリティカルエリアヒット時SE
* @desc クリティカルエリアヒット時のSE
* @default Attack3
*
* @param miss SE
* @type file
* @reauire 1
* @dir audio/se/
* @text 入力時失敗SE
* @desc 入力時失敗のSE
* @default Buzzer1
*
*
* @command HzTimingBar
* @text タイミングバー実行
* @desc タイミングバーを実行します。
*
* @arg varNumber
* @text 結果代入変数ID
* @desc 結果を返す変数番号。ミスの場合は0・ヒットの場合は1・クリティカルの場合は2が返される。
* @type variable
* @default 0
*
* @arg hitArea
* @text ヒット範囲
* @desc ヒット範囲の最小値・最大値を0から100の間で設定。min-max
* @default 70-90
*
* @arg criticalArea
* @text クリティカル範囲
* @desc クリティカル範囲の最小値・最大値を0から100の間で設定。min-max
* 使用しない場合、無入力
* @default 10-20
*
* @arg requireArea
* @text 入力必須範囲
* @desc 入力必須範囲複数可の最小値・最大値を0から100の間で設定。min-max,min-max。使用しない場合、無入力
* @default
*
* @arg x
* @text コマンド位置X
* @desc コマンドの表示位置を指定。-1で画面中央
* @type number
* @min -1
* @default -1
*
* @arg y
* @text コマンド位置Y
* @desc コマンドの表示位置を指定。-1で画面中央
* @type number
* @min -1
* @default -1
*/
// 必須エリア追加
// 必須エリアヒット時、効果音を出す
// プラグインコマンド
// TODO:アイテム・スキル使用時の実行
// TODO:状態異常の効果追加(エリア非表示・高速/低速)
(function () {
var pluginName = document.currentScript.src.split("/").pop().replace(/\.js$/, "");
var parameters = PluginManager.parameters(pluginName);
var requiredSe = parameters['required SE'];
var hitSe = parameters['hit SE'];
var criticalSe = parameters['critical SE'];
var missSe = parameters['miss SE'];
var result = 0;
var hitAreaHitted = false;
var criticalAreaHitted = false;
PluginManager.registerCommand(pluginName, "HzTimingBar", function (obj) {
result = 0;
var args = Object.entries(obj).map(([key, value]) => `${value}`);
this.setWaitMode("hzTimingBar");
var varNo = Number(args[0]);
var hitAreaParm = String(args[1]);
var criticalAreaParm = args[2] || false;
var requiredAreaParm = args[3] ? '[' + args[3] + ']' : false;
var x = Number(args[4] || -1) < 0 ? Graphics.width / 2 : Number(args[4]);
var y = Number(args[5] || -1) < 0 ? Graphics.height / 2 : Number(args[5]);
if (!hitAreaParm) {
hitAreaHitted = true;
}
if (!criticalAreaParm) {
criticalAreaHitted = true;
}
var hitArea = hitAreaParm.split("-").map(function (elm) { return Number(elm); });
if (criticalAreaParm) {
var criticalArea = criticalAreaParm.split("-").map(function (elm) { return Number(elm); });
} else {
var criticalArea = null;
}
if (requiredAreaParm) {
var requiredAreas = requiredAreaParm.substring(1, requiredAreaParm.length - 1).split(",").map(function (requiredArea) {
return requiredArea.split("-").map(function (elm) {
return Number(elm);
});
});
} else {
var requiredAreas = null;
}
// this._timingBar = new HzTimingBar(x, y, hitArea, criticalArea, requiredAreas);
// 変数が代入されないバグ修正
this._timingBar = new HzTimingBar(x, y, hitArea, criticalArea, requiredAreas, varNo);
});
// 待機状態の更新用関数に機能追加
var _Game_Interpreter_updateWaitMode = Game_Interpreter.prototype.updateWaitMode;
Game_Interpreter.prototype.updateWaitMode = function () {
var waiting = null;
switch (this._waitMode) {
case 'hzTimingBar':
// 待機状態の更新
// ※ waitingには必ずtrueかfalseをセットすること
waiting = this._timingBar.update();
if (!waiting) {
// 終了処理
this._timingBar.terminate();
this._timingBar = null;
}
break;
}
if (waiting !== null) {
if (!waiting) {
this._waitMode = '';
}
return waiting;
}
return _Game_Interpreter_updateWaitMode.call(this);
};
// コマンド入力実行用クラス
function HzTimingBar() {
this.initialize.apply(this, arguments);
}
HzTimingBar.DELAY = 20;
HzTimingBar.maxFrame = 100;
HzTimingBar.WIDTH = parameters['bar width'] ? Number(parameters['bar width']) : 500;
HzTimingBar.HEIGHT = 35;
HzTimingBar.RADIUS = 5;
HzTimingBar.speed = 1.8;
// 初期化処理(プロパティの初期化・スプライトの作成等を行う)
// HzTimingBar.prototype.initialize = function (x, y, hitArea, criticalArea, requiredAreas) {
// this._varNo = 1;
// 変数が代入されないバグ修正
//
HzTimingBar.prototype.initialize = function (x, y, hitArea, criticalArea, requiredAreas, varNo) {
this._hitArea = hitArea;
this._varNo = varNo;
$gameVariables.setValue(this._varNo, 0);
// クリティカルエリアが無いとバーの左に表示されるのを修正
// this._criticalArea = criticalArea != null ? criticalArea : [-1, 0];
this._criticalArea = criticalArea != null ? criticalArea : [];
this._requiredAreas = requiredAreas != null ? requiredAreas : [];
this._frame = -HzTimingBar.DELAY;
this._hitRequired = [];
for (var i = 0; i < this._requiredAreas.length; i++) {
this._hitRequired.push(this._requiredAreas[i].length != 2);
}
// 描画コンテナ
this._container = new Sprite();
this._container.position.set(x - HzTimingBar.WIDTH / 2, y - HzTimingBar.HEIGHT / 2);
// 枠
var barFrameBmp = new Bitmap(HzTimingBar.WIDTH + 4, HzTimingBar.HEIGHT + 4);
// var framePath = roundedRectangle(barFrameBmp.context, 2, 2, HzTimingBar.WIDTH, HzTimingBar.HEIGHT, HzTimingBar.RADIUS);
roundedRectangle(barFrameBmp.context, 2, 2, HzTimingBar.WIDTH, HzTimingBar.HEIGHT, HzTimingBar.RADIUS);
barFrameBmp.context.fillStyle = "#000000";
barFrameBmp.context.lineWidth = 2;
barFrameBmp.context.strokeStyle = "#000000";
barFrameBmp.context.fill();
barFrameBmp.context.stroke();
var barFrame = new Sprite(barFrameBmp);
barFrame.position.set(-2, -2);
this._container.addChild(barFrame);
// 必須エリア
for (var i = 0; i < this._requiredAreas.length; i++) {
var requiredArea = this._requiredAreas[i];
var reqWidth = HzTimingBar.WIDTH * (requiredArea[1] - requiredArea[0]) / HzTimingBar.maxFrame;
var reqBmp = new Bitmap(reqWidth, HzTimingBar.HEIGHT - 1);
reqBmp.context.fillStyle = "#000000";
reqBmp.context.rect(0, 1, reqWidth, HzTimingBar.HEIGHT - 1);
reqBmp.context.fill();
var req = new Sprite(reqBmp);
req.position.set(HzTimingBar.WIDTH * requiredArea[0] / HzTimingBar.maxFrame, 0);
this._container.addChild(req);
}
// ヒットエリア
var hitWidth = HzTimingBar.WIDTH * (this._hitArea[1] - this._hitArea[0]) / HzTimingBar.maxFrame;
var hitBmp = new Bitmap(hitWidth, HzTimingBar.HEIGHT - 1);
hitBmp.context.fillStyle = "#ffff00";
hitBmp.context.rect(0, 1, hitWidth, HzTimingBar.HEIGHT - 1);
hitBmp.context.fill();
var hit = new Sprite(hitBmp);
hit.position.set(HzTimingBar.WIDTH * this._hitArea[0] / HzTimingBar.maxFrame, 0);
this._container.addChild(hit);
// クリティカルエリア
var criticalWidth = HzTimingBar.WIDTH * (this._criticalArea[1] - this._criticalArea[0]) / HzTimingBar.maxFrame;
var criticalBmp = new Bitmap(criticalWidth, HzTimingBar.HEIGHT - 1);
criticalBmp.context.fillStyle = "#ff00ff";
criticalBmp.context.rect(0, 1, criticalWidth, HzTimingBar.HEIGHT - 1);
criticalBmp.context.fill();
var critical = new Sprite(criticalBmp);
critical.position.set(HzTimingBar.WIDTH * this._criticalArea[0] / HzTimingBar.maxFrame, 0);
this._container.addChild(critical);
// カーソル
var cursorBmp = new Bitmap(18, 32 + HzTimingBar.HEIGHT + 2);
cursorBmp.context.fillStyle = "#ff1493";
cursorBmp.context.strokeStyle = "#FFFFFF";
cursorBmp.context.lineWidth = 3;
cursorBmp.context.moveTo(1, 1);
cursorBmp.context.lineTo(17, 1);
cursorBmp.context.lineTo(9, 17);
cursorBmp.context.lineTo(1, 1);
cursorBmp.context.moveTo(9, HzTimingBar.HEIGHT + 17);
cursorBmp.context.lineTo(17, HzTimingBar.HEIGHT + 33);
cursorBmp.context.lineTo(1, HzTimingBar.HEIGHT + 33);
cursorBmp.context.lineTo(9, HzTimingBar.HEIGHT + 17);
cursorBmp.context.fill();
cursorBmp.context.stroke();
this._cursor = new Sprite(cursorBmp);
this._cursor.opacity = 0;
this._cursor.position.set(-9, -17);
this._container.addChild(this._cursor);
SceneManager._scene._spriteset.addChild(this._container);
};
// 更新処理終了時はfalseを返す
HzTimingBar.prototype.update = function () {
// タイマーによる時間制限
if ($gameTimer.isWorking() && $gameTimer._frames === 0) {
// 終了(失敗)
if (missSe) {
AudioManager.playSe({ name: missSe, volume: 90, pitch: 100, pan: 0 });
}
$gameVariables.setValue(this._varNo, 0);
return false;
}
this._frame += HzTimingBar.speed;
// カーソルアニメーション
if (this._frame >= 0) {
this._cursor.x = HzTimingBar.WIDTH * this._frame / HzTimingBar.maxFrame;
this._cursor.opacity = 255;
} else {
this._cursor.x = 0;
this._cursor.opacity = (HzTimingBar.DELAY + this._frame) / HzTimingBar.DELAY * 255;
}
// 時間経過でミス
if (this._frame > HzTimingBar.maxFrame) {
// 設計ミスと予想:タイムオーバーで必須エリア未クリア時にミス判定
if (!this.allRequiredAreaHitted()) {
$gameVariables.setValue(this._varNo, 0);
if (missSe) {
AudioManager.playSe({ name: missSe, volume: 90, pitch: 100, pan: 0 });
}
}
return false;
}
// ボタン押下時の判定
// ボタン押下時の判定 (マウス対応追加)
var inputCheck = Input.isTriggered('ok') || TouchInput.isTriggered();
if (inputCheck && this._frame >= 0) {
// 必須エリアチェック
for (var i = 0; i < this._requiredAreas.length; i++) {
var requiredArea = this._requiredAreas[i];
if (requiredArea[0] <= this._frame && this._frame < requiredArea[1]) {
this._hitRequired[i] = true;
if (requiredSe) {
AudioManager.playSe({ name: requiredSe, volume: 90, pitch: 100, pan: 0 });
}
return true;
}
}
if (this._criticalArea[0] <= this._frame && this._frame < this._criticalArea[1]) {
// if (this.allRequiredAreaHitted()) {
// result = 2;
result = result > 2 ? result : 2;
if (criticalSe) {
AudioManager.playSe({ name: criticalSe, volume: 90, pitch: 100, pan: 0 });
}
$gameVariables.setValue(this._varNo, result);
criticalAreaHitted = true;
if (this.allRequiredAreaHitted() && hitAreaHitted && criticalAreaHitted) {
return false;
}
return true;
// }
} else if (this._hitArea[0] <= this._frame && this._frame < this._hitArea[1]) {
// if (this.allRequiredAreaHitted()) {
// result = 1;
result = result > 1 ? result : 1;
if (hitSe) {
AudioManager.playSe({ name: hitSe, volume: 90, pitch: 100, pan: 0 });
}
$gameVariables.setValue(this._varNo, result);
hitAreaHitted = true;
if (this.allRequiredAreaHitted() && hitAreaHitted && criticalAreaHitted) {
return false;
}
return true;
// }
}
// if (result === 0) {
if (missSe) {
AudioManager.playSe({ name: missSe, volume: 90, pitch: 100, pan: 0 });
}
result = 0;
$gameVariables.setValue(this._varNo, result);
this._frame = HzTimingBar.maxFrame;
// }
// $gameVariables.setValue(this._varNo, result);
// return false;
}
return true;
};
HzTimingBar.prototype.allRequiredAreaHitted = function () {
for (var i = 0; i < this._hitRequired.length; i++) {
if (this._hitRequired[i] === false) {
return false;
}
}
return true;
};
// 終了処理
HzTimingBar.prototype.terminate = function () {
SceneManager._scene._spriteset.removeChild(this._container);
this._container = null;
};
// 角丸長方形を描画
function roundedRectangle(ctx, top, left, width, height, borderRadius) {
ctx.beginPath();
ctx.moveTo(left + borderRadius, top);
ctx.lineTo(left + width - borderRadius, top);
ctx.quadraticCurveTo(left + width, top, left + width, top + borderRadius);
ctx.lineTo(left + width, top + height - borderRadius);
ctx.quadraticCurveTo(left + width, top + height, left + width - borderRadius, top + height);
ctx.lineTo(left + borderRadius, top + height);
ctx.quadraticCurveTo(left, top + height, left, top + height - borderRadius);
ctx.lineTo(left, top + borderRadius);
ctx.quadraticCurveTo(left, top, left + borderRadius, top);
ctx.closePath();
}
})();