DisasterLogic.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace App\Module\Farm\Logics;
  3. use App\Module\Farm\Dtos\DisasterInfoDto;
  4. use App\Module\Farm\Enums\BUFF_TYPE;
  5. use App\Module\Farm\Enums\DISASTER_TYPE;
  6. use App\Module\Farm\Enums\GROWTH_STAGE;
  7. use App\Module\Farm\Enums\LAND_STATUS;
  8. use App\Module\Farm\Events\DisasterGeneratedEvent;
  9. use App\Module\Farm\Models\FarmCrop;
  10. use App\Module\Farm\Models\FarmGodBuff;
  11. use Illuminate\Support\Collection;
  12. use Illuminate\Support\Facades\Log;
  13. /**
  14. * 灾害管理逻辑
  15. */
  16. class DisasterLogic
  17. {
  18. /**
  19. * 获取作物的灾害信息
  20. *
  21. * @param int $cropId
  22. * @return Collection
  23. */
  24. public function getCropDisasters(int $cropId): Collection
  25. {
  26. try {
  27. $crop = FarmCrop::find($cropId);
  28. if (!$crop || empty($crop->disasters)) {
  29. return collect();
  30. }
  31. $disasters = collect($crop->disasters);
  32. return $disasters->map(function ($disaster) {
  33. return DisasterInfoDto::fromArray($disaster);
  34. });
  35. } catch (\Exception $e) {
  36. Log::error('获取作物灾害信息失败', [
  37. 'crop_id' => $cropId,
  38. 'error' => $e->getMessage(),
  39. 'trace' => $e->getTraceAsString()
  40. ]);
  41. return collect();
  42. }
  43. }
  44. /**
  45. * 获取作物的活跃灾害
  46. *
  47. * @param int $cropId
  48. * @return Collection
  49. */
  50. public function getActiveDisasters(int $cropId): Collection
  51. {
  52. try {
  53. $crop = FarmCrop::find($cropId);
  54. if (!$crop || empty($crop->disasters)) {
  55. return collect();
  56. }
  57. $disasters = collect($crop->disasters)->filter(function ($disaster) {
  58. return ($disaster['status'] ?? '') === 'active';
  59. });
  60. return $disasters->map(function ($disaster) {
  61. return DisasterInfoDto::fromArray($disaster);
  62. });
  63. } catch (\Exception $e) {
  64. Log::error('获取作物活跃灾害失败', [
  65. 'crop_id' => $cropId,
  66. 'error' => $e->getMessage(),
  67. 'trace' => $e->getTraceAsString()
  68. ]);
  69. return collect();
  70. }
  71. }
  72. /**
  73. * 生成灾害
  74. *
  75. * @param int $cropId
  76. * @return DisasterInfoDto|null
  77. */
  78. public function generateDisaster(int $cropId): ?DisasterInfoDto
  79. {
  80. try {
  81. $crop = FarmCrop::find($cropId);
  82. if (!$crop) {
  83. throw new \Exception('作物不存在');
  84. }
  85. $land = $crop->land;
  86. if (!$land) {
  87. throw new \Exception('土地不存在');
  88. }
  89. // 只在发芽期和生长期生成灾害
  90. if (!in_array($crop->growth_stage, [GROWTH_STAGE::SPROUT, GROWTH_STAGE::GROWTH])) {
  91. return null;
  92. }
  93. // 跳过已经有灾害的土地
  94. if ($land->status === LAND_STATUS::DISASTER) {
  95. return null;
  96. }
  97. $userId = $crop->user_id;
  98. $seed = $crop->seed;
  99. // 获取种子的灾害抵抗属性
  100. $disasterResistance = $seed->disaster_resistance ?? [];
  101. // 获取土地的灾害抵抗属性
  102. $landDisasterResistance = $land->landType->disaster_resistance ?? 0;
  103. // 检查用户是否有有效的神灵加持
  104. $activeBuffs = FarmGodBuff::where('user_id', $userId)
  105. ->where('expire_time', '>', now())
  106. ->pluck('buff_type')
  107. ->toArray();
  108. // 灾害类型及其基础概率
  109. $disasterTypes = [
  110. DISASTER_TYPE::DROUGHT => 0.1, // 干旱
  111. DISASTER_TYPE::PEST => 0.1, // 虫害
  112. DISASTER_TYPE::WEED => 0.1, // 杂草
  113. ];
  114. // 随机选择一种灾害类型
  115. $randomDisasterType = array_rand($disasterTypes);
  116. $baseProb = $disasterTypes[$randomDisasterType];
  117. // 计算最终概率,考虑种子抵抗、土地抵抗和神灵加持
  118. $seedResistance = $disasterResistance[$this->getDisasterKey($randomDisasterType)] ?? 0;
  119. $finalProb = $baseProb - $seedResistance - $landDisasterResistance;
  120. // 如果有对应的神灵加持,则不生成该类型的灾害
  121. $buffType = DISASTER_TYPE::getPreventBuffType($randomDisasterType);
  122. if ($buffType && in_array($buffType, $activeBuffs)) {
  123. $finalProb = 0;
  124. }
  125. // 确保概率在有效范围内
  126. $finalProb = max(0, min(1, $finalProb));
  127. // 随机决定是否生成灾害
  128. if (mt_rand(1, 100) <= $finalProb * 100) {
  129. // 生成灾害
  130. $disasterInfo = [
  131. 'type' => $randomDisasterType,
  132. 'generated_ts' => now()->toDateTimeString(),
  133. 'status' => 'active'
  134. ];
  135. // 更新作物灾害信息
  136. $disasters = $crop->disasters ?? [];
  137. $disasters[] = $disasterInfo;
  138. $crop->disasters = $disasters;
  139. // 更新土地状态为灾害
  140. $land->status = LAND_STATUS::DISASTER;
  141. // 保存更改
  142. $crop->save();
  143. $land->save();
  144. // 触发灾害生成事件
  145. event(new DisasterGeneratedEvent($userId, $crop, $randomDisasterType, $disasterInfo));
  146. Log::info('灾害生成成功', [
  147. 'user_id' => $userId,
  148. 'crop_id' => $crop->id,
  149. 'land_id' => $land->id,
  150. 'disaster_type' => $randomDisasterType
  151. ]);
  152. return DisasterInfoDto::fromArray($disasterInfo);
  153. }
  154. return null;
  155. } catch (\Exception $e) {
  156. Log::error('灾害生成失败', [
  157. 'crop_id' => $cropId,
  158. 'error' => $e->getMessage(),
  159. 'trace' => $e->getTraceAsString()
  160. ]);
  161. return null;
  162. }
  163. }
  164. /**
  165. * 获取灾害类型对应的键名
  166. *
  167. * @param int $disasterType
  168. * @return string
  169. */
  170. private function getDisasterKey(int $disasterType): string
  171. {
  172. $keys = [
  173. DISASTER_TYPE::DROUGHT => 'drought',
  174. DISASTER_TYPE::PEST => 'pest',
  175. DISASTER_TYPE::WEED => 'weed',
  176. ];
  177. return $keys[$disasterType] ?? '';
  178. }
  179. }