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/rt/rt_data.ling
# rt_data.ling - text records and small data helpers shared by the editor and exported games.
# Scene files are line-oriented: one record per line, fields separated by "|", each field "key=value".
# Values are escaped so a field can hold any text: % -> %25 | -> %7C = -> %3D newline -> %0A
fn esc(s) {
bind t = str_replace(s, "%", "%25")
bind t = str_replace(t, "|", "%7C")
bind t = str_replace(t, "=", "%3D")
bind t = str_replace(t, "\n", "%0A")
return str_replace(t, "\r", "")
}
fn unesc(s) {
bind t = str_replace(s, "%0A", "\n")
bind t = str_replace(t, "%3D", "=")
bind t = str_replace(t, "%7C", "|")
return str_replace(t, "%25", "%")
}
# the part of a field before / after its first "="
fn kv_key(field) {
bind i = str_find(field, "=")
if i < 0.0 { return str_trim(field) }
return str_trim(str_slice(field, 0.0, i))
}
fn kv_val(field) {
bind i = str_find(field, "=")
if i < 0.0 { return "" }
return unesc(str_slice(field, i + 1.0, str_len(field) - i - 1.0))
}
# value of `key` in a record (list of fields), or `d` when absent
fn rec_get(fields, key, d) {
bind i = 0.0
while i < len(fields) {
bind f = list_get(fields, i)
if kv_key(f) == key { return kv_val(f) }
bind i = i + 1.0
}
return d
}
fn rec_num(fields, key, d) {
bind v = rec_get(fields, key, "")
if v == "" { return d }
return to_number(v)
}
fn rec_has(fields, key) {
bind i = 0.0
while i < len(fields) {
if kv_key(list_get(fields, i)) == key { return true }
bind i = i + 1.0
}
return false
}
# "k=v" field builders
fn fld(k, v) { return "|" + k + "=" + esc("" + v) }
fn fldn(k, v) { return "|" + k + "=" + num_str(round(v * 10000.0) / 10000.0) }
# "a,b,c" <-> numbers
fn nums_parse(s) {
bind out = []
bind p = str_split(s, ",")
bind i = 0.0
while i < len(p) {
bind t = str_trim(list_get(p, i))
if t != "" { bind out = list_push(out, to_number(t)) }
bind i = i + 1.0
}
return out
}
fn nums_text(l) {
bind s = ""
bind i = 0.0
while i < len(l) {
if i > 0.0 { bind s = s + "," }
bind s = s + num_str(round(list_get(l, i) * 10000.0) / 10000.0)
bind i = i + 1.0
}
return s
}
fn nums_get(l, i, d) {
if i < len(l) { return list_get(l, i) }
return d
}
# path helpers (forward slashes everywhere)
fn p_norm(p) { return str_replace(p, "\\", "/") }
fn p_base(p) {
bind q = p_norm(p)
bind parts = str_split(q, "/")
bind i = len(parts) - 1.0
while i >= 0.0 {
if list_get(parts, i) != "" { return list_get(parts, i) }
bind i = i - 1.0
}
return ""
}
fn p_dir(p) {
bind q = p_norm(p)
bind i = str_len(q) - 1.0
while i >= 0.0 {
if str_slice(q, i, 1.0) == "/" { return str_slice(q, 0.0, i) }
bind i = i - 1.0
}
return ""
}
fn p_ext(p) {
bind b = p_base(p)
bind parts = str_split(b, ".")
if len(parts) < 2.0 { return "" }
return str_lower(list_get(parts, len(parts) - 1.0))
}
fn p_stem(p) {
bind b = p_base(p)
bind e = p_ext(p)
if e == "" { return b }
return str_slice(b, 0.0, str_len(b) - str_len(e) - 1.0)
}
fn p_join(a, b) {
if a == "" { return b }
if b == "" { return a }
bind q = p_norm(a)
if str_ends_with(q, "/") { return q + b }
return q + "/" + p_norm(b)
}
fn p_is_abs(p) {
bind q = p_norm(p)
if str_starts_with(q, "/") { return true }
if str_len(q) > 1.0 && str_slice(q, 1.0, 1.0) == ":" { return true }
return false
}
# make `p` relative to `root` when it lies inside it
fn p_rel(root, p) {
bind r = p_norm(root)
bind q = p_norm(p)
if !str_ends_with(r, "/") { bind r = r + "/" }
if str_starts_with(str_lower(q), str_lower(r)) { return str_slice(q, str_len(r), str_len(q) - str_len(r)) }
return q
}
# list helpers (lists are values: always rebind the result)
fn l_has(l, v) {
bind i = 0.0
while i < len(l) {
if list_get(l, i) == v { return true }
bind i = i + 1.0
}
return false
}
fn l_index(l, v) {
bind i = 0.0
while i < len(l) {
if list_get(l, i) == v { return i }
bind i = i + 1.0
}
return 0.0 - 1.0
}
fn l_remove_at(l, k) {
bind out = []
bind i = 0.0
while i < len(l) {
if i != k { bind out = list_push(out, list_get(l, i)) }
bind i = i + 1.0
}
return out
}
fn l_set_at(l, k, v) {
bind out = []
bind i = 0.0
while i < len(l) {
if i == k { bind out = list_push(out, v) } else { bind out = list_push(out, list_get(l, i)) }
bind i = i + 1.0
}
return out
}
fn l_insert_at(l, k, v) {
bind out = []
bind i = 0.0
while i < len(l) {
if i == k { bind out = list_push(out, v) }
bind out = list_push(out, list_get(l, i))
bind i = i + 1.0
}
if k >= len(l) { bind out = list_push(out, v) }
return out
}
# store-list helpers: remove / insert in a st_ list
fn sl_remove(key, k) {
st_ldel(key, k)
}
fn sl_insert(key, k, v) {
bind n = st_llen(key)
st_lpush(key, v)
bind i = n
while i > k {
st_lset(key, i, st_lget(key, i - 1.0))
bind i = i - 1.0
}
st_lset(key, k, v)
}
fn sl_to_list(key) {
bind out = []
bind i = 0.0
while i < st_llen(key) {
bind out = list_push(out, st_lget(key, i))
bind i = i + 1.0
}
return out
}
fn sl_from_list(key, l) {
st_lclear(key)
bind i = 0.0
while i < len(l) {
st_lpush(key, list_get(l, i))
bind i = i + 1.0
}
}
# simple key=value settings text
fn cfg_get(text, key, d) {
bind lines = str_split(text, "\n")
bind i = 0.0
while i < len(lines) {
bind ln = str_trim(list_get(lines, i))
if str_starts_with(ln, key + "=") { return str_slice(ln, str_len(key) + 1.0, str_len(ln) - str_len(key) - 1.0) }
bind i = i + 1.0
}
return d
}
fn cfg_all(text, key) {
bind out = []
bind lines = str_split(text, "\n")
bind i = 0.0
while i < len(lines) {
bind ln = str_trim(list_get(lines, i))
if str_starts_with(ln, key + "=") { bind out = list_push(out, str_slice(ln, str_len(key) + 1.0, str_len(ln) - str_len(key) - 1.0)) }
bind i = i + 1.0
}
return out
}
# strip surrounding quotes
fn unquote(s) {
bind t = str_trim(s)
if str_len(t) >= 2.0 && str_starts_with(t, "\"") && str_ends_with(t, "\"") { return str_slice(t, 1.0, str_len(t) - 2.0) }
return t
}
# truthiness of a flag that may arrive as a number (1 / 0) or a bool (fs_list's "is directory")
fn is_true(v) {
if v == true { return true }
if v == 1.0 { return true }
return false
}
# is node `id` in the editor's selection (an empty list in games)
fn rt_selected(id) {
bind n = st_llen("sel_ids")
bind i = 0.0
while i < n {
if st_lget("sel_ids", i) == id { return true }
bind i = i + 1.0
}
return false
}