What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

Help Me Further Understand A Custom Class

ounvme

New Member
Joined
Dec 2, 2011
Messages
38
Reaction score
1
My First Custom Class

I have a pretty decent background in macro programming for MMO games. I play a Disc Priest and I would like to automate some farming tasks. Currently the method is target kill target kill. This consumes much more time and mana vs AoE killing in some areas. The default CC for disc is heal oriented. I would prefer to have it AoE kill oriented with heal checks and precautions. I have made a simple modification that uses the spell Holy Nova and it tested ok but I know I can make it better.

Seeing this line "NearbyFriendlyPlayers.Count" is there a syntax for NearbyEnemies? I would like to have to BOT use Holy Nova when there are more than 2 enemies in range and to use another spell when there are 2 or less. This will improve speed and efficiency.

I would really appreciate if someone can add comments to the lines of code so I can teach myself exactly how this piece of code thinks. My basic changes should be easily seen as I simply commented out the lines I didnt want or need.

Once I understand the flow of the code and what its doing I will be able to have a high level of control over what I want my bots to do.

Code:
#region Revision Info

// This file is part of Singular - A community driven Honorbuddy CC
// $Author: exemplar $
// $Date: 2011-04-14 11:12:40 +0200 (to, 14 apr 2011) $
// $HeadURL: http://svn.apocdev.com/singular/tags/v1/Singular/ClassSpecific/Priest/Discipline.cs $
// $LastChangedBy: exemplar $
// $LastChangedDate: 2011-04-14 11:12:40 +0200 (to, 14 apr 2011) $
// $LastChangedRevision: 281 $
// $Revision: 281 $

#endregion

using System;
using System.Collections.Generic;
using System.Linq;

using Singular.Settings;

using Styx.Combat.CombatRoutine;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using TreeSharp;

using Action = TreeSharp.Action;

namespace Singular
{
    partial class SingularRoutine
    {
        public List<WoWPlayer> ResurrectablePlayers
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWPlayer>().Where(
                    p => !p.IsMe && p.Dead && p.IsFriendly && p.IsInMyPartyOrRaid &&
                         p.DistanceSqr < 40 * 40 && !Blacklist.Contains(p.Guid)).ToList();
            }
        }

        [Class(WoWClass.Priest)]
        [Spec(TalentSpec.DisciplineHealingPriest)]
        [Spec(TalentSpec.DisciplinePriest)]
        [Behavior(BehaviorType.Rest)]
        [Context(WoWContext.All)]
        public Composite CreateDiscHealRest()
        {
            return new PrioritySelector(
                CreateWaitForCast(),
                // Heal self before resting. There is no need to eat while we have 100% mana
                CreateDiscHealOnlyBehavior(true),
                // Rest up damnit! Do this first, so we make sure we're fully rested.
                CreateDefaultRestComposite(SingularSettings.Instance.DefaultRestHealth, SingularSettings.Instance.DefaultRestMana),
                // Make sure we're healing OOC too!
                CreateDiscHealOnlyBehavior(),
                // Can we res people?
                new Decorator(
                    ret => ResurrectablePlayers.Count != 0,
                    new Sequence(
                        CreateSpellCast("Resurrection", ret => true, ret => ResurrectablePlayers.FirstOrDefault()),
                        new Action(ret => Blacklist.Add(ResurrectablePlayers.FirstOrDefault().Guid, TimeSpan.FromSeconds(15)))
                        ))
                );
        }

        private Composite CreateDiscHealOnlyBehavior()
        {
            return CreateDiscHealOnlyBehavior(false);
        }

        private Composite CreateDiscHealOnlyBehavior(bool selfOnly)
        {
            // Atonement - Tab 1  index 10 - 1/2 pts
            NeedHealTargeting = true;
            return new
                Decorator(
                ret => HealTargeting.Instance.FirstUnit != null,
                new PrioritySelector(
                    ctx => selfOnly ? Me : HealTargeting.Instance.FirstUnit,
                    CreateWaitForCast(),
                    // Ensure we're in range of the unit to heal, and it's in LOS.
                    //CreateMoveToAndFace(35f, ret => (WoWUnit)ret),
                    //CreateSpellBuff("Renew", ret => HealTargeting.Instance.TargetList.FirstOrDefault(u => !u.HasAura("Renew") && u.HealthPercent < 90) != null, ret => HealTargeting.Instance.TargetList.FirstOrDefault(u => !u.HasAura("Renew") && u.HealthPercent < 90)),
                    CreateSpellBuff(
                        "Power Word: Shield", ret => !((WoWUnit)ret).HasAura("Weakened Soul") && ((WoWUnit)ret).Combat, ret => (WoWUnit)ret),
                    new Decorator(
                        ret =>
                        NearbyFriendlyPlayers.Count(p => !p.Dead && p.HealthPercent < SingularSettings.Instance.Priest.PrayerOfHealing) >
                        SingularSettings.Instance.Priest.PrayerOfHealingCount &&
                        (SpellManager.CanCast("Prayer of Healing") || SpellManager.CanCast("Divine Hymn")),
                        new Sequence(
                            CreateSpellCast("Archangel"),
                            // This will skip over DH if we can't cast it.
                            // If we can, the sequence fails, since PoH can't be cast (as we're still casting at this point)
                            new DecoratorContinue(
                                ret => SpellManager.CanCast("Divine Hymn"),
                                CreateSpellCast("Divine Hymn")),
                            CreateSpellCast("Prayer of Healing"))),
                    CreateSpellBuff(
                        "Pain Supression", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.PainSuppression, ret => (WoWUnit)ret),
                    CreateSpellBuff("Penance", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.Penance, ret => (WoWUnit)ret),
                    CreateSpellCast(
                        "Flash Heal", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.FlashHeal, ret => (WoWUnit)ret),
                    CreateSpellCast(
                        "Binding Heal",
                        ret =>
                        (WoWUnit)ret != Me && ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.BindingHealThem &&
                        Me.HealthPercent < SingularSettings.Instance.Priest.BindingHealMe,
                        ret => (WoWUnit)ret),
                    CreateSpellCast(
                        "Greater Heal", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.GreaterHeal, ret => (WoWUnit)ret),
                    CreateSpellCast("Heal", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.Heal, ret => (WoWUnit)ret),
                    CreateSpellBuff("Renew", ret => ((WoWUnit)ret).HealthPercent < SingularSettings.Instance.Priest.Renew, ret => (WoWUnit)ret),
                    CreateSpellBuff("Prayer of Mending", ret => ((WoWUnit)ret).HealthPercent < 90, ret => (WoWUnit)ret)

                    // Divine Hymn
                    // Desperate Prayer
                    // Prayer of Mending
                    // Prayer of Healing
                    // Power Word: Barrier
                    // TODO: Add smite healing. Only if Atonement is talented. (Its useless otherwise)
                    ));
        }

        [Class(WoWClass.Priest)]
        [Spec(TalentSpec.DisciplineHealingPriest)]
        [Spec(TalentSpec.DisciplinePriest)]
        [Behavior(BehaviorType.Heal)]
        [Context(WoWContext.All)]
        public Composite CreateDiscHeaComposite()
        {
            return new PrioritySelector(
                // Firstly, deal with healing people!
                CreateDiscHealOnlyBehavior());
        }

        // This behavior is used in combat/heal AND pull. Just so we're always healing our party.
        // Note: This will probably break shit if we're solo, but oh well!
        [Class(WoWClass.Priest)]
        [Spec(TalentSpec.DisciplineHealingPriest)]
        [Spec(TalentSpec.DisciplinePriest)]
        [Behavior(BehaviorType.Combat)]
        [Behavior(BehaviorType.Pull)]
        [Context(WoWContext.All)]
        public Composite CreateDiscCombatComposite()
        {
            return new PrioritySelector(
                //Pull stuff
                new Decorator(
                    ret => !Me.IsInParty && !Me.Combat,
                    new PrioritySelector(
                        CreateEnsureTarget(),
                        CreateMoveToAndFace(28f, ret => Me.CurrentTarget),
                        CreateSpellCast("Holy Nova", ret => !Me.IsInParty && !Me.Combat)
                       // CreateSpellCast("Smite", ret => !Me.IsInParty && !Me.Combat)
                        )),
                // If we have nothing to heal, and we're in combat (or the leader is)... kill something!
                new Decorator(
                    ret => Me.Combat || (RaFHelper.Leader != null && RaFHelper.Leader.Combat),
                    new PrioritySelector(
                        CreateEnsureTarget(),
                        CreateMoveToAndFace(39f, ret => Me.CurrentTarget),
                      //  CreateSpellBuff("Shadow Word: Pain", ret => !Me.IsInParty || Me.ManaPercent >= SingularSettings.Instance.Priest.DpsMana),
                        //Solo combat rotation
                        new Decorator(
                            ret => !Me.IsInParty,
                            new PrioritySelector(
                                CreateSpellCast("Holy Nova")))
                               // CreateSpellCast("Penance"))),
                        //Don't smite while mana is below the setting while in a party (default 70)
                      //  CreateSpellCast("Smite", ret => !Me.IsInParty || Me.ManaPercent >= SingularSettings.Instance.Priest.DpsMana)
                     ))   
                );
        }
    }
}
 
Last edited:
Singular's coding is actually pretty complex. Seeing as how it covers almost every spec, for every class in wow.
Many things are used deep within the certain tree node, that are declared in files in other folders even.

I don't modifying singular. Have a look at other Priest CCs.
 
Thanks for the tip...That would explain why I wasn't able to follow everything that was going on.
 
Thanks for the tip...That would explain why I wasn't able to follow everything that was going on.

if you look in the developers section,i wrote a guide on how to write a CC, that should help you get started. not only that but then you get to learn from scratch instead of figuring out someone else code.
 
It's actually easier for me to work backwards and learn....just the way my brain works. However I did read and follow that guide.
Here is what I came up with and tested. It does work however it is ungodly slow inbetween commands. I am not used to the code strusture. Here is what I am looking for. How can I make everthing happen like its fluid without pauses? As it stands right now the bot will fire off 1 Holy Nova then get beat to 50% before it casts again.

Main Loop
Check Buffs
Move to spot 1,2,3,4....
Count the mobs attacking me
Either AoE or single target kill > 2 adds
Loot
Repeat

I have a feeling the moblist could be creating this issue if it has to scan all the mobs in a specific area.
Is there a command like this NearbyFriendlyPlayers.Count for mobs?

I also used some of the casting and buff code from JSP. It would seem more efficient to have 1 call to check buffs and all the aura checks/castting in 1 subroutine. Less code less mess simple structure.

Essentially I need 2 chunks of code. The buff chunk and the combat chunk.


Code:
//CustomClass Template - Created by CodenameGamma
//Replace Layout with the CC name, 
//and WoWClass.Mage with the Class your Designing for.
//Created July, 3rd 2010
//For use with Honorbuddy
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Styx;
using Styx.Combat.CombatRoutine;
using Styx.Helpers;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace AoEDiscPriest
{
    class Classname : CombatRoutine
    {
        public override sealed string Name { get { return "AoE Discipline Priest v1.0 (Farm/Grind)"; } }
        public override WoWClass Class { get { return WoWClass.Priest; } }
        private static LocalPlayer Me { get { return ObjectManager.Me; } }
        private void slog(string format, params object[] args) //use for slogging
        {
            Logging.Write(format, args);
        }
        public override bool WantButton
        {
            get
            {
            return true;
            }
        }
        public override void OnButtonPress()
        {
        //ConfigForm.ShowDialog();
        }





        #region Spell facilities

        private bool InnerFire
        {
            get
            {
                return Me.Auras.ContainsKey("Inner Fire");
            }
            set
            {
                if (!Me.Auras.ContainsKey("Inner Fire"))
                    CastSpell("Inner Fire");
            }
        }

        private bool PowerWordFortitude
        {
            get
            {
                return Me.Auras.ContainsKey("Power Word: Fortitude");
            }
            set
            {
                if (!Me.Auras.ContainsKey("Power Word: Fortitude"))
                    CastSpell("Power Word: Fortitude");
            }
        }

        private bool ShadowProtection
        {
            get
            {
                return Me.Auras.ContainsKey("Shadow Protection");
            }
            set
            {
                if (!Me.Auras.ContainsKey("Shadow Protection"))
                    CastSpell("Shadow Protection");
            }
        }

        private bool CastSpell(String spellName)
        {
            if (SpellManager.CanCast(spellName))
            {
                Logging.Write("[Casting] {0}", spellName);
                SpellManager.Cast(spellName);
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool CastSpell(String spellName, bool stop)
        {
            if (SpellManager.CanCast(spellName))
            {
                Navigator.PlayerMover.MoveStop();
                Logging.Write("[Casting] {0}", spellName);
                SpellManager.Cast(spellName);
                return true;
            }
            else
            {
                return false;
            }
        }

        private void RangeCheck(int range)
        {
            if (!Me.GotTarget || Me.CurrentTarget.Dead) return;

            Logging.WriteDebug("[Doing] RangeCheck ({0})", range);

            if ((Me.CurrentTarget.Distance > range || !Me.CurrentTarget.InLineOfSight) && !Me.IsMoving)
            {
                WoWPoint toMove = WoWMovement.CalculatePointFrom(Me.CurrentTarget.Location, (float)(range - 1));
                Navigator.MoveTo(toMove);
            }
        }

        #endregion





        #region CC_Begin


        public override bool NeedRest
        {
            get
            {
                return false;
            }
        }

        public override void Rest()
        {


        }

        #endregion

        #region Pull

        public override void Pull()
        {
            RangeCheck(10);
            Me.CurrentTarget.Face();
            Navigator.PlayerMover.MoveStop();
            Combat();
        }

        #endregion

        #region Pull Buffs

        public override bool NeedPullBuffs { get { return false; } }

        public override void PullBuff() { }

        #endregion

        #region Pre Combat Buffs

        public override bool NeedPreCombatBuffs
        {
            get
            {
                if (!InnerFire || !PowerWordFortitude || !ShadowProtection)
                {
                    return true;
                }
                return false;
            }
        }

        public override void PreCombatBuff()
        {
            if (!InnerFire) InnerFire = true;
            if (!PowerWordFortitude) PowerWordFortitude = true;
            if (!ShadowProtection) ShadowProtection = true;
            return;
        }
        #endregion

        #region Combat Buffs

        public override bool NeedCombatBuffs { get { return false; } }

        public override void CombatBuff()
        {

        }

        #endregion

        #region Heal

        public override bool NeedHeal { get { return false; } }

        public override void Heal()
        {

        }

        #endregion

        #region Falling

        public void HandleFalling()
        {
            if (!SpellManager.HasSpell("Levitate"))
                CastSpell("Levitate");
        }

        #endregion

        #region Combat
        public override void Combat()
        {
            if (!Me.GotTarget || Me.CurrentTarget.Dead)
                Targeting.Instance.FirstUnit.Target();
            if (!Me.GotTarget || Me.CurrentTarget.Dead) return;

            if (!Me.IsFacing(Me.CurrentTarget))
                Me.CurrentTarget.Face();

            // Multitarget AoE Holy Nova
            if (MobList.Count > 2)
            {
                if (Me.CurrentTarget.Distance <= 10 && SpellManager.CanCast("Holy Nova"))
                    CastSpell("Holy Nova");
                RangeCheck(10);
                
                return;
            }

            // Single Target
            if (SpellManager.CanCast("Holy Fire"))
            {
                CastSpell("Holy Fire");
                return;
            }
            else if (SpellManager.CanCast("Penance"))
            {
                CastSpell("Penance");
                return;
            }
            else
            {
                CastSpell("Smite");
                return;
            }
             
        }
        #endregion
       
   
        #region Spells


        private void AutoAttack()
        {
            if (!Me.IsAutoAttacking)
            {
                Lua.DoString("StartAttack()");
            }

        }
      #endregion

        #region MobList

        private List<WoWUnit> MobList
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                    .Where(
                        unit =>
                            // avoid players for grinding
                            !unit.IsPlayer &&
                                (
                            // Targetting me
                                    (unit.IsTargetingMeOrPet && (unit.IsNeutral || unit.IsHostile)) ||
                            // Have threat
                                    (unit.ThreatInfo.ThreatValue > 0) ||
                            // Targetting my target (i.e. Tol Barad 25% fighting mobs
                                    (unit.CurrentTarget == Me.CurrentTarget && !unit.IsFriendly)
                                )
                    ).ToList();
            }
        }
        #endregion    
    } 
  }
 
Last edited:
After looking at some other CC's I decided to take a wack at using Trees. Everythign is going green so why not? More to come after I climb a little higher on the learning curve.
 
So I have something that works but I still have the same problem of it having long delays between any task. Any ideas how to fix this behavior?
 
I've edited the discipline priest one for Singular but I dont really have a use for any of the healing parts. I really want and need it to be as light and efficient as possible. I want to basically use it to AOE farm certin locations automatically but if it is going to go dog slow I'd rather do it manually. It makes no sense to me to use a bot for 3 hours to farm what I could do by myself in 20 minutes.
 
I've edited the discipline priest one for Singular but I dont really have a use for any of the healing parts. I really want and need it to be as light and efficient as possible. I want to basically use it to AOE farm certin locations automatically but if it is going to go dog slow I'd rather do it manually. It makes no sense to me to use a bot for 3 hours to farm what I could do by myself in 20 minutes.

So delete all of that?
 
So I have something that works but I still have the same problem of it having long delays between any task. Any ideas how to fix this behavior?
That's most likely HB processing time. The main reason *not* to use Singular.

The way HB works is that it's dependant on the FPS you have in game. Every frame that gets rendered allows HB 1 interaction with the game. Singular does a lot of that outside of the main class class (mehheh).

The best way to solve the long delays is:
- Don't use Singular. That's a lot of overhead right there that you don't want. Not if you're going for the "ultralight" implementation
- Get your FPS ingame as high as possible. 30 at the very least, 60 if you can get it.
- Decide *exactly* what you want the class to do and completely ignore everything else. Testing will show what you've missed.
 
Oddly enough it wasn't a FPS issue or an HB issue at all. It was an OS problem. I have a dual boot system and for the hell of it I tried it in XP and it screams. It doesnt like 7 on my system for some reason. No i have my CC working like it should and tweaking it for max performance.
 
That's most likely HB processing time. The main reason *not* to use Singular.

The way HB works is that it's dependant on the FPS you have in game. Every frame that gets rendered allows HB 1 interaction with the game. Singular does a lot of that outside of the main class class (mehheh).

The best way to solve the long delays is:
- Don't use Singular. That's a lot of overhead right there that you don't want. Not if you're going for the "ultralight" implementation
- Get your FPS ingame as high as possible. 30 at the very least, 60 if you can get it.
- Decide *exactly* what you want the class to do and completely ignore everything else. Testing will show what you've missed.

... wrong.

Singular in fact, is one of the fastest CCs available, because of how its written.

We only load the behaviors actually needed, not the *entire* CC at once. If you're playing as a Disc Priest, you won't have Shadow Priest behaviors loaded (unless the rotation manually loads them in, which it doesn't). The slowest part about Singular, is the startup time, because its compiling a list of all behaviors. And even then, it takes < 5ms.

A lag between things happening is usually CC related, and sometimes bot related. I'd suggest dumping a bunch of logging statements and doing some debugging.
 
Back
Top