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>
This commit is contained in:
2026-10-31 08:13:00 +03:30
co-authored by Claude Opus 5.5
parent e02347142b
commit 8dbaf3d97e
54 changed files with 1184 additions and 486 deletions
+11 -7
View File
@@ -1,6 +1,10 @@
import * as THREE from 'three';
import { catalog } from '../economy/gameState.js';
import { textTexture } from './textures.js';
import { COMPANY } from '../../config/company.js';
// Brand colours for materials and canvas drawings.
const BRAND = COMPANY.colors;
const PRODUCT_COLORS = {
juice: '#e5ae47',
water: '#9fd3e6',
@@ -24,14 +28,14 @@ const label = (text, sub) =>
textTexture(512, 160, (c, width, height) => {
c.fillStyle = '#0f2a2a';
c.fillRect(0, 0, width, height);
c.fillStyle = '#3ee0c9';
c.fillStyle = BRAND.primary;
c.fillRect(0, height - 10, width, 10);
c.textAlign = 'center';
c.direction = 'rtl';
c.fillStyle = '#e9fff8';
c.font = 'bold 56px Vazirmatn, Tahoma, sans-serif';
c.fillText(text, width / 2, 72);
c.fillStyle = '#9fe9da';
c.fillStyle = BRAND.primaryLighter;
c.font = '34px Vazirmatn, Tahoma, sans-serif';
c.fillText(sub, width / 2, 126);
});
@@ -75,10 +79,10 @@ export function createVendingMachine(def) {
parent.add(mesh);
return mesh;
};
const shell = mat('#0b8f88', {
const shell = mat(BRAND.primary, {
metalness: 0.45,
roughness: 0.3,
emissive: '#0b8f88',
emissive: BRAND.primary,
emissiveIntensity: 0.15,
}),
white = mat('#eef5f3', {
@@ -190,7 +194,7 @@ export function createVendingMachine(def) {
const sign = new THREE.Mesh(
new THREE.PlaneGeometry(1.86, 0.3),
new THREE.MeshBasicMaterial({
map: label('روبات مارکت', def.name),
map: label(COMPANY.shortName, def.name),
}),
);
sign.position.set(0, 2.62, 0.575);
@@ -213,7 +217,7 @@ export function createVendingMachine(def) {
// Objective beacon: a tall column of light visible across the city, like a mission marker.
const beaconMaterial = new THREE.MeshBasicMaterial({
color: '#3ee0c9',
color: BRAND.primary,
transparent: true,
opacity: 0.22,
depthWrite: false,
@@ -230,7 +234,7 @@ export function createVendingMachine(def) {
const ring = new THREE.Mesh(
new THREE.RingGeometry(1.6, 1.9, 40),
new THREE.MeshBasicMaterial({
color: '#3ee0c9',
color: BRAND.primary,
transparent: true,
opacity: 0.6,
depthWrite: false,