xzjv
Community Developer
- Joined
- Mar 17, 2014
- Messages
- 1,243
- Reaction score
- 46
There's a lot of new stuff in QuestTools over the last few months so i'm intending to put together some documentation to help out people making profiles and plugins. This is obviously a work in progress.
Custom Conditions
Nesox was successfully convinced of the need to have additional conditions for our IF/When/While tags, so thank him for that when you get a chance. Plugins can declare their own conditions like so:
For example:
QuestTools Conditions
The following conditions are available to use in any profile if you have QuestTools installed. Many of them are not heavily tested and may have issues still, or be deprecated if they aren't found to be useful.
Quick Checks:
Rifts:
Bot/Hero:
Inventory:
Actors:
Profile Management:
IsCastingOrLoading()
This condition will be true if the bot is not valid, loading a level, casting / channeling or otherwise busy.
Current Difficulty
Item Count
These conditions returns the sum of both backpack and stash counts for the Item.
ActorFound
Actor found is special because it looks at the QuestTools ActorHistory instead of at the current list of actors in DemonBuddy. If the actor has been seen at all since the last world change it will return True. This means that even if you get knocked out of range of the monster or it uses an ability that makes it disappear the profile can still know it has been found.
CurrentAnimation
Returns true if the actor exists and is currently using the specified animation. To discover what animations are available for an actor you can use Trinity debug logging in the debug tab. There is also a debug message every time CurrentAnimation condition is used which shows the animation name.
ProfileSetting
Provides way for profiles split over multiple files to be aware of the same data. This can replace the 'Zeta.Bot.Settings.GlobalSettings.Instance.LastProfile.Contains()' hack. Profile settings are reset on bot start event.
Saving a setting: You can choose any string for the name and value parameters.
Reading a setting:
When Tag
The 'When' tag allows you to execute a special part of your profile whenever a condition is met.
This tag has two main components:
When DemonBuddy finds this tag it will do the following:
When the condition is evaluated to True, the bot starts executing the stored child tags. It does this with priority over whatever else the bot may have been doing.
A Simple Example: View attachment WhenTest1.xml
This profile goes to StoneFort, then starts exploring, when the Actor (an environment pillar with fire on top of it) comes within range, the When tag will move towards it and then wait for five seconds, then continue exploring.
Note the order of tags in the profile; the When tag is positioned before the teleport to stonefort and before starting to explore, yet it occurs after arriving at stonefort and while exploring. When tags can be placed anywhere in the profile, but QuestTools will only start checking the condition after the tag has been encountered.
Supported Tags
Currently most of the default and QuestTools tags are supported. If you find one that doesn't work please report it in the Trinity/QuestTools section of the forum.
To be supported your tag must implement the interface IEnhancedProfileBehavior, which defines a set of methods that are needed to prepare a ProfileBehavior to be executed by the BotBehaviorQueue. Take a look at the tags within QuestTools plugin if you need some examples.
Additional Tag Options
Persist:
By default all when tags are cleared when a profile is loaded (or reloaded). Setting persist to True will cause a When tag to be remembered and stay active after loading a new profile.
Repeat:
By default when tags are only executed once. Setting repeat to True will cause a When tag to be executed again and again so long as the condition is met.
Name
Adding a unique name is useful for identification purposes as the name will be shown in logging messages when it's executing.
Another Example: View attachment WhenTest2.xml
In this example the bot will go to Stonefort, and then move towards and away from the waypoint repeatedly, to illustrate some of the above options.
QuestTools Plugin
Creating plugins is a bit weird in my opinion and more difficult than it needs to be. inheriting your plugin from QuestToolsPlugin instead of IPlugin makes things a little easier and tidier.
Example 1:
Things to note:
Example 2: No Settings Specified
Take a look at KadalaSpree for a complete example of a plugin using this method. https://www.thebuddyforum.com/demon...plugin-kadalaspree-stand-gambling-plugin.html
Custom Conditions
Nesox was successfully convinced of the need to have additional conditions for our IF/When/While tags, so thank him for that when you get a chance. Plugins can declare their own conditions like so:
For example:
Code:
public static class CustomConditions
{
public static void Initialize()
{
ScriptManager.RegisterShortcutsDefinitions((typeof(CustomConditions)));
}
public static bool CurrentSceneName(string sceneName)
{
return ZetaDia.Me.CurrentScene.Name.ToLowerInvariant().Contains(sceneName.ToLowerInvariant());
}
}
QuestTools Conditions
The following conditions are available to use in any profile if you have QuestTools installed. Many of them are not heavily tested and may have issues still, or be deprecated if they aren't found to be useful.
Quick Checks:
- IsCastingOrLoading()
- IsVendorWindowOpen()
- KeyAboveMedian(int actorId)
- KeyBelowMedian(int actorId)
- KeyAboveUpperFence(int actorId)
- KeyBelowLowerFence(int actorId)
- KeyAboveUpperQuartile(int actorId)
- KeyBelowLowerQuartile(int actorId)
- IsKeyOutlier(int actorId)
- HighestKeyCountId(int id)
- LowestKeyCountId(int id)
- IsAnyKeyOutlier()
Rifts:
- CurrentWave(int waveNumber)
Bot/Hero:
- CurrentSceneName(string sceneName)
- CurrentDifficulty(string difficulty)
- CurrentDifficultyLessThan(string difficulty)
- CurrentDifficultyGreaterThan(string difficulty)
- CurrentClass(string actorClass)
- CurrentHeroLevel(int level)
Inventory:
- ItemCount(int actorId)
- ItemCountGreaterThan(int actorId, int amount)
- ItemCountLessThan(int actorId, int amount)
Actors:
- ActorIsAlive(int actorId)
- ActorFound(int actorId)
- ActorExistsNearMe(int actorId, float range)
- HasBeenOperated(int actorId)
- CurrentAnimation(int actorId, string animationName)
Profile Management:
- ProfileSetting(string key)
- UsedOnce(string id)
IsCastingOrLoading()
This condition will be true if the bot is not valid, loading a level, casting / channeling or otherwise busy.
Code:
<!-- Weeping Hollow Waypoint -->
<While condition="CurrentLevelAreaId != 19954 and CurrentWorldId != 135193>
<UseWaypoint waypointNumber="6" questId="1"/>
<WaitWhile condition="IsCastingOrLoading()" />
</While>
Current Difficulty
- CurrentDifficulty(string difficulty)
- CurrentDifficultyLessThan(string difficulty)
- CurrentDifficultyGreaterThan(string difficulty)
Code:
<If condition="CurrentDifficultyLessThan('Torment1')">
<LogMessage quest="1" output="There are no keywardens below Torment Difficulty!" />
<Command name="SetDifficulty" value="Torment1" />
<LeaveGame questId="1" reason="to change difficulty" stayInParty="False" />
</If>
Item Count
- ItemCount(int actorId)
- ItemCountGreaterThan(int actorId, int amount)
- ItemCountLessThan(int actorId, int amount)
These conditions returns the sum of both backpack and stash counts for the Item.
Code:
<If condition="ItemCount(202124) <= 50">
<LogMessage quest="1" output="You less than 50 of this Item!" />
</If>
ActorFound
- ActorFound(int actorId)
Actor found is special because it looks at the QuestTools ActorHistory instead of at the current list of actors in DemonBuddy. If the actor has been seen at all since the last world change it will return True. This means that even if you get knocked out of range of the monster or it uses an ability that makes it disappear the profile can still know it has been found.
Code:
<When condition="ActorFound(256054) and CurrentLevelAreaId == 109538" name="A4 Keywarden">
<LogMessage questId="1" output=">Found A4 Keywarden!" />
</When>
CurrentAnimation
- CurrentAnimation(int actorId, string animationName)
Returns true if the actor exists and is currently using the specified animation. To discover what animations are available for an actor you can use Trinity debug logging in the debug tab. There is also a debug message every time CurrentAnimation condition is used which shows the animation name.
Code:
<If condition="CurrentAnimation(364715,'OpenWorld_LootRunObelisk_B_dead_02')">
<LogMessage output="Rift or Trial Portal is Open" />
</If>
ProfileSetting
- ProfileSetting(string key)
Provides way for profiles split over multiple files to be aware of the same data. This can replace the 'Zeta.Bot.Settings.GlobalSettings.Instance.LastProfile.Contains()' hack. Profile settings are reset on bot start event.
Saving a setting: You can choose any string for the name and value parameters.
Code:
<ProfileSetting name="KeyMode" value="Zerg" />
Reading a setting:
Code:
<If condition="ProfileSetting('KeyMode') == 'Zerg'">
<LogMessage questId="1" output=">Mode is set to zerg!" />
</If>
When Tag
The 'When' tag allows you to execute a special part of your profile whenever a condition is met.
This tag has two main components:
- The Condition to be satisfied
- The Child tags embedded within.
When DemonBuddy finds this tag it will do the following:
a) Read and store all the child tags and the condition
b) Continue executing the rest of the profile.
c) Check continuously to see if the condition is met.
b) Continue executing the rest of the profile.
c) Check continuously to see if the condition is met.
When the condition is evaluated to True, the bot starts executing the stored child tags. It does this with priority over whatever else the bot may have been doing.
A Simple Example: View attachment WhenTest1.xml
This profile goes to StoneFort, then starts exploring, when the Actor (an environment pillar with fire on top of it) comes within range, the When tag will move towards it and then wait for five seconds, then continue exploring.
Code:
<?xml version="1.0" encoding="utf-8"?>
<Profile>
<Name>WhenTest1</Name>
<KillMonsters>True</KillMonsters>
<PickupLoot>True</PickupLoot>
<GameParams act="OpenWorld" resumeFromSave="False" isPrivate="True" numGames="-1" />
<Order>
<!-- Environment a3dun_rmpt_Coal_Piles-845 (157012) -->
<When condition="ActorExistsAt(157012,4159,4031,9,50)">
<LogMessage questId="1" output="> Found Environment Actor a3dun_rmpt_Coal_Piles" />
<MoveToActor questId="1" pathPrecision="5" actorId="157012" />
<WaitTimer questId="1" stepId="2" waitTime="5000" />
</When>
<While condition="CurrentLevelAreaId != 93173">
<UseWaypoint questId="1" waypointNumber="27" />
<WaitWhile questId="1" condition="IsCastingOrLoading()" />
</While>
<ExploreDungeon questId="1" direction="Any" until="FullyExplored"
routeMode="WeightedNearestMinimapUnvisited" boxSize="40"
pathPrecision="60" boxTolerance="0.1" />
</Order>
</Profile>
Note the order of tags in the profile; the When tag is positioned before the teleport to stonefort and before starting to explore, yet it occurs after arriving at stonefort and while exploring. When tags can be placed anywhere in the profile, but QuestTools will only start checking the condition after the tag has been encountered.
Supported Tags
Currently most of the default and QuestTools tags are supported. If you find one that doesn't work please report it in the Trinity/QuestTools section of the forum.
To be supported your tag must implement the interface IEnhancedProfileBehavior, which defines a set of methods that are needed to prepare a ProfileBehavior to be executed by the BotBehaviorQueue. Take a look at the tags within QuestTools plugin if you need some examples.
Code:
#region IEnhancedProfileBehavior
public void Update()
{
UpdateBehavior();
}
public void Start()
{
OnStart();
}
public void Done()
{
_isDone = true;
}
#endregion
Additional Tag Options
Persist:
By default all when tags are cleared when a profile is loaded (or reloaded). Setting persist to True will cause a When tag to be remembered and stay active after loading a new profile.
Repeat:
By default when tags are only executed once. Setting repeat to True will cause a When tag to be executed again and again so long as the condition is met.
Name
Adding a unique name is useful for identification purposes as the name will be shown in logging messages when it's executing.
Another Example: View attachment WhenTest2.xml
In this example the bot will go to Stonefort, and then move towards and away from the waypoint repeatedly, to illustrate some of the above options.
Code:
<?xml version="1.0" encoding="utf-8"?>
<Profile>
<Name>WhenTest2</Name>
<KillMonsters>True</KillMonsters>
<PickupLoot>True</PickupLoot>
<GameParams act="OpenWorld" resumeFromSave="False" isPrivate="True" numGames="-1" />
<Order>
<!-- When close to Waypoint (6442) -->
<When condition="ActorExistsNearMe(6442,20)" name="MoveAway" repeat="True">
<LogMessage questId="1" output="> Moving away from waypoint!" />
<SafeMoveTo questId="312429" stepId="2" x="4274" y="4210" z="-25" pathPrecision="5" />
<WaitTimer questId="1" stepId="2" waitTime="2000" />
</When>
<!-- When waypoint (6442) is found within 300 yards -->
<When condition="ActorExistsNearMe(6442,300)" name="MoveTowards" repeat="True">
<LogMessage questId="1" output="> Oh look there's a waypoint!" />
<MoveToActor questId="1" pathPrecision="5" actorId="6442" interactAttempts="1" />
<WaitTimer questId="1" stepId="2" waitTime="2000" />
</When>
<While condition="CurrentLevelAreaId != 93173">
<UseWaypoint questId="1" waypointNumber="27" />
<WaitWhile questId="1" condition="IsCastingOrLoading()" />
</While>
<WaitTimer questId="1" stepId="2" waitTime="20000" />
</Order>
</Profile>
QuestTools Plugin
Creating plugins is a bit weird in my opinion and more difficult than it needs to be. inheriting your plugin from QuestToolsPlugin instead of IPlugin makes things a little easier and tidier.
Example 1:
Code:
namespace KadalaSpree
{
public class KadalaPlugin : QuestToolsPlugin
{
private KadalaPlugin()
{
Name = "Kadala";
Version = new Version(2, 5, 4);
Author = "xzjv";
Description = "Buys stuff from kadala";
SettingsClass = KadalaSpreeSettings.Instance;
SettingsXaml = "Kadala.Settings.xaml";
}
public override void OnPulse()
{
// Do some stuff here
}
}
}
Things to note:
- You don't have to override the methods that you don't care about with empty methods. Just use the ones you need.
- There is a wrapper for the dirty work of providing a settings page.
- Specify the file name of your .XAML based UI
- Specify the instance of your settings class.
Example 2: No Settings Specified
Code:
namespace Arcanum
{
public class Arcanum : QuestToolsPlugin
{
private Arcanum()
{
Name = "Arcanum";
Version = new Version(1, 0, 7);
Author = "xzjv";
Description = "Enchanting";
}
public override void OnEnabled()
{
Logger.Log("{0} v{1} by {2} Enabled!", Name, Version, Author);
UI.Attach();
}
public override void OnDisabled()
{
Logger.Log("{0} v{1} by {2} Disabled!", Name, Version, Author);
UI.Detach();
}
}
}
Take a look at KadalaSpree for a complete example of a plugin using this method. https://www.thebuddyforum.com/demon...plugin-kadalaspree-stand-gambling-plugin.html
Last edited:






