灵符lingfu

soul-symphony-ling

↓ 0 downloads

Soul Symphony — Ling Edition. A spiritual 3D adventure in the soul realm: collect the letters of the world-song, clear the monster lattice, and duel in the arcade arena. Wind Waker-style toon renderer with volumetric light pools, SSAO, bloom and oil-slick tilt-shift — all ling-lang vectors.

Keywords game, 3d, toon, music, soul-symphony
lingfu install soul-symphony-ling
AuthorsLing Lin <taellinglin@gmail.com>
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (1947)

game/arcade.ling
# ════════════════════════════════════════════════════════════════════════════
# arcade.ling — โหมดอาร์เคด: ปรับแต่งลูก (สี/สแตมป์/แบนด์/ชื่อ) + ต่อสู้ศัตรู AI
#   วาดลูกแต่ง = body orb (สีกำหนดเอง) + สแตมป์ขั้วบน/ล่าง + แบนด์รอบเส้นศูนย์สูตร
#   สแตมป์/แบนด์ = สามเหลี่ยมเติม (fill) + สีดินสอ → คุมสี/รูปทรงได้แม่นยำ
# ════════════════════════════════════════════════════════════════════════════
bind STAMP_N = 8
bind BAND_N  = 8

# ── สว็อตช์สี: สี่เหลี่ยมเติมที่ (x,y) ครึ่งขนาด s ──
ฟังก์ชัน วาดสว็อตช์(x, y, s, r, g, b) {
    สีดินสอ(r, g, b)
    วาดสามเหลี่ยม3มิติ(x-s, y-s, 0.0,  x+s, y-s, 0.0,  x+s, y+s, 0.0)
    วาดสามเหลี่ยม3มิติ(x-s, y-s, 0.0,  x+s, y+s, 0.0,  x-s, y+s, 0.0)
}

# ── เฉดสี (0..1) → [r,g,b] สดใส ──
ฟังก์ชัน สีจากเฉด(h) {
    bind r = ไซน์(h*6.28319) * 0.5 + 0.5
    bind g = ไซน์(h*6.28319 + 2.094) * 0.5 + 0.5
    bind b = ไซน์(h*6.28319 + 4.189) * 0.5 + 0.5
    คืน [floor(r*200+55), floor(g*200+55), floor(b*200+55)]
}

# ── สแตมป์: ตราเวกเตอร์เติม (fill) ในระนาบ xz ที่ (ox,oy,oz) ขนาด s, หมุน rot ──
#   (ตั้งสีดินสอก่อนเรียก)
ฟังก์ชัน วาดสแตมป์(id, ox, oy, oz, s, rot) {
    ถ้า id == 0 {                      # ดาว 5 แฉก (พัดจากจุดศูนย์กลาง)
        bind k = 0
        ขณะที่ k < 10 {
            bind a0 = rot + k * 0.62832;  bind a1 = rot + (k + 1) * 0.62832
            bind r0 = 0.42;  ถ้า k - ปัดลง(k/2.0)*2 == 0 { bind r0 = 1.0 }
            bind r1 = 0.42;  ถ้า (k + 1) - ปัดลง((k + 1)/2.0)*2 == 0 { bind r1 = 1.0 }
            วาดสามเหลี่ยม3มิติ(ox, oy, oz,  ox+โคไซน์(a0)*r0*s, oy, oz+ไซน์(a0)*r0*s,  ox+โคไซน์(a1)*r1*s, oy, oz+ไซน์(a1)*r1*s)
            bind k = k + 1
        }
    }
    ถ้า id == 1 {                      # เฟือง/จักร (พัด รัศมีสลับ = ฟันเฟือง)
        bind k = 0
        ขณะที่ k < 16 {
            bind a0 = rot + k * 0.3927;  bind a1 = rot + (k + 1) * 0.3927
            bind r0 = 0.66;  ถ้า k - ปัดลง(k/2.0)*2 == 0 { bind r0 = 1.0 }
            bind r1 = 0.66;  ถ้า (k + 1) - ปัดลง((k + 1)/2.0)*2 == 0 { bind r1 = 1.0 }
            วาดสามเหลี่ยม3มิติ(ox, oy, oz,  ox+โคไซน์(a0)*r0*s, oy, oz+ไซน์(a0)*r0*s,  ox+โคไซน์(a1)*r1*s, oy, oz+ไซน์(a1)*r1*s)
            bind k = k + 1
        }
    }
    ถ้า id == 2 {                      # เพชร (ข้าวหลามตัด — 2 สามเหลี่ยม)
        bind p0x = ox+โคไซน์(rot)*s;          bind p0z = oz+ไซน์(rot)*s
        bind p1x = ox+โคไซน์(rot+1.5708)*s;   bind p1z = oz+ไซน์(rot+1.5708)*s
        bind p2x = ox+โคไซน์(rot+3.14159)*s;  bind p2z = oz+ไซน์(rot+3.14159)*s
        bind p3x = ox+โคไซน์(rot+4.71239)*s;  bind p3z = oz+ไซน์(rot+4.71239)*s
        วาดสามเหลี่ยม3มิติ(p0x,oy,p0z, p1x,oy,p1z, p2x,oy,p2z)
        วาดสามเหลี่ยม3มิติ(p0x,oy,p0z, p2x,oy,p2z, p3x,oy,p3z)
    }
    ถ้า id == 3 {                      # สามเหลี่ยม (1 ฟิล)
        bind a0 = rot;  bind a1 = rot + 2.0944;  bind a2 = rot + 4.18879
        วาดสามเหลี่ยม3มิติ(ox+โคไซน์(a0)*s,oy,oz+ไซน์(a0)*s, ox+โคไซน์(a1)*s,oy,oz+ไซน์(a1)*s, ox+โคไซน์(a2)*s,oy,oz+ไซน์(a2)*s)
    }
    ถ้า id == 4 {                      # กากบาท/บวก (2 แท่งเติม = 4 สามเหลี่ยม)
        bind w = s * 0.32
        วาดสามเหลี่ยม3มิติ(ox-s,oy,oz-w, ox+s,oy,oz-w, ox+s,oy,oz+w)
        วาดสามเหลี่ยม3มิติ(ox-s,oy,oz-w, ox+s,oy,oz+w, ox-s,oy,oz+w)
        วาดสามเหลี่ยม3มิติ(ox-w,oy,oz-s, ox+w,oy,oz-s, ox+w,oy,oz+s)
        วาดสามเหลี่ยม3มิติ(ox-w,oy,oz-s, ox+w,oy,oz+s, ox-w,oy,oz+s)
    }
}

# ── แบนด์: ลายเติม (fill) รอบเส้นศูนย์สูตร (วนรอบไร้รอยต่อ) — ตั้งสีดินสอก่อน ──
ฟังก์ชัน วาดแบนด์(id, cx, cy, cz, R, t) {
    bind N = 24
    bind k = 0
    ถ้า id == 0 {                      # แท่งตั้งเติม (มีช่องว่างระหว่างแท่ง)
        ขณะที่ k < N {
            bind a0 = t*0.3 + k * 6.28319 / N;  bind a1 = t*0.3 + (k + 0.5) * 6.28319 / N
            bind ax=cx+โคไซน์(a0)*R; bind az=cz+ไซน์(a0)*R
            bind bx=cx+โคไซน์(a1)*R; bind bz=cz+ไซน์(a1)*R
            วาดสามเหลี่ยม3มิติ(ax,cy-0.6,az, bx,cy-0.6,bz, bx,cy+0.6,bz)
            วาดสามเหลี่ยม3มิติ(ax,cy-0.6,az, bx,cy+0.6,bz, ax,cy+0.6,az)
            bind k = k + 1
        }
    }
    ถ้า id == 1 {                      # ฟันเลื่อย (สามเหลี่ยมยอดขึ้น/ลงสลับ)
        ขณะที่ k < N {
            bind a0 = t*0.3 + k * 6.28319 / N;  bind a1 = t*0.3 + (k + 1) * 6.28319 / N
            bind am = (a0 + a1) * 0.5
            bind ax=cx+โคไซน์(a0)*R; bind az=cz+ไซน์(a0)*R
            bind bx=cx+โคไซน์(a1)*R; bind bz=cz+ไซน์(a1)*R
            bind mx=cx+โคไซน์(am)*R; bind mz=cz+ไซน์(am)*R
            bind ty = cy - 0.85;  ถ้า k - ปัดลง(k/2.0)*2 == 0 { bind ty = cy + 0.85 }
            วาดสามเหลี่ยม3มิติ(ax,cy,az, bx,cy,bz, mx,ty,mz)
            bind k = k + 1
        }
    }
    ถ้า id == 2 {                      # แถบทึบ (วงแหวนตันรอบกลาง)
        ขณะที่ k < N {
            bind a0 = t*0.3 + k * 6.28319 / N;  bind a1 = t*0.3 + (k + 1) * 6.28319 / N
            bind ax=cx+โคไซน์(a0)*R; bind az=cz+ไซน์(a0)*R
            bind bx=cx+โคไซน์(a1)*R; bind bz=cz+ไซน์(a1)*R
            วาดสามเหลี่ยม3มิติ(ax,cy-0.7,az, bx,cy-0.7,bz, bx,cy+0.7,bz)
            วาดสามเหลี่ยม3มิติ(ax,cy-0.7,az, bx,cy+0.7,bz, ax,cy+0.7,az)
            bind k = k + 1
        }
    }
    ถ้า id == 3 {                      # เพชรเติมเรียงรอบ
        bind M = 12
        ขณะที่ k < M {
            bind a = t*0.3 + k * 6.28319 / M
            bind px = cx + โคไซน์(a)*R;  bind pz = cz + ไซน์(a)*R
            bind ox = โคไซน์(a)*0.6;  bind oz = ไซน์(a)*0.6
            วาดสามเหลี่ยม3มิติ(px,cy-0.8,pz, px+ox,cy,pz+oz, px,cy+0.8,pz)
            วาดสามเหลี่ยม3มิติ(px,cy-0.8,pz, px,cy+0.8,pz, px-ox,cy,pz-oz)
            bind k = k + 1
        }
    }
}

# ── วาดเมชจาก blob (pos+idx) แบบเติม + สีสุ่มคงที่ต่อหน้า (hueBase แยกแต่ละเมช) ──
ฟังก์ชัน วาดเมชสุ่มสี(P, I, ox, oy, oz, scale, hueBase) {
    bind n = ความยาว(I)
    bind k = 0;  bind f = 0.0
    ขณะที่ k + 2 < n {
        bind a = list_get(I,k)*3;  bind b = list_get(I,k+1)*3;  bind c = list_get(I,k+2)*3
        bind h = hueBase + f * 0.6
        สีดินสอ(floor((ไซน์(h)*0.5+0.5)*150+55), floor((ไซน์(h+2.094)*0.5+0.5)*150+55), floor((ไซน์(h+4.189)*0.5+0.5)*150+55))
        วาดสามเหลี่ยม3มิติ(
            ox+list_get(P,a)*scale,   oy+list_get(P,a+1)*scale,   oz+list_get(P,a+2)*scale,
            ox+list_get(P,b)*scale,   oy+list_get(P,b+1)*scale,   oz+list_get(P,b+2)*scale,
            ox+list_get(P,c)*scale,   oy+list_get(P,c+1)*scale,   oz+list_get(P,c+2)*scale)
        bind k = k + 3;  bind f = f + 1.0
    }
}

# ── อารีนาจาก arena.glb (InnerWalls/Floor/Trough) — สีสุ่มต่อหน้า ──
ฟังก์ชัน วาดอารีนาโมเดล(ox, oy, oz, scale) {
    วาดเมช(arena_Cube_pos,     arena_Cube_idx,     ox, oy, oz, scale, 1)   # native batched (ling-gpu)
    วาดเมช(arena_Cube_001_pos, arena_Cube_001_idx, ox, oy, oz, scale, 1)
    วาดเมช(arena_Cube_002_pos, arena_Cube_002_idx, ox, oy, oz, scale, 1)
}

# ── 6 ดาบพิคอัพ (อาร์เคด): item id (พลังพิเศษต่างกัน) + สี + ตราบิลบอร์ด ──
#   3=Torpedo 5=DualSword 6=Burn 7=Freeze 8=Nova 9=Quake (พลังของแต่ละไอเทม)
bind ดาบไอดี = [3, 5, 6, 7, 8, 9]
bind ดาบสี   = [[120,200,255],[210,228,255],[255,120,40],[150,220,255],[220,160,255],[255,200,120]]

# ── สเตตัสดาบ 6 เล่ม: [cooldown, power, range, comboMax, R, G, B] ──
#   0 Quick · 1 Great · 2 Twin · 3 Flame · 4 Frost · 5 Nova
bind ดาบสเตตัส = [
    [0.20, 1.1, 15.0, 3.0, 120,200,255],
    [0.55, 3.4, 23.0, 1.0, 255,120,70],
    [0.14, 0.8, 13.0, 4.0, 210,228,255],
    [0.38, 1.9, 18.0, 2.0, 255,150,40],
    [0.44, 1.7, 18.0, 2.0, 150,220,255],
    [0.62, 2.8, 26.0, 1.0, 220,160,255]
]

# อาร์คเติม: เสี้ยวพระจันทร์เติม (strip ระหว่าง rad-width..rad ตามมุม yaw±span*prog) ที่ y=cy
ฟังก์ชัน อาร์คเติม(cx, cy, cz, yaw, span, rad, width, prog, r, g, b) {
    สีดินสอ(r, g, b)
    bind n = 9;  bind k = 0
    ขณะที่ k < n {
        bind a0 = yaw - span + (k / 9.0) * 2.0*span*prog
        bind a1 = yaw - span + ((k + 1) / 9.0) * 2.0*span*prog
        bind o0x = cx+ไซน์(a0)*rad;        bind o0z = cz+โคไซน์(a0)*rad
        bind o1x = cx+ไซน์(a1)*rad;        bind o1z = cz+โคไซน์(a1)*rad
        bind i0x = cx+ไซน์(a0)*(rad-width); bind i0z = cz+โคไซน์(a0)*(rad-width)
        bind i1x = cx+ไซน์(a1)*(rad-width); bind i1z = cz+โคไซน์(a1)*(rad-width)
        วาดสามเหลี่ยม3มิติ(i0x,cy,i0z, o0x,cy,o0z, o1x,cy,o1z)
        วาดสามเหลี่ยม3มิติ(i0x,cy,i0z, o1x,cy,o1z, i1x,cy,i1z)
        bind k = k + 1
    }
}

# วาดสแลชเติม: เอฟเฟกต์ฟันแบบเติม ต่างกันต่อชนิดดาบ (typ 0..5)
ฟังก์ชัน วาดสแลชเติม(typ, cx, cy, cz, yaw, prog, rad, r, g, b) {
    โหมดผสม(1)
    bind fwx = ไซน์(yaw);  bind fwz = โคไซน์(yaw)
    ถ้า typ == 0 { อาร์คเติม(cx, cy, cz, yaw, 1.0, rad, 2.5, prog, r,g,b) }
    ถ้า typ == 1 { อาร์คเติม(cx, cy, cz, yaw, 1.7, rad, 5.0, prog, r,g,b) }
    ถ้า typ == 2 { อาร์คเติม(cx, cy-1.2, cz, yaw, 1.2, rad, 2.0, prog, r,g,b)  อาร์คเติม(cx, cy+1.2, cz, yaw, 1.2, rad, 2.0, prog, r,g,b) }
    ถ้า typ == 3 { อาร์คเติม(cx, cy, cz, yaw, 1.3, rad, 3.2, prog, r,g,b)  อนุภาคทรงกลม(cx+fwx*rad*0.8, cy, cz+fwz*rad*0.8, 2.4, 18, prog*4.0, r, g, floor(b*0.3)) }
    ถ้า typ == 4 {
        อาร์คเติม(cx, cy, cz, yaw, 1.3, rad, 3.0, prog, r,g,b)
        bind sk = 0
        ขณะที่ sk < 4 { bind sa = yaw - 1.0 + sk*0.66
            วาดสามเหลี่ยม3มิติ(cx+ไซน์(sa)*rad, cy-1.6, cz+โคไซน์(sa)*rad,  cx+ไซน์(sa)*rad*1.12, cy, cz+โคไซน์(sa)*rad*1.12,  cx+ไซน์(sa)*rad, cy+1.6, cz+โคไซน์(sa)*rad)
            bind sk = sk + 1 }
    }
    ถ้า typ == 5 {
        bind r2 = rad * prog;  bind nk = 0
        ขณะที่ nk < 16 { bind na0 = nk*0.3927;  bind na1 = (nk+1)*0.3927
            วาดสามเหลี่ยม3มิติ(cx+ไซน์(na0)*(r2-3.0),cy,cz+โคไซน์(na0)*(r2-3.0), cx+ไซน์(na0)*r2,cy,cz+โคไซน์(na0)*r2, cx+ไซน์(na1)*r2,cy,cz+โคไซน์(na1)*r2)
            วาดสามเหลี่ยม3มิติ(cx+ไซน์(na0)*(r2-3.0),cy,cz+โคไซน์(na0)*(r2-3.0), cx+ไซน์(na1)*r2,cy,cz+โคไซน์(na1)*r2, cx+ไซน์(na1)*(r2-3.0),cy,cz+โคไซน์(na1)*(r2-3.0))
            bind nk = nk + 1 }
    }
    flush_3d();  โหมดผสม(0)
}

# เส้นบิลบอร์ด: จุด 2D (ax,ay)->(bx,by) ในระนาบหันกล้อง ที่ฐานโลก (wx,wy,wz); rx,rz = แกนขวา (yaw)
ฟังก์ชัน เส้นป้าย(wx, wy, wz, ax, ay, bx, by, rx, rz) {
    วาดเส้น3มิติ(wx + ax*rx, wy - ay, wz + ax*rz,  wx + bx*rx, wy - by, wz + bx*rz)
}

# สามเหลี่ยมเติมจาก 3 จุด 2D (a=แนวขวากล้อง, b=แนวตั้ง/ขึ้น) ในระนาบหันกล้อง
ฟังก์ชัน สามเหลี่ยมป้าย(cx, cy, cz, a0, b0, a1, b1, a2, b2, rx, rz) {
    วาดสามเหลี่ยม3มิติ(cx+a0*rx, cy-b0, cz+a0*rz,  cx+a1*rx, cy-b1, cz+a1*rz,  cx+a2*rx, cy-b2, cz+a2*rz)
}

# ── สแลชทิศทาง: ฟันโค้งเติม ตามมุม dirAngle (0=นอน, ทวน 45° ต่อทิศ), กวาดตาม prog ──
ฟังก์ชัน วาดสแลชทิศ(cx, cy, cz, camYaw, dirAngle, prog, len, r, g, b) {
    โหมดผสม(1)
    bind rx = โคไซน์(camYaw);  bind rz = 0.0 - ไซน์(camYaw)
    bind ca = โคไซน์(dirAngle);  bind sa = ไซน์(dirAngle)   # แกนหลักใบมีด (2D)
    bind px = 0.0 - sa;  bind py = ca                        # แกนตั้งฉาก (ความหนา)
    สีดินสอ(r, g, b)
    bind sweep = 0.0 - 1.0 + prog*2.0
    bind k = 0
    ขณะที่ k < 10 {
        bind t0 = 0.0 - 1.0 + (k/10.0)*2.0
        bind t1 = 0.0 - 1.0 + ((k+1)/10.0)*2.0
        ถ้า t0 < sweep {
            bind w0 = (1.0 - t0*t0)*len*0.3
            bind w1 = (1.0 - t1*t1)*len*0.3
            bind c0a = ca*t0*len;  bind c0b = sa*t0*len
            bind c1a = ca*t1*len;  bind c1b = sa*t1*len
            สามเหลี่ยมป้าย(cx,cy,cz,  c0a+px*w0, c0b+py*w0,  c0a-px*w0, c0b-py*w0,  c1a+px*w1, c1b+py*w1, rx, rz)
            สามเหลี่ยมป้าย(cx,cy,cz,  c0a-px*w0, c0b-py*w0,  c1a-px*w1, c1b-py*w1,  c1a+px*w1, c1b+py*w1, rx, rz)
        }
        bind k = k + 1
    }
    flush_3d();  โหมดผสม(0)
}

# ── ริบบิ้นสแลช: ระนาบแบนที่โค้งตามการเหวี่ยง (เหวี่ยงรอบแกนเล็ง, เอียงตามเส้นฟัน) ──
#   aimYaw = ทิศเหวี่ยง · roll = เอียงเส้นฟัน (0=นอน, π/2=ตั้ง) · prog 0..1 · reach = ระยะใบมีด
ฟังก์ชัน วาดริบบิ้นสแลช(cx, cy, cz, aimYaw, roll, prog, reach, r, g, b, sat, spin) {
    โหมดผสม(1)
    bind roll = roll + spin                                  # หมุนระนาบตามอินพุตไมค์
    bind fade = 1.0 - prog                                   # จางออก
    bind lum = r*0.30 + g*0.59 + b*0.11                      # ปรับอิ่มสีตาม FFT เพลง
    bind sr = lum + (r-lum)*sat;  bind sg = lum + (g-lum)*sat;  bind sb = lum + (b-lum)*sat
    ถ้า sr < 0.0 { bind sr = 0.0 }  ถ้า sr > 255.0 { bind sr = 255.0 }
    ถ้า sg < 0.0 { bind sg = 0.0 }  ถ้า sg > 255.0 { bind sg = 255.0 }
    ถ้า sb < 0.0 { bind sb = 0.0 }  ถ้า sb > 255.0 { bind sb = 255.0 }
    สีดินสอ(floor(sr*fade), floor(sg*fade), floor(sb*fade))
    bind fwx = ไซน์(aimYaw);  bind fwz = โคไซน์(aimYaw)        # แกนเล็ง (หน้า)
    bind ppx = ไซน์(aimYaw + 1.5708);  bind ppz = โคไซน์(aimYaw + 1.5708)   # แกนตั้งฉากแนวนอน
    bind cr = โคไซน์(roll);  bind srl = ไซน์(roll)
    bind sdx = ppx*cr;  bind sdy = 0.0 - srl;  bind sdz = ppz*cr            # แกนเหวี่ยง = perp*cos(roll) + up*sin(roll)
    bind span = 1.45
    bind rOut = reach * (0.45 + prog*0.85)                   # โตขึ้น (scale out)
    bind rIn = rOut * 0.10                                    # ใบหนาขึ้น (ดาบหนา)
    bind k = 0
    ขณะที่ k < 10 {
        bind f0 = k / 10.0;  bind f1 = (k + 1) / 10.0
        ถ้า f0 < prog {
            bind a0 = 0.0 - span + f0*2.0*span;  bind a1 = 0.0 - span + f1*2.0*span
            bind c0 = โคไซน์(a0);  bind s0 = ไซน์(a0);  bind c1 = โคไซน์(a1);  bind s1 = ไซน์(a1)
            bind d0x = fwx*c0 + sdx*s0;  bind d0y = sdy*s0;  bind d0z = fwz*c0 + sdz*s0
            bind d1x = fwx*c1 + sdx*s1;  bind d1y = sdy*s1;  bind d1z = fwz*c1 + sdz*s1
            วาดสามเหลี่ยม3มิติ(cx+d0x*rIn,cy+d0y*rIn,cz+d0z*rIn,  cx+d0x*rOut,cy+d0y*rOut,cz+d0z*rOut,  cx+d1x*rOut,cy+d1y*rOut,cz+d1z*rOut)
            วาดสามเหลี่ยม3มิติ(cx+d0x*rIn,cy+d0y*rIn,cz+d0z*rIn,  cx+d1x*rOut,cy+d1y*rOut,cz+d1z*rOut,  cx+d1x*rIn,cy+d1y*rIn,cz+d1z*rIn)
        }
        bind k = k + 1
    }
    flush_3d();  โหมดผสม(0)
}

# ── สีรุ้ง ROYGBIV จากสเปกตรัม FFT: ผสมสีรุ้ง 8 ย่านถ่วงด้วยพลังงาน → "คลื่นเสียง" ──
ฟังก์ชัน สีรุ้งFFT(bands) {
    bind ROYG = [255,0,0,  255,127,0,  255,230,0,  60,230,0,  0,160,255,  50,30,230,  150,0,225,  220,0,160]
    bind cr = 6.0;  bind cg = 6.0;  bind cb = 10.0;  bind tot = 0.05
    bind i = 0
    ขณะที่ i < 8 {
        bind e = list_get(bands, i);  ถ้า e < 0.0 { bind e = 0.0 }
        bind e = e*e                                   # เน้นย่านเด่น
        bind b3 = i*3
        bind cr = cr + list_get(ROYG,b3)*e
        bind cg = cg + list_get(ROYG,b3+1)*e
        bind cb = cb + list_get(ROYG,b3+2)*e
        bind tot = tot + e
        bind i = i + 1
    }
    คืน [cr/tot, cg/tot, cb/tot]
}

# ── ดาบเหวี่ยง: ใบมีดที่กวาดตามส่วนโค้งเดียวกับสแลช (นอน/ตั้ง/ทแยง/กลับ) ──
ฟังก์ชัน วาดดาบเหวี่ยง(cx, cy, cz, aimYaw, roll, prog, reach, r, g, b) {
    โหมดผสม(0)
    bind span = 1.45
    bind a = 0.0 - span + prog*2.0*span                      # มุมเหวี่ยงปัจจุบัน (ขอบนำ)
    bind fwx = ไซน์(aimYaw);  bind fwz = โคไซน์(aimYaw)
    bind ppx = ไซน์(aimYaw + 1.5708);  bind ppz = โคไซน์(aimYaw + 1.5708)
    bind cr = โคไซน์(roll);  bind srl = ไซน์(roll)
    bind sdx = ppx*cr;  bind sdy = 0.0 - srl;  bind sdz = ppz*cr             # แกนเหวี่ยง (เอียงตามทิศฟัน)
    bind c = โคไซน์(a);  bind s = ไซน์(a)
    bind dx = fwx*c + sdx*s;  bind dy = sdy*s;  bind dz = fwz*c + sdz*s      # ทิศใบมีด
    bind ta = a + 1.5708;  bind tc = โคไซน์(ta);  bind ts = ไซน์(ta)
    bind tx = fwx*tc + sdx*ts;  bind ty = sdy*ts;  bind tz = fwz*tc + sdz*ts  # tangent (ความหนา)
    bind rH = reach*0.18;  bind rT = reach*1.15;  bind w = reach*0.06
    bind hax = cx+dx*rH+tx*w;  bind hay = cy+dy*rH+ty*w;  bind haz = cz+dz*rH+tz*w
    bind hbx = cx+dx*rH-tx*w;  bind hby = cy+dy*rH-ty*w;  bind hbz = cz+dz*rH-tz*w
    bind tpx = cx+dx*rT;  bind tpy = cy+dy*rT;  bind tpz = cz+dz*rT
    สีดินสอ(228, 232, 248)                                    # ใบมีดเงิน (สามเหลี่ยมเรียว)
    วาดสามเหลี่ยม3มิติ(hax,hay,haz, hbx,hby,hbz, tpx,tpy,tpz)
    สีดินสอ(r, g, b)                                          # การ์ด (สีดาบ)
    วาดเส้น3มิติ(cx+dx*rH+tx*w*2.6, cy+dy*rH+ty*w*2.6, cz+dz*rH+tz*w*2.6,  cx+dx*rH-tx*w*2.6, cy+dy*rH-ty*w*2.6, cz+dz*rH-tz*w*2.6)
    flush_3d()
}

# ── เทรลแหวน: ลิสต์แบนของ N ตัวอย่าง × 6 ช่อง (px,py,pz, vx,vy,vz) ──
ฟังก์ชัน รายการศูนย์(n) {
    bind L = [];  bind i = 0
    ขณะที่ i < n { bind L = list_push(L, 0.0);  bind i = i + 1 }
    คืน L
}

# เขียนตัวอย่างใหม่ลงหัวบัฟเฟอร์ (ตำแหน่ง+ความเร็ว) → คืนบัฟใหม่
ฟังก์ชัน เขียนเทรล(buf, head, px, py, pz, vx, vy, vz) {
    bind b = floor(head)*6
    bind buf = ตั้งรายการ(buf, b, px)
    bind buf = ตั้งรายการ(buf, b+1, py)
    bind buf = ตั้งรายการ(buf, b+2, pz)
    bind buf = ตั้งรายการ(buf, b+3, vx)
    bind buf = ตั้งรายการ(buf, b+4, vy)
    bind buf = ตั้งรายการ(buf, b+5, vz)
    คืน buf
}

# วาดเทรลแหวน: แหวนโซนิคบูม (เก่า=ใหญ่+จาง, ใหม่=เล็ก+สว่าง) ตั้งฉากกับทิศวิ่ง
# + แกนหนาเรียวลอดผ่านศูนย์กลางแหวน จางหายไปด้านท้าย
# สี: ผสมพาเลตลูก (br,bg,bb) + สีดาบ (sr,sg,sb), ปรับอิ่มสีด้วย sat (จาก FFT เพลง)
# spin = หมุนแหวน/แกนรอบทิศวิ่ง (จากไมค์) ; แกนเรียวกลับทาง (เก่า=หนา)
ฟังก์ชัน วาดเทรลแหวน(buf, head, N, br, bg, bb, sr, sg, sb, sat, spin) {
    โหมดผสม(1)                                  # บวกแสง (เรืองแสง)
    bind seg = 9.0
    bind bcr = (br + sr)*0.5;  bind bcg = (bg + sg)*0.5;  bind bcb = (bb + sb)*0.5   # ผสมพาเลต+ดาบ
    bind lum = bcr*0.30 + bcg*0.59 + bcb*0.11
    bind cr = lum + (bcr - lum)*sat;  bind cg = lum + (bcg - lum)*sat;  bind cb = lum + (bcb - lum)*sat
    ถ้า cr < 0.0 { bind cr = 0.0 }  ถ้า cr > 255.0 { bind cr = 255.0 }
    ถ้า cg < 0.0 { bind cg = 0.0 }  ถ้า cg > 255.0 { bind cg = 255.0 }
    ถ้า cb < 0.0 { bind cb = 0.0 }  ถ้า cb > 255.0 { bind cb = 255.0 }
    bind csp = โคไซน์(spin);  bind ssp = ไซน์(spin)
    bind havePrev = 0.0
    bind pcx = 0.0;  bind pcy = 0.0;  bind pcz = 0.0
    bind pux = 0.0;  bind puy = 0.0;  bind puz = 0.0;  bind page = 0.0
    bind i = 0.0
    ขณะที่ i < N {
        bind idx = floor((head + i) % N)        # i=0 เก่าสุด … i=N-1 ใหม่สุด
        bind b = idx*6
        bind px = list_get(buf, b);    bind py = list_get(buf, b+1);  bind pz = list_get(buf, b+2)
        bind vx = list_get(buf, b+3);  bind vy = list_get(buf, b+4);  bind vz = list_get(buf, b+5)
        bind sp = รากที่สอง(vx*vx + vy*vy + vz*vz)
        ถ้า sp > 0.012 {
            bind age = (N - 1.0 - i) / N          # ใหม่สุด≈0 … เก่าสุด≈1
            bind fade = 1.0 - age                 # ใหม่=สว่าง, เก่า=จาง
            bind alpha = fade * fade
            set_alpha(alpha)
            bind rad = (0.25 + sp*3.6) * (0.4 + age*1.4)   # ขนาดแปรตามความเร็ว; เก่า=ขยาย (โซนิคบูม)
            bind inv = 1.0 / sp
            bind dxn = vx*inv;  bind dyn = vy*inv;  bind dzn = vz*inv
            bind ux = 0.0 - dzn;  bind uz = dxn               # u = cross(d, up).xz
            bind ul = รากที่สอง(ux*ux + uz*uz)
            ถ้า ul < 0.0001 { bind ux = 1.0;  bind uz = 0.0;  bind ul = 1.0 }
            bind u0x = ux/ul;  bind u0y = 0.0;  bind u0z = uz/ul
            bind w0x = dyn*u0z - dzn*u0y;  bind w0y = dzn*u0x - dxn*u0z;  bind w0z = dxn*u0y - dyn*u0x   # w = cross(d,u)
            # หมุน (u,w) รอบทิศวิ่ง d ตามอินพุตไมค์
            bind uix = u0x*csp + w0x*ssp;  bind uiy = u0y*csp + w0y*ssp;  bind uiz = u0z*csp + w0z*ssp
            bind wx = w0x*csp - u0x*ssp;   bind wy = w0y*csp - u0y*ssp;   bind wz = w0z*csp - u0z*ssp
            สีดินสอ(cr*fade, cg*fade, cb*fade)
            bind k = 0.0
            ขณะที่ k < seg {
                bind a0 = (k / seg) * 6.2832;  bind a1 = ((k+1.0) / seg) * 6.2832
                bind c0 = โคไซน์(a0);  bind s0 = ไซน์(a0);  bind c1 = โคไซน์(a1);  bind s1 = ไซน์(a1)
                วาดเส้น3มิติ(px + (uix*c0 + wx*s0)*rad, py + (uiy*c0 + wy*s0)*rad, pz + (uiz*c0 + wz*s0)*rad,  px + (uix*c1 + wx*s1)*rad, py + (uiy*c1 + wy*s1)*rad, pz + (uiz*c1 + wz*s1)*rad)
                bind k = k + 1.0
            }
            ถ้า havePrev > 0.5 {
                bind wN = 0.1 + age*0.6;  bind wP = 0.1 + page*0.6     # แกนเรียวกลับทาง (เก่า=หนา)
                bind ax = pcx + pux*wP;  bind ay = pcy + puy*wP;  bind az = pcz + puz*wP
                bind bx = pcx - pux*wP;  bind by = pcy - puy*wP;  bind bz = pcz - puz*wP
                bind cx2 = px + uix*wN;  bind cy2 = py + uiy*wN;  bind cz2 = pz + uiz*wN
                bind dx2 = px - uix*wN;  bind dy2 = py - uiy*wN;  bind dz2 = pz - uiz*wN
                สีดินสอ((cr+255.0)*0.5*fade, (cg+255.0)*0.5*fade, (cb+255.0)*0.5*fade)   # แกนสว่างกว่าแหวน
                วาดสามเหลี่ยม3มิติ(ax,ay,az, bx,by,bz, dx2,dy2,dz2)
                วาดสามเหลี่ยม3มิติ(ax,ay,az, dx2,dy2,dz2, cx2,cy2,cz2)
            }
            bind pcx = px;  bind pcy = py;  bind pcz = pz
            bind pux = uix;  bind puy = uiy;  bind puz = uiz;  bind page = age
            bind havePrev = 1.0
        }
        bind i = i + 1.0
    }
    set_alpha(1.0)
    flush_3d();  โหมดผสม(0)
}

# ── ดีบักฮิตบ็อกซ์: วงกลมเส้นแดงบนระนาบ XZ ที่ y ──
ฟังก์ชัน วาดฮิตบ็อกซ์(cx, cy, cz, r) {
    โหมดผสม(1);  สีดินสอ(255, 60, 60)
    bind k = 0;  bind px = cx + r;  bind pz = cz
    ขณะที่ k < 24 {
        bind a = ((k + 1) / 24.0) * 6.28318
        bind nx = cx + โคไซน์(a)*r;  bind nz = cz + ไซน์(a)*r
        วาดเส้น3มิติ(px, cy, pz, nx, cy, nz)
        bind px = nx;  bind pz = nz;  bind k = k + 1
    }
    flush_3d();  โหมดผสม(0)
}

# ── ตัวเลขลอยแบบ 7-ส่วน (บิลบอร์ด) — ฟอนต์เวกเตอร์ไม่มีเลข เลยวาดเอง ──
ฟังก์ชัน บิตมี(mask, bit) { คืน floor(mask/bit) - floor(mask/(bit*2.0))*2.0 }

ฟังก์ชัน เลข7ส่วน(dg, wx, wy, wz, dx, w, h, rx, rz) {
    bind masks = [63,6,91,79,102,109,125,7,127,111]   # a=1,b=2,c=4,d=8,e=16,f=32,g=64
    bind m = list_get(masks, floor(dg));  bind h2 = h*0.5
    ถ้า บิตมี(m,1)  > 0.5 { เส้นป้าย(wx,wy,wz, dx,h, dx+w,h, rx,rz) }
    ถ้า บิตมี(m,2)  > 0.5 { เส้นป้าย(wx,wy,wz, dx+w,h, dx+w,h2, rx,rz) }
    ถ้า บิตมี(m,4)  > 0.5 { เส้นป้าย(wx,wy,wz, dx+w,h2, dx+w,0.0, rx,rz) }
    ถ้า บิตมี(m,8)  > 0.5 { เส้นป้าย(wx,wy,wz, dx,0.0, dx+w,0.0, rx,rz) }
    ถ้า บิตมี(m,16) > 0.5 { เส้นป้าย(wx,wy,wz, dx,h2, dx,0.0, rx,rz) }
    ถ้า บิตมี(m,32) > 0.5 { เส้นป้าย(wx,wy,wz, dx,h, dx,h2, rx,rz) }
    ถ้า บิตมี(m,64) > 0.5 { เส้นป้าย(wx,wy,wz, dx,h2, dx+w,h2, rx,rz) }
}

# n (จำนวนเต็ม 0..999) ลอยที่ (wx,wy,wz) สูง s, หันกล้อง camYaw, สี r,g,b
# เลขลอยเหนือหัว: ฟอนต์อังกฤษ + เครื่องหมาย, วนสี ROYGBIV เร็ว (~60 สี/วิ)
ฟังก์ชัน วาดเลขRGB(n, sign, wx, wy, wz, s, camYaw, t) {
    bind ph = floor(t*60.0) * 0.10472                              # 60 ก้าวสี/วินาที
    bind cr = floor((ไซน์(ph)*0.5+0.5)*255.0)
    bind cg = floor((ไซน์(ph+2.094)*0.5+0.5)*255.0)
    bind cb = floor((ไซน์(ph+4.189)*0.5+0.5)*255.0)
    bind str = to_str(floor(n))
    ถ้า sign > 0.5 { bind str = "+" + str }
    ถ้า sign < 0.0-0.5 { bind str = "-" + str }
    โหมดผสม(1);  สีดินสอ(cr, cg, cb)
    วาดสตริงป้าย(str, wx, wy, wz, s, camYaw)
    flush_3d();  โหมดผสม(0)
}

ฟังก์ชัน วาดเลขป้าย(n, sign, wx, wy, wz, s, camYaw, r, g, b) {
    bind rx = โคไซน์(camYaw);  bind rz = 0.0 - ไซน์(camYaw)
    สีดินสอ(r, g, b)
    bind nn = floor(n);  ถ้า nn < 0.0 { bind nn = 0.0 }
    bind nd = 1;  ถ้า nn >= 10.0 { bind nd = 2 }  ถ้า nn >= 100.0 { bind nd = 3 }
    bind w = s*0.6;  bind gap = s*0.34
    bind sw = 0.0;  ถ้า sign != 0.0 { bind sw = w + gap }                  # ช่องเครื่องหมาย +/-
    bind startx = 0.0 - (nd*w + (nd-1)*gap + sw)*0.5
    bind h2 = s*0.5
    ถ้า sign != 0.0 {
        เส้นป้าย(wx,wy,wz, startx, h2, startx+w, h2, rx,rz)                                       # ขีดนอน (−)
        ถ้า sign > 0.0 { เส้นป้าย(wx,wy,wz, startx+w*0.5, h2-w*0.55, startx+w*0.5, h2+w*0.55, rx,rz) }   # ขีดตั้ง → (+)
        bind startx = startx + sw
    }
    bind p = 0
    ขณะที่ p < nd {
        bind pw = nd - 1 - p;  bind divv = 1.0
        bind q = 0;  ขณะที่ q < pw { bind divv = divv*10.0;  bind q = q+1 }
        bind dg = floor(nn/divv) - floor(nn/(divv*10.0))*10.0
        เลข7ส่วน(dg, wx, wy, wz, startx + p*(w+gap), w, s, rx, rz)
        bind p = p+1
    }
}

# ── ไฮสกอร์: แทรกแบบเรียงมาก→น้อย, ตัดเหลือ 10 → คืน [names, scores] ──
ฟังก์ชัน แทรกไฮสกอร์(names, scores, nm, sc) {
    bind nN = [];  bind nS = [];  bind placed = 0.0;  bind i = 0
    ขณะที่ i < ความยาว(scores) {
        ถ้า placed < 0.5 { ถ้า sc > list_get(scores, i) { bind nN = list_push(nN, nm);  bind nS = list_push(nS, sc);  bind placed = 1.0 } }
        bind nN = list_push(nN, list_get(names, i));  bind nS = list_push(nS, list_get(scores, i))
        bind i = i + 1
    }
    ถ้า placed < 0.5 { bind nN = list_push(nN, nm);  bind nS = list_push(nS, sc) }
    bind cN = [];  bind cS = [];  bind j = 0
    ขณะที่ j < ความยาว(nS) { ถ้า j < 10 { bind cN = list_push(cN, list_get(nN, j));  bind cS = list_push(cS, list_get(nS, j)) }  bind j = j + 1 }
    คืน [cN, cS]
}

# ── บันทึกไฮสกอร์ → highscores.dat ("name,score;...") ──
ฟังก์ชัน บันทึกไฮสกอร์(names, scores) {
    bind out = "";  bind i = 0
    ขณะที่ i < ความยาว(scores) {
        ถ้า i > 0 { bind out = out + ";" }
        bind out = out + list_get(names, i) + "," + to_str(floor(list_get(scores, i)))
        bind i = i + 1
    }
    เขียนไฟล์("highscores.dat", out)
    คืน 0
}

# ── วาดตารางไฮสกอร์ (ชื่อ a-z + คะแนน 7-ส่วน) ──
# ── พื้นหลังไฮสกอร์: 4 ออร์บ AI สู้กันในอารีนา (หลังตารางคะแนน, หรี่แสง) ──
ฟังก์ชัน วาดเอไออารีนาพื้นหลัง(t) {
    โหมดผสม(1)
    bind i = 0
    ขณะที่ i < 4 {
        bind ph = i * 1.5708
        bind sp = 0.55 + i*0.13
        bind ox = โคไซน์(t*sp + ph) * (7.0 + i*0.6)
        bind oy = ไซน์(t*0.9 + ph) * 4.0
        bind oz = 16.0 + ไซน์(t*sp*0.7 + ph) * 4.0
        bind cr = floor((ไซน์(ph)*0.5+0.5)*180+50)
        bind cg = floor((ไซน์(ph+2.094)*0.5+0.5)*180+50)
        bind cb = floor((ไซน์(ph+4.189)*0.5+0.5)*180+50)
        orb_particles(ox, oy, oz, 1.1, 28.0, t*1.4+i*2.0, floor(cr*0.55), floor(cg*0.55), floor(cb*0.55))
        สีดินสอ(floor(cr*0.7), floor(cg*0.7), floor(cb*0.7))
        อนุภาคทรงกลม(ox, oy, oz, 0.7, 10, t*2.0+i, cr, cg, cb)
        bind i = i + 1
    }
    bind cl = ไซน์(t*1.9)                                       # ปะทะกลางสนามเป็นจังหวะ
    ถ้า cl > 0.7 { สีดินสอ(255, 235, 190);  อนุภาคทรงกลม(ไซน์(t*1.3)*4.0, โคไซน์(t)*2.0, 15.0, (cl-0.7)*9.0, 12, t*5.0, 255, 235, 190) }
    flush_3d();  โหมดผสม(0)
}

ฟังก์ชัน วาดไฮสกอร์(names, scores, t) {
    โหมดผสม(1)
    สีดินสอ(255, 220, 90);  วาดสตริง("high scores", 0.0, 0.0-7.2, 0.0, 1.1, 1)
    bind n = ความยาว(scores);  ถ้า n > 8 { bind n = 8 }
    bind i = 0
    ขณะที่ i < n {
        bind yy = 0.0-4.5 + i*1.5;  bind c = 245 - i*16
        สีดินสอ(c, c, 210);  วาดสตริง(list_get(names, i), 0.0-5.5, yy, 0.0, 0.6, 1)
        วาดเลขป้าย(list_get(scores, i), 0.0, 6.5, yy + 0.35, 0.0, 0.8, 0.0, 255, 225, 110)
        bind i = i + 1
    }
    flush_3d();  โหมดผสม(0)
}

# ── ดาบพิคอัพ slot 0..5 ที่ (wx,wy,wz) ขนาด s, หันกล้อง, บ๊อบตามเวลา ──
ฟังก์ชัน วาดดาบพิค(slot, wx, wy, wz, s, camYaw, t) {
    bind rx = โคไซน์(camYaw);  bind rz = 0.0 - ไซน์(camYaw)
    bind c = list_get(ดาบสี, slot)
    bind by = wy + ไซน์(t*2.0 + slot)*0.5
    โหมดผสม(1)
    สีดินสอ(list_get(c,0), list_get(c,1), list_get(c,2))
    อนุภาคทรงกลม(wx, by + s*1.1, wz, 0.8, 12, t*2.0+slot, list_get(c,0), list_get(c,1), list_get(c,2))
    เส้นป้าย(wx, by, wz,  0.0, 0.0-s,   0.0, s*0.5, rx, rz)            # ใบดาบ
    เส้นป้าย(wx, by, wz,  0.0-s*0.45, s*0.45,  s*0.45, s*0.45, rx, rz) # ด้ามขวาง
    ถ้า slot == 0 { เส้นป้าย(wx,by,wz, 0.0,0.0-s, s*0.3,0.0-s*0.55, rx,rz)  เส้นป้าย(wx,by,wz, 0.0,0.0-s, 0.0-s*0.3,0.0-s*0.55, rx,rz) }   # หัวลูกศร
    ถ้า slot == 1 { เส้นป้าย(wx,by,wz, s*0.3,0.0-s, s*0.3,s*0.5, rx,rz) }                                                              # ใบคู่
    ถ้า slot == 2 { เส้นป้าย(wx,by,wz, 0.0,0.0-s*0.4, s*0.25,0.0-s*0.05, rx,rz)  เส้นป้าย(wx,by,wz, s*0.25,0.0-s*0.05, 0.0,s*0.25, rx,rz) }  # เปลว
    ถ้า slot == 3 { เส้นป้าย(wx,by,wz, 0.0,0.0-s*0.45, s*0.3,0.0-s*0.1, rx,rz)  เส้นป้าย(wx,by,wz, s*0.3,0.0-s*0.1, 0.0,s*0.25, rx,rz)  เส้นป้าย(wx,by,wz, 0.0,s*0.25, 0.0-s*0.3,0.0-s*0.1, rx,rz)  เส้นป้าย(wx,by,wz, 0.0-s*0.3,0.0-s*0.1, 0.0,0.0-s*0.45, rx,rz) }  # เพชรน้ำแข็ง
    ถ้า slot == 4 { bind k = 0  ขณะที่ k < 8 { bind a0 = k*0.7854  bind a1 = (k+1)*0.7854  เส้นป้าย(wx,by,wz, โคไซน์(a0)*s*0.45,0.0-s*0.25+ไซน์(a0)*s*0.45, โคไซน์(a1)*s*0.45,0.0-s*0.25+ไซน์(a1)*s*0.45, rx,rz)  bind k = k+1 } }  # วงโนวา
    ถ้า slot == 5 { เส้นป้าย(wx,by,wz, 0.0-s*0.45,s*0.2, s*0.45,s*0.2, rx,rz) }                                                         # ด้ามคู่ (quake)
    flush_3d();  โหมดผสม(0)
}

# ── ป้ายลอยแบบบิลบอร์ด: หันเข้าหากล้อง (yaw), แนวนอนเสมอ, ขนาดจอคงที่ ──
#   ชดเชยระยะ: sc ∝ ระยะกล้อง → ไม่ย่อ/ขยายตามใกล้ไกล (zdist เกมเพลย์ 4.6, focal 1080)
ฟังก์ชัน วาดสตริงป้าย(s, ox, oy, oz, sscale, camYaw) {
    bind q = ฉาย3มิติ(ox, oy, oz)
    ถ้า list_get(q,0) <= 0.0 - 99998.0 { คืน 0 }      # หลังกล้อง → ข้าม
    bind d = list_get(q,2) + 4.6
    ถ้า d < 1.0 { bind d = 1.0 }
    bind sc = sscale * d * 0.030
    bind rx = โคไซน์(camYaw);  bind rz = 0.0 - ไซน์(camYaw)   # แกนขวา (บิลบอร์ด yaw)
    bind n = ความยาว(s)
    bind adv = sc * 0.66
    bind startk = 0.0 - (n - 1) * 0.5
    bind i = 0
    ขณะที่ i < n {
        bind ch = ส่วนสตริง(s, i, 1)
        bind code = ตัวอักษรเป็นดัชนี(ch)
        ถ้า code >= 0 {
            bind off = (startk + i) * adv
            วาดอักขระEnglish(code, ox + rx*off, oy, oz + rz*off, rx,0,rz, 0,0.0-1.0,0, sc)
        }
        bind i = i + 1
    }
}

# ── ศัตรู: ตารางชื่อ + คำคัตเอาต์ ──
bind ศัตรูชื่อ = ["Nyx","Ling","yisen","Zev","Quo","Vox","Jade","Bryn","Cade","Dax","Fynn","Gael","Hal","Ivo","Kael","Mika","Orin","Pax","Rune","Sage","Tynan","Ulla","Wren","Xena","Yael","Zion"]
bind บอสชื่อ = ["The Warden","Omega","Void King","Apex","Nemesis","The Maw"]
bind คำเปิด = ["lock in","spin the dice","cut the line","push the pace","break the seal","light the fuse","draw first blood"]
bind คำตี   = ["nice read","too slow","right there","clean shot","bad move"]
bind คำเพล  = ["fading fast","can't hold","almost done","no more left","falling apart"]
bind คำใบ้   = ["watch your flank","don't panic","move now","bait it out"]

ฟังก์ชัน สุ่มในรายการ(lst) { คืน list_get(lst, floor(สุ่ม() * ความยาว(lst))) }
ฟังก์ชัน ดึงคำเปิด() { คืน สุ่มในรายการ(คำเปิด) }
ฟังก์ชัน ดึงคำตี()   { คืน สุ่มในรายการ(คำตี) }
ฟังก์ชัน ดึงคำเพล()  { คืน สุ่มในรายการ(คำเพล) }
ฟังก์ชัน ดึงคำใบ้()   { คืน สุ่มในรายการ(คำใบ้) }

# สร้างศัตรู: คืน [hpMax, def, res, atk, cool, diff, bodyHue, stampHue, bandHue, stamp, band, name]
#   สเตตัส/ดิฟ ขึ้นตามรอบ (Smash-style gauntlet)
ฟังก์ชัน สร้างศัตรู(รอบ) {
    ถ้า รอบ >= 10.0 {                                   # ── บอสจบ (รอบ 10) ──
        คืน [620.0, 6.0, 0.45, 15.0, 0.5, 0.95, สุ่ม(), สุ่ม(), สุ่ม(), floor(สุ่ม()*STAMP_N), floor(สุ่ม()*BAND_N), สุ่มในรายการ(บอสชื่อ)]
    }
    bind hpMax = 55.0 + รอบ * 16.0
    bind def = รอบ * 0.6
    bind res = รอบ * 0.04;  ถ้า res > 0.45 { bind res = 0.45 }
    bind atk = 4.0 + รอบ * 1.5
    bind cool = 1.7 - รอบ * 0.08;  ถ้า cool < 0.6 { bind cool = 0.6 }
    bind diff = รอบ * 0.09;  ถ้า diff > 0.9 { bind diff = 0.9 }
    คืน [hpMax, def, res, atk, cool, diff, สุ่ม(), สุ่ม(), สุ่ม(), floor(สุ่ม()*STAMP_N), floor(สุ่ม()*BAND_N), สุ่มในรายการ(ศัตรูชื่อ)]
}

# ── วาดศัตรู: ลูกแต่ง (สีตามฮิว) + แฟลชโดนตี + วงการ์ด + ป้ายชื่อลอย ──
ฟังก์ชัน วาดศัตรู(fx, fy, fz, R, bh, sh, nh, stamp, band, t, hurt, guard, name, camYaw) {
    bind cb = สีจากเฉด(bh);  bind cs = สีจากเฉด(sh);  bind cn = สีจากเฉด(nh)
    bind br = list_get(cb,0);  bind bg = list_get(cb,1);  bind bb = list_get(cb,2)
    ถ้า hurt > 0.0 { bind br = 255;  bind bg = floor(120 + hurt*400);  bind bb = floor(120 + hurt*400) }
    วาดลูกแต่ง(fx, fy, fz, R, br,bg,bb,
        list_get(cs,0),list_get(cs,1),list_get(cs,2),
        list_get(cn,0),list_get(cn,1),list_get(cn,2),
        stamp, band, t, 0.0)
    ถ้า guard > 0.5 {
        โหมดผสม(1);  สีดินสอ(255, 210, 90)
        bind gk = 0
        ขณะที่ gk < 16 {
            bind a0 = gk * 0.3927;  bind a1 = (gk + 1) * 0.3927
            วาดเส้น3มิติ(fx+โคไซน์(a0)*R*1.6, fy, fz+ไซน์(a0)*R*1.6,  fx+โคไซน์(a1)*R*1.6, fy, fz+ไซน์(a1)*R*1.6)
            bind gk = gk + 1
        }
        flush_3d();  โหมดผสม(0)
    }
    โหมดผสม(1);  สีดินสอ(255, 235, 190);  วาดสตริงป้าย(name, fx, fy - R - 3.5, fz, 1.0, camYaw);  flush_3d();  โหมดผสม(0)
}

# ── แถบเลือด HUD (มุมจอ): พื้นเทา + เติมตามอัตราส่วน, ป้ายชื่อ ──
#   ใช้กล้องโอเวอร์เลย์ 2D (เรียกหลัง ตั้งกล้องมอง overlay) — x,y โลก, w กว้าง
ฟังก์ชัน แถบHP(x, y, w, frac, r, g, b, name, ชิด) {
    ถ้า frac < 0.0 { bind frac = 0.0 }
    สีดินสอ(40, 40, 55)
    วาดสามเหลี่ยม3มิติ(x, y-0.5, 0.0,  x+w, y-0.5, 0.0,  x+w, y+0.5, 0.0)
    วาดสามเหลี่ยม3มิติ(x, y-0.5, 0.0,  x+w, y+0.5, 0.0,  x, y+0.5, 0.0)
    bind fw = w * frac
    bind fx0 = x;  ถ้า ชิด > 0.5 { bind fx0 = x + w - fw }    # ชิด=1 → เติมจากขวา (ศัตรู)
    สีดินสอ(r, g, b)
    วาดสามเหลี่ยม3มิติ(fx0, y-0.5, 0.0,  fx0+fw, y-0.5, 0.0,  fx0+fw, y+0.5, 0.0)
    วาดสามเหลี่ยม3มิติ(fx0, y-0.5, 0.0,  fx0+fw, y+0.5, 0.0,  fx0, y+0.5, 0.0)
    โหมดผสม(1);  สีดินสอ(235, 235, 255)
    bind nx = x;  ถ้า ชิด > 0.5 { bind nx = x + w - ความยาว(name)*0.45 }
    วาดสตริง(name, nx + 1.5, y - 1.6, 0.0, 0.5, 1)
    โหมดผสม(0)
}

# ── ลูกแต่ง: body orb (สี) + โครงทรงกลม + สแตมป์ขั้วบน/ล่าง + แบนด์รอบกลาง ──
ฟังก์ชัน วาดลูกแต่ง(cx, cy, cz, R, br,bg,bb, sr,sg,sb, ar,ag,ab, stampId, bandId, t, fxId) {
    bind tb = t;  bind tc = t*0.6;  bind Rs = R;  bind bandCy = cy   # เอฟเฟกต์ (fx 0 = นิ่ง)
    ถ้า fxId == 1.0 { bind tb = t*3.0;  bind tc = t*2.2 }                          # spin: หมุนเร็ว
    ถ้า fxId == 2.0 { bind Rs = R * (1.0 + 0.09*ไซน์(t*3.0)) }                      # pulse: เต้น
    ถ้า fxId == 3.0 { bind tb = t*2.6;  bind bandCy = cy + ไซน์(t*1.6)*R*0.45 }      # orbit: แบนด์บ๊อบ+หมุน
    # body = อนุภาคเรืองแสง (additive)
    โหมดผสม(1)
    orb_particles(cx, cy, cz, Rs*0.95, 600.0, t,         br, bg, bb)       # ของเหลว: สีลำตัว (ชั้นนอก)
    orb_particles(cx, cy, cz, Rs*0.68, 340.0, t*1.3+2.0, sr, sg, sb)       # สีสแตมป์ (ชั้นกลาง)
    orb_particles(cx, cy, cz, Rs*0.42, 200.0, t*1.7+4.0, ar, ag, ab)       # สีแบนด์ (ชั้นใน)
    flush_3d()
    # โครงทรงกลม (วงละติจูด) ให้เห็นเป็นลูกชัดขึ้น
    สีดินสอ(floor(br*0.5), floor(bg*0.5), floor(bb*0.5))
    bind lat = 0
    ขณะที่ lat < 3 {
        bind ly = cy + (lat - 1) * R * 0.55
        bind lr = รากที่สอง(R*R - (ly-cy)*(ly-cy))
        bind k = 0
        ขณะที่ k < 20 {
            bind a0 = k * 0.31416;  bind a1 = (k + 1) * 0.31416
            วาดเส้น3มิติ(cx+โคไซน์(a0)*lr, ly, cz+ไซน์(a0)*lr,  cx+โคไซน์(a1)*lr, ly, cz+ไซน์(a1)*lr)
            bind k = k + 1
        }
        bind lat = lat + 1
    }
    flush_3d()
    # สแตมป์ + แบนด์ = ฟิลทึบ (normal blend)
    โหมดผสม(0)
    สีดินสอ(sr, sg, sb)
    วาดสแตมป์(stampId, cx, cy - Rs*1.0, cz, Rs*0.5, tc)            # ขั้วบน (เล็กลง)
    วาดสแตมป์(stampId, cx, cy + Rs*1.0, cz, Rs*0.5, 0.0 - tc)      # ขั้วล่าง
    สีดินสอ(ar, ag, ab)                                            # ลายเล็กในแกน (สีแบนด์) — สองโทน หมุนสวน
    วาดสแตมป์(stampId, cx, cy - Rs*1.0, cz, Rs*0.26, 0.0 - tc*1.5)
    วาดสแตมป์(stampId, cx, cy + Rs*1.0, cz, Rs*0.26, tc*1.5)
    สีดินสอ(ar, ag, ab)
    วาดแบนด์(bandId, cx, bandCy, cz, Rs*1.03, tb)
    flush_3d()
}

← Back to index