This code snippet encapsulates the player character's spell book into a re-usable class. It hides away all of the details about reading data structures from game client memory. You can use it to test if a player has learnt a particular spell when determining how your bot AI should set up the rotations, etc.
I have removed the spell database reading functions due to code dependencies that made this snippet too large to easily explain. I may post an updated version later with the database functionality restored.
You will need to change the section of code that references your memory reading/writing library in the class.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using Voodoo;
using WarcraftVoodoo.Version41014333;
using WarcraftVoodoo.Structures;
namespace WarcraftVoodoo
{
/// <summary>
/// Encapsulates the character's spell book into a single class that can be queried for
/// known and learnt spells.
///
/// Edit: 11/06/28 -- removed spell database lookups when released to public due to too many code dependencies.
/// </summary>
public class SpellBook
{
// Which game client does this spell book refer to
private GameClient m_gameClient;
// The number of known spells in the spell book.
private CachedValue<int> m_knownSpellCount;
///A list of all the spell information in the spell book.
private CachedValue<List<SpellInfo>> m_allSpells;
/// <summary>
/// Construct a new spell book encapsulation
/// </summary>
/// <param name="gc">The game client this spell book refers to</param>
public SpellBook(GameClient gc)
{
m_gameClient = gc;
// set up the cache for reading the known spell count
m_knownSpellCount = new CachedValue<int>(() =>
{
return m_gameClient.MemoryEditor.ReadInt(m_gameClient.ToAbsoluteAddress(LocalPlayerAddress.SpellBookSpellCount));
});
// we only need to refresh the number of known spells every now and again
m_knownSpellCount.UpdateInterval = TimeSpan.FromSeconds(30);
// set up the cache for reading the list of spells
m_allSpells = new CachedValue<List<SpellInfo>>(() =>
{
return GetAllSpells();
});
// we only need to refresh the list of known spells every now and again
m_allSpells.UpdateInterval = TimeSpan.FromSeconds(30);
}
/// <summary>
/// Return the number of known spells in the spell book
/// </summary>
public int KnownSpellCount
{
get
{
return m_knownSpellCount.Value;
}
}
/// <summary>
/// Return the number of learnt spells in the spell book
/// </summary>
public int LearntSpellCount
{
get
{
return LearntSpells.Count();
}
}
/// <summary>
/// Return all of the spell information in the spellbook.
/// </summary>
/// <returns>List of spell information structures.</returns>
private List<SpellInfo> GetAllSpells()
{
int numberOfSpellsKnown = KnownSpellCount;
List<SpellInfo> spells = new List<SpellInfo>();
uint firstSpellPtr = m_gameClient.MemoryEditor.ReadUInt(m_gameClient.ToAbsoluteAddress(LocalPlayerAddress.SpellBook));
for (uint spellIdx = 0; spellIdx < numberOfSpellsKnown; spellIdx++)
{
uint currSpellPtr = m_gameClient.MemoryEditor.ReadUInt(firstSpellPtr + spellIdx * sizeof(uint));
SpellInfo spellData = (SpellInfo)(m_gameClient.MemoryEditor.ReadObject(currSpellPtr, typeof(SpellInfo)));
spells.Add(spellData);
}
return spells;
}
/// <summary>
/// Return a list of all learnt spell identifiers.
/// </summary>
public IEnumerable<uint> LearntSpells
{
get
{
return AllSpells.Where(s => s.State == SpellInfo.SpellState.IsLearnt).Select(s => s.ID);
}
}
/// <summary>
/// Return a list of all known spell identifiers, irrelevant of whether they can be cast or not.
/// </summary>
public IEnumerable<uint> KnownSpells
{
get
{
return AllSpells.Where(s => s.State == SpellInfo.SpellState.IsKnown).Select(s => s.ID);
}
}
/// <summary>
/// Return a list of all spells in the player's spellbook, irrelevant of
/// whether we know them or can cast them.
/// </summary>
public IEnumerable<SpellInfo> AllSpells
{
get
{
return m_allSpells.Value;
}
}
/// <summary>
/// Determine whether the character has learnt any of the specified spells.
/// </summary>
/// <param name="spellList">A list of spell identifiers to search for.</param>
/// <returns>True if the character has learnt the spell.</returns>
public bool HasLearntSpell(IEnumerable<uint> spellList)
{
return spellList.Intersect(LearntSpells).Any();
}
/// <summary>
/// Determine whether the character knows about any of the specified spells.
/// </summary>
/// <param name="spellList">A list of spell identifiers to search for.</param>
/// <returns>True if the character knows the spell.</returns>
public bool HasSpell(IEnumerable<uint> spellList)
{
return spellList.Intersect(KnownSpells).Any();
}
}
}
This is the SpellInfo structure as read from the game client's memory.
Code:
namespace WarcraftVoodoo.Structures
{
/// <summary>
/// Spell Information structure in the player character's spell book
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
public struct SpellInfo
{
/// <summary>
/// The possible states of a spell in the spell book
/// </summary>
public enum SpellState : int
{
IsLearnt = 1, // the spell has been learnt and can be cast
IsKnown = 2 // the spell is known but not yet learnt
};
/// <summary>
/// The state of the spell in the spell book
/// </summary>
[FieldOffset(0x0)]
public SpellState State;
/// <summary>
/// The spell identifier of the spell in the spell book
/// </summary>
[FieldOffset(0x04)]
public uint ID;
}
}
And this is how I use it:
Code:
private void Debug_DumpLeadersKnownSpells()
{
PlayerCharacter leadPlayerCharacter = PartyManager.LeadPlayerCharacter;
if (leadPlayerCharacter == null)
{
return;
}
WowLocalPlayer localPlayer = leadPlayerCharacter.LocalPlayer;
if (localPlayer == null)
{
return;
}
Log.LogDebug(String.Format("These are all of the spells that {0} knows about.", localPlayer.Name));
IEnumerable<uint> learntSpells = localPlayer.SpellBook.LearntSpells;
foreach (uint spellID in learntSpells)
{
Log.LogDebug(String.Format("Spell ID #{0}", spellID));
}
}