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).
キーワード game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdevコード / ファイル (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
}