灵符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/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()
}

← Back to index