灵符lingfu

lingdev

↓ 0 downloads

LingDev: a Godot / Unity-style game editor written in Ling. 2-D and 3-D scenes, Flash-style timeline and cutscenes, HUDs over 3-D, Ling scripting, audio / music / MIDI, builds for Windows, Linux, macOS, web, Raspberry Pi and LingOS. Ships with Ling Agent (an N64-style spy FPS), Ling Breaker (a 3-D breakout) and Moko's Picture Book (a kawaii storybook platformer).

Windows · x86-64Linux · x86-64LingOS · x86-64LingOS · Pi 5 ARM64Web · WASM
Keywords game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdev
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (136)

ling/demo_story.ling
# demo_story.ling - builds "Moko's Picture Book", the kawaii storybook platformer demo that ships
# with LingDev (rt_story.ling, `story` mode): four pages of pastel ground, candy clouds, egg blocks,
# springs, puffs and fruit, a title over a live page, the page select and the page-cleared card.
# Every page is ordinary scene nodes, so it opens and edits in LingDev like any project.

fn sbd_root() { return p_join(st_get("app_root", "."), "projects/MokoStory") }

fn demo_story_open() {
    if file_exists(p_join(sbd_root(), "lingdev.toml")) { return project_open_path(p_join(sbd_root(), "lingdev.toml")) }
    return demo_story_build()
}

fn demo_story_build() {
    bind dir = p_join(st_get("app_root", "."), "projects")
    plat_mkdir(dir)
    if !proj_create(dir, "MokoStory", 0.0, 1280.0, 720.0) { return false }
    plat_delete(p_join(proj_root(), "scenes/level.lscene"))
    proj_remove_scene("scenes/level.lscene")
    st_set("proj_title", "Moko's Picture Book")
    st_set("proj_author", "LingDev demo")
    plat_mkdir(p_join(proj_root(), "assets"))
    bkd_copy_dir(p_join(st_get("app_root", "."), "assets/sfx/story"), p_join(proj_root(), "assets/sfx"), ["wav"])
    bkd_copy_dir(p_join(st_get("app_root", "."), "assets/music/story"), p_join(proj_root(), "assets/music"), ["wav", "beats", "txt"])
    sbd_page1()
    sbd_page2()
    sbd_page3()
    sbd_page4()
    sbd_menus()
    st_set("proj_start", "scenes/title.lscene")
    proj_save()
    gen_write_all()
    assets_mark_stale()
    open_scene(p_join(proj_root(), "scenes/page1.lscene"))
    status("Built Moko's Picture Book: 4 pages, title, page select. F5 plays from the title.")
    return true
}

# ── building blocks (ground top at y = 0, +y is down, Moko's lane is z = 0) ──
fn sbd_box(name, tag, x, y, z, sx, sy, sz, col) {
    bind b = nd_add("box", name, 0.0)
    nd_set(b, "px", x)  nd_set(b, "py", y)  nd_set(b, "pz", z)
    nd_set(b, "sx", sx)  nd_set(b, "sy", sy)  nd_set(b, "sz", sz)
    nd_set(b, "col", col)
    nd_set(b, "tag", tag)
    return b
}
fn sbd_sphere(name, tag, x, y, z, sx, sy, sz, col) {
    bind b = nd_add("sphere", name, 0.0)
    nd_set(b, "px", x)  nd_set(b, "py", y)  nd_set(b, "pz", z)
    nd_set(b, "sx", sx)  nd_set(b, "sy", sy)  nd_set(b, "sz", sz)
    nd_set(b, "col", col)
    nd_set(b, "tag", tag)
    return b
}
# a ground strip from x0 to x1 with its top at `top`; a soft cap of grass / candy on it
fn sbd_ground(x0, x1, top) {
    bind hw = (x1 - x0) * 0.5
    sbd_box("Ground", "ground", x0 + hw, top + 3.0, 0.0, hw, 3.0, 1.6, st_get("sbd_soil", c_rgb(255.0, 226.0, 196.0)))
    sbd_box("Grass", "", x0 + hw, top - 0.05, 0.0, hw + 0.08, 0.16, 1.7, st_get("sbd_grass", c_rgb(150.0, 225.0, 160.0)))
    # stitched rim stripe on the front face
    sbd_box("Stitch", "", x0 + hw, top + 0.45, 0.0 - 1.62, hw - 0.1, 0.03, 0.01, st_get("sbd_stitch", c_rgb(255.0, 255.0, 255.0)))
    return 0.0
}
fn sbd_cloud(x, y, w) {
    sbd_box("Cloud", "cloud", x, y + 0.15, 0.0, w * 0.5, 0.18, 0.9, c_rgb(255.0, 255.0, 255.0))
    bind k = 0.0
    while k <= w {
        sbd_sphere("Puff", "", x - w * 0.5 + k, y + 0.12, 0.0, 0.55, 0.4, 0.8, c_rgb(255.0, 252.0, 255.0))
        bind k = k + 1.0
    }
    return 0.0
}
fn sbd_fruit(x, y, kind) {
    bind f = sbd_sphere("Fruit " + kind, "fruit", x, y, 0.0, 0.3, 0.3, 0.3, c_rgb(255.0, 120.0, 120.0))
    nd_set(f, "ex", "kind=" + kind)
    return f
}
# a gentle arc of fruit from (x0, y0) to (x1, y0) rising by `hgt`
fn sbd_arc(x0, x1, y0, hgt, n, kind) {
    bind k = 0.0
    while k < n {
        bind u = k / max(1.0, n - 1.0)
        sbd_fruit(x0 + (x1 - x0) * u, y0 - sin(u * 3.14159) * hgt, kind)
        bind k = k + 1.0
    }
    return 0.0
}
fn sbd_row(x0, y, n, step, kind) {
    bind k = 0.0
    while k < n {
        sbd_fruit(x0 + k * step, y, kind)
        bind k = k + 1.0
    }
    return 0.0
}
fn sbd_puff(x, y, kind) {
    bind p = sbd_sphere("Puff " + kind, "puff", x, y, 0.0, 0.42, 0.42, 0.42, c_rgb(140.0, 190.0, 255.0))
    nd_set(p, "ex", "kind=" + kind)
    return p
}
fn sbd_eggblock(x, y) {
    bind b = sbd_box("Egg block", "eggblock", x, y, 0.0, 0.5, 0.5, 0.5, c_rgb(255.0, 220.0, 120.0))
    nd_set(b, "ex", "eggs=3")
    return b
}
fn sbd_spring(x, top) { return sbd_box("Spring", "spring", x, top - 0.45, 0.0, 0.45, 0.45, 0.45, c_rgb(255.0, 120.0, 140.0)) }
fn sbd_flower(x, top, z, c) { return sbd_sphere("Flower", "flower", x, top, z, 1.0 + rand() * 0.4, 1.0, 1.0, c) }
fn sbd_tree(x, top, z, c) { return sbd_sphere("Tree", "tree", x, top, z, 1.2 + rand() * 0.5, 1.0, 1.0, c) }
fn sbd_cloudface(x, y, z, c) { return sbd_sphere("Cloud face", "cloudface", x, y, z, 1.0 + rand() * 0.6, 1.0, 1.0, c) }
# soft paper hills far behind the lane
fn sbd_hills(x0, x1, cols) {
    bind x = x0
    bind k = 0.0
    while x < x1 {
        bind c = list_get(cols, k - floor(k / len(cols)) * len(cols))
        bind r = 7.0 + rand() * 6.0
        sbd_sphere("Hill", "", x, 4.0, 18.0 + rand() * 10.0, r, r * 0.8, 4.0, c)
        bind x = x + r * 1.3
        bind k = k + 1.0
    }
    return 0.0
}
fn sbd_page_begin(name, n, title, lucky, beats, bpm, bg) {
    sc_new(name, "3d")
    st_lclear("undo")
    st_lclear("redo")
    sel_clear()
    sc_set("bg", bg)
    sc_set("amb", 0.58)
    sc_set("floor", 0.0)
    sc_set("music", "assets/music/page" + num_str(n) + ".wav")
    bind next = "scenes/pages.lscene"
    if n < 4.0 { bind next = "scenes/page" + num_str(n + 1.0) + ".lscene" }
    sc_set("ex", "mode=story;page=" + num_str(n) + ";title=" + title + ";lucky=" + lucky + ";goal=30;bpm=" + num_str(bpm) + ";beats=" + beats + ";death=9;menu=scenes/pages.lscene;next=" + next + ";shade=1;rim=0.1;rimcol=255,230,240")
    bind p = nd_add("spawn", "Moko start", 0.0)
    nd_set(p, "px", 2.0)
    nd_set(p, "py", 0.0 - 1.0)
    nd_set(p, "tag", "player")
    bind sun = nd_add("light", "Sun", 0.0)
    nd_set(sun, "px", 20.0)  nd_set(sun, "py", 0.0 - 20.0)  nd_set(sun, "pz", 0.0 - 12.0)
    nd_set(sun, "a", 1.4)  nd_set(sun, "b", 120.0)  nd_set(sun, "col", c_rgb(255.0, 248.0, 236.0))
    st_set("ed_tx", 10.0)  st_set("ed_ty", 0.0 - 2.0)  st_set("ed_tz", 0.0)
    st_set("ed_yaw", 0.0)  st_set("ed_pitch", 0.15)  st_set("ed_dist", 22.0)
    return 0.0
}
fn sbd_save(n) { save_scene(p_join(proj_root(), "scenes/page" + num_str(n) + ".lscene")) }

# ── page 1: Marshmallow Meadow ───────────────────────────────────────────────
fn sbd_page1() {
    st_set("sbd_soil", c_rgb(255.0, 226.0, 196.0))
    st_set("sbd_grass", c_rgb(150.0, 225.0, 160.0))
    st_set("sbd_stitch", c_rgb(255.0, 255.0, 255.0))
    sbd_page_begin("Marshmallow Meadow", 1.0, "Marshmallow Meadow", "grapes", "assets/music/page1.beats", 100.0, c_rgb(190.0, 228.0, 255.0))
    sbd_ground(0.0 - 6.0, 22.0, 0.0)
    sbd_ground(25.0, 40.0, 0.0)
    sbd_ground(40.0, 52.0, 0.0 - 1.5)
    sbd_ground(56.0, 80.0, 0.0)
    sbd_ground(84.0, 120.0, 0.0)
    sbd_cloud(31.0, 0.0 - 4.0, 4.0)
    sbd_cloud(66.0, 0.0 - 4.5, 5.0)
    sbd_cloud(96.0, 0.0 - 4.0, 4.0)
    sbd_eggblock(9.0, 0.0 - 3.0)
    sbd_eggblock(60.0, 0.0 - 3.0)
    sbd_spring(46.0, 0.0 - 1.5)
    sbd_row(5.0, 0.0 - 0.8, 5.0, 1.2, "apple")
    sbd_arc(20.0, 27.0, 0.0 - 1.5, 2.5, 5.0, "banana")
    sbd_row(29.5, 0.0 - 5.2, 4.0, 1.0, "grapes")
    sbd_row(42.0, 0.0 - 2.3, 4.0, 1.4, "apple")
    sbd_fruit(46.0, 0.0 - 9.0, "melon")
    sbd_arc(52.0, 57.0, 0.0 - 2.0, 2.0, 4.0, "banana")
    sbd_row(64.0, 0.0 - 5.7, 5.0, 1.0, "grapes")
    sbd_row(70.0, 0.0 - 0.8, 4.0, 1.3, "apple")
    sbd_arc(80.0, 85.0, 0.0 - 1.2, 2.2, 4.0, "banana")
    sbd_row(94.5, 0.0 - 5.2, 4.0, 1.0, "apple")
    sbd_row(100.0, 0.0 - 0.8, 6.0, 1.2, "grapes")
    sbd_fruit(108.0, 0.0 - 1.0, "heart")
    sbd_fruit(112.0, 0.0 - 0.8, "melon")
    sbd_puff(15.0, 0.0 - 0.6, "walker")
    sbd_puff(35.0, 0.0 - 0.6, "walker")
    sbd_puff(48.0, 0.0 - 2.1, "walker")
    sbd_puff(62.0, 0.0 - 3.0, "flyer")
    sbd_puff(74.0, 0.0 - 0.6, "walker")
    sbd_puff(90.0, 0.0 - 0.6, "walker")
    sbd_puff(104.0, 0.0 - 0.6, "spiky")
    sbd_meadow_deco(c_rgb(255.0, 170.0, 200.0))
    sbd_save(1.0)
}
fn sbd_meadow_deco(petal) {
    bind fc = [petal, c_rgb(255.0, 230.0, 120.0), c_rgb(190.0, 170.0, 255.0), c_rgb(255.0, 200.0, 160.0)]
    bind x = 0.0 - 4.0
    while x < 118.0 {
        sbd_flower(x + rand() * 2.0, 0.0, 1.0 + rand() * 0.6, list_get(fc, floor(rand() * 3.99)))
        if rand() < 0.5 { sbd_flower(x + rand() * 2.0, 0.0, 0.0 - 1.3 - rand() * 0.3, list_get(fc, floor(rand() * 3.99))) }
        bind x = x + 3.0 + rand() * 2.0
    }
    bind x = 4.0
    while x < 118.0 {
        sbd_tree(x, 0.0, 4.0 + rand() * 3.0, list_get([c_rgb(150.0, 225.0, 170.0), c_rgb(255.0, 190.0, 210.0), c_rgb(170.0, 210.0, 255.0)], floor(rand() * 2.99)))
        bind x = x + 9.0 + rand() * 6.0
    }
    bind x = 0.0
    while x < 125.0 {
        sbd_cloudface(x, 0.0 - 9.0 - rand() * 3.0, 14.0 + rand() * 6.0, c_rgb(255.0, 255.0, 255.0))
        bind x = x + 16.0 + rand() * 8.0
    }
    sbd_hills(0.0 - 20.0, 140.0, [c_rgb(200.0, 240.0, 200.0), c_rgb(255.0, 225.0, 235.0), c_rgb(215.0, 230.0, 255.0)])
    return 0.0
}

# ── page 2: Candy Cloud Heights ──────────────────────────────────────────────
fn sbd_page2() {
    st_set("sbd_soil", c_rgb(246.0, 160.0, 200.0))
    st_set("sbd_grass", c_rgb(255.0, 245.0, 250.0))
    st_set("sbd_stitch", c_rgb(255.0, 150.0, 190.0))
    sbd_page_begin("Candy Cloud Heights", 2.0, "Candy Cloud Heights", "banana", "", 120.0, c_rgb(255.0, 214.0, 232.0))
    sbd_ground(0.0 - 6.0, 14.0, 0.0)
    bind xs = [18.0, 23.0, 28.5, 34.0, 40.0, 46.0, 52.0, 58.5, 65.0, 71.0, 77.0]
    bind ys = [0.0 - 2.0, 0.0 - 4.0, 0.0 - 6.0, 0.0 - 4.0, 0.0 - 2.5, 0.0 - 4.5, 0.0 - 6.5, 0.0 - 5.0, 0.0 - 3.5, 0.0 - 5.5, 0.0 - 3.0]
    bind k = 0.0
    while k < len(xs) {
        sbd_cloud(list_get(xs, k), list_get(ys, k), 4.0)
        sbd_row(list_get(xs, k) - 1.0, list_get(ys, k) - 1.0, 3.0, 1.0, list_get(["banana", "apple", "grapes"], k - floor(k / 3.0) * 3.0))
        bind k = k + 1.0
    }
    sbd_ground(82.0, 120.0, 0.0)
    sbd_eggblock(6.0, 0.0 - 3.0)
    sbd_eggblock(40.0, 0.0 - 6.0)
    sbd_spring(12.0, 0.0)
    sbd_fruit(12.0, 0.0 - 9.0, "melon")
    sbd_fruit(52.0, 0.0 - 10.0, "melon")
    sbd_row(3.0, 0.0 - 0.8, 4.0, 1.2, "banana")
    sbd_row(86.0, 0.0 - 0.8, 8.0, 1.4, "apple")
    sbd_fruit(100.0, 0.0 - 1.0, "heart")
    sbd_arc(104.0, 114.0, 0.0 - 1.0, 3.0, 6.0, "banana")
    sbd_puff(30.0, 0.0 - 9.0, "flyer")
    sbd_puff(46.0, 0.0 - 8.0, "flyer")
    sbd_puff(62.0, 0.0 - 8.5, "flyer")
    sbd_puff(90.0, 0.0 - 0.6, "walker")
    sbd_puff(98.0, 0.0 - 0.6, "walker")
    sbd_puff(108.0, 0.0 - 0.6, "spiky")
    bind x = 0.0
    while x < 125.0 {
        sbd_cloudface(x, 0.0 - 4.0 - rand() * 8.0, 10.0 + rand() * 10.0, list_get([c_rgb(255.0, 255.0, 255.0), c_rgb(255.0, 235.0, 245.0)], floor(rand() * 1.99)))
        bind x = x + 9.0 + rand() * 6.0
    }
    sbd_hills(0.0 - 20.0, 140.0, [c_rgb(255.0, 235.0, 245.0), c_rgb(230.0, 220.0, 255.0)])
    sbd_save(2.0)
}

# ── page 3: Sleepy Moon Forest (night) ───────────────────────────────────────
fn sbd_page3() {
    st_set("sbd_soil", c_rgb(150.0, 130.0, 200.0))
    st_set("sbd_grass", c_rgb(120.0, 190.0, 200.0))
    st_set("sbd_stitch", c_rgb(255.0, 240.0, 170.0))
    sbd_page_begin("Sleepy Moon Forest", 3.0, "Sleepy Moon Forest", "apple", "assets/music/page3.beats", 135.0, c_rgb(60.0, 62.0, 118.0))
    sc_set("amb", 0.5)
    sbd_ground(0.0 - 6.0, 18.0, 0.0)
    sbd_ground(22.0, 30.0, 0.0 - 2.0)
    sbd_ground(30.0, 44.0, 0.0)
    sbd_ground(48.0, 56.0, 0.0 - 3.0)
    sbd_ground(56.0, 76.0, 0.0)
    sbd_ground(80.0, 120.0, 0.0)
    sbd_spring(27.0, 0.0 - 2.0)
    sbd_spring(52.0, 0.0 - 3.0)
    sbd_cloud(36.0, 0.0 - 5.0, 4.0)
    sbd_cloud(66.0, 0.0 - 5.0, 5.0)
    sbd_eggblock(14.0, 0.0 - 3.0)
    sbd_eggblock(90.0, 0.0 - 3.0)
    sbd_row(4.0, 0.0 - 0.8, 5.0, 1.2, "apple")
    sbd_arc(18.0, 23.0, 0.0 - 1.5, 2.5, 4.0, "grapes")
    sbd_fruit(27.0, 0.0 - 10.0, "melon")
    sbd_row(34.5, 0.0 - 6.2, 4.0, 1.0, "apple")
    sbd_row(38.0, 0.0 - 0.8, 4.0, 1.3, "banana")
    sbd_arc(44.0, 49.0, 0.0 - 1.5, 2.5, 4.0, "grapes")
    sbd_fruit(52.0, 0.0 - 11.0, "melon")
    sbd_row(64.0, 0.0 - 6.2, 5.0, 1.0, "apple")
    sbd_row(84.0, 0.0 - 0.8, 6.0, 1.4, "banana")
    sbd_fruit(96.0, 0.0 - 1.0, "heart")
    sbd_row(100.0, 0.0 - 0.8, 6.0, 1.3, "apple")
    sbd_puff(12.0, 0.0 - 0.6, "spiky")
    sbd_puff(36.0, 0.0 - 0.6, "walker")
    sbd_puff(42.0, 0.0 - 0.6, "spiky")
    sbd_puff(60.0, 0.0 - 0.6, "walker")
    sbd_puff(70.0, 0.0 - 3.5, "flyer")
    sbd_puff(88.0, 0.0 - 0.6, "walker")
    sbd_puff(104.0, 0.0 - 0.6, "spiky")
    sbd_puff(112.0, 0.0 - 3.0, "flyer")
    bind fc = [c_rgb(170.0, 230.0, 255.0), c_rgb(255.0, 240.0, 170.0), c_rgb(220.0, 190.0, 255.0)]
    bind x = 0.0 - 4.0
    while x < 118.0 {
        sbd_flower(x + rand() * 2.0, 0.0, 1.0 + rand() * 0.6, list_get(fc, floor(rand() * 2.99)))
        bind x = x + 3.5 + rand() * 2.0
    }
    bind x = 2.0
    while x < 118.0 {
        sbd_tree(x, 0.0, 3.5 + rand() * 4.0, list_get([c_rgb(90.0, 140.0, 170.0), c_rgb(130.0, 110.0, 190.0)], floor(rand() * 1.99)))
        bind x = x + 6.0 + rand() * 5.0
    }
    # the moon with a sleepy face
    sbd_cloudface(60.0, 0.0 - 14.0, 30.0, c_rgb(255.0, 245.0, 190.0))
    sbd_hills(0.0 - 20.0, 140.0, [c_rgb(80.0, 90.0, 150.0), c_rgb(100.0, 80.0, 150.0)])
    sbd_save(3.0)
}

# ── page 4: Rainbow Picnic ───────────────────────────────────────────────────
fn sbd_page4() {
    st_set("sbd_soil", c_rgb(250.0, 190.0, 140.0))
    st_set("sbd_grass", c_rgb(170.0, 230.0, 255.0))
    st_set("sbd_stitch", c_rgb(255.0, 150.0, 120.0))
    sbd_page_begin("Rainbow Picnic", 4.0, "Rainbow Picnic", "banana", "", 110.0, c_rgb(214.0, 240.0, 255.0))
    sbd_ground(0.0 - 6.0, 16.0, 0.0)
    sbd_ground(20.0, 26.0, 0.0 - 2.0)
    sbd_ground(30.0, 36.0, 0.0 - 4.0)
    sbd_ground(40.0, 70.0, 0.0)
    sbd_ground(74.0, 82.0, 0.0 - 2.5)
    sbd_ground(86.0, 124.0, 0.0)
    sbd_cloud(28.0, 0.0 - 7.5, 4.0)
    sbd_cloud(56.0, 0.0 - 4.5, 6.0)
    sbd_cloud(96.0, 0.0 - 4.5, 5.0)
    sbd_spring(62.0, 0.0)
    sbd_eggblock(8.0, 0.0 - 3.0)
    sbd_eggblock(48.0, 0.0 - 3.0)
    sbd_eggblock(104.0, 0.0 - 3.0)
    sbd_row(3.0, 0.0 - 0.8, 5.0, 1.3, "banana")
    sbd_arc(16.0, 21.0, 0.0 - 1.0, 2.5, 4.0, "apple")
    sbd_row(21.0, 0.0 - 2.8, 3.0, 1.5, "grapes")
    sbd_row(31.0, 0.0 - 4.8, 3.0, 1.5, "apple")
    sbd_row(26.5, 0.0 - 8.7, 4.0, 1.0, "banana")
    sbd_row(44.0, 0.0 - 0.8, 6.0, 1.3, "grapes")
    sbd_row(53.5, 0.0 - 5.7, 6.0, 1.0, "apple")
    sbd_fruit(62.0, 0.0 - 10.0, "melon")
    sbd_row(75.0, 0.0 - 3.3, 4.0, 1.5, "banana")
    sbd_row(90.0, 0.0 - 0.8, 4.0, 1.3, "apple")
    sbd_row(94.0, 0.0 - 5.7, 5.0, 1.0, "grapes")
    sbd_fruit(108.0, 0.0 - 1.0, "heart")
    sbd_arc(110.0, 120.0, 0.0 - 1.0, 3.0, 6.0, "banana")
    sbd_fruit(118.0, 0.0 - 6.0, "melon")
    sbd_puff(12.0, 0.0 - 0.6, "walker")
    sbd_puff(46.0, 0.0 - 0.6, "walker")
    sbd_puff(52.0, 0.0 - 0.6, "spiky")
    sbd_puff(66.0, 0.0 - 0.6, "walker")
    sbd_puff(78.0, 0.0 - 5.5, "flyer")
    sbd_puff(92.0, 0.0 - 0.6, "walker")
    sbd_puff(100.0, 0.0 - 7.0, "flyer")
    sbd_puff(114.0, 0.0 - 0.6, "spiky")
    sbd_meadow_deco(c_rgb(255.0, 140.0, 120.0))
    # a rainbow arch behind the picnic
    bind cols = [c_rgb(255.0, 120.0, 130.0), c_rgb(255.0, 190.0, 110.0), c_rgb(255.0, 235.0, 120.0), c_rgb(150.0, 225.0, 150.0), c_rgb(130.0, 190.0, 255.0), c_rgb(190.0, 150.0, 255.0)]
    bind k = 0.0
    while k < 6.0 {
        bind r = nd_add("torus", "Rainbow", 0.0)
        nd_set(r, "px", 60.0)  nd_set(r, "py", 6.0)  nd_set(r, "pz", 34.0)
        nd_set(r, "sx", 26.0 - k * 1.2)  nd_set(r, "sy", 0.6)  nd_set(r, "sz", 26.0 - k * 1.2)
        nd_set(r, "rx", 1.5708)
        nd_set(r, "col", list_get(cols, k))
        bind k = k + 1.0
    }
    sbd_save(4.0)
}

# ── menus ────────────────────────────────────────────────────────────────────
# a menu row: plain text, the selected row turns candy pink
fn sbd_button(txt, act, x, y, w, col, anc) {
    bind b = bkd_button(txt, act, x, y, w, col, anc)
    nd_set(b, "ex", "style=text;hi=" + num_str(c_rgb(240.0, 90.0, 150.0)))
    return b
}
fn sbd_menu_begin(name, music) {
    sc_new(name, "2d")
    st_lclear("undo")
    st_lclear("redo")
    sel_clear()
    sc_set("bg", c_rgb(255.0, 240.0, 246.0))
    sc_set("music", music)
}
fn sbd_card(x, y, w, h, col, a, anc) {
    bind p = nd_add("panel", "Card", 0.0)
    nd_set(p, "px", x)  nd_set(p, "py", y)  nd_set(p, "w", w)  nd_set(p, "h", h)
    nd_set(p, "a", a)  nd_set(p, "col", col)  nd_set(p, "anc", anc)
    return p
}
fn sbd_menus() {
    bind ink = c_rgb(110.0, 64.0, 90.0)
    bind pink = c_rgb(240.0, 110.0, 160.0)
    bind mint = c_rgb(80.0, 170.0, 140.0)
    bind soft = c_rgb(170.0, 120.0, 150.0)
    # title
    sbd_menu_begin("Title", "assets/music/title.wav")
    sc_set("ex", "bg3d=scenes/page1.lscene;bgspin=0.02;bgzd=2")
    sbd_card(0.0, 70.0, 760.0, 210.0, c_rgb(255.0, 250.0, 242.0), 0.9, "tc")
    bkd_label("Moko's", 0.0, 90.0, 1280.0, 40.0, pink, "tc")
    bkd_label("Picture Book", 0.0, 140.0, 1280.0, 74.0, ink, "tc")
    bkd_label("a kawaii storybook platformer made with LingDev", 0.0, 236.0, 1280.0, 16.0, soft, "tc")
    sbd_card(0.0, 0.0 - 70.0, 460.0, 230.0, c_rgb(255.0, 250.0, 242.0), 0.9, "bc")
    sbd_button("OPEN THE BOOK", "goto:scenes/pages.lscene", 0.0, 0.0 - 236.0, 360.0, ink, "bc")
    sbd_button("QUIT", "quit", 0.0, 0.0 - 182.0, 360.0, soft, "bc")
    bkd_label("A D / arrows move   SPACE jump (hold to flutter)   S pound   J lick   K throw egg", 0.0, 0.0 - 118.0, 1280.0, 13.0, soft, "bc")
    save_scene(p_join(proj_root(), "scenes/title.lscene"))
    # page select
    sbd_menu_begin("Pages", "assets/music/title.wav")
    sc_set("ex", "bg3d=scenes/page2.lscene;bgspin=0.02;bgzd=2")
    sbd_card(0.0, 40.0, 1000.0, 640.0, c_rgb(255.0, 250.0, 242.0), 0.92, "tc")
    bkd_label("Choose a page", 0.0, 64.0, 1280.0, 42.0, ink, "tc")
    bind names = ["1   Marshmallow Meadow", "2   Candy Cloud Heights", "3   Sleepy Moon Forest", "4   Rainbow Picnic"]
    bind notes = ["Soft hills, sleepy puffs and the first fruits. Lucky fruit: grapes.", "Hop across candy clouds. Watch the flying puffs. Lucky fruit: banana.", "A moonlit forest with springs and spiky puffs. Lucky fruit: apple.", "The big picnic under the rainbow. Lucky fruit: banana."]
    bind k = 0.0
    while k < 4.0 {
        bind y = 150.0 + k * 118.0
        sbd_button(list_get(names, k), "goto:scenes/page" + num_str(k + 1.0) + ".lscene", 200.0, y, 420.0, ink, "tl")
        bkd_label(list_get(notes, k), 640.0, y + 4.0, 460.0, 15.0, soft, "tl")
        bkd_label("{$done" + num_str(k + 1.0) + "}", 640.0, y + 50.0, 460.0, 15.0, mint, "tl")
        bind k = k + 1.0
    }
    sbd_button("BACK", "goto:scenes/title.lscene", 200.0, 628.0, 160.0, soft, "tl")
    save_scene(p_join(proj_root(), "scenes/pages.lscene"))
    # page cleared
    sbd_menu_begin("Page cleared", "assets/music/title.wav")
    sc_set("ex", "bg3d=scenes/page4.lscene;bgspin=0.04;bgzd=2")
    sbd_card(0.0, 120.0, 700.0, 440.0, c_rgb(255.0, 250.0, 242.0), 0.94, "tc")
    bkd_label("Yummy! Page clear!", 0.0, 150.0, 1280.0, 48.0, pink, "tc")
    bkd_label("{$pagename}", 0.0, 220.0, 1280.0, 24.0, ink, "tc")
    bkd_label("Fruits  {$fruits}      Melons  {$melons}      Time  {$time}", 0.0, 290.0, 1280.0, 20.0, ink, "tc")
    bkd_label("{$hearts}", 0.0, 340.0, 1280.0, 30.0, c_rgb(255.0, 170.0, 60.0), "tc")
    sbd_button("NEXT PAGE", "goto:@next", 0.0, 420.0, 300.0, ink, "tc")
    sbd_button("THE BOOK", "goto:scenes/pages.lscene", 0.0, 474.0, 300.0, soft, "tc")
    save_scene(p_join(proj_root(), "scenes/cleared.lscene"))
}

← Back to index