feat: add JailService and bust-out config keys

Add JailService with bustOut(), sendToJail(), releaseFromJail(), and
calculateBustOutChance() methods. Add two config keys (bust_out_stat_multiplier
and bust_out_max_chance) to the jail config block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Doar Mihai
2026-06-28 22:42:59 +03:00
co-authored by Claude Sonnet 4.6
parent 68c4bbabd7
commit 799f46a5f2
2 changed files with 95 additions and 4 deletions
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace App\Services\Game;
use App\Models\Character;
class JailService
{
/**
* Calculate bust-out success chance for a character (0.0 to 1.0).
*/
public function calculateBustOutChance(Character $character): float
{
$base = config('game.jail.breakout_chance', 0.05);
$multiplier = config('game.jail.bust_out_stat_multiplier', 0.003);
$cap = config('game.jail.bust_out_max_chance', 0.80);
return min($cap, $base + ($character->dexterity + $character->speed) * $multiplier);
}
/**
* Attempt to bust out of jail.
*
* Deducts breakout_cost cash regardless of outcome.
* On success: releases character immediately.
* On failure: multiplies remaining sentence by sentence_multiplier.
*
* @return array{success: bool, message: string}
*/
public function bustOut(Character $character): array
{
$cost = config('game.jail.breakout_cost', 1000);
if ($character->money < $cost) {
return [
'success' => false,
'message' => 'You need $' . number_format($cost) . ' to attempt a bust-out.',
];
}
$character->money -= $cost;
$successRate = $this->calculateBustOutChance($character);
$roll = random_int(0, 9999) / 10000;
if ($roll <= $successRate) {
$this->releaseFromJail($character);
$character->save();
return [
'success' => true,
'message' => "You escaped! You're free.",
];
}
// Failure: extend sentence
$multiplier = config('game.jail.sentence_multiplier', 1.5);
$remaining = max(0, $character->in_jail_until->diffInSeconds(now()));
$extended = (int) ceil($remaining * $multiplier);
$character->in_jail_until = now()->addSeconds($extended);
$character->save();
$newReleaseTime = $character->in_jail_until->diffForHumans();
return [
'success' => false,
'message' => "Bust failed — your sentence was extended to {$newReleaseTime}.",
];
}
/**
* Send a character to jail for the given number of seconds.
*/
public function sendToJail(Character $character, int $seconds): void
{
$character->status = 'in_jail';
$character->in_jail_until = now()->addSeconds($seconds);
$character->save();
}
/**
* Release a character from jail immediately.
*/
public function releaseFromJail(Character $character): void
{
$character->status = 'active';
$character->in_jail_until = null;
}
}
+6 -4
View File
@@ -79,10 +79,12 @@ return [
// Jail system configuration // Jail system configuration
'jail' => [ 'jail' => [
'sentence_length' => 3600, // 1 hour base sentence 'sentence_length' => 3600,
'breakout_chance' => 0.05, // 5% chance to break out per attempt 'breakout_chance' => 0.05,
'breakout_cost' => 1000, // Cost to attempt breakout 'breakout_cost' => 1000,
'sentence_multiplier' => 1.5, // Sentence increases by 50% for repeat offenses 'sentence_multiplier' => 1.5,
'bust_out_stat_multiplier' => 0.003, // per combined dex+speed point
'bust_out_max_chance' => 0.80, // cap at 80%
], ],
// Economy configuration // Economy configuration