-- Copyright 2008 James Whitehead II -- Credits for name 'DressToKill' go to Aska on StormReaver(EU) DressToKill = {} local L = DressToKillLocals local DEBUG = false local function debug(fmt, ...) local msg = "|cffffff78DressToKill:|r " if select("#", ...) > 0 then local succ,err = pcall(string.format, fmt, ...) if not succ then error(debugstack()) else msg = msg .. err end else msg = msg .. fmt end if DEBUG then ChatFrame1:AddMessage(msg) end DressToKillDebugScrollFrame:AddMessage(msg) end function DressToKill:SetDebug(value) DEBUG = value end local function print(fmt, ...) local msg = "|cffffff78DressToKill:|r " if select("#", ...) > 0 then msg = msg .. fmt:format(...) else msg = msg .. fmt end ChatFrame1:AddMessage(msg) end local slotNames = { BackSlot = L["Back"], ChestSlot = L["Chest"], FeetSlot = L["Feet"], Finger0Slot = L["Left Finger"], Finger1Slot = L["Right Finger"], HeadSlot = L["Head"], HandsSlot = L["Hands"], LegsSlot = L["Legs"], NeckSlot = L["Neck Slot"], RangedSlot = L["Ranged Slot"], ShoulderSlot = L["Shoulders"], Trinket0Slot = L["Trinket 0"], Trinket1Slot = L["Trinket 1"], WaistSlot = L["Waist"], WristSlot = L["Wrist"], --MainHandSlot = L["Main Hand"], --SecondaryHandSlot = L["Secondary Hand"], } local slotIds = {} function DressToKill:Initialize() -- Only run this code once if self.initDone then return end self.initDone = true for slot in pairs(slotNames) do local id = GetInventorySlotInfo(slot) if id then slotIds[slot] = id else local err = string.format(L["Could not get inventory slot information for %s"], tostring(slot)) error(err) return end end DressToKillDB = DressToKillDB or {} self.profile = DressToKillDB self.profile.weightFuncs = self.profile.weightFuncs or { ["Healing/Mana"] = {handler = [[local healing = GetSpellBonusHealing() local mana = UnitManaMax("player") return (healing * 1.0) + (mana * 0.7)]]}, ["Health/Armor"] = {handler = [[local health = UnitHealthMax("player") local base, pos, neg = UnitArmor("player") local armor = base + pos - neg return (health * 1.0) + (armor * 0.8)]]}, ["Attack Power"] = {handler = [[local base, pos, neg = UnitAttackPower("player") local apower = base + pos - neg return apower]]}, } end function DressToKill:FindEmptySlot() for bag=0,4 do for slot=1,GetContainerNumSlots(bag) do if not GetContainerItemInfo(bag, slot) then return bag, slot end end end end local function scanFunction(weightFunction) debug(L["********** Beginning an inventory scan **********"]) UIErrorsFrame:AddMessage("Please do not change buffs or forms during scan...", 1, 0.2, 0.2) local blacklist = {} local slotAvail = {} for slotName,slotId in pairs(slotIds) do debug(L["Collecting available items for %s"], slotName) local avail = GetInventoryItemsForSlot(slotId) if next(avail) then local c = 0 for k,v in pairs(avail) do c = c + 1 end debug(L["Found %d available items"], c) slotAvail[slotId] = avail else debug(L["Found no available items"]) end end -- Let's handle the weapons, since they're somewhat complicated local mainslot = GetInventorySlotInfo("MainHandSlot") local offslot = GetInventorySlotInfo("SecondaryHandSlot") -- Unequip both weapon slots local mh_link = GetInventoryItemLink("player", mainslot) local oh_link = GetInventoryItemLink("player", offslot) debug(L["Mainhand link: %s"], tostring(mh_link)) debug(L["Offhand link: %s"], tostring(oh_link)) if mh_link then debug(L["Found a mainhand weapon: %s"], mh_link) local mh_bag, mh_slot = DressToKill:FindEmptySlot() if not mh_bag then print(L["Out of inventory space, cannot proceed"]) return end debug(L["Unequipping the mainhand weapon"]) PickupInventoryItem(mainslot) PickupContainerItem(mh_bag, mh_slot) coroutine.yield() end if oh_link then debug(L["Found an offhand weapon: %s"], oh_link) local oh_bag, oh_slot = DressToKill:FindEmptySlot() if not oh_bag then print(L["Out of inventory space, cannot proceed"]) return end debug(L["Unequipping the offhand weapon"]) PickupInventoryItem(offslot) PickupContainerItem(oh_bag, oh_slot) coroutine.yield() end local mh_avail = GetInventoryItemsForSlot(mainslot) local oh_avail = GetInventoryItemsForSlot(offslot) -- Calculate weapon base score with nothing equipped local linen_shirt = "\124cffffffff\124Hitem:2576:0:0:0:0:0:0:0\124h[White Linen Shirt]\124h\124r" local weapon_max, weapon_win = (- math.huge), {} -- Try on each mainhand weapon local score for mh_mask,item in pairs(mh_avail) do local name, link = GetItemInfo(item) debug(L["Equipping mainhand weapon: %s"], link) local mh_equipped = DressToKill:EquipItem(mainslot, mh_mask) blacklist[mh_mask] = true if mh_equipped then debug(L["Successfully equipped %s"], link) local mh_link = GetInventoryItemLink("player", mainslot) local mh_score = weightFunction(mh_link, mainslot) debug(L["Mainhand score: %d"], mh_score) if not IsEquippedItemType("Two-Hand") then debug(L["This is a one hand weapon, try offhand weapons"]) -- Try to equip each off-hand weapon to compliment this one for oh_mask,item in pairs(oh_avail) do if not blacklist[oh_mask] then score = mh_score local name, link = GetItemInfo(item) debug(L["Equipping offhand weapon: %s"], link) local oh_equipped = DressToKill:EquipItem(offslot, oh_mask) -- If we equipped offhand, then score it too if oh_equipped then local oh_link = GetInventoryItemLink("player", offslot) or linen_shirt local oh_score = weightFunction(oh_link, offslot) score = oh_score debug(L["Got score of %s for %s/%s"], score, mh_link, oh_link) else debug(L["Failed to equip %s"], link) end if score >= weapon_max then weapon_max = score weapon_win.mh = mh_mask weapon_win.oh = oh_equipped and oh_mask or nil end -- Unequip the offhand item if oh_equipped then debug(L["Unequipping %s"], link) DressToKill:UnequipItem(offslot, oh_mask, oh_stash) end end end else score = mh_score debug("Got score of %s for %s", score, mh_link) if score >= weapon_max then weapon_max = score weapon_win.mh = mh_mask weapon_win.oh = nil end end -- Unequip this mainhand item DressToKill:UnequipItem(mainslot, mh_mask, mh_stash) blacklist[mh_mask] = false end end -- Equip the weapon winners (MAINHAND) DressToKill:EquipItem(mainslot, weapon_win.mh, mh_stash) local link = GetInventoryItemLink("player", mainslot) print("Choosing %s", link) debug("Choosing %s", link) blacklist[weapon_win.mh] = true -- Equip the offhand winner, if there was one if weapon_win.oh then DressToKill:EquipItem(offslot, weapon_win.oh, oh_stash) local link = GetInventoryItemLink("player", offslot) print("Choosing %s", link) blacklist[weapon_win.oh] = true end local stash = {} -- Loop through all of the inventory slots for slotId,avail in pairs(slotAvail) do local maxScore, winner = (- math.huge), nil local link = GetInventoryItemLink("player", slotId) if link then -- We current have an item equipped, so stash that item elsewhere local bag,slot = DressToKill:FindEmptySlot() if not bag or not slot then print(L["Out of inventory space, cannot proceed"]) return end stash.bag = bag stash.slot = slot PickupInventoryItem(slotId) PickupContainerItem(bag, slot) coroutine.yield() end -- Get the base score for this slot, so we can normalize our scores local linen_shirt = "\124cffffffff\124Hitem:2576:0:0:0:0:0:0:0\124h[White Linen Shirt]\124h\124r" local base = weightFunction(linen_shirt, slotId) or 0 -- Loop through all of the available items, and try equipping each of them for mask,item in pairs(avail) do if not blacklist[mask] then local name,link = GetItemInfo(item) debug(L["Trying on %s"], link) local equipped = DressToKill:EquipItem(slotId, mask, stash) if equipped then local link = GetInventoryItemLink("player", slotId) local score = weightFunction(link, slotId) - base debug("Got score of %s for %s", score, link) if score >= maxScore then maxScore = score winner = mask end -- Unequip the item DressToKill:UnequipItem(slotId, mask, stash) else debug(L["Item not equipped... skipping..."]) end else debug(L["Item blacklisted, skipping..."]) end end -- Now equip the item that won this round and blacklist assuming there was a winner if winner then DressToKill:EquipItem(slotId, winner, stash) local link = GetInventoryItemLink("player", slotId) debug(L["Choosing %s for this slot"], link) print(L["Choosing %s"], link) blacklist[winner] = true else debug(L["There was no winner from this round, not enough trinkets or rings?"]) end end UIErrorsFrame:AddMessage("Scan complete!", 0.2, 1, 0.2) print("Evaluation complete!") debug(L["Evaluation complete!"]) end function DressToKill:EquipItem(slotId, mask, stash) local bag,slot = DressToKill:ItemInBag(mask) local eqslot = DressToKill:ItemEquipped(mask) if bag then -- Equip the item PickupContainerItem(bag, slot) EquipCursorItem(slotId) local equipped = coroutine.yield() return equipped elseif eqslot then -- The item was equipped, so find the current slot in the stash PickupContainerItem(stash.bag, stash.slot) EquipCursorItem(slotId) local equipped = coroutine.yield() return equipped end end function DressToKill:UnequipItem(slotId, mask, stash) local bag,slot = DressToKill:ItemInBag(mask) local eqslot = DressToKill:ItemEquipped(mask) if bag then -- Just put the item back PickupInventoryItem(slotId) PickupContainerItem(bag, slot) coroutine.yield() elseif eqslot then -- Put it back into the stash slot PickupInventoryItem(slotId) PickupContainerItem(stash.bag, stash.slot) coroutine.yield() end end local eventFrame = CreateFrame("Frame") eventFrame:RegisterEvent("UNIT_INVENTORY_CHANGED") eventFrame:RegisterEvent("EQUIP_BIND_CONFIRM") eventFrame:RegisterEvent("UI_ERROR_MESSAGE") --local LEVEL_LOW = LEVEL_TOO_LOW:gsub("%%d", "%%d%+"):gsub("%.", "%%.") local function OnEvent(self, event, arg1) self.equipped = true local thread = DressToKill.currentThread if thread and coroutine.status(thread) ~= "dead" then if event == "EQUIP_BIND_CONFIRM" and CursorHasItem() then self.equipped = false debug(L["Clearing non-soulbound item"]) ClearCursor() elseif event == "UI_ERROR_MESSAGE" and arg1 == ERR_PROFICIENCY_NEEDED then self.equipped = false debug(L["Clearing item we don't have the proficiency for"]) ClearCursor() elseif event == "UI_ERROR_MESSAGE" and arg1 == ERR_2HSKILLNOTFOUND then self.equipped = false debug(L["Clearing item since we lack dual wielding skill"]) ClearCursor() elseif event == "UI_ERROR_MESSAGE" and arg1 == ERR_CANT_EQUIP_SKILL then self.equipped = false debug(L["Clearing item since we lack the skill to equip it"]) ClearCursor() elseif event == "UI_ERROR_MESSAGE" then self.equipped = false debug(L["Clearing item due to API saying it is valid for an invalid slot"]) ClearCursor() elseif event == "UI_ERROR_MESSAGE" then debug(L["Got an UI error message: %s"], arg1) end self:Show() end end local function OnUpdate(self, elapsed) self:Hide() local succ,err = coroutine.resume(DressToKill.currentThread, self.equipped) assert(succ, err) end eventFrame:SetScript("OnEvent", OnEvent) eventFrame:SetScript("OnUpdate", OnUpdate) eventFrame:Hide() SLASH_DRESSTOKILL1 = "/dtk" SLASH_DRESSTOKILL2 = "/dress" SLASH_DRESSTOKILL3 = "/dresstokill" SlashCmdList["DRESSTOKILL"] = function(msg, editbox) DressToKill:Initialize() if msg and msg:lower():match("%s*config%s*") then InterfaceOptionsFrame_OpenToCategory(DressToKillOptionsFrame) return end if msg and msg:lower():match("%s*debug%s*") then InterfaceOptionsFrame_OpenToCategory(DressToKillDebugFrame) return end -- Check to see if a function was specified on the commandline local weightName = msg:match("^%s*(.+)$") if not weightName then if DressToKill.profile.selected then weightName = DressToKill.profile.selected else print(L["You don't have a default weight function selected"]) return end end -- Make sure the function actually exist if not DressToKill.profile.weightFuncs[weightName] then print(L["The weight function '%s' doesn't exist"], weightName) return end -- Compile the function local source = DressToKill.profile.weightFuncs[weightName].handler local newsrc = string.format("return function (link, slot) %s end", source) local weightFunction,err = loadstring(newsrc) if not weightFunction then print("Failed to compile weight function: " .. tostring(err)) return else local succ,err = pcall(weightFunction) if not succ then print("Failed when running weight function: " .. tostring(err)) return else weightFunction = err end end print(L["Dressing to kill with %s..."], weightName) DressToKill.currentThread = coroutine.create(scanFunction) coroutine.resume(DressToKill.currentThread, weightFunction) end local ITEM_INVENTORY_PLAYER = 0x00100000 local ITEM_INVENTORY_BACKPACK = 0x00200000 local ITEM_INVENTORY_BAGS = 0x00400000 local ITEM_INVENTORY_BANK = 0x00800000 local MASK_BAG = 0xf00 local MASK_SLOT = 0x3f local bagMap = { [0x100] = 1, [0x200] = 2, [0x400] = 3, [0x800] = 4, } function DressToKill:ItemInBank(mask) if bit.band(mask, ITEM_INVENTORY_BANK) > 0 then end end function DressToKill:ItemInBag(mask) if bit.band(mask, ITEM_INVENTORY_BAGS) > 0 then local bag = bagMap[bit.band(mask, MASK_BAG)] local slot = bit.band(mask, MASK_SLOT) return bag, slot elseif bit.band(mask, ITEM_INVENTORY_BACKPACK) > 0 then local slot = bit.band(mask, MASK_SLOT) return 0, slot end end function DressToKill:ItemEquipped(mask) if bit.band(mask, ITEM_INVENTORY_PLAYER) > 0 then local slot = bit.band(mask, MASK_SLOT) return slot end end --[[ ITEM_INVENTORY_LOCATION_PLAYER = 0x00100000 ITEM_INVENTORY_LOCATION_BACKPACK = 0x00200000 ITEM_INVENTORY_LOCATION_BAGS = 0x00400000 ITEM_INVENTORY_LOCATION_BANK = 0x00800000 -- -- With four netherweave bags: -- In backpack, slots 1 -> 16 -- -- 001100000000000000000001 -- 001100000000000000000010 -- ... -- 001100000000000000010000 -- -- First bag, slots 1 -> 16 -- -- 010000000000000100000001 -- ... -- 010000000000000100010000 -- -- Second bag, slots 1 -> 16 -- -- 010000000000001000000001 -- ... -- 010000000000001000010000 -- -- Third bag, slots 1 -> 16 -- -- 010000000000010000000001 -- ... -- 010000000000010000010000 -- -- Fourth bag, slots 1 -> 16 -- -- 010000000000100000000001 -- ... -- 010000000000100000010000 -- -- Hands(10) -- 000100000000000000001010 -- -- Waist(6) -- 000100000000000000000110 -- -- Finger0(11) -- 000100000000000000001011 -- -- Finger1(12) -- 000100000000000000001011 --]]