灵符lingfu

lingdev

↓ 0 ダウンロード

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
キーワード game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdev
ホームページhttps://ling-lang.org
ライセンスApache-2.0 OR MIT
作者ling-lin
READMEコード / ファイルバージョン依存グラフ依存パッケージセキュリティビルド設定

コード / ファイル (136)

ling/scriptpanel.ling
# scriptpanel.ling - scripts/*.ling of the project: a file list and a small line-based code editor.
# The editor keeps the file as lines; the current line is edited through the text buffer.
# Enter adds a line below, Backspace on an empty line removes it, Tab indents, Ctrl+S saves.

fn script_list() {
    bind out = []
    if proj_root() == "" { return out }
    bind items = plat_list(p_join(proj_root(), "scripts"))
    bind n = len(items) / 3.0
    bind i = 0.0
    while i < n {
        bind nm = list_get(items, i * 3.0)
        if !is_true(list_get(items, i * 3.0 + 1.0)) && p_ext(nm) == "ling" { bind out = list_push(out, p_join(p_join(proj_root(), "scripts"), nm)) }
        bind i = i + 1.0
    }
    return out
}
fn script_open(path) {
    bind text = plat_read(path)
    st_set("ed_path", path)
    sl_from_list("ed_lines", str_split(str_replace(text, "\r", ""), "\n"))
    if st_llen("ed_lines") < 0.5 { st_lpush("ed_lines", "") }
    st_set("ed_cur", 0.0)
    st_set("ed_dirty", 0.0)
    st_set("ed_scroll", 0.0)
    st_set("focus", "")
}
fn script_create(name) {
    if proj_root() == "" { status("Open a project first")  return 0.0 }
    bind nm = str_lower(str_replace(str_trim(name), " ", "_"))
    if nm == "" { return 0.0 }
    bind path = p_join(proj_root(), "scripts/" + nm + ".ling")
    if !file_exists(path) { plat_write(path, script_template(nm)) }
    script_open(path)
    bind i = sel_index()
    if i >= 0.0 && nd_get(i, "scr") == "" { undo_push()  nd_set(i, "scr", nm) }
    st_set("dock_tab", 2.0)
    st_set("show_dock", 1.0)
    assets_mark_stale()
    status("Script " + nm + ".ling" + sel(i >= 0.0, " attached to " + nd_get(i, "name"), ""))
    return 0.0
}
fn script_save() {
    bind path = st_get("ed_path", "")
    if path == "" { return 0.0 }
    bind s = ""
    bind i = 0.0
    while i < st_llen("ed_lines") {
        bind s = s + st_lget("ed_lines", i) + "\n"
        bind i = i + 1.0
    }
    if plat_write(path, s) { st_set("ed_dirty", 0.0)  status("Saved " + p_base(path)) } else { status("Could not write " + path) }
    return 0.0
}
fn script_line_col(ln) {
    bind t = str_trim(ln)
    if str_starts_with(t, "#") { return C_TEXT_DIM }
    if str_starts_with(t, "fn ") { return C_GREEN }
    if str_starts_with(t, "bind ") || str_starts_with(t, "return") { return C_TEXT }
    if str_starts_with(t, "if ") || str_starts_with(t, "while ") || str_starts_with(t, "else") { return C_YELLOW }
    return C_TEXT
}

fn script_panel(x, y, w, h) {
    bind lw = 180.0
    g_rect(x, y, lw, h, C_BG_SIDE)
    u_vsep(x + lw - 1.0, y, h)
    ds_word(x + 8.0, y + 8.0, 10.0, "SCRIPTS", ds_col("ctl"), true)
    if u_hot(x + lw - 30.0, y + 4.0, 22.0, 20.0) { u_tip("New script") }
    if u_icon_button("sp_new", x + lw - 30.0, y + 4.0, 22.0, 20.0, "plus", 11.0, C_TEXT_STRONG, false) { modal_text("New script name", "new_script", "enemy") }
    bind files = script_list()
    bind top = y + 30.0
    bind i = 0.0
    while i < len(files) {
        bind f = list_get(files, i)
        bind ry = top + i * 20.0
        bind cur = st_get("ed_path", "") == f
        bind hot = u_hot(x, ry, lw - 2.0, 20.0)
        if cur { g_rect(x, ry, lw - 2.0, 20.0, C_SEL_ROW) } else if hot { g_rect(x, ry, lw - 2.0, 20.0, C_HOVER_ROW) }
        icon_draw("script", x + 14.0, ry + 10.0, 11.0, C_GREEN)
        g_text_fit(x + 26.0, ry + 4.0, lw - 34.0, 11.0, sel(cur, C_TEXT_STRONG, C_TEXT), p_base(f))
        if hot && u_pressed() { script_open(f) }
        if hot && u_rpressed() { ctx_open("asset", f, u_mx(), u_my()) }
        bind i = i + 1.0
    }
    if len(files) < 0.5 { g_text_wrap(x + 10.0, top + 6.0, lw - 20.0, 11.0, C_TEXT_DIM, sel(proj_root() == "", "Open a project to see its scripts.", "No scripts yet. + creates one from a template.")) }
    # editor
    bind ex = x + lw
    bind ew = w - lw
    g_rect(ex, y, ew, h, C_BG_DEEP)
    bind path = st_get("ed_path", "")
    bind bar = 26.0
    g_rect(ex, y, ew, bar, C_BG_PANEL)
    u_hsep(ex, y + bar - 1.0, ew)
    if path == "" {
        g_text(ex + 12.0, y + 7.0, 12.0, C_TEXT_DIM, "Pick a script, or create one. Hooks: <name>_ready(i)  _update(i, dt)  _draw(i)  _input(i, key)  _trigger(i, other)  _hit(i, other)  _net(i, msg)")
        g_text_wrap(ex + 12.0, y + bar + 12.0, ew - 24.0, 12.0, C_TEXT_DIM, "Scripts run in the built game (Build > Generate / run.bat) and in `ling run main.ling`. The editor's Play uses the built-in behaviours: player controller, camera rigs, triggers, timelines, cutscenes, HUD buttons.")
        return 0.0
    }
    g_text(ex + 12.0, y + 7.0, 12.0, C_TEXT_STRONG, p_base(path) + sel(st_get("ed_dirty", 0.0) > 0.5, " *", ""))
    bind bx = ex + ew - 300.0
    if u_button("sp_save", bx, y + 3.0, 60.0, 20.0, "Save", C_BLUE) { script_save() }
    if u_button("sp_reload", bx + 64.0, y + 3.0, 60.0, 20.0, "Reload", 0.0) { script_open(path) }
    if u_button("sp_reveal", bx + 128.0, y + 3.0, 70.0, 20.0, "Explorer", 0.0) { plat_reveal(path) }
    if u_button("sp_hook", bx + 202.0, y + 3.0, 90.0, 20.0, "+ update hook", 0.0) {
        bind nm = p_stem(path)
        st_lpush("ed_lines", "")
        st_lpush("ed_lines", "fn " + nm + "_update(i, dt) {")
        st_lpush("ed_lines", "    ")
        st_lpush("ed_lines", "}")
        st_set("ed_dirty", 1.0)
    }
    # text area
    bind ty = y + bar
    bind th = h - bar
    bind lh = 16.0
    bind n = st_llen("ed_lines")
    bind cur = clampf(st_get("ed_cur", 0.0), 0.0, max(0.0, n - 1.0))
    bind focused = st_get("focus", "") == "code"
    bind hot = u_hot(ex, ty, ew - 12.0, th)
    if hot && u_pressed() {
        bind line = floor((u_my() - ty + st_get("ed_scroll", 0.0) - 4.0) / lh)
        if line >= 0.0 && line < n {
            bind cur = line
            st_set("focus", "code")
            text_set(st_lget("ed_lines", cur))
            bind focused = true
        }
    }
    if focused && u_pressed() && !hot && st_get("blk", 0.0) < 0.5 { st_set("focus", "")  bind focused = false }
    if focused {
        bind cur_text = text_poll()
        if cur_text != st_lget("ed_lines", cur) { st_lset("ed_lines", cur, cur_text)  st_set("ed_dirty", 1.0) }
        if key_pressed("enter") {
            bind indent = ""
            bind ln = st_lget("ed_lines", cur)
            bind k = 0.0
            while k < str_len(ln) && str_slice(ln, k, 1.0) == " " { bind indent = indent + " "  bind k = k + 1.0 }
            if str_ends_with(str_trim(ln), "{") { bind indent = indent + "    " }
            sl_insert("ed_lines", cur + 1.0, indent)
            bind cur = cur + 1.0
            text_set(indent)
            st_set("ed_dirty", 1.0)
        }
        if key_pressed("backspace") && st_lget("ed_lines", cur) == "" && n > 1.0 && cur > 0.0 {
            st_ldel("ed_lines", cur)
            bind cur = cur - 1.0
            text_set(st_lget("ed_lines", cur))
            st_set("ed_dirty", 1.0)
        }
        if key_pressed("up") && cur > 0.0 { bind cur = cur - 1.0  text_set(st_lget("ed_lines", cur)) }
        if key_pressed("down") && cur < n - 1.0 { bind cur = cur + 1.0  text_set(st_lget("ed_lines", cur)) }
        if key_pressed("pageup") { bind cur = max(0.0, cur - 20.0)  text_set(st_lget("ed_lines", cur)) }
        if key_pressed("pagedown") { bind cur = min(n - 1.0, cur + 20.0)  text_set(st_lget("ed_lines", cur)) }
        if key_pressed("tab") { text_set(st_lget("ed_lines", cur) + "    ") }
        if u_ctrl() && key_pressed("s") { script_save() }
        if key_pressed("escape") { st_set("focus", "") }
        # keep the cursor line in view
        bind cy = 4.0 + cur * lh
        bind sc = st_get("ed_scroll", 0.0)
        if cy < sc { st_set("ed_scroll", cy) }
        if cy + lh > sc + th { st_set("ed_scroll", cy + lh - th) }
    }
    st_set("ed_cur", cur)
    bind n = st_llen("ed_lines")
    bind off = u_scroll_y("ed_sb", ex, ty, ew, th, n * lh + 8.0, st_get("ed_scroll", 0.0))
    st_set("ed_scroll", off)
    u_clip(ex, ty, ew - 12.0, th)
    bind gutter = 40.0
    g_rect(ex, ty, gutter, th, C_BG_SIDE)
    bind i = 0.0
    while i < n {
        bind ly = ty + 4.0 + i * lh - off
        if ly + lh >= ty && ly <= ty + th {
            bind ln = st_lget("ed_lines", i)
            if i == cur { g_rect(ex + gutter, ly - 1.0, ew - gutter, lh, sel(focused, C_SEL_ROW, C_HOVER_ROW)) }
            g_text_r(ex + gutter - 6.0, ly, 10.0, C_TEXT_DIM, num_str(i + 1.0))
            g_text(ex + gutter + 8.0, ly, 12.0, script_line_col(ln), ln)
            if i == cur && focused && floor(st_get("now", 0.0) * 2.0) % 2.0 < 0.5 { g_rect(ex + gutter + 8.0 + g_text_w(12.0, ln) + 1.0, ly, 1.0, lh - 2.0, C_TEXT_STRONG) }
        }
        bind i = i + 1.0
    }
    u_unclip()
    return 0.0
}

← 索引に戻る