WoWInterface SVN ItemRack-Cataclysm

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /trunk
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

ItemRack/ItemRackButtons.blp Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:executable + Added: svn:mime-type + application/octet-stream
ItemRack/ItemRackGear.blp Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:executable + Added: svn:mime-type + application/octet-stream
ItemRack/readme.txt New file
0,0 → 1,225
This is a mod to make swapping equipment easier through popout slot menus, equip slot buttons, gear sets and automated swaps.
 
__ New in 2.72 - By Kharthus __
 
* TOC update for WoW 4.2.0
 
__ New in 2.71 - By Kharthus __
 
* TOC update for WoW 4.1.0
 
__ New in 2.7 - By Yewbacca __
 
* Created new consolidated patterns for ItemLink/ItemString-to-ItemRackStyleID, ItemRackStyleID-to-BaseID, ItemLink/ItemString-to-BaseID
* Created new functions that use those patterns, and various other new helper functions, in order to consolidate all ItemString-handling in one place
* Replaced all old-style handling of itemIDs (string.gsub/find/match) and made everything use the new functions instead, for more robust handling as well as easy updates in the future
* Replaced GUI drawing code with references to new functions, to get icons and tooltips to work in the GUI
* Updated the GUI code so that it always draws item tooltips using the player's current level, which ensures that mousing over heirlooms in saved sets will actually show their stats for your CURRENT level (the old code set all levels to 0 which made it so the stats were shown incorrectly as level 1)
* Improved the two functions that actually LOCATE the stored equipment set items inside the player's inventory/equipment/bank (whether it's for equipping, or simply displaying a tooltip), by making sure those functions pass the INCOMING ID through the new level updater (UpdateIRString) to ensure all old Item IDs are up to date, thus fixing the bug where leveling up would cause ItemRack to fail strict item search on all saved sets and resort to any-item-with-the-same-baseID; this injection point was chosen because it fixes the problem completely throughout the board, since everything that locates items relies on the item finding functions
* Updated the functions for getting info for an item (GetInfoByID), posting an itemlink to the chatbox when shift clicking an item in ItemRack's GUI (ChatLinkID), and the function for showing an item in a tooltip, so that they all pass the incoming ID through the level updater (UpdateIRString) in order to work with up-to-date information, since it's important that stats are correct whether it's for posting chat links or looking at a tooltip's stats
* Commented and cleaned up terrible code here and there, as I untangled the messy code (minor changes)
 
__ New in 2.65 - By Kharthus __
 
* Reverted gear swap code to 2.243 version
 
__ New in 2.64 - By Kharthus __
 
* Fixed slash commands
 
__ New in 2.63 - By Kharthus __
 
* Replaced all getglobal calls with _G
* Fixed Titan's Grip and Shaman off-hand
 
__ New in 2.62 - By Kharthus __
 
* Fixed lua errors caused by chat links
* Fixed issues with clicking on Dockable Buttons
* Fixed event code
* Fixed icon updates on gear swaps
 
__ New in 2.61 - By Kiki __
 
* Fixed lua errors caused by tooltips
 
__ New in 2.60 - By Kiki __
 
* Fixed for WoW 4.0
 
__ New in 2.25 __
 
* Fix for Titan's Grip and dual spec
* Fix for Shaman's dual wield
 
__ Quick Start Guide __
 
Minimap button:
* Right-click the minimap button to open options or create sets
* Left-click the minimap button to choose a set
* Shift-click the minimap button to unequip the last set equipped
* Alt-right-click the minimap button to toggle events on/off
* Alt-left-click the minimap button to show hidden sets
 
Dockable buttons:
* Alt+click slots on the character sheet to create/remove buttons
* Alt+click yourself in the character sheet to create/remove a set button
* Alt+click the created buttons to toggle their auto-queue status
* Shift+drag buttons to break them apart if they're docked to each other
* Drag the menu's border around to dock it to a different side of buttons
* Right-click the menu's border to rotate the menu
* Size, alpha, spacing, etc are in options
 
Creating/equipping sets:
* You create sets in the Sets tab after right-clicking the minimap button
* Select slots for the set, choose a name and icon and click Save
* Once a set is saved, there are several ways to equip it:
1. Left-click the minimap button and choose the set
2. Mouseover a set button you've created (Alt+click yourself in character sheet)
3. Use a key binding you define in the set ("Bind Key" button)
4. In macros with /itemrack equip setname
5. In events or scripts that use EquipSet("setname")
 
Popout menus:
* Click an item or set in a menu to equip it
* Shift+click a set in a menu to unequip it
* Alt+click an item in a menu to hide/unhide it
* Hold Alt as you mouseover a slot to show all hidden items
 
While at a bank:
* Items/sets in the bank have a blue border.
* Selecting an item or set that's in the bank will pull it from the bank to your bags.
* Selecting an item or set that's not in the bank will attempt to put it all into the bank.
 
__ Slash Commands __
 
/itemrack : list the most common slash commands
/itemrack opt : summon the options GUI
/itemrack equip setname : equips a set
/itemrack reset : resets buttons
/itemrack reset everything : wipes all settings, sets and events
/itemrack lock/unlock : locks and unlocks the buttons
/itemrack toggle set name[, second set name] : equips/unequips "set name" (or swaps between two sets if a second set given)
 
__ Macro Functions __
 
EquipSet("setname") -- equips "setname"
UnequipSet("setname") -- unequips "setname"
ToggleSet("setname") -- toggles (equips then unequips) "setname"
IsSetEquipped("setname") -- returns true if "setname" is equipped
 
In the unlikely event that another mod (or default UI in the future) uses these function names, you can use their long version ItemRack.EquipSet(), ItemRack.UnequipSet(), etc. This mod only commandeers the shortened names if they appear to be unused.
 
__ Events __
 
2.2 (re)introduces events. These are scripts to automatically equip and unequip gear as things happen in game.
 
To use an event:
1. In the 'Sets' tab, create or make sure you have a set you'd like to equip when the event happens.
2. In the 'Events' tab, click the red ? icon beside the event you want to use.
3. Choose the set for this event.
4. Ensure the event has a check beside it.
 
As events are enabled, a separate process watches for those events and equips (and unequips if chosen) as they happen.
 
If you want to create or edit an event, there are four types of events:
 
Buff: These events equip gear as you gain buffs. ie, Evocation, Drinking and being on a mount.
Stance: These events equip gear when you change stances or forms. ie, Battle Stance, Moonkin Form, Shadowform
Zone: These events equip gear when you're in one of a list of zones. ie, the PVP event includes all arena and BG maps.
Script: For those with lua knowledge, you can create your own event based on a game event. A couple examples are in the default events.
 
When dealing with events, it's good to keep some things in mind:
* You'll get the most predictable behavior by having sets that don't overlap. If you're a warrior with a Tanking, DPS and PVP set, consider not including weapons in those sets. If you decide to make an event to swap in a 2H when you go into Berserker Stance and a 1h+shield when you go into Defensive Stance, you won't step on the toes of events that swap in PVP gear in a BG/arena or a tuxedo in a city.
* A gold gear icon on the minimap button (and on the sets button if you've created one) means that events are enabled. If you decide you want to temporarily shut down all events, Alt+click the minimap button or the sets button. (You can disable events in options also)
* For non-English users, you might want to edit the events that have English text within them. I try to keep it locale-independant when possible (ie, warrior and most druid stances use the numbers instead of names), but you'll never enter "Stormwind City" on a deDE client for the city event.
* Script Events do not have a "set" defined to them like other events do. They need to EquipSet("setname") explicitly. Its set button will always be the macro keys icon.
* Advanced users of 1.9x may notice the lack of a delay option in scripted events. I've decided to pull this down into the scripting system to streamline the event process. For now, you can use ItemRack.CreateTimer and ItemRack.StartTimer defined in ItemRack.lua.
 
__ Future plans __
 
* Move back to an associated set icon for script events
* Possible option of flags for less "persistent" events (only equip if specific state changes)
* Tighter integration/use of EquipItemByName for unique slots
* Moving options to the default UI's new option panels
* Dynamically reordering gear swaps to handle unique-equipped gems in different sets
* Supporting the existing ItemRackFu and TitanFu plugins if possible
* Native ButtonFacade support
* Possibly adding a Combat Log event type
* A set button to sit to the left of weapon slot since the character model isn't an intuitive "button"
* Moving slot key bindings back to default key binding interface
 
* Set equips while casting will wait until casting ends. (For now this is just sets. Swapping invidual slots from a menu isn't delayed yet.)
* Buff/Mount and Stance events now have an option "Except in PVP instances" to ignore that event in BGs or arenas
* Added new option "Disable Alt+Click" to disable toggling auto queue on buttons
* Druid Tree of Life event fixed
 
__ More documentation to come __
2.65, 10/22/10, gear swap code
2.64, 10/17/10, slash commands
2.63, 10/17/10, global variables, titan's grip, shaman DW
2.62, 10/16/10, chatframe errors, dockable buttons, event handling
2.61, 10/15/10, tooltip lua errors
2.60, 10/13/10, toc update, fix for wow 4.0
2.25, 4/17/09, toc update, TG talent fix for dual wield, shaman dual wield talent fix
2.243, 10/14/08, more DK, Titan's Grip, shaman dual wield support
2.242, 8/17/08, delayed OnLogin to timer, basic Titan's Grip support
2.241, 8/13/08, removed nil of .old in unequip, PLAYER_AURAS_CHANGED to UNIT_AURA, button font to styles
2.24, 8/9/08, added self,offset to OnVerticalScroll, changed GetPlayerBuffName to UnitAura
2.23, 7/17/08, changed "Tree of Life Form" to "Tree of Life", added "Except in PVP instances" option to buff/stance events, added "Disable alt+clicK" option, UNIT_SPELLCAST_x now watched to delay set swaps via existing SetsWaiting, changed if elseif elseif etc in OnEvent to tabled ItemRack.EventHandlers
2.22, 7/10/08, MouseIsOver in menu update checks for frame visibility, second set to /itemrack toggle, alt+left click (show hidden sets) on minimap button with "Allow hidden items" disabled will behave the same as alt+right click (toggle events), "Equip on choosen set" renamed "Equip in options" and works for slots now as well as sets, changed shine flash from default UI's fade to inhouse timer (taint issue)
2.21, 6/29/08, added options Equip on choosing set, Set menu wrap, Show minimap tooltips, alt+left click minimap button=show hidden sets, alt+right click minimap button=toggle events, added swimming, druid flight form, druid swift flight form to events, prevent mount event on taxi (SaberHawke), gear icon on minimap regardless of dockable set button status, check for existing set on tooltip
2.2, 6/19/08, added event system
2.16, 6/8/08, added item lock throttle, equippable items in container cache (updated at end of lock update), replaced buff cache with GetPlayerBuffName, Feign Death checked with GetPlayerBuffName if enUS, gear icon shows if all queues disabled, but half alpha
2.15, 5/26/08, menu strata forced high in DockWindows, item tooltips look in bank if not on person, "^Requires" exception for IsRed changed to ITEM_MIN_SKILL global, set equip/unequip wait system added, hide set checkbutton, ignore/show/hide cloak/helm buttons back for now
2.14, 5/15/08, BankFrame:IsVisible() now a flag again, ITEM_SPELL_CHARGES split to separate search strings (Maldivia), Cooldown90 option, TrinketMenuMode fixed
2.13, 5/13/08, added topLevel="true" to ItemRackMenuFrame (Romracer), Menu on Shift tooltip bug (Romracer)
2.12, 5/12/08, ITEM_SPELL_CHARGES_P1 removed, %d+ replaced with %-?%d+, empty->2h swap fix by Romracer, AnythingLocked() fixed, ValidBag uses GetItemFamily
2.1, 7/14/07, tooltip fix for 2.2 patch, minor edits/fixes over past months
2.0-beta, 1/30/07, complete rewrite.
1.991, 12/23/06, bug fixed: hide/show made combat-aware
1.99, 12/4/06, added: 2.0 changes, key bindings
1.983, 10/19/06, changed: "inventory" attribute to "item" attribute
1.982, 10/14/06, updated: for lua 5.1 (by Esamynn), uses secure action buttons
1.974, 8/22/06, toc changed
1.973, 8/2/06, bugs fixed: changed evocation to ITEMRACK_BUFFS_CHANGED, changed Spirit Tab Begin arg1 to ItemRack.Buffs
1.972, 7/29/06, bug fixes: arg1 preserved through BuffsChanged, GetMouseFocus() could be nil in BAG_UPDATE
1.971, 7/28/06, bug fixes: changed BankFrame:IsVisible to a flag when bank open/closed, SPELLS_CHANGED events now PLAYER_AURAS_CHANGED for 1.12
1.97, 7/27/06, added: bank support, ITEMRACK_BUFFS_CHANGED
1.96, 4/20/06, bug fixed: 4606 error, checked if queue empty, changed: unequip will unequip whole set even if partial unequipped, reset events will nil .old itemid's in sets
1.95, 4/6/06, changes: IsSetEquipped "optimizations" removed, temporary events reverted to earlier versions, tooltip fix scaled back to one SetOwner, queuing a worn set clears queue for those slots, added: queued item insets to character sheet
1.94, 3/29/06, bug fix: menu not appearing on bar
1.93, 3/29/06, bug fixes: nil errors from tooltip changes, added: relic slot support
1.92, 3/20/06, bug fixes: attempt to fix arithmetic on string value 4706
1.91, 3/19/06, bug fixes: invalid key for 'next', couple nil errors, changed: mount event to old style, added: option ('Show set icon labels') to show/hide set labels, option ('Auto toggle sets') to auto toggle sets, shift on chosing set toggles set
1.9, 1/24/06, release of 1.891-1.897
1.897 (1.9 beta7) 2/24/06, bug fixed: queue jams (hopefully) gone for good, added: option to show/hide cloak/helm, AQ mount checks, changed: consolidated timers
1.896 (1.9 beta6) 2/21/06, bug fixed: new user and post-combat/death error
1.895 (1.9 beta5) 2/20/06, bug fixed: hide tradables fixed
1.894 (1.9 beta4) 2/19/06, changed: options to scrollable list, form events use IR_FORM global, combat/death queue redone to a single "set" instead of a full queue, alt+click on locked bar won't add/remove slots, one-hand won't show up in offhand for non-DW'ers, items clicked on cooldown won't appear to be used, added: square minimap, large cooldown, reset events
1.893 (1.9 beta3) 2/5/06, bug fixed: TrinketMenu Mode menu fixed if only trinkets on bar, added: Large font option for event script, changed: Event scripts overhauled for new EquipSet
1.892 (1.9 beta2) 1/29/06, bug fixed: non-standard bags recognized, old freespace code removed, 2h swaps on bar immediate move
1.891 (1.9 beta1) 1/28/06, changed: new EquipSet (enchants recognized), ^Requires dropped from "red" check, player-made events won't unregister on zoning, minimap button scaling fixed, /itemrack reset fixed
1.84, 1/20/06, bug fixed: menu update only checks MouseIsOver for plugin if it's installed
1.83, 1/19/06, added: titan support/docking, bug fixed: scroll wheel on lists, changed: checks for TitanRider enabled, events unregister/register on zoning
1.82, 1/16/06, changed: ALT+click/hide behavior made an option for all but sets (default off), bugs fixed: notify with no bar on screen should reliably fire, 2h->1h+offhand swaps will moved orphaned offhand to leftmost bag again
1.81, 1/14/06, bug fixed: Mount event
1.8, 1/12/06, added: IsSetEquipped(setname) returns true if all slots in a set are currently worn, error message when attempting to equip a set that doesn't exist, TitanPanel_ItemRackUpdate, note that events are suspended when set/event window up, bugs fixed: Disenchants/enchants/etc during gear swaps will abort the swap, Bloodvine will show up in menu on enUS clients, ammo count shows again, changed: initialization done in phases, removed cooldown boundry checks, tooltip scans changed to GetItemInfo, new events: Eating-Drinking, Low Mana, Skinning, Mage:Evocation, Priest:Spirit Tap Begin, Priest:Spirit Tap End
1.74, 1/8/06: bugs fixed: bags should work properly on deDE clients, set icon will remember last set when leaving an event, "Mount" event no longer disabled if TitanRider installed
1.73, 1/7/06: bugs fixed: Scrollbars enabled properly (the thumb will appear in 1.9.1 patch), Bindings saved using new SaveBindings, Soul Bags treated like quivers, ammo slot name lifted from tooltip for SaveSet, notify will happen if bar is off screen, changed: In TrinketMenu Mode, hold Shift to keep menu open on the bar, if any event uses ITEMRACK_NOTIFY trigger notify is automatically enabled, removed: check for TitanRider
1.72, 1/3/06: added: reset button added to options, bugs fixed: items used from action bars noticed by mod, users without SCT won't have notify messages "stick", scaling fixes for 1.9, removed scalebugfix (huzzah!)
1.71, 12/21/05: changed: inventory items used elsewhere in the UI reflects in the mod, arg1 and arg2 save on delayed events, added: ITEMRACK_ITEMUSED event (arg1=item name, arg2=item slot), Insignia Ready/Insignia Used events, examples for making scripts that swap on use, bugs fixed: added pink tiger to problem mounts
1.7, 12/8/05, added: events (see events manual.txt), sets queue, sets menu on minimap button, 30 second notify, French localization by Tinou, more slash commands (/itemrack help)
1.61, 11/02/05, bugs fixed: tooltip back to standard GameTooltip (should fix issues with Tipster, TipBuddy and other tooltip mods), set builder now updates properly when nothing on the bar
1.6, 10/31/05, bug fixed: error when items on notify queue are banked, "Container" in DE localized, added: menu pops up on character sheet, audible alert on notify, changed: ItemRack_CurrentSet made global
1.5, 10/5/05, bug fixed: pairs of items of same name with one worn swap, "Container" in German, added: Flip bar growth option, alt+click items in menu to hide them, changed: selecting set equips it
1.42, 10/2/05, bug fixed: two items of same name will swap in sets, key bindings in normal key binding window force to sets
1.41, 9/30/05, bug fixed: nil error on mouseover set icon
1.4, 9/30/05, added: sets, minimap button, /itemrack opt, help text, changed: using sharpening stones/poisons/enchants/etc will work on the bar, only wearable items will show in menu, options moved to a tab on sets window, bugs fixed: ghost countdowns for real
1.32, 9/18/05, bugs fixed: overhead errors fade normally, tabard on bar with cooldown/notify won't error, switching from 2h to 1h+shield queue works properly, swapping to empty won't attempt to put items in quivers, "ghost" cooldowns no longer show on empty slots, removed check for 1.7 in supposed client bug fix for 1.7 that never happened, changed: queues process as sets in stages
1.31, 9/8/05, bug fixed: rotate menu works when bar is horizontal
1.3, 9/7/05, added: German translation by Leelaa, Alt+right click an item will add a space after it, changed: right-click will activate an item same as left click, holding Alt while mouseover items will show full tooltip if Tiny Tooltips enabled, option TrinketMenu Clicks renamed TrinketMenu Mode, while TrinketMenu Mode enabled trinkets show as one menu, bugs fixed: overlapping tooltips, queue won't process upon FD, ammo count shows when adding to rack
1.21, 9/3/05, added: ammo shows quantity, changed: custom tooltip used instead of standard, bug fixed: thrown weapons recognized, multiple ammo stacks show as one in menu, error clicking empty slot with notify on, weapons will queue while dead
1.2, 9/3/05, added: options to hide tooltips or make them smaller, notify when items ready, /itemrack scale as alternative to set scale, changed: spacers to trinkets when TrinketMenu Clicks checked, OnUpdates given ItemRack_InvFrame parent, bugs fixed: mod hides when UI hidden, hunter FD won't block item swaps
1.1, 9/2/05, added: option to swap to empty slots, option to reverse menu growth, left/right-click trinkets options added, changed: "Soulbound Only" renamed to "Hide Tradables", some slots ignore soulbound flag now: trinket, ammo, shirt, tabard. quest and conjured items now show with 'Soulbound Only' on. bug fixed: items queued while dead will swap on res before release
1.0, 8/31/05, initial release
Property changes : Added: svn:executable +
ItemRack/ItemRackButtons.xml New file
0,0 → 1,209
<Ui>
<Frame name="ItemRackBracketTemplate" virtual="true">
<Layers>
<Layer level="OVERLAY">
<Texture name="$parentTexture" file="Interface\AddOns\ItemRack\Brackets"/>
</Layer>
</Layers>
</Frame>
<Frame name="ItemRackMainBracket" inherits="ItemRackBracketTemplate"/>
<Frame name="ItemRackMenuBracket" inherits="ItemRackBracketTemplate"/>
 
<Frame name="ItemRackTimeTemplate" enableMouse="false" virtual="true">
<Size>
<AbsDimension x="36" y="12"/>
</Size>
<Anchors>
<Anchor point="BOTTOMRIGHT"/>
</Anchors>
<Layers>
<Layer level="OVERLAY">
<FontString name="$parentTime" inherits="NumberFontNormal" justifyH="CENTER"/>
</Layer>
</Layers>
</Frame>
 
<CheckButton name="ItemRackButtonsTemplate" movable="true" parent="UIParent" inherits="ActionButtonTemplate,SecureActionButtonTemplate" hidden="true" virtual="true">
<!-- <Layers>
<Layer level="OVERLAY">
<Texture name="$parentQueue" file="Interface\Cursor\Interact" hidden="true">
<Size x="16" y="16"/>
<Anchors>
<Anchor point="TOPLEFT"/>
</Anchors>
</Texture>
</Layer>
</Layers> -->
<Scripts>
<PostClick>
ItemRack.ButtonPostClick(self,button)
</PostClick>
<OnDragStart>
ItemRack.StartMovingButton(self)
</OnDragStart>
<OnDragStop>
ItemRack.StopMovingButton(self)
</OnDragStop>
<OnEnter>
ItemRack.OnEnterButton(self)
</OnEnter>
<OnLeave>
ItemRack.ClearTooltip(self)
</OnLeave>
</Scripts>
</CheckButton>
<CheckButton name="ItemRackButton0" inherits="ItemRackButtonsTemplate" id="0"/>
<CheckButton name="ItemRackButton1" inherits="ItemRackButtonsTemplate" id="1"/>
<CheckButton name="ItemRackButton2" inherits="ItemRackButtonsTemplate" id="2"/>
<CheckButton name="ItemRackButton3" inherits="ItemRackButtonsTemplate" id="3"/>
<CheckButton name="ItemRackButton4" inherits="ItemRackButtonsTemplate" id="4"/>
<CheckButton name="ItemRackButton5" inherits="ItemRackButtonsTemplate" id="5"/>
<CheckButton name="ItemRackButton6" inherits="ItemRackButtonsTemplate" id="6"/>
<CheckButton name="ItemRackButton7" inherits="ItemRackButtonsTemplate" id="7"/>
<CheckButton name="ItemRackButton8" inherits="ItemRackButtonsTemplate" id="8"/>
<CheckButton name="ItemRackButton9" inherits="ItemRackButtonsTemplate" id="9"/>
<CheckButton name="ItemRackButton10" inherits="ItemRackButtonsTemplate" id="10"/>
<CheckButton name="ItemRackButton11" inherits="ItemRackButtonsTemplate" id="11"/>
<CheckButton name="ItemRackButton12" inherits="ItemRackButtonsTemplate" id="12"/>
<CheckButton name="ItemRackButton13" inherits="ItemRackButtonsTemplate" id="13"/>
<CheckButton name="ItemRackButton14" inherits="ItemRackButtonsTemplate" id="14"/>
<CheckButton name="ItemRackButton15" inherits="ItemRackButtonsTemplate" id="15"/>
<CheckButton name="ItemRackButton16" inherits="ItemRackButtonsTemplate" id="16"/>
<CheckButton name="ItemRackButton17" inherits="ItemRackButtonsTemplate" id="17"/>
<CheckButton name="ItemRackButton18" inherits="ItemRackButtonsTemplate" id="18"/>
<CheckButton name="ItemRackButton19" inherits="ItemRackButtonsTemplate" id="19"/>
<CheckButton name="ItemRackButton20" inherits="ItemRackButtonsTemplate" id="20"/>
 
<Frame parent="ItemRackButton0" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton1" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton2" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton3" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton4" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton5" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton6" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton7" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton8" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton9" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton10" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton11" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton12" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton13" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton14" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton15" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton16" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton17" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton18" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton19" inherits="ItemRackTimeTemplate"/>
<Frame parent="ItemRackButton20" inherits="ItemRackTimeTemplate"/>
 
<Frame parent="ItemRackButton0" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton1" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton2" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton3" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton4" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton5" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton6" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton7" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton8" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton9" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton10" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton11" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton12" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton13" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton14" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton15" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton16" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton17" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton18" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton19" inherits="ItemRackQueueTemplate"/>
<Frame parent="ItemRackButton20" inherits="ItemRackQueueTemplate"/>
 
<Button name="ItemRackButtonMenuTemplate" virtual="true">
<Size>
<AbsDimension x="16" y="16"/>
</Size>
<HighlightTexture file="Interface\AddOns\ItemRack\ItemRackButtons" alphaMode="ADD">
<TexCoords left=".25" right=".375" top="0" bottom=".125"/>
</HighlightTexture>
<Scripts>
<OnClick>
PlaySound("igMainMenuOptionCheckBoxOn")
ItemRack.ButtonMenuOnClick(self)
</OnClick>
<OnEnter>
ItemRack.OnTooltip(self)
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
</Scripts>
</Button>
 
<Frame name="ItemRackButtonMenu" parent="ItemRackMenuFrame">
<Size>
<AbsDimension x="36" y="36"/>
</Size>
<Frames>
<Button name="ItemRackButtonMenuLock" inherits="ItemRackButtonMenuTemplate">
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="1" y="-1"/>
</Offset>
</Anchor>
</Anchors>
<NormalTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".75" right=".875" top=".125" bottom=".25"/>
</NormalTexture>
<PushedTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".875" right="1" top=".125" bottom=".25"/>
</PushedTexture>
</Button>
<Button name="ItemRackButtonMenuQueue" inherits="ItemRackButtonMenuTemplate">
<Anchors>
<Anchor point="TOPRIGHT">
<Offset>
<AbsDimension x="-1" y="-1"/>
</Offset>
</Anchor>
</Anchors>
<NormalTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left="0" right=".125" top=".5" bottom=".625"/>
</NormalTexture>
<PushedTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".125" right=".25" top=".5" bottom=".625"/>
</PushedTexture>
</Button>
<Button name="ItemRackButtonMenuOptions" inherits="ItemRackButtonMenuTemplate">
<Anchors>
<Anchor point="BOTTOMLEFT">
<Offset>
<AbsDimension x="1" y="1"/>
</Offset>
</Anchor>
</Anchors>
<NormalTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".75" right=".875" top=".25" bottom=".375"/>
</NormalTexture>
<PushedTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".875" right="1" top=".25" bottom=".375"/>
</PushedTexture>
</Button>
<Button name="ItemRackButtonMenuClose" inherits="ItemRackButtonMenuTemplate">
<Anchors>
<Anchor point="BOTTOMRIGHT">
<Offset>
<AbsDimension x="-1" y="1"/>
</Offset>
</Anchor>
</Anchors>
<NormalTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".375" right=".5" top=".25" bottom=".375"/>
</NormalTexture>
<PushedTexture file="Interface\AddOns\ItemRack\ItemRackButtons">
<TexCoords left=".5" right=".625" top=".25" bottom=".375"/>
</PushedTexture>
</Button>
</Frames>
</Frame>
</Ui>
Property changes : Added: svn:executable +
ItemRack/ItemRack.xml New file
0,0 → 1,190
<Ui>
<Frame name="ItemRackFrame" hidden="true">
<Scripts>
<OnLoad>
ItemRack.OnLoad(self)
</OnLoad>
<OnEvent>
ItemRack.OnEvent(self,event,...)
</OnEvent>
<OnUpdate>
ItemRack.OnUpdate(self,elapsed)
</OnUpdate>
</Scripts>
</Frame>
 
<Frame name="ItemRackMenuFrame" frameStrata="HIGH" enableMouse="false" movable="true" parent="UIParent" hidden="true" clampedToScreen="true" topLevel="true">
<Size>
<AbsDimension x="64" y="64" />
</Size>
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
<BackgroundInsets>
<AbsInset left="4" right="4" top="4" bottom="4" />
</BackgroundInsets>
<TileSize>
<AbsValue val="16" />
</TileSize>
<EdgeSize>
<AbsValue val="16" />
</EdgeSize>
<BorderColor>
<Color r=".3" g=".3" b=".3"/>
</BorderColor>
</Backdrop>
<Scripts>
<OnHide>
ItemRack.MenuOnHide()
</OnHide>
</Scripts>
</Frame>
 
<GameTooltip name="ItemRackTooltip" inherits="GameTooltipTemplate" hidden="true">
<Scripts>
<OnLoad>
self:SetOwner(WorldFrame,"ANCHOR_NONE")
</OnLoad>
</Scripts>
</GameTooltip>
 
<Button name="ItemRackMinimapFrame" toplevel="true" frameStrata="LOW" parent="Minimap" enableMouse="true" movable="true">
<Size>
<AbsDimension x="33" y="33"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="-15" y="0"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture name="ItemRackMinimapIcon" file="Interface\AddOns\ItemRack\ItemRackIcon">
<Size>
<AbsDimension x="21" y="21"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="7" y="-6"/>
</Offset>
</Anchor>
</Anchors>
</Texture>
</Layer>
<Layer level="ARTWORK">
<Texture file="Interface\Minimap\MiniMap-TrackingBorder">
<Size>
<AbsDimension x="56" y="56"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT"/>
</Anchors>
</Texture>
</Layer>
<Layer level="OVERLAY">
<Texture name="ItemRackMinimapGear" file="Interface\AddOns\ItemRack\ItemRackGear" hidden="true">
<Size>
<AbsDimension x="16" y="16"/>
</Size>
<Anchors>
<Anchor point="CENTER" relativeTo="ItemRackMinimapIcon" relativePoint="TOPLEFT">
<Offset x="2" y="-2"/>
</Anchor>
</Anchors>
</Texture>
<Texture name="ItemRackMinimapShine" file="Interface\ComboFrame\ComboPoint" alphaMode="ADD" hidden="true">
<Size x="32" y="32"/>
<Anchors>
<Anchor point="CENTER">
<Offset x="3" y="3"/>
</Anchor>
</Anchors>
<TexCoords left="0.5625" right="1" top="0" bottom="1"/>
</Texture>
</Layer>
</Layers>
<HighlightTexture alphaMode="ADD" file="Interface\Minimap\UI-Minimap-ZoomButton-Highlight"/>
<Scripts>
<OnLoad>
self:RegisterForClicks("LeftButtonUp","RightButtonUp")
self:RegisterForDrag("LeftButton")
</OnLoad>
<OnMouseDown>
ItemRackMinimapIcon:SetTexCoord(.075,.925,.075,.925)
</OnMouseDown>
<OnMouseUp>
ItemRackMinimapIcon:SetTexCoord(0,1,0,1)
</OnMouseUp>
<OnEnter>
ItemRack.MinimapOnEnter(self)
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
<OnDragStart>
self:LockHighlight()
ItemRack.StartTimer("MinimapDragging")
ItemRackMinimapIcon:SetTexCoord(.075,.925,.075,.925)
</OnDragStart>
<OnDragStop>
ItemRack.StopTimer("MinimapDragging")
self:UnlockHighlight()
ItemRackMinimapIcon:SetTexCoord(0,1,0,1)
</OnDragStop>
<OnClick>
ItemRack.MinimapOnClick(self,button)
ItemRackMinimapIcon:SetTexCoord(0,1,0,1)
</OnClick>
</Scripts>
</Button>
 
<Frame name="ItemRackQueueTemplate" enableMouse="false" virtual="true">
<Size>
<AbsDimension x="20" y="20"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="-2" y="2"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="OVERLAY">
<Texture name="$parentQueue" hidden="true" file="Interface\AddOns\ItemRack\ItemRackGear"/>
</Layer>
</Layers>
</Frame>
 
<Frame parent="CharacterAmmoSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterHeadSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterNeckSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterShoulderSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterShirtSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterChestSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterWaistSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterLegsSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterFeetSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterWristSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterHandsSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterFinger0Slot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterFinger1Slot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterTrinket0Slot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterTrinket1Slot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterBackSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterMainHandSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterSecondaryHandSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterRangedSlot" inherits="ItemRackQueueTemplate"/>
<Frame parent="CharacterTabardSlot" inherits="ItemRackQueueTemplate"/>
 
<!-- All player Events (Buff/Zone/Stance/Script) go through this frame -->
<Frame name="ItemRackEventProcessingFrame" hidden="true" parent="UIParent">
<Scripts>
<OnEvent>
ItemRack.ProcessingFrameOnEvent(self,event,...)
</OnEvent>
</Scripts>
</Frame>
 
</Ui>
Property changes : Added: svn:executable +
ItemRack/ItemRackButtons.lua New file
0,0 → 1,688
ItemRack.Docking = {} -- temporary table for current docking potential
 
ItemRack.BracketInfo = { ["TOP"] = {36,12,.25,.75,0,.25}, -- bracket construction info
["BOTTOM"] = {36,12,.25,.75,.75,1}, -- cx,cy,left,right,top,bottom
["LEFT"] = {12,36,0,.25,.25,.75},
["RIGHT"] = {12,36,.75,1,.25,.75},
["TOPLEFT"] = {12,12,0,.25,0,.25},
["TOPRIGHT"] = {12,12,.75,1,0,.25},
["BOTTOMLEFT"] = {12,12,0,.25,.75,1},
["BOTTOMRIGHT"] = {12,12,.75,1,.75,1}
}
 
ItemRack.ReflectClicked = {} -- buttons clicked (checked)
ItemRack.LockedButtons = {} -- buttons locked (desaturated)
 
ItemRack.NewAnchor = nil
 
function ItemRack.InitButtons()
ItemRackUser.Buttons = ItemRackUser.Buttons or {}
 
ItemRack.oldPaperDollItemSlotButton_OnModifiedClick = PaperDollItemSlotButton_OnModifiedClick
PaperDollItemSlotButton_OnModifiedClick = ItemRack.newPaperDollItemSlotButton_OnModifiedClick
 
--ItemRack.oldCharacterAmmoSlot_OnClick = CharacterAmmoSlot:GetScript("OnClick")
--CharacterAmmoSlot:SetScript("OnClick",ItemRack.newCharacterAmmoSlot_OnClick)
 
ItemRack.oldCharacterModelFrame_OnMouseUp = CharacterModelFrame:GetScript("OnMouseUp")
CharacterModelFrame:SetScript("OnMouseUp",ItemRack.newCharacterModelFrame_OnMouseUp)
 
local button
for i=0,20 do
button = _G["ItemRackButton"..i]
if i<20 then
button:SetAttribute("type","item")
button:SetAttribute("slot",i)
else
button:SetAttribute("shift-slot*",ATTRIBUTE_NOOP)
button:SetAttribute("alt-slot*",ATTRIBUTE_NOOP)
end
button:RegisterForDrag("LeftButton","RightButton")
button:RegisterForClicks("LeftButtonUp","RightButtonUp")
-- button:SetAttribute("alt-slot*",ATTRIBUTE_NOOP)
-- button:SetAttribute("shift-slot*",ATTRIBUTE_NOOP)
ItemRack.MenuMouseoverFrames["ItemRackButton"..i]=1
end
 
ItemRack.CreateTimer("ButtonsDocking",ItemRack.ButtonsDocking,.2,1) -- (repeat) on while buttons docking
ItemRack.CreateTimer("MenuDocking",ItemRack.MenuDocking,.2,1) -- (repeat) on while menu docking
 
ItemRackMenuFrame:SetScript("OnMouseDown",ItemRack.MenuFrameOnMouseDown)
ItemRackMenuFrame:SetScript("OnMouseUp",ItemRack.MenuFrameOnMouseUp)
ItemRackMenuFrame:EnableMouse(1)
 
ItemRack.CreateTimer("ReflectClickedUpdate",ItemRack.ReflectClickedUpdate,.2,1)
 
ItemRackFrame:RegisterEvent("UNIT_INVENTORY_CHANGED")
ItemRackFrame:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
ItemRackFrame:RegisterEvent("ITEM_LOCK_CHANGED")
ItemRackFrame:RegisterEvent("UPDATE_BINDINGS")
ItemRack.ReflectMainScale()
ItemRack.ReflectMenuOnRight()
ItemRack.ConstructLayout()
ItemRack.UpdateButtonCooldowns()
ItemRack.ReflectHideOOC()
ItemRack.ReflectCooldownFont()
ItemRack.UpdateCombatQueue()
ItemRack.KeyBindingsChanged()
ItemRack.UpdateDisableAltClick()
end
 
function ItemRack.UpdateDisableAltClick()
if not InCombatLockdown() then
for i=0,19 do
_G["ItemRackButton"..i]:SetAttribute("alt-slot*",ItemRackSettings.DisableAltClick=="OFF" and ATTRIBUTE_NOOP or nil)
end
end
end
 
function ItemRack.newPaperDollItemSlotButton_OnModifiedClick(self, button)
if IsAltKeyDown() then
ItemRack.ToggleButton(self:GetID())
else
ItemRack.oldPaperDollItemSlotButton_OnModifiedClick(self, button)
end
end
 
function ItemRack.newCharacterAmmoSlot_OnClick(self, button)
if IsAltKeyDown() then
ItemRack.newPaperDollItemSlotButton_OnModifiedClick(self, button)
elseif button=="LeftButton" then
-- only call old function if LeftButton. We never UseInventoryItem(0) (in theory)
ItemRack.oldCharacterAmmoSlot_OnClick(self, button)
end
end
 
function ItemRack.newCharacterModelFrame_OnMouseUp(self, button)
if IsAltKeyDown() then
ItemRack.ToggleButton(20)
end
ItemRack.oldCharacterModelFrame_OnMouseUp(self, button)
end
 
function ItemRack.AddButton(id)
ItemRackUser.Buttons[id] = {}
local button = _G["ItemRackButton"..id]
button:ClearAllPoints()
if ItemRack.NewAnchor and ItemRackUser.Buttons[ItemRack.NewAnchor] then
ItemRackUser.Buttons[id].Side = "LEFT"
ItemRackUser.Buttons[id].DockTo = ItemRack.NewAnchor
local dockinfo = ItemRack.DockInfo[ItemRackUser.Buttons[id].Side]
button:SetPoint("LEFT","ItemRackButton"..ItemRack.NewAnchor,"RIGHT",dockinfo.xoff*(ItemRackUser.ButtonSpacing or 4),dockinfo.yoff*(ItemRackUser.ButtonSpacing or 4))
else
button:SetPoint("CENTER",UIParent,"CENTER")
end
ItemRack.NewAnchor = id
_G["ItemRackButton"..id.."Icon"]:SetTexture(ItemRack.GetTextureBySlot(id))
button:Show()
ItemRack.UpdateButtonCooldowns()
if id==20 then
ItemRack.UpdateCurrentSet()
if ItemRack.ReflectEventsRunning then
ItemRack.ReflectEventsRunning()
end
end
end
 
function ItemRack.RemoveButton(id)
if InCombatLockdown() then
ItemRack.Print("Sorry, you can't add or remove buttons during combat.")
return
end
local child,xpos,ypos
local dockedTo = ItemRackUser.Buttons[id].DockedTo
for i in pairs(ItemRackUser.Buttons) do
if ItemRackUser.Buttons[i].DockTo == id then
ItemRackUser.Buttons[i].DockTo = nil
ItemRackUser.Buttons[i].Side = nil
child = _G["ItemRackButton"..i]
xpos,ypos = child:GetLeft(),child:GetTop()
child:ClearAllPoints()
child:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",xpos,ypos)
ItemRackUser.Buttons[i].Left = xpos
ItemRackUser.Buttons[i].Top = ypos
end
end
ItemRack.NewAnchor = nil
ItemRackUser.Buttons[id] = nil
_G["ItemRackButton"..id]:Hide()
end
 
function ItemRack.ToggleButton(id)
if InCombatLockdown() then
ItemRack.Print("Sorry, you can't add or remove buttons during combat.")
elseif ItemRackUser.Buttons[id] then
ItemRack.RemoveButton(id)
else
ItemRack.AddButton(id)
end
end
 
--[[ Button Movement ]]
 
function ItemRack.Near(v1,v2)
if v1 and v2 and math.abs(v1-v2)<12 then
return 1
end
end
 
-- which: Main/Menu, side="LEFT"/"TOPRIGHT"/etc, relativeTo=button, corner="TOPLEFT"/"TOPRIGHT"/etc
-- shapes ItemRackMainBracket or ItemRackMenuBracket to a side and draws it there
function ItemRack.MoveBracket(which,side,relativeTo,corner)
local bracket = _G["ItemRack"..which.."Bracket"]
if bracket then
local texture = _G["ItemRack"..which.."BracketTexture"]
local info = ItemRack.BracketInfo[side]
bracket:SetWidth(info[1])
bracket:SetHeight(info[2])
texture:SetTexCoord(info[3],info[4],info[5],info[6])
bracket:ClearAllPoints()
bracket:SetPoint(corner,relativeTo,corner)
bracket:SetParent(relativeTo)
bracket:SetAlpha(1)
bracket:Show()
end
end
 
function ItemRack.HideBrackets()
ItemRackMainBracket:Hide()
ItemRackMenuBracket:Hide()
ItemRack.Docking.Side = nil
ItemRack.Docking.From = nil
ItemRack.Docking.To = nil
end
 
-- returns true if candidate is not already docked to button in a docking chain
function ItemRack.LegalDock(button,candidate)
while ItemRackUser.Buttons[candidate].DockTo do
if ItemRackUser.Buttons[candidate].DockTo==button then
return nil -- candidate is already docked somehow to this button
end
candidate = ItemRackUser.Buttons[candidate].DockTo
end
return 1
end
 
-- return button if it's not docked, or the original button of dock chain if docked
function ItemRack.FindParent(button)
while ItemRackUser.Buttons[button].DockTo do
if not ItemRackUser.Buttons[button].DockTo then
return button
end
button = ItemRackUser.Buttons[button].DockTo
end
return button
end
 
-- while buttons drag, this function periodically lights up docking possibilities
function ItemRack.ButtonsDocking()
 
local button,dock = ItemRack.ButtonMoving
local buttonID = button:GetID()
local near = ItemRack.Near
if not button then
ItemRack.StopTimer("ButtonsDocking")
return
end
 
ItemRack.HideBrackets()
 
for i in pairs(ItemRackUser.Buttons) do
dock = _G["ItemRackButton"..i]
if near(button:GetLeft(),dock:GetRight()) and (near(button:GetTop(),dock:GetTop()) or near(button:GetBottom(),dock:GetBottom())) and ItemRack.LegalDock(buttonID,i) then
ItemRack.MoveBracket("Main","LEFT",button,"TOPLEFT")
ItemRack.MoveBracket("Menu","RIGHT",dock,"TOPRIGHT")
ItemRack.Docking.Side = "LEFT"
elseif near(button:GetRight(),dock:GetLeft()) and (near(button:GetTop(),dock:GetTop()) or near(button:GetBottom(),dock:GetBottom())) and ItemRack.LegalDock(buttonID,i) then
ItemRack.MoveBracket("Main","LEFT",dock,"TOPLEFT")
ItemRack.MoveBracket("Menu","RIGHT",button,"TOPRIGHT")
ItemRack.Docking.Side = "RIGHT"
elseif near(button:GetTop(),dock:GetBottom()) and (near(button:GetLeft(),dock:GetLeft()) or near(button:GetRight(),dock:GetRight())) and ItemRack.LegalDock(buttonID,i) then
ItemRack.MoveBracket("Main","TOP",button,"TOPLEFT")
ItemRack.MoveBracket("Menu","BOTTOM",dock,"BOTTOMLEFT")
ItemRack.Docking.Side = "TOP"
elseif near(button:GetBottom(),dock:GetTop()) and (near(button:GetLeft(),dock:GetLeft()) or near(button:GetRight(),dock:GetRight())) and ItemRack.LegalDock(buttonID,i) then
ItemRack.MoveBracket("Main","TOP",dock,"TOPLEFT")
ItemRack.MoveBracket("Menu","BOTTOM",button,"BOTTOMLEFT")
ItemRack.Docking.Side = "BOTTOM"
end
 
if ItemRack.Docking.Side then
ItemRack.Docking.From = buttonID
ItemRack.Docking.To = i
break
end
end
end
 
function ItemRack.StartMovingButton(self)
if ItemRackUser.Locked=="ON" then return end
if IsShiftKeyDown() then
ItemRack.ButtonMoving = self
else
ItemRack.ButtonMoving = _G["ItemRackButton"..ItemRack.FindParent(self:GetID())]
end
for i in pairs(ItemRackUser.Buttons) do -- highlight parent buttons
if not ItemRackUser.Buttons[i].DockTo then
_G["ItemRackButton"..i]:LockHighlight()
end
end
ItemRack.ButtonMoving:StartMoving()
ItemRack.StartTimer("ButtonsDocking")
end
 
function ItemRack.StopMovingButton(self)
if ItemRackUser.Locked=="ON" then return end
ItemRack.StopTimer("ButtonsDocking")
ItemRack.ButtonMoving:StopMovingOrSizing()
ItemRack.NewAnchor = nil
local buttonID = ItemRack.ButtonMoving:GetID()
if ItemRack.Docking.Side then
ItemRack.ButtonMoving:ClearAllPoints()
local dockinfo = ItemRack.DockInfo[ItemRack.Docking.Side]
ItemRack.ButtonMoving:SetPoint(ItemRack.Docking.Side,"ItemRackButton"..ItemRack.Docking.To,ItemRack.OppositeSide[ItemRack.Docking.Side],dockinfo.xoff*(ItemRackUser.ButtonSpacing or 4),dockinfo.yoff*(ItemRackUser.ButtonSpacing or 4))
ItemRackUser.Buttons[buttonID].DockTo=ItemRack.Docking.To
ItemRackUser.Buttons[buttonID].Side=ItemRack.Docking.Side
ItemRackUser.Buttons[buttonID].Left = nil
ItemRackUser.Buttons[buttonID].Top = nil
else
ItemRackUser.Buttons[buttonID].DockTo=nil
ItemRackUser.Buttons[buttonID].Side=nil
ItemRackUser.Buttons[buttonID].Left = ItemRack.ButtonMoving:GetLeft()
ItemRackUser.Buttons[buttonID].Top = ItemRack.ButtonMoving:GetTop()
end
for i in pairs(ItemRackUser.Buttons) do
_G["ItemRackButton"..i]:UnlockHighlight()
end
ItemRack.HideBrackets()
end
 
function ItemRack.ConstructLayout()
 
if InCombatLockdown() then
table.insert(ItemRack.RunAfterCombat,"ConstructLayout")
return
end
local button,dockinfo
 
-- flag all buttons to be drawn
for i in pairs(ItemRackUser.Buttons) do
ItemRackUser.Buttons[i].needsDrawn = 1
end
 
-- draw undocked buttons first
for i in pairs(ItemRackUser.Buttons) do
if ItemRackUser.Buttons[i].needsDrawn and not ItemRackUser.Buttons[i].DockTo then
-- button = ItemRack.CreateButton(ItemRackUser.Buttons[i].name,i,ItemRackUser.Buttons[i].type)
button = _G["ItemRackButton"..i]
ItemRackUser.Buttons[i].needsDrawn = nil
button:ClearAllPoints()
if ItemRackUser.Buttons[i].Left then
button:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",ItemRackUser.Buttons[i].Left,ItemRackUser.Buttons[i].Top)
else
button:SetPoint("CENTER","UIParent","CENTER")
end
button:Show()
end
end
local done
-- iterate over docked buttons in the order they're docked
while not done do
done = 1
for i in pairs(ItemRackUser.Buttons) do
if ItemRackUser.Buttons[i].needsDrawn and not ItemRackUser.Buttons[ItemRackUser.Buttons[i].DockTo].needsDrawn then -- if this button's DockTo is already drawn
-- button = ItemRack.CreateButton(ItemRackUser.Buttons[i].name,i,ItemRackUser.Buttons[i].type)
button = _G["ItemRackButton"..i]
ItemRackUser.Buttons[i].needsDrawn = nil
button:ClearAllPoints()
dockinfo = ItemRack.DockInfo[ItemRackUser.Buttons[i].Side]
button:SetPoint(ItemRackUser.Buttons[i].Side,"ItemRackButton"..ItemRackUser.Buttons[i].DockTo,ItemRack.OppositeSide[ItemRackUser.Buttons[i].Side],dockinfo.xoff*(ItemRackUser.ButtonSpacing or 4),dockinfo.yoff*(ItemRackUser.ButtonSpacing or 4))
button:Show()
done = nil
end
end
end
ItemRack.UpdateButtons()
end
 
-- updates icons for equipment slots by grabbing the texture directly from the player's worn items
function ItemRack.UpdateButtons()
for i in pairs(ItemRackUser.Buttons) do
if i<20 then
_G["ItemRackButton"..i.."Icon"]:SetTexture(ItemRack.GetTextureBySlot(i))
end
--[[ ranged ammo is now infinite, so the below ammo count updater has been commented out
if i==0 then --ranged "ammo" slot
local baseID = ItemRack.GetIRString(ItemRack.GetID(0),true) --get the ItemRack-style ID for the ammo item in inventory slot 0 (ranged ammo) and convert it to just its baseID
if baseID~=0 then -- verify that we properly have the ammo item's baseID
ItemRackButton0Count:SetText(GetItemCount(baseID)) -- write the ammo count on top of the slot
else
ItemRackButton0Count:SetText("") -- clear the ammo count since there is no ammo in the slot
end
end]]
end
ItemRack.UpdateCurrentSet()
ItemRack.UpdateButtonCooldowns()
end
 
--[[ Menu ]]
 
function ItemRack.DockMenuToButton(button)
if (button==13 or button==14) and ItemRackSettings.TrinketMenuMode=="ON" and ItemRackUser.Buttons[13] and ItemRackUser.Buttons[14] then
button = 13 + (ItemRackSettings.AnchorOther=="ON" and 1 or 0)
end
 
local parent = ItemRack.FindParent(button)
-- get docking and orientation from parent of this button group, use defaults if none defined
local menuDock = ItemRackUser.Buttons[parent].MenuDock or "BOTTOMLEFT"
local mainDock = ItemRackUser.Buttons[parent].MainDock or "TOPLEFT"
local menuOrient = ItemRackUser.Buttons[parent].MenuOrient or "VERTICAL"
ItemRack.DockWindows(menuDock,_G["ItemRackButton"..button],mainDock,menuOrient,button)
end
 
function ItemRack.OnEnterButton(self)
ItemRack.InventoryTooltip(self)
if ItemRack.IsTimerActive("ButtonsDocking") or (not IsShiftKeyDown() and ItemRackSettings.MenuOnShift=="ON") or ItemRackSettings.MenuOnRight=="ON" then
return -- don't show menu while buttons docking
end
local button = self:GetID()
ItemRack.DockMenuToButton(button)
ItemRack.BuildMenu(button)
end
 
--[[ Menu Docking ]]
 
function ItemRack.MenuFrameOnMouseDown(self,button)
if button=="LeftButton" then
ItemRack.MenuDockingTo = ItemRack.menuMovable
if ItemRack.MenuDockingTo then
for i in pairs(ItemRackUser.Buttons) do
if i~=ItemRack.MenuDockingTo then
_G["ItemRackButton"..i]:SetAlpha(ItemRackUser.Alpha/3)
end
end
ItemRackMenuFrame:StartMoving()
ItemRack.StartTimer("MenuDocking")
end
end
end
 
function ItemRack.MenuFrameOnMouseUp(self,button)
if button=="LeftButton" and ItemRack.MenuDockingTo then
ItemRack.StopTimer("MenuDocking")
for i in pairs(ItemRackUser.Buttons) do
_G["ItemRackButton"..i]:SetAlpha(ItemRackUser.Alpha)
end
local parent = ItemRack.FindParent(ItemRack.MenuDockingTo)
ItemRackUser.Buttons[parent].MenuDock = ItemRack.menuDock
ItemRackUser.Buttons[parent].MainDock = ItemRack.mainDock
ItemRack.DockMenuToButton(ItemRack.MenuDockingTo)
ItemRack.BuildMenu()
ItemRack.MenuDockingTo = nil
ItemRackMenuFrame:StopMovingOrSizing()
ItemRack.HideBrackets()
elseif button=="RightButton" then
if ItemRack.menuMovable then
local parent = ItemRack.FindParent(ItemRack.menuMovable)
local button = ItemRackUser.Buttons[parent]
button.MenuOrient = (button.MenuOrient=="VERTICAL") and "HORIZONTAL" or "VERTICAL"
ItemRack.DockMenuToButton(ItemRack.menuMovable)
ItemRack.BuildMenu()
end
end
end
 
function ItemRack.MenuDocking()
 
local main = _G["ItemRackButton"..ItemRack.MenuDockingTo]
local menu = ItemRackMenuFrame
local mainscale = main:GetEffectiveScale()
local menuscale = menu:GetEffectiveScale()
local near = ItemRack.Near
 
if near(main:GetRight()*mainscale,menu:GetLeft()*menuscale) then
if near(main:GetTop()*mainscale,menu:GetTop()*menuscale) then
ItemRack.mainDock = "TOPRIGHT"
ItemRack.menuDock = "TOPLEFT"
elseif near(main:GetBottom()*mainscale,menu:GetBottom()*menuscale) then
ItemRack.mainDock = "BOTTOMRIGHT"
ItemRack.menuDock = "BOTTOMLEFT"
end
elseif near(main:GetLeft()*mainscale,menu:GetRight()*menuscale) then
if near(main:GetTop()*mainscale,menu:GetTop()*menuscale) then
ItemRack.mainDock = "TOPLEFT"
ItemRack.menuDock = "TOPRIGHT"
elseif near(main:GetBottom()*mainscale,menu:GetBottom()*menuscale) then
ItemRack.mainDock = "BOTTOMLEFT"
ItemRack.menuDock = "BOTTOMRIGHT"
end
elseif near(main:GetRight()*mainscale,menu:GetRight()*menuscale) then
if near(main:GetTop()*mainscale,menu:GetBottom()*menuscale) then
ItemRack.mainDock = "TOPRIGHT"
ItemRack.menuDock = "BOTTOMRIGHT"
elseif near(main:GetBottom()*mainscale,menu:GetTop()*menuscale) then
ItemRack.mainDock = "BOTTOMRIGHT"
ItemRack.menuDock = "TOPRIGHT"
end
elseif near(main:GetLeft()*mainscale,menu:GetLeft()*menuscale) then
if near(main:GetTop()*mainscale,menu:GetBottom()*menuscale) then
ItemRack.mainDock = "TOPLEFT"
ItemRack.menuDock = "BOTTOMLEFT"
elseif near(main:GetBottom()*mainscale,menu:GetTop()*menuscale) then
ItemRack.mainDock = "BOTTOMLEFT"
ItemRack.menuDock = "TOPLEFT"
end
end
ItemRack.MoveBracket("Main",ItemRack.mainDock,main,ItemRack.mainDock)
ItemRack.MoveBracket("Menu",ItemRack.menuDock,menu,ItemRack.menuDock)
end
 
--[[ Using buttons ]]
 
function ItemRack.ButtonPostClick(self,button)
self:SetChecked(0)
local id = self:GetID()
if button=="RightButton" and ItemRackSettings.MenuOnRight=="ON" then
if ItemRackMenuFrame:IsVisible() and ItemRack.menuOpen==id then
ItemRackMenuFrame:Hide()
else
ItemRack.DockMenuToButton(id)
ItemRack.BuildMenu(id)
end
elseif IsShiftKeyDown() then
if id<20 then
if ChatFrame1EditBox:IsVisible() then
ChatFrame1EditBox:Insert(GetInventoryItemLink("player",id))
end
elseif ItemRackUser.CurrentSet then
ItemRack.UnequipSet(ItemRackUser.CurrentSet)
end
elseif IsAltKeyDown() then
if id<20 and ItemRackSettings.DisableAltClick=="OFF" then
if not ItemRackUser.Queues[id] then
LoadAddOn("ItemRackOptions")
ItemRackOptFrame:Show()
ItemRackOpt.TabOnClick(self,4)
ItemRackOpt.SetupQueue(id)
end
ItemRackUser.QueuesEnabled[id] = not ItemRackUser.QueuesEnabled[id] and 1 or nil
if ItemRackOptSubFrame7 and ItemRackOptSubFrame7:IsVisible() and ItemRackOpt.SelectedSlot==id then
ItemRackOptQueueEnable:SetChecked(ItemRackUser.QueuesEnabled[id])
end
ItemRack.UpdateCombatQueue()
elseif id==20 then
ItemRack.ToggleEvents(self)
end
elseif id<20 then
ItemRack.ReflectItemUse(id)
elseif id==20 then
if button=="LeftButton" and ItemRackUser.CurrentSet then
if ItemRackSettings.EquipToggle=="ON" then
ItemRack.ToggleSet(ItemRackUser.CurrentSet)
else
ItemRack.EquipSet(ItemRackUser.CurrentSet)
end
else
ItemRack.ToggleOptions(self,2) -- summon set options
end
end
end
 
function ItemRack.ReflectClickedUpdate()
local reflect = ItemRack.ReflectClicked
local found
for i in pairs(reflect) do
reflect[i] = reflect[i] - .2
if reflect[i]<0 then
_G["ItemRackButton"..i]:SetChecked(0)
reflect[i] = nil
end
found = 1
end
if not found then
ItemRack.StopTimer("ReflectClickedUpdate")
end
end
 
function ItemRack.UpdateButtonCooldowns()
for i in pairs(ItemRackUser.Buttons) do
if i<20 then
CooldownFrame_SetTimer(_G["ItemRackButton"..i.."Cooldown"],GetInventoryItemCooldown("player",i))
end
end
ItemRack.WriteButtonCooldowns()
end
 
function ItemRack.WriteButtonCooldowns()
if ItemRackSettings.CooldownCount=="ON" then
for i in pairs(ItemRackUser.Buttons) do
ItemRack.WriteCooldown(_G["ItemRackButton"..i.."Time"],GetInventoryItemCooldown("player",i))
end
end
end
 
function ItemRack.UpdateButtonLocks()
local isLocked
for i in pairs(ItemRackUser.Buttons) do
if i<20 then
isLocked = IsInventoryItemLocked(i)
alreadyLocked = ItemRack.LockedButtons[i]
if isLocked and not alreadyLocked then
_G["ItemRackButton"..i.."Icon"]:SetDesaturated(1)
ItemRack.LockedButtons[i] = 1
elseif not isLocked and alreadyLocked then
_G["ItemRackButton"..i.."Icon"]:SetDesaturated(0)
ItemRack.LockedButtons[i] = nil
end
end
end
end
 
--[[ Button menu ]]
 
function ItemRack.ButtonMenuOnClick(self)
 
if self==ItemRackButtonMenuClose then
ItemRack.RemoveButton(ItemRack.menuOpen)
elseif self==ItemRackButtonMenuOptions then
ItemRack.ToggleOptions(self)
elseif self==ItemRackButtonMenuLock then
ItemRackUser.Locked = ItemRackUser.Locked=="ON" and "OFF" or "ON"
ItemRack.ReflectLock()
elseif self==ItemRackButtonMenuQueue then
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOptFrame:Hide()
else
LoadAddOn("ItemRackOptions")
ItemRackOptFrame:Show()
if ItemRack.menuOpen<20 then
ItemRackOpt.TabOnClick(self,4)
ItemRackOpt.SetupQueue(ItemRack.menuOpen)
else
ItemRackOpt.TabOnClick(self,3)
end
end
end
end
 
function ItemRack.ReflectMainScale(changing)
if InCombatLockdown() then
table.insert(ItemRack.RunAfterCombat,"ReflectMainScale")
return
end
local scale = ItemRackUser.MainScale or 1
local button
for i=0,20 do
button = ItemRackUser.Buttons[i]
if not changing or not button or not button.Left then
_G["ItemRackButton"..i]:SetScale(scale)
else
local frame = _G["ItemRackButton"..i]
local oldscale = frame:GetScale() or 1
local framex = frame:GetLeft()*oldscale
local framey = frame:GetTop()*oldscale
frame:SetScale(scale)
frame:SetPoint("TOPLEFT",UIParent,"BOTTOMLEFT",framex/scale,framey/scale)
ItemRackUser.Buttons[i].Left = framex/scale -- frame:GetLeft()
ItemRackUser.Buttons[i].Top = framey/scale -- frame:GetTop()
end
end
end
 
function ItemRack.ReflectMenuOnRight()
for i=0,20 do
_G["ItemRackButton"..i]:SetAttribute("slot2",ItemRackSettings.MenuOnRight=="ON" and ATTRIBUTE_NOOP or nil)
end
end
 
function ItemRack.ReflectHideOOC()
for i in pairs(ItemRackUser.Buttons) do
if ItemRackSettings.HideOOC=="ON" and not ItemRack.inCombat then
_G["ItemRackButton"..i]:Hide()
else
_G["ItemRackButton"..i]:Show()
end
end
end
 
--[[ Cooldowns ]]
 
function ItemRack.WriteCooldown(where,start,duration)
local cooldown = duration - (GetTime()-start)
if start==0 or ItemRackSettings.CooldownCount=="OFF" then
where:SetText("")
elseif cooldown<3 and not where:GetText() then
-- this is a global cooldown. don't display it. not accurate but at least not annoying
else
where:SetText((cooldown<(ItemRackSettings.Cooldown90=="ON" and 90 or 60) and math.floor(cooldown+.5).." s") or (cooldown<3600 and math.ceil(cooldown/60).." m") or math.ceil(cooldown/3600).." h")
end
end
 
--[[ Key binding display ]]
 
function ItemRack.KeyBindingsChanged()
local key
for i in pairs(ItemRackUser.Buttons) do
if ItemRackSettings.ShowHotKeys=="ON" then
key = GetBindingKey("CLICK ItemRackButton"..i..":LeftButton")
_G["ItemRackButton"..i.."HotKey"]:SetText(GetBindingText(key or "",nil,1))
else
_G["ItemRackButton"..i.."HotKey"]:SetText("")
end
end
end
 
function ItemRack.ResetButtons()
for i in pairs(ItemRackUser.Buttons) do
ItemRack.RemoveButton(i)
end
ItemRackUser.Alpha = 1
ItemRackUser.Locked = "OFF"
ItemRackUser.MainScale = 1
ItemRackUser.MenuScale = .85
if ItemRackOpt then
ItemRackOpt.UpdateSlider("Alpha")
ItemRackOpt.UpdateSlider("MenuScale")
ItemRackOpt.UpdateSlider("MainScale")
end
end
Property changes : Added: svn:executable +
ItemRack/ItemRack.lua New file
0,0 → 1,1978
ItemRack = {}
 
local disable_delayed_swaps = nil -- temporary. change nil to 1 to stop attempting to delay set swaps while casting
 
ItemRack.Version = 2.72
 
ItemRackUser = {
Sets = {}, -- user's sets
ItemsUsed = {}, -- items that have been used (for notify purposes)
Hidden = {}, -- items the user chooses to hide in menus
Queues = {}, -- item auto queue sorts
QueuesEnabled = {}, -- which queues are enabled
Locked = "OFF", -- buttons locked
EnableEvents = "ON", -- whether all events enabled
EnableQueues = "ON", -- whether all auto queues enabled
ButtonSpacing = 4, -- padding between docked buttons
Alpha = 1, -- alpha of buttons
MainScale = 1, -- scale of the dockable buttons
MenuScale = .85, -- scale of the menu in relation to docked buttons
SetMenuWrap = "OFF", -- whether user defines when to wrap the menu
SetMenuWrapValue = 3, -- when to wrap the menu if user defined
}
 
ItemRackSettings = {
MenuOnShift = "OFF", -- open menus on shift only
MenuOnRight = "OFF", -- open menus on right-click only
HideOOC = "OFF", -- hide dockable buttons when out of combat
Notify = "ON", -- notify when a used item comes off cooldown
NotifyThirty = "OFF", -- notify when a used item reaches 30 seconds cooldown
NotifyChatAlso = "OFF", -- send cooldown notifications to chat also
ShowTooltips = "ON", -- show all itemrack tooltips
TinyTooltips = "OFF", -- whether to condense tooltips to most important info
TooltipFollow = "OFF", -- whether tooltip follows pointer
CooldownCount = "OFF", -- whether cooldowns displayed numerically over buttons
LargeNumbers = "OFF", -- whether cooldown numbers displayed in large font
AllowEmpty = "ON", -- allow empty slot as a choice in menus
HideTradables = "OFF", -- allow non-soulbound gear to appear in menu
AllowHidden = "ON", -- allow the ability to hide items/sets in the menu with alt+click
ShowMinimap = "ON", -- whether to show the minimap button
SquareMinimap = "OFF", -- whether to position minimap button as if on a square minimap
TrinketMenuMode = "OFF", -- whether to merge top/bottom trinkets to one menu (leftclick=top,rightclick=bottom)
AnotherOther = "OFF", -- whether to dock the merged trinket menu to bottom trinket
EquipToggle = "OFF", -- whether to toggle equipping a set when choosing to equip it
ShowHotKeys = "OFF", -- show key bindings on dockable buttons
Cooldown90 = "OFF", -- whether to count cooldown in seconds at 90 instead of 60
EquipOnSetPick = "OFF", -- whether to equip a set when picked in the set tab of options
MinimapTooltip = "ON", -- whether to display the minimap button tooltip to explain clicks
CharacterSheetMenus = "ON", -- whether to display slot menus on mouseover of the character sheet
DisableAltClick = "OFF", -- whether to disable Alt+click from toggling auto queue (to allow self cast through)
}
 
-- these are default items with non-standard behavior
-- keep = 1/nil whether to suspend auto queue while equipped
-- priority = 1/nil whether to equip as it comes off cooldown even if equipped is off cooldown waiting to be used
-- delay = time(seconds) after use before swapping out
ItemRackItems = {
["11122"] = { keep=1 }, -- carrot on a stick
["13209"] = { keep=1 }, -- seal of the dawn
["19812"] = { keep=1 }, -- rune of the dawn
["12846"] = { keep=1 }, -- argent dawn commission
["25653"] = { keep=1 }, -- riding crop
}
 
ItemRack.NoTitansGrip = {
["Polearms"] = 1,
["Fishing Poles"] = 1,
["Staves"] = 1
}
 
ItemRack.Menu = {}
ItemRack.LockList = {} -- index -2 to 11, flag whether item is tagged already for swap
ItemRack.BankSlots = { -1,5,6,7,8,9,10,11 }
ItemRack.KnownItems = {} -- cache of known item locations for fast lookup
 
ItemRack.SlotInfo = {
[0] = { name="AmmoSlot", real="Ammo", INVTYPE_AMMO=1 },
[1] = { name="HeadSlot", real="Head", INVTYPE_HEAD=1 },
[2] = { name="NeckSlot", real="Neck", INVTYPE_NECK=1 },
[3] = { name="ShoulderSlot", real="Shoulder", INVTYPE_SHOULDER=1 },
[4] = { name="ShirtSlot", real="Shirt", INVTYPE_BODY=1 },
[5] = { name="ChestSlot", real="Chest", INVTYPE_CHEST=1, INVTYPE_ROBE=1 },
[6] = { name="WaistSlot", real="Waist", INVTYPE_WAIST=1 },
[7] = { name="LegsSlot", real="Legs", INVTYPE_LEGS=1 },
[8] = { name="FeetSlot", real="Feet", INVTYPE_FEET=1 },
[9] = { name="WristSlot", real="Wrist", INVTYPE_WRIST=1 },
[10] = { name="HandsSlot", real="Hands", INVTYPE_HAND=1 },
[11] = { name="Finger0Slot", real="Top Finger", INVTYPE_FINGER=1, other=12 },
[12] = { name="Finger1Slot", real="Bottom Finger", INVTYPE_FINGER=1, other=11 },
[13] = { name="Trinket0Slot", real="Top Trinket", INVTYPE_TRINKET=1, other=14 },
[14] = { name="Trinket1Slot", real="Bottom Trinket", INVTYPE_TRINKET=1, other=13 },
[15] = { name="BackSlot", real="Cloak", INVTYPE_CLOAK=1 },
[16] = { name="MainHandSlot", real="Main hand", INVTYPE_WEAPONMAINHAND=1, INVTYPE_2HWEAPON=1, INVTYPE_WEAPON=1, other=17 },
[17] = { name="SecondaryHandSlot", real="Off hand", INVTYPE_WEAPON=1, INVTYPE_WEAPONOFFHAND=1, INVTYPE_SHIELD=1, INVTYPE_HOLDABLE=1, other=16 },
[18] = { name="RangedSlot", real="Ranged", INVTYPE_RANGED=1, INVTYPE_THROWN=1, INVTYPE_RANGEDRIGHT=1, INVTYPE_RELIC=1 },
[19] = { name="TabardSlot", real="Tabard", INVTYPE_TABARD=1 },
}
 
ItemRack.DockInfo = { -- docking-dependent values
LEFT = { xoff=1, yoff=0, menuSide="TOP", menuDir="TOP", orient="VERT", xadd=40, yadd=0 },
RIGHT = { xoff=-1, yoff=0, menuSide="TOP", menuDir="TOP", orient="VERT", xadd=40, yadd=0 },
TOP = { xoff=0, yoff=-1, menuSide="LEFT", menuDir="LEFT", orient="HORZ", xadd=0, yadd=40 },
BOTTOM = { xoff=0, yoff=1, menuSide="LEFT", menuDir="LEFT", orient="HORZ", xadd=0, yadd=40 },
TOPRIGHTTOPLEFT = { xoff=0, yoff=8, xdir=1, ydir=-1, xstart=8, ystart=-8 },
BOTTOMRIGHTBOTTOMLEFT = { xoff=0, yoff=-8, xdir=1, ydir=1, xstart=8, ystart=44 },
TOPLEFTTOPRIGHT = { xoff=0, yoff=8, xdir=-1, ydir=-1, xstart=-44, ystart=-8 },
BOTTOMLEFTBOTTOMRIGHT = { xoff=0, yoff=-8, xdir=-1, ydir=1, xstart=-44, ystart=44 },
TOPRIGHTBOTTOMRIGHT = { xoff=8, yoff=0, xdir=-1, ydir=1, xstart=-44, ystart=44 },
BOTTOMRIGHTTOPRIGHT = { xoff=8, yoff=0, xdir=-1, ydir=-1, xstart=-44, ystart=-8 },
TOPLEFTBOTTOMLEFT = { xoff=-8, yoff=0, xdir=1, ydir=1, xstart=8, ystart=44 },
BOTTOMLEFTTOPLEFT = { xoff=-8, yoff=0, xdir=1, ydir=-1, xstart=8, ystart=-8 },
}
ItemRack.OppositeSide = { LEFT="RIGHT", RIGHT="LEFT", TOP="BOTTOM", BOTTOM="TOP" }
 
ItemRack.MenuMouseoverFrames = {PaperDollFrame=1,CharacterTrinket1Slot=1} -- frames besides ItemRackMenuFrame that can keep menu open on mouseover
 
ItemRack.CombatQueue = {} -- items waiting to swap in
ItemRack.RunAfterCombat = {} -- functions to run when player drops out of combat
 
-- miscellaneous tooltips ElementName, Line1, Line2
ItemRack.TooltipInfo = {
{"ItemRackButtonMenuLock","Lock Buttons","Toggle locked state to prevent buttons/menus from moving and to hide borders and control buttons.\n\nHold ALT while you open a menu to access these control buttons while locked."},
{"ItemRackButtonMenuQueue","Auto Queue","Set up the auto queue for this slot.\n\nAlt+click the slot this menu opened from to toggle its auto queue on/off."},
{"ItemRackButtonMenuOptions","Options","Open Options window to change settings, configure sets or auto queues."},
{"ItemRackButtonMenuClose","Remove","Remove the slot this menu opened from."},
{"ItemRackOptSetsHideCheckButton","Hide Set","Check this to make the set hidden in menus."},
{"ItemRackOptItemStatsPriority","Priority","Check this to make this item auto equip when it comes off cooldown even if the equipped item is off cooldown and waiting to be used."},
{"ItemRackOptItemStatsKeepEquipped","Pause Queue","Check this to suspend the auto queue for this slot until the item is unequipped. (For instance if you have another mod handling the auto equip of a riding crop."},
{"ItemRackOptQueueEnable","Auto Queue This Slot","Check this to allow this slot to auto queue. When an item goes on cooldown, it will swap for an item higher on the list that's off cooldown."},
{"ItemRackOptSetsHideCheckButton","Hide","Hide this set in menus. (Equivalent of Alt+clicking the set in the menu)"},
{"ItemRackOptSetsSaveButton","Save Set","Save this set. Some settings like key binding, cloak/helm visibility and whether it's hidden can only be changed to a saved set."},
{"ItemRackOptSetsDeleteButton","Delete Set","Delete this set definition. If you want to remove it from the menu and may want it again in the future, check 'Hide' to the left."},
{"ItemRackOptSetsBindButton","Bind Key to Set","This will let you bind a key or key combination to equip a set."},
{"ItemRackOptEventNew","New Event","Create a new event."},
{"ItemRackOptEventEdit","Edit Event","Edit this event. Note: if you edit the name and save, it will create a copy of the event with the new name."},
{"ItemRackOptEventDelete","Delete Event","If this event is enabled or has a set associated with it, it will remove the tags and drop it in the list. If this is an untagged event, it will delete it entirely."},
{"ItemRackOptEventEditSave","Save Event","Saves changes to this event. Note: if you edit the name and save, it will create a copy of the event with the new name."},
{"ItemRackOptEventEditCancel","Cancel Changes","Cancel any changes just made to this event and return to event list."},
{"ItemRackOptEventEditBuffAnyMount","Any mount","Checking this will check if any mount is active instead of a specific buff."},
{"ItemRackOptEventEditExpand","Edit in Editor","This will detach the script edit box above to a resizable text editor."},
{"ItemRackFloatingEditorUndo","Undo","Revert the text to its last saved state."},
{"ItemRackFloatingEditorTest","Test","Run the text below as a script to make sure there are no syntax errors. (Script Errors in Interface Options should be enabled to see any)\nNote: This test cannot simulate any condition or test for expected behavior other than the ability to run."},
{"ItemRackFloatingEditorSave","Save Event","Save changes to this event and return to the event list."},
{"ItemRackOptToggleInvAll","Toggle All","This will toggle between selecting all slots and selecting no slots."}
}
 
ItemRack.BankOpen = nil -- 1 if bank is open, nil if not
 
ItemRack.LastCurrentSet = nil -- last known current set
 
function ItemRack.OnLoad(self)
ItemRack.InitTimers()
ItemRack.CreateTimer("OnLogin",ItemRack.OnPlayerLogin,1)
ItemRack.StartTimer("OnLogin")
-- run ItemRack.OnPlayerLogin 1 second after player in world
end
 
ItemRack.EventHandlers = {}
 
function ItemRack.OnEvent(self,event,...)
ItemRack.EventHandlers[event](self,event,...)
end
 
function ItemRack.OnPlayerLogin()
local handler = ItemRack.EventHandlers
handler.ITEM_LOCK_CHANGED = ItemRack.OnItemLockChanged
handler.ACTIONBAR_UPDATE_COOLDOWN = ItemRack.UpdateButtonCooldowns
handler.UNIT_INVENTORY_CHANGED = ItemRack.OnUnitInventoryChanged
handler.UPDATE_BINDINGS = ItemRack.KeyBindingsChanged
handler.PLAYER_REGEN_ENABLED = ItemRack.OnLeavingCombatOrDeath
handler.PLAYER_UNGHOST = ItemRack.OnLeavingCombatOrDeath
handler.PLAYER_ALIVE = ItemRack.OnLeavingCombatOrDeath
handler.PLAYER_REGEN_DISABLED = ItemRack.OnEnteringCombat
handler.BANKFRAME_CLOSED = ItemRack.OnBankClose
handler.BANKFRAME_OPENED = ItemRack.OnBankOpen
handler.UNIT_SPELLCAST_START = ItemRack.OnCastingStart
handler.UNIT_SPELLCAST_STOP = ItemRack.OnCastingStop
handler.UNIT_SPELLCAST_SUCCEEDED = ItemRack.OnCastingStop
handler.UNIT_SPELLCAST_INTERRUPTED = ItemRack.OnCastingStop
handler.CHARACTER_POINTS_CHANGED = ItemRack.UpdateClassSpecificStuff
handler.PLAYER_TALENT_UPDATE = ItemRack.UpdateClassSpecificStuff
handler.ACTIVE_TALENT_GROUP_CHANGED = ItemRack.UpdateClassSpecificStuff
 
ItemRack.InitCore()
ItemRack.InitButtons()
ItemRack.InitEvents()
end
 
function ItemRack.OnCastingStart(self,event,unit)
if unit=="player" then
local _,_,_,_,startTime,endTime = UnitCastingInfo("player")
if endTime-startTime>0 then
ItemRack.NowCasting = 1
end
end
end
 
function ItemRack.OnCastingStop(self,event,unit)
if unit=="player" then
if not ItemRack.NowCasting then
return
else
ItemRack.NowCasting = nil
if #(ItemRack.SetsWaiting)>0 and not ItemRack.AnythingLocked() then
ItemRack.ProcessSetsWaiting()
end
end
end
end
 
function ItemRack.OnItemLockChanged()
ItemRack.StartTimer("LocksChanged")
ItemRack.LocksHaveChanged = 1
end
 
function ItemRack.OnUnitInventoryChanged(self,event,unit)
if unit=="player" then
ItemRack.UpdateButtons()
if ItemRackMenuFrame:IsVisible() then
ItemRack.BuildMenu()
end
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
for i=0,19 do
if not ItemRackOpt.Inv[i].selected then
ItemRackOpt.Inv[i].id = ItemRack.GetID(i)
end
end
ItemRackOpt.UpdateInv()
end
end
end
 
function ItemRack.OnLeavingCombatOrDeath()
if not ItemRack.IsPlayerReallyDead() and next(ItemRack.CombatQueue) then
local combat = ItemRackUser.Sets["~CombatQueue"].equip
local queue = ItemRack.CombatQueue
for i in pairs(combat) do
combat[i] = nil
end
for i in pairs(queue) do
combat[i] = queue[i]
queue[i] = nil
end
ItemRackUser.Sets["~CombatQueue"].oldset = ItemRack.CombatSet
ItemRack.UpdateCombatQueue()
ItemRack.EquipSet("~CombatQueue")
end
if event=="PLAYER_REGEN_ENABLED" then
ItemRack.inCombat = nil
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.ListScrollFrameUpdate()
ItemRackOptSetsBindButton:Enable()
end
if ItemRack.ReflectHideOOC then
ItemRack.ReflectHideOOC()
end
if next(ItemRack.RunAfterCombat) then
for i=1,#(ItemRack.RunAfterCombat) do
ItemRack[ItemRack.RunAfterCombat[i]]()
end
for i=1,#(ItemRack.RunAfterCombat) do
table.remove(ItemRack.RunAfterCombat,i)
end
end
end
end
 
function ItemRack.OnEnteringCombat()
ItemRack.inCombat = 1
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.ListScrollFrameUpdate()
ItemRackOptSetsBindButton:Disable()
end
if ItemRack.ReflectHideOOC then
ItemRack.ReflectHideOOC()
end
end
 
function ItemRack.OnBankClose()
ItemRack.BankOpen = nil
ItemRackMenuFrame:Hide()
end
 
function ItemRack.OnBankOpen()
ItemRack.BankOpen = 1
end
 
function ItemRack.UpdateClassSpecificStuff()
local _,class = UnitClass("player")
 
if class=="WARRIOR" or class=="ROGUE" or class=="HUNTER" or class=="DEATHKNIGHT" then
ItemRack.CanWearOneHandOffHand = 1
end
 
if class=="WARRIOR" then
if (select(5,GetTalentInfo(2,20))>0) then
ItemRack.HasTitansGrip = 1
ItemRack.SlotInfo[17].INVTYPE_2HWEAPON = 1
else
ItemRack.HasTitansGrip = nil
ItemRack.SlotInfo[17].INVTYPE_2HWEAPON = nil
end
end
 
if class=="SHAMAN" then
if (GetPrimaryTalentTree() == 2) then
ItemRack.CanWearOneHandOffHand = 1
else
ItemRack.CanWearOneHandOffHand = nil
end
end
end
 
function ItemRack.InitCore()
ItemRackUser.Sets["~Unequip"] = { equip={} }
ItemRackUser.Sets["~CombatQueue"] = { equip={} }
 
ItemRack.UpdateClassSpecificStuff()
 
ItemRack.DURABILITY_PATTERN = string.match(DURABILITY_TEMPLATE,"(.+) .+/.+") or ""
ItemRack.REQUIRES_PATTERN = string.gsub(ITEM_MIN_SKILL,"%%.",".+")
 
-- pattern splitter by Maldivia http://forums.worldofwarcraft.com/thread.html?topicId=6441208576
local function split(str, t)
local start, stop, single, plural = str:find("\1244(.-):(.-);")
if start then
split(str:sub(1, start - 1) .. single .. str:sub(stop + 1), t)
split(str:sub(1, start - 1) .. plural .. str:sub(stop + 1), t)
else
tinsert(t, (str:gsub("%%d","%%d+")))
end
return t
end
ItemRack.CHARGES_PATTERNS = {}
split(ITEM_SPELL_CHARGES,ItemRack.CHARGES_PATTERNS)
tinsert(ItemRack.CHARGES_PATTERNS,ITEM_SPELL_CHARGES_NONE)
-- for enUS, ItemRack.CHARGES_PATTERNS now {"%d+ Charge","%d+ Charges","No Charges"}
 
ItemRack.CreateTimer("MenuMouseover",ItemRack.MenuMouseover,.25,1)
ItemRack.CreateTimer("TooltipUpdate",ItemRack.TooltipUpdate,1,1)
ItemRack.CreateTimer("CooldownUpdate",ItemRack.CooldownUpdate,1,1)
ItemRack.CreateTimer("MinimapDragging",ItemRack.MinimapDragging,0,1)
ItemRack.CreateTimer("LocksChanged",ItemRack.LocksChanged,.2)
ItemRack.CreateTimer("MinimapShine",ItemRack.MinimapShineUpdate,0,1)
 
for i=-2,11 do
ItemRack.LockList[i] = {}
end
 
hooksecurefunc("UseInventoryItem",ItemRack.newUseInventoryItem)
hooksecurefunc("UseAction",ItemRack.newUseAction)
hooksecurefunc("UseItemByName",ItemRack.newUseItemByName)
hooksecurefunc("PaperDollFrame_OnShow",ItemRack.newPaperDollFrame_OnShow)
 
ItemRackFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
ItemRackFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
ItemRackFrame:RegisterEvent("PLAYER_UNGHOST")
ItemRackFrame:RegisterEvent("PLAYER_ALIVE")
ItemRackFrame:RegisterEvent("BANKFRAME_CLOSED")
ItemRackFrame:RegisterEvent("BANKFRAME_OPENED")
ItemRackFrame:RegisterEvent("CHARACTER_POINTS_CHANGED")
ItemRackFrame:RegisterEvent("PLAYER_TALENT_UPDATE")
if not disable_delayed_swaps then
-- in the event delayed swaps while casting don't work well,
-- make disable_delayed_swaps=1 at top of this file to disable it
ItemRackFrame:RegisterEvent("UNIT_SPELLCAST_START")
ItemRackFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
ItemRackFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
ItemRackFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
end
ItemRack.StartTimer("CooldownUpdate")
ItemRack.MoveMinimap()
ItemRack.ReflectAlpha()
ItemRack.SetSetBindings()
 
SlashCmdList["ItemRack"] = ItemRack.SlashHandler
SLASH_ItemRack1 = "/itemrack"
 
EquipSet = ItemRack.EquipSet -- for convenience in macros/events, shorter names
ToggleSet = ItemRack.ToggleSet
UnequipSet = ItemRack.UnequipSet
IsSetEquipped = ItemRack.IsSetEquipped
 
-- new option defaults to pre-existing settings here
ItemRackSettings.Cooldown90 = ItemRackSettings.Cooldown90 or "OFF" -- 2.14
ItemRackSettings.EquipOnSetPick = ItemRackSettings.EquipOnSetPick or "OFF" -- 2.21
ItemRackUser.SetMenuWrap = ItemRackUser.SetMenuWrap or "OFF" -- 2.21
ItemRackUser.SetMenuWrapValue = ItemRackUser.SetMenuWrapValue or 3 -- 2.21
ItemRackSettings.MinimapTooltip = ItemRackSettings.MinimapTooltip or "ON" -- 2.21
ItemRackSettings.CharacterSheetMenus = ItemRackSettings.CharacterSheetMenus or "ON" -- 2.22
ItemRackSettings.DisableAltClick = ItemRackSettings.DisableAltClick or "OFF" -- 2.23
end
 
function ItemRack.Print(msg)
if msg then
DEFAULT_CHAT_FRAME:AddMessage("|cFFCCCCCCItemRack: |cFFFFFFFF"..msg)
end
end
 
function ItemRack.UpdateCurrentSet()
local texture = ItemRack.GetTextureBySlot(20)
local setname = ItemRackUser.CurrentSet or ""
if ItemRackButton20 and ItemRackUser.Buttons[20] then
ItemRackButton20Icon:SetTexture(texture)
ItemRackButton20Name:SetText(setname)
end
ItemRackMinimapIcon:SetTexture(texture)
if setname ~= ItemRack.LastCurrentSet then
ItemRack.MinimapShineFadeIn()
ItemRack.LastCurrentSet = setname
end
end
 
--[[ Item info gathering ]]
 
function ItemRack.GetTextureBySlot(slot)
if slot==20 then
if ItemRackUser.CurrentSet and ItemRackUser.Sets[ItemRackUser.CurrentSet] then
return ItemRackUser.Sets[ItemRackUser.CurrentSet].icon
else
return "Interface\\AddOns\\ItemRack\\ItemRackIcon"
end
else
local texture = GetInventoryItemTexture("player",slot)
if texture then
return texture
else
_,texture = GetInventorySlotInfo(ItemRack.SlotInfo[slot].name)
return texture
end
end
end
 
-- itemlink/itemstring converter.
-- give it a regular itemLink/itemString and leave the second AND third parameters blank to receive an ItemRack-style ID: "62384:0:4041:4041:0:0:0:0:85:146"
-- give it an ItemRack-style ID and set the second parameter to true to receive the base itemID (ONLY for ItemRack-style IDs!): "62384"
-- give it a regular itemLink/itemString and set the second AND third parameters to true to receive the base itemID (ONLY for regular itemLinks/itemStrings!): "62384"
-- returns 0 on pattern matching failure (happens if no itemstring found/invalid itemstring format)
ItemRack.iSPatternRegularToIR = "item:(".."%-?%d+"..strrep(":%-?%d+", 9)..")" --example: "62384:0:4041:4041:0:0:0:0:85:146", where 85 is the player's level when the itemLink/itemString was captured, in other words it's a regular itemString with the "item:" part removed
ItemRack.iSPatternBaseIDFromIR = "^(%-?%d+)" --this must *only* be used on ItemRack-style IDs, and will return the first field (the itemID), allowing us to do loose item matching
ItemRack.iSPatternBaseIDFromRegular = "item:(%-?%d+)" --this must *only* be used regular itemLinks/itemStrings, and will return the first field (the itemID), allowing us to do loose item matching
function ItemRack.GetIRString(inputString,baseid,regular)
return string.match(inputString or "", (baseid and (regular and ItemRack.iSPatternBaseIDFromRegular or ItemRack.iSPatternBaseIDFromIR) or ItemRack.iSPatternRegularToIR)) or 0
end
 
-- itemrack itemstring updater.
-- takes a saved ItemRack-style ID and returns an updated version with the latest player level injected, which helps us update outdated IDs saved when the player was lower level
function ItemRack.UpdateIRString(itemRackID)
return (string.gsub(itemRackID or "", "%d+:(%-?%d+)$", UnitLevel("player")..":%1")) --note: parenthesis to discard 2nd return value (number of substitutions, which will always be 1)
end
 
-- returns the provided ItemRack-style ID string with "item:" prepended, which turns it into a normal itemstring which we can then use for item lookups, itemlink generation and so on.
-- sure, it's a simple function right now, but if the itemrack ID format above ever needs changing it'll be very easy to update the IRString to ItemString code in this one place.
function ItemRack.IRStringToItemString(itemRackID)
return "item:"..(itemRackID or "")
end
 
-- returns an ItemRack-style ID (62384:0:4041:4041:0:0:0:0:85:146) if an item exists in that slot, or 0 for none
-- bag,nil = inventory slot; bag,slot = container slot
function ItemRack.GetID(bag,slot)
local itemLink
if slot then
itemLink = GetContainerItemLink(bag,slot)
else
itemLink = GetInventoryItemLink("player",bag)
end
return ItemRack.GetIRString(itemLink)
end
 
-- takes two ItemRack-style IDs (one or both of the parameters can be a baseID instead if needed) and returns true if those items share the same base itemID
function ItemRack.SameID(id1,id2)
return ItemRack.GetIRString(id1,true) == ItemRack.GetIRString(id2,true)
end
 
-- takes an ItemRack-style ID and returns the name, texture, equipslot and quality
function ItemRack.GetInfoByID(id)
local name,texture,equip,quality
if id and id~=0 then
name,_,quality,_,_,_,_,_,equip,texture = GetItemInfo(ItemRack.IRStringToItemString(ItemRack.UpdateIRString(id))) --ensure the stored ID is brought up to date, then generate a regular ItemString from it and get the item info
else
name,texture,quality = "(empty)","Interface\\Icons\\INV_Misc_QuestionMark",0 --default response on invalid ID
end
return name,texture,equip,quality
end
 
-- takes an ItemRack-style ID and returns how many items you own with that particular baseID (will not differentiate between enchanted/unenchanted versions, etc)
function ItemRack.GetCountByID(id)
return tonumber(GetItemCount(ItemRack.GetIRString(id,true)))
end
 
-- searches player's inventory&equipment and returns inv,bag,slot of a specific ItemRack-style ID (62384:0:4041:4041:0:0:0:0:85:146) or the first matching item with the same base id (62384) if specific id not found
-- nil,bag,slot = item found in a bag; inv,nil,nil = item found in one of the player's equipment slots; nil,nil,nil = item not found (at least not in equipment/inventory, but it might still exist in bank, we cannot check that though since the player has to be at the bank to read its contents)
-- what it does: it first looks for an EXACT match in the list of "known IDs", which is a cache of the last known location of every item the player has in their equipment and inventory
-- it then looks for an EXACT match in the player's equipment and inventory, and if that fails it looks for a BASEID match in the player's equipment and inventory
function ItemRack.FindItem(id,lock)
 
local locklist, getid, sameid = ItemRack.LockList, ItemRack.GetID, ItemRack.SameID --GetID will be used to look up the ItemRack-style ID for each item we pass over while we loop through the player's equipment/inventory
 
id = ItemRack.UpdateIRString(id) --we must update the incoming ItemRack-style ID to always match the player's current level no matter what, since all WoW ItemStrings contain the player's current level at the time of query, thus if we don't update the level in our OLD ID it won't match the CURRENT ID even if it is the EXACT same item. this simple update ensures that the exact item can be accurately located even if the player has dinged since last saving the set.
 
-- look for item in known items cache first (this cache is frequently rebuilt, such as when clicking the buttons to change a set, AS WELL as when the actual set change takes place, it's a bit overkill in fact, but at least it is up to date -- in fact the entire design is stupid. if the cache is ALWAYS rebuilt EVERY TIME a set change takes place, then the MANUAL search code further down will never take place unless the item is COMPLETELY MISSING. likewise, it means that we're constantly rebuilding a cache of ItemRack-style IDs, and then doing the EXACT same job AGAIN further down, in the "search for..." sections at the bottom of this function... bad design and lots of redundancy, heh. a better design would be to just search through our cache twice, first to look for an exact match, and then to look for a baseID match.)
local knownID = ItemRack.KnownItems[id]
if knownID then
local bag,slot = math.floor(knownID/100),mod(knownID,100)
if bag<0 and not slot then
bag = bag*-1
if id==getid(bag) and (not lock or not locklist[-2][bag]) then
if lock then locklist[-2][bag]=1 end
return bag
end
else
if id==getid(bag,slot) and (not lock or not locklist[bag][slot]) then
if lock then locklist[bag][slot]=1 end
return nil,bag,slot
end
end
end
 
-- search bags
for i=4,0,-1 do
for j=1,GetContainerNumSlots(i) do
if id==getid(i,j) and (not lock or not locklist[i][j]) then
if lock then locklist[i][j]=1 end
return nil,i,j
end
end
end
-- search worn equipment
for i=0,19 do
if id==getid(i) and (not lock or not locklist[-2][i]) then
if lock then locklist[-2][i]=1 end
return i
end
end
-- search bags for base id matches
for i=4,0,-1 do
for j=1,GetContainerNumSlots(i) do
if sameid(id,getid(i,j)) and (not lock or not locklist[i][j]) then
if lock then locklist[i][j]=1 end
return nil,i,j
end
end
end
-- search worn equipment for base id matches
for i=0,19 do
if sameid(id,getid(i)) and (not lock or not locklist[-2][i]) then
if lock then locklist[-2][i]=1 end
return i
end
end
end
 
-- searches player's bank and returns bag,slot of a specific ItemRack-style ID (62384:0:4041:4041:0:0:0:0:85:146) or the first matching item with the same base id (62384) if specific id not found
-- bag,slot = item found in a bank bag; nil, nil = item not found in bank
function ItemRack.FindInBank(id,lock)
 
local locklist, getid, sameid = ItemRack.LockList, ItemRack.GetID, ItemRack.SameID --GetID will be used to look up the ItemRack-style ID for each item we pass over while we loop through the player's bank
 
id = ItemRack.UpdateIRString(id) --just as with the FindItem() patch above, we must ensure that the incoming ID to this function is brought up to date before we start scanning
 
if ItemRack.BankOpen then -- only proceed if bank is open
for _,i in pairs(ItemRack.BankSlots) do -- try to find an exact match at first
if ItemRack.ValidBag(i) then
for j=1,GetContainerNumSlots(i) do
if id==getid(i,j) and (not lock or locklist[i][j]) then
if lock then locklist[i][j]=1 end
return i,j
end
end
end
end
for _,i in pairs(ItemRack.BankSlots) do -- otherwise resort to a loose baseID match
if ItemRack.ValidBag(i) then
for j=1,GetContainerNumSlots(i) do
if sameid(id,getid(i,j)) and (not lock or not locklist[i][j]) then
if lock then locklist[i][j]=1 end
return i,j
end
end
end
end
end
end
 
-- returns true if the bagid (0-4) is a normal "Container", as opposed to quivers and ammo pouches
function ItemRack.ValidBag(bagid)
local baseID,bagtype
if bagid==0 or bagid==-1 then
return 1
else
local invID = ContainerIDToInventoryID(bagid)
baseID = ItemRack.GetIRString(GetInventoryItemLink("player",invID),true,true) --get the baseID for the container
if GetItemFamily(baseID)==0 then
return 1
end
-- if baseID then
-- _,_,_,_,_,_,bagtype = GetItemInfo(baseID)
-- if bagtype=="Bag" or bagtype=="Conteneur" or bagtype=="Beh\195\164lter" then
-- return 1
-- end
-- end
end
end
 
function ItemRack.ClearLockList() -- this function is called very frequently, such as every time you click a set popup button to change the current set, AS WELL as when the actual set change takes place, and will call PopulateKnownItems in order to re-build the cache of current item locations and their itemstrings
for i=-2,11 do
for j in pairs(ItemRack.LockList[i]) do
ItemRack.LockList[i][j] = nil
end
end
if ItemRack.LocksHaveChanged then
ItemRack.LocksHaveChanged = nil
ItemRack.PopulateKnownItems()
end
end
 
function ItemRack.FindSpace()
for i=4,0,-1 do
if ItemRack.ValidBag(i) then
for j=1,GetContainerNumSlots(i) do
if not GetContainerItemLink(i,j) and not ItemRack.LockList[i][j] then
ItemRack.LockList[i][j] = 1
return i,j
end
end
end
end
end
 
function ItemRack.FindBankSpace()
if not ItemRack.BankOpen then return end
for _,i in pairs(ItemRack.BankSlots) do
if ItemRack.ValidBag(i) then
for j=1,GetContainerNumSlots(i) do
if not GetContainerItemLink(i,j) and not ItemRack.LockList[i][j] then
ItemRack.LockList[i][j] = 1
return i,j
end
end
end
end
end
 
function ItemRack.IsRed(which)
local r,g,b = _G["ItemRackTooltipText"..which]:GetTextColor()
if r>.9 and g<.2 and b<.2 then
return 1
end
end
 
function ItemRack.PlayerCanWear(invslot,bag,slot)
local found,lines,txt = false
 
local i=1
while _G["ItemRackTooltipTextLeft"..i] do
-- ClearLines doesn't remove colors, manually remove them
_G["ItemRackTooltipTextLeft"..i]:SetTextColor(0,0,0)
_G["ItemRackTooltipTextRight"..i]:SetTextColor(0,0,0)
i=i+1
end
ItemRackTooltip:SetBagItem(bag,slot)
 
for i=2,ItemRackTooltip:NumLines() do
txt = _G["ItemRackTooltipTextLeft"..i]:GetText()
-- if either left or right text is red and this isn't a Durability x/x line, this item can't be worn
if (ItemRack.IsRed("Left"..i) or ItemRack.IsRed("Right"..i)) and not string.find(txt,ItemRack.DURABILITY_PATTERN) and not string.match(txt,ItemRack.REQUIRES_PATTERN) then
return nil
end
end
 
local _,_,itemType = ItemRack.GetInfoByID(ItemRack.GetID(bag,slot))
if itemType=="INVTYPE_WEAPON" and invslot==17 and not ItemRack.CanWearOneHandOffHand then
-- if this is a One-Hand going to offhand, and player can't wear one-hand offhands, this item can't be worn
return nil
end
 
-- the gammut was run, this item can be worn
return 1
end
 
function ItemRack.IsSoulbound(bag,slot)
ItemRackTooltip:SetBagItem(bag,slot)
for i=2,5 do
text = _G["ItemRackTooltipTextLeft"..i]:GetText()
if text==ITEM_SOULBOUND or text==ITEM_BIND_QUEST or text==ITEM_CONJURED then
return 1
end
end
end
 
-- function happens .2 seconds after last ITEM_LOCK_CHANGE
function ItemRack.LocksChanged()
ItemRack.UpdateButtonLocks()
if ItemRack.SetSwapping then
ItemRack.LockChangedDuringSetSwap()
elseif ItemRackMenuFrame:IsVisible() and ItemRack.BankOpen and not ItemRack.AnythingLocked() then
ItemRackMenuFrame:Hide()
ItemRack.BuildMenu()
elseif #(ItemRack.SetsWaiting)>0 and not ItemRack.AnythingLocked() then
ItemRack.ProcessSetsWaiting()
end
end
 
function ItemRack.PopulateKnownItems()
local known = ItemRack.KnownItems
for i in pairs(known) do
known[i] = nil
end
local id
local getid = ItemRack.GetID
for i=0,19 do
id = getid(i) --grab ItemRack-style ID for every currently worn equipment piece
if id~=0 then
known[id] = i*-1 --we were able to generate a valid ID for this item, so store its location (slot)
end
end
for i=0,4 do
for j=1,GetContainerNumSlots(i) do
id = getid(i,j) --grab ItemRack-style ID for every bag item
if id~=0 then
if IsEquippableItem(ItemRack.GetIRString(id,true)) then --only proceed if this is an equippable item (test against the baseID of the item)
known[id] = i*100+j --we were able to generate a valid ID for this item, so store its location (as a bag container offset)
end
end
end
end
end
 
--[[ Timers ]]
 
function ItemRack.InitTimers()
ItemRack.TimerPool = {}
ItemRack.Timers = {}
end
 
-- ItemRack.CreateTimer(name,func,delay,rep)
 
-- name = arbitrary name to identify this timer
-- func = function to run when the delay finishes
-- delay = time (in seconds) after the timer is started before func is run
-- rep = nil or 1, whether to repeat the delay once it's reached
--
-- The standard use is to create a timer, and then ItemRack.StartTimer
-- when you want to run the delayed function.
--
-- You can do /script ItemRack.TimerDebug() anytime to see all timer status
 
function ItemRack.CreateTimer(name,func,delay,rep)
ItemRack.TimerPool[name] = { func=func,delay=delay,rep=rep,elapsed=delay }
end
 
function ItemRack.IsTimerActive(name)
for i,j in ipairs(ItemRack.Timers) do
if j==name then
return i
end
end
return nil
end
 
function ItemRack.StartTimer(name,delay)
ItemRack.TimerPool[name].elapsed = delay or ItemRack.TimerPool[name].delay
if not ItemRack.IsTimerActive(name) then
table.insert(ItemRack.Timers,name)
ItemRackFrame:Show()
end
end
 
function ItemRack.StopTimer(name)
local idx = ItemRack.IsTimerActive(name)
if idx then
table.remove(ItemRack.Timers,idx)
if table.getn(ItemRack.Timers)<1 then
ItemRackFrame:Hide()
end
end
end
 
function ItemRack.OnUpdate(self,elapsed)
local timerPool
for _,name in ipairs(ItemRack.Timers) do
timerPool = ItemRack.TimerPool[name]
timerPool.elapsed = timerPool.elapsed - elapsed
if timerPool.elapsed < 0 then
timerPool.func(elapsed)
if timerPool.rep then
timerPool.elapsed = timerPool.delay
else
ItemRack.StopTimer(name)
end
end
end
end
 
function ItemRack.TimerDebug()
local on = "|cFF00FF00On"
local off = "|cFFFF0000Off"
DEFAULT_CHAT_FRAME:AddMessage("|cFF44AAFFItemRackFrame is "..(ItemRackFrame:IsVisible() and on or off))
for i in pairs(ItemRack.TimerPool) do
DEFAULT_CHAT_FRAME:AddMessage(i.." is "..(ItemRack.IsTimerActive(i) and on or off))
end
end
 
--[[ Menu ]]
 
function ItemRack.DockWindows(menuDock,relativeTo,mainDock,menuOrient,movable)
ItemRackMenuFrame:ClearAllPoints()
ItemRack.currentDock = mainDock..menuDock
ItemRackMenuFrame:SetPoint(menuDock,relativeTo,mainDock,ItemRack.DockInfo[ItemRack.currentDock].xoff,ItemRack.DockInfo[ItemRack.currentDock].yoff)
ItemRackMenuFrame:SetParent(relativeTo)
ItemRackMenuFrame:SetFrameStrata("HIGH")
ItemRack.mainDock = mainDock
ItemRack.menuDock = menuDock
ItemRack.menuOrient = menuOrient
ItemRack.menuMovable = movable
ItemRack.menuDockedTo = relativeTo:GetName()
ItemRack.MenuMouseoverFrames[relativeTo:GetName()] = 1 -- add frame to mouseover candidates
ItemRack.ReflectLock(not ItemRack.menuMovable)
ItemRack.ReflectMenuScale()
end
 
function ItemRack.AlreadyInMenu(id)
for i=1,#(ItemRack.Menu) do
if ItemRack.Menu[i]==id then
return 1
end
end
end
 
function ItemRack.AddToMenu(itemID)
if ItemRackSettings.AllowHidden=="OFF" or (IsAltKeyDown() or not ItemRack.IsHidden(itemID)) then
table.insert(ItemRack.Menu,itemID)
end
end
 
-- builds a popout menu for slots or set button
-- id = 0-19 for inventory slots, or 20 for set, or nil for last defined slot/set menu (ItemRack.menuOpen)
-- before calling ItemRack.BuildMenu, you should call ItemRack.DockWindows
-- if menuInclude, then also include the worn item(s) in the menu
function ItemRack.BuildMenu(id,menuInclude)
if id then
ItemRack.menuOpen = id
ItemRack.menuInclude = menuInclude
else
id = ItemRack.menuOpen
menuInclude = ItemRack.menuInclude
end
 
local showButtonMenu = (ItemRackButtonMenu and ItemRack.menuMovable) and (IsAltKeyDown() or ItemRackUser.Locked=="OFF")
 
for i in pairs(ItemRack.Menu) do
ItemRack.Menu[i] = nil
end
 
local itemLink,itemID,itemName,equipSlot,itemTexture
 
if id<20 then
if menuInclude then
itemID = ItemRack.GetID(id)
if itemID~=0 then
ItemRack.AddToMenu(itemID)
end
if ItemRack.SlotInfo[id].other then
itemID = ItemRack.GetID(ItemRack.SlotInfo[id].other)
if itemID~=0 then
ItemRack.AddToMenu(itemID)
end
end
end
for i=0,4 do
for j=1,GetContainerNumSlots(i) do
itemID = ItemRack.GetID(i,j)
itemName,itemTexture,equipSlot = ItemRack.GetInfoByID(itemID)
if ItemRack.SlotInfo[id][equipSlot] and ItemRack.PlayerCanWear(id,i,j) and (ItemRackSettings.HideTradables=="OFF" or ItemRack.IsSoulbound(i,j)) then
if id~=0 or not ItemRack.AlreadyInMenu(itemID) then
ItemRack.AddToMenu(itemID)
end
end
end
end
if ItemRack.BankOpen then
for _,i in pairs(ItemRack.BankSlots) do
for j=1,GetContainerNumSlots(i) do
itemID = ItemRack.GetID(i,j)
itemName,itemTexture,equipSlot = ItemRack.GetInfoByID(itemID)
if ItemRack.SlotInfo[id][equipSlot] and ItemRack.PlayerCanWear(id,i,j) and (ItemRackSettings.HideTradables=="OFF" or ItemRack.IsSoulbound(i,j)) then
if id~=0 or not ItemRack.AlreadyInMenu(itemID) then
ItemRack.AddToMenu(itemID)
end
end
end
end
elseif ItemRack.GetID(id)~=0 and ItemRackSettings.AllowEmpty=="ON" then
table.insert(ItemRack.Menu,0)
end
else
for i in pairs(ItemRackUser.Sets) do
if not string.match(i,"^~") then --do not list internal sets, prefixed with ~
ItemRack.AddToMenu(i)
end
table.sort(ItemRack.Menu)
end
end
if showButtonMenu then
table.insert(ItemRack.Menu,"MENU")
end
 
if #(ItemRack.Menu)<1 then
ItemRackMenuFrame:Hide()
else
-- display outward from docking point
local col,row,xpos,ypos = 0,0,ItemRack.DockInfo[ItemRack.currentDock].xstart,ItemRack.DockInfo[ItemRack.currentDock].ystart
local max_cols = 1
local button
 
if ItemRackUser.SetMenuWrap=="ON" then
max_cols = ItemRackUser.SetMenuWrapValue
elseif #(ItemRack.Menu)>24 then
max_cols = 5
elseif #(ItemRack.Menu)>18 then
max_cols = 4
elseif #(ItemRack.Menu)>9 then
max_cols = 3
elseif #(ItemRack.Menu)>4 then
max_cols = 2
end
 
for i=1,#(ItemRack.Menu) do
button = ItemRack.CreateMenuButton(i,ItemRack.Menu[i]) or ItemRackButtonMenu
button:SetPoint("TOPLEFT",ItemRackMenuFrame,ItemRack.menuDock,xpos,ypos)
button:SetFrameLevel(ItemRackMenuFrame:GetFrameLevel()+1)
if ItemRack.menuOrient=="VERTICAL" then
xpos = xpos + ItemRack.DockInfo[ItemRack.currentDock].xdir*40
col = col + 1
if col==max_cols then
xpos = ItemRack.DockInfo[ItemRack.currentDock].xstart
col = 0
ypos = ypos + ItemRack.DockInfo[ItemRack.currentDock].ydir*40
row = row + 1
end
button:Show()
else
ypos = ypos + ItemRack.DockInfo[ItemRack.currentDock].ydir*40
col = col + 1
if col==max_cols then
ypos = ItemRack.DockInfo[ItemRack.currentDock].ystart
col = 0
xpos = xpos + ItemRack.DockInfo[ItemRack.currentDock].xdir*40
row = row + 1
end
button:Show()
end
icon = _G["ItemRackMenu"..i.."Icon"]
if icon then
icon:SetDesaturated(0)
if IsAltKeyDown() and ItemRackSettings.AllowHidden=="ON" and IsAltKeyDown() and ItemRack.IsHidden(ItemRack.Menu[i]) then
icon:SetDesaturated(1)
end
end
end
if showButtonMenu then
table.remove(ItemRack.Menu)
else
ItemRackButtonMenu:Hide()
end
local i = #(ItemRack.Menu)+1
while _G["ItemRackMenu"..i] do
_G["ItemRackMenu"..i]:Hide()
i=i+1
end
 
if col==0 then
row = row-1
end
 
if ItemRack.menuOrient=="VERTICAL" then
ItemRackMenuFrame:SetWidth(12+(max_cols*40))
ItemRackMenuFrame:SetHeight(12+((row+1)*40))
else
ItemRackMenuFrame:SetWidth(12+((row+1)*40))
ItemRackMenuFrame:SetHeight(12+(max_cols*40))
end
 
ItemRack.StartTimer("MenuMouseover")
ItemRackMenuFrame:Show()
ItemRack.UpdateMenuCooldowns()
local count
local border
for i=1,#(ItemRack.Menu) do
border = _G["ItemRackMenu"..i.."Border"]
border:Hide()
if ItemRack.menuOpen==20 then
_G["ItemRackMenu"..i.."Name"]:SetText(ItemRack.Menu[i])
local missing = ItemRack.MissingItems(ItemRack.Menu[i])
if missing==0 then
border:SetVertexColor(1,.1,.1)
border:Show()
elseif missing==1 then
border:SetVertexColor(.3,.5,1)
border:Show()
end
else
_G["ItemRackMenu"..i.."Name"]:SetText("")
if ItemRack.Menu[i]~=0 and ItemRack.GetCountByID(ItemRack.Menu[i])==0 then
border:SetVertexColor(.3,.5,1)
border:Show()
end
end
if ItemRack.menuOpen==0 then
count = ItemRack.GetCountByID(ItemRack.Menu[i])
_G["ItemRackMenu"..i.."Count"]:SetText(count>0 and count or "")
else
_G["ItemRackMenu"..i.."Count"]:SetText("")
end
end
end
end
 
function ItemRack.UpdateMenuCooldowns()
local baseID
for i=1,#(ItemRack.Menu) do
baseID = tonumber(ItemRack.GetIRString(ItemRack.Menu[i],true)) --get baseID and convert it to number to be able to use it in numerical comparisons below
if baseID and baseID>0 and ItemRack.menuOpen<20 then
CooldownFrame_SetTimer(_G["ItemRackMenu"..i.."Cooldown"],GetItemCooldown(baseID))
else
_G["ItemRackMenu"..i.."Cooldown"]:Hide()
end
end
ItemRack.WriteMenuCooldowns()
end
 
function ItemRack.WriteMenuCooldowns()
if ItemRackSettings.CooldownCount=="ON" and ItemRackMenuFrame:IsVisible() then
local baseID
for i=1,#(ItemRack.Menu) do
baseID = ItemRack.GetIRString(ItemRack.Menu[i],true)
if baseID then
ItemRack.WriteCooldown(_G["ItemRackMenu"..i.."Time"],GetItemCooldown(baseID))
else
_G["ItemRackMenu"..i.."Time"]:SetText("")
end
end
end
end
 
function ItemRack.MenuMouseover()
local frame = GetMouseFocus()
if MouseIsOver(ItemRackMenuFrame) or IsShiftKeyDown() or (frame and frame:GetName() and frame:IsVisible() and ItemRack.MenuMouseoverFrames[frame:GetName()]) then
return -- keep menu open if mouse over menu, shift is down or mouse is immediately over a mouseover frame
end
for i in pairs(ItemRack.MenuMouseoverFrames) do
frame = _G[i]
if frame and frame:IsVisible() and MouseIsOver(frame) then
return -- keep menu open if some frame beneath mouse is a mouseover frame
end
end
ItemRack.StopTimer("MenuMouseover")
ItemRackMenuFrame:Hide()
end
 
function ItemRack.MenuOnHide()
ItemRack.menuDockedTo = nil
end
 
function ItemRack.CreateMenuButton(idx,itemID)
if itemID=="MENU" then return end
local button
if not _G["ItemRackMenu"..idx] then
button = CreateFrame("CheckButton","ItemRackMenu"..idx,ItemRackMenuFrame,"ActionButtonTemplate")
button:SetID(idx)
button:SetFrameStrata("HIGH")
-- button:SetFrameLevel(ItemRackMenuFrame:GetFrameLevel()+1)
button:RegisterForClicks("LeftButtonUp","RightButtonUp")
button:SetScript("OnClick",ItemRack.MenuOnClick)
button:SetScript("OnEnter",ItemRack.MenuTooltip)
button:SetScript("OnLeave",ItemRack.ClearTooltip)
CreateFrame("Frame",nil,button,"ItemRackTimeTemplate")
ItemRack.SetFont("ItemRackMenu"..idx)
-- local font = button:CreateFontString("ItemRackMenu"..idx.."Time","OVERLAY","NumberFontNormal")
-- font:SetJustifyH("CENTER")
-- font:SetWidth(36)
-- font:SetHeight(12)
-- font:SetPoint("BOTTOMRIGHT","ItemRackMenu"..idx,"BOTTOMRIGHT")
end
if itemID~=0 then
if ItemRackUser.Sets[itemID] then
_G["ItemRackMenu"..idx.."Icon"]:SetTexture(ItemRackUser.Sets[itemID].icon)
else
local _,texture = ItemRack.GetInfoByID(itemID)
_G["ItemRackMenu"..idx.."Icon"]:SetTexture(texture)
end
else
_G["ItemRackMenu"..idx.."Icon"]:SetTexture(select(2,GetInventorySlotInfo(ItemRack.SlotInfo[ItemRack.menuOpen].name)))
end
return _G["ItemRackMenu"..idx]
end
 
-- takes an ItemRack-style ID, finds the best match in the player's inventory, and puts its ItemLink to the chat editbox.
-- if the item is missing, it uses the ItemRack-style ID as-is to generate a clickable ItemLink from the stored data
function ItemRack.ChatLinkID(itemID)
local inv,bag,slot = ItemRack.FindItem(itemID)
if bag then
ChatFrame1EditBox:Insert(GetContainerItemLink(bag,slot))
elseif inv then
ChatFrame1EditBox:Insert(GetInventoryItemLink("player",inv))
else
local _,itemLink = GetItemInfo(ItemRack.IRStringToItemString(ItemRack.UpdateIRString(itemID))) --ensure the stored ID is brought up to date, then generate a regular ItemString from it and get the item info
if itemLink then
ChatFrame1EditBox:Insert(itemLink)
end
end
end
 
function ItemRack.MenuOnClick(self,button)
self:SetChecked(0)
local item = ItemRack.Menu[self:GetID()]
ItemRack.ClearLockList()
if IsAltKeyDown() and ItemRackSettings.AllowHidden=="ON" then
ItemRack.ToggleHidden(item)
ItemRack.BuildMenu()
elseif IsShiftKeyDown() and ChatFrame1EditBox:IsVisible() then
ItemRack.ChatLinkID(item)
elseif ItemRack.menuInclude then
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.Inv[ItemRack.menuOpen].id = item
ItemRackOpt.Inv[ItemRack.menuOpen].selected = 1
ItemRackOpt.UpdateInv()
ItemRackMenuFrame:Hide()
end
elseif ItemRack.menuOpen<20 then
if ItemRack.BankOpen then
if ItemRack.GetCountByID(item)==0 then
local bankBag,bankSlot = ItemRack.FindInBank(item)
if bankBag then
local freeBag,freeSlot = ItemRack.FindSpace()
if freeBag and not SpellIsTargeting() and not GetCursorInfo() then
PickupContainerItem(bankBag,bankSlot)
PickupContainerItem(freeBag,freeSlot)
else
ItemRack.Print("Not enough room in bags to pull this item from bank.")
end
end
else
local bankBag,bankSlot = ItemRack.FindBankSpace()
if bankBag then
local _,bag,slot = ItemRack.FindItem(item)
if bag and not SpellIsTargeting() and not GetCursorInfo() then
PickupContainerItem(bag,slot)
PickupContainerItem(bankBag,bankSlot)
end
else
ItemRack.Print("Not enough room in bank to put this item.")
end
end
else
if ItemRackSettings.EquipOnSetPick=="ON" and ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.Inv[ItemRack.menuOpen].id = item
ItemRackOpt.Inv[ItemRack.menuOpen].selected = 1
ItemRackOpt.UpdateInv()
end
if ItemRack.menuOpen>=13 and ItemRack.menuOpen<=14 and ItemRackSettings.TrinketMenuMode=="ON" and ItemRackUser.Buttons[13] and ItemRackUser.Buttons[14] then
ItemRack.menuOpen = button=="RightButton" and 14 or 13
end
ItemRack.EquipItemByID(item,ItemRack.menuOpen)
ItemRackMenuFrame:Hide()
end
elseif ItemRack.menuOpen==20 then
if ItemRack.BankOpen then
if ItemRack.MissingItems(item)==1 then
ItemRack.GetBankedSet(item)
else
ItemRack.PutBankedSet(item)
end
elseif ItemRackSettings.EquipToggle=="ON" or IsShiftKeyDown() then
ItemRack.ToggleSet(item)
else
ItemRack.EquipSet(item)
end
if not ItemRack.BankOpen then
ItemRack.StopTimer("MenuMouseover")
ItemRackMenuFrame:Hide()
end
end
end
 
function ItemRack.EquipItemByID(id,slot)
if not id then return end
if not ItemRack.SlotInfo[slot].swappable and (UnitAffectingCombat("player") or ItemRack.IsPlayerReallyDead()) then
ItemRack.AddToCombatQueue(slot,id)
elseif not GetCursorInfo() and not SpellIsTargeting() then
if id~=0 then -- not an empty slot
local _,b,s = ItemRack.FindItem(id)
if b then
local _,_,isLocked = GetContainerItemInfo(b,s)
if not isLocked and not IsInventoryItemLocked(slot) then
-- neither container item nor inventory item locked, perform swap
local _,_,equipSlot = ItemRack.GetInfoByID(id)
if equipSlot~="INVTYPE_2HWEAPON" or (ItemRack.HasTitansGrip and not ItemRack.NoTitansGrip[select(7,GetItemInfo(GetContainerItemLink(b,s))) or ""]) or not GetInventoryItemLink("player",17) then
PickupContainerItem(b,s)
PickupInventoryItem(slot)
else
local bfree,sfree = ItemRack.FindSpace()
if bfree then
PickupInventoryItem(17)
PickupContainerItem(bfree,sfree)
PickupInventoryItem(slot)
PickupContainerItem(b,s)
else
ItemRack.Print("Not enough room to perform swap.")
end
end
end
end
else
local b,s = ItemRack.FindSpace()
if b and not IsInventoryItemLocked(slot) then
PickupInventoryItem(slot)
PickupContainerItem(b,s)
else
ItemRack.Print("Not enough room to perform swap.")
end
end
end
end
 
--[[ Hooks to capture item use outside the mod ]]
 
function ItemRack.ReflectItemUse(id)
if ItemRackUser.Buttons[id] then
_G["ItemRackButton"..id]:SetChecked(1)
ItemRack.ReflectClicked[id] = 1
ItemRack.StartTimer("ReflectClickedUpdate")
end
local baseID = ItemRack.GetIRString(GetInventoryItemLink("player",id),true,true)
if baseID then
ItemRackUser.ItemsUsed[baseID] = 1
end
end
 
function ItemRack.newPaperDollFrame_OnShow()
ItemRack.UpdateCombatQueue()
end
 
function ItemRack.newUseInventoryItem(slot)
ItemRack.ReflectItemUse(slot)
end
 
function ItemRack.newUseAction(slot,cursor,self)
if IsEquippedAction(slot) then
local actionType,actionId = GetActionInfo(slot)
if actionType=="item" then
for i=0,19 do
if tonumber(ItemRack.GetIRString(GetInventoryItemLink("player",i),true,true))==actionId then --compare baseID of given item (converted to number) to actionId
ItemRack.ReflectItemUse(i)
break
end
end
end
end
end
 
function ItemRack.newUseItemByName(name)
for i=0,19 do
if name==GetItemInfo(GetInventoryItemLink("player",i) or 0) then
ItemRack.ReflectItemUse(i)
break
end
end
end
 
--[[ Combat queue ]]
 
function ItemRack.IsPlayerReallyDead()
local dead = UnitIsDeadOrGhost("player")
if select(2,UnitClass("player"))=="HUNTER" then
if GetLocale()=="enUS" and UnitAura("player","Feign Death") then
return nil
else
for i=1,24 do
if select(3,UnitBuff("player",i))=="Interface\\Icons\\Ability_Rogue_FeignDeath" then
return nil
end
end
end
end
return dead
end
 
function ItemRack.AddToCombatQueue(slot,id)
if ItemRack.CombatQueue[slot]==id then
ItemRack.CombatQueue[slot] = nil
else
ItemRack.CombatQueue[slot] = id
end
ItemRack.UpdateCombatQueue()
end
 
function ItemRack.UpdateCombatQueue()
local queue,id
for i in pairs(ItemRackUser.Buttons) do
queue = _G["ItemRackButton"..i.."Queue"]
if ItemRack.CombatQueue[i] then
queue:SetTexture(select(2,ItemRack.GetInfoByID(ItemRack.CombatQueue[i])))
queue:SetAlpha(1)
queue:Show()
elseif ItemRackUser.QueuesEnabled[i] then
queue:SetTexture("Interface\\AddOns\\ItemRack\\ItemRackGear")
queue:SetAlpha(ItemRackUser.EnableQueues=="ON" and 1 or .5)
queue:Show()
elseif i~=20 then
queue:Hide()
end
end
if PaperDollFrame:IsVisible() then
for i=1,19 do
queue = _G["Character"..ItemRack.SlotInfo[i].name.."Queue"]
if ItemRack.CombatQueue[i] then
queue:SetTexture(select(2,ItemRack.GetInfoByID(ItemRack.CombatQueue[i])))
queue:Show()
else
queue:Hide()
end
end
end
end
 
--[[ Tooltip ]]
 
-- request a tooltip of an inventory slot
function ItemRack.InventoryTooltip(self)
local id = self:GetID()
if id==20 then
ItemRack.SetTooltip(self,ItemRackUser.CurrentSet)
else
ItemRack.TooltipOwner = self
ItemRack.TooltipType = "INVENTORY"
ItemRack.TooltipSlot = id
ItemRack.TooltipBag = ItemRack.CombatQueue[id] and ItemRack.GetInfoByID(ItemRack.CombatQueue[id])
ItemRack.StartTimer("TooltipUpdate",0)
end
end
 
-- request a tooltip of a menu item (called when hovering over a button in the popout menu of SET NAMES that comes up when clicking the minimap button or bar addon plugin, this is NOT the "Sets" dropdown INSIDE ItemRack's GUI)
function ItemRack.MenuTooltip(self)
local id = self:GetID()
if ItemRack.menuOpen==20 then
ItemRack.SetTooltip(self,ItemRack.Menu[id])
else
ItemRack.TooltipOwner = self
ItemRack.TooltipType = "BAG"
local invMaybe
invMaybe,ItemRack.TooltipBag,ItemRack.TooltipSlot = ItemRack.FindItem(ItemRack.Menu[self:GetID()])
if ItemRack.TooltipBag and ItemRack.TooltipSlot then
ItemRack.StartTimer("TooltipUpdate",0)
else -- if invMaybe then
ItemRack.IDTooltip(self,ItemRack.Menu[id])
end
end
end
 
-- request a tooltip of a straight item id (called when hovering over items from the currently displayed set inside ItemRack's GUI)
function ItemRack.IDTooltip(self,itemID) --itemID is an ItemRack-style ID
ItemRack.AnchorTooltip(self)
local inv,bag,slot = ItemRack.FindItem(itemID) --try to find the item in the player's equipment and inventory, first tries to find the exact item, then looks for any item with the same baseID
if inv then -- item found in player's worn equipment
GameTooltip:SetInventoryItem("player",inv)
elseif bag then -- item found in player's bags
GameTooltip:SetBagItem(bag,slot)
else --cannot find the item in player's inventory or worn equipment!
bag,slot = ItemRack.FindInBank(itemID) --try to find the item in the player's bank IF they currently have the bank frame open
if bag then -- item found in player's bank
itemID = GetContainerItemLink(bag,slot) -- grab the itemLink from the found item in the player's bank
else -- item is completely missing (no such strict OR baseID found anywhere): it's not in inventory, bank or worn items
itemID = ItemRack.IRStringToItemString(ItemRack.UpdateIRString(itemID)) -- ensure the stored ID is brought up to date, then generate a regular ItemString from it which can be used to display the required tooltip
end
GameTooltip:SetHyperlink(itemID)
end
ItemRack.ShrinkTooltip(self)
GameTooltip:Show()
end
 
function ItemRack.ClearTooltip(self)
GameTooltip:Hide()
ItemRack.StopTimer("TooltipUpdate")
ItemRack.TooltipType = nil
end
 
function ItemRack.AnchorTooltip(owner)
if string.match(ItemRack.menuDockedTo or "","^Character") then
GameTooltip:SetOwner(owner,"ANCHOR_RIGHT")
elseif ItemRackSettings.TooltipFollow=="ON" then
if owner.GetLeft and owner:GetLeft() and owner:GetLeft()<400 then
GameTooltip:SetOwner(owner,"ANCHOR_RIGHT")
else
GameTooltip:SetOwner(owner,"ANCHOR_LEFT")
end
else
GameTooltip_SetDefaultAnchor(GameTooltip,owner)
end
end
 
-- display the tooltip created in the functions above, once a second if item has a cooldown
function ItemRack.TooltipUpdate()
if ItemRack.TooltipType then
local cooldown
ItemRack.AnchorTooltip(ItemRack.TooltipOwner)
if ItemRack.TooltipType=="BAG" then
GameTooltip:SetBagItem(ItemRack.TooltipBag,ItemRack.TooltipSlot)
cooldown = GetContainerItemCooldown(ItemRack.TooltipBag,ItemRack.TooltipSlot)
else
GameTooltip:SetInventoryItem("player",ItemRack.TooltipSlot)
cooldown = GetInventoryItemCooldown("player",ItemRack.TooltipSlot)
end
ItemRack.ShrinkTooltip(ItemRack.TooltipOwner) -- if TinyTooltips on, shrink it
if ItemRack.TooltipType=="INVENTORY" and ItemRack.TooltipBag then
GameTooltip:AddLine("Queued: "..ItemRack.TooltipBag)
end
GameTooltip:Show()
if cooldown==0 then
-- stop updates if this trinket has no cooldown
ItemRack.StopTimer("TooltipUpdate")
ItemRack.TooltipType = nil
end
end
 
end
 
-- normal tooltip for options
function ItemRack.OnTooltip(self,line1,line2)
if ItemRackSettings.ShowTooltips=="ON" then
ItemRack.AnchorTooltip(self)
if line1 then
GameTooltip:AddLine(line1)
GameTooltip:AddLine(line2,.8,.8,.8,1)
GameTooltip:Show()
return
else
local name = self:GetName() or ""
for i=1,#(ItemRack.TooltipInfo) do
if ItemRack.TooltipInfo[i][1]==name and ItemRack.TooltipInfo[i][2] then
GameTooltip:AddLine(ItemRack.TooltipInfo[i][2])
GameTooltip:AddLine(ItemRack.TooltipInfo[i][3],.8,.8,.8,1)
GameTooltip:Show()
return
end
end
end
end
end
 
function ItemRack.ShrinkTooltip(owner)
if ItemRackSettings.TinyTooltips=="ON" then
local r,g,b = GameTooltipTextLeft1:GetTextColor()
local name = GameTooltipTextLeft1:GetText()
local line,charge,durability,cooldown
for i=2,GameTooltip:NumLines() do
line = _G["GameTooltipTextLeft"..i]
if line:IsVisible() then
line = line:GetText() or ""
if string.match(line,ItemRack.DURABILITY_PATTERN) then
durability = line
end
if string.match(line,COOLDOWN_REMAINING) then
cooldown = line
end
for j in pairs(ItemRack.CHARGES_PATTERNS) do
if string.find(line,ItemRack.CHARGES_PATTERNS[j]) then
charge = line
end
end
end
end
ItemRack.AnchorTooltip(owner)
GameTooltip:AddLine(name,r,g,b)
GameTooltip:AddLine(charge,1,1,1)
GameTooltip:AddLine(durability,1,1,1)
GameTooltip:AddLine(cooldown,1,1,1)
end
end
 
function ItemRack.SetTooltip(self,setname)
local set = setname and ItemRackUser.Sets[setname] and ItemRackUser.Sets[setname].equip
if set then
local itemName,itemColor
ItemRack.AnchorTooltip(self)
GameTooltip:AddLine(setname)
if ItemRackSettings.TinyTooltips~="ON" then
for i=0,19 do
if set[i] then
itemName = ItemRack.GetInfoByID(set[i])
if itemName then
if itemName~="(empty)" and ItemRack.GetCountByID(set[i])==0 then
if not ItemRack.FindInBank(set[i]) then
itemColor = "FFFF1111"
else
itemColor = "FF4C80FF"
end
else
itemColor = "FFAAAAAA"
end
GameTooltip:AddLine("|cFFFFFFFF"..ItemRack.SlotInfo[i].real..": |c"..itemColor..itemName)
end
end
end
end
GameTooltip:Show()
end
end
 
--[[ Notify ]]
 
function ItemRack.Notify(msg)
PlaySound("GnomeExploration")
if SCT_Display then -- send via SCT if it exists
SCT_Display(msg,{r=.2,g=.7,b=.9})
elseif SHOW_COMBAT_TEXT=="1" then
CombatText_AddMessage(msg, CombatText_StandardScroll, .2, .7, .9) -- or default UI's SCT
else
-- send vis UIErrorsFrame if neither SCT exists
UIErrorsFrame:AddMessage(msg,.2,.7,.9,1,UIERRORS_HOLD_TIME)
end
if ItemRackSettings.NotifyChatAlso=="ON" then
DEFAULT_CHAT_FRAME:AddMessage("|cff33b2e5"..msg)
end
end
 
function ItemRack.CooldownUpdate()
local inv,bag,slot,start,duration,name,remain
for i in pairs(ItemRackUser.ItemsUsed) do
start,duration = GetItemCooldown(i)
if start and ItemRackUser.ItemsUsed[i]<3 then
ItemRackUser.ItemsUsed[i] = ItemRackUser.ItemsUsed[i] + 1 -- count for 3 seconds before seeing if this is a real cooldown
elseif start then
if start>0 then
remain = duration - (GetTime()-start)
if ItemRackUser.ItemsUsed[i]<5 then
if remain>29 then
ItemRackUser.ItemsUsed[i] = 30 -- first actual cooldown greater than 30 seconds, tag it for 30+0 notify
elseif remain>5 then
ItemRackUser.ItemsUsed[i] = 5 -- first actual cooldown less than 30 but greater than 5, tag for 0 notify
end
end
end
if ItemRackUser.ItemsUsed[i]==30 and start>0 and remain<30 then
if ItemRackSettings.NotifyThirty=="ON" then
name = GetItemInfo(i)
if name then
ItemRack.Notify(name.." ready soon!")
end
end
ItemRackUser.ItemsUsed[i]=5 -- tag for just 0 notify now
elseif ItemRackUser.ItemsUsed[i]==5 and start==0 then
if ItemRackSettings.Notify=="ON" then
name = GetItemInfo(i)
if name then
ItemRack.Notify(name.." ready!")
end
end
end
if start==0 then
ItemRackUser.ItemsUsed[i] = nil
end
end
end
 
-- update cooldown numbers
if ItemRackSettings.CooldownCount=="ON" then
ItemRack.WriteButtonCooldowns()
ItemRack.WriteMenuCooldowns()
end
 
if ItemRack.PeriodicQueueCheck then
ItemRack.PeriodicQueueCheck()
end
end
 
--[[ Character sheet menus ]]
 
ItemRack.oldPaperDollItemSlotButton_OnEnter = PaperDollItemSlotButton_OnEnter
function PaperDollItemSlotButton_OnEnter(self)
ItemRack.oldPaperDollItemSlotButton_OnEnter(self)
if ItemRack.menuDockedTo~=self:GetName() and (ItemRackSettings.MenuOnShift=="OFF" or IsShiftKeyDown()) and ItemRackSettings.CharacterSheetMenus=="ON" then
ItemRack.DockMenuToCharacterSheet(self)
end
end
 
function ItemRack.DockMenuToCharacterSheet(self)
local name = self:GetName()
for i=0,19 do
if name=="Character"..ItemRack.SlotInfo[i].name then
slot = i
end
end
if slot then
if slot==0 or (slot>=16 and slot<=18) then
ItemRack.DockWindows("TOPLEFT",self,"BOTTOMLEFT","VERTICAL")
else
if slot==14 and ItemRackSettings.TrinketMenuMode=="ON" then
self = CharacterTrinket0Slot
end
ItemRack.DockWindows("TOPLEFT",self,"TOPRIGHT","HORIZONTAL")
end
ItemRack.BuildMenu(slot)
end
end
 
--[[ Minimap button ]]
 
function ItemRack.MinimapDragging()
local xpos,ypos = GetCursorPosition()
local xmin,ymin = Minimap:GetLeft(), Minimap:GetBottom()
 
xpos = xmin-xpos/Minimap:GetEffectiveScale()+70
ypos = ypos/Minimap:GetEffectiveScale()-ymin-70
 
ItemRackSettings.IconPos = math.deg(math.atan2(ypos,xpos))
ItemRack.MoveMinimap()
end
 
function ItemRack.MoveMinimap()
if ItemRackSettings.ShowMinimap=="ON" then
local xpos,ypos
local angle = ItemRackSettings.IconPos or -100
if ItemRackSettings.SquareMinimap=="ON" then
-- brute force method until trig solution figured out - min/max a point on a circle beyond square
xpos = 110 * cos(angle)
ypos = 110 * sin(angle)
xpos = math.max(-82,math.min(xpos,84))
ypos = math.max(-86,math.min(ypos,82))
else
xpos = 80*cos(angle)
ypos = 80*sin(angle)
end
ItemRackMinimapFrame:SetPoint("TOPLEFT","Minimap","TOPLEFT",52-xpos,ypos-52)
ItemRackMinimapFrame:Show()
else
ItemRackMinimapFrame:Hide()
end
end
 
function ItemRack.MinimapOnClick(self,button)
if IsShiftKeyDown() then
if ItemRackUser.CurrentSet and ItemRackUser.Sets[ItemRackUser.CurrentSet] then
ItemRack.UnequipSet(ItemRackUser.CurrentSet)
end
elseif IsAltKeyDown() and (button=="RightButton" or ItemRackSettings.AllowHidden=="OFF") then
ItemRack.ToggleEvents(self)
elseif button=="LeftButton" then
if ItemRackMenuFrame:IsVisible() then
ItemRackMenuFrame:Hide()
else
local xpos,ypos = GetCursorPosition()
if ypos>400 then
ItemRack.DockWindows("TOPRIGHT",ItemRackMinimapFrame,"BOTTOMRIGHT","VERTICAL")
else
ItemRack.DockWindows("BOTTOMRIGHT",ItemRackMinimapFrame,"TOPRIGHT","VERTICAL")
end
ItemRack.BuildMenu(20)
end
else
ItemRack.ToggleOptions(self)
end
end
 
function ItemRack.MinimapOnEnter(self)
if ItemRackSettings.MinimapTooltip=="ON" then
ItemRack.OnTooltip(self,"ItemRack","Left click: Select a set\nRight click: Open options\nAlt left click: Show hidden sets\nAlt right click: Toggle events\nShift click: Unequip this set")
end
end
 
 
function ItemRack.MinimapShineUpdate(elapsed)
ItemRack.MinimapShineAlpha = ItemRack.MinimapShineAlpha + (elapsed*2*ItemRack.MinimapShineDirection)
if ItemRack.MinimapShineAlpha < .1 then
ItemRack.StopTimer("MinimapShine")
ItemRackMinimapShine:Hide()
elseif ItemRack.MinimapShineAlpha > .9 then
ItemRack.MinimapShineDirection = -1
else
ItemRackMinimapShine:SetAlpha(ItemRack.MinimapShineAlpha)
end
end
 
function ItemRack.MinimapShineFadeIn()
ItemRack.MinimapShineAlpha = .1
ItemRack.MinimapShineDirection = 1
ItemRackMinimapShine:Show()
ItemRack.StartTimer("MinimapShine")
end
 
--[[ Non-LoD options support ]]
 
function ItemRack.ToggleOptions(self,tab)
if not ItemRackOptFrame then
EnableAddOn("ItemRackOptions") -- it's LoD, and required. Enable if disabled
LoadAddOn("ItemRackOptions")
end
if ItemRackOptFrame:IsVisible() then
ItemRackOptFrame:Hide()
else
ItemRackOptFrame:Show()
if tab then
ItemRackOpt.TabOnClick(self,tab)
end
end
end
 
function ItemRack.ReflectLock(override)
if ItemRackUser.Locked=="ON" or override then
ItemRackMenuFrame:EnableMouse(0)
ItemRackMenuFrame:SetBackdropBorderColor(0,0,0,0)
ItemRackMenuFrame:SetBackdropColor(0,0,0,0)
else
ItemRackMenuFrame:EnableMouse(1)
ItemRackMenuFrame:SetBackdropBorderColor(.3,.3,.3,1)
ItemRackMenuFrame:SetBackdropColor(1,1,1,1)
end
if ItemRackOptFrame then
ItemRackOpt.ListScrollFrameUpdate()
end
end
 
function ItemRack.ReflectAlpha()
if ItemRackButton0 then
for i=0,20 do
_G["ItemRackButton"..i]:SetAlpha(ItemRackUser.Alpha)
end
end
ItemRackMenuFrame:SetAlpha(ItemRackUser.Alpha)
end
 
function ItemRack.ReflectMenuScale(scale)
scale = scale or ItemRackUser.MenuScale
ItemRackMenuFrame:SetScale(scale)
end
 
function ItemRack.SetFont(button)
local item = _G[button.."Time"]
if ItemRackSettings.LargeNumbers=="ON" then
item:SetFont("Fonts\\FRIZQT__.TTF",16,"OUTLINE")
item:SetTextColor(1,.82,0,1)
item:ClearAllPoints()
item:SetPoint("CENTER",button,"CENTER")
else
item:SetFont("Fonts\\ARIALN.TTF",14,"OUTLINE")
item:SetTextColor(1,1,1,1)
item:ClearAllPoints()
item:SetPoint("BOTTOM",button,"BOTTOM")
end
end
 
function ItemRack.ReflectCooldownFont()
local item
for i=0,20 do
ItemRack.SetFont("ItemRackButton"..i)
end
local i=1
while _G["ItemRackMenu"..i] do
ItemRack.SetFont("ItemRackMenu"..i)
i=i+1
end
end
 
--[[ Hidden menu items ]]
 
function ItemRack.AddHidden(id)
if id then
for i=1,#(ItemRackUser.Hidden) do
if ItemRackUser.Hidden[i]==id then
return
end
end
table.insert(ItemRackUser.Hidden,id)
end
end
 
function ItemRack.RemoveHidden(id)
for i=1,#(ItemRackUser.Hidden) do
if ItemRackUser.Hidden[i]==id then
table.remove(ItemRackUser.Hidden,i)
break
end
end
end
 
function ItemRack.IsHidden(id)
for i=1,#(ItemRackUser.Hidden) do
if ItemRackUser.Hidden[i]==id then
return 1
end
end
return nil
end
 
function ItemRack.ToggleHidden(id)
if ItemRack.IsHidden(id) then
ItemRack.RemoveHidden(id)
else
ItemRack.AddHidden(id)
end
end
 
--[[ Key bindings ]]
 
function ItemRack.SetSetBindings()
local buttonName,button
for i in pairs(ItemRackUser.Sets) do
if ItemRackUser.Sets[i].key then
buttonName = "ItemRack"..UnitName("player")..GetRealmName()..i
button = _G[buttonName] or CreateFrame("Button",buttonName,nil,"SecureActionButtonTemplate")
button:SetAttribute("type","macro")
button:SetAttribute("macrotext","/script ItemRack.RunSetBinding(\""..i.."\")")
SetBindingClick(ItemRackUser.Sets[i].key,buttonName)
end
end
SaveBindings(GetCurrentBindingSet())
end
 
function ItemRack.RunSetBinding(setname)
if ItemRackSettings.EquipToggle=="ON" then
ItemRack.ToggleSet(setname)
else
ItemRack.EquipSet(setname)
end
end
 
--[[ Slash Handler ]]
 
function ItemRack.SlashHandler(arg1)
 
if arg1 and string.match(arg1,"equip") then
local set = string.match(arg1,"equip (.+)")
if not set then
ItemRack.Print("Usage: /itemrack equip set name")
ItemRack.Print("ie: /itemrack equip pvp gear")
else
ItemRack.EquipSet(set)
end
return
elseif arg1 and string.match(arg1,"toggle") then
local sets = string.match(arg1,"toggle (.+)")
if not sets then
ItemRack.Print("Usage: /itemrack toggle set name[, second set name]")
ItemRack.Print("ie: /itemrack toggle pvp gear, tanking set")
else
local set1,set2 = string.match(sets,"(.+), ?(.+)")
if not set1 then
ItemRack.ToggleSet(sets)
else
if ItemRack.IsSetEquipped(set1) then
ItemRack.EquipSet(set2)
else
ItemRack.EquipSet(set1)
end
end
end
return
end
 
arg1 = string.lower(arg1)
 
if arg1=="reset" then
ItemRack.ResetButtons()
elseif arg1=="reset everything" then
ItemRack.ResetEverything()
elseif arg1=="lock" then
ItemRackUser.Locked="ON"
ItemRack.ReflectLock()
elseif arg1=="unlock" then
ItemRackUser.Locked="OFF"
ItemRack.ReflectLock()
elseif arg1=="opt" or arg1=="options" or arg1=="config" then
ItemRack.ToggleOptions()
else
ItemRack.Print("/itemrack opt : summons options window.")
ItemRack.Print("/itemrack equip set name : equip set 'set name'.")
ItemRack.Print("/itemrack toggle set name[, second set] : toggles set 'set name'.")
ItemRack.Print("/itemrack reset : resets buttons and their settings.")
ItemRack.Print("/itemrack reset everything : wipes ItemRack to default.")
ItemRack.Print("/itemrack lock/unlock : locks/unlocks the buttons.")
end
 
end
 
--[[ Bank Support ]]
 
-- returns 1 if the set has a banked item, 0 if there is an item missing entirely, nil if item is on person
function ItemRack.MissingItems(setname)
local missing
if not setname or not ItemRackUser.Sets[setname] then return end
for _,i in pairs(ItemRackUser.Sets[setname].equip) do
if i~=0 and ItemRack.GetCountByID(i)==0 then
missing = 0
if ItemRack.FindInBank(i) then
return 1
end
end
end
return missing
end
 
-- pulls setname from bank to bags
function ItemRack.GetBankedSet(setname)
if ItemRack.MissingItems(setname)~=1 or SpellIsTargeting() or GetCursorInfo() then return end
local bag,slot,freeBag,freeSlot
ItemRack.ClearLockList()
for _,i in pairs(ItemRackUser.Sets[setname].equip) do
bag,slot = ItemRack.FindInBank(i)
if bag then
freeBag,freeSlot = ItemRack.FindSpace()
if freeBag then
PickupContainerItem(bag,slot)
PickupContainerItem(freeBag,freeSlot)
else
ItemRack.Print("Not enough room in bags to pull all items from '"..setname.."'.")
return
end
end
end
end
 
-- pushes setname from bags/worn to bank
function ItemRack.PutBankedSet(setname)
if SpellIsTargeting() or GetCursorInfo() then return end
local bag,slot,freeBag,freeSlot
ItemRack.ClearLockList()
for _,i in pairs(ItemRackUser.Sets[setname].equip) do
if i~=0 then
freeBag,freeSlot = ItemRack.FindBankSpace()
if freeBag then
inv,bag,slot = ItemRack.FindItem(i)
if inv then
PickupInventoryItem(inv)
elseif bag then
PickupContainerItem(bag,slot)
end
if CursorHasItem() then
PickupContainerItem(freeBag,freeSlot)
end
else
ItemRack.Print("Not enough room in bank to store all items from '"..setname.."'.")
return
end
end
end
end
 
function ItemRack.ResetEverything()
StaticPopupDialogs["ItemRackCONFIRMRESET"] = {
text = "This will restore ItemRack to its default state, wiping all sets, buttons, events and settings.\nThe UI will be reloaded. Continue?",
button1 = "Yes", button2 = "No", timeout = 0, hideOnEscape = 1, showAlert = 1,
OnAccept = function() ItemRackUser=nil ItemRackSettings=nil ItemRackItems=nil ItemRackEvents=nil ReloadUI() end
}
StaticPopup_Show("ItemRackCONFIRMRESET")
end
 
-- if cpu profiling on, this will add a page to TinyPad with each ItemRack.func()'s time
function ItemRack.ProfileFuncs()
if TinyPadPages then
UpdateAddOnCPUUsage()
local total = 0
local t = {}
local whole,decimal
for i in pairs(ItemRack) do
if type(ItemRack[i])=="function" then
whole = GetFunctionCPUUsage(ItemRack[i])
decimal = whole - math.floor(whole)
whole = math.floor(whole)
table.insert(t,string.format("%04d.%02d %s",whole,decimal,i))
end
end
table.sort(t)
local info = "ItemRack profile "..date().." "..UnitName("player").."\n"
for i=1,#(t) do
info = info..t[i].."\n"
end
table.insert(TinyPadPages,info)
end
end
Property changes : Added: svn:executable +
ItemRack/ItemRack.toc New file
0,0 → 1,15
## Interface: 40200
## Title: ItemRack
## Author: Gello - Updated for WoW 4.0 by Kiki + Kharthus + Yewbacca
## SavedVariables: ItemRackSettings, ItemRackItems, ItemRackEvents
## SavedVariablesPerCharacter: ItemRackUser
ItemRack.lua
ItemRack.xml
ItemRackButtons.lua
ItemRackButtons.xml
ItemRackEquip.lua
ItemRackQueue.lua
ItemRackEvents.lua
 
 
 
Property changes : Added: svn:executable +
ItemRack/ItemRackEquip.lua New file
0,0 → 1,305
-- ItemRackEquip.lua : ItemRack.EquipSet and its supporting functions.
 
ItemRack.SwapList = {} -- table of item ids that want to swap in, indexed by slot
ItemRack.AbortSwap = nil -- reasons: 1=not enough room, 2=item on cursor, 3=in spell targeting mode, 4=item lock
ItemRack.AbortReasons = {"Not enough room.","Something is on the cursor.","In spell targeting mode.","Another swap is in progress."}
 
ItemRack.SetsWaiting = {} -- numerically indexed table of {"setname",func} ie {"pvp",ItemRack.EquipSet}
 
function ItemRack.ProcessSetsWaiting()
local setwaiting = ItemRack.SetsWaiting[1][1]
local whichequip = ItemRack.SetsWaiting[1][2]
table.remove(ItemRack.SetsWaiting,1)
whichequip(setwaiting)
end
 
function ItemRack.AddSetToSetsWaiting(setwaiting,whichequip)
local wait = ItemRack.SetsWaiting
for i in pairs(wait) do
if wait[i][1]==setwaiting and wait[i][2]==whichequip then
return
end
end
table.insert(wait,{setwaiting,whichequip})
end
 
function ItemRack.EquipSet(setname)
if not setname or not ItemRackUser.Sets[setname] then
ItemRack.Print("Set \""..tostring(setname).."\" doesn't exist.")
return
end
if ItemRack.NowCasting or ItemRack.AnythingLocked() then
-- a swap is in progress, add this set to the wait list and leave
ItemRack.AddSetToSetsWaiting(setname,ItemRack.EquipSet)
return
end
local set = ItemRackUser.Sets[setname]
local swap = ItemRack.SwapList
for i in pairs(swap) do
swap[i] = nil
end
local inv,bag,slot
local couldntFind
for i in pairs(set.equip) do
if ItemRack.GetID(i)~=set.equip[i] then -- if intended item is not worn (exact match)
inv,bag,slot = ItemRack.FindItem(set.equip[i])
if not inv and not bag then
-- if not found at all, then start/add to list of items not found
couldntFind = couldntFind or "Could not find: "
couldntFind = couldntFind.."["..tostring(ItemRack.GetInfoByID(set.equip[i])).."] "
elseif inv~=i then -- and finding intended item doesn't point to worn
swap[i] = set.equip[i] -- then note this item for a swap
end
end
end
ItemRack.Print(couldntFind) -- if couldntFind is nil then nothing will print
 
-- at this point, ItemRack.SwapList has only what needs to be swapped, indexed by slot
if not next(swap) then
-- ItemRack.Print("Set already equipped.")
ItemRack.EndSetSwap(setname) -- end swap if set already equipped
return
end
 
if set.old then
for i in pairs(set.old) do
set.old[i] = nil -- wipe old items
end
set.oldset = ItemRackUser.CurrentSet
end
 
-- if in combat or dead, combat queue items wanting to equip and only let swappables through
if UnitAffectingCombat("player") or ItemRack.IsPlayerReallyDead() then
for i in pairs(swap) do
if not ItemRack.SlotInfo[i].swappable then
ItemRack.AddToCombatQueue(i,swap[i])
-- print("Combat queue "..ItemRack.GetInfoByID(swap[i]))
swap[i] = nil
if set.old then
set.old[i] = ItemRack.GetID(i)
ItemRack.CombatSet = setname
elseif set.oldset then
ItemRack.CombatSet = set.oldset
end
end
end
end
if not next(swap) then
return
end
 
if ItemRackUser.Sets[setname].ShowHelm then
ShowHelm(ItemRackUser.Sets[setname].ShowHelm)
end
if ItemRackUser.Sets[setname].ShowCloak then
ShowCloak(ItemRackUser.Sets[setname].ShowCloak)
end
 
ItemRack.IterateSwapList(setname) -- run SwapList swaps
if not next(swap) then
ItemRack.EndSetSwap(setname)
return -- leave if swap completed on first pass
end
 
-- a second pass is needed. ItemRack.SwapList (swap) has the list of remaining items to swap.
-- With ItemRack.SetSwapping defined, ITEM_LOCK_CHANGED will call LockChangedDuringSetSwap()
-- to determine when to run a second pass.
ItemRack.SetSwapping = setname
end
 
function ItemRack.AnythingLocked()
local isLocked = nil
for i=1,19 do
if IsInventoryItemLocked(i) then
return 1
end
end
if not isLocked then
for i=0,4 do
for j=1,GetContainerNumSlots(i) do
if select(3,GetContainerItemInfo(i,j)) then
return 1
end
end
end
end
end
 
function ItemRack.LockChangedDuringSetSwap()
if not ItemRack.AnythingLocked() then
local setname = ItemRack.SetSwapping
ItemRack.SetSwapping = nil
ItemRack.IterateSwapList(setname)
ItemRack.EndSetSwap(setname)
end
end
 
function ItemRack.IterateSwapList(setname)
 
local set = ItemRackUser.Sets[setname]
local swap = ItemRack.SwapList
 
ItemRack.AbortSwap = nil
ItemRack.ClearLockList()
 
local treatAs2H = nil
local skip = nil
for i=0,19 do -- go in order to handle skips correctly
if skip or ItemRack.AbortSwap then
skip = nil
elseif swap[i] then
if swap[i]==0 then -- if intended to be empty
bag,slot = ItemRack.FindSpace()
if bag then
if set.old then
set.old[i] = ItemRack.GetID(i)
end
ItemRack.MoveItem(i,nil,bag,slot) -- empty slot
swap[i] = nil
else
ItemRack.AbortSwap = 1
return
end
else
inv,bag,slot = ItemRack.FindItem(swap[i],1)
if bag then
if i==16 and ItemRack.HasTitansGrip then
local subtype = select(7,GetItemInfo(GetContainerItemLink(bag,slot)))
if subtype and ItemRack.NoTitansGrip[subtype] then
treatAs2H = 1
end
end
-- TODO: Polarms, Fishing Poles and Staves (7th GetItemInfo) cannot
-- be equipped alongside Two-Handed Axes, Two-Handed Maces and Two-Handed Swords
if (not ItemRack.HasTitansGrip or treatAs2H) and select(3,ItemRack.GetInfoByID(swap[i]))=="INVTYPE_2HWEAPON" then
-- this is a 2H weapon. swap both slots at once if offhand equipped
if set.old then
set.old[i] = ItemRack.GetID(i)
set.old[i+1] = ItemRack.GetID(i+1)
end
if GetInventoryItemLink("player",17) then
local freeBag,freeSlot = ItemRack.FindSpace()
if freeBag then
ItemRack.MoveItem(17,nil,freeBag,freeSlot)
else
ItemRack.AbortSwap=1
end
end
ItemRack.MoveItem(bag,slot,16,nil)
swap[16] = nil
swap[17] = nil -- fix by Romracer
skip = 1
else
if set.old then
set.old[i] = ItemRack.GetID(i)
end
ItemRack.MoveItem(bag,slot,i,nil)
swap[i] = nil
end
elseif inv==(i+1) and ItemRack.SameID(swap[i+1],ItemRack.GetID(i)) then
-- item is in other slot and other slot wants to go to this one
if set.old then
set.old[i] = ItemRack.GetID(i)
set.old[i+1] = ItemRack.GetID(i+1)
end
ItemRack.MoveItem(i,nil,i+1,nil)
swap[i] = nil
swap[i+1] = nil
skip = 1
end
end
end
end
if ItemRack.AbortSwap then
ItemRack.Print("Swap stopped. "..(ItemRack.AbortReasons[ItemRack.AbortSwap] or ""))
end
end
 
function ItemRack.EndSetSwap(setname)
ItemRack.SetSwapping = nil
if setname then
if not string.match(setname,"^~") then --do not list internal sets, prefixed with ~
ItemRackUser.CurrentSet = setname
ItemRack.UpdateCurrentSet()
elseif ItemRackUser.Sets[setname].oldset then
-- if this is a special set that stored a setname, set current to that setname
ItemRackUser.CurrentSet = ItemRackUser.Sets[setname].oldset
ItemRackUser.Sets[setname].oldset = nil
end
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.ChangeEditingSet()
end
end
-- ItemRack.Print("End of set swap. CurrentSet: "..tostring(ItemRackUser.CurrentSet))
end
 
-- moves an item from bag,slot to bag,slot (slot is nil for bag=inv)
function ItemRack.MoveItem(fromBag,fromSlot,toBag,toSlot)
local abort
if CursorHasItem() then
abort = 2
elseif SpellIsTargeting() then
abort = 3
elseif (not fromSlot and IsInventoryItemLocked(fromBag)) or (not toSlot and IsInventoryItemLocked(toBag)) then
abort = 4
elseif (fromSlot and select(3,GetContainerItemInfo(fromBag,fromSlot))) or (toSlot and select(3,GetContainerItemInfo(toBag,toSlot))) then
abort = 4
end
if abort then
ItemRack.AbortSwap = abort
return
else
if fromSlot then
PickupContainerItem(fromBag,fromSlot)
else
PickupInventoryItem(fromBag)
end
if toSlot then
PickupContainerItem(toBag,toSlot)
else
PickupInventoryItem(toBag)
end
end
end
 
function ItemRack.IsSetEquipped(setname,exact)
if setname and ItemRackUser.Sets[setname] then
local set = ItemRackUser.Sets[setname].equip
local id
for i in pairs(set) do
id = ItemRack.GetID(i)
if (exact and set[i]~=id) or (not exact and not ItemRack.SameID(set[i],ItemRack.GetID(i))) then
return nil
end
end
return 1
end
end
 
function ItemRack.UnequipSet(setname)
if setname and ItemRackUser.Sets[setname] and ItemRackUser.Sets[setname].old then
if ItemRack.AnythingLocked() then
ItemRack.AddSetToSetsWaiting(setname,ItemRack.UnequipSet)
return
end
local old = ItemRackUser.Sets[setname].old
local unequip = ItemRackUser.Sets["~Unequip"].equip
for i in pairs(unequip) do
unequip[i] = nil
end
for i in pairs(old) do
unequip[i] = old[i]
-- old[i] = nil
end
ItemRackUser.Sets["~Unequip"].oldset = ItemRackUser.Sets[setname].oldset
ItemRack.EquipSet("~Unequip")
end
end
 
function ItemRack.ToggleSet(setname,exact)
if ItemRack.IsSetEquipped(setname,exact) then
ItemRack.UnequipSet(setname)
else
ItemRack.EquipSet(setname)
end
end
Property changes : Added: svn:executable +
ItemRack/ItemRackEvents.lua New file
0,0 → 1,422
--[[ Default event definitions
 
Events can be one of four types:
Buff : Triggered by PLAYER_AURAS_CHANGED and delayed .3 sec
Zone : Triggered by ZONE_CHANGED_NEW_AREA and delayed .5 sec
Stance : Triggered by UPDATE_SHAPESHIFT_FORM and not delayed
Script : User-defined trigger
 
Buff and Stance share an attribute :
NotInPVP : nil or 1, whether to ignore this event if pvp flag is set
 
Buff, Zone and Stance share an attribute :
Unequip : nil or 1, whether to unequip the set when condition ends
 
Buff has a special case attribute:
Anymount: nil or 1, whether the buff is any mount (IsPlayerMounted())
 
Zone has a table:
Zones : Indexed by name of zone, lookup table for zones to define this event
 
Script has its own attributes:
Trigger : Event (ie "UNIT_AURA") that triggers the script
Script : Actual script run through RunScript
 
The set to equip is defined in ItemRackUser.Events.Set, indexed by event name
The set to equip is nil if it's a Script event. Sets equip/unequip explicitly
Whether an event is enabled is in ItemRackuser.Events.Enabled, indexed by event name
]]
 
-- increment this value when default events are changed to deploy them to existing events
ItemRack.EventsVersion = 14
 
-- default events, loaded when no events exist or ItemRack.EventsVersion is increased
ItemRack.DefaultEvents = {
["PVP"] = {
Type = "Zone",
Unequip = 1,
Zones = {
["Alterac Valley"] = 1,
["Arathi Basin"] = 1,
["Warsong Gulch"] = 1,
["Eye of the Storm"] = 1,
["Ruins of Lordaeron"] = 1,
["Blade's Edge Arena"] = 1,
["Nagrand Arena"] = 1,
}
},
["City"] = {
Type = "Zone",
Unequip = 1,
Zones = {
["Ironforge"] = 1,
["Stormwind City"] = 1,
["Darnassus"] = 1,
["The Exodar"] = 1,
["Orgrimmar"] = 1,
["Thunder Bluff"] = 1,
["Silvermoon City"] = 1,
["Undercity"] = 1,
["Shattrath City"] = 1,
["Dalaran"] = 1,
}
},
["Mounted"] = { Type = "Buff", Unequip = 1, Anymount = 1 },
["Drinking"] = { Type = "Buff", Unequip = 1, Buff = "Drink" },
["Evocation"] = { Type = "Buff", Unequip = 1, Buff = "Evocation" },
 
["Warrior Battle"] = { Type = "Stance", Stance = 1 },
["Warrior Defensive"] = { Type = "Stance", Stance = 2 },
["Warrior Berserker"] = { Type = "Stance", Stance = 3 },
 
["Priest Shadowform"] = { Type = "Stance", Unequip = 1, Stance = 1 },
 
["Druid Humanoid"] = { Type = "Stance", Stance = 0 },
["Druid Bear"] = { Type = "Stance", Stance = 1 },
["Druid Aquatic"] = { Type = "Stance", Stance = 2 },
["Druid Cat"] = { Type = "Stance", Stance = 3 },
["Druid Travel"] = { Type = "Stance", Stance = 4 },
["Druid Moonkin"] = { Type = "Stance", Stance = "Moonkin Form" },
["Druid Tree of Life"] = { Type = "Stance", Stance = "Tree of Life" },
["Druid Flight Form"] = { Type = "Stance", Unequip = 1, Stance = "Flight Form" },
["Druid Swift Flight Form"] = { Type = "Stance", Unequip = 1, Stance = "Swift Flight Form" },
 
["Rogue Stealth"] = { Type = "Stance", Unequip = 1, Stance = 1 },
 
["Shaman Ghostwolf"] = { Type = "Stance", Unequip = 1, Stance = 1 },
 
["Swimming"] = {
["Trigger"] = "MIRROR_TIMER_START",
["Type"] = "Script",
["Script"] = "local set = \"Name of set\"\nif IsSwimming() and not IsSetEquipped(set) then\n EquipSet(set)\n if not SwimmingEvent then\n function SwimmingEvent()\n if not IsSwimming() then\n ItemRack.StopTimer(\"SwimmingEvent\")\n UnequipSet(set)\n end\n end\n ItemRack.CreateTimer(\"SwimmingEvent\",SwimmingEvent,.5,1)\n end\n ItemRack.StartTimer(\"SwimmingEvent\")\nend\n--[[Equips a set when swimming and breath gauge appears and unequips soon after you stop swimming.]]",
},
 
["Buffs Gained"] = {
Type = "Script",
Trigger = "UNIT_AURA",
Script = "if arg1==\"player\" then\n IRScriptBuffs = IRScriptBuffs or {}\n local buffs = IRScriptBuffs\n for i in pairs(buffs) do\n if not UnitAura(\"player\",i) then\n buffs[i] = nil\n end\n end\n local i,b = 1,1\n while b do\n b = UnitBuff(\"player\",i)\n if b and not buffs[b] then\n ItemRack.Print(\"Gained buff: \"..b)\n buffs[b] = 1\n end\n i = i+1\n end\nend\n--[[For script demonstration purposes. Doesn't equip anything just informs when a buff is gained.]]",
},
 
["After Cast"] = {
Type = "Script",
Trigger = "UNIT_SPELLCAST_SUCCEEDED",
Script = "local spell = \"Name of spell\"\nlocal set = \"Name of set\"\nif arg1==\"player\" and arg2==spell then\n EquipSet(set)\nend\n\n--[[This event will equip \"Name of set\" when \"Name of spell\" has finished casting. Change the names for your own use.]]",
},
 
["Dual Spec"] = {
Type = "Script",
Trigger = "ACTIVE_TALENT_GROUP_CHANGED",
Script = "local set1 = \"Name of Set1\"\nlocal set2 = \"Name of Set2\"\nif ItemRack.HasTitansGrip and GetInventoryItemLink(\"player\",17) then\n local b,s = ItemRack.FindSpace()\n if b then\n ItemRack.MoveItem(17,nil,b,s)\n end\nend\nlocal at = GetActiveTalentGroup()\nif at == 1 then\n ItemRack.EquipSet(set1)\nelseif at == 2 then\n ItemRack.EquipSet(set2)\nend\n\n--[[This event will equip \"Name of Set1\" when you switch to primary talents and \"Name of Set2\" when switching to secondary talents. Edit the names for your own use.]]",
},
}
 
-- resetDefault to reload/update default events, resetAll to wipe all events and recreate them
function ItemRack.LoadEvents(resetDefault,resetAll)
 
local version = tonumber(ItemRackSettings.EventsVersion) or 0
if ItemRack.EventsVersion > version then
resetDefault = 1 -- force a load of default events (leaving custom ones intact)
ItemRackSettings.EventsVersion = ItemRack.EventsVersion
end
 
if not ItemRackUser.Events or resetAll then
ItemRackUser.Events = {
Enabled = {}, -- indexed by name of event, whether an event is enabled
Set = {} -- indexed by name of event, the set defined for the event, if any
}
end
 
if not ItemRackEvents or resetAll then
ItemRackEvents = {}
end
 
if resetDefault or resetAll then
for i in pairs(ItemRack.DefaultEvents) do
ItemRack.CopyDefaultEvent(i)
end
end
 
ItemRack.CleanupEvents()
if ItemRackOpt then
ItemRackOpt.PopulateEventList() -- if options loaded, recreate event list there
end
end
 
function ItemRack.CopyDefaultEvent(eventName)
ItemRackEvents[eventName] = {}
local event = ItemRackEvents[eventName]
local default = ItemRack.DefaultEvents[eventName]
 
for i in pairs(default) do
if type(default[i])~="table" then
event[i] = default[i]
else
-- recursive scares me :P /chicken
-- this copies a sub-table. if events ever go one more table deep, do a recursive copy
event[i] = {}
for j in pairs(default[i]) do
event[i][j] = default[i][j]
end
end
end
end
 
-- clear sets of deleted events, clear events with deleted sets
function ItemRack.CleanupEvents()
local event = ItemRackUser.Events
 
-- go through ItemRackUser.Events.Set for deleted events or sets
for i in pairs(event.Set) do
if not ItemRackEvents[i] then
-- this event no longer exists, remove it
event.Set[i] = nil
event.Enabled[i] = nil
end
if not ItemRackUser.Sets[event.Set[i]] then
-- this set no longer exists, remove it
event.Set[i] = nil
event.Enabled[i] = nil
end
end
 
-- go through ItemRackUser.Events.Enabled for deleted events
for i in pairs(event.Enabled) do
if not ItemRackEvents[i] then
-- this event no longer exists, remove it
event.Set[i] = nil
event.Enabled[i] = nil
end
end
end
 
function ItemRack.ResetEvents(resetDefault,resetAll)
if not resetDefault and not resetAll then
StaticPopupDialogs["ItemRackConfirmResetEvents"] = {
text = "Do you want to restore just Default events, or wipe All events and restore to default?",
button1 = "Default", button2 = "Cancel", button3 = "All", timeout = 0, hideOnEscape = 1, whileDead = 1,
OnAccept = function() ItemRack.ResetEvents(1) end,
OnAlt = function() ItemRack.ResetEvents(1,1) end,
}
StaticPopup_Show("ItemRackConfirmResetEvents")
else
ItemRack.LoadEvents(resetDefault,resetAll)
end
end
 
function ItemRack.InitEvents()
ItemRack.LoadEvents()
 
ItemRack.CreateTimer("EventsBuffTimer",ItemRack.ProcessBuffEvent,.25)
ItemRack.CreateTimer("EventsZoneTimer",ItemRack.ProcessZoneEvent,.33)
 
if ItemRackButton20Queue then
ItemRackButton20Queue:SetTexture("Interface\\AddOns\\ItemRack\\ItemRackGear")
else
print("ItemRackButton20Queue doesn't exist?")
end
 
ItemRack.RegisterEvents()
end
 
function ItemRack.RegisterEvents()
local frame = ItemRackEventProcessingFrame
frame:UnregisterAllEvents()
ItemRack.ReflectEventsRunning()
if ItemRackUser.EnableEvents=="OFF" then
return
end
local enabled = ItemRackUser.Events.Enabled
local events = ItemRackEvents
local eventType
for eventName in pairs(enabled) do
eventType = events[eventName].Type
if eventType=="Buff" then
if not frame:IsEventRegistered("UNIT_AURA") then
frame:RegisterEvent("UNIT_AURA")
end
elseif eventType=="Stance" then
if not frame:IsEventRegistered("UPDATE_SHAPESHIFT_FORM") then
frame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
end
elseif eventType=="Zone" then
if not frame:IsEventRegistered("ZONE_CHANGED_NEW_AREA") then
frame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
end
elseif eventType=="Script" then
if not frame:IsEventRegistered(events[eventName].Trigger) then
frame:RegisterEvent(events[eventName].Trigger)
end
end
end
ItemRack.ProcessStanceEvent()
ItemRack.ProcessZoneEvent()
ItemRack.ProcessBuffEvent()
end
 
function ItemRack.ToggleEvents(self)
ItemRackUser.EnableEvents = ItemRackUser.EnableEvents=="ON" and "OFF" or "ON"
if not next(ItemRackUser.Events.Enabled) then
-- user is turning on events with no events enabled, go to events frame
LoadAddOn("ItemRackOptions")
ItemRackOptFrame:Show()
ItemRackOpt.TabOnClick(self,3)
else
if ItemRackOptFrame and ItemRackOptFrame:IsVisible() then
ItemRackOpt.ListScrollFrameUpdate()
end
end
ItemRack.RegisterEvents()
end
 
--[[ Event processing ]]
 
function ItemRack.ProcessingFrameOnEvent(self,event,...)
local enabled = ItemRackUser.Events.Enabled
local events = ItemRackEvents
local startBuff, startZone, startStance, eventType
local arg1,arg2 = ...;
 
for eventName in pairs(enabled) do
eventType = events[eventName].Type
if event=="UNIT_AURA" and eventType=="Buff" and arg1=="player" then
startBuff = 1
elseif event=="UPDATE_SHAPESHIFT_FORM" and eventType=="Stance" then
startStance = 1
elseif event=="ZONE_CHANGED_NEW_AREA" and eventType=="Zone" then
startZone = 1
elseif eventType=="Script" and events[eventName].Trigger==event then
RunScript(events[eventName].Script)
end
end
if startStance then
ItemRack.ProcessStanceEvent()
end
if startBuff then
ItemRack.StartTimer("EventsBuffTimer")
end
if startZone then
ItemRack.StartTimer("EventsZoneTimer")
end
end
 
function ItemRack.GetStanceNumber(name)
if tonumber(name) then
return name
end
for i=1,GetNumShapeshiftForms() do
if name==select(2,GetShapeshiftFormInfo(i)) then
return i
end
end
end
 
function ItemRack.ProcessStanceEvent()
local enabled = ItemRackUser.Events.Enabled
local events = ItemRackEvents
 
local currentStance = GetShapeshiftForm()
local stance, setToEquip, setToUnequip, setname, skip
 
for eventName in pairs(enabled) do
if events[eventName].Type=="Stance" then
skip = nil
if events[eventName].NotInPVP then
local _,instanceType = IsInInstance()
if instanceType=="arena" or instanceType=="pvp" then
skip = 1
end
end
if not skip then
stance = ItemRack.GetStanceNumber(events[eventName].Stance)
setname = ItemRackUser.Events.Set[eventName]
if stance==currentStance and not ItemRack.IsSetEquipped(setname) then
-- if this event is for this stance, then we'll want to equip this one
setToEquip = ItemRackUser.Events.Set[eventName]
end
if stance~=currentStance and events[eventName].Unequip and ItemRack.IsSetEquipped(setname) then
-- if this event is for last stance, then we'll want to unequip it
setToUnequip = ItemRackUser.Events.Set[eventName]
end
end
end
end
if setToUnequip then
ItemRack.UnequipSet(setToUnequip)
end
if setToEquip then
ItemRack.EquipSet(setToEquip)
end
end
 
function ItemRack.ProcessZoneEvent()
local enabled = ItemRackUser.Events.Enabled
local events = ItemRackEvents
 
local currentZone = GetRealZoneText()
local setToEquip, setToUnequip, setname
 
for eventName in pairs(enabled) do
if events[eventName].Type=="Zone" then
setname = ItemRackUser.Events.Set[eventName]
if events[eventName].Zones[currentZone] and not ItemRack.IsSetEquipped(setname) then
setToEquip = setname
elseif not events[eventName].Zones[currentZone] and events[eventName].Unequip and ItemRack.IsSetEquipped(setname) then
setToUnequip = setname
end
end
end
if setToUnequip then
ItemRack.UnequipSet(setToUnequip)
end
if setToEquip then
ItemRack.EquipSet(setToEquip)
end
end
 
function ItemRack.ProcessBuffEvent()
local enabled = ItemRackUser.Events.Enabled
local events = ItemRackEvents
 
local buff, setname, isSetEquipped, skip
 
for eventName in pairs(enabled) do
if events[eventName].Type=="Buff" then
skip = nil
if events[eventName].NotInPVP then
local _,instanceType = IsInInstance()
if instanceType=="arena" or instanceType=="pvp" then
skip = 1
end
end
if not skip then
if events[eventName].Anymount then
buff = IsMounted() and not UnitOnTaxi("player")
else
buff = UnitAura("player",events[eventName].Buff)
end
setname = ItemRackUser.Events.Set[eventName]
isSetEquipped = ItemRack.IsSetEquipped(setname)
if buff and not isSetEquipped then
ItemRack.EquipSet(setname)
elseif not buff and isSetEquipped then
ItemRack.UnequipSet(setname)
end
end
end
end
end
 
function ItemRack.ReflectEventsRunning()
if ItemRackUser.EnableEvents=="ON" and next(ItemRackUser.Events.Enabled) then
-- if events enabled and an event is enabled, show gear icons on set and minimap button
if ItemRackUser.Buttons[20] then
ItemRackButton20Queue:Show()
end
ItemRackMinimapGear:Show()
else
if ItemRackUser.Buttons[20] then
ItemRackButton20Queue:Hide()
end
ItemRackMinimapGear:Hide()
end
end
Property changes : Added: svn:executable +
ItemRack/ItemRackIcon.blp Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:mime-type + application/octet-stream Added: svn:executable +
ItemRack/ItemRackQueue.lua New file
0,0 → 1,111
-- ItemRackQueue.lua
 
function ItemRack.PeriodicQueueCheck()
if SpellIsTargeting() then
return
end
if ItemRackUser.EnableQueues=="ON" then
for i in pairs(ItemRackUser.QueuesEnabled) do
ItemRack.ProcessAutoQueue(i)
end
end
end
 
function ItemRack.ProcessAutoQueue(slot)
if not slot or IsInventoryItemLocked(slot) then
return
end
 
local start,duration,enable = GetInventoryItemCooldown("player",slot)
local timeLeft = GetTime()-start
local baseID = ItemRack.GetIRString(GetInventoryItemLink("player",slot),true,true)
local icon = _G["ItemRackButton"..slot.."Queue"]
 
if not baseID then return end
 
local buff = GetItemSpell(baseID)
if buff then
if UnitAura("player",buff) or (start>0 and (duration-timeLeft)>30 and timeLeft<1) then
icon:SetDesaturated(1)
return
end
end
 
if ItemRackItems[baseID] then
if ItemRackItems[baseID].keep then
icon:SetVertexColor(1,.5,.5)
return -- leave if .keep flag set on this item
end
if ItemRackItems[baseID].delay then
-- leave if currently equipped trinket is on cooldown for less than its delay
if start>0 and (duration-timeLeft)>30 and timeLeft<ItemRackItems[baseID].delay then
icon:SetDesaturated(1)
return
end
end
end
icon:SetDesaturated(0)
icon:SetVertexColor(1,1,1)
 
local ready = ItemRack.ItemNearReady(baseID)
if ready and ItemRack.CombatQueue[slot] then
ItemRack.CombatQueue[slot] = nil
ItemRack.UpdateCombatQueue()
end
 
local list,rank = ItemRackUser.Queues[slot]
 
local candidate,bag,s
for i=1,#(list) do
candidate = string.match(list[i],"(%d+)") --FIXME: not sure what list[i] is; it might simply be cleaning up some sort of slot number to make sure it is numeric, OR it might actually be an itemID... if it is the latter then this (conversion to baseID) should be handled by either ItemRack.GetIRString(list[i],true) if list[i] is an ItemRack-style ID or ItemRack.GetIRString(list[i],true,true) if list[i] is a regular ItemLink/ItemString, MOST things point to it being an ItemRack-style ID, but I do not want to mess anything up if this is in fact just a regular number, so I'll leave the line as it is
if list[i]==0 then
break
elseif ready and candidate==baseID then
break
else
if not ready or enable==0 or (ItemRackItems[candidate] and ItemRackItems[candidate].priority) then
if ItemRack.ItemNearReady(candidate) then
if GetItemCount(candidate)>0 and not IsEquippedItem(candidate) then
_,bag,s = ItemRack.FindItem(list[i])
if bag then
if ItemRack.CombatQueue[slot]~=list[i] then
ItemRack.EquipItemByID(list[i],slot)
end
break
end
end
end
end
end
end
end
 
function ItemRack.ItemNearReady(id)
local start,duration = GetItemCooldown(id)
if start==0 or duration-(GetTime()-start)<30 then
return 1
end
end
 
function ItemRack.SetQueue(slot,newQueue)
if not newQueue then
ItemRackUser.QueuesEnabled[slot] = nil
elseif type(newQueue)=="table" then
ItemRackUser.Queues[slot] = ItemRackUser.Queues[slot] or {}
for i in pairs(ItemRackUser.Queues[slot]) do
ItemRackUser.Queues[slot][i] = nil
end
for i=1,#(newQueue) do
table.insert(ItemRackUser.Queues[slot],newQueue[i])
end
if ItemRackOptFrame:IsVisible() then
if ItemRackOptSubFrame7:IsVisible() and ItemRackOpt.SelectedSlot==slot then
ItemRackOpt.SetupQueue(slot)
else
ItemRackOpt.UpdateInv()
end
end
ItemRackUser.QueuesEnabled[slot] = 1
end
ItemRack.UpdateCombatQueue()
end
Property changes : Added: svn:executable +
ItemRack/Brackets.tga Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:mime-type + application/octet-stream Added: svn:executable +