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}