137 lines
4.2 KiB
PHP
137 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\ParticipateStatus;
|
|
use App\Http\Requests\StoreHuntStepRequest;
|
|
use App\Models\Hunts;
|
|
use App\Models\HuntSteps;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class HuntStepsController extends Controller
|
|
{
|
|
public function store(StoreHuntStepRequest $request, Hunts $hunt): JsonResponse
|
|
{
|
|
if ($hunt->creator_id !== auth()->id()) {
|
|
return response()->json(['message' => 'Unauthorized'], 403);
|
|
}
|
|
|
|
$step = $hunt->steps()->create($request->validated());
|
|
|
|
return response()->json($step, 201);
|
|
}
|
|
|
|
public function validateStep(Request $request, Hunts $hunt, HuntSteps $step): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'latitude' => 'required|numeric',
|
|
'longitude' => 'required|numeric',
|
|
]);
|
|
|
|
$user = $request->user();
|
|
|
|
if ((int) $step->hunt_id !== (int) $hunt->id) {
|
|
return response()->json(['message' => 'Step does not belong to this hunt'], 404);
|
|
}
|
|
|
|
$participation = $user->participations()->where('hunt_id', $hunt->id)->first();
|
|
|
|
if (! $participation) {
|
|
return response()->json(['message' => 'Not participating in this hunt'], 403);
|
|
}
|
|
|
|
if ($participation->pivot->status !== ParticipateStatus::InProgress->value) {
|
|
return response()->json(['message' => 'Hunt is joined but not started'], 409);
|
|
}
|
|
|
|
if ((int) $participation->pivot->current_step_number !== (int) $step->step_number) {
|
|
return response()->json(['message' => 'This is not your current step'], 400);
|
|
}
|
|
|
|
$distance = $this->distanceMeters(
|
|
(float) $validated['latitude'],
|
|
(float) $validated['longitude'],
|
|
(float) $step->latitude,
|
|
(float) $step->longitude,
|
|
);
|
|
|
|
if ($distance > $step->radius_m) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'You are too far from the target.',
|
|
'distance_m' => round($distance),
|
|
'radius_m' => $step->radius_m,
|
|
], 400);
|
|
}
|
|
|
|
$nextStepNumber = (int) $step->step_number + 1;
|
|
$nextStep = $hunt->steps()->where('step_number', $nextStepNumber)->first();
|
|
|
|
$updateData = [];
|
|
|
|
if ($nextStep) {
|
|
$updateData['current_step_number'] = $nextStepNumber;
|
|
} else {
|
|
$updateData['current_step_number'] = $nextStepNumber;
|
|
$updateData['status'] = ParticipateStatus::Completed->value;
|
|
}
|
|
|
|
$hunt->participants()->updateExistingPivot($user->id, $updateData);
|
|
|
|
$response = [
|
|
'success' => true,
|
|
'message' => 'Step validated',
|
|
'revealed_hint' => [
|
|
'title' => $step->title,
|
|
'hint_text' => $step->hint_text,
|
|
'hint_media_url' => $step->hint_media_url,
|
|
],
|
|
];
|
|
|
|
if ($nextStep) {
|
|
$response['next_step'] = $this->stepTargetPayload($nextStep);
|
|
} else {
|
|
$response['message'] = 'Hunt completed';
|
|
$response['completed'] = true;
|
|
}
|
|
|
|
return response()->json($response);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function stepTargetPayload(HuntSteps $step): array
|
|
{
|
|
return [
|
|
'id' => $step->id,
|
|
'hunt_id' => $step->hunt_id,
|
|
'step_number' => $step->step_number,
|
|
'title' => $step->title,
|
|
'latitude' => $step->latitude,
|
|
'longitude' => $step->longitude,
|
|
'radius_m' => $step->radius_m,
|
|
'ar_model' => 'chest',
|
|
];
|
|
}
|
|
|
|
private function distanceMeters(float $latitudeFrom, float $longitudeFrom, float $latitudeTo, float $longitudeTo): float
|
|
{
|
|
$earthRadius = 6371000;
|
|
|
|
$latFrom = deg2rad($latitudeFrom);
|
|
$lonFrom = deg2rad($longitudeFrom);
|
|
$latTo = deg2rad($latitudeTo);
|
|
$lonTo = deg2rad($longitudeTo);
|
|
|
|
$latDelta = $latTo - $latFrom;
|
|
$lonDelta = $lonTo - $lonFrom;
|
|
|
|
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
|
|
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
|
|
|
|
return $angle * $earthRadius;
|
|
}
|
|
}
|