lingdev
↓ 0 downloadsLingDev: 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).
Keywords game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdevCode / Files (136)
ling/demo_breaker.ling
# demo_breaker.ling - builds the "Ling Breaker" demo that ships with LingDev: a 3-D breakout in
# `brick` mode (rt_brick.ling). Five stages of bricks you can edit as ordinary box nodes (tag
# `brick`, ex t= / hp= / drop= / slide=), a title over a live stage, stage select, game over and
# victory screens. The music is the author's own masters with MIDI-derived .beats files and the
# sound effects are synthesised; both ship in LingDev's assets and are copied into the project.
fn bkd_root() { return p_join(st_get("app_root", "."), "projects/LingBreaker") }
fn demo_breaker_open() {
if file_exists(p_join(bkd_root(), "lingdev.toml")) { return project_open_path(p_join(bkd_root(), "lingdev.toml")) }
return demo_breaker_build()
}
fn demo_breaker_build() {
bind dir = p_join(st_get("app_root", "."), "projects")
plat_mkdir(dir)
if !proj_create(dir, "LingBreaker", 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", "Ling Breaker")
st_set("proj_author", "LingDev demo")
bkd_install_audio()
bkd_stage1()
bkd_stage2()
bkd_stage3()
bkd_stage4()
bkd_stage5()
bkd_menus()
st_set("proj_start", "scenes/title.lscene")
proj_save()
gen_write_all()
assets_mark_stale()
open_scene(p_join(proj_root(), "scenes/stage1.lscene"))
status("Built the Ling Breaker demo: 5 stages, title, stage select. F5 plays from the title.")
return true
}
# ── audio: sfx + music + beats from LingDev's assets ─────────────────────────
fn bkd_copy_dir(src, dst, exts) {
plat_mkdir(dst)
bind items = plat_list(src)
bind n = len(items) / 3.0
bind i = 0.0
while i < n {
bind nm = list_get(items, i * 3.0)
bind ext = p_ext(nm)
if !is_true(list_get(items, i * 3.0 + 1.0)) && l_has(exts, ext) {
bind d = p_join(dst, nm)
if !file_exists(d) || ext == "beats" || ext == "txt" { plat_copy(p_join(src, nm), d) }
}
bind i = i + 1.0
}
return 0.0
}
fn bkd_install_audio() {
bind root = st_get("app_root", ".")
plat_mkdir(p_join(proj_root(), "assets"))
bkd_copy_dir(p_join(root, "assets/sfx/breaker"), p_join(proj_root(), "assets/sfx"), ["wav"])
bkd_copy_dir(p_join(root, "assets/music/breaker"), p_join(proj_root(), "assets/music"), ["wav", "beats", "txt"])
return 0.0
}
# ── building blocks ──────────────────────────────────────────────────────────
fn bkd_box(name, x, y, z, sx, sy, sz, col, tag) {
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 bkd_stage_begin(name, n, title, speed) {
sc_new(name, "3d")
st_lclear("undo")
st_lclear("redo")
sel_clear()
sc_set("bg", c_rgb(10.0, 6.0, 26.0))
sc_set("amb", 0.42)
sc_set("floor", 0.0)
sc_set("music", "assets/music/stage" + num_str(n) + ".wav")
bind next = ""
if n < 5.0 { bind next = "scenes/stage" + num_str(n + 1.0) + ".lscene" }
sc_set("ex", "mode=brick;stage=" + num_str(n) + ";title=" + title + ";field=-7.2,7.2,19.2;speed=" + num_str(speed) + ";beats=assets/music/stage" + num_str(n) + ".beats;next=" + next + ";shade=1;rim=0.16;rimcol=150,190,255")
# floor tiles on the grid (each tile glows on its own under the ball's light)
bind fx = 0.0 - 6.3
while fx < 6.4 {
bind fz = 0.0 - 0.6
while fz < 19.0 {
bind odd = floor((fx + 6.3) / 1.8 + 0.1) + floor((fz + 0.6) / 1.8 + 0.1)
bind tint = c_rgb(20.0, 18.0, 48.0)
if odd - floor(odd / 2.0) * 2.0 > 0.5 { bind tint = c_rgb(26.0, 22.0, 60.0) }
bkd_box("Floor", fx, 0.08, fz, 0.9, 0.08, 0.9, tint, "")
bind fz = fz + 1.8
}
bind fx = fx + 1.8
}
bkd_box("Wall L", 0.0 - 7.5, 0.0 - 0.45, 8.85, 0.3, 0.45, 10.35, c_rgb(70.0, 40.0, 110.0), "wall")
bkd_box("Wall R", 7.5, 0.0 - 0.45, 8.85, 0.3, 0.45, 10.35, c_rgb(40.0, 60.0, 120.0), "wall")
bkd_box("Wall back", 0.0, 0.0 - 0.45, 19.5, 7.8, 0.45, 0.3, c_rgb(60.0, 46.0, 120.0), "wall")
bkd_box("Paddle", 0.0, 0.0 - 0.34, 0.6, 1.3, 0.32, 0.32, c_rgb(230.0, 235.0, 255.0), "paddle")
st_set("ed_tx", 0.0)
st_set("ed_ty", 0.0 - 0.5)
st_set("ed_tz", 8.4)
st_set("ed_yaw", 0.0)
st_set("ed_pitch", 0.86)
st_set("ed_dist", 17.0)
}
# a brick at grid column c (0..7) and row r (0 = back row)
fn bkd_brick(c, r, t, ex) {
bind x = 0.0 - 6.3 + c * 1.8
bind z = 17.8 - r * 0.95
bind col = bk_row_col(r)
if t == "tough" { bind col = bk_dark(bk_row_col(r), 0.62) }
if t == "steel" { bind col = c_rgb(150.0, 158.0, 178.0) }
if t == "bomb" { bind col = c_rgb(215.0, 45.0, 35.0) }
if t == "crystal" { bind col = c_rgb(140.0, 245.0, 255.0) }
if t == "gold" { bind col = c_rgb(255.0, 200.0, 50.0) }
bind b = bkd_box("Brick " + num_str(c) + "," + num_str(r), x, 0.0 - 0.36, z, 0.82, 0.36, 0.4, col, "brick")
bind e = "t=" + t + ";row=" + num_str(r)
if ex != "" { bind e = e + ";" + ex }
nd_set(b, "ex", e)
return b
}
fn bkd_save(n) { save_scene(p_join(proj_root(), "scenes/stage" + num_str(n) + ".lscene")) }
# 1: Prism Garden - rainbow rows, a few crystals
fn bkd_stage1() {
bkd_stage_begin("Prism Garden", 1.0, "PRISM GARDEN", 9.5)
bind r = 0.0
while r < 6.0 {
bind c = 0.0
while c < 8.0 {
bind t = "normal"
if (r == 2.0 && (c == 1.0 || c == 6.0)) || (r == 4.0 && (c == 3.0 || c == 4.0)) { bind t = "crystal" }
bkd_brick(c, r, t, "")
bind c = c + 1.0
}
bind r = r + 1.0
}
bkd_save(1.0)
}
# 2: Neon Pyramid - tough bricks, bombs in the core, gold at the top
fn bkd_stage2() {
bkd_stage_begin("Neon Pyramid", 2.0, "NEON PYRAMID", 10.0)
bind r = 0.0
while r < 7.0 {
bind c = 0.0
while c < 8.0 {
bind half = 3.5
bind span = 0.5 + r * 0.6
if abs(c - half) <= span {
bind t = "normal"
if r == 0.0 { bind t = "gold" }
else if abs(c - half) > span - 0.9 { bind t = "tough" }
else if r == 3.0 || r == 5.0 { bind t = "bomb" }
bkd_brick(c, r, t, sel(t == "tough", "hp=2", ""))
}
bind c = c + 1.0
}
bind r = r + 1.0
}
bkd_save(2.0)
}
# 3: Steel Orbit - steel walls with gaps, a sliding row
fn bkd_stage3() {
bkd_stage_begin("Steel Orbit", 3.0, "STEEL ORBIT", 10.5)
bind c = 0.0
while c < 8.0 {
bkd_brick(c, 0.0, sel(c == 0.0 || c == 7.0, "gold", "tough"), "hp=3")
bkd_brick(c, 1.0, "normal", "")
if c != 3.0 && c != 4.0 { bkd_brick(c, 2.0, "steel", "") }
bkd_brick(c, 4.0, "normal", "")
bkd_brick(c, 5.0, sel(c == 2.0 || c == 5.0, "crystal", "normal"), "")
bind c = c + 1.0
}
# the sliding row
bind k = 0.0
while k < 3.0 {
bind b = bkd_brick(1.5 + k * 2.0, 3.0, "steel", "slide=1.6")
bind k = k + 1.0
}
bkd_brick(0.0, 6.0, "steel", "")
bkd_brick(7.0, 6.0, "steel", "")
bkd_save(3.0)
}
# 4: Bomb Garden - a checkerboard of bombs for chain reactions
fn bkd_stage4() {
bkd_stage_begin("Bomb Garden", 4.0, "BOMB GARDEN", 11.0)
bind r = 0.0
while r < 7.0 {
bind c = 0.0
while c < 8.0 {
bind odd = (c + r) - floor((c + r) / 2.0) * 2.0
bind t = "normal"
if odd > 0.5 && r > 0.5 && r < 6.0 { bind t = "bomb" }
if r == 0.0 { bind t = "tough" }
if r == 6.0 && (c == 0.0 || c == 7.0) { bind t = "crystal" }
bkd_brick(c, r, t, sel(t == "tough", "hp=2", ""))
bind c = c + 1.0
}
bind r = r + 1.0
}
bkd_save(4.0)
}
# 5: Ling Star - a star of gold and crystal held by steel and tough bricks
fn bkd_stage5() {
bkd_stage_begin("Ling Star", 5.0, "LING STAR", 11.5)
bind rows = ["...TT...", "..TGGT..", "TTGCCGTT", ".TGCCGT.", "..TGGT..", ".TT..TT.", "S.B..B.S", "SS....SS"]
bind r = 0.0
while r < len(rows) {
bind line = list_get(rows, r)
bind c = 0.0
while c < 8.0 {
bind ch = str_slice(line, c, 1.0)
if ch == "T" { bkd_brick(c, r, "tough", "hp=2") }
if ch == "G" { bkd_brick(c, r, "gold", "") }
if ch == "C" { bkd_brick(c, r, "crystal", "drop=M") }
if ch == "S" { bkd_brick(c, r, "steel", "") }
if ch == "B" { bkd_brick(c, r, "bomb", "") }
bind c = c + 1.0
}
bind r = r + 1.0
}
bind k = 0.0
while k < 2.0 {
bkd_brick(2.0 + k * 3.0, 6.5, "steel", "slide=2.2")
bind k = k + 1.0
}
bkd_save(5.0)
}
# ── menus ────────────────────────────────────────────────────────────────────
fn bkd_menu_begin(name, music) {
sc_new(name, "2d")
st_lclear("undo")
st_lclear("redo")
sel_clear()
sc_set("bg", c_rgb(10.0, 6.0, 26.0))
sc_set("music", music)
}
fn bkd_label(txt, x, y, w, size, col, anc) {
bind l = nd_add("label", "Text", 0.0)
nd_set(l, "txt", txt)
nd_set(l, "px", x)
nd_set(l, "py", y)
nd_set(l, "w", w)
nd_set(l, "h", size + 8.0)
nd_set(l, "a", size)
nd_set(l, "col", col)
nd_set(l, "anc", anc)
nd_set(l, "name", str_slice(txt, 0.0, min(18.0, str_len(txt))))
return l
}
fn bkd_button(txt, act, x, y, w, col, anc) {
bind b = nd_add("button", txt, 0.0)
nd_set(b, "txt", txt)
nd_set(b, "scr", act)
nd_set(b, "px", x)
nd_set(b, "py", y)
nd_set(b, "w", w)
nd_set(b, "h", 44.0)
nd_set(b, "a", 20.0)
nd_set(b, "col", col)
nd_set(b, "anc", anc)
nd_set(b, "ex", "style=text")
return b
}
fn bkd_band(y, h, a, anc) {
bind band = nd_add("panel", "Band", 0.0)
nd_set(band, "px", 0.0)
nd_set(band, "py", y)
nd_set(band, "w", 1280.0)
nd_set(band, "h", h)
nd_set(band, "a", a)
nd_set(band, "col", c_rgb(8.0, 5.0, 24.0))
nd_set(band, "anc", anc)
return band
}
fn bkd_menus() {
bind ink = c_rgb(235.0, 240.0, 255.0)
bind pink = c_rgb(255.0, 110.0, 210.0)
bind cyan = c_rgb(110.0, 210.0, 255.0)
bind dim = c_rgb(150.0, 160.0, 210.0)
# title over a slowly turning stage
bkd_menu_begin("Title", "assets/music/title.wav")
sc_set("ex", "bg3d=scenes/stage5.lscene;bgspin=0.05;bgzd=2")
bkd_band(110.0, 200.0, 0.55, "tc")
bkd_label("LING BREAKER", 0.0, 130.0, 1280.0, 84.0, pink, "tc")
bkd_label("a stellar 3-D breakout made with LingDev", 0.0, 240.0, 1280.0, 18.0, cyan, "tc")
bkd_band(0.0 - 40.0, 270.0, 0.55, "bc")
bkd_button("PLAY", "set:newrun=1;goto:scenes/stage1.lscene", 0.0, 0.0 - 250.0, 320.0, ink, "bc")
bkd_button("STAGE SELECT", "goto:scenes/select.lscene", 0.0, 0.0 - 196.0, 320.0, ink, "bc")
bkd_button("QUIT", "quit", 0.0, 0.0 - 142.0, 320.0, ink, "bc")
bkd_label("mouse / A D / arrows / stick move click / SPACE launch + fire P pause", 0.0, 0.0 - 70.0, 1280.0, 14.0, dim, "bc")
save_scene(p_join(proj_root(), "scenes/title.lscene"))
# stage select
bkd_menu_begin("Stage select", "assets/music/title.wav")
sc_set("ex", "bg3d=scenes/stage2.lscene;bgspin=0.04;bgzd=2")
bkd_band(0.0, 720.0, 0.62, "tl")
bkd_label("STAGE SELECT", 0.0, 70.0, 1280.0, 46.0, cyan, "tc")
bind names = ["1 PRISM GARDEN", "2 NEON PYRAMID", "3 STEEL ORBIT", "4 BOMB GARDEN", "5 LING STAR"]
bind notes = ["Rainbow rows and a few crystals. Every crystal drops a power-up.", "Tough walls around a core of bombs. Gold at the top.", "Steel you cannot break without a fireball, and a sliding row.", "A checkerboard of bombs. Start a chain reaction.", "The final star: gold, crystal and moving steel."]
bind k = 0.0
while k < 5.0 {
bind y = 170.0 + k * 92.0
bkd_button(list_get(names, k), "set:newrun=1;goto:scenes/stage" + num_str(k + 1.0) + ".lscene", 200.0, y, 400.0, ink, "tl")
bkd_label(list_get(notes, k), 620.0, y + 12.0, 560.0, 15.0, dim, "tl")
bind k = k + 1.0
}
bkd_button("BACK", "goto:scenes/title.lscene", 200.0, 640.0, 200.0, dim, "tl")
save_scene(p_join(proj_root(), "scenes/select.lscene"))
# game over
bkd_menu_begin("Game over", "assets/music/title.wav")
sc_set("ex", "bg3d=scenes/stage4.lscene;bgspin=0.03;bgzd=2")
bkd_band(140.0, 380.0, 0.62, "tc")
bkd_label("GAME OVER", 0.0, 160.0, 1280.0, 70.0, pink, "tc")
bkd_label("SCORE {$score}", 0.0, 270.0, 1280.0, 30.0, ink, "tc")
bkd_label("HI-SCORE {$hi}", 0.0, 320.0, 1280.0, 20.0, c_rgb(255.0, 220.0, 120.0), "tc")
bkd_label("{$newhi}", 0.0, 360.0, 1280.0, 22.0, cyan, "tc")
bkd_button("TRY AGAIN", "set:newrun=1;goto:scenes/stage1.lscene", 0.0, 420.0, 320.0, ink, "tc")
bkd_button("TITLE", "goto:scenes/title.lscene", 0.0, 470.0, 320.0, dim, "tc")
save_scene(p_join(proj_root(), "scenes/gameover.lscene"))
# victory
bkd_menu_begin("Victory", "assets/music/title.wav")
sc_set("ex", "bg3d=scenes/stage5.lscene;bgspin=0.08;bgzd=2")
bkd_band(140.0, 380.0, 0.62, "tc")
bkd_label("ALL STAGES CLEAR!", 0.0, 160.0, 1280.0, 60.0, cyan, "tc")
bkd_label("SCORE {$score}", 0.0, 270.0, 1280.0, 30.0, ink, "tc")
bkd_label("HI-SCORE {$hi}", 0.0, 320.0, 1280.0, 20.0, c_rgb(255.0, 220.0, 120.0), "tc")
bkd_label("{$newhi}", 0.0, 360.0, 1280.0, 22.0, pink, "tc")
bkd_button("PLAY AGAIN", "set:newrun=1;goto:scenes/stage1.lscene", 0.0, 420.0, 320.0, ink, "tc")
bkd_button("TITLE", "goto:scenes/title.lscene", 0.0, 470.0, 320.0, dim, "tc")
save_scene(p_join(proj_root(), "scenes/victory.lscene"))
}