Table of Contents

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: 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

[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)


2. Creating a Behavior Tree

We now need to create a new Behavior Tree for our new addon.


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().


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




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.

-- 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)


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.

  1. Create the sillyjumper.btreecontext = {} table
  2. 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
		}
-- 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


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,
		}
-- 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


9. Creating a private Subtree

10. Common Errors and Infos

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)