Skip to content

Tutorials » Combat Rotation » Step 1: Template


  • Copy the template below and paste it into a text editor of your choice.
  • Save the template in your Mayo folder under: /Utils/PlugIns/Rotations
  • It must have the .cs extension. Example: Test.cs

So we'll save our template under /Utils/PlugIns/Rotations/Test.cs


Template (Open and copy me!)
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Mayo.Core;
using Mayo.GameClient;
using Mayo.Manager.Movement;
using Mayo.Manager.Objects.Interface;
using Mayo.Manager.Rotations.Interface;

public class CustomRotation : ICustomRotation
{
    // ######################################################################
    // VARS
    // ######################################################################

    private Client GC { get; set; } = null!;
    public double CombatDistance { get; } = 3.0;

    // ######################################################################
    // INITIALIZE
    // —————————————————————————————————————————————————————————————————————
    // Executed once when the task starts.
    // ######################################################################

    public void Initialize(Client GC)
    {
        this.GC = GC;

        Log.Write("My custom rotation.", GC);
    }

    // ######################################################################
    // BUFFS
    // —————————————————————————————————————————————————————————————————————
    // It is checked regularly and applied outside of combat.
    // Can also be used for enchantments, conjurings or similar purposes.
    // ######################################################################

    public async Task<Dictionary<object, List<Func<bool>>>> Buffs()
    {
        // Example for custom purpose:
        var MainHandWeapon = GC.OM.GetEquippedItem(Constants.EquipSlot.MainHand);
        if (MainHandWeapon != null && !MainHandWeapon.HasPoison)
            await GC.SM.CastMacroAsync(new()
            {
                "/use Deadly Poison IX",
                "/use 16",
            });

        // Buffs:
        var Buffs = new Dictionary<object, List<Func<bool>>>();

        Buffs.Add("Spell Name", new() // By name
        {
            () => !GC.OM.Me.HasAura("Spell Name"),
        });

        Buffs.Add(12345, new() // By ID
        {
            () => !GC.OM.Me.HasAura(12345),
        });

        return Buffs;
    }

    // ######################################################################
    // COMBAT APPROACH
    // —————————————————————————————————————————————————————————————————————
    // Everything that needs to be done on the way to the target.
    // All actions are performed between 10-30 yds between player and target.
    // ######################################################################

    // ###################################
    // APPROACHING NPC
    // ———————————————————————————————————
    // My behavior toward an NPC.
    // ###################################

    public async Task CombatApproach(IUnit Target)
    {
        var Distance = Target.Distance3D;

        await GC.SM.TryCastAsync("Stealth", new()
        {
            () => Distance >= 20,
            () => Distance <= 30,
            () => !GC.OM.Me.IsInCombat,
            () => !GC.OM.Me.IsInShapeshiftForm(Constants.ShapeshiftForm.Stealth),
        });
    }

    // ###################################
    // APPROACHING PLAYER
    // ———————————————————————————————————
    // My behavior toward a player.
    // ###################################

    public async Task CombatApproach(IPlayer Target)
    {

    }

    // ######################################################################
    // COMBAT ROTATION
    // —————————————————————————————————————————————————————————————————————
    // Repeated execution until target dies or the task has stopped.
    // ######################################################################

    // ###################################
    // ME VS NPC
    // ###################################

    public async Task CombatRotation(IUnit Target)
    {
        if (Target.Distance3D > CombatDistance)
        {
            await GC.MM.FollowAsync(Target, new GroundPathConfig(GC)
            {
                StopFollowDistance = 2.0,
            });

            await GC.MM.InteractAsync(Target);
        }

        if (GC.OM.Me.CurrentPower < 40)
        {
            await GC.MM.InteractAsync(Target);
            return;
        }

        await GC.SM.TryCastAsync("Sinister Strike", Target, new()
        {
            () => GC.OM.Me.ComboPoints <= 1,
        });

        await GC.SM.TryCastAsync("Eviscerate", Target, new()
        {
            () => GC.OM.Me.ComboPoints >= 2,
        });
    }

    // ###################################
    // ME VS PLAYER
    // ###################################

    public async Task CombatRotation(IPlayer Target)
    {
        if (Target.Distance3D > CombatDistance)
        {
            await GC.MM.FollowAsync(Target, new GroundPathConfig(GC)
            {
                StopFollowDistance = 2.0,
            });

            await GC.MM.InteractAsync(Target);
        }

        if (GC.OM.Me.CurrentPower < 40)
        {
            await GC.MM.InteractAsync(Target);
            return;
        }

        await GC.SM.TryCastAsync("Sinister Strike", Target, new()
        {
            () => GC.OM.Me.ComboPoints <= 1,
        });

        await GC.SM.TryCastAsync("Eviscerate", Target, new()
        {
            () => GC.OM.Me.ComboPoints >= 2,
        });
    }
}

Don't let the content of the template confuse you. It contains a very simplified version of a rogue rotation and is intended only as an example to get you started.


In the following steps, we'll explain how to customize the template step by step.