添加怪物群
This commit is contained in:
parent
0c6cbd669a
commit
b4ec385827
|
|
@ -97,7 +97,7 @@ class Game
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->dungeonId = $data['dungeonId'] ?? $this->dungeonId;
|
$this->dungeonId = $data['dungeonId'] ?? $this->dungeonId;
|
||||||
$this->state = $data['state'] ?? $this->state;
|
$this->state = self::MENU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,10 @@ class ItemDisplay
|
||||||
|
|
||||||
// 属性名称
|
// 属性名称
|
||||||
private static array $statNames = [
|
private static array $statNames = [
|
||||||
'atk' => '攻击',
|
'patk' => '物攻',
|
||||||
'def' => '防御',
|
'matk' => '魔攻',
|
||||||
|
'pdef' => '物防',
|
||||||
|
'mdef' => '魔防',
|
||||||
'hp' => '生命',
|
'hp' => '生命',
|
||||||
'crit' => '暴击',
|
'crit' => '暴击',
|
||||||
'critdmg' => '暴伤',
|
'critdmg' => '暴伤',
|
||||||
|
|
@ -296,7 +298,7 @@ class ItemDisplay
|
||||||
|
|
||||||
// 简要属性
|
// 简要属性
|
||||||
$stats = [];
|
$stats = [];
|
||||||
foreach (['atk', 'def', 'hp'] as $key) {
|
foreach (['patk', 'matk', 'pdef', 'mdef', 'hp'] as $key) {
|
||||||
$value = $item[$key] ?? 0;
|
$value = $item[$key] ?? 0;
|
||||||
if ($value > 0) {
|
if ($value > 0) {
|
||||||
$statName = self::$statNames[$key] ?? $key;
|
$statName = self::$statNames[$key] ?? $key;
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
return [
|
return [
|
||||||
// 词条名称定义
|
// 词条名称定义
|
||||||
'affix_definitions' => [
|
'affix_definitions' => [
|
||||||
'atk' => '攻击',
|
'patk' => '物攻',
|
||||||
'def' => '防御',
|
'matk' => '魔攻',
|
||||||
|
'pdef' => '物防',
|
||||||
|
'mdef' => '魔防',
|
||||||
'hp' => '生命值',
|
'hp' => '生命值',
|
||||||
'crit' => '暴击率',
|
'crit' => '暴击率',
|
||||||
'critdmg' => '暴击伤害',
|
'critdmg' => '暴击伤害',
|
||||||
|
|
@ -20,11 +22,52 @@ return [
|
||||||
'weapon' => [
|
'weapon' => [
|
||||||
'names' => ['铁剑', '玄铁剑', '青钢剑', '寒冰剑', '烈焰刀', '雷霆锤'],
|
'names' => ['铁剑', '玄铁剑', '青钢剑', '寒冰剑', '烈焰刀', '雷霆锤'],
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'atk' => ['base' => [5, 12, 22, 38], 'growth' => 1.8],
|
'patk' => ['base' => [4, 10, 18, 30], 'growth' => 1.5],
|
||||||
|
'matk' => ['base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||||
|
],
|
||||||
|
// 特定物品的属性偏向配置 (覆盖默认 fixed_primary)
|
||||||
|
'specific_config' => [
|
||||||
|
'铁剑' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [6, 14, 24, 40], 'growth' => 1.8], // 高物攻
|
||||||
|
'matk' => ['base' => [0, 0, 0, 0], 'growth' => 0.0], // 无魔攻
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'玄铁剑' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [8, 18, 30, 50], 'growth' => 2.0], // 更高物攻
|
||||||
|
'matk' => ['base' => [0, 0, 0, 0], 'growth' => 0.0],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'青钢剑' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [5, 12, 22, 35], 'growth' => 1.6],
|
||||||
|
'matk' => ['base' => [1, 3, 6, 10], 'growth' => 0.5],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'寒冰剑' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [2, 5, 10, 15], 'growth' => 0.8], // 低物攻
|
||||||
|
'matk' => ['base' => [6, 15, 28, 45], 'growth' => 2.0], // 高魔攻
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'烈焰刀' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [4, 10, 18, 30], 'growth' => 1.5], // 均衡
|
||||||
|
'matk' => ['base' => [4, 10, 18, 30], 'growth' => 1.5],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'雷霆锤' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'patk' => ['base' => [7, 16, 28, 45], 'growth' => 1.9],
|
||||||
|
'matk' => ['base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||||
|
]
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'crit' => ['weight' => 50, 'base' => [1, 3, 5, 10], 'growth' => 0.3],
|
'crit' => ['weight' => 40, 'base' => [1, 3, 5, 10], 'growth' => 0.3],
|
||||||
'critdmg' => ['weight' => 50, 'base' => [3, 8, 14, 24], 'growth' => 0.7],
|
'critdmg' => ['weight' => 40, 'base' => [3, 8, 14, 24], 'growth' => 0.7],
|
||||||
|
'matk' => ['weight' => 20, 'base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0],
|
'common' => [0, 0],
|
||||||
|
|
@ -33,18 +76,63 @@ return [
|
||||||
'legendary' => [1, 2],
|
'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => [
|
'affix_weights' => [
|
||||||
'atk' => 40,
|
'patk' => 30,
|
||||||
'crit' => 30,
|
'matk' => 20,
|
||||||
'critdmg' => 30,
|
'crit' => 25,
|
||||||
'hp' => 15,
|
'critdmg' => 25,
|
||||||
'def' => 10,
|
'hp' => 10,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'armor' => [
|
'armor' => [
|
||||||
'names' => ['布衣', '皮甲', '铁甲', '精钢甲', '玄武甲', '龙鳞甲'],
|
'names' => ['布衣', '皮甲', '铁甲', '精钢甲', '玄武甲', '龙鳞甲', '法袍', '灵纹袍'],
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'def' => ['base' => [3, 8, 15, 26], 'growth' => 0.8],
|
'pdef' => ['base' => [2, 6, 12, 20], 'growth' => 0.6],
|
||||||
|
'mdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.5],
|
||||||
|
],
|
||||||
|
'specific_config' => [
|
||||||
|
'布衣' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [1, 3, 6, 10], 'growth' => 0.4],
|
||||||
|
'mdef' => ['base' => [1, 3, 6, 10], 'growth' => 0.4],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'皮甲' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [3, 7, 14, 22], 'growth' => 0.7],
|
||||||
|
'mdef' => ['base' => [1, 2, 4, 8], 'growth' => 0.3],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'铁甲' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [5, 12, 20, 35], 'growth' => 1.0], // 高物防
|
||||||
|
'mdef' => ['base' => [0, 1, 2, 5], 'growth' => 0.2], // 低魔防
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'精钢甲' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [7, 15, 25, 45], 'growth' => 1.2],
|
||||||
|
'mdef' => ['base' => [1, 3, 6, 10], 'growth' => 0.3],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'玄武甲' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [10, 20, 35, 60], 'growth' => 1.5],
|
||||||
|
'mdef' => ['base' => [5, 10, 18, 30], 'growth' => 0.8],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'法袍' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [1, 3, 6, 12], 'growth' => 0.4],
|
||||||
|
'mdef' => ['base' => [5, 12, 20, 35], 'growth' => 1.0], // 高魔防
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'灵纹袍' => [
|
||||||
|
'fixed_primary' => [
|
||||||
|
'pdef' => ['base' => [2, 5, 10, 18], 'growth' => 0.5],
|
||||||
|
'mdef' => ['base' => [8, 18, 30, 50], 'growth' => 1.3], // 极高魔防
|
||||||
|
]
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'hp' => ['weight' => 100, 'base' => [10, 25, 45, 75], 'growth' => 3.5],
|
'hp' => ['weight' => 100, 'base' => [10, 25, 45, 75], 'growth' => 3.5],
|
||||||
|
|
@ -56,18 +144,19 @@ return [
|
||||||
'legendary' => [1, 1],
|
'legendary' => [1, 1],
|
||||||
],
|
],
|
||||||
'affix_weights' => [
|
'affix_weights' => [
|
||||||
'def' => 40,
|
'pdef' => 30,
|
||||||
|
'mdef' => 30,
|
||||||
'hp' => 40,
|
'hp' => 40,
|
||||||
'atk' => 10,
|
'patk' => 5,
|
||||||
'crit' => 15,
|
'matk' => 5,
|
||||||
'critdmg' => 15,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'boots' => [
|
'boots' => [
|
||||||
'names' => ['布鞋', '皮靴', '铁靴', '疾风靴', '幽步靴', '龙鳞靴'],
|
'names' => ['布鞋', '皮靴', '铁靴', '疾风靴', '幽步靴', '龙鳞靴'],
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'def' => ['base' => [2, 5, 10, 18], 'growth' => 0.5],
|
'pdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.4],
|
||||||
|
'mdef' => ['base' => [1, 3, 6, 12], 'growth' => 0.3],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'hp' => ['weight' => 60, 'base' => [8, 20, 35, 60], 'growth' => 2.5],
|
'hp' => ['weight' => 60, 'base' => [8, 20, 35, 60], 'growth' => 2.5],
|
||||||
|
|
@ -80,10 +169,10 @@ return [
|
||||||
'legendary' => [1, 2],
|
'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => [
|
'affix_weights' => [
|
||||||
'def' => 35,
|
'pdef' => 25,
|
||||||
'hp' => 35,
|
'mdef' => 25,
|
||||||
|
'hp' => 30,
|
||||||
'crit' => 20,
|
'crit' => 20,
|
||||||
'critdmg' => 10,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -93,8 +182,9 @@ return [
|
||||||
'crit' => ['base' => [2, 5, 8, 12], 'growth' => 0.4],
|
'crit' => ['base' => [2, 5, 8, 12], 'growth' => 0.4],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'critdmg' => ['weight' => 60, 'base' => [5, 12, 20, 35], 'growth' => 0.8],
|
'critdmg' => ['weight' => 40, 'base' => [5, 12, 20, 35], 'growth' => 0.8],
|
||||||
'atk' => ['weight' => 40, 'base' => [3, 8, 15, 25], 'growth' => 1.0],
|
'patk' => ['weight' => 30, 'base' => [2, 6, 12, 20], 'growth' => 0.8],
|
||||||
|
'matk' => ['weight' => 30, 'base' => [2, 6, 12, 20], 'growth' => 0.8],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0],
|
'common' => [0, 0],
|
||||||
|
|
@ -103,10 +193,10 @@ return [
|
||||||
'legendary' => [1, 2],
|
'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => [
|
'affix_weights' => [
|
||||||
'crit' => 35,
|
'crit' => 30,
|
||||||
'critdmg' => 35,
|
'critdmg' => 30,
|
||||||
'atk' => 20,
|
'patk' => 20,
|
||||||
'hp' => 10,
|
'matk' => 20,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -116,7 +206,8 @@ return [
|
||||||
'hp' => ['base' => [15, 35, 60, 100], 'growth' => 4.0],
|
'hp' => ['base' => [15, 35, 60, 100], 'growth' => 4.0],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'def' => ['weight' => 50, 'base' => [2, 6, 12, 20], 'growth' => 0.6],
|
'pdef' => ['weight' => 25, 'base' => [1, 5, 10, 18], 'growth' => 0.5],
|
||||||
|
'mdef' => ['weight' => 25, 'base' => [1, 5, 10, 18], 'growth' => 0.5],
|
||||||
'critdmg' => ['weight' => 50, 'base' => [3, 8, 15, 25], 'growth' => 0.5],
|
'critdmg' => ['weight' => 50, 'base' => [3, 8, 15, 25], 'growth' => 0.5],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
|
|
@ -126,10 +217,11 @@ return [
|
||||||
'legendary' => [1, 2],
|
'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => [
|
'affix_weights' => [
|
||||||
'hp' => 40,
|
'hp' => 30,
|
||||||
'def' => 30,
|
'pdef' => 20,
|
||||||
'atk' => 15,
|
'mdef' => 20,
|
||||||
'crit' => 15,
|
'crit' => 15,
|
||||||
|
'critdmg' => 15,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,24 @@
|
||||||
// ========== 通用装备模板 ==========
|
// ========== 通用装备模板 ==========
|
||||||
$weaponTemplate = [
|
$weaponTemplate = [
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'atk' => ['base' => [5, 12, 22, 38], 'growth' => 1.8],
|
'patk' => ['base' => [4, 10, 18, 30], 'growth' => 1.5],
|
||||||
|
'matk' => ['base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'crit' => ['weight' => 50, 'base' => [1, 3, 5, 10], 'growth' => 0.3],
|
'crit' => ['weight' => 40, 'base' => [1, 3, 5, 10], 'growth' => 0.3],
|
||||||
'critdmg' => ['weight' => 50, 'base' => [3, 8, 14, 24], 'growth' => 0.7],
|
'critdmg' => ['weight' => 40, 'base' => [3, 8, 14, 24], 'growth' => 0.7],
|
||||||
|
'matk' => ['weight' => 20, 'base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2]
|
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2]
|
||||||
],
|
],
|
||||||
'affix_weights' => ['atk' => 40, 'crit' => 30, 'critdmg' => 30, 'hp' => 15, 'def' => 10],
|
'affix_weights' => ['patk' => 30, 'matk' => 20, 'crit' => 25, 'critdmg' => 25, 'hp' => 10],
|
||||||
];
|
];
|
||||||
|
|
||||||
$armorTemplate = [
|
$armorTemplate = [
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'def' => ['base' => [3, 8, 15, 26], 'growth' => 0.8],
|
'pdef' => ['base' => [2, 6, 12, 20], 'growth' => 0.6],
|
||||||
|
'mdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.5],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'hp' => ['weight' => 100, 'base' => [10, 25, 45, 75], 'growth' => 3.5],
|
'hp' => ['weight' => 100, 'base' => [10, 25, 45, 75], 'growth' => 3.5],
|
||||||
|
|
@ -35,7 +38,7 @@ $armorTemplate = [
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 1],
|
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 1],
|
||||||
],
|
],
|
||||||
'affix_weights' => ['def' => 40, 'hp' => 40, 'atk' => 10, 'crit' => 15, 'critdmg' => 15],
|
'affix_weights' => ['pdef' => 30, 'mdef' => 30, 'hp' => 40, 'patk' => 5, 'matk' => 5],
|
||||||
];
|
];
|
||||||
|
|
||||||
$ringTemplate = [
|
$ringTemplate = [
|
||||||
|
|
@ -43,13 +46,14 @@ $ringTemplate = [
|
||||||
'crit' => ['base' => [2, 5, 8, 12], 'growth' => 0.4],
|
'crit' => ['base' => [2, 5, 8, 12], 'growth' => 0.4],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'critdmg' => ['weight' => 60, 'base' => [5, 12, 20, 35], 'growth' => 0.8],
|
'critdmg' => ['weight' => 40, 'base' => [5, 12, 20, 35], 'growth' => 0.8],
|
||||||
'atk' => ['weight' => 40, 'base' => [3, 8, 15, 25], 'growth' => 1.0],
|
'patk' => ['weight' => 30, 'base' => [2, 6, 12, 20], 'growth' => 0.8],
|
||||||
|
'matk' => ['weight' => 30, 'base' => [2, 6, 12, 20], 'growth' => 0.8],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => ['crit' => 35, 'critdmg' => 35, 'atk' => 20, 'hp' => 10],
|
'affix_weights' => ['crit' => 30, 'critdmg' => 30, 'patk' => 20, 'matk' => 20],
|
||||||
];
|
];
|
||||||
|
|
||||||
$necklaceTemplate = [
|
$necklaceTemplate = [
|
||||||
|
|
@ -57,18 +61,20 @@ $necklaceTemplate = [
|
||||||
'hp' => ['base' => [15, 35, 60, 100], 'growth' => 4.0],
|
'hp' => ['base' => [15, 35, 60, 100], 'growth' => 4.0],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'def' => ['weight' => 50, 'base' => [2, 6, 12, 20], 'growth' => 0.6],
|
'pdef' => ['weight' => 25, 'base' => [1, 5, 10, 18], 'growth' => 0.5],
|
||||||
|
'mdef' => ['weight' => 25, 'base' => [1, 5, 10, 18], 'growth' => 0.5],
|
||||||
'critdmg' => ['weight' => 50, 'base' => [3, 8, 15, 25], 'growth' => 0.5],
|
'critdmg' => ['weight' => 50, 'base' => [3, 8, 15, 25], 'growth' => 0.5],
|
||||||
],
|
],
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => ['hp' => 40, 'def' => 30, 'atk' => 15, 'crit' => 15],
|
'affix_weights' => ['hp' => 30, 'pdef' => 20, 'mdef' => 20, 'crit' => 15, 'critdmg' => 15],
|
||||||
];
|
];
|
||||||
|
|
||||||
$bootsTemplate = [
|
$bootsTemplate = [
|
||||||
'fixed_primary' => [
|
'fixed_primary' => [
|
||||||
'def' => ['base' => [2, 5, 10, 18], 'growth' => 0.5],
|
'pdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.4],
|
||||||
|
'mdef' => ['base' => [1, 3, 6, 12], 'growth' => 0.3],
|
||||||
],
|
],
|
||||||
'random_primary_pool' => [
|
'random_primary_pool' => [
|
||||||
'hp' => ['weight' => 60, 'base' => [8, 20, 35, 60], 'growth' => 2.5],
|
'hp' => ['weight' => 60, 'base' => [8, 20, 35, 60], 'growth' => 2.5],
|
||||||
|
|
@ -77,7 +83,7 @@ $bootsTemplate = [
|
||||||
'random_primary_count' => [
|
'random_primary_count' => [
|
||||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||||
],
|
],
|
||||||
'affix_weights' => ['def' => 35, 'hp' => 35, 'crit' => 20, 'critdmg' => 10],
|
'affix_weights' => ['pdef' => 25, 'mdef' => 25, 'hp' => 30, 'crit' => 20],
|
||||||
];
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -119,6 +125,9 @@ return [
|
||||||
['type' => 'armor', 'name' => '皮甲', 'rate' => 10] + $armorTemplate,
|
['type' => 'armor', 'name' => '皮甲', 'rate' => 10] + $armorTemplate,
|
||||||
['type' => 'consume', 'name' => '黄龙丹', 'rate' => 25, 'heal' => 50],
|
['type' => 'consume', 'name' => '黄龙丹', 'rate' => 25, 'heal' => 50],
|
||||||
],
|
],
|
||||||
|
'minions' => [
|
||||||
|
['name' => '野狼帮帮众', 'hp' => 30, 'patk' => 5, 'matk' => 2, 'pdef' => 0, 'mdef' => 0, 'exp' => 10, 'count' => 2],
|
||||||
|
],
|
||||||
'weight' => 30,
|
'weight' => 30,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
@ -132,10 +141,13 @@ return [
|
||||||
'exp' => 100,
|
'exp' => 100,
|
||||||
'spirit_stones' => 20,
|
'spirit_stones' => 20,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '眨眼剑法', 'quality' => 'rare', 'atk' => 15, 'rate' => 20, 'affixes' => ['crit' => 5]],
|
['type' => 'weapon', 'name' => '眨眼剑法', 'quality' => 'rare', 'patk' => 15, 'rate' => 20, 'affixes' => ['crit' => 5]],
|
||||||
['type' => 'necklace', 'name' => '长生锁', 'rate' => 15] + $necklaceTemplate,
|
['type' => 'necklace', 'name' => '长生锁', 'rate' => 15] + $necklaceTemplate,
|
||||||
['type' => 'consume', 'name' => '清灵散', 'rate' => 40, 'heal' => 80],
|
['type' => 'consume', 'name' => '清灵散', 'rate' => 40, 'heal' => 80],
|
||||||
],
|
],
|
||||||
|
'minions' => [
|
||||||
|
['name' => '铁奴', 'hp' => 80, 'patk' => 12, 'pdef' => 8, 'exp' => 30, 'count' => 1],
|
||||||
|
],
|
||||||
'weight' => 10,
|
'weight' => 10,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -188,7 +200,7 @@ return [
|
||||||
'exp' => 150,
|
'exp' => 150,
|
||||||
'spirit_stones' => 40,
|
'spirit_stones' => 40,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '青叶法器', 'quality' => 'rare', 'atk' => 25, 'rate' => 20],
|
['type' => 'weapon', 'name' => '青叶法器', 'quality' => 'rare', 'matk' => 25, 'rate' => 20],
|
||||||
['type' => 'ring', 'name' => '储物戒', 'rate' => 15] + $ringTemplate,
|
['type' => 'ring', 'name' => '储物戒', 'rate' => 15] + $ringTemplate,
|
||||||
['type' => 'consume', 'name' => '合气丹', 'rate' => 30, 'heal' => 100],
|
['type' => 'consume', 'name' => '合气丹', 'rate' => 30, 'heal' => 100],
|
||||||
],
|
],
|
||||||
|
|
@ -243,8 +255,8 @@ return [
|
||||||
'exp' => 300,
|
'exp' => 300,
|
||||||
'spirit_stones' => 100,
|
'spirit_stones' => 100,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '金竺笔', 'quality' => 'epic', 'atk' => 45, 'rate' => 15, 'affixes' => ['atk' => 10]],
|
['type' => 'weapon', 'name' => '金竺笔', 'quality' => 'epic', 'matk' => 45, 'rate' => 15, 'affixes' => ['matk' => 10]],
|
||||||
['type' => 'armor', 'name' => '墨蛟甲', 'quality' => 'epic', 'def' => 30, 'rate' => 15],
|
['type' => 'armor', 'name' => '墨蛟甲', 'quality' => 'epic', 'pdef' => 20, 'mdef' => 15, 'rate' => 15],
|
||||||
['type' => 'consume', 'name' => '筑基丹', 'rate' => 50, 'heal' => 500],
|
['type' => 'consume', 'name' => '筑基丹', 'rate' => 50, 'heal' => 500],
|
||||||
],
|
],
|
||||||
'weight' => 15,
|
'weight' => 15,
|
||||||
|
|
@ -302,7 +314,7 @@ return [
|
||||||
'exp' => 400,
|
'exp' => 400,
|
||||||
'spirit_stones' => 150,
|
'spirit_stones' => 150,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '烈焰刀', 'quality' => 'epic', 'atk' => 70, 'rate' => 20],
|
['type' => 'weapon', 'name' => '烈焰刀', 'quality' => 'epic', 'patk' => 50, 'matk' => 30, 'rate' => 20],
|
||||||
['type' => 'ring', 'name' => '传音符', 'rate' => 20] + $ringTemplate,
|
['type' => 'ring', 'name' => '传音符', 'rate' => 20] + $ringTemplate,
|
||||||
['type' => 'consume', 'name' => '定颜丹', 'rate' => 10, 'heal' => 800],
|
['type' => 'consume', 'name' => '定颜丹', 'rate' => 10, 'heal' => 800],
|
||||||
],
|
],
|
||||||
|
|
@ -358,8 +370,8 @@ return [
|
||||||
'exp' => 600,
|
'exp' => 600,
|
||||||
'spirit_stones' => 250,
|
'spirit_stones' => 250,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '血灵钻', 'quality' => 'epic', 'atk' => 100, 'rate' => 20, 'affixes' => ['crit' => 10]],
|
['type' => 'weapon', 'name' => '血灵钻', 'quality' => 'epic', 'matk' => 100, 'rate' => 20, 'affixes' => ['crit' => 10]],
|
||||||
['type' => 'armor', 'name' => '血灵甲', 'quality' => 'epic', 'def' => 60, 'rate' => 20],
|
['type' => 'armor', 'name' => '血灵甲', 'quality' => 'epic', 'pdef' => 40, 'mdef' => 30, 'rate' => 20],
|
||||||
['type' => 'consume', 'name' => '血灵丹', 'rate' => 30, 'heal' => 1000],
|
['type' => 'consume', 'name' => '血灵丹', 'rate' => 30, 'heal' => 1000],
|
||||||
],
|
],
|
||||||
'weight' => 15,
|
'weight' => 15,
|
||||||
|
|
@ -414,7 +426,7 @@ return [
|
||||||
'exp' => 1000,
|
'exp' => 1000,
|
||||||
'spirit_stones' => 400,
|
'spirit_stones' => 400,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '青元剑', 'quality' => 'legendary', 'atk' => 150, 'rate' => 15, 'affixes' => ['atk' => 15]],
|
['type' => 'weapon', 'name' => '青元剑', 'quality' => 'legendary', 'patk' => 100, 'matk' => 80, 'rate' => 15, 'affixes' => ['patk' => 15]],
|
||||||
['type' => 'consume', 'name' => '虚天鼎碎片', 'rate' => 10, 'heal' => 2000], // 剧情物品作为高回复药
|
['type' => 'consume', 'name' => '虚天鼎碎片', 'rate' => 10, 'heal' => 2000], // 剧情物品作为高回复药
|
||||||
['type' => 'ring', 'name' => '黑煞戒', 'rate' => 20] + $ringTemplate,
|
['type' => 'ring', 'name' => '黑煞戒', 'rate' => 20] + $ringTemplate,
|
||||||
],
|
],
|
||||||
|
|
@ -473,7 +485,7 @@ return [
|
||||||
'exp' => 1500,
|
'exp' => 1500,
|
||||||
'spirit_stones' => 600,
|
'spirit_stones' => 600,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '引魂钟', 'quality' => 'epic', 'atk' => 200, 'rate' => 20],
|
['type' => 'weapon', 'name' => '引魂钟', 'quality' => 'epic', 'matk' => 200, 'rate' => 20],
|
||||||
['type' => 'boots', 'name' => '踏浪靴', 'rate' => 15] + $bootsTemplate,
|
['type' => 'boots', 'name' => '踏浪靴', 'rate' => 15] + $bootsTemplate,
|
||||||
['type' => 'consume', 'name' => '降尘丹', 'rate' => 25, 'heal' => 1500],
|
['type' => 'consume', 'name' => '降尘丹', 'rate' => 25, 'heal' => 1500],
|
||||||
],
|
],
|
||||||
|
|
@ -529,7 +541,7 @@ return [
|
||||||
'exp' => 2500,
|
'exp' => 2500,
|
||||||
'spirit_stones' => 1000,
|
'spirit_stones' => 1000,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '天都尸火', 'quality' => 'legendary', 'atk' => 300, 'rate' => 15, 'affixes' => ['critdmg' => 20]],
|
['type' => 'weapon', 'name' => '天都尸火', 'quality' => 'legendary', 'matk' => 300, 'rate' => 15, 'affixes' => ['critdmg' => 20]],
|
||||||
['type' => 'consume', 'name' => '补天丹', 'rate' => 10, 'heal' => 3000],
|
['type' => 'consume', 'name' => '补天丹', 'rate' => 10, 'heal' => 3000],
|
||||||
['type' => 'necklace', 'name' => '虚天鼎', 'quality' => 'legendary', 'hp' => 2000, 'rate' => 5],
|
['type' => 'necklace', 'name' => '虚天鼎', 'quality' => 'legendary', 'hp' => 2000, 'rate' => 5],
|
||||||
],
|
],
|
||||||
|
|
@ -585,8 +597,8 @@ return [
|
||||||
'exp' => 4000,
|
'exp' => 4000,
|
||||||
'spirit_stones' => 1500,
|
'spirit_stones' => 1500,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '金蛟剪', 'quality' => 'legendary', 'atk' => 450, 'rate' => 15, 'affixes' => ['crit' => 15]],
|
['type' => 'weapon', 'name' => '金蛟剪', 'quality' => 'legendary', 'patk' => 300, 'matk' => 200, 'rate' => 15, 'affixes' => ['crit' => 15]],
|
||||||
['type' => 'armor', 'name' => '金蛟鳞甲', 'quality' => 'legendary', 'def' => 250, 'rate' => 15],
|
['type' => 'armor', 'name' => '金蛟鳞甲', 'quality' => 'legendary', 'pdef' => 180, 'mdef' => 120, 'rate' => 15],
|
||||||
['type' => 'consume', 'name' => '九曲灵参', 'rate' => 10, 'heal' => 5000],
|
['type' => 'consume', 'name' => '九曲灵参', 'rate' => 10, 'heal' => 5000],
|
||||||
],
|
],
|
||||||
'weight' => 15,
|
'weight' => 15,
|
||||||
|
|
@ -644,7 +656,7 @@ return [
|
||||||
'exp' => 5000,
|
'exp' => 5000,
|
||||||
'spirit_stones' => 2000,
|
'spirit_stones' => 2000,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '落云剑', 'quality' => 'epic', 'atk' => 500, 'rate' => 20],
|
['type' => 'weapon', 'name' => '落云剑', 'quality' => 'epic', 'patk' => 350, 'matk' => 250, 'rate' => 20],
|
||||||
['type' => 'necklace', 'name' => '定魂珠', 'rate' => 15] + $necklaceTemplate,
|
['type' => 'necklace', 'name' => '定魂珠', 'rate' => 15] + $necklaceTemplate,
|
||||||
['type' => 'consume', 'name' => '培婴丹', 'rate' => 25, 'heal' => 3000],
|
['type' => 'consume', 'name' => '培婴丹', 'rate' => 25, 'heal' => 3000],
|
||||||
],
|
],
|
||||||
|
|
@ -700,8 +712,8 @@ return [
|
||||||
'exp' => 10000,
|
'exp' => 10000,
|
||||||
'spirit_stones' => 5000,
|
'spirit_stones' => 5000,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '黑风旗', 'quality' => 'legendary', 'atk' => 800, 'rate' => 15, 'affixes' => ['atk' => 20]],
|
['type' => 'weapon', 'name' => '黑风旗', 'quality' => 'legendary', 'matk' => 800, 'rate' => 15, 'affixes' => ['matk' => 20]],
|
||||||
['type' => 'armor', 'name' => '魔龙甲', 'quality' => 'legendary', 'def' => 500, 'rate' => 15],
|
['type' => 'armor', 'name' => '魔龙甲', 'quality' => 'legendary', 'pdef' => 350, 'mdef' => 250, 'rate' => 15],
|
||||||
['type' => 'consume', 'name' => '万年灵乳', 'rate' => 20, 'heal' => 8000],
|
['type' => 'consume', 'name' => '万年灵乳', 'rate' => 20, 'heal' => 8000],
|
||||||
],
|
],
|
||||||
'weight' => 15,
|
'weight' => 15,
|
||||||
|
|
@ -759,7 +771,7 @@ return [
|
||||||
'exp' => 15000,
|
'exp' => 15000,
|
||||||
'spirit_stones' => 8000,
|
'spirit_stones' => 8000,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '八灵尺', 'quality' => 'legendary', 'atk' => 1200, 'rate' => 15, 'affixes' => ['crit' => 20]],
|
['type' => 'weapon', 'name' => '八灵尺', 'quality' => 'legendary', 'matk' => 1200, 'rate' => 15, 'affixes' => ['crit' => 20]],
|
||||||
['type' => 'ring', 'name' => '雪晶珠', 'quality' => 'legendary', 'crit' => 15, 'rate' => 15],
|
['type' => 'ring', 'name' => '雪晶珠', 'quality' => 'legendary', 'crit' => 15, 'rate' => 15],
|
||||||
['type' => 'consume', 'name' => '回阳水', 'rate' => 10, 'heal' => 10000],
|
['type' => 'consume', 'name' => '回阳水', 'rate' => 10, 'heal' => 10000],
|
||||||
],
|
],
|
||||||
|
|
@ -815,8 +827,8 @@ return [
|
||||||
'exp' => 30000,
|
'exp' => 30000,
|
||||||
'spirit_stones' => 15000,
|
'spirit_stones' => 15000,
|
||||||
'drops' => [
|
'drops' => [
|
||||||
['type' => 'weapon', 'name' => '青竹蜂云剑', 'quality' => 'legendary', 'atk' => 2000, 'rate' => 20, 'affixes' => ['atk' => 30, 'crit' => 20]],
|
['type' => 'weapon', 'name' => '青竹蜂云剑', 'quality' => 'legendary', 'patk' => 1500, 'matk' => 1000, 'rate' => 20, 'affixes' => ['patk' => 30, 'crit' => 20]],
|
||||||
['type' => 'armor', 'name' => '五行甲', 'quality' => 'legendary', 'def' => 1500, 'rate' => 20],
|
['type' => 'armor', 'name' => '五行甲', 'quality' => 'legendary', 'pdef' => 1000, 'mdef' => 1000, 'rate' => 20],
|
||||||
['type' => 'consume', 'name' => '飞升令', 'rate' => 100, 'heal' => 99999], // 象征性物品
|
['type' => 'consume', 'name' => '飞升令', 'rate' => 100, 'heal' => 99999], // 象征性物品
|
||||||
],
|
],
|
||||||
'weight' => 15,
|
'weight' => 15,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ return [
|
||||||
'min_level' => 1,
|
'min_level' => 1,
|
||||||
'desc' => '韩立在七玄门最好的朋友,虽无灵根但武功高强。',
|
'desc' => '韩立在七玄门最好的朋友,虽无灵根但武功高强。',
|
||||||
'base_stats' => [
|
'base_stats' => [
|
||||||
'hp' => 100, 'patk' => 15, 'matk' => 5, 'pdef' => 5, 'mdef' => 3, 'crit' => 10, 'critdmg' => 150, 'growth' => 1.2
|
'hp' => 100, 'patk' => 15, 'matk' => 5, 'pdef' => 5, 'mdef' => 3, 'crit' => 10, 'critdmg' => 130, 'growth' => 1.2
|
||||||
],
|
],
|
||||||
// 天赋权重:武功型,偏攻击和暴击
|
// 天赋权重:武功型,偏攻击和暴击
|
||||||
'talent_weights' => [
|
'talent_weights' => [
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@ namespace Game\Entities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple representation of an equipment/consumable item.
|
* Simple representation of an equipment/consumable item.
|
||||||
*
|
|
||||||
* The game only needs an associative array for storage, but having a class
|
|
||||||
* makes random generation easier and keeps the logic tidy.
|
|
||||||
*/
|
*/
|
||||||
class Item
|
class Item
|
||||||
{
|
{
|
||||||
|
|
@ -15,12 +12,19 @@ class Item
|
||||||
public int $level = 1; // Item level
|
public int $level = 1; // Item level
|
||||||
private array $affixes;
|
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.
|
* Generate a random item of the given type and level.
|
||||||
* @param string $type Item type (weapon, armor, etc.)
|
|
||||||
* @param int $level Item level
|
|
||||||
* @param string|null $specificName If provided, use this name instead of random selection
|
|
||||||
* @return array Item data array
|
|
||||||
*/
|
*/
|
||||||
public static function randomItem(string $type, int $level = 1, ?string $specificName = null): array
|
public static function randomItem(string $type, int $level = 1, ?string $specificName = null): array
|
||||||
{
|
{
|
||||||
|
|
@ -30,323 +34,7 @@ class Item
|
||||||
$data = require __DIR__ . '/../../src/Data/items.php';
|
$data = require __DIR__ . '/../../src/Data/items.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 品质对照表:affix 数量和索引
|
// 品质对照表
|
||||||
$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'; // 70% 普通
|
|
||||||
elseif ($roll <= 90) $quality = 'rare'; // 20% 稀有
|
|
||||||
elseif ($roll <= 98) $quality = 'epic'; // 8% 史诗
|
|
||||||
else $quality = 'legendary'; // 2% 传说
|
|
||||||
|
|
||||||
$item = new self();
|
|
||||||
$item->type = $type;
|
|
||||||
$item->quality = $quality;
|
|
||||||
$item->level = $level;
|
|
||||||
|
|
||||||
// 获取品质对应的词条数量和属性索引
|
|
||||||
$affixCount = $qualityConfig[$quality]['affixes'];
|
|
||||||
$qualityIndex = $qualityConfig[$quality]['index'];
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ① 基础名称与基础属性 (Scaling with Level)
|
|
||||||
// ===========================
|
|
||||||
|
|
||||||
$typeConfig = $data['types'][$type] ?? null;
|
|
||||||
|
|
||||||
if (!$typeConfig) {
|
|
||||||
$item->name = '未知物品';
|
|
||||||
$item->desc = '未知';
|
|
||||||
return $item->toArray(); // Helper method needed or manual array return
|
|
||||||
}
|
|
||||||
|
|
||||||
$names = $typeConfig['names'] ?? ['未知物品'];
|
|
||||||
|
|
||||||
// Use specific name if provided, otherwise random selection
|
|
||||||
if ($specificName !== null && in_array($specificName, $names)) {
|
|
||||||
$item->name = $specificName;
|
|
||||||
} else {
|
|
||||||
$item->name = $names[array_rand($names)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ① 生成主属性(固定 + 随机)
|
|
||||||
// ===========================
|
|
||||||
|
|
||||||
// 初始化所有可能的属性为0
|
|
||||||
$item->atk = 0;
|
|
||||||
$item->def = 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 {
|
|
||||||
// 装备:固定主属性 + 随机主属性
|
|
||||||
$fixedPrimaries = $typeConfig['fixed_primary'] ?? [];
|
|
||||||
$randomPool = $typeConfig['random_primary_pool'] ?? [];
|
|
||||||
$randomCountConfig = $typeConfig['random_primary_count'][$quality] ?? [0, 0];
|
|
||||||
|
|
||||||
// Step 1: 生成固定主属性(必定存在)
|
|
||||||
foreach ($fixedPrimaries as $statKey => $statConfig) {
|
|
||||||
$baseValue = $statConfig['base'][$qualityIndex];
|
|
||||||
$growth = $statConfig['growth'];
|
|
||||||
|
|
||||||
// 主属性数值 = 基础值 + 等级成长 + 随机波动
|
|
||||||
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15))); // 15%随机波动
|
|
||||||
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
|
|
||||||
|
|
||||||
// 赋值到对应属性
|
|
||||||
switch ($statKey) {
|
|
||||||
case 'atk':
|
|
||||||
$item->atk = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'def':
|
|
||||||
$item->def = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'hp':
|
|
||||||
$item->hp = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'crit':
|
|
||||||
$item->crit = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'critdmg':
|
|
||||||
$item->critdmg = $finalValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 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))); // 15%随机波动
|
|
||||||
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
|
|
||||||
|
|
||||||
// 赋值到对应属性
|
|
||||||
switch ($statKey) {
|
|
||||||
case 'atk':
|
|
||||||
$item->atk = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'def':
|
|
||||||
$item->def = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'hp':
|
|
||||||
$item->hp = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'crit':
|
|
||||||
$item->crit = $finalValue;
|
|
||||||
break;
|
|
||||||
case 'critdmg':
|
|
||||||
$item->critdmg = $finalValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置描述
|
|
||||||
$item->desc = "Lv.{$level} {$quality}品质的" . match($type) {
|
|
||||||
'weapon' => '武器',
|
|
||||||
'armor' => '防具',
|
|
||||||
'ring' => '戒指',
|
|
||||||
'necklace' => '项链',
|
|
||||||
default => '装备'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ② 词条池(固定值 + 百分比)
|
|
||||||
// ===========================
|
|
||||||
|
|
||||||
$affixNames = $data['affix_definitions'];
|
|
||||||
$affixWeights = $typeConfig['affix_weights'] ?? [];
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ③ 随机 affix(使用权重系统,避免重复)
|
|
||||||
// ===========================
|
|
||||||
$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;
|
|
||||||
|
|
||||||
// 固定 or 百分比?
|
|
||||||
$usePercent = (bool)rand(0, 1);
|
|
||||||
|
|
||||||
if ($usePercent) {
|
|
||||||
// Percentages scale slower but better for higher levels
|
|
||||||
$base = rand(2, 8); // 提高百分比基础值
|
|
||||||
$v = $base + floor($level / 3); // 每3级增加1%
|
|
||||||
$item->affixes[] = "{$affixNames[$key]} +{$v}%";
|
|
||||||
} else {
|
|
||||||
// Flat values scale with level and quality
|
|
||||||
$qualityMultiplier = match($quality) {
|
|
||||||
'legendary' => 2.0,
|
|
||||||
'epic' => 1.5,
|
|
||||||
'rare' => 1.2,
|
|
||||||
default => 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
$base = rand(2, 8);
|
|
||||||
$multiplier = match($key) {
|
|
||||||
'atk' => 1.5,
|
|
||||||
'def' => 1.0,
|
|
||||||
'hp' => 8,
|
|
||||||
'crit' => 0.5,
|
|
||||||
'critdmg' => 0.8,
|
|
||||||
default => 1
|
|
||||||
};
|
|
||||||
$v = floor(($base + ($level * $multiplier)) * $qualityMultiplier);
|
|
||||||
$item->affixes[] = "{$affixNames[$key]} +{$v}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ④ 返回数组格式
|
|
||||||
// ===========================
|
|
||||||
return [
|
|
||||||
'name' => $item->name,
|
|
||||||
'type' => $item->type,
|
|
||||||
'quality' => $item->quality,
|
|
||||||
'level' => $item->level,
|
|
||||||
'atk' => $item->atk ?? 0,
|
|
||||||
'def' => $item->def ?? 0,
|
|
||||||
'hp' => $item->hp ?? 0,
|
|
||||||
'crit' => $item->crit ?? 0,
|
|
||||||
'critdmg' => $item->critdmg ?? 0,
|
|
||||||
'heal' => $item->heal ?? 0,
|
|
||||||
'affixes' => $item->affixes ?? [],
|
|
||||||
'desc' => $item->desc,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Weighted random selection helper
|
|
||||||
* @param array $weights Associative array of key => weight
|
|
||||||
* @return string Selected key
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to first key
|
|
||||||
return array_key_first($weights);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// ⑤ 生成特定物品(用于掉落)
|
|
||||||
// ===========================
|
|
||||||
/**
|
|
||||||
* Generate a specific item based on a configuration array.
|
|
||||||
* Expected keys:
|
|
||||||
* - type: Item type (weapon, armor, etc.)
|
|
||||||
* - name (optional): Specific item name (e.g., '铁剑'). If provided, this exact item will be generated with random quality/affixes
|
|
||||||
* - quality (optional): Force specific quality (overrides random quality)
|
|
||||||
* - level (optional): Item level
|
|
||||||
* - affixes (optional): Force specific affixes (overrides random affixes)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Load data definitions
|
|
||||||
static $data = null;
|
|
||||||
if ($data === null) {
|
|
||||||
$data = require __DIR__ . '/../../src/Data/items.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate item with specific name (if provided)
|
|
||||||
$item = self::randomItem($type, $itemLevel, $specificName);
|
|
||||||
|
|
||||||
// Override quality if specified (otherwise uses random quality from randomItem)
|
|
||||||
if ($quality) {
|
|
||||||
$item['quality'] = $quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override affixes if specified (otherwise uses random affixes from randomItem)
|
|
||||||
if (!empty($affixes)) {
|
|
||||||
$item['affixes'] = $affixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用详细配置生成装备(支持 maps.php 中的 fixed_primary, random_primary_pool 等)
|
|
||||||
* @param array $spec 掉落配置
|
|
||||||
* @param int $baseLevel 基础等级
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function createFromSpecWithConfig(array $spec, int $baseLevel): array
|
|
||||||
{
|
|
||||||
$type = $spec['type'] ?? 'weapon';
|
|
||||||
$name = $spec['name'] ?? '未知装备';
|
|
||||||
$itemLevel = $spec['level'] ?? $baseLevel;
|
|
||||||
|
|
||||||
// 品质配置
|
|
||||||
$qualityConfig = [
|
$qualityConfig = [
|
||||||
'common' => ['affixes' => 0, 'index' => 0],
|
'common' => ['affixes' => 0, 'index' => 0],
|
||||||
'rare' => ['affixes' => 1, 'index' => 1],
|
'rare' => ['affixes' => 1, 'index' => 1],
|
||||||
|
|
@ -361,61 +49,76 @@ class Item
|
||||||
elseif ($roll <= 98) $quality = 'epic';
|
elseif ($roll <= 98) $quality = 'epic';
|
||||||
else $quality = 'legendary';
|
else $quality = 'legendary';
|
||||||
|
|
||||||
// 如果配置指定了品质,则使用指定品质
|
$item = new self();
|
||||||
if (isset($spec['quality'])) {
|
$item->type = $type;
|
||||||
$quality = $spec['quality'];
|
$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();
|
||||||
}
|
}
|
||||||
|
|
||||||
$qualityIndex = $qualityConfig[$quality]['index'];
|
$names = $typeConfig['names'] ?? ['未知物品'];
|
||||||
$affixCount = $qualityConfig[$quality]['affixes'];
|
|
||||||
|
if ($specificName !== null && in_array($specificName, $names)) {
|
||||||
|
$item->name = $specificName;
|
||||||
|
} else {
|
||||||
|
$item->name = $names[array_rand($names)];
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化属性
|
// 初始化属性
|
||||||
$item = [
|
$item->patk = 0;
|
||||||
'name' => $name,
|
$item->matk = 0;
|
||||||
'type' => $type,
|
$item->pdef = 0;
|
||||||
'quality' => $quality,
|
$item->mdef = 0;
|
||||||
'level' => $itemLevel,
|
$item->hp = 0;
|
||||||
'atk' => 0,
|
$item->crit = 0;
|
||||||
'def' => 0,
|
$item->critdmg = 0;
|
||||||
'hp' => 0,
|
|
||||||
'crit' => 0,
|
|
||||||
'critdmg' => 0,
|
|
||||||
'heal' => 0,
|
|
||||||
'affixes' => [],
|
|
||||||
'desc' => "Lv.{$itemLevel} {$quality}品质",
|
|
||||||
];
|
|
||||||
|
|
||||||
// ===== 处理固定主属性 =====
|
if ($type === 'consume') {
|
||||||
$fixedPrimary = $spec['fixed_primary'] ?? [];
|
$baseStats = $typeConfig['base_stats'] ?? [0,0,0,0];
|
||||||
foreach ($fixedPrimary as $statKey => $statConfig) {
|
$growth = $typeConfig['growth'] ?? 0;
|
||||||
$baseValues = $statConfig['base'] ?? [0, 0, 0, 0];
|
$item->heal = $baseStats[$qualityIndex] + ($level * $growth) + rand(0, 10);
|
||||||
$growth = $statConfig['growth'] ?? 0;
|
$item->desc = "Lv.{$level} {$quality}品质的药剂";
|
||||||
|
} else {
|
||||||
|
// 检查是否有特定物品配置
|
||||||
|
$specificConfig = $typeConfig['specific_config'][$item->name] ?? [];
|
||||||
|
|
||||||
$baseValue = $baseValues[$qualityIndex] ?? $baseValues[0];
|
// 合并配置:优先使用特定配置,否则使用默认配置
|
||||||
|
$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)));
|
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
|
||||||
$finalValue = (int)($baseValue + ($itemLevel * $growth) + $randomBonus);
|
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
|
||||||
|
$item->$statKey = $finalValue;
|
||||||
$item[$statKey] = $finalValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 处理随机主属性 =====
|
// 2. 随机主属性
|
||||||
$randomPool = $spec['random_primary_pool'] ?? [];
|
|
||||||
$randomCountConfig = $spec['random_primary_count'][$quality] ?? [0, 0];
|
|
||||||
|
|
||||||
if (!empty($randomPool)) {
|
if (!empty($randomPool)) {
|
||||||
[$minCount, $maxCount] = $randomCountConfig;
|
[$minCount, $maxCount] = $randomCountConfig;
|
||||||
$randomCount = rand($minCount, $maxCount);
|
$randomCount = rand($minCount, $maxCount);
|
||||||
|
|
||||||
if ($randomCount > 0) {
|
if ($randomCount > 0) {
|
||||||
$availableStats = $randomPool;
|
|
||||||
$selectedRandoms = [];
|
$selectedRandoms = [];
|
||||||
|
$availableStats = $randomPool;
|
||||||
|
|
||||||
for ($i = 0; $i < $randomCount && !empty($availableStats); $i++) {
|
for ($i = 0; $i < $randomCount && !empty($availableStats); $i++) {
|
||||||
$weights = [];
|
$weights = [];
|
||||||
foreach ($availableStats as $statKey => $statConfig) {
|
foreach ($availableStats as $statKey => $statConfig) {
|
||||||
$weights[$statKey] = $statConfig['weight'] ?? 50;
|
$weights[$statKey] = $statConfig['weight'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$selectedStat = self::weightedRandom($weights);
|
$selectedStat = self::weightedRandom($weights);
|
||||||
$selectedRandoms[] = $selectedStat;
|
$selectedRandoms[] = $selectedStat;
|
||||||
unset($availableStats[$selectedStat]);
|
unset($availableStats[$selectedStat]);
|
||||||
|
|
@ -423,42 +126,33 @@ class Item
|
||||||
|
|
||||||
foreach ($selectedRandoms as $statKey) {
|
foreach ($selectedRandoms as $statKey) {
|
||||||
$statConfig = $randomPool[$statKey];
|
$statConfig = $randomPool[$statKey];
|
||||||
$baseValues = $statConfig['base'] ?? [0, 0, 0, 0];
|
$baseValue = $statConfig['base'][$qualityIndex];
|
||||||
$growth = $statConfig['growth'] ?? 0;
|
$growth = $statConfig['growth'];
|
||||||
|
|
||||||
$baseValue = $baseValues[$qualityIndex] ?? $baseValues[0];
|
|
||||||
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
|
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
|
||||||
$finalValue = (int)($baseValue + ($itemLevel * $growth) + $randomBonus);
|
$finalValue = (int)($baseValue + ($level * $growth) + $randomBonus);
|
||||||
|
$item->$statKey += $finalValue;
|
||||||
$item[$statKey] = ($item[$statKey] ?? 0) + $finalValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 生成词条 =====
|
$item->desc = "Lv.{$level} {$quality}品质的" . match($type) {
|
||||||
static $data = null;
|
'weapon' => '武器',
|
||||||
if ($data === null) {
|
'armor' => '防具',
|
||||||
$data = require __DIR__ . '/../../src/Data/items.php';
|
'boots' => '鞋子',
|
||||||
|
'ring' => '戒指',
|
||||||
|
'necklace' => '项链',
|
||||||
|
default => '装备'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$affixNames = $data['affix_definitions'] ?? [
|
// 3. 词条
|
||||||
'atk' => '攻击',
|
$affixNames = $data['affix_definitions'];
|
||||||
'def' => '防御',
|
$affixWeights = $typeConfig['affix_weights'] ?? [];
|
||||||
'hp' => '生命值',
|
$item->affixes = [];
|
||||||
'crit' => '暴击率',
|
|
||||||
'critdmg' => '暴击伤害',
|
|
||||||
];
|
|
||||||
|
|
||||||
$affixWeights = $spec['affix_weights'] ?? [
|
|
||||||
'atk' => 25,
|
|
||||||
'def' => 25,
|
|
||||||
'hp' => 25,
|
|
||||||
'crit' => 15,
|
|
||||||
'critdmg' => 10,
|
|
||||||
];
|
|
||||||
|
|
||||||
$selectedAffixes = [];
|
$selectedAffixes = [];
|
||||||
for ($i = 0; $i < $affixCount; $i++) {
|
for ($i = 0; $i < $affixCount; $i++) {
|
||||||
|
if ($type == 'consume') break;
|
||||||
if (empty($affixWeights)) break;
|
if (empty($affixWeights)) break;
|
||||||
|
|
||||||
$availableWeights = array_diff_key($affixWeights, array_flip($selectedAffixes));
|
$availableWeights = array_diff_key($affixWeights, array_flip($selectedAffixes));
|
||||||
|
|
@ -471,8 +165,8 @@ class Item
|
||||||
|
|
||||||
if ($usePercent) {
|
if ($usePercent) {
|
||||||
$base = rand(2, 8);
|
$base = rand(2, 8);
|
||||||
$v = $base + floor($itemLevel / 3);
|
$v = $base + floor($level / 3);
|
||||||
$item['affixes'][] = "{$affixNames[$key]} +{$v}%";
|
$item->affixes[] = "{$affixNames[$key]} +{$v}%";
|
||||||
} else {
|
} else {
|
||||||
$qualityMultiplier = match($quality) {
|
$qualityMultiplier = match($quality) {
|
||||||
'legendary' => 2.0,
|
'legendary' => 2.0,
|
||||||
|
|
@ -483,76 +177,109 @@ class Item
|
||||||
|
|
||||||
$base = rand(2, 8);
|
$base = rand(2, 8);
|
||||||
$multiplier = match($key) {
|
$multiplier = match($key) {
|
||||||
'atk' => 1.5,
|
'patk' => 1.5,
|
||||||
'def' => 1.0,
|
'matk' => 1.5,
|
||||||
|
'pdef' => 1.0,
|
||||||
|
'mdef' => 1.0,
|
||||||
'hp' => 8,
|
'hp' => 8,
|
||||||
'crit' => 0.5,
|
'crit' => 0.5,
|
||||||
'critdmg' => 0.8,
|
'critdmg' => 0.8,
|
||||||
default => 1
|
default => 1
|
||||||
};
|
};
|
||||||
$v = floor(($base + ($itemLevel * $multiplier)) * $qualityMultiplier);
|
$v = floor(($base + ($level * $multiplier)) * $qualityMultiplier);
|
||||||
$item['affixes'][] = "{$affixNames[$key]} +{$v}";
|
$item->affixes[] = "{$affixNames[$key]} +{$v}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置描述
|
return $item->toArray();
|
||||||
$item['desc'] = "Lv.{$itemLevel} {$quality}品质的" . match($type) {
|
}
|
||||||
'weapon' => '武器',
|
|
||||||
'armor' => '防具',
|
private static function weightedRandom(array $weights): string
|
||||||
'boots' => '鞋子',
|
{
|
||||||
'ring' => '戒指',
|
$totalWeight = array_sum($weights);
|
||||||
'necklace' => '项链',
|
$rand = rand(1, $totalWeight);
|
||||||
default => '装备'
|
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;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function createFromSpecWithConfig(array $spec, int $baseLevel): array
|
||||||
* 计算物品的售价(灵石)
|
{
|
||||||
* 基于品质、等级、属性数量计算
|
return self::createFromSpec($spec, $baseLevel);
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
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
|
public static function calculateSellPrice(array $item): int
|
||||||
{
|
{
|
||||||
// 消耗品售价低
|
if (($item['type'] ?? '') === 'consume') {
|
||||||
if ($item['type'] === 'consume') {
|
$basePrice = match($item['quality'] ?? 'common') {
|
||||||
$basePrice = match($item['quality']) {
|
'common' => 1, 'rare' => 3, 'epic' => 8, 'legendary' => 20, default => 1
|
||||||
'common' => 1,
|
|
||||||
'rare' => 3,
|
|
||||||
'epic' => 8,
|
|
||||||
'legendary' => 20,
|
|
||||||
default => 1
|
|
||||||
};
|
};
|
||||||
return (int)($basePrice + ($item['level'] ?? 1) * 0.5);
|
return (int)($basePrice + ($item['level'] ?? 1) * 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 装备基础价格(根据品质)
|
$basePrice = match($item['quality'] ?? 'common') {
|
||||||
$basePrice = match($item['quality']) {
|
'common' => 5, 'rare' => 15, 'epic' => 40, 'legendary' => 100, default => 5
|
||||||
'common' => 5,
|
|
||||||
'rare' => 15,
|
|
||||||
'epic' => 40,
|
|
||||||
'legendary' => 100,
|
|
||||||
default => 5
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 等级加成(每级增加基础价格的10%)
|
|
||||||
$levelBonus = $basePrice * ($item['level'] ?? 1) * 0.1;
|
$levelBonus = $basePrice * ($item['level'] ?? 1) * 0.1;
|
||||||
|
|
||||||
// 主属性加成
|
|
||||||
$statBonus = 0;
|
$statBonus = 0;
|
||||||
$statBonus += ($item['atk'] ?? 0) * 0.5;
|
$statBonus += ($item['patk'] ?? 0) * 0.5;
|
||||||
$statBonus += ($item['def'] ?? 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['hp'] ?? 0) * 0.1;
|
||||||
$statBonus += ($item['crit'] ?? 0) * 1.0;
|
$statBonus += ($item['crit'] ?? 0) * 1.0;
|
||||||
$statBonus += ($item['critdmg'] ?? 0) * 0.3;
|
$statBonus += ($item['critdmg'] ?? 0) * 0.3;
|
||||||
|
|
||||||
// 词条加成(每个词条增加20%)
|
|
||||||
$affixCount = count($item['affixes'] ?? []);
|
$affixCount = count($item['affixes'] ?? []);
|
||||||
$affixBonus = $basePrice * $affixCount * 0.2;
|
$affixBonus = $basePrice * $affixCount * 0.2;
|
||||||
|
|
||||||
$totalPrice = $basePrice + $levelBonus + $statBonus + $affixBonus;
|
return max(1, (int)($basePrice + $levelBonus + $statBonus + $affixBonus));
|
||||||
|
|
||||||
return max(1, (int)$totalPrice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,62 +64,116 @@ class Monster
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Hydrate monster base stats from maps.php
|
// 3. Hydrate monster base stats from maps.php
|
||||||
$monster->name = $selectedMonster['name'];
|
$monster->hydrateFromConfig($selectedMonster);
|
||||||
$monster->level = $selectedMonster['level'];
|
|
||||||
$monster->baseHp = $selectedMonster['hp'];
|
|
||||||
$monster->hp = $selectedMonster['hp'];
|
|
||||||
$monster->basePatk = $selectedMonster['patk'] ?? $selectedMonster['atk'] ?? 4;
|
|
||||||
$monster->patk = $monster->basePatk;
|
|
||||||
$monster->baseMatk = $selectedMonster['matk'] ?? 2;
|
|
||||||
$monster->matk = $monster->baseMatk;
|
|
||||||
$monster->basePdef = $selectedMonster['pdef'] ?? $selectedMonster['def'] ?? 0;
|
|
||||||
$monster->pdef = $monster->basePdef;
|
|
||||||
$monster->baseMdef = $selectedMonster['mdef'] ?? 0;
|
|
||||||
$monster->mdef = $monster->baseMdef;
|
|
||||||
$monster->crit = $selectedMonster['crit'] ?? 5;
|
|
||||||
$monster->critdmg = $selectedMonster['critdmg'] ?? 150;
|
|
||||||
$monster->expReward = $selectedMonster['exp'];
|
|
||||||
$monster->spiritStoneReward = $selectedMonster['spirit_stones'] ?? 0;
|
|
||||||
|
|
||||||
// 4. Generate equipment from drops and equip them
|
return $monster;
|
||||||
$drops = $selectedMonster['drops'] ?? [];
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Monster[]
|
||||||
|
*/
|
||||||
|
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 = new self();
|
||||||
|
$monster->hydrateFromConfig($selectedConfig);
|
||||||
|
|
||||||
|
// 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): void
|
||||||
|
{
|
||||||
|
$this->name = $config['name'];
|
||||||
|
$this->level = $config['level'] ?? 1;
|
||||||
|
$this->baseHp = $config['hp'] ?? 20;
|
||||||
|
$this->hp = $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'] ?? 150;
|
||||||
|
$this->expReward = $config['exp'] ?? 0;
|
||||||
|
$this->spiritStoneReward = $config['spirit_stones'] ?? 0;
|
||||||
|
|
||||||
|
// Drops & Equipment
|
||||||
|
$drops = $config['drops'] ?? [];
|
||||||
foreach ($drops as $drop) {
|
foreach ($drops as $drop) {
|
||||||
$type = $drop['type'] ?? '';
|
$type = $drop['type'] ?? '';
|
||||||
$rate = $drop['rate'] ?? 0;
|
$rate = $drop['rate'] ?? 0;
|
||||||
|
|
||||||
// 消耗品放入掉落表,不装备
|
|
||||||
if ($type === 'consume') {
|
if ($type === 'consume') {
|
||||||
$spec = $drop;
|
$spec = $drop;
|
||||||
unset($spec['rate']);
|
unset($spec['rate']);
|
||||||
$item = Item::createFromSpec($spec, $monster->level);
|
$item = Item::createFromSpec($spec, $this->level);
|
||||||
$monster->dropTable[] = [
|
$this->dropTable[] = [
|
||||||
'item' => $item,
|
'item' => $item,
|
||||||
'rate' => $rate,
|
'rate' => $rate,
|
||||||
];
|
];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 装备类:生成并装备
|
|
||||||
if (in_array($type, ['weapon', 'armor', 'boots', 'ring', 'necklace'])) {
|
if (in_array($type, ['weapon', 'armor', 'boots', 'ring', 'necklace'])) {
|
||||||
// 先判断是否生成这件装备(使用掉落率)
|
if (rand(1, 100) > $rate) continue;
|
||||||
if (rand(1, 100) > $rate) {
|
|
||||||
continue; // 没有掉落这件装备
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用掉落配置生成装备
|
|
||||||
$spec = $drop;
|
$spec = $drop;
|
||||||
unset($spec['rate']);
|
unset($spec['rate']);
|
||||||
$item = Item::createFromSpecWithConfig($spec, $monster->level);
|
$item = Item::createFromSpecWithConfig($spec, $this->level);
|
||||||
|
$this->equip[$type] = $item;
|
||||||
// 装备到对应槽位(覆盖已有的)
|
|
||||||
$monster->equip[$type] = $item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Apply equipment stats to monster
|
$this->applyEquipmentStats();
|
||||||
$monster->applyEquipmentStats();
|
|
||||||
|
|
||||||
return $monster;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ class Partner
|
||||||
public int $level = 1;
|
public int $level = 1;
|
||||||
public int $exp = 0;
|
public int $exp = 0;
|
||||||
public int $maxExp = 100;
|
public int $maxExp = 100;
|
||||||
|
public int $hp = 100; // 当前血量
|
||||||
|
|
||||||
public array $baseStats = [];
|
public array $baseStats = [];
|
||||||
public array $equip = []; // weapon, armor, ring, boots, necklace
|
public array $equip = []; // weapon, armor, ring, boots, necklace
|
||||||
|
|
@ -56,6 +57,10 @@ class Partner
|
||||||
$this->equip = $data['equip'] ?? [];
|
$this->equip = $data['equip'] ?? [];
|
||||||
$this->talents = $data['talents'] ?? $this->talents;
|
$this->talents = $data['talents'] ?? $this->talents;
|
||||||
$this->talentWeights = $data['talentWeights'] ?? $this->talentWeights;
|
$this->talentWeights = $data['talentWeights'] ?? $this->talentWeights;
|
||||||
|
|
||||||
|
// 设置当前血量为最大血量
|
||||||
|
$stats = $this->getStats();
|
||||||
|
$this->hp = $data['hp'] ?? $stats['maxHp'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -166,6 +171,7 @@ class Partner
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据权重自动分配天赋点
|
* 根据权重自动分配天赋点
|
||||||
|
* 重要: HP(生命值)总是必须至少加1点
|
||||||
*/
|
*/
|
||||||
private function autoAllocateTalent(int $points): void
|
private function autoAllocateTalent(int $points): void
|
||||||
{
|
{
|
||||||
|
|
@ -184,6 +190,12 @@ class Partner
|
||||||
$remaining -= $share;
|
$remaining -= $share;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关键: 确保 HP 至少获得1点(HP是必须的)
|
||||||
|
if ($allocated['hp'] < 1) {
|
||||||
|
$allocated['hp'] = 1;
|
||||||
|
$remaining--;
|
||||||
|
}
|
||||||
|
|
||||||
// 剩余点数按权重优先分配
|
// 剩余点数按权重优先分配
|
||||||
$sortedTalents = $this->talentWeights;
|
$sortedTalents = $this->talentWeights;
|
||||||
arsort($sortedTalents);
|
arsort($sortedTalents);
|
||||||
|
|
@ -223,4 +235,18 @@ class Partner
|
||||||
{
|
{
|
||||||
return array_sum($this->talents);
|
return array_sum($this->talents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复队友血量
|
||||||
|
*/
|
||||||
|
public function heal(int $amount): int
|
||||||
|
{
|
||||||
|
$stats = $this->getStats();
|
||||||
|
$maxHp = $stats['maxHp'];
|
||||||
|
|
||||||
|
$oldHp = $this->hp;
|
||||||
|
$this->hp = min($this->hp + $amount, $maxHp);
|
||||||
|
|
||||||
|
return $this->hp - $oldHp; // 返回实际恢复的血量
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ use Game\Entities\Partner;
|
||||||
class Battle
|
class Battle
|
||||||
{
|
{
|
||||||
public Player $player;
|
public Player $player;
|
||||||
public Monster $monster;
|
/** @var Monster[] */
|
||||||
|
public array $enemies = [];
|
||||||
|
|
||||||
/** @var array<string, int> 同伴当前HP */
|
/** @var array<string, int> 同伴当前HP */
|
||||||
private array $partnerHp = [];
|
private array $partnerHp = [];
|
||||||
|
|
@ -35,7 +36,7 @@ class Battle
|
||||||
private string $reset = "\033[0m";
|
private string $reset = "\033[0m";
|
||||||
|
|
||||||
private int $round = 0;
|
private int $round = 0;
|
||||||
private int $monsterMaxHp = 0;
|
private int $totalMaxHp = 0;
|
||||||
|
|
||||||
public function __construct(public Game $game)
|
public function __construct(public Game $game)
|
||||||
{
|
{
|
||||||
|
|
@ -49,8 +50,8 @@ class Battle
|
||||||
{
|
{
|
||||||
$this->partnerHp = [];
|
$this->partnerHp = [];
|
||||||
foreach ($this->player->partners as $partner) {
|
foreach ($this->player->partners as $partner) {
|
||||||
$stats = $partner->getStats();
|
// 从Partner对象的hp属性读取,允许队友在战斗外也能恢复
|
||||||
$this->partnerHp[$partner->id] = $stats['maxHp'];
|
$this->partnerHp[$partner->id] = $partner->hp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +69,31 @@ class Battle
|
||||||
return $alive;
|
return $alive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将战斗中的队友HP同步回Partner对象
|
||||||
|
*/
|
||||||
|
private function syncPartnerHp(): void
|
||||||
|
{
|
||||||
|
foreach ($this->player->partners as $partner) {
|
||||||
|
$partner->hp = $this->partnerHp[$partner->id] ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取存活的敌人
|
||||||
|
* @return Monster[]
|
||||||
|
*/
|
||||||
|
private function getAliveEnemies(): array
|
||||||
|
{
|
||||||
|
$alive = [];
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
if ($enemy->hp > 0) {
|
||||||
|
$alive[] = $enemy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $alive;
|
||||||
|
}
|
||||||
|
|
||||||
public function start()
|
public function start()
|
||||||
{
|
{
|
||||||
$out = $this->game->output;
|
$out = $this->game->output;
|
||||||
|
|
@ -77,8 +103,15 @@ class Battle
|
||||||
|
|
||||||
while ($this->player->hp > 0) {
|
while ($this->player->hp > 0) {
|
||||||
Screen::delay(500000);
|
Screen::delay(500000);
|
||||||
$this->monster = Monster::create($this->game->dungeonId);
|
|
||||||
$this->monsterMaxHp = $this->monster->hp;
|
// 创建敌人群组
|
||||||
|
$this->enemies = Monster::createGroup($this->game->dungeonId);
|
||||||
|
|
||||||
|
$this->totalMaxHp = 0;
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
$this->totalMaxHp += $enemy->hp;
|
||||||
|
}
|
||||||
|
|
||||||
$this->round = 0;
|
$this->round = 0;
|
||||||
|
|
||||||
// 显示遭遇界面
|
// 显示遭遇界面
|
||||||
|
|
@ -88,7 +121,10 @@ class Battle
|
||||||
|
|
||||||
// 战斗循环
|
// 战斗循环
|
||||||
while (true) {
|
while (true) {
|
||||||
if ($this->checkExit($out)) return;
|
if ($this->checkExit($out)) {
|
||||||
|
$this->syncPartnerHp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->round++;
|
$this->round++;
|
||||||
$this->renderBattleScreen($out, $playerFirst);
|
$this->renderBattleScreen($out, $playerFirst);
|
||||||
|
|
@ -105,23 +141,31 @@ class Battle
|
||||||
Screen::delay(800000);
|
Screen::delay(800000);
|
||||||
if ($result) break;
|
if ($result) break;
|
||||||
|
|
||||||
if ($this->checkExit($out)) return;
|
if ($this->checkExit($out)) {
|
||||||
|
$this->syncPartnerHp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 怪物攻击
|
// 怪物攻击
|
||||||
if ($this->monsterAttack($out)) {
|
if ($this->enemiesAttack($out)) {
|
||||||
Screen::delay(1000000);
|
Screen::delay(1000000);
|
||||||
|
$this->syncPartnerHp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Screen::delay(800000);
|
Screen::delay(800000);
|
||||||
} else {
|
} else {
|
||||||
// 怪物先攻
|
// 怪物先攻
|
||||||
if ($this->monsterAttack($out)) {
|
if ($this->enemiesAttack($out)) {
|
||||||
Screen::delay(1000000);
|
Screen::delay(1000000);
|
||||||
|
$this->syncPartnerHp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Screen::delay(800000);
|
Screen::delay(800000);
|
||||||
|
|
||||||
if ($this->checkExit($out)) return;
|
if ($this->checkExit($out)) {
|
||||||
|
$this->syncPartnerHp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 玩家攻击
|
// 玩家攻击
|
||||||
$result = $this->playerAttack($out);
|
$result = $this->playerAttack($out);
|
||||||
|
|
@ -135,6 +179,8 @@ class Battle
|
||||||
}
|
}
|
||||||
|
|
||||||
Screen::delay(500000);
|
Screen::delay(500000);
|
||||||
|
// 同步队友HP到Partner对象,然后保存状态
|
||||||
|
$this->syncPartnerHp();
|
||||||
$this->game->saveState();
|
$this->game->saveState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +194,11 @@ class Battle
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln(" {$this->red}⚔️ 遭遇敌人!{$this->reset}");
|
$out->writeln(" {$this->red}⚔️ 遭遇敌人!{$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln(" {$this->bold}{$this->white}{$this->monster->name}{$this->reset} {$this->cyan}Lv.{$this->monster->level}{$this->reset}");
|
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
$out->writeln(" {$this->bold}{$this->white}{$enemy->name}{$this->reset} {$this->cyan}Lv.{$enemy->level}{$this->reset}");
|
||||||
|
}
|
||||||
|
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("{$this->yellow}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{$this->reset}");
|
$out->writeln("{$this->yellow}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{$this->reset}");
|
||||||
Screen::delay(1000000); // 1秒
|
Screen::delay(1000000); // 1秒
|
||||||
|
|
@ -163,12 +213,29 @@ class Battle
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}第 {$this->round} 回合{$this->reset} {$this->white}[q] 逃跑{$this->reset} {$this->cyan}║{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}第 {$this->round} 回合{$this->reset} {$this->white}[q] 逃跑{$this->reset} {$this->cyan}║{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}╠══════════════════════════════════════════╣{$this->reset}");
|
$out->writeln("{$this->cyan}╠══════════════════════════════════════════╣{$this->reset}");
|
||||||
|
|
||||||
// 怪物信息
|
// 敌人信息
|
||||||
$monsterHpPercent = max(0, $this->monster->hp) / $this->monsterMaxHp;
|
foreach ($this->enemies as $enemy) {
|
||||||
$monsterHpBar = $this->renderHpBar($monsterHpPercent, 20);
|
if ($enemy->hp <= 0) {
|
||||||
$monsterHpText = max(0, $this->monster->hp) . "/" . $this->monsterMaxHp;
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀{$this->reset} {$this->white}{$enemy->name}{$this->reset} {$this->red}[已击败]{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}👹{$this->reset} {$this->bold}{$this->monster->name}{$this->reset} Lv.{$this->monster->level}");
|
continue;
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$monsterHpBar} {$this->white}{$monsterHpText}{$this->reset}");
|
}
|
||||||
|
$enemyHpPercent = max(0, $enemy->hp) / $enemy->baseHp; // 使用baseHp作为最大值近似,或者应该在hydrate时保存maxHp
|
||||||
|
// 实际上Monster没有maxHp属性,hp初始值就是最大值。但在战斗中hp会减少。
|
||||||
|
// 我们需要知道最大HP。Monster::create时hp=baseHp+equipHp。
|
||||||
|
// 简单起见,假设当前hp <= 初始hp。如果需要精确显示条,应该在Monster类加maxHp。
|
||||||
|
// 这里暂时用 $enemy->baseHp + equipHp 估算,或者直接存一个 maxHp。
|
||||||
|
// 为了简单,我们假设满血是初始状态。
|
||||||
|
// 更好的做法是Monster类加一个maxHp属性。
|
||||||
|
// 暂时用 $enemy->hp / $enemy->hp (如果满血) ... 不行。
|
||||||
|
// 让我们修改Monster类加maxHp? 或者这里不显示条,只显示数值?
|
||||||
|
// 或者我们假定 create 出来的 hp 就是 maxHp。
|
||||||
|
// $enemy->maxHp = $enemy->hp; // 在create里做最好。
|
||||||
|
// 这里先只显示数值吧,或者大概估算。
|
||||||
|
|
||||||
|
$hpText = max(0, $enemy->hp);
|
||||||
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}👹{$this->reset} {$this->bold}{$enemy->name}{$this->reset} Lv.{$enemy->level} HP: {$this->red}{$hpText}{$this->reset}");
|
||||||
|
}
|
||||||
|
|
||||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||||
|
|
||||||
// VS 分隔
|
// VS 分隔
|
||||||
|
|
@ -204,7 +271,7 @@ class Battle
|
||||||
private function renderHpBar(float $percent, int $width): string
|
private function renderHpBar(float $percent, int $width): string
|
||||||
{
|
{
|
||||||
$filled = (int)($percent * $width);
|
$filled = (int)($percent * $width);
|
||||||
$empty = $width - $filled;
|
$empty = max($width - $filled,0);
|
||||||
|
|
||||||
// 根据血量百分比选择颜色
|
// 根据血量百分比选择颜色
|
||||||
if ($percent > 0.6) {
|
if ($percent > 0.6) {
|
||||||
|
|
@ -221,7 +288,16 @@ class Battle
|
||||||
|
|
||||||
private function determineFirstStrike(): bool
|
private function determineFirstStrike(): bool
|
||||||
{
|
{
|
||||||
$levelDiff = $this->player->level - $this->monster->level;
|
// Use the leader's level for comparison
|
||||||
|
$leader = $this->enemies[count($this->enemies) - 1]; // Assume leader is last or first?
|
||||||
|
// In createGroup we put minions first, so leader is last.
|
||||||
|
// Let's just use the highest level enemy.
|
||||||
|
$maxLevel = 0;
|
||||||
|
foreach ($this->enemies as $e) {
|
||||||
|
if ($e->level > $maxLevel) $maxLevel = $e->level;
|
||||||
|
}
|
||||||
|
|
||||||
|
$levelDiff = $this->player->level - $maxLevel;
|
||||||
$playerChance = 50;
|
$playerChance = 50;
|
||||||
$levelBonus = max(-30, min(30, $levelDiff * 5));
|
$levelBonus = max(-30, min(30, $levelDiff * 5));
|
||||||
$playerChance += $levelBonus;
|
$playerChance += $levelBonus;
|
||||||
|
|
@ -233,9 +309,20 @@ class Battle
|
||||||
{
|
{
|
||||||
$stats = $this->player->getStats();
|
$stats = $this->player->getStats();
|
||||||
|
|
||||||
|
// Target first alive enemy
|
||||||
|
$target = null;
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
if ($enemy->hp > 0) {
|
||||||
|
$target = $enemy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$target) return true; // All dead
|
||||||
|
|
||||||
// 计算物理伤害和魔法伤害
|
// 计算物理伤害和魔法伤害
|
||||||
$physicalDamage = max(1, $stats['patk'] - $this->monster->pdef);
|
$physicalDamage = max(1, $stats['patk'] - $target->pdef);
|
||||||
$magicDamage = max(0, $stats['matk'] - $this->monster->mdef);
|
$magicDamage = max(0, $stats['matk'] - $target->mdef);
|
||||||
$baseDamage = $physicalDamage + $magicDamage;
|
$baseDamage = $physicalDamage + $magicDamage;
|
||||||
|
|
||||||
$critRate = $stats['crit'];
|
$critRate = $stats['crit'];
|
||||||
|
|
@ -245,22 +332,27 @@ class Battle
|
||||||
|
|
||||||
if ($isCrit) {
|
if ($isCrit) {
|
||||||
$damage = (int)($baseDamage * ($critDmg / 100));
|
$damage = (int)($baseDamage * ($critDmg / 100));
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你发起攻击... {$this->red}{$this->bold}暴击!{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}");
|
||||||
} else {
|
} else {
|
||||||
$damage = $baseDamage;
|
$damage = $baseDamage;
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你发起攻击...");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}...");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->monster->hp -= $damage;
|
$target->hp -= $damage;
|
||||||
|
|
||||||
if ($this->monster->hp <= 0) {
|
if ($target->hp <= 0) {
|
||||||
$this->monster->hp = 0;
|
$target->hp = 0;
|
||||||
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}");
|
||||||
|
|
||||||
|
// Check if all enemies are dead
|
||||||
|
if (empty($this->getAliveEnemies())) {
|
||||||
Screen::delay(500000);
|
Screen::delay(500000);
|
||||||
$this->showVictory($out, $stats);
|
$this->showVictory($out, $stats);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -273,11 +365,22 @@ class Battle
|
||||||
$alivePartners = $this->getAlivePartners();
|
$alivePartners = $this->getAlivePartners();
|
||||||
|
|
||||||
foreach ($alivePartners as $partner) {
|
foreach ($alivePartners as $partner) {
|
||||||
|
// Target first alive enemy
|
||||||
|
$target = null;
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
if ($enemy->hp > 0) {
|
||||||
|
$target = $enemy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$target) return true; // All dead
|
||||||
|
|
||||||
$stats = $partner->getStats();
|
$stats = $partner->getStats();
|
||||||
|
|
||||||
// 计算物理伤害和魔法伤害
|
// 计算物理伤害和魔法伤害
|
||||||
$physicalDamage = max(1, $stats['patk'] - $this->monster->pdef);
|
$physicalDamage = max(1, $stats['patk'] - $target->pdef);
|
||||||
$magicDamage = max(0, $stats['matk'] - $this->monster->mdef);
|
$magicDamage = max(0, $stats['matk'] - $target->mdef);
|
||||||
$baseDamage = $physicalDamage + $magicDamage;
|
$baseDamage = $physicalDamage + $magicDamage;
|
||||||
|
|
||||||
$critRate = $stats['crit'];
|
$critRate = $stats['crit'];
|
||||||
|
|
@ -287,22 +390,26 @@ class Battle
|
||||||
|
|
||||||
if ($isCrit) {
|
if ($isCrit) {
|
||||||
$damage = (int)($baseDamage * ($critDmg / 100));
|
$damage = (int)($baseDamage * ($critDmg / 100));
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 发起攻击... {$this->red}{$this->bold}暴击!{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}");
|
||||||
} else {
|
} else {
|
||||||
$damage = $baseDamage;
|
$damage = $baseDamage;
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 发起攻击...");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}...");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->monster->hp -= $damage;
|
$target->hp -= $damage;
|
||||||
|
|
||||||
if ($this->monster->hp <= 0) {
|
if ($target->hp <= 0) {
|
||||||
$this->monster->hp = 0;
|
$target->hp = 0;
|
||||||
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}");
|
||||||
|
|
||||||
|
if (empty($this->getAliveEnemies())) {
|
||||||
Screen::delay(500000);
|
Screen::delay(500000);
|
||||||
$this->showVictory($out, $this->player->getStats());
|
$this->showVictory($out, $this->player->getStats());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Screen::delay(400000); // 每个同伴攻击间隔
|
Screen::delay(400000); // 每个同伴攻击间隔
|
||||||
}
|
}
|
||||||
|
|
@ -310,8 +417,11 @@ class Battle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function monsterAttack($out): bool
|
private function enemiesAttack($out): bool
|
||||||
{
|
{
|
||||||
|
$aliveEnemies = $this->getAliveEnemies();
|
||||||
|
|
||||||
|
foreach ($aliveEnemies as $enemy) {
|
||||||
// 选择攻击目标:玩家或存活的同伴
|
// 选择攻击目标:玩家或存活的同伴
|
||||||
$alivePartners = $this->getAlivePartners();
|
$alivePartners = $this->getAlivePartners();
|
||||||
$targets = ['player'];
|
$targets = ['player'];
|
||||||
|
|
@ -325,11 +435,11 @@ class Battle
|
||||||
if ($target === 'player') {
|
if ($target === 'player') {
|
||||||
// 攻击玩家
|
// 攻击玩家
|
||||||
$playerStats = $this->player->getStats();
|
$playerStats = $this->player->getStats();
|
||||||
$physicalDamage = max(1, $this->monster->patk - $playerStats['pdef']);
|
$physicalDamage = max(1, $enemy->patk - $playerStats['pdef']);
|
||||||
$magicDamage = max(0, $this->monster->matk - $playerStats['mdef']);
|
$magicDamage = max(0, $enemy->matk - $playerStats['mdef']);
|
||||||
$damage = $physicalDamage + $magicDamage;
|
$damage = $physicalDamage + $magicDamage;
|
||||||
|
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$this->monster->name} 向你发起攻击...");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向你发起攻击...");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 你受到 {$damage} 点伤害{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 你受到 {$damage} 点伤害{$this->reset}");
|
||||||
|
|
||||||
$this->player->hp -= $damage;
|
$this->player->hp -= $damage;
|
||||||
|
|
@ -337,18 +447,18 @@ class Battle
|
||||||
if ($this->player->hp <= 0) {
|
if ($this->player->hp <= 0) {
|
||||||
$this->player->hp = 0;
|
$this->player->hp = 0;
|
||||||
Screen::delay(500000);
|
Screen::delay(500000);
|
||||||
$this->showDefeat($out);
|
$this->showDefeat($out, $enemy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 攻击同伴
|
// 攻击同伴
|
||||||
$partner = $this->player->partners[$target];
|
$partner = $this->player->partners[$target];
|
||||||
$partnerStats = $partner->getStats();
|
$partnerStats = $partner->getStats();
|
||||||
$physicalDamage = max(1, $this->monster->patk - $partnerStats['pdef']);
|
$physicalDamage = max(1, $enemy->patk - $partnerStats['pdef']);
|
||||||
$magicDamage = max(0, $this->monster->matk - $partnerStats['mdef']);
|
$magicDamage = max(0, $enemy->matk - $partnerStats['mdef']);
|
||||||
$damage = $physicalDamage + $magicDamage;
|
$damage = $physicalDamage + $magicDamage;
|
||||||
|
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$this->monster->name} 向 {$partner->name} 发起攻击...");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向 {$partner->name} 发起攻击...");
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 {$partner->name} 受到 {$damage} 点伤害{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 {$partner->name} 受到 {$damage} 点伤害{$this->reset}");
|
||||||
|
|
||||||
$this->partnerHp[$target] -= $damage;
|
$this->partnerHp[$target] -= $damage;
|
||||||
|
|
@ -358,6 +468,8 @@ class Battle
|
||||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$partner->name} 倒下了!{$this->reset}");
|
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$partner->name} 倒下了!{$this->reset}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Screen::delay(400000);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -371,21 +483,45 @@ class Battle
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} {$this->green}{$this->bold}🎉 胜 利 ! 🎉{$this->reset} {$this->yellow}║{$this->reset}");
|
$out->writeln("{$this->yellow}║{$this->reset} {$this->green}{$this->bold}🎉 胜 利 ! 🎉{$this->reset} {$this->yellow}║{$this->reset}");
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} {$this->yellow}║{$this->reset}");
|
$out->writeln("{$this->yellow}║{$this->reset} {$this->yellow}║{$this->reset}");
|
||||||
$out->writeln("{$this->yellow}╠══════════════════════════════════════════╣{$this->reset}");
|
$out->writeln("{$this->yellow}╠══════════════════════════════════════════╣{$this->reset}");
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} 击败: {$this->white}{$this->monster->name}{$this->reset}");
|
|
||||||
|
$enemyNames = [];
|
||||||
|
foreach ($this->enemies as $e) $enemyNames[] = $e->name;
|
||||||
|
$out->writeln("{$this->yellow}║{$this->reset} 击败: {$this->white}" . implode(', ', array_unique($enemyNames)) . "{$this->reset}");
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} 血量: {$this->green}{$this->player->hp}{$this->reset}/{$stats['maxHp']}");
|
$out->writeln("{$this->yellow}║{$this->reset} 血量: {$this->green}{$this->player->hp}{$this->reset}/{$stats['maxHp']}");
|
||||||
|
|
||||||
|
// 汇总经验和灵石
|
||||||
|
$totalExp = 0;
|
||||||
|
$totalStones = 0;
|
||||||
|
$allDrops = [];
|
||||||
|
|
||||||
|
foreach ($this->enemies as $enemy) {
|
||||||
|
$totalExp += $enemy->expReward;
|
||||||
|
$totalStones += $enemy->spiritStoneReward;
|
||||||
|
|
||||||
|
// 掉落
|
||||||
|
foreach ($enemy->getEquippedItems() as $item) {
|
||||||
|
$this->player->addItem($item);
|
||||||
|
$allDrops[] = $item;
|
||||||
|
}
|
||||||
|
foreach ($enemy->dropTable as $drop) {
|
||||||
|
if (rand(1, 100) <= $drop['rate']) {
|
||||||
|
$this->player->addItem($drop['item']);
|
||||||
|
$allDrops[] = $drop['item'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 经验
|
// 经验
|
||||||
$exp = $this->monster->expReward;
|
|
||||||
$levelUpMsg = "";
|
$levelUpMsg = "";
|
||||||
if ($this->player->gainExp($exp)) {
|
if ($this->player->gainExp($totalExp)) {
|
||||||
$levelUpMsg = " {$this->yellow}🎊 升级! Lv.{$this->player->level}{$this->reset}";
|
$levelUpMsg = " {$this->yellow}🎊 升级! Lv.{$this->player->level}{$this->reset}";
|
||||||
}
|
}
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} 经验: {$this->cyan}+{$exp}{$this->reset}{$levelUpMsg}");
|
$out->writeln("{$this->yellow}║{$this->reset} 经验: {$this->cyan}+{$totalExp}{$this->reset}{$levelUpMsg}");
|
||||||
|
|
||||||
// 同伴经验(存活的同伴获得经验)
|
// 同伴经验
|
||||||
$alivePartners = $this->getAlivePartners();
|
$alivePartners = $this->getAlivePartners();
|
||||||
if (!empty($alivePartners)) {
|
if (!empty($alivePartners)) {
|
||||||
$partnerExp = (int)($exp * 0.8); // 同伴获得80%经验
|
$partnerExp = (int)($totalExp * 0.8);
|
||||||
foreach ($alivePartners as $partner) {
|
foreach ($alivePartners as $partner) {
|
||||||
$partnerLevelUp = "";
|
$partnerLevelUp = "";
|
||||||
if ($partner->gainExp($partnerExp)) {
|
if ($partner->gainExp($partnerExp)) {
|
||||||
|
|
@ -396,32 +532,14 @@ class Battle
|
||||||
}
|
}
|
||||||
|
|
||||||
// 灵石
|
// 灵石
|
||||||
$spiritStones = $this->monster->spiritStoneReward;
|
if ($totalStones > 0) {
|
||||||
if ($spiritStones > 0) {
|
$this->player->addSpiritStones($totalStones);
|
||||||
$this->player->addSpiritStones($spiritStones);
|
$out->writeln("{$this->yellow}║{$this->reset} 灵石: {$this->yellow}+{$totalStones}{$this->reset}");
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} 灵石: {$this->yellow}+{$spiritStones}{$this->reset}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 掉落怪物装备
|
if (!empty($allDrops)) {
|
||||||
$drops = [];
|
|
||||||
|
|
||||||
// 1. 掉落怪物的装备(100%掉落)
|
|
||||||
foreach ($this->monster->getEquippedItems() as $item) {
|
|
||||||
$this->player->addItem($item);
|
|
||||||
$drops[] = $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 消耗品按概率掉落
|
|
||||||
foreach ($this->monster->dropTable as $drop) {
|
|
||||||
if (rand(1, 100) <= $drop['rate']) {
|
|
||||||
$this->player->addItem($drop['item']);
|
|
||||||
$drops[] = $drop['item'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($drops)) {
|
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} {$this->white}掉落:{$this->reset}");
|
$out->writeln("{$this->yellow}║{$this->reset} {$this->white}掉落:{$this->reset}");
|
||||||
foreach ($drops as $item) {
|
foreach ($allDrops as $item) {
|
||||||
$out->writeln("{$this->yellow}║{$this->reset} " . ItemDisplay::renderDrop($item, ""));
|
$out->writeln("{$this->yellow}║{$this->reset} " . ItemDisplay::renderDrop($item, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,19 +548,21 @@ class Battle
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
|
|
||||||
$this->game->saveState();
|
$this->game->saveState();
|
||||||
Screen::delay(1500000); // 1.5秒,让玩家看清战利品
|
Screen::delay(1500000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function showDefeat($out)
|
private function showDefeat($out, ?Monster $killer = null)
|
||||||
{
|
{
|
||||||
Screen::clear($out);
|
Screen::clear($out);
|
||||||
|
$killerName = $killer ? $killer->name : "敌人";
|
||||||
|
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("{$this->red}╔══════════════════════════════════════════╗{$this->reset}");
|
$out->writeln("{$this->red}╔══════════════════════════════════════════╗{$this->reset}");
|
||||||
$out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}");
|
$out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}");
|
||||||
$out->writeln("{$this->red}║{$this->reset} {$this->red}{$this->bold}💀 战 败 ! 💀{$this->reset} {$this->red}║{$this->reset}");
|
$out->writeln("{$this->red}║{$this->reset} {$this->red}{$this->bold}💀 战 败 ! 💀{$this->reset} {$this->red}║{$this->reset}");
|
||||||
$out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}");
|
$out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}");
|
||||||
$out->writeln("{$this->red}╠══════════════════════════════════════════╣{$this->reset}");
|
$out->writeln("{$this->red}╠══════════════════════════════════════════╣{$this->reset}");
|
||||||
$out->writeln("{$this->red}║{$this->reset} 你被 {$this->white}{$this->monster->name}{$this->reset} 击败了...");
|
$out->writeln("{$this->red}║{$this->reset} 你被 {$this->white}{$killerName}{$this->reset} 击败了...");
|
||||||
$out->writeln("{$this->red}║{$this->reset}");
|
$out->writeln("{$this->red}║{$this->reset}");
|
||||||
$out->writeln("{$this->red}║{$this->reset} {$this->white}不要气馁,休整后再战!{$this->reset}");
|
$out->writeln("{$this->red}║{$this->reset} {$this->white}不要气馁,休整后再战!{$this->reset}");
|
||||||
$out->writeln("{$this->red}╚══════════════════════════════════════════╝{$this->reset}");
|
$out->writeln("{$this->red}╚══════════════════════════════════════════╝{$this->reset}");
|
||||||
|
|
|
||||||
|
|
@ -186,16 +186,66 @@ class InventoryPanel
|
||||||
$stats = $player->getStats();
|
$stats = $player->getStats();
|
||||||
$maxHp = $stats['maxHp'];
|
$maxHp = $stats['maxHp'];
|
||||||
|
|
||||||
// 检查是否已满血
|
// 检查玩家血量
|
||||||
if ($player->hp >= $maxHp) {
|
$playerNeedsHeal = $player->hp < $maxHp;
|
||||||
$out->writeln("你的生命值已满,无需使用!");
|
|
||||||
|
// 检查队友血量
|
||||||
|
$partnersNeedHeal = [];
|
||||||
|
foreach ($player->partners as $partner) {
|
||||||
|
$partnerStats = $partner->getStats();
|
||||||
|
$partnerMaxHp = $partnerStats['maxHp'];
|
||||||
|
if ($partner->hp < $partnerMaxHp) {
|
||||||
|
$partnersNeedHeal[] = $partner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果都不需要恢复
|
||||||
|
if (!$playerNeedsHeal && empty($partnersNeedHeal)) {
|
||||||
|
$out->writeln("你和队友的生命值都已满,无需使用!");
|
||||||
Screen::sleep(1);
|
Screen::sleep(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 heal 方法恢复生命,不超过上限
|
// 选择恢复目标
|
||||||
|
$target = null;
|
||||||
|
if ($playerNeedsHeal && !empty($partnersNeedHeal)) {
|
||||||
|
// 需要询问玩家选择
|
||||||
|
$out->writeln("");
|
||||||
|
$out->writeln("选择恢复目标:");
|
||||||
|
$out->writeln("[1] 恢复自己");
|
||||||
|
foreach ($partnersNeedHeal as $i => $partner) {
|
||||||
|
$out->writeln("[" . ($i + 2) . "] 恢复 {$partner->name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$choice = Screen::input($out, "选择:");
|
||||||
|
$choiceInt = (int)$choice;
|
||||||
|
|
||||||
|
if ($choiceInt === 1) {
|
||||||
|
$target = 'player';
|
||||||
|
} elseif ($choiceInt >= 2 && $choiceInt - 2 < count($partnersNeedHeal)) {
|
||||||
|
$target = $partnersNeedHeal[$choiceInt - 2];
|
||||||
|
} else {
|
||||||
|
$out->writeln("无效选择");
|
||||||
|
Screen::sleep(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} elseif ($playerNeedsHeal) {
|
||||||
|
$target = 'player';
|
||||||
|
} elseif (!empty($partnersNeedHeal)) {
|
||||||
|
$target = $partnersNeedHeal[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用消耗品进行恢复
|
||||||
|
if ($target === 'player') {
|
||||||
$actualHeal = $player->heal($item['heal']);
|
$actualHeal = $player->heal($item['heal']);
|
||||||
$out->writeln("你使用了 {$item['name']},恢复了 {$actualHeal} HP!(当前: {$player->hp}/{$maxHp})");
|
$out->writeln("你使用了 {$item['name']},恢复了 {$actualHeal} HP!(当前: {$player->hp}/{$maxHp})");
|
||||||
|
} else {
|
||||||
|
// 恢复队友
|
||||||
|
$partnerStats = $target->getStats();
|
||||||
|
$partnerMaxHp = $partnerStats['maxHp'];
|
||||||
|
$actualHeal = $target->heal($item['heal']);
|
||||||
|
$out->writeln("你使用了 {$item['name']} 来恢复 {$target->name},恢复了 {$actualHeal} HP!(当前: {$target->hp}/{$partnerMaxHp})");
|
||||||
|
}
|
||||||
|
|
||||||
// Decrease quantity or remove
|
// Decrease quantity or remove
|
||||||
if (($player->inventory[$index]['quantity'] ?? 1) > 1) {
|
if (($player->inventory[$index]['quantity'] ?? 1) > 1) {
|
||||||
|
|
|
||||||
|
|
@ -63,11 +63,11 @@ class TalentPanel
|
||||||
$this->game->output->writeln("{$this->cyan}║{$this->reset} 暴击: {$this->yellow}{$stats['crit']}%{$this->reset} 暴伤: {$this->yellow}{$stats['critdmg']}%{$this->reset}");
|
$this->game->output->writeln("{$this->cyan}║{$this->reset} 暴击: {$this->yellow}{$stats['crit']}%{$this->reset} 暴伤: {$this->yellow}{$stats['critdmg']}%{$this->reset}");
|
||||||
$this->game->output->writeln("{$this->cyan}╚════════════════════════════════════════╝{$this->reset}");
|
$this->game->output->writeln("{$this->cyan}╚════════════════════════════════════════╝{$this->reset}");
|
||||||
$this->game->output->writeln("");
|
$this->game->output->writeln("");
|
||||||
$this->game->output->writeln("[1-5] 加点 | [r] 重置天赋 | [99] 返回");
|
$this->game->output->writeln("[1-5] 加点 | [r] 重置天赋 | [0] 返回");
|
||||||
|
|
||||||
$choice = Input::ask($this->game->output, "请选择: ");
|
$choice = Input::ask($this->game->output, "请选择: ");
|
||||||
|
|
||||||
if ($choice == 99) {
|
if ($choice == 0) {
|
||||||
$this->game->state = Game::MENU;
|
$this->game->state = Game::MENU;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user