mirror of
https://github.com/Deathgarden-Rebirth/Deathgarden_Rebirth-Rewrite.git
synced 2026-09-09 11:45:18 -05:00
Merge pull request #49 from Deathgarden-Rebirth/4_daily-weekly-event-challenges
Added Feature: Weekly, Daily and Event challenges
This commit is contained in:
167
dist/app/Classes/Factory/TimedChallengeFactory.php
vendored
Normal file
167
dist/app/Classes/Factory/TimedChallengeFactory.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Factory;
|
||||
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Enums\Game\Faction;
|
||||
use App\Enums\Game\RewardType;
|
||||
use App\Exceptions\TimedChallengeFactoryException;
|
||||
use App\Http\Responses\Api\General\Reward;
|
||||
use App\Models\Game\TimedChallenge;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
abstract class TimedChallengeFactory
|
||||
{
|
||||
const AVAILABLE_DAILY_RUNNER = [
|
||||
'/Game/Challenges/Weekly/Challenge_BleedOut_RunnerWeekly.Challenge_BleedOut_RunnerWeekly' => 1,
|
||||
'/Game/Challenges/Daily/Challenge_Domination_Runner.Challenge_Domination_Runner' => 1,
|
||||
'/Game/Challenges/Weekly/Challenge_Emotional_RunnerWeekly.Challenge_Emotional_RunnerWeekly' => 3,
|
||||
'/Game/Challenges/Weekly/Challenge_Shields_RunnerWeekly.Challenge_Shields_RunnerWeekly' => 10
|
||||
] ;
|
||||
|
||||
const AVAILABLE_DAILY_HUNTER = [
|
||||
'/Game/Challenges/Daily/Challenge_Domination_Hunter.Challenge_Domination_Hunter' => 1,
|
||||
'/Game/Challenges/Weekly/Challenge_DroneActivation_HunterWeekly.Challenge_DroneActivation_HunterWeekly' => 5,
|
||||
'/Game/Challenges/Weekly/Challenge_Emotional_HunterWeekly.Challenge_Emotional_HunterWeekly' => 3,
|
||||
'/Game/Challenges/Weekly/Challenge_Headshot_HunterWeekly.Challenge_Headshot_HunterWeekly' => 1,
|
||||
'/Game/Challenges/Weekly/Challenge_InDenial_HunterWeekly.Challenge_InDenial_HunterWeekly' => 3,
|
||||
];
|
||||
|
||||
const AVAILABLE_WEEKLY_RUNNER = [
|
||||
'/Game/Challenges/Weekly/Challenge_BleedOut_RunnerWeekly.Challenge_BleedOut_RunnerWeekly' => 5,
|
||||
'/Game/Challenges/Weekly/Challenge_Emotional_RunnerWeekly.Challenge_Emotional_RunnerWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_Greed_RunnerWeekly.Challenge_Greed_RunnerWeekly' => 50,
|
||||
'/Game/Challenges/Weekly/Challenge_Shields_RunnerWeekly.Challenge_Shields_RunnerWeekly' => 100,
|
||||
'/Game/Challenges/Weekly/Challenge_SpeedCapture_RunnerWeekly.Challenge_SpeedCapture_RunnerWeekly' => 15,
|
||||
'/Game/Challenges/Weekly/Challenge_UPs_RunnerWeekly.Challenge_UPs_RunnerWeekly' => 5,
|
||||
'/Game/Challenges/Weekly/Challenge_Wasteful_RunnerWeekly.Challenge_Wasteful_RunnerWeekly' => 100
|
||||
];
|
||||
|
||||
const AVAILABLE_WEEKLY_HUNTER = [
|
||||
'/Game/Challenges/Weekly/Challenge_ARB_Damage_HunterWeekly.Challenge_ARB_Damage_HunterWeekly' => 5000,
|
||||
'/Game/Challenges/Weekly/Challenge_AssaultRifleWins_HunterWeekly.Challenge_AssaultRifleWins_HunterWeekly' => 100,
|
||||
'/Game/Challenges/Weekly/Challenge_Damage_HunterWeekly.Challenge_Damage_HunterWeekly' => 5000,
|
||||
'/Game/Challenges/Weekly/Challenge_DroneActivation_HunterWeekly.Challenge_DroneActivation_HunterWeekly' => 25,
|
||||
'/Game/Challenges/Weekly/Challenge_Emotional_HunterWeekly.Challenge_Emotional_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_Greed_HunterWeekly.Challenge_Greed_HunterWeekly' => 50,
|
||||
'/Game/Challenges/Weekly/Challenge_Headshot_HunterWeekly.Challenge_Headshot_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_HuntingShotgunWins_HunterWeekly.Challenge_HuntingShotgunWins_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_InDenial_HunterWeekly.Challenge_InDenial_HunterWeekly' => 20,
|
||||
'/Game/Challenges/Weekly/Challenge_LMGWins_HunterWeekly.Challenge_LMGWins_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_Reveals_hunterWeekly.Challenge_Reveals_hunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_RingOut_hunterWeekly.Challenge_RingOut_hunterWeekly' => 5,
|
||||
'/Game/Challenges/Weekly/Challenge_Mines_HunterWeekly.Challenge_Mines_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_ShotgunDowns_HunterWeekly.Challenge_ShotgunDowns_HunterWeekly' => 10,
|
||||
'/Game/Challenges/Weekly/Challenge_Wasteful_HunterWeekly.Challenge_Wasteful_HunterWeekly' => 100,
|
||||
];
|
||||
|
||||
const DAILY_REWARDS = [
|
||||
'CurrencyA' => [
|
||||
'min' => 300,
|
||||
'max' => 500,
|
||||
],
|
||||
'CurrencyB' => [
|
||||
'min' => 600,
|
||||
'max' => 1000,
|
||||
],
|
||||
'CurrencyC' => [
|
||||
'min' => 500,
|
||||
'max' => 900,
|
||||
],
|
||||
];
|
||||
|
||||
const WEEKLY_REWARDS = [
|
||||
'CurrencyA' => [
|
||||
'min' => 2000,
|
||||
'max' => 3000,
|
||||
],
|
||||
'CurrencyB' => [
|
||||
'min' => 4000,
|
||||
'max' => 6000,
|
||||
],
|
||||
'CurrencyC' => [
|
||||
'min' => 3000,
|
||||
'max' => 5000,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Makes a new TimedChallenge Instance that's not yet saved to the Database.
|
||||
*
|
||||
* @param Carbon $startTime
|
||||
* @param Faction $faction
|
||||
* @param ChallengeType $type
|
||||
* @return void
|
||||
* @throws TimedChallengeFactoryException
|
||||
*/
|
||||
public static function makeChallenge(
|
||||
Carbon $startTime,
|
||||
Faction $faction,
|
||||
ChallengeType $type,
|
||||
): TimedChallenge
|
||||
{
|
||||
[$blueprintPath, $completionValue] = static::pickChallenge($faction, $type);
|
||||
|
||||
$challenge = new TimedChallenge();
|
||||
$challenge->type = $type;
|
||||
$challenge->faction = $faction;
|
||||
$challenge->blueprint_path = $blueprintPath;
|
||||
$challenge->completion_value = $completionValue;
|
||||
|
||||
$challenge->start_time = $startTime;
|
||||
|
||||
if($type === ChallengeType::Daily)
|
||||
$challenge->end_time = $startTime->copy()->addDay();
|
||||
else
|
||||
$challenge->end_time = $startTime->copy()->addWeek();
|
||||
|
||||
$challenge->rewards = [static::pickReward($type)];
|
||||
|
||||
return $challenge;
|
||||
}
|
||||
|
||||
protected static function pickChallenge(
|
||||
Faction $faction,
|
||||
ChallengeType $type,
|
||||
): array {
|
||||
$challengeArray = match ($faction) {
|
||||
Faction::Hunter => $type === ChallengeType::Daily ? static::AVAILABLE_DAILY_HUNTER : static::AVAILABLE_WEEKLY_HUNTER,
|
||||
Faction::Runner => $type === ChallengeType::Daily ? static::AVAILABLE_DAILY_RUNNER : static::AVAILABLE_WEEKLY_RUNNER,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if($challengeArray === null)
|
||||
throw new TimedChallengeFactoryException('Unallowed Faction ('. $faction->value .'), could not select a challenge.');
|
||||
|
||||
$blueprintPath = array_rand($challengeArray);
|
||||
$completionValue = $challengeArray[$blueprintPath];
|
||||
|
||||
return [$blueprintPath, $completionValue];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ChallengeType $type
|
||||
* @return Reward
|
||||
* @throws TimedChallengeFactoryException
|
||||
*/
|
||||
protected static function pickReward(
|
||||
ChallengeType $type,
|
||||
): Reward {
|
||||
$rewardsArray = match ($type) {
|
||||
ChallengeType::Daily => static::DAILY_REWARDS,
|
||||
ChallengeType::Weekly => static::WEEKLY_REWARDS,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if($rewardsArray === null)
|
||||
throw new TimedChallengeFactoryException('Unallowed Challenge Type (' . $type->value . '), could not select a reward.');
|
||||
|
||||
$pickedReward = array_rand($rewardsArray);
|
||||
|
||||
return new Reward(
|
||||
RewardType::Currency,
|
||||
round(rand($rewardsArray[$pickedReward]['min'], $rewardsArray[$pickedReward]['max']) / 10) * 10,
|
||||
$pickedReward,
|
||||
);
|
||||
}
|
||||
}
|
||||
94
dist/app/Console/Commands/GenerateTimedChallenges.php
vendored
Normal file
94
dist/app/Console/Commands/GenerateTimedChallenges.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Classes\Factory\TimedChallengeFactory;
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Enums\Game\Faction;
|
||||
use App\Enums\Game\RewardType;
|
||||
use App\Exceptions\TimedChallengeFactoryException;
|
||||
use App\Http\Responses\Api\General\Reward;
|
||||
use App\Models\Game\TimedChallenge;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class GenerateTimedChallenges extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:generate-timed-challenges';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate the Daily and Weekly challenges for the upcoming day/week';
|
||||
|
||||
const INTERVAL_HOUR = 21;
|
||||
const INTERVAL_MINUTE = 0;
|
||||
|
||||
const WEEKLY_INTERVAL_DAY = 'tuesday';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$dailyToday = Carbon::today();
|
||||
$dailyToday->setTime(static::INTERVAL_HOUR, static::INTERVAL_MINUTE);
|
||||
|
||||
// Put the current Daily a day in the past when the current time the job runs is still before the new Daily
|
||||
if(Carbon::now()->isBefore($dailyToday))
|
||||
$dailyToday->subDay();
|
||||
|
||||
$dailyTomorrow = $dailyToday->copy()->addDay();
|
||||
$currentWeekly = Carbon::parse('last '.static::WEEKLY_INTERVAL_DAY);
|
||||
$currentWeekly->setTime(static::INTERVAL_HOUR, static::INTERVAL_MINUTE);
|
||||
$nextWeekly = $currentWeekly->copy()->addWeek();
|
||||
|
||||
$dailys = [$dailyToday, $dailyTomorrow];
|
||||
$weeklys = [$currentWeekly, $nextWeekly];
|
||||
|
||||
$log = Log::channel('challengeCreation');
|
||||
|
||||
foreach ([Faction::Hunter, Faction::Runner] as $faction) {
|
||||
foreach ($dailys as $time) {
|
||||
$dailyExists = TimedChallenge::whereDate('start_time', $time)
|
||||
->where('type', ChallengeType::Daily)
|
||||
->where('faction', $faction)
|
||||
->exists();
|
||||
|
||||
if(!$dailyExists) {
|
||||
try {
|
||||
$newDaily = TimedChallengeFactory::makeChallenge($time, $faction, ChallengeType::Daily);
|
||||
$newDaily->save();
|
||||
} catch (TimedChallengeFactoryException $e) {
|
||||
$log->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($weeklys as $time) {
|
||||
$weeklyExists = TimedChallenge::whereDate('start_time', $time)
|
||||
->where('type', ChallengeType::Weekly)
|
||||
->where('faction', $faction)
|
||||
->exists();
|
||||
|
||||
if(!$weeklyExists) {
|
||||
try {
|
||||
$newDaily = TimedChallengeFactory::makeChallenge($time, $faction, ChallengeType::Weekly);
|
||||
$newDaily->save();
|
||||
} catch (TimedChallengeFactoryException $e) {
|
||||
$log->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
dist/app/Console/Kernel.php
vendored
1
dist/app/Console/Kernel.php
vendored
@@ -15,6 +15,7 @@ protected function schedule(Schedule $schedule): void
|
||||
$schedule->command('model:prune')->daily();
|
||||
$schedule->command('matchmaking:process')->everyFiveSeconds();
|
||||
$schedule->command('matchmaking:cleanup')->everyFifteenSeconds();
|
||||
$schedule->command('app:generate-timed-challenges')->daily();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
11
dist/app/Enums/Game/ChallengeType.php
vendored
Normal file
11
dist/app/Enums/Game/ChallengeType.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\Game;
|
||||
|
||||
enum ChallengeType: string
|
||||
{
|
||||
case None = 'None';
|
||||
case Daily = 'Daily';
|
||||
case Weekly = 'Weekly';
|
||||
case Event = 'Event';
|
||||
}
|
||||
6
dist/app/Enums/Game/RewardType.php
vendored
6
dist/app/Enums/Game/RewardType.php
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
enum RewardType: string
|
||||
{
|
||||
case Currency = 'Currency';
|
||||
case Inventory = 'Inventory';
|
||||
case Progression = 'Progression';
|
||||
case Currency = 'currency';
|
||||
case Inventory = 'inventory';
|
||||
case Progression = 'progression';
|
||||
}
|
||||
|
||||
8
dist/app/Exceptions/TimedChallengeFactoryException.php
vendored
Normal file
8
dist/app/Exceptions/TimedChallengeFactoryException.php
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class TimedChallengeFactoryException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -2,19 +2,30 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Player;
|
||||
|
||||
use App\Enums\Game\RewardType;
|
||||
use App\Helper\Uuid\UuidHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\Player\ExecuteChallengeProgressionBatchRequest;
|
||||
use App\Http\Requests\Api\Player\GetChallengeProgressionBatchRequest;
|
||||
use App\Http\Requests\Api\Player\GetChallengesRequest;
|
||||
use App\Http\Responses\Api\General\Reward;
|
||||
use App\Http\Responses\Api\Player\Challenges\ChallengeProgressionBatchResponse;
|
||||
use App\Http\Responses\Api\Player\Challenges\ChallengeProgressionEntry;
|
||||
use App\Http\Responses\Api\Player\Challenges\GetChallengesEntry;
|
||||
use App\Models\Game\Challenge;
|
||||
use App\Models\Game\Matchmaking\Game;
|
||||
use App\Models\Game\PickedChallenge;
|
||||
use App\Models\Game\TimedChallenge;
|
||||
use App\Models\User\PlayerData;
|
||||
use App\Models\User\User;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\UnauthorizedException;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
@@ -26,9 +37,30 @@ class ChallengeController extends Controller
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function getChallenges()
|
||||
public function getChallenges(GetChallengesRequest $request)
|
||||
{
|
||||
return json_encode(['challenges' => []]);
|
||||
$user = $request->user;
|
||||
$timedChallenges = TimedChallenge::where('start_time', '<', Carbon::now())
|
||||
->where('end_time', '>', Carbon::now())
|
||||
->where('type', $request->type)
|
||||
->get();
|
||||
|
||||
$challenges = [];
|
||||
$timedChallenges->each(function (TimedChallenge $challenge) use (&$challenges, $user) {
|
||||
$challenges[] = new GetChallengesEntry(
|
||||
$challenge->id,
|
||||
$challenge->start_time,
|
||||
$challenge->end_time,
|
||||
$challenge->type,
|
||||
$challenge->completion_value,
|
||||
$challenge->faction,
|
||||
$challenge->blueprint_path,
|
||||
$challenge->rewards,
|
||||
$challenge->hasPlayerClaimed($user->playerData()->id),
|
||||
);
|
||||
});
|
||||
|
||||
return Response::json(['challenges' => $challenges]);
|
||||
}
|
||||
|
||||
public function getProgressionBatch(GetChallengeProgressionBatchRequest $request)
|
||||
@@ -36,12 +68,23 @@ public function getProgressionBatch(GetChallengeProgressionBatchRequest $request
|
||||
$user = User::findOrFail($request->userId);
|
||||
$playerData = $user->playerData();
|
||||
|
||||
$timedChallengeIds = [];
|
||||
|
||||
foreach ($request->challengeIds as $index => $id) {
|
||||
if(Str::startsWith($id, GetChallengesEntry::ID_PREFIX)) {
|
||||
$timedChallengeIds[] = Str::remove(GetChallengesEntry::ID_PREFIX, $id);
|
||||
unset($request->challengeIds[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
$challengeIdsToCheck = UuidHelper::convertFromHexToUuidCollecton($request->challengeIds, true);
|
||||
|
||||
/** @var Challenge[]|Collection $challengesToCheck */
|
||||
$challengesToCheck = Challenge::findMany($challengeIdsToCheck);
|
||||
/** @var PickedChallenge[]|Collection $pickedChallengesToCheck */
|
||||
$pickedChallengesToCheck = PickedChallenge::findMany($challengeIdsToCheck);
|
||||
/** @var TimedChallenge[]|Collection $timedChallengesToCheck */
|
||||
$timedChallengesToCheck = TimedChallenge::findMany($timedChallengeIds);
|
||||
|
||||
$response = new ChallengeProgressionBatchResponse();
|
||||
|
||||
@@ -57,6 +100,20 @@ public function getProgressionBatch(GetChallengeProgressionBatchRequest $request
|
||||
$response->progressionBatch[] = $newEntry;
|
||||
}
|
||||
|
||||
foreach ($timedChallengesToCheck as $challenge) {
|
||||
$progress = $challenge->getProgressForPlayer($playerData->id);
|
||||
|
||||
$entry = new ChallengeProgressionEntry(
|
||||
GetChallengesEntry::ID_PREFIX . $challenge->id,
|
||||
$challenge->progress >= $challenge->completion_value,
|
||||
$progress,
|
||||
);
|
||||
|
||||
$entry->rewardsClaimed = $challenge->rewards;
|
||||
|
||||
$response->progressionBatch[] = $entry;
|
||||
}
|
||||
|
||||
foreach ($pickedChallengesToCheck as $challenge) {
|
||||
$response->progressionBatch[] = new ChallengeProgressionEntry(
|
||||
Uuid::fromString($challenge->id)->getHex()->toString(),
|
||||
@@ -65,6 +122,8 @@ public function getProgressionBatch(GetChallengeProgressionBatchRequest $request
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($challengesToCheck as $challenge) {}
|
||||
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
@@ -83,7 +142,11 @@ public function executeChallengeProgressionBatch(ExecuteChallengeProgressionBatc
|
||||
$processedChallenges = [];
|
||||
|
||||
foreach ($request->operations as $operation) {
|
||||
$challengeId = Uuid::fromString($operation['challengeId'])->toString();
|
||||
$challengeId = $operation['challengeId'];
|
||||
|
||||
if(!Str::startsWith($challengeId, GetChallengesEntry::ID_PREFIX)) {
|
||||
$challengeId = Uuid::fromString($operation['challengeId'])->toString();
|
||||
}
|
||||
|
||||
// Skip if we already processed this challenge because the completed ones are always first in the array.
|
||||
if(in_array($challengeId, $processedChallenges))
|
||||
@@ -99,8 +162,13 @@ public function executeChallengeProgressionBatch(ExecuteChallengeProgressionBatc
|
||||
|
||||
protected function addProgressToChallenge(string $challengeId, int $newProgress, User $user): void
|
||||
{
|
||||
/** @var Challenge|null $foundChallenge */
|
||||
$foundChallenge = $user->playerData()->challenges()->find($challengeId);
|
||||
if(Str::startsWith($challengeId, GetChallengesEntry::ID_PREFIX)) {
|
||||
$foundChallenge = TimedChallenge::find(Str::remove(GetChallengesEntry::ID_PREFIX, $challengeId));
|
||||
}
|
||||
else {
|
||||
/** @var Challenge|null $foundChallenge */
|
||||
$foundChallenge = $user->playerData()->challenges()->find($challengeId);
|
||||
}
|
||||
|
||||
if($foundChallenge !== null) {
|
||||
$foundChallenge->playerData()->updateExistingPivot($user->playerData()->id, [
|
||||
@@ -119,13 +187,38 @@ protected function addProgressToChallenge(string $challengeId, int $newProgress,
|
||||
|
||||
protected function setChallengeAsCompleted(string $challengeId, User $user)
|
||||
{
|
||||
/** @var Challenge|null $foundChallenge */
|
||||
$foundChallenge = $user->playerData()->challenges()->find($challengeId);
|
||||
$isTimed = false;
|
||||
// If the Challenge id starts with the Prefix, handle it as a timed challenge.
|
||||
if(Str::startsWith($challengeId, GetChallengesEntry::ID_PREFIX)) {
|
||||
$foundChallenge = TimedChallenge::find(Str::remove(GetChallengesEntry::ID_PREFIX, $challengeId));
|
||||
$isTimed = true;
|
||||
}
|
||||
else {
|
||||
/** @var Challenge|null $foundChallenge */
|
||||
$foundChallenge = $user->playerData()->challenges()->find($challengeId);
|
||||
}
|
||||
|
||||
if($foundChallenge !== null) {
|
||||
$foundChallenge->playerData()->updateExistingPivot($user->playerData()->id, [
|
||||
$pivotToEdit = [
|
||||
'progress' => $foundChallenge->completion_value,
|
||||
]);
|
||||
];
|
||||
|
||||
if($isTimed) {
|
||||
$playerData = $user->playerData();
|
||||
$hasClaimed = $foundChallenge->hasPlayerClaimed($playerData->id);
|
||||
|
||||
if(!$hasClaimed) {
|
||||
$rewardsToAdd = $foundChallenge->getRewards();
|
||||
|
||||
foreach($rewardsToAdd as $reward) {
|
||||
$this->addReward($reward, $playerData);
|
||||
}
|
||||
$playerData->save();
|
||||
$pivotToEdit['claimed'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$foundChallenge->playerData()->updateExistingPivot($user->playerData()->id, $pivotToEdit);
|
||||
$foundChallenge->save();
|
||||
return;
|
||||
}
|
||||
@@ -137,4 +230,33 @@ protected function setChallengeAsCompleted(string $challengeId, User $user)
|
||||
$foundChallenge->progress = $foundChallenge->completion_value;
|
||||
$foundChallenge->save();
|
||||
}
|
||||
|
||||
protected function addReward(Reward $reward, PlayerData &$playerData): void
|
||||
{
|
||||
if ($reward->type === RewardType::Currency) {
|
||||
switch ($reward->id) {
|
||||
case 'CurrencyA':
|
||||
$playerData->currency_a += $reward->amount;
|
||||
break;
|
||||
case 'CurrencyB':
|
||||
$playerData->currency_b += $reward->amount;
|
||||
break;
|
||||
case 'CurrencyC':
|
||||
$playerData->currency_c += $reward->amount;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ($reward->type === RewardType::Inventory) {
|
||||
$itemUuid = Uuid::fromString($reward->id)->toString();
|
||||
|
||||
try {
|
||||
// Since we can only have one occurrence of an item in the inventory, and an exception gets thrown when we try to add the same item twice
|
||||
$playerData->inventory()->attach($itemUuid);
|
||||
} catch (UniqueConstraintViolationException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
dist/app/Http/Requests/Api/Player/GetChallengesRequest.php
vendored
Normal file
42
dist/app/Http/Requests/Api/Player/GetChallengesRequest.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\Player;
|
||||
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class GetChallengesRequest extends FormRequest
|
||||
{
|
||||
public User $user;
|
||||
|
||||
public ChallengeType $type;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return \Auth::check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'data.userId' => 'required|exists:users,id',
|
||||
'data.challengeType' => ['required', Rule::enum(ChallengeType::class)],
|
||||
];
|
||||
}
|
||||
|
||||
public function passedValidation(): void
|
||||
{
|
||||
$this->user = User::find($this->input('data.userId'));
|
||||
$this->type = ChallengeType::tryFrom($this->input('data.challengeType'));
|
||||
}
|
||||
}
|
||||
56
dist/app/Http/Responses/Api/Player/Challenges/GetChallengesEntry.php
vendored
Normal file
56
dist/app/Http/Responses/Api/Player/Challenges/GetChallengesEntry.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Responses\Api\Player\Challenges;
|
||||
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Enums\Game\Faction;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class GetChallengesEntry implements \JsonSerializable
|
||||
{
|
||||
const ID_PREFIX = 'Timed:';
|
||||
|
||||
public function __construct(
|
||||
public int $id,
|
||||
public Carbon $startTime,
|
||||
public Carbon $endTime,
|
||||
public ChallengeType $type,
|
||||
public int $completionValue,
|
||||
public Faction $faction,
|
||||
public string $challengeBlueprint,
|
||||
public array $rewards,
|
||||
public bool $claimed,
|
||||
)
|
||||
{}
|
||||
|
||||
public function getChallengeId(): string {
|
||||
return static::ID_PREFIX . $this->id;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
$json = [
|
||||
'lifetime' =>
|
||||
[
|
||||
'creationTime' => $this->startTime->toIso8601ZuluString(),
|
||||
'expirationTime' => $this->endTime->toIso8601ZuluString(),
|
||||
],
|
||||
'challengeType' => $this->type,
|
||||
'challengeId' => $this->getChallengeId(),
|
||||
'challengeCompletionValue' => $this->completionValue,
|
||||
'faction' => $this->faction,
|
||||
'challengeBlueprint' => $this->challengeBlueprint,
|
||||
];
|
||||
|
||||
$rewards = [];
|
||||
foreach ($this->rewards as &$reward) {
|
||||
$reward['claimed'] = $this->claimed;
|
||||
$reward['weight'] = 100;
|
||||
$rewards[] = $reward;
|
||||
}
|
||||
$json['rewards'] = $rewards;
|
||||
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
78
dist/app/Models/Game/TimedChallenge.php
vendored
Normal file
78
dist/app/Models/Game/TimedChallenge.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Game;
|
||||
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Enums\Game\Faction;
|
||||
use App\Enums\Game\RewardType;
|
||||
use App\Http\Responses\Api\General\Reward;
|
||||
use App\Models\User\PlayerData;
|
||||
use Carbon\Carbon;
|
||||
use Eloquent;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @mixin IdeHelperTimedChallenge
|
||||
*/
|
||||
class TimedChallenge extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
'type' => ChallengeType::class,
|
||||
'faction' => Faction::class,
|
||||
'start_time' => 'datetime',
|
||||
'end_time' => 'datetime',
|
||||
'rewards' => 'array',
|
||||
];
|
||||
|
||||
public function getProgressForPlayer(int $playerDataId): int
|
||||
{
|
||||
$foundProgress = $this->playerData()->where('id', '=', $playerDataId)->first();
|
||||
|
||||
if ($foundProgress !== null)
|
||||
return $foundProgress->pivot->progress;
|
||||
|
||||
// We create challange relation and just return 0 since you cannot have progress for the challenge we just linked.
|
||||
$this->playerData()->attach($playerDataId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function hasPlayerClaimed(int $playerDataId): bool {
|
||||
$foundPlayerData = $this->playerData()->where('id', '=', $playerDataId)->first();
|
||||
|
||||
if ($foundPlayerData !== null)
|
||||
return $foundPlayerData->pivot->claimed;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Reward[]
|
||||
*/
|
||||
public function getRewards(): array {
|
||||
if ($this->rewards === null)
|
||||
return [];
|
||||
|
||||
$result = [];
|
||||
foreach ($this->rewards as $reward) {
|
||||
$result[] = new Reward(
|
||||
RewardType::tryFrom($reward['type']),
|
||||
$reward['amount'],
|
||||
$reward['id'],
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function playerData(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PlayerData::class)->withPivot(['progress', 'claimed']);
|
||||
}
|
||||
|
||||
public static function currentChallenges(): Eloquent|Builder {
|
||||
return static::where('start_time', '>', Carbon::now())
|
||||
->where('end_time', '<', Carbon::now());
|
||||
}
|
||||
}
|
||||
5
dist/app/Models/User/PlayerData.php
vendored
5
dist/app/Models/User/PlayerData.php
vendored
@@ -12,6 +12,7 @@
|
||||
use App\Models\Game\Challenge;
|
||||
use App\Models\Game\CharacterData;
|
||||
use App\Models\Game\QuitterState;
|
||||
use App\Models\Game\TimedChallenge;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@@ -143,6 +144,10 @@ public function challenges(): BelongsToMany
|
||||
return $this->belongsToMany(Challenge::class)->withPivot(['progress']);
|
||||
}
|
||||
|
||||
public function timedChallenges(): BelongsToMany {
|
||||
return $this->belongsToMany(TimedChallenge::class)->withPivot(['progress', 'claimed']);
|
||||
}
|
||||
|
||||
public function quitterState(): HasOne
|
||||
{
|
||||
return $this->hasOne(QuitterState::class);
|
||||
|
||||
7
dist/config/logging.php
vendored
7
dist/config/logging.php
vendored
@@ -135,6 +135,13 @@
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'challengeCreation' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/challengeCreation.log'),
|
||||
'level' => 'info',
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
|
||||
36
dist/database/migrations/2024_12_03_201112_create_timed_challenges_table.php
vendored
Normal file
36
dist/database/migrations/2024_12_03_201112_create_timed_challenges_table.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\Game\ChallengeType;
|
||||
use App\Enums\Game\Faction;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('timed_challenges', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->enum('type', array_column(ChallengeType::cases(), 'value'));
|
||||
$table->string('blueprint_path');
|
||||
$table->enum('faction', array_column(Faction::cases(), 'value'));
|
||||
$table->integer('completion_value');
|
||||
$table->datetime('start_time');
|
||||
$table->datetime('end_time');
|
||||
$table->json('rewards')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('timed_challenges');
|
||||
}
|
||||
};
|
||||
33
dist/database/migrations/2024_12_03_203904_create_player_data_timed_chalenge_table.php
vendored
Normal file
33
dist/database/migrations/2024_12_03_203904_create_player_data_timed_chalenge_table.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('player_data_timed_challenge', function (Blueprint $table) {
|
||||
$table->foreignId('timed_challenge_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->foreignId('player_data_id')->constrained('player_data')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->unsignedInteger('progress')->default(0);
|
||||
$table->boolean('claimed')->default(false);
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['timed_challenge_id', 'player_data_id'], 'unique_primary');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('player_data_timed_challenge');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user