nx_wasm_pong.nx source
↩ module page · 103 lines · 4933 B
1// nx_wasm_pong.nx -- BROWSER-PLAYABLE sovereign Pong (Sport): BASE-RELATIVE integer core, runs native (-> PNG)
2// AND in the browser (base = 0). NO float, NO JS logic, NO canvas drawing. You are the LEFT paddle (up/down);
3// a beatable AI controls the right. The ball bounces off walls + paddles with SPIN (where it hits the paddle
4// sets its vertical speed -> skill), and the AI tracks slower than max spin so you can score. Endless, scores
5// climb. init()/tick(input)/render()/score()/ww()/hh()/fb_off() = the wasm interface. license_tier: ORIGINAL
6import "nx_nishi_font_core.nx"
7const W: i64 = 200
8const H: i64 = 120
9const PADH: i64 = 16 // paddle half-height
10const LX: i64 = 8
11const RX: i64 = 192
12const O_FB: i64 = 0 // framebuffer W*H i64
13const O_ST: i64 = 192000 // scalars[16]: 0=bx 1=by 2=bvx 3=bvy 4=lpy 5=rpy 6=lscore 7=rscore
14const O_MSK: i64 = 192128 // HUD glyph scratch
15const O_STR: i64 = 200320 // HUD string scratch (left score @ +0, right score @ +32)
16
17func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) }
18func clampi(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return lo } if v > hi { return hi } return v }
19
20func fillr(base: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 {
21 let fb: *i64 = (base + O_FB) as *i64
22 var yy: i64 = y0; if yy < 0 { yy = 0 }
23 var ye: i64 = y1; if ye > H { ye = H }
24 while yy < ye {
25 var xx: i64 = x0; if xx < 0 { xx = 0 }
26 var xe: i64 = x1; if xe > W { xe = W }
27 while xx < xe { fb[yy*W + xx] = c; xx = xx + 1 }
28 yy = yy + 1
29 }
30 return 0
31}
32
33func serve(base: i64, dir: i64) -> i64 {
34 let st: *i64 = (base + O_ST) as *i64
35 st[0] = W/2; st[1] = H/2; st[2] = dir*3; st[3] = 2
36 return 0
37}
38func init_impl(base: i64) -> i64 {
39 let st: *i64 = (base + O_ST) as *i64
40 st[4] = H/2; st[5] = H/2; st[6] = 0; st[7] = 0
41 serve(base, 1)
42 return 0
43}
44
45func tick_impl(base: i64, input: i64) -> i64 {
46 let st: *i64 = (base + O_ST) as *i64
47 if (input & 1) != 0 { st[4] = clampi(st[4] - 4, PADH, H - PADH) }
48 if (input & 2) != 0 { st[4] = clampi(st[4] + 4, PADH, H - PADH) }
49 // AI right paddle tracks the ball at speed 2 (slower than max spin -> beatable)
50 if st[5] < st[1] { st[5] = clampi(st[5] + 2, PADH, H - PADH) }
51 if st[5] > st[1] { st[5] = clampi(st[5] - 2, PADH, H - PADH) }
52 st[0] = st[0] + st[2]; st[1] = st[1] + st[3]
53 if st[1] <= 2 { st[1] = 2; st[3] = 0 - st[3] }
54 if st[1] >= H - 2 { st[1] = H - 2; st[3] = 0 - st[3] }
55 if st[2] < 0 { if st[0] <= LX + 3 { var d: i64 = st[1] - st[4]; if d < 0 { d = 0 - d }
56 if d < PADH + 3 { st[2] = 3; st[3] = clampi((st[1] - st[4]) / 4, 0 - 3, 3); st[0] = LX + 3 } } }
57 if st[2] > 0 { if st[0] >= RX - 3 { var d: i64 = st[1] - st[5]; if d < 0 { d = 0 - d }
58 if d < PADH + 3 { st[2] = 0 - 3; st[3] = clampi((st[1] - st[5]) / 4, 0 - 3, 3); st[0] = RX - 3 } } }
59 if st[0] < 0 { st[7] = st[7] + 1; serve(base, 1) } // AI scores
60 if st[0] > W { st[6] = st[6] + 1; serve(base, 0 - 1) } // player scores
61 return 0
62}
63
64func draw_num(base: i64, x: i64, off: i64, val: i64) -> i64 {
65 let fb: *i64 = (base + O_FB) as *i64
66 let sbuf: *u8 = (base + O_STR + off) as *u8
67 var slen: i64 = 0
68 var sv: i64 = val
69 if sv <= 0 { sbuf[0] = 48 as u8; slen = 1 } else {
70 let tmp: *u8 = (base + O_STR + off + 12) as *u8
71 var k: i64 = 0
72 while sv > 0 { tmp[k] = (48 + (sv % 10)) as u8; sv = sv / 10; k = k + 1 }
73 var q: i64 = 0
74 while q < k { sbuf[q] = tmp[k - 1 - q]; q = q + 1 }
75 slen = k
76 }
77 let mask: *u8 = (base + O_MSK) as *u8
78 nf_draw_text_mem(fb, W, H, mask, x, 4, 16, sbuf, slen, rgb(235, 240, 250))
79 return 0
80}
81
82func render_impl(base: i64) -> i64 {
83 let fb: *i64 = (base + O_FB) as *i64
84 let st: *i64 = (base + O_ST) as *i64
85 var p: i64 = 0
86 while p < W*H { fb[p] = rgb(12, 16, 22); p = p + 1 }
87 var y: i64 = 4
88 while y < H { fillr(base, W/2-1, y, W/2+1, y+6, rgb(60,70,84)); y = y + 12 } // center dashes
89 fillr(base, LX-3, st[4]-PADH, LX+3, st[4]+PADH, rgb(90,210,122)) // left paddle (player)
90 fillr(base, RX-3, st[5]-PADH, RX+3, st[5]+PADH, rgb(224,90,80)) // right paddle (AI)
91 fillr(base, st[0]-2, st[1]-2, st[0]+3, st[1]+3, rgb(240,240,240)) // ball
92 draw_num(base, W/2 - 24, 0, st[6]) // player score (left)
93 draw_num(base, W/2 + 12, 32, st[7]) // AI score (right)
94 return 0
95}
96
97func init() -> i64 { return init_impl(0) }
98func tick(input: i64) -> i64 { return tick_impl(0, input) }
99func render() -> i64 { return render_impl(0) }
100func score() -> i64 { let st: *i64 = (0 + O_ST) as *i64; return st[6] }
101func fb_off() -> i64 { return O_FB }
102func ww() -> i64 { return W }
103func hh() -> i64 { return H }