Files
vending-machine-game/src/constants/locations.js
T
HamedandClaude Opus 5.5 8dbaf3d97e refactor: centralize brand, company info, assets and Persian strings
Phase 1 of turning the game into the company landing page.

- Add src/config/company.js with name, tagline, contact, about and the
  product line, plus a brand palette built around #00a693 (contrast
  checked); placeholders are marked TODO(company).
- Add src/config/assets.js listing every loaded file with its expected
  format; placeholders are marked TODO(asset).
- Move all Persian interface text, dialogue, missions and scenery text
  into src/i18n/fa.js; components and game code read from it.
- Expose the palette as CSS variables (--color-primary and shades) and
  Tailwind colours, and use it for 3D materials and map drawings.
- Fix the arrival line that still called the manager's car a white
  pickup.
- Add tests that block hard-coded Persian text and low-contrast colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 08:13:00 +03:30

215 lines
6.1 KiB
JavaScript

import { COMPANY } from '../config/company.js';
import { strings } from '../i18n/fa.js';
// Robot Market machines in the playable Park Mellat area. Positions are path points taken from
// the OpenStreetMap data (local metres, see geo.js); the machine is placed beside that path.
export const CATEGORIES = {
factory: {
label: strings.categories.factory,
icon: '🏭',
},
park: {
label: strings.categories.park,
icon: '🌳',
},
university: {
label: strings.categories.university,
icon: '🎓',
},
school: {
label: strings.categories.school,
icon: '🏫',
},
metro: {
label: strings.categories.metro,
icon: 'Ⓜ',
},
mall: {
label: strings.categories.mall,
icon: '🛍',
},
test: {
label: strings.categories.test,
icon: '🧪',
},
};
const ALL_DAY = [7 * 60, 23 * 60];
export const FACTORY = {
name: COMPANY.name,
tagline: COMPANY.tagline,
address: COMPANY.contact.address,
// Centre of the compound and the direction of its driveway (towards the gate on Kaleh Street).
center: {
x: -10308.4,
z: -14964.2,
},
axis: {
x: Math.SQRT1_2,
z: Math.SQRT1_2,
},
};
export const machines = [
{
id: 'factory',
name: strings.places.officeMachine,
category: 'factory',
address: strings.places.officeAddress(FACTORY.address),
anchor: null,
hours: [0, 24 * 60],
priceFactor: 0.5,
products: ['water', 'juice', 'tea', 'biscuit', 'chocolate'],
note: strings.places.officeNote,
},
{
id: 'mellat-park',
name: 'بوستان ملت',
category: 'park',
address: 'بزرگراه وکیل‌آباد، ورودی جنوبی بوستان ملت، نزدیک ایستگاه مترو پارک ملت',
anchor: {
x: -487.6,
z: -326.4,
},
hours: ALL_DAY,
priceFactor: 1,
products: ['water', 'juice', 'lemonade', 'tea', 'biscuit', 'chocolate', 'cake', 'nuts'],
note: 'نوشیدنی‌های خنک برای پیاده‌روی در پارک',
},
{
id: 'school-rahil',
name: 'مجموعه آموزشی راحیل',
category: 'school',
address: 'بولوار امامت، کوچهٔ امامت ۲، کنار در ورودی مدرسه',
anchor: {
x: -554.6,
z: -569.7,
},
hours: [7 * 60 + 30, 13 * 60 + 30],
priceFactor: 0.9,
products: ['water', 'milk', 'juice', 'biscuit', 'cake'],
note: 'فقط در ساعت کاری مدرسه (۷:۳۰ تا ۱۳:۳۰) باز است',
},
{
id: 'ferdowsi-university',
name: 'دانشگاه فردوسی مشهد',
category: 'university',
address: 'میدان آزادی، پردیس دانشگاه فردوسی، مسیر پیاده‌روی مرکزی',
anchor: {
x: -936,
z: 645,
},
hours: ALL_DAY,
priceFactor: 0.8,
products: ['water', 'tea', 'milk', 'biscuit', 'chocolate', 'nuts', 'cake'],
note: 'تخفیف دانشجویی: ۲۰٪ ارزان‌تر',
},
{
id: 'literature-faculty',
name: 'دانشکدهٔ ادبیات دکتر شریعتی',
category: 'university',
address: 'پردیس دانشگاه فردوسی، بولوار دانش، جلوی دانشکدهٔ ادبیات',
anchor: {
x: -417.1,
z: 966.3,
},
hours: ALL_DAY,
priceFactor: 0.8,
products: ['water', 'tea', 'lemonade', 'biscuit', 'chocolate'],
note: 'تخفیف دانشجویی: ۲۰٪ ارزان‌تر',
},
{
id: 'medical-university',
name: 'دانشگاه علوم پزشکی مشهد',
category: 'university',
address: 'میدان آزادی، پردیس دانشگاه علوم پزشکی مشهد',
anchor: {
x: -272.1,
z: 150.6,
},
hours: ALL_DAY,
priceFactor: 0.8,
products: ['water', 'juice', 'milk', 'nuts', 'biscuit'],
note: 'تخفیف دانشجویی: ۲۰٪ ارزان‌تر',
},
{
id: 'azadi-metro',
name: 'ایستگاه مترو آزادی',
category: 'metro',
address: 'بولوار ملک‌آباد، ورودی ایستگاه مترو آزادی',
anchor: {
x: 494.1,
z: 280.3,
},
hours: [6 * 60, 22 * 60],
priceFactor: 1,
products: ['water', 'tea', 'juice', 'chocolate', 'biscuit'],
note: 'دستگاه کنار گیت‌های مترو',
},
{
id: 'mashhad-mall',
name: 'مشهد مال',
category: 'mall',
address: 'خیابان اخوان ثالث، کنار ورودی پارکینگ مشهد مال',
anchor: {
x: 1940.6,
z: -367.8,
},
hours: [10 * 60, 23 * 60],
priceFactor: 1.1,
products: ['water', 'juice', 'milk', 'tea', 'lemonade', 'chocolate', 'cake', 'nuts', 'biscuit'],
note: 'بیشترین تنوع محصول؛ قیمت کمی بالاتر',
},
];
// GPS test points at real coordinates beside the factory, for checking real-world mode on a phone.
// They exist only while real-world mode is on; `spot` is where the player stands to buy.
/**
* Runs the test point operation for this subsystem.
* @param {*} n - n.
* @param {*} lat - lat.
* @param {*} lon - lon.
* @returns {*} Operation result, when applicable.
*/
const testPoint = (n, lat, lon) => ({
id: `test-${n}`,
name: strings.places.testPoint(n.toLocaleString('fa-IR')),
category: 'test',
test: true,
address: strings.places.testAddress(`${lat.toFixed(6)}, ${lon.toFixed(6)}`),
anchor: null,
spot: {
lat,
lon,
},
hours: [0, 24 * 60],
priceFactor: 0.5,
products: ['water', 'biscuit'],
note: strings.places.testNote,
});
machines.push(
testPoint(1, 36.450276, 59.423431),
testPoint(2, 36.450172, 59.423209),
testPoint(3, 36.449976, 59.422866),
);
// Shown on the map as the next area to unlock.
export const lockedPlaces = [
{
name: 'پارک کوهسنگی',
note: strings.places.comingSoon,
},
{
name: 'حرم و مرکز شهر',
note: strings.places.comingSoon,
},
];
export const START = {
machineId: 'factory',
};
export const CITY_START = 'mellat-park';
/**
* Runs the machine by id operation for this subsystem.
* @param {*} id - id.
* @returns {*} Operation result, when applicable.
*/
export const machineById = (id) => machines.find((m) => m.id === id) ?? null;