PetLogic.php 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  1. <?php
  2. namespace App\Module\Pet\Logic;
  3. use App\Module\GameItems\Dtos\ItemDto;
  4. use App\Module\GameItems\Services\ItemService;
  5. use App\Module\Pet\Enums\PetStatus;
  6. use App\Module\Pet\Events\PetCreatedEvent;
  7. use App\Module\Pet\Events\PetLevelUpEvent;
  8. use App\Module\Pet\Events\PetRemouldEvent;
  9. use App\Module\Pet\Events\PetSkillUsedEvent;
  10. use App\Module\Pet\Events\PetStatusChangedEvent;
  11. use App\Module\Pet\Events\PetUpdateEvent;
  12. use App\Module\Pet\Models\PetConfig;
  13. use App\Module\Pet\Models\PetLevelConfig;
  14. use App\Module\Pet\Models\PetRemouldLog;
  15. use App\Module\Pet\Models\PetSkill;
  16. use App\Module\Pet\Models\PetSkillLog;
  17. use App\Module\Pet\Models\PetUser;
  18. use Exception;
  19. use Illuminate\Support\Facades\DB;
  20. use Illuminate\Support\Facades\Log;
  21. use UCore\Dto\Res;
  22. use UCore\Exception\LogicException;
  23. use Uraus\Kku\Common\DataPetSimple;
  24. /**
  25. * 宠物逻辑类
  26. *
  27. * 处理宠物相关的业务逻辑,包括创建宠物、升级宠物、喂养宠物、
  28. * 洗髓宠物、使用技能等功能。该类是宠物模块内部使用的核心逻辑类,
  29. * 不对外提供服务,由PetService调用。
  30. */
  31. class PetLogic
  32. {
  33. /**
  34. * 构造函数
  35. */
  36. public function __construct()
  37. {
  38. // 构造函数不需要初始化任何属性
  39. }
  40. /**
  41. * 创建宠物
  42. *
  43. * @param int $userId 用户ID
  44. * @param string $name 宠物名称
  45. * @param int|null $grade 宠物品阶,如果为null则随机生成(1一品,2二品,3三品,4四品)
  46. * @param array $options 其他选项
  47. * @return array 创建结果
  48. * @throws Exception
  49. */
  50. public function createPet(int $userId, string $name, ?int $grade = null, array $options = []): array
  51. {
  52. // 验证宠物名称
  53. if (empty($name) || mb_strlen($name) > 20) {
  54. throw new Exception("宠物名称不能为空且不能超过20个字符");
  55. }
  56. // 检查用户宠物数量限制
  57. $petCount = PetUser::where('user_id', $userId)->count();
  58. $maxPets = config('pet.max_pets_per_user', 3);
  59. if ($petCount >= $maxPets) {
  60. throw new Exception("已达到最大宠物数量限制: {$maxPets}");
  61. }
  62. // 如果未指定品阶,则根据概率随机生成
  63. if ($grade === null) {
  64. $grade = $this->generateRandomGrade();
  65. }
  66. // 获取宠物配置
  67. $petConfig = PetConfig::where('pet_type', $options['pet_type'] ?? 'default')->first();
  68. $nextLevelConfig = PetLevelConfig::where('level', 2)->first();
  69. if (!$petConfig) {
  70. $petConfig = PetConfig::first(); // 使用默认配置
  71. if (!$petConfig) {
  72. throw new Exception("宠物配置不存在");
  73. }
  74. }
  75. // 创建宠物
  76. $pet = new PetUser();
  77. $pet->user_id = $userId;
  78. $pet->name = $name;
  79. $pet->grade = $grade;
  80. $pet->level = 1;
  81. $pet->experience = 0;
  82. $pet->max_experience = $nextLevelConfig->exp_required;
  83. $pet->stamina = $petConfig->stamina_max ?? 100;
  84. $pet->max_stamina = $petConfig->stamina_max ?? 100;
  85. $pet->status = PetStatus::NORMAL;
  86. $pet->save();
  87. // 触发宠物创建事件
  88. event(new PetCreatedEvent(
  89. $userId,
  90. $pet->id
  91. ));
  92. Log::info('宠物创建成功', [
  93. 'user_id' => $userId,
  94. 'pet_id' => $pet->id,
  95. 'name' => $name,
  96. 'grade' => $grade
  97. ]);
  98. return [
  99. 'pet_id' => $pet->id,
  100. 'grade' => $grade
  101. ];
  102. }
  103. /**
  104. * 根据概率随机生成宠物品阶
  105. *
  106. * @return int 品阶值(1一品,2二品,3三品,4四品)
  107. */
  108. protected function generateRandomGrade(): int
  109. {
  110. $probabilities = config('pet.grade_probability', [
  111. 1 => 0.6, // 一品阶:60%
  112. 2 => 0.25, // 二品阶:25%
  113. 3 => 0.1, // 三品阶:10%
  114. 4 => 0.05 // 四品阶:5%
  115. ]);
  116. $rand = mt_rand(1, 100) / 100;
  117. $cumulative = 0;
  118. foreach ($probabilities as $grade => $probability) {
  119. $cumulative += $probability;
  120. if ($rand <= $cumulative) {
  121. return $grade;
  122. }
  123. }
  124. // 默认返回一品
  125. return 1;
  126. }
  127. /**
  128. * 宠物升级
  129. *
  130. * @param int $petId 宠物ID
  131. * @return array 升级结果
  132. */
  133. public function levelUpPet(PetUser $pet): Res
  134. {
  135. $change = false;
  136. // 记录旧等级
  137. $oldLevel = $pet->level;
  138. foreach (range(1, 20) as $level) {
  139. // 获取当前等级配置
  140. $currentLevelConfig = PetLevelConfig::where('level', $pet->level)->first();
  141. if (!$currentLevelConfig) {
  142. break;
  143. }
  144. // 获取下一级配置
  145. $nextLevelConfig = PetLevelConfig::where('level', $pet->level + 1)->first();
  146. if (!$nextLevelConfig) {
  147. break;
  148. }
  149. if($pet->experience < $nextLevelConfig->exp_required){
  150. break;
  151. }
  152. // 获取下一级配置
  153. $nextLevelConfig2 = PetLevelConfig::where('level', $pet->level + 2)->first();
  154. // 升级宠物
  155. $pet->level += 1;
  156. $pet->experience = $pet->experience - $nextLevelConfig->exp_required;
  157. if ($nextLevelConfig2) {
  158. $pet->max_experience = $nextLevelConfig2->exp_required;
  159. } else {
  160. $pet->max_experience = 0;
  161. }
  162. $change = true;
  163. }
  164. if ($change) {
  165. $pet->save();
  166. // 获取新解锁的技能
  167. $unlockedSkills = [];
  168. if ($nextLevelConfig->unlock_skills) {
  169. $unlockedSkills = json_decode($nextLevelConfig->unlock_skills, true);
  170. }
  171. // 触发宠物升级事件
  172. event(new PetLevelUpEvent(
  173. $pet->user_id,
  174. $pet->id,
  175. $oldLevel,
  176. $pet->level,
  177. $unlockedSkills
  178. ));
  179. // 触发宠物更新事件,表示宠物数据发生重大变更
  180. event(new PetUpdateEvent(
  181. $pet->user_id,
  182. $pet->id
  183. ));
  184. Log::info('宠物升级成功', [
  185. 'pet_id' => $pet->id,
  186. 'old_level' => $oldLevel,
  187. 'new_level' => $pet->level,
  188. 'unlocked_skills' => $unlockedSkills
  189. ]);
  190. }
  191. return Res::success('');
  192. }
  193. /**
  194. * 宠物喂养
  195. *
  196. * @param int $petId 宠物ID
  197. * @param int $itemId 物品ID(狗粮)
  198. * @param int $amount 数量
  199. * @return array 喂养结果
  200. * @throws Exception
  201. */
  202. public function feedPet(int $petId, int $itemId, int $amount): array
  203. {
  204. // 获取宠物信息
  205. /**
  206. * @var PetUser $pet
  207. */
  208. $pet = PetUser::findOrFail($petId);
  209. // 获取物品信息
  210. /**
  211. * @var ItemDto $item
  212. */
  213. $item = ItemService::getItemInfo($itemId);
  214. if (!$item) {
  215. throw new LogicException("物品不存在");
  216. }
  217. // 消耗物品
  218. $consumeResult = ItemService::consumeItem(
  219. $pet->user_id,
  220. $itemId,
  221. null,
  222. $amount,
  223. [
  224. 'source_type' => 'pet_feed',
  225. 'source_id' => $petId,
  226. 'details' => [ 'pet_id' => $petId ]
  227. ]
  228. );
  229. if (!$consumeResult['success']) {
  230. throw new LogicException("物品消耗失败: " . ($consumeResult['message'] ?? '未知错误'));
  231. }
  232. // 计算获得的经验值和体力
  233. $expGained = ($item->numericAttributes['pet_exp'] ?? 0) * $amount;
  234. $staminaGained = ($item->numericAttributes['pet_power'] ?? 0) * $amount;
  235. if (!$expGained && !$staminaGained) {
  236. throw new LogicException('错误的参数');
  237. }
  238. // 增加经验值
  239. $pet->experience += $expGained;
  240. // 更新体力值
  241. $pet->stamina += $staminaGained;
  242. $pet->save();
  243. // 喂养完成后恢复正常状态
  244. // $pet->refresh();
  245. // $pet->status = PetStatus::NORMAL;
  246. $pet->save();
  247. // 检查升级
  248. $this->check_uplevel($pet);
  249. // 创建旧状态数据
  250. $oldStatusData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  251. $oldStatusData->id = $pet->id;
  252. $oldStatusData->status = PetStatus::FEEDING->value;
  253. // 创建新状态数据
  254. $newStatusData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  255. $newStatusData->id = $pet->id;
  256. $newStatusData->status = PetStatus::NORMAL->value;
  257. // 创建完整的宠物数据
  258. $petData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  259. $petData->id = $pet->id;
  260. $petData->name = $pet->name;
  261. $petData->level = $pet->level;
  262. // $petData->grade = $pet->grade->value;
  263. $petData->power = $pet->stamina;
  264. $petData->exp = $pet->experience;
  265. // 触发宠物状态变更事件
  266. event(new PetStatusChangedEvent(
  267. $pet->user_id,
  268. $pet->id,
  269. $oldStatusData,
  270. $newStatusData,
  271. 'pet_feed_complete',
  272. $petData
  273. ));
  274. Log::info('宠物喂养成功', [
  275. 'pet_id' => $petId,
  276. 'item_id' => $itemId,
  277. 'amount' => $amount,
  278. 'exp_gained' => $expGained,
  279. 'stamina_gained' => $staminaGained,
  280. ]);
  281. return [
  282. 'exp_gained' => $expGained,
  283. 'stamina_gained' => $staminaGained,
  284. ];
  285. }
  286. /**
  287. * 增加宠物经验值
  288. *
  289. * @param int $petId 宠物ID
  290. * @param int $expAmount 经验值数量
  291. * @return bool 是否触发升级
  292. */
  293. protected function check_uplevel(PetUser $pet): Res
  294. {
  295. if ($pet->max_experience === 0) {
  296. return Res::error('最高等级,无法升级');
  297. }
  298. if ($pet->experience >= $pet->max_experience) {
  299. return $this->levelUpPet($pet);
  300. }
  301. return Res::error('经验不足');
  302. }
  303. /**
  304. * 增加宠物体力
  305. *
  306. * @param int $petId 宠物ID
  307. * @param int $staminaAmount 体力数量
  308. * @return int 实际增加的体力值
  309. */
  310. protected function addStamina(PetUser $pet, int $staminaAmount): int
  311. {
  312. // 获取宠物等级配置
  313. $levelConfig = PetLevelConfig::where('level', $pet->level)->first();
  314. $maxStamina = $levelConfig ? ($levelConfig->numeric_attributes->stamina_max ?? 100) : 100;
  315. // 计算实际增加的体力值
  316. $oldStamina = $pet->stamina;
  317. $newStamina = max($maxStamina, $oldStamina + $staminaAmount);
  318. $actualGained = $newStamina - $oldStamina;
  319. return $actualGained;
  320. }
  321. /**
  322. * 宠物洗髓
  323. *
  324. * @param int $petId 宠物ID
  325. * @param int $itemId 洗髓道具ID,如果为0则使用钻石
  326. * @return array 洗髓结果
  327. * @throws Exception
  328. */
  329. public function remouldPet(int $petId, int $itemId = 0): array
  330. {
  331. // 获取宠物信息
  332. $pet = PetUser::findOrFail($petId);
  333. // 记录旧品阶
  334. $oldGrade = $pet->grade;
  335. // 如果使用道具
  336. if ($itemId > 0) {
  337. // 验证物品是否为洗髓道具
  338. $remouldItemId = config('pet.remould_cost.item_id');
  339. if ($itemId != $remouldItemId) {
  340. throw new Exception("该物品不是洗髓道具");
  341. }
  342. // 消耗物品
  343. $consumeResult = ItemService::consumeItem(
  344. $pet->user_id,
  345. $itemId,
  346. null,
  347. 1,
  348. [
  349. 'source_type' => 'pet_remould',
  350. 'source_id' => $petId,
  351. 'details' => [ 'pet_id' => $petId ]
  352. ]
  353. );
  354. if (!$consumeResult['success']) {
  355. throw new Exception("物品消耗失败: " . ($consumeResult['message'] ?? '未知错误'));
  356. }
  357. } else {
  358. // 使用钻石
  359. $diamondCost = config('pet.remould_cost.diamond', 50);
  360. // TODO: 消耗钻石的逻辑,需要调用相关服务
  361. // 这里需要根据实际项目中的钻石消耗方式进行实现
  362. // 暂时使用占位代码
  363. $diamondConsumeSuccess = true;
  364. if (!$diamondConsumeSuccess) {
  365. throw new Exception("钻石不足,无法进行洗髓");
  366. }
  367. }
  368. // 随机生成新品阶
  369. $newGrade = $this->generateRandomGrade();
  370. // 更新宠物品阶
  371. $pet->grade = $newGrade;
  372. $pet->save();
  373. // 记录洗髓日志
  374. PetRemouldLog::create([
  375. 'pet_id' => $petId,
  376. 'old_grade' => $oldGrade,
  377. 'new_grade' => $newGrade,
  378. 'remould_time' => now()
  379. ]);
  380. // 触发宠物洗髓事件
  381. event(new PetRemouldEvent(
  382. $pet->user_id,
  383. $pet->id,
  384. $oldGrade,
  385. $newGrade
  386. ));
  387. // 触发宠物更新事件,表示宠物数据发生重大变更
  388. event(new PetUpdateEvent(
  389. $pet->user_id,
  390. $pet->id
  391. ));
  392. Log::info('宠物洗髓成功', [
  393. 'pet_id' => $petId,
  394. 'old_grade' => $oldGrade,
  395. 'new_grade' => $newGrade,
  396. 'item_id' => $itemId
  397. ]);
  398. return [
  399. 'old_grade' => $oldGrade,
  400. 'new_grade' => $newGrade
  401. ];
  402. }
  403. /**
  404. * 使用宠物技能
  405. *
  406. * @param int $petId 宠物ID
  407. * @param int $skillId 技能ID
  408. * @param array $params 技能参数
  409. * @return array 技能使用结果
  410. * @throws Exception
  411. */
  412. public function useSkill(int $petId, int $skillId, array $params = []): array
  413. {
  414. // 获取宠物信息
  415. $pet = PetUser::findOrFail($petId);
  416. // 获取技能信息
  417. $skill = PetSkill::findOrFail($skillId);
  418. // 检查技能冷却时间
  419. $lastUsed = PetSkillLog::where('pet_id', $petId)
  420. ->where('skill_id', $skillId)
  421. ->orderBy('used_at', 'desc')
  422. ->first();
  423. if ($lastUsed) {
  424. $cooldownSeconds = $skill->cool_down;
  425. $now = now();
  426. $lastUsedTime = $lastUsed->used_at;
  427. // 确保时间计算的正确性
  428. if ($lastUsedTime instanceof \Carbon\Carbon) {
  429. $secondsSinceLastUse = $now->diffInSeconds($lastUsedTime, false);
  430. } else {
  431. // 如果不是Carbon对象,尝试解析
  432. $lastUsedTime = \Carbon\Carbon::parse($lastUsedTime);
  433. $secondsSinceLastUse = $now->diffInSeconds($lastUsedTime, false);
  434. }
  435. // 如果secondsSinceLastUse为负数,说明lastUsed时间在未来,这是异常情况
  436. if ($secondsSinceLastUse < 0) {
  437. Log::warning('检测到异常的技能使用时间', [
  438. 'pet_id' => $petId,
  439. 'skill_id' => $skillId,
  440. 'last_used_at' => $lastUsedTime->toDateTimeString(),
  441. 'current_time' => $now->toDateTimeString(),
  442. 'seconds_diff' => $secondsSinceLastUse
  443. ]);
  444. // 异常情况下,重置为0,允许技能使用
  445. $secondsSinceLastUse = 0;
  446. }
  447. if ($secondsSinceLastUse < $cooldownSeconds) {
  448. $remainingCooldown = $cooldownSeconds - $secondsSinceLastUse;
  449. // 记录详细的冷却信息用于调试
  450. Log::info('技能冷却检查', [
  451. 'pet_id' => $petId,
  452. 'skill_id' => $skillId,
  453. 'cooldown_seconds' => $cooldownSeconds,
  454. 'seconds_since_last_use' => $secondsSinceLastUse,
  455. 'remaining_cooldown' => $remainingCooldown,
  456. 'last_used_at' => $lastUsedTime->toDateTimeString(),
  457. 'current_time' => $now->toDateTimeString()
  458. ]);
  459. throw new Exception("技能冷却中,还需等待 {$remainingCooldown} 秒");
  460. }
  461. }
  462. // 检查体力是否足够
  463. if ($pet->stamina < $skill->stamina_cost) {
  464. throw new Exception("体力不足,无法使用技能");
  465. }
  466. // 先执行技能效果,确保技能能够成功激活
  467. $effectResult = $this->executeSkillEffect($pet, $skill, $params);
  468. // 检查技能效果是否成功
  469. if (isset($effectResult['success']) && $effectResult['success'] === false) {
  470. // 技能效果执行失败,记录失败日志但不消耗体力,不进入冷却
  471. Log::warning('宠物技能使用失败', [
  472. 'pet_id' => $petId,
  473. 'skill_id' => $skillId,
  474. 'params' => $params,
  475. 'effect_result' => $effectResult,
  476. 'reason' => $effectResult['message'] ?? '技能使用失败'
  477. ]);
  478. throw new Exception($effectResult['message'] ?? '技能使用失败');
  479. }
  480. // 技能效果成功,消耗体力
  481. $pet->stamina -= $skill->stamina_cost;
  482. $pet->save();
  483. // 记录技能使用日志(只有成功时才记录)
  484. $skillLog = PetSkillLog::create([
  485. 'pet_id' => $petId,
  486. 'skill_id' => $skillId,
  487. 'used_at' => now(),
  488. 'effect_result' => json_encode($effectResult)
  489. ]);
  490. // 触发宠物技能使用事件
  491. event(new PetSkillUsedEvent(
  492. $pet->user_id,
  493. $pet->id,
  494. $skillId,
  495. $params
  496. ));
  497. Log::info('宠物技能使用成功', [
  498. 'pet_id' => $petId,
  499. 'skill_id' => $skillId,
  500. 'params' => $params,
  501. 'effect_result' => $effectResult
  502. ]);
  503. return [
  504. 'effect_result' => $effectResult
  505. ];
  506. }
  507. /**
  508. * 执行技能效果
  509. *
  510. * @param PetUser $pet 宠物对象
  511. * @param PetSkill $skill 技能对象
  512. * @param array $params 技能参数
  513. * @return array 技能效果结果
  514. */
  515. protected function executeSkillEffect(PetUser $pet, PetSkill $skill, array $params): array
  516. {
  517. // 根据技能名称执行不同的效果
  518. switch ($skill->skill_name) {
  519. case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_HARVESTING->value:
  520. return $this->activateAutoHarvestSkill($pet, $skill, $params);
  521. case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_PLANTING->value:
  522. return $this->activateAutoPlantSkill($pet, $skill, $params);
  523. case \App\Module\Pet\Enums\PET_SKILL_NAME::DISASTER_PROTECTION->value:
  524. return $this->activateDisasterProtectionSkill($pet, $skill, $params);
  525. case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_WEEDING->value:
  526. return $this->activateAutoWeedingSkill($pet, $skill, $params);
  527. case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_WATERING->value:
  528. return $this->activateAutoWateringSkill($pet, $skill, $params);
  529. case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_PEST_CONTROL->value:
  530. return $this->activateAutoPestControlSkill($pet, $skill, $params);
  531. default:
  532. return [
  533. 'success' => false,
  534. 'message' => '未知技能效果'
  535. ];
  536. }
  537. }
  538. /**
  539. * 激活自动收菜技能
  540. *
  541. * @param PetUser $pet 宠物对象
  542. * @param PetSkill $skill 技能对象
  543. * @param array $params 技能参数
  544. * @return array 激活结果
  545. */
  546. protected function activateAutoHarvestSkill(PetUser $pet, PetSkill $skill, array $params): array
  547. {
  548. // 检查是否已有相同技能在激活中
  549. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  550. ->where('skill_name', $skill->skill_name)
  551. ->where('status', 'active')
  552. ->where('end_time', '>', now())
  553. ->first();
  554. if ($existingActiveSkill) {
  555. return [
  556. 'success' => false,
  557. 'message' => '该技能已经在激活中,无法重复激活'
  558. ];
  559. }
  560. // 技能持续时间(默认2小时)
  561. $duration = $params['duration'] ?? 7200; // 2小时
  562. $endTime = now()->addSeconds($duration);
  563. // 创建技能激活记录
  564. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  565. 'pet_id' => $pet->id,
  566. 'skill_id' => $skill->id,
  567. 'skill_name' => $skill->skill_name,
  568. 'start_time' => now(),
  569. 'end_time' => $endTime,
  570. 'status' => 'active',
  571. 'config' => json_encode([
  572. 'auto_harvest' => true,
  573. 'check_interval' => 60,
  574. // 每分钟检查一次
  575. 'last_check_time' => now()->toDateTimeString()
  576. ])
  577. ]);
  578. Log::info('自动收菜技能激活成功', [
  579. 'pet_id' => $pet->id,
  580. 'skill_id' => $skill->id,
  581. 'duration' => $duration,
  582. 'end_time' => $endTime->toDateTimeString(),
  583. 'active_skill_id' => $activeSkill->id
  584. ]);
  585. return [
  586. 'success' => true,
  587. 'skill_type' => 'auto_harvest',
  588. 'duration' => $duration,
  589. 'end_time' => $endTime->toDateTimeString(),
  590. 'message' => "自动收菜技能已激活,持续时间:{$duration}秒"
  591. ];
  592. }
  593. /**
  594. * 激活自动播种技能
  595. *
  596. * @param PetUser $pet 宠物对象
  597. * @param PetSkill $skill 技能对象
  598. * @param array $params 技能参数
  599. * @return array 激活结果
  600. */
  601. protected function activateAutoPlantSkill(PetUser $pet, PetSkill $skill, array $params): array
  602. {
  603. // 检查是否已有相同技能在激活中
  604. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  605. ->where('skill_name', $skill->skill_name)
  606. ->where('status', 'active')
  607. ->where('end_time', '>', now())
  608. ->first();
  609. if ($existingActiveSkill) {
  610. return [
  611. 'success' => false,
  612. 'message' => '该技能已经在激活中,无法重复激活'
  613. ];
  614. }
  615. // 技能持续时间(默认4小时)
  616. $duration = $params['duration'] ?? 14400; // 4小时
  617. $endTime = now()->addSeconds($duration);
  618. // 创建技能激活记录
  619. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  620. 'pet_id' => $pet->id,
  621. 'skill_id' => $skill->id,
  622. 'skill_name' => $skill->skill_name,
  623. 'start_time' => now(),
  624. 'end_time' => $endTime,
  625. 'status' => 'active',
  626. 'config' => json_encode([
  627. 'auto_plant' => true,
  628. 'check_interval' => 60,
  629. // 每分钟检查一次
  630. 'last_check_time' => now()->toDateTimeString(),
  631. 'preferred_seeds' => $params['preferred_seeds'] ?? []
  632. // 优先使用的种子ID列表
  633. ])
  634. ]);
  635. Log::info('自动播种技能激活成功', [
  636. 'pet_id' => $pet->id,
  637. 'skill_id' => $skill->id,
  638. 'duration' => $duration,
  639. 'end_time' => $endTime->toDateTimeString(),
  640. 'active_skill_id' => $activeSkill->id
  641. ]);
  642. return [
  643. 'success' => true,
  644. 'skill_type' => 'auto_plant',
  645. 'duration' => $duration,
  646. 'end_time' => $endTime->toDateTimeString(),
  647. 'message' => "自动播种技能已激活,持续时间:{$duration}秒"
  648. ];
  649. }
  650. /**
  651. * 激活灾害防护技能
  652. *
  653. * @param PetUser $pet 宠物对象
  654. * @param PetSkill $skill 技能对象
  655. * @param array $params 技能参数
  656. * @return array 激活结果
  657. */
  658. protected function activateDisasterProtectionSkill(PetUser $pet, PetSkill $skill, array $params): array
  659. {
  660. // 检查是否已有相同技能在激活中
  661. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  662. ->where('skill_name', $skill->skill_name)
  663. ->where('status', 'active')
  664. ->where('end_time', '>', now())
  665. ->first();
  666. if ($existingActiveSkill) {
  667. return [
  668. 'success' => false,
  669. 'message' => '该技能已经在激活中,无法重复激活'
  670. ];
  671. }
  672. // 技能持续时间(默认6小时)
  673. $duration = $params['duration'] ?? 21600; // 6小时
  674. $endTime = now()->addSeconds($duration);
  675. // 创建技能激活记录
  676. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  677. 'pet_id' => $pet->id,
  678. 'skill_id' => $skill->id,
  679. 'skill_name' => $skill->skill_name,
  680. 'start_time' => now(),
  681. 'end_time' => $endTime,
  682. 'status' => 'active',
  683. 'config' => json_encode([
  684. 'disaster_protection' => true,
  685. 'protected_types' => $params['disaster_types'] ?? [ 'all' ],
  686. // 防护的灾害类型
  687. 'check_interval' => 300,
  688. // 每5分钟检查一次
  689. 'last_check_time' => now()->toDateTimeString()
  690. ])
  691. ]);
  692. Log::info('灾害防护技能激活成功', [
  693. 'pet_id' => $pet->id,
  694. 'skill_id' => $skill->id,
  695. 'duration' => $duration,
  696. 'end_time' => $endTime->toDateTimeString(),
  697. 'active_skill_id' => $activeSkill->id
  698. ]);
  699. return [
  700. 'success' => true,
  701. 'skill_type' => 'disaster_protection',
  702. 'duration' => $duration,
  703. 'end_time' => $endTime->toDateTimeString(),
  704. 'message' => "灾害防护技能已激活,持续时间:{$duration}秒"
  705. ];
  706. }
  707. /**
  708. * 变更宠物状态
  709. *
  710. * @param int $petId 宠物ID
  711. * @param PetStatus $status 新状态
  712. * @param string $reason 变更原因
  713. * @return bool 是否变更成功
  714. */
  715. public function changeStatus(int $petId, PetStatus $status, string $reason = ''): bool
  716. {
  717. // 获取宠物信息
  718. $pet = PetUser::findOrFail($petId);
  719. // 记录旧状态
  720. $oldStatus = $pet->status;
  721. // 如果状态相同,则不需要变更
  722. if ($oldStatus === $status) {
  723. return true;
  724. }
  725. // 更新状态
  726. $pet->status = $status;
  727. $pet->save();
  728. // 创建旧状态数据
  729. $oldStatusData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  730. $oldStatusData->id = $pet->id;
  731. $oldStatusData->status = $oldStatus->value;
  732. // 创建新状态数据
  733. $newStatusData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  734. $newStatusData->id = $pet->id;
  735. $newStatusData->status = $status->value;
  736. // 创建完整的宠物数据
  737. $petData = new \App\Module\Pet\Dtos\DataPetSimpleDto();
  738. $petData->id = $pet->id;
  739. $petData->name = $pet->name;
  740. $petData->typeId = $pet->type_id;
  741. $petData->level = $pet->level;
  742. $petData->grade = $pet->grade->value;
  743. $petData->status = $status->value;
  744. // 触发宠物状态变更事件
  745. event(new PetStatusChangedEvent(
  746. $pet->user_id,
  747. $pet->id,
  748. $oldStatusData,
  749. $newStatusData,
  750. $reason,
  751. $petData
  752. ));
  753. Log::info('宠物状态变更成功', [
  754. 'pet_id' => $petId,
  755. 'old_status' => $oldStatus->value,
  756. 'new_status' => $status->value,
  757. 'reason' => $reason
  758. ]);
  759. return true;
  760. }
  761. /**
  762. * 恢复宠物体力
  763. *
  764. * @param int $petId 宠物ID
  765. * @param int $minutes 经过的分钟数
  766. * @return int 恢复的体力值
  767. */
  768. public function recoverStamina(int $petId, int $minutes): int
  769. {
  770. // 获取宠物信息
  771. $pet = PetUser::findOrFail($petId);
  772. // 获取宠物等级配置
  773. $levelConfig = PetLevelConfig::where('level', $pet->level)->first();
  774. $maxStamina = $levelConfig ? ($levelConfig->numeric_attributes->stamina_max ?? 100) : 100;
  775. $recoveryRate = $levelConfig ? ($levelConfig->numeric_attributes->stamina_recovery ?? 5) : 5;
  776. // 计算恢复的体力值
  777. $recoveryAmount = $recoveryRate * $minutes;
  778. $oldStamina = $pet->stamina;
  779. $newStamina = min($maxStamina, $oldStamina + $recoveryAmount);
  780. $actualRecovered = $newStamina - $oldStamina;
  781. // 更新体力值
  782. if ($actualRecovered > 0) {
  783. $pet->stamina = $newStamina;
  784. $pet->save();
  785. Log::info('宠物体力恢复成功', [
  786. 'pet_id' => $petId,
  787. 'minutes' => $minutes,
  788. 'recovery_rate' => $recoveryRate,
  789. 'old_stamina' => $oldStamina,
  790. 'new_stamina' => $newStamina,
  791. 'actual_recovered' => $actualRecovered
  792. ]);
  793. }
  794. return $actualRecovered;
  795. }
  796. /**
  797. * 计算宠物战力
  798. *
  799. * @param int $petId 宠物ID
  800. * @return int 战力值
  801. */
  802. public function calculatePower(int $petId): int
  803. {
  804. // 获取宠物信息
  805. $pet = PetUser::findOrFail($petId);
  806. // 获取宠物等级配置
  807. $levelConfig = PetLevelConfig::where('level', $pet->level)->first();
  808. // 基础战力
  809. $basePower = $levelConfig ? ($levelConfig->numeric_attributes->base_power ?? 100) : 100;
  810. // 品阶加成
  811. $gradeBonus = config('pet.grade_attribute_bonus.' . $pet->grade, 0);
  812. // 计算最终战力
  813. $power = $basePower * (1 + $gradeBonus);
  814. return (int)$power;
  815. }
  816. /**
  817. * 激活自动除草技能
  818. *
  819. * @param PetUser $pet 宠物对象
  820. * @param PetSkill $skill 技能对象
  821. * @param array $params 技能参数
  822. * @return array 激活结果
  823. */
  824. protected function activateAutoWeedingSkill(PetUser $pet, PetSkill $skill, array $params): array
  825. {
  826. // 检查是否已有相同技能在激活中
  827. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  828. ->where('skill_name', $skill->skill_name)
  829. ->where('status', 'active')
  830. ->where('end_time', '>', now())
  831. ->first();
  832. if ($existingActiveSkill) {
  833. return [
  834. 'success' => false,
  835. 'message' => '该技能已经在激活中,无法重复激活'
  836. ];
  837. }
  838. // 技能持续时间(默认3小时)
  839. $duration = $params['duration'] ?? 10800; // 3小时
  840. $endTime = now()->addSeconds($duration);
  841. // 创建技能激活记录
  842. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  843. 'pet_id' => $pet->id,
  844. 'skill_id' => $skill->id,
  845. 'skill_name' => $skill->skill_name,
  846. 'start_time' => now(),
  847. 'end_time' => $endTime,
  848. 'status' => 'active',
  849. 'config' => json_encode([
  850. 'auto_weeding' => true,
  851. 'disaster_type' => \App\Module\Farm\Enums\DISASTER_TYPE::WEED->value,
  852. // 专门处理杂草灾害
  853. 'check_interval' => 300,
  854. 'last_check_time' => now()->toDateTimeString(),
  855. 'auto_use_items' => $params['auto_use_items'] ?? true
  856. // 是否自动使用除草道具
  857. ])
  858. ]);
  859. Log::info('自动除草技能激活成功', [
  860. 'pet_id' => $pet->id,
  861. 'skill_id' => $skill->id,
  862. 'duration' => $duration,
  863. 'end_time' => $endTime->toDateTimeString(),
  864. 'active_skill_id' => $activeSkill->id
  865. ]);
  866. return [
  867. 'success' => true,
  868. 'skill_type' => 'auto_weeding',
  869. 'duration' => $duration,
  870. 'end_time' => $endTime->toDateTimeString(),
  871. 'message' => "自动除草技能已激活,持续时间:{$duration}秒"
  872. ];
  873. }
  874. /**
  875. * 激活自动浇水技能
  876. *
  877. * @param PetUser $pet 宠物对象
  878. * @param PetSkill $skill 技能对象
  879. * @param array $params 技能参数
  880. * @return array 激活结果
  881. */
  882. protected function activateAutoWateringSkill(PetUser $pet, PetSkill $skill, array $params): array
  883. {
  884. // 检查是否已有相同技能在激活中
  885. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  886. ->where('skill_name', $skill->skill_name)
  887. ->where('status', 'active')
  888. ->where('end_time', '>', now())
  889. ->first();
  890. if ($existingActiveSkill) {
  891. return [
  892. 'success' => false,
  893. 'message' => '该技能已经在激活中,无法重复激活'
  894. ];
  895. }
  896. // 技能持续时间(默认4小时)
  897. $duration = $params['duration'] ?? 14400; // 4小时
  898. $endTime = now()->addSeconds($duration);
  899. // 创建技能激活记录
  900. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  901. 'pet_id' => $pet->id,
  902. 'skill_id' => $skill->id,
  903. 'skill_name' => $skill->skill_name,
  904. 'start_time' => now(),
  905. 'end_time' => $endTime,
  906. 'status' => 'active',
  907. 'config' => json_encode([
  908. 'auto_watering' => true,
  909. 'disaster_type' => \App\Module\Farm\Enums\DISASTER_TYPE::DROUGHT->value,
  910. // 专门处理干旱灾害
  911. 'check_interval' => 300,
  912. // 每5分钟检查一次
  913. 'last_check_time' => now()->toDateTimeString(),
  914. 'auto_use_items' => $params['auto_use_items'] ?? true
  915. // 是否自动使用浇水道具
  916. ])
  917. ]);
  918. Log::info('自动浇水技能激活成功', [
  919. 'pet_id' => $pet->id,
  920. 'skill_id' => $skill->id,
  921. 'duration' => $duration,
  922. 'end_time' => $endTime->toDateTimeString(),
  923. 'active_skill_id' => $activeSkill->id
  924. ]);
  925. return [
  926. 'success' => true,
  927. 'skill_type' => 'auto_watering',
  928. 'duration' => $duration,
  929. 'end_time' => $endTime->toDateTimeString(),
  930. 'message' => "自动浇水技能已激活,持续时间:{$duration}秒"
  931. ];
  932. }
  933. /**
  934. * 激活自动杀虫技能
  935. *
  936. * @param PetUser $pet 宠物对象
  937. * @param PetSkill $skill 技能对象
  938. * @param array $params 技能参数
  939. * @return array 激活结果
  940. */
  941. protected function activateAutoPestControlSkill(PetUser $pet, PetSkill $skill, array $params): array
  942. {
  943. // 检查是否已有相同技能在激活中
  944. $existingActiveSkill = \App\Module\Pet\Models\PetActiveSkill::where('pet_id', $pet->id)
  945. ->where('skill_name', $skill->skill_name)
  946. ->where('status', 'active')
  947. ->where('end_time', '>', now())
  948. ->first();
  949. if ($existingActiveSkill) {
  950. return [
  951. 'success' => false,
  952. 'message' => '该技能已经在激活中,无法重复激活'
  953. ];
  954. }
  955. // 技能持续时间(默认3小时)
  956. $duration = $params['duration'] ?? 10800; // 3小时
  957. $endTime = now()->addSeconds($duration);
  958. // 创建技能激活记录
  959. $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
  960. 'pet_id' => $pet->id,
  961. 'skill_id' => $skill->id,
  962. 'skill_name' => $skill->skill_name,
  963. 'start_time' => now(),
  964. 'end_time' => $endTime,
  965. 'status' => 'active',
  966. 'config' => json_encode([
  967. 'auto_pest_control' => true,
  968. 'disaster_type' => \App\Module\Farm\Enums\DISASTER_TYPE::PEST->value,
  969. // 专门处理虫害灾害
  970. 'check_interval' => 300,
  971. // 每5分钟检查一次
  972. 'last_check_time' => now()->toDateTimeString(),
  973. 'auto_use_items' => $params['auto_use_items'] ?? true
  974. // 是否自动使用杀虫道具
  975. ])
  976. ]);
  977. Log::info('自动杀虫技能激活成功', [
  978. 'pet_id' => $pet->id,
  979. 'skill_id' => $skill->id,
  980. 'duration' => $duration,
  981. 'end_time' => $endTime->toDateTimeString(),
  982. 'active_skill_id' => $activeSkill->id
  983. ]);
  984. return [
  985. 'success' => true,
  986. 'skill_type' => 'auto_pest_control',
  987. 'duration' => $duration,
  988. 'end_time' => $endTime->toDateTimeString(),
  989. 'message' => "自动杀虫技能已激活,持续时间:{$duration}秒"
  990. ];
  991. }
  992. }