How to edit Default Routine to Support Dual Totem - .cs added as attachment UPDATED FOR #896 http://pastebin.com/x3QCtLFJ or Attachment Important If you are using Shockwave Totem and you want it to be cast right on monsters position. Code: var err1 = LokiPoe.InGameState.SkillBarPanel.UseAt(_totemSlot, true, Utility.CalculatePointAtDistanceAfterStart(myPos, cachedPosition,cachedDistance -3)); If you are using any other totem and you want it to keep some distance from enemies Code: var err1 = LokiPoe.InGameState.SkillBarPanel.UseAt(_totemSlot, true, Utility.CalculatePointAtDistanceAfterStart(myPos, cachedPosition,cachedDistance / 2)); Credits to Ben's Templar CR and Pushedx.
Looks good! I started working on some example logic for a smaller routine for dual totem last night based on what you mentioned, but I ran into a fun client issue with casting totems. In short, it's possible to try and cast a totem too fast, and the client simply doesn't do anything. If you don't use the previous Beta's style of checking for casting, the totems will never cast in the correct place. Here's that basic CR, which you should be able to see the casting behavior is correct for sandwiching mobs between totems. You need to register and have one post to see spoilers! One thing that doesn't account for, is CanCast, as well as having a cast timeout in case the client decides where you want to cast at is not a valid location, which also has to be handled. Combat range has to be lowered some, because if you try casting behind mobs too far, the cursor will be off-screen or overlap the gui itself.
The bouncing is amazing, I'm gonna use it! I'm using it just on monster.Rarity > Rarity.Magic since Magic and White monsters always come in packs and there is no need to sandwich them. Tomorrow I'm gonna try to create some logic to kill clusters of monsters in order to position right in the mdidle of the pack so with the Area of Effect reaches the maximum number of monsters. I'm curious about monsters.First, does it return the closest monster? the thoughest monster?
monsters.First returns the first element in the sequence. monsters is generated by: Code: var monsters = LokiPoe.ObjectManager.GetObjectsByType<Monster>() .Where( m => !AreaStateCache.Current.IsBlacklisted(m.Id) && !m.CannotDie && m.Distance < (_currentLeashRange != -1 ? _currentLeashRange : 50) && m.IsActive) .OrderBy(m => m.Distance).ToList(); So, simply the closest. A targeting weight priority simple we be added back to the CR soon so users can customize weights like before. That way you can do things like, add weight based on # of surrounding mobs, or if it's a necro make it a top priority, etc... CR stuff is just low priority right now as I'm trying to work out some of the larger issues being reported that stem from the API itself.
Updated the thread and I need help from you pushedx. I am trying to add Ice Spear with power charge on crit so I can keep up my power charges in order to deal more damage even though I'm dual totem I can still cast Ice Spear to gain Power Charges. I've added Code: private int _iceSpearSlot = -1; Code: _iceSpearSlot = -1; Code: var icespear = LokiPoe.InGameState.SkillBarPanel.Skills.FirstOrDefault(s => s.Name == "Ice Spear"); if (IsCastableHelper(icespear)) { _iceSpearSlot = icespear.Slot; } Code: private int _maxPowerCharges = -1; private int MaxPowerCharges { get { // This is so we avoid looking up this mostly static stat, every time. if (_maxPowerCharges == -1) { _maxPowerCharges = LokiPoe.ObjectManager.Me.GetStat(StatType.MaxPowerCharges); } return _maxPowerCharges; } } private int PowerCharges { get { Aura aura = LokiPoe.ObjectManager.Me.Auras.FirstOrDefault(a => a.InternalName == "power_charge"); if (aura != null) { return aura.Charges; } return 0; } } private TimeSpan PowerChargeTimeLeft { get { Aura aura = LokiPoe.ObjectManager.Me.Auras.FirstOrDefault(a => a.InternalName == "power_charge"); if (aura != null) { return aura.TimeLeft; } return TimeSpan.Zero; } } Now here's the logic I want it to execute, it will always drop 1 totem near our target no matter what, if we dont have the max number of Power Charges up, or they are finishing soon we cast Ice Spear so we gain them, and only after we have the previous condition we drop the second totem. Here is what I was using: Code: if (_totemSlot != -1) { var skill = LokiPoe.InGameState.SkillBarPanel.Slot(_totemSlot); var currentTotems = skill.DeployedObjects; int minimumTotemDistance = 15; bool targetHasTotemNear = currentTotems.Any(o => o.Position.Distance(bestTarget.Position) < minimumTotemDistance); int numberTotemsNearTarget = currentTotems.Count(o => o.Position.Distance(bestTarget.Position) < minimumTotemDistance); [B] int numberTotemsThatHaveAnAliveMonsterNear = currentTotems.Count(o => o.Position.Distance(CombatTargeting.Targets<Monster>().Any.Position) < minimumTotemDistance); //Code in bold is wrong //Here's where I need help. Using numberTotemsNearTarget works, but it makes bot unnefective because we have a totem that is currently damaging monsters, but since //it is out of range from our BestTarget it recasts the totem instead of charging up the Power Charges, so instead of using numberTotemsNearTarget I want to know the //number of totems that are actually damaging any momsters, because if they are I won't bother recasting it [/B] if (skill.CanUse() && numberTotemsNearTarget < 1 ) { var err1 = LokiPoe.InGameState.SkillBarPanel.UseAt(_totemSlot, true, Utility.CalculatePointAtDistanceAfterStart(myPos, cachedPosition, cachedDistance - 3)); //_totemStopwatch.Restart(); if (err1 == LokiPoe.InGameState.UseError.None) return true; Log.ErrorFormat("[Logic] UseAt returned {0} for {1}.", err1, skill.Name); } else { // keep powercharges up if (_iceSpearSlot != -1) { // See if we can use the skill. var skillTest = LokiPoe.InGameState.SkillBarPanel.Slot(_iceSpearSlot); if (skillTest.CanUse()) { if (PowerChargeTimeLeft.TotalSeconds <= 5 || PowerCharges < MaxPowerCharges) { var err1 = LokiPoe.InGameState.SkillBarPanel.UseAt(_iceSpearSlot, true, cachedPosition); if (err1 == LokiPoe.InGameState.UseError.None) { await Coroutine.Sleep(Utility.LatencySafeValue(500)); await Coroutines.FinishCurrentAction(false); return true; } Log.ErrorFormat("[Logic] Use returned {0} for {1}.", err1, skill.Name); } } } if (skill.CanUse() && numberTotemsNearTarget < 2) { var err1 = LokiPoe.InGameState.SkillBarPanel.UseAt(_totemSlot, true, Utility.CalculatePointAtDistanceAfterStart(myPos, cachedPosition, cachedDistance - 3)); //_totemStopwatch.Restart(); if (err1 == LokiPoe.InGameState.UseError.None) return true; Log.ErrorFormat("[Logic] UseAt returned {0} for {1}.", err1, skill.Name); } } } Without the bold code it is working, I don't know how to count the number of totems who have an alive monster within the minimum distance set by minimumTotemDistance
Once you have a Skill, you can access DeployedObjects to get the current objects created from the skill. If you look at the code for the DualTotemRoutine, that's this portion of it: Code: var totemSkill = LokiPoe.InGameState.SkillBarPanel.Slot2; var totems = totemSkill.DeployedObjects.ToList(); var oldestTotem = totems.Count > 0 ? totems[0] : null; var newestTotem = totems.Count > 1 ? totems[1] : null; In this case, skill is your totemSkill, so you don't have to use .Slot2. Now, you can just check the two totem objects and see how many mobs are around them. Code: if(oldestTotem != null) { var mobsAroundTotem = Utility.NumberOfMobsNear(oldestTotem, 20); if(mobsAroundTotem == 0) { // You should recast the totem, since it's most likely not attacking anything. } } As I mentioned in the other thread about general dual totem stuff, you have to consider the game's casting mechanics for totems. If you have only one totem up, then the next totem you cast will be where you cast it. If you have two totems up, the oldestTotem, is going to be replaced with the new cast, so you shouldn't care about checking newestTotem, as it'd take two casts to replace it. While it is possible to check the CurrentAction of a totem to see if it's casting or not, you should probably use the mobs around check instead, because of the nature of the game and the fact the APi works out of process. Between attacks, there is a time where the Actor doesn't have a current action, so if you processed based on that, then you'd be recasting at times you shouldn't be, depending on when the API actually read the current data from the client.
The default behavior of ExampleRoutine is to cast curses that are on your skill bar under this scenario: Code: // Handle curse logic - curse magic+ and packs of 4+, but only cast within MaxRangeRange. var checkCurses = myPos.Distance(cachedPosition) < ExampleRoutineSettings.Instance.MaxRangeRange && (cachedRarity >= Rarity.Magic || Utility.NumberOfMobsNear(bestTarget, 20) >= 3);