alter-egoism/js/plugins/Keke_LoopBalloon.js
2025-11-01 14:17:42 -05:00

709 lines
21 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=============================================================================
// Keke_LoopBalloon - ループ・フキダシアイコン
// バージョン: 1.1.0
//=============================================================================
// Copyright (c) 2022 ケケー
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
//=============================================================================
/*:
* @target MZ
* @plugindesc 改造_フキダシアイコンをループ表示させる
* @author ケケー
* @url https://kekeelabo.com
*
* @help
* 【ver.1.1.0】
* ◎フキダシアイコンをループ表示できる
* ◎プラグインコマンドや注釈でフキダシアイコンを制御できる
*
*
* 【機能1】プラグインコマンドでフキダシアイコンを表示
* => プラグインコマンド → フキダシアイコンの表示
*  対象キャラ・フキダシアイコンIDを入力
*
*
* 【機能2】プラグインコマンドでフキダシアイコンを消去
* => プラグインコマンド → フキダシアイコンの消去
*  対象キャラを入力
*
*
* 【機能3】注釈でフキダシアイコンを表示
* => イベントコマンドの『注釈』を使う
* ★注釈の置き場所は「現在のページの一番上」か「1ページ目の一番上」
*  まず現在のページのがあればそれを使い、なければ1ページ目のものを使う
*  注釈は複数つなげることも可能
* 置いた注釈の中に
* <balloon: フキダシアイコンID, (ループウェイト), (ループ回数)>
*  ※ループウェイトとループ回数は省略可能
* 例)
* <balloon: 1>
*  びっくりのフキダシアイコンを表示
* <balloon: 0>
*  フキダシアイコンを表示しない
* <balloon: 2, 30, 5>
*  はてなのフキダシアイコンを表示
*  ループ間に 30フレーム のウェイトを挟み、
*  5回 表示したら終了
*
*
* 【付属】標準のアイコンID
* 1: びっくり
* 2: はてな
* 3: 音符
* 4: ハート
* 5: 怒り
* 6: 汗
* 7: くしゃくしゃ
* 8: 沈黙
* 9: 電球
* 10: Zzz
* 11: ユーザー定義1
* 12: ユーザー定義2
* 13: ユーザー定義3
* 14: ユーザー定義4
* 15: ユーザー定義5
*
*
* ● 利用規約 ●
* MITライセンスのもと、自由に使ってくれて大丈夫です
*
*
*
* @param 位置の自動調整
* @desc positionAdjust フキダシの位置をキャラの拡大率に応じて自動調整する
* @type boolean
* @default true
*
* @param サイズ自動調整
* @desc sizeAdjust フキダシの大きさをキャラの拡大率に応じて自動調整する
* @type boolean
* @default true
*
*
* @command フキダシアイコンの表示
* @desc showBalloon フキダシアイコンをループ表示する
*
* @arg 対象キャラ-名前
* @desc taget-name ***:イベント検索、セルフ:イベント自身、プレイヤー:同、全:全キャラ、\v[***]:変数。, で複数指定
*
* @arg 対象キャラ-ID
* @desc taget-chara 1〜:イベントID。0:イベント自身、-1:プレイヤー、-2:全キャラ、\v[***]:変数, で複数、~ でまとめて指定
*
* @arg フキダシアイコンID
* @desc balloonId ループ表示するフキダシアイコンのID
* @type number
*
* @arg ループウェイト
* @desc roopWait ループの間に挟むウェイト。5 なら 5フレーム。0~5 なら 0 から 5 の間でランダム
*
* @arg ループ回数
* @desc roopNum ループ表示する回数。5 なら 5回 表示。0~5 なら 0 から 5 の間でランダム。空欄なら無制限
*
*
*
* @command フキダシアイコンの消去
* @desc deleteBalloon 表示中のフキダシアイコンを消去する
*
* @arg 対象キャラ-名前
* @desc target-name ***:イベント検索、セルフ:イベント自身、プレイヤー:同、全:全キャラ、\v[***]:変数。, で複数指定
*
* @arg 対象キャラ-ID
* @desc target-id 1〜:イベントID。0:イベント自身、-1:プレイヤー、-2:全キャラ、\v[***]:変数, で複数、~ でまとめて指定
*/
(() => {
//- プラグイン名
const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1];
//==================================================
//-- パラメータ受け取り
//==================================================
//- 真偽化
function toBoolean(str) {
if (!str) {
return false;
}
const str2 = str.toString().toLowerCase();
if (str2 == "true" || str2 == "on") {
return true;
}
if (str2 == "false" || str2 == "off") {
return false;
}
return Number(str);
}
let parameters = PluginManager.parameters(pluginName);
const keke_positionAdjust = toBoolean(parameters["位置の自動調整"]);
const keke_sizeAdjust = toBoolean(parameters["サイズ自動調整"]);
parameters = null;
//==================================================
//-- プラグインコマンド
//==================================================
//- フキダシアイコンの表示
PluginManager.registerCommand(pluginName, "フキダシアイコンの表示", (args) => {
// フキダシIDを取得
const balloonId = Number(convertVariable(args["フキダシアイコンID"]));
if (!balloonId) {
return;
}
// 対象キャラを取得
const names = convertVariable(args["対象キャラ-名前"]);
const ids = convertVariable(args["対象キャラ-ID"]);
const charas = [...getCharasByName(names), ...getCharasById(ids)];
if (!charas || !charas.length) {
return;
}
// 各パラメータを取得
const loopWait = convertVariable(args["ループウェイト"]);
const loopNum = randomise(convertVariable(args["ループ回数"]));
// ループフキダシをセット
charas.forEach((chara) => {
// フキダシスプライトの消去
deleteBalloonSprite(chara);
chara._loopBalloonKe = { id: balloonId, wait: loopWait, num: loopNum };
});
});
//- フキダシアイコンの消去
PluginManager.registerCommand(pluginName, "フキダシアイコンの消去", (args) => {
// 対象キャラを取得
const names = convertVariable(args["対象キャラ-名前"]);
const ids = convertVariable(args["対象キャラ-ID"]);
const charas = [...getCharasByName(names), ...getCharasById(ids)];
if (!charas || !charas.length) {
return;
}
// ループフキダシを消去
charas.forEach((chara) => {
// フキダシスプライトの消去
deleteBalloonSprite(chara);
chara._loopBalloonKe = null;
chara._loopBalloonCommentKe = null;
// イベントのみフキダシ消去済みフラグをオン
if (chara._eventId) {
chara._loopBalloonDeletedKe = true;
}
});
});
//==================================================
//-- ループフキダシ
//==================================================
//- ゲーム・キャラクターベース/更新(処理追加)
const _Game_CharacterBase_update = Game_CharacterBase.prototype.update;
Game_CharacterBase.prototype.update = function () {
_Game_CharacterBase_update.apply(this);
// ループフキダシの更新
updateLoopBalloon(this);
};
//- ループフキダシの更新
function updateLoopBalloon(chara) {
// 注釈からのフキダシ取得
getBalloonByComment(chara);
const balloon = chara._loopBalloonKe || chara._loopBalloonCommentKe;
// フキダシデータがなければリターン
if (!balloon) {
return;
}
// フキダシ表示中はリターン
if (chara._balloonPlaying) {
return;
}
// ウェイトを適用
if (balloon.waitDuration) {
balloon.waitDuration--;
return;
}
// フキダシを表示
$gameTemp.requestBalloon(chara, balloon.id);
// ウェイトをセット
balloon.waitDuration = randomise(balloon.wait);
}
//- 注釈からのフキダシ取得
function getBalloonByComment(event) {
// イベント以外はリターン
if (!event._eventId) {
return;
}
// 消去済みならリターン
if (event._loopBalloonDeletedKe) {
return;
}
// 前回のフキダシを保存
const preBalloon = event._loopBalloonCommentKe;
// ページ切り替わったら初期化
initAtPageChange(event);
// すでに取得済みならリターン
if (event._loopBalloonCommentKe) {
return;
}
// フキダシパラメータの取得
const p = getBalloonParameter(event);
if (p) {
event._loopBalloonCommentKe = p;
}
// フキダシが変更されたら
if (preBalloon && (!p || preBalloon.id != p.id)) {
// フキダシスプライトの消去
deleteBalloonSprite(event);
}
}
//- ページが切り替わったら初期化
function initAtPageChange(event) {
// ページが切り替わったら
if (event._currentPageKeLpbl != event._pageIndex) {
// 注釈パラメータを初期化
event._loopBalloonCommentKe = null;
}
// 現在のページを保存
event._currentPageKeLpbl = event._pageIndex;
}
//- フキダシパラメータの取得
function getBalloonParameter(event) {
// 最初の注釈の取得
const comment = getFirstComment(event);
if (!comment) {
return null;
}
var fukidasi_list = [
"びっくり",
"はてな",
"びっくりはてな",
"げんき",
"おんぷ",
"ほし",
"ながれぼし",
"きらきら",
"はーと",
"しつれん",
"くちびる",
"せきめん",
"よだれ",
"むっつり",
"むかむか",
"かじる",
"ばくだん",
"あせり",
"がす",
"ほらー",
"ちんもく",
"くしゃくしゃ",
"ぐるぐる",
"ねむい",
"やるき",
"ばくはつ",
"ぴこーん",
"いわ",
"ざんぞう",
"われる",
"けつえき",
"ばんそうこう",
"どくろ",
"うんこ",
"こおり",
"きえる",
"かぜ",
"あせ",
"あたふた",
"ひらめき",
"きらり",
"きらりきらり",
"わるい",
"ほのお",
"おはな",
"はなまる",
"ぱっかーん",
"まと",
"あっぷ",
"だうん",
"enter",
"shop",
"upleft",
"down",
"check",
"armour",
"upright",
"left",
"hint",
"weapon",
"downright",
"right",
"event",
"inn",
"downleft",
"up",
"quest",
"item",
"gDown",
"yajiDown",
"mission",
"start",
"gLeft",
"yajiLeft",
"boss",
"goal",
"gRight",
"yajiRight",
"save",
"next",
"gUp",
"yajiUp"
];
// 全ての注釈を処理
const regExp = /<fukidasi_auto\s+@name\s*=\s*([^>]+)>/i;
let p = null;
// パラメータを検索
while (true) {
// マッチング
const match = comment.match(regExp);
if (!match || !match[1]) {
break;
}
// パラメータを取得
const descs = match[1].replace(/\s/g, "").split(",");
p = {};
p.id = Number(fukidasi_list.indexOf(descs[0]));
p.wait = descs[1];
p.num = randomise(descs[2]);
break;
}
return p;
}
//- ゲーム・キャラクターベース/フキダシの終了(処理追加)
const _Game_CharacterBase_endBalloon = Game_CharacterBase.prototype.endBalloon;
Game_CharacterBase.prototype.endBalloon = function () {
_Game_CharacterBase_endBalloon.apply(this);
// フキダシの回数カウント
countBalloonNum(this);
};
//- フキダシの回数カウント
function countBalloonNum(chara) {
const balloon = chara._loopBalloonKe;
if (!balloon || !balloon.num) {
return;
}
balloon.num--;
// 終了
if (balloon.num < 0) {
chara._loopBalloonKe = null;
}
}
Sprite_Balloon.prototype.update = function () {
Sprite.prototype.update.call(this);
if (this._duration > 0) {
this._duration--;
if (this._duration <= 0 && this._target._character && this._target._character._loopBalloonCommentKe) {
this._duration = 8 * this.speed() + this.waitTime();
}
if (this._duration > 0) {
this.updatePosition();
this.updateFrame();
}
}
};
//- フキダシスプライトの消去
function deleteBalloonSprite(chara) {
const spriteset = SceneManager._scene._spriteset;
const balloons = spriteset._balloonSprites;
if (!balloons || !balloons.length) {
return;
}
balloons.forEach((balloon) => {
if (balloon.targetObject == chara) {
spriteset.removeBalloon(balloon);
}
});
}
//- スブライトフキダシ/破棄(処理追加)
const _Sprite_Balloon_destroy = Sprite_Balloon.prototype.destroy;
Sprite_Balloon.prototype.destroy = function (options) {
// フキダシスプライト破棄時のエラー防止
if (!this._texture) {
return;
}
_Sprite_Balloon_destroy.apply(this, arguments);
};
//- ゲームインタープリター/クリア(処理追加)
const _Game_Interpreter_clear = Game_Interpreter.prototype.clear;
Game_Interpreter.prototype.clear = function () {
_Game_Interpreter_clear.apply(this);
// イベントのフキダシ消去済みフラグをオフ
if ($gameMap) {
$gameMap.events().forEach((event) => (event ? (event._loopBalloonDeletedKe = null) : null));
}
};
//==================================================
//-- フキダシ位置調整
//==================================================
//- スブライトフキダシ/位置の更新(処理追加)
const _Sprite_Balloon_updatePosition = Sprite_Balloon.prototype.updatePosition;
Sprite_Balloon.prototype.updatePosition = function () {
_Sprite_Balloon_updatePosition.apply(this);
// キャラスプライトを取得(顔グラメイクプラグインに対応)
const target = this._target._faceBaseSpriteKeFcmk ? this._target._faceBaseSpriteKeFcmk._faceSprite : this._target;
// 位置の自動調整
if (keke_positionAdjust) {
this.y = this._target.y - target.height * target.scale.y + 8;
}
// サイズ自動調整
if (keke_sizeAdjust) {
const scale = target.scale.y > 1 ? 1 + (target.scale.y - 1) / 2 : 1;
this.scale.x = scale;
this.scale.y = scale;
}
};
//==================================================
//-- 文字列基本 /ベーシック
//==================================================
// 文字列の数字リスト化
function strToNumList(str) {
const list = [];
str = str.replace(/\[/g, "");
str = str.replace(/\]/g, "");
const strs = str.split(",");
let s2 = null;
for (let s of strs) {
if (s.includes("~")) {
s2 = s.split("~");
s2 = s2.map((s) => Number(s));
if (s2[1] >= s2[0]) {
for (let i = s2[0]; i <= s2[1]; i++) {
list.push(i);
}
} else {
for (let i = s2[1]; i <= s2[0]; i++) {
list.push(i);
}
}
} else {
list.push(Number(s));
}
}
return list;
}
//- ランダマイズ
function randomise(val) {
if (typeof val != "string" || !val.includes("~")) {
return val;
}
const vals = val.replace(/\s/g, "").split("~");
const calcs = [Number(vals[0]), Number(vals[1])];
calcs.sort((a, b) => a - b);
return Math.round(calcs[0] + Math.random() * (calcs[1] - calcs[0]));
}
//- 変数の置換
function convertVariable(str) {
if (!str) {
return str;
}
const regExp = /[\x1b\\]v\[(\d+)\]/gi;
while (true) {
const match = regExp.exec(str);
if (!match) {
break;
}
const val = $gameVariables.value(Number(match[1]));
str = str.replace(match[0], val);
}
return str;
}
//==================================================
//-- イベント基本 /ベーシック
//==================================================
//- 最初の注釈の取得
function getFirstComment(event) {
const comments = [];
// 現在のページの最初の注釈を取得
const currentPage = event.page();
comments.push(getFirstComment_page(currentPage));
//現在のページだけでいいので、他のページは不要
// // 1ページの最初の注釈を取得
// if (event._pageIndex != 0) {
// const onePage = event.event().pages[0];
// comments.push(getFirstComment_page(onePage));
// }
// 注釈を全て統合
let comment = "";
comments.forEach((cmt) => {
if (!cmt) {
return;
}
comment += cmt + "\n";
});
return comment;
}
//- 最初の注釈の取得-ページ
function getFirstComment_page(page) {
if (!page) {
return null;
}
let list = page.list;
if (!list[0] || list[0].code != 108) {
return null;
}
// 最初の注釈を全て読み込み
let comment = "";
let i = 0;
while (true) {
const command = list[i];
if (!command || !(command.code == 108 || command.code == 408)) {
break;
}
comment += command.parameters[0] + "\n";
i++;
}
return comment;
}
//==================================================
//-- プラグインコマンド基本 /ベーシック
//==================================================
let PlcmPreter = null;
//- プラグインコマンド呼び出しプリターを保存(処理追加)
const _PluginManager_callCommand = PluginManager.callCommand;
PluginManager.callCommand = function (self, pluginName, commandName, args) {
PlcmPreter = self;
_PluginManager_callCommand.apply(this, arguments);
PlcmPreter = null;
};
//- プラグインコマンド呼び出しイベントを取得
function getPlcmEvent() {
return getPreterEvent(PlcmPreter);
}
//- インタープリターのイベント取得
function getPreterEvent(preter) {
if (!preter) {
return null;
}
return preter.character(preter.eventId());
}
//- 名前でのキャラリスト取得
function getCharasByName(names, self) {
if (!names) {
return [];
}
const nameList = names.replace(/\s/g, "").split(",");
let charas = [];
let match = null;
for (const name of nameList) {
// イベントを取得
$gameMap.events().forEach((event) => {
const note = event.event().name + " " + event.event().note;
if (note.includes(name)) {
charas.push(event);
}
});
// セルフを取得
if (name.match(/^(セルフ|自分|自身|self)$/)) {
self = self || getPlcmEvent() || (charaSpritePlcm ? charaSpritePlcm._character : null) || $gamePlayer;
if (self) {
charas.push(self);
}
}
// プレイヤーを取得
if (name.match(/^(プレイヤー|操作キャラ|player)$/)) {
charas = [...charas, $gamePlayer];
}
// フォロワーを取得
match = name.match(/^(フォロワー|フォロアー|隊列|隊列キャラ|follower)(\d*)$/);
if (match) {
const id = match[2] ? Number(match[2]) - 1 : 0;
charas = id != null ? [...charas, $gamePlayer._followers._data[id]] : [...charas, ...$gamePlayer._followers._data];
}
// パーティを取得
if (name.match(/^(パーティ|味方|味方全員|味方全体|party)$/)) {
charas = [...charas, $gamePlayer, ...$gamePlayer._followers._data];
}
// 乗り物を取得
match = name.match(/^(乗り物|乗物|乗機|vehicle)(\d*)$/);
if (match) {
const id = match[2] ? Number(match[2]) - 1 : 0;
charas = id ? [...charas, $gameMap._vehicles[id]] : [...charas, ...$gameMap._vehicles];
}
// 全て取得
if (name.match(/^(全て|すべて|全部|全体|all)$/)) {
charas = [...$gameMap.events(), $gamePlayer, ...$gamePlayer._followers._data, ...$gameMap._vehicles];
}
// 選択なし
if (name.match(/^(なし|無し|none)$/)) {
}
}
charas = charas.filter((chara) => chara);
return charas;
}
//- IDでのキャラリスト取得
function getCharasById(ids, self) {
if (!ids) {
return [];
}
const idList = !ids ? [] : strToNumList(ids.toString());
let charas = [];
for (const id of idList) {
// イベントを取得
if (id > 0) {
charas.push($gameMap.event(id));
}
// セルフを取得
if (id == 0) {
self = self || getPlcmEvent() || (charaSpritePlcm ? charaSpritePlcm._character : null) || $gamePlayer;
if (self && !idList.includes(self._eventId)) {
charas.push(self);
}
}
// プレイヤーを取得
if (id == -1) {
charas = [...charas, $gamePlayer];
}
// フォロワーを取得
if (id <= -10 && id >= -99) {
charas = id == -10 ? [...charas, ...$gamePlayer._followers._data] : [...charas, $gamePlayer._followers._data[Math.abs(id) - 11]];
}
// 乗り物を取得
if (id <= -100) {
charas = id == -100 ? [...charas, ...$gameMap._vehicles] : [...charas, $gameMap._vehicles[Math.abs(id) - 101]];
}
// 全て取得
if (id == -2) {
charas = [...$gameMap.events(), $gamePlayer, ...$gamePlayer._followers._data, ...$gameMap._vehicles];
}
}
charas = charas.filter((chara) => chara);
return charas;
}
})();