Game Scripting

Wikipedia: Scripting language
Tengo is well suited for NPC behaviour, ability definitions, and quest logic in a game engine. The Go host loads and evaluates the script each tick, providing game state through a custom game module. Scripts can be hot-reloaded and modders can write them without touching Go.

fmt := import("fmt")
player := {health: 45, position: {x: 20, y: 15}, weapon: "sword"}
npc    := {
    name:         "Guard",
    health:       80,
    position:     {x: 10, y: 10},
    state:        "idle",
    alert_range:  8,
    attack_range: 2,
}
within := func(a, b, r) {
    dx := a.x - b.x
    dy := a.y - b.y
    return dx*dx + dy*dy <= r*r
}
tick := func(npc, player) {
    if npc.state == "idle" {
        if within(player.position, npc.position, npc.alert_range) {
            npc.state = "alerted"
            return {action: "say", text: "Halt! Who goes there?"}
        }
        return {action: "patrol"}
    }

    if npc.state == "alerted" {
        if within(player.position, npc.position, npc.attack_range) {
            npc.state = "fighting"
        } else {
            return {action: "move_towards", target: player.position}
        }
    }

    if npc.state == "fighting" {
        if npc.health < 20 {
            npc.state = "fleeing"
            return {action: "say", text: "Mercy!"}
        }
        dmg := 12
        if player.weapon != "" { dmg = 8 }
        return {action: "attack", damage: dmg}
    }

    if npc.state == "fleeing" {
        return {action: "run"}
    }

    return {action: "idle"}
}

print_tick := func(npc, result) {
    fmt.printf("%-10s → %s", npc.state, result["action"])
    if !is_undefined(result["text"]) {
        fmt.printf(": %q", result["text"])
    }
    if !is_undefined(result["target"]) {
        t := result["target"]
        fmt.printf(" (%d,%d)", t.x, t.y)
    }
    if !is_undefined(result["damage"]) {
        fmt.printf(" (dmg %d)", result["damage"])
    }
    fmt.println("")
}
positions := [
    {x: 20, y: 15},  // out of range  → patrol
    {x: 14, y: 12},  // alert range   → alerted, say
    {x: 12, y: 11},  // closing in    → move_towards
    {x: 10, y: 10},  // attack range  → fighting, attack
]

for _, pos in positions {
    player.position = pos
    result := tick(npc, player)
    print_tick(npc, result)
}
fmt.println("--- NPC takes heavy damage ---")
npc.health = 15
result := tick(npc, player)
print_tick(npc, result)

try it

idle       → patrol
alerted    → say: "Halt! Who goes there?"
alerted    → move_towards (12,11)
fighting   → attack (dmg 8)
--- NPC takes heavy damage ---
fleeing    → say: "Mercy!"
loading…