nx_wasm_td.nx source
↩ module page · 173 lines · 7261 B
1// nx_wasm_td.nx -- BROWSER-PLAYABLE sovereign Tower-Defence: BASE-RELATIVE integer core, runs native (-> PNG)
2// AND in the browser (base = 0). NO float, NO JS logic, NO canvas drawing. CLICK to place a tower (costs gold)
3// near the lane; towers auto-fire at creeps in range; creeps walk the lane to your base (lose a life if they
4// reach it); kills pay gold; escalating waves. Adds a click(x,y) export (the shim maps a canvas click to game
5// coords). init()/tick(input)/render()/score()/ww()/hh()/fb_off()/click(x,y) = the wasm interface. licen_tier: ORIGINAL
6import "nx_nishi_font_core.nx"
7const W: i64 = 220
8const H: i64 = 140
9const NC: i64 = 6
10const PY: i64 = 78 // lane center y
11const RANGE: i64 = 34
12const DMG: i64 = 3
13const FIRE_CD: i64 = 10
14const SPEED: i64 = 1
15const COST: i64 = 10
16const REWARD: i64 = 4
17const MAXT: i64 = 14
18const O_FB: i64 = 0 // framebuffer W*H i64
19const O_CX: i64 = 246400 // creep x[16] (W*H*8)
20const O_CHP: i64 = 246528
21const O_CA: i64 = 246656 // creep on-field[16]
22const O_TX: i64 = 246784 // tower x[16]
23const O_TY: i64 = 246912
24const O_TC: i64 = 247040 // tower cooldown[16]
25const O_ST: i64 = 247168 // scalars[16]: 0=gold 1=lives 2=wave 3=ntowers 4=tick 5=gameover
26const O_MSK: i64 = 247296
27const O_STR: i64 = 255488
28
29func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) }
30func iabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
31
32func fillr(base: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 {
33 let fb: *i64 = (base + O_FB) as *i64
34 var yy: i64 = y0; if yy < 0 { yy = 0 }
35 var ye: i64 = y1; if ye > H { ye = H }
36 while yy < ye {
37 var xx: i64 = x0; if xx < 0 { xx = 0 }
38 var xe: i64 = x1; if xe > W { xe = W }
39 while xx < xe { fb[yy*W + xx] = c; xx = xx + 1 }
40 yy = yy + 1
41 }
42 return 0
43}
44func disc(base: i64, cx: i64, cy: i64, rad: i64, c: i64) -> i64 {
45 let fb: *i64 = (base + O_FB) as *i64
46 var dy: i64 = 0 - rad; let r2: i64 = rad*rad
47 while dy <= rad {
48 var dx: i64 = 0 - rad
49 while dx <= rad {
50 if dx*dx + dy*dy <= r2 { let x: i64 = cx+dx; let y: i64 = cy+dy
51 if x >= 0 { if y >= 0 { if x < W { if y < H { fb[y*W + x] = c } } } } }
52 dx = dx + 1
53 }
54 dy = dy + 1
55 }
56 return 0
57}
58
59func spawn_wave(base: i64) -> i64 {
60 let cx: *i64 = (base + O_CX) as *i64
61 let chp: *i64 = (base + O_CHP) as *i64
62 let ca: *i64 = (base + O_CA) as *i64
63 let st: *i64 = (base + O_ST) as *i64
64 var i: i64 = 0
65 while i < NC { cx[i] = 0 - (i*30 + 10); chp[i] = 5 + st[2]*2; ca[i] = 1; i = i + 1 }
66 return 0
67}
68func init_impl(base: i64) -> i64 {
69 let st: *i64 = (base + O_ST) as *i64
70 st[0] = 40; st[1] = 10; st[2] = 1; st[3] = 0; st[4] = 0; st[5] = 0
71 spawn_wave(base)
72 return 0
73}
74
75func click_impl(base: i64, x: i64, y: i64) -> i64 {
76 let tx: *i64 = (base + O_TX) as *i64
77 let ty: *i64 = (base + O_TY) as *i64
78 let tc: *i64 = (base + O_TC) as *i64
79 let st: *i64 = (base + O_ST) as *i64
80 if st[5] == 1 { init_impl(base); return 0 } // click restarts after game over
81 if iabs(y - PY) <= 11 { return 0 } // can't build on the lane
82 if st[3] >= MAXT { return 0 }
83 if st[0] < COST { return 0 }
84 var t: i64 = 0
85 while t < st[3] { if iabs(tx[t]-x) < 16 { if iabs(ty[t]-y) < 16 { return 0 } } t = t + 1 } // occupied
86 tx[st[3]] = x; ty[st[3]] = y; tc[st[3]] = 0; st[3] = st[3] + 1; st[0] = st[0] - COST
87 return 0
88}
89
90func tick_impl(base: i64, input: i64) -> i64 {
91 let cx: *i64 = (base + O_CX) as *i64
92 let chp: *i64 = (base + O_CHP) as *i64
93 let ca: *i64 = (base + O_CA) as *i64
94 let tx: *i64 = (base + O_TX) as *i64
95 let ty: *i64 = (base + O_TY) as *i64
96 let tc: *i64 = (base + O_TC) as *i64
97 let st: *i64 = (base + O_ST) as *i64
98 if st[5] == 1 { return 0 }
99 var t: i64 = 0
100 while t < st[3] {
101 if tc[t] > 0 { tc[t] = tc[t] - 1 } else {
102 var i: i64 = 0; var fired: i64 = 0
103 while i < NC {
104 if fired == 0 { if ca[i] == 1 { if cx[i] >= 0 {
105 let dx: i64 = cx[i] - tx[t]; let dy: i64 = PY - ty[t]
106 if dx*dx + dy*dy < RANGE*RANGE {
107 chp[i] = chp[i] - DMG; tc[t] = FIRE_CD; fired = 1
108 if chp[i] <= 0 { ca[i] = 0; st[0] = st[0] + REWARD }
109 }
110 } } }
111 i = i + 1
112 }
113 }
114 t = t + 1
115 }
116 var j: i64 = 0; var resolved: i64 = 1
117 while j < NC {
118 if ca[j] == 1 { cx[j] = cx[j] + SPEED; if cx[j] >= W { ca[j] = 0; st[1] = st[1] - 1 } else { resolved = 0 } }
119 j = j + 1
120 }
121 if resolved == 1 { st[2] = st[2] + 1; spawn_wave(base) }
122 if st[1] <= 0 { st[5] = 1 }
123 st[4] = st[4] + 1
124 return 0
125}
126
127func hud(base: i64, x: i64, off: i64, val: i64, col: i64) -> i64 {
128 let fb: *i64 = (base + O_FB) as *i64
129 let sbuf: *u8 = (base + O_STR + off) as *u8
130 var slen: i64 = 0; var sv: i64 = val
131 if sv <= 0 { sbuf[0] = 48 as u8; slen = 1 } else {
132 let tmp: *u8 = (base + O_STR + off + 12) as *u8; var k: i64 = 0
133 while sv > 0 { tmp[k] = (48 + (sv % 10)) as u8; sv = sv / 10; k = k + 1 }
134 var q: i64 = 0; while q < k { sbuf[q] = tmp[k - 1 - q]; q = q + 1 }
135 slen = k
136 }
137 let mask: *u8 = (base + O_MSK) as *u8
138 nf_draw_text_mem(fb, W, H, mask, x, 3, 16, sbuf, slen, col)
139 return 0
140}
141func render_impl(base: i64) -> i64 {
142 let fb: *i64 = (base + O_FB) as *i64
143 let cx: *i64 = (base + O_CX) as *i64
144 let chp: *i64 = (base + O_CHP) as *i64
145 let ca: *i64 = (base + O_CA) as *i64
146 let tx: *i64 = (base + O_TX) as *i64
147 let ty: *i64 = (base + O_TY) as *i64
148 let st: *i64 = (base + O_ST) as *i64
149 var p: i64 = 0; while p < W*H { fb[p] = rgb(28, 36, 26); p = p + 1 }
150 fillr(base, 0, PY-9, W, PY+9, rgb(120, 110, 86)) // lane
151 fillr(base, W-9, PY-13, W, PY+13, rgb(90, 200, 120)) // base
152 var t: i64 = 0
153 while t < st[3] { disc(base, tx[t], ty[t], 6, rgb(70, 110, 210)); disc(base, tx[t], ty[t], 2, rgb(160,190,240)); t = t + 1 }
154 var i: i64 = 0
155 while i < NC { if ca[i] == 1 { if cx[i] >= 0 {
156 disc(base, cx[i], PY, 5, rgb(224, 80, 58))
157 let hpw: i64 = chp[i]; if hpw > 0 { fillr(base, cx[i]-5, PY-9, cx[i]-5+hpw, PY-7, rgb(120,220,120)) }
158 } } i = i + 1 }
159 hud(base, 4, 0, st[0], rgb(245,210,90)) // gold
160 hud(base, 80, 32, st[1], rgb(120,220,150)) // lives
161 hud(base, 150, 64, st[2], rgb(200,200,230)) // wave
162 if st[5] == 1 { fillr(base, 0,0,W,4, rgb(200,40,40)); fillr(base, 0,H-4,W,H, rgb(200,40,40)); fillr(base, 0,0,4,H, rgb(200,40,40)); fillr(base, W-4,0,W,H, rgb(200,40,40)) }
163 return 0
164}
165
166func init() -> i64 { return init_impl(0) }
167func tick(input: i64) -> i64 { return tick_impl(0, input) }
168func render() -> i64 { return render_impl(0) }
169func click(x: i64, y: i64) -> i64 { return click_impl(0, x, y) }
170func score() -> i64 { let st: *i64 = (0 + O_ST) as *i64; return st[2] }
171func fb_off() -> i64 { return O_FB }
172func ww() -> i64 { return W }
173func hh() -> i64 { return H }