diff --git a/src/core/architects/blackout.ts b/src/core/architects/blackout.ts index b0324ce..d2e83c8 100644 --- a/src/core/architects/blackout.ts +++ b/src/core/architects/blackout.ts @@ -137,7 +137,7 @@ const BLACKOUT = [ plan.lakeSize >= 3 && plan.pearlRadius > 0 && !cavern.context.hasSlugs && - 0.03, + cavern.context.anchorWhimsy * 0.03, }, ] as const satisfies readonly Architect[]; export default BLACKOUT; diff --git a/src/core/architects/build_and_power.ts b/src/core/architects/build_and_power.ts index 9ae6869..7ab0fef 100644 --- a/src/core/architects/build_and_power.ts +++ b/src/core/architects/build_and_power.ts @@ -209,7 +209,7 @@ function bidHelper( max: number, dormant: number, active: number, -): number | false { +): number { let extantCount = 0; let unestablishedCount = 0; for (const p of plans) { @@ -217,18 +217,18 @@ function bidHelper( unestablishedCount++; } else if (p.metadata?.tag === TAG) { if (p.metadata.template !== template) { - return false; + return 0; } extantCount += 1; if (extantCount >= max) { - return false; + return 0; } } } if (extantCount > 0) { return active; } else { - return unestablishedCount >= max * 2 && dormant; + return unestablishedCount >= max * 2 ? dormant : 0; } } @@ -262,7 +262,7 @@ export const BUILD_AND_POWER = [ intersectsOnly(plans, plan, null) && hops.length > 5 && !hops.some((h) => plans[h].metadata?.tag === TAG) && - bidHelper(plans, GEOLOGICAL_CENTER, 3, 0.04, 10) + cavern.context.planWhimsy * bidHelper(plans, GEOLOGICAL_CENTER, 3, 0.04, 10) ); }, }, @@ -292,7 +292,7 @@ export const BUILD_AND_POWER = [ amd.special === 'gasLeak' && hops.length > 3 && !hops.some((h) => plans[h].metadata?.tag === TAG) && - bidHelper(plans, SUPPORT_STATION, 3, 10, 5) + cavern.context.planWhimsy * bidHelper(plans, SUPPORT_STATION, 3, 10, 5) ); }, }, diff --git a/src/core/architects/established_hq/fixed_complete.ts b/src/core/architects/established_hq/fixed_complete.ts index c2a2b6a..2b3cd65 100644 --- a/src/core/architects/established_hq/fixed_complete.ts +++ b/src/core/architects/established_hq/fixed_complete.ts @@ -86,7 +86,7 @@ const FIXED_COMPLETE = [ name: "Hq.FixedComplete", ...BASE, ...FC_BASE, - anchorBid: ({ plan }) => !plan.fluid && plan.pearlRadius > 6 && 0.1, + anchorBid: ({ cavern, plan }) => !plan.fluid && plan.pearlRadius > 6 && cavern.context.anchorWhimsy * 0.1, }, ] as const satisfies readonly Architect[]; diff --git a/src/core/architects/established_hq/gas_leak.ts b/src/core/architects/established_hq/gas_leak.ts index baa27fe..36f7289 100644 --- a/src/core/architects/established_hq/gas_leak.ts +++ b/src/core/architects/established_hq/gas_leak.ts @@ -9,6 +9,7 @@ import { ELECTRIC_FENCE_ID, } from "../../models/building"; import { gCreatures } from "../utils/creature_spawners"; +import { hintSelectLaserGroup } from "../utils/hints"; import { gObjectives } from "../utils/objectives"; import { mkVars } from "../utils/script"; import { BASE, HqMetadata, getPlaceBuildings } from "./base"; @@ -76,6 +77,7 @@ const GAS_LEAK_BASE: Pick< // Loop `${v.holdLoop};`, ); + hintSelectLaserGroup(sb); }, }; @@ -90,7 +92,7 @@ const GAS_LEAK = [ cavern.context.hasAirLimit && plan.lakeSize > 3 && plan.pearlRadius > 3 && - 0.2, + cavern.context.anchorWhimsy * 0.2, }, ] satisfies Architect[]; diff --git a/src/core/architects/established_hq/island.ts b/src/core/architects/established_hq/island.ts index 41360aa..55df8e4 100644 --- a/src/core/architects/established_hq/island.ts +++ b/src/core/architects/established_hq/island.ts @@ -118,12 +118,12 @@ const ISLAND = [ { of: Rough.WATER, width: 2, grow: 2 }, { of: Rough.MIX_FRINGE }, ), - anchorBid: ({ plan }) => + anchorBid: ({ cavern, plan }) => plan.fluid === Tile.WATER && plan.lakeSize > 3 && plan.path.baseplates.length === 1 && plan.pearlRadius > 5 && - 0.2, + cavern.context.anchorWhimsy * 0.2, }, ] satisfies Architect[]; diff --git a/src/core/architects/established_hq/lost.ts b/src/core/architects/established_hq/lost.ts index 8fa7bd0..2a22f96 100644 --- a/src/core/architects/established_hq/lost.ts +++ b/src/core/architects/established_hq/lost.ts @@ -91,7 +91,7 @@ const LOST = [ hops.length <= MAX_HOPS && plans[cavern.anchor]?.metadata?.tag !== "mobFarm" && !plans.some((p) => p.metadata?.tag === "hq") && - (plans[hops[0]].metadata?.tag === "nomads" ? 5 : 0.5), + cavern.context.planWhimsy * (plans[hops[0]].metadata?.tag === "nomads" ? 5 : 0.5), }, ] as const satisfies readonly Architect[]; diff --git a/src/core/architects/lost_miners.ts b/src/core/architects/lost_miners.ts index 4a4ef4e..eebffdf 100644 --- a/src/core/architects/lost_miners.ts +++ b/src/core/architects/lost_miners.ts @@ -340,7 +340,7 @@ const LOST_MINERS = [ (r, p) => (p.metadata?.tag === "lostMiners" ? r + 1 : r), 0, ) < 4 && - MULTIPLIERS[cavern.context.biome], + cavern.context.planWhimsy * MULTIPLIERS[cavern.context.biome], }, ] as const satisfies readonly Architect[]; export default LOST_MINERS; diff --git a/src/core/architects/mob_farm.ts b/src/core/architects/mob_farm.ts index 90f777a..94d9d4e 100644 --- a/src/core/architects/mob_farm.ts +++ b/src/core/architects/mob_farm.ts @@ -30,8 +30,8 @@ import { mkVars } from "./utils/script"; import { MOB_FARM_NO_LONGER_BLOCKING, } from "../lore/graphs/events"; -import { HINT_SELECT_LASER_GROUP } from "../lore/graphs/hints"; import { gObjectives } from "./utils/objectives"; +import { hintSelectLaserGroup } from "./utils/hints"; const BANLIST = [ DOCKS, @@ -160,8 +160,6 @@ const BASE: PartialArchitect = { }, script({ cavern, plan, sb }) { const v = mkVars(`p${plan.id}MbFm`, [ - "hintGroup", - "msgHintGroup", "msgNotBlocking", ]); const rng = cavern.dice.script(plan.id); @@ -183,12 +181,7 @@ const BASE: PartialArchitect = { // Hint to tell players about control groups. This isn't super annoying // under normal circumstances, but here it's almost a necessity that the // player have their lasers bound to a single key. - sb.declareInt(v.hintGroup, 0); - [MINING_LASER, SMLC].forEach((e) => - sb.when(`${e.id}.click`, `((${e.id}>1))${v.hintGroup}=1;`), - ); - sb.declareString(v.msgHintGroup, HINT_SELECT_LASER_GROUP); - sb.if(`${v.hintGroup}>0`, `msg:${v.msgHintGroup};`); + hintSelectLaserGroup(sb); }, monsterSpawnScript: (args) => monsterSpawnScript(args, { diff --git a/src/core/architects/ore_waste.ts b/src/core/architects/ore_waste.ts index 731fa73..35ee432 100644 --- a/src/core/architects/ore_waste.ts +++ b/src/core/architects/ore_waste.ts @@ -3,11 +3,15 @@ import { Architect, BaseMetadata } from "../models/architect"; import { ORE_REFINERY, POWER_STATION, SUPPORT_STATION, TELEPORT_PAD, TOOL_STORE } from "../models/building"; import { Tile } from "../models/tiles"; import { DefaultSpawnArchitect, PartialArchitect } from "./default"; +import { hintEjectRefineOre } from "./utils/hints"; import { getTotalOre } from "./utils/resources"; import { mkRough, Rough } from "./utils/rough"; const METADATA = {tag: "oreWaste"} as const satisfies BaseMetadata; +// TO DO: +// Need a hint for when there's a lot of ore collected, but few studs. + /** * Approximately how much ore is needed to build a functional base capable * of producing Building Studs. @@ -23,8 +27,8 @@ const ORE_OVERHEAD = ( POWER_STATION.ore + 5 + // Support Station Lv1 SUPPORT_STATION.ore + - // Ore Refinery Lv4 (2 ore/stud) - ORE_REFINERY.ore + 5 * 3 + // Ore Refinery Lv1 + ORE_REFINERY.ore ); function wasted(t: Tile) { @@ -52,7 +56,7 @@ const BASE: PartialArchitect = { hops: 0.20, order: -0.20, }, - caveOreSeamBias: 0.5, + caveOreSeamBias: 0.6, ...cavern.initialContext, }); return { ...cavern, context }; @@ -73,11 +77,44 @@ const BASE: PartialArchitect = { // ore. No reason to prolong this with an additional ore objective. return undefined; } - const studs = (getTotalOre(cavern) - ORE_OVERHEAD) * 0.12 / 2; + // Assume 5 ore per stud - better ratios are available by upgrading the + // refinery, but this is really just a bonus. After some testing, I noticed + // some major issues that make ore collection excruciatingly boring: + + // 1. Assuming the player won't build a remote base (at a cost of 1 stud) + // and use this to "teleport" the ore back to their main base, even with + // an upgraded refinery each stud takes up at least two cargo spaces. + // This means double the trips. Even with an armada of Tunnel + // Transports, the game seems to take forever between the point when + // enough ore is available on the ground and when it's all collected and + // converted to studs. By this point there's really no interaction left. + // 2. Early game takes a very long time. The player will spend much of it + // without a power station - which means no Electric Fences. No Support + // Station means no air but also no vehicles. Rubble is everywhere but + // there's no reward for clearing it. While this is not _entirely_ true + // since the rubble around the edges of caves actually does have normal + // amounts of ore, the player is unlikely to notice this and assume no + // rubble contains ore. + // 3. Vehicle AI is bad and annoying. I frequently observed STTs picking up + // a single Energy Crystal, driving back to base, picking up one stud, + // and then driving back out to some other random cave to collect a + // single ore before driving back to drop all three off at the Tool + // Store. All the while, piles of ore sitting _near_ the base would be + // ignored. + // 4. Continuing to mine after enough resources are exposed actively makes + // things worse - If the player doesn't turn off crystal collection, + // vehicles will be distracted. + const studs = (getTotalOre(cavern) - ORE_OVERHEAD) * cavern.context.crystalGoalRatio / 5; + if (studs <= 0) { + return undefined; + } return { studs: Math.floor(studs / 5) * 5, sufficient: false, } + }, + script({sb}) { + hintEjectRefineOre(sb); } }; @@ -95,7 +132,7 @@ const ORE_WASTE = [ plan.lakeSize >= 3 && plan.pearlRadius > 0 && cavern.plans.reduce((r, p) => p.hasErosion ? r + 1 : r, 0) < 3 && - 0.03, + cavern.context.anchorWhimsy * 0.03, }, ] as const satisfies readonly Architect[]; export default ORE_WASTE; diff --git a/src/core/architects/seismic/boss_battle.ts b/src/core/architects/seismic/boss_battle.ts index 4bcda6b..2f93f0f 100644 --- a/src/core/architects/seismic/boss_battle.ts +++ b/src/core/architects/seismic/boss_battle.ts @@ -215,7 +215,7 @@ const BOSS_BATTLE = [ plan.pearlRadius >= 5 && plan.path.baseplates.length === 1 && !plan.intersects.some((_, i) => plans[i].metadata?.tag === "seismic") && - 0.1, + cavern.context.planWhimsy * 0.1, }, ] as const satisfies readonly Architect[]; diff --git a/src/core/architects/seismic/eruption.ts b/src/core/architects/seismic/eruption.ts index 83ba5be..a130d82 100644 --- a/src/core/architects/seismic/eruption.ts +++ b/src/core/architects/seismic/eruption.ts @@ -119,7 +119,7 @@ const ERUPTION = [ (_, i) => plans[i].fluid === Tile.WATER || plans[i].metadata?.tag === "seismic", ) && - 1, + cavern.context.planWhimsy * 1, }, ] as const satisfies readonly Architect[]; diff --git a/src/core/architects/seismic/secret_tunnel.ts b/src/core/architects/seismic/secret_tunnel.ts index dcf0b93..9a8ba14 100644 --- a/src/core/architects/seismic/secret_tunnel.ts +++ b/src/core/architects/seismic/secret_tunnel.ts @@ -82,12 +82,12 @@ const SECRET_TUNNEL = [ name: "Seismic.SecretTunnel", ...BASE, ...mkRough({ of: Rough.SOLID_ROCK }, { of: Rough.VOID, grow: 1 }), - hallBid: ({ plan, plans }) => + hallBid: ({ cavern, plan, plans }) => !plan.fluid && plan.path.kind === "auxiliary" && plan.path.exclusiveSnakeDistance > 1 && !plan.intersects.some((_, i) => plans[i].metadata?.tag === "seismic") && - 0.75, + cavern.context.planWhimsy * 0.75, }, ] as const satisfies readonly Architect[]; diff --git a/src/core/architects/slugs.ts b/src/core/architects/slugs.ts index 6d24f86..32e013f 100644 --- a/src/core/architects/slugs.ts +++ b/src/core/architects/slugs.ts @@ -142,7 +142,7 @@ const SLUGS = [ !plan.hasErosion && intersectsOnly(plans, plan, null) && !plans.some((p) => p.metadata?.tag === "slugNest") && - 0.25, + cavern.context.planWhimsy * 0.25, }, { name: "Slugs.Hall", diff --git a/src/core/architects/treasure.ts b/src/core/architects/treasure.ts index 035ac1c..81e35cd 100644 --- a/src/core/architects/treasure.ts +++ b/src/core/architects/treasure.ts @@ -145,12 +145,12 @@ const TREASURE = [ { of: Rough.LOOSE_ROCK, shrink: 1 }, { of: Rough.HARD_ROCK, grow: 0.5 }, ), - caveBid: ({ plans, plan }) => + caveBid: ({ cavern, plans, plan }) => !plan.fluid && plan.path.baseplates.length === 1 && isDeadEnd(plan) && intersectsOnly(plans, plan, null) && - 0.5, + cavern.context.planWhimsy * 0.5, }, { name: "Treasure.Hoard.Sealed", @@ -160,11 +160,11 @@ const TREASURE = [ { of: Rough.ALWAYS_LOOSE_ROCK }, { of: Rough.ALWAYS_HARD_ROCK, grow: 0.5 }, ), - caveBid: ({ plan }) => + caveBid: ({ cavern, plan }) => !plan.fluid && plan.path.baseplates.length === 1 && isDeadEnd(plan) && - 0.5, + cavern.context.planWhimsy * 0.5, }, { name: "Treasure.Rich.Open", @@ -215,13 +215,13 @@ const TREASURE = [ { of: Rough.LOOSE_ROCK, shrink: 1 }, { of: Rough.HARD_ROCK, grow: 0.5 }, ), - caveBid: ({ plans, plan }) => + caveBid: ({ cavern, plans, plan }) => plan.fluid === Tile.WATER && plan.pearlRadius > 3 && plan.path.baseplates.length >= 1 && isDeadEnd(plan) && intersectsOnly(plans, plan, null) && - 0.5, + cavern.context.planWhimsy * 0.5, }, { name: "Treasure.Rich.Lava.Island", @@ -248,13 +248,13 @@ const TREASURE = [ { of: Rough.BRIDGE_ON_LAVA, width: 2, grow: 3 }, { of: Rough.HARD_ROCK, grow: 0.5 }, ), - caveBid: ({ plans, plan }) => + caveBid: ({ cavern, plans, plan }) => plan.fluid === Tile.LAVA && plan.pearlRadius > 3 && plan.path.baseplates.length >= 1 && isDeadEnd(plan) && intersectsOnly(plans, plan, null) && - 0.5, + cavern.context.planWhimsy * 0.5, }, ] as const satisfies readonly Architect[]; export default TREASURE; diff --git a/src/core/architects/utils/hints.ts b/src/core/architects/utils/hints.ts new file mode 100644 index 0000000..2b073d2 --- /dev/null +++ b/src/core/architects/utils/hints.ts @@ -0,0 +1,27 @@ +import { HINT_EJECT_ORE, HINT_SELECT_LASER_GROUP } from "../../lore/graphs/hints"; +import { MINING_LASER, ORE_REFINERY } from "../../models/building"; +import { LMLC, SMLC } from "../../models/vehicle"; +import { mkVars, ScriptBuilder } from "./script"; + +export function hintEjectRefineOre(sb: ScriptBuilder) { + const v = mkVars('hintEjectOre', ['do', 'msg']); + sb.declareInt(v.do, 0); + sb.when( + 'ore>10', + `((${ORE_REFINERY.id}==0))return;`, + `((studs<5))return;`, + `${v.do}=1;` + ); + sb.declareString(v.msg, HINT_EJECT_ORE); + sb.if(`${v.do}>0`, `msg:${v.msg};`); +} + +export function hintSelectLaserGroup(sb: ScriptBuilder) { + const v = mkVars('hintLG', ['do', 'msg']); + sb.declareInt(v.do, 0); + [MINING_LASER, SMLC, LMLC].forEach((e) => + sb.when(`${e.id}.click`, `((${e.id}>1))${v.do}=1;`), + ); + sb.declareString(v.msg, HINT_SELECT_LASER_GROUP); + sb.if(`${v.do}>0`, `msg:${v.msg};`); +} diff --git a/src/core/architects/utils/tile_scripts.ts b/src/core/architects/utils/tile_scripts.ts index 222c686..637af39 100644 --- a/src/core/architects/utils/tile_scripts.ts +++ b/src/core/architects/utils/tile_scripts.ts @@ -4,7 +4,7 @@ import { EventChainLine, ScriptBuilder, transformPoint } from "./script"; const TRIGGERS = { flood: (cavern, x, y) => `place:${transformPoint(cavern, [x, y])},${Tile.WATER.id};`, - waste: (cavern, x, y) => `place:${transformPoint(cavern, [x, y])},${Tile.WASTE_RUBBLE_3.id};`, + waste: (cavern, x, y) => `place:${transformPoint(cavern, [x, y])},${Tile.WASTE_RUBBLE_2.id};`, } as const satisfies {[key: string]: (cavern: PreprogrammedCavern, x: number, y: number) => EventChainLine}; export function tileScript({cavern, sb}: { diff --git a/src/core/common/context.ts b/src/core/common/context.ts index 0689c14..3ff5010 100644 --- a/src/core/common/context.ts +++ b/src/core/common/context.ts @@ -100,6 +100,8 @@ export type CavernContext = { * Bias toward (or against) the anchor being in the center. */ readonly anchorGravity: number; + readonly anchorWhimsy: number; + readonly planWhimsy: number; /** * How blobby and jagged caves should be. * 0 results in perfectly squashed octagons. @@ -299,7 +301,9 @@ const STANDARD_DEFAULTS = { optimalAuxiliaryPathCount: 2, randomAuxiliaryPathCount: 3, auxiliaryPathMinAngle: Math.PI / 4, - anchorGravity: 0, + anchorGravity: 1, + anchorWhimsy: 1, + planWhimsy: 1, caveBaroqueness: 0.16, hallBaroqueness: 0.07, caveCrystalRichness: { base: 0.16, hops: 0.32, order: 0.32 }, diff --git a/src/core/lore/graphs/hints.ts b/src/core/lore/graphs/hints.ts index 34a28ac..0b47a1f 100644 --- a/src/core/lore/graphs/hints.ts +++ b/src/core/lore/graphs/hints.ts @@ -1,5 +1,9 @@ +export const HINT_EJECT_ORE = `\ +You're gathering quite the stockpile there. Click on the Tool Store to \ +dispense some ore. Your Rock Raiders can take it to the Ore Refinery to make \ +Building Studs.`; export const HINT_SELECT_LASER_GROUP = `\ -Hint: Hold SHIFT+click to select multiple units.\ -CTRL+[0-9] assigns a group of units that you can recall with [0-9].\ +Hint: Hold SHIFT+click to select multiple units. \ +CTRL+[0-9] assigns a group of units that you can recall with [0-9]. \ X activates laser mode.`; diff --git a/src/core/lore/graphs/orders.ts b/src/core/lore/graphs/orders.ts index af274b3..b0471af 100644 --- a/src/core/lore/graphs/orders.ts +++ b/src/core/lore/graphs/orders.ts @@ -80,10 +80,10 @@ const ORDERS = phraseGraph( "get it back in working order", ), ), - ).then(joiner) + ) .then( skip, - pg( + pg(joiner).then( state("hasMonsters").then(state("hasSlugs"), skip), state("hasSlugs"), ) diff --git a/src/core/transformers/01_planning/03_anchor.ts b/src/core/transformers/01_planning/03_anchor.ts index 89d7372..9b2765a 100644 --- a/src/core/transformers/01_planning/03_anchor.ts +++ b/src/core/transformers/01_planning/03_anchor.ts @@ -17,17 +17,13 @@ export type AnchoredCavern = WithPlanType< export default function anchor(cavern: FloodedCavern): AnchoredCavern { const architects = encourageDisable(ARCHITECTS, cavern); - const gravities: number[] = cavern.plans - .map((plan) => { - if (plan.kind === "cave") { - return Math.pow( - Math.min(...plan.path.baseplates.map(bp => Math.hypot(...bp.center))), - -cavern.context.anchorGravity); - } - return 1; - }); - - console.log(gravities); + // const gravities: number[] = cavern.plans + // .map((plan) => { + // if (plan.kind === "cave") { + // return Math.min(...plan.path.baseplates.map(bp => Math.hypot(...bp.center))) / cavern.context.anchorGravity; + // } + // return 1; + // }); // Choose a spawn and an architect for that spawn. const anchor = cavern.dice.pickSpawn.weightedChoice( @@ -40,7 +36,7 @@ export default function anchor(cavern: FloodedCavern): AnchoredCavern { .filter((p) => p.kind === "cave") .map((plan) => ({ item: { ...plan, architect, hops: [] }, - bid: (architect.anchorBid!({ cavern, plan }) || 0) * gravities[plan.id], + bid: (architect.anchorBid!({ cavern, plan }) || 0), // * gravities[plan.id], })); })); diff --git a/src/core/transformers/04_ephemera/05_serialize.ts b/src/core/transformers/04_ephemera/05_serialize.ts index 5a90aac..4ad3a17 100644 --- a/src/core/transformers/04_ephemera/05_serialize.ts +++ b/src/core/transformers/04_ephemera/05_serialize.ts @@ -33,7 +33,7 @@ function indent(it: string, prefix: string) { function comments(cavern: ProgrammedCavern) { return `Cavern generated by groundHog v${process.env.REACT_APP_VERSION} https://github.com/charredUtensil/groundhog -initialContext: ${JSON.stringify(cavern.initialContext, null, 2)}`; +${JSON.stringify({initialContext: cavern.initialContext}, null, 2)}`; } /** diff --git a/src/webui/components/context_editor/controls.tsx b/src/webui/components/context_editor/controls.tsx index ca225d2..e478cd6 100644 --- a/src/webui/components/context_editor/controls.tsx +++ b/src/webui/components/context_editor/controls.tsx @@ -183,3 +183,64 @@ export const Slider = ({ ); }; + +export const ExponentialSlider = ({ + of, + min, + max, + step, + zeroLabel, + update, + initialContext, + context, +}: { + of: KeysMatching; + min: number; + max: number; + step?: number; + zeroLabel?: string; +} & UpdateData) => { + const value = Math.log2(context[of]); + const label = (() => { + if (zeroLabel && value === 0) { + return zeroLabel; + } + if (value < 0) { + return `1/${Math.pow(2, -value).toFixed()}` + } + return context[of].toFixed(); + })(); + return ( + <> +

+ {of}: {label} +

+
+ update({ [of]: Math.pow(2, ev.target.valueAsNumber) })} + /> + {of in initialContext ? ( + + ) : ( +
+ )} +
+ + ); +}; \ No newline at end of file diff --git a/src/webui/components/context_editor/index.tsx b/src/webui/components/context_editor/index.tsx index 3b47fba..edf9d16 100644 --- a/src/webui/components/context_editor/index.tsx +++ b/src/webui/components/context_editor/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react"; import { CavernContext, inferContextDefaults } from "../../../core/common"; import { MAX_PLUS_ONE } from "../../../core/common/prng"; import styles from "./style.module.scss"; -import { Choice, CurveSliders, Slider } from "./controls"; +import { Choice, CurveSliders, ExponentialSlider, Slider } from "./controls"; import { ArchitectsInput } from "./architects"; import { PartialCavernContext } from "../../../core/common/context"; import { Cavern } from "../../../core/models/cavern"; @@ -237,16 +237,31 @@ function CavernContextInputInner({

Anchor

- +

Establish

+ + {( [ "caveCrystalRichness", @@ -272,7 +287,6 @@ function CavernContextInputInner({ step={0.25} {...rest} /> -

Pearl

diff --git a/src/webui/components/map_preview/objectives.tsx b/src/webui/components/map_preview/objectives.tsx index fb3a79a..5cd1b41 100644 --- a/src/webui/components/map_preview/objectives.tsx +++ b/src/webui/components/map_preview/objectives.tsx @@ -19,7 +19,7 @@ export default function ObjectivesPreview({cavern}:{cavern: Cavern}) { if (plan.architect?.objectives) { const [x, y] = plan.path!.baseplates[0].center; return ( - + )