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
+37 -5
View File
@@ -1,13 +1,17 @@
<?php
use App\Http\Controllers\Web\AccountController;
use App\Http\Controllers\Web\Admin\AdminWorldController;
use App\Http\Controllers\Web\Admin\CitizenAdminController;
use App\Http\Controllers\Web\Admin\CityAdminController;
use App\Http\Controllers\Web\Admin\StoreAdminController;
use App\Http\Controllers\Web\AuthController;
use App\Http\Controllers\Web\CharacterChatController;
use App\Http\Controllers\Web\CharacterCustomizeController;
use App\Http\Controllers\Web\ChatController;
use App\Http\Controllers\Web\CityActionController;
use App\Http\Controllers\Web\CityMarketController;
use App\Http\Controllers\Web\CityPresenceController;
use App\Http\Controllers\Web\CitySocialController;
use App\Http\Controllers\Web\CityViewerController;
use App\Http\Controllers\Web\DashboardController;
@@ -78,17 +82,43 @@ Route::middleware('auth')->group(function (): void {
Route::get('/city/data', [CityViewerController::class, 'data'])->name('city.data');
// Citizen lifecycle actions inside the world.
Route::post('/city/actions/position', [CityActionController::class, 'position'])->name('city.actions.position');
Route::post('/city/actions/{action}', [CityActionController::class, 'handle'])->name('city.actions.handle');
Route::post('/city/actions/position', [CityActionController::class, 'position'])
->middleware('throttle:60,1')
->name('city.actions.position');
Route::post('/city/actions/{action}', [CityActionController::class, 'handle'])
->middleware('throttle:90,1')
->name('city.actions.handle');
// Character social graph and chat.
Route::post('/city/social/{verb}', [CitySocialController::class, 'handle'])->name('city.social.handle');
Route::post('/city/social/{verb}', [CitySocialController::class, 'handle'])
->middleware('throttle:30,1')
->name('city.social.handle');
Route::get('/city/social/inbox', [CitySocialController::class, 'inbox'])->name('city.social.inbox');
Route::post('/city/messages/{character}', [CharacterChatController::class, 'send'])->name('city.messages.send');
Route::post('/city/messages/{character}', [CharacterChatController::class, 'send'])
->middleware('throttle:60,1')
->name('city.messages.send');
Route::get('/city/messages/{character}', [CharacterChatController::class, 'index'])->name('city.messages.index');
// Multiplayer presence + marketplace + journal + cosmetics.
Route::get('/city/presence', CityPresenceController::class)
->middleware('throttle:60,1')
->name('city.presence');
Route::get('/city/market', [CityMarketController::class, 'index'])->name('city.market.index');
Route::post('/city/market/sell', [CityMarketController::class, 'sell'])
->middleware('throttle:30,1')
->name('city.market.sell');
Route::post('/city/market/buy', [CityMarketController::class, 'buy'])
->middleware('throttle:30,1')
->name('city.market.buy');
Route::get('/city/customize', [CharacterCustomizeController::class, 'show'])->name('city.customize.show');
Route::put('/city/customize', [CharacterCustomizeController::class, 'update'])
->middleware('throttle:20,1')
->name('city.customize.update');
// NPC chat (SSE stream + message POST).
Route::post('/city/chat/{agent}', [ChatController::class, 'send'])->name('city.chat.send');
Route::post('/city/chat/{agent}', [ChatController::class, 'send'])
->middleware('throttle:30,1')
->name('city.chat.send');
Route::get('/city/chat/{agent}/stream', [ChatController::class, 'stream'])->name('city.chat.stream');
});
});
@@ -115,4 +145,6 @@ Route::middleware(['auth', EnsureTermsAccepted::class, EnsureUserIsAdmin::class]
Route::get('/cities', [CityAdminController::class, 'index'])->name('cities.index');
Route::post('/cities', [CityAdminController::class, 'store'])->name('cities.store');
Route::post('/cities/{city}/expand', [CityAdminController::class, 'expand'])->name('cities.expand');
Route::get('/world', [AdminWorldController::class, 'index'])->name('world.index');
});