Skip to main content

VluxyAI

Simple, composable AI for Roblox. An AI is a brain (a table of states), a pathfinder (plans), a mover (executes) and some senses (write a blackboard), stacked with the builder into an agent.

local VluxyAI = require(ReplicatedStorage.Packages.VluxyAI)

local agent = VluxyAI.new(model)
	:UseBrain(Stalker)
	:UsePathfinder(VluxyAI.Pathfinders.Navmesh.new({ AgentRadius = 2 }))
	:UseMover(VluxyAI.Movers.Humanoid.new(model))
	:AddSense(VluxyAI.Senses.Sight.new({ Range = 40 }))
	:Build()
	:Start()

Every part is a contract in Types; pass your own module in to extend the package.

Types​

HumanoidPresetOptions​

interface HumanoidPresetOptions {
Navmesh: NavmeshOptions?--

Options for Navmesh.new.

Straight: StraightOptions?--

Options for Straight.new, the direct walker.

Mover: HumanoidOptions?--

Options for HumanoidMover.new.

Sight: SightOptions?--

Options for Sight.new.

Proximity: ProximityOptions?--

Options for Proximity.new.

Navigation: NavigationOptions?--

Navigator tuning. Default { RepathInterval = 0.25, MaxPathAge = 2 }.

Graph: Pathfinder?--

Authored nodes (Graph): routes go over the graph first (Hybrid with Prefer = "Graph", RefineEnds = false) and the navmesh answers only where the graph has no way. Without it, the navmesh plans everything.

Doors: boolean?--

Walk through doors: Doors.Step with WalkThrough for "Door" steps, a Doors.Opener sense, and a direct walker that sees past unlocked doors. For doors rigs do not collide with (Collision.LetRigsThrough). Default false.

SafeWatch: boolean?--

Add SafeWatch, so the agent notices players in safe areas and is moved out of one that switches on around it (to a Graph node when given). Default true.

}

PuppetPresetOptions​

interface PuppetPresetOptions {
Navmesh: NavmeshOptions?--

Options for [Navmesh.new]: AgentRadius, AgentHeight and the rest.

Mover: CFrameMoverOptions?--

Options for [CFrameMover.new]: Speed, TurnSpeed, HeightOffset. HeightOffset defaults to 0 here, so the anchor's pivot is the floor point.

Navigation: NavigationOptions?--

Navigator tuning. Default { RepathInterval = 0.25, MaxPathAge = 2 }.

}

HeadlessPresetOptions​

interface HeadlessPresetOptions {
Name: string?--

Used in messages. Default "Agent".

Position: Vector3--

Where it starts: the floor point.

Yaw: number?--

Starting yaw in radians. Default 0.

Mover: CFrameMoverOptions?--

Speed, TurnSpeed and the rest, as Record.new takes them.

Pathfinder: Pathfinder?--

Default a Navmesh built from Navmesh. A Graph or Straight keeps PathfindingService out of it.

Navmesh: NavmeshOptions?--

Options for the default Navmesh. Ignored when Pathfinder is given.

Navigation: NavigationOptions?--

Navigator tuning. Default { RepathInterval = 0.25, MaxPathAge = 2 }.

}

Properties​

Utility​

VluxyAI.Utility: Utility

When​

VluxyAI.When: When

Ready-made When predicates for transitions and interrupts: When.Has, When.New, When.Below, When.PathDone, When.Timer and the rest.

States​

VluxyAI.States: States

Cleaner​

VluxyAI.Cleaner: Cleaner

The package's cleanup bag, for anything you build around an agent.

Signal​

VluxyAI.Signal: Signal

The package's Signal; make one for a Hearing noise source.

Pathfinders​

VluxyAI.Pathfinders: {
Navmesh: Navmesh,
Straight: Straight,
Ladder: Ladder,
Graph: Graph,
Hybrid: Hybrid
}

The built-in Types.Pathfinders: Navmesh for geometry, Straight for a clear line, Ladder to try several in order, Graph for A* over authored nodes, and Hybrid to compose a graph with a local pathfinder.

Movers​

VluxyAI.Movers: {
Humanoid: HumanoidMover,
CFrame: CFrameMover,
Record: Record
}

The built-in Types.Movers. Record has no Instance at all, for headless agents.

Senses​

VluxyAI.Senses: {
Sight: Sight,
Proximity: Proximity,
Hearing: Hearing,
Sounds: Sounds,
Awareness: Awareness,
Watched: Watched,
Surroundings: Surroundings,
Neighbours: Neighbours,
Throttle: Throttle,
SafeWatch: SafeWatch,
Clues: Clues,
Hideout: Hideout,
Stamina: Stamina
}

The built-in Types.Senses. Hearing is for noises your code fires; Sounds is for the tagged Sound instances the world is already playing; Awareness turns what the others wrote into a detection meter; Neighbours is the group sense, so enemies sharing a map know about each other and, when asked, keep out of each other's way.

Director​

VluxyAI.Director: Director

Pacing and shared knowledge for a group of enemies: a tension meter, a difficulty knob and a shared blackboard, read through director:Sense().

Interest​

VluxyAI.Interest: Interest

Points of interest with a strength each, and Best to pick where an enemy looks next. States.Roam walks one.

Claims​

VluxyAI.Claims: Claims

A shared "who is on what" table, so two enemies never chase the same player unless you say so.

Doors​

VluxyAI.Doors: Doors

Doors agents open: :OnStep("Door", VluxyAI.Doors.Step()) over your door script, through attributes or a driver of your own.

SafeAreas​

VluxyAI.SafeAreas: SafeAreas

Parts tagged VLUXYAI_SAFE that agents never enter while VLUXYAI_SAFE/Enabled is not false. Call VluxyAI.SafeAreas.Setup() once on the server.

Collision​

VluxyAI.Collision: Collision

The rig collision group, and parts agents walk through while players still collide with them.

PlayerNoise​

VluxyAI.PlayerNoise: PlayerNoise

Players make noise by moving: speed bands turned into noises on your Hearing signal.

Voice​

VluxyAI.Voice: Voice

Random voice lines by state, added as a sense: :AddSense(VluxyAI.Voice.new({ Lines = ... })).

Tables​

VluxyAI.Tables: Tables

The package's small table helpers, including Tables.Prefixed.

Perception​

VluxyAI.Perception: Perception

The helpers the built-in senses are made of: alive player roots, cones, line of sight, and HumanoidOf for a state that acts on a target. The place to start for a custom sense.

Animator​

VluxyAI.Animator: NPCAnimator

The NPC animation helper.

Locomotion​

VluxyAI.Locomotion: Locomotion

Idle and walk for a rig from its real speed, plus one-shots by name, through the animator.

Footsteps​

VluxyAI.Footsteps: Footsteps

Footstep audio from measured movement, for the client's replica rigs (or a server rig).

Sync​

VluxyAI.Sync: {
Broadcaster: Broadcaster,
Replica: Replica,
Look: Look
}

Client-side presentation: a Broadcaster watches an agent on the server, a Replica drives a rig and your client states on the client. The game owns the remotes between them.

Combat​

VluxyAI.Combat: {Kill: Kill}

Attack.State, the one generic state, and Kill.Live and Kill.BlackBox, the two ways an agent kills a character.

Debug​

VluxyAI.Debug: {
PathVisual: PathVisual,
GraphVisual: GraphVisual,
StateLabel: StateLabel,
SenseVisual: SenseVisual,
Rig: Rig
}

Opt-in debug views, and a runtime R15 rig for tests and playgrounds.

Internals​

VluxyAI.Internals: {
Runner: Runner,
Navigator: Navigator
}

The pieces the Agent is made of, for a custom scheduler or a client renderer that wants to run a brain table itself.

Presets​

VluxyAI.Presets: {
Humanoid: (
model: Model,
) → Builder,
Puppet: (
anchor: Model | BasePart,
) → Builder,
Headless: (options: HeadlessPresetOptions) → Builder
}

Ready-made stacks. Presets.Humanoid(model) returns a Builder already carrying a Navmesh pathfinder (graph-first Hybrid when Graph is given) with Straight as its direct walker, a HumanoidMover mover, Sight, Proximity and SafeWatch senses, and a quarter-second repath; Doors = true adds walking through doors. Add a brain, any senses of your own, and build. It is the stack the examples build by hand, opt-in and overridable.

local agent = VluxyAI.Presets.Humanoid(rig, { Graph = graph, Doors = true })
	:UseBrain(Brain)
	:AddSense(VluxyAI.Senses.Clues.new(interest))
	:Build()

Presets.Puppet(anchor) is the server half of puppet mode, for an enemy with no visible body on the server: a Navmesh pathfinder and a CFrameMover on the anchor (HeightOffset 0, so its pivot is the floor point) with the same repath. A Part anchor is wrapped in a Model of its own and made invisible, anchored and inert. It adds no senses; stream it with a Broadcaster once built, and draw it on the client with a puppet Replica.

local agent = VluxyAI.Presets.Puppet(anchorPart, {
	Navmesh = { AgentRadius = 4, AgentHeight = 12 },
	Mover = { Speed = 10, TurnSpeed = 6 },
})
	:UseBrain(Brain)
	:AddSense(VluxyAI.Senses.Hearing.new(noises))
	:Build()
local broadcaster = VluxyAI.Sync.Broadcaster.new(agent, { PositionRate = 10 })

Presets.Headless(options) is the same without any Instance on the server: a Record mover holds the pivot, and the agent moves only when it is stepped. Start it with Start(true) and step it from your own scheduler, or with Start() to tick on Heartbeat as usual.

local agent = VluxyAI.Presets.Headless({
	Name = "Goblin 12",
	Position = spawn,
	Pathfinder = VluxyAI.Pathfinders.Graph.new(nodes),
	Mover = { Speed = 10 },
})
	:UseBrain(Brain)
	:Build()
	:Start(true)

Functions​

new​

VluxyAI.new(entity: Model?) → Builder

Starts a Builder for entity. With no entity it builds a headless agent: give it a Record mover, which holds the pivot instead.

NewAgent​

VluxyAI.NewAgent(definition: AgentDefinition) → Agent

Builds an Agent straight from a definition table, for code that assembles definitions itself rather than through the Builder.

Errors

TypeDescription
stringThe definition failed validation; the message names the field.

Validate​

VluxyAI.Validate(definition: AgentDefinition) → ()

Checks a definition without building it. See Validate.

Errors

TypeDescription
stringThe path and problem of the first bad field.

ValidateBrain​

VluxyAI.ValidateBrain(
brain: Brain,
path: string?--

Prefix for messages.

) → ()

Checks a brain table on its own. See Validate.Brain.

Errors

TypeDescription
string
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Starts a [Builder] for `entity`. With no entity it builds a headless agent: give it a\n[Record] mover, which holds the pivot instead.",
            "params": [
                {
                    "name": "entity",
                    "desc": "",
                    "lua_type": "Model?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Builder"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 167,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "NewAgent",
            "desc": "Builds an [Agent] straight from a definition table, for code that assembles definitions\nitself rather than through the [Builder].",
            "params": [
                {
                    "name": "definition",
                    "desc": "",
                    "lua_type": "AgentDefinition"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Agent"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "string",
                    "desc": "The definition failed validation; the message names the field."
                }
            ],
            "source": {
                "line": 179,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Validate",
            "desc": "Checks a definition without building it. See [Validate].",
            "params": [
                {
                    "name": "definition",
                    "desc": "",
                    "lua_type": "AgentDefinition"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "string",
                    "desc": "The path and problem of the first bad field."
                }
            ],
            "source": {
                "line": 189,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "ValidateBrain",
            "desc": "Checks a brain table on its own. See [Validate.Brain].",
            "params": [
                {
                    "name": "brain",
                    "desc": "",
                    "lua_type": "Brain"
                },
                {
                    "name": "path",
                    "desc": "Prefix for messages.",
                    "lua_type": "string?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "string",
                    "desc": ""
                }
            ],
            "source": {
                "line": 200,
                "path": "lib/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "Utility",
            "desc": "Utility scoring: [Utility.Best], [Utility.Weighted], [Utility.Curves].",
            "lua_type": "Utility",
            "source": {
                "line": 208,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "When",
            "desc": "Ready-made `When` predicates for transitions and interrupts: [When.Has], [When.New],\n[When.Below], [When.PathDone], [When.Timer] and the rest.",
            "lua_type": "When",
            "source": {
                "line": 217,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "States",
            "desc": "Ready-made states: [States.Patrol], [States.Investigate], [States.Search], [States.Wander]\nand [States.Wait].",
            "lua_type": "States",
            "source": {
                "line": 226,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Cleaner",
            "desc": "The package's cleanup bag, for anything you build around an agent.",
            "lua_type": "Cleaner",
            "source": {
                "line": 234,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Signal",
            "desc": "The package's [Signal]; make one for a [Hearing] noise source.",
            "lua_type": "Signal",
            "source": {
                "line": 242,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Pathfinders",
            "desc": "The built-in [Types.Pathfinder]s: [Navmesh] for geometry, [Straight] for a clear line,\n[Ladder] to try several in order, [Graph] for A* over authored nodes, and [Hybrid] to\ncompose a graph with a local pathfinder.",
            "lua_type": "{ Navmesh: Navmesh, Straight: Straight, Ladder: Ladder, Graph: Graph, Hybrid: Hybrid }",
            "source": {
                "line": 252,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Movers",
            "desc": "The built-in [Types.Mover]s. [Record] has no Instance at all, for headless agents.",
            "lua_type": "{ Humanoid: HumanoidMover, CFrame: CFrameMover, Record: Record }",
            "source": {
                "line": 266,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Senses",
            "desc": "The built-in [Types.Sense]s. [Hearing] is for noises your code fires; [Sounds] is for the\ntagged `Sound` instances the world is already playing; [Awareness] turns what the others\nwrote into a detection meter; [Neighbours] is the group sense, so enemies sharing a map\nknow about each other and, when asked, keep out of each other's way.",
            "lua_type": "{ Sight: Sight, Proximity: Proximity, Hearing: Hearing, Sounds: Sounds, Awareness: Awareness, Watched: Watched, Surroundings: Surroundings, Neighbours: Neighbours, Throttle: Throttle, SafeWatch: SafeWatch, Clues: Clues, Hideout: Hideout, Stamina: Stamina }",
            "source": {
                "line": 281,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Director",
            "desc": "Pacing and shared knowledge for a group of enemies: a tension meter, a difficulty knob and a\nshared blackboard, read through `director:Sense()`.",
            "lua_type": "Director",
            "source": {
                "line": 304,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Interest",
            "desc": "Points of interest with a strength each, and `Best` to pick where an enemy looks next.\n[States.Roam] walks one.",
            "lua_type": "Interest",
            "source": {
                "line": 313,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Claims",
            "desc": "A shared \"who is on what\" table, so two enemies never chase the same player unless you say so.",
            "lua_type": "Claims",
            "source": {
                "line": 321,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Doors",
            "desc": "Doors agents open: `:OnStep(\"Door\", VluxyAI.Doors.Step())` over your door script, through\nattributes or a driver of your own.",
            "lua_type": "Doors",
            "source": {
                "line": 330,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "SafeAreas",
            "desc": "Parts tagged `VLUXYAI_SAFE` that agents never enter while `VLUXYAI_SAFE/Enabled` is not `false`. Call\n`VluxyAI.SafeAreas.Setup()` once on the server.",
            "lua_type": "SafeAreas",
            "source": {
                "line": 339,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Collision",
            "desc": "The rig collision group, and parts agents walk through while players still collide with them.",
            "lua_type": "Collision",
            "source": {
                "line": 347,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "PlayerNoise",
            "desc": "Players make noise by moving: speed bands turned into noises on your Hearing signal.",
            "lua_type": "PlayerNoise",
            "source": {
                "line": 355,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Voice",
            "desc": "Random voice lines by state, added as a sense: `:AddSense(VluxyAI.Voice.new({ Lines = ... }))`.",
            "lua_type": "Voice",
            "source": {
                "line": 363,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Tables",
            "desc": "The package's small table helpers, including [Tables.Prefixed].",
            "lua_type": "Tables",
            "source": {
                "line": 371,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Perception",
            "desc": "The helpers the built-in senses are made of: alive player roots, cones, line of sight, and\n`HumanoidOf` for a state that acts on a target. The place to start for a custom sense.",
            "lua_type": "Perception",
            "source": {
                "line": 380,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Animator",
            "desc": "The NPC animation helper.",
            "lua_type": "NPCAnimator",
            "source": {
                "line": 388,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Locomotion",
            "desc": "Idle and walk for a rig from its real speed, plus one-shots by name, through the animator.",
            "lua_type": "Locomotion",
            "source": {
                "line": 396,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Footsteps",
            "desc": "Footstep audio from measured movement, for the client's replica rigs (or a server rig).",
            "lua_type": "Footsteps",
            "source": {
                "line": 404,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Sync",
            "desc": "Client-side presentation: a [Broadcaster] watches an agent on the server, a [Replica] drives\na rig and your client states on the client. The game owns the remotes between them.",
            "lua_type": "{ Broadcaster: Broadcaster, Replica: Replica, Look: Look }",
            "source": {
                "line": 413,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Combat",
            "desc": "[Attack.State], the one generic state, and [Kill.Live] and [Kill.BlackBox], the two ways an\nagent kills a character.",
            "lua_type": "{ Kill: Kill }",
            "source": {
                "line": 426,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Debug",
            "desc": "Opt-in debug views, and a runtime R15 rig for tests and playgrounds.",
            "lua_type": "{ PathVisual: PathVisual, GraphVisual: GraphVisual, StateLabel: StateLabel, SenseVisual: SenseVisual, Rig: Rig }",
            "source": {
                "line": 437,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Internals",
            "desc": "The pieces the [Agent] is made of, for a custom scheduler or a client renderer that wants to\nrun a brain table itself.",
            "lua_type": "{ Runner: Runner, Navigator: Navigator }",
            "source": {
                "line": 452,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "Presets",
            "desc": "Ready-made stacks. `Presets.Humanoid(model)` returns a [Builder] already carrying a\n[Navmesh] pathfinder (graph-first [Hybrid] when `Graph` is given) with [Straight] as its\ndirect walker, a [HumanoidMover] mover, [Sight], [Proximity] and [SafeWatch] senses, and a\nquarter-second repath; `Doors = true` adds walking through doors. Add a brain, any senses of\nyour own, and build. It is the stack the examples build by hand, opt-in and overridable.\n\n```lua\nlocal agent = VluxyAI.Presets.Humanoid(rig, { Graph = graph, Doors = true })\n\t:UseBrain(Brain)\n\t:AddSense(VluxyAI.Senses.Clues.new(interest))\n\t:Build()\n```\n\n`Presets.Puppet(anchor)` is the server half of puppet mode, for an enemy with no visible body\non the server: a [Navmesh] pathfinder and a [CFrameMover] on the anchor (`HeightOffset` `0`, so\nits pivot is the floor point) with the same repath. A Part anchor is wrapped in a Model of its\nown and made invisible, anchored and inert. It adds no senses; stream it with a [Broadcaster]\nonce built, and draw it on the client with a puppet [Replica].\n\n```lua\nlocal agent = VluxyAI.Presets.Puppet(anchorPart, {\n\tNavmesh = { AgentRadius = 4, AgentHeight = 12 },\n\tMover = { Speed = 10, TurnSpeed = 6 },\n})\n\t:UseBrain(Brain)\n\t:AddSense(VluxyAI.Senses.Hearing.new(noises))\n\t:Build()\nlocal broadcaster = VluxyAI.Sync.Broadcaster.new(agent, { PositionRate = 10 })\n```\n\n`Presets.Headless(options)` is the same without any Instance on the server: a [Record] mover\nholds the pivot, and the agent moves only when it is stepped. Start it with `Start(true)` and\nstep it from your own scheduler, or with `Start()` to tick on Heartbeat as usual.\n\n```lua\nlocal agent = VluxyAI.Presets.Headless({\n\tName = \"Goblin 12\",\n\tPosition = spawn,\n\tPathfinder = VluxyAI.Pathfinders.Graph.new(nodes),\n\tMover = { Speed = 10 },\n})\n\t:UseBrain(Brain)\n\t:Build()\n\t:Start(true)\n```",
            "lua_type": "{ Humanoid: (model: Model, options: HumanoidPresetOptions?) -> Builder, Puppet: (anchor: Model | BasePart, options: PuppetPresetOptions?) -> Builder, Headless: (options: HeadlessPresetOptions) -> Builder }",
            "source": {
                "line": 617,
                "path": "lib/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "HumanoidPresetOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Navmesh",
                    "lua_type": "NavmeshOptions?",
                    "desc": "Options for [Navmesh.new]."
                },
                {
                    "name": "Straight",
                    "lua_type": "StraightOptions?",
                    "desc": "Options for [Straight.new], the direct walker."
                },
                {
                    "name": "Mover",
                    "lua_type": "HumanoidOptions?",
                    "desc": "Options for [HumanoidMover.new]."
                },
                {
                    "name": "Sight",
                    "lua_type": "SightOptions?",
                    "desc": "Options for [Sight.new]."
                },
                {
                    "name": "Proximity",
                    "lua_type": "ProximityOptions?",
                    "desc": "Options for [Proximity.new]."
                },
                {
                    "name": "Navigation",
                    "lua_type": "NavigationOptions?",
                    "desc": "Navigator tuning. Default `{ RepathInterval = 0.25, MaxPathAge = 2 }`."
                },
                {
                    "name": "Graph",
                    "lua_type": "Pathfinder?",
                    "desc": "Authored nodes ([Graph]): routes go over the graph first ([Hybrid] with `Prefer = \"Graph\"`, `RefineEnds = false`) and the navmesh answers only where the graph has no way. Without it, the navmesh plans everything."
                },
                {
                    "name": "Doors",
                    "lua_type": "boolean?",
                    "desc": "Walk through doors: [Doors.Step] with `WalkThrough` for `\"Door\"` steps, a [Doors.Opener] sense, and a direct walker that sees past unlocked doors. For doors rigs do not collide with ([Collision.LetRigsThrough]). Default `false`."
                },
                {
                    "name": "SafeWatch",
                    "lua_type": "boolean?",
                    "desc": "Add [SafeWatch], so the agent notices players in safe areas and is moved out of one that switches on around it (to a `Graph` node when given). Default `true`."
                }
            ],
            "source": {
                "line": 471,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "PuppetPresetOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Navmesh",
                    "lua_type": "NavmeshOptions?",
                    "desc": "Options for [Navmesh.new]: `AgentRadius`, `AgentHeight` and the rest."
                },
                {
                    "name": "Mover",
                    "lua_type": "CFrameMoverOptions?",
                    "desc": "Options for [CFrameMover.new]: `Speed`, `TurnSpeed`, `HeightOffset`. `HeightOffset` defaults to `0` here, so the anchor's pivot is the floor point."
                },
                {
                    "name": "Navigation",
                    "lua_type": "NavigationOptions?",
                    "desc": "Navigator tuning. Default `{ RepathInterval = 0.25, MaxPathAge = 2 }`."
                }
            ],
            "source": {
                "line": 494,
                "path": "lib/init.luau"
            }
        },
        {
            "name": "HeadlessPresetOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Name",
                    "lua_type": "string?",
                    "desc": "Used in messages. Default `\"Agent\"`."
                },
                {
                    "name": "Position",
                    "lua_type": "Vector3",
                    "desc": "Where it starts: the floor point."
                },
                {
                    "name": "Yaw",
                    "lua_type": "number?",
                    "desc": "Starting yaw in radians. Default `0`."
                },
                {
                    "name": "Mover",
                    "lua_type": "CFrameMoverOptions?",
                    "desc": "`Speed`, `TurnSpeed` and the rest, as [Record.new] takes them."
                },
                {
                    "name": "Pathfinder",
                    "lua_type": "Pathfinder?",
                    "desc": "Default a [Navmesh] built from `Navmesh`. A [Graph] or [Straight] keeps PathfindingService out of it."
                },
                {
                    "name": "Navmesh",
                    "lua_type": "NavmeshOptions?",
                    "desc": "Options for the default [Navmesh]. Ignored when `Pathfinder` is given."
                },
                {
                    "name": "Navigation",
                    "lua_type": "NavigationOptions?",
                    "desc": "Navigator tuning. Default `{ RepathInterval = 0.25, MaxPathAge = 2 }`."
                }
            ],
            "source": {
                "line": 514,
                "path": "lib/init.luau"
            }
        }
    ],
    "name": "VluxyAI",
    "desc": "Simple, composable AI for Roblox. An AI is a **brain** (a table of states), a **pathfinder**\n(plans), a **mover** (executes) and some **senses** (write a blackboard), stacked with the\nbuilder into an **agent**.\n\n```lua\nlocal VluxyAI = require(ReplicatedStorage.Packages.VluxyAI)\n\nlocal agent = VluxyAI.new(model)\n\t:UseBrain(Stalker)\n\t:UsePathfinder(VluxyAI.Pathfinders.Navmesh.new({ AgentRadius = 2 }))\n\t:UseMover(VluxyAI.Movers.Humanoid.new(model))\n\t:AddSense(VluxyAI.Senses.Sight.new({ Range = 40 }))\n\t:Build()\n\t:Start()\n```\n\nEvery part is a contract in [Types]; pass your own module in to extend the package.",
    "source": {
        "line": 156,
        "path": "lib/init.luau"
    }
}