feat(city): add player marketplace, mission journal, character customization and presence

- Player-to-player marketplace with listings, buying and selling
- Mission system with progress tracking and money rewards
- Character appearance customization (skin, hair, face, beard, accessory)
- Multiplayer presence feed showing nearby citizens in near-real-time
- Admin world dashboard with live platform metrics
- Bus and train transportation with temporary ride state
- Mobile joystick controls and minimap HUD
- Education bonus applied to work salary
This commit is contained in:
2026-10-17 22:51:20 -03:00
parent 18778b3d26
commit a620f83dd8
30 changed files with 1822 additions and 10 deletions
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use App\Http\Controllers\Web\Admin\AdminWorldController;
use Illuminate\Console\Command;
/**
* Prints a one-shot world metrics report for operators and CI logs.
*/
class CityReport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'city:report';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Print world metrics (citizens, economy, market, missions)';
/**
* Execute the console command.
*/
public function handle(): int
{
$metrics = app(AdminWorldController::class)->metrics();
$this->info('City10 world report');
$this->line('Users: '.$metrics['users']['total']
.' (pending '.$metrics['users']['pending']
.', active '.$metrics['users']['active']
.', banned '.$metrics['users']['banned'].')');
$this->line('Cities: '.$metrics['cities']);
$this->line('Characters: '.$metrics['characters']['total']
.' (online '.$metrics['characters']['online']
.', hospital '.$metrics['characters']['hospitalized']
.', avg health '.$metrics['characters']['avg_health']
.', money '.$metrics['characters']['total_money'].')');
$this->line('World: '.$metrics['world']['stores'].' stores, '
.$metrics['world']['houses_complete'].' houses, '
.$metrics['world']['jobs'].' jobs, '
.$metrics['world']['messages'].' messages, '
.$metrics['world']['transaction_volume'].' coins traded');
$this->line('Market: '.$metrics['market']['listings'].' active listings');
$this->line('Missions: '.$metrics['missions']['active'].' active, '
.$metrics['missions']['completed'].' completed');
return self::SUCCESS;
}
}
+11 -1
View File
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\Building;
use App\Models\Character;
use App\Support\MissionTracker;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
@@ -102,6 +103,12 @@ class CityTick extends Command
return;
}
// Temporary bus/train rides expire after ride_until.
if ($character->ride_kind !== null && $character->ride_until !== null
&& $character->ride_until->isPast()) {
$character->forceFill(['ride_kind' => null, 'ride_until' => null])->save();
}
$elapsedMin = 1.0;
if ($character->last_tick_at !== null) {
$elapsedMin = max(0.5, $character->last_tick_at->diffInMinutes($now));
@@ -124,7 +131,9 @@ class CityTick extends Command
case Character::STATE_WORKING:
if ($character->job_id !== null) {
$salary = (float) ($character->job?->salary ?? 0);
$character->money = round((float) $character->money + ($salary / 30) * $elapsedMin, 2);
// Education boosts take-home pay up to +50% at 100% education.
$bonus = 1 + (min(100, (int) $character->education) / 200);
$character->money = round((float) $character->money + ($salary * $bonus / 30) * $elapsedMin, 2);
}
$character->energy = max(0, (int) $character->energy - (int) round(1.2 * $elapsedMin));
break;
@@ -132,6 +141,7 @@ class CityTick extends Command
case Character::STATE_STUDYING:
$character->education = min(100, (int) $character->education + (int) round(0.5 * $elapsedMin));
$character->energy = max(0, (int) $character->energy - (int) round(0.8 * $elapsedMin));
MissionTracker::sync($character, 'education');
break;
case Character::STATE_EXERCISING: