name = '未知怪物'; $monster->expReward = 10; return $monster; } // 2. Pick a monster using weighted random from map config $totalWeight = 0; foreach ($monsterConfig as $m) { $totalWeight += $m['weight'] ?? 100; } $rand = rand(1, $totalWeight); $selectedMonster = null; foreach ($monsterConfig as $m) { $rand -= $m['weight'] ?? 100; if ($rand <= 0) { $selectedMonster = $m; break; } } // Fallback if (!$selectedMonster) { $selectedMonster = $monsterConfig[0]; } // 3. Hydrate monster base stats from maps.php $monster->hydrateFromConfig($selectedMonster,$dungeonId); $status = $monster->getStats(); $monster->hp = $status['maxHp']; return $monster; } /** * Create a group of monsters with random, diverse enemies (1-5 monsters) * Each monster is independently selected from the dungeon's monster pool using weighted random selection * @return Actor[] */ public static function createGroup(int $dungeonId): array { // Load data static $maps = null; if ($maps === null) { $maps = require __DIR__ . '/../../src/Data/maps.php'; } $monsterConfig = $maps[$dungeonId]['monsters'] ?? []; if (empty($monsterConfig)) { return [self::create($dungeonId)]; } // Determine group size (1-5 enemies) $groupSize = rand(1, 5); $group = []; // Create each enemy independently using weighted random selection for ($i = 0; $i < $groupSize; $i++) { $totalWeight = 0; foreach ($monsterConfig as $m) { $totalWeight += $m['weight'] ?? 100; } $rand = rand(1, $totalWeight); $selectedConfig = null; foreach ($monsterConfig as $m) { $rand -= $m['weight'] ?? 100; if ($rand <= 0) { $selectedConfig = $m; break; } } if (!$selectedConfig) { $selectedConfig = $monsterConfig[0]; } // Create monster from selected config $monster = self::create($dungeonId); // Add suffix to distinguish multiple monsters of same type if ($groupSize > 1) { $monster->name .= " (" . ($i + 1) . ")"; } $group[] = $monster; } return $group; } public function hydrateFromConfig(array $config,$dungeonId): void { $this->name = $config['name']; $this->level = $config['level'] ?? 1; $this->baseHp = $config['hp'] ?? 20; $this->hp = $this->baseHp; $this->maxHp = $this->baseHp; $this->basePatk = $config['patk'] ?? $config['atk'] ?? 4; $this->patk = $this->basePatk; $this->baseMatk = $config['matk'] ?? 2; $this->matk = $this->baseMatk; $this->basePdef = $config['pdef'] ?? $config['def'] ?? 0; $this->pdef = $this->basePdef; $this->baseMdef = $config['mdef'] ?? 0; $this->mdef = $this->baseMdef; $this->crit = $config['crit'] ?? 5; $this->critdmg = $config['critdmg'] ?? 130; $this->expReward = $config['exp'] ?? 0; $this->spiritStoneReward = $config['spirit_stones'] ?? 0; // 根据等级和基础属性分配天赋点 $this->allocateTalentsByLevel(); static $allItems = null; if ($allItems === null) { $allItems = require __DIR__ . '/../../src/Data/item_new.php'; } // Drops & Equipment & Spells $drops = $config['drops'] ?? []; $index = ($dungeonId - 1) * 5; $drops_eq = array_slice($allItems,$index,5); $drops = array_merge($drops,$drops_eq); foreach ($drops as $drop) { $type = $drop['type'] ?? ''; $rate = $drop['rate'] ?? 20; // 处理任务物品 if ($type === 'quest_item') { $item = Item::createQuestItem($drop['name'] ?? '未知道具'); $this->dropTable[] = [ 'item' => $item, 'rate' => $rate, ]; continue; } if ($type === 'consume') { $spec = $drop; unset($spec['rate']); $item = Item::createConsume($spec, $this->level); $this->dropTable[] = [ 'item' => $item, 'rate' => $rate, ]; continue; } if (in_array($type, ['weapon', 'armor', 'boots', 'ring', 'necklace'])) { if (rand(1, 100) > $rate) continue; $spec = $drop; unset($spec['rate']); $item = Item::createFromSpecWithConfig($spec, $this->level); $this->equip[$type] = $item; } } // 为怪物配置法术 $this->generateSpells($config); } /** * 为怪物随机生成法术(穿着法术,类似装备) * 配置示例 1: 'spells' => [1, 3, 10] - 随机从这些法术ID中选择 * 配置示例 2: 'spells' => [ * ['id' => 1, 'name' => '治愈术'], * ['id' => 10, 'name' => '火焰术'], * ['id' => 30, 'name' => '防御法术'], * ] - 为怪物分配具体的法术(可选指定名称) */ private function generateSpells(array $config): void { $spellConfigs = $config['spells'] ?? []; if (empty($spellConfigs)) { return; } static $spellsData = null; if ($spellsData === null) { $spellsData = require __DIR__ . '/../../src/Data/spells.php'; } // 处理两种配置格式 $selectedSpells = []; // 格式2: 配置数组 [['id' => 1, 'name' => '治愈术', 'rate' => 30]] foreach ($spellConfigs as $spellConfig) { if (is_array($spellConfig) && isset($spellConfig['id'])) { $selectedSpells[] = [ 'id' => $spellConfig['id'], 'name' => $spellConfig['name'] ?? null, 'rate' => $spellConfig['rate'] ?? 30, // 默认30%掉落概率 ]; } } foreach ($selectedSpells as $spellConfig) { $spellId = $spellConfig['id']; $customName = $spellConfig['name'] ?? null; $dropRate = $spellConfig['rate'] ?? 30; if (rand(1, 100) > $dropRate) continue; // 随机决定法术品质:70% 普通, 20% 稀有, 8% 史诗, 2% 传奇 $roll = rand(1, 100); if ($roll <= 70) $quality = 'common'; elseif ($roll <= 90) $quality = 'rare'; elseif ($roll <= 98) $quality = 'epic'; else $quality = 'legendary'; // 创建法术物品 $spell = Item::createSpell($spellId, $quality, $this->level); // 如果提供了自定义名称,则覆盖默认名称 if ($customName) { $spell['name'] = $customName; } foreach ($this->skillSlots as $slot => $content) { if ($content === null) { $this->skillSlots[$slot] = $spell; break; } } } } /** * 随机掉落装备物品(从穿着的装备随机掉落) * @param int $dropRate 掉落概率(0-100) * @return array 掉落的物品列表 */ public function getRandomEquipmentDrops(int $dropRate = 50): array { $drops = []; foreach ($this->equip as $item) { if (!empty($item)) { // 每件装备有独立的掉落概率 if (rand(1, 100) <= $dropRate) { $drops[] = $item; } } } return $drops; } /** * 随机掉落法术(从穿着的法术随机掉落) * 使用每个法术配置的掉落概率,如果未指定则使用默认值 * @param int $defaultRate 默认掉落概率(0-100),当法术未指定rate时使用 * @return array 掉落的法术列表 */ public function getRandomSpellDrops(int $dropRate = 50): array { $drops = []; foreach ($this->skillSlots as $spell) { if (!empty($spell)) { // 每个法术有独立的掉落概率 if (rand(1, 100) <= $dropRate) { $drops[] = $spell; } } } return $drops; } /** * 根据等级和基础属性分配天赋点和权重 * 怪物根据等级获得天赋点,并按基础属性的占比分配权重 */ private function allocateTalentsByLevel(): void { // 每级获得 3 点天赋点(与玩家一致) $talentPoints = ($this->level - 1) * 3; if ($talentPoints <= 0) { return; } // 计算基础属性的权重比(考虑每点天赋的效果) // talentBonus 定义了每点天赋对应的属性增益 $talentBonusMap = [ 'hp' => 10, 'patk' => 5, 'matk' => 4, 'pdef' => 3, 'mdef' => 3, 'crit' => 1, 'critdmg' => 5, ]; // 计算每个属性的权重(基础属性值 / 天赋加成) $weights = [ 'hp' => max(1, (int)($this->baseHp / $talentBonusMap['hp'])), 'patk' => max(1, (int)($this->basePatk / $talentBonusMap['patk'])), 'matk' => max(1, (int)($this->baseMatk / $talentBonusMap['matk'])), 'pdef' => max(1, (int)($this->basePdef / $talentBonusMap['pdef'])), 'mdef' => max(1, (int)($this->baseMdef / $talentBonusMap['mdef'])), 'crit' => max(1, (int)($this->crit / $talentBonusMap['crit'])), 'critdmg' => 0, ]; // dd($weights); // 设置权重 $this->talentWeights = $weights; $this->talentPoints = $talentPoints; // 自动按权重分配天赋点 $this->autoAllocateTalents($talentPoints); } }