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/companions.ling
# โมเดลราชา/ราชินี (วาดKing/วาดQueen) โหลดจาก main.ling (../game/king_model + queen_model) ก่อน companions
# (เดิมใช้ LOD อบ king_lod/queen_lod — เปลี่ยนเป็นโมเดลสดเพื่อรองรับแอนิเมชัน)
# companions.ling — สหาย AI ราชา/ราชินี ในโหมดสตอรี่
# ราชา = ลูกหมึกน้ำเงินโปร่งใส, ราชินี = ลูกหมึกแดงโปร่งใส; เดินอยู่ "ในลูก"
# ท่าทาง (anim): 0 idle · 1 walk · 2 run · 3 jump · 4 attack · 5 guard
# โมเดลจาก ../game/king_model + ../game/queen_model (แปลงด้วย tools/glb2ling.py)
# (เลิกใช้ อบสหาย/LOD — วาดโมเดลสด วาดKing/วาดQueen ใน วาดสหาย เพื่อรองรับแอนิเมชัน)
# ── แขนสหาย: เมชหลักเป็น rigid triangle-soup จึงเสริมแขน pose-down แบบ procedural ──
ฟังก์ชัน จุดสหาย(mx, my, mz, yaw, sc, lx, ly, lz) {
bind cs = โคไซน์(yaw); bind sn = ไซน์(yaw)
คืน [mx + (lx*cs + lz*sn)*sc, my + ly*sc, mz + (lz*cs - lx*sn)*sc]
}
ฟังก์ชัน วาดแขนสหาย(meshId, mx, my, mz, yaw, sc, armSwing, anim, t, r, g, b) {
bind shoulderX = 0.46; bind elbowX = 0.50; bind handX = 0.43
ถ้า meshId > 0.5 { bind shoulderX = 0.56; bind elbowX = 0.60; bind handX = 0.52 }
bind shoulderY = 0.0 - 2.02; bind elbowY = 0.0 - 1.46; bind handY = 0.0 - 0.92
bind swing = armSwing * 0.26
ถ้า anim == 6.0 { bind swing = swing * 0.25 }
ถ้า anim == 3.0 { bind swing = ไซน์(t*8.0) * 0.08 }
bind side = 0.0 - 1.0
ขณะที่ side <= 1.0 {
bind sh = จุดสหาย(mx, my, mz, yaw, sc, shoulderX*side, shoulderY, 0.02)
bind el = จุดสหาย(mx, my, mz, yaw, sc, elbowX*side, elbowY, swing*side*0.45)
bind ha = จุดสหาย(mx, my, mz, yaw, sc, handX*side, handY, swing*side)
สีดินสอ(floor(r*0.72+42), floor(g*0.72+42), floor(b*0.72+42))
draw_line_3d(list_get(sh,0), list_get(sh,1), list_get(sh,2), list_get(el,0), list_get(el,1), list_get(el,2))
draw_line_3d(list_get(el,0), list_get(el,1), list_get(el,2), list_get(ha,0), list_get(ha,1), list_get(ha,2))
สีดินสอ(238, 205, 150)
draw_line_3d(list_get(ha,0)-0.05*sc, list_get(ha,1), list_get(ha,2), list_get(ha,0)+0.05*sc, list_get(ha,1), list_get(ha,2))
bind side = side + 2.0
}
}
# ── วาดสหาย: โมเดลสดในลูกของเหลวจริง + ท่าทาง ──
# meshId = 0 ราชา, 1 ราชินี
# (cx,cy,cz)=ฐานเท้า · phase=เฟสก้าว · yaw=ทิศ · anim=ท่า ; โลกนี้ up = -Y
ฟังก์ชัน วาดสหาย(meshId, liqH, ballR, cx, cy, cz, vx, vz, phase, yaw, anim, t, swR, swG, swB, slashP, flatShade) {
bind modelH = 3.0
bind sc = (ballR * 1.18) / modelH # สูง ~59% ของเส้นผ่าศูนย์กลางลูก; แขน/เด้งยังอยู่ใน orb
bind speed = รากที่สอง(vx*vx + vz*vz)
bind gait = speed * 5.0; ถ้า gait > 1.0 { bind gait = 1.0 } # 0 idle … 1 run (ช้าลง — ไม่ frantic)
# ── วงจรเดิน/วิ่ง: แขน+ขาสลับเฟส (ธรรมชาติ) · เด้ง 2 ครั้ง/รอบ · sync กับการกลิ้ง ──
bind sw = ไซน์(phase) # การแกว่งหลัก
bind breath = ไซน์(t*1.5 + cx*0.2)*0.025
bind bob = 0.0 - (ไซน์(phase*2.0)*0.05*gait + breath)*sc # เด้งตอนผ่านเท้า
bind sway = sw*0.07*gait + ไซน์(t*1.5 + cz*0.2)*0.022 # เอนสะโพก
bind arm = sw*0.34*gait + ไซน์(t*1.4 + cx*0.3)*0.04 # แขนแกว่ง (สลับซ้าย-ขวา)
bind leg = 0.0 - sw*0.22*gait # ขาแกว่งตรงข้ามแขน (ธรรมชาติ)
bind lean = gait*0.24; bind tuck = 0.0; bind fwd = 0.08*gait # ก้มวิ่งเล็กน้อย
ถ้า anim == 6.0 { bind tuck = 0.40; bind bob = 0.20*sc; bind arm = 0.0-0.15; bind leg = 0.0; bind lean = 0.18; bind fwd = 0.0 } # ชาร์จ (ย่อตัว)
ถ้า anim == 3.0 { bind tuck = 0.72; bind bob = 0.0; bind arm = ไซน์(t*8.0)*0.14; bind leg = 0.0; bind lean = 0.14 } # ลอย (เข่ายกขึ้น)
ถ้า anim == 7.0 { bind tuck = 0.45; bind fwd = 0.38; bind arm = ไซน์(phase*5.0)*0.5; bind lean = 0.42; bind leg = 0.1 } # จู่โจมกลางอากาศ
ถ้า anim == 4.0 { bind fwd = 0.40; bind arm = ไซน์(phase*4.0)*0.45; bind lean = 0.32; bind leg = 0.0-sw*0.1 } # โจมตีพื้น
ถ้า anim == 5.0 { bind fwd = 0.0-0.16; bind bob = 0.05*sc; bind arm = 0.0-0.2; bind leg = 0.0; bind lean = 0.0-0.1 } # การ์ด
bind fx = ไซน์(yaw)*fwd*sc; bind fz = โคไซน์(yaw)*fwd*sc
bind mx = cx + fx; bind mz = cz + fz; bind my = cy + bob
# ── โมเดล LOD อบเป็นดิสเพลย์ลิสต์ (เล่นซ้ำเนทีฟ — เร็วกว่าเมชเต็ม ~54x) ──
# meshId = แฮนเดิลเมชอบ (king/queen LOD); yaw หมุนรอบ Y
โหมดผสม(0)
# ── แอนิเมชันทั้งตัว (ling-animation): โมเดลหมุนแกน Y → บิดลำตัว + หายใจ/สควอช ──
bind twist = ก้าวแกว่ง(phase, 1.0, 1.0)*0.20*gait # บิดลำตัวตอนเดิน (gait_swing)
ถ้า anim == 3.0 { bind twist = ไซน์(t*10.0)*0.14 } # ลอย
ถ้า anim == 7.0 { bind twist = ไซน์(phase*5.0)*0.5 } # จู่โจมกลางอากาศ
ถ้า anim == 4.0 { bind twist = ไซน์(phase*4.0)*0.6 } # โจมตีพื้น
bind sqz = 1.0; ถ้า gait < 0.15 { bind sqz = หายใจ(t, 1.1, 0.05) } # idle → หายใจ (breathe)
ถ้า anim == 6.0 { bind sqz = 0.90 } # ชาร์จ: ย่อ
ถ้า anim == 3.0 { bind sqz = 1.10 } # ลอย: ยืด
ถ้า anim == 5.0 { bind sqz = 0.94 } # การ์ด: ย่อ
bind myaw = yaw + twist; bind dsc = sc * sqz
# ── โมเดลในลูก = BSDF จริง (ปิด flat + ตั้งวัสดุผิว/ผ้า/เคลือบ) — ที่เหลือในเลเวลยังแบน/ตูน ──
set_flat_shade(0.0)
set_material("roughness", 0.55)
set_material("specular", 0.35)
set_material("subsurface", 0.28)
set_material("sheen", 0.15)
set_material("clearcoat", 0.20)
ถ้า meshId < 0.5 { วาดKing(mx, my, mz, dsc, myaw) } มิฉะนั้น { วาดQueen(mx, my, mz, dsc, myaw) }
วาดแขนสหาย(meshId, mx, my, mz, myaw, dsc, arm, anim, t, swR, swG, swB)
flush_3d()
reset_material()
set_flat_shade(flatShade)
# ── ดาบฟัน (ราชา=แดง · ราชินี=น้ำเงิน) เมื่อโจมตี ──
ถ้า slashP > 0.0-0.5 {
bind ssy = my - sc*1.5
วาดริบบิ้นสแลช(mx, ssy, mz, yaw, 0.0, slashP, sc*1.4, swR, swG, swB, 1.0, 0.0)
วาดดาบเหวี่ยง(mx, ssy, mz, yaw, 0.0, slashP, sc*1.4, swR, swG, swB)
flush_3d()
}
# ── ลูกของเหลวจริง โปร่งใส (รัศมี ballR, ศูนย์ที่กลางลำตัวโมเดล = my - 1.5*sc) ──
โหมดผสม(1)
liquid_draw_surface(liqH, 1.0, mx, my - sc*1.5, mz, ballR, ballR)
flush_3d(); โหมดผสม(0)
}
# ── AI สหาย: คืน [vx, vz, anim, jump] — พฤติกรรมตามบริบท ──
# ล่าศัตรูในระยะ · ตามผู้เล่นเมื่ออยู่ลำพัง · ปิดล้อม/โจมตี/กระโดดฟัน · เดินเล่นตอนว่าง
# ex,ez=ศัตรูใกล้สุด · hasEnemy>0.5 มีศัตรู · grounded>0.5 อยู่พื้น · rnd=สุ่ม 0..1
ฟังก์ชัน สหายคิด(cx, cz, vx, vz, px, pz, ex, ez, hasEnemy, grounded, rnd) {
bind dpx = px - cx; bind dpz = pz - cz; bind dp = รากที่สอง(dpx*dpx + dpz*dpz) + 0.001
bind anim = 0.0; bind jump = 0.0; bind acc = 0.0
bind tx = px; bind tz = pz; bind hot = 0.0
ถ้า hasEnemy > 0.5 { # มีศัตรูในระยะ → ล่า/ปิดล้อม
bind dex = ex - cx; bind dez = ez - cz; bind de = รากที่สอง(dex*dex + dez*dez) + 0.001
ถ้า de < 20.0 { bind tx = ex; bind tz = ez; bind hot = 1.0 }
}
bind dtx = tx - cx; bind dtz = tz - cz; bind dt = รากที่สอง(dtx*dtx + dtz*dtz) + 0.001
# ── ฟิสิกส์ = ผู้เล่น: เร่ง 0.045, หน่วง 0.99 (โมเมนตัม); เบรก 0.90 ตอนใกล้เป้า ──
bind dmp = 0.90 # ใกล้/นิ่ง → เบรก
ถ้า hot > 0.5 { # ── โหมดล่าศัตรู ──
ถ้า dt < 5.5 { bind anim = 4.0; ถ้า rnd < 0.06 { bind jump = 1.0 } } # โจมตี + กระโดดฟัน
มิฉะนั้น {
bind acc = 0.045; bind anim = 2.0; bind dmp = 0.99
bind vx = vx + (dtx/dt)*acc; bind vz = vz + (dtz/dt)*acc
ถ้า rnd < 0.02 { bind jump = 1.0 } # โดดพุ่งเข้า
}
}
มิฉะนั้น { # ── ตามผู้เล่น / เดินเล่น ──
ถ้า dt > 12.0 { bind acc = 0.045; bind anim = 2.0; bind dmp = 0.99; ถ้า rnd < 0.025 { bind jump = 1.0 } } # ไกล → วิ่งตาม
มิฉะนั้น { ถ้า dt > 5.0 { bind acc = 0.045; bind anim = 1.0; bind dmp = 0.96 } # ปานกลาง → เดิน
มิฉะนั้น { bind anim = 0.0; ถ้า rnd < 0.01 { bind jump = 1.0 } } } # ใกล้ → ว่าง + กระโดดเล่น
ถ้า dt > 5.0 { bind vx = vx + (dtx/dt)*acc; bind vz = vz + (dtz/dt)*acc }
}
bind vx = vx * dmp; bind vz = vz * dmp # หน่วงแบบผู้เล่น (โมเมนตัม/เบรก)
bind sp = รากที่สอง(vx*vx + vz*vz)
ถ้า hot < 0.5 { ถ้า sp < 0.03 { bind anim = 0.0 } } # นิ่ง → idle
คืน [vx, vz, anim, jump]
}
# ── แถบเลือดสหาย เหนือหัว (กรอบ + แถบ flush แยก → ทับกรอบชัด ไม่ z-fight) ──
# (x,y,z)=ตำแหน่งแถบ · hf=สัดส่วนเลือด · (r,g,b)=สีธงสหาย (ราชาแดง/ราชินีน้ำเงิน)
# OPTIMIZED: Using draw_quad_3d for health bars → 2× faster rendering
ฟังก์ชัน สหายแถบเลือด(x, y, z, hf, r, g, b) {
bind h = hf; ถ้า h > 1.0 { bind h = 1.0 } ถ้า h < 0.0 { bind h = 0.0 }
โหมดผสม(0)
# Background quad
สีดินสอ(16, 8, 10)
draw_quad_3d(x-4.6,y+0.18,z, x+4.6,y+0.18,z, x+4.6,y-1.02,z, x-4.6,y-1.02,z)
flush_3d()
# Health fill quad
bind hw = 8.6 * h
สีดินสอ(floor(235*(1.0-h)+20), floor(70+170*h), floor(80+b*0.25)) # เต็ม=สีธง → ใกล้ตาย=แดง
draw_quad_3d(x-4.3,y,z, x-4.3+hw,y,z, x-4.3+hw,y-0.84,z, x-4.3,y-0.84,z)
flush_3d()
}