灵符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/online_world.ling
# online_world.ling — โลก "ออนไลน์" : ผังกระเบื้อง 16×16 ที่ "งอก" ตามเวลาจริง (datetime)
#   เวลาโลก() = วินาที epoch → กำหนดการกำเนิดแบบคงเส้น แต่ค่อย ๆ วิวัฒน์:
#   ต้นไม้ งอก→โต→แก่→ผุ→กลายเป็นพื้น เป็นวัฏจักรช้า ๆ; ดวงอาทิตย์/จันทร์ ปั๊มพลังงาน
#   เร่ง/หน่วงการเติบโต-ผุพัง; ดาว = ตำแหน่งผู้เล่นบนแผนที่.  ลุคทริปปี้ สโลว์โมชัน.
#
# STATE THREADING: ฟังก์ชัน calls in this language see module `bind` globals as
# a read-only snapshot taken at load time — any reassignment inside a function
# body is local to that call and discarded on return (no shared mutable
# globals across a function-call boundary). So every value that must survive
# from one frame to the next travels as an explicit "state bundle" list,
# passed in and returned out by วาดโลกออนไลน์ every frame, and the caller
# (main.ling) is the one holding the persistent `bind`.  Index layout below.

bind OW_X = 0
bind OW_Y = 1
bind OW_Z = 2
bind OW_VX = 3
bind OW_VY = 4
bind OW_VZ = 5
bind OW_YAW = 6
bind OW_ROLL = 7
bind OW_LASTT = 8
bind OW_SYNCT = 9
bind OW_RIDS = 10
bind OW_RX = 11
bind OW_RY = 12
bind OW_RZ = 13
bind OW_RYAW = 14
bind OW_RNAME = 15

bind OW_TILE = 40.0
bind OW_WALK = 10.0
bind OW_MAXP = 24.0

# แฮชคงที่ต่อช่อง (ใช้แค่ param + builtin → ทำงานได้ทุกที่ ไม่พึ่ง global)
ฟังก์ชัน สุ่มโลก(a, b) {
    bind s = ไซน์(a*12.9898 + b*78.233) * 43758.5453
    คืน s - ปัดลง(s)
}

ฟังก์ชัน รีเซ็ตโลกออนไลน์() {
    คืน [
        OW_TILE * 0.5, 0.0 - 2.1, OW_TILE * 0.5,
        0.0, 0.0, 0.0,
        0.0, 0.0, 0.0, 0.0,
        list_new(), list_new(), list_new(), list_new(), list_new(), list_new()
    ]
}

ฟังก์ชัน owMod(a, m) { คืน a - ปัดลง(a / m) * m }

# ── remote player table: fixed-capacity parallel arrays, id == -1 means empty ──
# `rt` is [ids, x, y, z, yaw, name] — the six OW_R* slices of the state bundle.
ฟังก์ชัน owRemoteEnsureInit(rt) {
    bind ids = list_get(rt, 0)
    ถ้า ความยาว(ids) >= OW_MAXP { คืน rt }
    bind xs = list_get(rt, 1);  bind ys = list_get(rt, 2);  bind zs = list_get(rt, 3)
    bind yaws = list_get(rt, 4);  bind names = list_get(rt, 5)
    bind i = 0.0
    ขณะที่ i < OW_MAXP {
        bind ids = list_push(ids, 0.0 - 1.0)
        bind xs = list_push(xs, 0.0)
        bind ys = list_push(ys, 0.0)
        bind zs = list_push(zs, 0.0)
        bind yaws = list_push(yaws, 0.0)
        bind names = list_push(names, "")
        bind i = i + 1.0
    }
    คืน [ids, xs, ys, zs, yaws, names]
}

ฟังก์ชัน owRemoteUpsert(rt, id, x, y, z, yaw, name) {
    bind rt = owRemoteEnsureInit(rt)
    bind ids = list_get(rt, 0);  bind xs = list_get(rt, 1);  bind ys = list_get(rt, 2)
    bind zs = list_get(rt, 3);  bind yaws = list_get(rt, 4);  bind names = list_get(rt, 5)
    bind slot = 0.0 - 1.0
    bind i = 0.0
    ขณะที่ i < OW_MAXP { ถ้า list_get(ids, i) == id { bind slot = i };  bind i = i + 1.0 }
    ถ้า slot < 0.0 {
        bind i = 0.0
        ขณะที่ i < OW_MAXP { ถ้า slot < 0.0 { ถ้า list_get(ids, i) < 0.0 { bind slot = i } };  bind i = i + 1.0 }
    }
    ถ้า slot >= 0.0 {
        bind ids = list_set(ids, slot, id)
        bind xs = list_set(xs, slot, x)
        bind ys = list_set(ys, slot, y)
        bind zs = list_set(zs, slot, z)
        bind yaws = list_set(yaws, slot, yaw)
        bind names = list_set(names, slot, name)
    }
    คืน [ids, xs, ys, zs, yaws, names]
}

ฟังก์ชัน owRemoteRemove(rt, id) {
    bind rt = owRemoteEnsureInit(rt)
    bind ids = list_get(rt, 0)
    bind i = 0.0
    ขณะที่ i < OW_MAXP { ถ้า list_get(ids, i) == id { bind ids = list_set(ids, i, 0.0 - 1.0) };  bind i = i + 1.0 }
    คืน [ids, list_get(rt,1), list_get(rt,2), list_get(rt,3), list_get(rt,4), list_get(rt,5)]
}

# Encode the whole live table as "id,x,y,z,yaw,name;id,x,y,z,yaw,name;..." for broadcast.
ฟังก์ชัน owEncodeSnapshot(rt, selfX, selfY, selfZ, selfYaw, selfName) {
    bind ids = list_get(rt, 0);  bind xs = list_get(rt, 1);  bind ys = list_get(rt, 2)
    bind zs = list_get(rt, 3);  bind yaws = list_get(rt, 4);  bind names = list_get(rt, 5)
    bind out = "0," + to_str(selfX) + "," + to_str(selfY) + "," + to_str(selfZ) + "," + to_str(selfYaw) + "," + selfName
    bind i = 0.0
    ขณะที่ i < ความยาว(ids) {
        ถ้า list_get(ids, i) >= 0.0 {
            bind out = out + ";" + to_str(list_get(ids, i)) + "," + to_str(list_get(xs, i)) + "," + to_str(list_get(ys, i)) + "," + to_str(list_get(zs, i)) + "," + to_str(list_get(yaws, i)) + "," + list_get(names, i)
        }
        bind i = i + 1.0
    }
    คืน out
}

# Host: drain every queued client message this frame, update the live table,
# and retire anyone the transport just reported as disconnected.
ฟังก์ชัน owHostPumpNetwork(rt) {
    bind rt = owRemoteEnsureInit(rt)
    bind msg = เน็ตรับจาก()
    bind guard = 0
    ขณะที่ msg != "" { ถ้า guard < 64 {
        bind pipe = แยก(msg, "|")
        ถ้า ความยาว(pipe) >= 2 {
            bind senderId = list_get(pipe, 0) * 1.0
            bind fields = แยก(list_get(pipe, 1), ",")
            ถ้า ความยาว(fields) >= 5 {
                bind px = list_get(fields, 0) * 1.0
                bind py = list_get(fields, 1) * 1.0
                bind pz = list_get(fields, 2) * 1.0
                bind pyaw = list_get(fields, 3) * 1.0
                bind pname = list_get(fields, 4)
                bind rt = owRemoteUpsert(rt, senderId, px, py, pz, pyaw, pname)
            }
        }
        bind guard = guard + 1
        bind msg = เน็ตรับจาก()
    }  มิฉะนั้น { bind msg = "" } }

    bind evLines = แยก(เน็ตเหตุการณ์(), "\n")
    bind ei = 0
    ขณะที่ ei < ความยาว(evLines) {
        bind el = list_get(evLines, ei)
        ถ้า starts_with(el, "disconnect:") { bind rt = owRemoteRemove(rt, ส่วนสตริง(el, 11, ความยาว(el) - 11) * 1.0) }
        bind ei = ei + 1
    }
    คืน rt
}

# Joiner: the host's broadcast snapshot replaces the whole table every frame,
# so there is nothing to age out — a dropped host just stops updating it.
# `selfName` excludes our own entry (the host echoes it back as one of the peers).
ฟังก์ชัน owClientPumpNetwork(rt, selfName) {
    bind rt = owRemoteEnsureInit(rt)
    bind latest = ""
    bind msg = เน็ตรับ()
    bind guard = 0
    ขณะที่ msg != "" { ถ้า guard < 64 { bind latest = msg;  bind guard = guard + 1;  bind msg = เน็ตรับ() }  มิฉะนั้น { bind msg = "" } }
    ถ้า latest == "" { คืน rt }

    bind ids = list_get(rt, 0)
    bind i = 0.0
    ขณะที่ i < ความยาว(ids) { bind ids = list_set(ids, i, 0.0 - 1.0);  bind i = i + 1.0 }
    bind rt = [ids, list_get(rt,1), list_get(rt,2), list_get(rt,3), list_get(rt,4), list_get(rt,5)]

    bind entries = แยก(latest, ";")
    bind ei = 0
    ขณะที่ ei < ความยาว(entries) {
        bind fields = แยก(list_get(entries, ei), ",")
        ถ้า ความยาว(fields) >= 6 {
            bind ename = list_get(fields, 5)
            ถ้า ename != selfName {
                bind rt = owRemoteUpsert(rt, list_get(fields,0)*1.0, list_get(fields,1)*1.0, list_get(fields,2)*1.0, list_get(fields,3)*1.0, list_get(fields,4)*1.0, ename)
            }
        }
        bind ei = ei + 1
    }
    คืน rt
}

ฟังก์ชัน owHex(ch) {
    ถ้า ch == "0" { คืน 0.0 }; ถ้า ch == "1" { คืน 1.0 }; ถ้า ch == "2" { คืน 2.0 }; ถ้า ch == "3" { คืน 3.0 }
    ถ้า ch == "4" { คืน 4.0 }; ถ้า ch == "5" { คืน 5.0 }; ถ้า ch == "6" { คืน 6.0 }; ถ้า ch == "7" { คืน 7.0 }
    ถ้า ch == "8" { คืน 8.0 }; ถ้า ch == "9" { คืน 9.0 }
    ถ้า ch == "a" { คืน 10.0 }; ถ้า ch == "b" { คืน 11.0 }; ถ้า ch == "c" { คืน 12.0 }; ถ้า ch == "d" { คืน 13.0 }; ถ้า ch == "e" { คืน 14.0 }; ถ้า ch == "f" { คืน 15.0 }
    ถ้า ch == "A" { คืน 10.0 }; ถ้า ch == "B" { คืน 11.0 }; ถ้า ch == "C" { คืน 12.0 }; ถ้า ch == "D" { คืน 13.0 }; ถ้า ch == "E" { คืน 14.0 }; ถ้า ch == "F" { คืน 15.0 }
    คืน 0.0
}

ฟังก์ชัน owSeedFromHash(h) {
    bind n = ความยาว(h);  ถ้า n > 14 { bind n = 14 }
    bind i = 0;  bind seed = 0.0
    ขณะที่ i < n {
        bind seed = seed * 16.0 + owHex(ส่วนสตริง(h, i, 1))
        ถ้า seed > 1000000.0 { bind seed = seed - ปัดลง(seed / 1000000.0) * 1000000.0 }
        bind i = i + 1
    }
    คืน seed
}

ฟังก์ชัน owTileNoise(seed, ix, iz) { คืน สุ่มโลก(ix*1.731 + seed*0.013, iz*2.193 + seed*0.017) }

ฟังก์ชัน owRoad(ix, iz, seed) {
    bind mx = owMod(ix + ปัดลง(seed / 11.0), 5.0)
    bind mz = owMod(iz + ปัดลง(seed / 17.0), 5.0)
    bind road = 0.0
    ถ้า mx == 0.0 { bind road = 1.0 }
    ถ้า mz == 0.0 { bind road = 1.0 }
    ถ้า owMod(ix + iz + ปัดลง(seed / 23.0), 17.0) == 0.0 { bind road = 1.0 }
    คืน road
}

ฟังก์ชัน owOpenTile(ix, iz, seed) {
    ถ้า ix == 0.0 { ถ้า iz == 0.0 { คืน 1.0 } }
    ถ้า ix > 0.0 - 3.0 { ถ้า ix < 3.0 { ถ้า iz > 0.0 - 3.0 { ถ้า iz < 3.0 { คืน 1.0 } } } }
    ถ้า owRoad(ix, iz, seed) > 0.5 { คืน 1.0 }
    ถ้า owTileNoise(seed + 77.0, ix, iz) > 0.84 { คืน 1.0 }
    คืน 0.0
}

ฟังก์ชัน owBlockedAt(x, z, seed) {
    bind ix = ปัดลง(x / OW_TILE)
    bind iz = ปัดลง(z / OW_TILE)
    ถ้า owOpenTile(ix, iz, seed) > 0.5 { คืน 0.0 }
    bind lx = x - ix * OW_TILE
    bind lz = z - iz * OW_TILE
    ถ้า lx > OW_WALK { ถ้า lx < OW_TILE - OW_WALK { ถ้า lz > OW_WALK { ถ้า lz < OW_TILE - OW_WALK { คืน 1.0 } } } }
    คืน 0.0
}

ฟังก์ชัน owGround(x, z, seed) { คืน 0.0 }

ฟังก์ชัน วาดตึกเมือง(x0, z0, x1, z1, baseY, h, r, g, b) {
    bind topY = baseY - h
    สีดินสอ(floor(r*1.18), floor(g*1.18), floor(b*1.18))
    draw_quad_3d(x0,topY,z0,  x1,topY,z0,  x1,topY,z1,  x0,topY,z1)
    สีดินสอ(r, g, b)
    draw_quad_3d(x0,baseY,z0, x1,baseY,z0, x1,topY,z0, x0,topY,z0)
    draw_quad_3d(x1,baseY,z0, x1,baseY,z1, x1,topY,z1, x1,topY,z0)
    สีดินสอ(floor(r*0.72), floor(g*0.72), floor(b*0.78))
    draw_quad_3d(x1,baseY,z1, x0,baseY,z1, x0,topY,z1, x1,topY,z1)
    draw_quad_3d(x0,baseY,z1, x0,baseY,z0, x0,topY,z0, x0,topY,z1)
}

# ต้นไม้ตามช่วงชีวิต ph (0..1): เกิด→โต→แก่→ผุ; สี เขียว→ทอง→น้ำตาล (ทริปปี้)
ฟังก์ชัน วาดต้นไม้(x, gy, z, ph, t) {
    bind gr = ไซน์(ph * 3.14159)                       # 0 ตอนเกิด/ตาย, 1 กลางชีวิต
    ถ้า gr < 0.05 { คืน 0 }
    bind h = 0.7 + gr*3.4
    bind fol = 0.35 + gr*1.9
    โหมดผสม(0)
    สีดินสอ(floor(95.0 - ph*35.0), floor(58.0 - ph*22.0), 32)            # ลำต้น (แก่=เข้ม)
    icosahedron(x, gy - h*0.5, z, 0.26, h*0.5, 0.26, 0.0, t*0.2, 0.0, 2, 0, 0, 0)
    bind fr = floor(55.0 + ph*170.0)
    bind fg = floor(210.0 - ph*150.0)
    bind fb = floor(70.0 + (ไซน์(ph*6.283 + t*0.6)*0.5+0.5)*70.0)        # เรืองสีทริปปี้
    ถ้า ph > 0.78 { bind fg = floor(110.0 - (ph-0.78)*320.0) }           # ผุ = น้ำตาลลง
    สีดินสอ(fr, fg, fb)
    icosahedron(x, gy - h, z, fol, fol*1.25, fol, 0.3, t*0.4 + x*0.2, 0.0, 2, 0, 0, 0)
    คืน 0
}

# วาดโลกออนไลน์: playable procedural city shard (เรียกทุกเฟรมในสถานะ ST_ONLINE).
# `state` = the OW_* bundle above; returns the updated bundle for the caller
# to hold onto and pass back in next frame (see the state-threading note up top).
ฟังก์ชัน วาดโลกออนไลน์(t, playerName, netState, isHost, state) {
    bind owX = list_get(state, OW_X);  bind owY = list_get(state, OW_Y);  bind owZ = list_get(state, OW_Z)
    bind owVX = list_get(state, OW_VX);  bind owVY = list_get(state, OW_VY);  bind owVZ = list_get(state, OW_VZ)
    bind owCamYaw = list_get(state, OW_YAW);  bind owRoll = list_get(state, OW_ROLL)
    bind owLastT = list_get(state, OW_LASTT);  bind owSyncT = list_get(state, OW_SYNCT)
    bind rt = [list_get(state,OW_RIDS), list_get(state,OW_RX), list_get(state,OW_RY), list_get(state,OW_RZ), list_get(state,OW_RYAW), list_get(state,OW_RNAME)]

    bind ET = เวลาโลก()                                # วินาที epoch (datetime จริง)
    bind WT = ET * 0.02                                 # เวลาโลกช้า ๆ (สโลว์โมชัน)
    bind dayBucket = ปัดลง(ET / 86400.0)
    bind owShardHash = แฮชเข้ารหัส("city:" + playerName + ":" + to_str(dayBucket))
    bind owSeed = owSeedFromHash(owShardHash)

    bind dt = t - owLastT;  ถ้า dt < 0.001 { bind dt = 0.016 };  ถ้า dt > 0.05 { bind dt = 0.05 };  bind owLastT = t
    bind step = dt * 60.0

    # ── ดวงอาทิตย์/จันทร์: โคจรแนวตั้ง (กลางวัน/คืน) → พลังงานเร่ง/หน่วง ──
    bind dayAng = WT * 0.10
    bind sunE = ไซน์(dayAng)                            # >0 กลางวัน, <0 กลางคืน
    bind sunX = โคไซน์(dayAng)*58.0;  bind sunY = 0.0 - ไซน์(dayAng)*46.0;  bind sunZ = 0.0 - 18.0
    bind moonX = 0.0 - sunX;  bind moonY = 0.0 - sunY;  bind moonZ = sunZ
    bind energy = sunE*0.5 + 0.5                        # 0..1 พลังเติบโต (ขยาย/หด)

    # ── ท้องฟ้า: ไล่สีกลางวัน↔คืน + ทริปปี้ ──
    bind skR = floor(6.0 + energy*46.0 + (ไซน์(t*0.13)*0.5+0.5)*14.0)
    bind skG = floor(8.0 + energy*38.0 + (ไซน์(t*0.11+2.0)*0.5+0.5)*16.0)
    bind skB = floor(20.0 + energy*30.0 + (ไซน์(t*0.09+4.0)*0.5+0.5)*26.0)
    เติม(skR, skG, skB)
    ตั้งหมอก(skR, skG, skB, 180.0, 760.0)               # หมอกฟ้ากลืนขอบ (ลึกขึ้น)

    bind fx = ไซน์(owCamYaw);  bind fz = โคไซน์(owCamYaw)
    bind rgx = fz;  bind rgz = 0.0 - fx
    bind mvx = 0.0;  bind mvz = 0.0
    ถ้า กดค้าง("w") { bind mvx = mvx + fx;   bind mvz = mvz + fz }
    ถ้า กดค้าง("s") { bind mvx = mvx - fx;   bind mvz = mvz - fz }
    ถ้า กดค้าง("d") { bind mvx = mvx + rgx;  bind mvz = mvz + rgz }
    ถ้า กดค้าง("a") { bind mvx = mvx - rgx;  bind mvz = mvz - rgz }
    bind glx = จอยแกน("lx");  bind gly = จอยแกน("ly")
    ถ้า glx*glx + gly*gly > 0.04 { bind mvx = mvx + fx*gly + rgx*glx;  bind mvz = mvz + fz*gly + rgz*glx }
    bind ml = รากที่สอง(mvx*mvx + mvz*mvz)
    bind acc = 0.14 * step
    ถ้า กดค้าง("lshift") { bind acc = 0.22 * step }
    ถ้า กดค้าง("rshift") { bind acc = 0.22 * step }
    ถ้า ml > 0.01 { bind owVX = owVX + (mvx/ml)*acc;  bind owVZ = owVZ + (mvz/ml)*acc }
    ถ้า กดค้าง("left") { bind owCamYaw = owCamYaw - 0.035*step }
    ถ้า กดค้าง("right") { bind owCamYaw = owCamYaw + 0.035*step }

    bind ballR = 2.1
    bind groundY = owGround(owX, owZ, owSeed)
    bind grounded = 0.0;  ถ้า owY >= groundY - ballR - 0.05 { bind grounded = 1.0 }
    ถ้า grounded > 0.5 { ถ้า กดปุ่ม("space") { bind owVY = 0.0 - 1.25 } }
    bind owPrevX = owX;  bind owPrevZ = owZ
    bind owVY = owVY + 0.060*step
    bind owVX = owVX * 0.93;  bind owVZ = owVZ * 0.93;  bind owVY = owVY * 0.998
    bind owX = owX + owVX*step;  bind owY = owY + owVY*step;  bind owZ = owZ + owVZ*step
    ถ้า owBlockedAt(owX, owZ, owSeed) > 0.5 {
        bind sxTry = owX
        bind szTry = owPrevZ
        ถ้า owBlockedAt(sxTry, szTry, owSeed) < 0.5 {
            bind owZ = owPrevZ
            bind owVZ = owVZ * 0.25
        }
        มิฉะนั้น {
            bind sxTry = owPrevX
            bind szTry = owZ
            ถ้า owBlockedAt(sxTry, szTry, owSeed) < 0.5 {
                bind owX = owPrevX
                bind owVX = owVX * 0.25
            }
            มิฉะนั้น {
                bind owX = owPrevX;  bind owZ = owPrevZ;  bind owVX = 0.0 - owVX*0.18;  bind owVZ = 0.0 - owVZ*0.18
            }
        }
    }
    bind groundY = owGround(owX, owZ, owSeed)
    ถ้า owY > groundY - ballR { bind owY = groundY - ballR;  ถ้า owVY > 0.0 { bind owVY = 0.0 } }
    bind spd = รากที่สอง(owVX*owVX + owVZ*owVZ)
    bind owRoll = owRoll + spd * step * 0.08
    ถ้า spd > 0.08 {
        bind targetYaw = atan2(owVX, owVZ)
        bind dyaw = targetYaw - owCamYaw
        ขณะที่ dyaw > 3.14159 { bind dyaw = dyaw - 6.28318 }
        ขณะที่ dyaw < 0.0-3.14159 { bind dyaw = dyaw + 6.28318 }
        bind owCamYaw = owCamYaw + dyaw*0.04
    }

    # ── network sync: ~20 Hz is plenty for LAN avatars, keeps bandwidth flat ──
    bind owSyncT = owSyncT + dt
    ถ้า netState >= 2.0 {
        ถ้า isHost > 0.5 {
            bind rt = owHostPumpNetwork(rt)
            ถ้า owSyncT > 0.05 {
                bind owSyncT = 0.0
                เน็ตส่ง(owEncodeSnapshot(rt, owX, owY, owZ, owCamYaw, playerName))
            }
        }  มิฉะนั้น {
            bind rt = owClientPumpNetwork(rt, playerName)
            ถ้า owSyncT > 0.05 {
                bind owSyncT = 0.0
                เน็ตส่ง(to_str(owX) + "," + to_str(owY) + "," + to_str(owZ) + "," + to_str(owCamYaw) + "," + playerName)
            }
        }
    }

    # ── กล้องติดตามลูก: เข้าเมืองได้จริง ไม่ใช่แค่มุม auto-tour ──
    bind camX = owX - ไซน์(owCamYaw)*82.0
    bind camZ = owZ - โคไซน์(owCamYaw)*82.0
    ตั้งกล้องมอง(camX, owY - 54.0, camZ, owX, owY - 2.0, owZ)
    ตั้งระยะห่าง(3.2)
    ตั้งแสงรอบข้าง(0.45 + energy*0.4)
    ล้างแสง()
    เพิ่มแสง(sunX, sunY, sunZ, 1.0, 0.88, 0.62, 1.8, 680.0)
    เพิ่มแสง(owX, owY - 8.0, owZ - 12.0, 0.5, 0.75, 1.0, 1.1, 150.0)
    set_depth_test(1)

    # ── ดาว (โดมฟ้า): จาง=ดาวพื้นหลัง ──
    โหมดผสม(1)
    bind sN = 0
    ขณะที่ sN < 46 {
        bind sa = สุ่มโลก(sN*1.3+0.7, 9.1)*6.283;  bind sb = สุ่มโลก(sN*2.1+4.4, 3.3)
        bind sx = โคไซน์(sa)*(40.0+sb*40.0);  bind sz = ไซน์(sa)*(40.0+sb*40.0);  bind sy = 0.0 - 36.0 - sb*14.0
        bind tw = ไซน์(t*1.4 + sN)*0.5+0.5
        สีดินสอ(floor(120+tw*120), floor(120+tw*120), floor(150+tw*100))
        วาดเส้น3มิติ(sx, sy, sz, sx+0.3, sy, sz)
        bind sN = sN + 1
    }
    โหมดผสม(0)

    # ── Procedural city around the player: roads, plazas, buildings, living terrain ──
    bind ci = ปัดลง(owX / OW_TILE);  bind cj = ปัดลง(owZ / OW_TILE)
    bind di = 0.0 - 6.0
    ขณะที่ di <= 6.0 {
        bind dj = 0.0 - 6.0
        ขณะที่ dj <= 6.0 {
            bind ix = ci + di;  bind iz = cj + dj
            bind x0 = ix*OW_TILE;  bind z0 = iz*OW_TILE;  bind x1 = x0+OW_TILE;  bind z1 = z0+OW_TILE
            bind baseY = 0.0
            bind rn = owTileNoise(owSeed, ix, iz)
            bind road = owRoad(ix, iz, owSeed)
            bind plaza = 0.0;  ถ้า rn > 0.84 { bind plaza = 1.0 };  ถ้า ix == 0.0 { ถ้า iz == 0.0 { bind plaza = 1.0;  bind road = 0.0 } }
            bind hue = owSeed*0.00017 + ix*0.07 + iz*0.11 + WT*0.004
            ถ้า road > 0.5 {
                สีดินสอ(floor(34 + (ไซน์(hue)*0.5+0.5)*22), floor(38 + energy*28), floor(52 + (ไซน์(hue+2.0)*0.5+0.5)*34))
                draw_quad_3d(x0,baseY,z0, x1,baseY,z0, x1,baseY,z1, x0,baseY,z1)
                สีดินสอ(120, 150, 190)
                ถ้า owMod(ix + iz, 2.0) == 0.0 { วาดเส้น3มิติ(x0+OW_TILE*0.5, baseY-0.03, z0+OW_WALK*0.55, x0+OW_TILE*0.5, baseY-0.03, z1-OW_WALK*0.55) }
            }
            มิฉะนั้น { ถ้า plaza > 0.5 {
                สีดินสอ(floor(38 + energy*70), floor(92 + energy*80), floor(78 + (ไซน์(hue)*0.5+0.5)*65))
                draw_quad_3d(x0,baseY,z0, x1,baseY,z0, x1,baseY,z1, x0,baseY,z1)
                bind life = WT*(0.035 + energy*0.06) + rn
                วาดต้นไม้(x0+OW_TILE*0.5, baseY, z0+OW_TILE*0.5, life - ปัดลง(life), t)
            }
            มิฉะนั้น {
                สีดินสอ(42, 45, 56)
                draw_quad_3d(x0,baseY,z0, x1,baseY,z0, x1,baseY,z1, x0,baseY,z1)
                bind bh = 14.0 + rn*78.0 + energy*16.0 + (ไซน์(WT*0.05 + ix)*0.5+0.5)*20.0
                bind br = floor((ไซน์(hue)*0.5+0.5)*105+65)
                bind bg = floor((ไซน์(hue+2.094)*0.5+0.5)*105+65)
                bind bb = floor((ไซน์(hue+4.189)*0.5+0.5)*120+75)
                วาดตึกเมือง(x0+OW_WALK, z0+OW_WALK, x1-OW_WALK, z1-OW_WALK, baseY, bh, br, bg, bb)
            } }
            bind dj = dj + 1.0
        }
        bind di = di + 1.0
    }
    flush_3d()

    # ── Player ball in the shard ──
    bind pc = สีจากเฉด((owSeed*0.00001 + t*0.025) - ปัดลง(owSeed*0.00001 + t*0.025))
    วาดลูกแต่ง(owX, owY, owZ, ballR, list_get(pc,0), list_get(pc,1), list_get(pc,2), 220,235,255, 255,210,120, 1, 3, t+owRoll, 2.0)
    flush_3d()

    # ── other real players connected to this shard right now ──
    bind rids = list_get(rt, 0);  bind rxs = list_get(rt, 1);  bind rys = list_get(rt, 2)
    bind rzs = list_get(rt, 3);  bind rnames = list_get(rt, 5)
    bind ri = 0.0
    ขณะที่ ri < ความยาว(rids) {
        ถ้า list_get(rids, ri) >= 0.0 {
            bind rid = list_get(rids, ri)
            bind rx = list_get(rxs, ri);  bind ry = list_get(rys, ri);  bind rz = list_get(rzs, ri)
            bind rname = list_get(rnames, ri)
            bind rc = สีจากเฉด((owSeed*0.00001 + rid*0.137) - ปัดลง(owSeed*0.00001 + rid*0.137))
            วาดลูกแต่ง(rx, ry, rz, ballR, list_get(rc,0), list_get(rc,1), list_get(rc,2), 200,220,255, 120,220,255, 1, 3, t + rid, 2.0)
            สีดินสอ(200, 225, 255)
            วาดสตริง(rname, rx, ry - ballR - 1.4, rz, 0.5, 1)
            flush_3d()
        }
        bind ri = ri + 1.0
    }

    # ── ดวงอาทิตย์ + จันทร์ (เรืองแสง additive) ──
    โหมดผสม(1)
    สีดินสอ(255, floor(210 + ไซน์(t)*30.0), 90)
    icosahedron(sunX, sunY, sunZ, 5.0, 5.0, 5.0, 0.0, t*0.3, 0.0, 2, 0, 0, 0)
    อนุภาคทรงกลม(sunX, sunY, sunZ, 8.0, 40, t*2.0, 255, 220, 120)
    สีดินสอ(170, 185, 230)
    icosahedron(moonX, moonY, moonZ, 3.4, 3.4, 3.4, 0.0, t*0.2, 0.0, 2, 0, 0, 0)
    อนุภาคทรงกลม(moonX, moonY, moonZ, 5.0, 22, t*1.4, 180, 200, 255)
    flush_3d();  โหมดผสม(0)
    set_depth_test(0)

    # ── ป้าย ──
    สีดินสอ(180, 210, 235);  วาดสตริง("online city shard", 0.0, 0.0 - 9.2, 0.0, 0.72, 1)
    สีดินสอ(140, 165, 200);  วาดสตริง("crypto " + ส่วนสตริง(owShardHash, 0, 8) + "  x " + to_str(ปัดลง(owX)) + " z " + to_str(ปัดลง(owZ)), 0.0, 0.0 - 8.1, 0.0, 0.42, 1)
    สีดินสอ(120, 140, 175);  วาดสตริง("wasd roll  arrows camera  space hop  esc back", 0.0, 9.0, 0.0, 0.38, 1)
    flush_3d()

    คืน [
        owX, owY, owZ, owVX, owVY, owVZ, owCamYaw, owRoll, owLastT, owSyncT,
        list_get(rt,0), list_get(rt,1), list_get(rt,2), list_get(rt,3), list_get(rt,4), list_get(rt,5)
    ]
}

← Back to index