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:
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Character;
|
||||
use App\Models\City;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Lightweight multiplayer presence feed.
|
||||
*
|
||||
* Returns the positions and states of characters active in a city so the
|
||||
* viewer can render other citizens in near-real-time without WebSockets.
|
||||
*/
|
||||
class CityPresenceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Snapshot of citizens in the requested (or default) city.
|
||||
*/
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
abort_unless($user !== null, 401);
|
||||
|
||||
$me = Character::ensureForUser($user);
|
||||
$slug = $request->query('city');
|
||||
$city = $me->city;
|
||||
if (is_string($slug) && $slug !== '') {
|
||||
$city = City::query()
|
||||
->where('slug', $slug)
|
||||
->orWhere('id', ctype_digit($slug) ? (int) $slug : 0)
|
||||
->first() ?? $city;
|
||||
}
|
||||
|
||||
$cacheKey = 'presence.'.$city->id.'.'.now()->timestamp;
|
||||
$citizens = Cache::remember($cacheKey, 1, function () use ($city, $me) {
|
||||
return Character::query()
|
||||
->where('city_id', $city->id)
|
||||
->where(function ($q): void {
|
||||
$q->where('is_online', true)
|
||||
->orWhere('updated_at', '>=', now()->subMinutes(10));
|
||||
})
|
||||
->with('user:id,name')
|
||||
->get()
|
||||
->map(fn (Character $c): array => [
|
||||
'id' => $c->id,
|
||||
'name' => $c->name,
|
||||
'type' => $c->user_id === null ? 'child' : 'citizen',
|
||||
'status' => $c->state,
|
||||
'position' => [(float) $c->pos_x, (float) $c->pos_y, (float) $c->pos_z],
|
||||
'is_online' => (bool) $c->is_online,
|
||||
'is_me' => (int) $c->id === (int) $me->id,
|
||||
'owner_name' => $c->user?->name,
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'city_id' => $city->id,
|
||||
'server_time' => now()->toIso8601String(),
|
||||
'citizens' => $citizens,
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user