Skip to main content

States

Factories for the states every enemy ends up writing: States.Patrol, States.Investigate, States.Search, States.Wander and States.Wait. Exposed as VluxyAI.States.

local States = VluxyAI.States
local When = VluxyAI.When

local Brain: VluxyAI.Brain = {
	Initial = "Patrol",
	Interrupts = {
		{ To = "Hunt", When = When.Has("SeenTarget"), Except = { "Hunt", "Attack" } },
		{ To = "Investigate", When = When.New("HeardAt"), Except = { "Hunt", "Attack" } },
	},
	States = {
		Patrol = States.Patrol({ Points = "Nodes", Speed = 10 }),
		Investigate = States.Investigate({ Target = "HeardPosition", Speed = 14, Then = "Patrol" }),
		Hunt = { ... },
		Search = States.Search({ Around = "SeenPosition", Radius = 12, Points = 3, Duration = 14, Then = "Patrol" }),
		Attack = VluxyAI.Combat.Attack.State({ ... }),
	},
}

States.Roam walks the best point of an Interest map, marking each visited, so an enemy that has lost you checks the places you would plausibly be.

Every factory accepts Transitions (checked before the state's own logic, like any state), Enter and Exit hooks run after the built-in ones, Speed to set on entry, and Key, the agent.Data key its scratch lives under (default: the state's own name). Positions may be given as a blackboard or Data key, a Vector3, or a function of the agent. A point the navigator cannot reach is skipped on the next tick, so a search point inside a wall costs nothing but a failed plan.

Types​

RoamOptions​

interface RoamOptions {
Interest: Interest | string--

The map of points to roam, or the agent.Data key it is under, so a brain written once can roam a map each agent is given (SetData({ Interest = map })). Required.

Range: number?--

Studs beyond which a point scores nothing for distance. Default 120.

Tags: {string}?--

Only points with one of these tags.

Linger: number | {number}?--

Seconds to stand at each point before moving on. Default 1.

Duration: number | {number}?--

Seconds to roam before Then. Without it, roams until a transition or until no point scores.

Then: string?--

The state to enter when time is up or no point is worth a look.

Considerations: {Consideration<InterestPoint>}?--

Extra scoring for Interest.Best.

Stray: number?--

Chance, 0 to 1, that a pick ignores the clues and goes to a random point it has not looked at lately (still nearer ones more often), so a searcher is not perfectly predictable. Default 0.

Speed: number?--

Set on entry.

Random: Random?--

For ranged Linger and Duration.

Key: string?--

Default "Roam".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Functions​

RandomPoint​

States.RandomPoint(
random: Random,
centre: Vector3,
radius: number
) → Vector3--

A point on the horizontal disc of radius around centre, uniformly distributed, at the centre's height.

Patrol​

States.Patrol(options: PatrolOptions) → State

Types

​

interface PatrolOptions {
Points: string | {Vector3} | (agent: Agent) → {Vector3}--

The route: a Data key holding a list, the list itself, or a function. Required.

Order: "Loop" | "PingPong" | "Random"?--

Loop back to the first point, walk the list both ways, or pick any other point. Default "Loop".

Pause: number | {number}?--

Seconds to stand at each point, or a { min, max } range. Default none.

Speed: number?--

Set on entry.

Random: Random?--

For Random order and a ranged Pause. Default agent.Data.Random, else a new one.

Key: string?--

The Data key for the current index. Default "Patrol".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Walks the points in order, forever. The index survives leaving and re-entering the state, so an enemy that broke off to investigate resumes where it was. A point that cannot be reached is skipped.

Investigate​

States.Investigate(options: InvestigateOptions) → State

Types

​

interface InvestigateOptions {
Target: string | Vector3 | (agent: Agent) → Vector3?--

Where to go: a blackboard or Data key such as "HeardPosition", a position, or a function. Read once on entry. Required.

Then: string?--

The state to enter when there, when the target is missing, or when the path fails. Without it the state waits for a transition.

Linger: number | {number}?--

Seconds to stand at the spot before Then. Default 0.

Acknowledge: string?--

A blackboard key to Agent:Acknowledge on entry, such as "HeardAt", so the noise that brought the agent here does not count as new again.

Speed: number?--

Set on entry.

Random: Random?--

For a ranged Linger.

Key: string?--

Default "Investigate".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Goes to one place and, once there (or once the path fails), lingers and moves on to Then. The place is read on entry, so a newer noise re-enters the state through a transition rather than pulling the agent mid-walk.

States.Search(options: SearchOptions) → State

Types

​

interface SearchOptions {
Around: string | Vector3 | (agent: Agent) → Vector3?--

The centre: usually "SeenPosition" or "LastKnownPosition". Missing means the agent's own position. Read once on entry.

Radius: number?--

Studs around the centre the points are picked in. Default 12.

Points: number?--

How many points to pick. They are walked in turn and cycled until Duration runs out. Default 3.

Duration: number | {number}?--

Seconds to search before Then. Default 15.

Then: string?--

The state to enter when time is up. Without it the state searches until a transition.

Speed: number?--

Set on entry.

Random: Random?--

Picks the points. Default agent.Data.Random, else a new one.

Key: string?--

Default "Search".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Picks a few random points around a spot and walks them in turn until Duration runs out: the "where did they go" after losing sight. A point the navigator cannot reach is skipped.

Wander​

States.Wander(options: WanderOptions?) → State

Types

​

interface WanderOptions {
Home: string | Vector3 | (agent: Agent) → Vector3?--

The centre to stay around: a key, a position, or a function. Missing means where the agent stood when it first entered the state, kept from then on.

Radius: number?--

Studs around home the points are picked in. Default 20.

Pause: number | {number}?--

Seconds to stand at each point. Default { 1, 3 }.

Speed: number?--

Set on entry.

Random: Random?--

Picks the points and the pauses.

Key: string?--

Default "Wander".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Drifts between random points around home, pausing at each. The Guardian's idle, as a line.

Roam​

States.Roam(options: RoamOptions) → State

Asks the Interest map for the best point, walks there, lingers, marks it visited, and asks again. Points the agent just visited score nothing, so it spreads its search. When no point scores, or Duration runs out, it enters Then.

GraphRoam​

States.GraphRoam(options: GraphRoamOptions) → State

Types

​

interface GraphRoamOptions {
Graph: {GraphNode} | Graph | (agent: Agent) → {GraphNode}--

The nodes: a Graph pathfinder (its GetNodes), the node list, or a function. Required.

Pick: ((
agent: Agent,
nodes: {GraphNode},
current: number
) → number?)?--

The next destination's node index, from the node nearest the agent. nil stands still and asks again next tick. Default: any other node at random.

Pause: number | {number}?--

Seconds to stand at each destination, or a { min, max } range. Default none.

Speed: number?--

Set on entry.

Random: Random?--

For the default pick and a ranged Pause. Default agent.Data.Random, else a new one.

Key: string?--

The Data key for the route. Default "GraphRoam".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Roams an authored graph one node at a time. It picks a destination, routes to it with Graph.AStar from the node nearest the agent, and sends the agent to each node of the route in turn, starting the next leg only once the last one arrived. So the graph decides the order while the agent's own pathfinder plans every leg, unlike Hybrid, which walks the middle of a graph route straight. An agent off the graph walks to the nearest node first; a leg that fails re-plans from where the agent stands.

The route is kept at agent.Data[Key], for a brain or a debug view to read: Route (node indices), Leg (the index into Route being walked), Next (that node's index) and Goal. It survives leaving and re-entering the state, so an enemy that broke off to investigate resumes its leg.

Errors

TypeDescription
"Graph is required"

Wait​

States.Wait(options: WaitOptions?) → State

Types

​

interface WaitOptions {
Duration: number | {number}?--

Seconds to stand still before Then. Without it the state waits for a transition.

Then: string?--

The state to enter when the time is up.

Speed: number?--

Set on entry.

Random: Random?--

For a ranged Duration.

Key: string?--

Default "Wait".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Stops and stands still: dormant, stunned, listening. With Duration and Then it moves on by itself; otherwise its transitions decide.

Stare​

States.Stare(options: StareOptions) → State

Types

​

interface StareOptions {
Target: string | Vector3 | (agent: Agent) → Vector3?--

What to look at, read every tick: usually "SafePosition" from SafeWatch.

Duration: number | {number}?--

Seconds to stare before Then. Default 4.

Then: string?--

The state to enter when the time is up.

Cooldown: number?--

Seconds after leaving during which When.Timer("States.<Key>Cooldown") is false, so a brain does not stare again at once. Default 0.

Face: boolean?--

Turn toward the target while staring. Off for a statue. Default true.

Speed: number?--

Set on entry.

Random: Random?--

For a ranged Duration.

Key: string?--

Default "Stare".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Stops, and stares at something it cannot or will not reach: a player in a safe room, a light it will not step into. After Duration it moves on to Then; on the way out it starts the States.<Key>Cooldown timer for Cooldown seconds, which a transition into this state checks with When.Timer so the agent walks away instead of turning straight back.

Stare = VluxyAI.States.Stare({ Target = "SafePosition", Duration = 4, Cooldown = 20, Then = "Patrol" }),
-- and, wherever the agent should notice:
{ To = "Stare", When = When.All(When.Has("SafeTarget"), When.Timer("States.StareCooldown")) },

Burrow​

States.Burrow(options: BurrowOptions) → State

Types

​

interface BurrowOptions {
To: string | Vector3 | (agent: Agent) → Vector3?--

Where to come up, as a floor position, read once on entry: usually the Key of a Hideout sense. Required.

Then: string?--

The state to enter once it has risen, or at once when To has no position.

Depth: number?--

Studs it sinks under the floor. Default 7.

Time: number?--

Seconds to sink, and again to rise. Default 1.5.

Height: number?--

Studs above the floor its body ends up at. Default 3, a humanoid's root.

Key: string?--

Default "Burrow".

Transitions: {Transition}?
Enter: ((agent: Agent) → ())?
Exit: ((agent: Agent) → ())?
}

Vanishes and comes back somewhere else: stops, sinks Depth studs into the floor over Time seconds, moves under the To position, rises out of the floor there over another Time, and enters Then. For a statue that burrows, a ghost that fades through the floor, a creature that drops into a vent: pair it with Hideout so it only ever comes up where nobody is looking. A voice line or a sound keyed on this state's name covers the whole move.

Errors

TypeDescription
"To is required"
Show raw api
{
    "functions": [
        {
            "name": "RandomPoint",
            "desc": "",
            "params": [
                {
                    "name": "random",
                    "desc": "",
                    "lua_type": "Random"
                },
                {
                    "name": "centre",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "radius",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "A point on the horizontal disc of `radius` around `centre`, uniformly distributed, at the centre's height.",
                    "lua_type": "Vector3"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 193,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Patrol",
            "desc": "Walks the points in order, forever. The index survives leaving and re-entering the state, so\nan enemy that broke off to investigate resumes where it was. A point that cannot be reached\nis skipped.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "PatrolOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 243,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Investigate",
            "desc": "Goes to one place and, once there (or once the path fails), lingers and moves on to `Then`.\nThe place is read on entry, so a newer noise re-enters the state through a transition rather\nthan pulling the agent mid-walk.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "InvestigateOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 366,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Search",
            "desc": "Picks a few random points around a spot and walks them in turn until `Duration` runs out:\nthe \"where did they go\" after losing sight. A point the navigator cannot reach is skipped.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "SearchOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 455,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Wander",
            "desc": "Drifts between random points around home, pausing at each. The Guardian's idle, as a line.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "WanderOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 537,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Roam",
            "desc": "Asks the [Interest] map for the best point, walks there, lingers, marks it visited, and asks\nagain. Points the agent just visited score nothing, so it spreads its search. When no point\nscores, or `Duration` runs out, it enters `Then`.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "RoamOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 635,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "GraphRoam",
            "desc": "Roams an authored graph one node at a time. It picks a destination, routes to it with\n[Graph.AStar] from the node nearest the agent, and sends the agent to each node of the route\nin turn, starting the next leg only once the last one arrived. So the graph decides the order\nwhile the agent's own pathfinder plans every leg, unlike [Hybrid], which walks the middle of a\ngraph route straight. An agent off the graph walks to the nearest node first; a leg that fails\nre-plans from where the agent stands.\n\nThe route is kept at `agent.Data[Key]`, for a brain or a debug view to read: `Route` (node\nindices), `Leg` (the index into `Route` being walked), `Next` (that node's index) and `Goal`.\nIt survives leaving and re-entering the state, so an enemy that broke off to investigate\nresumes its leg.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "GraphRoamOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Graph is required\"",
                    "desc": ""
                }
            ],
            "source": {
                "line": 785,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Wait",
            "desc": "Stops and stands still: dormant, stunned, listening. With `Duration` and `Then` it moves on by\nitself; otherwise its transitions decide.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "WaitOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 936,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Stare",
            "desc": "Stops, and stares at something it cannot or will not reach: a player in a safe room, a light\nit will not step into. After `Duration` it moves on to `Then`; on the way out it starts the\n`States.<Key>Cooldown` timer for `Cooldown` seconds, which a transition into this state checks\nwith `When.Timer` so the agent walks away instead of turning straight back.\n\n```lua\nStare = VluxyAI.States.Stare({ Target = \"SafePosition\", Duration = 4, Cooldown = 20, Then = \"Patrol\" }),\n-- and, wherever the agent should notice:\n{ To = \"Stare\", When = When.All(When.Has(\"SafeTarget\"), When.Timer(\"States.StareCooldown\")) },\n```",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "StareOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 1011,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "Burrow",
            "desc": "Vanishes and comes back somewhere else: stops, sinks `Depth` studs into the floor over `Time`\nseconds, moves under the `To` position, rises out of the floor there over another `Time`, and\nenters `Then`. For a statue that burrows, a ghost that fades through the floor, a creature that\ndrops into a vent: pair it with [Hideout] so it only ever comes up where nobody is looking.\nA voice line or a sound keyed on this state's name covers the whole move.",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "BurrowOptions"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "State"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"To is required\"",
                    "desc": ""
                }
            ],
            "source": {
                "line": 1087,
                "path": "lib/Brain/States.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "PatrolOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Points",
                    "lua_type": "string | { Vector3 } | (agent: Agent) -> { Vector3 }",
                    "desc": "The route: a `Data` key holding a list, the list itself, or a function. Required."
                },
                {
                    "name": "Order",
                    "lua_type": "\"Loop\" | \"PingPong\" | \"Random\"?",
                    "desc": "Loop back to the first point, walk the list both ways, or pick any other point. Default `\"Loop\"`."
                },
                {
                    "name": "Pause",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand at each point, or a `{ min, max }` range. Default none."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For `Random` order and a ranged `Pause`. Default `agent.Data.Random`, else a new one."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "The `Data` key for the current index. Default `\"Patrol\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 222,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "InvestigateOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Target",
                    "lua_type": "string | Vector3 | (agent: Agent) -> Vector3?",
                    "desc": "Where to go: a blackboard or `Data` key such as `\"HeardPosition\"`, a position, or a function. Read once on entry. Required."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter when there, when the target is missing, or when the path fails. Without it the state waits for a transition."
                },
                {
                    "name": "Linger",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand at the spot before `Then`. Default `0`."
                },
                {
                    "name": "Acknowledge",
                    "lua_type": "string?",
                    "desc": "A blackboard key to [Agent:Acknowledge] on entry, such as `\"HeardAt\"`, so the noise that brought the agent here does not count as new again."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For a ranged `Linger`."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Investigate\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 344,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "SearchOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Around",
                    "lua_type": "string | Vector3 | (agent: Agent) -> Vector3?",
                    "desc": "The centre: usually `\"SeenPosition\"` or `\"LastKnownPosition\"`. Missing means the agent's own position. Read once on entry."
                },
                {
                    "name": "Radius",
                    "lua_type": "number?",
                    "desc": "Studs around the centre the points are picked in. Default `12`."
                },
                {
                    "name": "Points",
                    "lua_type": "number?",
                    "desc": "How many points to pick. They are walked in turn and cycled until `Duration` runs out. Default `3`."
                },
                {
                    "name": "Duration",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to search before `Then`. Default `15`."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter when time is up. Without it the state searches until a transition."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "Picks the points. Default `agent.Data.Random`, else a new one."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Search\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 433,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "WanderOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Home",
                    "lua_type": "string | Vector3 | (agent: Agent) -> Vector3?",
                    "desc": "The centre to stay around: a key, a position, or a function. Missing means where the agent stood when it first entered the state, kept from then on."
                },
                {
                    "name": "Radius",
                    "lua_type": "number?",
                    "desc": "Studs around home the points are picked in. Default `20`."
                },
                {
                    "name": "Pause",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand at each point. Default `{ 1, 3 }`."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "Picks the points and the pauses."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Wander\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 518,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "RoamOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Interest",
                    "lua_type": "Interest | string",
                    "desc": "The map of points to roam, or the `agent.Data` key it is under, so a brain written once can roam a map each agent is given (`SetData({ Interest = map })`). Required."
                },
                {
                    "name": "Range",
                    "lua_type": "number?",
                    "desc": "Studs beyond which a point scores nothing for distance. Default `120`."
                },
                {
                    "name": "Tags",
                    "lua_type": "{ string }?",
                    "desc": "Only points with one of these tags."
                },
                {
                    "name": "Linger",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand at each point before moving on. Default `1`."
                },
                {
                    "name": "Duration",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to roam before `Then`. Without it, roams until a transition or until no point scores."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter when time is up or no point is worth a look."
                },
                {
                    "name": "Considerations",
                    "lua_type": "{ Consideration<InterestPoint> }?",
                    "desc": "Extra scoring for [Interest.Best]."
                },
                {
                    "name": "Stray",
                    "lua_type": "number?",
                    "desc": "Chance, `0` to `1`, that a pick ignores the clues and goes to a random point it has not looked at lately (still nearer ones more often), so a searcher is not perfectly predictable. Default `0`."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For ranged `Linger` and `Duration`."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Roam\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 610,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "GraphRoamOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Graph",
                    "lua_type": "{ GraphNode } | Graph | (agent: Agent) -> { GraphNode }",
                    "desc": "The nodes: a [Graph] pathfinder (its `GetNodes`), the node list, or a function. Required."
                },
                {
                    "name": "Pick",
                    "lua_type": "((agent: Agent, nodes: { GraphNode }, current: number) -> number?)?",
                    "desc": "The next destination's node index, from the node nearest the agent. `nil` stands still and asks again next tick. Default: any other node at random."
                },
                {
                    "name": "Pause",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand at each destination, or a `{ min, max }` range. Default none."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For the default pick and a ranged `Pause`. Default `agent.Data.Random`, else a new one."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "The `Data` key for the route. Default `\"GraphRoam\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 755,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "WaitOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Duration",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stand still before `Then`. Without it the state waits for a transition."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter when the time is up."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For a ranged `Duration`."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Wait\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 917,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "StareOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Target",
                    "lua_type": "string | Vector3 | (agent: Agent) -> Vector3?",
                    "desc": "What to look at, read every tick: usually `\"SafePosition\"` from [SafeWatch]."
                },
                {
                    "name": "Duration",
                    "lua_type": "number | { number }?",
                    "desc": "Seconds to stare before `Then`. Default `4`."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter when the time is up."
                },
                {
                    "name": "Cooldown",
                    "lua_type": "number?",
                    "desc": "Seconds after leaving during which `When.Timer(\"States.<Key>Cooldown\")` is `false`, so a brain does not stare again at once. Default `0`."
                },
                {
                    "name": "Face",
                    "lua_type": "boolean?",
                    "desc": "Turn toward the target while staring. Off for a statue. Default `true`."
                },
                {
                    "name": "Speed",
                    "lua_type": "number?",
                    "desc": "Set on entry."
                },
                {
                    "name": "Random",
                    "lua_type": "Random?",
                    "desc": "For a ranged `Duration`."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Stare\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 981,
                "path": "lib/Brain/States.luau"
            }
        },
        {
            "name": "BurrowOptions",
            "desc": "",
            "fields": [
                {
                    "name": "To",
                    "lua_type": "string | Vector3 | (agent: Agent) -> Vector3?",
                    "desc": "Where to come up, as a floor position, read once on entry: usually the `Key` of a [Hideout] sense. Required."
                },
                {
                    "name": "Then",
                    "lua_type": "string?",
                    "desc": "The state to enter once it has risen, or at once when `To` has no position."
                },
                {
                    "name": "Depth",
                    "lua_type": "number?",
                    "desc": "Studs it sinks under the floor. Default `7`."
                },
                {
                    "name": "Time",
                    "lua_type": "number?",
                    "desc": "Seconds to sink, and again to rise. Default `1.5`."
                },
                {
                    "name": "Height",
                    "lua_type": "number?",
                    "desc": "Studs above the floor its body ends up at. Default `3`, a humanoid's root."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "Default `\"Burrow\"`."
                },
                {
                    "name": "Transitions",
                    "lua_type": "{ Transition }?",
                    "desc": ""
                },
                {
                    "name": "Enter",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                },
                {
                    "name": "Exit",
                    "lua_type": "((agent: Agent) -> ())?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 1063,
                "path": "lib/Brain/States.luau"
            }
        }
    ],
    "name": "States",
    "desc": "Factories for the states every enemy ends up writing: [States.Patrol], [States.Investigate],\n[States.Search], [States.Wander] and [States.Wait]. Exposed as `VluxyAI.States`.\n\n```lua\nlocal States = VluxyAI.States\nlocal When = VluxyAI.When\n\nlocal Brain: VluxyAI.Brain = {\n\tInitial = \"Patrol\",\n\tInterrupts = {\n\t\t{ To = \"Hunt\", When = When.Has(\"SeenTarget\"), Except = { \"Hunt\", \"Attack\" } },\n\t\t{ To = \"Investigate\", When = When.New(\"HeardAt\"), Except = { \"Hunt\", \"Attack\" } },\n\t},\n\tStates = {\n\t\tPatrol = States.Patrol({ Points = \"Nodes\", Speed = 10 }),\n\t\tInvestigate = States.Investigate({ Target = \"HeardPosition\", Speed = 14, Then = \"Patrol\" }),\n\t\tHunt = { ... },\n\t\tSearch = States.Search({ Around = \"SeenPosition\", Radius = 12, Points = 3, Duration = 14, Then = \"Patrol\" }),\n\t\tAttack = VluxyAI.Combat.Attack.State({ ... }),\n\t},\n}\n```\n\n[States.Roam] walks the best point of an [Interest] map, marking each visited, so an enemy\nthat has lost you checks the places you would plausibly be.\n\nEvery factory accepts `Transitions` (checked before the state's own logic, like any state),\n`Enter` and `Exit` hooks run after the built-in ones, `Speed` to set on entry, and `Key`, the\n`agent.Data` key its scratch lives under (default: the state's own name). Positions may be\ngiven as a blackboard or `Data` key, a `Vector3`, or a function of the agent. A point the\nnavigator cannot reach is skipped on the next tick, so a search point inside a wall costs\nnothing but a failed plan.",
    "source": {
        "line": 64,
        "path": "lib/Brain/States.luau"
    }
}