/trunk
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 |
<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> |
<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> |
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 |
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 |
## 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 |
-- 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 |
--[[ 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 |
-- 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 |