From fbfa8d011d615aacf8cc932aa242a0ea0982dae6 Mon Sep 17 00:00:00 2001 From: "hamed@B" Date: Wed, 28 Oct 2026 12:53:12 +0330 Subject: [PATCH] docs: document React architecture and enforce migration checks in CI --- .gitea/workflows/ci-cd.yml | 6 +- .github/workflows/pages.yml | 6 +- Dockerfile | 2 +- README.md | 83 +++++----- docs/01-product-and-scenario.md | 35 +++++ docs/02-game-rules.md | 71 +++++++++ docs/03-architecture.md | 54 +++++++ docs/04-world-and-data.md | 44 ++++++ docs/05-device-and-offline.md | 33 ++++ docs/06-development-and-deployment.md | 55 +++++++ docs/07-implemented-plans.md | 36 +++++ docs/08-review-and-backlog.md | 43 ++++++ docs/09-agent-task-guide.md | 48 ++++++ docs/10-game-reference.md | 210 ++++++++++++++++++++++++++ docs/11-react-migration.md | 42 ++++++ docs/README.md | 39 +++++ 16 files changed, 762 insertions(+), 45 deletions(-) create mode 100644 docs/01-product-and-scenario.md create mode 100644 docs/02-game-rules.md create mode 100644 docs/03-architecture.md create mode 100644 docs/04-world-and-data.md create mode 100644 docs/05-device-and-offline.md create mode 100644 docs/06-development-and-deployment.md create mode 100644 docs/07-implemented-plans.md create mode 100644 docs/08-review-and-backlog.md create mode 100644 docs/09-agent-task-guide.md create mode 100644 docs/10-game-reference.md create mode 100644 docs/11-react-migration.md create mode 100644 docs/README.md diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index 7f3ff2f..a091231 100644 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -41,8 +41,12 @@ jobs: - uses: actions/checkout@v4 - name: Install run: npm ci --no-audit --no-fund + - name: Check formatting + run: npm run format:check + - name: Lint + run: npm run lint - name: Test - run: node --test tests/*.test.js + run: npm test - name: Build run: npm run build diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 8e44639..ad636ca 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -27,8 +27,12 @@ jobs: node-version: 22 cache: npm - run: npm ci + - name: Check formatting + run: npm run format:check + - name: Lint + run: npm run lint - name: Test game logic - run: node --test tests/*.test.js + run: npm test - run: npm run build - uses: actions/configure-pages@v5 - uses: actions/upload-pages-artifact@v3 diff --git a/Dockerfile b/Dockerfile index b913e05..c910160 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ ARG NPM_REGISTRY=https://registry.npmjs.org/ COPY package.json package-lock.json ./ RUN npm ci --no-audit --no-fund --registry="$NPM_REGISTRY" --fetch-retries=3 --fetch-timeout=60000 COPY . . -RUN node --test tests/*.test.js && npm run build +RUN npm run format:check && npm run lint && npm test && npm run build # Stage 2: serve dist/ with Caddy, which also obtains and renews the HTTPS certificate. FROM caddy:2.8.4-alpine diff --git a/README.md b/README.md index 771d13f..97d5d7c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,25 @@ # عملیات مشهد — روبات مارکت -بازی سه‌بعدی مرورگری (Three.js) با حال‌وهوای بازی‌های اکشن سوم‌شخص، بدون سلاح و خشونت. یک پدر و فرزندش از کارخانهٔ سازندهٔ دستگاه‌ها راه می‌افتند، در خیابان‌های واقعی مشهد می‌گردند، دستگاه‌های وندینگ روبات مارکت را پیدا می‌کنند و با هر خرید امتیاز می‌گیرند. روبات راهنمای روبات مارکت داخل دستگاه‌ها زندگی می‌کند: وقتی خانواده به یک دستگاه می‌رسند، از آن بیرون می‌آید، خوش‌آمد می‌گوید و راهنمایی‌شان می‌کند. +📚 [مستندات کامل بازی و راهنمای توسعه برای Agent](docs/README.md) — قوانین، سناریو، معماری، تاریخچه و بک‌لاگ بر اساس بررسی کد فعلی. + +بازی سه‌بعدی مرورگری (React 18 + Three.js، JavaScript، Tailwind CSS) با حال‌وهوای بازی‌های اکشن سوم‌شخص، بدون سلاح و خشونت. یک پدر و فرزندش از کارخانهٔ سازندهٔ دستگاه‌ها راه می‌افتند، در خیابان‌های واقعی مشهد می‌گردند، دستگاه‌های وندینگ روبات مارکت را پیدا می‌کنند و با هر خرید امتیاز می‌گیرند. روبات راهنمای روبات مارکت داخل دستگاه‌ها زندگی می‌کند: وقتی خانواده به یک دستگاه می‌رسند، از آن بیرون می‌آید، خوش‌آمد می‌گوید و راهنمایی‌شان می‌کند. بازی یک PWA است: روی گوشی نصب می‌شود و آفلاین هم اجرا می‌شود. 🎮 [اجرای آنلاین بازی](https://hamedabdollahzade.github.io/vending-machine-game/) +## نسخهٔ React و نمایشگاه کارخانه + +رابط با React، وضعیت مشترک با Redux Toolkit و درخواست‌ها با Axios پیاده شده‌اند. موتور Three.js از رابط جداست. [معماری جدید و راهنمای Agent](docs/11-react-migration.md) مرجع ساختار فعلی است. + +سه دستگاه نمونهٔ ۳۵، ۴۸ و ۶۰ کانال در حیاط کارخانه قرار دارند. با Q نزدیک نمونه‌ها یا دکمهٔ «مدل‌های کارخانه» داخل محوطه، قیمت و امکانات را از روبات بپرسید. قیمت دستگاه‌ها به‌ترتیب ۴٬۶۸۰٬۰۰۰٬۰۰۰، ۵٬۴۸۰٬۰۰۰٬۰۰۰ و ۵٬۹۸۰٬۰۰۰٬۰۰۰ ریال است؛ این معرفی بودجهٔ هتومان را کم نمی‌کند. تصاویر/GIFهای کاتالوگ هنوز اضافه نشده‌اند؛ نمونه‌های سه‌بعدی موجودند و رابط برای نبود فایل پیام جایگزین دارد. + +```sh +npm test +npm run lint +npm run format:check +``` + ## داستان و هدف - **شروع:** سالن تولید کارخانهٔ **شرکت روبات مارکت** (بزرگراه مشهد–چناران، خیابان کاله، کنار پارک علم و فناوری خراسان). کارخانه این بخش‌ها را دارد: @@ -96,7 +110,7 @@ npm install npm run dev npm run build -node --test tests/*.test.js +npm test ``` - `?quality=low` یا دکمهٔ «گرافیک» سایه‌ها را خاموش می‌کند (پیش‌فرض گوشی‌ها). @@ -132,29 +146,14 @@ docker compose logs -f ## CI/CD روی Gitea -مخزن اصلی `ssh://git@git.my-rm.com:2222/Hamed/vending-machine-game.git` با نام `origin` است. GitHub با نام `github` باقی مانده. گردش‌کار [.gitea/workflows/ci-cd.yml](.gitea/workflows/ci-cd.yml) با هر push خودش اجرا می‌شود: +گردش‌کار `.gitea/workflows/ci-cd.yml` تست و build را اجرا می‌کند. برای main تصویر Docker در Registry ساخته و منتشر می‌شود؛ سپس runner از Docker socket مشترک سرور و فایل `deploy/docker-compose.yml` برای pull و up استفاده می‌کند. انتشار فعلی SSH نیست. -- **هر push و pull request:** نصب، تست‌ها و build -- **push به `main`:** بعد از قبولی تست‌ها، کد از راه SSH به سرور فرستاده می‌شود و `docker compose up -d --build` اجرا می‌شود. +- Secrets لازم: `REGISTRY_USERNAME` و `REGISTRY_TOKEN` با دسترسی read/write به package. +- Variables اختیاری: `NPM_REGISTRY` برای mirror و `HEALTH_URL` برای بررسی پاسخ سایت. +- شبکهٔ خارجی `frontend_proxy` و Nginx Proxy Manager برای HTTPS در این روش لازم‌اند؛ خود کانتینر روی پورت ۸۰ داخلی است. +- Compose ریشه روش مستقل دیگری است که Caddy مستقیماً TLS می‌دهد؛ با Compose مخصوص deploy اشتباه نشود. -**راه‌اندازی یک‌باره:** -1. در سرور، Docker و Docker Compose نصب باشد و کاربر SSH بتواند `docker` اجرا کند (عضو گروه `docker` یا root). -2. یک کلید SSH مخصوص انتشار بسازید و کلید عمومی را به `~/.ssh/authorized_keys` سرور اضافه کنید: - ```sh - ssh-keygen -t ed25519 -N '' -f deploy_key -C gitea-deploy - ssh-copy-id -i deploy_key.pub user@server - ``` -3. در Gitea، بخش Settings → Actions → Secrets مخزن، این‌ها را تعریف کنید: - - `DEPLOY_HOST`: آدرس سرور - - `DEPLOY_SSH_KEY`: محتوای فایل `deploy_key` - - در صورت نیاز: `DEPLOY_USER` (پیش‌فرض root)، `DEPLOY_PORT` (پیش‌فرض ۲۲) و `DEPLOY_KNOWN_HOSTS` (خروجی `ssh-keyscan server`) -4. اختیاری، در بخش Variables: - - `DEPLOY_PATH`: پوشهٔ روی سرور - - `SITE_ADDRESS`: دامنه‌ها - - `HEALTH_URL`: مثلاً `https://robotmarkets.ir/`، تا بعد از انتشار بالا آمدن سایت چک شود - - `NPM_REGISTRY`: اگر runner یا سرور به `registry.npmjs.org` دسترسی ندارد (معمول در سرورهای ایران)، آدرس یک mirror در دسترس npm را اینجا بگذارید. همین مقدار برای build روی سرور هم استفاده می‌شود. - -تا وقتی `DEPLOY_HOST` تعریف نشده، مرحلهٔ انتشار رد می‌شود و فقط تست و build اجرا می‌شوند. هر مرحله محدودیت زمانی دارد (تست ۱۵ دقیقه، انتشار ۳۰ دقیقه) تا runner هیچ‌وقت بی‌پایان معطل نماند. +پیش از push نام remote و branch مقصد را بررسی کنید. [جزئیات انتشار و تفاوت روش‌ها](docs/06-development-and-deployment.md). ## ساخت دوبارهٔ دادهٔ نقشه @@ -206,29 +205,29 @@ node scripts/fetch-imagery.mjs ## ساختار -- `src/main.js`: صحنه، دوربین سوم‌شخص، کنترل‌ها، رابط کاربری (HUD)، نقشه، خرید و حلقهٔ بازی -- `src/city.js`: ساخت شهر سه‌بعدی از داده‌ها (خیابان، پیاده‌رو، ساختمان، پارک، درخت، ورودی مترو، کوه‌ها) در تکه‌های ۲ کیلومتری که دورها پنهان می‌شوند -- `src/map-render.js`: نقشهٔ گوشه، نقشهٔ کامل شهر (کاشی‌های کش‌شده و نمای کلی) و نقشهٔ ایران -- `src/routing.js`: گراف خیابان‌ها، مسیریابی A* و پیدا کردن نام خیابان -- `src/collision.js`: برخورد با ساختمان‌ها برای شخصیت‌ها، ماشین و دوربین -- `src/locations.js`: دستگاه‌ها، آدرس‌ها، ساعت کاری و محصولات -- `src/game-state.js`، `src/payment.js`، `src/scoring.js`: پول، خرید، پرداخت، امتیاز، درجه و مأموریت‌ها -- `src/characters.js`: پدر و فرزند، و روبات راهنما که از دستگاه‌ها بیرون می‌آید -- `src/factory.js`: محوطهٔ کارخانه، شامل سالن تولید، خط مونتاژ، دفتر، حیاط و تابلوها، به‌همراه دفترها، کامیون و ورود مدیر -- `src/staff.js`: آدم‌های کارخانه، یعنی کارمندهای مفصل (هودی، کت‌وشلوار، مانتو و روسری) با برچسب اسم، و ۴۰ کارگر به‌صورت instanced -- `src/car-models.js`: ماشین خانواده (سدان اسپرت)، وانت مدیر و کامیون -- `src/pwa.js` و `public/manifest.webmanifest`: نصب، کار آفلاین و پیام به‌روزرسانی -- `src/realworld.js`: GPS، قطب‌نما، نرم‌سازی و حالت پایدار موقعیت، و شعاع رسیدن -- `src/imagery.js`: زمین ماهواره‌ای که کاشی‌هایش با نزدیک شدن بارگذاری و با دور شدن آزاد می‌شوند -- `src/buildings-data.js`: خواندن فایل ساختمان‌های Microsoft -- `src/ar.js`: نمای دوربین (AR)، شامل تصویر دوربین پشت گوشی، جهت گوشی و زاویهٔ دید -- `src/vending.js`، `src/vehicle.js`، `src/traffic.js`: مدل دستگاه، فیزیک ماشین و ترافیک شهری -- `src/geo.js`: تبدیل طول و عرض جغرافیایی به مختصات بازی +- `src/game/createGame.js و src/game/runtime/`: صحنه، دوربین سوم‌شخص، کنترل‌ها، رابط کاربری (HUD)، نقشه، خرید و حلقهٔ بازی +- `src/game/world/city.js`: ساخت شهر سه‌بعدی از داده‌ها (خیابان، پیاده‌رو، ساختمان، پارک، درخت، ورودی مترو، کوه‌ها) در تکه‌های ۲ کیلومتری که دورها پنهان می‌شوند +- `src/game/world/mapRender.js`: نقشهٔ گوشه، نقشهٔ کامل شهر (کاشی‌های کش‌شده و نمای کلی) و نقشهٔ ایران +- `src/game/world/routing.js`: گراف خیابان‌ها، مسیریابی A* و پیدا کردن نام خیابان +- `src/game/world/collision.js`: برخورد با ساختمان‌ها برای شخصیت‌ها، ماشین و دوربین +- `src/constants/locations.js`: دستگاه‌ها، آدرس‌ها، ساعت کاری و محصولات +- `src/game/economy/gameState.js`، `src/game/economy/payment.js`، `src/game/economy/scoring.js`: پول، خرید، پرداخت، امتیاز، درجه و مأموریت‌ها +- `src/game/models/characters.js`: پدر و فرزند، و روبات راهنما که از دستگاه‌ها بیرون می‌آید +- `src/game/models/factory.js`: محوطهٔ کارخانه، شامل سالن تولید، خط مونتاژ، دفتر، حیاط و تابلوها، به‌همراه دفترها، کامیون و ورود مدیر +- `src/game/models/staff.js`: آدم‌های کارخانه، یعنی کارمندهای مفصل (هودی، کت‌وشلوار، مانتو و روسری) با برچسب اسم، و ۴۰ کارگر به‌صورت instanced +- `src/game/models/carModels.js`: ماشین خانواده (سدان اسپرت)، وانت مدیر و کامیون +- `src/game/device/pwa.js` و `public/manifest.webmanifest`: نصب، کار آفلاین و پیام به‌روزرسانی +- `src/game/device/realworld.js`: GPS، قطب‌نما، نرم‌سازی و حالت پایدار موقعیت، و شعاع رسیدن +- `src/game/world/imagery.js`: زمین ماهواره‌ای که کاشی‌هایش با نزدیک شدن بارگذاری و با دور شدن آزاد می‌شوند +- `src/game/world/buildingsData.js`: خواندن فایل ساختمان‌های Microsoft +- `src/game/device/ar.js`: نمای دوربین (AR)، شامل تصویر دوربین پشت گوشی، جهت گوشی و زاویهٔ دید +- `src/game/models/vending.js`، `src/game/systems/vehicle.js`، `src/game/systems/traffic.js`: مدل دستگاه، فیزیک ماشین و ترافیک شهری +- `src/utils/geo.js`: تبدیل طول و عرض جغرافیایی به مختصات بازی ## محدودیت‌ها و گام‌های بعدی - مدل‌ها هندسی و سبک‌اند و کیفیت گرافیکی بازی‌های AAA را ندارند. -- خیابان‌ها یک‌طرفه و چراغ راهنمایی ندارند، و ترافیک فقط روی بلوارهای اصلی است. +- داده و ترافیک جهت یک‌طرفه دارند، اما مسیریابی هنوز دوطرفه است و چراغ راهنمایی شبیه‌سازی نمی‌شود؛ ترافیک روی خیابان‌های اصلی است. - پیشنهادهای بعدی: امتیاز ایمنی (خط عابر و چراغ قرمز)، تاکسی و قطار شهری، چرخهٔ روز و شب، ذخیرهٔ پیشرفت، جدول امتیاز آنلاین، و محله‌ها و شهرهای دیگر. نقشه: © مشارکت‌کنندگان OpenStreetMap (ODbL). ساختمان‌ها: © Microsoft (ODbL). تصویر ماهواره‌ای: Esri, Maxar, Earthstar Geographics, and the GIS User Community. مرز استان‌ها: Natural Earth. diff --git a/docs/01-product-and-scenario.md b/docs/01-product-and-scenario.md new file mode 100644 index 0000000..25222b4 --- /dev/null +++ b/docs/01-product-and-scenario.md @@ -0,0 +1,35 @@ +# محصول، سناریو و دامنهٔ فعلی + +## هویت + +نام فعلی: «عملیات مشهد — روبات مارکت». بازی مرورگری فارسی، راست‌به‌چپ، سه‌بعدی، بدون سلاح و خشونت است. هدف اصلی کشف دستگاه‌های فرضی وندینگ در جغرافیای واقعی مشهد، خرید نمایشی و پیشرفت در مأموریت‌هاست. مختصات و نقشه واقعی‌اند؛ وجود یک دستگاه در بازی به معنای وجود دستگاه واقعی در آن نشانی نیست. + +## سناریوی قابل اجرا + +1. صفحهٔ briefing نقشهٔ ایران و متن مأموریت را نشان می‌دهد. فقط مشهد فعال است؛ شهرهای دیگر نمایش قفل دارند. +2. پس از شروع، پدر و کودک در حیاط کارخانهٔ شرکت روبات مارکت، کنار ماشین خانواده و رو به دفتر مهندسی هستند. README فعلی شروع را «سالن تولید» توصیف می‌کند؛ محل spawn واقعی در `factory.start` حیاط است. +3. خانواده می‌تواند سالن تولید، دفاتر و کارکنان را ببیند. گفت‌وگوهای آغازین به معرفی کارخانه و ورود مدیر اشاره می‌کنند. +4. اولین هدف، خرید از دستگاه دفتر فنی مهندسی است. روبات از کنار دستگاه بیرون می‌آید، خوش‌آمد می‌گوید و کنترل E و ماشین F را معرفی می‌کند. +5. خانواده با خودرو از کارخانه به شهر می‌رود؛ گراف فعلی مسیر کارخانه تا بوستان ملت را حدود ۱۸ تا ۲۳ کیلومتر محاسبه می‌کند (تست محدودهٔ طول مسیر). +6. اهداف شهری شامل بوستان ملت، مدرسه، دانشگاه فردوسی، مترو و مشهد مال هستند. خریدهای دیگر و کشف آزاد نیز ممکن‌اند. +7. تکمیل همهٔ هفت مأموریت، یا نبود هیچ خرید قابل پرداخت مطابق منطق فعلی پایان بودجه، پنجرهٔ پایان را باز می‌کند. پس از تکمیل همهٔ مأموریت‌ها دکمهٔ ادامه وجود دارد؛ «شروع دوباره» عملیات تازه می‌سازد. + +ترتیب نمایشی مأموریت‌ها خطی است، اما شرایط تکمیل همهٔ مأموریت‌ها پس از هر خرید بررسی می‌شوند؛ انجام زودتر یک هدف ممنوع نیست. الزام «رفتن با ماشین» در متن بعضی مأموریت‌هاست و به‌عنوان شرط در رکورد خرید ذخیره/بررسی نمی‌شود. + +## شخصیت‌ها + +- پدر و کودک انسانی تیم قابل کنترل‌اند؛ یک player مشترک، جهت و انیمیشن آن‌ها را هدایت می‌کند. +- روبات فیروزه‌ای/سفید Robot Market راهنمای دستگاه‌هاست. با نزدیک‌شدن ظاهر، با دورشدن جمع و در سفر همراه خانواده نمی‌شود. مدل هندسی در `characters.js` و صورت‌های تصویری در `public/robot` هستند. +- روبات می‌تواند دست تکان بدهد، اشاره کند و همراه زیرنویس حرف بزند. صورت زیرنویس بر اساس نام فایل تصویری انتخاب می‌شود. +- کارکنان دفتر: حامد (React، هودی مشکی)، رایان (Go، هودی آبی)، علیرضا (C++، هودی مشکی)، رضا (C++، کت‌وشلوار)، محمد؛ چهار همکار حسابداری و آقای محمدی مدیر. +- نام React، Go و C++ در گفت‌وگو و مانیتور کارکنان بخشی از داستان کارخانه است؛ بازی با این فناوری‌ها نوشته نشده است. + +## کارخانه + +پنج بخش تولید: خم‌کاری، برش لیزر، رنگ‌کاری، یخچال‌سازی، مونتاژ؛ ۴۰ کارگر instanced، ابزار و قطعات متحرک، دستگاه‌های آماده، بسته‌بندی و بارگیری. کامیون هشت بسته را بار می‌زند، خارج می‌شود، مدتی غایب می‌ماند، بازمی‌گردد و دنده‌عقب به سکو می‌رسد. این چرخه تزئینی است؛ امتیاز و موجودی کالا را تغییر نمی‌دهد. + +مدیر با وانت سفید به سبک KMC T8 وارد می‌شود، پارک می‌کند، تا دفتر راه می‌رود و می‌نشیند؛ گفت‌وگوی او بعد از نشستن فعال می‌شود. گفت‌وگوی هر محدوده/کارمند معمولاً یک بار در هر عملیات و با نزدیک‌شدن فعال می‌شود. برای متن دقیق، `factory.talks` و `greet`/`startMission` در `main.js` مرجع‌اند. + +## خارج از دامنهٔ فعلی + +پرداخت بانکی، سرور حساب کاربری، ذخیرهٔ پیشرفت، leaderboard آنلاین، شهر قابل بازی غیر از مشهد، مأموریت مبارزه، چرخهٔ روز/شب و شبیه‌سازی کامل قوانین ترافیک پیاده‌سازی نشده‌اند. پارک کوهسنگی و حرم/مرکز شهر در فهرست مناطق آینده‌اند، نه مأموریت فعال. diff --git a/docs/02-game-rules.md b/docs/02-game-rules.md new file mode 100644 index 0000000..813e785 --- /dev/null +++ b/docs/02-game-rules.md @@ -0,0 +1,71 @@ +# قوانین بازی، اقتصاد و قراردادهای رفتاری + +## حالت، زمان و ورودی + +- حالت جابه‌جایی `walk` یا `drive` است. GPS حالت مستقل `real.on` است و حرکت را به موقعیت گوشی می‌سپارد. +- شروع: ۵۰۰ هتومان، صفر خرید، XP صفر، مأموریت انجام‌شدهٔ خالی، ساعت ۰۸:۳۰. +- هر ثانیهٔ واقعی فعال = ده ثانیهٔ بازی؛ `state.minutes += dt * 10 / 60`. +- briefing، فروشگاه، نقشه، پنجرهٔ پایان و مخفی‌بودن document شبیه‌سازی را متوقف می‌کنند. زیرنویس‌ها به‌تنهایی حرکت را نمی‌بندند. پنل پرداخت شبیه‌سازی را متوقف نمی‌کند، اما `busy` حرکت پیاده را محدود می‌کند. +- WASD و جهت‌ها: حرکت نسبی به دوربین/کنترل ماشین؛ Shift: دویدن؛ Space: ترمز دستی؛ F: سوار/پیاده؛ E: فروشگاه؛ C: کشیدن کارت؛ Escape: انصراف پیش از تأیید؛ M: نقشه. +- کنترل حروف با `KeyboardEvent.code` است تا کیبورد فارسی هم کار کند. چرخ ماوس فاصلهٔ دوربین را بین ۳ و ۱۲ تغییر می‌دهد. روی دسکتاپ Pointer Lock و در fallback/موبایل drag وجود دارد. +- موبایل joystick و دکمه‌های F، E و toggle دویدن دارد. E موبایل در مرحلهٔ ready کارت می‌کشد. +- سرعت هدف پیاده ۳٫۴ و دویدن ۶٫۵ متر/ثانیه با نرم‌سازی است. برخورد دایره‌ای شخصیت شعاع ۱ متر دارد. مرز جهان با فاصلهٔ ۱۵ متر محدود می‌شود. + +## ماشین + +سوارشدن در فاصلهٔ حداکثر ۴٫۵ متر و خارج پرداخت/مشغولی؛ پیاده‌شدن در قدرمطلق سرعت حداکثر ۳ متر/ثانیه. UI پیشنهاد پیاده‌شدن را با شرط محدودتر سرعت زیر ۱ و دستگاه نزدیک نشان می‌دهد. سمت خروج راننده بررسی برخورد می‌شود. + +ثابت‌های `CAR`: سقف جلو ۵۵٫۶ متر/ثانیه (حدود ۲۰۰ km/h)، عقب ۶، شتاب ۹٫۵، ترمز ۱۴، drag برابر ۱٫۶، فرمان حداکثر ۰٫۵۵ رادیان، wheelBase برابر ۲٫۷. فرمان در سرعت بالا محدودتر می‌شود. برخورد دیوار دو دایرهٔ جلو/عقب و حرکت زیرگام دارد. تصادف ترافیک سرعت را کم می‌کند و NPC پس از تماس طولانی حذف/بازپدید می‌شود. جریمهٔ نقدی، خسارت قابل تعمیر و سوخت وجود ندارد. + +## پول و محصول + +۱۰ هتومان = ۱۰۰٬۰۰۰ ریال؛ محاسبات با هتومان صحیح و نمایش با رقم فارسی است. قیمت هر دستگاه `Math.round(baseCost * priceFactor)` است؛ مثال آب کارخانه `round(15 × .5) = 8`. + +| محصول | نوع | قیمت پایه هتومان | +|---|---|---:| +| آب‌میوه | drink | ۳۵ | +| آب معدنی | drink | ۱۵ | +| شیر کاکائو | drink | ۴۰ | +| چای سرد | drink | ۴۵ | +| لیموناد | drink | ۴۰ | +| بیسکویت | snack | ۲۵ | +| شکلات | snack | ۳۰ | +| آجیل | snack | ۶۵ | +| کیک | snack | ۳۵ | + +خرید نامحدود تا حد بودجه است؛ موجودی انبار، مصرف محصول، فروش دوباره و رزرو پول برای مأموریت نداریم. کانال‌های عددی ۲۱ تا ۶۰ متعلق به نسخهٔ قدیمی‌اند؛ فروشگاه فعلی کارت محصول با productId دارد. + +`purchaseBlocker` به ترتیب دستگاه/محصول ناشناخته، busy، بسته‌بودن و کمبود پول را کنترل می‌کند. بازبودن شامل ساعت شروع و فاقد ساعت پایان است: `start <= minutes % 1440 < end`. بازهٔ عبور از نیمه‌شب پشتیبانی نشده است. + +## پرداخت و تحویل + +`idle → approaching → ready → approved → delivery → idle` + +- بازکردن فروشگاه: پیاده، دستگاه نزدیک (۳٫۴ متر در حالت مجازی)، عملیات تمام‌نشده و بدون busy/پرداخت فعال. +- انتخاب محصول و `beginPayment`: بدون برداشت؛ busy روشن و دوربین نزدیک کارت‌خوان. +- بعد از بیش از ۱ ثانیه آماده؛ تنها C/دکمهٔ کشیدن کارت خرید را ثبت و مبلغ را یک بار کسر می‌کند. +- انصراف در approaching یا ready بدون تغییر پول/خرید؛ پس از approved مجاز نیست. +- پس از بیش از ۱٫۲ ثانیهٔ approved، تحویل ۲٫۶ ثانیه‌ای آغاز می‌شود. busy تا پایان تحویل باقی می‌ماند. +- رکورد خرید شامل machineId، category، productId، type، price و minutes است. XP و جایزهٔ مأموریت در پایان تحویل اعمال می‌شوند، نه لحظهٔ انتخاب. +- همه‌چیز نمایشی و داخل مرورگر است؛ اتصال درگاه یا تراکنش واقعی وجود ندارد. + +## امتیاز و درجه + +هر خرید ۱۰۰ XP؛ اولین خرید از هر machineId، ۲۵۰؛ اولین ترکیب drink و snack در همان دستگاه، ۱۵۰؛ تکمیل پنج دستهٔ شهری park/university/school/metro/mall، ۵۰۰. factory و test در پاداش پنج دسته و explorer شمرده نمی‌شوند. خرید در real.on بعد از تحویل ۲۰۰ XP اضافه می‌دهد؛ کشف/ترکیب برای دستگاه آزمایشی هم ممکن است. + +| درجه | حداقل XP | +|---|---:| +| تازه‌وارد | ۰ | +| کاوشگر | ۶۰۰ | +| راه‌بلد | ۱۴۰۰ | +| شکارچی دستگاه | ۲۶۰۰ | +| استاد نقشه | ۴۲۰۰ | +| افسانهٔ مشهد | ۶۵۰۰ | + +مأموریت‌ها تنها یک بار با missionsDone جایزه می‌دهند؛ مشخصات کامل در [مرجع](10-game-reference.md). activeMission اولین مأموریت انجام‌نشده است، ولی completeMissions همهٔ شرط‌ها را بررسی می‌کند. + +## پایان و reset + +`checkEnd` در پایان خرید و تقریباً هر نیم‌ثانیه بررسی می‌شود: همهٔ مأموریت‌ها کامل یا `canAffordAnything` منفی. تابع بودجه دستگاه ارزان قابل خرید فعلی/با پایان ساعت دیرتر در همان روز را می‌شمارد، نه امکان کسب پول در آینده؛ محدودیت‌های این رفتار در بازبینی آمده است. + +reset پول، خرید، XP، ساعت، payment، spawn، ماشین، مأموریت، مقصد و greeting روبات را تازه می‌کند. reset فعلی پاک‌سازی کامل همهٔ timerها، گفت‌وگوها، GPS و انیمیشن کامیون نیست؛ Agent نباید آن را بدون تست کامل فرض کند. diff --git a/docs/03-architecture.md b/docs/03-architecture.md new file mode 100644 index 0000000..aeb93b8 --- /dev/null +++ b/docs/03-architecture.md @@ -0,0 +1,54 @@ +# معماری و فناوری + +## پشتهٔ فعلی + +JavaScript ES Modules، Three.js/WebGL، Vite، HTML و CSS فارسی RTL و Canvas 2D برای نقشه، تابلو، تکسچر و portrait. فونت Vazirmatn با Fontsource داخل bundle می‌آید. نسخهٔ lock فعلی: Three.js `0.180.0`، Vite `6.4.4`، Fontsource `5.2.5`؛ در package.json نسخه‌ها بازهٔ caret دارند. Node 22 در CI/Docker استفاده می‌شود؛ target خروجی ES2022 برای top-level await است. TypeScript، React، موتور فیزیک بیرونی و framework رابط در بازی وجود ندارند. + +## نقشهٔ ماژول‌ها + +| فایل | مسئولیت | +|---|---| +| main.js | bootstrap، DOM، player/mode/camera، حلقه، اتصال پرداخت و جایزه، GPS/AR، UI و debug | +| game-state.js | catalog، پول و ساعت، خرید، blockers و امکان ادامه با بودجه | +| payment.js | ماشین حالت تراکنش و جلوگیری از برداشت دوباره | +| scoring.js | XP، درجه، مأموریت و جایزهٔ مأموریت | +| locations.js | مشخصات کارخانه، دستگاه‌ها، test points و مناطق قفل | +| characters.js | createFamily معمولی (پدر/کودک) و robot-only، مفصل و انیمیشن | +| factory.js | ساختمان/دکور/کارخانه، کارکنان، موقعیت spawn، talks، مدیر و کامیون | +| staff.js | مدل کارکنان مفصلی و crew به شکل InstancedMesh | +| car-models.js | سدان خانواده، pickup مدیر و کامیون | +| vehicle.js | state و فیزیک آرکید ماشین | +| traffic.js | ماشین‌های NPC، spawn و حرکت روی شبکهٔ اصلی و one-way داده | +| city.js | مدل شهر، زمین fallback، ساختمان، فضای سبز و chunk visibility | +| collision.js | spatial grid چندضلعی‌ها، resolveCircle و clearFraction دوربین | +| routing.js | گراف پیاده/سواره، A*، نزدیک‌ترین node/segment و طول مسیر | +| geo.js | تبدیل lat/lon و مختصات محلی، bearing و فاصلهٔ فارسی | +| map-render.js | minimap، fullmap و نقشهٔ استان‌های ایران با Canvas | +| vending.js | مدل دستگاه، slot، card، flap، beacon و محصول تحویلی | +| imagery.js | کاشی ماهواره‌ای، تبدیل Web Mercator، بارگذاری/آزادسازی texture | +| buildings-data.js | decode فایل باینری RMB2 | +| realworld.js | smoothFix، stabilizer، geolocation/compass و شعاع رسیدن | +| ar.js | rear camera، حسگر جهت، quaternion و تطبیق FOV | +| pwa.js | نصب، ثبت SW، اعلان نسخه و reload | +| textures.js | ساخت CanvasTexture مشترک | +| style.css / index.html | ساختار DOM، HUD، modalها و ظاهر desktop/mobile | + +## جریان داده + +`locations/catalog + map JSON + public binary/index → ساخت world/graph/collision → reset → requestAnimationFrame`. + +حلقهٔ اصلی dt را عادی به ۰٫۲۵ ثانیه محدود و با گام‌های حداکثر ۱/۳۰ شبیه‌سازی می‌کند؛ debug حداکثر ۱ ثانیه دارد. در حالت فعال: ساعت، حرکت، payment، delivery، traffic؛ سپس route، hints، talks و guide. پس از آن انیمیشن‌ها، دوربین، HUD و render اجرا می‌شوند. توقف شبیه‌سازی الزاماً همهٔ انیمیشن‌های بصری را متوقف نمی‌کند. + +`main.js` وضعیت مرکزی را در متغیرهای ماژولی نگه می‌دارد؛ event bus یا store جدا نداریم. `state` اقتصادی از `player/car/cam/real` جداست. `payment` در اتصال UI فیلدهای اضافی station/timer/delivering می‌گیرد که در createPayment تعریف نشده‌اند. Object.assign به createPayment فیلدهای اضافی را حذف نمی‌کند؛ هنگام refactor به این وابستگی توجه شود. + +## قرارداد توسعه + +- محاسبهٔ قیمت فقط از priceAt و catalog؛ UI نسخهٔ دوم قیمت نسازد. +- برداشت فقط در swipe موفق؛ XP/مأموریت پس از تحویل؛ busy تا پایان تحویل. +- دستگاه با id پایدار معرفی شود؛ مأموریت‌ها و خریدهای ذخیره‌شوندهٔ آینده به id وابسته‌اند. +- مبنای geo تغییر نکند مگر همهٔ JSON/binary/anchorها و تست‌ها با هم مهاجرت کنند. +- تغییر mode باید مسیر، دوربین، ورودی و HUD را در نظر بگیرد؛ تغییر real/AR باید lifecycle stream/sensor را هم رعایت کند. +- geometry، material و texture پویا هنگام حذف آزاد شوند؛ مدل تازه نباید هر frame asset بسازد. +- main.js حدود ۱۴۴۳ و factory.js حدود ۶۹۸ خط‌اند؛ جداسازی تدریجی ممکن است، ولی بازنویسی سراسری برای یک تسک کوچک ضرورت ندارد. + +backend اختصاصی بازی وجود ندارد. Gitea Registry و Caddy زیرساخت build/میزبانی‌اند، نه API بازی. GPS و تصویر دوربین در این کد به سرور بازی ارسال نمی‌شوند؛ سرویس مکان‌یابی مرورگر و دانلود assetها همچنان محیط بیرونی خود را دارند. diff --git a/docs/04-world-and-data.md b/docs/04-world-and-data.md new file mode 100644 index 0000000..48c13f1 --- /dev/null +++ b/docs/04-world-and-data.md @@ -0,0 +1,44 @@ +# جهان، نقشه، داده‌ها و دارایی‌ها + +## مختصات و قرارداد فایل + +مبدأ `ORIGIN = {lat:36.315, lon:59.539}`، هر واحد یک متر، +X شرق و -Z شمال. تبدیل صفحهٔ مماس محلی با latitude/longitude انجام می‌شود؛ terrain ارتفاع جغرافیایی واقعی ندارد. جهت heading ماشین با `(cos(h), -sin(h))` در صفحهٔ XZ است. bearing صفر شمال و ۹۰ شرق است. + +محدودهٔ فعلی: X از -۱۲۲۲۰ تا ۳۱۵۹؛ Z از -۱۶۴۵۱ تا ۲۰۷۸. `scripts/build-map-data.mjs` REGION را از lat 36.298 تا 36.462 و lon 59.405 تا 59.572 می‌گیرد. + +`src/data/mellat.json`: origin، attribution، bounds، roadTypes، areaKinds، names، roads، buildings، areas، oneway، labels. snapshot: ۶۱۹۶ road، ۳۹۸۶ ساختمان OSM، ۲۶۸ area، ۲۹۱۰ شاخص road یک‌طرفه و ۹۵ label. این شمارش‌ها تعداد رکورد فایل‌اند، نه شمارش همهٔ خیابان‌های واقعی. + +- road: `[typeIndex, nameIndex, x0, z0, x1, z1, ...]` +- building در runtime: `[height, x0, z0, ...]` +- جدول names برای lookup نام road استفاده می‌شود. +- دادهٔ استان‌ها در `src/data/iran.json` است. + +## گراف و برخورد + +buildGraph گراف پیاده با همهٔ مسیرها، و سواره با road type ≤ 7 می‌سازد. نزدیک‌ترین node از بزرگ‌ترین مؤلفهٔ متصل انتخاب می‌شود؛ A* با هزینهٔ طول کار می‌کند. connector ابتدا/انتهای مسیر به node نزدیک، خط مستقیم است. گراف فعلی دوطرفه است؛ oneway در routing مصرف نمی‌شود، هرچند traffic از آن استفاده می‌کند. + +خط طلایی روی زمین فقط تا حدود ۷۰۰ متر از polyline ساخته می‌شود. نقشهٔ کامل و minimap مسیر کامل را می‌گیرند. هدف معمولاً مأموریت فعال است؛ waypoint دستی مقدم است و پس از خرید/اعلام مأموریت بعدی می‌تواند پاک شود. + +برخورد بر مبنای footprintهای OSM + Microsoft + factory است؛ spatial cell برابر ۲۴ متر. resolveCircle برای player، خودرو و نقاط spawn استفاده می‌شود. clearFraction مانع ردشدن خط دید دوربین از دیوار می‌شود. ساختمان‌های Microsoft داخل محوطهٔ کارخانه کنار گذاشته می‌شوند. + +## دارایی‌ها و بهینه‌سازی + +- `public/data/buildings.bin`: قالب RMB2؛ header شامل count، minX، minZ، quantization step؛ هر polygon تعداد رأس/ارتفاع نیم‌متری و مختصات delta دارد. در snapshot بررسی‌شده ۱۴۱٬۴۹۷ ساختمان و ۳٬۴۷۸٬۹۱۰ بایت دارد. ارتفاع‌ها تخمینی‌اند. decoder فعلی برای فایل کوتاه/خراب اعتبارسنجی کامل ندارد. +- city بخش‌های ۲۰۰۰ متری و ساختمان‌ها بلوک‌های ۵۰۰ متری دارد؛ ساخت/visibility وابسته به فاصله است. worker و props با instancing کاهش draw call دارند. +- `public/imagery/index.json`: ۳۴۰ tile زوم۱۵ و ۶۶۰ tile زوم۱۷ (مجموع ۱۰۰۰ JPG). imagery هم‌زمان حداکثر ۶ درخواست می‌فرستد؛ tileهای دور geometry/material/texture آزاد می‌کنند. +- زوم۱۷: شعاع ۴۵۰ متر در low و ۷۵۰ در high؛ زوم۱۵: ۱۸۰۰ و ۲۸۰۰. محدودهٔ حذف ۱٫۲ برابر range است. +- با index ماهواره‌ای موجود، زمین/جادهٔ procedural شهر جایگزین می‌شود. نبود دانلود bin/index fallback دارد؛ index موجود با JPGهای غایب لزوماً به fallback بدون تصویر تبدیل نمی‌شود. +- portraitهای روبات: front، happy، wink، surprised، laugh، love، curious، serious، excited؛ نام فایل با face زیرنویس تطبیق دارد. +- آیکون‌های PWA در `public/icons` و manifest در `public/manifest.webmanifest` هستند. + +## بازتولید داده + +```sh +node scripts/build-map-data.mjs --osm city.json zone-factory.json corridor.json major.json --poi poi.json --provinces ne_10m_admin_1_states_provinces.geojson +node scripts/build-buildings.mjs +node scripts/fetch-imagery.mjs +``` + +این‌ها عملیات جدا از npm build هستند. raw exportها ورودی بیرونی‌اند؛ خودکار دانلود/بازنویسی نکنید مگر تسک نیاز داشته باشد. پس از تغییر REGION یا مبدأ باید anchors، factory، graph، binary، tile index و تست دسترس‌پذیری با هم بررسی شوند. `npm run build` فقط فایل‌های موجود را بسته‌بندی می‌کند. + +Attribution در کد/رابط وجود دارد: OpenStreetMap، Microsoft، Esri/Maxar/Earthstar و Natural Earth. متن مجوز و شرایط استفادهٔ بیرونی در این بازبینی حقوقی بررسی نشده است؛ attribution فعلی در تغییر دارایی‌ها حفظ شود و متن README دربارهٔ منبع تصاویر مرجع پروژه باقی بماند. diff --git a/docs/05-device-and-offline.md b/docs/05-device-and-offline.md new file mode 100644 index 0000000..f9a44bb --- /dev/null +++ b/docs/05-device-and-offline.md @@ -0,0 +1,33 @@ +# GPS، دوربین، حسگر و PWA + +## حالت دنیای واقعی + +کاربر از briefing یا نوار پایین real mode را می‌زند؛ modal معرفی/ایمنی و سپس اجازهٔ قطب‌نما و مکان‌یابی درخواست می‌شود. HTTPS یا localhost لازم است. joystick و صفحه‌کلید جای player را تغییر نمی‌دهند؛ updateReal موقعیت را از گوشی می‌گیرد. ماشین مجازی در این حالت قابل استفاده نیست. خانواده پنهان و دوربین در ارتفاع ۱٫۶ متر قرار می‌گیرد. + +مکان‌یابی ابتدا getCurrentPosition تقریبی (۱۵ ثانیه timeout، maximumAge ۶۰ ثانیه) و سپس watchPosition دقیق (۳۰ ثانیه timeout و maximumAge ۳ ثانیه) دارد. timeout اولیهٔ watch فقط وضعیت جست‌وجو است؛ کد۱ permission، کد۲ unavailable و کد۳ timeout. + +smoothFix وزن را با واریانس accuracy دو fix می‌سازد؛ پس از فاصلهٔ زمانی بیش از ۳۰ ثانیه fix تازه جایگزین می‌شود. accuracy کف ۳ متر دارد. stabilizer پرش خارج از دسترس را رد می‌کند، پس از سه رد پیاپی نمونهٔ بعدی امکان پذیرش دارد؛ تا drift بیش از max(3, accuracy/2) یا speed گزارش‌شده بیش از .7 نباشد مکان نمایشی ثابت می‌ماند. + +updateReal نقطه را از برخورد ساختمان بیرون می‌آورد؛ اختلاف بالای ۱۵۰ متر teleport، در فاصله‌های کمتر حرکت نرم تا حداکثر ۸ m/s دارد. فقط fix داخل bounds اعمال می‌شود؛ خارج bounds پیام نزدیک‌ترین دستگاه نمایش داده می‌شود. + +شعاع تعامل `min(50, max(20, accuracy × 2.5))` متر است. نزدیک‌ترین دستگاه فعال در این شعاع انتخاب می‌شود؛ سه test point فقط در real فعال‌اند. شرط سرعت بیش از ۶ m/s در interact جلوی بازکردن فروشگاه را می‌گیرد. این شرط در swipePayment تکرار نشده؛ محدودیت تست/بک‌لاگ محسوب می‌شود. + +## AR + +getUserMedia با rear/environment، video مطلوب ۱۲۸۰×۷۲۰ و بدون audio؛ تصویر video پشت WebGL alpha قرار می‌گیرد. city، imagery، factory، traffic، car، sky و parkGate در AR پنهان‌اند؛ دستگاه و روبات و مسیر روی تصویر می‌مانند. این AR بر پایهٔ GPS/قطب‌نماست، نه WebXR، تشخیص سطح یا occlusion واقعی. انتظار دقت سانتی‌متری درست نیست. + +زاویهٔ گوشی به quaternion با deviceorientationabsolute یا fallback deviceorientation تبدیل می‌شود؛ iOS compassHeading می‌تواند offset شمال را اصلاح کند. FOV از فرض زاویهٔ ضلع بلند ۶۵ درجه و نسبت video/screen محاسبه می‌شود. stable mode چرخش را آرام‌تر می‌کند. بدون حسگر drag و جهت حرکت/compass fallback هستند. + +خاموش‌کردن AR trackهای دوربین را stop می‌کند ولی حسگر برای نمای اول‌شخص ادامه دارد؛ خاموش‌کردن real باید tracking، AR و sensors را متوقف کند. reset معمول بازی فعلاً معادل خاموش‌کردن real نیست. + +## PWA و ذخیره + +setupPwa در build service worker نسبی `./sw.js` ثبت می‌کند؛ dev server ثبت نمی‌کند. نصب با beforeinstallprompt و راهنمای Add to Home Screen برای iOS فراهم است. manifest و icon محلی هستند. + +Vite plugin فهرست assetهای build و public را precache می‌کند؛ JPGهای imagery مستثنا هستند و فقط پس از مشاهده cache می‌شوند. بنابراین «آفلاین» برای همهٔ نقاط ماهواره‌ای پس از اولین اجرا تضمین نشده است. Navigation network-first با fallback صفحهٔ cache؛ assetهای دیگر cache-first؛ imagery cache جدا دارد. نسخهٔ تازه با skip-waiting و controllerchange reload می‌شود. + +localStorage فعلی فقط کیفیت گرافیک را نگه می‌دارد. پول، خرید، XP، مأموریت و موقعیت player ذخیرهٔ ماندگار ندارند. نصب PWA با ذخیرهٔ پیشرفت یکی نیست. حالت low پیش‌فرض ورودی coarse است: سایه خاموش و pixelRatio ۱؛ high تا ۱٫۷۵. query `?quality=low` و کلید گرافیک انتخاب را تغییر می‌دهند. + +## آزمون دستی ضروری + +مجوز ردشده/داده‌شده، قطع GPS، fix خارج جهان، نمونهٔ قدیمی و jump، نزدیک‌ترین test point، سرعت بالا حین پرداخت، دو بار روشن/خاموش، screen rotation، توقف camera track، بازگشت از background، Android/iOS، آفلاین مسیر بازدیدشده/جدید و update از نسخهٔ نصب‌شده. این‌ها در تست Node به‌طور انتهابه‌انتها پوشش ندارند. diff --git a/docs/06-development-and-deployment.md b/docs/06-development-and-deployment.md new file mode 100644 index 0000000..1301e3c --- /dev/null +++ b/docs/06-development-and-deployment.md @@ -0,0 +1,55 @@ +# اجرا، تست، انتشار و نگهداری + +## محیط توسعه + +```sh +npm ci +npm run dev +node --test tests/*.test.js +npm run build +npm run preview +``` + +در محیط این مخزن طبق `/home/hamed/.codex/RTK.md` فرمان‌های shell با `rtk` پیشوند اجرا می‌شوند؛ مثال `rtk npm run build`. خروجی dist تولیدی و در gitignore است. Node 22 با CI هماهنگ است. query `?debug` شیء window.game را برای بررسی state/player/car/cam/payment/route و ابزارهای موجود در انتهای main در دسترس می‌گذارد؛ این دسترسی فقط ابزار توسعه است. + +## پوشش تست فعلی + +| فایل | تعریف تست | پوشش | +|---|---:|---| +| payment.test.js | ۳ | برداشت یک بار، مرحلهٔ ready، انصراف/بسته/نامعتبر/پول، قیمت و ساعت | +| scoring.test.js | ۲ | کشف/ترکیب/پنج دسته، درجه و جایزهٔ مأموریت | +| realworld.test.js | ۴ | smoothFix، arrival/bounds، stabilizer، quaternion و FOV | +| world.test.js | ۷ | geo، collision، مسیر کارخانه/دستگاه، خودرو، one-way ترافیک، binary و tile | + +در تاریخ مبنا همهٔ این فایل‌ها موفق اجرا شدند. runner خروجی را به چهار فایل تجمیع کرده؛ عدد چهار در خلاصهٔ خروجی تعداد فایل‌هاست. تست browser E2E، اجرای فیزیکی گوشی، CI واقعی، build Docker و سایت زنده در این بازبینی اجرا نشده‌اند. + +## GitHub Pages + +`.github/workflows/pages.yml`: push به main یا اجرای دستی؛ checkout، Node22/npm ci، تست، build، configure-pages، upload فقط dist و deploy-pages با مجوز pages/id-token. در Settings → Pages، Source باید GitHub Actions باشد. `base:'./'` برای مسیر مخزن است. لینک ثبت‌شدهٔ پروژه: https://hamedabdollahzade.github.io/vending-machine-game/ (دسترس‌پذیری فعلی بررسی نشده). + +## Docker مستقل + +Dockerfile: build با node:22-alpine و npm ci + تست + build؛ runtime caddy:2.8.4-alpine و dist در /srv. Compose ریشه از سورس build، پورت‌های ۸۰/۴۴۳ و volume گواهی دارد؛ SITE_ADDRESS پیش‌فرض robotmarkets.ir و www.robotmarkets.ir، NPM_REGISTRY قابل تنظیم. در این مدل Caddy مسئول TLS است. + +```sh +docker compose up -d --build +``` + +Caddy assets hashed را immutable یک‌ساله؛ HTML/SW/manifest را no-cache؛ mapdata را یک هفته cache می‌کند. zstd/gzip و headerهای geolocation/camera/sensor و nosniff تنظیم‌اند. try_files ناشناخته را به index.html می‌فرستد؛ برای فایل دادهٔ گمشده باید این رفتار در QA در نظر گرفته شود. + +## Gitea فعلی؛ تفاوت مهم با README قدیمی + +گردش‌کار `.gitea/workflows/ci-cd.yml` فعلی از SSH deployment استفاده نمی‌کند. توضیح قدیمی DEPLOY_HOST/DEPLOY_SSH_KEY در README برای این workflow معتبر نیست. + +1. test job روی Node22: npm ci، تست و build؛ timeout پانزده دقیقه. +2. publish برای main و غیر PR: Docker build، push تصویر با tag زمان/run/SHA به git.my-rm.com، سپس تلاش link package به مخزن. ناموفق بودن link مانع انتشار تصویر نمی‌شود. timeout سی دقیقه. +3. deploy با image خروجی publish و Docker daemon مشترک runner/server: compose pull و up از `deploy/docker-compose.yml`، نمایش ps و image prune. timeout پانزده دقیقه. +4. HEALTH_URL اختیاری برای پاسخ HTTP تست می‌شود؛ این تست صحت بازی یا CSS/WebGL را تضمین نمی‌کند. + +نیازها: secretهای REGISTRY_USERNAME و REGISTRY_TOKEN با مجوز package read/write؛ variable اختیاری NPM_REGISTRY و HEALTH_URL؛ runner دارای Docker CLI/Compose و دسترسی socket daemon میزبان. مقدار secretها در مستندات نوشته نشود. + +Compose مخصوص deploy تصویر آمادهٔ IMAGE را می‌گیرد، container_name برابر vending-machine-game و شبکهٔ خارجی frontend_proxy دارد. پورت میزبان publish نمی‌کند؛ SITE_ADDRESS=:80 و TLS با Nginx Proxy Manager بیرون container است. پس آن را با compose ریشه که Caddy TLS دارد یکی ندانید. + +## کنترل تغییر + +قبل از کار git status و remote را بررسی کنید؛ origin را GitHub فرض نکنید (README از origin در Gitea و remote github یاد می‌کند). build محلی به معنی انتشار نیست. برای هر تسک، تست مرتبط + build و بررسی diff لازم است؛ تغییر داده/زیرساخت نیازمند بررسی خاص خود است. این مستندات خودشان دستور push یا deploy نیستند. diff --git a/docs/07-implemented-plans.md b/docs/07-implemented-plans.md new file mode 100644 index 0000000..d57001e --- /dev/null +++ b/docs/07-implemented-plans.md @@ -0,0 +1,36 @@ +# برنامه‌های اجراشده و تاریخچهٔ تغییر جهت + +این جدول از git log محلی استخراج شده؛ تاریخ دقیق و جزئیات هر مرحله را با `git show ` بررسی کنید. پیام‌های «update» نقشهٔ محصول قابل استناد ندارند و از آن‌ها قابلیت حدس زده نشده است. + +| مرحله | شاهد گیت | نتیجه/وضعیت کنونی | +|---|---|---| +| پایهٔ اولیه | f7c6aee | شروع مخزن | +| سفر جنگل و پیک‌نیک | 5857b11 | داستان و مراحل تعاملی؛ سناریوی تاریخی، با شهر جایگزین شده | +| Pages و لینک | 6d4c4b7 | workflow ساخت dist و README؛ workflow هنوز هست | +| روبات جای کودک | 8a49dc0 | در آن مرحله child تعویض شد؛ در نسخهٔ فعلی کودک انسانی برگشته و روبات نقش راهنمای دستگاه دارد | +| تبدیل به شهر مشهد | 95a2ae8 | جابه‌جایی آزاد سوم‌شخص، دستگاه شهری، خرید/امتیاز و نقشه | +| کارخانه/PWA/GPS | 0939dac | شروع کارخانه، نصب و حالت real | +| گیر ماشین بزرگراه | 2f74788 | اصلاح حرکت/برخورد در مسیر چناران | +| Docker/HTTPS | 47b2b72 | build چندمرحله و Caddy/Compose مستقل | +| Gitea CI/CD | 22aa11b | گردش‌کار اولیه | +| محدودیت registry | 9832f30 | registry قابل تنظیم و timeout/retry نصب | +| تصویر registry | 894e3e2 | انتشار image به‌جای build سورس روی سرور | +| نام کارخانه/GPS | 8cc0e8c | Robot Market، status موقعیت و سه نقطهٔ آزمایشی | +| Docker socket deploy | c1141ad | جایگزینی deploy SSH با daemon مشترک runner | +| تحمل خطای package link | 9564880 | خطای link مانع job نمی‌شود | +| AR و stable GPS | 6ce5600 | rear camera، نرم‌سازی و شعاع تعامل گسترده | +| real اول‌شخص/UI موبایل | 5cea802 | دوربین گوشی و اعلان‌های وسط صفحه | +| ماهواره/ساختمان | 9b289f7 | Esri tile و footprint Microsoft، lazy rendering | +| کارخانه مفصل و خودرو سریع | 751af76 | کارکنان و بخش‌ها، مدیر و کامیون، سقف سرعت حدود ۲۰۰ km/h | + +## چیزهایی که نباید به‌عنوان برنامهٔ انجام‌شده ثبت شوند + +ذخیرهٔ پیشرفت، leaderboard آنلاین، مأموریت ایمنی، چراغ قرمز، تاکسی/قطار شهری، شهر دیگر و چرخهٔ روز/شب صرفاً پیشنهادند. وجود نام فناوری در مانیتور کارکنان به معنای backend واقعی Go یا frontend React نیست. نمایش مقصد قفل‌شده به معنای منطقهٔ قابل بازی نیست. + +## تصمیم‌های بالفعل که Agent باید حفظ کند + +- تجربهٔ فعلی خانوادهٔ انسانی + روبات در دستگاه است؛ رجوع به پیام تاریخی جایگزینی روبات بدون بررسی کد، نقش‌ها را اشتباه می‌کند. +- اقتصاد، زمان و امتیاز در ماژول جدا؛ پرداخت نمایشی و برداشت پس از swipe. +- map و data محلی‌اند؛ runtime برای راه‌اندازی به سرویس route آنلاین متکی نیست. +- اسکریپت‌های بازتولید داده بخشی از build روزمره نیستند. +- مسیر deploy جاری Gitea image/socket است؛ SSH فقط شرح قدیمی README است. diff --git a/docs/08-review-and-backlog.md b/docs/08-review-and-backlog.md new file mode 100644 index 0000000..37d5e92 --- /dev/null +++ b/docs/08-review-and-backlog.md @@ -0,0 +1,43 @@ +# بازبینی کد و بک‌لاگ پیشنهادی + +تاریخ: ۲۰۲۶/۱۰/۲۸، مبنا 751af76. بررسی: منبع همهٔ حوزه‌های اصلی، دادهٔ محلی، تست‌ها، build و فایل‌های انتشار. اجرای تعاملی WebGL/گوشی یا محیط production انجام نشده است. موارد زیر اصلاح‌شده محسوب نمی‌شوند. اولویت‌ها پیشنهاد مهندسی‌اند، نه تسک مصوب. + +## یافته‌های قابل پیگیری + +| شناسه | اولویت | شاهد و اثر | معیار پذیرش پیشنهادی | +|---|---|---|---| +| R01 | بالا | `swipePayment` شکست خرید را به idle برمی‌گرداند، ولی `swipe()` در main روی false فوراً return می‌کند. اگر دستگاه بین آماده‌شدن و کشیدن کارت بسته شود، پنل پرداخت ممکن است باز و بدون مسیر بازیابی بماند؛ cancel در idle false است. | بازتولید عبور ساعت پایان بین begin/ready/swipe؛ بدون برداشت، پیام خطا و پنل قابل بستن/تلاش مجدد، busy خاموش؛ تست اتصال UI | +| R02 | بالا | `canAffordAnything(state,machines)` در `checkEnd` test pointهای .5 قیمت را حتی در حالت غیرواقعی می‌شمارد. در UI و منطق بودجه تعریف «دستگاه فعال» یکسان نیست. با این حال factory نیز ارزان و شبانه‌روزی است، پس صرف حذف test در همین داده‌ها الزاماً نتیجهٔ تابع پایان را عوض نمی‌کند. دسترسی جغرافیایی نیز در تابع منظور نیست. | تعریف دستگاه‌های قابل احتساب بر اساس mode و دسترسی؛ تست حذف نقاط test در حالت عادی و مرز پول/ساعت | +| R03 | متوسط | `buildGraph` همهٔ یال‌ها را دوطرفه می‌سازد؛ oneway فقط در traffic است. خط مسیریابی ماشین ممکن است خلاف جهت راهنمایی کند. | گراف رانندگی جهت‌دار، پیاده طبق سیاست مطلوب؛ تست مسیر مجاز/ممنوع در دو جهت و مسیر کارخانه | +| R04 | متوسط | reset متغیرهای lines/lineTimer، timeoutهای reward/banner و stick را پاک نمی‌کند؛ real tracking/AR را خاموش نمی‌کند؛ factory.reset فقط resetArrival است و چرخهٔ کامیون ادامه دارد. | ابتدا تعریف سیاست reset؛ سپس reset وسط دیالوگ/پاداش/joystick/GPS/بارگیری بدون پیام یا ورودی عملیات قبلی؛ تست lifecycle | +| R05 | متوسط | ساعت بازی حین پرداخت ادامه دارد؛ checkEnd هر نیم‌ثانیه خریدها را می‌بیند ولی پاداش مأموریت بعد از تحویل اعمال می‌شود. اگر آخرین خرید پول را تمام کند، پایان بودجه می‌تواند پیش از جایزهٔ نجات‌دهنده نمایش داده شود. | تست خرید آخر با جایزهٔ نقدی، درحال‌تحویل پایان ندهد؛ پس از تسویهٔ پاداش وضعیت نهایی محاسبه شود | +| R06 | متوسط | شرط سرعت GPS فقط interact است. پس از بازشدن shop/آغاز payment سرعت و fix در swipe مجدداً اعتبارسنجی نمی‌شوند؛ paused بودن shop نیز updateReal را متوقف می‌کند. | سیاست اعتبار fix/سرعت/رسیدن در تأیید خرید تعریف شود؛ سناریؤ تغییر سرعت یا مکان وسط پرداخت بدون برداشت نامعتبر | +| R07 | متوسط | SW نسخه را از نام فایل‌ها می‌سازد و assets بدون hash مثل buildings.bin یا portrait در cache-first می‌مانند. تغییر فقط محتوای public با نام ثابت لزوماً cache جدید نمی‌سازد؛ cache imagery هم نسخه ندارد. | build با تغییر یک asset هم‌نام؛ نسخهٔ نصب‌شده فایل تازه بگیرد و tile قدیمی طبق سیاست مشخص invalidate شود | +| R08 | متوسط | main دانلود فایل optional را catch می‌کند، ولی decodeBuildings خارج catch است. پاسخ موفق با bin خراب یا HTML fallback می‌تواند bootstrap را قطع کند. | header/length/count معتبر یا خطای کنترل‌شده و fallback OSM؛ تست فایل کوتاه، magic غلط، truncated vertex | +| R09 | متوسط | وقتی imagery index موجود است city از satellite mode استفاده می‌کند، اما tile غایب/failed فقط failed علامت می‌خورد و دوباره تلاش نمی‌شود. آفلاین در منطقهٔ دیده‌نشده احتمالاً زمین کامل ندارد. | آزمون index موجود+JPG 404/offline؛ fallback خوانا و سیاست retry روشن | +| R10 | متوسط | `canAffordAnything` پس از بسته‌شدن همهٔ دستگاه‌های مقرون‌به‌صرفه به فردا نگاه نمی‌کند، ولی ساعت نمایش و isOpen روزانه modulo دارند. مأموریت مدرسه بدون پنجرهٔ شکست/انتظار ممکن است معطل بماند. | تصمیم محصول دربارهٔ روز دوم، شکست/تکرار مأموریت و انتظار؛ تست ۱۳:۳۰، ۲۳:۰۰ و ۰۰:۰۰ | +| R11 | پایین | `places` پایان از همهٔ خریدها و denominator از کل machines است؛ testها حتی وقتی غیرفعال‌اند در کل مکان‌ها می‌آیند. متن «با ماشین» فقط متن است و نه شرط مأموریت. | شمارش UI با دستگاه فعال و سیاست مأموریت مطابقت داشته باشد؛ پوشش test/real و خرید پیاده | +| R12 | متوسط | bundle JS حدود ۱٫۷۹ مگابایت و main بزرگ؛ world JSON و Three.js در payload شروع‌اند. تست فعلی FPS/زمان شروع یا مصرف حافظه را نمی‌سنجد. | baseline روی گوشی ضعیف قبل refactor؛ هدف عددی زمان شروع/FPS با توافق صاحب محصول، بدون حذف قابلیت | + +R01 و R05 با import مستقیم توابع فعلی در Node نیز بررسی شدند: + +- R01: مدرسه در minutes=809.98 وارد پرداخت/ready شد؛ در minutes=810، swipe برابر false، phase برابر idle، busy برابر false و balance همچنان 500 بود. مسیر main پس از false پنل را نمی‌بندد؛ اثر بصری پنل نیازمند بازتولید مرورگر است. +- R05: خرید آب کارخانه با balance=8، پس از swipe balance=0 و busy=true و canAffordAnything=false شد؛ completeMissions سپس balance=20 و canAffordAnything=true ساخت. این اختلاف پنجرهٔ checkEnd پیش از delivery را تأیید می‌کند؛ ترتیب نمایش modal نیازمند تست یکپارچه است. +- R02: در ۲۳:۰۰ با balance=8، با همهٔ تعاریف و بدون testها هر دو canAffordAnything=true بودند؛ کارخانه دلیل باقی‌ماندن نتیجه است. این مورد ناهماهنگی دامنه/سیاست است، نه ادعای اثبات‌شدهٔ تغییر خروجی پایان صرفاً به‌خاطر testها. + +passing بودن تست‌های فعلی رد سناریوهای بالا نیست. + +## ناهماهنگی اسناد قدیمی + +- README هنوز Gitea deploy را SSH و DEPLOY_HOST توضیح می‌دهد؛ workflow فعلی registry + Docker socket است. +- README می‌گوید خیابان‌ها یک‌طرفه ندارند؛ داده و traffic one-way دارند اما routing ندارد. به‌جای حکم کلی این تفکیک لازم است. +- README شروع را سالن تولید نوشته؛ spawn واقعی حیاط کنار ماشین است. +- آرشیو مکالمهٔ قدیمی از روبات جای کودک و جنگل می‌گوید؛ وضعیت فعلی خانواده انسانی و روبات دستگاه است. + +## پیشنهادهای محصول؛ نیازمند تصمیم + +ذخیرهٔ پیشرفت محلی با schemaVersion و migration؛ برنامهٔ آزمون کامل موبایل؛ قواعد واقعی‌تر ترافیک/چراغ؛ پیشرفت مناطق و شهرها؛ مأموریت کارخانه/نقش کارکنان؛ تجربهٔ GPS با اعتبارسنجی روشن؛ accessibility و کاهش حرکت/مصرف باتری. backend و leaderboard تصمیم معماری/محصول جدا می‌خواهند؛ آن‌ها را صرفاً برای رفع یک UI bug اضافه نکنید. + +## ترتیب اجرایی پیشنهادی + +ابتدا صحت تراکنش و reset (R01/R04/R05)، سپس منطق mode/پایان (R02/R06/R10/R11)، سپس قابلیت اطمینان offline/data (R07/R08/R09)، سپس route direction (R03) و اندازه‌گیری کارایی (R12). هر تغییر بهتر است تسک مستقل با تست مربوط باشد. diff --git a/docs/09-agent-task-guide.md b/docs/09-agent-task-guide.md new file mode 100644 index 0000000..ddcc620 --- /dev/null +++ b/docs/09-agent-task-guide.md @@ -0,0 +1,48 @@ +# قرارداد تحویل تسک به Agent + +## متن شروع پیشنهادی + +> ابتدا docs/README.md و سندهای مرتبط با این تسک را بخوان. سپس کد فعلی و git status را بررسی کن؛ مستندات snapshot هستند و ممکن است پس از commit مبنا تغییر کرده باشند. رفتار فعلی، تصمیم محصول و پیشنهادهای آینده را یکی نگیر. فقط محدودهٔ این تسک را پیاده کن و مدارک تست، محدودیت و مستندات به‌روز را تحویل بده. + +## قالب آمادهٔ تسک + +```text +عنوان: +هدف کاربر و مسئله: +مراجع docs و شناسهٔ بک‌لاگ: +رفتار فعلی و مراحل بازتولید: +رفتار مطلوب (تصمیم صاحب بازی): +دامنهٔ تغییر / فایل‌های احتمالی: +خارج از دامنه: +قواعدی که باید حفظ شوند: +معیار پذیرش قابل مشاهده: +سناریوهای خطا و مرزها: +تست خودکار مرتبط: +تست دستی desktop/mobile/GPS/offline در صورت نیاز: +نیاز به asset یا دادهٔ بیرونی: +خروجی مورد انتظار (کد، سند، نتیجهٔ تست، تصویر در صورت نیاز): +مجوز commit/push/deploy و remote مقصد، اگر خواسته شده: +``` + +## چک‌لیست Agent + +1. دستورهای AGENTS/RTK قابل اعمال و وضعیت تغییرات کاربر را بخوان؛ remote و branch را فرض نکن. +2. منبع حقیقت تسک را مشخص کن: اقتصاد game-state/scoring؛ مکان locations؛ نمایش main/style؛ حسگر realworld/ar؛ انتشار workflow/compose. +3. قبل از تغییر، تست مربوط و مسیر وابستگی را بفهم. برای bug ابتدا failing regression معنادار بساز؛ برای متن صرف تست تقلیدی نساز. +4. تغییر را با دامنهٔ کوچک انجام بده. محاسبهٔ قیمت یا شرط مأموریت را در UI دوباره تعریف نکن. +5. مقدار پول قبل/پس، busy، تراکنش یکتا، mode، timer و رفتار لغو را در تسک‌های مرتبط حفظ کن. +6. تست مرتبط و build را اجرا کن؛ برای GPU/permissions/offline از تست Node نتیجهٔ بصری استنتاج نکن. +7. سند مرتبط را هم اصلاح کن؛ قابلیت آینده را «انجام‌شده» علامت نزن مگر معیار پذیرش محقق شده باشد. +8. نتیجه را با فایل‌ها، شواهد، ریسک/محدودیت و عملیات واقعاً انجام‌شده گزارش کن. + +## نمونهٔ تسک مستقل + +عنوان: بازیابی UI پس از بسته‌شدن دستگاه در مرحلهٔ پرداخت (R01). + +معیار پذیرش: در ۱۳:۲۹:۵۹ از مدرسه پرداخت را شروع کن، قبل swipe ساعت از ۱۳:۳۰ بگذرد؛ پول/خرید تغییر نکنند، پیام بسته‌بودن نمایش داده شود، پنل گیر نکند، کنترل حرکت برگردد و کاربر دستگاه دیگری انتخاب کند. لغو عادی و خرید موفق/برداشت یک‌باره همچنان پاس باشند. تغییر به حفظ ساعت پرداخت تنها با تصمیم صریح محصول انجام شود. + +این نمونه پیشنهاد است و اجرای آن با ساخت این سند مجاز/انجام‌شده تلقی نمی‌شود. + +## دستورهای مخزن و توصیف فناوری + +در پایان این بررسی یک فایل AGENTS.md با دستورهای عمومی React/Next.js در workspace دیده شد. آن فایل از تغییرات این نوبت نیست. Agent توسعه باید نسخهٔ حاضر آن را پیش از کار بخواند و دستورهای قابل اعمال را رعایت کند؛ توصیف فناوری در این اسناد گزارش وضعیت واقعی کد است، نه جایگزین AGENTS یا مجوز مهاجرت به React/Redux/Tailwind. انتخاب زبان/UI برای توسعهٔ بعدی و هر تعارض عملی باید در همان تسک روشن شود. برای این تسک مستندسازی، زبان برنامه و کتابخانهٔ رابط تغییر نکرده‌اند. diff --git a/docs/10-game-reference.md b/docs/10-game-reference.md new file mode 100644 index 0000000..a36e58d --- /dev/null +++ b/docs/10-game-reference.md @@ -0,0 +1,210 @@ +# مرجع دقیق داده‌های قابل بازی + +این جدول از exportهای کد در snapshot 751af76 استخراج شده است؛ با تغییر locations/game-state/scoring باید به‌روزرسانی شود. + +## دستگاه‌ها + +### دستگاه دفتر فنی مهندسی — factory + +- دسته: کارخانه (factory) +- نشانی: بزرگراه مشهد–چناران، خیابان کاله، کنار پارک علم و فناوری خراسان، ساختمان اداری، دفتر فنی مهندسی +- ساعت: 00:00 تا 24:00؛ ضریب قیمت 0.5 +- موقعیت تعریف‌شده: factory.demo در factory.js +- دستگاه عادی +- توضیح: دستگاه دفتر برنامه‌نویس‌ها؛ نصف قیمت برای مهمان‌های کارخانه + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 8 | +| آب‌میوه | juice | drink | 18 | +| چای سرد | tea | drink | 23 | +| بیسکویت | biscuit | snack | 13 | +| شکلات | chocolate | snack | 15 | + +### بوستان ملت — mellat-park + +- دسته: پارک (park) +- نشانی: بزرگراه وکیل‌آباد، ورودی جنوبی بوستان ملت، نزدیک ایستگاه مترو پارک ملت +- ساعت: 07:00 تا 23:00؛ ضریب قیمت 1 +- موقعیت تعریف‌شده: anchor X=-487.6, Z=-326.4 +- دستگاه عادی +- توضیح: نوشیدنی‌های خنک برای پیاده‌روی در پارک + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 15 | +| آب‌میوه | juice | drink | 35 | +| لیموناد | lemonade | drink | 40 | +| چای سرد | tea | drink | 45 | +| بیسکویت | biscuit | snack | 25 | +| شکلات | chocolate | snack | 30 | +| کیک | cake | snack | 35 | +| آجیل | nuts | snack | 65 | + +### مجموعه آموزشی راحیل — school-rahil + +- دسته: مدرسه (school) +- نشانی: بولوار امامت، کوچهٔ امامت ۲، کنار در ورودی مدرسه +- ساعت: 07:30 تا 13:30؛ ضریب قیمت 0.9 +- موقعیت تعریف‌شده: anchor X=-554.6, Z=-569.7 +- دستگاه عادی +- توضیح: فقط در ساعت کاری مدرسه (۷:۳۰ تا ۱۳:۳۰) باز است + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 14 | +| شیر کاکائو | milk | drink | 36 | +| آب‌میوه | juice | drink | 32 | +| بیسکویت | biscuit | snack | 23 | +| کیک | cake | snack | 32 | + +### دانشگاه فردوسی مشهد — ferdowsi-university + +- دسته: دانشگاه (university) +- نشانی: میدان آزادی، پردیس دانشگاه فردوسی، مسیر پیاده‌روی مرکزی +- ساعت: 07:00 تا 23:00؛ ضریب قیمت 0.8 +- موقعیت تعریف‌شده: anchor X=-936, Z=645 +- دستگاه عادی +- توضیح: تخفیف دانشجویی: ۲۰٪ ارزان‌تر + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 12 | +| چای سرد | tea | drink | 36 | +| شیر کاکائو | milk | drink | 32 | +| بیسکویت | biscuit | snack | 20 | +| شکلات | chocolate | snack | 24 | +| آجیل | nuts | snack | 52 | +| کیک | cake | snack | 28 | + +### دانشکدهٔ ادبیات دکتر شریعتی — literature-faculty + +- دسته: دانشگاه (university) +- نشانی: پردیس دانشگاه فردوسی، بولوار دانش، جلوی دانشکدهٔ ادبیات +- ساعت: 07:00 تا 23:00؛ ضریب قیمت 0.8 +- موقعیت تعریف‌شده: anchor X=-417.1, Z=966.3 +- دستگاه عادی +- توضیح: تخفیف دانشجویی: ۲۰٪ ارزان‌تر + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 12 | +| چای سرد | tea | drink | 36 | +| لیموناد | lemonade | drink | 32 | +| بیسکویت | biscuit | snack | 20 | +| شکلات | chocolate | snack | 24 | + +### دانشگاه علوم پزشکی مشهد — medical-university + +- دسته: دانشگاه (university) +- نشانی: میدان آزادی، پردیس دانشگاه علوم پزشکی مشهد +- ساعت: 07:00 تا 23:00؛ ضریب قیمت 0.8 +- موقعیت تعریف‌شده: anchor X=-272.1, Z=150.6 +- دستگاه عادی +- توضیح: تخفیف دانشجویی: ۲۰٪ ارزان‌تر + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 12 | +| آب‌میوه | juice | drink | 28 | +| شیر کاکائو | milk | drink | 32 | +| آجیل | nuts | snack | 52 | +| بیسکویت | biscuit | snack | 20 | + +### ایستگاه مترو آزادی — azadi-metro + +- دسته: ایستگاه مترو (metro) +- نشانی: بولوار ملک‌آباد، ورودی ایستگاه مترو آزادی +- ساعت: 06:00 تا 22:00؛ ضریب قیمت 1 +- موقعیت تعریف‌شده: anchor X=494.1, Z=280.3 +- دستگاه عادی +- توضیح: دستگاه کنار گیت‌های مترو + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 15 | +| چای سرد | tea | drink | 45 | +| آب‌میوه | juice | drink | 35 | +| شکلات | chocolate | snack | 30 | +| بیسکویت | biscuit | snack | 25 | + +### مشهد مال — mashhad-mall + +- دسته: مرکز خرید (mall) +- نشانی: خیابان اخوان ثالث، کنار ورودی پارکینگ مشهد مال +- ساعت: 10:00 تا 23:00؛ ضریب قیمت 1.1 +- موقعیت تعریف‌شده: anchor X=1940.6, Z=-367.8 +- دستگاه عادی +- توضیح: بیشترین تنوع محصول؛ قیمت کمی بالاتر + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 17 | +| آب‌میوه | juice | drink | 39 | +| شیر کاکائو | milk | drink | 44 | +| چای سرد | tea | drink | 50 | +| لیموناد | lemonade | drink | 44 | +| شکلات | chocolate | snack | 33 | +| کیک | cake | snack | 39 | +| آجیل | nuts | snack | 72 | +| بیسکویت | biscuit | snack | 28 | + +### نقطهٔ آزمایشی ۱ — test-1 + +- دسته: نقطهٔ آزمایشی GPS (test) +- نشانی: کنار کارخانهٔ روبات مارکت · 36.450276, 59.423431 +- ساعت: 00:00 تا 24:00؛ ضریب قیمت 0.5 +- موقعیت تعریف‌شده: GPS 36.450276, 59.423431 +- فقط حالت دنیای واقعی؛ نقطهٔ آزمایشی +- توضیح: برای آزمایش حالت دنیای واقعی: با گوشی تا اینجا راه بروید + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 8 | +| بیسکویت | biscuit | snack | 13 | + +### نقطهٔ آزمایشی ۲ — test-2 + +- دسته: نقطهٔ آزمایشی GPS (test) +- نشانی: کنار کارخانهٔ روبات مارکت · 36.450172, 59.423209 +- ساعت: 00:00 تا 24:00؛ ضریب قیمت 0.5 +- موقعیت تعریف‌شده: GPS 36.450172, 59.423209 +- فقط حالت دنیای واقعی؛ نقطهٔ آزمایشی +- توضیح: برای آزمایش حالت دنیای واقعی: با گوشی تا اینجا راه بروید + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 8 | +| بیسکویت | biscuit | snack | 13 | + +### نقطهٔ آزمایشی ۳ — test-3 + +- دسته: نقطهٔ آزمایشی GPS (test) +- نشانی: کنار کارخانهٔ روبات مارکت · 36.449976, 59.422866 +- ساعت: 00:00 تا 24:00؛ ضریب قیمت 0.5 +- موقعیت تعریف‌شده: GPS 36.449976, 59.422866 +- فقط حالت دنیای واقعی؛ نقطهٔ آزمایشی +- توضیح: برای آزمایش حالت دنیای واقعی: با گوشی تا اینجا راه بروید + +| محصول | productId | نوع | قیمت نهایی هتومان | +|---|---|---|---:| +| آب معدنی | water | drink | 8 | +| بیسکویت | biscuit | snack | 13 | + +## مأموریت‌ها + +| ترتیب | id | عنوان | target | XP | هتومان | شرط تکمیل واقعی | +|---:|---|---|---|---:|---:|---| +| 1 | factory | در دفتر فنی مهندسی کارخانه از دستگاه وندینگ خرید کن | factory | 100 | 20 | وجود خرید factory | +| 2 | first | با ماشین تا بوستان ملت برو و اولین خرید شهری را انجام بده | mellat-park | 150 | 30 | وجود خرید mellat-park | +| 3 | school | قبل از تعطیلی مدرسه (۱۳:۳۰) از دستگاه مدرسه خرید کن | school-rahil | 200 | 40 | وجود خرید school-rahil؛ ساعت خرید از اعتبارسنجی خرید می‌آید | +| 4 | campus | به دانشگاه فردوسی برو و یک خوراکی بخر | ferdowsi-university | 200 | 40 | خرید snack از ferdowsi-university | +| 5 | metro | از دستگاه ایستگاه مترو آزادی خرید کن | azadi-metro | 150 | 30 | وجود خرید azadi-metro | +| 6 | mall | با ماشین تا مشهد مال برو و خرید کن | mashhad-mall | 250 | 50 | وجود خرید mashhad-mall | +| 7 | explorer | از ۵ مکان متفاوت شهر خرید کن | پویا/نزدیک‌ترین بازدیدنشده | 400 | 80 | حداقل ۵ machineId متفاوت در پنج دستهٔ شهری؛ factory/test حذف | + +«پنج مکان» با «پنج دسته» متفاوت است: explorer مکان متمایز می‌شمارد؛ پاداش allCategories دسته متمایز. خریدهای از پیش انجام‌شده می‌توانند مأموریت غیر فعال را هم تکمیل کنند. رسیدن جغرافیایی یا سوارشدن ماشین در شرط مأموریت ذخیره نمی‌شود. + +## نقاط ثابت + +مرکز کارخانه X=-10308.4, Z=-14964.2 و محور (0.7071067811865476, 0.7071067811865476)؛ مکان دقیق ماشین و خانواده از factory.start/car محاسبه می‌شود. diff --git a/docs/11-react-migration.md b/docs/11-react-migration.md new file mode 100644 index 0000000..4cfe7ee --- /dev/null +++ b/docs/11-react-migration.md @@ -0,0 +1,42 @@ +# وضعیت پس از انتقال به React + +این سند بر اسناد snapshot پیش از ریفکتور اولویت دارد. زبان JavaScript و رابط React 18 + Vite است؛ Ant Design و TypeScript اضافه نشده‌اند. Tailwind CSS استایل رابط را تولید می‌کند. Three.js و نسخهٔ قبلی Vite ارتقا داده نشده‌اند. + +## معماری + +- `src/main.jsx` و `routes/AppRoutes.jsx`: Provider و lazy/Suspense برای صفحهٔ بازی. +- `pages/game/`: briefing، HUD، نقشه، خرید/پرداخت، زیرنویس و نمایشگاه با JSX؛ بدون innerHTML و بدون ارسال مستقیم HTTP از کامپوننت. +- `components/shared/GameNode.jsx`: نمایش props و هماهنگی modal بومی؛ از store یا services وابستگی ندارد. +- `hooks/useGameEngine.js`: بارگذاری موتور و cleanup در unmount. +- `game/createGame.js`: راه‌اندازی مرتب زیرسیستم‌ها از runtimeBindings؛ توقف مشترک با AbortSignal. +- `game/runtime/`: چرخهٔ عمر، ورودی/حرکت، خرید، نقشه، HUD، برخوردهای داستانی، GPS/AR و render loop جدا هستند. context فقط برای state زندهٔ شبیه‌سازی و اشیای غیرقابل سریال‌سازی است؛ داخل Redux renderer/Scene/Texture نمی‌گذاریم. +- `game/models/`: ساخت هندسهٔ کارخانه، خانواده، کارکنان، خودرو، دستگاه و نمونه‌های نمایشگاه. factory/city/family اکنون مراحل مستقل ساخت و helperهای نام‌دار دارند. +- `game/economy/`: gameState، payment و scoring با قرارداد اقتصادی قبلی. +- `constants/`: مکان‌ها و showroomCatalog؛ `utils/`: geo و showroomAnswers؛ `types/`: JSDoc قراردادها. +- `services/apiClient.js`: تنها Axios instance، timeout و error normalization. worldService دادهٔ optional و tileها را می‌گیرد؛ imageryDownloadService اسکریپت آماده‌سازی تصویر را پوشش می‌دهد. + +## مرز Redux و موتور + +slice `game` تصویر قابل سریال‌سازی پیشرفت اقتصادی را برای بخش‌های مشترک رابط دارد. موتور تنها نویسندهٔ قواعد شبیه‌سازی است؛ رابط فرمان می‌فرستد و مستقلاً پول/XP تغییر نمی‌دهد. `interface` snapshot نمایش را نگه می‌دارد. bridge وضعیت pause را هم‌زمان برای موتور در دسترس می‌گذارد و انتشار React را به ده بار در ثانیه محدود می‌کند؛ WebGL مستقل render می‌کند. callbackها در registry محلی bridge‌اند و در Redux قرار ندارند. وضعیت موقت preview تصویر داخل کامپوننت است. + +## استایل + +`styles/game.css` ورودی Tailwind است. `styles/gameTheme.js` لایهٔ پایه و tokenهای ظاهر قبلی را برای جلوگیری از تغییر ناخواستهٔ UI نگه می‌دارد و از plugin تنظیمات Tailwind تولید می‌شود. کامپوننت‌های جدید نمایشگاه از utilityهای Tailwind استفاده می‌کنند. inline style تنها برای مختصات پویا/پیشرفت به کار می‌رود. ادامهٔ طراحی نباید لایهٔ CSS مستقل موازی بسازد. + +## نمایشگاه + +سه مدل rm-35/rm-48/rm-60 در حیاط، نزدیک نقطهٔ شروع و خودرو هستند. نمایش سه‌بعدی نمونه‌ها به‌ترتیب ۳۵، ۴۸ و ۶۰ slot دارد. با نزدیک‌شدن، نشان تعامل Q ظاهر می‌شود؛ در محوطه، دکمهٔ «مدل‌های کارخانه» نیز کاتالوگ را باز می‌کند. روبات قیمت و امکانات را از یک کاتالوگ مشترک پاسخ می‌دهد. + +قیمت‌ها به ریال‌اند: ۴٬۶۸۰٬۰۰۰٬۰۰۰؛ ۵٬۴۸۰٬۰۰۰٬۰۰۰؛ ۵٬۹۸۰٬۰۰۰٬۰۰۰. این‌ها قیمت دستگاه کامل‌اند و به اقتصاد خوراکی/هتومان، XP، موجودی و مأموریت وصل نیستند. پاسخ‌ها محلی و تعیین‌شده‌اند؛ backend یا مدل زبانی بیرونی ندارند. + +مسیر فایل‌های `images/machine-*-base.webp`، `images/machine-*-white.webp` و `gifs/machine-*.gif` ثبت شده، اما فایل اصلی هنوز تحویل نشده است. UI نبود تصویر را به‌وضوح نشان می‌دهد و مدل سه‌بعدی مستقل وجود دارد. مسیرهای ریشه‌ای کاتالوگ هنگام نمایش نسبت به پوشهٔ انتشار حل می‌شوند تا GitHub Pages هم کار کند. + +## آزمون و انتشار + +`npm test` شامل تست Node قبلی و Vitest/React Testing Library است. CIهای GitHub/Gitea و Dockerfile نیز همین فرمان را اجرا می‌کنند. `npm run lint`، `npm run format:check` و `npm run build` بررسی‌های دیگرند. نسخهٔ Vitest تازه انتخاب شده فاقد هشدارهای audit مرحلهٔ نصب است؛ نسخه‌های قبلی بازی ارتقا نیافتند. + +Service Worker همچنان در Vite تولید می‌شود. با تأیید کاربر، `fetch` بومی داخل آن استثنای قاعدهٔ Axios است؛ interception و cache آفلاین آن حفظ شده‌اند. assetهای جدید build در precache می‌آیند؛ tileهای ماهواره‌ای فقط پس از مشاهده cache می‌شوند. صف ارسال آنلاین جدید اضافه نشده است. + +## محدودیت بررسی + +اجرای مرورگر محلی برای briefing، شروع، HUD، نقشه و پرسیدن قیمت نمایشگاه انجام شده است. تست حسگر واقعی، GPS میدانی، camera permission روی گوشی و انتشار روی سرور در این ریفکتور انجام نشده‌اند. یافته‌های رفتاری قدیمی docs/08 الزاماً اصلاح نشده‌اند؛ آن‌ها تسک‌های جدا هستند. بلوک داده/هندسه و بعضی الگوریتم‌های قدیمی هنوز می‌توانند طولانی‌تر از هدف تقریبی ۳۰ خط باشند؛ ادعای انطباق صددرصد تمام توابع و nesting وجود ندارد. برای تغییر بعدی هر ماژول، قواعد AGENTS روی کد تازه الزامی است. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..a42a138 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,39 @@ +# مرجع توسعهٔ بازی روبات مارکت + +تاریخ بررسی: ۲۰۲۶/۱۰/۲۸ — مبنا: commit `751af76` در checkout محلی. این مجموعه از کد فعلی، تست‌ها، فایل‌های انتشار و تاریخچهٔ گیت استخراج شده است. وضعیت سرویس‌های آنلاین و اجرای بصری روی گوشی در این بررسی تأیید نشده‌اند. + +## وضعیت جدید React + +ابتدا [معماری و قابلیت‌های پس از انتقال به React](11-react-migration.md) را بخوانید. اسناد زیر snapshot پیش از ریفکتورند؛ قواعد اقتصادی حفظ شده‌اند، اما مسیر فایل‌ها، فناوری UI و فرمان تست تغییر کرده‌اند. + +## مسیر مطالعه برای Agent + +۱. [وضعیت و دامنه](01-product-and-scenario.md) +۲. [قوانین و اقتصاد](02-game-rules.md) +۳. [معماری و فناوری](03-architecture.md) +۴. [جهان، نقشه و داده](04-world-and-data.md) +۵. [GPS، AR و آفلاین](05-device-and-offline.md) +۶. [اجرا، تست و انتشار](06-development-and-deployment.md) +۷. [تاریخچهٔ کارهای انجام‌شده](07-implemented-plans.md) +۸. [بازبینی کد و بک‌لاگ پیشنهادی](08-review-and-backlog.md) +۹. [راهنمای تحویل تسک به Agent](09-agent-task-guide.md) +۱۰. [فهرست دقیق دستگاه‌ها و مأموریت‌ها](10-game-reference.md) + +## نحوهٔ استفاده + +- «پیاده‌سازی‌شده» یعنی در کد وجود دارد؛ تضمین صحت بصری یا آماده‌بودن محیط production نیست. +- «یافتهٔ کد» از بررسی استاتیک به دست آمده است؛ موارد نیازمند بازتولید صریحاً مشخص شده‌اند. +- «پیشنهاد» تصمیم محصول یا تسک مصوب محسوب نمی‌شود. صاحب بازی باید اولویت و رفتار مطلوب را تعیین کند. +- کد مرجع رفتار فعلی است. تغییر اقتصاد، سناریو، داده یا انتشار باید همراه به‌روزرسانی سند مربوط انجام شود. +- سناریوی جنگل و پیک‌نیک در تاریخچه قرار دارد؛ شرح نسخهٔ فعال نیست. روبات فعلی راهنمای دستگاه است و جای کودک راه نمی‌رود. +- مستندات هیچ مجوزی برای push، deploy، تغییر سرور، دانلود مجدد داده یا استفاده از secret ایجاد نمی‌کنند؛ محدودهٔ تسک کاربر تعیین‌کننده است. + +## نتیجهٔ بررسی این نوبت + +چهار فایل تست فعلی با ۱۶ تعریف تست بررسی و اجرای آن‌ها موفق شد. `npm run build` موفق بود: ۳۳ ماژول، JavaScript حدود ۱٬۷۹۴٫۳۵ کیلوبایت (gzip حدود ۵۷۶٫۶۱)، CSS حدود ۱۹٫۷۰ کیلوبایت. هشدار chunk بزرگ‌تر از ۵۰۰ کیلوبایت باقی است. این اعداد اندازهٔ bundle هستند، نه اندازهٔ کل public یا اندازه‌گیری FPS. + +در این نوبت فقط مستندات اضافه/اصلاح می‌شوند؛ یافته‌های گزارش‌شده به‌صورت ضمنی اصلاح نشده‌اند. جزئیات محدودیت آزمون و کارهای پیشنهادی در سند بازبینی آمده است. + +## محدودهٔ فایل‌های این تحویل + +خروجی این کار پوشهٔ docs و لینک آن در README ریشه است. هنگام پایان بررسی، AGENTS.md و دو فایل src/data/iran.json و src/data/mellat.json نیز در وضعیت تغییرکردهٔ workspace دیده شدند؛ آن‌ها توسط این کار ایجاد/ویرایش نشده‌اند و در تحویل مستندسازی منظور نیستند. مبنای کد برنامه 751af76 است؛ شمارش داده‌ها از فایل‌های حاضر هنگام بررسی گرفته شده و باید پس از هر بازتولید داده تازه شود.