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/rt/rt_ai.ling
# rt_ai.ling - small, deterministic game-AI helpers for scripts: steering, patrols, distances,
# plus a thin pass-through to the engine's behaviour trees on the desktop runtime.
fn ai_dist(i, j) {
bind a = nd_world_pos(i)
bind b = nd_world_pos(j)
bind dx = list_get(a, 0) - list_get(b, 0)
bind dy = list_get(a, 1) - list_get(b, 1)
bind dz = list_get(a, 2) - list_get(b, 2)
return sqrt(dx * dx + dy * dy + dz * dz)
}
fn ai_dist_xz(i, j) {
bind a = nd_world_pos(i)
bind b = nd_world_pos(j)
bind dx = list_get(a, 0) - list_get(b, 0)
bind dz = list_get(a, 2) - list_get(b, 2)
return sqrt(dx * dx + dz * dz)
}
# turn node i to face node j (yaw only)
fn ai_look_at(i, j) {
bind a = nd_world_pos(i)
bind b = nd_world_pos(j)
nd_set(i, "ry", atan2(list_get(b, 0) - list_get(a, 0), list_get(b, 2) - list_get(a, 2)))
}
# move node i toward (tx, tz) at `speed` units/s; returns the remaining distance
fn ai_seek(i, tx, tz, speed, dt) {
bind x = nd_get(i, "px")
bind z = nd_get(i, "pz")
bind dx = tx - x
bind dz = tz - z
bind d = sqrt(dx * dx + dz * dz)
if d < 0.001 { return 0.0 }
bind step = min(d, speed * dt)
nd_set(i, "px", x + dx / d * step)
nd_set(i, "pz", z + dz / d * step)
nd_set(i, "ry", atan2(dx, dz))
return d - step
}
# move away from (tx, tz)
fn ai_flee(i, tx, tz, speed, dt) {
bind x = nd_get(i, "px")
bind z = nd_get(i, "pz")
bind dx = x - tx
bind dz = z - tz
bind d = sqrt(dx * dx + dz * dz)
if d < 0.001 { bind dx = 1.0 bind d = 1.0 }
nd_set(i, "px", x + dx / d * speed * dt)
nd_set(i, "pz", z + dz / d * speed * dt)
nd_set(i, "ry", atan2(dx, dz))
}
# follow a list of points [x0,z0, x1,z1, ...] in a loop
fn ai_patrol(i, pts, speed, dt) {
bind n = floor(len(pts) / 2.0)
if n < 1.0 { return 0.0 }
bind key = "ai_pt_" + num_str(nd_id(i))
bind k = st_get(key, 0.0)
if k >= n { bind k = 0.0 }
bind rem = ai_seek(i, list_get(pts, k * 2.0), list_get(pts, k * 2.0 + 1.0), speed, dt)
if rem < 0.05 { st_set(key, (k + 1.0) % n) }
return rem
}
# wander around a home point (new random target every few seconds)
fn ai_wander(i, hx, hz, radius, speed, dt) {
bind key = "ai_wt_" + num_str(nd_id(i))
bind tkey = "ai_wn_" + num_str(nd_id(i))
bind now = st_get("game_t", 0.0)
if st_has(key) < 0.5 || now > st_get(tkey, 0.0) {
bind ang = rand() * 6.2831853
bind r = rand() * radius
st_set(key, [hx + cos(ang) * r, hz + sin(ang) * r])
st_set(tkey, now + 2.0 + rand() * 3.0)
}
bind tgt = st_get(key, [hx, hz])
return ai_seek(i, list_get(tgt, 0), list_get(tgt, 1), speed, dt)
}
# chase the player when within `sight`, otherwise wander; returns true while chasing
fn ai_guard(i, sight, speed, dt) {
bind p = game_player()
if p >= 0.0 && ai_dist_xz(i, p) < sight {
bind pp = nd_world_pos(p)
ai_seek(i, list_get(pp, 0), list_get(pp, 2), speed, dt)
return true
}
bind home = st_get("ai_home_" + num_str(nd_id(i)), [])
if len(home) < 2.0 {
bind home = [nd_get(i, "px"), nd_get(i, "pz")]
st_set("ai_home_" + num_str(nd_id(i)), home)
}
ai_wander(i, list_get(home, 0), list_get(home, 1), 4.0, speed * 0.5, dt)
return false
}
# behaviour tree helpers (desktop engine; returns "" where unavailable)
fn ai_bt(i, src) {
bind key = "ai_bt_" + num_str(nd_id(i))
if st_has(key) < 0.5 { st_set(key, plat_bt_build(src)) }
return st_get(key, 0.0 - 1.0)
}
fn ai_bt_tick(i, k, v) {
bind h = st_get("ai_bt_" + num_str(nd_id(i)), 0.0 - 1.0)
if h < 0.0 { return "" }
plat_bt_set(h, k, v)
return plat_bt_tick(h)
}