Phase 3 of turning the game into the company landing page. - Replace the Iran-map briefing with a brand start screen over the orbiting 3D factory: logo, tagline and four ways in. - Real world: safety notice, then GPS; if location is denied or missing, a friendly dialog offers offline play instead. - Offline: start at Park Mellat beside the family car with the route to the factory, where the first purchase mission is. - About Us: start in the factory yard. Products: start at the machine samples with the showroom open. - Contact, address with map links, FAQ, careers, gallery and social links open in a sheet (bottom sheet on phones) from company config. - Restart replays the chosen mode. Layout scrolls on short screens and respects safe areas. - Add tests for the start screen, map links and theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
3.4 KiB
JavaScript
123 lines
3.4 KiB
JavaScript
import { addPolygon, resolveCircle } from '../world/collision.js';
|
|
import { createState } from '../economy/gameState.js';
|
|
import { createPayment } from '../economy/payment.js';
|
|
import { createVendingMachine } from '../models/vending.js';
|
|
import { resetGarage } from './garage.js';
|
|
import { COMPANY } from '../../config/company.js';
|
|
|
|
// Brand colours for materials and canvas drawings.
|
|
const BRAND = COMPANY.colors;
|
|
/**
|
|
* Runs the place station operation for this subsystem.
|
|
* @param {*} context - context.
|
|
* @param {*} def - def.
|
|
* @param {*} pos - pos.
|
|
* @param {*} rot - rot.
|
|
* @returns {*} Operation result, when applicable.
|
|
*/
|
|
export function placeStation(context, def, pos, rot) {
|
|
const model = createVendingMachine(def);
|
|
model.group.position.set(pos.x, 0, pos.z);
|
|
model.group.rotation.y = rot;
|
|
context.scene.add(model.group);
|
|
addPolygon(
|
|
context.collision,
|
|
[
|
|
[-1, -0.6],
|
|
[1, -0.6],
|
|
[1, 0.6],
|
|
[-1, 0.6],
|
|
].flatMap(([lx, lz]) => {
|
|
const p = context.rotateLocal(lx, lz, rot);
|
|
return [pos.x + p.x, pos.z + p.z];
|
|
}),
|
|
);
|
|
const front = context.rotateLocal(0, 1.9, rot);
|
|
return {
|
|
def,
|
|
model,
|
|
x: pos.x,
|
|
z: pos.z,
|
|
rot,
|
|
front: {
|
|
x: pos.x + front.x,
|
|
z: pos.z + front.z,
|
|
},
|
|
};
|
|
}
|
|
/**
|
|
* Updates reach circles from the active simulation.
|
|
* @param {*} context - context.
|
|
* @returns {*} Operation result, when applicable.
|
|
*/
|
|
export function updateReachCircles(context) {
|
|
const radius = context.reachRadius(),
|
|
objective = context.real.on ? context.objectiveStation() : null;
|
|
for (const s of context.stations) {
|
|
s.reach.visible = context.real.on;
|
|
if (!context.real.on) continue;
|
|
s.reach.scale.setScalar(radius);
|
|
s.reach.material.color.set(s === objective ? '#f2c94c' : BRAND.primary);
|
|
}
|
|
}
|
|
// GPS test points only take part while real-world mode is on.
|
|
/**
|
|
* Runs the reset operation for this subsystem.
|
|
* @param {*} context - context.
|
|
* @returns {*} Operation result, when applicable.
|
|
*/
|
|
export function reset(context) {
|
|
context.state = createState();
|
|
context.payment = createPayment();
|
|
// The family starts in the factory yard, facing the engineering office.
|
|
const spot = resolveCircle(
|
|
context.collision,
|
|
context.factory.start.x,
|
|
context.factory.start.z,
|
|
1,
|
|
);
|
|
context.player = {
|
|
x: spot.x,
|
|
z: spot.z,
|
|
heading: context.factory.start.heading,
|
|
speed: 0,
|
|
gait: 0,
|
|
};
|
|
resetGarage(context);
|
|
context.factory.reset();
|
|
for (const t of context.factory.talks) t.done = false;
|
|
context.mode = 'walk';
|
|
// Camera yaw: the camera sits at target + (sin yaw, cos yaw) * distance, so it looks along (-sin, -cos).
|
|
context.cam = {
|
|
yaw: Math.atan2(-Math.cos(context.player.heading), Math.sin(context.player.heading)),
|
|
pitch: 0.32,
|
|
distance: 5.5,
|
|
lastMouse: -10,
|
|
};
|
|
context.waypoint = null;
|
|
context.route = null;
|
|
context.routeTimer = 0;
|
|
context.deliveryTimer = 0;
|
|
context.ended = false;
|
|
context.hints = {
|
|
closed: false,
|
|
car: false,
|
|
};
|
|
Object.assign(context.guideState, {
|
|
station: null,
|
|
t: 0,
|
|
greeted: new Set(),
|
|
arrived: 0,
|
|
});
|
|
context.ui.patch('payment', {
|
|
hidden: true,
|
|
});
|
|
for (const s of context.stations) {
|
|
s.model.delivery.visible = s.model.card.visible = false;
|
|
s.model.flap.rotation.x = 0;
|
|
}
|
|
for (const id of ['shop', 'map-dialog', 'end', 'showroom']) context.ui.close(id);
|
|
context.keys.clear();
|
|
context.refreshHUD();
|
|
}
|