Compare commits
2 Commits
6aedb96342
...
0658960b70
| Author | SHA1 | Date | |
|---|---|---|---|
| 0658960b70 | |||
| dabc1f1cd4 |
|
|
@ -209,33 +209,33 @@ class ItemDisplay
|
|||
*/
|
||||
public static function renderListItem(array $item, bool $showType = true, bool $showQuantity = true): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
// 名称(带品质颜色和强化)
|
||||
$parts[] = self::formatName($item);
|
||||
|
||||
// 数量
|
||||
$quantity = $item['quantity'] ?? 1;
|
||||
if ($showQuantity && $quantity > 1) {
|
||||
$parts[] = self::$gray . "x{$quantity}" . self::$reset;
|
||||
}
|
||||
|
||||
// 类型
|
||||
if ($showType) {
|
||||
$type = $item['type'] ?? '';
|
||||
$typeName = self::getTypeName($type);
|
||||
$parts[] = self::$gray . "[{$typeName}]" . self::$reset;
|
||||
}
|
||||
|
||||
// 主属性(简洁版)或法术信息
|
||||
$type = $item['type'] ?? '';
|
||||
if ($type === 'spell') {
|
||||
// 法术显示由 SpellDisplay 处理(计算方式和基础数值)
|
||||
// 需要导入 SpellDisplay 后使用
|
||||
$statsStr = SpellDisplay::formatSpellCompact($item);
|
||||
$statsStr = SpellDisplay::renderListItem($item);
|
||||
// 为了向后兼容,使用内联逻辑(调用处应该使用 SpellDisplay)
|
||||
} else {
|
||||
// 显示装备的属性
|
||||
$parts = [];
|
||||
|
||||
// 名称(带品质颜色和强化)
|
||||
$parts[] = self::formatName($item);
|
||||
|
||||
// 数量
|
||||
$quantity = $item['quantity'] ?? 1;
|
||||
if ($showQuantity && $quantity > 1) {
|
||||
$parts[] = self::$gray . "x{$quantity}" . self::$reset;
|
||||
}
|
||||
|
||||
// 类型
|
||||
if ($showType) {
|
||||
$type = $item['type'] ?? '';
|
||||
$typeName = self::getTypeName($type);
|
||||
$parts[] = self::$gray . "[{$typeName}]" . self::$reset;
|
||||
}
|
||||
$statsStr = self::formatStatsCompact($item);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,9 @@ class SpellCalculator
|
|||
// 默认使用魔攻
|
||||
$baseDamage = (int)($attackerStats['matk'] * $actualMultiplier);
|
||||
}
|
||||
|
||||
|
||||
$baseDamage += ($spellInfo['base'] ?? 0) * $spellInfo['enhanceLevel'];
|
||||
|
||||
// 计算防御减免
|
||||
$resistance = $defenderStats['mdef'] ?? 0;
|
||||
$damage = max(1, $baseDamage - $resistance);
|
||||
|
|
|
|||
|
|
@ -128,19 +128,15 @@ class SpellDisplay
|
|||
// 计算方式
|
||||
$calcType = $spell['calc_type'] ?? 'matk';
|
||||
$calcDesc = self::getCalcTypeDescription($calcType);
|
||||
$parts[] = self::$cyan . "{$calcDesc}" . self::$reset;
|
||||
|
||||
$ratio = $spell['damage_ratio'];
|
||||
$parts[] = self::$cyan . "{$calcDesc} x $ratio" . self::$reset;
|
||||
// 基础值
|
||||
if ($spellType === 'damage_single' || $spellType === 'damage_aoe') {
|
||||
$base = $spell['base'] ?? [5, 12, 25, 45];
|
||||
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
|
||||
$baseValue = $base[$qualityIndex] ?? 5;
|
||||
$parts[] = self::$yellow . "基础:{$baseValue}" . self::$reset;
|
||||
$base = $spell['base'] ?? 0;
|
||||
$parts[] = self::$yellow . "基础:{$base}" . self::$reset;
|
||||
} elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') {
|
||||
$base = $spell['base'] ?? [8, 18, 38, 65];
|
||||
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
|
||||
$baseValue = $base[$qualityIndex] ?? 8;
|
||||
$parts[] = self::$yellow . "基础:{$baseValue}" . self::$reset;
|
||||
$base = $spell['base'] ?? 0;
|
||||
$parts[] = self::$yellow . "基础:{$base}" . self::$reset;
|
||||
}
|
||||
|
||||
// 消耗
|
||||
|
|
@ -447,77 +443,8 @@ class SpellDisplay
|
|||
public static function renderSlot(string $slotName, array $spell, string $linePrefix = "║ "): array
|
||||
{
|
||||
$lines = [];
|
||||
|
||||
// 第一行:槽位名 + 法术名
|
||||
$lines[] = $linePrefix . self::$cyan . $slotName . self::$reset . ": " . self::formatName($spell);
|
||||
|
||||
// 计算方式和法术类型
|
||||
$calcType = $spell['calc_type'] ?? 'matk';
|
||||
$spellType = $spell['spellType'] ?? $spell['type'] ?? 'unknown';
|
||||
$typeName = self::getTypeName($spellType);
|
||||
|
||||
// 计算方式的完整描述
|
||||
$calcTypeDescMap = [
|
||||
'matk' => '基于魔攻',
|
||||
'patk' => '基于物攻',
|
||||
'hybrid' => '混合伤害',
|
||||
'hp_percent' => '基于HP%',
|
||||
'crit_heal' => '暴击治疗',
|
||||
'crit_damage' => '暴击伤害',
|
||||
'defense' => '基于防御',
|
||||
'def_pierce' => '防御穿透',
|
||||
'status_bonus' => '状态加成',
|
||||
'enemy_count_bonus' => '敌人加成',
|
||||
'low_def_bonus' => '克低防',
|
||||
'matk_scaled' => '群体伤害',
|
||||
'dispersed_damage' => '分散伤害',
|
||||
'crit_aoe' => '暴击范围',
|
||||
'smart_heal' => '智能治疗',
|
||||
'hp_missing' => '缺血治疗',
|
||||
'team_sync' => '队伍同步',
|
||||
];
|
||||
|
||||
$calcDesc = $calcTypeDescMap[$calcType] ?? $calcType;
|
||||
$lines[] = $linePrefix . " " . self::$white . "类型: " . self::$magenta . $typeName . self::$reset .
|
||||
self::$white . " | 计算: " . self::$green . $calcDesc . self::$reset;
|
||||
|
||||
// 显示基础数值和倍数
|
||||
if ($spellType === 'damage_single' || $spellType === 'damage_aoe') {
|
||||
$ratio = $spell['damage_ratio'];
|
||||
$base = $spell['base'] ?? [5, 12, 25, 45];
|
||||
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
|
||||
$baseValue = $base[$qualityIndex] ?? 5;
|
||||
$lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "x{$ratio}" . self::$reset .
|
||||
self::$white . " | 基础值: " . self::$yellow . "{$baseValue}" . self::$reset;
|
||||
} elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') {
|
||||
$ratio = $spell['heal_ratio'];
|
||||
$healBase = $spell['heal_base'] ?? null;
|
||||
$base = $spell['base'] ?? [8, 18, 38, 65];
|
||||
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
|
||||
$baseValue = $base[$qualityIndex] ?? 8;
|
||||
|
||||
if ($healBase) {
|
||||
$healBaseValue = $healBase[$qualityIndex] ?? 20;
|
||||
$lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "x{$ratio}" . self::$reset .
|
||||
self::$white . " | 基数: " . self::$yellow . "{$healBaseValue}" . self::$reset .
|
||||
self::$white . " | 基础: " . self::$yellow . "{$baseValue}" . self::$reset;
|
||||
} else {
|
||||
$lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "x{$ratio}" . self::$reset .
|
||||
self::$white . " | 基础值: " . self::$yellow . "{$baseValue}" . self::$reset;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示消耗
|
||||
$cost = $spell['cost'] ?? 0;
|
||||
$enhanceLevel = $spell['enhanceLevel'] ?? 0;
|
||||
$actualCost = max(1, $cost - ($enhanceLevel * 2));
|
||||
if ($enhanceLevel > 0) {
|
||||
$lines[] = $linePrefix . " " . self::$white . "消耗: " . self::$yellow . "{$cost}" . self::$reset .
|
||||
" → " . self::$green . "{$actualCost}" . self::$reset .
|
||||
self::$yellow . " +{$enhanceLevel}" . self::$reset;
|
||||
} else {
|
||||
$lines[] = $linePrefix . " " . self::$white . "消耗: " . self::$green . "{$actualCost}" . self::$reset;
|
||||
}
|
||||
$lines[] = $linePrefix . self::$cyan . $slotName . self::$reset . ": " . self::renderListItem($spell);
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ return [
|
|||
'exp' => 100,
|
||||
'spirit_stones' => 20,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '眨眼剑法', 'quality' => 'rare', 'patk' => 15, 'rate' => 15],
|
||||
['type' => 'weapon', 'name' => '眨眼剑法', 'quality' => 'rare', 'rate' => 15] + $weaponTemplate,
|
||||
['type' => 'necklace', 'name' => '长生锁', 'rate' => 20] + $necklaceTemplate,
|
||||
['type' => 'armor', 'name' => '疗愈大衣', 'quality' => 'rare', 'rate' => 12] + $armorTemplate,
|
||||
['type' => 'ring', 'name' => '医道戒', 'rate' => 10] + $ringTemplate,
|
||||
|
|
@ -426,7 +426,7 @@ return [
|
|||
'exp' => 400,
|
||||
'spirit_stones' => 150,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '烈焰刀', 'quality' => 'epic', 'patk' => 50, 'matk' => 30, 'rate' => 15],
|
||||
['type' => 'weapon', 'name' => '烈焰刀', 'quality' => 'epic', 'rate' => 15] + $weaponTemplate,
|
||||
['type' => 'ring', 'name' => '传音符', 'rate' => 15] + $ringTemplate,
|
||||
['type' => 'armor', 'name' => '烈焰战甲', 'quality' => 'rare', 'rate' => 12] + $armorTemplate,
|
||||
['type' => 'boots', 'name' => '云游靴', 'quality' => 'rare', 'rate' => 10] + $bootsTemplate,
|
||||
|
|
@ -582,7 +582,7 @@ return [
|
|||
'exp' => 1000,
|
||||
'spirit_stones' => 400,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '青元剑', 'quality' => 'legendary', 'patk' => 100, 'matk' => 80, 'rate' => 20],
|
||||
['type' => 'weapon', 'name' => '青元剑', 'quality' => 'legendary', 'rate' => 20] + $weaponTemplate,
|
||||
['type' => 'armor', 'name' => '黑煞甲', 'quality' => 'legendary', 'rate' => 18] + $armorTemplate,
|
||||
['type' => 'ring', 'name' => '黑煞戒', 'rate' => 15] + $ringTemplate,
|
||||
['type' => 'boots', 'name' => '黑煞靴', 'quality' => 'rare', 'rate' => 12] + $bootsTemplate,
|
||||
|
|
@ -817,8 +817,8 @@ return [
|
|||
'exp' => 4000,
|
||||
'spirit_stones' => 1500,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '金蛟剪', 'quality' => 'legendary', 'patk' => 300, 'matk' => 200, 'rate' => 20],
|
||||
['type' => 'armor', 'name' => '金蛟鳞甲', 'quality' => 'legendary', 'pdef' => 180, 'mdef' => 120, 'rate' => 20],
|
||||
['type' => 'weapon', 'name' => '金蛟剪', 'quality' => 'legendary', 'rate' => 20] + $weaponTemplate,
|
||||
['type' => 'armor', 'name' => '金蛟鳞甲', 'quality' => 'legendary', 'rate' => 20] + $armorTemplate,
|
||||
['type' => 'necklace', 'name' => '金蛟珠', 'quality' => 'epic', 'rate' => 15] + $necklaceTemplate,
|
||||
['type' => 'boots', 'name' => '金蛟靴', 'quality' => 'rare', 'rate' => 12] + $bootsTemplate,
|
||||
['type' => 'consume', 'name' => '九曲灵参', 'rate' => 25, 'heal' => 5000],
|
||||
|
|
@ -898,7 +898,7 @@ return [
|
|||
'exp' => 5000,
|
||||
'spirit_stones' => 2000,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '落云剑', 'quality' => 'epic', 'patk' => 350, 'matk' => 250, 'rate' => 15],
|
||||
['type' => 'weapon', 'name' => '落云剑', 'quality' => 'epic', 'rate' => 15] + $weaponTemplate,
|
||||
['type' => 'necklace', 'name' => '定魂珠', 'rate' => 20] + $necklaceTemplate,
|
||||
['type' => 'armor', 'name' => '云中甲', 'quality' => 'rare', 'rate' => 12] + $armorTemplate,
|
||||
['type' => 'boots', 'name' => '飘云靴', 'quality' => 'rare', 'rate' => 10] + $bootsTemplate,
|
||||
|
|
@ -1135,8 +1135,8 @@ return [
|
|||
'exp' => 30000,
|
||||
'spirit_stones' => 15000,
|
||||
'drops' => [
|
||||
['type' => 'weapon', 'name' => '青竹蜂云剑', 'quality' => 'legendary', 'patk' => 1500, 'matk' => 1000, 'rate' => 15],
|
||||
['type' => 'armor', 'name' => '五行甲', 'quality' => 'legendary', 'pdef' => 1000, 'mdef' => 1000, 'rate' => 15],
|
||||
['type' => 'weapon', 'name' => '青竹蜂云剑', 'quality' => 'legendary', 'rate' => 15] + $weaponTemplate,
|
||||
['type' => 'armor', 'name' => '五行甲', 'quality' => 'legendary', 'rate' => 15] + $armorTemplate,
|
||||
['type' => 'necklace', 'name' => '凤凰链', 'quality' => 'legendary', 'rate' => 12] + $necklaceTemplate,
|
||||
['type' => 'boots', 'name' => '凤凰靴', 'quality' => 'epic', 'rate' => 10] + $bootsTemplate,
|
||||
['type' => 'consume', 'name' => '飞升令', 'rate' => 25, 'heal' => 99999],
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ class Actor
|
|||
public int $mana = 100;
|
||||
public int $maxMana = 100;
|
||||
|
||||
// 防护系统
|
||||
public bool $isProtecting = false; // 是否处于防护状态
|
||||
public float $protectDamageReduction = 0.2; // 防护角色伤害减免 20%
|
||||
public float $protectDamageTakenBonus = 0.3; // 防护角色承受伤害增加 30%
|
||||
public int $protectThreatBonus = 50; // 防护角色的威胁值加成
|
||||
|
||||
// 技能槽位系统 (新法术系统)
|
||||
public array $skillSlots = [
|
||||
'skill1' => null,
|
||||
|
|
@ -285,4 +291,52 @@ class Actor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入防护状态
|
||||
*/
|
||||
public function enterProtectMode(): bool
|
||||
{
|
||||
if ($this->isProtecting) {
|
||||
return false; // 已经在防护状态
|
||||
}
|
||||
$this->isProtecting = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出防护状态
|
||||
*/
|
||||
public function exitProtectMode(): bool
|
||||
{
|
||||
if (!$this->isProtecting) {
|
||||
return false; // 未在防护状态
|
||||
}
|
||||
$this->isProtecting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取防护状态下的伤害减免(用于保护队友)
|
||||
*/
|
||||
public function getProtectDamageReduction(): float
|
||||
{
|
||||
return $this->isProtecting ? $this->protectDamageReduction : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取防护状态下的伤害增加(防护者承受更多伤害)
|
||||
*/
|
||||
public function getProtectDamageTakenBonus(): float
|
||||
{
|
||||
return $this->isProtecting ? $this->protectDamageTakenBonus : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取威胁值加成
|
||||
*/
|
||||
public function getThreatBonus(): int
|
||||
{
|
||||
return $this->isProtecting ? $this->protectThreatBonus : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class Item
|
|||
|
||||
// 随机品质
|
||||
$roll = rand(1, 100);
|
||||
// $roll = 100;
|
||||
if ($roll <= 70) $quality = 'common';
|
||||
elseif ($roll <= 90) $quality = 'rare';
|
||||
elseif ($roll <= 98) $quality = 'epic';
|
||||
|
|
@ -89,6 +90,7 @@ class Item
|
|||
$item->desc = "Lv.{$level} {$quality}品质的药剂";
|
||||
} else {
|
||||
// 检查是否有特定物品配置
|
||||
// dd($typeConfig);
|
||||
$specificConfig = $typeConfig['specific_config'][$item->name] ?? [];
|
||||
|
||||
// 合并配置:优先使用特定配置,否则使用默认配置
|
||||
|
|
@ -208,15 +210,12 @@ class Item
|
|||
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) {
|
||||
|
|
@ -300,6 +299,7 @@ class Item
|
|||
$growth = 0;
|
||||
if (isset($spellInfo['base']) && isset($spellInfo['growth'])) {
|
||||
$baseArray = $spellInfo['base'];
|
||||
|
||||
$growthArray = $spellInfo['growth'];
|
||||
|
||||
// 确保索引在范围内
|
||||
|
|
@ -308,8 +308,8 @@ class Item
|
|||
$growth = $growthArray[$qualityIndex] ?? ($growthArray[0] ?? 0);
|
||||
|
||||
// 应用计算公式:finalValue = baseValue + (level * growth) + randomBonus
|
||||
$randomBonus = rand(0, max(1, (int)($baseValue * 0.15)));
|
||||
$finalBaseValue = (int)($baseValue + ($level * $growth) + $randomBonus);
|
||||
$randomBonus = rand(0, max(1, (int)($baseValue * 3)));
|
||||
$finalBaseValue = (int)($baseValue + ($level * $growth * 10) + $randomBonus);
|
||||
} else {
|
||||
$finalBaseValue = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ class Battle
|
|||
private function renderHpBar(float $percent, int $width): string
|
||||
{
|
||||
$filled = (int)($percent * $width);
|
||||
$empty = max($width - $filled,0);
|
||||
$empty = max($width - $filled,1);
|
||||
|
||||
// 根据血量百分比选择颜色
|
||||
if ($percent > 0.6) {
|
||||
|
|
@ -308,7 +308,7 @@ class Battle
|
|||
$color = $this->red;
|
||||
}
|
||||
|
||||
$bar = $color . str_repeat("█", $filled) . $this->white . str_repeat("░", $empty) . $this->reset;
|
||||
$bar = $color . str_repeat("█", $filled) . $this->white . str_repeat("░", $empty?:1) . $this->reset;
|
||||
return "[" . $bar . "]";
|
||||
}
|
||||
|
||||
|
|
@ -395,6 +395,59 @@ class Battle
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算目标的威胁值(用于敌人选择目标)
|
||||
* 威胁值 = 输出能力 + 防御能力 + 防护状态加成
|
||||
*/
|
||||
private function calculateThreat(Actor $target): int
|
||||
{
|
||||
$stats = $target->getStats();
|
||||
|
||||
// 基础威胁值 = (物攻 + 魔攻) / 2
|
||||
$threat = (int)(($stats['patk'] + $stats['matk']) / 2);
|
||||
|
||||
// 防御角色威胁值大幅上升
|
||||
$threat += $target->getThreatBonus();
|
||||
|
||||
// 血量比例也会影响威胁值(血量越多越有威胁)
|
||||
$threat += (int)($stats['hp'] / $stats['maxHp'] * 10);
|
||||
|
||||
return $threat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能选择单体目标(考虑防护角色机制)
|
||||
*/
|
||||
private function selectTargetWithThreat(Actor $actor): ?Actor
|
||||
{
|
||||
$opponents = $this->getOpponents($actor);
|
||||
$aliveOpponents = array_filter($opponents, fn($e) => $e->hp > 0);
|
||||
|
||||
if (empty($aliveOpponents)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 优先级1:选择处于防护状态的目标(防御者)
|
||||
$protectingTargets = array_filter($aliveOpponents, fn($e) => $e->isProtecting);
|
||||
if (!empty($protectingTargets)) {
|
||||
return current($protectingTargets);
|
||||
}
|
||||
|
||||
// 优先级2:选择威胁值最高的目标
|
||||
$maxThreat = -1;
|
||||
$bestTarget = null;
|
||||
|
||||
foreach ($aliveOpponents as $target) {
|
||||
$threat = $this->calculateThreat($target);
|
||||
if ($threat > $maxThreat) {
|
||||
$maxThreat = $threat;
|
||||
$bestTarget = $target;
|
||||
}
|
||||
}
|
||||
|
||||
return $bestTarget ?? current($aliveOpponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能选择法术 (通用)
|
||||
*/
|
||||
|
|
@ -491,15 +544,9 @@ class Battle
|
|||
*/
|
||||
private function castDamageSingleSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $damageBonus, string $name): bool
|
||||
{
|
||||
// 自动选择目标
|
||||
// 自动选择目标(使用威胁值系统)
|
||||
if (!$target) {
|
||||
$opponents = $this->getOpponents($caster);
|
||||
foreach ($opponents as $enemy) {
|
||||
if ($enemy->hp > 0) {
|
||||
$target = $enemy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$target = $this->selectTargetWithThreat($caster);
|
||||
}
|
||||
|
||||
if (!$target) return true;
|
||||
|
|
@ -518,7 +565,7 @@ class Battle
|
|||
// 显示法术施放信息
|
||||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||||
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||||
|
||||
if ($isCrit) {
|
||||
|
|
@ -527,19 +574,28 @@ class Battle
|
|||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 对 {$target->name} 造成 {$this->green}{$damage}{$this->reset} 点魔法伤害");
|
||||
}
|
||||
|
||||
$target->hp -= $damage;
|
||||
// 应用防护机制:防护角色承受更多伤害
|
||||
$actualDamage = (int)($damage * (1 + $target->getProtectDamageTakenBonus()));
|
||||
|
||||
$target->hp -= $actualDamage;
|
||||
|
||||
// 如果防护角色正在保护队友,需要显示保护效果
|
||||
if ($target->isProtecting && $actualDamage > $damage) {
|
||||
$extraDamage = $actualDamage - $damage;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️ 防护状态:额外承受 {$extraDamage} 伤害!{$this->reset}");
|
||||
}
|
||||
|
||||
if ($target->hp <= 0) {
|
||||
$target->hp = 0;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}");
|
||||
|
||||
|
||||
// 如果是玩家击败了所有敌人
|
||||
if (($caster instanceof Player || $caster instanceof Partner) && empty($this->getAliveEnemies())) {
|
||||
Screen::delay(500000);
|
||||
$this->showVictory($out, $stats);
|
||||
$this->showVictory($out, $stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 如果是敌人击败了玩家
|
||||
if ($target instanceof Player) {
|
||||
Screen::delay(500000);
|
||||
|
|
@ -730,6 +786,59 @@ class Battle
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该自动进入防护状态来保护低血量队友
|
||||
* 条件:自身血量健康 (>50%) 且有队友血量低 (<30%)
|
||||
*/
|
||||
private function checkAutoEnterProtectMode(Actor $actor, $out): void
|
||||
{
|
||||
// 仅玩家和队友可以进入防护状态,敌人不需要
|
||||
if (!($actor instanceof Player) && !($actor instanceof Partner)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 已经在防护状态,不需要再进入
|
||||
if ($actor->isProtecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stats = $actor->getStats();
|
||||
$healthRatio = $stats['hp'] / $stats['maxHp'];
|
||||
|
||||
// 自身血量需要健康(>50%)
|
||||
if ($healthRatio <= 0.5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有队友血量低
|
||||
$allies = $this->getAllies($actor);
|
||||
$hasLowHpAlly = false;
|
||||
|
||||
foreach ($allies as $ally) {
|
||||
// 跳过自己和倒下的队友
|
||||
if ($ally === $actor || $ally->hp <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$allyStats = $ally->getStats();
|
||||
$allyHealthRatio = $allyStats['hp'] / $allyStats['maxHp'];
|
||||
|
||||
// 如果有队友血量低于30%,进入防护状态
|
||||
if ($allyHealthRatio < 0.5) {
|
||||
$hasLowHpAlly = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 自动进入防护状态
|
||||
if ($hasLowHpAlly) {
|
||||
$actor->enterProtectMode();
|
||||
|
||||
$actorName = ($actor instanceof Player) ? "你" : $actor->name;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️{$this->reset} {$actorName} 主动进入防护状态,为队友抗伤!{$this->reset}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行角色的回合行动 (通用)
|
||||
*/
|
||||
|
|
@ -738,6 +847,10 @@ class Battle
|
|||
if ($actor->hp <= 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否应该自动进入防护状态
|
||||
$this->checkAutoEnterProtectMode($actor, $out);
|
||||
|
||||
// 1. 尝试使用法术
|
||||
$selectedSpell = $this->smartSelectSpell($actor);
|
||||
|
||||
|
|
@ -773,15 +886,8 @@ class Battle
|
|||
}
|
||||
|
||||
// 2. 如果没有使用法术,执行普通攻击
|
||||
// 寻找目标
|
||||
$target = null;
|
||||
$opponents = $this->getOpponents($actor);
|
||||
foreach ($opponents as $enemy) {
|
||||
if ($enemy->hp > 0) {
|
||||
$target = $enemy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 寻找目标(使用威胁值系统)
|
||||
$target = $this->selectTargetWithThreat($actor);
|
||||
|
||||
if (!$target) return true; // 无目标,回合结束
|
||||
|
||||
|
|
@ -815,7 +921,16 @@ class Battle
|
|||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
||||
}
|
||||
|
||||
$target->hp -= $damage;
|
||||
// 应用防护机制:防护角色承受更多伤害
|
||||
$actualDamage = (int)($damage * (1 + $target->getProtectDamageTakenBonus()));
|
||||
|
||||
$target->hp -= $actualDamage;
|
||||
|
||||
// 如果防护角色正在保护队友,需要显示保护效果
|
||||
if ($target->isProtecting && $actualDamage > $damage) {
|
||||
$extraDamage = $actualDamage - $damage;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️ 防护状态:额外承受 {$extraDamage} 伤害!{$this->reset}");
|
||||
}
|
||||
|
||||
// 蓝量恢复机制
|
||||
// 攻击者恢复 15 点
|
||||
|
|
|
|||
|
|
@ -264,26 +264,27 @@ class InventoryPanel
|
|||
$slot = $item['type'];
|
||||
|
||||
// 获取新装备原有的强化等级
|
||||
// $newItemEnhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
// 调整为不能继承强化等级
|
||||
$newItemEnhanceLevel = 0;
|
||||
$newItemEnhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
|
||||
// $newItemEnhanceLevel = 0;
|
||||
|
||||
// If there's already an item in the slot, swap enhance levels
|
||||
if (isset($player->equip[$slot]) && !empty($player->equip[$slot])) {
|
||||
$oldItem = $player->equip[$slot];
|
||||
$oldEnhanceLevel = $oldItem['enhanceLevel'] ?? 0;
|
||||
|
||||
// 旧装备继承新装备的强化等级
|
||||
$oldItem['enhanceLevel'] = $newItemEnhanceLevel;
|
||||
$player->addItem($oldItem);
|
||||
|
||||
$oldEnhanceStr = $oldEnhanceLevel > 0 ? "+{$oldEnhanceLevel}" : "";
|
||||
$newEnhanceStr = $newItemEnhanceLevel > 0 ? "+{$newItemEnhanceLevel}" : "";
|
||||
$out->writeln("已取下 {$oldItem['name']}{$newEnhanceStr} 并放入背包");
|
||||
|
||||
// 新装备继承旧装备的强化等级
|
||||
$item['enhanceLevel'] = $oldEnhanceLevel;
|
||||
}
|
||||
// 调整为不能继承强化等级
|
||||
// if (isset($player->equip[$slot]) && !empty($player->equip[$slot])) {
|
||||
// $oldItem = $player->equip[$slot];
|
||||
// $oldEnhanceLevel = $oldItem['enhanceLevel'] ?? 0;
|
||||
//
|
||||
// // 旧装备继承新装备的强化等级
|
||||
// $oldItem['enhanceLevel'] = $newItemEnhanceLevel;
|
||||
// $player->addItem($oldItem);
|
||||
//
|
||||
// $oldEnhanceStr = $oldEnhanceLevel > 0 ? "+{$oldEnhanceLevel}" : "";
|
||||
// $newEnhanceStr = $newItemEnhanceLevel > 0 ? "+{$newItemEnhanceLevel}" : "";
|
||||
// $out->writeln("已取下 {$oldItem['name']}{$newEnhanceStr} 并放入背包");
|
||||
//
|
||||
// // 新装备继承旧装备的强化等级
|
||||
// $item['enhanceLevel'] = $oldEnhanceLevel;
|
||||
// }
|
||||
|
||||
$player->equip[$slot] = $item;
|
||||
|
||||
|
|
|
|||
|
|
@ -175,8 +175,17 @@ class StatsPanel
|
|||
if ($this->isPlayer) {
|
||||
$out->writeln("║ 💰灵石: {$this->green}{$this->game->player->spiritStones}{$this->reset}");
|
||||
}
|
||||
|
||||
|
||||
$out->writeln("║ HP: {$this->red}{$stats['hp']}{$this->reset}/{$stats['maxHp']}");
|
||||
|
||||
// 显示防护状态
|
||||
$protectStatus = $actor->isProtecting ? "{$this->green}✓ 防护中{$this->reset}" : "{$this->red}✗ 未防护{$this->reset}";
|
||||
$out->writeln("║ 状态: {$protectStatus}");
|
||||
|
||||
if ($actor->isProtecting) {
|
||||
$out->writeln("║ 伤害减免: {$this->green}20%{$this->reset} | 伤害增加: {$this->red}30%{$this->reset}");
|
||||
}
|
||||
|
||||
$out->writeln("║ 物攻: {$this->yellow}{$stats['patk']}{$this->reset}");
|
||||
$out->writeln("║ 魔攻: {$this->blue}{$stats['matk']}{$this->reset}");
|
||||
$out->writeln("║ 物防: {$this->yellow}{$stats['pdef']}{$this->reset}");
|
||||
|
|
@ -268,7 +277,11 @@ class StatsPanel
|
|||
// 显示操作选项 - 统一玩家和队友的操作
|
||||
$out->writeln("[1] 装备物品 | [2] 卸下装备 | [3] 强化装备");
|
||||
$out->writeln("[5] 装备技能 | [6] 卸下技能 | [7] 强化技能");
|
||||
|
||||
|
||||
// 防护状态切换
|
||||
$protectToggle = $actor->isProtecting ? "p] 退出防护" : "[p] 进入防护";
|
||||
$out->writeln("[{$protectToggle}");
|
||||
|
||||
if ($this->isPlayer) {
|
||||
$out->writeln("[s] 切换角色 | [0] 返回");
|
||||
} else {
|
||||
|
|
@ -281,6 +294,20 @@ class StatsPanel
|
|||
return $this->showCharacterSelect();
|
||||
}
|
||||
|
||||
// 处理防护状态切换
|
||||
if (strtolower($choice) === 'p') {
|
||||
if ($actor->isProtecting) {
|
||||
$actor->exitProtectMode();
|
||||
$out->writeln("{$this->green}已退出防护状态{$this->reset}");
|
||||
} else {
|
||||
$actor->enterProtectMode();
|
||||
$out->writeln("{$this->green}已进入防护状态{$this->reset}");
|
||||
}
|
||||
$this->game->saveState();
|
||||
Screen::pause($out);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 统一处理操作
|
||||
switch ($choice) {
|
||||
case '1':
|
||||
|
|
@ -328,7 +355,7 @@ class StatsPanel
|
|||
foreach ($slots as $slotKey => $slotName) {
|
||||
$currentItem = $actor->equip[$slotKey] ?? null;
|
||||
if ($currentItem) {
|
||||
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . ItemDisplay::formatName($currentItem));
|
||||
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . ItemDisplay::renderListItem($currentItem));
|
||||
} else {
|
||||
$this->game->output->writeln("[{$slotIdx}] {$slotName}: (空)");
|
||||
}
|
||||
|
|
@ -424,7 +451,7 @@ class StatsPanel
|
|||
foreach ($slots as $slot => $name) {
|
||||
if (!empty($actor->equip[$slot])) {
|
||||
$item = $actor->equip[$slot];
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::formatName($item));
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::renderListItem($item));
|
||||
$equipped[$idx] = $slot;
|
||||
$idx++;
|
||||
}
|
||||
|
|
@ -509,7 +536,7 @@ class StatsPanel
|
|||
if (!empty($actor->equip[$slot])) {
|
||||
$item = $actor->equip[$slot];
|
||||
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::formatName($item) . " {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::renderListItem($item) . " {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||
$equipped[$idx] = $slot;
|
||||
$idx++;
|
||||
}
|
||||
|
|
@ -566,7 +593,7 @@ class StatsPanel
|
|||
$out->writeln("{$this->cyan} 装 备 强 化 等 级 选 择{$this->reset}");
|
||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("装备: " . ItemDisplay::formatName($item));
|
||||
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
|
||||
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("可选等级:");
|
||||
|
|
@ -672,7 +699,7 @@ class StatsPanel
|
|||
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("装备: " . ItemDisplay::formatName($item));
|
||||
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
|
||||
$out->writeln("目标: +{$targetLevel}");
|
||||
$out->writeln("");
|
||||
|
||||
|
|
@ -803,7 +830,7 @@ class StatsPanel
|
|||
foreach ($skillSlots as $slot) {
|
||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($currentSpell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($currentSpell));
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
|
||||
} else {
|
||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||
}
|
||||
|
|
@ -849,7 +876,7 @@ class StatsPanel
|
|||
foreach ($skillSlots as $slot) {
|
||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($currentSpell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($currentSpell));
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
|
||||
$hasSkill = true;
|
||||
} else {
|
||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||
|
|
@ -901,7 +928,7 @@ class StatsPanel
|
|||
foreach ($skillSlots as $slot) {
|
||||
$spell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($spell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($spell));
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($spell));
|
||||
$equipped[$i] = $slot;
|
||||
$hasSkill = true;
|
||||
} else {
|
||||
|
|
@ -951,7 +978,7 @@ class StatsPanel
|
|||
$out->writeln("{$this->cyan} 技 能 强 化 等 级 选 择{$this->reset}");
|
||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("技能: " . ItemDisplay::formatName($item));
|
||||
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
|
||||
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("可选等级:");
|
||||
|
|
@ -1053,7 +1080,7 @@ class StatsPanel
|
|||
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||
$out->writeln("");
|
||||
$out->writeln("技能: " . ItemDisplay::formatName($item));
|
||||
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
|
||||
$out->writeln("目标: +{$targetLevel}");
|
||||
$out->writeln("");
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,86 @@ use Game\Entities\Item;
|
|||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$monster = \Game\Entities\Monster::create(1);
|
||||
$monster = \Game\Entities\Monster::create(6);
|
||||
dd($monster->getRandomEquipmentDrops(100));
|
||||
//$player = new \Game\Entities\Player();
|
||||
|
||||
$player = new \Game\Entities\Player();
|
||||
|
||||
$weaponTemplate = [
|
||||
'fixed_primary' => [
|
||||
'patk' => ['base' => [4, 10, 18, 30], 'growth' => 1.5],
|
||||
'matk' => ['base' => [2, 5, 10, 18], 'growth' => 0.8],
|
||||
],
|
||||
'random_primary_pool' => [
|
||||
'crit' => ['weight' => 40, 'base' => [1, 3, 5, 10], 'growth' => 0.3],
|
||||
'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' => [
|
||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2]
|
||||
],
|
||||
'affix_weights' => ['patk' => 30, 'matk' => 20, 'crit' => 25, 'critdmg' => 25, 'hp' => 10],
|
||||
];
|
||||
|
||||
$armorTemplate = [
|
||||
'fixed_primary' => [
|
||||
'pdef' => ['base' => [2, 6, 12, 20], 'growth' => 0.6],
|
||||
'mdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.5],
|
||||
],
|
||||
'random_primary_pool' => [
|
||||
'hp' => ['weight' => 100, 'base' => [10, 25, 45, 75], 'growth' => 3.5],
|
||||
],
|
||||
'random_primary_count' => [
|
||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 1],
|
||||
],
|
||||
'affix_weights' => ['pdef' => 30, 'mdef' => 30, 'hp' => 40, 'patk' => 5, 'matk' => 5],
|
||||
];
|
||||
|
||||
$ringTemplate = [
|
||||
'fixed_primary' => [
|
||||
'crit' => ['base' => [2, 5, 8, 12], 'growth' => 0.4],
|
||||
],
|
||||
'random_primary_pool' => [
|
||||
'critdmg' => ['weight' => 40, 'base' => [5, 12, 20, 35], 'growth' => 0.8],
|
||||
'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' => [
|
||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||
],
|
||||
'affix_weights' => ['crit' => 30, 'critdmg' => 30, 'patk' => 20, 'matk' => 20],
|
||||
];
|
||||
|
||||
$necklaceTemplate = [
|
||||
'fixed_primary' => [
|
||||
'hp' => ['base' => [15, 35, 60, 100], 'growth' => 4.0],
|
||||
],
|
||||
'random_primary_pool' => [
|
||||
'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],
|
||||
],
|
||||
'random_primary_count' => [
|
||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||
],
|
||||
'affix_weights' => ['hp' => 30, 'pdef' => 20, 'mdef' => 20, 'crit' => 15, 'critdmg' => 15],
|
||||
];
|
||||
|
||||
$bootsTemplate = [
|
||||
'fixed_primary' => [
|
||||
'pdef' => ['base' => [1, 4, 8, 15], 'growth' => 0.4],
|
||||
'mdef' => ['base' => [1, 3, 6, 12], 'growth' => 0.3],
|
||||
],
|
||||
'random_primary_pool' => [
|
||||
'hp' => ['weight' => 60, 'base' => [8, 20, 35, 60], 'growth' => 2.5],
|
||||
'crit' => ['weight' => 40, 'base' => [1, 3, 5, 8], 'growth' => 0.2],
|
||||
],
|
||||
'random_primary_count' => [
|
||||
'common' => [0, 0], 'rare' => [0, 1], 'epic' => [1, 1], 'legendary' => [1, 2],
|
||||
],
|
||||
'affix_weights' => ['pdef' => 25, 'mdef' => 25, 'hp' => 30, 'crit' => 20],
|
||||
];
|
||||
|
||||
$res = Item::createSpell(1, 'common', 10);
|
||||
dd($monster->equip,$monster->getStats(),$monster->name,$monster->skillSlots);
|
||||
// dd($monster->getRandomEquipmentDrops(100));
|
||||
dd(Item::randomItem('armor', 30));
|
||||
Loading…
Reference in New Issue
Block a user