code wiki / _hdl_build / nx_game_taming.nx

nx_game_taming.nx source

↩ module page · 309 lines · 16492 B

1// nx_game_taming.nx -- THE MULTIPLE PATHS to winning a companion (ws=game-interact rung 4, operator: 2// "i like multiple ways of taming -- from dominating in fighting, to items like the pal balls, and also 3// taming from capture ... train them or release them"). 4// 5// FIVE paths, each a genuinely different skill, each able to FAIL: 6// 1 DOMINANCE -- beat her decisively in combat (nx_game_breed brd_duel: margin >= species submit_bar) 7// 2 ITEM -- a bind-charm; odds are the real HP-dependent catch curve (composed from 8// nx_creature_world's cw_catch_a -- the ODDS ORACLE, not a second copy) 9// 3 SEDUCTION -- a non-combat contest of approaches vs her ARCHETYPE; reading her wrong costs you 10// 4 TRAINING -- the slow path: move trust/willpower over many interactions until she stays willingly 11// 5 RELEASE -- let her go; it costs you her, moves your reputation, and she REMEMBERS 12// 13// ★THE ARCHITECTURAL LAW (the A2 reconciliation): every path makes its own DECISION, then funnels 14// through the ONE canonical transfer verb `brd_capture`, which preserves genome + bond state exactly. 15// There is exactly one way a creature changes hands, so the paths can never drift apart. 16// 17// ★BOND IS NOT OWNERSHIP: capturing her by force gives you a companion with LOW TRUST and HIGH WILL -- 18// she is yours and she will not obey. Trust is earned afterward (path 4) whatever path took her. That is 19// the depth: the fast paths cost you loyalty, the slow path costs you time. 20// All thresholds are DATA rows. All integer, deterministic, caller-seeded. LIB, no main. 21// license_tier: ORIGINAL 22import "nx_syscalls.nx" 23import "nx_game_breed.nx" 24import "nx_creature_world.nx" 25// WIRING, NOT NEW CODE (2026-07-31). This file calls gx_phenotype, gx_breed_modern, GX_HORN, GX_TAIL and 26// GX_REC -- all five already declared in nx_game_genetics.nx, none of them reachable without this line. 27// nx_cc FAILS OPEN on undefined identifiers (seq1012), so the build never said so; found by 28// `nx_undefscan scan nx_game_taming corpus`, which separates ELSEWHERE-IN (a missing import, one line) 29// from NOWHERE (unwritten code) instead of lumping both into one scary number. 30import "nx_game_genetics.nx" 31 32// ---- path ids ---- 33const TM_DOMINANCE: i64 = 0 34const TM_ITEM: i64 = 1 35const TM_SEDUCE: i64 = 2 36const TM_TRAIN: i64 = 3 37const TM_RELEASE: i64 = 4 38const TM_NPATH: i64 = 5 39 40// ---- archetype rows: DATA, stride 6. An archetype is HOW she must be read. 41// [best_approach, worst_approach, seduce_bar, item_rate, train_rate, pride] 42// best/worst are approach ids; seduce_bar = disposition needed; item_rate = her catch rate (Pokemon 43// scale 0..255); train_rate = permil trust gained per good session; pride = extra will regained on insult. 44const TM_ARCH_STRIDE: i64 = 6 45const TM_NARCH: i64 = 4 46const TM_ARCH_PROUD: i64 = 0 47const TM_ARCH_SHY: i64 = 1 48const TM_ARCH_WILD: i64 = 2 49const TM_ARCH_CLEVER: i64 = 3 50 51// ---- approaches (the seduction verbs) ---- 52const TM_AP_GIFT: i64 = 0 53const TM_AP_FLATTER: i64 = 1 54const TM_AP_IMPRESS: i64 = 2 55const TM_AP_CHALLENGE: i64 = 3 56const TM_NAP: i64 = 4 57 58func tm_arch_default(tbl: *i64) -> i64 { 59 // PROUD: respects a challenge, despises flattery. Hard to seduce, hard to catch, slow to trust. 60 tbl[0]=TM_AP_CHALLENGE; tbl[1]=TM_AP_FLATTER; tbl[2]=750; tbl[3]=60; tbl[4]=40; tbl[5]=120 61 // SHY: opens to gifts, terrified by a challenge. Easy to catch, quick to bond once safe. 62 tbl[TM_ARCH_STRIDE+0]=TM_AP_GIFT; tbl[TM_ARCH_STRIDE+1]=TM_AP_CHALLENGE; tbl[TM_ARCH_STRIDE+2]=550; tbl[TM_ARCH_STRIDE+3]=170; tbl[TM_ARCH_STRIDE+4]=90; tbl[TM_ARCH_STRIDE+5]=40 63 // WILD: impressed by strength, unmoved by words. Middling catch, stubborn will. 64 tbl[2*TM_ARCH_STRIDE+0]=TM_AP_IMPRESS; tbl[2*TM_ARCH_STRIDE+1]=TM_AP_GIFT; tbl[2*TM_ARCH_STRIDE+2]=700; tbl[2*TM_ARCH_STRIDE+3]=110; tbl[2*TM_ARCH_STRIDE+4]=55; tbl[2*TM_ARCH_STRIDE+5]=90 65 // CLEVER: enjoys flattery she can see through, bored by brute display. 66 tbl[3*TM_ARCH_STRIDE+0]=TM_AP_FLATTER; tbl[3*TM_ARCH_STRIDE+1]=TM_AP_IMPRESS; tbl[3*TM_ARCH_STRIDE+2]=650; tbl[3*TM_ARCH_STRIDE+3]=90; tbl[3*TM_ARCH_STRIDE+4]=70; tbl[3*TM_ARCH_STRIDE+5]=70 67 return TM_NARCH 68} 69func tm_best(tbl: *i64, a: i64) -> i64 { return tbl[a*TM_ARCH_STRIDE + 0] } 70func tm_worst(tbl: *i64, a: i64) -> i64 { return tbl[a*TM_ARCH_STRIDE + 1] } 71func tm_seduce_bar(tbl: *i64, a: i64) -> i64{ return tbl[a*TM_ARCH_STRIDE + 2] } 72func tm_item_rate(tbl: *i64, a: i64) -> i64 { return tbl[a*TM_ARCH_STRIDE + 3] } 73func tm_train_rate(tbl: *i64, a: i64) -> i64{ return tbl[a*TM_ARCH_STRIDE + 4] } 74func tm_pride(tbl: *i64, a: i64) -> i64 { return tbl[a*TM_ARCH_STRIDE + 5] } 75 76// her archetype is DERIVED from her genome+species so it is stable and discoverable, never re-rolled 77func tm_archetype(ros: *i64, h: i64) -> i64 { 78 let g: i64 = en_get(ros, h, C_GENO) 79 let sp: i64 = en_get(ros, h, C_SPEC) 80 var a: i64 = (sp + gx_phenotype(g, GX_HORN) * 2 + gx_phenotype(g, GX_TAIL)) % TM_NARCH 81 if a < 0 { a = 0 - a } 82 return a 83} 84 85func tm_rng(s: *i64) -> i64 { 86 var x: i64 = s[0] 87 x = x ^ (x << 13); x = x ^ (x >> 7); x = x ^ (x << 17) 88 s[0] = x 89 if x < 0 { return 0 - x } 90 return x 91} 92func tm_clamp(v: i64, lo: i64, hi: i64) -> i64 { 93 if v < lo { return lo } 94 if v > hi { return hi } 95 return v 96} 97 98// ---- outcome codes (shared by every path so the caller has ONE switch) ---- 99const TM_OK: i64 = 1 // she is yours 100const TM_FAIL: i64 = 0 // attempt failed, she is still wild 101const TM_RESIST: i64 = 2 // failed AND she hardened (will/guard up) 102const TM_REFUSED: i64 = 3 // the attempt was not legal (bad handle, roster full, no item) 103 104// ===================================================================================================== 105// PATH 2 -- ITEM CAPTURE. Odds are NOT invented here: cw_catch_a is the real HP-dependent catch curve 106// (3*maxHP - 2*curHP) * rate * ball / (3*maxHP), from nx_creature_world. We set her catch rate from her 107// ARCHETYPE and let the proven oracle decide. Weakening her first is what makes it work -- that is the 108// whole reason the combat and capture systems are one loop and not two. 109// ===================================================================================================== 110func tm_item_try(ros: *i64, h: i64, tbl: *i64, c: *i64, ball: i64, s: *i64) -> i64 { 111 if en_valid(ros, h) == 0 { return TM_REFUSED } 112 if ball <= 0 { return TM_REFUSED } 113 cw_set_catch(c, tm_item_rate(tbl, tm_archetype(ros, h))) 114 let cbs: *i64 = sys_mmap(16) as *i64 115 cbs[0] = tm_rng(s) 116 if cw_try_catch(c, ball, cbs) == 1 { return TM_OK } 117 // a failed charm teaches her the shape of the trap: her guard rises, next throw is harder 118 en_set(ros, h, C_WILL, tm_clamp(en_get(ros, h, C_WILL) + 40, 0, 1000)) 119 en_set(ros, h, C_DISP, tm_clamp(en_get(ros, h, C_DISP) - 25, 0, 1000)) 120 return TM_RESIST 121} 122// the odds an item throw succeeds right now, in permil -- the UI shows this BEFORE you spend the charm 123func tm_item_odds_permil(ros: *i64, h: i64, tbl: *i64, c: *i64, ball: i64) -> i64 { 124 if en_valid(ros, h) == 0 { return 0 } 125 cw_set_catch(c, tm_item_rate(tbl, tm_archetype(ros, h))) 126 var a: i64 = cw_catch_a(c, ball) 127 if a > 255 { a = 255 } 128 if a < 0 { a = 0 } 129 return a * 1000 / 256 130} 131 132// ===================================================================================================== 133// PATH 3 -- SEDUCTION. A contest, not a purchase. Each turn you pick an approach; her ARCHETYPE decides 134// what it is worth. Her best approach moves disposition hard, her worst moves it BACKWARD and stiffens 135// her will. You must read her -- and reading her is possible, because archetype is derived from visible 136// traits (horns/tail), so an observant player can infer it. Reaching her seduce_bar = she comes willingly, 137// which is why this path yields the HIGHEST starting trust of any capture route. 138// ===================================================================================================== 139func tm_seduce_step(ros: *i64, h: i64, tbl: *i64, approach: i64, charisma: i64, s: *i64) -> i64 { 140 if en_valid(ros, h) == 0 { return TM_REFUSED } 141 let a: i64 = tm_archetype(ros, h) 142 var delta: i64 = 20 + charisma / 20 143 if approach == tm_best(tbl, a) { delta = 90 + charisma / 8 } 144 if approach == tm_worst(tbl, a) { delta = 0 - (60 + tm_pride(tbl, a) / 2) } 145 // her willpower blunts everything; a strong-willed woman is not swayed by a good line 146 let resist: i64 = en_get(ros, h, C_WILL) / 25 147 if delta > 0 { delta = delta - resist } 148 if delta < 0 { delta = delta - 0 } 149 // a little variance so it is never a solved sequence, but never enough to overturn a right read 150 delta = delta + (tm_rng(s) % 21) - 10 151 let nd: i64 = tm_clamp(en_get(ros, h, C_DISP) + delta, 0, 1000) 152 en_set(ros, h, C_DISP, nd) 153 if delta < 0 { en_set(ros, h, C_WILL, tm_clamp(en_get(ros, h, C_WILL) + tm_pride(tbl, a) / 3, 0, 1000)) } 154 if nd >= tm_seduce_bar(tbl, a) { return TM_OK } 155 if delta < 0 { return TM_RESIST } 156 return TM_FAIL 157} 158 159// ===================================================================================================== 160// PATH 4 -- TRAINING. The slow path, and the one that actually makes her YOURS. Activities move trust and 161// willpower; what you do matters more than how often. A companion taken by force starts here at zero. 162// ===================================================================================================== 163const TM_ACT_FEED: i64 = 0 164const TM_ACT_SPAR: i64 = 1 165const TM_ACT_WORK: i64 = 2 166const TM_ACT_PRAISE: i64 = 3 167const TM_ACT_REST: i64 = 4 168const TM_NACT: i64 = 5 169// activity rows: DATA, stride 2 = [trust_delta_scaled, will_delta] 170func tm_act_default(at: *i64) -> i64 { 171 at[0]=10; at[1]=0-6 // FEED : steady trust, softens will 172 at[2]=8; at[3]=0-10 // SPAR : bonds through shared effort, tires resistance 173 at[4]=4; at[5]=6 // WORK : useful but she resents being used 174 at[6]=12; at[7]=0-4 // PRAISE : strongest single trust move 175 at[8]=3; at[9]=0-14 // REST : little trust, big will relief 176 return TM_NACT 177} 178func tm_train(ros: *i64, h: i64, tbl: *i64, at: *i64, activity: i64, s: *i64) -> i64 { 179 if en_valid(ros, h) == 0 { return TM_REFUSED } 180 if activity < 0 { return TM_REFUSED } 181 if activity >= TM_NACT { return TM_REFUSED } 182 let a: i64 = tm_archetype(ros, h) 183 let rate: i64 = tm_train_rate(tbl, a) 184 var dt: i64 = at[activity*2 + 0] * rate / 50 185 // trust earned while she is still fighting you is worth less -- willpower is the brake 186 dt = dt - en_get(ros, h, C_WILL) / 120 187 if dt < 0 { dt = 0 } 188 dt = dt + (tm_rng(s) % 5) 189 en_set(ros, h, C_TRUST, tm_clamp(en_get(ros, h, C_TRUST) + dt, 0, 1000)) 190 en_set(ros, h, C_WILL, tm_clamp(en_get(ros, h, C_WILL) + at[activity*2 + 1], 0, 1000)) 191 return TM_OK 192} 193// ★OBEDIENCE: whether she actually does what you ask. Trust vs will, with a floor of pure chance so a 194// freshly-forced companion is genuinely unreliable. THIS is what makes a fast capture cost something. 195func tm_obeys(ros: *i64, h: i64, s: *i64) -> i64 { 196 if en_valid(ros, h) == 0 { return 0 } 197 let t: i64 = en_get(ros, h, C_TRUST) 198 let w: i64 = en_get(ros, h, C_WILL) 199 var chance: i64 = 500 + (t - w) / 2 200 chance = tm_clamp(chance, 50, 990) 201 if (tm_rng(s) % 1000) < chance { return 1 } 202 return 0 203} 204// she will breed / fight willingly only past a real bond 205const TM_WILLING_BAR: i64 = 600 206func tm_willing(ros: *i64, h: i64) -> i64 { 207 if en_valid(ros, h) == 0 { return 0 } 208 if en_get(ros, h, C_TRUST) < TM_WILLING_BAR { return 0 } 209 return 1 210} 211// escape risk: a high-will, low-trust captive can leave. Consequence for taking the fast path. 212func tm_escape_check(ros: *i64, h: i64, s: *i64) -> i64 { 213 if en_valid(ros, h) == 0 { return 0 } 214 let risk: i64 = (en_get(ros, h, C_WILL) - en_get(ros, h, C_TRUST)) / 3 215 if risk <= 0 { return 0 } 216 if (tm_rng(s) % 1000) < risk { return 1 } 217 return 0 218} 219 220// ===================================================================================================== 221// PATH 5 -- RELEASE. Letting her go is a real move with real consequences, not a delete key. 222// The released registry remembers her genome + how she was treated, so the world has memory. 223// registry: [0]=count then rows stride 4 = [genome_vec, trust_at_release, spec, returned] 224// ===================================================================================================== 225const TM_REL_STRIDE: i64 = 4 226func tm_rel_count(reg: *i64) -> i64 { return reg[0] } 227func tm_release(ros: *i64, h: i64, reg: *i64, regcap: i64, repbox: *i64) -> i64 { 228 if en_valid(ros, h) == 0 { return TM_REFUSED } 229 let n: i64 = reg[0] 230 if n >= regcap { return TM_REFUSED } 231 let trust: i64 = en_get(ros, h, C_TRUST) 232 reg[1 + n*TM_REL_STRIDE + 0] = comp_vec(ros, h) 233 reg[1 + n*TM_REL_STRIDE + 1] = trust 234 reg[1 + n*TM_REL_STRIDE + 2] = en_get(ros, h, C_SPEC) 235 reg[1 + n*TM_REL_STRIDE + 3] = 0 236 reg[0] = n + 1 237 // releasing a companion who TRUSTED you costs reputation; freeing one you never tamed earns a little 238 if trust >= TM_WILLING_BAR { repbox[0] = repbox[0] - 30 } 239 if trust < TM_WILLING_BAR { repbox[0] = repbox[0] + 10 } 240 en_destroy(ros, h) 241 return TM_OK 242} 243// a released companion may find you again -- and she remembers how it went. Returns her registry index 244// or -1. A well-treated release comes back MORE disposed to you; a betrayal comes back hostile. 245func tm_maybe_return(reg: *i64, s: *i64) -> i64 { 246 let n: i64 = reg[0] 247 if n <= 0 { return 0-1 } 248 var i: i64 = 0 249 while i < n { 250 if reg[1 + i*TM_REL_STRIDE + 3] == 0 { 251 let trust: i64 = reg[1 + i*TM_REL_STRIDE + 1] 252 var chance: i64 = 60 + trust / 8 253 if (tm_rng(s) % 1000) < chance { 254 reg[1 + i*TM_REL_STRIDE + 3] = 1 255 return i 256 } 257 } 258 i = i + 1 259 } 260 return 0-1 261} 262// re-admit a returning companion: her memory of you sets where the relationship restarts 263func tm_readmit(ros: *i64, reg: *i64, idx: i64) -> i64 { 264 if idx < 0 { return 0-1 } 265 if idx >= reg[0] { return 0-1 } 266 let h: i64 = en_spawn(ros) 267 if h == EN_NULL { return 0-1 } 268 let vec: i64 = reg[1 + idx*TM_REL_STRIDE + 0] 269 let trust: i64 = reg[1 + idx*TM_REL_STRIDE + 1] 270 // unpack the identity vector (((sp*32+iv0)*32+iv1)*32+iv2)*32+iv3 271 en_set(ros, h, C_IV3, vec % 32) 272 en_set(ros, h, C_IV2, (vec / 32) % 32) 273 en_set(ros, h, C_IV1, (vec / 1024) % 32) 274 en_set(ros, h, C_IV0, (vec / 32768) % 32) 275 en_set(ros, h, C_SPEC, reg[1 + idx*TM_REL_STRIDE + 2]) 276 en_set(ros, h, C_XP, 0); en_set(ros, h, C_LVL, 1) 277 en_set(ros, h, C_NICK, en_count(ros) - 1) 278 en_set(ros, h, C_TRUST, tm_clamp(trust / 2, 0, 1000)) 279 en_set(ros, h, C_WILL, 300) 280 en_set(ros, h, C_DISP, tm_clamp(300 + trust / 2, 0, 1000)) 281 return h 282} 283 284// ===================================================================================================== 285// THE UNIFIED ENTRY: whatever path decided, the transfer is ALWAYS brd_capture, and the path sets the 286// starting bond. Forced paths hand you a resentful companion; a willing path hands you a partner. 287// ===================================================================================================== 288func tm_acquire(dst: *i64, src: *i64, h: i64, path: i64) -> i64 { 289 if en_valid(src, h) == 0 { return 0-1 } 290 // set the bond BEFORE the transfer so brd_capture carries it across verbatim 291 if path == TM_DOMINANCE { en_set(src, h, C_TRUST, 100); en_set(src, h, C_WILL, 700) } 292 if path == TM_ITEM { en_set(src, h, C_TRUST, 150); en_set(src, h, C_WILL, 600) } 293 if path == TM_SEDUCE { en_set(src, h, C_TRUST, 550); en_set(src, h, C_WILL, 250) } 294 if path == TM_TRAIN { en_set(src, h, C_TRUST, 650); en_set(src, h, C_WILL, 200) } 295 return brd_capture(dst, src, h) 296} 297func tm_path_name(p: i64) -> *u8 { 298 if p == TM_DOMINANCE { return "dominance" as *u8 } 299 if p == TM_ITEM { return "bind-charm" as *u8 } 300 if p == TM_SEDUCE { return "seduction" as *u8 } 301 if p == TM_TRAIN { return "training" as *u8 } 302 return "release" 303} 304func tm_arch_name(a: i64) -> *u8 { 305 if a == TM_ARCH_PROUD { return "proud" as *u8 } 306 if a == TM_ARCH_SHY { return "shy" as *u8 } 307 if a == TM_ARCH_WILD { return "wild" as *u8 } 308 return "clever" 309}