Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

game_data.gno

23.13 Kb · 385 lines
  1// GnoMoji — Main Game Realm (game32)
  2// gno.land/r/g138gdmwj6heduu3st796lw6qqdwylgaxf7ty5gz/game32
  3//
  4// game29 closes the game28 audit holes (see SECURITY_GAME28.md):
  5//   - claims and achievements need an ed25519 voucher from the server's claim
  6//     signer (a dedicated key, never the treasury), with per-player nonces;
  7//   - per-rarity rate caps still apply on chain, plus per-coin vein caps;
  8//   - the Gnome Pass costs real ugnot (OriginSend), withdrawable by admin;
  9//   - PvP commit/reveal is hash-checked, casino bets settle on chain;
 10//   - player state can be imported once from game27/game28, then locked.
 11
 12package game32
 13
 14import (
 15	"gno.land/p/nt/avl/v0"
 16)
 17
 18const realmPkgPath = "gno.land/r/g138gdmwj6heduu3st796lw6qqdwylgaxf7ty5gz/game32"
 19
 20// ── State ────────────────────────────────────────────────────────────────────
 21
 22var (
 23	admin    address // deployer; can set roles, prices, veins, migrate, withdraw
 24	operator address // server hot account for casino seeds (defaults to admin)
 25
 26	// claimSignerPubKey is the ed25519 public key (32 bytes) whose signatures
 27	// authorise ClaimAndMint / achievement vouchers. Empty = claims disabled.
 28	claimSignerPubKey []byte
 29	claimNonces       avl.Tree // addr → last used voucher nonce (decimal string)
 30
 31	balances      avl.Tree // addr+":"+coinId → amount string
 32	claimBuckets  avl.Tree // addr → "m0:t0|m1:t1|m2:t2|m3:t3|m4:t4|m5:t5|m6:t6" (packed; lazy-migrated from old addr:r keys)
 33	veinMinted    avl.Tree // coinId → total ever credited by claims/rewards/migration
 34	veinMaxOverride avl.Tree // coinId → admin-set max (overrides the rarity default)
 35	veinOverrideActive bool   // true once any SetVeinMax has been called; gates the veinMaxOverride AVL read
 36	casinoRounds  avl.Tree // roundId → *casinoRound
 37	pvpLobbies    avl.Tree // lobbyId and lobbyId+":field" → string (see game_pvp.gno)
 38	pvpWagers     avl.Tree // lobbyId → "coinId:amount:creator:challenger"
 39	ownedTools    avl.Tree // addr → comma-separated tool IDs
 40	toolLife      avl.Tree // addr+":"+toolId → remaining life string
 41	lifetimeMined avl.Tree // addr+":"+coinId → total mined string (real claims only)
 42	rewarded      avl.Tree // addr+":"+coinId → total credited by rewards (medals etc.)
 43	achievements  avl.Tree // addr+":"+achId → "1"
 44	dailyCheckins avl.Tree // addr → unix seconds string
 45	skillRanks    avl.Tree // addr+":"+skillId → rank string
 46	miners        avl.Tree // addr → total mined all-time string
 47	playerXp      avl.Tree // addr → cumulative rarity-weighted XP (uint64 string)
 48	lastClaim     avl.Tree // addr → unix timestamp of last ClaimAndMint
 49	blacklist     avl.Tree // addr → "reason:timestamp"
 50	banExpiries   avl.Tree // addr → unix expiry (0 = permanent)
 51	guilds        avl.Tree // guildId → JSON{name, leader, memberCount, createdAt}
 52	guildMembers  avl.Tree // guildId+":"+addr → "1"
 53	playerGuild   avl.Tree // addr → guildId
 54	gnomePasses   avl.Tree // addr → unix expiry timestamp string
 55
 56	paused bool
 57
 58	// Gnome Pass price in ugnot per 30 days (admin-settable).
 59	passPriceUgnot = int64(5000000)
 60	passRevenueUgnot int64 // lifetime ugnot received for passes (accounting only)
 61
 62	pvpCounter   uint64 // monotonically increasing lobby number
 63	guildCounter uint64 // monotonically increasing guild number
 64
 65	// Relay (game32): treasury wallet address authorised to call RelayBuyTool
 66	// and RelayTrainSkill on behalf of players (gasless tool purchases).
 67	// Set by SetRelayAddress (admin only). Zero value = relay disabled.
 68	relayAddress address
 69	// relayNonces tracks per-player relay nonces (shared sequence across all
 70	// relay kinds) to prevent replay attacks on gasless calls.
 71	relayNonces avl.Tree // addr → last nonce (decimal string)
 72
 73	// Migration (game31 → game32). Batches are idempotent (set semantics +
 74	// batch registry); FinalizeMigration locks imports forever.
 75	// migrationAddrs tracks every unique player address imported via
 76	// MigrateBatch so MigrationStatus can report migrated-address count.
 77	migrationFinalized bool
 78	migrationBatches   avl.Tree // batchId → "kind:count"
 79	migrationAddrs     avl.Tree // addr → "1" (deduplicates across batch re-runs)
 80)
 81
 82// ── Tool Tables (game29) ──────────────────────────────────────────────────────
 83// One ID scheme shared with src/lib/game/catalog.ts. Every ingredient is
 84// mineable by the previous tool (rarity ≤ prev maxRarity). Repair = ceil(30%
 85// of cost). See REPORT.md §4 for the full progression table and §5 for the
 86// claim-rate cap table.
 87
 88// toolOrder is the canonical upgrade path from hands to omega.
 89var toolOrder = []string{
 90	"hands", "starter-twig", "twig", "wood", "stone", "iron", "gold",
 91	"diamond", "cosmic", "plasma", "neutron", "quasar", "void", "genesis", "omega",
 92}
 93
 94// toolRequires: BuyTool must panic unless the caller owns the prerequisite tool.
 95var toolRequires = map[string]string{
 96	"starter-twig": "hands",
 97	"twig":         "starter-twig",
 98	"wood":         "twig",
 99	"stone":        "wood",
100	"iron":         "stone",
101	"gold":         "iron",
102	"diamond":      "gold",
103	"cosmic":       "diamond",
104	"plasma":       "cosmic",
105	"neutron":      "plasma",
106	"quasar":       "neutron",
107	"void":         "quasar",
108	"genesis":      "void",
109	"omega":        "genesis",
110}
111
112// toolMaxRarity: highest coin rarity each tool can mine (used by the claim rate cap).
113var toolMaxRarity = map[string]int{
114	"hands":        0,
115	"starter-twig": 0,
116	"twig":         0,
117	"wood":         1,
118	"stone":        2,
119	"iron":         3,
120	"gold":         4,
121	"diamond":      5,
122	"cosmic":       6,
123	"plasma":       6,
124	"neutron":      6,
125	"quasar":       6,
126	"void":         6,
127	"genesis":      6,
128	"omega":        6,
129}
130
131// toolCosts maps toolId → slice of [coinId, amount] pairs.
132// Matches catalog.ts TOOLS array exactly. Amounts are for qty=1.
133var toolCosts = map[string][][2]string{
134	"starter-twig": {
135		{"grin", "13"},
136	},
137	"twig": {
138		{"grin", "14"}, {"joy", "9"}, {"cool", "7"},
139	},
140	"wood": {
141		{"seedling", "19"}, {"leaves", "16"}, {"fallen", "13"}, {"think", "11"}, {"sleep", "10"}, {"party", "8"},
142	},
143	"stone": {
144		{"rock", "50"}, {"log", "45"}, {"evergreen", "40"}, {"brick", "35"}, {"mushroom", "30"}, {"heart", "25"},
145		{"deciduous", "20"}, {"cactus", "15"},
146	},
147	"iron": {
148		{"gear", "42"}, {"chains", "42"}, {"wrench", "38"}, {"hammer", "34"}, {"anchor", "34"}, {"pick", "29"},
149		{"nut", "21"}, {"saw", "8"}, {"hammer-wrench", "17"}, {"magnet", "4"},
150	},
151	"gold": {
152		{"gold-medal", "150"}, {"crown", "140"}, {"trophy", "130"}, {"money-bag", "120"}, {"bolt", "120"}, {"sword", "110"},
153		{"dagger", "10"}, {"bow", "10"}, {"bullseye", "30"}, {"mahjong", "10"}, {"hanafuda", "10"}, {"fireworks", "10"},
154	},
155	"diamond": {
156		{"gem", "220"}, {"coin", "200"}, {"ring", "190"}, {"disco", "180"}, {"kite", "180"}, {"dodo", "160"},
157		{"scope", "10"}, {"xray", "10"}, {"tube", "10"}, {"dish", "10"}, {"lock", "40"}, {"key-lock", "30"},
158		{"eight", "20"}, {"puzzle", "30"},
159	},
160	"cosmic": {
161		{"galaxy", "336"}, {"moai", "320"}, {"hamsa", "320"}, {"mirror", "304"}, {"atom", "288"}, {"ufo", "256"},
162		{"whale-spout", "16"}, {"whale", "16"}, {"sun", "16"}, {"radioactive", "16"}, {"cyclone", "16"}, {"tornado", "16"},
163		{"sat", "16"}, {"castle", "16"}, {"liberty", "16"}, {"castle-jp", "16"},
164	},
165	"plasma": {
166		{"galaxy", "1003"}, {"atom", "935"}, {"moai", "935"}, {"comet", "867"}, {"jelly", "799"}, {"peacock", "799"},
167		{"om", "17"}, {"yin", "17"}, {"ophiuchus", "17"}, {"fleur", "17"}, {"trident", "17"}, {"radioactive", "17"},
168		{"biohazard", "17"}, {"castle-jp", "34"}, {"sun-face", "17"}, {"dragon-face", "17"}, {"sauropod", "17"}, {"moon-word", "34"},
169	},
170	"neutron": {
171		{"jelly", "2040"}, {"peacock", "1870"}, {"unicorn", "1870"}, {"galaxy", "1700"}, {"hamsa", "1700"}, {"dna", "1496"},
172		{"genie", "17"}, {"heart-organ", "34"}, {"mage", "34"}, {"monarch", "51"}, {"ninja", "34"}, {"brain", "17"},
173		{"lotus-sit", "34"}, {"lion", "51"}, {"tiger", "51"}, {"eagle", "51"}, {"wolf", "68"}, {"shark", "85"},
174	},
175	"quasar": {
176		{"unicorn", "4590"}, {"peacock", "4590"}, {"jelly", "4590"}, {"lotus", "3510"}, {"galaxy", "3780"}, {"dragon", "3510"},
177		{"fireworks", "54"}, {"medal", "27"}, {"sports-medal", "54"}, {"silver-medal", "81"}, {"bronze-medal", "81"}, {"tower", "108"},
178		{"hot-spring", "135"}, {"pirate", "81"}, {"fairy", "108"}, {"vampire", "108"}, {"heli", "54"}, {"bullet", "81"},
179		{"race", "108"},
180	},
181	"void": {
182		{"lotus", "8990"}, {"unicorn", "10850"}, {"dragon", "10540"}, {"peacock", "10850"}, {"jelly", "11470"}, {"galaxy", "3720"},
183		{"om", "31"}, {"yin", "31"}, {"trident", "62"}, {"fleur", "62"}, {"ophiuchus", "31"}, {"shooting", "62"},
184		{"sun-face", "62"}, {"dragon-face", "93"}, {"rex", "93"}, {"sauropod", "93"}, {"mammoth", "124"}, {"new-face", "93"},
185		{"kaaba", "31"}, {"witness", "31"}, {"genie", "31"},
186	},
187	"genesis": {
188		{"lotus", "18000"}, {"dragon", "18000"}, {"unicorn", "17520"}, {"peacock", "16560"}, {"jelly", "16080"}, {"infinity", "14160"},
189		{"genie", "48"}, {"witness", "48"}, {"sun-face", "144"}, {"shooting", "96"}, {"dragon-face", "168"}, {"om", "72"},
190		{"yin", "72"}, {"trident", "72"}, {"fleur", "72"}, {"kaaba", "72"}, {"rex", "144"}, {"sauropod", "144"},
191		{"mammoth", "168"}, {"new-face", "144"}, {"lion", "264"}, {"shark", "504"},
192	},
193	"omega": {
194		{"lotus", "26970"}, {"dragon", "25110"}, {"unicorn", "24180"}, {"peacock", "23250"}, {"jelly", "23250"}, {"infinity", "22320"},
195		{"genie", "65"}, {"witness", "74"}, {"sun-face", "195"}, {"shooting", "158"}, {"dragon-face", "233"}, {"om", "93"},
196		{"yin", "93"}, {"trident", "112"}, {"fleur", "112"}, {"ophiuchus", "93"}, {"kaaba", "93"}, {"rex", "195"},
197		{"sauropod", "214"}, {"mammoth", "233"}, {"new-face", "195"}, {"mage", "307"}, {"heart-organ", "233"}, {"sat", "381"},
198	},
199}
200
201// skillBaseCosts maps skillId → base cost at rank 0.
202// Actual cost at rank N = base × 2^N (doubles per rank).
203var skillBaseCosts = map[string][][2]string{
204	// STRIKE branch
205	"wrist":               {{"grin", "80"}, {"joy", "20"}},
206	"palm_strike":         {{"cool", "60"}, {"think", "40"}},
207	"follow":              {{"fire", "40"}, {"up", "40"}},
208	"knuckle_wrap":        {{"rock", "45"}, {"log", "40"}},
209	"heavy_swing":         {{"brick", "35"}, {"evergreen", "30"}},
210	"stone_breaker":       {{"rock", "70"}, {"hammer", "25"}, {"gear", "20"}},
211	"anvil_force":         {{"chains", "35"}, {"wrench", "30"}, {"brick", "60"}},
212	"point_drive":         {{"pick", "35"}, {"gear", "40"}, {"log", "90"}},
213	"kinetic_thrust":      {{"rocket", "20"}, {"bolt", "12"}, {"fire", "70"}},
214	"crushing_blow":       {{"crown", "10"}, {"trophy", "12"}, {"hammer", "45"}, {"bolt", "15"}},
215	"shatter_spike":       {{"sword", "18"}, {"pick", "45"}, {"chains", "45"}, {"gold-medal", "12"}},
216	"ironclad_cleave":     {{"anchor", "40"}, {"gear", "60"}, {"wrench", "50"}, {"money-bag", "15"}},
217	"thunder_jab":         {{"bolt", "25"}, {"rocket", "30"}, {"fire", "120"}, {"wand", "15"}},
218	"titan_hammer":        {{"gem", "14"}, {"coin", "18"}, {"crown", "22"}, {"trophy", "22"}, {"sword", "28"}},
219	"diamond_cleave":      {{"gem", "20"}, {"ring", "12"}, {"disco", "15"}, {"pick", "70"}, {"gold-medal", "24"}},
220	"hyper_drive":         {{"kite", "15"}, {"dodo", "12"}, {"bolt", "35"}, {"rocket", "45"}, {"gear", "70"}},
221	"quasar_impact":       {{"galaxy", "18"}, {"moai", "18"}, {"atom", "20"}, {"ufo", "16"}, {"comet", "18"}},
222	"black_hole_crush":    {{"galaxy", "30"}, {"atom", "28"}, {"moai", "24"}, {"gem", "35"}, {"ring", "28"}, {"comet", "22"}},
223	"mythic_annihilation": {{"dragon", "8"}, {"unicorn", "10"}, {"jelly", "12"}, {"peacock", "12"}, {"galaxy", "35"}, {"moai", "30"}},
224	"singularity_fist":    {{"infinity", "6"}, {"lotus", "8"}, {"dragon", "10"}, {"unicorn", "12"}, {"jelly", "14"}, {"galaxy", "40"}},
225	// FORTUNE branch
226	"fortune":             {{"clover", "16"}, {"heart", "40"}},
227	"four_leaf":           {{"seedling", "60"}, {"leaves", "50"}},
228	"edge":                {{"pizza", "20"}, {"spark", "30"}},
229	"glint":               {{"spark", "50"}, {"star", "12"}},
230	"spark_chaser":        {{"fire", "45"}, {"tada", "25"}},
231	"razor_focus":         {{"coffee", "25"}, {"game", "25"}, {"hundred", "30"}},
232	"precision_cut":       {{"eyes", "40"}, {"skull", "35"}, {"flutter", "18"}},
233	"aurora_gaze":         {{"rainbow", "15"}, {"spark", "60"}, {"moon", "20"}},
234	"starlight_gleam":     {{"star", "25"}, {"clover", "20"}, {"heart", "80"}},
235	"double_haul":         {{"gear", "30"}, {"anchor", "20"}, {"up", "70"}},
236	"twin_strike":         {{"chains", "25"}, {"wrench", "25"}, {"pick", "20"}},
237	"executioner":         {{"skull", "60"}, {"sword", "18"}, {"crown", "10"}, {"orb", "8"}},
238	"celestial_sight":     {{"planet", "10"}, {"moon", "30"}, {"star", "30"}, {"bead", "8"}},
239	"helix_harmony":       {{"dna", "12"}, {"spark", "80"}, {"rainbow", "25"}, {"orb", "10"}},
240	"fortune_split":       {{"money-bag", "15"}, {"gold-medal", "12"}, {"clover", "35"}, {"chains", "40"}},
241	"critical_surge":      {{"gem", "15"}, {"disco", "14"}, {"ring", "12"}, {"orb", "20"}, {"planet", "18"}},
242	"astral_scintillation": {{"ring", "14"}, {"gem", "16"}, {"dna", "20"}, {"star", "60"}, {"moon", "50"}},
243	"plunder_frenzy":      {{"coin", "25"}, {"money-bag", "30"}, {"gem", "12"}, {"crown", "25"}, {"trophy", "25"}},
244	"singularity_edge":    {{"lotus", "6"}, {"dragon", "8"}, {"galaxy", "25"}, {"hamsa", "20"}, {"atom", "22"}, {"mirror", "20"}},
245	"cosmic_fortune":      {{"peacock", "10"}, {"unicorn", "8"}, {"comet", "25"}, {"disco", "30"}, {"galaxy", "30"}, {"gem", "35"}},
246	// POCKET branch
247	"dustbag":             {{"grin", "200"}, {"sleep", "40"}},
248	"rhythm":              {{"music", "16"}, {"clap", "30"}},
249	"pocket_expansion":    {{"fallen", "45"}, {"herb", "35"}},
250	"steady_tempo":        {{"party", "40"}, {"wow", "30"}},
251	"cadence":             {{"music", "30"}, {"coffee", "20"}, {"clap", "50"}},
252	"deep_pouch":          {{"log", "70"}, {"rock", "60"}, {"mushroom", "35"}},
253	"vein_resonator":      {{"flutter", "20"}, {"rainbow", "15"}, {"tada", "40"}},
254	"cart_bed":            {{"log", "120"}, {"rock", "100"}, {"brick", "80"}, {"money-bag", "10"}},
255	"bounty_overflow":     {{"trophy", "15"}, {"crown", "12"}, {"pick", "45"}, {"game", "35"}},
256	"fracture_pulse":      {{"bolt", "25"}, {"wand", "15"}, {"hammer", "50"}, {"rocket", "25"}},
257	"metro_pulse":         {{"music", "45"}, {"coffee", "35"}, {"raise", "30"}, {"bolt", "15"}},
258	"hoarders_tote":       {{"coin", "22"}, {"gem", "10"}, {"money-bag", "35"}, {"rock", "150"}, {"log", "150"}},
259	"shatter_strike":      {{"gem", "14"}, {"bolt", "35"}, {"wand", "25"}, {"sword", "30"}, {"hammer", "60"}},
260	"super_conductor":     {{"coin", "20"}, {"ring", "12"}, {"music", "60"}, {"bolt", "30"}, {"coffee", "50"}},
261	"dodo_instinct":       {{"dodo", "14"}, {"kite", "14"}, {"crown", "22"}, {"trophy", "22"}, {"eyes", "80"}},
262	"void_rend":           {{"infinity", "6"}, {"jelly", "12"}, {"dragon", "8"}, {"atom", "30"}, {"comet", "25"}, {"bolt", "60"}},
263	"celestial_duplication": {{"infinity", "5"}, {"lotus", "6"}, {"dragon", "8"}, {"peacock", "10"}, {"coin", "40"}, {"gem", "35"}},
264	"eternity_flow":       {{"infinity", "5"}, {"lotus", "6"}, {"peacock", "8"}, {"hamsa", "25"}, {"mirror", "25"}, {"music", "100"}},
265	"abyssal_reserve":     {{"unicorn", "6"}, {"jelly", "10"}, {"moai", "25"}, {"ufo", "25"}, {"money-bag", "50"}, {"rock", "300"}},
266	"singularity_snap":    {{"infinity", "8"}, {"lotus", "8"}, {"dragon", "10"}, {"galaxy", "40"}, {"moai", "30"}, {"bolt", "80"}},
267
268	// ── Phase 5: Nature branch (level 20) ────────────────────────────────────
269	"deep_root":       {{"bear", "60"}, {"fox", "40"}},
270	"stone_sense":     {{"turtle", "30"}, {"coral", "15"}},
271	"bloom_tide":      {{"sakura", "20"}, {"bee", "50"}, {"deciduous", "80"}},
272	"thorn_hide":      {{"boar", "40"}, {"snake", "30"}, {"scorpion", "20"}},
273	"old_growth":      {{"elephant", "25"}, {"gorilla", "20"}, {"whale-spout", "15"}},
274	"storm_surge":     {{"storm", "25"}, {"lightning-cloud", "20"}, {"tornado", "15"}},
275	"predator_sense":  {{"tiger", "20"}, {"lion", "20"}, {"wolf", "20"}, {"eagle", "15"}},
276	"world_tree":      {{"rex", "12"}, {"mammoth", "10"}, {"dragon-face", "8"}, {"shooting", "8"}},
277
278	// ── Phase 5: Food branch (level 30) ──────────────────────────────────────
279	"hot_meal":     {{"ramen", "50"}, {"stew", "40"}},
280	"sugar_rush":   {{"candy", "60"}, {"cupcake", "40"}, {"donut", "30"}},
281	"second_wind":  {{"burger", "50"}, {"bacon", "40"}, {"cheese", "30"}},
282	"caffeinated":  {{"boba", "40"}, {"tea", "30"}, {"sake", "25"}},
283	"feast_mode":   {{"sushi", "30"}, {"curry", "40"}, {"pasta", "40"}, {"paella", "25"}},
284	"night_shift":  {{"honey", "30"}, {"pie", "25"}, {"cake", "20"}},
285	"brew_mastery": {{"beer", "40"}, {"wine", "30"}, {"cocktail", "20"}, {"champagne", "15"}},
286	"mythic_feast": {{"tropical-drink", "20"}, {"cheers", "15"}, {"toast", "12"}, {"mate", "12"}},
287
288	// ── Phase 5: People branch (level 40) ────────────────────────────────────
289	"mentor":        {{"baby", "60"}, {"child", "50"}},
290	"crowd_favor":   {{"clap", "80"}, {"heart-hands", "60"}},
291	"team_spirit":   {{"shake", "40"}, {"pray", "30"}, {"hug-pair", "30"}},
292	"athlete_heart": {{"surf", "30"}, {"swim", "30"}, {"run", "40"}, {"lift", "25"}},
293	"warrior_code":  {{"ninja", "20"}, {"fencer", "15"}, {"juggle", "15"}},
294	"elder_wisdom":  {{"monarch", "15"}, {"mage", "15"}, {"fairy", "12"}},
295	"hero_legend":   {{"hero", "12"}, {"vampire", "10"}, {"merfolk", "10"}},
296	"genie_wish":    {{"genie", "8"}, {"heart-organ", "12"}},
297
298	// ── Phase 5: Things branch (level 50) ────────────────────────────────────
299	"tempered_grip":   {{"gloves", "80"}, {"hammer", "60"}},
300	"field_repair":    {{"hammer-wrench", "40"}, {"saw", "30"}, {"nut", "50"}},
301	"master_craft":    {{"wrench", "60"}, {"driver", "50"}, {"clamp", "30"}},
302	"workshop_lore":   {{"toolbox", "30"}, {"abacus", "25"}, {"scroll", "20"}},
303	"iron_discipline": {{"axe", "30"}, {"dagger", "20"}, {"bow", "15"}},
304	"vault_keeper":    {{"lock", "40"}, {"key-lock", "30"}, {"old-key", "15"}},
305	"precision_forge": {{"scope", "20"}, {"telescope", "15"}, {"tube", "18"}, {"dish", "15"}},
306	"deep_science":    {{"alembic", "12"}, {"shield", "15"}, {"hammer-pick", "15"}, {"scale", "12"}},
307
308	// ── Phase 5: Travel branch (level 65) ────────────────────────────────────
309	"long_haul":      {{"car", "80"}, {"bus", "60"}},
310	"vein_compass":   {{"plane", "50"}, {"sail", "30"}},
311	"nomad_pace":     {{"moto", "40"}, {"cycle", "50"}, {"scooter", "40"}},
312	"deep_sea":       {{"ship", "30"}, {"whale-spout", "15"}, {"dolphin", "25"}},
313	"sky_route":      {{"rocket", "30"}, {"heli", "20"}, {"depart", "20"}},
314	"monument_seal":  {{"tower", "20"}, {"castle", "15"}, {"liberty", "12"}},
315	"orbital_drift":  {{"sat", "15"}, {"castle-jp", "12"}},
316	"sacred_ground":  {{"kaaba", "8"}, {"shrine", "10"}, {"temple", "10"}, {"mosque", "10"}},
317
318	// ── Phase 5: Signs branch (level 80) ─────────────────────────────────────
319	"amplify":       {{"sparkle", "80"}, {"eight-star", "60"}},
320	"resonance":     {{"recycle", "60"}, {"medical", "50"}},
321	"sigil_power":   {{"worship", "40"}, {"peace-sign", "30"}},
322	"zodiac_edge":   {{"aries", "20"}, {"taurus", "20"}, {"gemini", "20"}, {"cancer", "15"}},
323	"star_david":    {{"star-david", "15"}, {"dharma", "15"}, {"cross", "15"}, {"menorah", "12"}},
324	"hazard_forge":  {{"radioactive", "12"}, {"biohazard", "12"}},
325	"cosmic_sigil":  {{"trident", "10"}, {"fleur", "10"}, {"ophiuchus", "8"}},
326	"infinite_mark": {{"om", "8"}, {"yin", "8"}, {"witness", "6"}},
327}
328
329// skillMaxRanks maps skillId → maximum rank allowed.
330// Most skills cap at 3 or 4 — see catalog.ts SKILLS[*].max.
331var skillMaxRanks = map[string]uint64{
332	// Strike branch
333	"wrist": 5, "palm_strike": 4, "follow": 3, "knuckle_wrap": 4, "heavy_swing": 4,
334	"stone_breaker": 4, "anvil_force": 4, "point_drive": 4, "kinetic_thrust": 4,
335	"crushing_blow": 4, "shatter_spike": 4, "ironclad_cleave": 3, "thunder_jab": 3,
336	"titan_hammer": 3, "diamond_cleave": 3, "hyper_drive": 3, "quasar_impact": 3,
337	"black_hole_crush": 3, "mythic_annihilation": 3, "singularity_fist": 3,
338	// Fortune branch
339	"fortune": 4, "four_leaf": 3, "edge": 3, "glint": 3, "spark_chaser": 3,
340	"razor_focus": 3, "precision_cut": 3, "aurora_gaze": 3, "starlight_gleam": 3,
341	"double_haul": 3, "twin_strike": 3, "executioner": 3, "celestial_sight": 3,
342	"helix_harmony": 3, "fortune_split": 3, "critical_surge": 3,
343	"astral_scintillation": 3, "plunder_frenzy": 3, "singularity_edge": 3, "cosmic_fortune": 3,
344	// Pocket branch
345	"dustbag": 3, "rhythm": 3, "pocket_expansion": 3, "steady_tempo": 3, "cadence": 3,
346	"deep_pouch": 3, "vein_resonator": 3, "cart_bed": 3, "bounty_overflow": 3,
347	"fracture_pulse": 3, "metro_pulse": 3, "hoarders_tote": 3, "shatter_strike": 3,
348	"super_conductor": 3, "dodo_instinct": 3, "void_rend": 3, "celestial_duplication": 3,
349	"eternity_flow": 3, "abyssal_reserve": 3, "singularity_snap": 3,
350	// Phase 5 — Nature branch (unlocks level 20)
351	"deep_root": 3, "stone_sense": 3, "bloom_tide": 3, "thorn_hide": 3,
352	"old_growth": 3, "storm_surge": 3, "predator_sense": 3, "world_tree": 3,
353	// Phase 5 — Food branch (unlocks level 30)
354	"hot_meal": 3, "sugar_rush": 3, "second_wind": 3, "caffeinated": 3,
355	"feast_mode": 3, "night_shift": 3, "brew_mastery": 3, "mythic_feast": 3,
356	// Phase 5 — People branch (unlocks level 40)
357	"mentor": 3, "crowd_favor": 3, "team_spirit": 3, "athlete_heart": 3,
358	"warrior_code": 3, "elder_wisdom": 3, "hero_legend": 3, "genie_wish": 3,
359	// Phase 5 — Things branch (unlocks level 50)
360	"tempered_grip": 3, "field_repair": 3, "master_craft": 3, "workshop_lore": 3,
361	"iron_discipline": 3, "vault_keeper": 3, "precision_forge": 3, "deep_science": 3,
362	// Phase 5 — Travel branch (unlocks level 65)
363	"long_haul": 3, "vein_compass": 3, "nomad_pace": 3, "deep_sea": 3,
364	"sky_route": 3, "monument_seal": 3, "orbital_drift": 3, "sacred_ground": 3,
365	// Phase 5 — Signs branch (unlocks level 80)
366	"amplify": 3, "resonance": 3, "sigil_power": 3, "zodiac_edge": 3,
367	"star_david": 3, "hazard_forge": 3, "cosmic_sigil": 3, "infinite_mark": 3,
368}
369
370// toolMaxLife maps toolId → maximum durability in blocks broken.
371// 0 = indestructible (hands, starter-twig, twig). Matches catalog.ts tool.life.
372var toolMaxLife = map[string]uint64{
373	"hands": 0, "starter-twig": 0, "twig": 0,
374	"wood": 300, "stone": 900, "iron": 940, "gold": 4900, "diamond": 5300, "cosmic": 4500,
375	"plasma": 15000, "neutron": 15000, "quasar": 23000, "void": 30000, "genesis": 36000, "omega": 54000,
376}
377
378// ── Coin allowlist and rarity lookup ─────────────────────────────────────────
379// coinRarity maps every valid coin id → its rarity (0=dust .. 6=mythic).
380// ClaimMine and ClaimAndMint panic on any id not present here.
381// Generated from src/lib/game/catalog.ts COINS (1262 entries).
382// Populated across two init() functions in game_data_coins.gno and game_data_coins2.gno
383// to keep each file under the 594-line Onyx parser limit.
384
385var coinRarity map[string]int