code wiki / (root) / nx_wasm_diablo.nx

nx_wasm_diablo.nx source

↩ module page · 177 lines · 8724 B

1// nx_wasm_diablo.nx -- BROWSER-PLAYABLE sovereign isometric ARPG ("that generation of game"): BASE-RELATIVE 2// integer core, runs native (-> PNG) AND in the browser (base = 0). Built on nx_gfx_iso: an isometric stone 3// dungeon with 2.5D walls, a shaded hero, fallen-enemy sprites, and the SIGNATURE dynamic torch lighting that 4// FOLLOWS the hero. CLICK to move (the click maps a screen point to a destination); walk into a foe to strike 5// it; grab the gold. NO float, NO JS logic, NO canvas drawing. init()/tick(input)/render()/score()/ww()/hh()/ 6// fb_off()/click(x,y) = the wasm interface. license_tier: ORIGINAL 7import "nx_gfx_iso.nx" 8import "nx_nishi_font_core.nx" 9const W: i64 = 288 10const H: i64 = 192 11const OX: i64 = 144 // iso origin 12const OY: i64 = 22 13const NM: i64 = 4 14const SPD: i64 = 2 15const O_FB: i64 = 0 // framebuffer W*H i64 16const O_MX: i64 = 442368 // monster screen x[8] (W*H*8) 17const O_MY: i64 = 442432 18const O_MHP: i64 = 442496 19const O_MA: i64 = 442560 // monster alive[8] 20const O_ST: i64 = 442624 // 0=hx 1=hy 2=tx 3=ty 4=gold 5=hp 6=tick 7=atkcd 8=gx 9=gy 10=gtaken 11-15=fx 16=sfx 21const O_MSK: i64 = 442816 // (O_ST widened to 24 slots so st[16] = pending sfx event) 22const O_STR: i64 = 451008 23 24func iabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 25func isgn(v: i64) -> i64 { if v > 0 { return 1 } if v < 0 { return 0 - 1 } return 0 } 26 27func init_impl(base: i64) -> i64 { 28 let mx: *i64 = (base + O_MX) as *i64 29 let my: *i64 = (base + O_MY) as *i64 30 let mhp: *i64 = (base + O_MHP) as *i64 31 let ma: *i64 = (base + O_MA) as *i64 32 let st: *i64 = (base + O_ST) as *i64 33 st[0] = OX; st[1] = OY + 48 + TH/2 // hero feet at tile (3,3)-ish centre 34 st[2] = st[0]; st[3] = st[1] 35 st[4] = 0; st[5] = 100; st[6] = 0; st[7] = 0; st[16] = 0 36 st[8] = gx_isox(5, 4, OX); st[9] = gx_isoy(5, 4, OY) + TH/2; st[10] = 0 // gold spot 37 mx[0] = gx_isox(5, 1, OX); my[0] = gx_isoy(5, 1, OY) + TH/2 38 mx[1] = gx_isox(1, 5, OX); my[1] = gx_isoy(1, 5, OY) + TH/2 39 mx[2] = gx_isox(5, 5, OX); my[2] = gx_isoy(5, 5, OY) + TH/2 40 mx[3] = gx_isox(2, 1, OX); my[3] = gx_isoy(2, 1, OY) + TH/2 41 var i: i64 = 0; while i < NM { mhp[i] = 24; ma[i] = 1; i = i + 1 } 42 return 0 43} 44 45func click_impl(base: i64, x: i64, y: i64) -> i64 { 46 let st: *i64 = (base + O_ST) as *i64 47 if st[5] <= 0 { init_impl(base); return 0 } // dead -> click restarts 48 st[2] = x; st[3] = y // set move destination 49 return 0 50} 51 52func tick_impl(base: i64, input: i64) -> i64 { 53 let mx: *i64 = (base + O_MX) as *i64 54 let my: *i64 = (base + O_MY) as *i64 55 let mhp: *i64 = (base + O_MHP) as *i64 56 let ma: *i64 = (base + O_MA) as *i64 57 let st: *i64 = (base + O_ST) as *i64 58 if st[5] <= 0 { return 0 } 59 // move hero toward destination (proportional integer step) 60 let dx: i64 = st[2] - st[0]; let dy: i64 = st[3] - st[1]; let ad: i64 = iabs(dx) + iabs(dy) 61 st[15] = 0 62 if ad > SPD { st[0] = st[0] + (dx*SPD)/ad; st[1] = st[1] + (dy*SPD)/ad; st[15] = 1 } 63 if st[0] < OX-92 { st[0] = OX-92 } if st[0] > OX+92 { st[0] = OX+92 } 64 if st[1] < OY+10 { st[1] = OY+10 } if st[1] > OY+118 { st[1] = OY+118 } 65 if st[7] > 0 { st[7] = st[7] - 1 } 66 // combat + monster AI 67 var i: i64 = 0 68 while i < NM { 69 if ma[i] == 1 { 70 let ex: i64 = mx[i] - st[0]; let ey: i64 = my[i] - st[1]; let e2: i64 = ex*ex + ey*ey 71 if e2 < 22*22 { // adjacent: strike 72 if st[7] <= 0 { mhp[i] = mhp[i] - 10; st[7] = 16; st[11] = 7; st[12] = mx[i]; st[13] = my[i]; st[14] = i; st[16] = 1; if mhp[i] <= 0 { ma[i] = 0; st[4] = st[4] + 5; st[16] = 2 } } 73 if (st[6] % 36) == 0 { st[5] = st[5] - 3; if st[5] <= 0 { st[16] = 4 } } // foe bites back 74 } else { if (st[6] % 2) == 0 { // else shamble toward the hero 75 mx[i] = mx[i] - isgn(ex); my[i] = my[i] - isgn(ey) 76 } } 77 } 78 i = i + 1 79 } 80 if st[10] == 0 { if iabs(st[0]-st[8]) < 12 { if iabs(st[1]-st[9]) < 12 { st[10] = 1; st[4] = st[4] + 10; st[16] = 3 } } } 81 if st[11] > 0 { st[11] = st[11] - 1 } 82 st[6] = st[6] + 1 83 return 0 84} 85 86func draw_dungeon(base: i64) -> i64 { 87 let fb: *i64 = (base + O_FB) as *i64 88 gx_fill(fb, W, H, gx_rgb(6, 6, 10)) 89 var ty: i64 = 0 90 while ty < 7 { 91 var tx: i64 = 0 92 while tx < 7 { 93 let nz: i64 = ((tx*7 + ty*13) % 5 - 2)*7 94 let col: i64 = gx_rgb(92 + nz, 86 + nz, 76 + nz) 95 let cxp: i64 = gx_isox(tx, ty, OX); let syp: i64 = gx_isoy(tx, ty, OY) 96 gx_iso_tile(fb, W, H, cxp, syp, col) 97 gx_iso_tile_rim(fb, W, H, cxp, syp, col) 98 let crk: i64 = gx_shade(col, 130) // stone-crack speckle 99 gx_px(fb, W, H, cxp - 3 + ((tx*5) % 7), syp + 7, crk) 100 gx_px(fb, W, H, cxp + 2 + ((ty*3) % 5), syp + 9, crk) 101 gx_px(fb, W, H, cxp + (((tx+ty)*7) % 9) - 4, syp + 6, crk) 102 tx = tx + 1 103 } 104 ty = ty + 1 105 } 106 var k: i64 = 0 107 while k < 7 { gx_iso_cube(fb, W, H, gx_isox(k, 0, OX), gx_isoy(k, 0, OY) - 26, 26, gx_rgb(106, 98, 88)); k = k + 1 } 108 k = 1 109 while k < 7 { gx_iso_cube(fb, W, H, gx_isox(0, k, OX), gx_isoy(0, k, OY) - 26, 26, gx_rgb(94, 88, 80)); k = k + 1 } 110 return 0 111} 112 113func hud(base: i64, x: i64, off: i64, val: i64, col: i64) -> i64 { 114 let fb: *i64 = (base + O_FB) as *i64 115 let sbuf: *u8 = (base + O_STR + off) as *u8 116 var slen: i64 = 0; var sv: i64 = val 117 if sv <= 0 { sbuf[0] = 48 as u8; slen = 1 } else { 118 let tmp: *u8 = (base + O_STR + off + 12) as *u8; var kk: i64 = 0 119 while sv > 0 { tmp[kk] = (48 + (sv % 10)) as u8; sv = sv / 10; kk = kk + 1 } 120 var q: i64 = 0; while q < kk { sbuf[q] = tmp[kk - 1 - q]; q = q + 1 } 121 slen = kk 122 } 123 let mask: *u8 = (base + O_MSK) as *u8 124 nf_draw_text_mem(fb, W, H, mask, x, 4, 16, sbuf, slen, col) 125 return 0 126} 127 128func render_impl(base: i64) -> i64 { 129 let fb: *i64 = (base + O_FB) as *i64 130 let mx: *i64 = (base + O_MX) as *i64 131 let my: *i64 = (base + O_MY) as *i64 132 let ma: *i64 = (base + O_MA) as *i64 133 let st: *i64 = (base + O_ST) as *i64 134 draw_dungeon(base) 135 if st[10] == 0 { gx_ellipse(fb, W, H, st[8], st[9] - 3, 3, 3, gx_rgb(255, 215, 90)) } 136 var hph: i64 = 0 137 if st[15] == 1 { hph = st[6] } // animate the hero only while moving 138 // entities, depth-sorted: anything with feet-y above the hero is drawn first 139 var pass: i64 = 0 140 while pass < 2 { 141 var i: i64 = 0 142 while i < NM { 143 if ma[i] == 1 { 144 let behind: i64 = my[i] < st[1] 145 if (pass == 0) == (behind == 1) { gx_sprite_humanoid(fb, W, H, mx[i], my[i], gx_rgb(70, 120, 64), gx_rgb(150, 170, 120), st[6]) } 146 } 147 i = i + 1 148 } 149 if pass == 0 { gx_sprite_humanoid(fb, W, H, st[0], st[1], gx_rgb(160, 44, 40), gx_rgb(230, 186, 150), hph) } 150 pass = pass + 1 151 } 152 // signature: torch follows the hero 153 gx_light_radial(fb, W, H, st[0], st[1] - 22, 116, 50) 154 if st[10] == 0 { gx_light_add(fb, W, H, st[8], st[9] - 3, 12, gx_rgb(170, 128, 34)) } 155 var j: i64 = 0 156 while j < NM { if ma[j] == 1 { gx_light_add(fb, W, H, mx[j], my[j] - 24, 7, gx_rgb(90, 12, 10)) } j = j + 1 } 157 // attack FX (drawn after lighting so they pop): white hit-flash on the struck foe + a bright slash arc 158 if st[11] > 0 { 159 gx_ellipse_blend(fb, W, H, st[12], st[13] - 22, 4, 6, gx_rgb(255, 230, 180), 120) // hit spark on the foe 160 let sxm: i64 = (st[0] + st[12]) / 2; let sym: i64 = (st[1] + st[13]) / 2 - 20 161 gx_ellipse_blend(fb, W, H, sxm, sym, 8, 2, gx_rgb(255, 248, 205), 215) // thin slash crescent 162 gx_ellipse_blend(fb, W, H, sxm, sym - 1, 4, 1, gx_rgb(255,255,255), 240) 163 } 164 hud(base, 6, 0, st[4], gx_rgb(245, 210, 90)) // gold 165 hud(base, W-40, 64, st[5], gx_rgb(220, 90, 80)) // hp 166 return 0 167} 168 169func init() -> i64 { return init_impl(0) } 170func tick(input: i64) -> i64 { return tick_impl(0, input) } 171func sfx_event() -> i64 { let st: *i64 = (0 + O_ST) as *i64; let e: i64 = st[16]; st[16] = 0; return e } // 1=hit 2=kill 3=coin 4=gameover; cleared on read 172func render() -> i64 { return render_impl(0) } 173func click(x: i64, y: i64) -> i64 { return click_impl(0, x, y) } 174func score() -> i64 { let st: *i64 = (0 + O_ST) as *i64; return st[4] } 175func fb_off() -> i64 { return O_FB } 176func ww() -> i64 { return W } 177func hh() -> i64 { return H }