refactor: separate React views, Redux snapshots and Three.js runtime

This commit is contained in:
2026-10-28 12:26:40 +03:30
parent 6bd9b17173
commit d0da416923
77 changed files with 4941 additions and 3723 deletions
+1 -160
View File
@@ -14,163 +14,4 @@
<link rel="icon" type="image/png" href="./icons/favicon-64.png">
<link rel="apple-touch-icon" href="./icons/apple-touch-icon.png">
</head>
<body>
<video id="camera" playsinline muted autoplay hidden></video>
<canvas id="world"></canvas>
<div id="loading">در حال ساختن شهر مشهد…</div>
<!-- Mission briefing over the map of Iran -->
<section id="briefing">
<div class="brief-map"><canvas id="iran"></canvas></div>
<div class="brief-panel">
<img class="brief-robot" src="./robot/front.webp" alt="روبات راهنمای روبات مارکت" width="136" height="223">
<small class="tag">عملیات ۰۱ · خراسان رضوی</small>
<h1>عملیات مشهد</h1>
<p class="sub">روبات مارکت · شکار دستگاه‌های وندینگ</p>
<pre id="brief-text" aria-live="polite"></pre>
<button id="start-mission" type="button">شروع عملیات</button>
<div class="brief-actions">
<button id="real-mode" type="button" class="ghost">📍 بازی در دنیای واقعی (GPS)</button>
<button id="install" type="button" class="ghost" hidden>⬇ نصب بازی روی گوشی</button>
</div>
<p class="keys">حرکت: W A S D · دویدن: Shift · دوربین: ماوس · سوار و پیاده شدن: F · خرید: E · نقشه: M</p>
</div>
</section>
<!-- In-game HUD -->
<div id="hud" hidden>
<canvas id="compass" width="560" height="44"></canvas>
<button id="minimap-button" type="button" aria-label="باز کردن نقشهٔ شهر (M)">
<canvas id="minimap" width="220" height="220"></canvas>
<span class="map-hint">نقشه · M</span>
</button>
<div id="location"><span id="street">مشهد</span><small id="mode-label">پیاده</small><small id="gps-status" hidden></small></div>
<aside id="objectives">
<h2>مأموریت‌ها</h2>
<ol id="mission-list"></ol>
</aside>
<div id="waypoint" hidden><i></i><b id="waypoint-name"></b><span id="waypoint-distance"></span></div>
<div id="prompt" hidden></div>
<div id="subtitle" hidden><img id="subtitle-face" alt="" hidden><b id="subtitle-speaker"></b><span id="subtitle-text"></span></div>
<div id="awards" aria-live="polite"></div>
<div id="banner" hidden></div>
<div id="status">
<div class="stat"><small>بودجه</small><strong id="balance">۵۰۰</strong><span>هتومان</span></div>
<div class="stat"><small>ساعت</small><strong id="clock">۰۸:۳۰</strong></div>
<div class="stat" id="speed-stat" hidden><small>سرعت</small><strong id="speed">۰</strong><span>km/h</span></div>
</div>
<div id="rank"><b id="rank-name">تازه‌وارد</b><span id="xp">۰ XP</span><div class="bar"><i id="xp-bar"></i></div></div>
<div id="toolbar">
<button id="gps-toggle" type="button">📍 دنیای واقعی</button>
<button id="ar-toggle" type="button" hidden>📷 دوربین</button>
<button id="stable-toggle" type="button" class="on" hidden>🎯 پایدار: روشن</button>
<button id="sound" type="button" aria-pressed="false">صدا: خاموش</button>
<button id="quality" type="button">گرافیک</button>
<button id="restart" type="button">شروع دوباره</button>
</div>
<div id="touch" hidden>
<div id="stick"><i></i></div>
<div class="touch-buttons">
<button type="button" data-key="f">F</button>
<button type="button" data-key="e">E</button>
<button type="button" data-key="shift">دویدن</button>
</div>
</div>
<div id="crosshair"></div>
<small id="attribution">نقشه: © مشارکت‌کنندگان OpenStreetMap</small>
</div>
<!-- Payment at the card reader -->
<section id="payment" hidden>
<small>کارت‌خوان روبات مارکت</small>
<h2 id="payment-title"></h2>
<p id="payment-step"></p>
<div class="actions">
<button id="swipe" type="button" disabled>کشیدن کارت · C</button>
<button id="cancel-payment" type="button" class="ghost">انصراف</button>
</div>
</section>
<dialog id="map-dialog">
<header>
<div class="tabs" role="tablist">
<button type="button" role="tab" data-tab="city" aria-selected="true">مشهد</button>
<button type="button" role="tab" data-tab="iran" aria-selected="false">ایران</button>
</div>
<button type="button" class="close" id="map-close" aria-label="بستن">✕</button>
</header>
<div class="map-body">
<div class="map-view">
<canvas id="fullmap"></canvas>
<canvas id="iran-map" hidden></canvas>
<p class="map-help">کشیدن: جابه‌جایی · چرخ ماوس: زوم · کلیک روی دستگاه: انتخاب</p>
</div>
<aside class="map-side">
<h3>دستگاه‌های روبات مارکت</h3>
<div id="machine-detail" hidden>
<h4 id="detail-name"></h4>
<p id="detail-address"></p>
<p id="detail-meta"></p>
<p id="detail-products"></p>
<button id="route-button" type="button">مسیریابی تا اینجا</button>
</div>
<ul id="machine-list"></ul>
</aside>
</div>
</dialog>
<dialog id="shop">
<header>
<div>
<small id="shop-category"></small>
<h2 id="shop-name"></h2>
<p id="shop-address"></p>
</div>
<button type="button" class="close" id="shop-close" aria-label="بستن">✕</button>
</header>
<p id="shop-note"></p>
<div id="products"></div>
<p class="rate">۱۰ هتومان = ۱۰۰٬۰۰۰ ریال · مبالغ نمایشی‌اند و پرداخت واقعی انجام نمی‌شود.</p>
</dialog>
<dialog id="real-intro">
<img src="./robot/serious.webp" alt="" width="72" height="68">
<h2>بازی در دنیای واقعی</h2>
<p>با روشن کردن این حالت، موقعیت گوشی شما (GPS) روی نقشهٔ بازی نمایش داده می‌شود. وقتی واقعاً به محل یک دستگاه روبات مارکت برسید، می‌توانید خرید کنید و ۲۰۰ امتیاز اضافه بگیرید.</p>
<ul>
<li>هنگام راه رفتن به خیابان و ماشین‌ها نگاه کنید، نه به صفحه.</li>
<li>بچه‌ها فقط همراه یک بزرگ‌تر بازی کنند.</li>
<li>هنگام رانندگی از این حالت استفاده نکنید؛ در سرعت بالا خرید غیرفعال می‌شود.</li>
<li>موقعیت شما فقط روی همین گوشی می‌ماند و جایی ارسال نمی‌شود.</li>
</ul>
<div class="actions">
<button type="button" id="real-go">قبول دارم، شروع کن</button>
<button type="button" id="real-cancel" class="ghost">انصراف</button>
</div>
</dialog>
<dialog id="ios-install">
<h2>نصب روی آیفون</h2>
<ol>
<li>در Safari دکمهٔ <b>Share</b> (مربع با فلش رو به بالا) را بزنید.</li>
<li>گزینهٔ <b>Add to Home Screen</b> را انتخاب کنید.</li>
<li>روبات مارکت مثل یک اپ روی صفحهٔ اصلی گوشی شما قرار می‌گیرد و آفلاین هم اجرا می‌شود.</li>
</ol>
<button type="button" id="ios-close">متوجه شدم</button>
</dialog>
<div id="update-toast" hidden>نسخهٔ جدید بازی آماده است <button type="button" id="update-reload">به‌روزرسانی</button></div>
<dialog id="end">
<small id="end-tag">پایان عملیات</small>
<h2 id="end-title"></h2>
<p id="end-text"></p>
<ul id="end-stats"></ul>
<div class="actions">
<button id="again" type="button">عملیات دوباره</button>
<button id="continue" type="button" class="ghost">ادامهٔ گشت در شهر</button>
</div>
</dialog>
<script type="module" src="/src/main.js"></script>
</body>
</html>
<body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body></html>