CropLogic.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. <?php
  2. namespace App\Module\Farm\Logics;
  3. use App\Module\Farm\Dtos\CropInfoDto;
  4. use App\Module\Farm\Dtos\HarvestResultDto;
  5. use App\Module\Farm\Enums\GROWTH_STAGE;
  6. use App\Module\Farm\Enums\LAND_STATUS;
  7. use App\Module\Farm\Events\CropGrowthStageChangedEvent;
  8. use App\Module\Farm\Events\CropHarvestedEvent;
  9. use App\Module\Farm\Events\CropPlantedEvent;
  10. use App\Module\Farm\Events\DisasterClearedEvent;
  11. use App\Module\Farm\Events\LandStatusChangedEvent;
  12. use App\Module\Farm\Models\FarmCrop;
  13. use App\Module\Farm\Models\FarmHarvestLog;
  14. use App\Module\Farm\Models\FarmLand;
  15. use App\Module\Farm\Models\FarmSeed;
  16. use App\Module\Farm\Models\FarmSeedOutput;
  17. use App\Module\Farm\Models\FarmSowLog;
  18. use App\Module\GameItems\Services\ItemService;
  19. use Illuminate\Support\Facades\DB;
  20. use Illuminate\Support\Facades\Log;
  21. use UCore\Db\Helper;
  22. use UCore\DcatAdmin\Herlper;
  23. use UCore\Dto\Res;
  24. /**
  25. * 作物管理逻辑
  26. */
  27. class CropLogic
  28. {
  29. /**
  30. * 获取作物信息
  31. *
  32. * @param int $cropId
  33. * @return CropInfoDto|null
  34. */
  35. public function getCropInfo(int $cropId): ?CropInfoDto
  36. {
  37. try {
  38. $crop = FarmCrop::find($cropId);
  39. if (!$crop) {
  40. return null;
  41. }
  42. return CropInfoDto::fromModel($crop);
  43. } catch (\Exception $e) {
  44. Log::error('获取作物信息失败', [
  45. 'crop_id' => $cropId,
  46. 'error' => $e->getMessage(),
  47. 'trace' => $e->getTraceAsString()
  48. ]);
  49. return null;
  50. }
  51. }
  52. /**
  53. * 获取土地上的作物信息
  54. *
  55. * @param int $landId
  56. * @return CropInfoDto|null
  57. */
  58. public function getCropByLandId(int $landId): ?CropInfoDto
  59. {
  60. try {
  61. $crop = FarmCrop::where('land_id', $landId)->first();
  62. if (!$crop) {
  63. return null;
  64. }
  65. return CropInfoDto::fromModel($crop);
  66. } catch (\Exception $e) {
  67. Log::error('获取土地作物信息失败', [
  68. 'land_id' => $landId,
  69. 'error' => $e->getMessage(),
  70. 'trace' => $e->getTraceAsString()
  71. ]);
  72. return null;
  73. }
  74. }
  75. /**
  76. * 种植作物
  77. *
  78. * @param int $userId
  79. * @param int $landId
  80. * @param int $itemId 种子物品ID
  81. * @return array|null 返回包含CropInfoDto和日志ID的数组,格式为['crop' => CropInfoDto, 'log_id' => int]
  82. * @throws \Exception
  83. */
  84. public function plantCrop(int $userId, int $landId, int $itemId): ?array
  85. {
  86. try {
  87. // 检查是否已开启事务
  88. \UCore\Db\Helper::check_tr();
  89. // 获取土地信息
  90. $land = FarmLand::where('id', $landId)
  91. ->where('user_id', $userId)
  92. ->first();
  93. if (!$land) {
  94. throw new \Exception('土地不存在');
  95. }
  96. // 检查土地状态
  97. if ($land->status !== LAND_STATUS::IDLE->value) {
  98. throw new \Exception('土地状态不允许种植');
  99. }
  100. // 根据物品ID获取种子配置信息
  101. $seed = FarmSeed::where('item_id', $itemId)->first();
  102. if (!$seed) {
  103. throw new \Exception('种子配置不存在');
  104. }
  105. $seedId = $seed->id;
  106. // 创建作物记录
  107. $crop = new FarmCrop();
  108. $crop->land_id = $landId;
  109. $crop->user_id = $userId;
  110. $crop->seed_id = $seedId;
  111. $crop->plant_time = now();
  112. $crop->growth_stage = GROWTH_STAGE::SEED->value;
  113. $crop->stage_start_time = now(); // 设置当前阶段开始时间
  114. $crop->stage_end_time = now()->addSeconds($seed->seed_time);
  115. $crop->disasters = [];
  116. $crop->fertilized = false;
  117. $crop->last_disaster_check_time = null; // 初始化灾害检查时间
  118. $crop->can_disaster = false; // 种子期不能产生灾害
  119. $crop->save();
  120. // 创建种植日志
  121. $sowLog = new FarmSowLog();
  122. $sowLog->user_id = $userId;
  123. $sowLog->land_id = $landId;
  124. $sowLog->crop_id = $crop->id;
  125. $sowLog->seed_id = $seedId;
  126. $sowLog->sow_time = now();
  127. $sowLog->save();
  128. // 更新土地状态
  129. $land->status = LAND_STATUS::PLANTING->value;
  130. $land->save();
  131. // 触发作物种植事件
  132. event(new CropPlantedEvent($userId, $land, $crop));
  133. Log::info('作物种植成功', [
  134. 'user_id' => $userId,
  135. 'land_id' => $landId,
  136. 'seed_id' => $seedId,
  137. 'crop_id' => $crop->id,
  138. 'sow_log_id' => $sowLog->id
  139. ]);
  140. return [
  141. 'crop' => CropInfoDto::fromModel($crop),
  142. 'log_id' => $sowLog->id
  143. ];
  144. } catch (\Exception $e) {
  145. // 回滚事务
  146. DB::rollBack();
  147. Log::error('作物种植失败', [
  148. 'user_id' => $userId,
  149. 'land_id' => $landId,
  150. 'seed_id' => $seedId,
  151. 'error' => $e->getMessage(),
  152. 'trace' => $e->getTraceAsString()
  153. ]);
  154. return null;
  155. }
  156. }
  157. /**
  158. * 收获作物
  159. *
  160. * @param int $userId
  161. * @param int $landId
  162. * @return HarvestResultDto|null
  163. */
  164. public function harvestCrop(int $userId, int $landId): Res
  165. {
  166. try {
  167. // 事务检查
  168. Helper::check_tr();
  169. // 获取土地信息
  170. /**
  171. * @var FarmLand $land
  172. */
  173. $land = FarmLand::where('id', $landId)
  174. ->where('user_id', $userId)
  175. ->first();
  176. if (!$land) {
  177. throw new \Exception('土地不存在');
  178. }
  179. // 检查土地状态
  180. // if ($land->status !== LAND_STATUS::HARVESTABLE->value) {
  181. // throw new \Exception('土地状态不允许收获');
  182. // }
  183. // 获取作物信息
  184. $crop = FarmCrop::where('land_id', $landId)->first();
  185. if (!$crop) {
  186. throw new \Exception('作物不存在');
  187. }
  188. // 检查作物生长阶段
  189. if ($crop->growth_stage !== GROWTH_STAGE::MATURE) {
  190. throw new \Exception('作物未成熟,不能收获');
  191. }
  192. // 获取种子信息
  193. $seed = $crop->seed;
  194. if (!$seed) {
  195. throw new \Exception('种子信息不存在');
  196. }
  197. // 使用发芽期确定的最终产出果实ID,如果没有则随机选择
  198. if ($crop->final_output_item_id) {
  199. $outputItemId = $crop->final_output_item_id;
  200. // 获取对应的产出配置来确定数量范围
  201. $outputInfo = $this->getOutputInfoByItemId($seed->id, $outputItemId);
  202. $outputAmount = mt_rand($outputInfo['min_amount'], $outputInfo['max_amount']);
  203. Log::info('使用发芽期确定的最终产出果实', [
  204. 'crop_id' => $crop->id,
  205. 'final_output_item_id' => $outputItemId,
  206. 'output_amount' => $outputAmount
  207. ]);
  208. } else {
  209. // 兼容旧数据:如果没有预设的最终产出果实ID,则随机选择
  210. $outputInfo = $this->getRandomOutput($seed->id);
  211. $outputItemId = $outputInfo['item_id'];
  212. $outputAmount = mt_rand($outputInfo['min_amount'], $outputInfo['max_amount']);
  213. Log::warning('作物没有预设最终产出果实ID,使用随机选择', [
  214. 'crop_id' => $crop->id,
  215. 'seed_id' => $seed->id,
  216. 'random_output_item_id' => $outputItemId
  217. ]);
  218. }
  219. // 创建收获记录
  220. $harvestLog = new FarmHarvestLog();
  221. $harvestLog->user_id = $userId;
  222. $harvestLog->land_id = $landId;
  223. $harvestLog->crop_id = $crop->id;
  224. $harvestLog->seed_id = $seed->id;
  225. $harvestLog->output_amount = $outputAmount;
  226. $harvestLog->harvest_time = now();
  227. $harvestLog->created_at = now();
  228. $harvestLog->save();
  229. // 删除作物记录
  230. $crop->delete();
  231. // 更新土地状态
  232. $land->status = LAND_STATUS::IDLE;
  233. $land->save();
  234. // 触发作物收获事件
  235. event(new CropHarvestedEvent($userId, $land, $crop, $harvestLog, $outputItemId, $outputAmount));
  236. Log::info('作物收获成功', [
  237. 'user_id' => $userId,
  238. 'land_id' => $landId,
  239. 'crop_id' => $crop->id,
  240. 'seed_id' => $seed->id,
  241. 'output_item_id' => $outputItemId,
  242. 'output_amount' => $outputAmount,
  243. 'harvest_log_id' => $harvestLog->id
  244. ]);
  245. // 物品入包
  246. ItemService::addItem($userId, $outputItemId, $outputAmount,[
  247. 'source'=>'FarmHarve',
  248. 'FarmHarvestLog'=>$harvestLog->id
  249. ]);
  250. return Res::success();
  251. } catch (\Exception $e) {
  252. // 回滚事务
  253. Log::error('作物收获失败', [
  254. 'user_id' => $userId,
  255. 'land_id' => $landId,
  256. 'error' => $e->getMessage(),
  257. 'trace' => $e->getTraceAsString()
  258. ]);
  259. return Res::error('');
  260. }
  261. }
  262. /**
  263. * 使用化肥
  264. *
  265. * @param int $userId
  266. * @param int $landId
  267. * @return bool
  268. */
  269. public function useFertilizer(int $userId, int $landId,int $crop_growth_time): Res
  270. {
  271. try {
  272. Helper::check_tr();
  273. // 获取土地信息
  274. /**
  275. * @var FarmLand $land
  276. */
  277. $land = FarmLand::where('id', $landId)
  278. ->where('user_id', $userId)
  279. ->first();
  280. if (!$land) {
  281. throw new \Exception('土地不存在');
  282. }
  283. // 检查土地状态
  284. if ($land->status !== LAND_STATUS::PLANTING->valueInt()) {
  285. throw new \Exception('土地状态不允许使用化肥');
  286. }
  287. // 获取作物信息
  288. /**
  289. * @var FarmCrop $crop
  290. */
  291. $crop = FarmCrop::where('land_id', $landId)->first();
  292. if (!$crop) {
  293. throw new \Exception('作物不存在');
  294. }
  295. // 检查作物生长阶段
  296. if (!GROWTH_STAGE::canUseFertilizer($crop->growth_stage)) {
  297. throw new \Exception('当前生长阶段不能使用化肥');
  298. }
  299. // 检查是否已经使用过化肥
  300. if ($crop->fertilized) {
  301. throw new \Exception('当前阶段已经使用过化肥');
  302. }
  303. // 更新作物信息
  304. $crop->fertilized = true;
  305. // 根据 crop_growth_time 参数减少当前阶段时间
  306. if ($crop->stage_end_time) {
  307. $currentTime = now();
  308. $endTime = $crop->stage_end_time;
  309. // dd($endTime);
  310. $remainingTime = $currentTime->diffInSeconds($endTime, false);
  311. if ($remainingTime > 0) {
  312. // 确保减少的时间不超过剩余时间
  313. $reducedTime = min($crop_growth_time, $remainingTime);
  314. $crop->stage_end_time = $endTime->subSeconds($reducedTime);
  315. Log::info('化肥减少生长时间', [
  316. 'crop_id' => $crop->id,
  317. 'reduced_time' => $reducedTime,
  318. 'original_end_time' => $endTime->toDateTimeString(),
  319. 'new_end_time' => $crop->stage_end_time->toDateTimeString(),
  320. 'stage_start_time' => $crop->stage_start_time->toDateTimeString()
  321. ]);
  322. }
  323. }
  324. $crop->save();
  325. // 触发土地状态变更事件,确保前端能够获取到土地状态变更
  326. // 虽然土地状态没有实际变化,但我们需要通知前端作物状态已更新
  327. event(new LandStatusChangedEvent(
  328. $userId,
  329. $landId,
  330. $land->status, // 旧状态
  331. $land->status // 新状态(实际上没有变化,但需要触发事件)
  332. ));
  333. Log::info('使用化肥成功', [
  334. 'user_id' => $userId,
  335. 'land_id' => $landId,
  336. 'crop_id' => $crop->id,
  337. 'growth_stage' => $crop->growth_stage,
  338. 'stage_end_time' => $crop->stage_end_time
  339. ]);
  340. return Res::success('',[
  341. 'crop_id' => $crop->id,
  342. ]);
  343. } catch (\Exception $e) {
  344. Log::error('使用化肥失败', [
  345. 'user_id' => $userId,
  346. 'land_id' => $landId,
  347. 'error' => $e->getMessage(),
  348. 'trace' => $e->getTraceAsString()
  349. ]);
  350. return Res::error('使用化肥失败');
  351. }
  352. }
  353. /**
  354. * 清理灾害
  355. *
  356. * @param int $userId
  357. * @param int $landId
  358. * @param int $disasterType
  359. * @return bool
  360. */
  361. public function clearDisaster(int $userId, int $landId, int $disasterType): bool
  362. {
  363. try {
  364. // 获取土地信息
  365. $land = FarmLand::where('id', $landId)
  366. ->where('user_id', $userId)
  367. ->first();
  368. if (!$land) {
  369. throw new \Exception('土地不存在');
  370. }
  371. // 检查土地状态
  372. if ($land->status !== LAND_STATUS::DISASTER->value) {
  373. throw new \Exception('土地没有灾害');
  374. }
  375. // 获取作物信息
  376. $crop = FarmCrop::where('land_id', $landId)->first();
  377. if (!$crop) {
  378. throw new \Exception('作物不存在');
  379. }
  380. // 检查灾害是否存在
  381. $disasters = $crop->disasters ?? [];
  382. $disasterIndex = -1;
  383. $disasterInfo = null;
  384. foreach ($disasters as $index => $disaster) {
  385. if (($disaster['type'] ?? 0) == $disasterType && ($disaster['status'] ?? '') === 'active') {
  386. $disasterIndex = $index;
  387. $disasterInfo = $disaster;
  388. break;
  389. }
  390. }
  391. if ($disasterIndex === -1 || !$disasterInfo) {
  392. throw new \Exception('指定类型的灾害不存在');
  393. }
  394. // 更新灾害状态
  395. $disasters[$disasterIndex]['status'] = 'cleared';
  396. $disasters[$disasterIndex]['cleared_at'] = now()->toDateTimeString();
  397. $crop->disasters = $disasters;
  398. // 检查是否还有其他活跃灾害
  399. $hasActiveDisaster = false;
  400. foreach ($disasters as $disaster) {
  401. if (($disaster['status'] ?? '') === 'active') {
  402. $hasActiveDisaster = true;
  403. break;
  404. }
  405. }
  406. // 如果没有其他活跃灾害,更新土地状态
  407. $oldLandStatus = $land->status;
  408. if (!$hasActiveDisaster) {
  409. $land->status = LAND_STATUS::PLANTING->value;
  410. }
  411. // 保存更改
  412. $crop->save();
  413. $land->save();
  414. // 如果土地状态发生了变化,触发土地状态变更事件
  415. if ($oldLandStatus !== $land->status) {
  416. event(new LandStatusChangedEvent($userId, $landId, $oldLandStatus, $land->status));
  417. }
  418. // 触发灾害清理事件
  419. event(new DisasterClearedEvent($userId, $crop, $disasterType, $disasterInfo));
  420. Log::info('灾害清理成功', [
  421. 'user_id' => $userId,
  422. 'land_id' => $landId,
  423. 'crop_id' => $crop->id,
  424. 'disaster_type' => $disasterType
  425. ]);
  426. return true;
  427. } catch (\Exception $e) {
  428. Log::error('灾害清理失败', [
  429. 'user_id' => $userId,
  430. 'land_id' => $landId,
  431. 'disaster_type' => $disasterType,
  432. 'error' => $e->getMessage(),
  433. 'trace' => $e->getTraceAsString()
  434. ]);
  435. return false;
  436. }
  437. }
  438. /**
  439. * 铲除作物
  440. *
  441. * @param int $userId
  442. * @param int $landId
  443. * @return bool
  444. */
  445. public function removeCrop(int $userId, int $landId): bool
  446. {
  447. try {
  448. // 开启事务
  449. DB::beginTransaction();
  450. // 获取土地信息
  451. $land = FarmLand::where('id', $landId)
  452. ->where('user_id', $userId)
  453. ->first();
  454. if (!$land) {
  455. throw new \Exception('土地不存在');
  456. }
  457. // 检查土地状态
  458. if ($land->status === LAND_STATUS::IDLE->value) {
  459. throw new \Exception('土地上没有作物');
  460. }
  461. // 获取作物信息
  462. $crop = FarmCrop::where('land_id', $landId)->first();
  463. if (!$crop) {
  464. // 如果没有作物但土地状态不是空闲,修正土地状态
  465. $land->status = LAND_STATUS::IDLE->value;
  466. $land->save();
  467. DB::commit();
  468. return true;
  469. }
  470. // 删除作物记录
  471. $crop->delete();
  472. // 更新土地状态
  473. $land->status = LAND_STATUS::IDLE->value;
  474. $land->save();
  475. // 提交事务
  476. DB::commit();
  477. Log::info('铲除作物成功', [
  478. 'user_id' => $userId,
  479. 'land_id' => $landId,
  480. 'crop_id' => $crop->id
  481. ]);
  482. return true;
  483. } catch (\Exception $e) {
  484. // 回滚事务
  485. DB::rollBack();
  486. Log::error('铲除作物失败', [
  487. 'user_id' => $userId,
  488. 'land_id' => $landId,
  489. 'error' => $e->getMessage(),
  490. 'trace' => $e->getTraceAsString()
  491. ]);
  492. return false;
  493. }
  494. }
  495. /**
  496. * 更新作物生长阶段
  497. *
  498. * @param int $cropId
  499. * @return bool
  500. */
  501. public function updateGrowthStage(int $cropId): bool
  502. {
  503. try {
  504. // 获取作物信息
  505. $crop = FarmCrop::find($cropId);
  506. if (!$crop) {
  507. throw new \Exception('作物不存在');
  508. }
  509. // 检查是否需要更新
  510. if (!$crop->stage_end_time || $crop->stage_end_time > now()) {
  511. return false;
  512. }
  513. // 获取当前生长阶段
  514. $oldStage = $crop->growth_stage;
  515. // 计算新的生长阶段
  516. $newStage = $this->calculateNextStage($crop);
  517. // 如果阶段没有变化,不需要更新
  518. if ($newStage === $oldStage) {
  519. return false;
  520. }
  521. // 计算新阶段的结束时间
  522. $stageEndTime = $this->calculateStageEndTime($crop, $newStage);
  523. // 更新作物信息
  524. $crop->growth_stage = $newStage;
  525. $crop->stage_start_time = now(); // 设置新阶段的开始时间
  526. $crop->stage_end_time = $stageEndTime;
  527. $crop->fertilized = false; // 重置施肥状态
  528. // 如果进入发芽期,确定最终产出果实ID
  529. if ($newStage === GROWTH_STAGE::SPROUT->value && !$crop->final_output_item_id) {
  530. $outputInfo = $this->getRandomOutput($crop->seed_id);
  531. $crop->final_output_item_id = $outputInfo['item_id'];
  532. Log::info('作物进入发芽期,确定最终产出果实', [
  533. 'crop_id' => $crop->id,
  534. 'user_id' => $crop->user_id,
  535. 'seed_id' => $crop->seed_id,
  536. 'final_output_item_id' => $crop->final_output_item_id
  537. ]);
  538. }
  539. $crop->save();
  540. // 触发生长阶段变更事件
  541. event(new CropGrowthStageChangedEvent($crop->user_id, $crop, $oldStage->value, $newStage));
  542. Log::info('作物生长阶段更新成功', [
  543. 'crop_id' => $cropId,
  544. 'user_id' => $crop->user_id,
  545. 'old_stage' => $oldStage,
  546. 'new_stage' => $newStage,
  547. 'stage_end_time' => $stageEndTime
  548. ]);
  549. return true;
  550. } catch (\Exception $e) {
  551. Log::error('作物生长阶段更新失败', [
  552. 'crop_id' => $cropId,
  553. 'error' => $e->getMessage(),
  554. 'trace' => $e->getTraceAsString()
  555. ]);
  556. return false;
  557. }
  558. }
  559. /**
  560. * 计算下一个生长阶段
  561. *
  562. * @param FarmCrop $crop
  563. * @return int
  564. */
  565. public function calculateNextStage(FarmCrop $crop): int
  566. {
  567. $currentStage = $crop->growth_stage;
  568. // 如果当前是成熟期,且超过一定时间,则进入枯萎期
  569. if ($currentStage === GROWTH_STAGE::MATURE->value) {
  570. // 成熟期持续时间,默认为24小时
  571. $matureDuration = 24 * 60 * 60;
  572. // 如果成熟期已经超过指定时间,则进入枯萎期
  573. if ($crop->stage_end_time && $crop->stage_end_time instanceof \Carbon\Carbon) {
  574. $endTimeMinusDuration = $crop->stage_end_time->copy()->subSeconds($matureDuration);
  575. if (now()->diffInSeconds($endTimeMinusDuration) > $matureDuration) {
  576. return GROWTH_STAGE::WITHERED->value;
  577. }
  578. }
  579. return GROWTH_STAGE::MATURE->value;
  580. }
  581. // 使用阶段映射确定下一个阶段
  582. $stageMap = [
  583. GROWTH_STAGE::SEED->value => GROWTH_STAGE::SPROUT->value,
  584. GROWTH_STAGE::SPROUT->value => GROWTH_STAGE::GROWTH->value,
  585. GROWTH_STAGE::GROWTH->value => GROWTH_STAGE::MATURE->value,
  586. GROWTH_STAGE::MATURE->value => GROWTH_STAGE::WITHERED->value,
  587. GROWTH_STAGE::WITHERED->value => GROWTH_STAGE::WITHERED->value, // 枯萎期保持不变
  588. ];
  589. // 确保返回整数值
  590. return $stageMap[$currentStage->value()] ?? GROWTH_STAGE::WITHERED->value;
  591. }
  592. /**
  593. * 计算阶段结束时间
  594. *
  595. * @param FarmCrop $crop
  596. * @param int $stage
  597. * @return \Carbon\Carbon|null
  598. */
  599. private function calculateStageEndTime(FarmCrop $crop, int $stage)
  600. {
  601. $seed = $crop->seed;
  602. if (!$seed) {
  603. return null;
  604. }
  605. $now = now();
  606. switch ($stage) {
  607. case GROWTH_STAGE::SEED->value:
  608. return $now->addSeconds($seed->seed_time);
  609. case GROWTH_STAGE::SPROUT->value:
  610. return $now->addSeconds($seed->sprout_time);
  611. case GROWTH_STAGE::GROWTH->value:
  612. return $now->addSeconds($seed->growth_time);
  613. case GROWTH_STAGE::MATURE->value:
  614. // 成熟期持续24小时后进入枯萎期
  615. return $now->addHours(24);
  616. case GROWTH_STAGE::WITHERED->value:
  617. // 枯萎期没有结束时间
  618. return null;
  619. default:
  620. return null;
  621. }
  622. }
  623. /**
  624. * 获取随机产出
  625. *
  626. * @param int $seedId
  627. * @return array
  628. */
  629. private function getRandomOutput(int $seedId): array
  630. {
  631. // 获取种子的所有产出配置
  632. $outputs = FarmSeedOutput::where('seed_id', $seedId)->get();
  633. if ($outputs->isEmpty()) {
  634. // 如果没有产出配置,使用种子的默认产出
  635. $seed = FarmSeed::find($seedId);
  636. return [
  637. 'item_id' => $seed->item_id,
  638. 'min_amount' => $seed->min_output,
  639. 'max_amount' => $seed->max_output,
  640. ];
  641. }
  642. // 按概率排序
  643. $outputs = $outputs->sortByDesc('probability');
  644. // 获取默认产出
  645. $defaultOutput = $outputs->firstWhere('is_default', true);
  646. // 随机选择产出
  647. $random = mt_rand(1, 100);
  648. $cumulativeProbability = 0;
  649. foreach ($outputs as $output) {
  650. $cumulativeProbability += $output->probability;
  651. if ($random <= $cumulativeProbability) {
  652. return [
  653. 'item_id' => $output->item_id,
  654. 'min_amount' => $output->min_amount,
  655. 'max_amount' => $output->max_amount,
  656. ];
  657. }
  658. }
  659. // 如果随机值超过了所有概率之和,使用默认产出
  660. if ($defaultOutput) {
  661. return [
  662. 'item_id' => $defaultOutput->item_id,
  663. 'min_amount' => $defaultOutput->min_amount,
  664. 'max_amount' => $defaultOutput->max_amount,
  665. ];
  666. }
  667. // 如果没有默认产出,使用第一个产出
  668. $firstOutput = $outputs->first();
  669. return [
  670. 'item_id' => $firstOutput->item_id,
  671. 'min_amount' => $firstOutput->min_amount,
  672. 'max_amount' => $firstOutput->max_amount,
  673. ];
  674. }
  675. /**
  676. * 根据物品ID获取产出配置信息
  677. *
  678. * @param int $seedId
  679. * @param int $itemId
  680. * @return array
  681. */
  682. private function getOutputInfoByItemId(int $seedId, int $itemId): array
  683. {
  684. // 获取种子的所有产出配置
  685. $outputs = FarmSeedOutput::where('seed_id', $seedId)->get();
  686. // 查找匹配的产出配置
  687. $targetOutput = $outputs->firstWhere('item_id', $itemId);
  688. if ($targetOutput) {
  689. return [
  690. 'item_id' => $targetOutput->item_id,
  691. 'min_amount' => $targetOutput->min_amount,
  692. 'max_amount' => $targetOutput->max_amount,
  693. ];
  694. }
  695. // 如果没有找到匹配的产出配置,使用种子的默认产出
  696. $seed = FarmSeed::find($seedId);
  697. return [
  698. 'item_id' => $itemId, // 使用传入的物品ID
  699. 'min_amount' => $seed->min_output,
  700. 'max_amount' => $seed->max_output,
  701. ];
  702. }
  703. }