Skip to main content

Interest

A shared map of points of interest with a strength each, and Best, which picks where an agent should look next. Exposed as VluxyAI.Interest. Make one per map, fill it from your level (hiding spots, doors, rooms, the generator), raise points from events, and give enemies a States.Roam over it or ask Best from a state of your own.

local interest = VluxyAI.Interest.new({ Decay = 0.1, Visited = 30 })
for _, locker in CollectionService:GetTagged("Locker") do
	interest:Add(locker, { Position = locker.Position, Strength = 1, Tags = { "Hiding" } })
end
noises:Connect(function(position, loudness)
	interest:BumpNear(position, 15, loudness or 1) -- a noise makes nearby spots worth a look
end)

Search = VluxyAI.States.Roam({ Interest = interest, Then = "Patrol", Duration = 40 }),

An enemy never learns where a player is from this; it learns where a player would plausibly be. Pair it with Sight's PredictedPosition for "they went that way" and the enemy hunts like something that has to look.

Types​

Clue​

interface Clue {
Radius: number--

Studs the clue spreads; points further away are not raised. 0 makes the kind no clue at all.

Heat: number--

How much it raises a point at its centre, less with distance, whatever the noise's loudness.

}

InterestPoint​

interface InterestPoint {
Id: any--

What it was added under.

Position: Vector3
Strength: number--

The current strength, after decay.

Base: number--

What it drifts back to.

Tags: {string}?--

For Best to filter on.

}

Functions​

new​

Interest.new(options: InterestOptions?) → Interest

Types

​

interface InterestOptions {
Decay: number?--

Strength drifts back toward a point's base at this much per second. Default 0.1.

Visited: number?--

Seconds a point stays uninteresting to an agent that just looked there. Default 30.

Key: string?--

The agent.Data key visits are remembered under. Default "Interest".

NoiseRadius: number?--

How far Interest:Heard spreads a noise whose kind has no entry in Clues. Default 24.

NoiseHeat: number?--

How much such a noise raises the points at its centre, per unit of loudness. Default 2.

Clues: {[string]: Clue}?--

How each kind of noise counts, by the kind passed to [Interest:Heard]: a door or an alarm is a big clue, a footstep none at all. Default {}.

}

Add​

Interest.Add(
self: Interest,
id: any,--

A part, a name, anything usable as a key. Adding it again replaces it.

) → ()

Types

​

interface InterestPointOptions {
Position: Vector3
Strength: number?--

The base strength. Default 1.

Tags: {string}?--

Labels such as "Hiding", "Door", "Room".

}

Errors

TypeDescription
stringNo Vector3 `Position`.

Remove​

Interest.Remove(
self: Interest,
id: any
) → ()

Get​

Interest.Get(
self: Interest,
id: any
) → InterestPoint?--

A copy with the strength as of now, or nil.

Points​

Interest.Points(self: Interest) → {InterestPoint}--

Every point, strengths as of now, in no particular order.

Bump​

Interest.Bump(
self: Interest,
id: any,
amount: number--

Added to the strength; it decays back toward the base. Negative lowers it.

) → ()

BumpNear​

Interest.BumpNear(
self: Interest,
position: Vector3,
radius: number,
amount: number--

Added in full at the position, fading to nothing at radius.

) → number--

How many points were raised.

A noise, a slammed door, a light: everything near it becomes worth a look.

Heard​

Interest.Heard(
self: Interest,
position: Vector3,--

Where the noise was.

loudness: number?,--

Default 1; only used for a kind with no entry in Clues.

kind: string?--

What it was: "Door", "Running", "Alarm", "Footstep".

) → number--

How many points it raised.

A noise as a clue: raises the points near it by what kind of noise it was (the Clues option), or by its loudness when the kind has no entry. Connect it to the signal your Hearing senses listen to, and every noise the players make becomes somewhere worth a look, weighted the way your game decides: a door opening counts, a footstep does not.

AddAll​

Interest.AddAll(
self: Interest,
positions: {Vector3},--

Floor positions, such as every room or every cell of a maze.

options: {
Strength: number?,
Tags: {string}?,
Skip: ((position: Vector3) → boolean)?
}?
) → number--

How many points were added. Their ids are their indices in positions.

Adds a point at each position, all with the same base strength and tags, leaving out any Skip returns true for (a safe room, say: Skip = VluxyAI.SafeAreas.Contains).

Set​

Interest.Set(
self: Interest,
id: any,
strength: number--

Sets both the current strength and the base.

) → ()

Visit​

Interest.Visit(
self: Interest,
agent: Agent,
id: any
) → ()

Marks the point as looked at by this agent, so Best passes over it for Visited seconds.

SinceVisit​

Interest.SinceVisit(
self: Interest,
agent: Agent,
id: any
) → number--

Seconds since this agent last visited the point, math.huge if never.

Best​

Interest.Best(
self: Interest,
agent: Agent,
options: BestOptions?
) → (
InterestPoint?,--

The point to look at next, or nil when every candidate scores 0.

number--

Its score in [0, 1].

)

Types

​

interface BestOptions {
Range: number?--

Studs at which distance alone scores a point to nothing. Default 120.

Tags: {string}?--

Only points carrying one of these tags.

Exclude: {any}?--

Point ids to leave out (the one the agent is standing at, say).

Considerations: {Consideration<InterestPoint>}?--

Extra scoring, multiplied in.

IgnoreStrength: boolean?--

Score every point as if equally interesting, so only nearness, visits and Considerations count: a pick that does not follow the clues. Default false.

}

Scores every point by strength (against the strongest) and nearness (linear to Range), multiplied, then any Considerations given, and returns the best. A point this agent visited within Visited seconds scores 0, so a roaming enemy spreads its search instead of circling one spot.

Destroy​

Interest.Destroy(self: Interest) → ()

Forgets every point. Agents keep their visit times in their own Data.

Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "InterestOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Interest"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 172,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Add",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "id",
                    "desc": "A part, a name, anything usable as a key. Adding it again replaces it.",
                    "lua_type": "any"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "InterestPointOptions"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "string",
                    "desc": "No Vector3 `Position`."
                }
            ],
            "source": {
                "line": 218,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Remove",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 238,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Get",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "A copy with the strength as of now, or `nil`.",
                    "lua_type": "InterestPoint?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 247,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Points",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                }
            ],
            "returns": [
                {
                    "desc": "Every point, strengths as of now, in no particular order.",
                    "lua_type": "{ InterestPoint }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 260,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Bump",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                },
                {
                    "name": "amount",
                    "desc": "Added to the strength; it decays back toward the base. Negative lowers it.",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 275,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "BumpNear",
            "desc": "A noise, a slammed door, a light: everything near it becomes worth a look.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "position",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "radius",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "amount",
                    "desc": "Added in full at the position, fading to nothing at `radius`.",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "How many points were raised.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 293,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Heard",
            "desc": "A noise as a clue: raises the points near it by what kind of noise it was (the `Clues` option),\nor by its loudness when the kind has no entry. Connect it to the signal your [Hearing] senses\nlisten to, and every noise the players make becomes somewhere worth a look, weighted the way\nyour game decides: a door opening counts, a footstep does not.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "position",
                    "desc": "Where the noise was.",
                    "lua_type": "Vector3"
                },
                {
                    "name": "loudness",
                    "desc": "Default `1`; only used for a kind with no entry in `Clues`.",
                    "lua_type": "number?"
                },
                {
                    "name": "kind",
                    "desc": "What it was: `\"Door\"`, `\"Running\"`, `\"Alarm\"`, `\"Footstep\"`.",
                    "lua_type": "string?"
                }
            ],
            "returns": [
                {
                    "desc": "How many points it raised.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 323,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "AddAll",
            "desc": "Adds a point at each position, all with the same base strength and tags, leaving out any\n`Skip` returns `true` for (a safe room, say: `Skip = VluxyAI.SafeAreas.Contains`).",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "positions",
                    "desc": "Floor positions, such as every room or every cell of a maze.",
                    "lua_type": "{ Vector3 }"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "{ Strength: number?, Tags: { string }?, Skip: ((position: Vector3) -> boolean)? }?"
                }
            ],
            "returns": [
                {
                    "desc": "How many points were added. Their ids are their indices in `positions`.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 343,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Set",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                },
                {
                    "name": "strength",
                    "desc": "Sets both the current strength and the base.",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 366,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Visit",
            "desc": "Marks the point as looked at by this agent, so `Best` passes over it for `Visited` seconds.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 395,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "SinceVisit",
            "desc": "",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                },
                {
                    "name": "id",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "Seconds since this agent last visited the point, `math.huge` if never.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 405,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Best",
            "desc": "Scores every point by strength (against the strongest) and nearness (linear to `Range`),\nmultiplied, then any `Considerations` given, and returns the best. A point this agent\nvisited within `Visited` seconds scores 0, so a roaming enemy spreads its search instead of\ncircling one spot.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                },
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "BestOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "The point to look at next, or `nil` when every candidate scores 0.",
                    "lua_type": "InterestPoint?"
                },
                {
                    "desc": "Its score in `[0, 1]`.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 422,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Forgets every point. Agents keep their visit times in their own Data.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Interest"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 494,
                "path": "lib/Group/Interest.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "InterestOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Decay",
                    "lua_type": "number?",
                    "desc": "Strength drifts back toward a point's base at this much per second. Default `0.1`."
                },
                {
                    "name": "Visited",
                    "lua_type": "number?",
                    "desc": "Seconds a point stays uninteresting to an agent that just looked there. Default `30`."
                },
                {
                    "name": "Key",
                    "lua_type": "string?",
                    "desc": "The `agent.Data` key visits are remembered under. Default `\"Interest\"`."
                },
                {
                    "name": "NoiseRadius",
                    "lua_type": "number?",
                    "desc": "How far [Interest:Heard] spreads a noise whose kind has no entry in `Clues`. Default `24`."
                },
                {
                    "name": "NoiseHeat",
                    "lua_type": "number?",
                    "desc": "How much such a noise raises the points at its centre, per unit of loudness. Default `2`."
                },
                {
                    "name": "Clues",
                    "lua_type": "{ [string]: Clue }?",
                    "desc": "How each kind of noise counts, by the kind passed to [Interest:Heard]: a door or an alarm is a big clue, a footstep none at all. Default `{}`."
                }
            ],
            "source": {
                "line": 43,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "Clue",
            "desc": "",
            "fields": [
                {
                    "name": "Radius",
                    "lua_type": "number",
                    "desc": "Studs the clue spreads; points further away are not raised. `0` makes the kind no clue at all."
                },
                {
                    "name": "Heat",
                    "lua_type": "number",
                    "desc": "How much it raises a point at its centre, less with distance, whatever the noise's loudness."
                }
            ],
            "source": {
                "line": 59,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "InterestPoint",
            "desc": "",
            "fields": [
                {
                    "name": "Id",
                    "lua_type": "any",
                    "desc": "What it was added under."
                },
                {
                    "name": "Position",
                    "lua_type": "Vector3",
                    "desc": ""
                },
                {
                    "name": "Strength",
                    "lua_type": "number",
                    "desc": "The current strength, after decay."
                },
                {
                    "name": "Base",
                    "lua_type": "number",
                    "desc": "What it drifts back to."
                },
                {
                    "name": "Tags",
                    "lua_type": "{ string }?",
                    "desc": "For `Best` to filter on."
                }
            ],
            "source": {
                "line": 83,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "InterestPointOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Position",
                    "lua_type": "Vector3",
                    "desc": ""
                },
                {
                    "name": "Strength",
                    "lua_type": "number?",
                    "desc": "The base strength. Default `1`."
                },
                {
                    "name": "Tags",
                    "lua_type": "{ string }?",
                    "desc": "Labels such as `\"Hiding\"`, `\"Door\"`, `\"Room\"`."
                }
            ],
            "source": {
                "line": 99,
                "path": "lib/Group/Interest.luau"
            }
        },
        {
            "name": "BestOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Range",
                    "lua_type": "number?",
                    "desc": "Studs at which distance alone scores a point to nothing. Default `120`."
                },
                {
                    "name": "Tags",
                    "lua_type": "{ string }?",
                    "desc": "Only points carrying one of these tags."
                },
                {
                    "name": "Exclude",
                    "lua_type": "{ any }?",
                    "desc": "Point ids to leave out (the one the agent is standing at, say)."
                },
                {
                    "name": "Considerations",
                    "lua_type": "{ Consideration<InterestPoint> }?",
                    "desc": "Extra scoring, multiplied in."
                },
                {
                    "name": "IgnoreStrength",
                    "lua_type": "boolean?",
                    "desc": "Score every point as if equally interesting, so only nearness, visits and `Considerations` count: a pick that does not follow the clues. Default `false`."
                }
            ],
            "source": {
                "line": 115,
                "path": "lib/Group/Interest.luau"
            }
        }
    ],
    "name": "Interest",
    "desc": "A shared map of points of interest with a strength each, and `Best`, which picks where an\nagent should look next. Exposed as `VluxyAI.Interest`. Make one per map, fill it from your\nlevel (hiding spots, doors, rooms, the generator), raise points from events, and give\nenemies a [States.Roam] over it or ask `Best` from a state of your own.\n\n```lua\nlocal interest = VluxyAI.Interest.new({ Decay = 0.1, Visited = 30 })\nfor _, locker in CollectionService:GetTagged(\"Locker\") do\n\tinterest:Add(locker, { Position = locker.Position, Strength = 1, Tags = { \"Hiding\" } })\nend\nnoises:Connect(function(position, loudness)\n\tinterest:BumpNear(position, 15, loudness or 1) -- a noise makes nearby spots worth a look\nend)\n\nSearch = VluxyAI.States.Roam({ Interest = interest, Then = \"Patrol\", Duration = 40 }),\n```\n\nAn enemy never learns where a player is from this; it learns where a player would plausibly\nbe. Pair it with Sight's `PredictedPosition` for \"they went that way\" and the enemy hunts\nlike something that has to look.",
    "source": {
        "line": 166,
        "path": "lib/Group/Interest.luau"
    }
}