OP Wiki

Ore fields

Ore data has two levels: per-type (minerals — what the material is and how much one extraction yields) and per-zone-per-type (mineralconfigs — how many nodes, how big, how full). Runtime node state lives in the mineralnodes table.

minerals

ColumnTypeUsed byMeaning
idxint(MaterialType) idxThe MaterialType enum ordinal (1=Titan … 19=DHDT, 0=Undefined). All joins use mineralconfigs.materialtype = minerals.idx.
namevarcharToMaterialType()Type name ("titan", "crude", …).
definitionintMaterialHelper → itemThe item definition produced by extraction (e.g. titan → def 168).
amountintDrillerModuleBase units per extraction (titan 1500, crude 1500, stermonit 975, liquizit 450, epriton 450, …).
extractionTypeintTerrainsModule0 = Solid (OreLayer), 1 = Liquid (LiquidLayer).
enablereffectrequiredbitmodule gatingWhether an enabler effect is required to extract.
geoscandocumentintscan itemsGeo-scan document item for this material.

mineralconfigs

One row per (zone, material). Consumed by MineralConfigurationReader → one MineralLayer per row:

ColumnTypeUsed byMeaning
zoneIdintlayer constructionOwning zone.
materialtypeint→ minerals.idxThe material.
maxnodesintMineralLayer.LoadMineralNodesNodes the zone maintains of this type.
maxtilespernodeintgeneratorMax tiles per node blob; also drives the spacing radius (below).
totalamountpernodeintgeneratorTotal material in a fresh node, distributed over the blob's tiles.
minthresholddoublegeneratorMinimum normalized per-tile fill (0–1); tiles below it are dropped.

Node generation formulas (MineralNodeGeneratorBase.cs)

For a new node of a type with the config above:

radius  = floor( sqrt(maxtilespernode / π) ) × 2
spacing = start position must be ≥ 2 × radius from the nearest existing node
blob    = random-walk flood fill (4-tile brush) up to maxtilespernode valid tiles
tile[i] = ( noise[i] / max(noise) ) normalized into [minthreshold, 1]
         × (tile[i] / Σtile) × totalamountpernode
         × uniform(0.9, 1.1)          // jitter, then truncated to uint

Keep-out zones around docking bases and teleports (KeepOutDist): epriton = 100 tiles (MINERAL_DISTANCE_FROM_BASE_MIN), flux ore = 200; all other types = 0.

Worked example — Daoden (zone_ASI) crude (id 2)

maxnodes=8, maxtilespernode=1257, totalamountpernode=125,000,000, minthreshold=0.5:

radius  = floor(√(1257/π)) × 2 = floor(20.0) × 2 = 40   → spacing ≥ 80 tiles
blob    = up to 1257 tiles
tile    = share ∈ [0.5, 1.0] × (125,000,000 / Σnormalized) × jitter

A full crude node holds 125 M units; at the base extraction of 1,500 units per drill cycle that is ~83,000 cycles of material per node, ~666,000 across the zone's 8 nodes.

Node state — mineralnodes

ColumnMeaning
zoneid, materialtypeOwning zone + type
x, y, width, heightNode bounding box (the blob's area)
dataCompressed uint per tile (row-major within the box)

Lifecycle (MineralNode.cs, MineralLayer.cs):

  • Load: on zone start, all stored nodes load; a node whose total is ≤ 1% of totalamountpernode is deleted instead.
  • Top-up: if fewer than maxnodes survived, new nodes generate (formula above).
  • Expiry: an untouched node expires after 7 days (TimeTracker); mining resets the clock. A node whose max tile value hits 0 also expires.
  • Persistence: dirty nodes flush to the DB on a 30-minute save timer.
  • Flux ore coupling: decrease/expiry of a FluxOre node publishes OreNpcSpawnMessage → the NPC spawner listens (OreNPCSpawner).

Extraction mechanics (MineralExtractor.cs, DrillerModule.cs)

The extraction amount is minerals.amount scaled by the robot's mining_amount_modifier property (aggregated from mining_amount_modifier, effect_mining_amount_modifier, drone_amplification_mining_amount_modifier, effect_excavator_mining_amount_modifier).

  • Solid (OreLayer): takes min(tile value, amount) from the single tile the robot stands on.
  • Liquid (LiquidLayer): drains the whole node, nearest tiles first (priority queue by squared distance), until the amount is satisfied.
  • Gravel (training zones only, GravelLayer): reads the tile value but does not decrement it — gravel nodes are inexhaustible.

Yield per type (refined products) is in Ores; per-zone node counts are in Zone index.