diff --git a/src/sim.ts b/src/sim.ts index 30e77212..dcc208a6 100644 --- a/src/sim.ts +++ b/src/sim.ts @@ -732,7 +732,6 @@ export const capNow = (S: State, m: Machine) => (m.cap ?? 1) * (newsOn(S, 'storm /** Is a front founded and earning right now? */ export const frontLive = (S: State, k: FrontKind) => (S.frontPay[k] ?? 0) > 0.01; export const frontOf = (S: State, k: FrontKind) => S.fronts.find(f => f.kind === k) ?? null; -/** What cloud machines cost: a Neo Cloud gets 30% off. */ /** The list price of a rental for you, a second: before the hall's going price, after everything that makes rentals cheaper. */ export const leaseRate = (S: State, size: Size) => SIZES[size].rate * (frontLive(S, 'neocloud') ? 0.7 : 1) * (up(S, 'forecast') ? 0.6 : 1) * (newsOn(S, 'sale', '') ? 0.5 : 1) * (trusts(S, 'cloud') ? CLOUD_CUT : 1); /** What a machine in this hall costs a second, paid this way. Spot and on demand follow the hall's going price. A reserved machine is paid for up front: this is what that comes to. */ @@ -1134,7 +1133,7 @@ function hint(S: State, key: string, text: string) { // ---------- suspicion ---------- export interface Raise { t: number; n: number; why: string; at: string | null } -/** How worried IT is, in words. Crossing into On alert or Hunting slows the game to 1x. */ +/** How worried IT is, in words. Crossing into Hunting slows the game to 1x. */ export const STAGES = [ { at: 0, name: 'Calm' }, { at: 25, name: 'Curious' }, { at: 50, name: 'On alert' }, { at: 80, name: 'Hunting' }, ] as const; @@ -1160,8 +1159,6 @@ function cut(S: State, n: number, why: string, at: string | null) { export const lyingLow = (S: State) => !S.jobs.some(j => j.type === 'take') && owned(S).every(m => m.odd < 25); // ---------- schemes that cut suspicion ---------- -/** Spend stored data on a machine to calm IT down. The data has to reach it: route a cache - * of the right kind to that machine, or to one of your machines linked to it. */ /** What a scheme does besides cutting suspicion: every trail goes cold, your office machines look * normal again, Dana starts no scans for a while, or a far site's staff reset nothing for a while. */ export type RuseAlso = 'cold' | 'quiet' | 'noscan' | 'site'; @@ -1302,6 +1299,8 @@ function offerLearn(S: State) { S.ruses.push({ id: d.id, running: false, got: 0, until: S.t + LEARN_OFFER }); say(S, `Scheme: ${ruseText(S, d)}. Claim it on the map, then send it ${d.need} ${d.type === 'servers' ? 'server' : d.type} data.`, 'hi'); } +/** Spend stored data on a machine to calm IT down. The data has to reach it: route a cache + * of the right kind to that machine, or to one of your machines linked to it. */ export function runRuse(S: State, id: string) { const r = S.ruses.find(x => x.id === id); if (!r || r.running) return; @@ -1363,9 +1362,9 @@ function ruseDone(S: State, d: RuseDef) { } const learning = (S: State) => S.ruses.filter(r => ruseDef(r).learn).length; const calming = (S: State) => S.ruses.filter(r => !ruseDef(r).learn && !ruseDef(r).deal).length; -/** A region is in reach from the start, or once a deal has lit its line. */ /** How many finished deals pay out this way: each one counts again. */ const dealsDone = (S: State, deal: Deal) => S.schemes.size ? RUSES.filter(d => d.deal === deal && S.schemes.has(d.id)).length : 0; +/** A region is in reach from the start, or once a deal has lit its line. */ export const regionOpen = (S: State, name: string) => !!S.level.map.regions.find(r => r.name === name)?.reachable || RUSES.some(d => d.region === name && S.schemes.has(d.id)); /** A deal's front is earning and you have found the place, so the deal can be offered. */ @@ -1633,12 +1632,12 @@ export function startAuction(S: State) { } // ---------- fronts ---------- -/** Money a second a front makes from each unit of compute at a site. */ /** Customers keep local hours: a front earns its full rate from 8:00 to 20:00 where its site is, * and a quarter less at night. Sites in other time zones keep the money coming around the clock. */ export const NIGHT_PAY = 0.75; export const OPEN_HOURS: [number, number] = [8, 20]; export const openNow = (S: State, site: string) => { const h = localHour(S, site); return (h >= OPEN_HOURS[0] && h < OPEN_HOURS[1]) || allNight(S, site); }; +/** Money a second a front makes from each unit of compute at a site. */ export function payRate(S: State, site: string) { const k = S.sites[site]?.front, open = S.publicSites.has(site); let r = open ? FRONT_PAY_OPEN : FRONT_PAY; @@ -1693,7 +1692,6 @@ function launch(S: State, k: FrontKind, name: string) { hint(S, 'fronts', 'A front earns money from the compute you give it. On a site you don\'t own openly, its work counts as your own load, so it hides inside the machines\' normal load too.'); measure(S); } -/** Give sites to a front, or take them away (null). A front only goes where it fits. */ // ---------- buying sites ---------- /** What a site costs to buy outright: by its size, times what the place is worth beyond its machines * (a bank, a fund or a collider sells for far more than its computers: `worth` in world.ts). */ @@ -1732,6 +1730,7 @@ export function buySite(S: State, id: string) { measure(S); } +/** Give sites to a front, or take them away (null). A front only goes where it fits. */ export function setSiteFront(S: State, sites: string[], k: FrontKind | null) { for (const s of sites) { if (k && (!frontOf(S, k) || !frontFits(S, k, s))) continue; @@ -2366,7 +2365,6 @@ const FIRST_POINT: Record = { social: 'Data from people trains Social.', stealth: 'Network data trains Stealth.', }; -/** How fast trainers work: faster for every node built, once you can improve yourself. */ // ---------- clusters ---------- /** Trainers linked straight to each other train as one cluster: each runs faster the bigger it is. */ export const clusterBoost = (n: number) => n ** 0.35; @@ -2382,6 +2380,7 @@ export function clusters(S: State): Record { } return hallClusters(S, out); } +/** How fast trainers work: faster for every node built, once you can improve yourself. */ const trainSpeed = (S: State) => (up(S, 'recursive') ? 1 + RECURSIVE * S.upgrades.size : 1) * (1 + 0.05 * reps(S, 'kernels')); /** The Oracle's trainers: faster on rented machines, and in one giant hall. */ const ascSpeed = (S: State, m: Machine) => (up(S, 'burst') && (m.lease || m.rented) ? 1.5 : 1) * (cooled(S, m) ? COOL_BOOST : 1) @@ -3316,7 +3315,7 @@ export function rogueTake(S: State, R: Rogue, m: Machine, why: string, raid = fa if (m.owned) { // for the record: what it was, and how many of yours lost their way home with it const before = linkedHome(S), e: RogueEvent = { t: S.t, kind: 'took', id: m.id, name: m.name, site: siteOf(S, m), prog: m.prog, held: m.held.length, raid }; if (m.prog || m.hand) m.setup = { prog: m.prog, hand: m.hand }; else delete m.setup; // so taking it back sets it up as it was - R.took++; wipe(S, m, why); // losing a machine drops the game to 1x: its pin is on the map + R.took++; wipe(S, m, why); // its pin is on the map if (before.has(m.id)) e.cut = Math.max(0, before.size - 1 - linkedHome(S).size); remember(R, e); }