Yours too! Your meshVox plugin is a godsend for developers I have gotten such amazing use out of it I’ve been hoping for support for that kind of thing for a while! I’ll be improving this resource soon with a new reformatted version soon that will focus on general usage and short form factor! I made this intentionally complicated to make sure each new object category was working properly but now I want to make it more elegant and pass variables rather the current code which has become redundant over time.
Another system I created is focused on Actions built using the Awareness Library.
Currently not completely useful as open source due to execution relying on interactions and features from a large codebase.
--[[
This Lua code is designed for use within Roblox Studio and involves complex interactions with game objects, NPCs (Non-Player Characters), and spells. It's structured to handle various actions such as navigation, interaction, and spell casting based on textual commands or queries. The code is modular, leveraging multiple tables and functions to process and execute commands within a game environment. Here's a breakdown of its functionality:
Initial Setup
queryspace: A table containing keywords associated with different game elements like enemies, NPCs, chests, etc. Each category has a list of related actions or descriptors.
RunService and isLocal: Determines if the script is running on the client-side.
actions: A table structured to hold various sub-tables for handling objects, navigation, interaction, word models, and spells.
aware, dfg, commands, actionmod: These lines import various modules required for the script to function, such as awareness algorithms, global spells, command processing, and action modules.
SpellCommands and Spells: These sections prepare the script to handle spell commands by loading them into tables for easy access.
Functions Overview
MagicSpell: A function to cast a spell, checking if the character has a tool equipped and using it as the spell's source if present.
querywordspace, keywordspace, querykeys, queryspells: Functions designed to search through the queryspace and SpellsQuery for matches based on the input query. They are used to identify actions or spells related to the query.
awareresponse: Generates a response based on navigation towards a target, including details like distance and direction.
Contextbl: A table of functions that return context-based strings for interactions with different game elements (e.g., enemies, chests, NPCs).
actions.objects, actions.navigate, actions.interact: These sub-tables within actions define functions for object handling, navigation, and interaction based on the game environment and player commands.
actions.spells.query and actions.spells.Cast: Functions for querying available spells based on the input and casting the identified spell.
actions.navigate.navinteract: Handles navigation and interaction with a target based on a query, potentially invoking spell casting if relevant.
actions.navigate.query: The main function that processes a query, identifies the relevant action (navigation, interaction, spell casting), and executes it.
Execution Flow
Initialization: Sets up the environment, loads necessary modules, and prepares spell commands.
Query Processing: When a query is received, it's processed to identify the relevant action - whether it's moving towards an object, interacting with it, or casting a spell.
Action Execution: Based on the query's context, the script executes the appropriate action. This could involve navigating the player towards a target, interacting with game objects, or casting spells.
Response Generation: After executing an action, the script can generate a descriptive response based on the action's outcome, providing feedback to the player.
Key Concepts
Modularity: The script is structured into distinct sections and functions, each handling a specific aspect of the game's interaction model.
Data-Driven: Actions and responses are driven by the queryspace and other data tables, allowing for flexible command processing.
Dynamic Interaction: The script dynamically handles interactions with the game world, allowing players to navigate, interact, and cast spells based on textual commands.
This code snippet is an example of how complex game logic can be implemented in Roblox Studio, leveraging Lua's capabilities for dynamic and interactive game development.
]]
local queryspace={
["Enemy"] = {"readying to assault", "striking", "aiming at","focus","assault","preparing to attack", "attacking", "targeting","target","attack","fight"},
["NPC"] = {"approaching", "greeting", "following","follow","coming near", "saluting", "trailing","pursue","approach"},
["chest"] = {"opening", "looting", "unlocking","open","treasure","unsealing", "plundering", "unfastening","unlatch","booty"},
["crystal"] = {"examining", "admiring", "touching","inspecting", "appreciating", "feeling"},
["dungeons"] = {"exploring", "investigating", "solving","scouting", "probing", "cracking"},
["fish"] = {"catching", "fishing", "catch fish","snaring", "angling", "hook fish","fish"},
["house"] = {"entering", "searching", "leaving","penetrating", "rummaging", "departing"},
["loot"] = {"lifting", "gathering", "keeping","plunder","lift","picking up", "collecting", "storing","loot","pick up"},
["mapobj"] = {"interacting", "ignoring", "destroying","examine","investigate"},
["plant"] = {"reaping", "gather","pluck","snatch","cultivate","reap","amassing","harvesting", "collect","pick","pick-up","farm","harvest","collecting"},
["player"] = {"approaching", "greeting", "following"},
["rubble"] = {"mining", "clearing", "mine rock", "removing", "excavate rock"},
["tree"] = {"cutting tree", "cut tree","chop tree","woodcut","woodcutting","lumberjack","slicing tree", "fell tree","hew tree","timber","logging","woodsman"}
}
local RunService = game:GetService("RunService")
local isLocal = RunService:IsClient()
local actions = {}
actions.objects = {}
actions.navigate = {}
actions.interact = {}
actions.wordmodel = {}
actions.spells={}
--import libraries awareness global spells
local aware = require(game.ReplicatedStorage.GlobalSpells.ChatbotAlgorithm.Awareness)
local player=game.Players.LocalPlayer
local dfg = require(game.ReplicatedStorage.GlobalSpells)
--subject,target
local commands = require(game.ReplicatedStorage.GlobalSpells.ChatbotAlgorithm.Commands)
local actionmod = require(game.ReplicatedStorage.GlobalSpells.ChatbotAlgorithm.ActionModule)
--local Remote=game.ReplicatedStorage.GlobalSpells.ChatbotAlgorithm.APICallsLearnData.APIData.RemoteEvent
if isLocal then
cm=game.Players.LocalPlayer.PlayerGui.Chatbot.LocalProcessor
else
cm=game.ReplicatedStorage.GlobalSpells.BindableFunction
end
--dfg.FindPath(Subject,Targ)
local Controller = dfg.LocalController()
local SpellCommands=game.ReplicatedStorage.GlobalSpells.ChatbotAlgorithm.SpellCommands:GetChildren()
local Spells={}--spells require a humanoid?
local SpellRemote=game.ReplicatedStorage.GlobalSpells:WaitForChild("RemoteEvent")
for i,v in SpellCommands do
Spells[v.Name]=v
end
local SpellsQuery={}
for i,v in SpellCommands do
SpellsQuery[i]=v.Name--:lower()
end
function actions.MagicSpell(Character,player,Type,Target)
if Character:FindFirstChildOfClass("Tool")==nil then
Controller.MagicSpell(Character.RightHand,Type,Target,nil,1,player)
else
Controller.MagicSpell(Character:FindFirstChildOfClass("Tool").Handle,Type,Target,nil,1,player)
end
end
--for i,v in commands do
--Controller[i]=v
--end
--for i,v in commands do
--local i=i
--Controller[i]=function(args) Remote:FireServer(args,i) end
--end
local near = aware.near
local navigation = {""}
local worldmodel = {}
local path = Controller.FindPath
local pathingdebounce = {}
--action.ConductAlchemy(Character,root,Herb)
--action.PickUpObject(Character,Object)
local function querywordspace(query)--query entire word space
for i,c in queryspace do
for t,v in c do
if string.gmatch(query,v)() then
print("Found pattern "..v.." inside "..query)
return v
end
end
end
end
local function keywordspace(query,key)--query key word space
for i,v in queryspace[key] do
if string.gmatch(query,v)() then
print("Found pattern "..v.." inside "..query)
return v
end
end
end
local function querykeys(query)--there are not many of these so i lower is fine.
for i,_ in queryspace do
if string.gmatch(query,i:lower())() then
print("Found pattern "..i.." inside "..query)
return i
end
end
end
local function queryspells(query)
for i,v in SpellsQuery do
if string.gmatch(query,v:lower())() then
print("Found Spell pattern "..v.." inside "..query)
return v
end
end
end
function actions.wordmodel.awareresponse(root, navigationtarget, magnitude, navroot, verb)
local res = ""
if navigationtarget then
local pos = root.CFrame.Position
if navigationtarget then
local magn = magnitude
local dist, dir = aware.judge.distance(root, magn, pos, navroot.Position, 80)
--local size = aware.judge.size(closestChest.Size)
if dir ==nil then dir="around here" end if dist==nil then dist="somewhere" end
local objstr=aware.judge.object(navigationtarget)
print(objstr)
if objstr==nil then objstr=navigationtarget.Name end
if verb==nil then verb="walking towards the" end
print(verb)
print(objstr)
print(dist)
print(dir)
return "I am " .. verb ..
" " .. objstr ..
" that is " .. dist ..
"" .. dir
end
end
end
local Contextbl = {
--target attack the enemy
["Enemy"] = function(root, obj, key)
return "preparing to attack", aware.get.NPCDescription(obj.Parent)
end, --walk towards the npc
["NPC"] = function(root, obj, key)
return nil, ""
end,
--do nothing just navigate to
--walk towards and open the chest
["chest"] = function(root, obj, key)
return "walking towards the lock of the", ""
end, --navigate to the animpoint inside the chest
--walk towards and examine the crystal
["crystal"] = function(root, obj, key)
return "checking out this", ""
end,
["dungeons"] = function(root, obj, key)
return "trying to find the center of this", ""
end,
["fish"] = function(root, obj, key)
if obj then Controller.PickUpObject({root.Parent, obj}) end
return "swimming towards the", ""
end,
["house"] = function(root, obj, key)
return "walking towards the door of the", ""
end,
["loot"] = function(root, obj, key)
if obj then Controller.PickUpObject({root.Parent, obj}) end
return "attempting to loot the", actionmod.PickUpObject(root.Parent, obj)
end,
["mapobj"] = function(root, obj, key)
return nil, ""
end,
["plant"] = function(root, obj, key)
if obj then Controller.PickUpObject({root.Parent, obj}) end
return nil, ""
end,
["player"] = function(root, obj, key)
return nil, ""
end,
["rubble"] = function(root, obj, key, Player)
local key = "mine rock"
commands[key](Player, root.Parent)
return nil, commands.commandresponse(key)
end,
["tree"] = function(root, obj, key, Player)
commands["cut tree"](Player, root.Parent)
return nil, commands.commandresponse("cut tree")
end
}
local keytable= {
[1] = "crystal",
[2] = "plant",
[3] = "tree",
[4] = "mapobj",
[5] = "rubble",
[6] = "chest",
[7] = "player",
[8] = "loot",
[9] = "fish",
[10] = "NPC",
[11] = "Enemy",
[12] = "house",
[13] = "dungeons"
}
for key, v in near do
worldmodel[key] = {}
--get the object with a query
actions.objects[key] = function(root, player, var, query) --get closest object and query the surroundings
local closestObject, NumObjects, ObjectSize, ObjectDistance = aware.get.objectsdata(root, key, query)
--query the array with a string
-- print(closestObject)
-- print(query)
if closestObject~=nil then
worldmodel[key] = {Close = closestObject, Amount = NumObjects, Size = ObjectSize, Magnitude = ObjectDistance}
else
worldmodel[key] = {Close = nil, Amount = NumObjects, Size = ObjectSize, Magnitude = ObjectDistance}
end
--rebuild the observation
if var then
return worldmodel[key][var]
end
return worldmodel[key] --return the variable or the table
end
actions.navigate[key] = function(root, player, query, verb,navigation) --navigate to the request place/object and return string
if pathingdebounce[root.Parent.Humanoid.DisplayName] == nil then
pathingdebounce[root.Parent.Humanoid.DisplayName] = false
end
if pathingdebounce[root.Parent.Humanoid.DisplayName] == false then
if query~=nil then query={Query=query} end
if navigation==nil then navigation = actions.objects[key](root, player, nil, query) end
--query the object array with a string with no var to recieve full array of data
if navigation.Close~=nil then -- if there is a navigate close target return the target and do pathing
print("Successful Environment Navigation Search") -- The object has beenn found
print(navigation)
Controller.WalkTween({root, root.Parent.Humanoid, navigation.Magnitude, 4})
--Calculate acceleration based on distance
local navroot = aware.get.root2(navigation.Close)
--get the navigation root
pathingdebounce[root.Parent.Humanoid.DisplayName] = true
path({root.Parent, navroot}) --navigate to object
task.delay(
3,
function()
pathingdebounce[root.Parent.Humanoid.DisplayName] = false
end
)
--delay and trigger debounce
local resultant
if verb == nil then
verb, resultant = Contextbl[key](root, nil, key, player)
if verb==nil then verb = "walking towards the" end
end
return actions.wordmodel.awareresponse(root, navigation.Close, navigation.Magnitude, navroot, verb), navigation,verb,resultant
--return a string describing the object and its position
end
end
return nil --no result found
end
--always returns a string
actions.interact[key] = function(root, player, query,navigation,destination,verb,resultant) -- navigate and interact with the object
if verb or resultant==nil then verb, resultant = Contextbl[key](root, nil, key, player) end
if navigation or destination==nil then destination, navigation = actions.navigate[key](root, player, query, verb) end
if destination ~= nil then
Contextbl[key](root, navigation.Close, key, player)
Controller[key](root, navigation.Close)
return destination .. " " .. resultant
end
return destination
end
end
local resultcache={}
function actions.spells.query(root,player,query)
local spellkey
--if resultcache[query]==nil then resultcache[query]=queryspells(query) end
--if resultcache[query]~=nil then spellkey=resultcache[query] else
spellkey= queryspells(query) --end
if spellkey then
--if resultcache[query]==nil then resultcache[query]=spellkey end
local spell=require(Spells[spellkey])
return spell[math.random(1,#spell)],spellkey
end
return nil
end
function actions.spells.Cast(root,player,Target,query)
local spell,Type= actions.spells.query(root,player,query)
if spell then
actions.MagicSpell(root.Parent,player,Type,Target)
return 'I am casting '..Type..". "..spell,Type
end
return nil
end
function actions.navigate.navinteract(root,player,query,key,query2)
local navigation=actions.objects[key](root, player, nil, query) --query the namespace and return the navigation result
if navigation.Close ~= nil then --if close entry is not nil then we got a direct result
local Result= querywordspace(query) -- if we get a result confirm with different context words which would imply navigation
if Result then
local destination,navigation,verb,resultant = actions.navigate[key](root, player, query2,nil,navigation) --navigate and return a string destination, navigation table, the verb and the resultant string
print("Navigating to target")
if destination then--if destination string is not nil then we are
local actiontoken= keywordspace(query,key)--search the key word space to determine if interaction
print("Got Interaction Result")
local spellcontext,SpellKey,interaction=nil,nil,nil
if actiontoken==nil then --if no action token found in the keyword space search the spells
spellcontext,SpellKey=actions.spells.Cast(root,player,navigation.Close,query)
interaction=destination
elseif actiontoken~=nil then --if we have a action token then we should interact with the object
-- repeat task.wait(.15) until root.Parent.PathingTag:GetAttribute("Complete")==true --wait for pathing to complete
interaction=actions.interact[key](root, player, query,navigation,destination,verb,resultant) end
--return the results as a string
if spellcontext==nil and interaction~="" then --no spell casted and interaction was successful
return interaction,key
elseif spellcontext~=nil then --spell was cast describe the spell and the navigation string.
return spellcontext..interaction,SpellKey
end
end
return destination --return just the navigation string if nothing else passed.
end
end
return nil
end
--this funcion generates 78 commands
function actions.navigate.query(root, player, query)--this function takes a query and executes a action command based on the
local query=query:lower()
local result=nil
for key, v in near do
local res=actions.navigate.navinteract(root,player,query,key,query)
if res then return res end
end
local explicitkey= querykeys(query)
if explicitkey~=nil then
result=actions.navigate.navinteract(root,player,query,explicitkey,nil)
end
if result==nil then result=actions.spells.Cast(root,player,navigation.Close,query) end
resultcache=nil
return result
end
--local actions=require(game.ReplicatedStorage.GlobalSpells.ActionModule2)
--actions.navigate.query(root, player, query)
return actions