a bit of progress
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Game;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Character;
|
||||
use App\Models\Attack as AttackModel;
|
||||
use App\Services\Game\CombatService;
|
||||
use App\Services\Game\HealthService;
|
||||
use App\Services\Game\JailService;
|
||||
use App\Services\Game\NotificationService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AttackController extends Controller
|
||||
{
|
||||
protected CombatService $combatService;
|
||||
protected HealthService $healthService;
|
||||
protected JailService $jailService;
|
||||
protected NotificationService $notificationService;
|
||||
|
||||
public function __construct(
|
||||
CombatService $combatService,
|
||||
HealthService $healthService,
|
||||
JailService $jailService,
|
||||
NotificationService $notificationService
|
||||
) {
|
||||
$this->combatService = $combatService;
|
||||
$this->healthService = $healthService;
|
||||
$this->jailService = $jailService;
|
||||
$this->notificationService = $notificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the attack page with potential targets
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$character = $request->user()->character;
|
||||
|
||||
if (!$character) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
// Check if character is in jail or hospital
|
||||
if ($character->isInJail() || $character->isInHospital() || !$character->canAct()) {
|
||||
return redirect()->route('game.dashboard')
|
||||
->with('error', 'You cannot attack while in jail, hospital, or unable to act.');
|
||||
}
|
||||
|
||||
// Get potential targets (other characters in same location, not in jail/hospital)
|
||||
$potentialTargets = Character::where('location_id', $character->location_id)
|
||||
->where('id', '!=', $character->id)
|
||||
->where('status', 'active')
|
||||
->where('health', '>', 0)
|
||||
->orderBy('level', 'desc')
|
||||
->get();
|
||||
|
||||
// Get attack cooldown
|
||||
$cooldownRemaining = $this->combatService->getAttackCooldownRemaining($character);
|
||||
|
||||
return view('game.attack.index', compact(
|
||||
'character',
|
||||
'potentialTargets',
|
||||
'cooldownRemaining'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show attack preview for a specific target
|
||||
*/
|
||||
public function preview(Request $request, Character $target): View
|
||||
{
|
||||
$attacker = $request->user()->character;
|
||||
|
||||
if (!$attacker) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
// Check if can attack this target
|
||||
if (!$this->combatService->canAttack($attacker, $target)) {
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('error', 'You cannot attack this target right now.');
|
||||
}
|
||||
|
||||
// Calculate preview stats
|
||||
$winProbability = $this->combatService->getWinProbability($attacker, $target);
|
||||
$hitChance = $this->combatService->getHitChance($attacker, $target);
|
||||
$attackerPower = $attacker->getAttackPowerAttribute();
|
||||
$defenderDefense = $target->getDefenseAttribute();
|
||||
$cooldownRemaining = $this->combatService->getAttackCooldownRemaining($attacker);
|
||||
|
||||
// Check for weapon and armor
|
||||
$attackerWeapon = $attacker->inventory()
|
||||
->where('equipped', true)
|
||||
->whereHas('item', function($q) {
|
||||
$q->where('type', 'weapon');
|
||||
})
|
||||
->first();
|
||||
|
||||
$defenderArmor = $target->inventory()
|
||||
->where('equipped', true)
|
||||
->whereHas('item', function($q) {
|
||||
$q->where('type', 'armor');
|
||||
})
|
||||
->first();
|
||||
|
||||
return view('game.attack.preview', compact(
|
||||
'attacker',
|
||||
'target',
|
||||
'winProbability',
|
||||
'hitChance',
|
||||
'attackerPower',
|
||||
'defenderDefense',
|
||||
'cooldownRemaining',
|
||||
'attackerWeapon',
|
||||
'defenderArmor'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an attack on another character
|
||||
*/
|
||||
public function attack(Request $request, Character $target): RedirectResponse
|
||||
{
|
||||
$attacker = $request->user()->character;
|
||||
|
||||
if (!$attacker) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
$isMugging = $request->has('mug') && $request->boolean('mug');
|
||||
|
||||
// Check if can attack this target
|
||||
if (!$this->combatService->canAttack($attacker, $target)) {
|
||||
$cooldown = $this->combatService->getAttackCooldownRemaining($attacker);
|
||||
if ($cooldown > 0) {
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('error', "You must wait {$cooldown} seconds before attacking again.");
|
||||
}
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('error', 'You cannot attack this target right now.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Process the attack
|
||||
$result = $this->combatService->processAttack($attacker, $target, $isMugging);
|
||||
|
||||
// Record the attack
|
||||
$attackRecord = AttackModel::create([
|
||||
'attacker_id' => $attacker->id,
|
||||
'defender_id' => $target->id,
|
||||
'attacker_damage' => $result['attacker_damage'],
|
||||
'defender_damage' => $result['defender_damage'],
|
||||
'attacker_health_after' => $attacker->health,
|
||||
'defender_health_after' => $target->health,
|
||||
'result' => $result['result']->value,
|
||||
'money_stolen' => $result['money_stolen'],
|
||||
'experience_gained' => $result['experience_gained'],
|
||||
'was_critical' => $result['was_critical'],
|
||||
'was_mugging' => $result['was_mugging'],
|
||||
'attacker_weapon_id' => $attacker->inventory()
|
||||
->where('equipped', true)
|
||||
->whereHas('item', function($q) { $q->where('type', 'weapon'); })
|
||||
->first()?->item_id,
|
||||
'defender_armor_id' => $target->inventory()
|
||||
->where('equipped', true)
|
||||
->whereHas('item', function($q) { $q->where('type', 'armor'); })
|
||||
->first()?->item_id,
|
||||
'attacked_at' => now(),
|
||||
]);
|
||||
|
||||
// Handle death consequences
|
||||
if ($result['defender_died']) {
|
||||
// Defender died - send to hospital
|
||||
$this->healthService->sendToHospital($target);
|
||||
|
||||
// If not mugging, send attacker to jail for killing
|
||||
if (!$isMugging) {
|
||||
$sentenceLength = config('game.jail.sentence_length', 3600);
|
||||
$this->jailService->sendToJail($attacker, $sentenceLength);
|
||||
}
|
||||
}
|
||||
|
||||
if ($result['attacker_died']) {
|
||||
// Attacker died - send to hospital
|
||||
$this->healthService->sendToHospital($attacker);
|
||||
}
|
||||
|
||||
// Send notifications
|
||||
if ($result['result']->value === 'win') {
|
||||
if ($isMugging) {
|
||||
$this->notificationService->send($request->user(), 'mug_success', [
|
||||
'target_name' => $target->name,
|
||||
'money_stolen' => $result['money_stolen'],
|
||||
]);
|
||||
} else {
|
||||
$this->notificationService->send($request->user(), 'attack_win', [
|
||||
'target_name' => $target->name,
|
||||
'experience_gained' => $result['experience_gained'],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->notificationService->send($request->user(), 'attack_result', [
|
||||
'target_name' => $target->name,
|
||||
'result' => $result['result']->value,
|
||||
]);
|
||||
}
|
||||
|
||||
// Send notification to defender
|
||||
if ($target->user) {
|
||||
$this->notificationService->send($target->user, 'attacked', [
|
||||
'attacker_name' => $attacker->name,
|
||||
'result' => $result['result']->value,
|
||||
'damage_taken' => $result['attacker_damage'],
|
||||
]);
|
||||
}
|
||||
|
||||
$successMessage = match ($result['result']->value) {
|
||||
'win' => $isMugging ? "You mugged {$target->name} and stole $" . number_format($result['money_stolen']) . "!" : "You defeated {$target->name}!",
|
||||
'lose' => "You lost to {$target->name}!",
|
||||
'draw' => "Your attack against {$target->name} ended in a draw.",
|
||||
default => "Attack result: {$result['result']->label()}",
|
||||
};
|
||||
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('success', $successMessage);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a hit/bounty on a character
|
||||
*/
|
||||
public function placeHit(Request $request, Character $target): RedirectResponse
|
||||
{
|
||||
$attacker = $request->user()->character;
|
||||
|
||||
if (!$attacker) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'amount' => ['required', 'integer', 'min:1000'],
|
||||
'reason' => ['nullable', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
$hitCost = config('game.attack.hit_list_cost', 5000);
|
||||
|
||||
if (!$attacker->hasMoney($validated['amount'] + $hitCost)) {
|
||||
return redirect()->back()
|
||||
->with('error', 'You need at least $' . number_format($validated['amount'] + $hitCost) . ' to place a hit.');
|
||||
}
|
||||
|
||||
// Place the hit - TODO: Implement Hit model
|
||||
$attacker->removeMoney($validated['amount'] + $hitCost);
|
||||
|
||||
$this->notificationService->send($request->user(), 'hit_placed', [
|
||||
'target_name' => $target->name,
|
||||
'amount' => $validated['amount'],
|
||||
]);
|
||||
|
||||
return redirect()->route('game.attack.index')
|
||||
->with('success', "Hit placed on {$target->name} for $" . number_format($validated['amount']) . "!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent attacks for a character
|
||||
*/
|
||||
public function history(Request $request): View
|
||||
{
|
||||
$character = $request->user()->character;
|
||||
|
||||
if (!$character) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
// Attacks made by this character
|
||||
$myAttacks = AttackModel::where('attacker_id', $character->id)
|
||||
->with(['defender', 'defender.user'])
|
||||
->latest('attacked_at')
|
||||
->paginate(20);
|
||||
|
||||
// Attacks made against this character
|
||||
$attacksOnMe = AttackModel::where('defender_id', $character->id)
|
||||
->with(['attacker', 'attacker.user'])
|
||||
->latest('attacked_at')
|
||||
->paginate(20);
|
||||
|
||||
return view('game.attack.history', compact(
|
||||
'character',
|
||||
'myAttacks',
|
||||
'attacksOnMe'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get characters who have attacked you recently (for revenge)
|
||||
*/
|
||||
public function revengeList(Request $request): View
|
||||
{
|
||||
$character = $request->user()->character;
|
||||
|
||||
if (!$character) {
|
||||
return redirect()->route('game.character.create');
|
||||
}
|
||||
|
||||
// Get characters who attacked this character and are still alive
|
||||
$revengeTargets = AttackModel::where('defender_id', $character->id)
|
||||
->where('attacker_id', '!=', $character->id)
|
||||
->whereHas('attacker', function($q) {
|
||||
$q->where('health', '>', 0)
|
||||
->where('status', 'active');
|
||||
})
|
||||
->with(['attacker', 'attacker.user'])
|
||||
->latest('attacked_at')
|
||||
->get()
|
||||
->unique('attacker_id')
|
||||
->pluck('attacker');
|
||||
|
||||
return view('game.attack.revenge', compact(
|
||||
'character',
|
||||
'revengeTargets'
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Http\Controllers\Game;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Game\CharacterDepositRequest;
|
||||
use App\Http\Requests\Game\CharacterWithdrawRequest;
|
||||
use App\Models\Character;
|
||||
use App\Models\Location;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Services\Game\ActivityLogService;
|
||||
use App\Services\Game\EconomyService;
|
||||
use App\Services\Game\EnergyService;
|
||||
use App\Services\Game\HealthService;
|
||||
@@ -21,13 +23,20 @@ class CharacterController extends Controller
|
||||
protected EnergyService $energyService;
|
||||
protected HealthService $healthService;
|
||||
protected NotificationService $notificationService;
|
||||
protected ActivityLogService $activityLogService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->economyService = new EconomyService();
|
||||
$this->energyService = new EnergyService();
|
||||
$this->healthService = new HealthService();
|
||||
$this->notificationService = new NotificationService();
|
||||
public function __construct(
|
||||
EconomyService $economyService,
|
||||
EnergyService $energyService,
|
||||
HealthService $healthService,
|
||||
NotificationService $notificationService,
|
||||
ActivityLogService $activityLogService
|
||||
) {
|
||||
$this->economyService = $economyService;
|
||||
$this->energyService = $energyService;
|
||||
$this->healthService = $healthService;
|
||||
$this->notificationService = $notificationService;
|
||||
$this->activityLogService = $activityLogService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +94,7 @@ class CharacterController extends Controller
|
||||
]);
|
||||
|
||||
// Log character creation
|
||||
ActivityLog::logCharacterCreated($character);
|
||||
$this->activityLogService->logCharacterCreated($character);
|
||||
|
||||
return redirect()->route('game.dashboard')
|
||||
->with('success', 'Character created successfully! Welcome to the game.');
|
||||
@@ -144,15 +153,11 @@ class CharacterController extends Controller
|
||||
/**
|
||||
* Deposit money to bank
|
||||
*/
|
||||
public function deposit(Request $request): RedirectResponse
|
||||
public function deposit(CharacterDepositRequest $request): RedirectResponse
|
||||
{
|
||||
$character = $request->user()->character;
|
||||
|
||||
$validated = $request->validate([
|
||||
'amount' => ['required', 'integer', 'min:1'],
|
||||
]);
|
||||
|
||||
if ($this->economyService->depositToBank($character, $validated['amount'])) {
|
||||
if ($this->economyService->depositToBank($character, $request->amount)) {
|
||||
return redirect()->back()
|
||||
->with('success', 'Money deposited to bank successfully!');
|
||||
}
|
||||
@@ -164,15 +169,11 @@ class CharacterController extends Controller
|
||||
/**
|
||||
* Withdraw money from bank
|
||||
*/
|
||||
public function withdraw(Request $request): RedirectResponse
|
||||
public function withdraw(CharacterWithdrawRequest $request): RedirectResponse
|
||||
{
|
||||
$character = $request->user()->character;
|
||||
|
||||
$validated = $request->validate([
|
||||
'amount' => ['required', 'integer', 'min:1'],
|
||||
]);
|
||||
|
||||
if ($this->economyService->withdrawFromBank($character, $validated['amount'])) {
|
||||
if ($this->economyService->withdrawFromBank($character, $request->amount)) {
|
||||
return redirect()->back()
|
||||
->with('success', 'Money withdrawn from bank successfully!');
|
||||
}
|
||||
|
||||
@@ -18,11 +18,14 @@ class CrimeController extends Controller
|
||||
protected EnergyService $energyService;
|
||||
protected NotificationService $notificationService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->crimeService = new CrimeService();
|
||||
$this->energyService = new EnergyService();
|
||||
$this->notificationService = new NotificationService();
|
||||
public function __construct(
|
||||
CrimeService $crimeService,
|
||||
EnergyService $energyService,
|
||||
NotificationService $notificationService
|
||||
) {
|
||||
$this->crimeService = $crimeService;
|
||||
$this->energyService = $energyService;
|
||||
$this->notificationService = $notificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Game;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Game\TrainRequest;
|
||||
use App\Services\Game\TrainingService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -12,9 +13,9 @@ class GymController extends Controller
|
||||
{
|
||||
protected TrainingService $trainingService;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(TrainingService $trainingService)
|
||||
{
|
||||
$this->trainingService = new TrainingService();
|
||||
$this->trainingService = $trainingService;
|
||||
}
|
||||
|
||||
public function index(Request $request): View
|
||||
@@ -35,10 +36,8 @@ class GymController extends Controller
|
||||
return view('game.gym.index', compact('character', 'stats', 'energyCost', 'dailyCap'));
|
||||
}
|
||||
|
||||
public function train(Request $request): RedirectResponse
|
||||
public function train(TrainRequest $request): RedirectResponse
|
||||
{
|
||||
$request->validate(['stat' => 'required|in:strength,defense,speed,dexterity']);
|
||||
|
||||
$character = $request->user()->character;
|
||||
$result = $this->trainingService->train($character, $request->stat);
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ class InventoryController extends Controller
|
||||
{
|
||||
protected EconomyService $economyService;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(EconomyService $economyService)
|
||||
{
|
||||
$this->economyService = new EconomyService();
|
||||
$this->economyService = $economyService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,9 +11,9 @@ class JailController extends Controller
|
||||
{
|
||||
protected JailService $jailService;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(JailService $jailService)
|
||||
{
|
||||
$this->jailService = new JailService();
|
||||
$this->jailService = $jailService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user