Sign in to follow this  
Straconis

Trying to fix this and over my head

Recommended Posts

Straconis    122
This is a mod for World of Warcraft that appears to be abbandoned there hasn't been any updates in about a year. I've been trying to repair it for the latest update patch, but am just over my head. The mod is broken up into 2 folders. 1 Folder is the mod itself the other is a Titan Panel plug in. Each Folder contains 3 files. Mod Folder: KillsToLevel Contents: KillsToLevel.toc main.lua pref.lua Titan Folder: TitanKillsToLevel Contents: main.lua TitanKillsToLevel.toc waffles.xml Here's the contents of the files: Mod Folder: KillsToLevel.toc
## Interface: 20300
## Title: KillsToLevel
## Notes: Displays the number of kills needed to level.
## Version: 1.4
## SavedVariablesPerCharacter: KillsToLevel_Pref
main.lua
pref.lua



main.lua
KillsToLevel_Pref = {}

local KillsToLevel_DefaultPref =
  {
   hidden = false,
   locked = false,
   
   scale = 1.0,
   message_format =  "Kills To Level: %s",
   
   text_colour =     {1, .8, .2}, -- Gold
   number_colour =   {1, 1, 1}, -- White
   decrease_colour = {0, 1, 0}, -- Green
   increase_colour = {1, .5, 0}, -- Orange
   
   animation_speed = 0.6180339887498948482045868343656381177203091798057628621354486227,
   
   -- Kills from this many seconds ago lose half their influence on the average.
   solo_halflife = 8*60,
   party_halflife = 6*60,
   raid_halflife = 5*60,
   
   -- For the alternate value, assume you'll be killing this many monsters
   -- relative to your own level.
   fallback_spread = { 2, 6, 8, 3, 1 }
  }

local KillsToLevel = CreateFrame("Frame", "KillsToLevelFrame", UIParent)

function KillsToLevel:ObjectDumpString(object, seen_table, depth)
  if type(object) == "table" then
    if not depth then depth = 3 else depth = depth + 1 end
    if not seen_table then seen_table = {} end
    if depth == 2 or seen_table[object] == 1 then
      return "|cff888888{...}|r"
    else
      seen_table[object] = 1
      local text = "{"
      local i, v = next(object,nil)
      while i do
        text = text ..self:ObjectDumpString(i, seen_table, depth).. "="..self:ObjectDumpString(v, seen_table, depth)
        i, v = next(object,i)
        if i then
          text = text .. ", "
        end
      end
      return text .. "}"
    end
  elseif type(object) == "number" then
    return "|cff00ff00"..object.."|r"
  elseif type(object) == "string" then
    return string.format("|cffffff00%q|r", object)
  elseif type(object) == "boolean" then
    return "|cff00ffff"..(object and "true" or "false").."|r"
  else
    return "|cffff8800"..type(object).."|r"
  end
end

function KillsToLevel:DumpObject(object)
  DEFAULT_CHAT_FRAME:AddMessage(self:ObjectDumpString(object))
end

-- Create an object for keeping track of decaying averages.
--   half_life - Values that are this many seconds old lose half their influence on the average.
--   good_samples - When we have less than this many samples, interpolate the average with the result of a function.
--   fallback_function - Function to calculate the value to use when we don't have enough information to work with.
--   fallback_object - Passed to fallback_function when invoked.
function KillsToLevel:CreateAverage(half_life, good_samples, fallback_function, fallback_object)
  local object = {}
  object.half_life = half_life
  object.last_update = time()
  object.total = 0
  object.weight = 0
  
  object.good_samples = fallback_function and good_samples or 0
  if object.good_samples > 0 then
    object.fallback_object = fallback_object or object
    object.fallback_function = fallback_function
    object.fallback_constant = 1 / object.good_samples
  end
  
  function object:AddValue(value)
    local now = time()
    local scale = math.pow(0.5, (now-self.last_update)/self.half_life)
    self.total = self.total * scale + value
    self.weight = self.weight * scale + 1
    self.last_update = now
  end
  
  function object:GetTotal()
    local scale = math.pow(0.5, (time()-self.last_update)/self.half_life)
    return self.total*scale, self.weight*scale
  end
  
  function object:GetAverage()
    local sum, weight = self:GetTotal()
    
    if weight >= self.good_samples then
      return sum / weight
    else
      local value = self.fallback_function(self.fallback_object)
      return self.fallback_constant * sum - self.fallback_constant * weight * value + value
    end
  end
  
  function object:Reset()
    object.last_update = time()
    object.total = 0
    object.weight = 0
  end
  
  return object
end

function KillsToLevel:DefaultSoloExperience()
  local spread = self.fallback_spread
  local total = 0
  local weight = 0
  local offset = self.player_level-(table.getn(spread)-1)/2-1
  
  for i, v in ipairs(spread) do
    weight = weight + v
    total = total + v*self:MonsterBaseExperience(self.player_level, i+offset > 1 and i+offset or 1)
  end
  
  return total / weight
end

function KillsToLevel:DefaultPartyExperience()
  local spread = self.fallback_spread
  local total = 0
  local weight = 0
  local offset = self.party_level_total/self.party_size-(table.getn(spread)-1)/2-1
  
  for i, v in ipairs(spread) do
    weight = weight + v
    total = total + v*self:MonsterBaseExperience(self.party_level_highest, i+offset > 1 and i+offset or 1)
  end
  
  return total / weight
end

function KillsToLevel:DefaultRaidExperience()
  -- No idea how much experience monsters in a raid give.
  return self.party_average:GetAverage()
end

function KillsToLevel:InitSelf()
  -- Preferences
  self.message_format = "Kills To Level: %s"
  self.hidden = false
  self.text_colour = {1, .8, .2}
  self.number_colour = {1, 1, 1}
  self.decrease_colour = {0, 1, 0}
  self.increase_colour = {1, .5, 0}
  
  -- Information about the current party. . . or rather just the nearby party members.
  self.player_level = 1
  self.party_level_total = 1
  self.party_level_highest = 1
  self.party_size = 1
  
  -- Player's maximum level.
  self.max_level = 60
  
  -- Information about the current raid.
  self.raid_size = 0
  
  -- The kills needed to level.
  self.kills_to_level = 0
  
  -- How much experience is needed to level.
  self.xp_to_level = 0
  self.rested_experience = 0
  
  -- Variables used for calculating the average experience yeild.
  self.solo_average =  self:CreateAverage(10*60, 3, self.DefaultSoloExperience,  self)
  self.party_average = self:CreateAverage(12*60, 3, self.DefaultPartyExperience, self)
  self.raid_average =  self:CreateAverage(18*60, 1, self.DefaultRaidExperience,  self)
  self.current_average_pool = self.solo_average
  
  -- Variables used for animation.
  self.ani_spd = 0.6180339887498948482045868343656381177203091798057628621354486227
  self.ani_spd_tmp = 0
  self.ani_pct = 0
  self.ani_start_value = 0
  self.ani_end_value = 0
  
  -- Register our event handlers.
  self:SetScript("OnEvent", self.OnEvent)
  self:SetScript("OnDragStart", self.OnDragStart)
  self:SetScript("OnDragStop", self.OnDragStop)
  self:RegisterEvent("PLAYER_ENTERING_WORLD")
  self:RegisterEvent("VARIABLES_LOADED")
  self:RegisterForDrag("LeftButton")
  
  -- Decided against the border. I'll just stick with the text.
  --[[ KillsToLevel:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background", 
                            edgeFile = "Interface/Tooltips/UI-Tooltip-Border", 
                            tile = true, tileSize = 16, edgeSize = 16, 
                            insets = { left = 4, right = 4, top = 4, bottom = 4 }})
  KillsToLevel:SetBackdropColor(0.5,0.5,0.5,0.8) ]]
  
  -- Position ourselves
  self:ClearAllPoints()
  self:SetFrameStrata("LOW")
  self:SetWidth(140)
  self:SetHeight(24)
  self:SetMovable(true)
  self:SetPoint("CENTER", 0, 0)
  
  -- Create the text for our window.
  self.message_frame = self:CreateFontString(self:GetName().."_Message")
  self.message_frame:ClearAllPoints()
  self.message_frame:SetPoint("TOPLEFT", 2, -2)
  self.message_frame:SetPoint("BOTTOMRIGHT", -2, 2)
  self.message_frame:SetShadowColor(0,0,0,0.8)
  self.message_frame:SetShadowOffset(1,-1)
  self.message_frame:SetFont(STANDARD_TEXT_FONT, 12)
  
  -- An array of functions to call when the kill counter changes.
  self.callbacks = {}
  
  self:ApplyPreferences()
end

function KillsToLevel:GetPreference(name, settings)
  if settings and settings[name] ~= nil then
    KillsToLevel_Pref[name] = settings[name]
    return settings[name]
  elseif KillsToLevel_Pref[name] ~= nil then
    return KillsToLevel_Pref[name]
  elseif KillsToLevel_DefaultPref[name] ~= nil then
    return KillsToLevel_DefaultPref[name]
  else
    DEFAULT_CHAT_FRAME:AddMessage("KillsToLevel: Unknown preference: "..name)
    return nil
  end
end

function KillsToLevel:ApplyPreferences(settings)
  self.message_format = self:GetPreference("message_format", settings)
  self.hidden = self:GetPreference("hidden", settings)
  
  self.animation_speed = self:GetPreference("animation_speed", settings)
  self.text_colour     = self:GetPreference("text_colour", settings)
  self.number_colour   = self:GetPreference("number_colour", settings)
  self.decrease_colour = self:GetPreference("decrease_colour", settings)
  self.increase_colour = self:GetPreference("increase_colour", settings)
  self.fallback_spread = self:GetPreference("fallback_spread", settings)
  self.solo_average.half_life  = self:GetPreference("solo_halflife", settings)
  self.party_average.half_life = self:GetPreference("party_halflife", settings)
  self.raid_average.half_life  = self:GetPreference("raid_halflife", settings)
  
  self.message_frame:SetTextColor(self.text_colour[1], self.text_colour[2], self.text_colour[3])
  
  self:SetScale(self:GetPreference("scale", settings))
  self:SetLockState(self:GetPreference("locked", settings))
  
  self.kills_to_level = self:KillsNeeded()
  self.ani_start_value = self.kills_to_level
  self.ani_end_value = self.kills_to_level
  self:SetKillText(self.ani_end_value, self.number_colour)
  
  if self:ShouldHide() then
   self:Hide()
  else
   self:Show()
  end
end

function KillsToLevel:RegisterCallback(func, object)
  local list = self.callbacks[func]
  if not list then
    list = {}
    list.has_nil = false
    self.callbacks[func] = list
  end
  
  if object == nil then
    list.has_nil = true
    func(self.kills_to_level)
  else
    table.insert(list, object)
    func(object, self.kills_to_level)
  end
end

function KillsToLevel:UnregisterCallback(func, object)
  local list = self.callbacks[func]
  if list then
    if object == nil then
      list.has_nil = false
    else
      for i,v in ipairs(list) do
        if v == object then
          table.remove(list, i)
          break
        end
      end
    end
  end
  if not list.has_nil and table.getn(list) == 0 then
    self.callbacks[func] = nil
  end
  self:DumpObject(self.callbacks)
end

function KillsToLevel:ShouldHide()
  return self.hidden or self.player_level == self.max_level
end

function KillsToLevel:SetHideState(hidden)
  if hidden ~= self.hidden then
    self.hidden = hidden and true or false
    if self:ShouldHide() then
      self:Hide()
    else
      self:Show()
    end
  end
end

function KillsToLevel:SetLockState(locked)
  if locked ~= self.locked then
    self.locked = locked and true or false
    if self.locked then
      self:EnableMouse(false)
    else
      self:EnableMouse(true)
    end
  end
end

-- Sets the text on the display window.
function KillsToLevel:SetKillText(count, colour)
  self.message_frame:SetText(string.format(self.message_format, 
                             string.format("|cff%.2x%.2x%.2x",
                                           math.floor(colour[1]*255+.5),
                                           math.floor(colour[2]*255+.5),
                                           math.floor(colour[3]*255+.5))..count.."|r"))
end

-- Guess how much experience a normal monster would yeild.
function KillsToLevel:MonsterBaseExperience(player_level, target_level)
  if player_level == target_level then
    return player_level * 5 + 45
  elseif player_level < target_level then
    return ((player_level * 5) + 45) * (1 + 0.05 * (target_level - player_level))
  else
    local zero_diff = 0
    if player_level < 8 then zero_diff = 5
    elseif player_level < 10 then zero_diff = 6
    elseif player_level < 12 then zero_diff = 7
    elseif player_level < 16 then zero_diff = 8
    elseif player_level < 20 then zero_diff = 9
    elseif player_level < 40 then zero_diff = 9 + floor(player_level/10)
    else zero_diff = 5 + floor(player_level/5) end
    return (player_level * 5 + 45) * (1 - (player_level - target_level) / zero_diff)
  end
end

-- Figure out at what level monsters stop yeilding experience.
function KillsToLevel:MonsterGreyLevel(player_level)
  if player_level < 6 then
    return 0
  elseif player_level < 40 then
    return player_level - 5 - math.floor(player_level/10)
  else
    return player_level - 1 - math.floor(player_level/5)
  end
end

-- Get the group bonus scale to apply to a monsters experience yeild, based on the party size.
function KillsToLevel:ExperienceMultiplier(group_size)
  if group_size == 1 or group_size == 2 then return 1
  elseif group_size == 3 then return 1.166
  elseif group_size == 4 then return 1.3
  else return 1.4 end
end

-- Collect information about nearby party members, with which to base experience calculations on.
function KillsToLevel:UpdatePartyInformation()
  self.raid_size = GetNumRaidMembers()
  if self.raid_size > 0 then
    self.party_level_total = 0
    self.party_level_highest = 0
    self.party_size = 0
    for n=1,40 do
      if UnitIsVisible("raid"..n) then
        local peer_level = UnitLevel("raid"..n)
        if peer_level > self.party_level_highest then
          self.party_level_highest = peer_level
        end
        self.party_size = self.party_size + 1
        self.party_level_total = self.party_level_total + peer_level
      end
    end
  else
    self.party_level_total = self.player_level
    self.party_level_highest = self.player_level
    self.party_size = 1
    for n=1,4 do
      if UnitIsVisible("party"..n) then
        local peer_level = UnitLevel("party"..n)
        if peer_level > self.party_level_highest then
          self.party_level_highest = peer_level
        end
        self.party_size = self.party_size + 1
        self.party_level_total = self.party_level_total + peer_level
      end
    end
  end
end

-- Figure out the average amount of experinece the player will get per kill, ignoring the rest bonus.
function KillsToLevel:AverageExperience()
  if self.raid_size > 0 then
    return self.raid_average:GetAverage()*self.player_level/self.party_level_total
  elseif self.party_size > 1 then
    return self.party_average:GetAverage()*self:ExperienceMultiplier(self.party_size)*self.player_level/self.party_level_total
  else
    return self.solo_average:GetAverage()
  end
end

function KillsToLevel:Accuracy()
  local sum, weight
  
  if self.raid_size > 0 then
    sum, weight = self.raid_average:GetTotal()
  elseif self.party_size > 1 then
    sum, weight = self.party_average:GetTotal()
  else
    sum, weight = self.solo_average:GetTotal()
  end
  
  if weight > 1 then
    return 1-1/weight
  else
    return weight
  end
end

-- 
function KillsToLevel:RecieveKillExperience(total, rest_bonus, group_bonus)
  if self.raid_size > 0 then
    self.raid_average:AddValue(((total-rest_bonus)*self.party_level_total)/self.player_level)
  elseif self.party_size > 1 then
    self.party_average:AddValue(((total-rest_bonus-group_bonus)*self.party_level_total)/self.player_level)
  else
    self.solo_average:AddValue(total-rest_bonus)
  end
  self:SetKillsToLevel(self.kills_to_level - 1)
end

-- Figure out how many kills are needed to reach some amount of experience
function KillsToLevel:KillsNeeded(experience_per_kill, target_experience)
  if not experience_per_kill then experience_per_kill = self:AverageExperience() end
  if not target_experience then target_experience = self.xp_to_level end
  
  if experience_per_kill <= 0 then
    return 99999
  elseif self.rested_experience >= target_experience then
    return math.min(math.ceil(target_experience*0.5/experience_per_kill), 99999)
  else
    return math.min(math.ceil((2*target_experience-self.rested_experience)/(2*experience_per_kill)), 99999)
  end
end

-- Add the rest bonus to an experience value.
function KillsToLevel:RestExperience(base_experience)
  if self.rested_experience > base_experience then return base_experience * 2 else
  return base_experience + self.rested_experience end
end

function KillsToLevel:InterpolateColour(a, b, i)
  local ii = 1-i
  return {a[1]*ii+b[1]*i, a[2]*ii+b[2]*i, a[3]*ii+b[3]*i}
end

function KillsToLevel:OnUpdate()
  if self.ani_start_value ~= self.ani_end_value then
    self.ani_pct = self.ani_pct + arg1 * self.ani_spd_tmp
    if self.ani_pct >= 1 then
      self.ani_start_value = self.ani_end_value
      self:SetKillText(self.ani_end_value, self.number_colour)
    else
      local value = math.floor(self.ani_end_value*self.ani_pct+self.ani_start_value*(1-self.ani_pct)+0.5)
      local intensity = self.ani_pct*2-1
      intensity = 1 - intensity * intensity
      if self.ani_start_value > self.ani_end_value then
        self:SetKillText(value, self:InterpolateColour(self.number_colour, self.decrease_colour, intensity))
      else
        self:SetKillText(value, self:InterpolateColour(self.number_colour, self.increase_colour, intensity))
      end
    end
  elseif self.kills_to_level ~= self.ani_end_value then
    self.ani_pct = 0
    self.ani_end_value = self.kills_to_level
    self.ani_spd_tmp = 1/(math.pow(math.abs(self.ani_start_value-self.ani_end_value),
                       0.3819660112501051517954131656343618822796908201942371378645513773)*self.animation_speed)
  else
    -- If nothing needs to be done, disable OnUpdate
    self:SetScript("OnUpdate", nil)
  end
end

function KillsToLevel:SetKillsToLevel(value)
  local new_value = value or self:KillsNeeded()
  if new_value ~= self.kills_to_level then
    self.kills_to_level = new_value
    self:SetScript("OnUpdate", self.OnUpdate)
    
    -- Let anything that wants to know that we changed the kill count.
    for func, object_list in pairs(self.callbacks) do
      if object_list.has_nil then
        func(new_value)
      end
      for index, object in ipairs(object_list) do
        func(object, new_value)
      end
    end
  end
end

function KillsToLevel:OnEvent(event)
  if event == "VARIABLES_LOADED" then
    self:ApplyPreferences()
  elseif event == "PLAYER_LEVEL_UP" then
    self.player_level = arg1
    if self.player_level == self.max_level then
      self:Hide()
    end
  elseif event == "PARTY_MEMBERS_CHANGED" then
    local old_party_size, old_raid_size = self.party_size, self.raid_size
    self:UpdatePartyInformation()
    if self.party_size ~= old_party_size or self.raid_size ~= old_raid_size then
      -- If the group changes, then force the kill count to the current best guess.
      self:SetKillsToLevel()
    end
  elseif event == "CHAT_MSG_COMBAT_XP_GAIN" then
    -- I'm not using actual words in the parsing here, hopefuly this will work for the non-english clients.
    
    -- The very first number is assumed to be xp, but only if there's a comma before it,
    -- otherwise it was probably experience from a quest.
    -- 'You gain X experience', versus 'X dies, you gain Y experience'.
    local start, last, total, rest_bonus, group_bonus = string.find(arg1, ",.-(%d+)", 1)
    if start then
      start, last, rest_bonus = string.find(arg1, "(%d+)", last+1)
      if start then start, last, group_bonus = string.find(arg1, "(%d+)", last+1) end
      
      if (rest_bonus and not group_bonus)  -- Rest_bonus might actually be the group_bonus, if we got one but not the other.
           and (self.raid_size > 1 -- Might get a group bonus if we're in a raid.
                or self.party_size > 2) -- Or if we're in a party with 3 or more people.
           and not self.rested_experience then -- And it couldn't have been a rest bonus if we weren't rested.
        group_bonus = rest_bonus -- So, we must have gotten the variables backwards.
        rest_bonus = 0
      end
      
      self:RecieveKillExperience(total, rest_bonus or 0, group_bonus or 0)
    end
  elseif event == "PLAYER_XP_UPDATE"  then
    self.xp_to_level = UnitXPMax("player") - UnitXP("player")
    self.rested_experience = GetXPExhaustion() or 0
    local accuracy = self:Accuracy()
    if accuracy > 0 then
      local real_kills_to_level = self:KillsNeeded()
      
      if self.kills_to_level * accuracy > real_kills_to_level or
         self.kills_to_level / accuracy < real_kills_to_level then
        -- If the counter gets too far from where we think it should be, we'll reset it.
        self:SetKillsToLevel(real_kills_to_level)
      end
    end
    -- OnUpdate will update the display if needed and unset itself when done.
    self:SetScript("OnUpdate", self.OnUpdate)
  elseif event == "PLAYER_ENTERING_WORLD" then
    self.player_level = UnitLevel("player")
    self.xp_to_level = UnitXPMax("player") - UnitXP("player")
    self.max_level = 60 + 10*GetAccountExpansionLevel()
    self.rested_experience = GetXPExhaustion() or 0
    self:UpdatePartyInformation()
    self.kills_to_level = self:KillsNeeded()
    self.ani_start_value = self.kills_to_level
    self.ani_end_value = self.kills_to_level
    self:SetKillText(self.kills_to_level, self.number_colour)
    this:RegisterEvent("PLAYER_LEAVING_WORLD")
    this:RegisterEvent("PLAYER_LEVEL_UP")
    this:RegisterEvent("PLAYER_XP_UPDATE")
    this:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN")
    this:RegisterEvent("PARTY_MEMBERS_CHANGED")
    if not self:ShouldHide() then
      this:Show()
    end
  elseif event == "PLAYER_LEAVING_WORLD" then
    this:Hide()
    this:UnregisterEvent("PARTY_MEMBERS_CHANGED")
    this:UnregisterEvent("CHAT_MSG_COMBAT_XP_GAIN")
    this:UnregisterEvent("PLAYER_XP_UPDATE")
    this:UnregisterEvent("PLAYER_LEVEL_UP")
    this:UnregisterEvent("PLAYER_LEAVING_WORLD")
  end
end

function KillsToLevel:OnDragStart()
  self:StartMoving()
end

function KillsToLevel:OnDragStop()
  self:StopMovingOrSizing()
end

KillsToLevel:InitSelf()



pref.lua
local KillsToLevelPref = CreateFrame("Frame", "KillsToLevelPrefFrame", UIParent)

SlashCmdList["KillsToLevel"] = function () KillsToLevelPref:Show() end
SLASH_KillsToLevel1 = "/k2l"
SLASH_KillsToLevel2 = "/kills2level"
SLASH_KillsToLevel3 = "/killstolevel"

local ColourNames =
  {"Black",  {0,0,0},
   "White",  {1,1,1},
   "Red",    {1,0,0},
   "Orange", {1,.5,0},
   "Yellow", {1,1,0},
   "Green",  {0,1,0},
   "Teal",   {0,1,1},
   "Blue",   {0,0,1},
   "Magenta",{1,0,1},
   "Light Red",   {1,.5,.5},
   "Light Orange",{1,.75,.5},
   "Light Yellow",{1,1,.5},
   "Light Green", {.5,1,.5},
   "Light Teal",  {.5,1,1},
   "Light Blue",  {.5,.5,1},
   "Light Magenta", {1,.5,1},
   "Dark Red",    {.5,0,0},
   "Brown",       {.5,.25,0},
   "Dark Yellow", {.5,.5,0},
   "Dark Green",  {0,.5,0},
   "Dark Teal",   {0,.5,.5},
   "Dark Blue",   {0,0,.5},
   "Dark Magenta",{.5,0,.5}
  }

function ColourError(a, b)
  return (a[1]-b[1])*(a[1]-b[1])+
         (a[2]-b[2])*(a[2]-b[2])+
         (a[3]-b[3])*(a[3]-b[3])
end

KillsToLevelPref:SetBackdrop({bgFile = "Interface/TutorialFrame/TutorialFrameBackground",
                            edgeFile = "Interface/DialogFrame/UI-DialogBox-Border",
                            tile = true, tileSize = 16, edgeSize = 16,
                            insets = { left = 4, right = 4, top = 4, bottom = 4 }})
--KillsToLevelPref:SetBackdropColor(0.1,0.1,0.2)
KillsToLevelPref:ClearAllPoints()
KillsToLevelPref:SetFrameStrata("HIGH")
KillsToLevelPref:SetWidth(240)
KillsToLevelPref:SetHeight(400)
KillsToLevelPref:SetPoint("CENTER", 0, 0)

KillsToLevelPref.childnum = 1
KillsToLevelPref.y = 0

function KillsToLevelPref:AddHeading(text, size)
  if not text then text = "CHILD_"..self.childnum end
  if not size then size = 14 end
  
  local frame = self:CreateFontString(self:GetName().."_Child"..self.childnum)
  frame:ClearAllPoints()
  frame:SetPoint("TOPLEFT", 4, self.y-4)
  frame:SetPoint("BOTTOMRIGHT", self, "TOPRIGHT", -4, self.y-size-4)
  frame:SetFont(STANDARD_TEXT_FONT, size)
  frame:SetTextColor(1,.8, .2)
  frame:SetText(text)
  self.y = self.y - size - 4
  self.childnum = self.childnum + 1
end

function KillsToLevelPref:AddPair(a, b)
  a:ClearAllPoints()
  a:SetPoint("TOPLEFT", 4, self.y)
  a:SetPoint("BOTTOMRIGHT", self, "TOPLEFT", 102, self.y-15)
  b:ClearAllPoints()
  b:SetPoint("TOPLEFT", 102, self.y)
  b:SetPoint("BOTTOMRIGHT", self, "TOPRIGHT", -4, self.y-15)
  self.y = self.y - 20
end

function KillsToLevelPref:AddOption(text, frame)
  local label = self:CreateFontString(self:GetName().."_Child"..self.childnum)
  self.childnum = self.childnum + 1
  label:SetFont(STANDARD_TEXT_FONT, 12)
  label:SetTextColor(1, 1, 1)
  label:SetText(text)
  self:AddPair(label, frame)
  return frame
end

function KillsToLevelPref:CreateColourEdit()
  local button = CreateFrame("Button", self:GetName().."_Child"..self.childnum, self, "OptionsButtonTemplate")
  button.true_text = true_text or "True"
  button.false_text = false_text or "False"
  
  button.index = 1
  
  button:SetText(ColourNames[1])
  button:SetTextColor(ColourNames[2][1], ColourNames[2][2], ColourNames[2][2])
  
  function button:SetColour(colour)
    local best, best_err = 1, ColourError(ColourNames[2], colour)
    self.index = 1
    while true do
      self.index = self.index + 2
      if not ColourNames[self.index] then
        self.index = best
        self:SetText(ColourNames[best])
        self:SetTextColor(ColourNames[best+1][1], ColourNames[best+1][2], ColourNames[best+1][3])
        return
      end
      local err = ColourError(ColourNames[self.index+1], colour)
      if err < best_err then
        best_err = err
        best = self.index
      end
    end
  end
  function button:GetColour()
    return ColourNames[self.index+1]
  end
  function button:OnClick()
    self.index = self.index + 2
    if not ColourNames[self.index] then self.index = 1 end
    self:SetText(ColourNames[self.index])
    self:SetTextColor(ColourNames[self.index+1][1], ColourNames[self.index+1][2], ColourNames[self.index+1][3])
  end
  
  button:SetScript("OnClick", button.OnClick)
  self.childnum = self.childnum + 1
  return button
end

function KillsToLevelPref:CreateButton(text, func, object)
  local button = CreateFrame("Button", self:GetName().."_Child"..self.childnum, self, "OptionsButtonTemplate")
  button:SetText(text or "Button")
  if func then
    button.func = func
    button.object = object
    function button:OnClick()
      self.func(self.object)
    end
    button:SetScript("OnClick", button.OnClick)
  end
  self.childnum = self.childnum + 1
  return button
end

function KillsToLevelPref:CreateToggle(true_text, false_text)
  local button = CreateFrame("Button", self:GetName().."_Child"..self.childnum, self, "OptionsButtonTemplate")
  button.true_text = true_text or "True"
  button.false_text = false_text or "False"
  
  button.state = true
  button:SetText(button.true_text)
  
  function button:SetState(state)
    self.state = state
    self:SetText(state and self.true_text or self.false_text)
  end
  function button:GetState()
    return self.state
  end
  function button:OnClick()
    self:SetState(not self:GetState())
  end
  
  button:SetScript("OnClick", button.OnClick)
  self.childnum = self.childnum + 1
  return button
end

function KillsToLevelPref:CreateEditBox()
  local editbox = CreateFrame("EditBox", self:GetName().."_Child"..self.childnum, self)
  editbox:SetFont(STANDARD_TEXT_FONT, 12)
  self.childnum = self.childnum + 1
  return editbox
end

function KillsToLevelPref:CreateSlider(low, high, interval)
  if not low then low = 1 end
  if not high then high = 100 end
  if not interval then interval = 1 end
  local slider = CreateFrame("Slider", self:GetName().."_Child"..self.childnum, self, "OptionsSliderTemplate")
  
  getglobal(slider:GetName().."Text"):Hide()
  getglobal(slider:GetName().."High"):Hide()
  getglobal(slider:GetName().."Low"):Hide()
  slider:SetMinMaxValues(low, high)
  slider:SetValue(math.floor(low+high+0.5))
  slider:SetValueStep(interval)
  self.childnum = self.childnum + 1
  return slider
end

function KillsToLevelPref:OnShow()
  local k = KillsToLevelFrame
  
  self.hidden:SetState(k:GetPreference("hidden"))
  self.locked:SetState(k:GetPreference("locked"))
  self.message_format:SetText(k:GetPreference("message_format"))
  self.scale:SetValue(k:GetPreference("scale"))
  self.speed:SetValue(1/k:GetPreference("animation_speed"))
  
  self.solo:SetValue(k:GetPreference("solo_halflife"))
  self.party:SetValue(k:GetPreference("party_halflife"))
  self.raid:SetValue(k:GetPreference("raid_halflife"))
  
  self.text_colour:SetColour(k:GetPreference("text_colour"))
  self.number_colour:SetColour(k:GetPreference("number_colour"))
  self.decrease_colour:SetColour(k:GetPreference("decrease_colour"))
  self.increase_colour:SetColour(k:GetPreference("increase_colour"))
  
  local scale = 100/math.max(k:GetPreference("fallback_spread")[1],
                             k:GetPreference("fallback_spread")[2],
                             k:GetPreference("fallback_spread")[3],
                             k:GetPreference("fallback_spread")[4],
                             k:GetPreference("fallback_spread")[5])
  
  self.level_offset1:SetValue(k:GetPreference("fallback_spread")[1]*scale)
  self.level_offset2:SetValue(k:GetPreference("fallback_spread")[2]*scale)
  self.level_offset3:SetValue(k:GetPreference("fallback_spread")[3]*scale)
  self.level_offset4:SetValue(k:GetPreference("fallback_spread")[4]*scale)
  self.level_offset5:SetValue(k:GetPreference("fallback_spread")[5]*scale)
end

function KillsToLevelPref:Apply()
  if not self.settings then
    self.settings = {}
    self.settings.fallback_spread = {}
  end
  
  self.settings.hidden = self.hidden:GetState()
  self.settings.locked = self.locked:GetState()
  
  self.settings.message_format = self.message_format:GetText()
  
  self.settings.scale = self.scale:GetValue()
  self.settings.animation_speed = 1/self.speed:GetValue()
  
  self.settings.solo_halflife = self.solo:GetValue()
  self.settings.party_halflife = self.party:GetValue()
  self.settings.raid_halflife = self.raid:GetValue()
  
  self.settings.text_colour = self.text_colour:GetColour()
  self.settings.number_colour = self.number_colour:GetColour()
  self.settings.decrease_colour = self.decrease_colour:GetColour()
  self.settings.increase_colour = self.increase_colour:GetColour()
  
  self.settings.fallback_spread[1] = self.level_offset1:GetValue()
  self.settings.fallback_spread[2] = self.level_offset2:GetValue()
  self.settings.fallback_spread[3] = self.level_offset3:GetValue()
  self.settings.fallback_spread[4] = self.level_offset4:GetValue()
  self.settings.fallback_spread[5] = self.level_offset5:GetValue()
  
  KillsToLevelFrame:ApplyPreferences(self.settings)
end

local p = KillsToLevelPref
p:SetScript("OnShow", p.OnShow)

p:AddHeading("KillsToLevel Preferences", 16)

p:AddHeading("Display")
p.hidden = p:AddOption("Hidden:", p:CreateToggle("Hidden", "Shown"))
p.locked = p:AddOption("Locked:", p:CreateToggle("Locked", "Unlocked"))
p.message_format = p:AddOption("Text:", p:CreateEditBox())
p.scale = p:AddOption("Scale:", p:CreateSlider(0.5, 2.0, .1))
p.speed = p:AddOption("Speed:", p:CreateSlider(0.2, 5.0, .1))

p:AddHeading("Colours")
p.text_colour = p:AddOption("Text:", p:CreateColourEdit())
p.number_colour = p:AddOption("Number:", p:CreateColourEdit())
p.decrease_colour = p:AddOption("Shrink:", p:CreateColourEdit())
p.increase_colour = p:AddOption("Grow:", p:CreateColourEdit())

p:AddHeading("Experience Halflife")
p.solo = p:AddOption("Solo:", p:CreateSlider(30, 30*60, 30))
p.party = p:AddOption("Party:", p:CreateSlider(30, 30*60, 30))
p.raid = p:AddOption("Raid:", p:CreateSlider(30, 30*60, 30))

p:AddHeading("Fallback Monster Levels")
p.level_offset1 = p:AddOption("Party Mean -2", p:CreateSlider())
p.level_offset2 = p:AddOption("Party Mean -1", p:CreateSlider())
p.level_offset3 = p:AddOption("Party Mean +0", p:CreateSlider())
p.level_offset4 = p:AddOption("Party Mean +1", p:CreateSlider())
p.level_offset5 = p:AddOption("Party Mean +2", p:CreateSlider())

p:AddPair(p:CreateButton("Close", p.Hide, p), p:CreateButton("Apply", p.Apply, p))

p:SetHeight(-p.y+4)
p:Hide()



Titan Folder: main.lua
function TitanPanelKillsToLevelButton_OnLoad()
	this.registry = {
		id = "KillsToLevel",
		version = "1.2",
		menuText = "KillsToLevel",
		buttonTextFunction = "TitanPanelKillsToLevelButton_GetButtonText", 
		tooltipTitle = "KillsToLevel", 
		tooltipTextFunction = "TitanPanelKillsToLevelButton_GetTooltipText",
		icon = "Interface/HelpFrame/HotIssueIcon.blp",
		iconWidth = 16,
		savedVariables = {
			ShowIcon = 1,
			ShowLabelText = 1,
			ShowCounter = false,
		}
	};
end

function TitanPanelKillsToLevelButton_GetButtonText()
	return string.format(TitanGetVar("KillsToLevel", "ShowLabelText") and
	                     KillsToLevelFrame.message_format or "%s",
	                     "|cffffffff"..KillsToLevelFrame.kills_to_level.."|r")
end

function TitanPanelKillsToLevelButton_KillsChanged()
	TitanPanelButton_UpdateButton("KillsToLevel")
end

function TitanPanelKillsToLevelButton_OnShow()
	KillsToLevelFrame:RegisterCallback(TitanPanelKillsToLevelButton_KillsChanged)
	if not TitanGetVar("KillsToLevel", "ShowCounter") then
		KillsToLevelFrame:SetHideState(true)
	end
end

function TitanPanelKillsToLevelButton_OnHide()
	-- Doesn't get renabled when the button is added back to the panel, how odd. . .
	-- KillsToLevelFrame:UnregisterCallback(TitanPanelKillsToLevelButton_KillsChanged)
	KillsToLevelFrame:SetHideState(false)
end

function TitanPanelKillsToLevelButton_GetTooltipText()
	local tooltip = "Expected XP/Kill average:\t|cffffffff"..math.floor(KillsToLevelFrame:AverageExperience()+0.5).."|r"
	
	if KillsToLevelFrame.rested_experience > 0 then
		tooltip = tooltip .. "\nExpected kills until not rested:\t|cffffffff"..
		          KillsToLevelFrame:KillsNeeded(nil, KillsToLevelFrame.rested_experience).."|r"
	end
	
	local count_error = KillsToLevelFrame.kills_to_level-KillsToLevelFrame:KillsNeeded()
	if count_error > 0 then
		tooltip = tooltip .. "\nExpected estimate error:\t|cff00ff00+"..count_error.."|r"
	elseif count_error < 0 then
		tooltip = tooltip .. "\nExpected estimate error:\t|cffff0000"..count_error.."|r"
	end
	
	return tooltip
end

function TitanPanelKillsToLevelButton_ToggleShowCounter()
	if TitanGetVar("KillsToLevel", "ShowCounter") then
		TitanSetVar("KillsToLevel", "ShowCounter", false)
		KillsToLevelFrame:SetHideState(true)
	else
		TitanSetVar("KillsToLevel", "ShowCounter", true)
		KillsToLevelFrame:SetHideState(false)
	end
	TitanPanelButton_UpdateButton("KillsToLevel");
end

function TitanPanelRightClickMenu_PrepareKillsToLevelMenu()
	UIDropDownMenu_AddButton({text="Show Counter",
	                          func=TitanPanelKillsToLevelButton_ToggleShowCounter,
	                          checked=TitanGetVar("KillsToLevel", "ShowCounter")})
	
	TitanPanelRightClickMenu_AddSpacer();
	TitanPanelRightClickMenu_AddToggleIcon("KillsToLevel");
	TitanPanelRightClickMenu_AddToggleLabelText("KillsToLevel");
	
	TitanPanelRightClickMenu_AddSpacer();
	TitanPanelRightClickMenu_AddCommand(TITAN_PANEL_MENU_HIDE, "KillsToLevel", TITAN_PANEL_MENU_FUNC_HIDE);
end



TitanKillsToLevel.toc
## Interface: 20300
## Title: Titan Panel [KillsToLevel]
## Notes: Adds an estimate for the number of kills until you next level to Titan Panel
## Dependencies: Titan KillsToLevel
## Version: 1.4
waffles.xml



waffles.xml
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
	<Script file="main.lua"/>
	<Frame parent="UIParent">
		<Frames>
			<Button name="TitanPanelKillsToLevelButton" inherits="TitanPanelComboTemplate" frameStrata="FULLSCREEN" toplevel="true">
				<Scripts>
					<OnLoad>
						TitanPanelKillsToLevelButton_OnLoad()
						TitanPanelButton_OnLoad()
					</OnLoad>
					<OnShow>
						TitanPanelKillsToLevelButton_OnShow()
						TitanPanelButton_OnShow()
					</OnShow>
					<OnHide>
						TitanPanelKillsToLevelButton_OnHide()
					</OnHide>
				</Scripts>
			</Button>
		</Frames>
	</Frame>
</Ui>



Like I said I'm way over my head, I planned on learning lua, but I'm not anywhere near capable of handeling this yet. Thank you to anyone who can help. [Edited by - Straconis on October 22, 2008 11:48:22 AM]

Share this post


Link to post
Share on other sites
Kylotan    9860
Help with what? I don't believe you asked a question or stated what your problem was. If you expect people to run it and test it themselves, you may be out of luck. It's usually better to state what exactly goes wrong and say what you expected to see instead. There are plenty of people here who use Lua, but I don't know if any of them are working on WoW mods, so the specific knowledge you want might be better found on a WoW forum. I don't know.

Share this post


Link to post
Share on other sites
Straconis    122
Sorry, must have had a brain fart I thought I put in the problem. The mod has 2 problems one with the mod iteslf one with the titan plug in.

Issue 1: After the patch the main part of the mod would open the preferences window when you log in and does not allow you to close the preferences window.

Issue 2: The Titan plug in doesn't show up in the list of Titan Plug ins on Titan Panel to open it. If it has any issues beyond there I'm not sure since I haven't gotten it to even list itself for display.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this