crop_id != 0) return ResultStatusServices::RS_LAND_HAS_CROP; $itemConfig = GameConfigServices::getParamByPath('item_list.' . $itemID); if (empty($itemConfig)) return ResultStatusServices::RS_NOT_FIND_IN_CONFIG; $cropConfig = GameConfigServices::getParamByPath('crop_list.' . $itemConfig['effect_id']); if (empty($cropConfig)) return ResultStatusServices::RS_NOT_FIND_IN_CONFIG; //修改农田数据 $landDbData->crop_id = $cropConfig['id']; $landDbData->crop_start_time = time(); $landDbData->crop_gather_time = time() + intval($cropConfig['seedling_time']) + intval($cropConfig['grow_time']) + intval($cropConfig['ripe_time']); $landDbData->is_mystery_seed = $isMysterySeed; if (!$landDbData->save()) return ResultStatusServices::RS_SAVE_DB_ERROR; } finally { } return ResultStatusServices::RS_OPERATION_SUCCESS; } /** * 收获作物 * @param $farmID * @param $position * @param $output * @param $itemID * @return int */ public static function gatherCrop($farmID, $position, &$output, &$itemID) { $output = 0; $itemID = null; //加锁 $lock = new RedisLock(Yii::$app->redis, RedisKeyUtil::lockLand($farmID, $position), RedisLock::FLAG_CATCH_EXCEPTIONS); if (!$lock->acquire(10)) return ResultStatusServices::RS_SAME_TIME_OPERATION; try { $landDbData = Land::findByFarmIDAndPosition($farmID, $position); if (!$landDbData) return ResultStatusServices::RS_NOT_FIND_IN_DB; //检查农作物 if ($landDbData->crop_id == 0) return ResultStatusServices::RS_LAND_NOT_CROP; //判断时间,农作物是否可收获 if ($landDbData->is_ripe != 1) return ResultStatusServices::RS_CROP_NOT_RIPE; //检查是否已收获过 if ($landDbData->has_gather != 0) return ResultStatusServices::RS_CROP_HAD_GATHER; //获取配置 $cropConfig = GameConfigServices::getParamByPath('crop_list.' . $landDbData->crop_id); if (empty($cropConfig)) return ResultStatusServices::RS_NOT_FIND_IN_CONFIG; //设置收获的物品 $itemID = $cropConfig['item_id']; //收获产量 $output = $landDbData->crop_num; //设置已收获标志 $landDbData->has_gather = 1; //保存数据 if (!$landDbData->save()) return ResultStatusServices::RS_SAVE_DB_ERROR; } finally { $lock->release(); } return ResultStatusServices::RS_OPERATION_SUCCESS; } /** * 更新所有农作物状态 * @param $farmID * @param $farmLevel * @return int */ public static function updateAllCropStatus($userID, $farmID, $farmLevel) { $landDbList = Land::findAll(['farm_id' => $farmID]); foreach ($landDbList as $land) { //检查是否需要更新 if ($land->crop_id != 0) { //更新单个农作物状态 $rs = self::updateCropStatus($userID, $farmID, $land->position, $farmLevel); if ($rs != ResultStatusServices::RS_OPERATION_SUCCESS) return $rs; } } return ResultStatusServices::RS_OPERATION_SUCCESS; } /** * 更新单个农作物状态 * @param $farmID * @param $position * @param $farmLevel * @return int */ public static function updateCropStatus($userID, $farmID, $position, $farmLevel) { //加锁 $lock = new RedisLock(Yii::$app->redis, RedisKeyUtil::lockLand($farmID, $position), RedisLock::FLAG_CATCH_EXCEPTIONS); if (!$lock->acquire(10)) return ResultStatusServices::RS_SAME_TIME_OPERATION; try { $landDbData = Land::findRedis($farmID, $position); if (!$landDbData) return ResultStatusServices::RS_NOT_FIND_IN_DB; //检查农作物 if ($landDbData->crop_id == 0) return ResultStatusServices::RS_LAND_NOT_CROP; //农作物是收获状态,直接返回 if ($landDbData->is_ripe == 1) return ResultStatusServices::RS_OPERATION_SUCCESS; $cropConfig = GameConfigServices::getParamByPath('crop_list.' . $landDbData->crop_id); if (NewbieGuideService::isNewbieCrop($landDbData->crop_id)) { if (time() >= self::getLandCropGatherTime($landDbData)) { $landDbData->crop_num = NewbieGuideService::NEWBIE_REWARD_GOLD; $landDbData->crop_min_num = NewbieGuideService::NEWBIE_REWARD_GOLD; $landDbData->has_dry = 0; $landDbData->has_bug = 0; $landDbData->has_grass = 0; $landDbData->is_ripe = 1; if (!$landDbData->save()) return ResultStatusServices::RS_SAVE_DB_ERROR; } return ResultStatusServices::RS_OPERATION_SUCCESS; } $change = false; //判断是否达到收获时间 if (time() < self::getLandCropGatherTime($landDbData)) { //未成熟,随机病虫害 $growPhaseTime1 = $landDbData->crop_start_time + $cropConfig['seedling_time']; if (time() > $growPhaseTime1) { //种子期不随机虫 //计算需要随机的次数 if ($landDbData->disease_rand_time == 0) $lastRandTime = time() - $landDbData->crop_start_time - $cropConfig['seedling_time']; else $lastRandTime = time() - $landDbData->disease_rand_time; $time = GameConfigServices::getCommonNumByID(GameConfigServices::COMMON_DISEASE_RAND_TIME); if ($time == 0) $time = 1; $randCount = intval($lastRandTime / $time); if ($randCount <= 0) $randCount = 0; if ($randCount > 10) $randCount = 10; //随机病害 for ($i = 0; $i < $randCount; $i++) { if ($landDbData->has_dry == 0 && RandServices::randLandDisease(GameConfigServices::COMMON_DRY_RAND_NUM)) $landDbData->has_dry = 1; if ($landDbData->has_bug == 0 && RandServices::randLandDisease(GameConfigServices::COMMON_BUG_RAND_NUM)) $landDbData->has_bug = 1; if ($landDbData->has_grass == 0 && RandServices::randLandDisease(GameConfigServices::COMMON_GRASS_RAND_NUM)) $landDbData->has_grass = 1; } //更新随机时间 if ($randCount > 0) $landDbData->disease_rand_time = time(); $change = true; } } else { //农作物成熟,计算产量 //农场增产量 $farmConfig = GameConfigServices::getParamByPath('farm_level.' . $farmLevel); $farmOutput = intval($farmConfig['output']); //农作物产量 $cropOutput = intval($cropConfig['output']); //农田等级产量 $landConfig = GameConfigServices::getLandUpgradeByLevelAndPosition($landDbData->level, $landDbData->position); $landOutput = intval($landConfig['output']); //vip // $vip = CardUtil::findByUserID($userID); // $vipConfig = GameConfigServices::getParamByPath('vip_list.' . $vip->level); // $vipCount = 0; // if (!empty($vipConfig)) $vipCount = (int)$vipConfig['fruit']; //总产量 // $totalOutput = $farmOutput + $cropOutput + $landOutput ; $addPercent = floatval($farmOutput) / 100 + floatval($landOutput) / 100; $totalOutput = $cropOutput * (1 + $addPercent); //计算保底产量 $minOutputPercent = GameConfigServices::getCommonNumByID(GameConfigServices::COMMON_MIN_OUTPUT_ID) / 100; $landDbData->crop_min_num = $totalOutput * $minOutputPercent; if ($landDbData->crop_min_num < 0) $landDbData->crop_min_num = 0; //干旱减产百分比 $dryReductionPercent = 0; if ($landDbData->has_dry) $dryReductionPercent = floatval(GameConfigServices::getCommonNumByID(GameConfigServices::COMMON_DRY_ID)) / 100; //虫害减产百分比 $bugReductionPercent = 0; if ($landDbData->has_bug) $bugReductionPercent = floatval(GameConfigServices::getCommonNumByID(GameConfigServices::COMMON_BUG_ID)) / 100; //杂草减产百分比 $grassReductionPercent = 0; if ($landDbData->has_grass) $grassReductionPercent = floatval(GameConfigServices::getCommonNumByID(GameConfigServices::COMMON_GRASS_ID)) / 100; //总减产百分比 $totalReductionPercent = $dryReductionPercent + $bugReductionPercent + $grassReductionPercent; //最终产量 $landDbData->crop_num = $totalOutput * (1 - $totalReductionPercent); if ($landDbData->crop_num < $landDbData->crop_min_num) $landDbData->crop_num = $landDbData->crop_min_num; $landDbData->crop_num = intval($landDbData->crop_num); //更改成熟状态 $landDbData->is_ripe = 1; $change = true; } //保存 if ($change && !$landDbData->save()) ResultStatusServices::RS_SAVE_DB_ERROR; } finally { $lock->release(); } return ResultStatusServices::RS_OPERATION_SUCCESS; } /** * 清理农田 * @param $farmID * @param $position * @return int */ public static function clearLand($farmID, $position) { try { $landDbData = Land::findByFarmIDAndPosition($farmID, $position); if (!$landDbData) return ResultStatusServices::RS_NOT_FIND_IN_DB; //检查是否需要清理 if ($landDbData->crop_id == 0) return ResultStatusServices::RS_LAND_NOT_NEED_CLEAR; $landDbData->crop_id = 0; $landDbData->crop_start_time = 0; $landDbData->is_ripe = 0; $landDbData->crop_num = 0; $landDbData->crop_min_num = 0; $landDbData->has_dry = 0; $landDbData->has_bug = 0; $landDbData->has_grass = 0; $landDbData->has_gather = 0; $landDbData->disease_rand_time = 0; //保存 if (!$landDbData->save()) ResultStatusServices::RS_SAVE_DB_ERROR; } finally { } return ResultStatusServices::RS_OPERATION_SUCCESS; } /** * 初始化新数据 * @param Land $landDbData * @param $farmID * @param $position * @return bool */ public static function initNewFarmDataAndSave(Land $landDbData, $farmID, $position) { $landDbData->farm_id = $farmID; $landDbData->position = $position; return $landDbData->save(); } /**获取农田可交互类型 * @param $fromUserID * @param $beUserID * @param $landDbData * @return array */ public static function getLandInteractionType($fromUserID, $beUserID, Land $landDbData) { if (!$landDbData || $landDbData->crop_id == 0) return []; $typeList = []; //是否偷取过 if (self::canStealLand($fromUserID, $beUserID, $landDbData)) { array_push($typeList, EnumUtil::INTERACTION_TYPE_STEAL); } //是否可以放虫 if (!$landDbData->is_ripe && !$landDbData->has_bug && !Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_BUG) ) { array_push($typeList, EnumUtil::INTERACTION_TYPE_BUG); } //是否可以放草 if (!$landDbData->is_ripe && !$landDbData->has_grass && !Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_GRASS) ) { array_push($typeList, EnumUtil::INTERACTION_TYPE_GRASS); } //是否可以浇水 if (!$landDbData->is_ripe && $landDbData->has_dry && !Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_WATERING) ) { array_push($typeList, EnumUtil::INTERACTION_TYPE_WATERING); } //是否可以除虫 if (!$landDbData->is_ripe && $landDbData->has_bug && !Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_DISINSECTION) ) { array_push($typeList, EnumUtil::INTERACTION_TYPE_DISINSECTION); } //是否可以除草 if (!$landDbData->is_ripe && $landDbData->has_grass && !Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_WEEDING) ) { array_push($typeList, EnumUtil::INTERACTION_TYPE_WEEDING); } return $typeList; } /** * 偷菜 * @param $farmID * @param $position * @param $stealCount * @return int */ public static function steal($farmID, $position, &$stealCount, $fromUserID = null, $beUserID = null) { //加锁 $lock = new RedisLock(Yii::$app->redis, RedisKeyUtil::lockLand($farmID, $position), RedisLock::FLAG_CATCH_EXCEPTIONS); if (!$lock->acquire(10)) return ResultStatusServices::RS_SAME_TIME_OPERATION; try { $landDbData = Land::findByFarmIDAndPosition($farmID, $position); if (!$landDbData) return ResultStatusServices::RS_NOT_FIND_IN_DB; //检查农作物 if ($landDbData->crop_id == 0) return ResultStatusServices::RS_LAND_NOT_CROP; //农作物是否成熟状态 if ($landDbData->is_ripe != 1) return ResultStatusServices::RS_CROP_NOT_RIPE; //农作物是否被收获过 if ($landDbData->has_gather == 1) return ResultStatusServices::RS_CROP_HAD_GATHER; if ($fromUserID && $beUserID) { if (!self::canStealLand($fromUserID, $beUserID, $landDbData)) return ResultStatusServices::RS_INTERACTION_CAN_NOT_STEAL; } $stolenTotal = $beUserID ? self::getCropStolenCount($beUserID, $landDbData) : 0; $totalOutput = self::getStealTotalOutput($landDbData, $stolenTotal); $maxStolenTotal = intval(ceil($totalOutput * self::STEAL_CROP_MAX_PERCENT / 100)); $protectedOutput = $totalOutput - $maxStolenTotal; $availableStealCount = $maxStolenTotal - $stolenTotal; if ($availableStealCount <= 0 || $landDbData->crop_num <= $protectedOutput) return ResultStatusServices::RS_CROP_STEAL_NOT_NUM; $stealPercent = mt_rand(self::STEAL_MIN_PERCENT, self::STEAL_MAX_PERCENT); $stealCount = intval(ceil($totalOutput * $stealPercent / 100)); if ($stealCount < 1) $stealCount = 1; if ($stealCount > $availableStealCount) $stealCount = $availableStealCount; if ($landDbData->crop_num - $stealCount < $protectedOutput) $stealCount = $landDbData->crop_num - $protectedOutput; //没有可偷取的数量 if ($stealCount <= 0) return ResultStatusServices::RS_CROP_STEAL_NOT_NUM; //偷取 $landDbData->crop_num -= $stealCount; //保存 if (!$landDbData->save()) ResultStatusServices::RS_SAVE_DB_ERROR; } finally { $lock->release(); } return ResultStatusServices::RS_OPERATION_SUCCESS; } public static function canStealLand($fromUserID, $beUserID, Land $landDbData) { if (!$landDbData || $landDbData->crop_id == 0) return false; if (!$landDbData->is_ripe || $landDbData->has_gather) return false; if (!$fromUserID || !$beUserID || $fromUserID == $beUserID) return false; if (Interaction::findRedis($fromUserID, $beUserID, $landDbData->crop_start_time, $landDbData->position, EnumUtil::INTERACTION_TYPE_STEAL)) return false; if (!self::hasDailyStealTargetQuota($fromUserID, $beUserID)) return false; $stolenTotal = self::getCropStolenCount($beUserID, $landDbData); $totalOutput = self::getStealTotalOutput($landDbData, $stolenTotal); $maxStolenTotal = intval(ceil($totalOutput * self::STEAL_CROP_MAX_PERCENT / 100)); if ($maxStolenTotal - $stolenTotal <= 0) return false; return $landDbData->crop_num > ($totalOutput - $maxStolenTotal); } public static function hasDailyStealTargetQuota($fromUserID, $beUserID) { $dayStart = strtotime('today'); $dayEnd = $dayStart + 86400; $targetList = Interaction::find() ->select('be_user_id') ->distinct() ->where([ 'from_user_id' => $fromUserID, 'type' => EnumUtil::INTERACTION_TYPE_STEAL, ]) ->andWhere(['>=', 'created_at', $dayStart]) ->andWhere(['<', 'created_at', $dayEnd]) ->column(); $targetList = array_map('intval', $targetList); if (in_array(intval($beUserID), $targetList)) return true; return count($targetList) < self::STEAL_DAILY_TARGET_LIMIT; } public static function getCropStolenCount($beUserID, Land $landDbData) { if (!$beUserID || !$landDbData) return 0; $sum = Interaction::find() ->where([ 'be_user_id' => $beUserID, 'land_create_time' => $landDbData->crop_start_time, 'position' => $landDbData->position, 'type' => EnumUtil::INTERACTION_TYPE_STEAL, ]) ->sum('num'); return intval($sum); } private static function getStealTotalOutput(Land $landDbData, $stolenTotal) { $totalOutput = intval($landDbData->crop_num) + intval($stolenTotal); if ($totalOutput <= 0 && $landDbData->crop_min_num > 0) $totalOutput = intval($landDbData->crop_min_num * 2); return max(1, $totalOutput); } /**获取农田收获时间 * @param Land $landDbData * @return int */ public static function getLandCropGatherTime(Land $landDbData) { $cropConfig = GameConfigServices::getParamByPath('crop_list.' . $landDbData->crop_id); if (empty($cropConfig)) return $landDbData->crop_start_time; //配置表生长周期是单个周期的时间差,为了方便计算,把生长周期变成从播种开始的时间 return $landDbData->crop_start_time + $cropConfig['seedling_time'] + $cropConfig['grow_time'] + $cropConfig['ripe_time']; } /** * 获取农作物当前阶段 * @param Land $landDbData * @return int */ public static function getLandCropPhase(Land $landDbData) { $cropConfig = GameConfigServices::getParamByPath('crop_list.' . $landDbData->crop_id); //配置表生长周期是单个周期的时间差,为了方便计算,把生长周期变成从播种开始的时间 $growPhaseTime1 = $landDbData->crop_start_time + $cropConfig['seedling_time']; $growPhaseTime2 = $growPhaseTime1 + $cropConfig['grow_time']; $growPhaseTime3 = $growPhaseTime2 + $cropConfig['ripe_time']; //每个阶段还需要的时间 if (time() < $growPhaseTime1) return EnumUtil::CROP_PHASE_SEED; elseif (time() < $growPhaseTime2) return EnumUtil::CROP_PHASE_SEEDLING; elseif (time() < $growPhaseTime3) return EnumUtil::CROP_PHASE_GROW; else return EnumUtil::CROP_PHASE_RIPE; } }