# WRobot Plugin & FightClass Development Guide

**Sources:**
- Basic Plugin Guide (Official Documentation)
- Plugin Template (Complete Working Example)
- Debug Guide (Visual Studio Setup)
- FightClass Snippets (C# & Lua)

**Purpose:** Complete guide for creating WRobot plugins and FightClasses

---

## TABLE OF CONTENTS

1. [Plugin Development](#plugin-development)
2. [Plugin with Settings](#plugin-with-settings)
3. [Plugin Template (Advanced)](#plugin-template-advanced)
4. [Debugging Setup](#debugging-setup)
5. [FightClass Development](#fightclass-development)
6. [FightClass C# Snippets](#fightclass-c-snippets)
7. [FightClass Lua Snippets](#fightclass-lua-snippets)

---

## PLUGIN DEVELOPMENT

### Basic Plugin Structure

**Minimum plugin code:**

```csharp
using System;
using robotManager.Helpful;
using wManager.Wow.Helpers;

public class Main : wManager.Plugin.IPlugin
{
    public void Initialize()
    {
        // Code runs when plugin is enabled and bot starts
    }

    public void Dispose()
    {
        // Code runs when bot is stopped
    }

    public void Settings()
    {
        // Code runs when user clicks plugin settings button
    }
}
```

### Simple Plugin Example

**List equipped items to log and chat:**

```csharp
using System;
using robotManager.Helpful;
using wManager.Wow.Helpers;

public class Main : wManager.Plugin.IPlugin
{
    public void Initialize()
    {
        Logging.Write("ExamplePlugin: Loaded.");
        Logging.Write("ExamplePlugin: Getting equipped items...");
        
        // Loop through all equipped items
        foreach(var item in EquippedItems.GetEquippedItems())
        {
            // Log to WRobot log
            Logging.Write(item.GetItemInfo.ItemEquipLoc + ": " + item.Name);
            
            // Write to WoW chat frame
            Lua.LuaDoString(
                String.Format(@"DEFAULT_CHAT_FRAME:AddMessage(""{0}: {1}"")",
                    item.GetItemInfo.ItemEquipLoc, item.Name));
        }
    }

    public void Dispose()
    {
        // Cleanup code
    }

    public void Settings()
    {
        Logging.Write("ExamplePlugin: No settings available.");
    }
}
```

**Save as:** `MyPlugin.cs` → Put in `WRobot/Plugins/` folder

---

## PLUGIN WITH SETTINGS

### Complete Plugin with Settings

```csharp
using System;
using System.ComponentModel;
using System.IO;
using robotManager;
using robotManager.Helpful;
using wManager.Wow.Helpers;
using wManager.Wow.ObjectManager;

public class Main : wManager.Plugin.IPlugin
{
    public void Initialize()
    {
        // IMPORTANT: Load settings first!
        ExamplePluginSettings.Load();
        
        // Example: Walk forward for specified time
        Move.Forward(Move.MoveAction.PressKey, 
            ExamplePluginSettings.CurrentSetting.ForwardTime);
        
        Move.JumpOrAscend();
    }

    public void Dispose()
    {
        // Cleanup
    }

    public void Settings()
    {
        // Load → Show Form → Save
        ExamplePluginSettings.Load();
        ExamplePluginSettings.CurrentSetting.ToForm();
        ExamplePluginSettings.CurrentSetting.Save();
    }
    
    [Serializable]
    public class ExamplePluginSettings : Settings
    {
        // Define settings with attributes
        [DefaultValue(1000)]
        [Category("Time")]
        [DisplayName("Time to walk forward")]
        [Description("The amount of time in ms that the bot will walk forward")]
        public int ForwardTime { get; set; }

        private ExamplePluginSettings()
        {
            // Set default values
            ForwardTime = 1000;
            
            // Configure settings window (width, height, title)
            ConfigWinForm(
                new System.Drawing.Point(300, 400), 
                "ExamplePlugin " + Translate.Get("Settings")
            );
        }

        public static ExamplePluginSettings CurrentSetting { get; set; }
        
        // Save settings
        public bool Save()
        {
            try
            {
                return Save(AdviserFilePathAndName("ExamplePlugin", 
                    ObjectManager.Me.Name + "." + Usefuls.RealmName));
            }
            catch (Exception e)
            {
                Logging.WriteError("ExamplePluginSettings > Save(): " + e);
                return false;
            }
        }
        
        // Load settings
        public static bool Load()
        {
            try
            {
                if (File.Exists(AdviserFilePathAndName("ExamplePlugin", 
                    ObjectManager.Me.Name + "." + Usefuls.RealmName)))
                {
                    CurrentSetting = Load<ExamplePluginSettings>(
                        AdviserFilePathAndName("ExamplePlugin", 
                        ObjectManager.Me.Name + "." + Usefuls.RealmName));
                    return true;
                }
                CurrentSetting = new ExamplePluginSettings();
            }
            catch (Exception e)
            {
                Logging.WriteError("ExamplePluginSettings > Load(): " + e);
            }
            return false;
        }
    }
}
```

### Settings Attributes

```csharp
[DefaultValue(100)]              // Default value (shown in bold if changed)
[Category("General Settings")]   // Category header
[DisplayName("HP Threshold")]    // Name shown to user
[Description("Use potion at")]   // Description at bottom
[Setting]                        // Mark as setting (required in some cases)
public int HPThreshold { get; set; }
```

---

## PLUGIN TEMPLATE (ADVANCED)

### Full-Featured Plugin Template

```csharp
using robotManager.Helpful;
using System.Threading;
using robotManager.Products;
using wManager.Wow.Enums;
using wManager.Wow.Helpers;
using wManager.Wow.ObjectManager;
using System;
using System.Collections.Generic;
using System.IO;

public class Main : wManager.Plugin.IPlugin
{
    private bool _isLaunched;
    private bool goToTown;

    public void Initialize()
    {
        Logging.Write("[MyPlugin] Started.");
        _isLaunched = true;
        goToTown = false;

        // Setup event watchers
        initializeToTownWatcher();
        watchForEvents();

        // Start main loop
        pluginLoop();
    }

    public void Dispose()
    {
        _isLaunched = false;
        Logging.Write("[MyPlugin] Disposed.");
    }
    
    public void Settings()
    {
        _settings.ToForm();
        _settings.Save();
    }

    // Main plugin loop
    public void pluginLoop()
    {
        while (Products.IsStarted && _isLaunched)
        {
            if (!Products.InPause)
            {
                // Your plugin logic here
                Logging.Write(_settings.testSettingValue);
                Thread.Sleep(3000);
            }
        }
    }

    // Watch for WoW events
    private void watchForEvents()
    {
        EventsLuaWithArgs.OnEventsLuaWithArgs += (LuaEventsId id, List<string> args) =>
        {
            if (id == LuaEventsId.PARTY_MEMBERS_CHANGED)
            {
                Logging.Write("Group changed.");
            }
            
            if (id == LuaEventsId.CHAT_MSG_SYSTEM)
            {
                Logging.Write("System message received.");
            }
        };
    }

    // Watch for ToTown state
    private void initializeToTownWatcher()
    {
        // Before ToTown
        robotManager.Events.FiniteStateMachineEvents.OnRunState += (engine, state, cancelable) =>
        {
            if (state != null && state.DisplayName == "To Town")
            {
                goToTown = true;
                Logging.Write("Going to town...");
            }
        };

        // After ToTown
        robotManager.Events.FiniteStateMachineEvents.OnAfterRunState += (engine, state) =>
        {
            if (state != null && state.DisplayName == "To Town")
            {
                goToTown = false;
                Logging.Write("Returned from town.");
            }
        };
    }

    // Settings property
    private pluginSettings _settings
    {
        get
        {
            try
            {
                if (pluginSettings.CurrentSetting == null)
                    pluginSettings.Load();
                return pluginSettings.CurrentSetting;
            }
            catch { }
            return new pluginSettings();
        }
        set
        {
            try
            {
                pluginSettings.CurrentSetting = value;
            }
            catch { }
        }
    }

    [Serializable]
    public class pluginSettings : Settings
    {
        public pluginSettings()
        {
            testSettingValue = "Hello World!";
            anothertestSettingValue = 100;
        }

        [Setting]
        [Category("General Settings")]
        [DisplayName("Test Setting Value")]
        [Description("This is a description.")]
        public string testSettingValue { get; set; }

        [Setting]
        [Category("Other Settings")]
        [DisplayName("Another Setting Value")]
        [Description("This is another description.")]
        public int anothertestSettingValue { get; set; }

        public static pluginSettings CurrentSetting { get; set; }

        public bool Save()
        {
            try
            {
                return Save(AdviserFilePathAndName("MyPluginName", 
                    ObjectManager.Me.Name + "." + Usefuls.RealmName));
            }
            catch (Exception e)
            {
                Logging.WriteError("MyPluginName > Save(): " + e);
                return false;
            }
        }

        public static bool Load()
        {
            try
            {
                if (File.Exists(AdviserFilePathAndName("MyPluginName", 
                    ObjectManager.Me.Name + "." + Usefuls.RealmName)))
                {
                    CurrentSetting = Load<pluginSettings>(
                        AdviserFilePathAndName("MyPluginName",
                        ObjectManager.Me.Name + "." + Usefuls.RealmName));
                    return true;
                }
                CurrentSetting = new pluginSettings();
            }
            catch (Exception e)
            {
                Logging.WriteError("MyPluginName > Load(): " + e);
            }
            return false;
        }
    }
}
```

---

## DEBUGGING SETUP

### Visual Studio Debug Configuration

**Step-by-step setup for debugging plugins/fightclasses:**

1. **Create Project**
   - New Project → Class Library (.NET Framework 4.0)
   - Name: YourPluginName

2. **Add References**
   - Right-click References → Add Reference → Browse
   - Add from `WRobot\Bin`:
     - `robotManager.dll`
     - `wManager.dll`
     - `MemoryRobot.dll`

3. **Add WRobot Executable**
   - Right-click Solution → Add → Existing Project
   - Change file filter to `*.exe`
   - Select `WRobot.exe` (retail) or `*.exe` (private server)

4. **Set Startup Project**
   - Right-click WRobot.exe → Set as StartUp Project

5. **Configure Output Directory**
   - Right-click your project → Properties → Build
   - Set Output path: `..\..\..\Plugins\` (for plugins)
   - Or: `..\..\..\FightClass\` (for fightclasses)

6. **Platform Target**
   - Properties → Build → Platform target: **x86**

7. **Launch Arguments (Optional)**
   - Right-click WRobot.exe → Properties
   - Debug → Command line arguments: `AutoAttachAndLog=true`

8. **Start Debugging**
   - Press **F5** to start with debugger
   - Set breakpoints in your code
   - Step through with F10/F11

### Attach to Running Process

```
Debug → Attach to Process → Select WRobot.exe
```

---

## FIGHTCLASS DEVELOPMENT

### Basic FightClass Structure

```csharp
using System;
using System.Threading;
using robotManager.Helpful;
using wManager.Wow.Helpers;
using wManager.Wow.ObjectManager;

public class MyFightClass
{
    public void Initialize()
    {
        // FightClass initialization
        Logging.Write("[FightClass] Loaded");
    }

    public void Rotation()
    {
        // Main combat rotation
        var me = ObjectManager.Me;
        var target = ObjectManager.Target;
        
        if (target == null || !target.IsAlive)
            return;
        
        // Your rotation logic here
        if (me.HealthPercent < 30)
        {
            // Use health potion
            ItemsManager.UseItem("Health Potion");
        }
        
        if (SpellManager.HasSpell("Fireball"))
        {
            SpellManager.CastSpellByName("Fireball");
        }
    }

    public void Dispose()
    {
        Logging.Write("[FightClass] Disposed");
    }
}
```

---

## FIGHTCLASS C# SNIPPETS

### Check if Spell is Usable

```csharp
public static bool IsSpellUsable(string spellName)
{
    return SpellManager.SpellUsableLUA(spellName) == 1;
}
```

### Cast Spell with Checks

```csharp
public static bool CastSpell(string spellName, WoWUnit target = null)
{
    if (!SpellManager.HasSpell(spellName))
        return false;
    
    if (SpellManager.GetSpellCooldown(spellName) > 0)
        return false;
    
    if (target != null)
    {
        ObjectManager.Me.Target = target.Guid;
    }
    
    SpellManager.CastSpellByName(spellName);
    Thread.Sleep(200);
    return true;
}
```

### Check Unit Aura (Buff/Debuff)

```csharp
public static bool HasAura(WoWUnit unit, string auraName)
{
    if (unit == null || !unit.IsValid)
        return false;
    
    return unit.HaveBuff(auraName);
}

public static int GetAuraStack(WoWUnit unit, string auraName)
{
    return Lua.LuaDoString<int>(
        $"local name, _, count = UnitBuff('{unit.Name}', '{auraName}'); " +
        $"return count or 0"
    );
}
```

### Get Spell Cooldown

```csharp
public static int GetSpellCooldown(string spellName)
{
    return (int)SpellManager.GetSpellCooldown(spellName);
}
```

### Check Power (Mana/Rage/Energy)

```csharp
public static int GetPower()
{
    return ObjectManager.Me.Mana;
}

public static int GetPowerPercent()
{
    return ObjectManager.Me.ManaPercentage;
}

public static int GetMaxPower()
{
    return ObjectManager.Me.MaxMana;
}
```

### Check if in Range

```csharp
public static bool IsInRange(WoWUnit target, float range)
{
    if (target == null || !target.IsValid)
        return false;
    
    return target.GetDistance <= range;
}

public static bool IsInMeleeRange(WoWUnit target)
{
    return IsInRange(target, 5f);
}
```

---

## FIGHTCLASS LUA SNIPPETS

### Check Spell Usable (Lua)

```lua
-- Check if spell is usable
local usable = IsUsableSpell("Fireball")
if usable then
    CastSpellByName("Fireball")
end
```

### Get Spell Cooldown (Lua)

```lua
-- Get cooldown of spell
local start, duration = GetSpellCooldown("Fireball")
local cooldown = start + duration - GetTime()

if cooldown <= 0 then
    -- Spell is ready
    CastSpellByName("Fireball")
end
```

### Check Unit Buff (Lua)

```lua
-- Check if player has buff
local name, _, count, _, duration, expirationTime = UnitBuff("player", "Power Word: Fortitude")
if name then
    print("Have buff: " .. name .. " with " .. count .. " stacks")
end
```

### Check Unit Debuff (Lua)

```lua
-- Check if target has debuff
local name = UnitDebuff("target", "Corruption")
if name then
    print("Target has " .. name)
end
```

### Get Power Type

```lua
-- Get current power type
local powerType = UnitPowerType("player")
-- 0 = Mana, 1 = Rage, 2 = Focus, 3 = Energy, 6 = Runic Power
```

### Check if Casting

```lua
-- Check if player is casting
local spellName = UnitCastingInfo("player")
if spellName then
    print("Casting: " .. spellName)
end
```

### Target Health

```lua
-- Get target health percent
local health = UnitHealth("target")
local maxHealth = UnitHealthMax("target")
local percent = (health / maxHealth) * 100
```

### Check Line of Sight

```lua
-- Check if target is in line of sight
local inSight = UnitIsVisible("target")
```

---

## COMMON FIGHTCLASS PATTERNS

### Mage Rotation Example

```csharp
public void Rotation()
{
    var me = ObjectManager.Me;
    var target = ObjectManager.Target;
    
    if (target == null || !target.IsAlive || me.IsCast)
        return;
    
    // Buff self
    if (!me.HaveBuff("Ice Armor"))
    {
        CastSpell("Ice Armor");
        return;
    }
    
    // AoE if multiple enemies
    var enemiesNearby = ObjectManager.GetObjectWoWUnit()
        .Where(u => u.IsTargetingMe && u.GetDistance < 10)
        .Count();
    
    if (enemiesNearby >= 3)
    {
        CastSpell("Frost Nova");
        Thread.Sleep(500);
        CastSpell("Cone of Cold");
        return;
    }
    
    // Single target rotation
    if (!target.HaveDebuff("Frostbolt"))
    {
        CastSpell("Frostbolt");
    }
    else if (IsSpellUsable("Fire Blast"))
    {
        CastSpell("Fire Blast");
    }
    else
    {
        CastSpell("Frostbolt");
    }
}
```

### Warrior Rage Management

```csharp
public void Rotation()
{
    var me = ObjectManager.Me;
    var rage = me.Rage;
    
    // Heroic Strike if high rage
    if (rage > 60)
    {
        CastSpell("Heroic Strike");
    }
    
    // Rend if not applied
    if (!target.HaveDebuff("Rend") && rage >= 10)
    {
        CastSpell("Rend");
    }
    
    // Execute if target low HP
    if (target.HealthPercent < 20 && rage >= 15)
    {
        CastSpell("Execute");
    }
}
```

---

## EVENT HANDLING

### Available Events

```csharp
// Lua events
EventsLuaWithArgs.OnEventsLuaWithArgs += (id, args) => { };

// Common Lua Events:
LuaEventsId.PARTY_MEMBERS_CHANGED
LuaEventsId.CHAT_MSG_SYSTEM
LuaEventsId.PLAYER_DEAD
LuaEventsId.PLAYER_ALIVE
LuaEventsId.PLAYER_REGEN_DISABLED  // Enter combat
LuaEventsId.PLAYER_REGEN_ENABLED   // Leave combat
LuaEventsId.UNIT_SPELLCAST_START
LuaEventsId.UNIT_SPELLCAST_SUCCEEDED
LuaEventsId.LOOT_OPENED
```

### FSM State Events

```csharp
// Before state runs
FiniteStateMachineEvents.OnRunState += (engine, state, cancelable) =>
{
    if (state.DisplayName == "Farming")
    {
        // Do something before farming
    }
};

// After state completes
FiniteStateMachineEvents.OnAfterRunState += (engine, state) =>
{
    if (state.DisplayName == "To Town")
    {
        // Do something after going to town
    }
};
```

---

## TIPS & BEST PRACTICES

### Plugin Development
1. Always load settings in `Initialize()`
2. Use `Products.IsStarted` to check if bot is running
3. Use `Products.InPause` to respect pause state
4. Clean up timers/events in `Dispose()`
5. Use Thread.Sleep() in loops to avoid CPU spike

### FightClass Development
1. Check `me.IsCast` before casting new spells
2. Add Thread.Sleep() after spell casts
3. Prioritize instant casts over long casts
4. Check spell cooldown before casting
5. Use Lua for complex buff/debuff checks

### Debugging
1. Use Logging.Write() extensively
2. Set breakpoints at critical points
3. Watch variables in debug mode
4. Test with small code changes
5. Keep backup copies of working code

---

## REFERENCE

- **Plugin Tutorial:** This guide
- **API Docs:** https://marsbars.gitlab.io/unoffical-wrobot-api-docs/
- **Forum:** https://wrobot.eu/forums/
- **C# Tips:** WRobot_CSharp_Tips_Final.md
