// Plotwren town layout: one fixed map, every plot has a fixed tier and a fixed price. Pure data, shared by the Worker and // the browser (build copies it to web/town.js). No auctions, no takeovers, no resale: a plot is sold once at its tier price. // © 2026 CyberMax. All rights reserved. export const WORLD = { w: 1536, h: 1024, tile: 16 }; // Prices in US cents. Featured is an optional monthly upgrade for any sold plot. export const TIERS = { stall: { name: 'Market stall', price: 500, w: 32, h: 32, blurb: 'A pixel stall on Market Row with your logo, name and link.' }, shop: { name: 'Shop', price: 1900, w: 48, h: 48, blurb: 'A shop on Maker Street with a big sign, your logo and a one-line pitch.' }, billboard: { name: 'Billboard', price: 4900, w: 64, h: 48, blurb: 'A billboard on Launch Boulevard, the road everyone crosses.' }, landmark: { name: 'Landmark', price: 14900, w: 96, h: 96, blurb: 'One of four landmark towers on Plotwren Square, the centre of town.' }, }; export const FEATURED = { name: 'Featured', price: 900, interval: 'month', blurb: 'Pinned to the Featured strip, a glowing outline on the map and first in the directory. Cancel any time.' }; export const DISTRICTS = { square: 'Plotwren Square', boulevard: 'Launch Boulevard', street: 'Maker Street', market: 'Market Row', yard: 'CyberMax Yard', }; // Roads (world px rectangles) drawn by the renderer; plots never overlap them. export const ROADS = [ { x: 0, y: 448, w: 1536, h: 48 }, // Launch Boulevard (east-west) { x: 744, y: 0, w: 48, h: 1024 }, // North-south avenue { x: 64, y: 240, w: 640, h: 32 }, // Maker Street (west) { x: 832, y: 240, w: 640, h: 32 }, // Maker Street (east) { x: 64, y: 704, w: 640, h: 24 }, // Market Row lane (west) { x: 832, y: 704, w: 640, h: 24 }, // Market Row lane (east) ]; export const WATER = { x: 0, y: 960, w: 1536, h: 64 }; export const FOUNTAIN = { x: 744, y: 448, w: 48, h: 48 }; const pad = (n, l = 2) => String(n).padStart(l, '0'); function build() { const plots = []; const add = (id, tier, district, x, y) => plots.push({ id, tier, district, x, y, w: TIERS[tier].w, h: TIERS[tier].h }); // Plotwren Square: four landmarks around the fountain crossing. add('L1', 'landmark', 'square', 636, 336); add('L2', 'landmark', 'square', 804, 336); add('L3', 'landmark', 'square', 636, 508); add('L4', 'landmark', 'square', 804, 508); // Launch Boulevard: 6 billboards each side of the square, north side of the road. let b = 1; for (let i = 0; i < 6; i++) add(`B${pad(b++)}`, 'billboard', 'boulevard', 72 + i * 92, 392); for (let i = 0; i < 6; i++) add(`B${pad(b++)}`, 'billboard', 'boulevard', 912 + i * 92, 392); // Maker Street: two rows of shops either side of each half of the street. let s = 1; for (const x0 of [72, 840]) { for (let i = 0; i < 10; i++) add(`H${pad(s++)}`, 'shop', 'street', x0 + i * 62, 184); for (let i = 0; i < 10; i++) add(`H${pad(s++)}`, 'shop', 'street', x0 + i * 62, 280); } // Market Row: stalls in two rows either side of each lane, south of the boulevard. let m = 1; for (const x0 of [72, 840]) { for (const y of [664, 736, 776]) for (let i = 0; i < 16; i++) add(`M${pad(m++, 3)}`, 'stall', 'market', x0 + i * 39, y); } return plots; } // CyberMax Yard: the house plots, north-west corner, never for sale (CyberMax's own products, clearly labelled). export const YARD = [ { id: 'Y1', x: 72, y: 56, w: 64, h: 64 }, { id: 'Y2', x: 152, y: 56, w: 64, h: 64 }, { id: 'Y3', x: 232, y: 56, w: 64, h: 64 }, { id: 'Y4', x: 312, y: 56, w: 64, h: 64 }, { id: 'Y5', x: 392, y: 56, w: 64, h: 64 }, { id: 'Y6', x: 472, y: 56, w: 64, h: 64 }, { id: 'Y7', x: 552, y: 56, w: 64, h: 64 }, { id: 'Y8', x: 632, y: 56, w: 64, h: 64 }, ]; export const PLOTS = build(); export const PLOT_BY_ID = Object.fromEntries(PLOTS.map((p) => [p.id, p])); export const priceOf = (id) => TIERS[PLOT_BY_ID[id]?.tier]?.price ?? null; export const usd = (cents) => `$${(cents / 100).toFixed(cents % 100 ? 2 : 0)}`;