My first routine ever and hacked together in 5 minutes so any suggestions/fixes are welcome (used uliana as template).
settings
Code:
using Trinity.Framework.Helpers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
using Trinity.Components.Combat.Resources;
using Trinity.Framework.Actors.ActorTypes;
using Trinity.Framework.Objects;
using Trinity.Framework.Reference;
using Trinity.UI;
using Zeta.Common;
using Zeta.Game.Internals.Actors;
namespace Trinity.Routines.Monk
{
public sealed class MonkInnaPetEP : MonkBase, IRoutine
{
#region Definition
public string DisplayName => "Monk Pet EP Routine";
public string Description => "INNA EP PETS";
public string Author => "Me";
public string Version => "0.1";
public string Url => "";
public override int PrimaryEnergyReserve => 100;
public Build BuildRequirements => new Build
{
Sets = new Dictionary<Set, SetBonus>
{
{ Sets.Innas, SetBonus.Third },
},
Skills = new Dictionary<Skill, Rune>
{
{ Skills.Monk.MysticAlly, null },
{ Skills.Monk.ExplodingPalm, null },
},
};
#endregion
public TrinityPower GetOffensivePower()
{
TrinityActor target;
TrinityPower power = null;
Vector3 position;
if (ShouldDashingStrike(out position))
return DashingStrike(position);
if (!WeightedUnits.Any(u => u.Distance < 20f && HasEP(u)))
return GetExplodingPalmPrimary();
if (ShouldCycloneStrike())
return CycloneStrike();
if (ShouldMysticAlly())
return MysticAlly();
return GetPrimary();
}
private TrinityPower GetExplodingPalmPrimary()
{
TrinityPower power = null;
var target = TargetUtil.LowestHealthTarget(6f, CurrentTarget.Position, SNOPower.Monk_ExplodingPalm);
return ExplodingPalm(target);
}
private TrinityPower GetPrimary()
{
TrinityPower power = null;
var target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
if (Skills.Monk.FistsOfThunder.CanCast())
power = FistsOfThunder(target);
else if (Skills.Monk.DeadlyReach.CanCast())
power = DeadlyReach(target);
else if (Skills.Monk.CripplingWave.CanCast())
power = CripplingWave(target);
else if (Skills.Monk.WayOfTheHundredFists.CanCast())
power = WayOfTheHundredFists(target);
return power;
}
// There is currently a bug with Attributes sometimes not showing up in Trinity's attributes list.
// Needs to be looked into, this is a work-around for now (or use ZetaDia lookup)
public bool HasEP(TrinityActor actor)
=> actor.Attributes.Powers.ContainsKey(SNOPower.Monk_ExplodingPalm) ||
actor.Attributes.GetAttributeDirectlyFromTable<bool>(ActorAttributeType.PowerBuff0VisualEffectB, (int)SNOPower.Monk_ExplodingPalm);
protected override bool ShouldMysticAlly()
{
var skill = Skills.Monk.MysticAlly;
var nearbyUnitsWithEP = WeightedUnits.Any(u => u.Distance < 20f && HasEP(u));
//var nearbyUnitsWithGungdoEP = CurrentTarget.HasDebuff((SNOPower)455436);
if (!skill.CanCast())
return false;
if (!TargetUtil.AnyMobsInRange(45f) && !CurrentTarget.IsTreasureGoblin)
return false;
if (!nearbyUnitsWithEP && !Legendary.Madstone.IsEquipped)
return false;
//if (!nearbyUnitsWithGungdoEP)
// return false;
return true;
}
public double MysticAllyCooldownMs
{
get
{
var baseCooldown = Runes.Monk.SustainedAttack.IsActive ? 14 : 30;
var multiplier = Legendary.TheFlowOfEternity.IsEquipped ? 0.6 : 1;
return 30 * 1000;
}
}
public TrinityPower GetDefensivePower() => GetBuffPower();
public TrinityPower GetBuffPower()
{
if (ShouldSweepingWind())
return SweepingWind();
if (ShouldMantraOfConviction())
return MantraOfConviction();
if (ShouldMantraOfHealing())
return MantraOfConviction();
if (ShouldMantraOfRetribution())
return MantraOfRetribution();
if (ShouldMantraOfSalvation())
return MantraOfSalvation();
if (ShouldEpiphany())
return Epiphany();
if (ShouldBreathOfHeaven())
return BreathOfHeaven();
if (ShouldSerenity())
return Serenity();
if (ShouldBlindingFlash())
return BlindingFlash();
if (ShouldInnerSanctuary())
return InnerSanctuary();
return null;
}
public TrinityPower GetDestructiblePower() => DefaultDestructiblePower();
public TrinityPower GetMovementPower(Vector3 destination)
{
if (AllowedToUse(Settings.DashingStrike, Skills.Monk.DashingStrike) && CanDashTo(destination))
DashingStrike(destination);
return Walk(destination);
}
protected override bool ShouldDashingStrike(out Vector3 position)
{
position = Vector3.Zero;
var skill = Skills.Monk.DashingStrike;
if (skill.TimeSinceUse < 3000 && skill.Charges < MaxDashingStrikeCharges)
return false;
if (!AllowedToUse(Settings.DashingStrike, Skills.Monk.DashingStrike))
return false;
return base.ShouldDashingStrike(out position);
}
protected override bool ShouldEpiphany()
{
if (!AllowedToUse(Settings.Epiphany, Skills.Monk.Epiphany))
return false;
return base.ShouldEpiphany();
}
#region Settings
public override int ClusterSize => Settings.ClusterSize;
public override float EmergencyHealthPct => Settings.EmergencyHealthPct;
IDynamicSetting IRoutine.RoutineSettings => Settings;
public MonkInnaPetEPSettings Settings { get; } = new MonkInnaPetEPSettings();
public sealed class MonkInnaPetEPSettings : NotifyBase, IDynamicSetting
{
private SkillSettings _epiphany;
private SkillSettings _dashingStrike;
private int _clusterSize;
private float _emergencyHealthPct;
[DefaultValue(6)]
public int ClusterSize
{
get { return _clusterSize; }
set { SetField(ref _clusterSize, value); }
}
[DefaultValue(0.4f)]
public float EmergencyHealthPct
{
get { return _emergencyHealthPct; }
set { SetField(ref _emergencyHealthPct, value); }
}
public SkillSettings Epiphany
{
get { return _epiphany; }
set { SetField(ref _epiphany, value); }
}
public SkillSettings DashingStrike
{
get { return _dashingStrike; }
set { SetField(ref _dashingStrike, value); }
}
#region Skill Defaults
private static readonly SkillSettings EpiphanyDefaults = new SkillSettings
{
UseMode = UseTime.Selective,
Reasons = UseReasons.Elites | UseReasons.HealthEmergency
};
private static readonly SkillSettings DashingStrikeDefaults = new SkillSettings
{
UseMode = UseTime.Default,
RecastDelayMs = 3500,
Reasons = UseReasons.Blocked
};
#endregion
public override void LoadDefaults()
{
base.LoadDefaults();
Epiphany = EpiphanyDefaults.Clone();
DashingStrike = DashingStrikeDefaults.Clone();
}
#region IDynamicSetting
public string GetName() => GetType().Name;
public UserControl GetControl() => UILoader.LoadXamlByFileName<UserControl>(GetName() + ".xaml");
public object GetDataContext() => this;
public string GetCode() => JsonSerializer.Serialize(this);
public void ApplyCode(string code) => JsonSerializer.Deserialize(code, this, true);
public void Reset() => LoadDefaults();
public void Save() { }
#endregion
}
#endregion
}
}
settings
Code:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:converters="clr-namespace:Trinity.UI.UIComponents.Converters"
mc:Ignorable="d" Background="#434343" d:DesignHeight="820" d:DesignWidth="390">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../UI/Template.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converters:PercentConverter x:Key="PercentConverter" />
<converters:EnumBooleanConverter x:Key="EnumBooleanConverter" />
<converters:EnumVisibilityConverter x:Key="HiddenWhenEnumTrueConverter" Reverse="True"/>
<converters:EnumVisibilityConverter x:Key="VisibleWhenEnumTrueConverter" />
<converters:FlagsToBoolConverter x:Key="FlagsToValueConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Border Padding="10">
<Grid>
<StackPanel>
<GroupBox>
<GroupBox.Header>General</GroupBox.Header>
<StackPanel>
<Slider Template="{DynamicResource LabelledSliderEditable}"
Tag="Cluster Size"
ToolTip="Number of monsters that must be grouped up before fighting starts"
Interval="100" IsSnapToTickEnabled="True"
Maximum="40" Minimum="1" SmallChange="100" TickFrequency="1" TickPlacement="BottomRight"
Value="{Binding Path=DataContext.ClusterSize}"
HorizontalAlignment="Stretch" Margin="0,0,0,0"
MinWidth="175"/>
<Slider Template="{DynamicResource LabelledSliderEditable}"
Tag="Health Emergency %"
ToolTip="How low your health must drop before the potion is used"
Interval="500" Maximum="99" Minimum="0"
SmallChange="1" LargeChange="5"
TickPlacement="None"
Value="{Binding Path=DataContext.EmergencyHealthPct, Converter={StaticResource PercentConverter}}"
HorizontalAlignment="Stretch" Margin="0"/>
</StackPanel>
</GroupBox>
<GroupBox>
<GroupBox.Header>Epiphany</GroupBox.Header>
<StackPanel>
<WrapPanel>
<RadioButton Content="Always" IsChecked="{Binding DataContext.Epiphany.UseMode, ConverterParameter=Always, Mode=TwoWay, Converter={StaticResource EnumBooleanConverter}}" />
<RadioButton Content="Selectively" IsChecked="{Binding DataContext.Epiphany.UseMode, ConverterParameter=Selective, Mode=TwoWay, Converter={StaticResource EnumBooleanConverter}}" />
</WrapPanel>
<StackPanel Margin="0,5,0,0" Visibility="{Binding DataContext.Epiphany.UseMode, ConverterParameter=Selective, Converter={StaticResource VisibleWhenEnumTrueConverter}}">
<CheckBox ToolTip="Use when there are elites close nearby"
IsChecked="{Binding DataContext.Epiphany.Reasons, ConverterParameter=Elites, Converter={converters:FlagsToBoolConverter}}">Elites nearby</CheckBox>
<CheckBox ToolTip="Use when you are surrounded by enemies"
IsChecked="{Binding DataContext.Epiphany.Reasons, ConverterParameter=Surrounded, Converter={converters:FlagsToBoolConverter}}">Surrounded</CheckBox>
<CheckBox ToolTip="Use when you're low on health"
IsChecked="{Binding DataContext.Epiphany.Reasons, ConverterParameter=HealthEmergency, Converter={converters:FlagsToBoolConverter}}">Health emergency</CheckBox>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox>
<GroupBox.Header>Dashing Strike</GroupBox.Header>
<StackPanel>
<WrapPanel Margin="0,5,0,0">
<RadioButton ToolTip="Use vault whenever it makes sense"
Content="Anytime" IsChecked="{Binding DataContext.DashingStrike.UseMode, Mode=TwoWay, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Default}" />
<RadioButton ToolTip="Use only while fighting monsters"
Content="In combat" IsChecked="{Binding DataContext.DashingStrike.UseMode, Mode=TwoWay, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=InCombat}" />
<RadioButton ToolTip="Use only while NOT fighting monsters"
Content="Out of combat" IsChecked="{Binding DataContext.DashingStrike.UseMode, Mode=TwoWay, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=OutOfCombat}" />
</WrapPanel>
</StackPanel>
</GroupBox>
</StackPanel>
</Grid>
</Border>
</UserControl>