hanli/src/Entities/Item.php
2025-12-04 23:52:50 +08:00

374 lines
14 KiB
PHP

<?php
namespace Game\Entities;
/**
* Simple representation of an equipment/consumable item.
*/
class Item
{
public string $name;
public string $type; // weapon, armor, consume
public string $quality; // common, rare, epic, legendary
public int $level = 1; // Item level
private array $affixes;
// Stats
public int $patk = 0;
public int $matk = 0;
public int $pdef = 0;
public int $mdef = 0;
public int $hp = 0;
public int $crit = 0;
public int $critdmg = 0;
public int $heal = 0;
public string $desc = '';
/**
* Generate a random item of the given type and level.
*/
public static function randomItem(string $type, int $level = 1, ?string $specificName = null): array
{
// Load data
static $data = null;
if ($data === null) {
$data = require __DIR__ . '/../../src/Data/items.php';
}
// 品质对照表
$qualityConfig = [
'common' => ['affixes' => 0, 'index' => 0],
'rare' => ['affixes' => 1, 'index' => 1],
'epic' => ['affixes' => 2, 'index' => 2],
'legendary' => ['affixes' => 3, 'index' => 3],
];
// 随机品质
$roll = rand(1, 100);
if ($roll <= 70) $quality = 'common';
elseif ($roll <= 90) $quality = 'rare';
elseif ($roll <= 98) $quality = 'epic';
else $quality = 'legendary';
$item = new self();
$item->type = $type;
$item->quality = $quality;
$item->level = $level;
$affixCount = $qualityConfig[$quality]['affixes'];
$qualityIndex = $qualityConfig[$quality]['index'];
$typeConfig = $data['types'][$type] ?? null;
if (!$typeConfig) {
$item->name = '未知物品';
$item->desc = '未知';
return $item->toArray();
}
$names = $typeConfig['names'] ?? ['未知物品'];
if ($specificName !== null && in_array($specificName, $names)) {
$item->name = $specificName;
} else {
$item->name = $names[array_rand($names)];
}
// 初始化属性
$item->patk = 0;
$item->matk = 0;
$item->pdef = 0;
$item->mdef = 0;
$item->hp = 0;
$item->crit = 0;
$item->critdmg = 0;
if ($type === 'consume') {
$baseStats = $typeConfig['base_stats'] ?? [0,0,0,0];
$growth = $typeConfig['growth'] ?? 0;
$item->heal = $baseStats[$qualityIndex] + ($level * $growth) + rand(0, 10);
$item->desc = "Lv.{$level} {$quality}品质的药剂";
} else {
// 检查是否有特定物品配置
$specificConfig = $typeConfig['specific_config'][$item->name] ?? [];
// 合并配置:优先使用特定配置,否则使用默认配置
$fixedPrimaries = $specificConfig['fixed_primary'] ?? $typeConfig['fixed_primary'] ?? [];
$randomPool = $specificConfig['random_primary_pool'] ?? $typeConfig['random_primary_pool'] ?? [];
$randomCountConfig = $typeConfig['random_primary_count'][$quality] ?? [0, 0];
// 1. 固定主属性
foreach ($fixedPrimaries as $statKey => $statConfig) {
$baseValue = $statConfig['base'][$qualityIndex];
$growth = $statConfig['growth'];
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
$item->$statKey = $finalValue;
}
// 2. 随机主属性
if (!empty($randomPool)) {
[$minCount, $maxCount] = $randomCountConfig;
$randomCount = rand($minCount, $maxCount);
if ($randomCount > 0) {
$selectedRandoms = [];
$availableStats = $randomPool;
for ($i = 0; $i < $randomCount && !empty($availableStats); $i++) {
$weights = [];
foreach ($availableStats as $statKey => $statConfig) {
$weights[$statKey] = $statConfig['weight'];
}
$selectedStat = self::weightedRandom($weights);
$selectedRandoms[] = $selectedStat;
unset($availableStats[$selectedStat]);
}
foreach ($selectedRandoms as $statKey) {
$statConfig = $randomPool[$statKey];
$baseValue = $statConfig['base'][$qualityIndex];
$growth = $statConfig['growth'];
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
$item->$statKey += $finalValue;
}
}
}
$item->desc = "Lv.{$level} {$quality}品质的" . match($type) {
'weapon' => '武器',
'armor' => '防具',
'boots' => '鞋子',
'ring' => '戒指',
'necklace' => '项链',
default => '装备'
};
}
// 3. 词条
$affixNames = $data['affix_definitions'];
$affixWeights = $typeConfig['affix_weights'] ?? [];
$item->affixes = [];
$selectedAffixes = [];
for ($i = 0; $i < $affixCount; $i++) {
if ($type == 'consume') break;
if (empty($affixWeights)) break;
$availableWeights = array_diff_key($affixWeights, array_flip($selectedAffixes));
if (empty($availableWeights)) break;
$key = self::weightedRandom($availableWeights);
$selectedAffixes[] = $key;
$usePercent = (bool)rand(0, 1);
if ($usePercent) {
$base = rand(2, 8);
$v = $base + floor($level / 3);
$item->affixes[] = "{$affixNames[$key]} +{$v}%";
} else {
$qualityMultiplier = match($quality) {
'legendary' => 2.0,
'epic' => 1.5,
'rare' => 1.2,
default => 1.0
};
$base = rand(2, 8);
$multiplier = match($key) {
'patk' => 1.5,
'matk' => 1.5,
'pdef' => 1.0,
'mdef' => 1.0,
'hp' => 8,
'crit' => 0.5,
'critdmg' => 0.8,
default => 1
};
$v = floor(($base + ($level * $multiplier)) * $qualityMultiplier);
$item->affixes[] = "{$affixNames[$key]} +{$v}";
}
}
return $item->toArray();
}
private static function weightedRandom(array $weights): string
{
$totalWeight = array_sum($weights);
$rand = rand(1, $totalWeight);
foreach ($weights as $key => $weight) {
$rand -= $weight;
if ($rand <= 0) return $key;
}
return array_key_first($weights);
}
public static function createFromSpec(array $spec, int $baseLevel): array
{
$type = $spec['type'] ?? 'weapon';
$quality = $spec['quality'] ?? null;
$itemLevel = $spec['level'] ?? $baseLevel;
$affixes = $spec['affixes'] ?? [];
$specificName = $spec['name'] ?? null;
$item = self::randomItem($type, $itemLevel, $specificName);
if ($quality) $item['quality'] = $quality;
if (!empty($affixes)) $item['affixes'] = $affixes;
// Allow overriding specific stats if provided in spec (e.g. from maps.php drops)
foreach (['patk', 'matk', 'pdef', 'mdef', 'hp', 'crit', 'critdmg'] as $stat) {
if (isset($spec[$stat])) {
$item[$stat] = $spec[$stat];
}
}
return $item;
}
public static function createFromSpecWithConfig(array $spec, int $baseLevel): array
{
return self::createFromSpec($spec, $baseLevel);
}
/**
* 创建法术物品 - 支持新的丰富法术系统
* @param int $spellId 法术ID
* @param string $quality 品质 (common, rare, epic, legendary)
* @param int $level 物品等级
* @return array 法术物品数组
*/
public static function createSpell(int $spellId, string $quality = 'common', int $level = 1): array
{
static $spellsData = null;
if ($spellsData === null) {
$spellsData = require __DIR__ . '/../../src/Data/spells.php';
}
// 查找法术信息
$spellInfo = null;
foreach ($spellsData as $category => $spells) {
if (is_array($spells) && !in_array($category, ['quality_levels', 'upgrades', 'dungeon_spell_drops', 'quality_drop_rates', 'spells_by_quality'])) {
if (isset($spells[$spellId])) {
$spellInfo = $spells[$spellId];
break;
}
}
}
if (!$spellInfo) {
// 默认法术
return [
'id' => uniqid('spell_'),
'type' => 'spell',
'name' => '未知法术',
'quality' => $quality,
'level' => $level,
'spellId' => $spellId,
'enhanceLevel' => 0,
'calc_type' => 'matk',
'cost' => 20,
'spellType' => 'damage_single',
'desc' => '未知的法术',
];
}
// 品质映射到数组索引 (common=0, rare=1, epic=2, legendary=3)
$qualityIndex = match($quality) {
'common' => 0,
'rare' => 1,
'epic' => 2,
'legendary' => 3,
default => 0,
};
// 提取品质相关的参数
$healRatio = $spellInfo['heal_ratio'][$qualityIndex] ?? ($spellInfo['heal_ratio'][0] ?? 0);
$damageRatio = $spellInfo['damage_ratio'][$qualityIndex] ?? ($spellInfo['damage_ratio'][0] ?? 1.0);
$healBase = $spellInfo['heal_base'][$qualityIndex] ?? ($spellInfo['heal_base'][0] ?? 0);
$critBonus = $spellInfo['crit_bonus'][$qualityIndex] ?? ($spellInfo['crit_bonus'][0] ?? 0);
$critDmgBonus = $spellInfo['crit_dmg_bonus'][$qualityIndex] ?? ($spellInfo['crit_dmg_bonus'][0] ?? 0);
$enemyCountBonus = $spellInfo['enemy_count_bonus'][$qualityIndex] ?? ($spellInfo['enemy_count_bonus'][0] ?? 0);
$dispersion = $spellInfo['dispersion'][$qualityIndex] ?? ($spellInfo['dispersion'][0] ?? 1.0);
$teamBonus = $spellInfo['team_bonus'][$qualityIndex] ?? ($spellInfo['team_bonus'][0] ?? 0);
$priorityBonus = $spellInfo['priority_bonus'][$qualityIndex] ?? ($spellInfo['priority_bonus'][0] ?? 0);
return [
'id' => uniqid('spell_'),
'type' => 'spell',
'name' => $spellInfo['name'],
'quality' => $quality,
'level' => $level,
'spellId' => $spellId,
'enhanceLevel' => 0,
'calc_type' => $spellInfo['calc_type'] ?? 'matk',
'cost' => $spellInfo['cost'] ?? 20,
'spellType' => $spellInfo['type'] ?? 'damage_single',
'desc' => $spellInfo['desc'] ?? '',
// 品质参数
'heal_ratio' => $healRatio,
'damage_ratio' => $damageRatio,
'heal_base' => $healBase,
'crit_bonus' => $critBonus,
'crit_dmg_bonus' => $critDmgBonus,
'enemy_count_bonus' => $enemyCountBonus,
'dispersion' => $dispersion,
'team_bonus' => $teamBonus,
'priority_bonus' => $priorityBonus,
];
}
public function toArray(): array
{
return [
'name' => $this->name,
'type' => $this->type,
'quality' => $this->quality,
'level' => $this->level,
'patk' => $this->patk,
'matk' => $this->matk,
'pdef' => $this->pdef,
'mdef' => $this->mdef,
'hp' => $this->hp,
'crit' => $this->crit,
'critdmg' => $this->critdmg,
'heal' => $this->heal,
'affixes' => $this->affixes ?? [],
'desc' => $this->desc,
];
}
public static function calculateSellPrice(array $item): int
{
if (($item['type'] ?? '') === 'consume') {
$basePrice = match($item['quality'] ?? 'common') {
'common' => 1, 'rare' => 3, 'epic' => 8, 'legendary' => 20, default => 1
};
return (int)($basePrice + ($item['level'] ?? 1) * 0.5);
}
$basePrice = match($item['quality'] ?? 'common') {
'common' => 5, 'rare' => 15, 'epic' => 40, 'legendary' => 100, default => 5
};
$levelBonus = $basePrice * ($item['level'] ?? 1) * 0.1;
$statBonus = 0;
$statBonus += ($item['patk'] ?? 0) * 0.5;
$statBonus += ($item['matk'] ?? 0) * 0.5;
$statBonus += ($item['pdef'] ?? 0) * 0.5;
$statBonus += ($item['mdef'] ?? 0) * 0.5;
$statBonus += ($item['hp'] ?? 0) * 0.1;
$statBonus += ($item['crit'] ?? 0) * 1.0;
$statBonus += ($item['critdmg'] ?? 0) * 0.3;
$affixCount = count($item['affixes'] ?? []);
$affixBonus = $basePrice * $affixCount * 0.2;
return max(1, (int)($basePrice + $levelBonus + $statBonus + $affixBonus));
}
}