creator_id !== auth()->id()) { return response()->json(['message' => 'Unauthorized'], 403); } $validated = $request->validated(); $step = $hunt->steps()->create($validated); return response()->json($step, 201); } /** * Validate a hunt step. */ public function validateStep(\Illuminate\Http\Request $request, \App\Models\Hunts $hunt, \App\Models\HuntSteps $step) { $request->validate([ 'latitude' => 'required|numeric', 'longitude' => 'required|numeric', ]); $user = $request->user(); // 1. Check participation $participation = $user->participations()->where('hunt_id', $hunt->id)->first(); if (!$participation) { return response()->json(['message' => 'Not participating in this hunt'], 403); } // 2. Check if this is the current step if ($participation->pivot->current_step_number !== $step->step_number) { return response()->json(['message' => 'This is not your current step'], 400); } // 3. Check proximity // Haversine formula $earthRadius = 6371000; // meters $latFrom = deg2rad($request->latitude); $lonFrom = deg2rad($request->longitude); $latTo = deg2rad($step->latitude); $lonTo = deg2rad($step->longitude); $latDelta = $latTo - $latFrom; $lonDelta = $lonTo - $lonFrom; $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) + cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2))); $distance = $angle * $earthRadius; if ($distance > $step->radius_m) { return response()->json([ 'success' => false, 'message' => 'You are too far from the target.', 'distance' => round($distance) . 'm' ], 400); } // 4. Update progress // Check if there is a next step $nextStepNumber = $step->step_number + 1; $nextStep = $hunt->steps()->where('step_number', $nextStepNumber)->first(); $updateData = []; if ($nextStep) { $updateData['current_step_number'] = $nextStepNumber; } else { $updateData['status'] = 'completed'; // Keep current_step_number as is (last step) or increment it to indicate "done"? // Usually keeping it at last step or marking status completed is enough. // Let's verify existing logic in HuntsController::show which filters based on step number. // If we increment, show() might need adjustment to show everything if completed. // For now, let's just mark completed and maybe keep same step number or increment. // If we increment, $nextStep is null, so user matches nothing in filter? // Let's increment current_step_number so that in show(), $step->step_number < $currentStepNumber becomes true for all steps. $updateData['current_step_number'] = $nextStepNumber; } $hunt->participants()->updateExistingPivot($user->id, $updateData); // 5. Return response $response = [ 'success' => true, 'message' => 'Step validated!', ]; if ($nextStep) { $response['next_step'] = [ 'step_number' => $nextStep->step_number, 'title' => $nextStep->title, 'description' => $nextStep->description, 'hint_text' => $nextStep->hint_text, 'hint_media_url' => $nextStep->hint_media_url, // Do NOT include lat/long ]; } else { $response['message'] = 'Hunt completed! Congratulations!'; $response['completed'] = true; } return response()->json($response); } }