124 lines
4.9 KiB
JavaScript
124 lines
4.9 KiB
JavaScript
/*:
|
||
* @target MZ
|
||
* @plugindesc \STATE[n] でステートのメモ欄、\SKILL[n] でスキルの説明欄を表示(<タグ> 除去)。v1.1.0
|
||
* @author Umu
|
||
*
|
||
* @help
|
||
* ■ 使い方
|
||
* イベントの「文章の表示」に以下のように記述してください。
|
||
* \STATE[10] # ステート ID10 のメモ欄テキストを挿入
|
||
* \SKILL[25] # スキル ID25 の説明テキストを挿入
|
||
*
|
||
* メモ欄・説明欄に改行があれば、そのまま改行されます。
|
||
* <タグ> で囲まれた部分はすべて削除されます。
|
||
*
|
||
* ■ 仕様
|
||
* ・Window_Base.convertEscapeCharacters の処理後に、独自制御文字を追加で処理します。
|
||
* ・制御文字は大文字小文字を問いません(例: \skill[1] も可)。
|
||
* ・無効な ID や空文字の場合は空文字列を返します。
|
||
*
|
||
* ●\STATE[n] : $dataStates[n].note を取得し、/<[^>]*>/g で <タグ> を除去
|
||
* ●\SKILL[n] : $dataSkills[n].description を取得し、同様に <タグ> を除去
|
||
*
|
||
* プラグインコマンド・パラメータはありません。
|
||
*
|
||
* ■ 利用規約
|
||
* MIT ライセンス (LICENSE.txt 同梱)
|
||
*/
|
||
|
||
(() => {
|
||
'use strict';
|
||
|
||
/** <タグ> を取り除くための正規表現 */
|
||
const TAG_REGEX = /<[^>]*>/g;
|
||
|
||
/** 元の convertEscapeCharacters を保持 */
|
||
const _convertEscapeCharacters = Window_Base.prototype.convertEscapeCharacters;
|
||
|
||
/**
|
||
* 文章中の制御文字を変換
|
||
* @param {string} text
|
||
* @returns {string}
|
||
*/
|
||
Window_Base.prototype.convertEscapeCharacters = function (text) {
|
||
// 既存の制御文字を最初に処理
|
||
text = _convertEscapeCharacters.call(this, text);
|
||
|
||
// \SKILL[n] — スキル説明欄を挿入(英語向けに幅で折り返し)
|
||
text = text.replace(/\x1bSKILL\[(\d+)]/gi, (_, p1) => {
|
||
const id = Number(p1);
|
||
const skill = $dataSkills[id];
|
||
if (skill && typeof skill.description === 'string') {
|
||
const raw = skill.description.replace(TAG_REGEX, '').trim();
|
||
const tmp = new Bitmap(1, 1);
|
||
tmp.fontFace = 'MPLUS1Code-Regular';
|
||
tmp.fontSize = 26;
|
||
const pad = this?.padding ?? 12;
|
||
const maxWidth = Math.max(200, (this?.innerWidth ?? 480) - pad * 2);
|
||
return wrapTextToWidth_CC(raw, tmp, maxWidth);
|
||
}
|
||
return '';
|
||
});
|
||
|
||
// \STATE[n] — ステートメモ欄を挿入(英語向けに幅で折り返し)
|
||
text = text.replace(/\x1bSTATE\[(\d+)]/gi, (_, p1) => {
|
||
const id = Number(p1);
|
||
const state = $dataStates[id];
|
||
if (state && typeof state.note === 'string') {
|
||
const raw = state.note.replace(TAG_REGEX, '').trim();
|
||
const tmp = new Bitmap(1, 1);
|
||
tmp.fontFace = 'MPLUS1Code-Regular';
|
||
tmp.fontSize = 26;
|
||
const pad = this?.padding ?? 12;
|
||
const maxWidth = Math.max(200, (this?.innerWidth ?? 480) - pad * 2);
|
||
return wrapTextToWidth_CC(raw, tmp, maxWidth);
|
||
}
|
||
return '';
|
||
});
|
||
|
||
return text;
|
||
};
|
||
|
||
//============================================================
|
||
// Optional: English-friendly wrapping helpers
|
||
//============================================================
|
||
function wrapTextToWidth_CC(text, bitmap, maxWidth) {
|
||
if (!text) return '';
|
||
const lines = [];
|
||
const rawLines = String(text).replace(/\r\n?|\u2028/g, "\n").split('\n');
|
||
for (const raw of rawLines) {
|
||
const toks = raw.split(/(\s+)/);
|
||
let cur = '';
|
||
for (const tk of toks) {
|
||
const next = cur + tk;
|
||
if (bitmap.measureTextWidth(next) <= maxWidth) cur = next; else if (!cur) {
|
||
const parts = splitChars_CC(tk, bitmap, maxWidth);
|
||
for (let i = 0; i < parts.length - 1; i++) lines.push(parts[i]);
|
||
cur = parts[parts.length - 1];
|
||
} else {
|
||
if (cur.trim()) lines.push(cur.trimEnd());
|
||
const parts = splitChars_CC(tk, bitmap, maxWidth);
|
||
for (let i = 0; i < parts.length - 1; i++) lines.push(parts[i]);
|
||
cur = parts[parts.length - 1];
|
||
}
|
||
}
|
||
lines.push(cur.trimEnd());
|
||
}
|
||
return lines.join('\n');
|
||
}
|
||
|
||
function splitChars_CC(token, bitmap, maxWidth) {
|
||
const out = [];
|
||
let buf = '';
|
||
for (const ch of token) {
|
||
const next = buf + ch;
|
||
if (bitmap.measureTextWidth(next) <= maxWidth) buf = next; else {
|
||
if (buf) out.push(buf);
|
||
buf = ch;
|
||
}
|
||
}
|
||
if (buf) out.push(buf);
|
||
return out.length ? out : [token];
|
||
}
|
||
})();
|
||
|