393 lines
15 KiB
JavaScript
393 lines
15 KiB
JavaScript
/*:ja
|
||
* @target MZ
|
||
* @author kurogoma knights
|
||
* @base PluginCommonBase
|
||
* @plugindesc スチルマネージャー (KN_Map依存/UniqueDataLoader参照)
|
||
*
|
||
* @help
|
||
* ■レイヤ(10~30)
|
||
* backX -> base -> face -> frontX
|
||
*
|
||
* ■ギャラリー
|
||
* ・指定シーンの登録スチル(解放済みのみ/全表示デバッグ)を左右キーで巡回
|
||
* ・キャンセルで終了
|
||
* ・HUDに index/total と解放数/登録数を表示
|
||
*
|
||
* @command SHOW_BY_ID
|
||
* @text 表示(ID)
|
||
* @desc 登録済みIDのスチルを表示します
|
||
* @arg id @type string @text ID
|
||
*
|
||
* @command HIDE
|
||
* @text 非表示
|
||
* @desc 表示中のスチルを全て消去します
|
||
*
|
||
* @command REGISTER_ALL
|
||
*
|
||
* @command OPEN_GALLERY
|
||
* @text ギャラリーを開く
|
||
* @desc 指定シーンのギャラリーを開始します(左右で切替/キャンセルで終了)
|
||
* @arg scene @type string @text シーンID
|
||
* @arg label @type string @text 表示シーン名
|
||
* @arg showAll @type boolean @default false @text すべて表示(デバッグ)
|
||
* @arg startIndex @type number @min 0 @default 0 @text 開始インデックス
|
||
*
|
||
*/
|
||
|
||
(() => {
|
||
'use strict';
|
||
const script = document.currentScript;
|
||
PluginManagerEx.createParameter(script);
|
||
|
||
// レイヤー数設定
|
||
const LAYER_COUNT = {
|
||
back: 3,
|
||
front: 9
|
||
};
|
||
|
||
// ピクチャID動的割当
|
||
// 10から順に back_5, back_4, back_3, back_2, back_1, base, face, front_1, front_2, front_3 ...
|
||
const PIC_ID = {};
|
||
let pid = Kurogoma.PIDConst.StillReservedStart;
|
||
for (let i = LAYER_COUNT.back; i >= 1; i--) PIC_ID[`back_${i}`] = pid++;
|
||
PIC_ID.base = pid++;
|
||
PIC_ID.face = pid++;
|
||
for (let i = 1; i <= LAYER_COUNT.front; i++) PIC_ID[`front_${i}`] = pid++;
|
||
|
||
// HUDウィンドウ
|
||
class Window_StillGalleryHUD extends Window_Base {
|
||
initialize(rect) {
|
||
super.initialize(rect);
|
||
this.opacity = 192;
|
||
this.contentsOpacity = 255;
|
||
this._title = '';
|
||
this._pageText = '';
|
||
}
|
||
setTexts(title, numerator, denominator) {
|
||
const pageText = `${numerator}/${denominator}`;
|
||
if (this._title !== title || this._pageText !== pageText) {
|
||
this._title = title;
|
||
this._pageText = pageText;
|
||
this.refresh();
|
||
}
|
||
}
|
||
refresh() {
|
||
this.contents.clear();
|
||
const cw = this.contentsWidth();
|
||
this.drawText(this._title, 12, 0, cw / 2, 'left');
|
||
this.drawText(this._pageText, cw / 2, 0, cw / 2 - 12, 'right');
|
||
}
|
||
}
|
||
|
||
// スチルマネージャ本体
|
||
class StillManager {
|
||
constructor() {
|
||
this.state = { scene: null };
|
||
for (const key of Object.keys(PIC_ID)) this.state[key] = "";
|
||
this._galleryActive = false;
|
||
this._galleryScene = null;
|
||
this._galleryLabel = null;
|
||
this._galleryShowAll = false;
|
||
this._galleryList = [];
|
||
this._galleryIndex = 0;
|
||
this._hud = null;
|
||
this._defs = {}; // スチル定義(セーブ対象外)
|
||
}
|
||
|
||
// スチル表示(ID指定)
|
||
showById(id, recordUnlock = true) {
|
||
this._ensureDefStore();
|
||
const rec = this._defs[id];
|
||
if (!rec) {
|
||
console.warn('[StillManager] 未登録のid:', id);
|
||
return false;
|
||
}
|
||
|
||
console.log("[StillManager] 表示中のid :", id);
|
||
|
||
const args = { scene: rec.scene };
|
||
for (const key of Object.keys(PIC_ID)) args[key] = rec[key];
|
||
this._applyShowArgs(args);
|
||
|
||
// 解放は記憶アイテム所持で判定するため、フラグ記録は不要
|
||
return true;
|
||
}
|
||
|
||
// 非表示
|
||
hide() {
|
||
Object.values(PIC_ID).forEach(id => $gameScreen.erasePicture(id));
|
||
for (const key of Object.keys(this.state)) {
|
||
if (key !== 'scene') this.state[key] = "";
|
||
}
|
||
}
|
||
|
||
// 定義登録
|
||
register(def) {
|
||
if (!def?.id) return console.warn('[StillManager] id がありません');
|
||
if (!def?.base) return console.warn('[StillManager] base は必須です:', def);
|
||
this._ensureDefStore();
|
||
const scene = def.scene || "";
|
||
if (!scene) return console.warn('[StillManager] sceneId を抽出できません:', def.base);
|
||
|
||
const rec = { id: String(def.id), scene };
|
||
for (const key of Object.keys(PIC_ID)) {
|
||
rec[key] = this._buildDefinitionPath(scene, def[key]);
|
||
}
|
||
|
||
this._defs[rec.id] = rec;
|
||
return true;
|
||
}
|
||
|
||
registerAll() {
|
||
const list = $dataUniques?.StillList;
|
||
if (!Array.isArray(list)) return console.warn("[StillManager] StillList.json が未ロード");
|
||
let count = 0;
|
||
this._ensureDefStore();
|
||
for (const def of list) if (this.register(def)) count++;
|
||
console.log(`[StillManager] ${count} 件 登録完了`);
|
||
}
|
||
|
||
/**
|
||
* 解放済みスチルID一覧を取得
|
||
* 記憶アイテム所持による解放(StillMemoryMap経由)
|
||
*/
|
||
getUnlockedIds(sceneId) {
|
||
this._ensureDefStore();
|
||
const unlockedSet = new Set();
|
||
|
||
// 1. 記憶アイテム所持による解放
|
||
const memoryMap = $dataUniques?.StillMemoryMap;
|
||
if (memoryMap && $gameParty) {
|
||
for (const [memoryItemName, stillIds] of Object.entries(memoryMap)) {
|
||
// 記憶アイテムを所持しているか
|
||
const item = $dataItems.find(i => i && i.name === memoryItemName);
|
||
if (item && $gameParty.hasItem(item)) {
|
||
// このアイテムに紐づくスチルIDを解放済みに追加
|
||
for (const stillId of stillIds) {
|
||
// このスチルが指定されたsceneIdに属するか確認
|
||
const def = this._defs[stillId];
|
||
if (def && def.scene === sceneId) {
|
||
unlockedSet.add(stillId);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return [...unlockedSet];
|
||
}
|
||
|
||
// ギャラリー
|
||
isGalleryActive() { return this._galleryActive; }
|
||
|
||
openGallery(sceneId, label = "", showAll = false, startIndex = 0) {
|
||
this._ensureDefStore();
|
||
|
||
let sceneIds = [];
|
||
if (sceneId.toLowerCase() === "all") {
|
||
sceneIds = [...new Set(Object.values(this._defs).map(r => r.scene))];
|
||
} else if (sceneId.includes(",")) {
|
||
sceneIds = sceneId.split(",").map(s => s.trim()).filter(Boolean);
|
||
} else {
|
||
sceneIds = [sceneId];
|
||
}
|
||
|
||
this._galleryScene = sceneId;
|
||
this._galleryLabel = label;
|
||
this._galleryShowAll = !!showAll;
|
||
|
||
const all = Object.values(this._defs)
|
||
.filter(r => sceneIds.includes(r.scene))
|
||
.map(r => r.id);
|
||
const unlocked = sceneIds.flatMap(s => this.getUnlockedIds(s));
|
||
this._galleryList = (showAll ? all : all.filter(id => unlocked.includes(id)));
|
||
|
||
if (!this._galleryList.length) {
|
||
$gameMessage.add(`ギャラリー: 表示対象がありません(scene=${sceneId}${showAll ? ", all" : ""})`);
|
||
return false;
|
||
}
|
||
|
||
this._galleryIndex = Math.max(0, Math.min(startIndex, this._galleryList.length - 1));
|
||
this._galleryActive = true;
|
||
this._ensureHud();
|
||
this._refreshHud();
|
||
this.showById(this._galleryList[this._galleryIndex], false);
|
||
$gameMap.hideUI?.();
|
||
$gameScreen.startTint([-68, -68, -68, 0], 1);
|
||
$gameMap.showGuide();
|
||
return true;
|
||
}
|
||
|
||
closeGallery() {
|
||
if (!this._galleryActive) return;
|
||
this._galleryActive = false;
|
||
this._galleryScene = null;
|
||
this._galleryLabel = null;
|
||
this._galleryList = [];
|
||
this._galleryIndex = 0;
|
||
this.hide();
|
||
this._disposeHud();
|
||
$gameMap.showUI?.();
|
||
$gameScreen.startTint([0, 0, 0, 0], 1);
|
||
$gameMap.hideGuide();
|
||
Input.clear();
|
||
}
|
||
|
||
nextEntry() {
|
||
if (!this._galleryActive) return;
|
||
this._galleryIndex = (this._galleryIndex + 1) % this._galleryList.length;
|
||
this.showById(this._galleryList[this._galleryIndex], false);
|
||
this._refreshHud();
|
||
}
|
||
|
||
prevEntry() {
|
||
if (!this._galleryActive) return;
|
||
this._galleryIndex = (this._galleryIndex - 1 + this._galleryList.length) % this._galleryList.length;
|
||
this.showById(this._galleryList[this._galleryIndex], false);
|
||
this._refreshHud();
|
||
}
|
||
|
||
updateGallery() {
|
||
if (!this._galleryActive) return;
|
||
if (Input.isTriggered('right') || Input.isRepeated('right') || Input.isTriggered('pagedown')) this.nextEntry();
|
||
else if (Input.isTriggered('left') || Input.isRepeated('left') || Input.isTriggered('pageup')) this.prevEntry();
|
||
else if (Input.isTriggered('cancel')) this.closeGallery();
|
||
}
|
||
|
||
_ensureHud() {
|
||
const scene = SceneManager._scene;
|
||
if (!(scene instanceof Scene_Map)) return;
|
||
if (this._hud && this._hud.parent) return;
|
||
const ww = 240;
|
||
const wh = Kurogoma.Layout.calcWindowHeight(1, false);
|
||
const rect = new Rectangle(4, 4, ww, wh);
|
||
this._hud = new Window_StillGalleryHUD(rect);
|
||
scene.addWindow(this._hud);
|
||
}
|
||
|
||
_disposeHud() {
|
||
if (!this._hud) return;
|
||
const p = this._hud.parent;
|
||
if (p) p.removeChild(this._hud);
|
||
this._hud.destroy?.();
|
||
this._hud = null;
|
||
}
|
||
|
||
_refreshHud() {
|
||
if (!this._hud) return;
|
||
const currentId = this._galleryList[this._galleryIndex];
|
||
let sceneIds = [];
|
||
|
||
if (this._galleryScene?.toLowerCase() === "all") {
|
||
sceneIds = [...new Set(Object.values(this._defs).map(r => r.scene))];
|
||
} else if (this._galleryScene?.includes(",")) {
|
||
sceneIds = this._galleryScene.split(",").map(s => s.trim()).filter(Boolean);
|
||
} else {
|
||
sceneIds = [this._galleryScene];
|
||
}
|
||
|
||
const allIds = Object.values(this._defs)
|
||
.filter(r => sceneIds.includes(r.scene))
|
||
.map(r => r.id);
|
||
|
||
const title = this._galleryLabel || this._galleryScene;
|
||
const currentPos = allIds.indexOf(currentId);
|
||
const numerator = currentPos >= 0 ? currentPos + 1 : this._galleryIndex + 1;
|
||
const denominator = allIds.length;
|
||
this._hud.setTexts(title, numerator, denominator);
|
||
}
|
||
|
||
_applyShowArgs(args) {
|
||
this.state.scene = args.scene;
|
||
for (const key of Object.keys(PIC_ID)) {
|
||
this._showLayerIfChanged(key, this._toDisplayValue(args[key]));
|
||
}
|
||
}
|
||
|
||
_showLayerIfChanged(layerKey, newPathNorm) {
|
||
const id = PIC_ID[layerKey];
|
||
const before = this.state[layerKey];
|
||
const next = (newPathNorm ?? before);
|
||
if (next === before) return;
|
||
if (next === "") {
|
||
$gameScreen.erasePicture(id);
|
||
this.state[layerKey] = "";
|
||
return;
|
||
}
|
||
const zoom = $gameScreen.zoomScale ? $gameScreen.zoomScale() : 1;
|
||
const scale = 100 / zoom;
|
||
$gameScreen.showPicture(id, next, 0, 0, 0, scale, scale, 255, 0);
|
||
this.state[layerKey] = next;
|
||
}
|
||
|
||
_toDisplayValue(v) {
|
||
if (!v) return "";
|
||
return this._stripImgPref(this._stripExt(v));
|
||
}
|
||
|
||
_ensureDefStore() {
|
||
// _defs はインスタンス変数(セーブ対象外)
|
||
}
|
||
|
||
_buildDefinitionPath(scene, v) {
|
||
return v ? `still/${scene}/${this._fname(v)}` : '';
|
||
}
|
||
_stripExt(s) { return String(s || '').replace(/\.[^/.]+$/, ''); }
|
||
_stripImgPref(s) { return String(s || '').replace(/^(?:img[\\/])?(?:pictures[\\/])?/i, ''); }
|
||
_fname(s) { return String(s || '').replace(/^.*[\\/]/, '').replace(/\.[^/.]+$/, ''); }
|
||
}
|
||
|
||
window.Kurogoma = window.Kurogoma || {};
|
||
window.Kurogoma.stillManager = new StillManager();
|
||
const stillManager = window.Kurogoma.stillManager;
|
||
|
||
// セーブデータロード後に定義を再登録
|
||
// _defs はセーブ対象外なのでロード後に毎回再構築が必要
|
||
const _DataManager_extractSaveContents = DataManager.extractSaveContents;
|
||
DataManager.extractSaveContents = function(contents) {
|
||
_DataManager_extractSaveContents.call(this, contents);
|
||
// 最新の定義を再登録
|
||
if ($dataUniques?.StillList) {
|
||
stillManager.registerAll();
|
||
}
|
||
};
|
||
|
||
// Map連動制御
|
||
|
||
const _Scene_Map_update = Scene_Map.prototype.update;
|
||
Scene_Map.prototype.update = function() {
|
||
if (stillManager?.isGalleryActive?.()) stillManager.updateGallery();
|
||
_Scene_Map_update.call(this);
|
||
};
|
||
|
||
const _Game_Player_canMove = Game_Player.prototype.canMove;
|
||
Game_Player.prototype.canMove = function() {
|
||
if (stillManager?.isGalleryActive?.()) return false;
|
||
return _Game_Player_canMove.call(this);
|
||
};
|
||
|
||
const _Scene_Map_processMapTouch = Scene_Map.prototype.processMapTouch;
|
||
Scene_Map.prototype.processMapTouch = function() {
|
||
if (stillManager?.isGalleryActive?.()) return;
|
||
_Scene_Map_processMapTouch.call(this);
|
||
};
|
||
|
||
const _Scene_Map_isMenuEnabled = Scene_Map.prototype.isMenuEnabled;
|
||
Scene_Map.prototype.isMenuEnabled = function() {
|
||
if (stillManager?.isGalleryActive?.()) return false;
|
||
return _Scene_Map_isMenuEnabled.call(this);
|
||
};
|
||
|
||
// プラグインコマンド
|
||
|
||
const expand = v => (typeof v === "string" ? v.replace(/\\V\[(\d+)\]/gi, (_, n) => $gameVariables.value(Number(n)) ?? "") : v);
|
||
PluginManagerEx.registerCommand(script, "SHOW_BY_ID", args => {
|
||
const id = expand(args.id);
|
||
$gameMap.hideUI?.();
|
||
stillManager.showById(id);
|
||
});
|
||
PluginManagerEx.registerCommand(script, "HIDE", () => { $gameMap.showUI?.(); stillManager.hide(); });
|
||
PluginManagerEx.registerCommand(script, "REGISTER_ALL", () => stillManager.registerAll());
|
||
PluginManagerEx.registerCommand(script, "OPEN_GALLERY", args => stillManager.openGallery(args.scene, args.label, args.showAll, args.startIndex));
|
||
|
||
})();
|