energyService = new EnergyService(); $this->economyService = new EconomyService(); $this->nerveService = new NerveService(); } /** * Attempt a crime */ public function attemptCrime(Character $character, Crime $crime): CrimeAttempt { // Check if character can attempt this crime if (!$crime->canBeAttemptedBy($character)) { throw new \Exception('Character cannot attempt this crime.'); } // Check if character can act if (!$character->canAct()) { throw new \Exception('Character cannot perform actions right now.'); } // Consume nerve first so energy is never deducted if nerve is insufficient if (!$this->nerveService->consume($character, $crime->nerve_cost)) { throw new \Exception('Not enough nerve to attempt this crime.'); } // Consume energy if (!$this->energyService->consume($character, $crime->energy_cost, 'crime')) { throw new \Exception('Not enough energy to attempt this crime.'); } // Calculate success rate $successRate = $crime->calculateSuccessRate($character); $isSuccess = random_int(0, 9999) / 10000 <= $successRate; $reward = 0; $experience = 0; if ($isSuccess) { // Successful crime $reward = $crime->calculateReward($character); $experience = $crime->calculateExperience($character); $this->economyService->addMoney($character, $reward, "Crime: {$crime->name}"); $character->addExperience($experience); // Log success ActivityLog::logCrimeAttempt($character, $crime, true, $reward); } else { // Failed crime - take health penalty $healthPenalty = (int) ($character->max_health * config('game.crime.failure_penalty', 0.10)); $character->takeDamage($healthPenalty); // If dead, send to hospital if ($character->isDead()) { $character->sendToHospital(); } // Log failure ActivityLog::logCrimeAttempt($character, $crime, false, 0); } // Create crime attempt record $attempt = CrimeAttempt::create([ 'character_id' => $character->id, 'crime_id' => $crime->id, 'success' => $isSuccess, 'reward' => $reward, 'experience_gained' => $experience, 'nerve_cost' => $crime->nerve_cost, 'attempted_at' => now(), ]); return $attempt; } /** * Get available crimes for a character */ public function getAvailableCrimes(Character $character): \Illuminate\Database\Eloquent\Collection { return Crime::query() ->where(function($query) use ($character) { $query->whereNull('requires_level') ->orWhere('requires_level', '<=', $character->level); }) ->where(function($query) use ($character) { $query->whereNull('location_id') ->orWhere('location_id', $character->location_id); }) ->where('energy_cost', '<=', $character->energy) ->orderBy('difficulty') ->orderBy('name') ->get(); } /** * Get recent crime attempts for a character */ public function getRecentAttempts(Character $character, int $limit = 10): \Illuminate\Database\Eloquent\Collection { return $character->crimeAttempts() ->with('crime') ->latest('attempted_at') ->take($limit) ->get(); } /** * Check if a crime is on cooldown for a character */ public function isOnCooldown(Character $character, Crime $crime): bool { $lastAttempt = $character->crimeAttempts() ->where('crime_id', $crime->id) ->latest('attempted_at') ->first(); if (!$lastAttempt) { return false; } $cooldown = $crime->cooldown; $timeSinceAttempt = now()->diffInSeconds($lastAttempt->attempted_at); return $timeSinceAttempt < $cooldown; } /** * Get remaining cooldown time for a crime */ public function getCooldownRemaining(Character $character, Crime $crime): int { if (!$this->isOnCooldown($character, $crime)) { return 0; } $lastAttempt = $character->crimeAttempts() ->where('crime_id', $crime->id) ->latest('attempted_at') ->first(); $cooldown = $crime->cooldown; $timeSinceAttempt = now()->diffInSeconds($lastAttempt->attempted_at); return max(0, $cooldown - $timeSinceAttempt); } /** * Create a new crime */ public function createCrime(array $data): Crime { return Crime::create($data); } /** * Update a crime */ public function updateCrime(Crime $crime, array $data): Crime { $crime->update($data); return $crime; } /** * Delete a crime */ public function deleteCrime(Crime $crime): bool { return $crime->delete(); } }