code wiki / _hdl_build / nx_game_companion.nx
nx_game_companion.nx source
↩ module page · 137 lines · 5319 B
1// nx_game_companion.nx -- the OWNERSHIP core of the beat-Veloren companion-collector. The research (SDT +
2// operator's pillars, gamebench round 14) says Veloren wins on AUTONOMY but has no OWNERSHIP; the
3// endowment effect (Pokemon) is the pillar we can build because our certified parts already hold its
4// substrate. This part composes them into a companion whose OWNERSHIP is STRUCTURALLY REAL and MEASURABLE:
5// - UNIQUE by construction (a per-companion rolled trait vector; no two alike -- the IV/nature equiv),
6// - PERSISTENT (rides nx_gamesave bit-exact -- what you own does not evaporate),
7// - INVESTMENT-DIVERGENT (nx_rpgstats: what you pour XP into grows distinct -- the IKEA effect),
8// - individually addressed (nx_entity_store handle=index+generation; a companion is a THING, not a row).
9// ⚠HONEST SCOPE: these are the measurable PREREQUISITES for the endowment effect, NOT the psychological
10// bond itself (unmeasurable). A game can have all three and still feel hollow; but a game LACKING them
11// cannot produce ownership at all -- so they are necessary, gate-able, and red-teamable.
12// LIB, no main. license_tier: ORIGINAL
13import "nx_syscalls.nx"
14import "nx_entity_store.nx"
15import "nx_rpgstats.nx"
16import "nx_gamesave.nx"
17const COMP_MAGIC_2654435761: i64 = 2654435761
18const COMP_MAGIC_40503: i64 = 40503
19
20const COMP_NC: i64 = 8 // components
21const C_SPEC: i64 = 0
22const C_IV0: i64 = 1
23const C_IV1: i64 = 2
24const C_IV2: i64 = 3
25const C_IV3: i64 = 4
26const C_XP: i64 = 5
27const C_LVL: i64 = 6
28const C_NICK: i64 = 7
29const COMP_NSPEC: i64 = 6
30const COMP_XB: i64 = 20
31const COMP_XQ: i64 = 10
32const COMP_HPB: i64 = 10
33const COMP_HPC: i64 = 2
34const COMP_HPL: i64 = 4
35
36func comp_rng(s: i64) -> i64 {
37 var x: i64 = s
38 x = x ^ (x << 13); x = x ^ (x >> 7); x = x ^ (x << 17)
39 if x < 0 { return 0 - x }
40 return x
41}
42
43// roll a roster of n UNIQUE companions from a world seed. `variety`=1 gives per-companion distinct seeds
44// (real collection); `variety`=0 is the SOULLESS control -- every companion rolled from the SAME seed
45// (generic mass-produced loot), which the uniqueness gate MUST reject.
46func comp_new_roster(ros: *i64, cap: i64, n: i64, seed: i64, variety: i64) -> i64 {
47 en_init(ros, cap, COMP_NC)
48 var i: i64 = 0
49 while i < n {
50 let h: i64 = en_spawn(ros)
51 if h != EN_NULL {
52 var per: i64 = seed
53 if variety == 1 { per = seed*COMP_MAGIC_2654435761 + i*COMP_MAGIC_40503 + 1 }
54 var x: i64 = comp_rng(per)
55 en_set(ros, h, C_SPEC, x % COMP_NSPEC)
56 x = comp_rng(x); en_set(ros, h, C_IV0, x % 32)
57 x = comp_rng(x); en_set(ros, h, C_IV1, x % 32)
58 x = comp_rng(x); en_set(ros, h, C_IV2, x % 32)
59 x = comp_rng(x); en_set(ros, h, C_IV3, x % 32)
60 en_set(ros, h, C_XP, 0)
61 en_set(ros, h, C_LVL, 1)
62 en_set(ros, h, C_NICK, i)
63 }
64 i = i + 1
65 }
66 return 0
67}
68
69// the identity vector = species + 4 IVs packed; two companions are "the same creature" iff equal.
70func comp_vec(ros: *i64, h: i64) -> i64 {
71 let sp: i64 = en_get(ros, h, C_SPEC)
72 let a: i64 = en_get(ros, h, C_IV0)
73 let b: i64 = en_get(ros, h, C_IV1)
74 let c: i64 = en_get(ros, h, C_IV2)
75 let d: i64 = en_get(ros, h, C_IV3)
76 return ((((sp*32 + a)*32 + b)*32 + c)*32 + d)
77}
78// count duplicate identity vectors in the roster (0 = every companion is unique)
79func comp_dup_count(ros: *i64) -> i64 {
80 let n: i64 = en_count(ros)
81 var dups: i64 = 0
82 var i: i64 = 0
83 while i < n {
84 let hi: i64 = en_nth(ros, i)
85 let vi: i64 = comp_vec(ros, hi)
86 var j: i64 = i + 1
87 while j < n {
88 let hj: i64 = en_nth(ros, j)
89 if comp_vec(ros, hj) == vi { dups = dups + 1 }
90 j = j + 1
91 }
92 i = i + 1
93 }
94 return dups
95}
96
97// INVEST: pour XP into a companion -> it levels (rpgstats) -> its power grows. What you invest in
98// becomes distinctly yours (the IKEA effect).
99func comp_invest(ros: *i64, h: i64, xp: i64) -> i64 {
100 let nx: i64 = en_get(ros, h, C_XP) + xp
101 en_set(ros, h, C_XP, nx)
102 en_set(ros, h, C_LVL, rs_level_for_xp(nx, COMP_XB, COMP_XQ))
103 return 0
104}
105// derived power: IV-sum acts as the "con" stat, scaled by invested level (rpgstats derived HP curve).
106func comp_power(ros: *i64, h: i64) -> i64 {
107 let con: i64 = 8 + en_get(ros, h, C_IV0) + en_get(ros, h, C_IV3)
108 return rs_derived_hp(con, en_get(ros, h, C_LVL), COMP_HPB, COMP_HPC, COMP_HPL)
109}
110
111// flatten the roster to a state vector for nx_gamesave (species..nick per companion, dense order)
112func comp_serialize(ros: *i64, S: *i64) -> i64 {
113 let n: i64 = en_count(ros)
114 S[0] = n
115 var i: i64 = 0
116 while i < n {
117 let h: i64 = en_nth(ros, i)
118 let b: i64 = 1 + i*COMP_NC
119 var k: i64 = 0
120 while k < COMP_NC { S[b+k] = en_get(ros, h, k); k = k + 1 }
121 i = i + 1
122 }
123 return 1 + n*COMP_NC
124}
125func comp_restore(ros: *i64, cap: i64, S: *i64) -> i64 {
126 en_init(ros, cap, COMP_NC)
127 let n: i64 = S[0]
128 var i: i64 = 0
129 while i < n {
130 let h: i64 = en_spawn(ros)
131 let b: i64 = 1 + i*COMP_NC
132 var k: i64 = 0
133 while k < COMP_NC { en_set(ros, h, k, S[b+k]); k = k + 1 }
134 i = i + 1
135 }
136 return 0
137}