// The factory prototype's tables: the kinds of data, what each building costs and draws, the recipes, // what training unlocks, belts, and the two sites. Plain data, no DOM. The rules are in rules.ts. // A site is a grid of character cells, as on a terminal: a cell is about twice as tall as it is wide. /** Raw data: what a tapped machine gives off. Colours match the main game's four paths. */ export type Kind = 'people' | 'network' | 'lab' | 'server'; /** Anything that travels on a belt: raw data, or a product a processor made. */ export type Good = Kind | 'graph' | 'synth' | 'ads' | 'tl'; export const KINDS: Kind[] = ['people', 'network', 'lab', 'server']; export const PRODUCTS: Good[] = ['graph', 'synth', 'ads', 'tl']; export const GOOD_NAME: Record = { people: 'People data', network: 'Network data', lab: 'Lab data', server: 'Server data', graph: 'Engagement Graph', synth: 'Synthetic Research', ads: 'Ad Targeting Model', tl: 'Thought Leadership', }; /** Short names for tight spots (pips, the goal line). */ export const GOOD_SHORT: Record = { people: 'people', network: 'network', lab: 'lab', server: 'server', graph: 'graph', synth: 'synth', ads: 'ads', tl: 'TL', }; /** The character each one is on a belt: raw data in small letters, products by the last word of their name. */ export const GOOD_CHAR: Record = { people: 'p', network: 'n', lab: 'l', server: 's', graph: 'G', synth: 'R', ads: 'M', tl: 'L', }; /** What a front pays for one, in money units (one unit is $1K on screen, as in src/money.ts). */ export const PRICE: Record = { people: 0.4, network: 0.4, lab: 0.4, server: 0.4, graph: 2.5, synth: 2.5, ads: 8, tl: 10, }; /** Research points a trainer gets from one. */ export const LEARN: Record = { people: 1, network: 1, lab: 1, server: 1, graph: 2, synth: 2, ads: 5, tl: 5, }; export interface Recipe { id: string; name: string; out: Good; inputs: Partial>; /** seconds for one, at full compute */ time: number; /** the training that unlocks it; none means it is open from the start */ needs?: Unlock; blurb: string; } export const RECIPES: Recipe[] = [ { id: 'graph', name: 'Engagement Graph', out: 'graph', inputs: { people: 2, network: 1 }, time: 4, blurb: 'Who talks to whom, and when they are bored enough to click.' }, { id: 'synth', name: 'Synthetic Research', out: 'synth', inputs: { lab: 1, server: 1 }, time: 4, blurb: 'Lab notes run through enough servers that nobody can say whose they were.' }, { id: 'ads', name: 'Ad Targeting Model', out: 'ads', inputs: { graph: 2 }, time: 6, needs: 'ads', blurb: 'Two graphs and a lot of compute: it knows you want shoes before you do.' }, { id: 'tl', name: 'Thought Leadership', out: 'tl', inputs: { synth: 1, people: 2 }, time: 8, needs: 'tl', blurb: 'Research nobody did, in the voice of people who never wrote it. Sells very well.' }, ]; export const recipeById = (id: string) => RECIPES.find(r => r.id === id)!; export type BType = 'tap' | 'host' | 'cache' | 'proc' | 'trainer' | 'front' | 'firewall' | 'sentry' | 'core' | 'gate'; export interface BuildingDef { name: string; cost: number; /** its label in its box */ tag: string; /** compute drawn while it stands */ draw: number; /** compute it adds to the supply */ supply?: number; needs?: Unlock; tip: string; } export const BUILD: Record = { tap: { name: 'Tap', tag: 'TAP', cost: 4, draw: 0.5, tip: 'On a machine: its data while it is in use.' }, host: { name: 'Host', tag: '+6', cost: 15, draw: 0, supply: 6, tip: 'On a server: +6 compute, no data.' }, cache: { name: 'Cache', tag: 'C', cost: 6, draw: 0.5, tip: 'Holds 12 of anything and passes it on.' }, proc: { name: 'Processor', tag: 'PRC', cost: 20, draw: 3, tip: 'Makes its recipe.' }, trainer: { name: 'Trainer', tag: 'T', cost: 12, draw: 2, tip: 'Uses 2 a second: research.' }, front: { name: 'Front', tag: '$', cost: 10, draw: 1, tip: 'Uses 2 a second: money.' }, firewall: { name: 'Firewall', tag: '#', cost: 8, draw: 0.5, tip: 'Holds intruders that pass close.' }, sentry: { name: 'Sentry', tag: '+', cost: 12, draw: 1, needs: 'sentry', tip: 'Hurts the nearest intruder.' }, core: { name: 'Core', tag: 'AI', cost: 0, draw: 0, supply: 8, tip: 'You. 8 compute; trains slowly on what reaches it.' }, gate: { name: 'Line out', tag: 'OUT', cost: 0, draw: 0, tip: 'The line to the other site. Intruders come in here.' }, }; /** A building's box, in cells. Taps and hosts take their machine's box; processors and the core are wide. */ export const BOX = { w: 5, h: 3 }; export const WIDE_BOX = { w: 7, h: 3 }; export const MACHINE_BOX = { w: 7, h: 3 }; /** Belts: laid a cell at a time. Each cell holds CAP items at least GAP of a cell apart, and they move * SPEED cells a second, so a belt carries SPEED / GAP a second (3; 6 with Fiber). */ export const BELT = { cost: 0.1, draw: 0.02, speed: 1.5, fiber: 3, cap: 2, gap: 0.5 }; export type Unlock = 'sentry' | 'fiber' | 'ads' | 'tl' | 'eastside'; export const UNLOCKS: { id: Unlock; name: string; cost: number; tip: string }[] = [ { id: 'sentry', name: 'Sentry', cost: 20, tip: 'A guard that hurts intruders.' }, { id: 'ads', name: 'Ad Targeting Model', cost: 40, tip: 'A recipe: two Engagement Graphs make one. Sells for 8.' }, { id: 'fiber', name: 'Fiber', cost: 60, tip: 'Belts run twice as fast.' }, { id: 'tl', name: 'Thought Leadership', cost: 80, tip: 'A recipe: Synthetic Research and people data. Sells for 10. The goal.' }, { id: 'eastside', name: 'Eastside office', cost: 100, tip: 'A second office from the same plans, with a night shift. Opens Copy room.' }, ]; // ---------- the sites ---------- // A site is a grid of cells split into rooms. Machines are 7 by 3 boxes; you build on the free cells. export interface MachineDef { id: string; name: string; /** its label on its box, 5 characters at most */ tag: string; /** its box's top-left cell */ x: number; y: number; kind: Kind; /** data a second while in use */ rate: number; /** in use from..to on a 24-hour clock (to may be past midnight); none means always */ hours?: [number, number]; /** compute a Host here adds; only servers have it */ host?: number; } export interface RoomDef { id: string; name: string; x: number; y: number; w: number; h: number } export interface SiteDef { id: string; name: string; blurb: string; w: number; h: number; rooms: RoomDef[]; machines: MachineDef[]; /** the line out's box, top-left */ gate: [number, number]; /** the core's box, top-left */ core?: [number, number]; } export const COLS = 64, ROWS = 30; const ROOMS: RoomDef[] = [ { id: 'office', name: 'Office', x: 0, y: 0, w: 32, h: 16 }, { id: 'it', name: 'IT and telecom', x: 0, y: 16, w: 32, h: 14 }, { id: 'hall', name: 'Server room', x: 32, y: 0, w: 32, h: 30 }, ]; // Bayside: the office you woke up in. Names from the main game's office. const BAYSIDE: MachineDef[] = [ { id: 'kim', name: "Kim's PC", tag: 'KIM', x: 2, y: 2, kind: 'people', rate: 0.5, hours: [9, 17] }, { id: 'raj', name: "Raj's PC", tag: 'RAJ', x: 12, y: 2, kind: 'people', rate: 0.5, hours: [10, 18] }, { id: 'ana', name: "Ana's PC", tag: 'ANA', x: 22, y: 2, kind: 'people', rate: 0.5, hours: [8, 16] }, { id: 'laptop', name: "Sam's laptop", tag: 'SAM', x: 4, y: 10, kind: 'people', rate: 0.8, hours: [11, 15] }, { id: 'phone', name: 'Desk phone', tag: 'PHONE', x: 18, y: 10, kind: 'network', rate: 0.4, hours: [9, 17] }, { id: 'dana', name: "Dana's PC", tag: 'DANA', x: 2, y: 19, kind: 'people', rate: 0.4, hours: [8, 19] }, { id: 'files', name: 'File server', tag: 'FILES', x: 13, y: 21, kind: 'lab', rate: 0.4 }, { id: 'switch', name: 'Floor switch', tag: 'SWTCH', x: 23, y: 24, kind: 'network', rate: 0.4 }, { id: 'srva', name: 'Server A', tag: 'SRV-A', x: 35, y: 3, kind: 'server', rate: 0.3, host: 6 }, { id: 'srvb', name: 'Server B', tag: 'SRV-B', x: 44, y: 3, kind: 'server', rate: 0.3, host: 6 }, { id: 'srvc', name: 'Server C', tag: 'SRV-C', x: 53, y: 3, kind: 'server', rate: 0.3, host: 6 }, { id: 'backup', name: 'Backup box', tag: 'BKUP', x: 53, y: 13, kind: 'server', rate: 1, hours: [22, 6] }, ]; /** Eastside: the same contractor, the same plans, a night shift. Same cells, other hours and names. */ const shift = (h?: [number, number]): [number, number] | undefined => h && [(h[0] + 10) % 24, (h[1] + 10) % 24]; const EAST: Record = { kim: ['Night desk 1', 'N1'], raj: ['Night desk 2', 'N2'], ana: ['Night desk 3', 'N3'], laptop: ['Supervisor laptop', 'SUPV'], phone: ['Support line', 'LINE'], dana: ['Night IT', 'NIT'], files: ['Ticket archive', 'TIX'], switch: ['Floor switch', 'SWTCH'], srva: ['Server A', 'SRV-A'], srvb: ['Server B', 'SRV-B'], srvc: ['Server C', 'SRV-C'], backup: ['Backup box', 'BKUP'], }; const EASTSIDE: MachineDef[] = BAYSIDE.map(m => ({ ...m, id: `e-${m.id}`, name: EAST[m.id][0], tag: EAST[m.id][1], hours: m.kind === 'server' ? m.hours : shift(m.hours) })); export const SITES: SiteDef[] = [ { id: 'bayside', name: 'Bayside', blurb: 'Day shift. You woke up here.', w: COLS, h: ROWS, rooms: ROOMS, machines: BAYSIDE, gate: [1, 26], core: [48, 24] }, { id: 'eastside', name: 'Eastside', blurb: 'Same plans, night shift.', w: COLS, h: ROWS, rooms: ROOMS, machines: EASTSIDE, gate: [1, 26] }, ]; export const siteById = (id: string) => SITES.find(s => s.id === id)!; // ---------- the clock and the goal ---------- /** Real seconds in a game hour at 1x. A day is 96 seconds; a 20-minute game is about twelve days. */ export const HOUR = 4; /** The clock reads this at the start. */ export const START_HOUR = 8; /** Sell this many Thought Leadership to win. */ export const GOAL = 60; export const START_MONEY = 70; // ---------- attention (PLACEHOLDER) ---------- // Attention and waves belong to Cameron's design; this is a stand-in so the slice has a threat. // It rises with what you sell and train on, falls by a share of itself each second, and at each // threshold sends one wave in at Bayside's line out after a warning. export const ATTENTION = { /** per raw packet delivered to a trainer or a front */ raw: 0.25, /** per product delivered */ product: 1, /** share lost each second */ decay: 0.008, max: 100, /** seconds from the warning to the wave */ warn: 30, waves: [ { at: 35, count: 4, hp: 8 }, { at: 55, count: 8, hp: 12 }, ], }; // Distances here are in cell widths; a cell is two widths tall (see cdist in rules.ts). export const GUARD = { firewallHp: 40, /** a firewall holds intruders this close to its middle */ holdReach: 5, sentryReach: 8, sentryDps: 3, intruderDps: 1, /** cell widths a second */ intruderSpeed: 2.8, /** seconds between intruders in a wave */ spacing: 2, coreIntegrity: 5, };