257 lines
12 KiB
JavaScript
257 lines
12 KiB
JavaScript
/*: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);
|
||
}
|
||
|
||
})();
|