pet_weak_until; if ($weakUntil <= 0 || $weakUntil > time()) { return; } $petID = (int)$user->pet_weak_pet_id; if ($petID > 0) { $pet = Pet::findByPetID($petID); if ($pet && (int)$pet->user_id == (int)$user->id) { $pet->hp = max(1, (int)ceil($pet->maxHp() * self::WEAK_RECOVER_PERCENT / 100)); $pet->save(true, ['hp', 'updated_at']); } } PetBattleParticipant::updateAll( ['active' => 0], ['be_user_id' => (int)$user->id, 'active' => 1] ); $user->pet_weak_until = 0; $user->pet_weak_pet_id = 0; $user->save(true, ['pet_weak_until', 'pet_weak_pet_id', 'updated_at']); } public static function resolveSteal($fromUserID, $beUserID) { $lock = new RedisLock(Yii::$app->redis, RedisKeyUtil::lockPetBattle($beUserID), RedisLock::FLAG_CATCH_EXCEPTIONS); if (!$lock->acquire(10)) { return [ 'ok' => false, 'status' => ResultStatusServices::RS_SAME_TIME_OPERATION, 'data' => self::battleData(false, self::RESULT_NO_BATTLE, true), ]; } try { $beUser = User::findById($beUserID, false); if (!$beUser) { return [ 'ok' => false, 'status' => ResultStatusServices::RS_NOT_FIND_USER, 'data' => self::battleData(false, self::RESULT_NO_BATTLE, true), ]; } self::recoverWeakIfExpired($beUser); $now = time(); if ((int)$beUser->pet_weak_until > $now) { return self::resolveWeakSteal($fromUserID, $beUser, $now); } $fromPet = self::findBattleReadyPet($fromUserID); $bePet = self::findBattleReadyPet($beUserID); if (!$fromPet || !$bePet) { return [ 'ok' => true, 'status' => ResultStatusServices::RS_OPERATION_SUCCESS, 'data' => self::battleData(false, self::RESULT_NO_BATTLE, true, 0, 0, $fromPet, $bePet), ]; } self::normalizePetHp($fromPet); self::normalizePetHp($bePet); if (self::isDirectWin($fromPet, $bePet)) { return [ 'ok' => true, 'status' => ResultStatusServices::RS_OPERATION_SUCCESS, 'data' => self::battleData(true, self::RESULT_DIRECT_WIN, true, 0, 0, $fromPet, $bePet), ]; } $damage = max(1, $fromPet->attackValue() - $bePet->defenseValue()); $bePet->hp = max(0, $bePet->currentHp() - $damage); if (!$bePet->save(true, ['hp', 'updated_at'])) { return [ 'ok' => false, 'status' => ResultStatusServices::RS_SAVE_DB_ERROR, 'data' => self::battleData(true, self::RESULT_DAMAGE, false, $damage, 0, $fromPet, $bePet), ]; } self::recordDamage($fromUserID, $beUserID, (int)$bePet->id, $damage); if ((int)$bePet->hp <= 0) { $weakUntil = $now + self::WEAK_DURATION; $beUser->pet_weak_until = $weakUntil; $beUser->pet_weak_pet_id = (int)$bePet->id; if (!$beUser->save(true, ['pet_weak_until', 'pet_weak_pet_id', 'updated_at'])) { return [ 'ok' => false, 'status' => ResultStatusServices::RS_SAVE_DB_ERROR, 'data' => self::battleData(true, self::RESULT_WEAK_OPENED, true, $damage, $weakUntil, $fromPet, $bePet), ]; } self::activateParticipants($beUserID, (int)$bePet->id, $weakUntil); return [ 'ok' => true, 'status' => ResultStatusServices::RS_OPERATION_SUCCESS, 'data' => self::battleData(true, self::RESULT_WEAK_OPENED, true, $damage, $weakUntil, $fromPet, $bePet), ]; } return [ 'ok' => true, 'status' => ResultStatusServices::RS_OPERATION_SUCCESS, 'data' => self::battleData(true, self::RESULT_DAMAGE, false, $damage, 0, $fromPet, $bePet), ]; } finally { $lock->release(); } } private static function resolveWeakSteal($fromUserID, User $beUser, $now) { $bePet = Pet::findByPetID((int)$beUser->pet_weak_pet_id); $weakUntil = (int)$beUser->pet_weak_until; if (!$bePet || !PetBattleParticipant::hasActive((int)$beUser->id, (int)$bePet->id, $fromUserID, $now)) { return [ 'ok' => false, 'status' => ResultStatusServices::RS_PET_BATTLE_NOT_PARTICIPATED, 'data' => self::battleData(true, self::RESULT_WEAK_BLOCKED, false, 0, $weakUntil, null, $bePet), ]; } return [ 'ok' => true, 'status' => ResultStatusServices::RS_OPERATION_SUCCESS, 'data' => self::battleData(true, self::RESULT_WEAK_STEAL, true, 0, $weakUntil, null, $bePet), ]; } private static function findBattleReadyPet($userID) { $pet = Pet::findEnterWarPetByUserID($userID); if (!$pet || (int)$pet->end_brooding_time > time()) { return null; } PetUtil::updateFeedTime($pet); $pet->save(true, ['feed', 'last_feed_update_time', 'updated_at']); self::normalizePetHp($pet); if ((int)$pet->feed <= 0 || $pet->currentHp() <= 0) { return null; } return $pet; } private static function normalizePetHp(Pet $pet) { $currentHp = $pet->currentHp(); if ((int)$pet->hp !== $currentHp) { $pet->hp = $currentHp; $pet->save(true, ['hp', 'updated_at']); } } private static function isDirectWin(Pet $fromPet, Pet $bePet) { return (int)$fromPet->fighting_capacity * self::POWER_DIRECT_WIN_DENOMINATOR >= (int)$bePet->fighting_capacity * self::POWER_DIRECT_WIN_NUMERATOR; } private static function recordDamage($fromUserID, $beUserID, $bePetID, $damage) { $participant = PetBattleParticipant::findCurrent($beUserID, $bePetID, $fromUserID); if (!$participant) { $participant = new PetBattleParticipant(); $participant->from_user_id = $fromUserID; $participant->be_user_id = $beUserID; $participant->be_pet_id = $bePetID; $participant->weak_until = 0; $participant->active = 0; $participant->damage_total = 0; } $participant->damage_total += max(0, (int)$damage); $participant->save(); } private static function activateParticipants($beUserID, $bePetID, $weakUntil) { PetBattleParticipant::updateAll( ['weak_until' => $weakUntil, 'active' => 1], ['be_user_id' => $beUserID, 'be_pet_id' => $bePetID, 'weak_until' => 0] ); } private static function battleData($triggered, $result, $allowSteal, $damage = 0, $weakUntil = 0, Pet $fromPet = null, Pet $bePet = null) { return [ 'triggered' => $triggered ? 1 : 0, 'allow_steal' => $allowSteal ? 1 : 0, 'result' => $result, 'damage' => (int)$damage, 'weak_until' => (int)$weakUntil, 'attacker_pet' => self::petData($fromPet), 'defender_pet' => self::petData($bePet), ]; } private static function petData(Pet $pet = null) { if (!$pet) { return null; } return [ 'id' => (int)$pet->id, 'user_id' => (int)$pet->user_id, 'hp' => $pet->currentHp(), 'max_hp' => $pet->maxHp(), 'attack' => $pet->attackValue(), 'defense' => $pet->defenseValue(), 'fighting_capacity' => (int)$pet->fighting_capacity, ]; } }