Skip to content
TAURION
07Prospecting & Mining

Prospecting & Mining

Region prospecting, ore distribution, prizes and artefact odds.

In one minute (for players)#

In Taurion you gather raw ore by prospecting and then mining, using a vehicle (your "character" controls one vehicle, e.g. a Raider, code rv st):

  1. Prospect a region — a cluster of map hexes, not a single tile. Drive your vehicle onto the region you want and issue the prospect order. Your vehicle sits still for a fixed number of blocks (a "block" is one tick of the blockchain; roughly the Polygon block time) while it scans. Faster vehicles and a Graviton Spectrometer (code lf scanner) scanner fitting shorten the wait.
  2. When the scan finishes you learn which of the nine ores that region holds (e.g. Trimideum, code raw a) and how much. You may also get a bonus Ancient Artefact, and — during the launch competition — a chance at a one-of-a-kind prize.
  3. Mine the region to pull the ore into your vehicle's cargo, block by block, until the region runs dry or your cargo is full. A Tritanian Drill (code lf pick) fitting makes you mine faster.
  4. A drained region can be re-prospected later (after a cooldown) to roll a fresh ore type and amount.

Throughout this doc, internal item codes are in backticks and the player-facing display name (from the game client) is given next to them, e.g. raw a (Trimideum) or rv st (Raider). The rest of the page is the precise, source-derived reference behind those four steps.


Authoritative, source-derived reference for Taurion's resource economy: the region system, prospecting (who, how long, what is found, the prize lottery), and mining (rates, cargo limits, region depletion, re-prospecting).

All rules below are taken directly from the Game State Processor (GSP) C++ source and the read-only config protos (proto/roconfig/). Internal item codes are in backticks; display names are the ones shown in the official client. Vehicle/item code prefixes: ore codes are raw araw i; vehicle codes are rv … (Red faction), bv … (Blue), gv … (Green) plus a size/role suffix (st, s, m, l, sc, mt, lt, vlt, sa, ma, la, vla).

Chain note. Three configurations exist, layered by merge (proto/roconfig.cpp:85-141):

  • mainnet (MAIN/POLYGON): base config only.
  • testnet (TEST/MUMBAI): base + testnet_merge. testnet.pb.text contains no prospecting/mining/prize overrides, so testnet behaves exactly like mainnet for everything in this document.
  • regtest (REGTEST/GANACHE): base + testnet + regtest merge, and the merge first clears safe_zones and params.prizes (proto/roconfig.cpp:135-136) before applying test_* overrides. Numbers from test_params.pb.text / test_resourcedist.pb.text therefore replace their mainnet counterparts and are only used by the unit tests / local nodes.

Unless stated otherwise, all numeric values in this doc are mainnet.


1. The region system#

1.1 What a region is#

The world map is a single large hexagonal grid (axial HexCoord, x/y). Every on-map tile belongs to exactly one region. Regions are the unit of granularity for prospecting and mining: you do not prospect a tile, you prospect the region that contains the tile your vehicle is standing on.

  • The map has numTiles = 67,108,864 (2^26) tile slots (mapdata/tiledata.hpp:56); not all are passable/on-map.
  • Region IDs are looked up from compacted embedded map data (mapdata/regionmap.cpp), keyed by coordinate: RegionMap::GetRegionId(HexCoord)RegionMap::IdT (a uint32_t, mapdata/regionmap.hpp:42).
  • Out-of-map coordinates return RegionMap::OUT_OF_MAP = (uint32_t)-1 (mapdata/regionmap.hpp:45).
  • Region IDs start at 0 and are not fully contiguous — the map was cropped after generation, so some IDs in the range are missing (mapdata/procmap.cpp:593-598).
  • Every coordinate that maps to a region also maps back to a set of tiles forming that region's shape via RegionMap::GetRegionShape (mapdata/regionmap.hpp:62).

The exact total number of regions is not a compiled-in constant; it is a property of the embedded regiondata.dat blob (mapdata/regiondata.dat.xz) and is logged at generation time (mapdata/procmap.cpp:598). It is on the order of hundreds of thousands of regions, each covering many contiguous hexes. See Open questions.

1.2 Region database row#

Each region that has ever been touched gets a DB row (RegionsTable, database/region.hpp). The mutable per-region state used by prospecting/mining:

Field (proto) Meaning
prospecting_character ID of the character currently prospecting this region (set while in progress).
prospection.name Owner (Xaya account) who completed the prospect.
prospection.height Block height at which prospecting finished.
prospection.resource Internal code of the mine-able resource found here (e.g. raw a).
resource_left (via GetResourceLeft/SetResourceLeft) Remaining mine-able units of that resource.

A region is in one of these states:

  1. Untouched — no prospecting_character, no prospection. Can be prospected.
  2. Being prospectedprospecting_character set. Locked.
  3. Prospected, has resourceprospection set, resource_left > 0. Mineable.
  4. Prospected, exhaustedprospection set, resource_left == 0. Re-prospectable once expiry passes.

2. Prospecting#

2.1 Who can prospect, and the entry conditions#

A character can be ordered to prospect via the prospect move (see §2.5). The move is accepted only if all of the following hold (BaseMoveProcessor::ParseCharacterProspecting, src/moveprocessor.cpp:833-879):

Requirement Source Notes
prospect value is an empty JSON object {} moveprocessor.cpp:839-849 Any extra fields → rejected.
Vehicle has a prospecting_blocks stat moveprocessor.cpp:851-855 All real vehicles do; see §2.4. A vehicle without it "cannot prospect".
Character is not busy (no ongoing operation) moveprocessor.cpp:857-862 IsBusy() = has an ongoing op (database/character.cpp:219-227).
Character is not inside a building moveprocessor.cpp:864-870 IsInBuilding() (database/character.hpp:190-193).
CanProspectRegion(...) for the region under the character moveprocessor.cpp:872-878 See §2.2.

The region is the one containing the character's current position (moveprocessor.cpp:872-873). A character does not need to be stationary to issue the move, but starting prospecting stops its movement (§2.3).

2.2 CanProspectRegion — when a region accepts a prospect#

src/prospecting.cpp:29-71. Returns true only if:

  1. Not already being prospected. If prospecting_character is set, reject (prospecting.cpp:34-42).
  2. Never prospected before → accept immediately (prospecting.cpp:44-45).
  3. Re-prospecting an expired region. If a previous prospection exists:
    • The current block height must be at least prospection.height + prospection_expiry_blocks (prospecting.cpp:47-58). Earlier than that → reject.
    • prospection_expiry_blocks = 5000 on mainnet (params.pb.text:9); 100 on regtest (test_params.pb.text:6).
    • AND the region must be exhausted: resource_left == 0 (prospecting.cpp:60-68). If any resource is still mineable, the region cannot be re-prospected even after the expiry window.

Test coverage of these branches: ProspectionInProgress, EmptyRegion, ReprospectingExpiration, ReprospectingResources (prospecting_tests.cpp:65-109). The expiry boundary is inclusive of "too early": at height = prospection.height + 100 it is still too early; at +101 it is allowed in the regtest test (prospecting_tests.cpp:82-93).

2.3 Starting prospecting (state transition)#

MoveProcessor::MaybeStartProspecting, src/moveprocessor.cpp:1520-1544:

  1. Re-validates with ParseCharacterProspecting.
  2. Sets region.prospecting_character = character.id (moveprocessor.cpp:1528).
  3. Clears any old prospection on the region (so a re-prospect wipes the previous result immediately) (moveprocessor.cpp:1530-1532).
  4. StopCharacter(c) — cancels movement and mining (moveprocessor.cpp:1534), so a vehicle cannot move/mine while prospecting.
  5. Reads the vehicle's effective prospecting_blocks (must be > 0, moveprocessor.cpp:1536-1537).
  6. Creates an ongoing operation that completes at current_height + prospecting_blocks (moveprocessor.cpp:1539-1543):
    • character.ongoing = op.id (marks the character busy).
    • op.height = ctx.Height() + blocks (the finish height).
    • op.character_id = c.id.
    • op.proto.prospection set (operation type tag).

Prospecting cannot be interleaved with mining or movement — only one of these may be active because issuing prospect calls StopCharacter.

2.4 Duration: prospecting_blocks per vehicle, and fitment modifiers#

Duration is in blocks (≈ Polygon block time per block). Each vehicle has a base prospecting_blocks. Fitments can reduce it.

Base prospecting_blocks by vehicle (display names from the game UI; stats from vehicles_starter.pb.text and vehicles_others.pb.text).

The code suffix below is the same for all three factions; prepend the faction prefix to get the real code: rv (Red), bv (Blue), gv (Green). For example the starter is rv st (Raider) / bv st (Barracuda) / gv st (Scarab). Starters are defined in vehicles_starter.pb.text:1-74; the rest in vehicles_others.pb.text.

Code suffix Red / Blue / Green name Size prospecting_blocks mining_rate max cargo_space
st Raider / Barracuda / Scarab (starter) STARTER 10 2,000,000 1,000,000
s Looter / Urchin / Mantis LIGHT 9 4,000,000 3,600,000
m Pillager / Crocodile / Centipede MEDIUM 8 6,000,000 11,500,000
l Marauder / Moray / Millipede HEAVY 7 8,000,000 27,600,000
sc Finder / Octo / Flea (scout) LIGHT 5 0 (cannot mine) 0
mt Blood Carrier / Cobra / Horse (transport) MEDIUM 8 2,000,000 125,000,000
lt Bone Carrier / Trident / Ox (transport) HEAVY 7 4,000,000 350,000,000
vlt Skull Carrier / Hydra / Elephant (transport) VERY_HEAVY 6 6,000,000 2,600,000,000
sa Devourer / Gorgon / Bee (attack) LIGHT 5 8,000,000 5,600,000
ma Exterminator / Siren / Wasp (attack) MEDIUM 5 10,000,000 25,000,000
la Devastator / Poseidon / Hornet (attack) HEAVY 4 12,000,000 105,000,000
vla Annihilator / Leviathan / Widow (attack) VERY_HEAVY 3 14,000,000 285,000,000

(Mining rate min is always 0; see §3.1. Every value in this table was verified against vehicles_others.pb.text for all three faction prefixes.)

Fitment modifiers are applied in ApplyFitments (src/fitments.cpp:162-254). Modifiers of the same kind are summed (not compounded) and applied once:

  • Graviton Spectrometer (lf scanner): prospecting_blocks: { percent: -20 } (fitments.pb.text:910-924) — reduces prospecting time by 20% per fitment.
  • Tritanian Drill (lf pick): mining_rate: { percent: 20 } (fitments.pb.text:889-905) — increases mining yield by 20%.

After applying the prospecting modifier, the result is clamped to at least 1 block: blocks = max(1, modified) (fitments.cpp:243-244). Both are high-slot fitments. Multiple lf scanner units stack additively (e.g. 3 ⇒ −60%).

2.5 The prospect move (JSON schema)#

A character move is sent under the player's account move; the per-character update object accepts a prospect key holding an empty object:

{
  "c": {
    "5": { "prospect": {} }
  }
}
  • "c" = character updates, keyed by character ID (here 5).
  • prospect MUST be {} (empty). Non-object or non-empty → rejected silently with a warning (moveprocessor.cpp:840-849).
  • Prospecting and mining are mutually exclusive in one move; prospecting is processed before waypoints/mining and calls StopCharacter (moveprocessor.cpp:1850 then mining at 1866).

Pending view. While the prospect move is in the mempool, the pending state exposes it (src/pending.cpp:198-220):

{ "prospecting": <regionId> }

A pending prospect clears any pending waypoints for that character (pending.cpp:219-221), and a character cannot have both pending prospect and pending mine (pending.cpp:235-247).

2.6 Finishing a prospect — what is found#

When the ongoing operation matures, ProcessAllOngoings calls FinishProspecting (src/ongoings.cpp:178-182), then clears character.ongoing (un-busies it). FinishProspecting (src/prospecting.cpp:126-179) does, in order:

  1. Clears prospecting_character; sets prospection.name = owner, prospection.height = current height (prospecting.cpp:138-143).
  2. Detects the resource via DetectResource (§2.7) → sets prospection.resource = type and region.resource_left = amount (prospecting.cpp:145-151).
  3. Maybe finds an ancient artefact (§2.8) (prospecting.cpp:153).
  4. Maybe wins a prize from the lottery (§2.9) (prospecting.cpp:155-178).

Resulting region JSON (gamestatejson.cpp:572-603):

{
  "id": 12345,
  "prospection": { "name": "domob", "height": 1040 },
  "resource": { "type": "raw a", "amount": 57000000 }
}

While in progress, instead of name/height the prospection block carries {"inprogress": <characterId>} (gamestatejson.cpp:581-591).

2.7 Resource detection — which ore and how much#

DetectResource (src/resourcedist.cpp:116-180) decides the resource type and initial amount from the character's position and the resource-distribution map. The resource map is a list of areas, each with a centre coordinate and one or two possible resource types (resourcedist.pb.text).

Distance fall-off (internal::FallOff, resourcedist.cpp:53-71):

  • CORE_RADIUS = 400, OUTER_RADIUS = 1000 (L1 hex distance) (resourcedist.cpp:35-38).
  • Within 400 of an area centre → full weight.
  • Between 400 and 1000 → linear interpolation down toward 1.
  • Beyond 1000 → weight 0 (area does not contribute).
  • The interpolation never drops below 1 inside the outer radius and never exceeds the base value; verified by ResourceFallOffTests (resourcedist_tests.cpp:46-92). Example: FallOff(700, val) ≈ val/2.

Type selection (resourcedist.cpp:127-170):

  1. Collect every area within OUTER_RADIUS of the position; for each, compute the fall-off-weighted chance from BASE_CHANCE = 100,000,000 (resourcedist.cpp:46).
  2. Build a de-duplicated set keyed by (resource type, area centre), sorted for deterministic ordering independent of proto order (resourcedist.cpp:99-110, 127-149; test TypeOrderDeterministic, resourcedist_tests.cpp:191-210).
  3. Pick one resource type by weight: rnd.SelectByWeight(weights) (resourcedist.cpp:163-169). Probability of a type = its summed weight / total weight. With overlapping areas, the same ore appearing near multiple centres accumulates weight. (RandomType test verifies the 3:2:1 split, resourcedist_tests.cpp:153-189.)
  4. If no area is in range, return type = "raw a" (Trimideum), amount = 0 (resourcedist.cpp:155-161; test NothingAvailable, resourcedist_tests.cpp:143-151). Such a region is "prospected" but yields a resource of 0 and is not mineable.

Amount (resourcedist.cpp:172-179):

  • Base amount = uniform integer in [min_region_ore, max_region_ore].
  • Mainnet: min_region_ore = 2,000,000, max_region_ore = 100,000,000 (params.pb.text:21-22).
  • Regtest: min = 10, max = 20 (test_params.pb.text:18-19).
  • Final amount = FallOff(distance_to_picked_area_centre, base_amount). So ore found near the edge of an area's influence is scaled down (and is at least 1 if within range). Test MinimumAmount: at distance 900 with base 2 the amount falls to 1 (resourcedist_tests.cpp:242-257).

2.7.1 The nine ores and their named display values

Code Display name Per-unit cargo space
raw a Trimideum 1
raw b Talon 1
raw c Henoix 1
raw d Orchanum 1
raw e Kalanite 1
raw f Voltar 1
raw g Ravolute 1
raw h Talgarite 1
raw i Liberite 1

(space: 1 for every ore: materials.pb.text:63-200.) Refined materials mat amat i (Agarite…I-77E) are produced from these ores by refining, which is out of scope for this doc.

2.7.2 Resource distribution map (real areas)

There are 88 areas on mainnet (resourcedist.pb.text:90-565). Rarer/higher-tier ores (raw fraw i) cluster toward the map extremes; the common Trimideum (raw a) and Talon (raw b) are spread broadly. The full list with display names:

# Centre (x, y) Resources (code → name)
1 (2963, 1965) raw a Trimideum
2 (4958, -2185) raw i Liberite, raw h Talgarite
3 (4348, -1045) raw h Talgarite, raw g Ravolute
4 (1898, 3795) raw f Voltar
5 (5263, -3055) raw g Ravolute, raw f Voltar
6 (3293, 665) raw g Ravolute, raw f Voltar
7 (5063, -3695) raw b Talon
8 (2028, 2355) raw a Trimideum
9 (3898, -1405) raw i Liberite, raw h Talgarite
10 (1633, 3025) raw c Henoix
11 (2243, 1785) raw c Henoix
12 (3033, -95) raw g Ravolute, raw f Voltar
13 (1608, 2355) raw b Talon, raw a Trimideum
14 (878, 3795) raw c Henoix
15 (3248, -1045) raw h Talgarite, raw g Ravolute
16 (2768, -485) raw i Liberite, raw h Talgarite
17 (763, 3165) raw b Talon
18 (4143, -3835) raw e Kalanite
19 (1378, 1695) raw b Talon, raw a Trimideum
20 (2403, -615) raw h Talgarite, raw g Ravolute
21 (3613, -3135) raw b Talon, raw a Trimideum
22 (3188, -2345) raw e Kalanite
23 (708, 2095) raw a Trimideum
24 (443, 2625) raw a Trimideum
25 (93, 3285) raw a Trimideum
26 (1858, -285) raw g Ravolute, raw f Voltar
27 (2438, -2305) raw a Trimideum
28 (228, 1875) raw b Talon, raw a Trimideum
29 (-172, 2555) raw a Trimideum
30 (1818, -1505) raw b Talon, raw a Trimideum
31 (848, 375) raw f Voltar
32 (2648, -3265) raw a Trimideum
33 (1583, -1215) raw a Trimideum
34 (2918, -3905) raw b Talon, raw a Trimideum
35 (-682, 3195) raw b Talon, raw a Trimideum
36 (1993, -2675) raw a Trimideum
37 (-227, 1705) raw c Henoix
38 (-897, 2665) raw c Henoix
39 (1483, -2115) raw a Trimideum
40 (993, -1195) raw e Kalanite
41 (-1457, 3605) raw c Henoix
42 (1703, -3115) raw a Trimideum
43 (-57, 365) raw c Henoix
44 (2023, -3935) raw e Kalanite
45 (-617, 1285) raw f Voltar
46 (598, -1425) raw b Talon, raw a Trimideum
47 (-137, -55) raw e Kalanite
48 (-1607, 2805) raw a Trimideum
49 (908, -2445) raw b Talon, raw a Trimideum
50 (-482, 295) raw d Orchanum
51 (-697, 705) raw h Talgarite, raw g Ravolute
52 (-1542, 2175) raw f Voltar
53 (1223, -3615) raw b Talon, raw a Trimideum
54 (423, -2355) raw a Trimideum
55 (173, -1855) raw e Kalanite
56 (-357, -1075) raw b Talon, raw a Trimideum
57 (-1607, 1405) raw d Orchanum
58 (-2307, 2645) raw g Ravolute, raw f Voltar
59 (-857, -335) raw f Voltar
60 (-2187, 2145) raw b Talon, raw a Trimideum
61 (-3117, 3865) raw f Voltar
62 (-1177, -635) raw h Talgarite, raw g Ravolute
63 (-2192, 1195) raw b Talon, raw a Trimideum
64 (-1247, -1015) raw f Voltar
65 (-2752, 1835) raw a Trimideum
66 (-2112, 235) raw d Orchanum
67 (-4057, 3885) raw g Ravolute, raw f Voltar
68 (-2927, 1245) raw a Trimideum
69 (-3417, 1945) raw a Trimideum
70 (-4447, 3845) raw h Talgarite, raw g Ravolute
71 (-2507, -155) raw b Talon, raw a Trimideum
72 (-2977, 705) raw b Talon, raw a Trimideum
73 (-1877, -1655) raw g Ravolute, raw f Voltar
74 (-3107, 365) raw a Trimideum
75 (-3842, 1715) raw a Trimideum
76 (-4902, 3815) raw a Trimideum
77 (-2892, -525) raw f Voltar
78 (-1427, -3495) raw h Talgarite, raw g Ravolute
79 (-4312, 2275) raw d Orchanum
80 (-1212, -3945) raw i Liberite, raw h Talgarite
81 (-3547, 505) raw d Orchanum
82 (-2107, -2415) raw h Talgarite
83 (-4312, 1595) raw b Talon
84 (-2037, -3115) raw i Liberite, raw h Talgarite
85 (-5067, 2625) raw f Voltar
86 (-5527, 3425) raw b Talon, raw a Trimideum
87 (-3737, -215) raw a Trimideum
88 (-4477, 1005) raw d Orchanum

Because of the 1:1 mapping of weight to ore and the additive overlap of areas, coverage of all nine ores within any 1000-L1 radius is intentional — the Resources test prospects a grid and asserts exactly 9 distinct ore types are reachable, and that raw a (Trimideum) is found in more regions than raw i (Liberite) (prospecting_tests.cpp:189-221).

2.8 Ancient artefacts (bonus drop on prospect)#

After resource detection, MaybeFindArtefact (prospecting.cpp:80-122) may grant an artefact based on the ore type just detected. The mapping is in resource_dist.possible_artefacts (resourcedist.pb.text:4-88). Each ore has an ordered list of {artefact, probability} entries; they are evaluated in order and the first one that succeeds is awarded — at most one artefact per prospect (prospecting.cpp:93-121, the loop breaks on success).

  • Each entry's roll is rnd.ProbabilityRoll(1, probability) → success chance = 1 / probability (prospecting.cpp:95).

  • The four artefacts (display names from the game UI):

    Code Display name
    art c Ancient Artefact (common)
    art uc Ancient Artefact (uncommon)
    art r Ancient Artefact (rare)
    art ur Ancient Artefact (ultra-rare)

Mainnet artefact tables by ore (resourcedist.pb.text:4-88); chance = 1/N:

Ore (code → name) Entries evaluated in order (artefact @ 1/N)
raw a Trimideum art c @ 1/20
raw b Talon art c @ 1/20
raw c Henoix art c @ 1/20, art uc @ 1/50
raw d Orchanum art c @ 1/20, art uc @ 1/50
raw e Kalanite art c @ 1/20, art uc @ 1/50
raw f Voltar art c @ 1/20, art uc @ 1/50, art r @ 1/100
raw g Ravolute art c @ 1/20, art uc @ 1/50, art r @ 1/100
raw h Talgarite art c @ 1/20, art uc @ 1/50, art r @ 1/100, art ur @ 1/200
raw i Liberite art c @ 1/20, art uc @ 1/50, art r @ 1/100, art ur @ 1/200

So rarer ores can drop rarer artefacts, but the common artefact is always rolled first; e.g. on raw i (Liberite) the overall chance of any artefact is 1 − (19/20)(49/50)(99/100)(199/200) ≈ 8.29%, with art ur (Ancient Artefact, ultra-rare) only possible if all earlier rolls fail.

If no artefact map entry exists for the ore (cannot happen for the nine real ores) a warning is logged and nothing is given (prospecting.cpp:86-90).

Cargo handling (prospecting.cpp:104-119): if the artefact's space (1 for all four artefacts, artefacts.pb.text) fits in free cargo, it is added to the character's inventory; otherwise it is dropped on the ground at the character's position into the GroundLootTable. Test CargoFull shows 8 prospects with room for 5 → 5 carried, 3 on the ground (prospecting_tests.cpp:353-374).

Regtest difference. test_resourcedist.pb.text overrides the raw a and raw f artefact tables (proto map keys override on merge): raw aart r@1/1 then art c@1/1 (used to test "always first in order" → always art r); raw fart c@1/2 then art r@1/2 (used to test randomisation, ~50% art c, ~25% art r, ~25% nothing). See prospecting_tests.cpp:287-351.

2.9 The prize lottery (competition)#

On every completed prospect, before returning, the code walks the configured prize tiers and may award one prize (prospecting.cpp:155-178). This was the launch competition; prizes are finite and global.

Mechanics (prospecting.cpp:156-178):

  1. Determine if the position is a low-prize zone: IsLowPrizeZone(pos) (prospecting.cpp:156).
  2. For each prize tier p, in config order:
    • found = ItemCounts.GetFound("<name> prize") — how many of that prize have already been won globally (prospecting.cpp:160-161).
    • If found == p.number (the tier is exhausted), skip it (prospecting.cpp:162-164).
    • Roll: rnd.ProbabilityRoll(lowChance ? 55 : 100, 100 * p.probability).
      • Normal zone: success chance = 100 / (100 · probability) = 1 / probability per prospect.
      • Low-prize zone: chance = 55 / (100 · probability) = 55% of normal (a 45% reduction) (prospecting.cpp:166-168).
    • On success: increment the global found count, add "<name> prize" to the character inventory, and stop (only one prize per prospect) (prospecting.cpp:170-177).

Prizes are stored in the character's inventory under the item code "<tier name> prize" (e.g. "cash prize").

Low-prize zone (Params::IsLowPrizeZone, src/params.cpp:30-49): any position within L1 distance 1250 of a faction safe-zone centre (the three starter spawns). Faction safe zones (safezones.pb.text:1-22):

Faction Centre (x, y) Spawn building
Red (r) (1960, -2601) building 6 (params.pb.text:35-37)
Green (g) (-3472, 1824) building 4
Blue (b) (547, 2497) building 5

Only safe_zones entries that have a faction count toward the low-prize zone; the small radius-30 neutral/partner no-combat zones do not (params.cpp:38-41). On regtest the prize roll still uses IsLowPrizeZone, but regtest clears all safe zones on merge (roconfig.cpp:135), so no low-prize zone exists there unless re-added (the test re-checks with explicit positions, prospecting_tests.cpp:39-41, 227, 269).

Mainnet prize table (params.pb.text:54-110) — 55 tiers total. The first tier cash lives inside the params block (params.pb.text:54); the remaining 54 were exported from a spreadsheet (params.pb.text:57-110). Each tier: number = total available globally, probability = N where per-prospect chance is 1/N (normal zone). Sorted here by rarity (largest N = rarest):

Tier name (<name> prize) number (total) probability (1/N) Approx normal chance
cash 10 350,000 1 in 350,000
Lastlight Mission Card 1 130,289 1 in 130,289
U.S.S. Enterprise NCC-1701-D + 3 Founders Supply Crates + Steam key 1 130,289 1 in 130,289
U.S.S. Voyager NCC-74656 + 3 Founders Supply Crates + Steam key 1 130,289 1 in 130,289
I.K.S. Rotarran + 3 Founders Supply Crates + Steam key 1 130,289 1 in 130,289
Silver Tier P1 Pistol 1 130,289 1 in 130,289
Bronze CDA-2 Armor 1 130,289 1 in 130,289
Mar 4 SMG 1 130,289 1 in 130,289
3 Spaceships 1 130,289 1 in 130,289
F1 Legendary Crate 1 130,289 1 in 130,289
Death Knight Head 1 130,289 1 in 130,289
Death Knight Body 1 130,289 1 in 130,289
Death Knight Arms 1 130,289 1 in 130,289
Death Knight Legs 1 130,289 1 in 130,289
347 E-Den 2 77,127 1 in 77,127
F1 Epic Crate 2 77,127 1 in 77,127
F1 Rare Crate 3 56,367 1 in 56,367
Gamerhash Coins 3 56,367 1 in 56,367
Gamerhash Store 3 56,367 1 in 56,367
PGFK Voucher 3 56,367 1 in 56,367
Legendary Crystals 3 56,367 1 in 56,367
Assassin's Creed 3 56,367 1 in 56,367
F1 Common Crate 4 44,905 1 in 44,905
Thermal Implant 50 5,063 1 in 5,063
200 DIO 50 5,063 1 in 5,063
Medium Transport 50 5,063 1 in 5,063
Silver Crate 5 37,530 1 in 37,530
5,000 TKT 5 37,530 1 in 37,530
10,000 1UP 5 37,530 1 in 37,530
Age of Rust MFT Token 25 9,499 1 in 9,499
Runner Mission Card 10 21,118 1 in 21,118
Cartesi Tokens 10 21,118 1 in 21,118
Dissolution DFT 10 21,118 1 in 21,118
5,000 DWD 10 21,118 1 in 21,118
Clown Weapon 10 21,118 1 in 21,118
Epic Keys 10 21,118 1 in 21,118
500 ZNZ 10 21,118 1 in 21,118
TBD Legendary 16 14,090 1 in 14,090
Dungeon Key 20 11,582 1 in 11,582
Starter Pack 20 11,582 1 in 11,582
Summoner Spellbook 20 11,582 1 in 11,582
Rare Keys 20 11,582 1 in 11,582
100 DAI 12 18,075 1 in 18,075
TBD Ultra Rare 35 7,015 1 in 7,015
TBD Rare 45 5,578 1 in 5,578
Prison Key Card 100 2,655 1 in 2,655
Package of 10 Training Tickets + 1 Trinity Boost 100 2,655 1 in 2,655
1% Player Shares 100 2,655 1 in 2,655
TBD Uncommon 75 3,477 1 in 3,477
TBD Common 125 2,150 1 in 2,150
100 TKT 250 1,109 1 in 1,109
Temporal Disrupter 200 1,374 1 in 1,374
Syphon 200 1,374 1 in 1,374
Graviton Spectrometer 200 1,374 1 in 1,374
Tritanian Drill 200 1,374 1 in 1,374

Display names for these prizes (with the partner-game suffix) are the ones shown in the game UI, e.g. "Prison Key Card""Prison Key Card (Age of Rust)".

Because tiers are checked in config order and the loop stops at the first success, a rarer-listed tier earlier in the list is evaluated first. Note the last four Taurion items (Temporal Disrupter, Syphon, Graviton Spectrometer, Tritanian Drill) are the most common prizes (1/1374) and are also obtainable as craftable fitments.

Regtest prize table (used by unit tests; test_params.pb.text:25-27):

Tier number probability (1/N)
gold 3 100
silver 1000 10
bronze 1 1

The Prizes test confirms exact behavior: over 10,000 prospects in a normal zone it finds all 3 gold, the 1 bronze, and ~1000 silver (expected value 1000); in a low-prize zone silver expectation drops to ~550 (55%) (prospecting_tests.cpp:223-283).


3. Mining#

Mining extracts the prospected resource from a region over many blocks, filling the character's cargo.

3.1 Mining rate per vehicle, and fitment modifiers#

Each vehicle has a mining_rate { min, max } stat (vehicles_*.pb.text). Per processed block, the amount attempted is a uniform integer in [min, max] (§3.4). On all real vehicles min = 0 and max scales with hull size and role; see the table in §2.4. Scouts (… sc) have mining_rate { min:0 max:0 } and cannot mine at all (vehicles_others.pb.text:113-114).

The Tritanian Drill (lf pick) fitment applies mining_rate { percent: 20 } (fitments.pb.text:889-905), increasing both min and max by 20% (fitments.cpp:249-254). Modifiers are summed across fitments and applied once to each of min and max.

A character with no mining field (e.g. after InitCharacterStats for a vehicle without mining_rate) simply cannot mine (fitments.cpp:141-144).

3.2 Who can mine, and entry conditions#

The mine move starts mining; accepted only if (BaseMoveProcessor::ParseCharacterMining, moveprocessor.cpp:881-954):

Requirement Source
mine value is an empty JSON object {} moveprocessor.cpp:887-897
Vehicle has a mining stat (can mine) moveprocessor.cpp:899-903
Character not busy (no ongoing op) moveprocessor.cpp:905-910
Character not inside a building moveprocessor.cpp:912-917
Character is not moving (!has_movement) moveprocessor.cpp:925-930
Region under the character is prospected (has_prospection) moveprocessor.cpp:932-940
Region has resource_left > 0 moveprocessor.cpp:942-951

3.3 Starting mining (state transition)#

MoveProcessor::MaybeStartMining (moveprocessor.cpp:1546-1557) simply sets character.mining.active = true after validation. Mining does not create an ongoing operation; it is processed every block while active (§3.4).

Ordering in a combined move: mining is started after prospecting and before waypoints, so that "set waypoints + mine" does not result in moving and mining at once (moveprocessor.cpp:1862-1866). Setting waypoints (StopCharacterStopMining) cancels mining (moveprocessor.cpp:1465).

The mine move:

{
  "c": {
    "5": { "mine": {} }
  }
}

mine MUST be {} (moveprocessor.cpp:891-897). Pending view (pending.cpp:231-258): { "mining": <regionId> }. A pending mine is rejected if the same character has a pending prospect, and setting waypoints clears a pending mine (pending.cpp:139-144, 235-247).

3.4 Per-block mining processing#

ProcessAllMining (src/mining.cpp:40-131) runs every block over all characters with mining.active (characters.QueryMining()):

For each active miner:

  1. Find the region under the character's position (mining.cpp:50-55).
  2. Region no longer prospected? If the region's prospection was cleared (e.g. exhausted last block and immediately re-prospected by someone), gracefully stop: clear mining.active and continue (mining.cpp:60-73).
  3. Roll the amount to mine: mined = rate.min + rnd.NextInt(rate.max − rate.min + 1) — uniform in [min, max] (mining.cpp:75-80).
  4. Rolled 0? Continue to the next character without running the stop logic — i.e. a 0-roll does not stop mining (mining.cpp:82-86; test ZeroRolls, mining_tests.cpp:174-195).
  5. Clamp to region's resource_left: if mined > left, set mined = left (mining.cpp:88-95).
  6. Clamp to free cargo: maxForSpace = free_cargo / item_space (item space is 1 for ores), so mined = min(mined, maxForSpace) (mining.cpp:97-111).
  7. If mined > 0: subtract from region resource_left, add mined of the resource type to the character inventory (mining.cpp:113-122).
  8. Else (mined == 0 after clamping, i.e. region empty OR cargo full): clear mining.active — the operation auto-stops (mining.cpp:123-129).

So mining auto-stops when the region is exhausted or the vehicle's cargo is full, but a random 0 roll keeps it active.

Basic example (mining_tests.cpp:112-118): rate fixed at 10, region has 100, cargo 1000 → after one block: inventory +10, region 90, still active.

3.5 Cargo and depletion edge cases#

  • Cargo full. FreeCargoSpace = cargo_space − used (database/character.cpp:243-249). When free cargo can't hold even one unit, the clamp drives mined to 0 and mining stops. Test CargoFull (mining_tests.cpp:159-172): starting with 95/100 cargo used and a region holding 100, block 1 mines 5 (cargo 95→100, region 100→95) and stays active; block 2 finds cargo full, clamps mined to 0 and stops (cargo stays 100, region stays 95).
  • Resource used up. Test ResourceUsedUp: region 5, rate 10 → mines 5, region 0, still active that block; next block left == 0mined clamps to 0 → mining stops (mining_tests.cpp:144-157).
  • Multiple miners on one region. All active miners are processed in the same block in DB order; each draws from the same shared resource_left. Test MultipleMiners: region 25, two miners at rate 10 — block 1 both take 10 (region→5); block 2 the first takes the remaining 5 and the second gets 0 and stops (mining_tests.cpp:234-263). Mining is first-come-first-served within a block, not split fairly.
  • Region de-prospected mid-mining → graceful stop (§3.4 step 2; test RegionNotProspected, mining_tests.cpp:136-142).

3.6 When a region empties, and re-prospecting#

  • A region is exhausted when resource_left reaches 0 via mining (mining.cpp:113-117).
  • An exhausted region keeps its prospection record (name, height, resource type) — it just has nothing left to mine. The region JSON still shows resource.amount = 0.
  • To make it productive again it must be re-prospected, which requires (§2.2): the region is exhausted (resource_left == 0) and at least prospection_expiry_blocks (5000 mainnet / 100 regtest) have passed since the last prospect finished.
  • Re-prospecting immediately clears the old prospection (moveprocessor.cpp:1530-1532), then re-runs DetectResource on completion — the new resource type and amount are rolled fresh and may differ from before.
  • Edge case: if a region is exhausted and re-prospected in adjacent blocks, a character that was mining it can find its prospection already cleared on the next mining pass and stops gracefully (mining.cpp:60-73).

4. Client-facing JSON (what the UI reads)#

The official client consumes the GSP game-state JSON. Relevant fields:

Character (gamestatejson.cpp)

  • prospectingblocks: effective prospecting_blocks of the vehicle (only present if the vehicle can prospect) (gamestatejson.cpp:291-292).
  • mining: present if the vehicle can mine (gamestatejson.cpp:213-233):
    { "rate": { "min": 0, "max": 4000000 }, "active": true, "region": 12345 }
    
    region only appears when active is true.
  • inventory.fungible: includes mined ores (raw araw i), artefacts (art *) and prizes ("<name> prize") (gamestatejson.cpp:237-249).

Ongoing operation (a prospect in progress; gamestatejson.cpp:480-569):

{
  "operation": "prospecting",
  "start_height": 1000,
  "end_height": 1010,
  "characterid": 5
}

end_height is the finish height (op.height + endDelta, with endDelta = 0 for prospecting) (gamestatejson.cpp:496-567).

Region (gamestatejson.cpp:572-603): see §2.6. Either prospection.inprogress (a character ID, while running) or prospection.{name,height} + resource.{type,amount} (after completion).


5. Quick rule summary#

  • Granularity: prospect/mine a region (a cluster of hexes), not a tile. Region ID from the embedded map; IDs sparse, start at 0.
  • Prospect entry: prospect:{}, vehicle has prospecting_blocks, not busy, not in a building, region not in-progress, and either never-prospected or (exhausted AND past expiry).
  • Duration: vehicle prospecting_blocks (10 down to 3), minus 20% per Graviton Spectrometer (lf scanner), floored at 1.
  • Found resource: weighted by nearby distribution areas (fall-off 400→1000), amount uniform in 2,000,000…100,000,000 (mainnet) then scaled by distance.
  • Bonus: ancient artefact (1/20…1/200 by ore), dropped to ground if cargo full; one global prize from a finite lottery (1/probability, ×0.55 in low-prize zones within 1250 of a faction spawn).
  • Mine entry: mine:{}, vehicle can mine, not busy/in building/moving, region prospected with resource_left > 0.
  • Mine rate: uniform [min,max] per block (min always 0; max 0…14,000,000 by vehicle), +20% per Tritanian Drill (lf pick).
  • Mining auto-stops when the region empties or cargo fills (but not on a 0 roll). Multiple miners share the pool first-come-first-served per block.
  • Re-prospect an exhausted region after prospection_expiry_blocks (5000 mainnet) blocks; the new ore type/amount is rolled fresh.

Open questions#

  1. Exact mainnet region count. The number of regions is embedded in regiondata.dat (logged at procmap.cpp:598), not a source constant. The doc states "hundreds of thousands" but the precise figure would require building and running the map-generation/region tooling against the shipped data blob.
  2. Average region size in hexes / region shape. GetRegionShape returns the tile set per region, but no aggregate (min/avg/max tiles per region) is encoded as a constant; regionmap_bench.cpp:82-87 logs per-region sizes only when run. The exact per-region hex footprint and any guaranteed shape (e.g. roughly hexagonal blobs of N tiles) is not asserted in the reviewed source.
  3. Real block time → wall-clock duration. prospecting_blocks and mining are in blocks; the wall-clock conversion depends on Polygon block cadence and the GSP's confirmation depth, which are outside the reviewed GSP rule source.
  4. cash prize redemption. The cash prize tier (10 available, 1/350,000) is awarded as the inventory item "cash prize" like any other; how/whether it is redeemed for value is not defined in the prospecting/mining code reviewed here.