fanren/src/System/SaveLoadService.php
2025-12-18 23:52:11 +08:00

168 lines
6.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Game\System;
use Game\Model\Item;
use Game\Model\Player;
use Game\Event\Event;
use Game\Event\EventDispatcher;
use Game\Model\Quest;
/**
* SaveLoadService: 负责将玩家状态持久化,并支持装备与地图位置的还原。
*/
class SaveLoadService {
private EventDispatcher $dispatcher;
private StateManager $stateManager;
private string $savePath;
public function __construct(EventDispatcher $dispatcher, StateManager $stateManager, string $savePath = 'save/player.json') {
$this->dispatcher = $dispatcher;
$this->stateManager = $stateManager;
$this->savePath = $savePath;
if (!is_dir(dirname($this->savePath))) {
mkdir(dirname($this->savePath), 0777, true);
}
}
public function hasSaveFile(): bool {
return file_exists($this->savePath);
}
/**
* 将完整游戏状态保存
*/
public function saveGame(): void {
$player = $this->stateManager->getPlayer();
$currentTile = $this->stateManager->getCurrentTile();
if (!$player) return;
try {
// 1. 序列化背包
$serializedInventory = array_map(fn(Item $item) => $this->itemToData($item), $player->getInventory());
// 2. ⭐ 新增:序列化当前装备槽位
$serializedEquipment = [];
foreach ($player->getEquipment() as $slot => $item) {
$serializedEquipment[$slot] = $item ? $this->itemToData($item) : null;
}
// 3. 序列化任务
$activeQuests = [];
foreach ($player->getActiveQuests() as $quest) {
$activeQuests[] = [
'config' => $quest->toArray(),
'currentCount' => $quest->getCurrentCount()
];
}
$data = [
'player' => [
'name' => $player->getName(),
'health' => $player->getHealth(),
'maxHealth' => $player->getMaxHealth(),
'base_attack' => $player->attack, // 保存基础值
'base_defense' => $player->defense,
'level' => $player->getLevel(),
'currentXp' => $player->getCurrentXp(),
'gold' => $player->getGold(),
'inventory' => $serializedInventory,
'equipment' => $serializedEquipment, // ⭐ 装备持久化
'activeQuests' => $activeQuests,
'completedQuests' => $player->getCompletedQuests(),
],
'world' => [
'currentTileId' => $currentTile->id // ⭐ 位置持久化
]
];
file_put_contents($this->savePath, json_encode($data, JSON_PRETTY_PRINT));
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '💾 进度已安全存入魔法卷轴!']));
} catch (\Exception $e) {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '❌ 存档失败: ' . $e->getMessage()]));
}
}
/**
* 加载存档并还原所有复杂对象
*/
public function loadGame(): ?Player {
if (!$this->hasSaveFile()) return null;
try {
$data = json_decode(file_get_contents($this->savePath), true);
$pData = $data['player'];
// 1. 创建基础玩家对象
$player = new Player($pData['name'], $pData['maxHealth'], $pData['base_attack'], $pData['base_defense']);
$player->setHealth($pData['health']);
$player->setLevel($pData['level']);
$player->setCurrentXp($pData['currentXp']);
$player->setGold($pData['gold']);
$player->setCompletedQuests($pData['completedQuests'] ?? []);
// 2. 还原背包物品 (包含 Stats 属性)
$inventory = array_map(fn($itemData) => $this->dataToItem($itemData), $pData['inventory']);
$player->setInventory($inventory);
// 3. ⭐ 还原装备 (确保 Stats 效果重新生效)
foreach ($pData['equipment'] as $slot => $itemData) {
if ($itemData) {
$item = $this->dataToItem($itemData);
$player->equipItem($item); // 使用装备方法,确保属性刷新
}
}
// 4. 还原任务进度
$activeQuests = [];
foreach ($pData['activeQuests'] as $qData) {
$config = $qData['config'];
$quest = new Quest($config['id'], $config['name'], $config['description'], $config['type'], $config['target'], $config['rewards']);
$quest->setCurrentCount($qData['currentCount']);
$activeQuests[$config['id']] = $quest;
}
$player->setActiveQuests($activeQuests);
// 5. ⭐ 还原地图位置
if (isset($data['world']['currentTileId'])) {
$this->stateManager->setCurrentTileId($data['world']['currentTileId']);
}
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '📂 欢迎回来,勇者!进度已加载。']));
return $player;
} catch (\Exception $e) {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '❌ 加载失败: ' . $e->getMessage()]));
return null;
}
}
/**
* 辅助Item 转 数组 (包含新增的 stats 字段)
*/
private function itemToData(Item $item): array {
return [
'id' => $item->id,
'name' => $item->name,
'type' => $item->type,
'description' => $item->description,
'value' => $item->value,
'effects' => $item->effects ?? [],
'stats' => $item->stats ?? [], // ⭐ 对应 D 阶段的属性加成
'slot' => $item->slot ?? null
];
}
/**
* 辅助:数组 转 Item
*/
private function dataToItem(array $d): Item {
$item = new Item($d['id'], $d['name'], $d['type'], $d['description'], $d['value'], $d['effects']);
$item->stats = $d['stats'] ?? [];
$item->slot = $d['slot'] ?? null;
return $item;
}
}