This commit is contained in:
hant 2025-12-08 23:25:06 +08:00
parent 5136e55932
commit b022abd6a9
8 changed files with 852 additions and 788 deletions

View File

@ -24,13 +24,14 @@ class ItemDisplay
// 类型名称 // 类型名称
private static array $typeNames = [ private static array $typeNames = [
'weapon' => '武器', 'weapon' => '武器',
'armor' => '护甲', 'armor' => '护甲',
'boots' => '靴子', 'boots' => '靴子',
'ring' => '戒指', 'ring' => '戒指',
'necklace' => '项链', 'necklace' => '项链',
'consume' => '消耗品', 'consume' => '消耗品',
'spell' => '法术', 'spell' => '法术',
'quest_item' => '任务物品',
]; ];
// 属性名称 // 属性名称
@ -84,6 +85,11 @@ class ItemDisplay
*/ */
public static function formatName(array $item): string public static function formatName(array $item): string
{ {
// 任务物品不显示等级
if (($item['type'] ?? '') === 'quest_item') {
return self::$magenta . $item['name'] . self::$reset;
}
$quality = $item['quality'] ?? $item['rarity'] ?? 'common'; $quality = $item['quality'] ?? $item['rarity'] ?? 'common';
$color = self::getQualityColor($quality); $color = self::getQualityColor($quality);
$name = ($item['name'] ?? '未知物品') . 'lv.' . $item['level']; $name = ($item['name'] ?? '未知物品') . 'lv.' . $item['level'];
@ -212,6 +218,17 @@ class ItemDisplay
// 主属性(简洁版)或法术信息 // 主属性(简洁版)或法术信息
$type = $item['type'] ?? ''; $type = $item['type'] ?? '';
// 特殊处理任务物品
if ($type === 'quest_item') {
$parts = [];
$parts[] = self::$magenta . "【任务物品】" . self::$reset . " {$item['name']}";
if ($showType) {
$parts[] = self::$gray . "[任务物品]" . self::$reset;
}
return implode(" ", $parts);
}
if ($type === 'spell') { if ($type === 'spell') {
// 法术显示由 SpellDisplay 处理(计算方式和基础数值) // 法术显示由 SpellDisplay 处理(计算方式和基础数值)
// 需要导入 SpellDisplay 后使用 // 需要导入 SpellDisplay 后使用
@ -270,6 +287,17 @@ class ItemDisplay
$type = $item['type'] ?? ''; $type = $item['type'] ?? '';
$typeName = self::getTypeName($type); $typeName = self::getTypeName($type);
// 特殊处理任务物品
if ($type === 'quest_item') {
$lines[] = $linePrefix . self::$magenta . "【任务物品】" . self::$reset . " {$item['name']}";
$lines[] = $linePrefix;
$lines[] = $linePrefix . "这是一个关键任务物品,";
$lines[] = $linePrefix . "用于解锁新的探险区域。";
$lines[] = $linePrefix;
$lines[] = $linePrefix . "无法装备、无法出售、无法交易。";
return $lines;
}
$enhanceLevel = $item['enhanceLevel'] ?? 0; $enhanceLevel = $item['enhanceLevel'] ?? 0;
// 名称行 // 名称行
@ -371,6 +399,11 @@ class ItemDisplay
*/ */
public static function renderDrop(array $item, string $prefix = " "): string public static function renderDrop(array $item, string $prefix = " "): string
{ {
// 特殊处理任务物品
if ($item['type'] === 'quest_item') {
return $prefix . "📜 " . self::$magenta . "【任务物品】" . self::$reset . " {$item['name']}";
}
$quality = $item['quality'] ?? $item['rarity'] ?? 'common'; $quality = $item['quality'] ?? $item['rarity'] ?? 'common';
$color = self::getQualityColor($quality); $color = self::getQualityColor($quality);
if ($item['type'] == 'potion_pool'){ if ($item['type'] == 'potion_pool'){

File diff suppressed because it is too large Load Diff

View File

@ -243,11 +243,11 @@ class Actor
{ {
// 多项式公式,避免过度指数增长 // 多项式公式,避免过度指数增长
// base: 100 (1级升2级的基础经验) // base: 100 (1级升2级的基础经验)
// linear: 10 * level (线性增长部分) // linear: 100 * level (线性增长部分)
// quadratic: 0.5 * level² (二次增长部分增长速度比1.2x指数慢得多) // quadratic: level * $level * $level (二次增长部分增长速度比1.2x指数慢得多)
$base = 100; $base = 100;
$linear = 10 * $level; $linear = 100 * $level;
$quadratic = (int)(0.5 * $level * $level); $quadratic = (int)($level * $level * $level);
return $base + $linear + $quadratic; return $base + $linear + $quadratic;
} }

View File

@ -232,6 +232,24 @@ class Item
return self::createFromSpec($spec, $baseLevel); return self::createFromSpec($spec, $baseLevel);
} }
/**
* 创建任务道具 - 用于地图解锁的特殊物品
* @param string $name 任务物品名称
* @return array 任务物品数组
*/
public static function createQuestItem(string $name): array
{
return [
'id' => uniqid('quest_'),
'type' => 'quest_item',
'name' => $name,
'quality' => 'legendary',
'level' => 1,
'quantity' => 1,
'desc' => '关键任务物品,用于解锁新区域',
];
}
/** /**
* 创建法术物品 - 支持新的丰富法术系统 * 创建法术物品 - 支持新的丰富法术系统
* @param int $spellId 法术ID * @param int $spellId 法术ID

View File

@ -152,6 +152,16 @@ class Monster extends Actor
$type = $drop['type'] ?? ''; $type = $drop['type'] ?? '';
$rate = $drop['rate'] ?? 0; $rate = $drop['rate'] ?? 0;
// 处理任务物品
if ($type === 'quest_item') {
$item = Item::createQuestItem($drop['name'] ?? '未知道具');
$this->dropTable[] = [
'item' => $item,
'rate' => $rate,
];
continue;
}
if ($type === 'consume') { if ($type === 'consume') {
$spec = $drop; $spec = $drop;
unset($spec['rate']); unset($spec['rate']);

View File

@ -9,6 +9,33 @@ class DungeonSelectPanel
{ {
public function __construct(public Game $game) {} public function __construct(public Game $game) {}
/**
* 检查玩家是否拥有指定物品
*/
private function hasItem(string $itemName): bool
{
foreach ($this->game->player->inventory as $item) {
if (($item['name'] ?? '') === $itemName) {
return true;
}
}
return false;
}
/**
* 检查玩家是否可以进入某个副本
*/
private function canEnterDungeon(array $map): bool
{
// 如果没有key_item要求可以进入
if ($map['key_item'] === null) {
return true;
}
// 检查玩家是否拥有该钥匙
return $this->hasItem($map['key_item']);
}
public function show() public function show()
{ {
Screen::clear($this->game->output); Screen::clear($this->game->output);
@ -20,8 +47,18 @@ class DungeonSelectPanel
$out->writeln(""); $out->writeln("");
$maps = require __DIR__ . '/../../src/Data/maps.php'; $maps = require __DIR__ . '/../../src/Data/maps.php';
$availableMaps = [];
// 只显示已解锁的地图
foreach ($maps as $id => $map) { foreach ($maps as $id => $map) {
$out->writeln("[{$id}] {$map['name']} (Lv.{$map['min_level']})"); if ($this->canEnterDungeon($map)) {
$availableMaps[$id] = $map;
$out->writeln("[{$id}] {$map['name']} (Lv.{$map['min_level']})");
} else {
// 显示未解锁的地图
$keyItem = $map['key_item'];
$out->writeln("[\033[90m{$id}\033[0m] {$map['name']} (未解锁 - 需要: {$keyItem})");
}
} }
$out->writeln(""); $out->writeln("");
@ -34,12 +71,25 @@ class DungeonSelectPanel
return; return;
} }
if (isset($maps[$dungeonId])) { // 验证选择的副本是否存在
$this->game->dungeonId = (int)$dungeonId; if (!isset($maps[$dungeonId])) {
$this->game->state = Game::BATTLE; $out->writeln("\033[91m无效副本\033[0m");
} else {
$out->writeln("无效副本");
Screen::sleep(1); Screen::sleep(1);
return;
} }
$selectedMap = $maps[$dungeonId];
// 检查是否有权限进入
if (!$this->canEnterDungeon($selectedMap)) {
$out->writeln("\033[91m该副本尚未解锁\033[0m");
$out->writeln("需要物品: \033[93m{$selectedMap['key_item']}\033[0m");
Screen::sleep(2);
return;
}
// 进入副本
$this->game->dungeonId = (int)$dungeonId;
$this->game->state = Game::BATTLE;
} }
} }

View File

@ -30,6 +30,7 @@ class InventoryPanel
'necklace' => '项链', 'necklace' => '项链',
'equipment' => '装备', 'equipment' => '装备',
'consume' => '消耗品', 'consume' => '消耗品',
'quest_item' => '任务物品',
]; ];
public function __construct(public Game $game) {} public function __construct(public Game $game) {}
@ -48,7 +49,8 @@ class InventoryPanel
// Filter items by category // Filter items by category
if ($category == 'equipment'){ if ($category == 'equipment'){
$items = array_values(array_filter($allItems, fn($item) => $item['type'] !== 'consume')); $items = array_values(array_filter($allItems, fn($item) =>
$item['type'] !== 'consume' && $item['type'] !== 'quest_item'));
}else{ }else{
$items = $category === 'all' $items = $category === 'all'
? $allItems ? $allItems
@ -157,30 +159,44 @@ class InventoryPanel
$out->writeln("║ 拥有数量: {$quantity}"); $out->writeln("║ 拥有数量: {$quantity}");
} }
// 计算售价 // 任务物品不显示售价
$sellPrice = \Game\Entities\Item::calculateSellPrice($item); if ($item['type'] !== 'quest_item') {
$out->writeln(""); // 计算售价
$out->writeln("║ 💰 售价: {$sellPrice} 灵石"); $sellPrice = \Game\Entities\Item::calculateSellPrice($item);
$out->writeln("");
$out->writeln("║ 💰 售价: {$sellPrice} 灵石");
}
$out->writeln("╚════════════════════════════════════════╝"); $out->writeln("╚════════════════════════════════════════╝");
$out->writeln(""); $out->writeln("");
$isEquip = in_array($item['type'], ['weapon', 'armor', 'boots', 'ring', 'necklace']); $isEquip = in_array($item['type'], ['weapon', 'armor', 'boots', 'ring', 'necklace']);
$isQuestItem = $item['type'] === 'quest_item';
if ($isEquip) { if ($isQuestItem) {
$out->writeln("[1] 装备"); // 任务物品无法操作
} elseif ($item['type'] === 'consume') { $out->writeln("这是一个关键任务物品,无法进行任何操作。");
$out->writeln("[1] 使用"); $out->writeln("");
$out->writeln("[0] 返回");
} else {
if ($isEquip) {
$out->writeln("[1] 装备");
} elseif ($item['type'] === 'consume') {
$out->writeln("[1] 使用");
}
$out->writeln("[2] 出售");
$out->writeln("[3] 丢弃");
$out->writeln("[0] 返回");
} }
$out->writeln("[2] 出售");
$out->writeln("[3] 丢弃");
$out->writeln("[0] 返回");
$choice = Screen::input($out, "选择操作:"); $choice = Screen::input($out, "选择操作:");
if ($choice == 1) $this->useItem($item, $inventoryIndex); if (!$isQuestItem) {
if ($choice == 2) $this->sellItem($item, $inventoryIndex); if ($choice == 1) $this->useItem($item, $inventoryIndex);
if ($choice == 3) $this->dropItem($item, $inventoryIndex); if ($choice == 2) $this->sellItem($item, $inventoryIndex);
if ($choice == 3) $this->dropItem($item, $inventoryIndex);
}
return $this->show($page, $category); return $this->show($page, $category);
} }

View File

@ -5,6 +5,7 @@ use Game\Entities\Item;
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
$monster = \Game\Entities\Monster::create(6); $monster = \Game\Entities\Monster::create(6);
dd($monster->getTotalExpForLevel(10) - $monster->getTotalExpForLevel(9));
dd($monster->getRandomEquipmentDrops(100)); dd($monster->getRandomEquipmentDrops(100));
//$player = new \Game\Entities\Player(); //$player = new \Game\Entities\Player();