--[[Idea;
Streamline the sort by using the new sorting function attached to listboxes.
1) Tag each backpack slot with the items weight when added (set to zero when removed)
2) When an item is added/moved to the pack, or the pack is opened, sort the group that contains it.
This is a huge change, as it would no longer be nessecary (or desireable) to clear the bag when it is opened.
Indeed, this method could be so secure that it may be worth just resorting each group when the bag is opened/closed or changed (but only if visible!)
]]
--Needs a tidy!
--Check the bag datais valid.
if SB==nil then SB={} end
if SB.x==nil then SB.x=100 end --}
if SB.y==nil then SB.y=100 end --}Position
if SB.icx==nil then SB.icx=4 end --]
if SB.icy==nil then SB.icy=3 end --]Size, in icons
if SB.override==nil then SB.override=false end --Override default bags?
if SB.labelwidth==nil then SB.labelwidth=80 end --Width of the text label for the group.
if SB.font==nil then SB.font="TrajanPro14" end --Label font
if SB.ShowItem==nil then SB.ShowItem=true end --un-hide a group when an item is looted in it?
--font size
SB.fontsize=string.sub(SB.font,-2)
if SB.fontsize=="ed" then SB.fontsize=13-1 else SB.fontsize=tonumber(SB.fontsize)-1 end
--Frame padding
pad={top=30,side=15,bottom=10}
--Calculate width and maximum height for the bag.
SB.width=SB.labelwidth+36*SB.icx+2*pad.side
SB.maxheight=Turbine.UI.Display.GetHeight()
--Functions
--Set the sizes of all the areas of the bag.
function SortBagSetSize()
SortBag.ListBox:SetSize( SB.labelwidth+SB.icx*36, SortBag:GetHeight()-(pad.top+pad.bottom)+1)
SortBag.ScrollBar:SetPosition(SB.labelwidth+SB.icx*36+10,pad.top+5)
SortBag.ScrollBar:SetHeight(SortBag:GetHeight()-(pad.top+pad.bottom)-10)
SortBag.Resize:SetPosition(SB.width-10,pad.top+10)
SortBag.Resize:SetSize(10,SortBag:GetHeight()-2*pad.top-10)
SortBag.ResizeBack:SetSize(10,SortBag:GetHeight()-pad.top-pad.bottom)
Search.label:SetSize(SB.labelwidth,SB.fontsize)
end
--Adjust the sizes of the GroupBoxes.
function SetGroupBoxSize()
if GroupBox==nil then return end
local height=0
for j=0,#Groups do
--Adjust GroupBox Widths
GroupBox[j]:SetMaxItemsPerLine(SB.icx)
GroupBox[j]:SetWidth(SB.icx*36)
--Hide the slot, or adjust its height
if Groups[j].hide then
GroupBox[j]:SetHeight(0)
GroupBox[j].label:SetBackColor(Turbine.UI.Color( 1,0.4,0,0 ))
else
GroupBox[j]:SetHeight( math.ceil(GroupBox[j]:GetItemCount()/SB.icx)*36)
GroupBox[j].label:SetBackColor(Turbine.UI.Color( 1,0,0,0.4 ))
end
--Adjust Label Width,Height
GroupBox[j].label:SetWidth(SB.labelwidth)
if GroupBox[j]:GetItemCount()>0 then
GroupBox[j].label:SetHeight(SB.fontsize)
else
GroupBox[j].label:SetHeight(0)
end
--Add to the height variable
height=height+math.max(GroupBox[j]:GetHeight(),GroupBox[j].label:GetHeight())
end
if height>SB.maxheight-(pad.top+pad.bottom) then height=SB.maxheight-(pad.top+pad.bottom) end
--Resize the window to fit the new height NOTE: The SB.fontsize is to accomodate the "search" box
SortBag:SetHeight(height+pad.top+pad.bottom+SB.fontsize)
--Change the title to show how many empty slots are left
SortBag:SetText(GroupBox[0]:GetItemCount().." empty slots left")
end
--Search function now added to the bag!
Search={}
Search.label=Turbine.UI.Label()
Search.label:SetBackColor( Turbine.UI.Color( 1,0.1,0.1,0.1 ) )
Search.label:SetMultiline(true)
Search.label:SetTextAlignment( Turbine.UI.ContentAlignment.MiddleCenter )
Search.label:SetMouseVisible (true)
Search.label:SetSize(SB.labelwidth,SB.fontsize)
Search.label.MouseDown=function()
Search.Textbox:SetText("")
SearchBag("")
end
Search.Textbox=Turbine.UI.TextBox()
Search.Textbox:SetSize(200,SB.fontsize)
--Scan the text entered.
Search.Textbox.TextChanged=function()
SearchBag(Search.Textbox:GetText())
end
function SearchBag(SearchString)
local txt1=string.lower(SearchString)
for i=1,size do
local item = backpack:GetItem(i)
if item~=nil then
local itemInfo=item:GetItemInfo();
local txt2=string.lower(itemInfo:GetName())
if string.find(txt2,txt1) then
BackpackSlots[i]:SetVisible(true)
else
BackpackSlots[i]:SetVisible(false)
end
end
end
end
--populate the bag.
function populatebag()
SortBag.ListBox:ClearItems()
--Add the search section. Fonts applied here too.
SortBag.ListBox:AddItem(Search.label)
SortBag.ListBox:AddItem(Search.Textbox)
Search.Textbox:SetFont(Turbine.UI.Lotro.Font[SB.font])
Search.label:SetFont(Turbine.UI.Lotro.Font[SB.font])
Search.label:SetText( "Search" )
GroupBox={}
BackpackSlots=nil
BackpackSlots={}
--Check for font size errors
if SB.labelwidth<SB.fontsize then SB.labelwidth=SB.fontsize end
--Build VirtPack
VirtPack=analysepack()
---Build a box for this group of items. Box 0 is for empty slots.
for i=0,#Groups do
GroupBox[i] = Turbine.UI.ListBox()
GroupBox[i]:SetOrientation( Turbine.UI.Orientation.Horizontal )
GroupBox[i]:SetAllowDrop( true )
GroupBox[i].DragDrop = function( sender, args )
--Empty slots allow drag-drop. Hopefully!
local shortcut = args.DragDropInfo:GetShortcut();
if ( shortcut ~= nil ) then
local item = GroupBox[i]:GetItemAt( args.X, args.Y );
local index
if item==nil then index=1 else index= item.id end
backpack:PerformShortcutDrop( shortcut, index, Turbine.UI.Control.IsShiftKeyDown() )
end
end
end
--Build the backpack Slots.
for i=1,size do
item = backpack:GetItem(i)
if item~=nil then AddCallback(item, "QuantityChanged", BagQuantityChanged) end
BackpackSlots[i]=Turbine.UI.Lotro.ItemControl( item )
BackpackSlots[i].id=i --so it can be passed for item swopping.
BackpackSlots[i]:SetBackground( "JackdawPlugins/SortPack/Back.tga" )
BackpackSlots[i]:SetBlendMode( Turbine.UI.BlendMode.Overlay )
end
--Move the items into the boxes
for i=1,#VirtPack do
--if i<10 then Turbine.Shell.WriteLine(VirtPack[i].id) end --Re-enable if the "not sorting correctly" bug rears its head again.
BackpackSlots[VirtPack[i].id].Group=VirtPack[i].Group
GroupBox[VirtPack[i].Group]:AddItem(BackpackSlots[VirtPack[i].id])
end
--Add the groups to the backpack
for j=0,#Groups do
--Build a label
GroupBox[j].label=Turbine.UI.Label()
GroupBox[j].label:SetBackColor( Turbine.UI.Color( 1,0,0,0.4 ) )
GroupBox[j].label:SetFont(Turbine.UI.Lotro.Font[SB.font])
GroupBox[j].label:SetMultiline(true)
GroupBox[j].label:SetTextAlignment( Turbine.UI.ContentAlignment.MiddleCenter )
GroupBox[j].label:SetText( Groups[j].Name )
GroupBox[j].label:SetMouseVisible (true)
GroupBox[j].label:SetSize(SB.labelwidth,0)--Initially of height 0
GroupBox[j].label.label=true
if j~=0 then
--Add the label and GroupBox, but not for empty slots
SortBag.ListBox:AddItem(GroupBox[j].label)
SortBag.ListBox:AddItem(GroupBox[j])
end
--Click events for the labels.
GroupBox[j].label.MouseClick=function(sender,args)
if ( args.Button == Turbine.UI.MouseButton.Left ) then
--Left Clicking the label collapses this group
Groups[j].hide=not(Groups[j].hide)
else
--Right clicking either collapses all groups, if this one is shown, or opens all groups if not.
Groups[j].hide=not(Groups[j].hide)
for i=0,#Groups do
Groups[i].hide=Groups[j].hide
end
end
SetGroupBoxSize()
end
end
--Empty Slots group added last.
SortBag.ListBox:AddItem(GroupBox[0].label)
SortBag.ListBox:AddItem(GroupBox[0])
--Function refresh the heights of the group boxes
SetGroupBoxSize()
end
--Show the backpack
function openbag()
--Clear out any previous search
Search.Textbox:SetText("")
SearchBag("")
--Show the bag!
SortBag:SetVisible(true)
end
--Events here replace the default bags with this one.
function SBOverride()
if SB.override then SortBag:SetWantsKeyEvents( true );
SortBag.KeyDown = function( sender, args )
if ( args.Action == Turbine.UI.Lotro.Action.Escape ) then
SortBag:Close()
end
if ( args.Action == Turbine.UI.Lotro.Action.ToggleBags or
args.Action == Turbine.UI.Lotro.Action.ToggleBag1 or
args.Action == Turbine.UI.Lotro.Action.ToggleBag2 or
args.Action == Turbine.UI.Lotro.Action.ToggleBag3 or
args.Action == Turbine.UI.Lotro.Action.ToggleBag4 or
args.Action == Turbine.UI.Lotro.Action.ToggleBag5 or
args.Action == Turbine.UI.Lotro.Action.ToggleBag6)
then
if SortBag:IsVisible() then
SortBag:Close()
else
openbag()
end
end
end
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack1, false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack2, false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack3, false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack4, false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack5, false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack6, false )
else
SortBag:SetWantsKeyEvents( false )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack1, true )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack2, true )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack3, true )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack4, true )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack5, true )
Turbine.UI.Lotro.LotroUI.SetEnabled( Turbine.UI.Lotro.LotroUIElement.Backpack6, true )
end
end
--Next three items update the bag if items are added or removed.
BagAdd=function( sender, args )
--args.Index = Bag slot
--args.Item = Item object
--Remove the slot from the group it is in.
for i=0,#Groups do
if GroupBox[i]:ContainsItem(BackpackSlots[args.Index]) then GroupBox[i]:RemoveItem(BackpackSlots[args.Index]) end
end
--Determine which group it belongs to
local destination=ItemGroup[ItemCategory[GetCat(args.Item)]]
if destination==nil then destination=0 end
--Insert the item in that group
BackpackSlots[args.Index]:SetItem(args.Item)
BackpackSlots[args.Index].Group=destination
GroupBox[destination]:InsertItem(position,BackpackSlots[args.Index])
if destination~=0 then
--Remove every item from that groupbox one at a time and resort them.
local temp={}
--cycle through the elements checking for weights
for i=GroupBox[destination]:GetItemCount(),1,-1 do
temp[i]=GroupBox[destination]:GetItem(i)
temp[i].Weight=weight(temp[i].id)
temp[i].Name=GroupBox[destination]:GetItem(i):GetItem():GetName()
end
--Remove all the items
GroupBox[destination]:ClearItems()
--Sort the items according to weight (ignoring alpha. I am happy with this compromise)
table.sort(temp, function (a,b)
if a.Weight == b.Weight then
--return a.Name < b.Name
--return strip(a.Name)< strip(b.Name)
if revorder then return strip(a.Name)> strip(b.Name) else return strip(a.Name)< strip(b.Name) end
else
if revorder then return (a.Weight > b.Weight) else return (a.Weight < b.Weight) end
end
end)
--Put the items in the group
for i=1,#temp do
GroupBox[destination]:AddItem(temp[i])
end
end
--Refresh heights
SetGroupBoxSize()
--Add the quantity changed event
AddCallback(args.Item, "QuantityChanged", BagQuantityChanged)
unhide(args.Item)
end
BagRemove=function( sender, args )
--Turbine.Shell.WriteLine("BagRemove "..args.Index)
--Note: this is sometimes called when looting *and no item is removed* causing the "empty slot" bug. Annoying, and not my fault!
--Also called if a stack is merged.
--args.Index = Bag slot
--Remove the quantity changed callback
item = backpack:GetItem(args.Index)
RemoveCallback(item, "QuantityChanged", BagQuantityChanged)
--Remove item from group
GroupBox[BackpackSlots[args.Index].Group]:RemoveItem(BackpackSlots[args.Index])
--Make the item for that slot nil
BackpackSlots[args.Index]:SetItem(nil)
BackpackSlots[args.Index].Group=0
--Add it to empty slots.
GroupBox[0]:AddItem(BackpackSlots[args.Index])
--Refesh Heights
SetGroupBoxSize()
end
BagMove=function( sender, args )
--15/12/11 This has changed completely! The notes below are now false.
--Turbine.Shell.WriteLine("BagMove Old Index"..args.OldIndex.." New Index "..args.NewIndex)
--[[
1) If an item has just been moved to an empty slot then just this fires.
2) If an item has been swopped with another; Say A in slot n1 with B in slot n2
ItemRemoved fires on slot n1
ItemMoved fires with B from n2 to n1
ItemAdded fires on slot n2 with item A
Essentially, item A (the moved item) gets held "in hand" making the target slot empty.
Recently I have come to the belief that it is this that causes the bug, and not ItemRemoved.
]]
--args.Item =Item object of the item that has been moved.
--args.NewIndex =Target Slot
--args.OldIndex =Destination Slot
BackpackSlots[args.NewIndex],BackpackSlots[args.OldIndex]=BackpackSlots[args.OldIndex],BackpackSlots[args.NewIndex]
--Refresh heights
SetGroupBoxSize()
end
BagQuantityChanged=function(sender,args)
unhide(sender)
end
function unhide(item)
if not(SB.ShowItem) then return end
local destination=ItemGroup[ItemCategory[GetCat(item)]]
if destination==nil then destination=0 end
--Un-Hide this group
if Groups[destination].hide then
Groups[destination].hide=false
SetGroupBoxSize()
end
end
AddCallback(backpack, "ItemAdded", BagAdd)
AddCallback(backpack, "ItemRemoved", BagRemove)
AddCallback(backpack, "ItemMoved", BagMove)
--Main Structure; Create the window to hold the backpack slots and populate it.
SortBag = Turbine.UI.Lotro.Window()
SortBag:SetPosition( SB.x,SB.y )
SortBag:SetText("SortBag")
SortBag.ListBox = Turbine.UI.ListBox()
SortBag.ListBox:SetParent( SortBag )
SortBag.ListBox:SetPosition( pad.side, pad.top )
SortBag.ListBox:SetMaxItemsPerLine(2)
SortBag.ListBox:SetOrientation( Turbine.UI.Orientation.Horizontal )
SortBag.ScrollBar=Turbine.UI.Lotro.ScrollBar()
SortBag.ScrollBar:SetOrientation( Turbine.UI.Orientation.Vertical )
SortBag.ListBox:SetVerticalScrollBar( SortBag.ScrollBar )
SortBag.ScrollBar:SetParent(SortBag)
SortBag.ScrollBar:SetPosition(SB.labelwidth+SB.icx*36+10,pad.top+5)
SortBag.ScrollBar:SetWidth(10)
--Remove this control when Turbine fix the backpack bug. again.
SortBag.Control=Turbine.UI.Control()
SortBag.Control:SetParent(SortBag)
SortBag.Control:SetMouseVisible(true)
SortBag.Control:SetBackground(0x41007e26)
SortBag.Control:SetSize(16,16)
SortBag.Control:SetPosition(pad.side-15,pad.top-15)
SortBag.Control.MouseClick=function()
--This was, originally, in Button.lua. And is better there imho.
local contextMenu = Turbine.UI.ContextMenu()
contextMenu.items = contextMenu:GetItems()
local option2=Turbine.UI.MenuItem("Re-stack the items - WARNING! EXPERIMENTAL!",true)
option2.Click = function () import "JackdawPlugins.SortPack.Stack" Turbine.Shell.WriteLine("<rgb=#888888>Starting stack") Stack() end
local option3=Turbine.UI.MenuItem("Override default bags",true,SB.override)
option3.Click = function () SB.override=not(SB.override) SBOverride() end
local option4=Turbine.UI.MenuItem("Open the sort order panel",true)
option4.Click = function () import"JackdawPlugins.SortPack.ListOption" ShowListOption() end
local option6=Turbine.UI.MenuItem("Create/Edit custom Categories",true)
option6.Click = function () import"JackdawPlugins.SortPack.CustomOption" ShowCustomOption() end
local option7=Turbine.UI.MenuItem("Show a group on item gain.",true,SB.ShowItem)
option7.Click = function () SB.ShowItem=not(SB.ShowItem) end
local option8=Turbine.UI.MenuItem("Open the Debug Window",true)
option8.Click = function () import"JackdawPlugins.SortPack.Debug" Debug() end
local option5 = Turbine.UI.MenuItem("Select SortBag label font;")
contextMenu.SubMenuItems = option5:GetItems()
local suboption5={}
local font={}
j=1
for i in pairs(Turbine.UI.Lotro.Font) do
font[j]=i
j=j+1
end
table.sort(font, function (a,b)
return (a < b)
end)
for i=1,#font do
suboption5[i] = Turbine.UI.MenuItem(font[i],true,font[i]==SB.font)
suboption5[i].Click = function ()
SB.font=font[i]
SB.fontsize=string.sub(SB.font,-2)
if SB.fontsize=="ed" then SB.fontsize=13-1 else SB.fontsize=tonumber(SB.fontsize)-1 end
populatebag()
end
contextMenu.SubMenuItems:Add(suboption5[i])
end
contextMenu.items:Add(option2)
contextMenu.items:Add(option5)
contextMenu.items:Add(option3)
contextMenu.items:Add(option7)
contextMenu.items:Add(option4)
contextMenu.items:Add(option6)
contextMenu.items:Add(option8)
contextMenu:ShowMenu()
end
SortBag.Resize=Turbine.UI.Control()
SortBag.Resize:SetParent(SortBag)
SortBag.Resize:SetZOrder(99)
SortBag.ResizeBack=Turbine.UI.Control()
SortBag.ResizeBack:SetParent(SortBag.Resize)
SortBag.ResizeBack:SetBackColor(Turbine.UI.Color(0.7,0.3,0.3,1))
SortBag.ResizeBack:SetVisible(false)
SortBag.ResizeBack:SetMouseVisible(false)
SortBag.Resize.MouseEnter=function() SortBag.ResizeBack:SetVisible(true) end
SortBag.Resize.MouseLeave=function() SortBag.ResizeBack:SetVisible(false) end
SortBag.Resize.MouseDown=function ()
draggingr=true
end
SortBag.Resize.MouseUp=function ()
draggingr=false
end
SortBag.Resize.MouseMove=function(sender,args)
if draggingr then
local oldwidth=SB.width
SB.width,dummy=SortBag:GetMousePosition()
if SortBag:IsAltKeyDown() then
SB.labelwidth=SB.width-oldwidth+SB.labelwidth
if SB.labelwidth<SB.fontsize then SB.labelwidth=SB.fontsize end
else
SB.icx=math.floor((SB.width-2*pad.side-SB.labelwidth)/36)
SB.icy=math.floor((SB.maxheight-(pad.top+pad.bottom))/36)
if SB.icx<3 then SB.icx=3 end
if SB.icy<1 then SB.icy=1 end
end
SB.width=36*SB.icx+2*pad.side+SB.labelwidth
SortBag:SetSize(SB.width,SB.maxheight)
end
end
SortBag.SizeChanged=function()
SortBagSetSize()
SetGroupBoxSize()
--Now, if the size of the bag would take the bottom of the bag off the screen, then re-position it.
local x,y=SortBag:GetPosition()
if y+SortBag:GetHeight()>SB.maxheight then y=SB.maxheight-SortBag:GetHeight() end
SortBag:SetPosition(x,y)
end
SortBag:SetSize(SB.width,SB.maxheight)
SortBag.PositionChanged=function()
local x,y=SortBag:GetPosition()
if x<0 then x=0 end
if y<0 then y=0 end
SortBag:SetPosition(x,y)
SB.x,SB.y=x,y
end
populatebag()
SBOverride()