183 lines
5.3 KiB
PHP
183 lines
5.3 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Services\Game;
|
||
|
|
|
||
|
|
use App\Models\Character;
|
||
|
|
use App\Models\Crime;
|
||
|
|
use App\Models\CrimeAttempt;
|
||
|
|
use App\Models\ActivityLog;
|
||
|
|
use App\Enums\CharacterStatus;
|
||
|
|
|
||
|
|
class CrimeService
|
||
|
|
{
|
||
|
|
protected EnergyService $energyService;
|
||
|
|
protected EconomyService $economyService;
|
||
|
|
|
||
|
|
public function __construct()
|
||
|
|
{
|
||
|
|
$this->energyService = new EnergyService();
|
||
|
|
$this->economyService = new EconomyService();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 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 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,
|
||
|
|
'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()
|
||
|
|
->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()
|
||
|
|
->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()
|
||
|
|
->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();
|
||
|
|
}
|
||
|
|
}
|