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