-- 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, ...) if DEBUG then local msg = "|cffffff78DressToKill:|r " if select("#", ...) > 0 then msg = msg .. fmt:format(...) else msg = msg .. fmt end ChatFrame1:AddMessage(msg) end 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 local avail = GetInventoryItemsForSlot(slotId) if next(avail) then slotAvail[slotId] = avail end end -- Let's handle the weapons, since they're somewhat complicated local mh_stash, oh_stash = {}, {} local mainslot, offslot = slotIds["MainHandSlot"], slotIds["SecondaryHandSlot"] -- Unequip both weapon slots local mh_link = GetInventoryItemLink("player", mainslot) local oh_link = GetInventoryItemLink("player", offslot) if mh_link then local mh_bag, mh_slot = DressToKill:FindEmptySlot() if not mh_bag then print(L["Out of inventory space, cannot proceed"]) return end mh_stash.bag = mh_bag mh_stash.slot = mh_slot end if oh_link then local oh_bag, oh_slot = DressToKill:FindEmptySlot() if not oh_bag then print(L["Out of inventory space, cannot proceed"]) return end oh_stash.bag = oh_bag oh_stash.slot = oh_slot end -- 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_base = weightFunction(linen_shirt, mainslot) + weightFunction(linen_shirt, offslot) local weapon_max, weapon_win = 0, {} -- Try on each mainhand weapon for mh_mask,item in pairs(slotAvail[mainslot]) do local mh_equipped = DressToKill:EquipItem(mainslot, mh_mask, mh_stash) blacklist[mh_mask] = true if mh_equipped then local mh_link = GetInventoryItemLink("player", mainslot) local mh_score = weightFunction(mh_link, mainslot) - weapon_base local score = mh_score if not IsEquippedItemType("Two-Hand") then -- Try to equip each off-hand weapon to compliment this one for oh_mask,item in pairs(slotAvail[offslot]) do local oh_equipped = DressToKill:EquipItem(offslot, oh_mask, oh_stash) -- 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) - weapon_base score = score + oh_score end debug("Got score of %s for %s and %s", score, mh_link, oh_link or "empty") 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 DressToKill:UnequipItem(offslot, oh_mask, oh_stash) end end else 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) blacklist[weapon_win.mh] = true -- Equip the offhand winner, if there was one if weapon_min.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 = 0, 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 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 DressToKill:EquipItem(slotId, winner, stash) local link = GetInventoryItemLink("player", slotId) print("Choosing %s", link) blacklist[winner] = true end UIErrorsFrame:AddMessage("Scan complete!", 0.2, 1, 0.2) print("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 function OnEvent(self, event, arg1) self.equipped = true 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() end local thread = DressToKill.currentThread if thread and coroutine.status(thread) ~= "dead" then 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_OpenToFrame(DressToKillOptionsFrame) 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: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 --]]