soul-symphony-ling
↓ 0 downloadsSoul 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-lingCode / Files (1947)
game/monster.ling
# ════════════════════════════════════════════════════════════════════════════
# monster.ling — มอนสเตอร์คริสตัลไวรัส: สเตตัส/ชนิด/ตำแหน่ง(AI) + โมเดล 3D + หนวดวงจร
# ย้ายมาจาก proc_gen_level.ling (อ้างอิงร่วม: pgRand, กระจายลาน, LVL_F เป็นโกลบอล)
# ฟังก์ชันทั้งหมดเป็นโกลบอล → เรียก pgRand/กระจายลาน/LVL_F (นิยามใน proc_gen_level/level)
# ได้ตราบที่ main.ling ใช้ทั้งสองไฟล์
# ════════════════════════════════════════════════════════════════════════════
# จำนวนมอนสเตอร์ ตามเลเวล (สูงสุด 31 — เกือบ 1 ตัว/ห้อง)
ฟังก์ชัน จำนวนมอน(lvl) { bind n = 5.0 + ปัดลง(lvl)*2.0; ถ้า n > 31.0 { bind n = 31.0 } คืน n } # ลาดชันเบาลง (เดิม 6+lvl*3)
# ── สปีชีส์มอน: 4 สายพันธุ์ × รูปคริสตัล × สี ROYGBIV × ระดับสเตตัส (โครงสร้าง) ──
# คืน [species(0-3), shape(0-3), hue(0-6 ROYGBIV), speedMul, hpMul, dmgMul]
# ช้า=อึด/แรง · เร็ว=เปราะ → สมดุล 4 ระดับ
ฟังก์ชัน สปีชีส์มอน(i, lvl) {
bind sp = ปัดลง(pgRand(i + 700.0, lvl) * 4.0); ถ้า sp > 3.0 { bind sp = 3.0 }
bind hue = ปัดลง(pgRand(i + 800.0, lvl) * 7.0); ถ้า hue > 6.0 { bind hue = 6.0 } # 0=แดง..6=ม่วง
คืน [sp, sp, hue, 0.6 + sp*0.3, 1.7 - sp*0.32, 0.7 + sp*0.4]
}
# สี ROYGBIV จาก hue index 0..6 → [r,g,b]
ฟังก์ชัน สีรุ้ง7(h) {
bind a = h * 0.8976 # 7 ช่วงรอบวงล้อสี (2π/7)
คืน [floor((ไซน์(a)*0.5+0.5)*205+45), floor((ไซน์(a+2.094)*0.5+0.5)*205+45), floor((ไซน์(a+4.189)*0.5+0.5)*205+45)]
}
# ชนิดมอน: 0 = drifter (ช้า เดินวน), 1 = hunter (เร็ว ไล่ผู้เล่น)
ฟังก์ชัน ชนิดมอน(i, lvl) {
ถ้า lvl < 1.5 { คืน 0 }
ถ้า pgRand(i + 500.0, lvl) > 0.55 { คืน 1 }
คืน 0
}
# 8 สเตตัส [STR,AGI,VIT,FOC,DEF,LCK,SPD,PWR] (สเกลตามเลเวล)
ฟังก์ชัน สเตตัสมอน(seed, lvl) {
bind b = 3.0 + lvl * 1.5
คืน [
floor(b + pgRand(seed+1.0, lvl)*lvl*3.0), floor(b + pgRand(seed+2.0, lvl)*lvl*3.0),
floor(b + pgRand(seed+3.0, lvl)*lvl*3.0), floor(b + pgRand(seed+4.0, lvl)*lvl*3.0),
floor(b + pgRand(seed+5.0, lvl)*lvl*3.0), floor(b + pgRand(seed+6.0, lvl)*lvl*3.0),
floor(b + pgRand(seed+7.0, lvl)*lvl*3.0), floor(b + pgRand(seed+8.0, lvl)*lvl*3.0)
]
}
# ฐานมอน: จุดเกิดมอน i → [bx, bz]
# ทิวทอเรียล (lvl<0.5): 1 ตัว/ห้อง — กลาง/เหนือ/ใต้/ออก/ตก ±704 (แมปห้องเดียวกับจุดเก็บอักษรใน main)
# ด่านอื่น: กระจายซันฟลาวเวอร์รอบจุดเกิดตาม spread (เหมือนเดิม)
ฟังก์ชัน ฐานมอน(i, n, lvl, spread) {
bind p = กระจายลาน(i, n, lvl+13.0, spread)
bind bx = list_get(p, 0); bind bz = list_get(p, 1)
ถ้า lvl < 0.5 {
bind bx = bx * 0.5; bind bz = bz * 0.5 # จิตเตอร์เล็กรอบกลางห้อง (~±22)
bind rm = i - ปัดลง(i/5.0)*5.0
ถ้า rm == 1.0 { bind bz = bz - 704.0 } # ห้องเหนือ
ถ้า rm == 2.0 { bind bz = bz + 704.0 } # ห้องใต้
ถ้า rm == 3.0 { bind bx = bx + 704.0 } # ห้องออก
ถ้า rm == 4.0 { bind bx = bx - 704.0 } # ห้องตก
}
คืน [bx, bz]
}
# ตำแหน่งมอน i → [x, z, anim]; AI ตามระยะ: idle/wander → walk → scurry(ไล่) → attack
# anim: 0 idle · 1 walk · 2 scurry · 3 attack ; speedMul จากสปีชีส์
# โกรธ = ตัวจับเวลาความแค้น (วิ) ที่ main ส่งเข้ามา ตั้งเมื่อมอนโดนตี/เห็นผู้เล่นใกล้
# > 0 = ล็อกเป้าไล่ล่าไม่เลิก ไม่ว่าจะไกลแค่ไหน
ฟังก์ชัน ตำแหน่งมอน(i, lvl, t, spread, typ, px, pz, โกรธ) {
bind sv = สปีชีส์มอน(i, lvl); bind spd = list_get(sv, 3) # speedMul สายพันธุ์
bind n = จำนวนมอน(lvl)
bind pB = ฐานมอน(i, n, lvl, spread)
bind bx = list_get(pB, 0); bind bz = list_get(pB, 1)
bind wr = 36.0 + pgRand(i + 340.0, lvl) * 24.0
bind a = t * (0.84 + pgRand(i + 420.0, lvl) * 0.64) * spd + i * 1.7
# เดินเล่น: เดิมเป็นวงกลมสมบูรณ์ (cos,sin รัศมีคงที่) มอนเลยดู "วิ่งวนเป็นวงกลม"
# ตลอดเวลา — ใส่ลิสซาจูส (คาบต่างกัน 0.61/0.83) + รัศมีที่หายใจเข้าออก
# เส้นทางเลยเป็นวงรีบิดที่ไม่ซ้ำรอยเดิม ดูเหมือนเดินสำรวจมากกว่าโคจร
bind wob = 1.0 + ไซน์(a*0.37 + i) * 0.35
bind ox = bx + โคไซน์(a*0.61) * wr * wob
bind oz = bz + ไซน์(a*0.83 + i*0.7) * wr * wob
bind dpx = px - ox; bind dpz = pz - oz; bind dd = รากที่สอง(dpx*dpx + dpz*dpz) + 0.001
bind anim = 0.0
ถ้า dd < spread { bind anim = 1.0 }
# ระยะล่า: กว้างขึ้นกว่าเดิม และ "โกรธ" = ล่าไม่จำกัดระยะ
bind huntR = 130.0; ถ้า typ > 0.5 { bind huntR = 200.0 }
ถ้า โกรธ > 0.0 { bind huntR = 100000.0 }
ถ้า dd < huntR {
bind chase = (0.06 + spd*0.16)
ถ้า typ < 0.5 { bind chase = chase * 0.6 }
ถ้า โกรธ > 0.0 { bind chase = chase * 2.1 } # แค้นแล้วเร่งเข้าใส่
ถ้า chase > 0.85 { bind chase = 0.85 }
# เข้าหาเป้าแบบส่ายเล็กน้อย (ตั้งฉากกับแนวเข้าหา) — ไม่พุ่งเป็นเส้นตรงจนดูเป็นหุ่น
bind nx = dpx/dd; bind nz = dpz/dd
bind weave = ไซน์(t*3.1 + i*2.2) * 0.22
ถ้า dd < 30.0 { bind weave = weave * (dd/30.0) } # ประชิดแล้วเลิกส่าย เข้าตรง ๆ
bind ox = ox + (nx + (0.0-nz)*weave) * dd * chase
bind oz = oz + (nz + nx*weave) * dd * chase
bind anim = 2.0
ถ้า dd < 24.0 { bind anim = 3.0 }
}
คืน [ox, oz, anim]
}
# ── ออกตาฮีดรอนเติม (ลำตัวมอนแหลม 3D) ────────────────────────────────────────
ฟังก์ชัน วาดออคตา(cx,cy,cz, rx,ry,rz) {
bind ty=cy-ry; bind by=cy+ry; bind ey=cy
bind ax=cx+rx; bind bx2=cx; bind cx2=cx-rx; bind dx2=cx
bind az=cz; bind bz2=cz+rz; bind cz2=cz; bind dz2=cz-rz
วาดสามเหลี่ยม3มิติ(cx,ty,cz, ax,ey,az, bx2,ey,bz2) วาดสามเหลี่ยม3มิติ(cx,ty,cz, bx2,ey,bz2, cx2,ey,cz2)
วาดสามเหลี่ยม3มิติ(cx,ty,cz, cx2,ey,cz2, dx2,ey,dz2) วาดสามเหลี่ยม3มิติ(cx,ty,cz, dx2,ey,dz2, ax,ey,az)
วาดสามเหลี่ยม3มิติ(cx,by,cz, bx2,ey,bz2, ax,ey,az) วาดสามเหลี่ยม3มิติ(cx,by,cz, cx2,ey,cz2, bx2,ey,bz2)
วาดสามเหลี่ยม3มิติ(cx,by,cz, dx2,ey,dz2, cx2,ey,cz2) วาดสามเหลี่ยม3มิติ(cx,by,cz, ax,ey,az, dx2,ey,dz2)
}
# ไอคอนบัฟ stat (0-7) ที่ (x,y) ระนาบ x-y — สีต่างกัน 8 แบบ (ใช้ทั้งมอน + ดรอป + HUD ผู้เล่น)
ฟังก์ชัน ไอคอนบัฟ(stat, x, y, z, s) {
bind h = stat * 0.785
สีดินสอ(floor((ไซน์(h)*0.5+0.5)*200+55), floor((ไซน์(h+2.094)*0.5+0.5)*200+55), floor((ไซน์(h+4.189)*0.5+0.5)*200+55))
วาดสามเหลี่ยม3มิติ(x,y-s,z, x+s,y,z, x,y+s,z)
วาดสามเหลี่ยม3มิติ(x,y-s,z, x,y+s,z, x-s,y,z)
}
# ── ท่อ (cylinder) ระหว่าง 2 จุด, รัศมี rad ──
ฟังก์ชัน วาดท่อ(ax, ay, az, bx, by, bz, rad, r, g, b) {
bind dx = bx-ax; bind dy = by-ay; bind dz = bz-az
bind ux = 0.0-dz; bind uz = dx
bind ul = รากที่สอง(ux*ux + uz*uz); ถ้า ul < 0.001 { bind ux = 1.0; bind uz = 0.0; bind ul = 1.0 }
bind uix = ux/ul; bind uiy = 0.0; bind uiz = uz/ul
bind wx = dy*uiz - dz*uiy; bind wy = dz*uix - dx*uiz; bind wz = dx*uiy - dy*uix
bind wl = รากที่สอง(wx*wx + wy*wy + wz*wz); ถ้า wl < 0.001 { bind wl = 1.0 }
bind wix = wx/wl; bind wiy = wy/wl; bind wiz = wz/wl
สีดินสอ(r, g, b)
bind sides = 4.0; bind s = 0.0
ขณะที่ s < sides {
bind a0 = (s/sides)*6.2832; bind a1 = ((s+1.0)/sides)*6.2832
bind c0 = โคไซน์(a0); bind n0 = ไซน์(a0); bind c1 = โคไซน์(a1); bind n1 = ไซน์(a1)
bind o0x=(uix*c0+wix*n0)*rad; bind o0y=(uiy*c0+wiy*n0)*rad; bind o0z=(uiz*c0+wiz*n0)*rad
bind o1x=(uix*c1+wix*n1)*rad; bind o1y=(uiy*c1+wiy*n1)*rad; bind o1z=(uiz*c1+wiz*n1)*rad
วาดสามเหลี่ยม3มิติ(ax+o0x,ay+o0y,az+o0z, ax+o1x,ay+o1y,az+o1z, bx+o1x,by+o1y,bz+o1z)
วาดสามเหลี่ยม3มิติ(ax+o0x,ay+o0y,az+o0z, bx+o1x,by+o1y,bz+o1z, bx+o0x,by+o0y,bz+o0z)
bind s = s + 1.0
}
}
# ── แขนวงจร (circuit limb): ท่อตรงหักมุมฉาก + โหนดเรืองแสง (สังเคราะห์ ไม่ออร์แกนิก) ──
ฟังก์ชัน วาดหนวด(bx, by, bz, ang, droop, len, t, phase, r, g, b) {
bind segs = 3.0; bind sg = len/segs
bind px = bx; bind py = by; bind pz = bz; bind k = 0.0
ขณะที่ k < segs {
bind f = (k+1.0)/segs
bind bend = (k - ปัดลง(k/2.0)*2.0) * 0.95 - 0.475 # หักสลับ ± (เทรซวงจร)
bind nx = px + โคไซน์(ang + bend)*sg
bind nz = pz + ไซน์(ang + bend)*sg
bind ny = py + droop*sg
วาดท่อ(px, py, pz, nx, ny, nz, 0.40*(1.0 - f*0.4), floor(r*0.5), floor(g*0.5), floor(b*0.5)) # เทรซบาง หรี่
bind pls = ไซน์(t*6.0 + phase + k*1.7)*0.5+0.5 # พัลส์ไฟฟ้า
โหมดผสม(1); อนุภาคทรงกลม(nx, ny, nz, 0.35+pls*0.4, 4, t*5.0, floor(60+pls*120), 255, floor(150+pls*100)) # โหนดไซแอน-เขียว
flush_3d(); โหมดผสม(0)
bind px = nx; bind py = ny; bind pz = nz; bind k = k + 1.0
}
โหมดผสม(1); สีดินสอ(120, 255, 200); อนุภาคทรงกลม(px, py, pz, 0.7, 4, t*5.0+phase, 120, 255, 200); flush_3d(); โหมดผสม(0) # พินคอนเนกเตอร์
}
# ── มอนคริสตัลแวมไพร์มิติที่ 5 (5th-dimensional vampire crystal) ──
# ผลึกลอยไร้ขา: แกนมืดแวมไพร์ (ออร่าเลือด) + สะเก็ดผลึกโคจรหลายแกน (เฟสเข้า-ออก)
# + ปีกผลึกกระพือ + ประกายดิจิทัล (digital organism). anim: 0 idle·1 walk·2 scurry·3 attack
# hitT>0 = โดนตี (สั่น+แฟลชขาว) · dieT>0 (1→0) = กำลังตาย (ยุบ+เผาเป็นเถ้า)
ฟังก์ชัน วาดมอน(x, z, floorY, t, seed, typ, hpfrac, lvl, anim, hitT, dieT) {
bind sv = สปีชีส์มอน(seed, lvl) # [sp, shape, hue, speedMul, hpMul, dmgMul]
bind shape = list_get(sv, 1)
bind col = สีรุ้ง7(list_get(sv, 2))
bind r1 = list_get(col, 0); bind g1 = list_get(col, 1); bind b1 = list_get(col, 2)
bind spin = t*0.8 + seed
bind spin2 = t*1.3 + seed*2.0 # แกนหมุนที่ 2 (มิติที่ 5)
bind bob = ไซน์(t*1.6 + seed) * 0.7
# ── กระโดด/กระโจน (pounce): scurry/attack → กระเด้งขึ้นเป็นจังหวะ ──
bind hopH = 0.0
ถ้า anim > 1.5 { bind hopH = 3.2 + anim*1.4 }
bind hop = 0.0
ถ้า hopH > 0.0 { bind hp0 = ไซน์(t*6.0 + seed); ถ้า hp0 < 0.0 { bind hp0 = 0.0 - hp0 } bind hop = hp0*hopH }
bind by = (floorY - 8.0) - hop + bob # ลอยเหนือพื้น (up = -y → ลบ = สูง)
# ── โดนตี: สั่น + แฟลชขาว (anim เล่นจนครบ hitT) ──
bind hit = hitT / 0.35; ถ้า hit > 1.0 { bind hit = 1.0 } ถ้า hit < 0.0 { bind hit = 0.0 }
bind mx = x + ไซน์(t*80.0 + seed)*hit*1.8
bind mz = z + โคไซน์(t*74.0 + seed)*hit*1.8
bind fr = floor(r1 + (255.0-r1)*hit); bind fg = floor(g1 + (255.0-g1)*hit); bind fb = floor(b1 + (255.0-b1)*hit)
# ════ กำลังตาย: ยุบ (crumple) + เผาเป็นเถ้า (burn away) ════
ถ้า dieT > 0.0 {
bind burn = 1.0 - dieT # 0→1 ระหว่างตาย
โหมดผสม(0)
ถ้า burn < 0.6 { # ครึ่งแรก: แกนผลึกหดหมุนบ้า
bind cr = 3.0 * (1.0 - burn/0.6)
สีดินสอ(floor(150*(1.0-burn)), floor(30*(1.0-burn)), floor(45*(1.0-burn)))
วาดออคตา(mx, by, mz, cr, cr*2.0, cr)
}
bind dk = 0.0 # สะเก็ดกระเด็นออก (crumple)
ขณะที่ dk < 6.0 {
bind da = (dk/6.0)*6.2832 + spin
bind dr2 = burn*7.0; bind ds = (1.0 - burn)*1.3
ถ้า ds > 0.05 {
สีดินสอ(floor(fr*0.5), floor(fg*0.4), floor(fb*0.5))
วาดออคตา(mx + โคไซน์(da)*dr2, by + burn*3.0 + ไซน์(dk), mz + ไซน์(da)*dr2, ds, ds*1.6, ds)
}
bind dk = dk + 1.0
}
โหมดผสม(1) # เผา: เถ้าถ่านลอยขึ้น (ส้ม-แดง เฟดหาย)
bind ek = 0.0
ขณะที่ ek < 14.0 {
bind ea = ek*0.9 + t*3.0; bind erad = 1.0 + burn*4.5; bind ef = 1.0 - burn
อนุภาคทรงกลม(mx + โคไซน์(ea)*erad, by - burn*11.0 - ek*0.35, mz + ไซน์(ea)*erad, 0.7*ef, 3, t*5.0+ek, 255, floor(120*ef+40), 30)
bind ek = ek + 1.0
}
flush_3d(); โหมดผสม(0)
คืน 0
}
# ════ มีชีวิต: ผลึกแวมไพร์ ════
# ── ออร่าเลือด + ประกายดิจิทัล + เงาซ้อนมิติที่ 5 (additive) ──
โหมดผสม(1)
bind aura = 0.6 + ไซน์(t*3.0 + seed)*0.25 + anim*0.15
อนุภาคทรงกลม(mx, by, mz, 2.6*aura, floor(8+anim*3), t*2.0+seed, 150, 24, 44) # แกนเลือด
อนุภาคทรงกลม(mx, by, mz, 3.2, 6, t*7.0+seed, 55, 145, 190) # ประกายไซแอน (ดิจิทัล)
อนุภาคทรงกลม(mx + ไซน์(spin2)*2.0, by + โคไซน์(spin2)*1.5, mz + โคไซน์(spin2)*2.0, 2.4, 6, t*4.0+seed, floor(fr*0.6), floor(fg*0.4), floor(fb*0.7)) # เงาซ้อน (after-image)
flush_3d()
โหมดผสม(0)
# ── แกนผลึกมืด (เพชรสูง) — แวมไพร์: มืด ขอบเรือง; สัดส่วนต่างตามสายพันธุ์ ──
bind pulse = 1.0 + ไซน์(t*4.0 + seed)*0.08 + hit*0.3
bind coreR = 3.0 * pulse; ถ้า shape == 3.0 { bind coreR = 3.6 * pulse }
bind coreH = coreR*2.1; ถ้า shape == 2.0 { bind coreH = coreR*2.7 }
set_flat_shade(0.0)
set_material("emission", fr*65536.0 + fg*256.0 + fb)
bind em = 0.24 + ไซน์(t*5.0 + seed)*0.08 + anim*0.04 + hit*0.22
ถ้า em < 0.12 { bind em = 0.12 }
ถ้า em > 0.58 { bind em = 0.58 }
set_material("emission_strength", em)
set_material("roughness", 0.42)
set_material("specular", 0.38)
สีดินสอ(floor(fr*0.35 + 20), floor(fg*0.18 + 8), floor(fb*0.32 + 18))
วาดออคตา(mx, by, mz, coreR, coreH, coreR)
# แกนใน (บัฟที่จะดรอป) เรือง
โหมดผสม(1)
bind bstat = floor(pgRand(seed+99.0, lvl) * 8.0)
ไอคอนบัฟ(bstat, mx, by, mz-0.3, 1.4)
flush_3d(); โหมดผสม(0)
# ── สะเก็ดผลึกโคจรหลายแกน (5th-dim): เฟสเข้า-ออก + กางออกตอน attack ──
bind splay = 0.0; ถ้า anim > 2.5 { bind splay = 2.2 } # attack → สะเก็ดพุ่งกาง
bind shardN = 6.0; bind sk = 0.0
ขณะที่ sk < shardN {
bind sa = (sk/shardN)*6.2832 + spin*1.6
bind ring = sk*1.05 + spin2*0.5 # เอียงวงต่อสะเก็ด (แกน 2)
bind orbR = 4.6 + splay + ไซน์(t*2.0 + sk)*0.5
bind pin = ไซน์(t*3.0 + sk*1.7)*0.5 + 0.5 # เฟสเข้า-ออก
bind ss = 0.6 + pin*0.8; bind br = 0.5 + pin*0.5
สีดินสอ(floor(fr*br), floor(fg*br), floor(fb*br))
วาดออคตา(mx + โคไซน์(sa)*orbR, by + ไซน์(sa)*orbR*ไซน์(ring), mz + ไซน์(sa)*orbR*โคไซน์(ring), ss, ss*1.7, ss)
bind sk = sk + 1.0
}
# ── ปีกผลึกแวมไพร์: สะเก็ดสามเหลี่ยมสองข้าง กระพือช้า ──
bind flap = ไซน์(t*2.4 + seed)*0.5 + 0.5 # 0..1
bind wyt = by - 2.0 - flap*2.0
สีดินสอ(floor(fr*0.4 + 15), floor(fg*0.2 + 8), floor(fb*0.45 + 20))
วาดสามเหลี่ยม3มิติ(mx+1.5, by, mz, mx+5.5+splay, wyt, mz-1.0, mx+6.5+splay, by+3.0, mz+0.5) # ปีกขวา
วาดสามเหลี่ยม3มิติ(mx+1.5, by, mz, mx+6.5+splay, by+3.0, mz+0.5, mx+4.0, by+4.5, mz)
วาดสามเหลี่ยม3มิติ(mx-1.5, by, mz, mx-5.5-splay, wyt, mz-1.0, mx-6.5-splay, by+3.0, mz+0.5) # ปีกซ้าย
วาดสามเหลี่ยม3มิติ(mx-1.5, by, mz, mx-6.5-splay, by+3.0, mz+0.5, mx-4.0, by+4.5, mz)
flush_3d()
reset_material()
# ── แถบเลือด เหนือหัว (กรอบ + แถบ) ──
bind hy = by - 9.0
bind hf = hpfrac; ถ้า hf > 1.0 { bind hf = 1.0 } ถ้า hf < 0.0 { bind hf = 0.0 }
โหมดผสม(0)
สีดินสอ(20, 6, 8)
draw_quad_3d(mx-4.3,hy+0.15,mz, mx+4.3,hy+0.15,mz, mx+4.3,hy-0.95,mz, mx-4.3,hy-0.95,mz)
flush_3d()
bind hw = 8.0 * hf
สีดินสอ(floor(235*(1.0-hf)+30), floor(60+180*hf), 70) # เขียว(เต็ม) → แดง(ใกล้ตาย)
draw_quad_3d(mx-4.0,hy,mz, mx-4.0+hw,hy,mz, mx-4.0+hw,hy-0.8,mz, mx-4.0,hy-0.8,mz)
flush_3d()
}
# ── เสียงมอน: เสียง/คอลเอาต์เฉพาะตัว ตามสายพันธุ์ (sp 0-3) + สถานะ (anim) ──
# anim: 0 idle (พึมพำเบา) · 1 walk · 2 scurry (จิ๊บ ๆ) · 3 attack (คำรามมืดดิจิทัล)
# sp กำหนดวัสดุ/พิตช์ → 4 สายพันธุ์ฟังต่างกัน; ใช้ engine ใหม่ (โน้ตมอร์ฟ) + GM waves
ฟังก์ชัน เสียงมอน(sp, anim, x, y, z, vol) {
bind hz = 84.0 + sp*52.0 # พิตช์ฐานต่อสายพันธุ์
bind mat = floor(sp) # วัสดุ 0-3 ต่อสายพันธุ์
ถ้า anim > 2.5 { # ── โจมตี: คำรามเปียก + กรี๊ดแตก ──
# ซ้อนสามชั้น: ก้นคอต่ำ (เนื้อ) + ฟันเลื่อยกลาง (คำราม) + นอยส์แหลม (กรีดร้อง)
โน้ตมอร์ฟ(x, y, z, 1.0, hz*0.55, vol*0.40, 0.42, 3.0, 1.0)
เสียงเอฟเฟกต์(x, y, z, 1.0, hz*1.5, vol*0.30, 0.26, "saw")
เสียงเอฟเฟกต์(x, y, z, 1.0, hz*3.0 + sp*40.0, vol*0.20, 0.12, "square")
เสียงเอฟเฟกต์(x, y, z, 1.0, 2600.0 + sp*300.0, vol*0.13, 0.09, "noise")
คืน 0
}
ถ้า anim < 0.5 { # ── idle: พึมพำเบาออร์แกนิก ──
โน้ตมอร์ฟ(x, y, z, 1.0, hz, vol*0.10, 0.7, mat, 0.22)
คืน 0
}
# ── walk/scurry: จิ๊บสกิตเตอร์ (มืดขึ้นตามความเร็ว) ──
bind mor = 0.45 + anim*0.18; ถ้า mor > 1.0 { bind mor = 1.0 }
โน้ตมอร์ฟ(x, y, z, 1.0, hz*2.0, vol*0.15, 0.16, 1.0, mor)
เสียงเอฟเฟกต์(x, y, z, 1.0, hz*4.0, vol*0.08, 0.05, "triangle")
คืน 0
}
# ── โดนตี: ครางเจ็บ — พิตช์ตกจากเสียงร้องลงไปหาก้นคอ + เศษคริสตัลแตก ─────────
# ความแรง (0..1) = ต่างกันตามอาวุธ ดาบ/ธนู/ระเบิด (main ส่งเข้ามา)
ฟังก์ชัน เสียงมอนเจ็บ(sp, x, y, z, vol, แรง) {
bind hz = 84.0 + sp*52.0
โน้ตมอร์ฟ(x, y, z, 1.0, hz*1.25, vol*(0.26 + แรง*0.24), 0.20 + แรง*0.16, 3.0, 1.0)
เสียงเอฟเฟกต์(x, y, z, 1.0, hz*2.1, vol*(0.16 + แรง*0.18), 0.13, "saw")
เสียงเอฟเฟกต์(x, y, z, 1.0, 3200.0, vol*(0.10 + แรง*0.14), 0.06, "noise") # คริสตัลบิ่น
คืน 0
}
# ── ตาย: กรี๊ดยาวจมลงต่ำ + คริสตัลระเบิด + หางลมหายใจ ──────────────────────
ฟังก์ชัน เสียงมอนตาย(sp, x, y, z, vol) {
bind hz = 84.0 + sp*52.0
เสียงเอฟเฟกต์(x, y, z, 1.0, hz*2.6, vol*0.42, 0.55, "saw") # กรี๊ด
โน้ตมอร์ฟ(x, y, z, 1.0, hz*0.5, vol*0.46, 0.85, 3.0, 1.0) # จมลงก้นคอ
เสียงเอฟเฟกต์(x, y, z, 1.0, 180.0, vol*0.34, 0.42, "square") # เนื้อคริสตัลยุบ
เสียงเอฟเฟกต์(x, y, z, 1.0, 4200.0, vol*0.24, 0.30, "noise") # สะเก็ดแตกกระจาย
คืน 0
}