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,43 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', __('admin.world.title'))
|
||||
|
||||
@section('content')
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div class="card" style="margin-bottom:1.25rem;">
|
||||
<h1 style="margin-top:0; color:#fff;">🌍 {{ __('admin.world.title') }}</h1>
|
||||
<p style="color:var(--muted);">{{ __('admin.world.hint') }}</p>
|
||||
</div>
|
||||
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:1rem;">
|
||||
@foreach ([
|
||||
['title' => __('admin.world.users'), 'value' => $metrics['users']['total'],
|
||||
'sub' => $metrics['users']['pending'].' pending · '.$metrics['users']['active'].' active · '.$metrics['users']['banned'].' banned'],
|
||||
['title' => __('admin.world.characters'), 'value' => $metrics['characters']['total'],
|
||||
'sub' => $metrics['characters']['online'].' online · '.$metrics['characters']['hospitalized'].' hospital'],
|
||||
['title' => __('admin.world.economy'), 'value' => $metrics['characters']['total_money'],
|
||||
'sub' => __('admin.world.trade_volume').': '.$metrics['world']['transaction_volume']],
|
||||
['title' => __('admin.world.world'), 'value' => $metrics['world']['houses_complete'],
|
||||
'sub' => $metrics['world']['stores'].' stores · '.$metrics['world']['jobs'].' jobs · '.$metrics['world']['messages'].' msgs'],
|
||||
['title' => __('admin.world.market'), 'value' => $metrics['market']['listings'],
|
||||
'sub' => __('admin.world.missions').': '.$metrics['missions']['completed'].' done / '.$metrics['missions']['active']],
|
||||
['title' => __('admin.world.health'), 'value' => $metrics['characters']['avg_health'],
|
||||
'sub' => __('admin.world.education').': '.$metrics['characters']['avg_education'].'%'],
|
||||
] as $card)
|
||||
<div class="card" style="margin:0;">
|
||||
<div style="color:var(--muted); font-size:.85rem;">{{ $card['title'] }}</div>
|
||||
<div style="color:#fff; font-size:1.8rem; font-weight:700;">{{ $card['value'] }}</div>
|
||||
<div style="color:var(--muted); font-size:.8rem; margin-top:.25rem;">{{ $card['sub'] }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:1.25rem;">
|
||||
<p style="color:var(--muted); margin:0;">
|
||||
<code>php artisan city:report</code> ·
|
||||
<a href="{{ route('admin.citizens.index') }}" style="color:#7dd3fc;">{{ __('nav.citizens') }}</a> ·
|
||||
<a href="{{ route('admin.cities.index') }}" style="color:#7dd3fc;">{{ __('nav.cities') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -27,6 +27,43 @@
|
||||
{{ __('city.error') }}
|
||||
</div>
|
||||
|
||||
{{-- Minimap --}}
|
||||
<div style="position:absolute; right:.7rem; bottom:4.6rem; z-index:12;">
|
||||
<canvas id="minimap" width="160" height="160"
|
||||
style="display:block; border-radius:.6rem; border:1px solid var(--border);
|
||||
box-shadow:0 8px 20px rgba(0,0,0,.4);"
|
||||
aria-label="{{ __('city.hud_minimap') }}"></canvas>
|
||||
<div style="margin-top:.3rem; font-size:.62rem; color:#cbd5e1; background:rgba(15,23,42,.8);
|
||||
border:1px solid var(--border); border-radius:.4rem; padding:.25rem .4rem; line-height:1.45;">
|
||||
<span style="color:#f472b6;">●</span> você
|
||||
<span style="color:#38bdf8;">●</span> jogadores
|
||||
<span style="color:#facc15;">●</span> pedestres<br>
|
||||
<span style="color:#94a3b8;">■</span> carros
|
||||
<span style="color:#a78bfa;">■</span> motos
|
||||
<span style="color:#4ade80;">■</span> bikes
|
||||
<span style="color:#60a5fa;">■</span> ônibus<br>
|
||||
<span style="color:#f59e0b;">■</span> lojas
|
||||
<span style="color:#22c55e;">●</span> praças
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Virtual joystick (mobile) --}}
|
||||
<div id="joystick-base"
|
||||
style="display:none; position:absolute; left:1.1rem; bottom:1.4rem; z-index:14;
|
||||
width:110px; height:110px; border-radius:50%;
|
||||
background:rgba(15,23,42,.55); border:2px solid rgba(148,163,184,.35);
|
||||
touch-action:none;">
|
||||
<div id="joystick-knob"
|
||||
style="position:absolute; left:50%; top:50%; width:44px; height:44px; margin:-22px 0 0 -22px;
|
||||
border-radius:50%; background:rgba(56,189,248,.75); border:2px solid #e0f2fe;"></div>
|
||||
</div>
|
||||
<style>
|
||||
@media (max-width: 820px), (pointer: coarse) {
|
||||
#joystick-base { display: block !important; }
|
||||
#minimap { width:120px !important; height:120px !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
{{-- Pedestrian red-light wait banner --}}
|
||||
<div id="ped-wait"
|
||||
style="display:none; position:absolute; bottom:4.2rem; left:50%; transform:translateX(-50%);
|
||||
@@ -77,6 +114,47 @@
|
||||
<button type="button" class="btn char-act" data-act="buy" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_buy') }}</button>
|
||||
<button type="button" class="btn char-act" data-act="sell" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_sell_v') }}</button>
|
||||
<button type="button" class="btn char-act" data-act="build" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_build') }}</button>
|
||||
<button type="button" id="btn-market" class="btn" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_market') }}</button>
|
||||
<button type="button" id="btn-customize" class="btn" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_customize') }}</button>
|
||||
<button type="button" id="btn-ride-bus" class="btn" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_ride_bus') }}</button>
|
||||
<button type="button" id="btn-ride-train" class="btn" style="padding:.25rem .45rem; font-size:.75rem;">{{ __('city.act_ride_train') }}</button>
|
||||
</div>
|
||||
|
||||
{{-- Marketplace --}}
|
||||
<div id="market-panel" style="display:none; margin-top:.55rem; border-top:1px solid var(--border); padding-top:.45rem;">
|
||||
<div style="color:#fff; font-weight:600;">🛒 {{ __('city.act_market') }}</div>
|
||||
<div id="market-list" style="max-height:120px; overflow:auto; font-size:.75rem;"></div>
|
||||
<div style="display:flex; gap:.25rem; margin-top:.3rem; flex-wrap:wrap;">
|
||||
<select id="market-item" style="flex:1; min-width:70px; padding:.2rem; background:#0b1220; color:var(--text); border:1px solid var(--border); border-radius:.3rem; font-size:.7rem;">
|
||||
<option value="product">product</option>
|
||||
<option value="food">food</option>
|
||||
<option value="water">water</option>
|
||||
<option value="materials">materials</option>
|
||||
</select>
|
||||
<input id="market-qty" type="number" min="1" max="100" value="1" style="width:48px; padding:.2rem; background:#0b1220; color:var(--text); border:1px solid var(--border); border-radius:.3rem; font-size:.7rem;">
|
||||
<input id="market-price" type="number" min="0.5" step="0.5" value="5" style="width:52px; padding:.2rem; background:#0b1220; color:var(--text); border:1px solid var(--border); border-radius:.3rem; font-size:.7rem;">
|
||||
<button type="button" id="market-sell" class="btn" style="padding:.2rem .4rem; font-size:.7rem;">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Appearance customization --}}
|
||||
<div id="customize-panel" style="display:none; margin-top:.55rem; border-top:1px solid var(--border); padding-top:.45rem;">
|
||||
<div style="color:#fff; font-weight:600;">🎨 {{ __('city.act_customize') }}</div>
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:.25rem; font-size:.72rem; margin-top:.3rem;">
|
||||
<label>skin <select id="cz-skin"><option>1</option><option>2</option><option>3</option><option>4</option></select></label>
|
||||
<label>hair <select id="cz-hair"><option>1</option><option>2</option><option>3</option><option>4</option></select></label>
|
||||
<label>shirt <select id="cz-shirt"><option>1</option><option>2</option><option>3</option><option>4</option><option>5</option></select></label>
|
||||
<label>face <select id="cz-face"><option>1</option><option>2</option><option>3</option></select></label>
|
||||
<label>beard <select id="cz-beard"><option>0</option><option>1</option><option>2</option></select></label>
|
||||
<label>acc <select id="cz-accessory"><option>0</option><option>1</option><option>2</option></select></label>
|
||||
</div>
|
||||
<button type="button" id="customize-save" class="btn" style="margin-top:.35rem; width:100%; padding:.3rem; font-size:.75rem;">💾</button>
|
||||
</div>
|
||||
|
||||
{{-- Missions journal --}}
|
||||
<div style="margin-top:.55rem; border-top:1px solid var(--border); padding-top:.45rem;">
|
||||
<div style="color:#fff; font-weight:600;">📜 {{ __('admin.world.missions') }}</div>
|
||||
<div id="mission-list" style="max-height:100px; overflow:auto; font-size:.72rem; margin-top:.25rem;"></div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:.6rem; color:#fff; font-weight:600;">{{ __('city.hud_jobs') }}</div>
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
<a href="{{ route('admin.stores.index') }}" class="badge badge-admin" style="text-decoration:none;">{{ __('nav.admin') }}</a>
|
||||
<a href="{{ route('admin.citizens.index') }}">{{ __('nav.citizens') }}</a>
|
||||
<a href="{{ route('admin.cities.index') }}">{{ __('nav.cities') }}</a>
|
||||
<a href="{{ route('admin.world.index') }}">{{ __('nav.world') }}</a>
|
||||
@endif
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
Reference in New Issue
Block a user