hanli/src/Core/ItemDisplay.php
hant 911f814581 角色面板增强:技能槽位显示法术的计算方式和基础数值
新增功能:
- ItemDisplay.renderSlot: 增强支持法术物品显示
  - 法术显示计算方式描述
  - 法术显示品质相关的基础数值
  - 法术显示消耗(含强化后的实际消耗)
  - 装备仍显示属性和词条(无改动)
- StatsPanel 导入 SpellDisplay 以支持未来扩展

改进内容:
- 角色面板技能栏显示法术的详细信息
- 显示14种计算方式的完整描述
- 显示伤害倍数或治疗系数(根据品质)
- 显示强化前后的魔法消耗变化
- 保持与战斗系统显示的一致性

显示格式示例:
[技能1] 火球术 Lv.5 +2
  计算: 基于魔攻
  倍数: 2.0x
  消耗: 20 → 16

[技能2] 恢复术 Lv.3
  计算: 基于魔攻
  治疗: 0.8x + 40
  消耗: 15

🧙 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 23:11:49 +08:00

535 lines
20 KiB
PHP

<?php
namespace Game\Core;
/**
* 统一的装备显示工具类
*/
class ItemDisplay
{
// 品质颜色
private static array $qualityColors = [
'common' => Colors::WHITE,
'rare' => Colors::BLUE,
'epic' => Colors::MAGENTA,
'legendary' => Colors::YELLOW,
];
// 品质名称
private static array $qualityNames = [
'common' => '普通',
'rare' => '稀有',
'epic' => '史诗',
'legendary' => '传说',
];
// 类型名称
private static array $typeNames = [
'weapon' => '武器',
'armor' => '护甲',
'boots' => '靴子',
'ring' => '戒指',
'necklace' => '项链',
'consume' => '消耗品',
'spell' => '法术',
];
// 属性名称
private static array $statNames = [
'patk' => '物攻',
'matk' => '魔攻',
'pdef' => '物防',
'mdef' => '魔防',
'hp' => '生命',
'crit' => '暴击',
'critdmg' => '暴伤',
'heal' => '治疗',
'cost' => '消耗',
'damage' => '伤害倍率',
];
// 颜色常量
private static string $reset = Colors::RESET;
private static string $yellow = Colors::YELLOW;
private static string $green = Colors::GREEN;
private static string $cyan = Colors::CYAN;
private static string $white = Colors::WHITE;
private static string $gray = Colors::GRAY;
/**
* 获取品质颜色
*/
public static function getQualityColor(string $quality): string
{
return self::$qualityColors[$quality] ?? self::$white;
}
/**
* 获取品质名称
*/
public static function getQualityName(string $quality): string
{
return self::$qualityNames[$quality] ?? '普通';
}
/**
* 获取类型名称
*/
public static function getTypeName(string $type): string
{
return self::$typeNames[$type] ?? ucfirst($type);
}
/**
* 格式化装备名称(带品质颜色和强化等级)
*/
public static function formatName(array $item): string
{
$quality = $item['quality'] ?? $item['rarity'] ?? 'common';
$color = self::getQualityColor($quality);
$name = ($item['name'] ?? '未知物品') . 'lv.' . $item['level'];
$enhanceLevel = $item['enhanceLevel'] ?? 0;
$enhanceStr = $enhanceLevel > 0 ? self::$yellow . "+{$enhanceLevel}" . self::$reset : "";
return $color . $name . self::$reset . $enhanceStr;
}
/**
* 格式化主属性(单行简洁版)
*/
public static function formatStatsCompact(array $item): string
{
$stats = [];
foreach (self::$statNames as $key => $name) {
$value = $item[$key] ?? 0;
if ($value > 0) {
if ($key === 'damage') {
$stats[] = "{$name}:{$value}x";
} else {
$stats[] = "{$name}+{$value}";
}
}
}
return $stats ? self::$green . "(" . implode(" ", $stats) . ")" . self::$reset : "";
}
/**
* 格式化法术的简洁信息(用于列表显示)
*/
public static function formatSpellCompact(array $spell): string
{
$calcType = $spell['calc_type'] ?? 'matk';
$spellType = $spell['spellType'] ?? $spell['type'] ?? 'unknown';
$calcTypeMap = [
'matk' => '魔攻',
'patk' => '物攻',
'hybrid' => '混合',
'hp_percent' => 'HP%',
'crit_heal' => '暴治',
'crit_damage' => '暴伤',
'defense' => '防御',
'low_def_bonus' => '克低防',
'matk_scaled' => '群伤',
'dispersed_damage' => '分散伤',
'crit_aoe' => '暴范',
'smart_heal' => '智能治',
'hp_missing' => '缺血治',
'team_sync' => '队伍同步',
];
$calcDesc = $calcTypeMap[$calcType] ?? $calcType;
// 根据法术类型显示基础数值
$valueStr = '';
if ($spellType === 'damage_single' || $spellType === 'damage_aoe') {
$damageRatio = $spell['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6];
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
$ratio = $damageRatio[$qualityIndex] ?? 1.0;
$valueStr = self::$green . "{$ratio}x" . self::$reset;
} elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') {
$healRatio = $spell['heal_ratio'] ?? [0.5, 0.8, 1.2, 1.8];
$qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common');
$ratio = $healRatio[$qualityIndex] ?? 0.5;
$valueStr = self::$green . "{$ratio}x" . self::$reset;
}
return self::$cyan . "[{$calcDesc}]" . self::$reset . ($valueStr ? " " . $valueStr : "");
}
/**
* 格式化主属性(多行详细版)
*/
public static function formatStatsDetailed(array $item, string $prefix = " "): array
{
$lines = [];
$enhanceLevel = $item['enhanceLevel'] ?? 0;
// 法术强化逻辑不同
$isSpell = ($item['type'] ?? '') === 'spell';
if ($isSpell) {
// 法术强化: 伤害+5%, 消耗-2
$damageMultiplier = 1 + ($enhanceLevel * 0.05);
$costReduction = $enhanceLevel * 2;
foreach (self::$statNames as $key => $name) {
$baseValue = $item[$key] ?? 0;
if ($baseValue > 0) {
if ($key === 'damage') {
$finalValue = number_format($baseValue * $damageMultiplier, 2);
if ($enhanceLevel > 0) {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$white . $baseValue . "x" . self::$reset . "" .
self::$green . $finalValue . "x" . self::$reset;
} else {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$green . $baseValue . "x" . self::$reset;
}
} elseif ($key === 'cost') {
$finalValue = max(1, $baseValue - $costReduction);
if ($enhanceLevel > 0) {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$white . $baseValue . self::$reset . "" .
self::$green . $finalValue . self::$reset;
} else {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$green . $baseValue . self::$reset;
}
} else {
// For other stats on spells, just display the base value
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$green . $baseValue . self::$reset;
}
}
}
} else {
// 装备强化逻辑
$enhanceMultiplier = 1 + ($enhanceLevel * 0.05);
foreach (self::$statNames as $key => $name) {
$baseValue = $item[$key] ?? 0;
if ($baseValue > 0) {
$finalValue = (int)($baseValue * $enhanceMultiplier);
if ($enhanceLevel > 0 && $key !== 'heal') {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$white . $baseValue . self::$reset . "" .
self::$green . $finalValue . self::$reset;
} else {
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
self::$green . $baseValue . self::$reset;
}
}
}
}
return $lines;
}
/**
* 格式化词条
*/
public static function formatAffixes(array $item, string $prefix = " "): array
{
$lines = [];
$affixes = $item['affixes'] ?? [];
if (!empty($affixes)) {
foreach ($affixes as $affix) {
$lines[] = $prefix . self::$yellow . "" . self::$reset . $affix;
}
}
return $lines;
}
/**
* 渲染物品简略信息(用于列表)
* 格式: [品质色]名称[/品质色]+强化 (类型) 主属性 <词条数>
*/
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') {
// 显示法术的计算方式和基础数值
$spellInfo = self::formatSpellCompact($item);
if ($spellInfo) {
$parts[] = $spellInfo;
}
} else {
// 显示装备的属性
$statsStr = self::formatStatsCompact($item);
if ($statsStr) {
$parts[] = $statsStr;
}
}
// 词条数量提示(仅限装备)
if ($type !== 'spell') {
$affixes = $item['affixes'] ?? [];
if (!empty($affixes)) {
$count = count($affixes);
$parts[] = self::$cyan . implode(',',$affixes);
}
}
return implode(" ", $parts);
}
/**
* 渲染物品详细信息(用于详情面板)
* 返回多行数组
*/
public static function renderDetail(array $item, string $linePrefix = ""): array
{
$lines = [];
$quality = $item['quality'] ?? $item['rarity'] ?? 'common';
$qualityColor = self::getQualityColor($quality);
$qualityName = self::getQualityName($quality);
$type = $item['type'] ?? '';
$typeName = self::getTypeName($type);
$enhanceLevel = $item['enhanceLevel'] ?? 0;
// 名称行
$lines[] = $linePrefix . self::formatName($item);
$lines[] = $linePrefix;
// 品质和类型
$lines[] = $linePrefix . "品质: " . $qualityColor . $qualityName . self::$reset;
$lines[] = $linePrefix . "类型: " . self::$white . $typeName . self::$reset;
// 强化等级
if ($enhanceLevel > 0) {
$bonus = $enhanceLevel * 5;
$lines[] = $linePrefix . "强化: " . self::$yellow . "+{$enhanceLevel}" . self::$reset .
self::$gray . " (属性+{$bonus}%)" . self::$reset;
}
$lines[] = $linePrefix;
// 法术特殊显示
if ($type === 'spell') {
$lines[] = $linePrefix . self::$white . "--- 法术信息 ---" . self::$reset;
$spellType = $item['spellType'] ?? $item['type'] ?? 'unknown';
$calcType = $item['calc_type'] ?? 'unknown';
// 计算方式映射
$calcTypeMap = [
'matk' => '基于魔攻',
'patk' => '基于物攻',
'hp_percent' => '基于最大生命值百分比',
'hybrid' => '基于(魔攻+物攻)混合',
'crit_heal' => '暴击率影响治疗效果',
'crit_damage' => '暴击伤害系数影响伤害',
'crit_aoe' => '暴击率影响范围伤害',
'defense' => '基于防御属性',
'low_def_bonus' => '对低防御敌人伤害加成',
'matk_scaled' => '随敌人数量加成',
'dispersed_damage' => '伤害分散到所有敌人',
'smart_heal' => '智能治疗(优先低血量)',
'hp_missing' => '基于缺失生命值',
'team_sync' => '基于队伍规模',
];
$calcDesc = $calcTypeMap[$calcType] ?? $calcType;
$lines[] = $linePrefix . " 计算方式: " . self::$cyan . $calcDesc . self::$reset;
// 基础数值
if ($spellType === 'damage_single' || $spellType === 'damage_aoe') {
$damageRatio = $item['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6];
$qualityIndex = self::getQualityIndex($quality);
$ratio = $damageRatio[$qualityIndex] ?? 1.0;
$lines[] = $linePrefix . " 伤害倍数: " . self::$green . $ratio . "x" . self::$reset;
} elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') {
$healRatio = $item['heal_ratio'] ?? [0.5, 0.8, 1.2, 1.8];
$healBase = $item['heal_base'] ?? [20, 40, 70, 120];
$qualityIndex = self::getQualityIndex($quality);
$ratio = $healRatio[$qualityIndex] ?? 0.5;
$base = $healBase[$qualityIndex] ?? 20;
$lines[] = $linePrefix . " 治疗系数: " . self::$green . $ratio . "x + " . $base . self::$reset;
}
$lines[] = $linePrefix;
}
// 主属性
$lines[] = $linePrefix . self::$white . "--- 主属性 ---" . self::$reset;
$statLines = self::formatStatsDetailed($item, $linePrefix . " ");
if (!empty($statLines)) {
$lines = array_merge($lines, $statLines);
} else {
$lines[] = $linePrefix . " " . self::$gray . "(无)" . self::$reset;
}
// 词条
$affixes = $item['affixes'] ?? [];
if (!empty($affixes)) {
$lines[] = $linePrefix;
$lines[] = $linePrefix . self::$white . "--- 词条 ---" . self::$reset;
$affixLines = self::formatAffixes($item, $linePrefix . " ");
$lines = array_merge($lines, $affixLines);
}
// 描述
$desc = $item['desc'] ?? '';
if ($desc) {
$lines[] = $linePrefix;
$lines[] = $linePrefix . self::$gray . $desc . self::$reset;
}
return $lines;
}
/**
* 渲染装备槽位信息(用于属性面板)
*/
public static function renderSlot(string $slotName, ?array $item, string $linePrefix = ""): array
{
$lines = [];
if ($item) {
$type = $item['type'] ?? '';
// 第一行:槽位名 + 装备/法术名
$lines[] = $linePrefix . self::$cyan . $slotName . self::$reset . ": " . self::formatName($item);
if ($type === 'spell') {
// 法术显示:计算方式和基础数值
$calcType = $item['calc_type'] ?? 'matk';
$spellType = $item['spellType'] ?? $item['type'] ?? 'unknown';
// 计算方式的完整描述
$calcTypeDescMap = [
'matk' => '基于魔攻',
'patk' => '基于物攻',
'hybrid' => '混合伤害',
'hp_percent' => '基于HP%',
'crit_heal' => '暴击治疗',
'crit_damage' => '暴击伤害',
'defense' => '基于防御',
'low_def_bonus' => '克低防',
'matk_scaled' => '群体伤害',
'dispersed_damage' => '分散伤害',
'crit_aoe' => '暴击范围',
'smart_heal' => '智能治疗',
'hp_missing' => '缺血治疗',
'team_sync' => '队伍同步',
];
$calcDesc = $calcTypeDescMap[$calcType] ?? $calcType;
$lines[] = $linePrefix . " " . self::$white . "计算: " . self::$green . $calcDesc . self::$reset;
// 显示基础数值
if ($spellType === 'damage_single' || $spellType === 'damage_aoe') {
$damageRatio = $item['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6];
$qualityIndex = self::getQualityIndex($item['quality'] ?? 'common');
$ratio = $damageRatio[$qualityIndex] ?? 1.0;
$lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "{$ratio}x" . self::$reset;
} elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') {
$healRatio = $item['heal_ratio'] ?? [0.5, 0.8, 1.2, 1.8];
$healBase = $item['heal_base'] ?? [20, 40, 70, 120];
$qualityIndex = self::getQualityIndex($item['quality'] ?? 'common');
$ratio = $healRatio[$qualityIndex] ?? 0.5;
$base = $healBase[$qualityIndex] ?? 20;
$lines[] = $linePrefix . " " . self::$white . "治疗: " . self::$yellow . "{$ratio}x + {$base}" . self::$reset;
}
// 显示消耗
$cost = $item['cost'] ?? 0;
$enhanceLevel = $item['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;
} else {
$lines[] = $linePrefix . " " . self::$white . "消耗: " . self::$green . "{$actualCost}" . self::$reset;
}
} else {
// 装备显示:属性和词条
$statsStr = self::formatStatsCompact($item);
if ($statsStr) {
$lines[] = $linePrefix . " " . $statsStr;
}
// 词条
$affixes = $item['affixes'] ?? [];
foreach ($affixes as $affix) {
$lines[] = $linePrefix . " " . self::$yellow . "" . self::$reset . " " . $affix;
}
}
} else {
$lines[] = $linePrefix . self::$cyan . $slotName . self::$reset . ": " .
self::$gray . "(空)" . self::$reset;
}
return $lines;
}
/**
* 渲染战斗掉落物品(简洁版)
*/
public static function renderDrop(array $item, string $prefix = " "): string
{
$quality = $item['quality'] ?? $item['rarity'] ?? 'common';
$color = self::getQualityColor($quality);
$name = ($item['name'] ?? '未知物品') . 'lv.' . $item['level'];
$enhanceLevel = $item['enhanceLevel'] ?? 0;
$enhanceStr = $enhanceLevel > 0 ? self::$yellow . "+{$enhanceLevel}" . self::$reset : "";
// 简要属性
$stats = [];
foreach (['patk', 'matk', 'pdef', 'mdef', 'hp'] as $key) {
$value = $item[$key] ?? 0;
if ($value > 0) {
$statName = self::$statNames[$key] ?? $key;
$stats[] = "{$statName}+{$value}";
}
}
$statsStr = $stats ? self::$gray . " (" . implode(" ", $stats) . ")" . self::$reset : "";
// 词条数量
$affixCount = count($item['affixes'] ?? []);
$affixStr = $affixCount > 0 ? self::$cyan . " <{$affixCount}词条>" . self::$reset : "";
return $prefix . "" . $color . $name . self::$reset . $enhanceStr . $statsStr . $affixStr;
}
/**
* 根据品质获取数组索引
*/
private static function getQualityIndex(string $quality): int
{
return match($quality) {
'common' => 0,
'rare' => 1,
'epic' => 2,
'legendary' => 3,
default => 0,
};
}
}