Behavior Trees are a wonderful thing when it comes to writing any kind of AI logic. It is currently in its “first working version” and there are still improvements coming in the future.
FFXIVMinion is not making use of this, but it *could* be used as well. Instead, it uses the 'older' Cause&Effect framework. Example Code how to build your own addon : newaddon.zip
BT Basics, How they work: Behavior trees for AI
If you want to write your own LUA Addon for GW2Minion, this framework is what you need to use.
These Tutorials will show you how to make your own Lua Addon and create some logic with our Behavior Framework.
[Module] Name=sillyjumper Dependencies=minionlib,GW2Minion Version=1 Files=sillyjumper.lua Enabled=1
Now open your addon lua file (sillyjumper.lua in my case) in a text editor and add this code (ofc. rename it to your addon name):
-- Your Addon should always be a LOCAL table, else anyone could just steal your code local sillyjumper = {} -- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load the files from your local addon folder. function sillyjumper.LoadBehaviorFiles() -- Load all our local "bot/addon" BTree files BehaviorManager:LoadBehaviorFromFolder(GetLuaModsPath() .. "\\\SillyJumper") end -- Registering our function for the event that is being called by the BT-Manager to refresh the "internal" list of available Behaviors. RegisterEventHandler("RefreshBehaviorFiles", sillyjumper.LoadBehaviorFiles)
We now need to create a new Behavior Tree for our new addon.
The Root-BehaviorTree-Node is being called about 4-5 times a second. It always starts from the top and goes downwards through the tree until an Action-Node returns self:fail() / self:running() or self:success().
EDIT: Above the lines where you add the Checkbox, you need to make sure the used variable is NOT nil, so put this above:
if (not Settings.sillyjumper.jump ) then Settings.sillyjumper.jump = false end
At this point, you can freely share your addon with anyone else, they just need to copy the folder into their …/LuaMods/.. folder.
But in the case that you want to keep your code private and put your addon into the minionapp store, the next steps are required.
-- Your Addon should always be a LOCAL table, else anyone could just steal your code local sillyjumper = {} -- Aquire the Private Addon Functions, so we can use them later. sillyjumper.modulefunctions = GetPrivateModuleFunctions() -- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load our files. function sillyjumper.LoadBehaviorFiles() -- The BehaviorTree Framework requires specific information in order to use an "external private BTree": local btreeinfo = { -- Our Main Behaviortree filename: filename = "sillyjumper.bt", -- Our Foldername and a mandatory subfolder where our "sillyjumper.bt" will be loaded from: filepath = GetLuaModsPath() .. "\\\SillyJumper\\data", -- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees. Reload = function() sillyjumper.LoadBehaviorFiles() end, -- if set, it will be treated as a private addon, loadable from the addon store private = false, } -- At this point we now have to branch between loading the files as developer (which has the original lua and .bt files in the local folder) -- And the user who just has the precompiled .paf file from the Addon Store in his folder. if ( FileExists(GetLuaModsPath() .. "\\\SillyJumper\\\data\\\sillyjumper.bt")) then d("[SillyJumper] - Loading Developer Version") -- Loads the BTree data from the locally present .bt file sillyjumper.btree = BehaviorManager:LoadBehavior( btreeinfo ) else d("[SillyJumper] - Loading SillyJumper Addon") -- We need to load the BTree data by ourself from our local .paf and pass that to the LoadBehavior() to create an instance of it. -- Get all files in our ..LuaMods\SillyJumper\data\ folder, find our sillyjumper.bt and load that into the BehavioerManager: local files = sillyjumper.modulefunctions.GetModuleFiles("data") if(table.valid(files)) then for _,filedata in pairs(files) do if( btreeinfo.filename == filedata.f) then local fileString = sillyjumper.modulefunctions.ReadModuleFile(filedata) if(fileString) then local fileFunction, errorMessage = loadstring(fileString) if (fileFunction) then btreeinfo.data = fileFunction() end end break end end end if (table.valid(btreeinfo.data)) then sillyjumper.btree = BehaviorManager:LoadBehavior( btreeinfo ) end end -- At this point we now have a local instance of our BehaviorTree => sillyjumper.btree if ( not table.valid(sillyjumper.btree)) then ml_error("[SillyJumper] - Failed to load SillyJumper behaviortree") end end -- Registering our function for the event that is being called by the BT-Manager to refresh its intern list of available Behaviors. RegisterEventHandler("RefreshBehaviorFiles", sillyjumper.LoadBehaviorFiles)
As you learned earlier, all BehaviorTree Nodes are sharing a “context” table which can be accessed at any time by all Nodes.
If you have large functions or code parts which are often reused and would clutter the Nodes, you can just move the code into a lua file.
We create a custom “context” table, define the functions and pass that to the BehaviorTree. All Nodes can then access the functions / code.
-- Your Addon should always be a LOCAL table, else anyone could just steal your code local sillyjumper = {} -- Aquire the Private Addon Functions, so we can use them later. sillyjumper.modulefunctions = GetPrivateModuleFunctions() -- Our custom context which will hold code and functions and make these available inside the BT-Nodes sillyjumper.btreecontext = {} -- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load our files. function sillyjumper.LoadBehaviorFiles() -- The BehaviorTree Framework requires specific information in order to use an "external private BTree": local btreeinfo = { -- Our Main Behaviortree filename: filename = "sillyjumper.bt", -- Our Foldername and a mandatory subfolder where our "sillyjumper.bt" will be loaded from: filepath = GetLuaModsPath() .. "\\\SillyJumper\\data", -- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees. Reload = function() sillyjumper.LoadBehaviorFiles() end, -- if set, it will be treated as a private addon, loadable from the addon store private = false, -- (optional) Callback function, is called by the BTree Framework when the BTree is started. Allows us to supply a custom "context" table to the BTree: LoadContext = function() return sillyjumper.GetContext() end, -- (optional) Function to draw the menu elements that always appears on the "Main menu" interface. DrawMenuCode = function(btree) end, -- (optional) Function to draw the menu elements that appears on the "Main menu" interface when the btree is selected. -- This locks the edit field inside the btree editor. DrawMainMenuCode = function(btree) end, -- (optional) Function to draw the menu elements that appears on the subtree node. This is also used for drawing custom properties in the taskmanager. -- This locks the edit field inside the btree editor. DrawSubMenuCode = function(btree) end }
-- Create a function which will then be usable inside the BT-Nodes sillyjumper.LetsJump = function() Player:Jump() end -- Is called when the BTree is started. Allows us to supply a custom context table to the BTree function sillyjumper.GetContext() sillyjumper.btreecontext.Jump = sillyjumper.LetsJump return sillyjumper.btreecontext end
In order to create and use a private subtree, you will need to extend the loading code again by a new function:
function sillyjumper.LoadBehaviorFiles() -- The BehaviorTree Framework requires specific information in order to use an "external private BTree": local btreeinfo = { -- Our Main Behaviortree filename: filename = "sillyjumper.bt", -- Our Foldername and a mandatory subfolder where our "sillyjumper.bt" will be loaded from: filepath = GetLuaModsPath() .. "\\\SillyJumper\\data", -- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees. Reload = function() sillyjumper.LoadBehaviorFiles() end, -- if set, it will be treated as a private addon, loadable from the addon store private = false, -- (optional) Callback function, is called by the BTree Framework when the BTree is started. Allows us to supply a custom "context" table to the BTree: LoadContext = function() return sillyjumper.GetContext() end, -- Required for private addons with additional private sub-behavior trees LoadSubTree = function(filename) return sillyjumper.LoadSubtreeData(filename) end, }
-- Required for private addons with additional private sub-behavior trees function sillyjumper.LoadSubtreeData(filename) if ( FileExists(GetLuaModsPath() .. "\\SillyJumper\\data\\"..filename)) then return FileLoad(GetLuaModsPath() .. "\\SillyJumper\\data\\"..filename) else local files = sillyjumper.modulefunctions.GetModuleFiles() if(table.valid(files)) then for _,filedata in pairs(files) do if( btreeinfo.filename == filedata.f) then local fileString = sillyjumper.modulefunctions.ReadModuleFile(filedata) if(fileString) then local fileFunction, errorMessage = loadstring(fileString) if (fileFunction) then return fileFunction() end end break end end end end end
local btreeinfo = { -- Our Main Behaviortree filename: filename = string, -- Our Foldername and a mandatory subfolder where our btree will be loaded from: filepath = string, -- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees. Reload = function() end, -- if set, it will be treated as a private addon, loadable from the addon store private = false, -- if set, the btree will not appear in the botmode dropdown internal = false, -- Preloaded task data. Important when using a private addon. data = table, -- (optional) Callback function, is called by the BTree Framework when the BTree is started. Allows us to supply a custom "context" table to the BTree: LoadContext = function() end, -- (optional) Function to draw the menu elements that always appears on the "Main menu" interface. (Requires internal) DrawMenuCode = function(btree) end, -- (optional) Function to draw the menu elements that appears on the "Main menu" interface when the btree is selected. -- This locks the edit field inside the btree editor. DrawMainMenuCode = function(btree) end, -- (optional) Function to draw the menu elements that appears on the subtree node. This is also used for drawing custom properties in the taskmanager. -- This locks the edit field inside the btree editor. DrawSubMenuCode = function(btree) end, -- Required for private addons with additional private sub-behavior trees LoadSubTree = function(filename) end, }
BehaviorManager:ToggleMenu() BehaviorManager:Ready() BehaviorManager:Running() BehaviorManager:Start() BehaviorManager:Stop() BehaviorManager:GetTicksThreshold() -- How often (in ms) the BTree is being called. BehaviorManager:SetTicksThreshold(milliseconds) BehaviorManager:GetLastTick() BehaviorManager:SetLastTick(ticks) BehaviorManager:LoadBehaviorFromFolder(folderpath) BehaviorManager:LoadBehavior(btreeinfo) BehaviorManager:LoadSubBehavior(btreeinfo) BehaviorManager:Paused(p) BehaviorManager:CurrentBTreeName() BehaviorManager:SetBtreeFile(botmode_name)