nyacronomicon/js/plugins/KN_EventSpawn.js
2026-03-12 22:39:10 -05:00

257 lines
12 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.

/*:ja
* @target MZ
* @author kurogoma knights
* @base PluginCommonBase
* @plugindesc イベント生成機能
*
* @command CREATE_EVENT
* @desc regionを参照してイベントを生成する
* エネミーはマップ敵リスト参照(region指定は80番台)
*
*/
(() => {
"use strict";
const script = document.currentScript;
const param = PluginManagerEx.createParameter(script);
// イベント生成
PluginManagerEx.registerCommand(script, "CREATE_EVENT", args => {
$gameMap.knCreateEvent();
$gameMap.knCreateRegionEnemyEvent();
$gameMap.knCreateRandomEvent();
});
// 開発中だけおきる問題のワークアラウンド関数
// ホットリロードが発生したとき、Game_Map.setupEvents と EventReSpawn.setupInitialSpawnEvents が再実行される。
// ただし CREATE_EVENT で生成したイベントは復元されない(手動生成のため)
window.Kurogoma = window.Kurogoma || {};
window.Kurogoma.spawnEvents = function() {
if (!$gameMap) {
console.warn('マップが読み込まれていません');
return;
}
console.log('イベント生成を実行します...');
$gameMap.knCreateEvent();
$gameMap.knCreateRegionEnemyEvent();
$gameMap.knCreateRandomEvent();
console.log('イベント生成完了');
};
// イベント生成
Game_Map.prototype.knCreateEvent = function() {
// 各カテゴリごとのデータを定義
const regionCounts = countRegions();
const enemyList = getEnemies();
// スポーンデータ定義: { weights: 重み付けオブジェクト, count: 生成数, region: リージョンID }
const spawnConfig = {
kaidan: { weights: { "階段": 1 }, count: regionCounts.kaidan.count, region: regionCounts.kaidan.region },
tobira: { weights: { "鍵扉": 1 }, count: regionCounts.tobira.count, region: regionCounts.tobira.region },
enemy: { weights: enemyList, count: regionCounts.enemy.count, region: regionCounts.enemy.region },
treasure: { weights: { "宝箱": 1 }, count: regionCounts.treasure.count, region: regionCounts.treasure.region },
potion: { weights: { "赤ポ": 8, "青ポ": 1 }, count: regionCounts.potion.count, region: regionCounts.potion.region },
gold: { weights: { "ゴールド": 1 }, count: regionCounts.gold.count, region: regionCounts.gold.region },
key: { weights: { "魔法の鍵": 1 }, count: regionCounts.key.count, region: regionCounts.key.region },
treasure2: { weights: { "銀箱": 1 }, count: regionCounts.treasure2.count, region: regionCounts.treasure2.region },
treasure3: { weights: { "金箱": 1 }, count: regionCounts.treasure3.count, region: regionCounts.treasure3.region },
shop: { weights: { "アイテムショップ": 1, "スキルショップ": 1 }, count: regionCounts.shop.count, region: regionCounts.shop.region },
item_shop: { weights: { "アイテムショップ": 1 }, count: regionCounts.item_shop.count, region: regionCounts.item_shop.region },
skill_shop: { weights: { "スキルショップ": 1 }, count: regionCounts.skill_shop.count, region: regionCounts.skill_shop.region },
rare_shop: { weights: { "レアショップ": 1 }, count: regionCounts.rare_shop.count, region: regionCounts.rare_shop.region },
enhance_shop: { weights: { "スキル強化ショップ": 1 }, count: regionCounts.enhance_shop.count, region: regionCounts.enhance_shop.region },
};
// 各カテゴリごとにイベントを生成
for (const key in spawnConfig) {
const { weights, count, region } = spawnConfig[key];
const list = getWeightedList(weights, count);
for (let i = 0; i < list.length; i++) {
spawnEvent(list[i], region);
}
}
};
// リージョン指定の敵イベント生成
// 敵イベントはマップに設定されている敵リストから取得 リージョンは最初のものを使用
Game_Map.prototype.knCreateRegionEnemyEvent = function() {
const enemyList = {};
$dataMap.encounterList.forEach(encounter => {
const troopId = encounter.troopId;
const regionSet = encounter.regionSet;
if ($dataTroops[troopId] && regionSet.length > 0) {
enemyList[troopId] = {};
enemyList[troopId].name = $dataTroops[troopId].name;
enemyList[troopId].region = regionSet[0];
}
});
// 各リージョンごとに敵イベントを生成
Object.entries(enemyList).forEach(([key, enemy]) => {
const count = countRegion(enemy.region);
for (let i = 0; i < count; i++) {
spawnEvent(enemy.name, enemy.region);
}
});
};
// 指定したリージョンをカウントする
function countRegion(region) {
const width = $dataMap.width;
const height = $dataMap.height;
let count = 0;
for (let i = 0; i < width * height; i++) {
const regionId = $dataMap.data[i + 5 * width * height]; // dataMapは6層構造でリージョンIDは6番目(10x10のマップなら500以降から)
if (regionId === region) {
count++;
}
}
return count;
}
// イベントランダム生成
Game_Map.prototype.knCreateRandomEvent = function() {
// 各カテゴリごとのデータを定義
const regionCounts = countRegions();
const enemyList = getEnemies();
// ランダム生成 指定リージョンがなければ生成しない
const randomRegionCount = regionCounts.random.count;
if (randomRegionCount <= 0)
return;
// ランダムスポーンデータ定義: { weights: 重み付けオブジェクト, count: 生成数 }
// 上から順に生成される
const randomSpawnConfig = {
kaidan: { weights: { "階段": 1 }, count: regionCounts.kaidan.count > 0 ? 0 : 2 }, // 階段あればランダムに作らない
tobira: { weights: { "鍵扉": 1 }, count: 0 },
shop: { weights: { "アイテムショップ": 1, "スキルショップ": 1 }, count: 0 }, // レアショップ除外
key: { weights: { "魔法の鍵": 1 }, count: Kurogoma.Random.chanceMultiple(50, 1) }, // 50%で1つ
treasure2: { weights: { "銀箱": 1 }, count: 0 },
treasure3: { weights: { "金箱": 1 }, count: 0 },
treasure: { weights: { "宝箱": 1 }, count: randomRegionCount * 0.1 },
gold: { weights: { "ゴールド": 1 }, count: randomRegionCount * 0.1 },
enemy: { weights: enemyList, count: randomRegionCount * 0.5 },
potion: { weights: { "赤ポ": 8, "青ポ": 1 }, count: randomRegionCount * 0.05 },
};
// 各カテゴリごとにイベントを生成
for (const key in randomSpawnConfig) {
const { weights, count } = randomSpawnConfig[key];
const list = getWeightedList(weights, count);
list.forEach(obj => spawnEvent(obj, 99));
}
};
// マップに設定されている敵リストの取得
function getEnemies() {
if (!$dataMap || !$dataMap.encounterList) return {};
const enemyWeights = {};
const encounterList = $dataMap.encounterList;
encounterList.forEach(encounter => {
// encounter は { troopId, weight, regionSet, ... } の形式
const troopId = encounter.troopId;
// troopId が有効かチェック
if ($dataTroops[troopId]) {
const troopName = $dataTroops[troopId].name;
// 同じ名前が既に登録されている場合は上書き
enemyWeights[troopName] = encounter.weight;
}
});
return enemyWeights;
}
// 重み付きリストを取得
function getWeightedList(weights, count) {
let list = [];
for (let i = 0; i < count; i++) {
list.push(getWeightedRandom(weights));
}
return list;
}
// 重み付きランダム抽選
function getWeightedRandom(weights) {
const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
let random = Math.random() * totalWeight;
for (const [item, weight] of Object.entries(weights)) {
random -= weight;
if (random < 0) return item;
}
}
// リージョンをカウントする
function countRegions() {
const width = $dataMap.width;
const height = $dataMap.height;
// 最初からオブジェクトに統一する
const regionCounts = {
enemy: { region: 1, count: 0 }, // 敵
treasure: { region: 2, count: 0 }, // 赤箱
potion: { region: 3, count: 0 }, // ポーション
gold: { region: 4, count: 0 }, // ゴールド
treasure2: { region: 5, count: 0 }, // 銀箱
treasure3: { region: 6, count: 0 }, // 金箱
key: { region: 7, count: 0 }, // 魔法の鍵
kaidan: { region: 10, count: 0 }, // 階段
tobira: { region: 11, count: 0 }, // 鍵扉
shop: { region: 20, count: 0 }, // アイテムショップ・スキルショップの混在(レアショップ除外)
item_shop: { region: 21, count: 0 }, // アイテムショップ専用
skill_shop: { region: 22, count: 0 }, // スキルショップ専用
rare_shop: { region: 23, count: 0 }, // レアショップ専用
enhance_shop: { region: 24, count: 0 }, // スキル強化ショップ専用
// region:80~89は指定敵用に予約knCreateRegionEnemyEventで使用、knCreateEventでは生成しない
fixed_enemy_80: { region: 80, count: 0 },
fixed_enemy_81: { region: 81, count: 0 },
fixed_enemy_82: { region: 82, count: 0 },
fixed_enemy_83: { region: 83, count: 0 },
fixed_enemy_84: { region: 84, count: 0 },
fixed_enemy_85: { region: 85, count: 0 },
fixed_enemy_86: { region: 86, count: 0 },
fixed_enemy_87: { region: 87, count: 0 },
fixed_enemy_88: { region: 88, count: 0 },
fixed_enemy_89: { region: 89, count: 0 },
random: { region: 99, count: 0 }, // ランダム指定のみ
};
// `regionCounts` を使ってリージョンID → キーの対応表を作成
const regionKeys = Object.fromEntries(
Object.entries(regionCounts).map(([key, { region }]) => [region, key])
);
// リージョンIDをカウント
for (let i = 0; i < width * height; i++) {
const regionId = $dataMap.data[i + 5 * width * height]; // dataMapは6層構造でリージョンIDは6番目(10x10のマップなら500以降から)
if (regionKeys[regionId]) {
const key = regionKeys[regionId];
regionCounts[key].count++;
}
}
return regionCounts;
}
// イベント生成
function spawnEvent(id, region) {
if (!id) {
console.warn(`region:${region} idが指定されていません`);
return;
}
const args = {
id: id, // コピー元のイベントID or イベント名
passable: "false", // 通行可能タイル上にだけ生成(true/false)
screen: "0", // 判定しない(0) 画面内にだけ生成(1)
overlap: "3", // キャラクター、イベントのどちらとも重複しない(3)
terrainTags: JSON.stringify([]), // 地形タグなし
regions: region ? JSON.stringify([region]) : "[]", // 指定したリージョンに生成
template: "true", // テンプレート生成を使用(true)
algorithm: "1", // 左上から順番に探す(1)(ランダムに探す"0"を設定した場合必ず生成されるとは限らないので注意)
};
PluginManager.callCommand($gameMap._interpreter, "EventReSpawn", "MAKE_RANDOM", args);
}
})();