code wiki / (root) / nx_sprite_forge.nx

nx_sprite_forge.nx source

↩ module page · 245 lines · 13220 B

1// nx_sprite_forge.nx -- THE SPRITE BUILDER (not a sprite): a sovereign, data-driven procedural character forge. 2// A reusable library of detailed, directionally-shaded armour/body PARTS (helm, breastplate, pauldrons, plated 3// legs, arms, sword, cape, skull, ribcage, staff, robe), plus a COMPOSER that builds a full character from a 4// SPEC = an ordered list of [part_id, colourA, colourB] records (back-to-front). New characters are new spec 5// DATA, not new code -- so the ecosystem (and team) emits knights, skeletons, mages, beasts at will. Integer / 6// no-float; bakes form-shading (light from upper-left); the engine's dynamic torch lighting is applied on top. 7// Built on nx_gfx_iso primitives. license_tier: ORIGINAL 8import "nx_gfx_iso.nx" 9 10func sf_white() -> i64 { return gx_rgb(255,255,255) } 11func sf_hi(c: i64) -> i64 { return gx_mix(c, gx_rgb(255,255,255), 95) } // bright highlight 12func sf_lt(c: i64) -> i64 { return gx_mix(c, gx_rgb(255,255,255), 55) } // lit 13func sf_sh(c: i64) -> i64 { return gx_shade(c, 120) } // shadow 14func sf_ed(c: i64) -> i64 { return gx_shade(c, 64) } // dark edge / crevice 15 16// ---- PART: cape (flowing cloth behind the body) ---- 17func sf_cape(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 18 var yy: i64 = 0 19 while yy < 46 { 20 let hw: i64 = 8 + (yy*6)/46 // flares 8 -> 14 21 let prog: i64 = (yy*256)/46 22 var dx: i64 = 0 - hw 23 while dx <= hw { 24 var c: i64 = gx_mix(ca, gx_shade(ca, 55), prog) // darker toward the hem 25 if dx < 0 - hw + 2 { c = gx_mix(c, gx_rgb(255,255,255), 45) } // left edge catches light 26 if ((dx + (yy/3)) % 7) == 0 { c = gx_shade(c, 72) } // vertical folds 27 if dx > hw - 1 { c = sf_ed(c) } 28 gx_px(fb, W, H, fx + dx, fy - 52 + yy, c) 29 dx = dx + 1 30 } 31 yy = yy + 1 32 } 33 return 0 34} 35 36// ---- PART: plated legs + boots (two legs) ---- 37func sf_leg1(fb: *i64, W: i64, H: i64, lx: i64, fy: i64, ca: i64) -> i64 { 38 var yy: i64 = fy - 30 39 while yy < fy - 2 { 40 var dx: i64 = 0 - 4 41 while dx <= 4 { 42 var c: i64 = ca 43 if dx < 0 - 2 { c = sf_lt(ca) } else { if dx > 2 { c = sf_sh(ca) } } 44 if dx == 0 - 4 { c = sf_ed(ca) } if dx == 4 { c = sf_ed(ca) } 45 gx_px(fb, W, H, lx + dx, yy, c); dx = dx + 1 46 } 47 yy = yy + 1 48 } 49 gx_ellipse(fb, W, H, lx - 1, fy - 18, 3, 3, sf_hi(ca)) // knee plate 50 gx_hspan(fb, W, H, lx - 5, lx + 5, fy - 2, sf_ed(ca)) // boot sole 51 gx_hspan(fb, W, H, lx - 5, lx + 6, fy - 1, sf_ed(ca)) // boot toe 52 gx_px(fb, W, H, lx - 4, fy - 8, sf_hi(ca)) // boot cuff glint 53 return 0 54} 55func sf_legs(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 56 sf_leg1(fb, W, H, fx - 5, fy, ca); sf_leg1(fb, W, H, fx + 5, fy, ca) 57 gx_hspan(fb, W, H, fx - 9, fx + 9, fy - 30, cb) // belt/tasset trim 58 gx_hspan(fb, W, H, fx - 9, fx + 9, fy - 29, gx_shade(cb, 150)) 59 gx_ellipse(fb, W, H, fx, fy - 29, 3, 2, gx_mix(cb, gx_rgb(255,255,255), 80)) // buckle 60 return 0 61} 62 63// ---- PART: breastplate (the detail centrepiece) ---- 64func sf_torso(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 65 var yy: i64 = fy - 52 66 while yy < fy - 30 { 67 let prog: i64 = ((yy - (fy-52))*256)/22 68 let hw: i64 = 11 - (prog*3)/256 // shoulders 11 -> waist 8 69 var dx: i64 = 0 - hw 70 while dx <= hw { 71 var c: i64 = ca 72 if dx >= 0 - 2 { if dx <= 1 { c = sf_hi(ca) } } // central ridge highlight 73 if dx < 0 - hw + 3 { c = sf_lt(ca) } // lit left 74 if dx > hw - 3 { c = sf_sh(ca) } // shadow right 75 if dx == 0 - hw { c = sf_ed(ca) } if dx == hw { c = sf_ed(ca) } 76 gx_px(fb, W, H, fx + dx, yy, c); dx = dx + 1 77 } 78 yy = yy + 1 79 } 80 gx_hspan(fb, W, H, fx - 9, fx + 9, fy - 40, sf_ed(ca)) // ab plate divisions 81 gx_hspan(fb, W, H, fx - 8, fx + 8, fy - 35, sf_ed(ca)) 82 gx_px(fb, W, H, fx - 1, fy - 52, sf_ed(ca)); gx_px(fb, W, H, fx, fy - 51, sf_ed(ca)); gx_px(fb, W, H, fx + 1, fy - 50, sf_ed(ca)) // neckline V 83 gx_px(fb, W, H, fx - 9, fy - 50, sf_ed(ca)); gx_px(fb, W, H, fx + 9, fy - 50, sf_ed(ca)) // rivets 84 gx_px(fb, W, H, fx - 8, fy - 44, sf_ed(ca)); gx_px(fb, W, H, fx + 8, fy - 44, sf_ed(ca)) 85 gx_hspan(fb, W, H, fx - 8, fx + 8, fy - 31, cb) // gold belt-line trim 86 return 0 87} 88 89// ---- PART: arms (two; the right grips a weapon) ---- 90func sf_arms(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 91 var yy: i64 = fy - 49 92 while yy < fy - 30 { 93 gx_px(fb, W, H, fx - 12, yy, sf_lt(ca)); gx_px(fb, W, H, fx - 11, yy, ca); gx_px(fb, W, H, fx - 10, yy, sf_sh(ca)) 94 gx_px(fb, W, H, fx + 10, yy, sf_lt(ca)); gx_px(fb, W, H, fx + 11, yy, ca); gx_px(fb, W, H, fx + 12, yy, sf_sh(ca)) 95 yy = yy + 1 96 } 97 gx_ellipse(fb, W, H, fx - 11, fy - 30, 2, 2, cb) // gauntlets 98 gx_ellipse(fb, W, H, fx + 11, fy - 30, 2, 2, cb) 99 gx_ellipse(fb, W, H, fx - 11, fy - 48, 3, 2, sf_hi(ca)) // shoulder highlight 100 return 0 101} 102 103// ---- PART: sword (held at the right hand, blade up) ---- 104func sf_sword(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 105 let bx: i64 = fx + 13 106 gx_vgrad(fb, W, H, bx - 1, fy - 34, bx + 1, fy - 30, gx_shade(cb,150), sf_ed(cb)) // grip (leather/dark) 107 gx_hspan(fb, W, H, bx - 5, bx + 5, fy - 35, cb) // crossguard 108 gx_hspan(fb, W, H, bx - 5, bx + 5, fy - 34, gx_shade(cb, 160)) 109 gx_ellipse(fb, W, H, bx, fy - 29, 2, 2, gx_mix(cb, gx_rgb(255,255,255), 90)) // pommel 110 var yy: i64 = fy - 62 111 while yy < fy - 35 { 112 gx_px(fb, W, H, bx - 2, yy, sf_sh(ca)); gx_px(fb, W, H, bx - 1, yy, ca) 113 gx_px(fb, W, H, bx, yy, sf_hi(ca)); gx_px(fb, W, H, bx + 1, yy, sf_lt(ca)); gx_px(fb, W, H, bx + 2, yy, sf_sh(ca)) // fuller highlight 114 yy = yy + 1 115 } 116 gx_px(fb, W, H, bx, fy - 63, gx_rgb(255,255,255)); gx_px(fb, W, H, bx, fy - 62, sf_hi(ca)) // tip glint 117 return 0 118} 119 120// ---- PART: pauldrons (two rounded shoulder plates) ---- 121func sf_pauldrons(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 122 gx_ellipse(fb, W, H, fx - 11, fy - 48, 6, 5, ca) 123 gx_ellipse(fb, W, H, fx - 12, fy - 50, 4, 3, sf_hi(ca)) // top catches light 124 gx_ellipse(fb, W, H, fx - 9, fy - 45, 3, 2, sf_sh(ca)) // underside shadow 125 gx_ellipse(fb, W, H, fx + 11, fy - 48, 6, 5, ca) 126 gx_ellipse(fb, W, H, fx + 10, fy - 50, 4, 3, sf_lt(ca)) 127 gx_ellipse(fb, W, H, fx + 13, fy - 45, 3, 2, sf_sh(ca)) 128 gx_px(fb, W, H, fx - 11, fy - 52, cb); gx_px(fb, W, H, fx + 11, fy - 52, cb) // rivet studs 129 return 0 130} 131 132// ---- PART: helm (domed, visor, plume) ---- 133func sf_helm(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 134 gx_ellipse(fb, W, H, fx, fy - 58, 8, 9, ca) // dome 135 gx_ellipse(fb, W, H, fx - 2, fy - 61, 4, 4, sf_hi(ca)) // top-left highlight 136 gx_ellipse(fb, W, H, fx + 4, fy - 56, 3, 5, sf_sh(ca)) // right shadow 137 gx_hspan(fb, W, H, fx - 7, fx + 7, fy - 55, sf_ed(ca)) // visor slit 138 gx_hspan(fb, W, H, fx - 7, fx + 7, fy - 54, gx_rgb(20,16,14)) 139 gx_px(fb, W, H, fx - 3, fy - 54, gx_rgb(150,70,40)); gx_px(fb, W, H, fx + 2, fy - 54, gx_rgb(150,70,40)) // eye glints in the visor 140 gx_hspan(fb, W, H, fx - 8, fx + 8, fy - 51, sf_sh(ca)) // jaw guard 141 // plume / crest 142 var yy: i64 = fy - 68 143 while yy < fy - 58 { 144 gx_px(fb, W, H, fx, yy, cb); gx_px(fb, W, H, fx - 1, yy, gx_shade(cb, 140)); gx_px(fb, W, H, fx + 1, yy, sf_lt(cb)) 145 yy = yy + 1 146 } 147 return 0 148} 149 150// ---- PART: skull (skeleton head) ---- 151func sf_skull(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 152 gx_ellipse(fb, W, H, fx, fy - 58, 7, 8, ca) // cranium (bone) 153 gx_ellipse(fb, W, H, fx - 2, fy - 61, 3, 3, sf_hi(ca)) 154 gx_ellipse(fb, W, H, fx - 3, fy - 56, 2, 3, gx_rgb(14,12,10)) // eye sockets 155 gx_ellipse(fb, W, H, fx + 3, fy - 56, 2, 3, gx_rgb(14,12,10)) 156 gx_px(fb, W, H, fx - 3, fy - 56, cb); gx_px(fb, W, H, fx + 3, fy - 56, cb) // eye-glints 157 gx_px(fb, W, H, fx, fy - 53, gx_rgb(14,12,10)) // nasal cavity 158 gx_px(fb, W, H, fx, fy - 52, gx_rgb(14,12,10)) 159 var t: i64 = 0 - 4 // teeth 160 while t <= 4 { gx_px(fb, W, H, fx + t, fy - 50, sf_sh(ca)); if (t % 2) == 0 { gx_px(fb, W, H, fx + t, fy - 49, gx_rgb(14,12,10)) } t = t + 1 } 161 return 0 162} 163 164// ---- PART: ribcage (skeleton torso) ---- 165func sf_ribs(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 166 var yy: i64 = fy - 50 167 while yy < fy - 30 { gx_px(fb, W, H, fx, yy, ca); gx_px(fb, W, H, fx - 1, yy, sf_sh(ca)); yy = yy + 1 } // spine 168 var r: i64 = 0 169 while r < 4 { 170 let ry: i64 = fy - 48 + r*4 171 var dx: i64 = 0 - 7 172 while dx <= 7 { 173 let yo: i64 = (dx*dx)/12 // curved rib 174 gx_px(fb, W, H, fx + dx, ry + yo, ca) 175 dx = dx + 1 176 } 177 r = r + 1 178 } 179 return 0 180} 181 182// ---- PART: staff (mage) ---- 183func sf_staff(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, ca: i64, cb: i64) -> i64 { 184 let bx: i64 = fx + 13 185 var yy: i64 = fy - 62 186 while yy < fy - 26 { gx_px(fb, W, H, bx, yy, ca); gx_px(fb, W, H, bx - 1, yy, sf_sh(ca)); gx_px(fb, W, H, bx + 1, yy, sf_lt(ca)); yy = yy + 1 } // shaft 187 gx_ellipse(fb, W, H, bx, fy - 64, 4, 4, cb) // orb 188 gx_ellipse(fb, W, H, bx - 1, fy - 65, 2, 2, gx_mix(cb, gx_rgb(255,255,255), 120)) 189 return 0 190} 191 192// ---- the dispatcher + composer ---- 193func sf_draw_part(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, pid: i64, ca: i64, cb: i64) -> i64 { 194 if pid == 1 { return sf_cape(fb, W, H, fx, fy, ca, cb) } 195 if pid == 2 { return sf_legs(fb, W, H, fx, fy, ca, cb) } 196 if pid == 3 { return sf_torso(fb, W, H, fx, fy, ca, cb) } 197 if pid == 4 { return sf_arms(fb, W, H, fx, fy, ca, cb) } 198 if pid == 5 { return sf_sword(fb, W, H, fx, fy, ca, cb) } 199 if pid == 6 { return sf_pauldrons(fb, W, H, fx, fy, ca, cb) } 200 if pid == 7 { return sf_helm(fb, W, H, fx, fy, ca, cb) } 201 if pid == 8 { return sf_skull(fb, W, H, fx, fy, ca, cb) } 202 if pid == 9 { return sf_ribs(fb, W, H, fx, fy, ca, cb) } 203 if pid == 10 { return sf_staff(fb, W, H, fx, fy, ca, cb) } 204 return 0 205} 206// compose a character from a SPEC = nrec records of [pid, ca, cb] (drawn in order, back-to-front) 207func sf_forge(fb: *i64, W: i64, H: i64, fx: i64, fy: i64, spec: *i64, nrec: i64) -> i64 { 208 gx_ellipse_blend(fb, W, H, fx, fy, 13, 5, gx_rgb(0,0,0), 150) // grounded drop shadow 209 var r: i64 = 0 210 while r < nrec { sf_draw_part(fb, W, H, fx, fy, spec[r*3], spec[r*3+1], spec[r*3+2]); r = r + 1 } 211 return 0 212} 213 214// ---- SPEC DATA (archetypes as data, not code paths) -- fill a caller buffer, return record count ---- 215func sf_rec(buf: *i64, i: i64, pid: i64, ca: i64, cb: i64) -> i64 { buf[i*3] = pid; buf[i*3+1] = ca; buf[i*3+2] = cb; return 0 } 216func sf_spec_knight(buf: *i64) -> i64 { 217 let steel: i64 = gx_rgb(120,126,140); let gold: i64 = gx_rgb(214,176,72); let red: i64 = gx_rgb(150,38,42) 218 sf_rec(buf, 0, 1, red, red) // cape 219 sf_rec(buf, 1, 2, steel, gold) // legs 220 sf_rec(buf, 2, 3, steel, gold) // breastplate 221 sf_rec(buf, 3, 4, steel, gold) // arms 222 sf_rec(buf, 4, 6, steel, gold) // pauldrons 223 sf_rec(buf, 5, 5, gx_rgb(190,196,210), gold) // sword 224 sf_rec(buf, 6, 7, steel, red) // helm + red plume 225 return 7 226} 227func sf_spec_skeleton(buf: *i64) -> i64 { 228 let bone: i64 = gx_rgb(206,200,176); let glow: i64 = gx_rgb(120,200,90); let rust: i64 = gx_rgb(120,96,70) 229 sf_rec(buf, 0, 2, bone, rust) // bony legs 230 sf_rec(buf, 1, 9, bone, bone) // ribcage 231 sf_rec(buf, 2, 4, bone, bone) // bony arms 232 sf_rec(buf, 3, 5, gx_rgb(120,110,96), rust) // rusty sword 233 sf_rec(buf, 4, 8, bone, glow) // skull + glowing eyes 234 return 5 235} 236func sf_spec_mage(buf: *i64) -> i64 { 237 let robe: i64 = gx_rgb(54,72,150); let gold: i64 = gx_rgb(214,176,72); let orb: i64 = gx_rgb(90,200,230) 238 sf_rec(buf, 0, 1, robe, robe) // robe back 239 sf_rec(buf, 1, 2, robe, gold) // robe skirt 240 sf_rec(buf, 2, 3, robe, gold) // robe torso 241 sf_rec(buf, 3, 4, robe, gold) // sleeves 242 sf_rec(buf, 4, 10, gold, orb) // staff + orb 243 sf_rec(buf, 5, 7, robe, gold) // hood 244 return 6 245}