-- StatRating: Better Equipment Swapper V0.1 -- Please accept this reccomendation to use the srMerchant function at the -- end of this file. Damaged gear has skewed stats so you'll want to repair -- before doing a full swap. Using the normal player:merchant function is -- dangerous as, depending on where the code is placed, it is possible to -- have the merchant window open while executing swap. This will sell the -- "better" gear instead of wearing it. -- If you are using this after collecting a quest item or weapon you are -- expecting to be better, or just not spending coin on repairs then -- strategically placed srCheckSwap's are recommended. -- Due to the lack of memory scans for the information and lack of optimization -- in this userfunction, I will suggest to avoid using this in 'per fight' -- events. At the moment, only full scans and swaps are supported. As a result -- the overall functionality is relatively slow. Using it too often will -- severely hinder bot performance. local DEBUGSR = false; -- Works with in-game part to gather tooltip info for bag items function CItem:botGetTooltip() local DEBUGTT = false; local tmpTip = { RoMScript("igf_GetTooltip('Left',"..self.BagId..")") }; local tmpInfo = {}; tmpInfo["Left"] = {}; tmpInfo["Right"] = {}; for i=1,40,1 do if tmpTip[i] then tmpInfo.Left[i] = tmpTip[i]:gsub("|c%w%w%w%w%w%w%w%w", ""); tmpInfo.Left[i] = tmpInfo.Left[i]:gsub("\n"," "); tmpInfo.Left[i] = tmpInfo.Left[i]:gsub("|r",""); end end tmpTip = { RoMScript("igf_GetTooltip('Right',"..self.BagId..")") } for i=1,40,1 do if tmpTip[i] then tmpInfo.Right[i] = tmpTip[i]:gsub("|c%w%w%w%w%w%w%w%w", ""); tmpInfo.Right[i] = tmpInfo.Right[i]:gsub("\n"," "); tmpInfo.Right[i] = tmpInfo.Right[i]:gsub("|r",""); end end if DEBUGTT then for i=1,40,1 do if tmpTip[i] then cprintf(cli.yellow, "ItemInfo: %s\t%s\n", tmpInfo.Left[i],tmpInfo.Right[i]); end end end return tmpInfo; end -- Works with in-game part to gather tooltip info for equipped items function CEquipItem:botEquipTooltip() local DEBUGTT = false; local tmpTip = { RoMScript("igf_EquipTooltip('Left',"..self.Slot..")") }; local tmpInfo = {}; tmpInfo["Left"] = {}; tmpInfo["Right"] = {}; for i=1,40,1 do if tmpTip[i] then tmpInfo["Left"][i] = tmpTip[i]:gsub("|c%w%w%w%w%w%w%w%w", ""); tmpInfo["Left"][i] = tmpInfo["Left"][i]:gsub("\n"," "); tmpInfo["Left"][i] = tmpInfo["Left"][i]:gsub("|r",""); end end tmpTip = { RoMScript("igf_EquipTooltip('Right',"..self.Slot..")") } for i=1,40,1 do if tmpTip[i] then tmpInfo["Right"][i] = tmpTip[i]:gsub("|c%w%w%w%w%w%w%w%w", ""); tmpInfo["Right"][i] = tmpInfo["Right"][i]:gsub("\n"," "); tmpInfo["Right"][i] = tmpInfo["Right"][i]:gsub("|r",""); end end if DEBUGTT then for i=1,40,1 do if tmpTip[i] then cprintf(cli.yellow, "ItemInfo: %s\t%s\n", tmpInfo["Left"][i],tmpInfo["Right"][i]); end end end return tmpInfo; end -- Checks for and swaps for better items function srCheckSwap() player:update(); inventory:update(); inventory:updateEquipment(); local equipItemInfo = {}; local equipItemData = {}; if DEBUGSR then print("Collecting equipment slot data."); end for i=1,22,1 do local eqslotitem = inventory.EquipSlots[i]; if not eqslotitem.Empty then equipItemInfo = eqslotitem:botEquipTooltip(); equipItemData[i] = srSetInfo(equipItemInfo); equipItemData[i].Empty = false; -- equipItemData[i].Bound = eqslotitem.Bound; -- equipItemData[i].Worth = eqslotitem.Worth; -- equipItemData[i].RequiredLvl = eqslotitem.RequiredLvl; else equipItemData[i].Empty = true; end if DEBUGSR then print("Equipment slot " .. i .. " info collected."); end end for slotNumber = 51, settings.profile.options.INV_MAX_SLOTS + 60, 1 do local slotitem = inventory.BagSlot[slotNumber]; local tmpItemInfo = {}; local tmpItemData = {}; if DEBUGSR then print("Collecting bag slot " .. slotNumber .. " data."); end if( slotitem and tonumber(slotitem.Id) > 0 and slotitem.Available) then tmpItemInfo = slotitem:botGetTooltip(); tmpItemData = srSetInfo(tmpItemInfo); -- tmpItemData.Bound = slotitem.Bound; -- tmpItemData.Worth = slotitem.Worth; -- tmpItemData.RequiredLvl = slotitem.RequiredLvl; if tmpItemData.Slot ~= 0 then if srCompare(tmpItemData,equipItemData) then cprintf(cli.yellow,"Wearing %s.",slotitem.Name); slotitem:use(); if tmpItemData.Bound == 3 then RoMScript("StaticPopup_EnterPressed(StaticPopup1);StaticPopup1:Hide();"); RoMScript("StaticPopup_EnterPressed(StaticPopup2);StaticPopup2:Hide();"); if DEBUGSR then print("Binding equipment."); end end yrest(500); inventory:updateEquipment(); local eqsnum = tmpItemData.Slot; local eqsitem = inventory.EquipSlots[eqsnum]; equipItemInfo = eqsitem:botEquipTooltip(); equipItemData[eqsitem] = srSetInfo(equipItemInfo); -- equipItemData[eqsitem].Bound = eqsitem.BoundStatus; -- equipItemData[eqsitem].Worth = eqsitem.Worth; -- equipItemData[eqsitem].RequiredLvl = eqsitem.RequiredLvl; end end end if DEBUGSR then print("Bag slot " .. slotNumber .. " data collected."); end end end -- Is it better or not? function srCompare(itemData,eqItemData) local tClass = player.Class1; local srChar = 0 ; local srArmorType = 0; local tmpLevel = player.Level; local srSlot = itemData.Slot if DEBUGSR then print("Checking ".. itemData.Name .."."); end if tClass == 4 or tClass == 5 or tClass == 8 then srChar = 1; -- cloth elseif tClass == 2 or tClass == 3 then srChar = 2; -- leather elseif tClass == 1 or tClass == 7 then srChar = 3; -- chain elseif tClass == 6 then srChar = 4; -- plate end local function magicNumber() local subStatS = 0; if itemData.WpArm == "Weapon" then subStatS = (itemData.PDefDiff) + (itemData.MADiff)*6 + ((itemData.MDmg) - (eqItemData[srSlot].MDmg))*12; end if itemData.WpArm == "Armor" then subStatS = (itemData.PDefDiff) + (itemData.MADiff)*6 + (itemData.HPDiff)/2 + (itemData.MPDiff)/5; end if DEBUGSR then print("magicNumber() check returning number: " .. subStatS .. "for " .. itemData.Name .. "\n"); end; return subStatS; end local function weaponNumber() local subStatS = 0; if (itemData.Type == "Dagger") then if not (tClass == 5 or tClass == 6 or tClass == 8 or tClass == 4) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end if (itemData.Type == "Sword")then if not (tClass == 4 or tClass == 5 or tClass == 8) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end if (itemData.Type == "2-HSword") then if not (tClass == 1 or tClass == 6 or tClass == 7) then subStatS = -1; else if eqItemData[17].Empty then subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; else subStatS = -1; end end end if (itemData.Type == "Axe") then if not (tClass == 1 or tClass == 7) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end if (itemData.Type == "2-HAxe") then if not (tClass == 1 or tClass == 7) then subStatS = -1; else if eqItemData[17].Empty then subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; else subStatS = -1; end end end if (itemData.Type == "1-HHammer") then if not (tClass == 1 or tClass == 6 or tClass == 7) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end if (itemData.Type == "2-HHammer") then if not (tClass == 1 or tClass == 6 or tClass == 7) then subStatS = -1; else if eqItemData[17].Empty then subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; else subStatS = -1; end end end if (itemData.Type == "Bow") then if not (tClass == 2) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end if (itemData.Type == "Crossbow") then if not (tClass == 2) then subStatS = -1; else subStatS = (itemData.PDefDiff) + (itemData.PADiff)*6 + (itemData.DPSDiff)*12; end end -- Bot already auto-decides ammo if (itemData.Type == "Ammunition") then subStatS = -1; end if (itemData.Type == "Projectiles") then subStatS = -1; end if DEBUGSR then print("weaponNumber() check returning number: " .. subStatS .. "for " .. itemData.Name .. "\n"); end return subStatS; end local function armorNumber() local subStatS = 0; subStatS = ((itemData.PDefDiff) + (itemData.PADiff) * 3 + (itemData.HPDiff)/2 + (itemData.MPDiff)/5); if DEBUGSR then print("armorNumber() check returning number: " .. subStatS .. "\n"); end; return subStatS; end if tmpLevel < itemData.RequiredLvl then if DEBUGSR then print("Level requirement check failed."); end return false; end if inventory.EquipSlots[srSlot].Empty then if srSlot == 10 and not (tClass == 2 or tClass == 3) then return false; elseif srSlot ~= 10 then if DEBUGSR then print("Empty equipment slot check passed."); end return true; elseif srSlot == 10 and tClass == 2 and itemData.Type == "Arrow" then if DEBUGSR then print("Empty equipment slot check passed."); end return true; elseif srSlot == 10 and tClass == 3 and itemData.Type == "Projectiles" then if DEBUGSR then print("Empty equipment slot check passed."); end return true; end elseif srSlot == 12 and inventory.EquipSlots[13].Empty then if DEBUGSR then print("Empty second ring slot check passed."); end return true; elseif srSlot == 14 and inventory.EquipSlots[15].Empty then if DEBUGSR then print("Empty second earring slot check passed."); end return true; end if itemData.Type == "Accessory" then srArmorType = 0; elseif itemData.Type == "Cloth" then if ((tClass == 4) or (tClass == 5) or (tClass == 7)) and (magicNumber() > 0) then if DEBUGSR then print("Cloth magicNumber() check passed."); end return true; elseif ((tClass == 4) or (tClass == 5) or (tClass == 7)) then if DEBUGSR then print("Cloth magicNumber() check failed."); end return false; end srArmorType = 1; elseif itemData.Type == "Leather" then srArmorType = 2; elseif itemData.Type == "Chain" then srArmorType = 3; elseif itemData.Type == "Plate" then srArmorType = 4; end if itemData.WpArm == "Armor" then if (armorNumber() > 0) and (srChar >= srArmorType) then if DEBUGSR then print("armorNumber() srArmorType check passed."); end return true; end end if itemData.Type == "Wand" then if magicNumber() > 0 then return true; end; elseif itemData.Type == "Staff" then if eqItemData[17].Empty then if magicNumber() > 0 then return true; end; else return false; end elseif itemData.WpArm == "Weapon" then if weaponNumber() > 0 then return true; end; end return false; end -- Set the item info into tables function srSetInfo(itemInfo) local itemData = { Name = itemInfo.Left[1], Tier = 0, RequiredLvl = 0, Bound = 0, Type = "", Slot = 0, WpArm = "", MDmg = 0, Worth = 0, Heal = 0, PADiff = 0, MADiff = 0, PDefDiff = 0, MDefDiff = 0, DPSDiff = 0, HPDiff = 0, MPDiff = 0 } for i=2,40,1 do if itemInfo.Left[i] then --Set Tier if itemInfo.Left[i]:find("Tier") then itemData["Tier"] = tonumber(itemInfo.Left[i]:sub(5)); end; --Set Bound if itemInfo.Left[i]:find("Bound") then itemData["Bound"] = 0 elseif itemInfo.Left[i]:find("Item Shop Item") then itemData["Bound"] = 2 elseif itemInfo.Left[i]:find("Binds when equipped") then itemData["Bound"] = 3 end --Set Required Level if itemInfo.Left[i]:find("Requires Level %d+") then itemData.RequiredLvl = tonumber(itemInfo.Left[i]:match("%d+")); end --Set Slot and Type if itemInfo.Right[i]:find("Main hand") then itemData["Slot"] = 16; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Weapon"; end if itemInfo.Right[i]:find("1-H") then itemData["Slot"] = 16; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Weapon"; end if itemInfo.Right[i]:find("2-H") then itemData["Slot"] = 16; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Weapon"; end if itemInfo.Right[i]:find("Ranged Weapon") then itemData["Slot"] = 11; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Weapon"; end if itemInfo.Right[i]:find("Ammunition") then itemData["Slot"] = 10; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Weapon"; end if itemInfo.Right[i]:find("Upper Body") then itemData["Slot"] = 4; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Lower Body") then itemData["Slot"] = 5; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Feet") then itemData["Slot"] = 3; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Belt") then itemData["Slot"] = 7; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Off-hand") then itemData["Slot"] = 17; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Cape") then itemData["Slot"] = 6; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Hands") then itemData["Slot"] = 2; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Head") then itemData["Slot"] = 1; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Shoulders") then itemData["Slot"] = 8; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Back") then itemData["Slot"] = 22; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Necklace") then itemData["Slot"] = 9; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Ring") then itemData["Slot"] = 12; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Earring") then itemData["Slot"] = 14; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end if itemInfo.Right[i]:find("Amulet") then itemData["Slot"] = 18; itemData["Type"] = itemInfo.Left[i]:gsub("%s",""); itemData["WpArm"] = "Armor"; end --Set MDmg if itemInfo.Left[i]:find("Magical Damage ") then itemData["Mdmg"] = tonumber(itemInfo.Left[i]:match("%s%d+%s")); end --Set Worth --if itemInfo.Left[i]:find("Worth%:%s%d+%sGold") then itemData["Worth"] = tonumber(itemInfo.Left[i]:match("%d+")); end; --From here down is Statrating info --Set Heal if itemInfo.Left[i]:find("%d+%.?%d-%sheal") then itemData["Heal"] = tonumber(srRemWild(string.gsub(itemInfo.Left[i]:match("%d+%.?%d-%sheal"),"heal",""))); end; --Set PADiff if itemInfo.Left[i]:find("Physical Attack%:") then if itemInfo.Right[i]:find("%(") then itemData["PADiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["PADiff"] = tonumber(itemInfo.Right[i]); end end --Set MADiff if itemInfo.Left[i]:find("Magical Attack%:") then if itemInfo.Right[i]:find("%(") then itemData["MADiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["MADiff"] = tonumber(itemInfo.Right[i]); end end --Set PDefDiff if itemInfo.Left[i]:find("Physical Defense%:") then if itemInfo.Right[i]:find("%(") then itemData["PDefDiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["PDefDiff"] = tonumber(itemInfo.Right[i]); end end --Set MDefDiff if itemInfo.Left[i]:find("Magical Defense%:") then if itemInfo.Right[i]:find("%(") then itemData["MDefDiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["MDefDiff"] = tonumber(itemInfo.Right[i]); end end --Set DPSDiff if itemInfo.Left[i]:find("DPS%:") then if itemInfo.Right[i]:find("%(") then itemData["DPSDiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["DPSDiff"] = tonumber(itemInfo.Right[i]); end end --Set HPDiff if itemInfo.Left[i]:find("Health points%:") then if itemInfo.Right[i]:find("%(") then itemData["HPDiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["HPDiff"] = tonumber(itemInfo.Right[i]); end end --Set MPDiff if itemInfo.Left[i]:find("Mana points%:") then if itemInfo.Right[i]:find("%(") then itemData["MPDiff"] = srRemWild(itemInfo.Right[i]:match("%(%s%+?%-?%s%d+%.?%d-%)")); else itemData["MPDiff"] = tonumber(itemInfo.Right[i]); end end end end return itemData; end -- Remove unwanted characters and return the right sign? function srRemWild(tmpInfo) local tmpSign = 0 if tmpInfo:find("%(") then tmpInfo = tmpInfo:gsub("%(",""); end if tmpInfo:find("%)") then tmpInfo = tmpInfo:gsub("%)",""); end if tmpInfo:find("%+") then tmpInfo = tmpInfo:gsub("%+",""); tmpSign = 1; end if tmpInfo:find("%-") then tmpInfo = tmpInfo:gsub("%-",""); tmpSign = 2; end local tmpNew if tmpSign == 1 then tmpNew = 0 + tonumber(tmpInfo); end if tmpSign == 2 then tmpNew = 0 - tonumber(tmpInfo); end if tmpSign == 0 then tmpNew = tmpInfo; end return tmpNew; end -- Identical to CPlayer:merchant accept that it does a does the swap check first. -- Use this for best results, damaged gear will have skewed stats. -- This is also to be safe and not accidentally sell items instead of wearing them. function srMerchant(_npcname, _option) inventory:update(); if self:openStore(_npcname, _option) then RoMScript("ClickRepairAllButton()"); yrest(1000); RoMScript("CloseWindows()"); yrest(1000); srCheckSwap(); end if self:openStore(_npcname, _option) then inventory:update(); if ( inventory:autoSell() ) then inventory:update(); end store:buyConsumable("healing", settings.profile.options.HEALING_POTION); store:buyConsumable("mana", settings.profile.options.MANA_POTION); store:buyConsumable("arrow_quiver", settings.profile.options.ARROW_QUIVER); store:buyConsumable("thrown_bag", settings.profile.options.THROWN_BAG); store:buyConsumable("poison", settings.profile.options.POISON); if settings.profile.options.EGGPET_ENABLE_CRAFT then if settings.profile.options.EGGPET_CRAFT_RATIO then local mRatio, wRatio, hRatio = string.match(settings.profile.options.EGGPET_CRAFT_RATIO,"(%d*)%s*:%s*(%d*)%s*:%s*(%d*)") if tonumber(mRatio) > 0 then store:buyConsumable("eggpet_hoe", settings.profile.options.EGGPET_HOE); end if tonumber(wRatio) > 0 then store:buyConsumable("eggpet_hatchet", settings.profile.options.EGGPET_HATCHET); end if tonumber(hRatio) > 0 then store:buyConsumable("eggpet_spade", settings.profile.options.EGGPET_SPADE); end else store:buyConsumable("eggpet_hoe", settings.profile.options.EGGPET_HOE); store:buyConsumable("eggpet_hatchet", settings.profile.options.EGGPET_HATCHET); store:buyConsumable("eggpet_spade", settings.profile.options.EGGPET_SPADE); end end inventory:update(); end RoMScript("CloseWindows()"); end -- This add-on is installed! SRSwap_Installed = true; -- TODO: -- Add check for second ring/second earring -- Add real checks for 2h vs current primary + offhand instead of current default false w/ offhand -- Reduce unnecessary info passing to functions -- Find and add memory scanning (major speed improve) -- * Scan equipment tooltip info -- * Scan bag tooltip info -- * Use real calculations instead of StatRating addon -- Add universal quest item check (work with AcceptQuestByName)