=====Behavior Tree Framwork===== 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:** [[http://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php|Behavior trees for AI]] If you want to write your own LUA Addon for GW2Minion, this framework is what you need to use. \\ ====Tutorials==== These Tutorials will show you how to make your own Lua Addon and create some logic with our Behavior Framework. === 1. Creating a new Addon=== {{::tutorial1.png?direct&200|}} * Create a new Folder under MinionApp/Bots/.../ with your addon name (I named mine sillyjumper) * Create two empty files: module.def and a lua file with your addon name. * Copy paste the code into your module.def: [Module] Name=sillyjumper Dependencies=minionlib,GW2Minion Version=1 Files=sillyjumper.lua Enabled=1 * //Name// - has to match your foldername * //Dependencies// - need to be set in order to be able to use functions defined by these two other "addons" * //Version// - not used yet * //Files// - a comma seperated list of your lua files that should be loaded. The order is important!. * //Enabled// - take a wild guess 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) \\ === 2. Creating a Behavior Tree=== We now need to create a new Behavior Tree for our new addon. {{youtube>ULgXFn-9860?medium}} \\ === 3. Behavior Tree Basics === 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()//. {{youtube>sQboQoW3sU0?medium}} {{youtube>fYUpPF_HdRQ?medium}} \\ === 4. Main Menu UI & Settings === * How to add your Addon GUI Elements to the Main Menu * How to save and load addon settings {{youtube>MMASYe_FEuU?medium}} 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 \\ === 5. Let's get Moving - Subtrees === * How to use existing Subtrees * What is the global node context and how to use it * Accessing game data and functions {{youtube>Xj1fNXEPIsc?medium}} \\ \\ \\ === 6. Making the Addon Private === 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. \\ * Read how private addons are working in general => **[[private_addon_developer_help|Private Addon API]]** * Open the addon lua file we created in the 1st Tutorial (sillyjumper.lua) in the text editor. * Instead of just loading "all BehaviorTree files" from our folder like before, we will now change this to load only a specific file, our main Behavior Tree: -- 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) * Don't forget to move your .bt file into the mandatory "data" folder (you can use any other folder name ofc, but your code needs to be changed as well then): {{::bttutorial_folder.png?direct&200|}} * Now reload your lua modules ingame and you should see that your Addon is loading: {{::bttutorial_ingame.png?direct&200|}} * Your Addon is now secured and if distributed through the MinionApp-Store, noone is able to read or steal your code. \\ === 7. Extending Node Context === 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.\\ * Add the following changes to your code: - Create the sillyjumper.btreecontext = {} table - Extend the local btreeinfo with the new "LoadContext = function() return sillyjumper.GetContext() end," function -- 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 } * Now lets create the Callback function in your lua file where your custom "context" table is being created and all your functions defined. * Additionally I'll create a simple test function which will then be made available in the "context" table. -- 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 * And now you can access this function in any node in your BTree with //context.Jump()// {{::bttutorial_ingame2.png?direct&200|}} \\ === 8. Creating Private SubTrees === 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, } * And then define this new Callback function (in my case, sillyjumper.LoadSubtreeData(filename)). This will load the requested file from your private addon folder. -- 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 * You should now be all setup for creating and loading private SubTrees and building your own Addon. \\ === 9. Creating a private Subtree === * Todo ...it works, but this is a larger time consuming part I'll do when I have time :D * === 10. Common Errors and Infos === * You ALWAYS need to be aware that if you save for example a "target", which is a metatable and directly linked to the game memory, in a context-table variable or anywhere else, you __cannot access__ or use that in a later call / pulse of the Behaviortree. You always need to make sure that the metatable you try to access still exists, else you will experiement the weirdest crashes. * You ALWAYS need to return a self:success(), self:fail() or self:running() in every if-else-end case inside your action nodes. If you miss even one case, your whole Tree will not work. * You can access each node's data by using self.settings, self.variables, self.info and ofcourse through the shared context table. * There are additional BehaviorManager functions that might be helpful to you: === 11. BTreeinfo === 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, } === 12. BehaviorManager functions === 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)