vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:

--[[ BestBuff 1.7

   Casts the best rank possible of various buffs on caster's target,
   irregardless of caster or target level.

   Configuration: /bestbuff
   It will bring up a window to set options and define buffs.
   Generally you will only need to do this once or very seldom.

   There are now two ways to use BestBuff:

   Old way: /script BestBuff(buff_name)
        ie: /script BestBuff("Power Word: Fortitude")

   New way: Go into /bestbuff config panel and check Enabled.
     Any buff defined there on your action bars will automatically BestBuff.

   This means if you have macros dedicated to casting BestBuff you can get
   rid of them.  Drag the spell from your spellbook to a hotkey and use it
   there.

   Self cast and spam which were previously turned on/off in the BestBuff()
   function are now defined in the BestBuff settings.  By default, buffs
   have no notification and do not self cast.

   To add/change/remove buffs, use the /bestbuff config panel.  For more
   permanent changes (in case you frequently lose SavedVariables.lua),
   instructions are in the BestBuff_Defaults.lua.

   To restore settings to default, use /bestbuff reset.

  -- Gello

         3/29/06 - 1.10 tooltip changes
         7/24/05 - changed highest rank to highest rank user knows
     4/11/05 - removed BestBuff from UISpecialFrames
     4/1/05 - added UI config window, ability to auto-BestBuff from action bar,
              saved buffs to SavedVariables.lua so users don't have to re-edit
              for every update.
     3/22/05 - updated toc to 1300
     3/18/05 - fixed a small bug causing excess spam
     3/17/05 - added switch to kill self cast, some cleanup and comments
     2/8/05 - initial release

]]

BestBuff_Original_UseAction = nil; -- for hooking UseAction

--[[ Variables saved to SavedVariables.lua ]]

BB = {};
BB.Buffs = {};
BB.Enabled = 1;
BB.Population = 0;
BB.Selected = 0;

--[[ Original BestBuff() left mostly unchanged ]]
--[[ no squelch or self cast disable flags, they're in config options now ]]

function BestBuff(intended_buff)

  local best_rank = 1;
  local target_rank = 1;
  local cast_rank = 1;
  local which_buff = 0;
  local x = 1;
  local text = nil;

  which_buff = nil;

  -- intended_buff is the string passed to us by user, see if this is in the buffnames list
  for x=1, table.getn(BB.Buffs), 1 do
      if (BB.Buffs[x].Name == intended_buff) then
          which_buff = x;
      end
  end

  -- if it is in the list...
  if which_buff then

      -- see which is the player's best castable rank due to player's level
      for x=1, table.getn(BB.Buffs[which_buff].Levels), 1 do
          if (UnitLevel("player")>=tonumber(BB.Buffs[which_buff].Levels[x])) then
              best_rank = x;
          end
      end

      -- see which is the target's best rank due to target's level
      if (UnitLevel("target")>0) then
          for x=1, table.getn(BB.Buffs[which_buff].Levels), 1 do
              if (UnitLevel("target")>=(tonumber(BB.Buffs[which_buff].Levels[x])-10)) then
                  target_rank = x;
              end
          end
      else
          target_rank = best_rank
      end

      -- if the target's best rank is greater than the caster's best rank,
      -- (ie, target is much higher level) then the actual rank will be
      -- the caster's best rank.
      if best_rank < target_rank then
          cast_rank = best_rank;
      else
          -- otherwise, choose the best rank for the target.
          cast_rank = target_rank;
      end

      -- attempt the spell
      BestBuff_CastSpellByName(intended_buff,"Rank "..cast_rank);

      -- if spam is on and we have a friendly target, spam away
      if BB.Buffs[which_buff].Notify==1 and UnitExists("target") then
          text = "Casting "..intended_buff.."(Rank "..cast_rank..") on "..UnitName("target")..".";
      end

      -- If the spell didn't actually cast, and selfcasting not disabled, self cast it
      if BB.Buffs[which_buff].SelfCast==1 and SpellIsTargeting() then
          cast_rank = best_rank;
          SpellTargetUnit("player");
          if BB.Buffs[which_buff].Notify==1 then
              text = "Casting "..intended_buff.."(Rank "..cast_rank..") on yourself.";
          end
      end

  else
      -- if we're here it means the user asked for a buff not in the buffnames list
      text = "Unable to cast "..intended_buff..".";
  end    

  -- if we gained a message to spam at some point, spam it
  if text and DEFAULT_CHAT_FRAME then
      DEFAULT_CHAT_FRAME:AddMessage(text);
  end

end


--[[ BestBuff callback functions ]]


function BestBuff_OnLoad()
    BestBuff_Original_UseAction = UseAction;
    UseAction = BestBuff_New_UseAction;
    this:RegisterEvent("VARIABLES_LOADED");
end

function BestBuff_OnEvent()

    if event=="VARIABLES_LOADED" then
        BestBuffFrame:Hide();
        if BB.Population<1 then
            BestBuff_PopulateBuffs()
        end
        if BB.Enabled then
            BestBuffEnableCheckButton:SetChecked(1);
        else
            BestBuffEnableCheckButton:SetChecked(0);
        end
        SlashCmdList["BestBuffCOMMAND"] = BestBuff_SlashHandler;
        SLASH_BestBuffCOMMAND1 = "/bestbuff";
    end

end

function BestBuff_SlashHandler(arg1)

    if string.find(arg1,"reset") then
        BestBuff_PopulateBuffs();
    end

    BestBuff_Startup();
    BestBuff_ClearEntry();
end


function BestBuff_New_UseAction(slot,arg2,onself)

    local intended_buff;
    local which_buff = nil;
    local i;

    if not BB.Enabled then
        BestBuff_Original_UseAction(slot,arg2,onself);
    else
        BestBuffTooltip:SetOwner(UIParent,"ANCHOR_NONE")
        BestBuffTooltip:SetAction(slot);
        intended_buff = getglobal("BestBuffTooltipTextLeft1"):GetText();
        
        for i=1,table.getn(BB.Buffs) do
            if BB.Buffs[i].Name == intended_buff then
                which_buff = intended_buff;
            end
        end

        if which_buff then
            BestBuff(intended_buff);
        else
            BestBuff_Original_UseAction(slot,arg2,onself);
        end
    end
end


--[[ Helper functions ]]

-- CastSpellByName() won't work from within UseAction, use CastSpell instead
function BestBuff_CastSpellByName(spell_name,spell_rank) --intended_buff,"Rank "..cast_rank);

        local id;
        local i = 1;
        local done = nil;
        local name;
        local rank;
        local id = nil;
        local intended_rank;

        _,_,intended_rank = string.find(spell_rank,"(%d+)")

        while not done do
                name,rank = GetSpellName(i,BOOKTYPE_SPELL);
                if not name then
                        done = true;
                else
                        if name==spell_name then
                                _,_,rank = string.find(rank,"(%d+)")            
                                if tonumber(rank)<=tonumber(intended_rank) then
                                        id = i;
                                end
                        end
                end
                i = i+1;
        end

    if id then
        CastSpell(id,BOOKTYPE_SPELL);
    end
end



-- restore or initialize Buff to defaults
function BestBuff_PopulateBuffs()

    BestBuffEnableCheckButton:SetChecked(0);
    BB = {};
    BB.Buffs = {};
    BB.Enabled = false;
    BB.Selected = 0;
    BB.Population = 1;
    foreach(BBDefaultBuffs,BestBuff_AddDefaultBuffs);
    BestBuff_SortBuffs();
end
function BestBuff_AddDefaultBuffs(arg1)

    local i;

    if arg1 then
        BB.Buffs[BB.Population] = {};
        BB.Buffs[BB.Population].Name = arg1;
        BB.Buffs[BB.Population].Notify = BBDefault_Notify;
        BB.Buffs[BB.Population].SelfCast = BBDefault_SelfCast;
        BB.Buffs[BB.Population].Levels = {};
        for i=1,table.getn(BBDefaultBuffs[arg1]) do
            BB.Buffs[BB.Population].Levels[i] = BBDefaultBuffs[arg1][i];
        end
    end
    BB.Population = BB.Population + 1;
end

-- always sort buffs alphabetically by .Name
function BestBuff_SortBuffs()

    local temp_buff = {};
    local changed = true;

    if BB.Population>2 then
        while changed do
            changed = false;
            for i=1,BB.Population-2 do
                if BB.Buffs[i].Name > BB.Buffs[i+1].Name then
                    temp_buff = BB.Buffs[i];
                    BB.Buffs[i] = BB.Buffs[i+1];
                    BB.Buffs[i+1] = temp_buff;
                    changed = true;
                end
            end
        end
    end
end


function BestBuff_Debug()

    local i;
    local j;
    local text;

    DEFAULT_CHAT_FRAME:AddMessage("Population: "..BB.Population);
    for i=1,table.getn(BB.Buffs) do
        text = "Buff #"..i..": "..BB.Buffs[i].Name..": ";
        for j=1,table.getn(BB.Buffs[i].Levels) do
            text = text .. BB.Buffs[i].Levels[j] .. " ";
        end
        DEFAULT_CHAT_FRAME:AddMessage(text);
    end
end

-- reset the lower "edit" area of the window
function BestBuff_ClearEntry()

    BestBuffEditBox_OnEscapePressed();
    BestBuff_SortBuffs()
    BB.Selected = 0;
    BestBuffEditNameBox:SetText("");
    BestBuffEditLevelsBox:SetText("");
    BestBuffNotifyCheckButton:SetChecked(0);
    BestBuffSelfCastCheckButton:SetChecked(0);
    BestBuffList_Update();
end

-- returns a string as levels separated by spaces
function BestBuff_LevelString(arg1)

    local text = "";

    if BB.Buffs[arg1].Levels then
        for i=1,table.getn(BB.Buffs[arg1].Levels) do
            if i>1 then
                text = text .. " ";
            end
            text = text .. BB.Buffs[arg1].Levels[i];
        end
    end

    return text;
end             

function BestBuff_Startup()
    BestBuffFrame:StopMovingOrSizing();
    BestBuffFrame:Show();
end


--[[ Dialog control functions ]]

function BestBuff_OnMouseDown(arg1)
    if arg1=="LeftButton" then
        BestBuffFrame:StartMoving();
    end
end

function BestBuff_OnMouseUp(arg1)
    if arg1=="LeftButton" then
        BestBuffFrame:StopMovingOrSizing();
    end
end

function BestBuffCloseButton_OnClick()

    BestBuff_ClearEntry();
    BestBuffFrame:Hide();
end

function BestBuffNameButton_OnClick(arg1)

    local index;
    local item;

    BestBuffEditBox_OnEscapePressed();

    index = arg1 + FauxScrollFrame_GetOffset(BestBuffScrollFrame)

    if index==BB.Selected then
        index = BB.Population;
        BB.Selected = 0;
    end

    if index < BB.Population then
        BB.Selected = index;
        BestBuffEditNameBox:SetText(BB.Buffs[index].Name);
        BestBuffEditLevelsBox:SetText(BestBuff_LevelString(index));
        BestBuffNotifyCheckButton:SetChecked(BB.Buffs[index].Notify);
        BestBuffSelfCastCheckButton:SetChecked(BB.Buffs[index].SelfCast);
        BestBuffList_Update();
    else
        BestBuff_ClearEntry();
    end
end

function BestBuffEnableCheckButton_OnClick()

    if BestBuffEnableCheckButton:GetChecked()==1 then
        BB.Enabled = true;
    else
        BB.Enabled = false;
    end
end

function BestBuffSelfCastCheckButton_OnClick()

    if BB.Selected>0 then
        BB.Buffs[BB.Selected].SelfCast = BestBuffSelfCastCheckButton:GetChecked();
    end
end

function BestBuffNotifyCheckButton_OnClick()

    if BB.Selected>0 then
        BB.Buffs[BB.Selected].Notify = BestBuffNotifyCheckButton:GetChecked();
    end
end

function BestBuffAddButton_OnClick()

    local i;
    local j;
    local text;
    local err_msg = "";

    text = BestBuffEditNameBox:GetText();

    for i=1,BB.Population-1 do
        if text=="" or BB.Buffs[i].Name==text then
            err_msg = "Can't add this buff.\nNew buffs must have a unique name.";
        end
    end

    if err_msg~="" then
        message(err_msg);
    else

        BB.Buffs[BB.Population] = {};
        BB.Buffs[BB.Population].Name = BestBuffEditNameBox:GetText();

        text = BestBuffEditLevelsBox:GetText();
        -- populate .Levels with numbers in text
        BB.Buffs[BB.Population].Levels = {};
        j = 1;
        for i in string.gfind(text,"(%d+)") do
            BB.Buffs[BB.Population].Levels[j] = i;
            j = j+1;
        end
        if j==1 then
            BB.Buffs[BB.Population].Levels[1] = 1;
        end

        BB.Buffs[BB.Population].Notify = BestBuffNotifyCheckButton:GetChecked();

        BB.Buffs[BB.Population].SelfCast = BestBuffSelfCastCheckButton:GetChecked();

        table.setn(BB.Buffs,BB.Population);
        BB.Population = BB.Population + 1;

        BestBuff_ClearEntry();

    end
end

function BestBuffChangeButton_OnClick()

    local item;
    local i;
    local j;
    local text;

    if BB.Selected>0 then
        BB.Buffs[BB.Selected].Name = BestBuffEditNameBox:GetText();
        text = BestBuffEditLevelsBox:GetText();
        -- populate .Levels with numbers in text
        BB.Buffs[BB.Selected].Levels = {};
        j = 1;
        for i in string.gfind(text,"(%d+)") do
            BB.Buffs[BB.Selected].Levels[j] = i;
            j = j+1;
        end
        if j==1 then
            BB.Buffs[BB.Selected].Levels[1] = 1;
        end
        BB.Buffs[BB.Selected].Notify = BestBuffNotifyCheckButton:GetChecked();
        BB.Buffs[BB.Selected].SelfCast = BestBuffSelfCastCheckButton:GetChecked();
        BestBuff_ClearEntry();
    end
end

function BestBuffDeleteButton_OnClick()

   if BB.Selected>0 then
        table.remove(BB.Buffs,BB.Selected);
        BB.Population = BB.Population - 1;
        BB.Selected = 0;
        BestBuff_ClearEntry();
   end
end

function BestBuffEditBox_OnEscapePressed()

    BestBuffEditNameBox:ClearFocus();
    BestBuffEditLevelsBox:ClearFocus();

    if BB.Selected>0 then
        BestBuffEditNameBox:SetText(BB.Buffs[BB.Selected].Name);
        BestBuffEditLevelsBox:SetText(BestBuff_LevelString(BB.Selected));
    end
end

function BestBuffList_Update()

    local index;
    local i;
    local itemText;
    local itemButton;

    FauxScrollFrame_Update(BestBuffScrollFrame, BB.Population-1, 8, 8);

    for i=1,8 do
        index = i + FauxScrollFrame_GetOffset(BestBuffScrollFrame);
        itemButton = getglobal("BestBuffName"..i);
        itemText = getglobal("BestBuffName"..i.."_Text");
        if (index<BB.Population) then
            itemText:SetText(BB.Buffs[index].Name);
            itemText:Show();
            if index==BB.Selected then
                itemButton:LockHighlight();
            else
                itemButton:UnlockHighlight();
            end
        else
            itemText:Hide();
            itemButton:UnlockHighlight();
        end
    end
end


--[[ BestBuff Tooltip/OnEnter functions ]]

function BestBuff_Tooltip(arg1,arg2)

    GameTooltip_SetDefaultAnchor(GameTooltip,this);
    GameTooltip:SetText(arg1);
    GameTooltip:AddLine(arg2, .75, .75, .75, 1);
    GameTooltip:Show();
end

function BestBuffEnableCheckButton_OnEnter()
    BestBuff_Tooltip("Enable Auto BestBuff","Checking this will allow BestBuff to alter the ranks of buffs cast directly from an action bar.  This will not affect the /script BestBuff() macro command at all.\n\nThe buffs below will still be used for /script BestBuff().");
end

function BestBuffSelfCastCheckButton_OnEnter()
    BestBuff_Tooltip("Self cast this buff","Checking this will cause this buff to cast on you if it cannot cast on the current target.");
end

function BestBuffNotifyCheckButton_OnEnter()
    BestBuff_Tooltip("Notify on casting this buff","Checking this will send a notification of the spell, rank and target to your main chat window when you cast this buff.");
end

function BestBuffAddButton_OnEnter()
    BestBuff_Tooltip("Add this buff","Click here to add this buff to the potential buffs list.");
end

function BestBuffChangeButton_OnEnter()
    BestBuff_Tooltip("Change this buff", "Click here to change the selected buff to the values entered above.");
end

function BestBuffDeleteButton_OnEnter()
    BestBuff_Tooltip("Delete this buff", "Click here to delete the selected buff from the potential buffs list.");
end

function BestBuffEditNameBox_OnEnter()
    BestBuff_Tooltip("Buff name", "This field is the exact cast-sensitive name of the buff as used by WoW. ie, Power Word: Fortitude");
end

function BestBuffEditLevelsBox_OnEnter()
    BestBuff_Tooltip("Buff levels", "This field is a list of the levels each rank of a buff is learned, separated by spaces.  Each number corresponds to a rank.\n\nie, 1 12 24 means:\n(Rank 1) is learned at level 1\n(Rank 2) is learned at level 12\n(Rank 3) is learned at level 24");
end

function HighestRank(spellname)

        local rank = 0;
        local foundspell,foundrank="";
        local i=1;

        while foundspell do
                foundspell, foundrank = GetSpellName(i,BOOKTYPE_SPELL);
                i = i + 1;
                _,_,foundrank=string.find(tostring(foundrank),"Rank (%d+)");
                foundrank = tonumber(foundrank);
                if foundspell==spellname and foundrank and foundrank>rank then
                        rank = foundrank;
                end
        end
        
        return rank;
end