| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- <?php
- namespace App\Module\Farm\Logics;
- use App\Module\Farm\Models\FarmMysterySeeLandEffect;
- use App\Module\Farm\Models\FarmSeedOutput;
- use App\Module\Farm\Models\FarmSeed;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Facades\Cache;
- /**
- * 神秘种子业务逻辑
- *
- * 处理神秘种子在不同土地类型上的概率调整和产出选择
- */
- class MysterySeeLLogic
- {
- /**
- * 为神秘种子选择最终产出(在发芽期调用)
- *
- * @param int $seedId 种子ID
- * @param int $landTypeId 土地类型ID
- * @return array 选择的产出信息 ['item_id' => int, 'min_amount' => int, 'max_amount' => int]
- */
- public function selectFinalOutput(int $seedId, int $landTypeId): array
- {
- try {
- // 获取调整后的概率分布
- $adjustedOutputs = $this->calculateAdjustedProbabilities($seedId, $landTypeId);
-
- // 执行随机选择
- $selectedOutput = $this->randomSelectOutput($adjustedOutputs);
-
- Log::info('神秘种子最终产出选择完成', [
- 'seed_id' => $seedId,
- 'land_type_id' => $landTypeId,
- 'selected_output' => $selectedOutput,
- 'adjusted_outputs_count' => count($adjustedOutputs)
- ]);
-
- return $selectedOutput;
-
- } catch (\Exception $e) {
- Log::error('神秘种子产出选择失败', [
- 'seed_id' => $seedId,
- 'land_type_id' => $landTypeId,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
-
- // 失败时使用默认逻辑
- return $this->getDefaultOutput($seedId);
- }
- }
- /**
- * 计算神秘种子在特定土地上的调整后概率
- *
- * @param int $seedId 种子ID
- * @param int $landTypeId 土地类型ID
- * @return array 调整后的产出配置
- */
- public function calculateAdjustedProbabilities(int $seedId, int $landTypeId): array
- {
- // 1. 获取基础产出配置
- $baseOutputs = FarmSeedOutput::where('seed_id', $seedId)
- ->get();
-
- if ($baseOutputs->isEmpty()) {
- // 如果没有产出配置,使用种子的默认配置
- return $this->getDefaultOutputConfig($seedId);
- }
- // 2. 获取土地影响配置(使用缓存)
- $cacheKey = "mystery_seed_land_effects_{$seedId}_{$landTypeId}";
- $landEffects = Cache::remember($cacheKey, 3600, function () use ($seedId, $landTypeId) {
- return FarmMysterySeeLandEffect::forSeed($seedId)
- ->forLandType($landTypeId)
- ->active()
- ->get()
- ->keyBy('output_item_id');
- });
- // 3. 应用概率调整(优先级:覆盖值 > 修正值)
- $adjustedOutputs = [];
- foreach ($baseOutputs as $output) {
- $baseProbability = $output->probability;
- $landEffect = $landEffects->get($output->item_id);
-
- // 优先使用覆盖值,其次使用修正值
- if ($landEffect && $landEffect->probability_override !== null) {
- // 使用覆盖值直接替换原概率
- $adjustedProbability = $landEffect->probability_override;
- $adjustmentType = 'override';
- } else {
- // 使用修正值调整原概率
- $modifier = $landEffect ? $landEffect->probability_modifier : 0;
- $adjustedProbability = $baseProbability + $modifier;
- $adjustmentType = 'modifier';
- }
-
- // 确保概率在合理范围内 (0-100)
- $adjustedProbability = max(0, min(100, $adjustedProbability));
-
- $adjustedOutputs[] = [
- 'item_id' => $output->item_id,
- 'min_amount' => $output->min_amount,
- 'max_amount' => $output->max_amount,
- 'disaster_min_amount' => $output->disaster_min_amount,
- 'disaster_max_amount' => $output->disaster_max_amount,
- 'original_probability' => $baseProbability,
- 'adjusted_probability' => $adjustedProbability,
- 'adjustment_type' => $adjustmentType,
- 'is_default' => $output->is_default
- ];
- }
- // 4. 概率归一化(可选)
- return $this->normalizeProbabilities($adjustedOutputs);
- }
- /**
- * 基于概率随机选择产出
- *
- * @param array $outputs 产出配置数组
- * @return array 选择的产出信息
- */
- private function randomSelectOutput(array $outputs): array
- {
- if (empty($outputs)) {
- throw new \Exception('没有可用的产出配置');
- }
- $random = mt_rand(1, 10000) / 100; // 精确到0.01%
- $cumulativeProbability = 0;
-
- foreach ($outputs as $output) {
- $cumulativeProbability += $output['adjusted_probability'];
-
- if ($random <= $cumulativeProbability) {
- return [
- 'item_id' => $output['item_id'],
- 'min_amount' => $output['min_amount'],
- 'max_amount' => $output['max_amount']
- ];
- }
- }
-
- // 如果随机值超出范围,返回默认产出
- $defaultOutput = collect($outputs)->firstWhere('is_default', true);
- if ($defaultOutput) {
- return [
- 'item_id' => $defaultOutput['item_id'],
- 'min_amount' => $defaultOutput['min_amount'],
- 'max_amount' => $defaultOutput['max_amount']
- ];
- }
-
- // 最后的保险:返回第一个产出
- $firstOutput = $outputs[0] ?? null;
- if ($firstOutput) {
- return [
- 'item_id' => $firstOutput['item_id'],
- 'min_amount' => $firstOutput['min_amount'],
- 'max_amount' => $firstOutput['max_amount']
- ];
- }
-
- throw new \Exception('神秘种子没有有效的产出配置');
- }
- /**
- * 归一化概率分布
- *
- * @param array $outputs 产出配置数组
- * @return array 归一化后的产出配置
- */
- private function normalizeProbabilities(array $outputs): array
- {
- $totalProbability = array_sum(array_column($outputs, 'adjusted_probability'));
-
- // 如果总概率为0,使用默认产出
- if ($totalProbability <= 0) {
- foreach ($outputs as &$output) {
- if ($output['is_default']) {
- $output['adjusted_probability'] = 100;
- } else {
- $output['adjusted_probability'] = 0;
- }
- }
- return $outputs;
- }
-
- // 如果需要归一化到100%(可选,根据需求决定是否启用)
- // if ($totalProbability != 100) {
- // $scaleFactor = 100 / $totalProbability;
- // foreach ($outputs as &$output) {
- // $output['adjusted_probability'] *= $scaleFactor;
- // }
- // }
-
- return $outputs;
- }
- /**
- * 获取默认产出(当出现错误时使用)
- *
- * @param int $seedId
- * @return array
- */
- private function getDefaultOutput(int $seedId): array
- {
- $seed = FarmSeed::find($seedId);
-
- if (!$seed) {
- throw new \Exception("种子ID {$seedId} 不存在");
- }
-
- return [
- 'item_id' => $seed->item_id,
- 'min_amount' => $seed->min_output,
- 'max_amount' => $seed->max_output
- ];
- }
- /**
- * 获取默认产出配置
- *
- * @param int $seedId
- * @return array
- */
- private function getDefaultOutputConfig(int $seedId): array
- {
- $seed = FarmSeed::find($seedId);
-
- if (!$seed) {
- throw new \Exception("种子ID {$seedId} 不存在");
- }
-
- return [[
- 'item_id' => $seed->item_id,
- 'min_amount' => $seed->min_output,
- 'max_amount' => $seed->max_output,
- 'disaster_min_amount' => $seed->disaster_min_output,
- 'disaster_max_amount' => $seed->disaster_max_output,
- 'original_probability' => 100,
- 'adjusted_probability' => 100,
- 'adjustment_type' => 'default',
- 'is_default' => true
- ]];
- }
- /**
- * 清除缓存
- *
- * @param int|null $seedId 种子ID,为null时清除所有
- * @param int|null $landTypeId 土地类型ID,为null时清除所有
- */
- public function clearCache(?int $seedId = null, ?int $landTypeId = null): void
- {
- if ($seedId && $landTypeId) {
- // 清除特定组合的缓存
- $cacheKey = "mystery_seed_land_effects_{$seedId}_{$landTypeId}";
- Cache::forget($cacheKey);
- } else {
- // 清除所有相关缓存(使用标签或前缀匹配)
- // 这里简化处理,实际项目中可以使用更精确的缓存清理策略
- Cache::flush();
- }
- }
- }
|