Custom AI
A brain tells an animal how to behave: how far it sees, where it roams, whom it attacks, when it runs away. You build brains in the Server Tool without code, or in Lua with modkit.ai.defineBrain. Both use the same fields, so you can start in the tool and move to a script later.
Three things put a brain into the world.
- A script spawns an animal with it.
- A spawner keeps a number of animals alive around a point.
- Wild AI hands every animal of one species that the game spawns to a brain.
The ai reference lists every field with its default and range.
Reading a position in the game
Spawners need coordinates. This command prints yours. Put it into any server script, walk to the place with a dinosaur and type /pos.
modkit.commands.add("pos", function(player)
player:message(("x = %.0f, y = %.0f, z = %.0f"):format(player.x, player.y, player.z))
end, true)
The last argument true limits the command to the admins from sv_admins.
A brain in the Server Tool
- Open the Server Tool and go to the tab AI. The switch on the left has three settings: Brains, Spawners and Wild AI. Stay on Brains.
- Click New brain. The tool offers five starting points: Predator, Prey, Guard, Passive and Blank. The first four fill the form with values that fit the name. Blank starts from the defaults. Pick Blank and name the brain
ridge_hunter. Names usea-z,0-9and_. - The editor on the right shows one section at a time. The tool works in metres and percent.
- Senses: sight range 80 m, view angle 220 degrees.
- Movement: roam radius 100 m, leash 150 m.
- Targets: smallest growth 30 percent, so hatchlings are left alone.
- Temperament: aggression 70 percent, flees below 20 percent health.
- Attack: leave it as it is. The brain finds the attacks of the species by itself.
- Pack: size 2.
- Idle: nothing.
- Body and despawn: despawn when no player was within 500 m for 300 seconds.
- The section Lua shows the brain as a
modkit.ai.defineBraincall, with a Copy button. - Click Save at the bottom. Revert throws away what you changed since the last save.
A running server picks up saved changes within a few seconds. Animals that already use the brain follow the new values right away. No restart is needed.
The same brain in Lua
Lua uses Unreal units and shares instead of metres and percent. 100 units are 1 m, and 70 percent is 0.7. Fields you leave out keep their defaults.
modkit.ai.defineBrain("ridge_hunter", {
label = "Ridge hunter",
senses = { sightRange = 8000, sightAngle = 220 },
movement = { roamRadius = 10000, leash = 15000 },
targets = { minGrowth = 0.3 },
temperament = { aggression = 0.7, fleeHealth = 0.2 },
pack = { size = 2 },
despawn = { noPlayerWithin = 50000, noPlayerSeconds = 300 },
})
A brain from a script replaces a brain of the same name from the tool while the server runs. A typo in a field name raises an error that names the field, so you see it in the server log when the resource starts.
Spawn two animals with it:
modkit.commands.add("hunters", function(player)
local x, y, z = player:pos()
if not x then return end
local ids, why = modkit.ai.spawn("carnotaurus", x + 6000, y, z, { count = 2, brain = "ridge_hunter" })
player:message(ids and "Two hunters are on their way." or why)
end, true)
A brain that thinks in Lua
The callback think runs before the built-in decision, every time the animal thinks while a player is within 150 m. Return nil and the built-in logic decides. Return a table and the animal does what it says.
This scavenger roams widely and attacks nobody. When a player died within the last two minutes, it trots to the carcass. When it is hurt, it runs from whoever hurt it.
local lastKill
modkit.events.add("playerDied", function(player)
lastKill = { x = player.x, y = player.y, z = player.z, at = os.time() }
end)
modkit.ai.defineBrain("scavenger", {
label = "Scavenger",
movement = { roamRadius = 20000, roamGait = "trot", homeMode = "wander" },
temperament = { aggression = 0, retaliate = false },
think = function(agent, senses)
if senses.health < 0.5 and senses.threat then
return { state = "flee", from = senses.threat }
end
if agent.state ~= "roam" or not lastKill or os.time() - lastKill.at > 120 then return end
if modkit.util.distance2d(agent, lastKill) > 1500 then
return { state = "moveTo", x = lastKill.x, y = lastKill.y, z = lastKill.z, gait = "trot" }
end
end,
})
agent is the table from modkit.ai.info. senses lists the players and animals in sight, the current threat, the hour of the day and the share of health. The reference has the full list of decisions.
think runs inside the server tick for every animal of the brain. Keep it to a few comparisons. An error in it is logged. After five errors the callback is switched off for that brain and the animals carry on with the built-in logic.
Scripts can also give direct orders to one animal with moveTo, follow, attack, flee, hold and release:
modkit.commands.add("escort", function(player)
local x, y, z = player:pos()
if not x then return end
local ids = modkit.ai.spawn("tyrannosaurus", x + 1500, y, z, { brain = "ridge_hunter", growth = 0.6 })
if ids then modkit.ai.follow(ids[1], player, 1200) end
end, true)
A spawner
In the tool, switch the left side to Spawners and create one. Pick the species and the brain, type in the position from /pos, and set radius, count and respawn time. Save. The server starts to fill the spawner within a few seconds.
The same from a script:
modkit.events.add("serverStarted", function()
modkit.ai.addSpawner({
id = "ridge_pack", species = "carnotaurus", brain = "ridge_hunter",
x = 112000, y = -64000, z = 2300, radius = 8000,
count = 4, respawnSeconds = 900,
activeAt = "night", playersWithin = 80000,
})
end)
The coordinates are placeholders. Use positions from your map.
- The spawner keeps
countanimals alive. It spawns them in groups of the brain'spack.size, each group at a random point insideradius. - After a death it waits
respawnSecondsbefore it refills. playersWithinstops it from spawning into an empty corner of the map. Together withdespawn.noPlayerWithinin the brain, animals exist only where players are.- A spawner always has its own growth value. It wins over
stats.growthof the brain. - Spawners from
addSpawnerlive until the server stops. Spawners from the tool are stored indata\ai\spawns.json.
Wild AI
Wild AI gives your brain to the animals the game spawns by itself. In the tool, switch the left side to Wild AI, pick a brain for a species and save. From a script:
modkit.ai.defineBrain("shy_prey", {
preset = "prey",
senses = { sightRange = 7000 },
temperament = { fleeFromEveryone = true, fleeSeconds = 12 },
movement = { homeMode = "wander" },
})
modkit.ai.setSpeciesBrain("deer", "shy_prey")
The plugin looks for new wild animals of the assigned species every 10 seconds and takes them over. From then on they have a handle, show up in modkit.ai.list() and fire the AI events. modkit.ai.setSpeciesBrain("deer", nil) gives them back to the game.
homeMode = "wander" suits wild animals. Their home point moves along with them, so they keep travelling the way the game placed them.
Watching what happens
modkit.events.add("aiStateChanged", function(id, from, to)
local info = modkit.ai.info(id)
if info then modkit.log.info(info.species .. " " .. id .. " (" .. info.brain .. "): " .. from .. " -> " .. to) end
end)
modkit.events.add("aiDied", function(id, killer)
if killer and killer.kind == "player" then
local player = modkit.players.get(killer.steamId)
if player then player:notify("The pack will remember that.") end
end
end)
Things to know
- At most 500 animals have a brain at the same time. All brains together get 2 ms per server tick.
- An animal far from every player thinks rarely. Within 60 m of a player it thinks five times per second, beyond 400 m every 20 seconds and without walking. Animals nobody is near cost almost nothing.
- Animals look for targets while a player is within 150 m of them. A
sightRangeabove 15000 changes nothing for spotting players. - An animal with a brain keeps the body, the movement and the attacks of its species. A deer with a predator brain still bites like a deer.
removeBrainandsetBrain(id, nil)hand animals back to the game's AI at any time.- Animals are gone after a restart. Spawners fill up again, and wild animals are taken over again.