├── Guidelime
├── Icons
│ ├── Arrow.blp
│ ├── black.blp
│ ├── check.blp
│ ├── lime.blp
│ ├── lime0.blp
│ ├── limev.blp
│ ├── discord.tga
│ ├── hearth.blp
│ ├── paypal.tga
│ ├── repair.blp
│ ├── scroll.blp
│ ├── lime_arrow.blp
│ ├── set_hearth.blp
│ ├── lime_marker.blp
│ ├── TitleHighlight.blp
│ ├── getflightpoint.blp
│ ├── lime_marker_friz.blp
│ ├── questunavailable.blp
│ ├── lime_marker_friz_green.blp
│ └── ArrowLicense.txt
├── Libs
│ ├── LibDBIcon-1.0
│ │ ├── lib.xml
│ │ └── LibDBIcon-1.0.lua
│ ├── CallbackHandler-1.0
│ │ ├── CallbackHandler-1.0.xml
│ │ └── CallbackHandler-1.0.lua
│ ├── HereBeDragons
│ │ ├── CallbackHandler-1.0
│ │ │ ├── CallbackHandler-1.0.xml
│ │ │ └── CallbackHandler-1.0.lua
│ │ ├── LibStub
│ │ │ ├── LibStub.toc
│ │ │ └── LibStub.lua
│ │ ├── CHANGELOG.md
│ │ └── HereBeDragons.toc
│ ├── LibStub
│ │ └── LibStub.lua
│ └── LibDataBroker-1.1
│ │ └── LibDataBroker-1.1.lua
├── QuestScan.lua
├── Bindings.xml
├── Guidelime-Cata.toc
├── Guidelime-Mists.toc
├── Guidelime-Wrath.toc
├── Guidelime-Vanilla.toc
├── QuestLog.lua
├── QuestLog-Classic.lua
├── CustomCode.lua
├── Data
│ ├── SkillDB.lua
│ ├── Internal
│ │ └── corrections.lua
│ ├── SkillDB_Locales.lua
│ ├── Data.lua
│ ├── PositionTools.lua
│ └── QuestieCorrections.lua
├── EditorTools.lua
├── Frames.lua
├── Import.lua
├── ActionButtons.lua
└── Guides.lua
├── .gitignore
├── Guidelime_MyGuideExample
├── Guidelime_MyGuideExample.zip
├── Guidelime_MyGuideExample.toc
└── MyGuide.lua
├── .pkgmeta
└── README.md
/Guidelime/Icons/Arrow.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/Arrow.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/black.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/black.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/check.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/check.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/lime.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/lime0.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime0.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/limev.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/limev.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/discord.tga:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/discord.tga
--------------------------------------------------------------------------------
/Guidelime/Icons/hearth.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/hearth.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/paypal.tga:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/paypal.tga
--------------------------------------------------------------------------------
/Guidelime/Icons/repair.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/repair.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/scroll.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/scroll.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/lime_arrow.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime_arrow.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/set_hearth.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/set_hearth.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/lime_marker.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime_marker.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/TitleHighlight.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/TitleHighlight.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/getflightpoint.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/getflightpoint.blp
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !Guidelime
3 | !Guidelime/**
4 | !Guidelime_MyGuideExample
5 | !README.md
6 | !.pkgmeta
7 | !.gitignore
8 |
--------------------------------------------------------------------------------
/Guidelime/Icons/lime_marker_friz.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime_marker_friz.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/questunavailable.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/questunavailable.blp
--------------------------------------------------------------------------------
/Guidelime/Icons/lime_marker_friz_green.blp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime/Icons/lime_marker_friz_green.blp
--------------------------------------------------------------------------------
/Guidelime_MyGuideExample/Guidelime_MyGuideExample.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-ri/Guidelime/HEAD/Guidelime_MyGuideExample/Guidelime_MyGuideExample.zip
--------------------------------------------------------------------------------
/.pkgmeta:
--------------------------------------------------------------------------------
1 | enable-nolib-creation: no
2 | package-as: Guidelime
3 |
4 | move-folders:
5 | Guidelime/Guidelime : Guidelime
6 |
7 | ignore:
8 | - Guidelime_MyGuideExample
9 |
--------------------------------------------------------------------------------
/Guidelime_MyGuideExample/Guidelime_MyGuideExample.toc:
--------------------------------------------------------------------------------
1 |
2 | ## Title: Guidelime_MyGuideExample
3 | ## Author: Me
4 | ## Version: 0.001-Beta
5 | ## Interface: 20501
6 | ## Dependencies: Guidelime
7 | ## Notes: My own guide for Guidelime
8 |
9 | MyGuide.lua
--------------------------------------------------------------------------------
/Guidelime/Icons/ArrowLicense.txt:
--------------------------------------------------------------------------------
1 | Everyone has full permission to do whatever they want with this model file in any non-commercial manner. Attribution to "Guillotine" is nice, but not necessary. For any commrcial use, contact me first at curse.guillotine@gmail.com
--------------------------------------------------------------------------------
/Guidelime/Libs/LibDBIcon-1.0/lib.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Guidelime/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/CallbackHandler-1.0/CallbackHandler-1.0.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/LibStub/LibStub.toc:
--------------------------------------------------------------------------------
1 | ## Interface: 20400
2 | ## Title: Lib: LibStub
3 | ## Notes: Universal Library Stub
4 | ## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
5 | ## X-Website: http://jira.wowace.com/browse/LS
6 | ## X-Category: Library
7 | ## X-License: Public Domain
8 |
9 | LibStub.lua
10 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Lib: HereBeDragons
2 |
3 | ## [2.15-release](https://github.com/Nevcairiel/HereBeDragons/tree/2.15-release) (2025-07-02)
4 | [Full Changelog](https://github.com/Nevcairiel/HereBeDragons/compare/2.14.5-release...2.15-release) [Previous Releases](https://github.com/Nevcairiel/HereBeDragons/releases)
5 |
6 | - Update TOCs
7 | - Update TOC versions
8 | - Add data for Mists Classic
9 | - Remove pre-TWW retail data
10 |
--------------------------------------------------------------------------------
/Guidelime_MyGuideExample/MyGuide.lua:
--------------------------------------------------------------------------------
1 | Guidelime.registerGuide([[
2 | [D *My* own guide for Guidelime \\ http://www.twitch.tv/mytwitch]
3 | [GA Alliance]
4 | [N1-6 My guide Part 1]
5 | [NX6-12 My guide Part 2]
6 | [QA123]
7 | [QC123]
8 | [QT123]
9 | ]], 'My Guides')
10 | -- See https://github.com/max-ri/Guidelime/wiki/WriteAGuide for instructions on how to write a guide.
11 | -- You can use *asterisks* for highlighting and \\ for linebreaks.
12 | -- The text 'My Guides' in the last line defines in which group your guide appears in the guides list. Use the same name for all of your guides.
13 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/HereBeDragons.toc:
--------------------------------------------------------------------------------
1 | ## Interface: 11507,50500,110107,110200
2 | ## Title: Lib: HereBeDragons
3 | ## Notes: HereBeDragons is a data API for the World of Warcraft mapping system
4 | ## Author: Nevcairiel
5 | ## X-eMail: h.leppkes@gmail.com
6 | ## X-Category: Library
7 | ## X-License: BSD
8 | ## X-Website: http://www.wowace.com/addons/herebedragons/
9 | ## Version: 2.15-release
10 | ## X-Curse-Project-ID: 94348
11 |
12 | LibStub\LibStub.lua
13 | CallbackHandler-1.0\CallbackHandler-1.0.lua
14 |
15 | # WoW 8.0+ version
16 | HereBeDragons-2.0.lua
17 | HereBeDragons-Pins-2.0.lua
18 |
19 | # Migration Data for 7.x -> 8.0 migration
20 | HereBeDragons-Migrate.lua
21 |
--------------------------------------------------------------------------------
/Guidelime/QuestScan.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.QS = addon.QS or {}; local QS = addon.QS
5 |
6 | function QS.resetScannedQuests()
7 | if addon.debugging then print("LIME: reset scanned quests") end
8 | QS.scannedQuests = {}
9 | QS.scannedGuides = {}
10 | end
11 |
12 | function QS.scanGuideQuests(guide)
13 | if not addon.guides[guide] or QS.scannedGuides[guide] then return end
14 | local time
15 | if addon.debugging then time = debugprofilestop() end
16 | if addon.guides[guide].steps ~= nil then
17 | for _,step in ipairs(addon.guides[guide].steps) do
18 | if step.elements ~= nil then
19 | for _, element in ipairs(step.elements) do
20 | if element.questId then
21 | if not QS.scannedQuests[element.questId] then QS.scannedQuests[element.questId] = {} end
22 | local entry = {name = guide, line = step.line, t = element.t}
23 | table.insert(QS.scannedQuests[element.questId], entry)
24 | end
25 | end
26 | end
27 | end
28 | end
29 | if addon.debugging then print("LIME: scanning quests for " .. guide .. " in " .. math.floor(debugprofilestop() - time) .. " ms") end
30 | QS.scannedGuides[guide] = true
31 | for _, next in ipairs(addon.guides[guide].next) do
32 | QS.scanGuideQuests(addon.guides[guide].group .. " " .. next)
33 | end
34 | end
--------------------------------------------------------------------------------
/Guidelime/Libs/LibStub/LibStub.lua:
--------------------------------------------------------------------------------
1 | -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
2 | -- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
3 | local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
4 | local LibStub = _G[LIBSTUB_MAJOR]
5 |
6 | if not LibStub or LibStub.minor < LIBSTUB_MINOR then
7 | LibStub = LibStub or {libs = {}, minors = {} }
8 | _G[LIBSTUB_MAJOR] = LibStub
9 | LibStub.minor = LIBSTUB_MINOR
10 |
11 | function LibStub:NewLibrary(major, minor)
12 | assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
13 | minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
14 |
15 | local oldminor = self.minors[major]
16 | if oldminor and oldminor >= minor then return nil end
17 | self.minors[major], self.libs[major] = minor, self.libs[major] or {}
18 | return self.libs[major], oldminor
19 | end
20 |
21 | function LibStub:GetLibrary(major, silent)
22 | if not self.libs[major] and not silent then
23 | error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
24 | end
25 | return self.libs[major], self.minors[major]
26 | end
27 |
28 | function LibStub:IterateLibraries() return pairs(self.libs) end
29 | setmetatable(LibStub, { __call = LibStub.GetLibrary })
30 | end
31 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/LibStub/LibStub.lua:
--------------------------------------------------------------------------------
1 | -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
2 | -- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
3 | local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
4 | local LibStub = _G[LIBSTUB_MAJOR]
5 |
6 | if not LibStub or LibStub.minor < LIBSTUB_MINOR then
7 | LibStub = LibStub or {libs = {}, minors = {} }
8 | _G[LIBSTUB_MAJOR] = LibStub
9 | LibStub.minor = LIBSTUB_MINOR
10 |
11 | function LibStub:NewLibrary(major, minor)
12 | assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
13 | minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
14 |
15 | local oldminor = self.minors[major]
16 | if oldminor and oldminor >= minor then return nil end
17 | self.minors[major], self.libs[major] = minor, self.libs[major] or {}
18 | return self.libs[major], oldminor
19 | end
20 |
21 | function LibStub:GetLibrary(major, silent)
22 | if not self.libs[major] and not silent then
23 | error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
24 | end
25 | return self.libs[major], self.minors[major]
26 | end
27 |
28 | function LibStub:IterateLibraries() return pairs(self.libs) end
29 | setmetatable(LibStub, { __call = LibStub.GetLibrary })
30 | end
31 |
--------------------------------------------------------------------------------
/Guidelime/Bindings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Guidelime.toggleMainFrame()
4 |
5 |
6 | Guidelime.toggleMapMarkers()
7 |
8 |
9 | Guidelime.toggleMinimapMarkers()
10 |
11 |
12 | --for targeting an npc
13 |
14 |
15 | --for targeting an npc
16 |
17 |
18 | --for targeting an npc
19 |
20 |
21 | --for targeting an npc
22 |
23 |
24 | --for targeting an npc
25 |
26 |
27 | --for using a quest item
28 |
29 |
30 | --for using a quest item
31 |
32 |
33 | --for using a quest item
34 |
35 |
36 | --for using a quest item
37 |
38 |
39 | --for using a quest item
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Guidelime/Guidelime-Cata.toc:
--------------------------------------------------------------------------------
1 | ## Title: Guidelime
2 | ## Author: Borick of Arthas(EU)
3 | ## Version: 5.016
4 | ## Interface: 40402
5 | ## SavedVariables: GuidelimeData
6 | ## SavedVariablesPerCharacter: GuidelimeDataChar
7 | ## Notes: Leveling guides with automatic progress updates|r|n|n|TInterface/Addons/Guidelime/Icons/Paypal:12:12|t |cff82c5ffhttps://tinyurl.com/guidelime-paypal|r|n|TInterface/Addons/BagBrother/Art/Discord:12:12:0:-2|t |cff82c5ffhttps://tinyurl.com/guidelime-discord|r|n|n
8 | ## X-License: All Rights Reserved
9 | ## OptionalDeps: Questie
10 | ## OptionalDeps: ClassicCodex
11 | ## OptionalDeps: HereBeDragons
12 |
13 | Libs\LibStub\LibStub.lua
14 | Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
15 | Libs\HereBeDragons\HereBeDragons-2.0.lua
16 | Libs\HereBeDragons\HereBeDragons-Pins-2.0.lua
17 | Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
18 | Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
19 |
20 | Data\Data.lua
21 | Data\MapDB.lua
22 | Data\Internal\QuestsDB.lua
23 | Data\Internal\CreaturesDB.lua
24 | Data\Internal\ObjectsDB.lua
25 | Data\Internal\ItemsDB.lua
26 | Data\Internal\QuestsDB_Locales.lua
27 | Data\Internal\CreaturesDB_Locales.lua
28 | Data\Internal\ObjectsDB_Locales.lua
29 | Data\Internal\ItemsDB_Locales.lua
30 | Data\PositionTools.lua
31 | Data\QuestsTools.lua
32 | Data\QuestieCorrections.lua
33 | Data\Questie.lua
34 | Data\ClassicCodexCorrections.lua
35 | Data\ClassicCodex.lua
36 | Data\FlightmasterDB.lua
37 | Data\FlightmasterDB_Locales.lua
38 | Data\SpellDB.lua
39 | Data\SkillDB.lua
40 | Data\SkillDB_Locales.lua
41 | Localization.lua
42 | Guidelime.lua
43 | MainWindow.lua
44 | CurrentGuide.lua
45 | ActionButtons.lua
46 | Events.lua
47 | Frames.lua
48 | Guides.lua
49 | Options.lua
50 | Editor.lua
51 | EditorTools.lua
52 | Import.lua
53 | GuideParser.lua
54 | Map.lua
55 | QuestLog.lua
56 | QuestScan.lua
57 | CustomCode.lua
58 |
--------------------------------------------------------------------------------
/Guidelime/Guidelime-Mists.toc:
--------------------------------------------------------------------------------
1 | ## Title: Guidelime
2 | ## Author: Borick of Arthas(EU)
3 | ## Version: 5.016
4 | ## Interface: 50500
5 | ## SavedVariables: GuidelimeData
6 | ## SavedVariablesPerCharacter: GuidelimeDataChar
7 | ## Notes: Leveling guides with automatic progress updates|r|n|n|TInterface/Addons/Guidelime/Icons/Paypal:12:12|t |cff82c5ffhttps://tinyurl.com/guidelime-paypal|r|n|TInterface/Addons/BagBrother/Art/Discord:12:12:0:-2|t |cff82c5ffhttps://tinyurl.com/guidelime-discord|r|n|n
8 | ## X-License: All Rights Reserved
9 | ## OptionalDeps: Questie
10 | ## OptionalDeps: ClassicCodex
11 | ## OptionalDeps: HereBeDragons
12 |
13 | Libs\LibStub\LibStub.lua
14 | Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
15 | Libs\HereBeDragons\HereBeDragons-2.0.lua
16 | Libs\HereBeDragons\HereBeDragons-Pins-2.0.lua
17 | Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
18 | Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
19 |
20 | Data\Data.lua
21 | Data\MapDB.lua
22 | Data\Internal\QuestsDB.lua
23 | Data\Internal\CreaturesDB.lua
24 | Data\Internal\ObjectsDB.lua
25 | Data\Internal\ItemsDB.lua
26 | Data\Internal\QuestsDB_Locales.lua
27 | Data\Internal\CreaturesDB_Locales.lua
28 | Data\Internal\ObjectsDB_Locales.lua
29 | Data\Internal\ItemsDB_Locales.lua
30 | Data\PositionTools.lua
31 | Data\QuestsTools.lua
32 | Data\QuestieCorrections.lua
33 | Data\Questie.lua
34 | Data\ClassicCodexCorrections.lua
35 | Data\ClassicCodex.lua
36 | Data\FlightmasterDB.lua
37 | Data\FlightmasterDB_Locales.lua
38 | Data\SpellDB.lua
39 | Data\SkillDB.lua
40 | Data\SkillDB_Locales.lua
41 | Localization.lua
42 | Guidelime.lua
43 | MainWindow.lua
44 | CurrentGuide.lua
45 | ActionButtons.lua
46 | Events.lua
47 | Frames.lua
48 | Guides.lua
49 | Options.lua
50 | Editor.lua
51 | EditorTools.lua
52 | Import.lua
53 | GuideParser.lua
54 | Map.lua
55 | QuestLog.lua
56 | QuestScan.lua
57 | CustomCode.lua
58 |
--------------------------------------------------------------------------------
/Guidelime/Guidelime-Wrath.toc:
--------------------------------------------------------------------------------
1 | ## Title: Guidelime
2 | ## Author: Borick of Arthas(EU)
3 | ## Version: 5.016
4 | ## Interface: 30404
5 | ## SavedVariables: GuidelimeData
6 | ## SavedVariablesPerCharacter: GuidelimeDataChar
7 | ## Notes: Leveling guides with automatic progress updates|r|n|n|TInterface/Addons/Guidelime/Icons/Paypal:12:12|t |cff82c5ffhttps://tinyurl.com/guidelime-paypal|r|n|TInterface/Addons/BagBrother/Art/Discord:12:12:0:-2|t |cff82c5ffhttps://tinyurl.com/guidelime-discord|r|n|n
8 | ## X-License: All Rights Reserved
9 | ## OptionalDeps: Questie
10 | ## OptionalDeps: ClassicCodex
11 | ## OptionalDeps: HereBeDragons
12 |
13 | Libs\LibStub\LibStub.lua
14 | Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
15 | Libs\HereBeDragons\HereBeDragons-2.0.lua
16 | Libs\HereBeDragons\HereBeDragons-Pins-2.0.lua
17 | Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
18 | Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
19 |
20 | Data\Data.lua
21 | Data\MapDB.lua
22 | Data\Internal\QuestsDB.lua
23 | Data\Internal\CreaturesDB.lua
24 | Data\Internal\ObjectsDB.lua
25 | Data\Internal\ItemsDB.lua
26 | Data\Internal\QuestsDB_Locales.lua
27 | Data\Internal\CreaturesDB_Locales.lua
28 | Data\Internal\ObjectsDB_Locales.lua
29 | Data\Internal\ItemsDB_Locales.lua
30 | Data\PositionTools.lua
31 | Data\QuestsTools.lua
32 | Data\QuestieCorrections.lua
33 | Data\Questie.lua
34 | Data\ClassicCodexCorrections.lua
35 | Data\ClassicCodex.lua
36 | Data\FlightmasterDB.lua
37 | Data\FlightmasterDB_Locales.lua
38 | Data\SpellDB.lua
39 | Data\SkillDB.lua
40 | Data\SkillDB_Locales.lua
41 | Localization.lua
42 | Guidelime.lua
43 | MainWindow.lua
44 | CurrentGuide.lua
45 | ActionButtons.lua
46 | Events.lua
47 | Frames.lua
48 | Guides.lua
49 | Options.lua
50 | Editor.lua
51 | EditorTools.lua
52 | Import.lua
53 | GuideParser.lua
54 | Map.lua
55 | QuestLog.lua
56 | QuestScan.lua
57 | CustomCode.lua
58 |
--------------------------------------------------------------------------------
/Guidelime/Guidelime-Vanilla.toc:
--------------------------------------------------------------------------------
1 | ## Title: Guidelime
2 | ## Author: Borick of Arthas(EU)
3 | ## Version: 5.016
4 | ## Interface: 11507
5 | ## SavedVariables: GuidelimeData
6 | ## SavedVariablesPerCharacter: GuidelimeDataChar
7 | ## Notes: Leveling guides with automatic progress updates|r|n|n|TInterface/Addons/Guidelime/Icons/Paypal:12:12|t |cff82c5ffhttps://tinyurl.com/guidelime-paypal|r|n|TInterface/Addons/BagBrother/Art/Discord:12:12:0:-2|t |cff82c5ffhttps://tinyurl.com/guidelime-discord|r|n|n
8 | ## X-License: All Rights Reserved
9 | ## OptionalDeps: Questie
10 | ## OptionalDeps: ClassicCodex
11 | ## OptionalDeps: HereBeDragons
12 |
13 | Libs\LibStub\LibStub.lua
14 | Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
15 | Libs\HereBeDragons\HereBeDragons-2.0.lua
16 | Libs\HereBeDragons\HereBeDragons-Pins-2.0.lua
17 | Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
18 | Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
19 |
20 | Data\Data.lua
21 | Data\MapDB.lua
22 | Data\Internal\QuestsDB.lua
23 | Data\Internal\CreaturesDB.lua
24 | Data\Internal\ObjectsDB.lua
25 | Data\Internal\ItemsDB.lua
26 | Data\Internal\QuestsDB_Locales.lua
27 | Data\Internal\CreaturesDB_Locales.lua
28 | Data\Internal\ObjectsDB_Locales.lua
29 | Data\Internal\ItemsDB_Locales.lua
30 | Data\PositionTools.lua
31 | Data\QuestsTools.lua
32 | Data\QuestieCorrections.lua
33 | Data\Questie.lua
34 | Data\ClassicCodexCorrections.lua
35 | Data\ClassicCodex.lua
36 | Data\FlightmasterDB.lua
37 | Data\FlightmasterDB_Locales.lua
38 | Data\SpellDB.lua
39 | Data\SkillDB.lua
40 | Data\SkillDB_Locales.lua
41 | Localization.lua
42 | Guidelime.lua
43 | MainWindow.lua
44 | CurrentGuide.lua
45 | ActionButtons.lua
46 | Events.lua
47 | Frames.lua
48 | Guides.lua
49 | Options.lua
50 | Editor.lua
51 | EditorTools.lua
52 | Import.lua
53 | GuideParser.lua
54 | Map.lua
55 | QuestLog-Classic.lua
56 | QuestScan.lua
57 | CustomCode.lua
58 |
--------------------------------------------------------------------------------
/Guidelime/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua:
--------------------------------------------------------------------------------
1 |
2 | assert(LibStub, "LibDataBroker-1.1 requires LibStub")
3 | assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
4 |
5 | local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
6 | if not lib then return end
7 | oldminor = oldminor or 0
8 |
9 |
10 | lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
11 | lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
12 | local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
13 |
14 | if oldminor < 2 then
15 | lib.domt = {
16 | __metatable = "access denied",
17 | __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
18 | }
19 | end
20 |
21 | if oldminor < 3 then
22 | lib.domt.__newindex = function(self, key, value)
23 | if not attributestorage[self] then attributestorage[self] = {} end
24 | if attributestorage[self][key] == value then return end
25 | attributestorage[self][key] = value
26 | local name = namestorage[self]
27 | if not name then return end
28 | callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
29 | callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
30 | callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
31 | callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
32 | end
33 | end
34 |
35 | if oldminor < 2 then
36 | function lib:NewDataObject(name, dataobj)
37 | if self.proxystorage[name] then return end
38 |
39 | if dataobj then
40 | assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
41 | self.attributestorage[dataobj] = {}
42 | for i,v in pairs(dataobj) do
43 | self.attributestorage[dataobj][i] = v
44 | dataobj[i] = nil
45 | end
46 | end
47 | dataobj = setmetatable(dataobj or {}, self.domt)
48 | self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
49 | self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
50 | return dataobj
51 | end
52 | end
53 |
54 | if oldminor < 1 then
55 | function lib:DataObjectIterator()
56 | return pairs(self.proxystorage)
57 | end
58 |
59 | function lib:GetDataObjectByName(dataobjectname)
60 | return self.proxystorage[dataobjectname]
61 | end
62 |
63 | function lib:GetNameByDataObject(dataobject)
64 | return self.namestorage[dataobject]
65 | end
66 | end
67 |
68 | if oldminor < 4 then
69 | local next = pairs(attributestorage)
70 | function lib:pairs(dataobject_or_name)
71 | local t = type(dataobject_or_name)
72 | assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
73 |
74 | local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
75 | assert(attributestorage[dataobj], "Data object not found")
76 |
77 | return next, attributestorage[dataobj], nil
78 | end
79 |
80 | local ipairs_iter = ipairs(attributestorage)
81 | function lib:ipairs(dataobject_or_name)
82 | local t = type(dataobject_or_name)
83 | assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
84 |
85 | local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
86 | assert(attributestorage[dataobj], "Data object not found")
87 |
88 | return ipairs_iter, attributestorage[dataobj], 0
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/Guidelime/QuestLog.lua:
--------------------------------------------------------------------------------
1 |
2 | local addonName, addon = ...
3 | local L = addon.L
4 |
5 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
6 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
7 | addon.F = addon.F or {}; local F = addon.F -- Frames
8 | addon.QS = addon.QS or {}; local QS = addon.QS -- QuestScan
9 |
10 | addon.QL = addon.QL or {}; local QL = addon.QL -- QuestLog
11 |
12 | function QL.updateQuestLog()
13 | if not GuidelimeData.showQuestLevels and not GuidelimeData.showQuestIds and not GuidelimeData.showTooltips then return end
14 |
15 | local numEntries, numQuests = GetNumQuestLogEntries();
16 |
17 | if (numEntries == 0) then return end
18 |
19 | local questIndex, questLogTitle, title, level, _, isHeader, isComplete, id, tooltip
20 | for i = 1, QUESTS_DISPLAYED, 1 do
21 | questIndex = i + FauxScrollFrame_GetOffset(QuestLogListScrollFrame);
22 |
23 | if (questIndex <= numEntries) then
24 | title, level, _, isHeader, _, isComplete, _, id = GetQuestLogTitle(questIndex)
25 |
26 | if (not isHeader) then
27 | local newTitle = title
28 | if GuidelimeData.showQuestLevels then
29 | local qtype = ""
30 | if QT.getQuestType(id) == "Dungeon" then
31 | qtype = "D"
32 | elseif QT.getQuestType(id) == "Raid" then
33 | qtype = "R"
34 | elseif QT.getQuestType(id) == "Group" then
35 | qtype = "P"
36 | elseif QT.getQuestType(id) == "Elite" then
37 | qtype = "+"
38 | end
39 | newTitle = format(" [%d%s] ", level, qtype) .. title
40 | end
41 | if GuidelimeData.showQuestIds then
42 | newTitle = newTitle .. format(" (#%d)", id)
43 | end
44 | tooltip = ""
45 | if QS.scannedQuests[id] then
46 | tooltip = tooltip .. "|T" .. addon.icons.MAP .. ":12|t" .. L.QUEST_CONTAINED_IN_GUIDE .. "\n"
47 | for _, entry in ipairs(QS.scannedQuests[id]) do
48 | tooltip = tooltip .. CG.getQuestIcon(id, entry.t, nil, isComplete)
49 | if GuidelimeData.showLineNumbers then tooltip = tooltip .. entry.line .. " " end
50 | tooltip = tooltip .. entry.name .. "\n"
51 | end
52 | else
53 | tooltip = tooltip .. "|T" .. addon.icons.MAP .. ":12|t" .. L.QUEST_NOT_CONTAINED_IN_GUIDE
54 | end
55 | if newTitle ~= title or GuidelimeData.showTooltips then
56 | questLogTitle = QuestLogListScrollFrame.buttons[i]
57 | questLogTitle:SetText(newTitle)
58 | QuestLogTitleButton_Resize(questLogTitle);
59 | if GuidelimeData.showTooltips and tooltip ~= "" then
60 | questLogTitle.tooltip = tooltip
61 | questLogTitle:SetScript("OnEnter", function(self) if self.tooltip ~= nil and self.tooltip ~= "" then GameTooltip:SetOwner(self, "ANCHOR_RIGHT",0,-32); GameTooltip:SetText(self.tooltip); GameTooltip:Show(); F.showingTooltip = true end end)
62 | questLogTitle:SetScript("OnLeave", function(self) if self.tooltip ~= nil and self.tooltip ~= "" and F.showingTooltip then GameTooltip:Hide(); F.showingTooltip = false end end)
63 | end
64 | end
65 | end
66 | end
67 | end
68 | end
69 | QuestLogFrame:HookScript('OnUpdate', QL.updateQuestLog)
70 |
71 | function QL.showQuestLogFrame(questId)
72 | local questLogIndex = GetQuestLogIndexByID(questId)
73 | if questLogIndex == 0 then return end
74 |
75 | -- if Questie is installed we use Questie's TrackerUtils as it handles (and hopefully gets updates for) possible quest log addons (Thanks!)
76 | local TrackerUtils = QuestieLoader and QuestieLoader:ImportModule("TrackerUtils")
77 | if TrackerUtils then return TrackerUtils:ShowQuestLog({Id = questId}) end
78 |
79 | SelectQuestLogEntry(questLogIndex)
80 | if not QuestLogFrame:IsShown() and not InCombatLockdown() then
81 | ShowUIPanel(QuestLogFrame)
82 | end
83 | QuestLog_UpdateQuestDetails()
84 | QuestLog_Update()
85 | end
86 |
--------------------------------------------------------------------------------
/Guidelime/QuestLog-Classic.lua:
--------------------------------------------------------------------------------
1 |
2 | local addonName, addon = ...
3 | local L = addon.L
4 |
5 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
6 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
7 | addon.F = addon.F or {}; local F = addon.F -- Frames
8 | addon.QS = addon.QS or {}; local QS = addon.QS -- QuestScan
9 |
10 | addon.QL = addon.QL or {}; local QL = addon.QL -- QuestLog
11 |
12 | function QL.updateQuestLog()
13 | if not GuidelimeData.showQuestLevels and not GuidelimeData.showQuestIds and not GuidelimeData.showTooltips then return end
14 |
15 | local numEntries, numQuests = GetNumQuestLogEntries();
16 |
17 | if (numEntries == 0) then return end
18 |
19 | local questIndex, questLogTitle, title, level, _, isHeader, isComplete, questCheck, questCheckXOfs, id, tooltip
20 | for i = 1, QUESTS_DISPLAYED, 1 do
21 | questIndex = i + FauxScrollFrame_GetOffset(QuestLogListScrollFrame);
22 |
23 | if (questIndex <= numEntries) then
24 | title, level, _, isHeader, _, isComplete, _, id = GetQuestLogTitle(questIndex)
25 |
26 | if (not isHeader) then
27 | local newTitle = title
28 | if GuidelimeData.showQuestLevels then
29 | local qtype = ""
30 | if QT.getQuestType(id) == "Dungeon" then
31 | qtype = "D"
32 | elseif QT.getQuestType(id) == "Raid" then
33 | qtype = "R"
34 | elseif QT.getQuestType(id) == "Group" then
35 | qtype = "P"
36 | elseif QT.getQuestType(id) == "Elite" then
37 | qtype = "+"
38 | end
39 | newTitle = format(" [%d%s] ", level, qtype) .. title
40 | end
41 | if GuidelimeData.showQuestIds then
42 | newTitle = newTitle .. format(" (#%d)", id)
43 | end
44 | tooltip = ""
45 | if QS.scannedQuests[id] then
46 | tooltip = tooltip .. "|T" .. addon.icons.MAP .. ":12|t" .. L.QUEST_CONTAINED_IN_GUIDE .. "\n"
47 | for _, entry in ipairs(QS.scannedQuests[id]) do
48 | tooltip = tooltip .. CG.getQuestIcon(id, entry.t, nil, isComplete)
49 | if GuidelimeData.showLineNumbers then tooltip = tooltip .. entry.line .. " " end
50 | tooltip = tooltip .. entry.name .. "\n"
51 | end
52 | else
53 | tooltip = tooltip .. "|T" .. addon.icons.MAP .. ":12|t" .. L.QUEST_NOT_CONTAINED_IN_GUIDE
54 | end
55 | if newTitle ~= title then
56 | questLogTitle = _G["QuestLogTitle"..i]
57 | questCheck = _G["QuestLogTitle"..i.."Check"]
58 | questLogTitle:SetText(newTitle)
59 | QuestLogDummyText:SetText(newTitle)
60 | questCheck:SetPoint("LEFT", questLogTitle, "LEFT", QuestLogDummyText:GetWidth()+24, 0);
61 | end
62 | if GuidelimeData.showTooltips and tooltip ~= "" then
63 | questLogTitle = _G["QuestLogTitle"..i]
64 | questLogTitle.tooltip = tooltip
65 | questLogTitle:SetScript("OnEnter", function(self) if self.tooltip ~= nil and self.tooltip ~= "" then GameTooltip:SetOwner(self, "ANCHOR_RIGHT",0,-32); GameTooltip:SetText(self.tooltip); GameTooltip:Show(); F.showingTooltip = true end end)
66 | questLogTitle:SetScript("OnLeave", function(self) if self.tooltip ~= nil and self.tooltip ~= "" and F.showingTooltip then GameTooltip:Hide(); F.showingTooltip = false end end)
67 | end
68 | end
69 | end
70 | end
71 | end
72 | QuestLogFrame:HookScript('OnUpdate', QL.updateQuestLog)
73 |
74 | function QL.showQuestLogFrame(questId)
75 | local questLogIndex = GetQuestLogIndexByID(questId)
76 | if questLogIndex == 0 then return end
77 |
78 | -- if Questie is installed we use Questie's TrackerUtils as it handles (and hopefully gets updates for) possible quest log addons (Thanks!)
79 | local TrackerUtils = QuestieLoader and QuestieLoader:ImportModule("TrackerUtils")
80 | if TrackerUtils then return TrackerUtils:ShowQuestLog({Id = questId}) end
81 |
82 | SelectQuestLogEntry(questLogIndex)
83 | if not QuestLogFrame:IsShown() and not InCombatLockdown() then
84 | ShowUIPanel(QuestLogFrame)
85 | end
86 | QuestLog_UpdateQuestDetails()
87 | QuestLog_Update()
88 | end
89 |
--------------------------------------------------------------------------------
/Guidelime/CustomCode.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
5 |
6 | addon.CC = addon.CC or {}; local CC = addon.CC -- CustomCode
7 |
8 | function CC.parseCustomLuaCode()
9 | CC.wipeFrameData()
10 | local guide = addon.guides[GuidelimeDataChar.currentGuide]
11 | if not (guide and guide.group) then return end
12 | local groupTable = Guidelime[guide.group]
13 | if not groupTable then Guidelime[guide.group] = true end
14 |
15 |
16 | if type(groupTable) == "table" then
17 | local frameCounter = 0
18 | groupTable.__index = groupTable
19 |
20 | for stepLine, step in ipairs(guide.steps) do
21 | if D.applies(step) then
22 | if step.eval and step.event then
23 | local args = {}
24 | local eval = nil
25 | for arg in step.eval:gmatch('[^,]+') do
26 | if not eval then
27 | eval = arg:gsub("%s","")
28 | else
29 | local c = string.match(arg,"^%s*(.*%S+)%s*$")
30 | if c then table.insert(args,c) end
31 | end
32 | end
33 | if eval then
34 | frameCounter = frameCounter + 1
35 | step.event = step.event:gsub("%s","")
36 | if step.event == "" then
37 | step.event = groupTable[eval]() or "OnStepActivation"
38 | end
39 | local eventList = {}
40 | for event in step.event:gmatch('[^,]+') do
41 | table.insert(eventList,event)
42 | end
43 | CC.registerStep(groupTable,eventList,eval,args,frameCounter,guide,step)
44 | end
45 | end
46 | end
47 | end
48 | end
49 | end
50 |
51 | function CC.registerStep(self,eventList,eval,args,frameCounter,guide,step)
52 |
53 | if frameCounter > #CC.customCodeData+1 then
54 | return
55 | end
56 | if #CC.customCodeData < frameCounter then
57 | table.insert(CC.customCodeData,CreateFrame("Frame"))
58 | end
59 |
60 | if type(self[eval]) ~= "function" then
61 | return
62 | end
63 |
64 | local frame = CC.customCodeData[frameCounter]
65 | frame.data = {}
66 | frame.data.guide = guide
67 | frame.data.step = step
68 | frame.args = args
69 |
70 | setmetatable(frame.data, self)
71 |
72 | local function EventHandler(s,...) --Executes the function if step is active or if it's specified on a 0 element step (e.g. guide name)
73 | if s.data.step.active or #s.data.step.elements == 0 or s.data.persistent then
74 | self[eval](s.data,args,...)
75 | end
76 | end
77 | local OnUpdate
78 | for _,eventRaw in pairs(eventList) do
79 | local event = {}
80 | eventRaw:gsub("[^:]+",function(e)
81 | table.insert(event,e)
82 | end)
83 |
84 | --print(eval,event)
85 | if event[1] == "OnUpdate" then
86 | OnUpdate = true
87 | frame:SetScript("OnUpdate",EventHandler)
88 | elseif event[1] == "OnLoad" then
89 | self[eval](frame.data,args,"OnLoad")
90 | elseif event[1] == "OnStepActivation" then
91 | frame.OnStepActivation = self[eval]
92 | elseif event[1] == "OnStepCompletion" then
93 | frame.OnStepCompletion = self[eval]
94 | elseif event[1] == "OnStepUpdate" then
95 | frame.OnStepUpdate = self[eval]
96 | else
97 | if #event == 1 then
98 | if not pcall(frame.RegisterEvent,frame,event[1]) then
99 | print("Error loading guide: Ignoring invalid event name at line "..step.line..": "..event[1])
100 | end
101 | else
102 | if not pcall(frame.RegisterUnitEvent,frame,unpack(event)) then
103 | print("Error loading guide: Ignoring invalid event name at line "..step.line)
104 | end
105 | end
106 | end
107 | end
108 | if not OnUpdate then
109 | CC.customCodeData[frameCounter]:SetScript("OnEvent",EventHandler)
110 | end
111 | end
112 |
113 | function CC.wipeFrameData()
114 | if not CC.customCodeData then
115 | CC.customCodeData = {}
116 | end
117 | for _,frame in pairs(CC.customCodeData) do
118 | frame:SetScript("OnUpdate", nil)
119 | frame:SetScript("OnEvent", nil)
120 | frame:UnregisterAllEvents()
121 | frame.OnStepActivation = nil
122 | frame.OnStepCompletion = nil
123 | frame.OnStepUpdate = nil
124 | frame.args = nil
125 | if frame.data then
126 | frame.data.persistent = nil
127 | frame.data.timer = nil
128 | end
129 | frame.data = nil
130 | end
131 | end
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Guidelime
2 |
3 | A WoW Classic addon for leveling guides with automatic progress updates
4 |
5 | ## What does it do?
6 |
7 | Guides are shown as a list of steps where the progress is updated automatically as much as possible (quests picked up / completed, quest
8 | items looted / mobs killed, etc). Only for some steps completion has to be confirmed manually (such as resupplying, getting new skills, etc).
9 | Locations referenced in the guide are shown on the map and minimap. Also there is an arrow pointing in the direction towards the next step.
10 |
11 | It completely supports **Burning Crusade** Classic and **Wrath of the Lich King** Classic. There are guides for Draenei, Blood Elf, and Death Knight starting zones, as well as Alliance and Horde Outland and Northrend guides.
12 |
13 | **Cataclysm** Classic is supported as well. Currently there are guides for the Worgen starting zone and for the new zones for leveling from 80 to 85. Unfortunately due to the world revamp old guides for leveling from 1 to 60 will not be useful anymore. **Stay tuned for new guides for leveling 1-60**.
14 |
15 | For **Mists of Pandaria** Classic there are guides for the Monk starting zones as well as guides for leveling from 85-90.
16 |
17 | The addon comes with an editor so even if the number of guides included is small (yet) it is very easy to create your own guides or to import
18 | your favorite guide.
19 |
20 | *This is for WoW Classic only. It will not run on private servers and/or retail.*
21 |
22 |
23 |
24 | ## Where do I find guides for the addon?
25 |
26 | See list of all available guides for a complete list of guides that have been written for Guidelime so far.
27 |
28 | ## How do I write a guide?
29 |
30 | How do I write a guide?
31 |
32 | ## How do I publish a guide?
33 |
34 | How do I publish a guide?
35 |
36 | ## How do I install the addon?
37 |
38 | When you download the zip file from this page (click "Clone or Download" then "Download Zip") you will find a *Guidelime* folder (inside the *Guidelime-master* folder). Copy this folder into your *Addons* folder.
39 |
40 | By using this method you will always get to most recent version of the addon (*master*) including all changes that are being made as soon as they are published.
41 |
42 | Alternatively you can also download the addon from https://www.curseforge.com/wow/addons/guidelime or install it via the CurseForge client or any other addon manager. This way you will get the latest stable release. (Which currently is also being updated in average once a day.)
43 |
44 | ## How do I install a guide?
45 |
46 | To install a guide please visit the list of guides mentioned above.
47 |
48 | You install a guide the same way you would install any other addon: Copy the folder containing the guide into your *Addons* folder (in the same folder where the *Guidelime* folder is in).
49 |
50 | You can also use the CurseForge client or other addon manager to install a guide.
51 |
52 | ## What is missing?
53 |
54 | #### More guides
55 |
56 | If you are a guide creator and you have your own guides that you would like to see in Guidelime feel free to contact me.
57 |
58 | #### Localization
59 |
60 | The addon is currently available in English, German, French, Russian and Chinese. If anyone would volunteer to do translations for other languages please contact me.
61 |
62 | The guides itself are language specific. Most guides are available in English language only. But using the editor you can create guides in your own language.
63 |
64 | ## How do I open the window again after I closed it accidentally?
65 |
66 | Type `/lime` or go to *Interface Options*, *AddOns*, *Guidelime*, *Options* and click on show window.
67 |
68 | ## FAQ
69 |
70 | Some more questions concerning this addon are answered in the FAQ
71 |
72 | ## Contact
73 |
74 | Discord: https://discord.gg/dsuCDKNWR7
75 |
76 | You'd like to support me?
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Guidelime/Data/SkillDB.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 |
3 | addon.SK = addon.SK or {}; local SK = addon.SK -- Data/SkillDB
4 |
5 | SK.skills = {
6 | ALCHEMY = { icon = "Interface\\Icons\\Trade_Alchemy", learnedBy = {2275, 2280, 3465, 11612, 28597, 51303}},
7 | HERBALISM = { icon = "Interface\\Icons\\Trade_Herbalism", learnedBy = {2372, 2373, 3571, 11994, 28696, 50301}},
8 | INSCRIPTION = { icon = "Interface\\Icons\\inv_inscription_tradeskill01", learnedBy = {45375, 45376, 45377, 45378, 45379, 45380}},
9 | ENGINEERING = { icon = "Interface\\Icons\\Trade_Engineering", learnedBy = {4039, 4040, 4041, 12657, 30351, 51305}},
10 | MINING = { icon = "Interface\\Icons\\Trade_Mining", learnedBy = {2581, 2582, 3568, 10249, 29355, 50309}},
11 | ENCHANTING = { icon = "Interface\\Icons\\trade_engraving", learnedBy = {7414, 7415, 7416, 13921, 28030, 51312}},
12 | LEATHERWORKING = { icon = "Interface\\Icons\\Trade_LeatherWorking", learnedBy = {2155, 2154, 3812, 10663, 32550, 51301}},
13 | JEWELCRAFTING = { icon = "Interface\\Icons\\inv_misc_gem_01", learnedBy = {25245, 25246, 28896, 28899, 28901, 51310}},
14 | TAILORING = { icon = "Interface\\Icons\\Trade_Tailoring", learnedBy = {3911, 3912, 3913, 12181, 26791, 51308}},
15 | SKINNING = { icon = "Interface\\Icons\\inv_misc_pelt_wolf_01", learnedBy = {8615, 8619, 8620, 10769, 32679, 50307}},
16 | BLACKSMITHING = { icon = "Interface\\Icons\\Trade_BlackSmithing", learnedBy = {2020, 2021, 3539, 9786, 29845, 51298}},
17 |
18 | FIRSTAID = { icon = "Interface\\Icons\\spell_holy_sealofsacrifice", learnedBy = {3279, 3280, 19903, 10847, 27029, 50299}},
19 | FISHING = { icon = "Interface\\Icons\\Trade_Fishing", learnedBy = {7733, 7734, 19889, 18249, 33100, 51293}},
20 | COOKING = { icon = "Interface\\Icons\\inv_misc_food_15", learnedBy = {2551, 3412, 19886, 18261, 33361, 51295}},
21 |
22 | RIDING = { icon = "Interface\\Icons\\Ability_Mount_RidingHorse", learnedBy = {33388, 33391, 34090, 34091}},
23 |
24 | SWORDS = {icon = "Interface\\Icons\\ability_meleedamage"},
25 | AXES = {icon = "Interface\\Icons\\inv_axe_01"},
26 | BOWS = {icon = "Interface\\Icons\\INV_Weapon_Bow_05"},
27 | GUNS = {icon = "Interface\\Icons\\INV_Weapon_Rifle_01"},
28 | MACES = {icon = "Interface\\Icons\\INV_Mace_01"},
29 | TWOHANDEDSWORDS = {icon = "Interface\\Icons\\ability_meleedamage"},
30 | DEFENSE = {icon = "Interface\\Icons\\Ability_Defend"},
31 | DUALWIELD = {icon = "Interface\\Icons\\Ability_DualWield"},
32 | STAVES = {icon = "Interface\\Icons\\INV_Staff_08"},
33 | TWOHANDEDMACES = {icon = "Interface\\Icons\\inv_mace_04"},
34 | UNARMED = {icon = "Interface\\Icons\\ability_golemthunderclap"},
35 | TWOHANDEDAXES = {icon = "Interface\\Icons\\inv_axe_04"},
36 | DAGGERS = {icon = "Interface\\Icons\\ability_steelmelee"},
37 | THROWN = {icon = "Interface\\Icons\\inv_throwingknife_02"},
38 | CROSSBOWS = {icon = "Interface\\Icons\\inv_weapon_crossbow_01"},
39 | WANDS = {icon = "Interface\\Icons\\ability_shootwand"},
40 | POLEARMS = {icon = "Interface\\Icons\\inv_spear_06"},
41 | FISTWEAPONS = {icon = "Interface\\Icons\\inv_gauntlets_04"},
42 | PLATEMAIL = {icon = "Interface\\Icons\\inv_chest_plate01"},
43 | MAIL = {icon = "Interface\\Icons\\inv_chest_chain_05"},
44 | LEATHER = {icon = "Interface\\Icons\\inv_chest_leather_09"},
45 | CLOTH = {icon = "Interface\\Icons\\inv_chest_cloth_21"},
46 | SHIELD = {icon = "Interface\\Icons\\inv_shield_04"},
47 | LOCKPICKING = {icon = "Interface\\Icons\\spell_nature_moonkey"},
48 | ARCHAEOLOGY = {icon = "Interface\\Icons\\Trade_Archaeology"}
49 | }
50 |
51 | function SK.getSkill(name)
52 | local p = name:upper():gsub("[ %-]","")
53 | return SK.skills[p] and p
54 | end
55 |
56 | function SK.isSkill(name)
57 | return SK.getSkill(name) ~= nil
58 | end
59 |
60 | function SK.getLocalizedName(name)
61 | return SK.skillDB_Locales[GetLocale()][name]
62 | end
63 |
64 | function SK.getSkillRank(name)
65 | local locName = SK.getLocalizedName(name)
66 | for i = 1, GetNumSkillLines() do
67 | local skillName, header, _, rank, _, _, max = GetSkillLineInfo(i)
68 | if not header and skillName == locName then
69 | --if addon.debugging then print("LIME: found", name, name == locName and "" or locName, "rank", rank) end
70 | return rank, max
71 | end
72 | end
73 | --if addon.debugging then print("LIME: not found", name, name == locName and "" or locName) end
74 | end
75 |
76 | function SK.isRequiredSkill(name, skillMin, skillMax, maxSkillMin)
77 | local value, valueMax = SK.getSkillRank(name)
78 | if skillMin ~= nil and (value or 0) < skillMin then return false end
79 | if skillMax ~= nil and (value or 0) >= skillMax then return false end
80 | if maxSkillMin ~= nil and (valueMax or 0) < maxSkillMin then return false end
81 | return true
82 | end
83 |
84 | function SK.getSkillIcon(name)
85 | return SK.skills[name] and SK.skills[name].icon
86 | end
87 |
88 | function SK.getSkillLearnedBy(name)
89 | return SK.skills[name] and SK.skills[name].learnedBy
90 | end
91 |
92 | function SK.getMaxSkillLearnedBySpell(id)
93 | if SK.maxSkillLearnedBy == nil then
94 | SK.maxSkillLearnedBy = {}
95 | local maxSkill = { 75, 125, 225, 300, 375, 450 }
96 | for name, S in pairs(SK.skills) do
97 | if S.learnedBy ~= nil then
98 | for i, id in ipairs(S.learnedBy) do
99 | SK.maxSkillLearnedBy[id] = { name, maxSkill[i] }
100 | end
101 | end
102 | end
103 | end
104 | if SK.maxSkillLearnedBy[id] ~= nil then
105 | return unpack(SK.maxSkillLearnedBy[id])
106 | end
107 | end
108 |
109 |
--------------------------------------------------------------------------------
/Guidelime/Libs/HereBeDragons/CallbackHandler-1.0/CallbackHandler-1.0.lua:
--------------------------------------------------------------------------------
1 | --[[ $Id: CallbackHandler-1.0.lua 26 2022-12-12 15:09:39Z nevcairiel $ ]]
2 | local MAJOR, MINOR = "CallbackHandler-1.0", 8
3 | local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
4 |
5 | if not CallbackHandler then return end -- No upgrade needed
6 |
7 | local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
8 |
9 | -- Lua APIs
10 | local securecallfunction, error = securecallfunction, error
11 | local setmetatable, rawget = setmetatable, rawget
12 | local next, select, pairs, type, tostring = next, select, pairs, type, tostring
13 |
14 |
15 | local function Dispatch(handlers, ...)
16 | local index, method = next(handlers)
17 | if not method then return end
18 | repeat
19 | securecallfunction(method, ...)
20 | index, method = next(handlers, index)
21 | until not method
22 | end
23 |
24 | --------------------------------------------------------------------------
25 | -- CallbackHandler:New
26 | --
27 | -- target - target object to embed public APIs in
28 | -- RegisterName - name of the callback registration API, default "RegisterCallback"
29 | -- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
30 | -- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
31 |
32 | function CallbackHandler.New(_self, target, RegisterName, UnregisterName, UnregisterAllName)
33 |
34 | RegisterName = RegisterName or "RegisterCallback"
35 | UnregisterName = UnregisterName or "UnregisterCallback"
36 | if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
37 | UnregisterAllName = "UnregisterAllCallbacks"
38 | end
39 |
40 | -- we declare all objects and exported APIs inside this closure to quickly gain access
41 | -- to e.g. function names, the "target" parameter, etc
42 |
43 |
44 | -- Create the registry object
45 | local events = setmetatable({}, meta)
46 | local registry = { recurse=0, events=events }
47 |
48 | -- registry:Fire() - fires the given event/message into the registry
49 | function registry:Fire(eventname, ...)
50 | if not rawget(events, eventname) or not next(events[eventname]) then return end
51 | local oldrecurse = registry.recurse
52 | registry.recurse = oldrecurse + 1
53 |
54 | Dispatch(events[eventname], eventname, ...)
55 |
56 | registry.recurse = oldrecurse
57 |
58 | if registry.insertQueue and oldrecurse==0 then
59 | -- Something in one of our callbacks wanted to register more callbacks; they got queued
60 | for event,callbacks in pairs(registry.insertQueue) do
61 | local first = not rawget(events, event) or not next(events[event]) -- test for empty before. not test for one member after. that one member may have been overwritten.
62 | for object,func in pairs(callbacks) do
63 | events[event][object] = func
64 | -- fire OnUsed callback?
65 | if first and registry.OnUsed then
66 | registry.OnUsed(registry, target, event)
67 | first = nil
68 | end
69 | end
70 | end
71 | registry.insertQueue = nil
72 | end
73 | end
74 |
75 | -- Registration of a callback, handles:
76 | -- self["method"], leads to self["method"](self, ...)
77 | -- self with function ref, leads to functionref(...)
78 | -- "addonId" (instead of self) with function ref, leads to functionref(...)
79 | -- all with an optional arg, which, if present, gets passed as first argument (after self if present)
80 | target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
81 | if type(eventname) ~= "string" then
82 | error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
83 | end
84 |
85 | method = method or eventname
86 |
87 | local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
88 |
89 | if type(method) ~= "string" and type(method) ~= "function" then
90 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
91 | end
92 |
93 | local regfunc
94 |
95 | if type(method) == "string" then
96 | -- self["method"] calling style
97 | if type(self) ~= "table" then
98 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
99 | elseif self==target then
100 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
101 | elseif type(self[method]) ~= "function" then
102 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
103 | end
104 |
105 | if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
106 | local arg=select(1,...)
107 | regfunc = function(...) self[method](self,arg,...) end
108 | else
109 | regfunc = function(...) self[method](self,...) end
110 | end
111 | else
112 | -- function ref with self=object or self="addonId" or self=thread
113 | if type(self)~="table" and type(self)~="string" and type(self)~="thread" then
114 | error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2)
115 | end
116 |
117 | if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
118 | local arg=select(1,...)
119 | regfunc = function(...) method(arg,...) end
120 | else
121 | regfunc = method
122 | end
123 | end
124 |
125 |
126 | if events[eventname][self] or registry.recurse<1 then
127 | -- if registry.recurse<1 then
128 | -- we're overwriting an existing entry, or not currently recursing. just set it.
129 | events[eventname][self] = regfunc
130 | -- fire OnUsed callback?
131 | if registry.OnUsed and first then
132 | registry.OnUsed(registry, target, eventname)
133 | end
134 | else
135 | -- we're currently processing a callback in this registry, so delay the registration of this new entry!
136 | -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
137 | registry.insertQueue = registry.insertQueue or setmetatable({},meta)
138 | registry.insertQueue[eventname][self] = regfunc
139 | end
140 | end
141 |
142 | -- Unregister a callback
143 | target[UnregisterName] = function(self, eventname)
144 | if not self or self==target then
145 | error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
146 | end
147 | if type(eventname) ~= "string" then
148 | error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
149 | end
150 | if rawget(events, eventname) and events[eventname][self] then
151 | events[eventname][self] = nil
152 | -- Fire OnUnused callback?
153 | if registry.OnUnused and not next(events[eventname]) then
154 | registry.OnUnused(registry, target, eventname)
155 | end
156 | end
157 | if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
158 | registry.insertQueue[eventname][self] = nil
159 | end
160 | end
161 |
162 | -- OPTIONAL: Unregister all callbacks for given selfs/addonIds
163 | if UnregisterAllName then
164 | target[UnregisterAllName] = function(...)
165 | if select("#",...)<1 then
166 | error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
167 | end
168 | if select("#",...)==1 and ...==target then
169 | error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
170 | end
171 |
172 |
173 | for i=1,select("#",...) do
174 | local self = select(i,...)
175 | if registry.insertQueue then
176 | for eventname, callbacks in pairs(registry.insertQueue) do
177 | if callbacks[self] then
178 | callbacks[self] = nil
179 | end
180 | end
181 | end
182 | for eventname, callbacks in pairs(events) do
183 | if callbacks[self] then
184 | callbacks[self] = nil
185 | -- Fire OnUnused callback?
186 | if registry.OnUnused and not next(callbacks) then
187 | registry.OnUnused(registry, target, eventname)
188 | end
189 | end
190 | end
191 | end
192 | end
193 | end
194 |
195 | return registry
196 | end
197 |
198 |
199 | -- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
200 | -- try to upgrade old implicit embeds since the system is selfcontained and
201 | -- relies on closures to work.
202 |
203 |
--------------------------------------------------------------------------------
/Guidelime/EditorTools.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
5 | addon.FM = addon.FM or {}; local FM = addon.FM -- Data/FlightmasterDB
6 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
7 | addon.PT = addon.PT or {}; local PT = addon.PT -- Data/PositionTools
8 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
9 | addon.GP = addon.GP or {}; local GP = addon.GP -- GuideParser
10 |
11 | addon.ET = addon.ET or {}; local ET = addon.ET -- EditorTools
12 |
13 | local function adjustPositions(guide, i, j, diff)
14 | for k = i, #guide.steps do
15 | local step = guide.steps[k]
16 | for l, element in ipairs(step.elements) do
17 | if k > i or l > j then
18 | element.startPos = element.startPos + diff
19 | element.endPos = element.endPos + diff
20 | end
21 | end
22 | end
23 | end
24 |
25 | function ET.removeAllCoordinates(guide)
26 | local text = guide.text
27 | local count = 0
28 | for i, step in ipairs(guide.steps) do
29 | for j, element in ipairs(step.elements) do
30 | if element.t == "GOTO" then
31 | count = count + 1
32 | local oldTag = text:sub(element.startPos, element.endPos)
33 | if addon.debugging then print("LIME: removing", oldTag) end
34 | text = text:sub(1, element.startPos - 1) .. text:sub(element.endPos + 1)
35 | adjustPositions(guide, i, j, -#oldTag)
36 | end
37 | end
38 | end
39 | return text, count
40 | end
41 |
42 | function ET.addQuestCoordinates(guide)
43 | local text = guide.text
44 | local count = 0
45 | for i, step in ipairs(guide.steps) do
46 | for j, element in ipairs(step.elements) do
47 | if GP.getSuperCode(element.t) == "QUEST" then
48 | local tag, first, last, coords = ET.addQuestTag(guide, element, element.questId, element.t, element.objective, element.title, true)
49 | if tag == nil then
50 | if addon.debugging then print("LIME: error reading quest tag \"" .. text:sub(element.startPos, element.endPos) .. "\"") end
51 | else
52 | local oldTag = text:sub(first.startPos, last.endPos)
53 | if coords ~= "" and tag ~= oldTag then
54 | count = count + 1
55 | if addon.debugging then print("LIME: replacing", oldTag, "with", tag) end
56 | text = text:sub(1, first.startPos - 1) .. tag .. text:sub(last.endPos + 1)
57 | adjustPositions(guide, i, j, #tag - #oldTag)
58 | end
59 | end
60 | end
61 | end
62 | end
63 | return text, count
64 | end
65 |
66 | function ET.addQuestTag(guide, selection, id, key, objectiveIndex, text, addCoordinates)
67 | local firstElement, lastElement = selection, selection
68 | local objective = ""
69 | if key == "COMPLETE" and objectiveIndex ~= nil then
70 | objective = "," .. objectiveIndex
71 | end
72 | local coords = ""
73 | if addCoordinates then
74 | local pos = PT.getQuestPosition(id, key, objectiveIndex)
75 | if pos ~= nil then
76 | if pos.radius == 0 then
77 | coords = "[G" .. pos.x .. "," .. pos.y .. pos.zone .. "]"
78 | else
79 | coords = "[G" .. pos.x .. "," .. pos.y .. "," .. (pos.radius + CG.DEFAULT_GOTO_RADIUS) .. pos.zone .. "]"
80 | end
81 | if firstElement ~= nil and firstElement.index > 1 and firstElement.step.elements[firstElement.index - 1].t == "GOTO" then
82 | firstElement = firstElement.step.elements[firstElement.index - 1]
83 | elseif lastElement ~= nil and #lastElement.step.elements > lastElement.index and lastElement.step.elements[lastElement.index + 1].t == "GOTO" then
84 | lastElement = lastElement.step.elements[lastElement.index + 1]
85 | end
86 | end
87 | end
88 | --[[
89 | local applies = ""
90 | if QT.getQuestRaces(id) ~= nil or QT.getQuestFaction(id) ~= nil then
91 | local races = {}
92 | local qraces = QT.getQuestRaces(id)
93 | if qraces == nil then qraces = D.racesPerFaction[QT.getQuestFaction(id)] end
94 | if guide.race ~= nil then
95 | for i, race in ipairs(guide.race) do
96 | if D.contains(qraces, race) then table.insert(races, race) end
97 | end
98 | if #races == #guide.race then races = nil end
99 | elseif guide.faction ~= nil then
100 | for i, race in ipairs(D.racesPerFaction[guide.faction]) do
101 | if D.contains(qraces, race) then table.insert(races, race) end
102 | end
103 | if #races == #D.racesPerFaction[guide.faction] then races = nil end
104 | else
105 | races = QT.getQuestRaces(id)
106 | end
107 | if races ~= nil then
108 | if #races == 0 then
109 | local racesLoc = {}
110 | for i, race in ipairs(qraces) do
111 | table.insert(racesLoc, D.getLocalizedRace(race))
112 | end
113 | F.createPopupFrame(L.ERROR_QUEST_RACE_ONLY .. table.concat(racesLoc, ", ") .. " (#" .. id .. ")"):Show()
114 | return
115 | end
116 | applies = applies .. table.concat(races, ",")
117 | end
118 | end
119 | if QT.getQuestClasses(id) ~= nil or QT.getQuestFaction(id) ~= nil then
120 | local classes = {}
121 | local qclasses = QT.getQuestClasses(id)
122 | if qclasses == nil then qclasses = D.classesPerFaction[QT.getQuestFaction(id)] end
123 | if guide.class ~= nil then
124 | for i, class in ipairs(guide.class) do
125 | if D.contains(qclasses, class) then table.insert(classes, class) end
126 | end
127 | if #classes == #guide.class then classes = nil end
128 | elseif guide.faction ~= nil then
129 | for i, class in ipairs(D.classesPerFaction[guide.faction]) do
130 | if D.contains(qclasses, class) then table.insert(classes, class) end
131 | end
132 | if #classes == #D.classesPerFaction[guide.faction] then classes = nil end
133 | else
134 | classes = QT.getQuestClasses(id)
135 | end
136 | if classes ~= nil then
137 | if #classes == 0 then
138 | local classesLoc = {}
139 | for i, class in ipairs(QT.getQuestClasses(id)) do
140 | table.insert(classesLoc, D.getLocalizedClass(class))
141 | end
142 | F.createPopupFrame(L.ERROR_QUEST_CLASS_ONLY .. table.concat(classesLoc, ", ")):Show()
143 | return
144 | end
145 | if applies ~= "" then applies = applies .. "," end
146 | applies = applies .. table.concat(classes, ",")
147 | end
148 | end
149 | if applies ~= "" then
150 | applies = "[A " .. applies .. "]"
151 | if lastElement ~= nil and #lastElement.step.elements > lastElement.index and lastElement.step.elements[lastElement.index + 1].t == "APPLIES" then
152 | lastElement = lastElement.step.elements[lastElement.index + 1]
153 | end
154 | end]]
155 | text = text or ""
156 | if text ~= "" then text = " " .. text end
157 | return coords .. "[" .. GP.codes["QUEST"] .. key:sub(1, 1) .. id .. objective .. text .. "]" --[[.. applies]], firstElement, lastElement, coords
158 | end
159 |
160 | function ET.addFlightTag(guide, selection, id, key, text, addCoordinates)
161 | local firstElement, lastElement = selection, selection
162 | local coords = ""
163 | if addCoordinates then
164 | local pos = FM.getFlightPoint(id)
165 | if pos ~= nil then
166 | if pos.radius == 0 then
167 | coords = "[G" .. pos.x .. "," .. pos.y .. pos.zone .. "]"
168 | else
169 | coords = "[G" .. pos.x .. "," .. pos.y .. "," .. (pos.radius + CG.DEFAULT_GOTO_RADIUS) .. pos.zone .. "]"
170 | end
171 | if firstElement ~= nil and firstElement.index > 1 and firstElement.step.elements[firstElement.index - 1].t == "GOTO" then
172 | firstElement = firstElement.step.elements[firstElement.index - 1]
173 | elseif lastElement ~= nil and #lastElement.step.elements > lastElement.index and lastElement.step.elements[lastElement.index + 1].t == "GOTO" then
174 | lastElement = lastElement.step.elements[lastElement.index + 1]
175 | end
176 | end
177 | end
178 | text = text or ""
179 | if text ~= "" then text = " " .. text end
180 | return coords .. "[" .. GP.codes[key] .. text .. "]", firstElement, lastElement, coords
181 | end
182 |
183 | function ET.addTargetTag(guide, selection, id, key, text, addCoordinates)
184 | local firstElement, lastElement = selection, selection
185 | local coords = ""
186 | if addCoordinates then
187 | local pos = PT.getNPCPosition(id)
188 | if pos ~= nil then
189 | if pos.radius == 0 then
190 | coords = "[G" .. pos.x .. "," .. pos.y .. pos.zone .. "]"
191 | else
192 | coords = "[G" .. pos.x .. "," .. pos.y .. "," .. (pos.radius + CG.DEFAULT_GOTO_RADIUS) .. pos.zone .. "]"
193 | end
194 | if firstElement ~= nil and firstElement.index > 1 and firstElement.step.elements[firstElement.index - 1].t == "GOTO" then
195 | firstElement = firstElement.step.elements[firstElement.index - 1]
196 | elseif lastElement ~= nil and #lastElement.step.elements > lastElement.index and lastElement.step.elements[lastElement.index + 1].t == "GOTO" then
197 | lastElement = lastElement.step.elements[lastElement.index + 1]
198 | end
199 | end
200 | end
201 | text = text or ""
202 | if text ~= "" then text = " " .. text end
203 | local tar = ""
204 | if id ~= nil then
205 | tar = "[TAR" .. id .. "]"
206 | end
207 | return coords .. tar .. "[" .. GP.codes[key] .. text .. "]", firstElement, lastElement, coords
208 | end
209 |
--------------------------------------------------------------------------------
/Guidelime/Data/Internal/corrections.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | addon.DB = addon.DB or {}; local DB = addon.DB
3 |
4 | -- https://github.com/max-ri/Guidelime/issues/47 -- race restrictions on quest chains introducing flight paths
5 | DB.questsDB[6181].race = {"Human"} -- A Swift Message
6 | DB.questsDB[6281].race = {"Human"} -- Continue to Stormwind
7 | DB.questsDB[6261].race = {"Human"} -- Dungar Longdrink
8 | DB.questsDB[6285].race = {"Human"} -- Return to Lewis
9 | DB.questsDB[6387].race = {"Dwarf", "Gnome"} -- Honor Students
10 | DB.questsDB[6391].race = {"Dwarf", "Gnome"} -- Ride to Ironforge
11 | DB.questsDB[6388].race = {"Dwarf", "Gnome"} -- Gryth Thurden
12 | DB.questsDB[6392].race = {"Dwarf", "Gnome"} -- Return to Brock
13 | DB.questsDB[6344].race = {"NightElf"} -- Nessa Shadowsong
14 | DB.questsDB[6341].race = {"NightElf"} -- The Bounty of Teldrassil
15 | DB.questsDB[6342].race = {"NightElf"} -- Flight to Auberdine
16 | DB.questsDB[6343].race = {"NightElf"} -- Return to Nessa
17 | DB.questsDB[6365].race = {"Orc", "Troll"} -- Meats to Orgrimmar
18 | DB.questsDB[6384].race = {"Orc", "Troll"} -- Ride to Orgrimmar
19 | DB.questsDB[6385].race = {"Orc", "Troll"} -- Doras the Wind Rider Master
20 | DB.questsDB[6386].race = {"Orc", "Troll"} -- Return to the Crossroads
21 | DB.questsDB[6361].race = {"Tauren"} -- A Bundle of Hides
22 | DB.questsDB[6362].race = {"Tauren"} -- Ride to Thunder Bluff
23 | DB.questsDB[6363].race = {"Tauren"} -- Tal the Wind Rider Master
24 | DB.questsDB[6364].race = {"Tauren"} -- Return to Jahan
25 | DB.questsDB[6321].race = {"Undead"} -- Supplying the Sepulcher
26 | DB.questsDB[6323].race = {"Undead"} -- Ride to the Undercity
27 | DB.questsDB[6322].race = {"Undead"} -- Michael Garrett
28 | DB.questsDB[6324].race = {"Undead"} -- Return to Podrig
29 |
30 | -- "Crown of the Earth pt. 5"#934 (Fill the Amethyst Phial...) has been replaced with Crown of the Earth pt. 5"#7383
31 | DB.questsDB[934].replacement = 7383
32 |
33 | -- issues with horde cloth donation quests
34 | -- trolls: 7833 wool - 7834 silk - 7835 mageweave - 7836 + 7837 runecloth npc=14727
35 | -- orcs: 7826 wool - 7827 silk - 7831 mageweave - 7824 + 7832 runecloth npc=14726
36 | DB.questsDB[7835].followup = 7836
37 | DB.questsDB[7836].prev = 7835
38 | DB.questsDB[7836].prequests = {[1] = 7835}
39 | DB.questsDB[7836].source = {[1] = {["id"] = 14727; ["type"] = "npc"}}
40 | DB.questsDB[7836].deliver = {[1] = {["id"] = 14727; ["type"] = "npc"}}
41 | DB.questsDB[7837].source = {[1] = {["id"] = 14727; ["type"] = "npc"}}
42 | DB.questsDB[7837].deliver = {[1] = {["id"] = 14727; ["type"] = "npc"}}
43 | DB.questsDB[7831].followup = 7824
44 | DB.questsDB[7824].prev = 7831
45 | DB.questsDB[7824].prequests = {[1] = 7831}
46 | DB.questsDB[7824].source = {[1] = {["id"] = 14726; ["type"] = "npc"}}
47 | DB.questsDB[7824].deliver = {[1] = {["id"] = 14726; ["type"] = "npc"}}
48 | DB.questsDB[7832].source = {[1] = {["id"] = 14726; ["type"] = "npc"}}
49 | DB.questsDB[7832].deliver = {[1] = {["id"] = 14726; ["type"] = "npc"}}
50 |
51 | -- night elf version "Donation of Wool" has wrong id
52 | DB.questsDB[7792] = DB.questsDB[7797]
53 | DB.questsDB[7797] = nil
54 | DB.questsDB[7798].prequests = { 7792 }
55 |
56 | -- issues with ironforge cloth donation quests
57 | -- dwarf: 7802 wool - 7803 silk - 7804 mageweave - 7805 + 7806 runecloth npc=14723
58 | -- gnome: 7807 wool - 7808 silk - 7809 mageweave - 7811 + 7812 runecloth npc=14724
59 | DB.questsDB[7808].followup = 7809
60 | DB.questsDB[7809].prev = 7808
61 | DB.questsDB[7809].prequests = {[1] = 7808}
62 | DB.questsDB[7803].followup = 7804
63 | DB.questsDB[7804].prev = 7803
64 | DB.questsDB[7804].prequests = {[1] = 7803}
65 |
66 | DB.questsDB[7802].source = {[1] = {["id"] = 14723; ["type"] = "npc"}}
67 | DB.questsDB[7802].deliver = {[1] = {["id"] = 14723; ["type"] = "npc"}}
68 | DB.questsDB[7803].source = {[1] = {["id"] = 14723; ["type"] = "npc"}}
69 | DB.questsDB[7803].deliver = {[1] = {["id"] = 14723; ["type"] = "npc"}}
70 | DB.questsDB[7804].source = {[1] = {["id"] = 14723; ["type"] = "npc"}}
71 | DB.questsDB[7804].deliver = {[1] = {["id"] = 14723; ["type"] = "npc"}}
72 |
73 | DB.questsDB[7807].source = {[1] = {["id"] = 14724; ["type"] = "npc"}}
74 | DB.questsDB[7807].deliver = {[1] = {["id"] = 14724; ["type"] = "npc"}}
75 | DB.questsDB[7808].source = {[1] = {["id"] = 14724; ["type"] = "npc"}}
76 | DB.questsDB[7808].deliver = {[1] = {["id"] = 14724; ["type"] = "npc"}}
77 | DB.questsDB[7809].source = {[1] = {["id"] = 14724; ["type"] = "npc"}}
78 | DB.questsDB[7809].deliver = {[1] = {["id"] = 14724; ["type"] = "npc"}}
79 |
80 |
81 | -- "Heeding the Call" (druid bear form quest) source npc for 5926 / 5928 are switched, reported by WittyWalnut137
82 | DB.questsDB[5926].source = {[1] = {["id"] = 6746; ["type"] = "npc"}}
83 | DB.questsDB[5928].source = {[1] = {["id"] = 3064; ["type"] = "npc"}}
84 |
85 | -- Timerbaw Hold quests replaced
86 | DB.questsDB[6131].replacement = 8460
87 | DB.questsDB[6221].replacement = 8461
88 | DB.questsDB[6241].replacement = 8464
89 |
90 | -- wrong name "Thunderbrew Lager" for "Thunderbrew"#117
91 | DB.questsDB[117].name = "Thunderbrew" -- reported by WittyWalnut137
92 | DB.questLocalesConverted["deDE"][117].name = "Donnerbräu"
93 | DB.questLocalesConverted["esES"][117].name = "Cebatruenos"
94 | DB.questLocalesConverted["esMX"][117].name = "Cebatruenos"
95 | DB.questLocalesConverted["frFR"][117].name = "La Tonnebière"
96 | DB.questLocalesConverted["ruRU"][117].name = "Громоварское"
97 | DB.questLocalesConverted["koKR"][117].name = "썬더브루 맥주"
98 |
99 | -- "Elmore's Task"#1097 can be started by Verner Osgood and Smith Argus; reported by WittyWalnut137
100 | DB.questsDB[1097].source[2] = {["id"] = 415; ["type"] = "npc"}
101 |
102 | -- prequests: Requires one of
103 | DB.questsDB[1122].oneOfPrequests = true
104 | DB.questsDB[5092].oneOfPrequests = true
105 | DB.questsDB[5096].oneOfPrequests = true
106 | DB.questsDB[5149].oneOfPrequests = true
107 | DB.questsDB[6383].oneOfPrequests = true
108 | DB.questsDB[4024].oneOfPrequests = true
109 | DB.questsDB[794].oneOfPrequests = true -- warlocks only have to do their own version of "Vile Familiars" (1499), reported by WittyWalnut137/shawnrobinson1977, confirmed on my warlock
110 | DB.questsDB[990].oneOfPrequests = true
111 | DB.questsDB[5922].oneOfPrequests = true
112 | DB.questsDB[6126].oneOfPrequests = true
113 |
114 | -- optional breadcrumb
115 | DB.questsDB[473].prequests = { 455 } -- "Report to Captain Stoutfist"#473 can only be accepted after having completed "The Algaz Gauntlet"#455 (see https://github.com/max-ri/Guidelime/issues/107)
116 | DB.questsDB[464].prequests = nil -- "War Banners"#464 can be accepted without having completed "Report to Captain Stoutfist"#473
117 | DB.questsDB[1880].prequests = nil -- mage lvl 10 quest, seen on my mage
118 | DB.questsDB[1861].prequests = nil -- mage lvl 10 quest, seen on my mage
119 | DB.questsDB[1302].prequests = nil -- James Hyal, reported by Branislav
120 | DB.questsDB[691].prequests = nil -- Worth Its Weight in Gold, reported by Branislav
121 | DB.questsDB[484].prequests = nil -- Young Crocolisk Skins, reported by Branislav / ahmp(in his guide)
122 | DB.questsDB[4136].prequests = nil -- Ribbly Screwspigot, reported by Mushroozard
123 | DB.questsDB[5096].prequests = nil -- Scarlet Diversions, marked as one of but apparently available without any! reported by Mushroozard
124 | DB.questsDB[4134].prequests = nil -- Lost Thunderbrew Recipe, reported by Mushroozard
125 | DB.questsDB[5082].prequests = nil -- Threat of the Winterfall, reported by Mushroozard
126 | DB.questsDB[5887].prequests = { 4102 } -- Salve via Hunting, reported by Mushroozard
127 | DB.questsDB[4768].prequests = nil -- The Darkstone Tablet, reported by Mushroozard
128 | DB.questsDB[207].prequests = { 204 } -- Kurzen's Mystery, reported by InsaneKane / ahmp(in his guide); (one of) the correct prerequisite(s) is "Bad Medicine"#204 as seen by Zarant (https://youtu.be/Nu1Y_Caf-Ds?t=19302) (probably "Second Rebellion"#203 too because that are the prerequisites for "Special Forces"#574 as well)
129 | DB.questsDB[429].prequests = nil -- Wild Hearts, reported by Olo
130 | DB.questsDB[1884].prequests = nil -- Ju-Ju Heaps, reported by Olo (another mage lvl 10)
131 | DB.questsDB[1204].prequests = nil -- Mudrock Soup and Bugs, reported by Zarant
132 | DB.questsDB[1395].prequests = nil -- Supplies for Nethergarde, reported by Zarant
133 | DB.questsDB[4126].prequests = nil -- Hurley Blackbreath, reported by Zarant
134 | DB.questsDB[5149].prequests = nil -- Pamela's Doll, marked as one of but apparently available without any! reported by Zarant
135 | DB.questsDB[4764].prequests = nil -- Doomrigger's Clasp, reported by Zarant
136 | DB.questsDB[4734].prequests = nil -- Egg Freezing, reported by Zarant
137 | DB.questsDB[2518].prequests = nil -- Tears of the Moon, reported by Zarant
138 | DB.questsDB[3447].prequests = { 3444 } -- Secret of the Circle; prequest "Into the Depths"#3446 definitely wrong reported by WittyWalnut137; correct prequest according to wowhead is "The Stone Circle"#3444
139 | DB.questsDB[3981].prequests = { 3906 } -- Commander Gor'shak; prequest "Disharmony of Fire"#3907 definitely wrong reported by WittyWalnut137; correct prequest according to wowhead is "Disharmony of Flame"#3906
140 | DB.questsDB[732].prequests = nil
141 |
142 | -- https://github.com/max-ri/Guidelime/issues/120
143 | DB.questsDB[614].faction = nil;
144 | DB.questsDB[615].faction = nil;
145 | DB.questsDB[615].faction = "Alliance";
146 | DB.questsDB[8553].faction = nil;
147 | DB.questsDB[5238].faction = "Horde";
148 | DB.questsDB[5092].oneOfPrequests = nil;
149 | DB.questsDB[5092].prequests = nil;
150 | DB.questsDB[8551].faction = nil;
151 | DB.questsDB[353].prequests = nil
152 |
--------------------------------------------------------------------------------
/Guidelime/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua:
--------------------------------------------------------------------------------
1 | --[[ $Id: CallbackHandler-1.0.lua 1131 2015-06-04 07:29:24Z nevcairiel $ ]]
2 | local MAJOR, MINOR = "CallbackHandler-1.0", 6
3 | local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
4 |
5 | if not CallbackHandler then return end -- No upgrade needed
6 |
7 | local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
8 |
9 | -- Lua APIs
10 | local tconcat = table.concat
11 | local assert, error, loadstring = assert, error, loadstring
12 | local setmetatable, rawset, rawget = setmetatable, rawset, rawget
13 | local next, select, pairs, type, tostring = next, select, pairs, type, tostring
14 |
15 | -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
16 | -- List them here for Mikk's FindGlobals script
17 | -- GLOBALS: geterrorhandler
18 |
19 | local xpcall = xpcall
20 |
21 | local function errorhandler(err)
22 | return geterrorhandler()(err)
23 | end
24 |
25 | local function CreateDispatcher(argCount)
26 | local code = [[
27 | local next, xpcall, eh = ...
28 |
29 | local method, ARGS
30 | local function call() method(ARGS) end
31 |
32 | local function dispatch(handlers, ...)
33 | local index
34 | index, method = next(handlers)
35 | if not method then return end
36 | local OLD_ARGS = ARGS
37 | ARGS = ...
38 | repeat
39 | xpcall(call, eh)
40 | index, method = next(handlers, index)
41 | until not method
42 | ARGS = OLD_ARGS
43 | end
44 |
45 | return dispatch
46 | ]]
47 |
48 | local ARGS, OLD_ARGS = {}, {}
49 | for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
50 | code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
51 | return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
52 | end
53 |
54 | local Dispatchers = setmetatable({}, {__index=function(self, argCount)
55 | local dispatcher = CreateDispatcher(argCount)
56 | rawset(self, argCount, dispatcher)
57 | return dispatcher
58 | end})
59 |
60 | --------------------------------------------------------------------------
61 | -- CallbackHandler:New
62 | --
63 | -- target - target object to embed public APIs in
64 | -- RegisterName - name of the callback registration API, default "RegisterCallback"
65 | -- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
66 | -- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
67 |
68 | function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
69 |
70 | RegisterName = RegisterName or "RegisterCallback"
71 | UnregisterName = UnregisterName or "UnregisterCallback"
72 | if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
73 | UnregisterAllName = "UnregisterAllCallbacks"
74 | end
75 |
76 | -- we declare all objects and exported APIs inside this closure to quickly gain access
77 | -- to e.g. function names, the "target" parameter, etc
78 |
79 |
80 | -- Create the registry object
81 | local events = setmetatable({}, meta)
82 | local registry = { recurse=0, events=events }
83 |
84 | -- registry:Fire() - fires the given event/message into the registry
85 | function registry:Fire(eventname, ...)
86 | if not rawget(events, eventname) or not next(events[eventname]) then return end
87 | local oldrecurse = registry.recurse
88 | registry.recurse = oldrecurse + 1
89 |
90 | Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
91 |
92 | registry.recurse = oldrecurse
93 |
94 | if registry.insertQueue and oldrecurse==0 then
95 | -- Something in one of our callbacks wanted to register more callbacks; they got queued
96 | for eventname,callbacks in pairs(registry.insertQueue) do
97 | local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
98 | for self,func in pairs(callbacks) do
99 | events[eventname][self] = func
100 | -- fire OnUsed callback?
101 | if first and registry.OnUsed then
102 | registry.OnUsed(registry, target, eventname)
103 | first = nil
104 | end
105 | end
106 | end
107 | registry.insertQueue = nil
108 | end
109 | end
110 |
111 | -- Registration of a callback, handles:
112 | -- self["method"], leads to self["method"](self, ...)
113 | -- self with function ref, leads to functionref(...)
114 | -- "addonId" (instead of self) with function ref, leads to functionref(...)
115 | -- all with an optional arg, which, if present, gets passed as first argument (after self if present)
116 | target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
117 | if type(eventname) ~= "string" then
118 | error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
119 | end
120 |
121 | method = method or eventname
122 |
123 | local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
124 |
125 | if type(method) ~= "string" and type(method) ~= "function" then
126 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
127 | end
128 |
129 | local regfunc
130 |
131 | if type(method) == "string" then
132 | -- self["method"] calling style
133 | if type(self) ~= "table" then
134 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
135 | elseif self==target then
136 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
137 | elseif type(self[method]) ~= "function" then
138 | error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
139 | end
140 |
141 | if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
142 | local arg=select(1,...)
143 | regfunc = function(...) self[method](self,arg,...) end
144 | else
145 | regfunc = function(...) self[method](self,...) end
146 | end
147 | else
148 | -- function ref with self=object or self="addonId" or self=thread
149 | if type(self)~="table" and type(self)~="string" and type(self)~="thread" then
150 | error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2)
151 | end
152 |
153 | if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
154 | local arg=select(1,...)
155 | regfunc = function(...) method(arg,...) end
156 | else
157 | regfunc = method
158 | end
159 | end
160 |
161 |
162 | if events[eventname][self] or registry.recurse<1 then
163 | -- if registry.recurse<1 then
164 | -- we're overwriting an existing entry, or not currently recursing. just set it.
165 | events[eventname][self] = regfunc
166 | -- fire OnUsed callback?
167 | if registry.OnUsed and first then
168 | registry.OnUsed(registry, target, eventname)
169 | end
170 | else
171 | -- we're currently processing a callback in this registry, so delay the registration of this new entry!
172 | -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
173 | registry.insertQueue = registry.insertQueue or setmetatable({},meta)
174 | registry.insertQueue[eventname][self] = regfunc
175 | end
176 | end
177 |
178 | -- Unregister a callback
179 | target[UnregisterName] = function(self, eventname)
180 | if not self or self==target then
181 | error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
182 | end
183 | if type(eventname) ~= "string" then
184 | error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
185 | end
186 | if rawget(events, eventname) and events[eventname][self] then
187 | events[eventname][self] = nil
188 | -- Fire OnUnused callback?
189 | if registry.OnUnused and not next(events[eventname]) then
190 | registry.OnUnused(registry, target, eventname)
191 | end
192 | end
193 | if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
194 | registry.insertQueue[eventname][self] = nil
195 | end
196 | end
197 |
198 | -- OPTIONAL: Unregister all callbacks for given selfs/addonIds
199 | if UnregisterAllName then
200 | target[UnregisterAllName] = function(...)
201 | if select("#",...)<1 then
202 | error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
203 | end
204 | if select("#",...)==1 and ...==target then
205 | error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
206 | end
207 |
208 |
209 | for i=1,select("#",...) do
210 | local self = select(i,...)
211 | if registry.insertQueue then
212 | for eventname, callbacks in pairs(registry.insertQueue) do
213 | if callbacks[self] then
214 | callbacks[self] = nil
215 | end
216 | end
217 | end
218 | for eventname, callbacks in pairs(events) do
219 | if callbacks[self] then
220 | callbacks[self] = nil
221 | -- Fire OnUnused callback?
222 | if registry.OnUnused and not next(callbacks) then
223 | registry.OnUnused(registry, target, eventname)
224 | end
225 | end
226 | end
227 | end
228 | end
229 | end
230 |
231 | return registry
232 | end
233 |
234 |
235 | -- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
236 | -- try to upgrade old implicit embeds since the system is selfcontained and
237 | -- relies on closures to work.
238 |
239 |
--------------------------------------------------------------------------------
/Guidelime/Frames.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.F = addon.F or {}; local F = addon.F
5 |
6 | function F.setTooltip(frame, tooltip, setupFunction)
7 | frame.tooltip = tooltip
8 | if tooltip then
9 | frame:SetScript("OnEnter", function(self) if self.tooltip and self.tooltip ~= "" then GameTooltip:SetOwner(self, "ANCHOR_RIGHT",0,-32); (setupFunction or GameTooltip.SetText)(GameTooltip, self.tooltip); GameTooltip:Show(); F.showingTooltip = true end end)
10 | frame:SetScript("OnLeave", function(self) if self.tooltip and self.tooltip ~= "" and F.showingTooltip then GameTooltip:Hide(); F.showingTooltip = false end end)
11 | end
12 | end
13 |
14 | function F.addSliderOption(frame, optionsTable, option, min, max, step, text, tooltip, updateFunction, afterUpdateFunction)
15 | --[[local slider = CreateFrame("Slider", addonName .. option, frame, "OptionsSliderTemplate")
16 | frame.options[option] = slider
17 | slider.editbox = CreateFrame("EditBox", nil, slider, "InputBoxTemplate")
18 | slider:SetMinMaxValues(min, max)
19 | slider:SetValueStep(step)
20 | slider:SetValue(optionsTable[option])
21 | slider.text = _G[addonName .. option .. "Text"]
22 | slider.text:SetText(text)
23 | slider.textLow = _G[addonName .. option .. "Low"]
24 | slider.textHigh = _G[addonName .. option .. "High"]
25 | slider.textLow:SetText(floor(min))
26 | slider.textHigh:SetText(floor(max))
27 | slider.textLow:SetTextColor(0.8,0.8,0.8)
28 | slider.textHigh:SetTextColor(0.8,0.8,0.8)
29 | slider:SetObeyStepOnDrag(true)
30 | slider.editbox:SetSize(45,30)
31 | slider.editbox:ClearAllPoints()
32 | slider.editbox:SetPoint("LEFT", slider, "RIGHT", 15, 0)
33 | slider.editbox:SetText(tostring(optionsTable[option]))
34 | slider.editbox:SetCursorPosition(0)
35 | slider.editbox:SetAutoFocus(false)
36 | slider:SetScript("OnValueChanged", function(self)
37 | slider.editbox:SetText(tostring(math.floor(slider:GetValue() * 100) / 100))
38 | slider.editbox:SetCursorPosition(0)
39 | optionsTable[option] = slider:GetValue()
40 | if addon.debuggging then print("OnValueChanged", slider:GetValue()) end
41 | if updateFunction ~= nil then updateFunction(self) end
42 | end)
43 | slider:SetScript("OnMouseUp", function()
44 | if afterUpdateFunction ~= nil then afterUpdateFunction(self) end
45 | end)
46 | slider.editbox:SetScript("OnEnterPressed", function()
47 | local val = slider.editbox:GetText()
48 | if tonumber(val) then
49 | slider:SetValue(tonumber(val))
50 | slider.editbox:ClearFocus()
51 | optionsTable[option] = slider:GetValue()
52 | if addon.debuggging then print("OnEnterPressed", slider:GetValue()) end
53 | if updateFunction ~= nil then updateFunction(self) end
54 | if afterUpdateFunction ~= nil then afterUpdateFunction(self) end
55 | end
56 | end)
57 | F.setTooltip(slider, tooltip)
58 | return slider--]]
59 | local slider = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
60 | frame.options[option] = slider
61 | slider.editbox = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
62 | slider:SetText(text .. ' (' .. min .. ' - ' .. max .. ')')
63 | slider.editbox:SetSize(45,30)
64 | slider.editbox:ClearAllPoints()
65 | slider.editbox:SetPoint("LEFT", slider, "RIGHT", 15, 0)
66 | slider.editbox:SetText(tostring(optionsTable[option]))
67 | slider.editbox:SetCursorPosition(0)
68 | slider.editbox:SetAutoFocus(false)
69 | slider.editbox:SetScript("OnEditFocusGained", function()
70 | slider.editbox:SetText(tostring(optionsTable[option]))
71 | end)
72 | slider.editbox:SetScript("OnEnterPressed", function()
73 | local val = slider.editbox:GetText()
74 | if tonumber(val) then
75 | slider.editbox:ClearFocus()
76 | optionsTable[option] = tonumber(val)
77 | if updateFunction ~= nil then updateFunction(slider) end
78 | if afterUpdateFunction ~= nil then afterUpdateFunction(slider) end
79 | end
80 | end)
81 | F.setTooltip(slider, tooltip)
82 | return slider
83 | end
84 |
85 | function F.addCheckbox(frame, text, tooltip)
86 | local checkbox = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate")
87 | if text ~= nil then
88 | local textString = checkbox:CreateFontString(nil, "ARTWORK", "GameFontNormal")
89 | textString:SetText(text)
90 | textString:SetPoint("LEFT", checkbox, "RIGHT", 0, 0)
91 | checkbox:SetFontString(textString)
92 | end
93 | F.setTooltip(checkbox, tooltip)
94 | return checkbox
95 | end
96 |
97 | function F.addCheckOption(frame, optionsTable, option, text, tooltip, updateFunction)
98 | local checkbox = F.addCheckbox(frame, text, tooltip)
99 | frame.options[option] = checkbox
100 | if optionsTable[option] ~= false then checkbox:SetChecked(true) end
101 | checkbox:SetScript("OnClick", function()
102 | optionsTable[option] = checkbox:GetChecked()
103 | if updateFunction ~= nil then updateFunction() end
104 | end)
105 | return checkbox
106 | end
107 |
108 | function F.isDoubleClick(frame)
109 | if frame.timer == nil or frame.timer < GetTime() - 1 then
110 | frame.timer = GetTime()
111 | elseif frame.timer ~= nil then
112 | frame.timer = nil
113 | return true
114 | end
115 | end
116 |
117 | function F.addMultilineText(frame, text, width, tooltip, clickFunc, doubleClickFunc)
118 | textbox = CreateFrame("EditBox", nil, frame, BackdropTemplateMixin and "BackdropTemplate")
119 | textbox:SetMultiLine(true)
120 | textbox:SetFontObject("GameFontNormal")
121 | if text ~= nil then textbox:SetText(text) end
122 | F.setTooltip(textbox, tooltip)
123 | if clickFunc ~= nil or doubleClickFunc ~= nil then
124 | textbox:SetScript("OnMouseUp", function(self, button)
125 | if clickFunc ~= nil then clickFunc(self, button) end
126 | if doubleClickFunc ~= nil and F.isDoubleClick(self) then
127 | doubleClickFunc(self, button)
128 | end
129 | end)
130 | end
131 | textbox:SetScript("OnEditFocusGained", function (self) self:ClearFocus() end)
132 | textbox:SetAutoFocus(false)
133 | if width then textbox:SetWidth(width) end
134 |
135 | return textbox
136 | end
137 |
138 | function F.addTextbox(frame, text, width, tooltip)
139 | local textbox = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
140 | textbox.text = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
141 | textbox.text:SetText(text)
142 | textbox:SetFontObject("GameFontNormal")
143 | textbox:SetHeight(10)
144 | textbox:SetWidth(width)
145 | textbox:SetTextColor(1,1,1,1)
146 | F.setTooltip(textbox, tooltip)
147 | return textbox
148 | end
149 |
150 | function F.createPopupFrame(message, okFunc, hasCancel, height)
151 | F.popupFrame = CreateFrame("FRAME", nil, F.popupFrame or UIParent, BackdropTemplateMixin and "BackdropTemplate")
152 | F.popupFrame:SetWidth(550)
153 | if height == nil then height = 150 end
154 | F.popupFrame:SetHeight(height)
155 | F.popupFrame:SetPoint("CENTER", UIParent, "CENTER")
156 | F.popupFrame:SetBackdrop({
157 | bgFile = "Interface/Addons/" .. addonName .. "/Icons/Black", --"Interface/DialogFrame/UI-DialogBox-Background",
158 | edgeFile = "Interface/DialogFrame/UI-DialogBox-Border",
159 | tile = false, edgeSize = 32,
160 | insets = { left = 11, right = 12, top = 12, bottom = 11}
161 | })
162 | F.popupFrame:SetBackdropColor(0,0,0,1)
163 | F.popupFrame:SetFrameStrata("DIALOG")
164 | F.popupFrame:SetFrameLevel(F.popupFrame:GetParent():GetFrameLevel() + 2)
165 | F.popupFrame:SetMovable(true)
166 | F.popupFrame:SetScript("OnKeyDown", function(self,key)
167 | if key == "ESCAPE" then
168 | self:Hide();
169 | end
170 | end)
171 | F.popupFrame:EnableMouse(true)
172 | F.popupFrame:SetScript("OnMouseDown", function(this) this:StartMoving() end)
173 | F.popupFrame:SetScript("OnMouseUp", function(this) this:StopMovingOrSizing() end)
174 | F.popupFrame:SetScript("OnHide", function(self)
175 | if self:GetParent() ~= UIParent then F.popupFrame = self:GetParent() else F.popupFrame = nil end
176 | end)
177 |
178 | if message ~= nil then
179 | F.popupFrame.message = F.popupFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
180 | F.popupFrame.message:SetWidth(530)
181 | F.popupFrame.message:SetWordWrap(true)
182 | F.popupFrame.message:SetText(message);
183 | F.popupFrame.message:SetPoint("TOP", 0, -30 )
184 | end
185 |
186 | F.popupFrame.okBtn = CreateFrame("BUTTON", nil, F.popupFrame, "UIPanelButtonTemplate")
187 | F.popupFrame.okBtn:SetSize(128, 24)
188 | F.popupFrame.okBtn:SetText( OKAY )
189 | if hasCancel then
190 | F.popupFrame.okBtn:SetPoint("BOTTOM", F.popupFrame, -70, 12)
191 | else
192 | F.popupFrame.okBtn:SetPoint("BOTTOM", F.popupFrame, 70, 12)
193 | end
194 | F.popupFrame.okBtn:SetScript("OnClick", function(self)
195 | if okFunc ~= nil and okFunc(self:GetParent()) == false then return end
196 | self:GetParent():Hide()
197 | end)
198 |
199 | if hasCancel then
200 | F.popupFrame.cancelBtn = CreateFrame("BUTTON", nil, F.popupFrame, "UIPanelButtonTemplate")
201 | F.popupFrame.cancelBtn:SetSize(128, 24)
202 | F.popupFrame.cancelBtn:SetText( CANCEL )
203 | F.popupFrame.cancelBtn:SetPoint("BOTTOM", F.popupFrame, 70, 12)
204 | F.popupFrame.cancelBtn:SetScript("OnClick", function(self)
205 | self:GetParent():Hide()
206 | end)
207 | end
208 |
209 | return F.popupFrame
210 | end
211 |
212 | function F.showUrlPopup(url)
213 | return F.showCopyPopup(url, L.URL, 100, 120, true)
214 | end
215 |
216 | function F.showCopyPopup(value, text, textwidth, height, multiline)
217 | local popup = F.createPopupFrame(nil, nil, false, height)
218 | if multiline then
219 | local scrollFrame = CreateFrame("ScrollFrame", nil, popup, "UIPanelScrollFrameTemplate")
220 | scrollFrame:SetPoint("TOPLEFT", popup, "TOPLEFT", 0, -20)
221 | scrollFrame:SetPoint("RIGHT", popup, "RIGHT", -30, 0)
222 | scrollFrame:SetPoint("BOTTOM", popup, "BOTTOM", 0, 40)
223 | local content = CreateFrame("Frame", nil, scrollFrame)
224 | content:SetSize(1, 1)
225 | scrollFrame:SetScrollChild(content)
226 | popup.textbox = CreateFrame("EditBox", nil, content)
227 | popup.textbox:SetMultiLine(true)
228 | popup.textbox:SetFontObject("GameFontNormal")
229 | popup.textbox:SetWidth(550 - textwidth - 30)
230 | popup.textbox.text = popup:CreateFontString(nil, "ARTWORK", "GameFontNormal")
231 | popup.textbox.text:SetText(text)
232 | else
233 | popup.textbox = F.addTextbox(popup, text, 550 - textwidth - 30)
234 | end
235 | popup.textbox.text:SetPoint("TOPLEFT", 20, -20)
236 | popup.textbox:SetPoint("TOPLEFT", 20 + textwidth, -20)
237 | popup.textbox:SetText(value)
238 | popup.textbox:SetFocus()
239 | popup.textbox:HighlightText()
240 | popup:Show()
241 | return popup
242 | end
243 |
244 | function F.SetResizeBounds(frame, minW, minH, maxW, maxH)
245 | if frame.SetResizeBounds ~= nil then
246 | frame:SetResizeBounds(minW, minH, maxW, maxH)
247 | else
248 | frame:SetMinResize(minW, minH)
249 | if maxW ~= nil or maxH ~= nil then frame:SetMaxResize(max) end
250 | end
251 | end
252 |
--------------------------------------------------------------------------------
/Guidelime/Data/SkillDB_Locales.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 |
3 | addon.SK.skillDB_Locales = {
4 | ["deDE"] = {
5 | COOKING = "Kochkunst",
6 | FIRSTAID = "Erste Hilfe",
7 | FISHING = "Angeln",
8 | ALCHEMY = "Alchemie",
9 | BLACKSMITHING = "Schmiedekunst",
10 | ENCHANTING = "Verzauberkunst",
11 | ENGINEERING = "Ingenieurskunst",
12 | HERBALISM = "Kräuterkunde",
13 | INSCRIPTION = "Inschriftenkunde",
14 | JEWELCRAFTING = "Juwelierskunst",
15 | LEATHERWORKING = "Lederverarbeitung",
16 | MINING = "Bergbau",
17 | SKINNING = "Kürschnerei",
18 | TAILORING = "Schneiderei",
19 | SWORDS = "Schwerter",
20 | AXES = "Äxte",
21 | BOWS = "Bögen",
22 | GUNS = "Schusswaffen",
23 | MACES = "Streitkolben",
24 | TWOHANDEDSWORDS = "Zweihandschwerter",
25 | DEFENSE = "Verteidigung",
26 | DUALWIELD = "Beidhändigkeit",
27 | STAVES = "Stäbe",
28 | TWOHANDEDMACES = "Zweihandstreitkolben",
29 | UNARMED = "Unbewaffnet",
30 | TWOHANDEDAXES = "Zweihandäxte",
31 | DAGGERS = "Dolche",
32 | THROWN = "Wurfwaffen",
33 | CROSSBOWS = "Armbrüste",
34 | WANDS = "Zauberstäbe",
35 | POLEARMS = "Stangenwaffen",
36 | FISTWEAPONS = "Faustwaffen",
37 | RIDING = "Reiten",
38 | PLATEMAIL = "Plattenpanzer",
39 | MAIL = "Schwere Rüstung",
40 | LEATHER = "Leder",
41 | CLOTH = "Stoff",
42 | SHIELD = "Schild",
43 | ARCHAEOLOGY = "Archäologie",
44 | },
45 | ["enUS"] = {
46 | COOKING = "Cooking",
47 | FIRSTAID = "First Aid",
48 | FISHING = "Fishing",
49 | ALCHEMY = "Alchemy",
50 | BLACKSMITHING = "Blacksmithing",
51 | ENCHANTING = "Enchanting",
52 | ENGINEERING = "Engineering",
53 | HERBALISM = "Herbalism",
54 | INSCRIPTION = "Inscription",
55 | JEWELCRAFTING = "Jewelcrafting",
56 | LEATHERWORKING = "Leatherworking",
57 | MINING = "Mining",
58 | SKINNING = "Skinning",
59 | TAILORING = "Tailoring",
60 | SWORDS = "Swords",
61 | AXES = "Axes",
62 | BOWS = "Bows",
63 | GUNS = "Guns",
64 | MACES = "Maces",
65 | TWOHANDEDSWORDS = "Two-Handed Swords",
66 | DEFENSE = "Defense",
67 | DUALWIELD = "Dual Wield",
68 | STAVES = "Staves",
69 | TWOHANDEDMACES = "Two-Handed Maces",
70 | UNARMED = "Unarmed",
71 | TWOHANDEDAXES = "Two-Handed Axes",
72 | DAGGERS = "Daggers",
73 | THROWN = "Thrown",
74 | CROSSBOWS = "Crossbows",
75 | WANDS = "Wands",
76 | POLEARMS = "Polearms",
77 | FISTWEAPONS = "Fist Weapons",
78 | RIDING = "Riding",
79 | PLATEMAIL = "Plate Mail",
80 | MAIL = "Mail",
81 | LEATHER = "Leather",
82 | CLOTH = "Cloth",
83 | SHIELD = "Shield",
84 | ARCHAEOLOGY = "Archaeology",
85 | },
86 | ["esES"] = {
87 | COOKING = "Cocina",
88 | FIRSTAID = "Primeros auxilios",
89 | FISHING = "Pesca",
90 | ALCHEMY = "Alquimia",
91 | BLACKSMITHING = "Herrería",
92 | ENCHANTING = "Encantamiento",
93 | ENGINEERING = "Ingeniería",
94 | HERBALISM = "Herboristería",
95 | INSCRIPTION = "Inscripción",
96 | JEWELCRAFTING = "Joyería",
97 | LEATHERWORKING = "Peletería",
98 | MINING = "Minería",
99 | SKINNING = "Desuello",
100 | TAILORING = "Sastrería",
101 | SWORDS = "Espadas",
102 | AXES = "Hachas",
103 | BOWS = "Arcos",
104 | GUNS = "Armas de fuego",
105 | MACES = "Mazas",
106 | TWOHANDEDSWORDS = "Espadas de dos manos",
107 | DEFENSE = "Defensa",
108 | DUALWIELD = "Doble empuñadura",
109 | STAVES = "Bastones",
110 | TWOHANDEDMACES = "Mazas de dos manos",
111 | UNARMED = "Sin armas",
112 | TWOHANDEDAXES = "Hachas de dos manos",
113 | DAGGERS = "Dagas",
114 | THROWN = "Armas arrojadizas",
115 | CROSSBOWS = "Ballestas",
116 | WANDS = "Varitas",
117 | POLEARMS = "Armas de asta",
118 | FISTWEAPONS = "Armas de puño",
119 | RIDING = "Equitación",
120 | PLATEMAIL = "Malla de placas",
121 | MAIL = "Malla",
122 | LEATHER = "Cuero",
123 | CLOTH = "Tela",
124 | SHIELD = "Escudo",
125 | ARCHAEOLOGY = "Arqueología",
126 | },
127 | ["esMX"] = {
128 | COOKING = "Cocina",
129 | FIRSTAID = "Primeros auxilios",
130 | FISHING = "Pesca",
131 | ALCHEMY = "Alquimia",
132 | BLACKSMITHING = "Herrería",
133 | ENCHANTING = "Encantamiento",
134 | ENGINEERING = "Ingeniería",
135 | HERBALISM = "Herboristería",
136 | INSCRIPTION = "Inscripción",
137 | JEWELCRAFTING = "Joyería",
138 | LEATHERWORKING = "Peletería",
139 | MINING = "Minería",
140 | SKINNING = "Desuello",
141 | TAILORING = "Sastrería",
142 | SWORDS = "Espadas",
143 | AXES = "Hachas",
144 | BOWS = "Arcos",
145 | GUNS = "Armas de fuego",
146 | MACES = "Mazas",
147 | TWOHANDEDSWORDS = "Espadas de dos manos",
148 | DEFENSE = "Defensa",
149 | DUALWIELD = "Doble empuñadura",
150 | STAVES = "Bastones",
151 | TWOHANDEDMACES = "Mazas de dos manos",
152 | UNARMED = "Sin armas",
153 | TWOHANDEDAXES = "Hachas de dos manos",
154 | DAGGERS = "Dagas",
155 | THROWN = "Armas arrojadizas",
156 | CROSSBOWS = "Ballestas",
157 | WANDS = "Varitas",
158 | POLEARMS = "Armas de asta",
159 | FISTWEAPONS = "Armas de puño",
160 | RIDING = "Equitación",
161 | PLATEMAIL = "Malla de placas",
162 | MAIL = "Malla",
163 | LEATHER = "Cuero",
164 | CLOTH = "Tela",
165 | SHIELD = "Escudo",
166 | ARCHAEOLOGY = "Arqueología",
167 | },
168 | ["frFR"] = {
169 | COOKING = "Cuisine",
170 | FIRSTAID = "Secourisme",
171 | FISHING = "Pêche",
172 | ALCHEMY = "Alchimie",
173 | BLACKSMITHING = "Forge",
174 | ENCHANTING = "Enchantement",
175 | ENGINEERING = "Ingénierie",
176 | HERBALISM = "Herboristerie",
177 | INSCRIPTION = "Calligraphie",
178 | JEWELCRAFTING = "Joaillerie",
179 | LEATHERWORKING = "Travail du cuir",
180 | MINING = "Minage",
181 | SKINNING = "Dépeçage",
182 | TAILORING = "Couture",
183 | SWORDS = "Epées",
184 | AXES = "Haches",
185 | BOWS = "Arcs",
186 | GUNS = "Armes à feu",
187 | MACES = "Masse",
188 | TWOHANDEDSWORDS = "Epées à deux mains",
189 | DEFENSE = "Défense",
190 | DUALWIELD = "Ambidextrie",
191 | STAVES = "Bâtons",
192 | TWOHANDEDMACES = "Masses à deux mains",
193 | UNARMED = "Mains nues",
194 | TWOHANDEDAXES = "Haches à deux mains",
195 | DAGGERS = "Dagues",
196 | THROWN = "Armes de jet",
197 | CROSSBOWS = "Arbalètes",
198 | WANDS = "Baguettes",
199 | POLEARMS = "Armes d'hast",
200 | FISTWEAPONS = "Armes de pugilat",
201 | RIDING = "Monte",
202 | PLATEMAIL = "Armure en plaques",
203 | MAIL = "Mailles",
204 | LEATHER = "Cuir",
205 | CLOTH = "Tissu",
206 | SHIELD = "Bouclier",
207 | ARCHAEOLOGY = "Archéologie",
208 | },
209 | ["itIT"] = {
210 | COOKING = "Cucina",
211 | FIRSTAID = "Primo Soccorso",
212 | FISHING = "Pesca",
213 | ALCHEMY = "Alchimia",
214 | BLACKSMITHING = "Forgiatura",
215 | ENCHANTING = "Incantamento",
216 | ENGINEERING = "Ingegneria",
217 | HERBALISM = "Erbalismo",
218 | INSCRIPTION = "Runografia",
219 | JEWELCRAFTING = "Oreficeria",
220 | LEATHERWORKING = "Conciatura",
221 | MINING = "Estrazione",
222 | SKINNING = "Scuoiatura",
223 | TAILORING = "Sartoria",
224 | SWORDS = "Spade",
225 | AXES = "Asce",
226 | BOWS = "Archi",
227 | GUNS = "Armi da Fuoco",
228 | MACES = "Mazze",
229 | TWOHANDEDSWORDS = "Spade a Due Mani",
230 | DEFENSE = "Difesa",
231 | DUALWIELD = "Combattimento con Due Armi",
232 | STAVES = "Bastoni",
233 | TWOHANDEDMACES = "Mazze a Due Mani",
234 | UNARMED = "Mani Nude",
235 | TWOHANDEDAXES = "Asce a Due Mani",
236 | DAGGERS = "Pugnali",
237 | THROWN = "Armi da Getto",
238 | CROSSBOWS = "Balestre",
239 | WANDS = "Bacchette",
240 | POLEARMS = "Armi ad Asta",
241 | FISTWEAPONS = "Tirapugni",
242 | RIDING = "Equitazione",
243 | PLATEMAIL = "Piastre",
244 | MAIL = "Maglia",
245 | LEATHER = "Cuoio",
246 | CLOTH = "Stoffa",
247 | SHIELD = "Scudo",
248 | ARCHAEOLOGY = "Archeologia",
249 | },
250 | ["koKR"] = {
251 | COOKING = "요리",
252 | FIRSTAID = "응급치료",
253 | FISHING = "낚시",
254 | ALCHEMY = "연금술",
255 | BLACKSMITHING = "대장기술",
256 | ENCHANTING = "마법부여",
257 | ENGINEERING = "기계공학",
258 | HERBALISM = "약초채집",
259 | INSCRIPTION = "주문각인",
260 | JEWELCRAFTING = "보석세공",
261 | LEATHERWORKING = "가죽세공",
262 | MINING = "채광",
263 | SKINNING = "무두질",
264 | TAILORING = "재봉술",
265 | SWORDS = "도검류",
266 | AXES = "도끼류",
267 | BOWS = "활류",
268 | GUNS = "총기류",
269 | MACES = "둔기류",
270 | TWOHANDEDSWORDS = "양손 도검류",
271 | DEFENSE = "방어",
272 | DUALWIELD = "쌍수 무기",
273 | STAVES = "지팡이류",
274 | TWOHANDEDMACES = "양손 둔기류",
275 | UNARMED = "맨손 전투",
276 | TWOHANDEDAXES = "양손 도끼류",
277 | DAGGERS = "단검류",
278 | THROWN = "투척 무기류",
279 | CROSSBOWS = "석궁류",
280 | WANDS = "마법봉류",
281 | POLEARMS = "장창류",
282 | FISTWEAPONS = "장착 무기류",
283 | RIDING = "탈것 타기",
284 | PLATEMAIL = "판금 갑옷",
285 | MAIL = "사슬",
286 | LEATHER = "가죽",
287 | CLOTH = "천",
288 | SHIELD = "방패",
289 | ARCHAEOLOGY = "고고학",
290 | },
291 | ["ptBR"] = {
292 | COOKING = "Culinária",
293 | FIRSTAID = "Primeiros socorros",
294 | FISHING = "Pesca",
295 | ALCHEMY = "Alquimia",
296 | BLACKSMITHING = "Ferraria",
297 | ENCHANTING = "Encantamento",
298 | ENGINEERING = "Engenharia",
299 | HERBALISM = "Herborismo",
300 | INSCRIPTION = "Escrivania",
301 | JEWELCRAFTING = "Joalheria",
302 | LEATHERWORKING = "Couraria",
303 | MINING = "Mineração",
304 | SKINNING = "Esfolamento",
305 | TAILORING = "Alfaiataria",
306 | SWORDS = "Espadas",
307 | AXES = "Machados",
308 | BOWS = "Arcos",
309 | GUNS = "Armas de Fogo",
310 | MACES = "Maças",
311 | TWOHANDEDSWORDS = "Espadas de Duas Mãos",
312 | DEFENSE = "Defesa",
313 | DUALWIELD = "Empunhar Duas Armas",
314 | STAVES = "Báculos",
315 | TWOHANDEDMACES = "Maças de Duas Mãos",
316 | UNARMED = "Combate desarmado",
317 | TWOHANDEDAXES = "Machados de Duas Mãos",
318 | DAGGERS = "Adagas",
319 | THROWN = "Arremesso",
320 | CROSSBOWS = "Bestas",
321 | WANDS = "Varinhas",
322 | POLEARMS = "Armas de Haste",
323 | FISTWEAPONS = "Armas de punho",
324 | RIDING = "Montaria",
325 | PLATEMAIL = "Armadura de Placa",
326 | MAIL = "Malha",
327 | LEATHER = "Couro",
328 | CLOTH = "Tecido",
329 | SHIELD = "Escudo",
330 | ARCHAEOLOGY = "Arqueologia",
331 | },
332 | ["ruRU"] = {
333 | COOKING = "Кулинария",
334 | FIRSTAID = "Первая помощь",
335 | FISHING = "Рыбная ловля",
336 | ALCHEMY = "Алхимия",
337 | BLACKSMITHING = "Кузнечное дело",
338 | ENCHANTING = "Наложение чар",
339 | ENGINEERING = "Инженерное дело",
340 | HERBALISM = "Травничество",
341 | INSCRIPTION = "Начертание",
342 | JEWELCRAFTING = "Ювелирное дело",
343 | LEATHERWORKING = "Кожевничество",
344 | MINING = "Горное дело",
345 | SKINNING = "Снятие шкур",
346 | TAILORING = "Портняжное дело",
347 | SWORDS = "Мечи",
348 | AXES = "Топоры",
349 | BOWS = "Луки",
350 | GUNS = "Ружья",
351 | MACES = "Дробящее оружие",
352 | TWOHANDEDSWORDS = "Двуручные мечи",
353 | DEFENSE = "Защита",
354 | DUALWIELD = "Бой двумя оружиями",
355 | STAVES = "Посохи",
356 | TWOHANDEDMACES = "Двуручное ударное оружие",
357 | UNARMED = "Рукопашный бой",
358 | TWOHANDEDAXES = "Двуручные топоры",
359 | DAGGERS = "Кинжалы",
360 | THROWN = "Метательное оружие",
361 | CROSSBOWS = "Арбалеты",
362 | WANDS = "Жезлы",
363 | POLEARMS = "Древковое оружие",
364 | FISTWEAPONS = "Кистевое оружие",
365 | RIDING = "Верховая езда",
366 | PLATEMAIL = "Латы",
367 | MAIL = "Кольчужные доспехи",
368 | LEATHER = "Кожа",
369 | CLOTH = "Ткань",
370 | SHIELD = "Щит",
371 | ARCHAEOLOGY = "Археология",
372 | },
373 | ["zhCN"] = {
374 | COOKING = "烹饪",
375 | FIRSTAID = "急救",
376 | FISHING = "钓鱼",
377 | ALCHEMY = "炼金术",
378 | BLACKSMITHING = "锻造",
379 | ENCHANTING = "附魔",
380 | ENGINEERING = "工程学",
381 | HERBALISM = "草药学",
382 | INSCRIPTION = "铭文",
383 | JEWELCRAFTING = "珠宝加工",
384 | LEATHERWORKING = "制皮",
385 | MINING = "采矿",
386 | SKINNING = "剥皮",
387 | TAILORING = "裁缝",
388 | SWORDS = "单手剑",
389 | AXES = "单手斧",
390 | BOWS = "弓",
391 | GUNS = "枪械",
392 | MACES = "单手锤",
393 | TWOHANDEDSWORDS = "双手剑",
394 | DEFENSE = "防御",
395 | DUALWIELD = "双武器",
396 | STAVES = "法杖",
397 | TWOHANDEDMACES = "双手锤",
398 | UNARMED = "徒手战斗",
399 | TWOHANDEDAXES = "双手斧",
400 | DAGGERS = "匕首",
401 | THROWN = "投掷武器",
402 | CROSSBOWS = "弩",
403 | WANDS = "魔杖",
404 | POLEARMS = "长柄武器",
405 | FISTWEAPONS = "拳套",
406 | RIDING = "骑术",
407 | PLATEMAIL = "板甲",
408 | MAIL = "锁甲",
409 | LEATHER = "皮甲",
410 | CLOTH = "布甲",
411 | SHIELD = "盾牌",
412 | ARCHAEOLOGY = "考古学",
413 | },
414 | ["zhTW"] = {
415 | COOKING = "烹飪",
416 | FIRSTAID = "急救",
417 | FISHING = "釣魚",
418 | ALCHEMY = "鍊金術",
419 | BLACKSMITHING = "鍛造",
420 | ENCHANTING = "附魔",
421 | ENGINEERING = "工程學",
422 | HERBALISM = "草藥學",
423 | INSCRIPTION = "銘文",
424 | JEWELCRAFTING = "珠寶設計",
425 | LEATHERWORKING = "製皮",
426 | MINING = "採礦",
427 | SKINNING = "剝皮",
428 | TAILORING = "裁縫",
429 | SWORDS = "劍",
430 | AXES = "斧",
431 | BOWS = "弓",
432 | GUNS = "槍械",
433 | MACES = "錘",
434 | TWOHANDEDSWORDS = "雙手劍",
435 | DEFENSE = "防禦",
436 | DUALWIELD = "雙武器",
437 | STAVES = "法杖",
438 | TWOHANDEDMACES = "雙手錘",
439 | UNARMED = "徒手戰鬥",
440 | TWOHANDEDAXES = "雙手斧",
441 | DAGGERS = "匕首",
442 | THROWN = "投擲武器",
443 | CROSSBOWS = "弩",
444 | WANDS = "魔杖",
445 | POLEARMS = "長柄武器",
446 | FISTWEAPONS = "拳套",
447 | RIDING = "騎術",
448 | PLATEMAIL = "鎧甲",
449 | MAIL = "鎖甲",
450 | LEATHER = "皮甲",
451 | CLOTH = "布甲",
452 | SHIELD = "盾牌",
453 | ARCHAEOLOGY = "考古学",
454 | },
455 | }
--------------------------------------------------------------------------------
/Guidelime/Data/Data.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | local HBD = LibStub("HereBeDragons-2.0")
5 |
6 | addon.SK = addon.SK or {}; local SK = addon.SK -- Data/SkillDB
7 | addon.SP = addon.SP or {}; local SP = addon.SP -- Data/SpellDB
8 |
9 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
10 |
11 | D.factions = {"Alliance", "Horde"}
12 | D.races = {Human = "Alliance", NightElf = "Alliance", Dwarf = "Alliance", Gnome = "Alliance", Orc = "Horde", Troll = "Horde", Tauren = "Horde", Undead = "Horde", Draenei = "Alliance", BloodElf = "Horde", Worgen = "Alliance", Goblin = "Horde", Pandaren = "Neutral"}
13 | D.raceIDs = {Human = 1, NightElf = 4, Dwarf = 3, Gnome = 7, Orc = 2, Troll = 8, Tauren = 6, Undead = 5, BloodElf = 10, Draenei = 11, Worgen = 22, Goblin = 9, Pandaren = 24}
14 | D.classes = {"Warrior", "Rogue", "Mage", "Warlock", "Hunter", "Priest", "Druid", "Paladin", "Shaman", "DeathKnight", "Monk"}
15 | D.classesWithFaction = {}
16 | D.classesPerRace = {
17 | Human = {"Warrior", "Paladin", "Hunter", "Rogue", "Priest", "Mage", "Warlock", "DeathKnight", "Monk"},
18 | NightElf = {"Warrior", "Hunter", "Rogue", "Priest", "Mage", "Druid", "DeathKnight", "Monk"},
19 | Dwarf = {"Warrior", "Paladin", "Hunter", "Rogue", "Priest", "Shaman", "Mage", "Warlock", "DeathKnight", "Monk"},
20 | Gnome = {"Warrior", "Rogue", "Priest", "Mage", "Warlock", "DeathKnight", "Monk"},
21 | Orc = {"Warrior", "Hunter", "Rogue", "Shaman", "Mage", "Warlock", "DeathKnight", "Monk"},
22 | Troll = {"Warrior", "Hunter", "Rogue", "Priest", "Shaman", "Mage", "Warlock", "Druid", "DeathKnight", "Monk"},
23 | Tauren = {"Warrior", "Paladin", "Hunter", "Priest", "Shaman", "Druid", "DeathKnight", "Monk"},
24 | Undead = {"Warrior", "Hunter", "Rogue", "Priest", "Mage", "Warlock", "DeathKnight", "Monk"},
25 | Draenei = {"Hunter", "Mage", "Paladin", "Priest", "Shaman", "Warrior", "DeathKnight", "Monk"},
26 | BloodElf = {"Warrior", "Hunter", "Mage", "Paladin", "Priest", "Rogue", "Warlock", "DeathKnight", "Monk"},
27 | Worgen = {"Warrior", "Hunter", "Rogue", "Priest", "Mage", "Warlock", "Druid", "DeathKnight"},
28 | Goblin = {"Warrior", "Hunter", "Rogue", "Priest", "Shaman", "Mage", "Warlock", "DeathKnight"},
29 | Pandaren = {"Hunter", "Mage", "Monk", "Priest", "Rogue", "Shaman", "Warrior"}
30 | }
31 | function D.getClass(class)
32 | class = class:upper():gsub(" ","")
33 | for i, c in ipairs(D.classes) do
34 | if c:upper() == class then return c end
35 | end
36 | end
37 | function D.getRace(race)
38 | race = race:upper():gsub(" ","")
39 | if race == "SCOURGE" then return "Undead" end
40 | for r, f in pairs(D.races) do
41 | if r:upper() == race then return r end
42 | end
43 | end
44 | function D.getSex(sex)
45 | if type(sex) == "number" then
46 | return sex == 2 and "Male" or "Female"
47 | else
48 | return sex:upper() == "MALE" and "Male" or "Female"
49 | end
50 | end
51 |
52 | D.class = D.getClass(select(2, UnitClass("player")))
53 | D.race = D.getRace(select(2, UnitRace("player")))
54 | D.sex = D.getSex(UnitSex("player"))
55 | D.faction = UnitFactionGroup("player")
56 | D.level = UnitLevel("player")
57 | D.xp = UnitXP("player")
58 | D.xpMax = UnitXPMax("player")
59 | D.wx, D.wy, D.instance = HBD:GetPlayerWorldPosition()
60 | D.face = GetPlayerFacing()
61 |
62 | D.reputations = {
63 | bootybay = 21,
64 | ironforge = 47,
65 | gnomeregan = 54,
66 | thoriumbrotherhood = 59,
67 | undercity = 68,
68 | darnassus = 69,
69 | syndicate = 70,
70 | stormwind = 72,
71 | orgrimmar = 76,
72 | thunderbluff = 81,
73 | bloodsailbuccaneers = 87,
74 | gelkisclancentaur = 92,
75 | magramclancentaur = 93,
76 | zandalartribe = 270,
77 | ravenholdt = 349,
78 | gadgetzan = 369,
79 | ratchet = 470,
80 | wildhammerclan = 471,
81 | leagueofarathor = 509,
82 | defilers = 510,
83 | argentdawn = 529,
84 | darkspeartrolls = 530,
85 | timbermawhold = 576,
86 | everlook = 577,
87 | wintersabertrainers = 589,
88 | cenarioncircle = 609,
89 | frostwolfclan = 729,
90 | stormpikeguard = 730,
91 | hydraxianwaterlords = 749,
92 | shendralar = 809,
93 | warsongoutriders = 889,
94 | silverwingsentinels = 890,
95 | darkmoonfaire = 909,
96 | broodofnozdormu = 910,
97 | silvermooncity = 911,
98 | tranquillien = 922,
99 | exodar = 930,
100 | aldor = 932,
101 | consortium = 933,
102 | scryers = 934,
103 | shatar = 935,
104 | maghar = 941,
105 | cenarionexpedition = 942,
106 | honorhold = 946,
107 | thrallmar = 947,
108 | violeteye = 967,
109 | sporeggar = 970,
110 | kurenai = 978,
111 | keepersoftime = 989,
112 | scaleofthesands = 990,
113 | lowercity = 1011,
114 | ashtonguedeathsworn = 1012,
115 | netherwing = 1015,
116 | shatariskyguard = 1031,
117 | ogrila = 1038,
118 | shatteredsunoffensive = 1077,
119 |
120 | valianceexpedition = 1050,
121 | silvercovenant = 1094,
122 | explorersleague = 1068,
123 | frostborn = 1126,
124 | warsongoffensive = 1085,
125 | sunreavers = 1124,
126 | handofvengeance = 1067,
127 | taunka = 1064,
128 | oracles = 1105,
129 | frenzyhearttribe = 1104,
130 |
131 | guild = 1169,
132 | gilneas = 1134,
133 | earthenring = 1135,
134 | guardiansofhyjal = 1158,
135 | therazane = 1171,
136 | dragonmawclan = 1172,
137 | ramkahen = 1173,
138 | wildhammerclan = 1174,
139 | baradinswardens = 1177,
140 | hellscreamsreach = 1178,
141 | avengersofhyjal = 1204,
142 | bilgewatercartel = 1133,
143 |
144 | goldenlotus = 1269,
145 | orderofthecloudserpent = 1271,
146 | anglers = 1302,
147 | natpagle = 1358,
148 | tillers = 1272,
149 | augustcelestials = 1341,
150 | shadopan = 1270,
151 | klaxxi = 1337,
152 | shadopanassault = 1435,
153 | lorewalkers = 1345,
154 | blackprince = 1359,
155 | sunreaveronslaught = 1388,
156 | pearlfinjinyu = 1242,
157 | kirintoroffensive = 1387,
158 | operationshieldwall = 1376,
159 | dominanceoffensive = 1375,
160 | tinamudclaw = 1280,
161 | emperorshaohao = 1492,
162 | ella = 1275,
163 | foresthozen = 1228,
164 | shangxisacademy = 1216,
165 | ginamudclaw = 1281,
166 | sho = 1278,
167 | fishfellreed = 1282,
168 | brewmasters = 1351,
169 | cheechee = 1277,
170 | haohanmudclaw = 1279,
171 | akamastrust = 1416,
172 | darkspearrebellion = 1440,
173 | joguthedrunk = 1273,
174 | farmerfung = 1283,
175 | oldhillpaw = 1276
176 | }
177 |
178 | D.racesPerFaction = {}
179 | for race, faction in pairs(D.races) do
180 | if D.racesPerFaction[faction] == nil then D.racesPerFaction[faction] = {} end
181 | table.insert(D.racesPerFaction[faction], race)
182 | end
183 |
184 | D.classesPerFaction = {}
185 | for i, class in ipairs(D.classes) do
186 | for i, faction in ipairs(D.factions) do
187 | if D.classesWithFaction[class] or faction == faction then
188 | if D.classesPerFaction[faction] == nil then D.classesPerFaction[faction] = {} end
189 | table.insert(D.classesPerFaction[faction], class)
190 | end
191 | end
192 | end
193 |
194 | function D.isClass(class)
195 | return D.getClass(class) ~= nil
196 | end
197 | function D.isRace(race)
198 | return D.getRace(race) ~= nil
199 | end
200 | function D.getFaction(faction)
201 | faction = faction:upper()
202 | for i, f in ipairs(D.factions) do
203 | if f:upper() == faction then return f end
204 | end
205 | end
206 | function D.isFaction(faction)
207 | return D.getFaction(faction) ~= nil
208 | end
209 | function D.getLocalizedRace(race)
210 | if C_CreatureInfo == nil then return race end
211 | local raceInfo = C_CreatureInfo.GetRaceInfo(D.raceIDs[race])
212 | return raceInfo and raceInfo.raceName or race
213 | end
214 | function D.getLocalizedClass(class)
215 | return LOCALIZED_CLASS_NAMES_MALE[class:upper()] or class
216 | end
217 |
218 | function D.getReputation(rep)
219 | return D.reputations[string.gsub(string.gsub(string.lower(rep), "[' ]", ""), "^the", "")]
220 | end
221 | function D.isReputation(rep)
222 | return D.getReputation(rep) ~= nil
223 | end
224 | function D.getLocalizedReputation(id)
225 | local name = GetFactionInfoByID(id)
226 | return name
227 | end
228 | function D.isRequiredReputation(id, repMin, repMax)
229 | local _, _, standing, _, _, value = GetFactionInfoByID(id)
230 | if repMin ~= nil and value < repMin then return false end
231 | if repMax ~= nil and value >= repMax then return false end
232 | return true
233 | end
234 |
235 | function D.contains(array, value)
236 | if not array then return end
237 | for i, v in ipairs(array) do
238 | if type(value) == "function" then
239 | if value(v) then return true end
240 | else
241 | if v == value then return true end
242 | end
243 | end
244 | return false
245 | end
246 |
247 | function D.find(array, func)
248 | if not array then return end
249 | for i, v in ipairs(array) do
250 | if func(v) then return v end
251 | end
252 | end
253 |
254 | function D.containsIgnoreCase(array, value)
255 | return D.contains(array, function(v) return v:upper() == value:upper() end)
256 | end
257 |
258 | function D.containsKey(table, value)
259 | for k, v in pairs(table) do
260 | if type(value) == "function" then
261 | if value(k) then return true end
262 | else
263 | if k == value then return true end
264 | end
265 | end
266 | return false
267 | end
268 |
269 | function D.applies(guide)
270 | if guide == nil then return false end
271 | if guide.races ~= nil then
272 | if not D.contains(guide.races, D.race) then return false end
273 | end
274 | if guide.classes ~= nil then
275 | if not D.contains(guide.classes, D.class) then return false end
276 | end
277 | if guide.faction ~= nil and guide.faction ~= D.faction then return false end
278 | return true
279 | end
280 |
281 | function D.hasRequirements(guide)
282 | if guide == nil then return true end
283 | if guide.reputation and not D.isRequiredReputation(guide.reputation, guide.repMin, guide.repMax) then return false end
284 | if guide.skillReq and not SK.isRequiredSkill(guide.skillReq, guide.skillMin, guide.skillMax) then return false end
285 | if guide.spellReq and not SP.isRequiredSpell(guide.spellReq, guide.spellMin, guide.spellMax) then return false end
286 | if guide.itemReq and not D.hasItem(guide.itemReq, guide.itemMin, guide.itemMax) then return false end
287 | return true
288 | end
289 |
290 | function D.isAlive()
291 | return not UnitIsDeadOrGhost("player")
292 | --return HBD:GetPlayerZone() == nil or C_DeathInfo.GetCorpseMapPosition(HBD:GetPlayerZone()) == nil
293 | end
294 |
295 | function D.bit(p)
296 | return 2 ^ (p - 1) -- 1-based indexing
297 | end
298 |
299 | -- Typical call: if hasbit(x, bit(3)) then ...
300 | function D.hasbit(x, p)
301 | return x % (p + p) >= p
302 | end
303 |
304 | function D.hasItem(itemReq, itemMin, itemMax)
305 | -- note that itemMin is not "minimum number of items", but "greater than" (not "greater or equal"), analog itemMax
306 | -- as pattern match for element.t == "APPLIES" in GuideParser.lua does not allow for <= or >=
307 |
308 | local itemcnt = GetItemCount(tonumber(itemReq))
309 |
310 | if itemMin ~= nil then return (itemcnt > itemMin) end
311 | if itemMax ~= nil then return (itemcnt < itemMax) end
312 |
313 | return nil
314 | end
315 |
316 | D.RACE_ICON_TCOORDS = {
317 | ["Human"] = {
318 | ["Male"] = {0, 0.125, 0, 0.25},
319 | ["Female"] = {0, 0.125, 0.5, 0.75},
320 | },
321 | ["Dwarf"] = {
322 | ["Male"] = {0.125, 0.25, 0, 0.25},
323 | ["Female"] = {0.125, 0.25, 0.5, 0.75},
324 | },
325 | ["Gnome"] = {
326 | ["Male"] = {0.25, 0.375, 0, 0.25},
327 | ["Female"] = {0.25, 0.375, 0.5, 0.75},
328 | },
329 | ["NightElf"] = {
330 | ["Male"] = {0.375, 0.5, 0, 0.25},
331 | ["Female"] = {0.375, 0.5, 0.5, 0.75},
332 | },
333 | ["Tauren"] = {
334 | ["Male"] = {0, 0.125, 0.25, 0.5},
335 | ["Female"] = {0, 0.125, 0.75, 1.0},
336 | },
337 | ["Undead"] = {
338 | ["Male"] = {0.125, 0.25, 0.25, 0.5},
339 | ["Female"] = {0.125, 0.25, 0.75, 1.0},
340 | },
341 | ["Troll"] = {
342 | ["Male"] = {0.25, 0.375, 0.25, 0.5},
343 | ["Female"] = {0.25, 0.375, 0.75, 1.0},
344 | },
345 | ["Orc"] = {
346 | ["Male"] = {0.375, 0.5, 0.25, 0.5},
347 | ["Female"] = {0.375, 0.5, 0.75, 1.0},
348 | },
349 | ["BloodElf"] = {
350 | ["Male"] = {0.5, 0.625, 0.25, 0.5},
351 | ["Female"] = {0.5, 0.625, 0.75, 1.0},
352 | },
353 | ["Draenei"] = {
354 | ["Male"] = {0.5, 0.625, 0, 0.25},
355 | ["Female"] = {0.5, 0.625, 0.5, 0.75},
356 | },
357 | ["Worgen"] = {
358 | ["Male"] = {0.625, 0.75, 0, 0.25},
359 | ["Female"] = {0.625, 0.75, 0.5, 0.75},
360 | },
361 | ["Goblin"] = {
362 | ["Male"] = {0.625, 0.75, 0.25, 0.5},
363 | ["Female"] = {0.625, 0.75, 0.75, 1.0},
364 | },
365 | ["Pandaren"] = {
366 | ["Male"] = {0.75, 0.875, 0, 0.25},
367 | ["Female"] = {0.75, 0.875, 0.5, 0.75},
368 | },
369 | };
370 |
371 | function D.getRaceIconText(race, sex, size)
372 | local coords = D.RACE_ICON_TCOORDS[D.getRace(race)][D.getSex(sex)]
373 | return "|TInterface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-RACES:" .. (size or 12) .. ":" .. (size or 12) .. ":0:0:128:128:" ..
374 | coords[1] * 128 .. ":" .. coords[2] * 128 .. ":" .. coords[3] * 128 .. ":" .. coords[4] * 128 .. ":::|t"
375 | end
376 |
377 | function D.getClassIconText(class, size)
378 | -- cf https://wowpedia.fandom.com/wiki/Class_icon; alternative icon suggestions there as well
379 | local coords = CLASS_ICON_TCOORDS[D.getClass(class):upper()]
380 | return "|TInterface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES:" .. (size or 12) .. ":" .. (size or 12) .. ":0:0:128:128:" ..
381 | coords[1] * 128 .. ":" .. coords[2] * 128 .. ":" .. coords[3] * 128 .. ":" .. coords[4] * 128 .. ":::|t"
382 | end
383 |
--------------------------------------------------------------------------------
/Guidelime/Data/PositionTools.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | local HBD = LibStub("HereBeDragons-2.0")
5 |
6 | addon.DM = addon.DM or {}; local DM = addon.DM -- Data/MapDB
7 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
8 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
9 | addon.GP = addon.GP or {}; local GP = addon.GP -- GuideParser
10 |
11 | addon.PT = addon.PT or {}; local PT = addon.PT -- Data/PositionTools
12 |
13 | local LIMIT_CENTER_POSITION = 400
14 | local LIMIT_POSITIONS = 1000
15 |
16 | local CLUSTER_DIST = 170
17 |
18 | local function findCluster(clusters, wx, wy, instance)
19 | local bestCluster, bestDist
20 | if clusters[instance] ~= nil then
21 | for i, cluster in ipairs(clusters[instance]) do
22 | local dist = (wx - cluster.wx) * (wx - cluster.wx) + (wy - cluster.wy) * (wy - cluster.wy)
23 | if dist < CLUSTER_DIST * CLUSTER_DIST and (bestDist == nil or bestDist > dist) then
24 | bestCluster = cluster
25 | bestDist = dist
26 | end
27 | end
28 | else
29 | clusters[instance] = {}
30 | end
31 | if bestCluster == nil then
32 | bestCluster = {wx = 0, wy = 0, count = 0, radius = 0, instance = instance}
33 | bestDist = 0
34 | table.insert(clusters[instance], bestCluster)
35 | end
36 | return bestCluster, bestDist
37 | end
38 |
39 | local function addToCluster(wx, wy, cluster, dist)
40 | cluster.wx = (cluster.wx * cluster.count + wx) / (cluster.count + 1)
41 | cluster.wy = (cluster.wy * cluster.count + wy) / (cluster.count + 1)
42 | -- this is an approximation only
43 | if dist ~= nil and cluster.radius < dist then cluster.radius = dist end
44 | cluster.count = cluster.count + 1
45 | --if addon.debugging then print("LIME: adding to cluster ", cluster.count, cluster.wx, cluster.wy) end
46 | end
47 |
48 | -- approximation in order to find a position equally far away from previous clusters
49 | local function selectFurthestPosition(positions, clusters)
50 | local maxPos, maxDist
51 | for _, pos in ipairs(positions) do
52 | if pos.wx and not pos.selected then
53 | if clusters[pos.instance] == nil then return pos end
54 | local dist = 0
55 | for _, cluster in ipairs(clusters[pos.instance]) do
56 | if cluster.wx == pos.wx and cluster.wy == pos.wy then
57 | dist = nil
58 | elseif dist ~= nil then
59 | dist = dist + 1 / ((cluster.wx - pos.wx) * (cluster.wx - pos.wx) + (cluster.wy - pos.wy) * (cluster.wy - pos.wy))
60 | end
61 | end
62 | if maxDist == nil or (dist ~= nil and dist < maxDist) then
63 | maxPos, maxDist = pos, dist
64 | end
65 | end
66 | end
67 | --if addon.debugging then print("LIME: furthest point is #", maxPos.wx, maxPos.wy, maxPos.mapid, maxDist) end
68 | return maxPos
69 | end
70 |
71 | local function convertClusterCoordinates(clusters)
72 | for instance, list in pairs(clusters) do
73 | for i = 1, #list do
74 | local cluster = list[i]
75 | cluster.x, cluster.y, cluster.zone = PT.GetZoneCoordinatesFromWorld(cluster.wx, cluster.wy, cluster.instance)
76 | if not cluster.x then
77 | if addon.debugging then print("LIME: error transforming (" .. cluster.wx .. "," .. cluster.wy .. "," .. cluster.instance .. ") into zone coordinates") end
78 | table.remove(list, i)
79 | end
80 | end
81 | end
82 | end
83 |
84 | local function selectBestCluster(clusters, currentPos)
85 | local maxCluster
86 | local count = 0
87 | for instance, list in pairs(clusters) do
88 | count = count + #list
89 | for _, cluster in ipairs(list) do
90 | if currentPos and currentPos.instance == cluster.instance then
91 | -- if current position is given weight cluster according to their distance:
92 | -- weight of cluster in 1000yd is its count; weight of cluster in 500yd is twice its count
93 | cluster.distance = math.sqrt((currentPos.wx - cluster.wx) * (currentPos.wx - cluster.wx) + (currentPos.wy - cluster.wy) * (currentPos.wy - cluster.wy))
94 | if cluster.distance < 1 then cluster.distance = 1 end
95 | cluster.weight = cluster.count * 1000 / cluster.distance
96 | if maxCluster == nil or maxCluster.instance ~= currentPos.instance or cluster.weight > maxCluster.weight then maxCluster = cluster end
97 | else
98 | if maxCluster == nil or (not currentPos and cluster.count > maxCluster.count) then maxCluster = cluster end
99 | end
100 | end
101 | end
102 | --if addon.debugging and count > 1 then print("LIME: biggest cluster out of", count, "count", maxCluster.count, "at", maxCluster.wx, maxCluster.wy, maxCluster.instance) end
103 | --if addon.debugging and count > 1 and currentPos then print("LIME: distance", maxCluster.distance, "from", currentPos.wx, currentPos.wy, "weight", maxCluster.weight) end
104 | return maxCluster
105 | end
106 |
107 | local function calculateClusters(positions)
108 | local clusters = {}
109 | --local time
110 | --if addon.debugging then time = debugprofilestop() end
111 | for i = 1, #positions do
112 | local pos = selectFurthestPosition(positions, clusters)
113 | --if addon.debugging then print("LIME: found position", pos.wx, pos.wy, pos.instance) end
114 | pos.selected = true
115 | local cluster, dist = findCluster(clusters, pos.wx, pos.wy, pos.instance)
116 | addToCluster(pos.wx, pos.wy, cluster, dist)
117 | end
118 | convertClusterCoordinates(clusters)
119 | return clusters
120 | end
121 |
122 | function PT.getQuestPosition(id, typ, index, currentPos)
123 | if index == nil then index = 0 end
124 | if type(index) == "number" then index = {index} end
125 | if PT.questPosition == nil then PT.questPosition = {} end
126 | if PT.questPosition[id] == nil then PT.questPosition[id] = {} end
127 | if PT.questPosition[id][typ] == nil then PT.questPosition[id][typ] = {} end
128 | local pos = PT.questPosition[id][typ][table.concat(index,",")]
129 | if pos and not pos.estimate then return pos end
130 | local clusters = pos and pos.clusters
131 | local estimate = true
132 | if not clusters then
133 | local filterZone = QT.getQuestSort(id)
134 | if filterZone ~= nil and DM.mapIDs[filterZone] == nil then filterZone = nil end
135 | local positions = QT.getQuestPositions(id, typ, index, filterZone)
136 | if positions ~= nil and #positions == 0 and filterZone ~= nil then
137 | positions = QT.getQuestPositions(id, typ, index)
138 | end
139 | if positions == nil or #positions > LIMIT_CENTER_POSITION then return end
140 | clusters = calculateClusters(positions)
141 | estimate = #positions > 1
142 | end
143 | local maxCluster = selectBestCluster(clusters, currentPos)
144 | --if addon.debugging then print("LIME: findCluster " .. #positions .. " positions " .. math.floor(debugprofilestop() - time) .. " ms"); time = debugprofilestop() end
145 | if maxCluster ~= nil then
146 | --if addon.debugging then print("LIME: getQuestPosition " .. math.floor(debugprofilestop() - time) .. " ms"); time = debugprofilestop() end
147 | pos = {x = math.floor(maxCluster.x * 10000) / 100, y = math.floor(maxCluster.y * 10000) / 100,
148 | wx = maxCluster.wx, wy = maxCluster.wy, instance = maxCluster.instance,
149 | zone = maxCluster.zone, mapID = DM.mapIDs[maxCluster.zone],
150 | radius = math.floor(math.sqrt(maxCluster.radius)) + CG.DEFAULT_GOTO_RADIUS, estimate = estimate,
151 | clusters = clusters}
152 | PT.questPosition[id][typ][table.concat(index,",")] = pos
153 | return pos
154 | end
155 | end
156 |
157 | function PT.getQuestPositionsLimited(id, typ, index, maxNumber, onlyWorld)
158 | local clusters = {}
159 | local filterZone = GP.getSuperCode(typ) == "QUEST" and QT.getQuestZone(id)
160 | local positions = QT.getQuestPositions(id, typ, index, filterZone)
161 | if positions == nil then return end
162 | if #positions == 0 and filterZone ~= nil then
163 | positions = QT.getQuestPositions(id, typ, index)
164 | if positions == nil then return end
165 | end
166 | return PT.getPositionsLimited(positions, maxNumber, onlyWorld)
167 | end
168 |
169 | function PT.getPositionsLimited(positions, maxNumber, onlyWorld)
170 | local clusters = {}
171 | if maxNumber > 0 and #positions > maxNumber then
172 | local positions2 = {}
173 | local wy, wx, _, instance = UnitPosition("player")
174 | -- fill part with the nearest positions
175 | local closestCount = math.ceil(maxNumber / 5)
176 | local minDist = {}
177 | for _, pos in ipairs(positions) do
178 | if pos.instance == instance then
179 | local dist = (pos.wx - wx) * (pos.wx - wx) + (pos.wy - wy) * (pos.wy - wy)
180 | for i = 1, closestCount do
181 | if minDist[i] == nil or minDist[i] > dist then
182 | table.insert(minDist, i, dist)
183 | table.insert(positions2, i, pos)
184 | break
185 | end
186 | end
187 | end
188 | end
189 | for i = closestCount + 1, #positions2 do
190 | positions2[i] = nil
191 | end
192 | -- fill up with positions spread out
193 | for i = #positions2 + 1, maxNumber do
194 | local pos = selectFurthestPosition(positions, clusters)
195 | pos.selected = true
196 | if clusters[pos.instance] == nil then clusters[pos.instance] = {} end
197 | table.insert(clusters[pos.instance], {wx = pos.wx, wy = pos.wy, count = 1, instance = pos.instance})
198 | table.insert(positions2, pos)
199 | end
200 | if addon.debugging then print("LIME: limited " .. #positions .. " positions to " .. #positions2 .. " positions. x = " .. wx .. " y = " .. wy) end
201 | positions = positions2
202 | end
203 | if onlyWorld then return positions end
204 | local result = {}
205 | for _, pos in ipairs(positions) do
206 | local x, y, zone = PT.GetZoneCoordinatesFromWorld(pos.wx, pos.wy, pos.instance)
207 | if x ~= nil then
208 | pos.x = math.floor(x * 10000) / 100
209 | pos.y = math.floor(y * 10000) / 100
210 | pos.zone = zone
211 | pos.mapID = DM.mapIDs[zone]
212 | table.insert(result, pos)
213 | elseif addon.debugging then
214 | print("LIME: error transforming (" .. maxCluster.wx .. "," .. maxCluster.wy .. "," .. maxCluster.instance .. ") into zone coordinates")
215 | end
216 | end
217 | return result
218 | end
219 |
220 | function PT.GetZoneCoordinatesFromWorld(worldX, worldY, instance, zone)
221 | if zone ~= nil then
222 | local x, y = HBD:GetZoneCoordinatesFromWorld(worldX, worldY, DM.mapIDs[zone], true)
223 | if x ~= nil and x > 0 and x < 1 and y ~= nil and y > 0 and y < 1 then
224 | local _, _, checkInstance = HBD:GetWorldCoordinatesFromZone(x, y, DM.mapIDs[zone])
225 | if checkInstance == instance then
226 | -- hack for some bfa zone names
227 | do
228 | local e = zone:find("[@!]")
229 | if e ~= nil then zone = zone:sub(1, e - 1) end
230 | end
231 | return x, y, zone
232 | end
233 | end
234 | else
235 | for zone, _ in pairs(DM.mapIDs) do
236 | local x, y, z = PT.GetZoneCoordinatesFromWorld(worldX, worldY, instance, zone)
237 | if x ~= nil then return x, y, z end
238 | end
239 | end
240 | end
241 |
242 | --[[function PT.getNPCPosition(id)
243 | if id == nil then return end
244 | if addon.dataSource == "QUESTIE" then return QUESTIE.getNPCPosition(id) end
245 | if addon.dataSource == "CLASSIC_CODEX" then return CLASSIC_CODEX.getNPCPosition(id) end
246 | if DB.creaturesDB[id] == nil or DB.creaturesDB[id].positions == nil then return end
247 | local p = DB.creaturesDB[id].positions[1]
248 | local x, y, z = QT.GetZoneCoordinatesFromWorld(p.y, p.x, p.mapid)
249 | return {instance = p.mapid, wx = p.y, wy = p.x, mapID = DM.mapIDs[z], x = x, y = y}
250 | end]]
251 |
252 | function PT.getNPCPosition(id, currentPos)
253 | if PT.npcPosition == nil then PT.npcPosition = {} end
254 | local pos = PT.npcPosition[id]
255 | if pos and not pos.estimate then return pos end
256 | local clusters = pos and pos.clusters
257 | local estimate = true
258 | if not clusters then
259 | local positions = QT.getNPCPositions(id)
260 | if positions == nil or #positions > LIMIT_CENTER_POSITION then return end
261 | clusters = calculateClusters(positions)
262 | estimate = #positions > 1
263 | end
264 | local maxCluster = selectBestCluster(clusters, currentPos)
265 | if maxCluster ~= nil then
266 | pos = {x = math.floor(maxCluster.x * 10000) / 100, y = math.floor(maxCluster.y * 10000) / 100,
267 | wx = maxCluster.wx, wy = maxCluster.wy, instance = maxCluster.instance,
268 | zone = maxCluster.zone, mapID = DM.mapIDs[maxCluster.zone],
269 | radius = math.floor(math.sqrt(maxCluster.radius)) + CG.DEFAULT_GOTO_RADIUS, estimate = estimate,
270 | clusters = clusters}
271 | PT.npcPosition[id] = pos
272 | return pos
273 | end
274 | end
275 |
276 | function PT.getNPCPositionsLimited(id, maxNumber, onlyWorld)
277 | local positions = QT.getNPCPositions(id)
278 | if positions == nil then return end
279 | return PT.getPositionsLimited(positions, maxNumber, onlyWorld)
280 | end
281 |
282 | function PT.getItemPosition(id, currentPos)
283 | if PT.itemPosition == nil then PT.itemPosition = {} end
284 | local pos = PT.itemPosition[id]
285 | if pos and not pos.estimate then return pos end
286 | local clusters = pos and pos.clusters
287 | local estimate = true
288 | if not clusters then
289 | local positions = QT.getItemPositions(id)
290 | if positions == nil or #positions > LIMIT_CENTER_POSITION then return end
291 | clusters = calculateClusters(positions)
292 | estimate = #positions > 1
293 | end
294 | local maxCluster = selectBestCluster(clusters, currentPos)
295 | if maxCluster ~= nil then
296 | pos = {x = math.floor(maxCluster.x * 10000) / 100, y = math.floor(maxCluster.y * 10000) / 100,
297 | wx = maxCluster.wx, wy = maxCluster.wy, instance = maxCluster.instance,
298 | zone = maxCluster.zone, mapID = DM.mapIDs[maxCluster.zone],
299 | radius = math.floor(math.sqrt(maxCluster.radius)) + CG.DEFAULT_GOTO_RADIUS, estimate = estimate,
300 | clusters = clusters}
301 | PT.itemPosition[id] = pos
302 | return pos
303 | end
304 | end
305 |
306 | function PT.getItemPositionsLimited(id, maxNumber, onlyWorld)
307 | local positions = QT.getItemPositions(id)
308 | if positions == nil then return end
309 | return PT.getPositionsLimited(positions, maxNumber, onlyWorld)
310 | end
311 |
312 |
313 |
314 |
315 |
316 |
--------------------------------------------------------------------------------
/Guidelime/Import.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
5 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
6 |
7 | addon.I = addon.I or {}; local I = addon.I -- Import
8 |
9 | local function findQuestType(line, pos)
10 | return QT.findInLists(line, {[L.WORD_LIST_ACCEPT] = "A", [L.WORD_LIST_COMPLETE] = "C", [L.WORD_LIST_TURN_IN] = "T", [L.WORD_LIST_SKIP] = "S"}, false, 1, pos)
11 | end
12 |
13 | local function parseLine(l, line, questids, previds, questname, activeQuests, turnedInQuests, faction, zone, newQuestIds)
14 | if newQuestIds ~= nil then print(l, table.concat(newQuestIds,",")) end
15 | local pos, err
16 | local count = 0
17 | -- find quest
18 | while true do
19 | local q, typ, objective, s, e, part, pre, post, lastTwo, skip
20 | local wordListMap = {}
21 | wordListMap["%[q([acts])%s*([%d/%?]+).-%]"] = function(...) s, e, typ, q = ...; typ = typ:upper(); skip = true end
22 | wordListMap["%[[^q].-%]"] = function(...) s, e = ...; skip = true end
23 | local questPattern = "\"([^\"]+)\""
24 | wordListMap[" " .. questPattern .. " "] = function(...) s, e, q = ... end
25 | wordListMap[" " .. questPattern .. L.WORD_LIST_PART_N:gsub(";", "; " .. questPattern)] = function(...) s, e, q, part = ...; part = tonumber(part) end
26 | wordListMap[L.WORD_LIST_PART_N] = function(...) s, e, part = ...; q = questname; part = tonumber(part) end
27 | local i = 1
28 | while L["WORD_LIST_PART_" .. i] ~= nil do
29 | local ii = i
30 | wordListMap[" " .. questPattern .. L["WORD_LIST_PART_" .. i]:gsub(";", "; " .. questPattern)] = function(...) s, e, q = ...; part = ii end
31 | wordListMap[L["WORD_LIST_PART_".. i]] = function(...) s, e = ...; q = questname; part = ii end
32 | i = i + 1
33 | end
34 | wordListMap[L.WORD_LIST_NEXT_PART] = function(...) s, e = ...; q = questname
35 | if questids ~= nil and #questids == 1 and QT.getQuestSeries(questids[1]) ~= nil then
36 | part = QT.getQuestSeries(questids[1]) + 1
37 | else
38 | part = 2
39 | end
40 | end
41 | if questids ~= nil then
42 | wordListMap[L.WORD_LIST_COMPLETE_LAST] = function(...) s, e, pre, post = ...; typ = "C" end
43 | wordListMap[L.WORD_LIST_TURN_IN_LAST] = function(...) s, e, pre, post = ...; typ = "T" end
44 | end
45 | if previds ~= nil then
46 | wordListMap[L.WORD_LIST_COMPLETE_LAST_TWO] = function(...) s, e, pre, post = ...; typ = "C"; lastTwo = true end
47 | wordListMap[L.WORD_LIST_TURN_IN_LAST_TWO] = function(...) s, e, pre, post = ...; typ = "T"; lastTwo = true end
48 | end
49 | for id, active in pairs(activeQuests) do
50 | for i, objList in ipairs(QT.getQuestObjectives(id)) do
51 | for j, object in ipairs(objList.names) do
52 | wordListMap[" " .. object:lower() .. "s? "] = function(...) s, e = ...; q = id; objective = i; typ = "C" end
53 | end
54 | end
55 | end
56 | QT.findInLists(line, wordListMap, true, pos)
57 | if skip then
58 | if q ~= nil then
59 | count = count + 1
60 | previds = questids
61 | questids = {}
62 | for id in q:gmatch("[^/]+") do
63 | if tonumber(id) ~= nil then
64 | table.insert(questids, tonumber(q))
65 | end
66 | end
67 | if #questids ~= 1 then err = "" end
68 | end
69 | pos = e
70 | else
71 | if s == nil then break end
72 | if pre ~= nil then s = s + #pre elseif s == 0 or line:sub(s, s):match("[%s%p]") then s = s + 1 end
73 | if post ~= nil then e = e - #post elseif e > #line or line:sub(e, e):match("[%s%p]") then e = e - 1 end
74 | count = count + 1
75 |
76 | if typ == nil then
77 | typ = findQuestType(line, s)
78 | if typ == nil then
79 | typ = "C"
80 | end
81 | end
82 |
83 | -- when quest to be completed is followed by a single digit interpret this is an objective #
84 | local title = line:sub(s,e):gsub("\"", "")
85 | if typ == "C" and objective == nil and tonumber(line:sub(e + 1, e + 1)) ~= nil then
86 | objective = tonumber(line:sub(e + 1, e + 1))
87 | e = e + 1
88 | end
89 |
90 | if type(q) == "number" then
91 | previds = questids
92 | questids = {q}
93 | elseif q ~= nil then
94 | previds = questids
95 | questids = QT.getPossibleQuestIdsByName(q, part)
96 | end
97 | if #questids > 1 and faction ~= nil then
98 | -- found more than one? only search correct faction
99 | local ids2 = {}
100 | for i, id in ipairs(questids) do
101 | if (QT.getQuestFaction(id) or faction) == faction then
102 | table.insert(ids2, id)
103 | end
104 | end
105 | if #ids2 > 0 then questids = ids2 end
106 | end
107 | if #questids > 1 and zone ~= nil then
108 | -- found more than one? only search in given zone
109 | local ids2 = {}
110 | for i, id in ipairs(questids) do
111 | if QT.getQuestZone(id) == zone then
112 | table.insert(ids2, id)
113 | end
114 | end
115 | if #ids2 > 0 then questids = ids2 end
116 | end
117 | if typ == "A" and newQuestIds ~= nil then
118 | if newQuestIds[1] == nil then
119 | err = "non matching number of quest ids specified"
120 | elseif #questids == 0 or D.contains(questids, newQuestIds[1]) or (#questids == 1 and QT.getQuestNameById(questids[1]) == QT.getQuestNameById(newQuestIds[1])) then
121 | questids = {newQuestIds[1]}
122 | table.remove(newQuestIds, 1)
123 | else
124 | err = "non matching quest id " .. newQuestIds[1] .. " specified"
125 | end
126 | elseif typ ~= "A" and #questids > 1 then
127 | -- found more than one? only search in active quests
128 | local ids2 = {}
129 | for i, id in ipairs(questids) do
130 | if activeQuests[id] then
131 | table.insert(ids2, id)
132 | end
133 | end
134 | if #ids2 > 0 then questids = ids2 end
135 | elseif typ == "A" and #questids > 1 then
136 | -- found more than one? exclude turned in quests
137 | local ids2 = {}
138 | for i, id in ipairs(questids) do
139 | if not turnedInQuests[id] then
140 | table.insert(ids2, id)
141 | end
142 | end
143 | if #ids2 > 0 then questids = ids2 end
144 | end
145 | -- bad idea
146 | --[[
147 | if q ~= nil and #questids > 1 and part == nil then
148 | --more than 1 found and no part given? assume part 1
149 | local ids2 = QT.getPossibleQuestIdsByName(q, 1)
150 | if ids2 ~= nil and #ids2 > 0 then questids = ids2 end
151 | end]]
152 | if #questids ~= 1 then err = "" end
153 |
154 | if #questids ~= 0 then
155 | questname = QT.getQuestNameById(questids[1])
156 | if questname == nil then err = "quest " .. questids[1] .. " not found" end
157 | else
158 | questname = nil
159 | end
160 |
161 | local rest = line:sub(e + 1)
162 | line = line:sub(1, s - 1)
163 | if objective ~= nil then objective = "," .. objective else objective = "" end
164 |
165 | if lastTwo then
166 | local questid = "?"
167 | if (typ == "C" or typ == "T") and previds ~= nil and #previds > 0 then
168 | questid = table.concat(previds, "/")
169 | if #previds > 1 then err = "" end
170 | else
171 | err = true
172 | end
173 | line = line .. "[Q" .. typ .. questid .. " -]"
174 | end
175 |
176 | local questid
177 | if #questids > 0 then questid = table.concat(questids, "/") else questid = "?" end
178 | line = line .. "[Q" .. typ .. questid .. objective .. " " .. title .. "]"
179 |
180 | if questids ~= nil and #questids == 1 then
181 | if QT.getQuestRaces(questids[1]) ~= nil or QT.getQuestClasses(questids[1]) ~= nil then
182 | line = line .. "[A "
183 | local first = true
184 | if QT.getQuestRaces(questids[1]) ~= nil then
185 | for i, race in ipairs(QT.getQuestRaces(questids[1])) do
186 | if not first then line = line .. "," end
187 | line = line .. D.getRace(race)
188 | first = false
189 | end
190 | end
191 | if QT.getQuestClasses(questids[1]) ~= nil then
192 | for i, class in ipairs(QT.getQuestClasses(questids[1])) do
193 | if not first then line = line .. "," end
194 | line = line .. D.getClass(class)
195 | first = false
196 | end
197 | end
198 | line = line .. "]"
199 | end
200 | end
201 | pos = #line
202 | line = line .. rest
203 | end
204 | if typ == "A" or typ == "C" or typ == "T" then
205 | for _, id in ipairs(questids) do
206 | if typ == "A" then
207 | if turnedInQuests[id] then err = "accept quest " .. id .. " after turn in" end
208 | activeQuests[id] = true
209 | elseif typ == "T" then
210 | if #questids == 1 and turnedInQuests[id] then err = "quest " .. id .. " turned in twice" end
211 | if line:find("%[OC?%]") == nil then
212 | activeQuests[id] = nil
213 | if #questids == 1 then turnedInQuests[id] = true end
214 | end
215 | elseif typ == "C" then
216 | if turnedInQuests[id] then err = "complete quest " .. id .. " after turn in" end
217 | end
218 | end
219 | end
220 | end
221 |
222 | --found the word quest(/s) but no quest tags were created? ids have to be added manually unless completing/turning in only one/two remaining active quests
223 | if count == 0 then
224 | local s, e
225 | QT.findInLists(line, {
226 | [L.WORD_LIST_QUESTS] = function(...) s, e = ...; count = 2 end,
227 | [L.WORD_LIST_QUEST] = function(...) s, e = ...; count = 1 end
228 | })
229 | if s ~= nil then
230 | s = s + 1
231 | e = e - 1
232 | local typ = findQuestType(line:sub(1, s - 1))
233 | if typ ~= nil then
234 | local rest = line:sub(e + 1)
235 | local title = line:sub(s,e)
236 | line = line:sub(1, s - 1)
237 | local id1, id2 = "?", "?"
238 | local activeIds = {}
239 | for id, _ in pairs(activeQuests) do table.insert(activeIds, id) end
240 | if (typ == "C" or typ == "T") and #activeIds == count then
241 | id1 = activeIds[1]
242 | id2 = activeIds[2]
243 | if typ == "T" then
244 | activeQuests[id1] = nil; turnedInQuests[id1] = true
245 | if id2 ~= nil then activeQuests[id2] = nil; turnedInQuests[id2] = true end
246 | end
247 | else
248 | err = ""
249 | end
250 | if count == 2 then
251 | line = line .. "[Q" .. typ .. id2 .. " -]"
252 | end
253 | line = line .. "[Q" .. typ .. id1 .. " " .. title .. "]"
254 | line = line .. rest
255 | else
256 | count = 0
257 | end
258 | end
259 | end
260 |
261 | if err == nil and newQuestIds ~= nil and #newQuestIds > 0 then
262 | err = "non matching number of quest ids specified for line " .. l
263 | end
264 |
265 | while QT.findInLists(line, {
266 | [L.WORD_LIST_GOTO] = function(s, e, pre, x, y, post)
267 | if pre ~= nil then s = s + #pre elseif s == 0 or line:sub(s, s):match("[%s%p]") then s = s + 1 end
268 | if post ~= nil then e = e - #post elseif e > #line or line:sub(e, e):match("[%s%p]") then e = e - 1 end
269 | line = line:sub(1, s - 1) .. "[G" .. x .. "," .. y .. (zone or "") .. "]" .. line:sub(e + 1)
270 | return true
271 | end})
272 | do end
273 |
274 | -- each of these tags can appear once per line
275 | for _, lists in ipairs({
276 | {[L.WORD_LIST_XP] = "XP"},
277 | {[L.WORD_LIST_SET_HEARTH] = "S", [L.WORD_LIST_HEARTH] = "H"},
278 | {[L.WORD_LIST_FLY] = "F"},
279 | {[L.WORD_LIST_GET_FLIGHT_POINT] = "P"},
280 | {[L.WORD_LIST_OPTIONAL_COMPLETE_WITH_NEXT] = "OC"}
281 | }) do
282 | local found = false
283 | for list, code in pairs(lists) do
284 | if line:find("%[" .. code) ~= nil then found = true; break end
285 | end
286 | if not found then
287 | local code, s, e, pre, post = QT.findInLists(line, lists)
288 | if code ~= nil and line:find("%[".. code) == nil and (count == 0 or code ~= "OC") then
289 | if pre ~= nil and #pre <= e - s then s = s + #pre elseif s == 0 or line:sub(s, s):match("[%s%p]") then s = s + 1 end
290 | if post ~= nil then e = e - #post elseif e > #line or line:sub(e, e):match("[%s%p]") then e = e - 1 end
291 | if e > s then code = code .. " " end
292 | line = line:sub(1, s - 1) .. "[" .. code .. line:sub(s, e) .. "]" .. line:sub(e + 1)
293 | end
294 | end
295 | end
296 |
297 | -- these tags can appear multiple times
298 | for list, result in pairs({
299 | [L.WORD_LIST_VENDOR] = "V",
300 | [L.WORD_LIST_REPAIR] = "R",
301 | [L.WORD_LIST_TRAIN] = "T"
302 | }) do
303 | local pos = 1
304 | while pos ~= nil do
305 | local code, s, e, pre, post = QT.findInLists(line, {[list] = result}, true, pos)
306 | pos = nil
307 | if code ~= nil then
308 | if pre ~= nil and #pre <= e - s then s = s + #pre elseif s == 0 or line:sub(s, s):match("[%s%p]") then s = s + 1 end
309 | if post ~= nil then e = e - #post elseif e > #line or line:sub(e, e):match("[%s%p]") then e = e - 1 end
310 | if e > s then code = code .. " " end
311 | if line:sub(s - 1 - #code , s - 1) ~= "[" .. code then
312 | local rest = line:sub(e + 1)
313 | line = line:sub(1, s - 1) .. "[" .. code .. line:sub(s, e) .. "]"
314 | pos = #line
315 | line = line .. rest
316 | else
317 | pos = e
318 | end
319 | end
320 | end
321 | end
322 |
323 | if err == "" then
324 | line = "ERROR " .. line
325 | elseif err ~= nil then
326 | line = "ERROR (" .. err .. ") " .. line
327 | end
328 | return line, questids, previds, questname, activeQuests, turnedInQuests
329 | end
330 |
331 | function I.importPlainText(text, faction, zone, newQuestIdsPerLine)
332 | local l = 0
333 | local questids
334 | local previds
335 | local questname = "?"
336 | local activeQuests = {}
337 | local turnedInQuests = {}
338 | local hasErrors = false
339 | if newQuestIdsPerLine == nil then newQuestIdsPerLine = {} end
340 |
341 | text = text:gsub("([^\n]+)", function(line)
342 | l = l + 1
343 | if line ~= "" then
344 | if line:sub(1, 6) == "ERROR " or line:sub(1, 6) == " " then line = line:sub(7) end
345 | line, questids, previds, questname, activeQuests, turnedInQuests = parseLine(l, line, questids, previds, questname, activeQuests, turnedInQuests, faction, zone, newQuestIdsPerLine[l])
346 | if line:sub(1, 6) == "ERROR " then hasErrors = true end
347 | return line
348 | end
349 | end)
350 | if hasErrors then
351 | text = text:gsub("([^\n]+)", function(line)
352 | if line:sub(1, 6) ~= "ERROR " and line:sub(1, 6) ~= " " then return " " .. line end
353 | end)
354 | end
355 | return text
356 | end
357 |
358 |
--------------------------------------------------------------------------------
/Guidelime/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua:
--------------------------------------------------------------------------------
1 |
2 | -----------------------------------------------------------------------
3 | -- LibDBIcon-1.0
4 | --
5 | -- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays.
6 | --
7 |
8 | local DBICON10 = "LibDBIcon-1.0"
9 | local DBICON10_MINOR = 44 -- Bump on changes
10 | if not LibStub then error(DBICON10 .. " requires LibStub.") end
11 | local ldb = LibStub("LibDataBroker-1.1", true)
12 | if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
13 | local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
14 | if not lib then return end
15 |
16 | lib.objects = lib.objects or {}
17 | lib.callbackRegistered = lib.callbackRegistered or nil
18 | lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
19 | lib.notCreated = lib.notCreated or {}
20 | lib.radius = lib.radius or 5
21 | local next, Minimap, CreateFrame = next, Minimap, CreateFrame
22 | lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
23 | local isDraggingButton = false
24 |
25 | function lib:IconCallback(event, name, key, value)
26 | if lib.objects[name] then
27 | if key == "icon" then
28 | lib.objects[name].icon:SetTexture(value)
29 | elseif key == "iconCoords" then
30 | lib.objects[name].icon:UpdateCoord()
31 | elseif key == "iconR" then
32 | local _, g, b = lib.objects[name].icon:GetVertexColor()
33 | lib.objects[name].icon:SetVertexColor(value, g, b)
34 | elseif key == "iconG" then
35 | local r, _, b = lib.objects[name].icon:GetVertexColor()
36 | lib.objects[name].icon:SetVertexColor(r, value, b)
37 | elseif key == "iconB" then
38 | local r, g = lib.objects[name].icon:GetVertexColor()
39 | lib.objects[name].icon:SetVertexColor(r, g, value)
40 | end
41 | end
42 | end
43 | if not lib.callbackRegistered then
44 | ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
45 | ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback")
46 | ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback")
47 | ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback")
48 | ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback")
49 | lib.callbackRegistered = true
50 | end
51 |
52 | local function getAnchors(frame)
53 | local x, y = frame:GetCenter()
54 | if not x or not y then return "CENTER" end
55 | local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
56 | local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
57 | return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
58 | end
59 |
60 | local function onEnter(self)
61 | if isDraggingButton then return end
62 |
63 | for _, button in next, lib.objects do
64 | if button.showOnMouseover then
65 | button.fadeOut:Stop()
66 | button:SetAlpha(1)
67 | end
68 | end
69 |
70 | local obj = self.dataObject
71 | if obj.OnTooltipShow then
72 | lib.tooltip:SetOwner(self, "ANCHOR_NONE")
73 | lib.tooltip:SetPoint(getAnchors(self))
74 | obj.OnTooltipShow(lib.tooltip)
75 | lib.tooltip:Show()
76 | elseif obj.OnEnter then
77 | obj.OnEnter(self)
78 | end
79 | end
80 |
81 | local function onLeave(self)
82 | lib.tooltip:Hide()
83 |
84 | if not isDraggingButton then
85 | for _, button in next, lib.objects do
86 | if button.showOnMouseover then
87 | button.fadeOut:Play()
88 | end
89 | end
90 | end
91 |
92 | local obj = self.dataObject
93 | if obj.OnLeave then
94 | obj.OnLeave(self)
95 | end
96 | end
97 |
98 | --------------------------------------------------------------------------------
99 |
100 | local onDragStart, updatePosition
101 |
102 | do
103 | local minimapShapes = {
104 | ["ROUND"] = {true, true, true, true},
105 | ["SQUARE"] = {false, false, false, false},
106 | ["CORNER-TOPLEFT"] = {false, false, false, true},
107 | ["CORNER-TOPRIGHT"] = {false, false, true, false},
108 | ["CORNER-BOTTOMLEFT"] = {false, true, false, false},
109 | ["CORNER-BOTTOMRIGHT"] = {true, false, false, false},
110 | ["SIDE-LEFT"] = {false, true, false, true},
111 | ["SIDE-RIGHT"] = {true, false, true, false},
112 | ["SIDE-TOP"] = {false, false, true, true},
113 | ["SIDE-BOTTOM"] = {true, true, false, false},
114 | ["TRICORNER-TOPLEFT"] = {false, true, true, true},
115 | ["TRICORNER-TOPRIGHT"] = {true, false, true, true},
116 | ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
117 | ["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
118 | }
119 |
120 | local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
121 | function updatePosition(button, position)
122 | local angle = rad(position or 225)
123 | local x, y, q = cos(angle), sin(angle), 1
124 | if x < 0 then q = q + 1 end
125 | if y > 0 then q = q + 2 end
126 | local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
127 | local quadTable = minimapShapes[minimapShape]
128 | local w = (Minimap:GetWidth() / 2) + lib.radius
129 | local h = (Minimap:GetHeight() / 2) + lib.radius
130 | if quadTable[q] then
131 | x, y = x*w, y*h
132 | else
133 | local diagRadiusW = sqrt(2*(w)^2)-10
134 | local diagRadiusH = sqrt(2*(h)^2)-10
135 | x = max(-w, min(x*diagRadiusW, w))
136 | y = max(-h, min(y*diagRadiusH, h))
137 | end
138 | button:SetPoint("CENTER", Minimap, "CENTER", x, y)
139 | end
140 | end
141 |
142 | local function onClick(self, b)
143 | if self.dataObject.OnClick then
144 | self.dataObject.OnClick(self, b)
145 | end
146 | end
147 |
148 | local function onMouseDown(self)
149 | self.isMouseDown = true
150 | self.icon:UpdateCoord()
151 | end
152 |
153 | local function onMouseUp(self)
154 | self.isMouseDown = false
155 | self.icon:UpdateCoord()
156 | end
157 |
158 | do
159 | local deg, atan2 = math.deg, math.atan2
160 | local function onUpdate(self)
161 | local mx, my = Minimap:GetCenter()
162 | local px, py = GetCursorPosition()
163 | local scale = Minimap:GetEffectiveScale()
164 | px, py = px / scale, py / scale
165 | local pos = 225
166 | if self.db then
167 | pos = deg(atan2(py - my, px - mx)) % 360
168 | self.db.minimapPos = pos
169 | else
170 | pos = deg(atan2(py - my, px - mx)) % 360
171 | self.minimapPos = pos
172 | end
173 | updatePosition(self, pos)
174 | end
175 |
176 | function onDragStart(self)
177 | self:LockHighlight()
178 | self.isMouseDown = true
179 | self.icon:UpdateCoord()
180 | self:SetScript("OnUpdate", onUpdate)
181 | isDraggingButton = true
182 | lib.tooltip:Hide()
183 | for _, button in next, lib.objects do
184 | if button.showOnMouseover then
185 | button.fadeOut:Stop()
186 | button:SetAlpha(1)
187 | end
188 | end
189 | end
190 | end
191 |
192 | local function onDragStop(self)
193 | self:SetScript("OnUpdate", nil)
194 | self.isMouseDown = false
195 | self.icon:UpdateCoord()
196 | self:UnlockHighlight()
197 | isDraggingButton = false
198 | for _, button in next, lib.objects do
199 | if button.showOnMouseover then
200 | button.fadeOut:Play()
201 | end
202 | end
203 | end
204 |
205 | local defaultCoords = {0, 1, 0, 1}
206 | local function updateCoord(self)
207 | local coords = self:GetParent().dataObject.iconCoords or defaultCoords
208 | local deltaX, deltaY = 0, 0
209 | if not self:GetParent().isMouseDown then
210 | deltaX = (coords[2] - coords[1]) * 0.05
211 | deltaY = (coords[4] - coords[3]) * 0.05
212 | end
213 | self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY)
214 | end
215 |
216 | local function createButton(name, object, db)
217 | local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
218 | button.dataObject = object
219 | button.db = db
220 | button:SetFrameStrata("MEDIUM")
221 | button:SetFixedFrameStrata(true)
222 | button:SetFrameLevel(8)
223 | button:SetFixedFrameLevel(true)
224 | button:SetSize(31, 31)
225 | button:RegisterForClicks("anyUp")
226 | button:RegisterForDrag("LeftButton")
227 | button:SetHighlightTexture(136477) --"Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight"
228 | local overlay = button:CreateTexture(nil, "OVERLAY")
229 | overlay:SetSize(53, 53)
230 | overlay:SetTexture(136430) --"Interface\\Minimap\\MiniMap-TrackingBorder"
231 | overlay:SetPoint("TOPLEFT")
232 | local background = button:CreateTexture(nil, "BACKGROUND")
233 | background:SetSize(20, 20)
234 | background:SetTexture(136467) --"Interface\\Minimap\\UI-Minimap-Background"
235 | background:SetPoint("TOPLEFT", 7, -5)
236 | local icon = button:CreateTexture(nil, "ARTWORK")
237 | icon:SetSize(17, 17)
238 | icon:SetTexture(object.icon)
239 | icon:SetPoint("TOPLEFT", 7, -6)
240 | button.icon = icon
241 | button.isMouseDown = false
242 |
243 | local r, g, b = icon:GetVertexColor()
244 | icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b)
245 |
246 | icon.UpdateCoord = updateCoord
247 | icon:UpdateCoord()
248 |
249 | button:SetScript("OnEnter", onEnter)
250 | button:SetScript("OnLeave", onLeave)
251 | button:SetScript("OnClick", onClick)
252 | if not db or not db.lock then
253 | button:SetScript("OnDragStart", onDragStart)
254 | button:SetScript("OnDragStop", onDragStop)
255 | end
256 | button:SetScript("OnMouseDown", onMouseDown)
257 | button:SetScript("OnMouseUp", onMouseUp)
258 |
259 | button.fadeOut = button:CreateAnimationGroup()
260 | local animOut = button.fadeOut:CreateAnimation("Alpha")
261 | animOut:SetOrder(1)
262 | animOut:SetDuration(0.2)
263 | animOut:SetFromAlpha(1)
264 | animOut:SetToAlpha(0)
265 | animOut:SetStartDelay(1)
266 | button.fadeOut:SetToFinalAlpha(true)
267 |
268 | lib.objects[name] = button
269 |
270 | if lib.loggedIn then
271 | updatePosition(button, db and db.minimapPos)
272 | if not db or not db.hide then
273 | button:Show()
274 | else
275 | button:Hide()
276 | end
277 | end
278 | lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
279 | end
280 |
281 | -- We could use a metatable.__index on lib.objects, but then we'd create
282 | -- the icons when checking things like :IsRegistered, which is not necessary.
283 | local function check(name)
284 | if lib.notCreated[name] then
285 | createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
286 | lib.notCreated[name] = nil
287 | end
288 | end
289 |
290 | -- Wait a bit with the initial positioning to let any GetMinimapShape addons
291 | -- load up.
292 | if not lib.loggedIn then
293 | local f = CreateFrame("Frame")
294 | f:SetScript("OnEvent", function(f)
295 | for _, button in next, lib.objects do
296 | updatePosition(button, button.db and button.db.minimapPos)
297 | if not button.db or not button.db.hide then
298 | button:Show()
299 | else
300 | button:Hide()
301 | end
302 | end
303 | lib.loggedIn = true
304 | f:SetScript("OnEvent", nil)
305 | end)
306 | f:RegisterEvent("PLAYER_LOGIN")
307 | end
308 |
309 | local function getDatabase(name)
310 | return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db
311 | end
312 |
313 | function lib:Register(name, object, db)
314 | if not object.icon then error("Can't register LDB objects without icons set!") end
315 | if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
316 | if not db or not db.hide then
317 | createButton(name, object, db)
318 | else
319 | lib.notCreated[name] = {object, db}
320 | end
321 | end
322 |
323 | function lib:Lock(name)
324 | if not lib:IsRegistered(name) then return end
325 | if lib.objects[name] then
326 | lib.objects[name]:SetScript("OnDragStart", nil)
327 | lib.objects[name]:SetScript("OnDragStop", nil)
328 | end
329 | local db = getDatabase(name)
330 | if db then
331 | db.lock = true
332 | end
333 | end
334 |
335 | function lib:Unlock(name)
336 | if not lib:IsRegistered(name) then return end
337 | if lib.objects[name] then
338 | lib.objects[name]:SetScript("OnDragStart", onDragStart)
339 | lib.objects[name]:SetScript("OnDragStop", onDragStop)
340 | end
341 | local db = getDatabase(name)
342 | if db then
343 | db.lock = nil
344 | end
345 | end
346 |
347 | function lib:Hide(name)
348 | if not lib.objects[name] then return end
349 | lib.objects[name]:Hide()
350 | end
351 |
352 | function lib:Show(name)
353 | check(name)
354 | local button = lib.objects[name]
355 | if button then
356 | button:Show()
357 | updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
358 | end
359 | end
360 |
361 | function lib:IsRegistered(name)
362 | return (lib.objects[name] or lib.notCreated[name]) and true or false
363 | end
364 |
365 | function lib:Refresh(name, db)
366 | check(name)
367 | local button = lib.objects[name]
368 | if db then
369 | button.db = db
370 | end
371 | updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
372 | if not button.db or not button.db.hide then
373 | button:Show()
374 | else
375 | button:Hide()
376 | end
377 | if not button.db or not button.db.lock then
378 | button:SetScript("OnDragStart", onDragStart)
379 | button:SetScript("OnDragStop", onDragStop)
380 | else
381 | button:SetScript("OnDragStart", nil)
382 | button:SetScript("OnDragStop", nil)
383 | end
384 | end
385 |
386 | function lib:GetMinimapButton(name)
387 | return lib.objects[name]
388 | end
389 |
390 | do
391 | local function OnMinimapEnter()
392 | if isDraggingButton then return end
393 | for _, button in next, lib.objects do
394 | if button.showOnMouseover then
395 | button.fadeOut:Stop()
396 | button:SetAlpha(1)
397 | end
398 | end
399 | end
400 | local function OnMinimapLeave()
401 | if isDraggingButton then return end
402 | for _, button in next, lib.objects do
403 | if button.showOnMouseover then
404 | button.fadeOut:Play()
405 | end
406 | end
407 | end
408 | Minimap:HookScript("OnEnter", OnMinimapEnter)
409 | Minimap:HookScript("OnLeave", OnMinimapLeave)
410 |
411 | function lib:ShowOnEnter(name, value)
412 | local button = lib.objects[name]
413 | if button then
414 | if value then
415 | button.showOnMouseover = true
416 | button.fadeOut:Stop()
417 | button:SetAlpha(0)
418 | else
419 | button.showOnMouseover = false
420 | button.fadeOut:Stop()
421 | button:SetAlpha(1)
422 | end
423 | end
424 | end
425 | end
426 |
427 | function lib:GetButtonList()
428 | local t = {}
429 | for name in next, lib.objects do
430 | t[#t+1] = name
431 | end
432 | return t
433 | end
434 |
435 | function lib:SetButtonRadius(radius)
436 | if type(radius) == "number" then
437 | lib.radius = radius
438 | for _, button in next, lib.objects do
439 | updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
440 | end
441 | end
442 | end
443 |
444 | function lib:SetButtonToPosition(button, position)
445 | updatePosition(lib.objects[button] or button, position)
446 | end
447 |
448 | -- Upgrade!
449 | for name, button in next, lib.objects do
450 | local db = getDatabase(name)
451 | if not db or not db.lock then
452 | button:SetScript("OnDragStart", onDragStart)
453 | button:SetScript("OnDragStop", onDragStop)
454 | end
455 | button:SetScript("OnEnter", onEnter)
456 | button:SetScript("OnLeave", onLeave)
457 | button:SetScript("OnClick", onClick)
458 | button:SetScript("OnMouseDown", onMouseDown)
459 | button:SetScript("OnMouseUp", onMouseUp)
460 |
461 | if not button.fadeOut then -- Upgrade to 39
462 | button.fadeOut = button:CreateAnimationGroup()
463 | local animOut = button.fadeOut:CreateAnimation("Alpha")
464 | animOut:SetOrder(1)
465 | animOut:SetDuration(0.2)
466 | animOut:SetFromAlpha(1)
467 | animOut:SetToAlpha(0)
468 | animOut:SetStartDelay(1)
469 | button.fadeOut:SetToFinalAlpha(true)
470 | end
471 | end
472 | lib:SetButtonRadius(lib.radius) -- Upgrade to 40
473 |
--------------------------------------------------------------------------------
/Guidelime/ActionButtons.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
5 | addon.SP = addon.SP or {}; local SP = addon.SP -- Data/SpellDB
6 | addon.EV = addon.EV or {}; local EV = addon.EV -- Events
7 | addon.F = addon.F or {}; local F = addon.F -- Frames
8 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
9 | addon.M = addon.M or {}; local M = addon.M -- Map
10 | addon.MW = addon.MW or {}; local MW = addon.MW -- MainWindow
11 | addon.QT = addon.QT or {}; local QT = addon.QT -- Data/QuestTools
12 |
13 | addon.AB = addon.AB or {}; local AB = addon.AB -- ActionButtons
14 |
15 | -- for key bindings
16 | _G["BINDING_NAME_GUIDELIME_TARGET_1"] = L.TARGET_1
17 | _G["BINDING_NAME_GUIDELIME_USE_ITEM_1"] = string.format(L.USE_ITEM_X, 1)
18 | for i = 2, 5 do
19 | _G["BINDING_NAME_GUIDELIME_TARGET_" .. i] = string.format(L.TARGET_X, i)
20 | _G["BINDING_NAME_GUIDELIME_USE_ITEM_" .. i] = string.format(L.USE_ITEM_X, i)
21 | end
22 |
23 | function AB.resetButtons(buttons)
24 | if not buttons then return end
25 | for _, button in pairs(buttons) do
26 | if button:IsShown() then
27 | if InCombatLockdown() then
28 | EV.updateAfterCombat = true
29 | return
30 | end
31 | ClearOverrideBindings(button)
32 | button:Hide()
33 | end
34 | end
35 | end
36 |
37 | -- ordering of raid markers to use
38 | -- default to triangle for friends because it is green and skull for enemies
39 | AB.targetRaidMarkerIndex = {4, 6, 3, 5, 1, 2, 7, 8}
40 |
41 | function AB.getTargetButtonIconText(npcId)
42 | if AB.targetNpcIdMarker == null or AB.targetNpcIdMarker[npcId] == null then return "" end
43 | return AB.getTargetMarkerIconText(AB.targetNpcIdMarker[npcId]) .. " "
44 | end
45 |
46 | function AB.getTargetMarkerIconText(marker)
47 | if marker and marker > 0 then
48 | return "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_" .. marker .. ":12|t"
49 | else
50 | return "|T" .. addon.icons.TARGET_BUTTON .. ":12|t"
51 | end
52 | end
53 |
54 | function AB.createTargetButton(i)
55 | local button = MW.mainFrame.targetButtons[i]
56 | if not button then
57 | button = CreateFrame("BUTTON", "GuidelimeTargetButton" .. i, MW.mainFrame, "SecureActionButtonTemplate,ActionButtonTemplate")
58 | button.index = i
59 | button:SetAttribute("type", "macro")
60 | button.texture = button:CreateTexture(nil, "BACKGROUND")
61 | button.texture:SetTexture(i == "Multi" and addon.icons.MULTI_TARGET_BUTTON or addon.icons.TARGET_BUTTON)
62 | button.texture:SetPoint("TOPLEFT", button, -2, 1)
63 | button.texture:SetPoint("BOTTOMRIGHT", button, 2, -2)
64 | button.texture2 = button:CreateTexture(nil, "OVERLAY")
65 | button.texture2:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons")
66 | button.texture2:SetPoint("TOPLEFT", button, 20, -22)
67 | button.texture2:SetPoint("BOTTOMRIGHT", button, -2, 0)
68 | button.hotkey = button:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
69 | button.hotkey:SetSize(32, 10)
70 | button.hotkey:SetPoint("TOPRIGHT", button, 0, -1)
71 | button.hotkey:SetJustifyH("RIGHT")
72 | MW.mainFrame.targetButtons[i] = button
73 | end
74 | button:ClearAllPoints()
75 | return button
76 | end
77 |
78 | -- global function to be used in the macro: Set target marker on target if it does not have one already; remove existing marker when no target
79 | function LIME_Mark(iconId)
80 | SetRaidTarget("player", iconId)
81 | SetRaidTarget("player", 0)
82 | if UnitGUID("target") and not GetRaidTargetIndex("target") then SetRaidTarget("target", iconId) end
83 | end
84 |
85 | local function getTargetMacro(t)
86 | return "/targetexact " .. t.name ..
87 | (t.marker and "\n/run LIME_Mark(".. t.marker .. ")" or "")
88 | end
89 |
90 | local function getTargetMacroMulti(targets)
91 | local macro = ""
92 | for i, t in ipairs(targets) do
93 | local m = getTargetMacro(t)
94 | if #macro + #m + 1 > 1023 then
95 | if addon.debugging then print("LIME: target macro multi is too long") end
96 | return macro
97 | end
98 | macro = macro .. m .. "\n"
99 | end
100 | if addon.debugging then print("LIME: target macro multi length is", #macro) end
101 | return macro
102 | end
103 |
104 | local function getTooltipHint(newline, disable)
105 | if disable or GuidelimeData.keyBound then return "" end
106 | return (newline and "\n" or "") .. MW.COLOR_INACTIVE .. L.TOOLTIP_HINT_KEY_BIND_BUTTON .. "|r"
107 | end
108 |
109 | local function getTargetTooltip(t, last)
110 | return GuidelimeData.showTooltips and
111 | (table.concat({string.format(L.TARGET_TOOLTIP, MW["COLOR_" .. QT.isFriendlyNpc(t.npcId)] .. t.name .. "|r"), M.getMapTooltip(t.element)}, "\n") ..
112 | getTooltipHint(true, last == false))
113 |
114 | end
115 |
116 | local function getTargetTooltipMulti(targets)
117 | if not GuidelimeData.showTooltips then return end
118 | local tooltips = {}
119 | for i, t in ipairs(targets) do
120 | tooltips[i] = getTargetTooltip(t, i == #targets)
121 | end
122 | return table.concat(tooltips, "\n")
123 | end
124 |
125 | local function keyBindButton(button, bindingName, buttonName, functionName)
126 | local key = GetBindingKey(bindingName)
127 | if key then
128 | button.hotkey:SetText(_G["KEY_" .. key] or key)
129 | SetOverrideBindingClick(button, true, key, buttonName)
130 | if addon.debugging then print("LIME: binding " .. key .. " to " .. functionName) end
131 | GuidelimeData.keyBound = true
132 | button.hotkey:Show()
133 | else
134 | button.hotkey:Hide()
135 | end
136 | end
137 |
138 | function AB.updateTargetButtons()
139 | if not MW.mainFrame then return end
140 | if MW.mainFrame.targetButtons == nil then
141 | MW.mainFrame.targetButtons = {}
142 | else
143 | AB.resetButtons(MW.mainFrame.targetButtons)
144 | end
145 | if not GuidelimeDataChar.showTargetButtons or not CG.currentGuide or not CG.currentGuide.firstActiveIndex then return end
146 | local targets = {}
147 | local i = 1
148 | local iFriendly = 1
149 | local iHostile = #AB.targetRaidMarkerIndex
150 | for s = CG.currentGuide.firstActiveIndex, CG.currentGuide.lastActiveIndex do
151 | local step = CG.currentGuide.steps[s]
152 | if step.active then
153 | for _, element in ipairs(step.elements) do
154 | if element.t == "TARGET" and element.targetNpcId > 0 and
155 | (not element.attached or not element.attached.completed) and
156 | (not element.attached or element.attached.t ~= "COMPLETE" or CG.isQuestObjectiveActive(element.attached.questId, element.objectives, element.attached.objective)) and
157 | (element.generated or not D.contains(CG.currentGuide.activeQuestNpcs, element.targetNpcId)) then
158 | if addon.debugging then print("LIME: show target button for npc", element.targetNpcId) end
159 | if InCombatLockdown() then
160 | EV.updateAfterCombat = true
161 | return
162 | end
163 | local name = QT.getNPCName(element.targetNpcId)
164 | if name then
165 | local t = D.find(targets, function(t) return t.name == name end)
166 | if not t then
167 | local marker
168 | if GuidelimeData.targetRaidMarkers and iFriendly <= iHostile then
169 | local friendly = QT.isFriendlyNpc(element.targetNpcId) == 'Friendly'
170 | if friendly then
171 | marker = AB.targetRaidMarkerIndex[iFriendly]
172 | iFriendly = iFriendly + 1
173 | else
174 | marker = AB.targetRaidMarkerIndex[iHostile]
175 | iHostile = iHostile - 1
176 | end
177 | end
178 | targets[i] = {name = name, index = i, marker = marker, npcId = element.targetNpcId}
179 | if GuidelimeDataChar.maxNumOfTargetButtons > 0 and i >= GuidelimeDataChar.maxNumOfTargetButtons then break end
180 | i = i + 1
181 | end
182 | end
183 | end
184 | end
185 | end
186 | end
187 | local pos = 1
188 | if #targets > 1 then
189 | local button = AB.createTargetButton("Multi")
190 | button.texture2:Hide()
191 | button:SetPoint("TOP" .. GuidelimeDataChar.showTargetButtons, MW.mainFrame, "TOP" .. GuidelimeDataChar.showTargetButtons,
192 | GuidelimeDataChar.showTargetButtons == "LEFT" and -36 or (GuidelimeDataChar.mainFrameShowScrollBar and 60 or 37), -2)
193 | button:SetAttribute("macrotext", "/cleartarget\n" .. getTargetMacroMulti(targets))
194 | F.setTooltip(button, getTargetTooltipMulti(targets))
195 | keyBindButton(button, "GUIDELIME_TARGET_1", "GuidelimeTargetButtonMulti", "multi target")
196 | button:Show()
197 | pos = 2
198 | end
199 | AB.targetNpcIdMarker = {}
200 | for _, t in ipairs(targets) do
201 | local button = AB.createTargetButton(t.index)
202 | AB.targetNpcIdMarker[t.npcId] = t.marker or 0
203 | if t.marker then
204 | SetRaidTargetIconTexture(button.texture2, t.marker)
205 | button.texture2:Show()
206 | else
207 | button.texture2:Hide()
208 | end
209 | button:SetPoint("TOP" .. GuidelimeDataChar.showTargetButtons, MW.mainFrame, "TOP" .. GuidelimeDataChar.showTargetButtons,
210 | GuidelimeDataChar.showTargetButtons == "LEFT" and -36 or (GuidelimeDataChar.mainFrameShowScrollBar and 60 or 37),
211 | 39 - pos * 41)
212 | button.npc = t.name
213 | button.marker = t.marker
214 | button:SetAttribute("macrotext", "/cleartarget\n" .. getTargetMacro(t))
215 | F.setTooltip(button, getTargetTooltip(t))
216 | keyBindButton(button, "GUIDELIME_TARGET_" .. pos, "GuidelimeTargetButton" .. t.index, t.name)
217 | button:Show()
218 | pos = pos + 1
219 | end
220 | AB.numberOfTargetButtons = pos - 1
221 | end
222 |
223 | function AB.createUseItemButton(i)
224 | local button = MW.mainFrame.useButtons[i]
225 | if not button then
226 | button = CreateFrame("BUTTON", "GuidelimeUseItemButton" .. i, MW.mainFrame, "SecureActionButtonTemplate,ActionButtonTemplate")
227 | button.texture = button:CreateTexture(nil,"BACKGROUND")
228 | button.texture:SetPoint("TOPLEFT", button, -2, 1)
229 | button.texture:SetPoint("BOTTOMRIGHT", button, 2, -2)
230 | button.cooldown = CreateFrame("Cooldown", nil, button, "CooldownFrameTemplate")
231 | button.cooldown:SetSize(32, 32)
232 | button.cooldown:SetPoint("CENTER", button, "CENTER", 0, 0)
233 | button.hotkey = button:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
234 | button.hotkey:SetSize(32, 10)
235 | button.hotkey:SetPoint("TOPRIGHT", button, 0, -1)
236 | button.hotkey:SetJustifyH("RIGHT")
237 | button.count = button:CreateFontString(nil, "ARTWORK", "NumberFontNormal")
238 | button.count:SetPoint("BOTTOMRIGHT", button, -1, 1)
239 | button.count:SetJustifyH("RIGHT")
240 | button.Update = function(self)
241 | local start, duration, enable
242 | if self.spellId then
243 | start, duration, enable = GetSpellCooldown(self.spellId)
244 | else
245 | start, duration, enable = QT.GetItemCooldown(self.itemId)
246 | end
247 | if enable == 1 and duration > 0 then
248 | self.cooldown:Show()
249 | self.cooldown:SetCooldown(start, duration)
250 | else
251 | self.cooldown:Hide()
252 | end
253 | end
254 | MW.mainFrame.useButtons[i] = button
255 | end
256 | button.spellId = nil
257 | button.cooldown:Hide()
258 | button:ClearAllPoints()
259 | return button
260 | end
261 |
262 | function AB.updateUseItemButtons()
263 | if not MW.mainFrame then return end
264 | if MW.mainFrame.useButtons == nil then
265 | MW.mainFrame.useButtons = {}
266 | else
267 | AB.resetButtons(MW.mainFrame.useButtons)
268 | end
269 | if not GuidelimeDataChar.showUseItemButtons or not CG.currentGuide or not CG.currentGuide.firstActiveIndex then return end
270 | local i = 1
271 | local startPos = GuidelimeDataChar.showUseItemButtons == GuidelimeDataChar.showTargetButtons and AB.numberOfTargetButtons and AB.numberOfTargetButtons > 0 and (AB.numberOfTargetButtons * 42 + 5) or 0
272 | local previousIds = {}
273 | for s = CG.currentGuide.firstActiveIndex, CG.currentGuide.lastActiveIndex do
274 | local step = CG.currentGuide.steps[s]
275 | if step.active then
276 | for _, element in ipairs(step.elements) do
277 | if element.t == "USE_ITEM" and element.useItemId > 0 and
278 | not (step.useItemElement and element.generated) and not (element.attached and element.attached.completed) and
279 | not D.contains(previousIds, element.useItemId) then
280 | if addon.debugging then print("LIME: show use item button for item", element.useItemId) end
281 | if InCombatLockdown() then
282 | EV.updateAfterCombat = true
283 | return
284 | end
285 | local button = AB.createUseItemButton(i)
286 | button:SetPoint("TOP" .. GuidelimeDataChar.showUseItemButtons, MW.mainFrame, "TOP" .. GuidelimeDataChar.showUseItemButtons,
287 | GuidelimeDataChar.showUseItemButtons == "LEFT" and -36 or (GuidelimeDataChar.mainFrameShowScrollBar and 60 or 37),
288 | 39 - i * 41 - startPos)
289 | button.itemId = element.useItemId
290 | button.texture:SetTexture(GetItemIcon(button.itemId))
291 | local name = EV.GetItemInfo(button.itemId)
292 | if name then
293 | button:SetAttribute("type", "item")
294 | button:SetAttribute("item", name)
295 | --F.setTooltip(button, name .. "\n" .. (QT.getUseItemTooltip(button.itemId) or ""))
296 | F.setTooltip(button, "item:" .. button.itemId, function(self, s)
297 | GameTooltip:SetHyperlink(s)
298 | GameTooltip:AddLine(getTooltipHint())
299 | end)
300 | keyBindButton(button, "GUIDELIME_USE_ITEM_" .. i, "GuidelimeUseItemButton" .. i, name)
301 | end
302 | local count = GetItemCount(button.itemId) or 0
303 | button.count:SetText(count ~= 1 and count or "")
304 | button.texture:SetAlpha((count > 0 and 1) or 0.5)
305 | button:Show()
306 | button:Update()
307 | table.insert(previousIds, element.useItemId)
308 | i = i + 1
309 | elseif element.t == "SPELL" and element.spellId ~= 0 and not element.completed then
310 |
311 | local id
312 | if element.spellId and GetSpellInfo(element.spellId) then id = element.spellId end
313 | if id == nil then id = SP.getSpellId(element.spell) end
314 | if addon.debugging then print("LIME: show button for spell", id) end
315 | if InCombatLockdown() then
316 | EV.updateAfterCombat = true
317 | return
318 | end
319 | local name, _, icon = GetSpellInfo(id)
320 | if name then
321 | local button = AB.createUseItemButton(i)
322 | button:SetPoint("TOP" .. GuidelimeDataChar.showUseItemButtons, MW.mainFrame, "TOP" .. GuidelimeDataChar.showUseItemButtons,
323 | GuidelimeDataChar.showUseItemButtons == "LEFT" and -36 or (GuidelimeDataChar.mainFrameShowScrollBar and 60 or 37),
324 | 39 - i * 41 - startPos)
325 | button.spellId = id
326 | button.texture:SetTexture(icon or addon.icons.SPELL)
327 | button.texture:SetAlpha(1)
328 | button:SetAttribute("type", "spell")
329 | button:SetAttribute("spell", name)
330 | if not IsSpellKnown(id) then
331 | local index = SP.getTradeSkillIndex(name)
332 | if index then
333 | button:SetAttribute("type", "macro")
334 | button:SetAttribute("macrotext", "/run DoTradeSkill(" .. index .. ")")
335 | local count = select(3, GetTradeSkillInfo(index))
336 | button.count:SetText(count)
337 | button.texture:SetAlpha((count > 0 and 1) or 0.2)
338 | else
339 | button:SetAlpha(0.2)
340 | end
341 | end
342 | F.setTooltip(button, "spell:" .. id, function(self, s)
343 | GameTooltip:SetHyperlink(s)
344 | GameTooltip:AddLine(getTooltipHint())
345 | end)
346 | keyBindButton(button, "GUIDELIME_USE_ITEM_" .. i, "GuidelimeUseItemButton" .. i, name)
347 | button:Show()
348 | button:Update()
349 | if GuidelimeDataChar.maxNumOfItemButtons > 0 and i >= GuidelimeDataChar.maxNumOfItemButtons then break end
350 | i = i + 1
351 | end
352 | end
353 | end
354 | end
355 | end
356 | end
--------------------------------------------------------------------------------
/Guidelime/Guides.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 | local L = addon.L
3 |
4 | addon.D = addon.D or {}; local D = addon.D -- Data/Data
5 | addon.CG = addon.CG or {}; local CG = addon.CG -- CurrentGuide
6 | addon.E = addon.E or {}; local E = addon.E -- Editor
7 | addon.F = addon.F or {}; local F = addon.F -- Frames
8 | addon.EV = addon.EV or {}; local EV = addon.EV -- Events
9 | addon.MW = addon.MW or {}; local MW = addon.MW -- MainWindow
10 | addon.O = addon.O or {}; local O = addon.O -- Options
11 |
12 | addon.G = addon.G or {}; local G = addon.G -- Guides
13 |
14 | G.GUIDE_LIST_URL = "https://github.com/max-ri/guidelime/wiki/GuideList"
15 |
16 | function G.loadGuide(name)
17 | if addon.debugging then print("LIME: load guide", name) end
18 |
19 | if G.guidesFrame ~= nil then
20 | if GuidelimeDataChar.currentGuide ~= nil and G.guidesFrame.guides[GuidelimeDataChar.currentGuide] ~= nil then
21 | G.guidesFrame.guides[GuidelimeDataChar.currentGuide]:SetBackdropColor(0,0,0,0)
22 | end
23 | G.guidesFrame.guides[name]:SetBackdropColor(1,1,0,1)
24 | G.guidesFrame.text1:SetText(L.CURRENT_GUIDE .. ": |cFFFFFFFF" .. name .. "\n")
25 | end
26 | if E.editorFrame ~= nil then
27 | E.editorFrame.text1:SetText(L.CURRENT_GUIDE .. ": |cFFFFFFFF" .. name .. "\n")
28 | if addon.guides[name] ~= nil then
29 | E.editorFrame.textBox:SetText(addon.guides[name].text:gsub("|","¦"))
30 | end
31 | end
32 | GuidelimeDataChar.currentGuide = name
33 | if addon.guides[name] ~= nil then
34 | GuidelimeData.lastGuideGroup = addon.guides[name].group
35 | end
36 | CG.loadCurrentGuide(true)
37 | EV.updateFromQuestLog()
38 | if GuidelimeDataChar.mainFrameShowing then
39 | MW.updateMainFrame()
40 | else
41 | GuidelimeDataChar.mainFrameShowing = true
42 | if O.optionsFrame ~= nil then O.optionsFrame.mainFrameShowing:SetChecked(true) end
43 | MW.showMainFrame()
44 | end
45 | end
46 |
47 | local function resetGuide()
48 | if GuidelimeDataChar.currentGuide == nil then return end
49 | GuidelimeDataChar.guideSkip[GuidelimeDataChar.currentGuide] = {}
50 | G.loadGuide(GuidelimeDataChar.currentGuide)
51 | end
52 |
53 | local function selectGuide(name)
54 | if addon.guides[name].reputation == nil or
55 | D.hasRequirements(addon.guides[name]) then
56 | G.loadGuide(name)
57 | end
58 | end
59 |
60 | function G.showGuides()
61 | if not addon.dataLoaded then loadData() end
62 |
63 | if G.isGuidesShowing() then
64 | G.guidesFrame:Hide()
65 | return
66 | end
67 |
68 | if InterfaceOptionsFrame then
69 | InterfaceOptionsFrame:Hide()
70 | else
71 | HideUIPanel(SettingsPanel)
72 | end
73 | if E.isEditorShowing() then E.editorFrame:Hide() end
74 |
75 | if G.guidesFrame == nil then
76 | G.guidesFrame = F.createPopupFrame(nil, nil, false, 700)
77 | G.guidesFrame:SetWidth(800)
78 | G.guidesFrame:SetPoint(GuidelimeDataChar.guidesFrameRelative, UIParent, GuidelimeDataChar.guidesFrameRelative, GuidelimeDataChar.guidesFrameX, GuidelimeDataChar.guidesFrameY)
79 |
80 | G.guidesFrame.okBtn:SetNormalTexture("Interface/Buttons/UI-Panel-MinimizeButton-Up")
81 | G.guidesFrame.okBtn:SetHighlightTexture("Interface/Buttons/UI-Panel-MinimizeButton-Highlight")
82 | G.guidesFrame.okBtn:SetPushedTexture("Interface/Buttons/UI-Panel-MinimizeButton-Down")
83 | G.guidesFrame.okBtn:ClearAllPoints()
84 | G.guidesFrame.okBtn:SetPoint("TOPRIGHT", G.guidesFrame, -10, -10)
85 | G.guidesFrame.okBtn:SetSize(24, 24)
86 | G.guidesFrame.okBtn:SetText(nil)
87 |
88 | G.guidesFrame.title = G.guidesFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
89 | G.guidesFrame.title:SetText(GetAddOnMetadata(addonName, "title") .. " |cFFFFFFFF" .. GetAddOnMetadata(addonName, "version"))
90 | G.guidesFrame.title:SetPoint("TOPLEFT", G.guidesFrame, "TOPLEFT", 20, -20)
91 | G.guidesFrame.title:SetFontObject("GameFontNormalLarge")
92 | local prev = G.guidesFrame.title
93 |
94 | G.guidesFrame.text1 = G.guidesFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
95 | G.guidesFrame.text1:SetText(L.CURRENT_GUIDE .. ": |cFFFFFFFF" .. (GuidelimeDataChar.currentGuide or "") .. "\n")
96 | G.guidesFrame.text1:SetPoint("TOPLEFT", prev, "TOPLEFT", 0, -30)
97 | prev = G.guidesFrame.text1
98 |
99 | G.guidesFrame.text2 = G.guidesFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
100 | G.guidesFrame.text2:SetText(L.AVAILABLE_GUIDES .. ":\n")
101 | G.guidesFrame.text2:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -10)
102 | prev = G.guidesFrame.text2
103 |
104 | local scrollFrame = CreateFrame("ScrollFrame", nil, G.guidesFrame, "UIPanelScrollFrameTemplate")
105 | scrollFrame:SetPoint("TOPLEFT", prev, "TOPLEFT", 0, -20)
106 | scrollFrame:SetPoint("RIGHT", G.guidesFrame, "RIGHT", -30, 0)
107 | scrollFrame:SetPoint("BOTTOM", G.guidesFrame, "BOTTOM", 0, 160)
108 |
109 | G.guidesFrame.content = CreateFrame("Frame", nil, scrollFrame)
110 | G.guidesFrame.content:SetSize(1, 1)
111 | scrollFrame:SetScrollChild(G.guidesFrame.content)
112 |
113 | G.guidesFrame.guideListMessage = F.addMultilineText(G.guidesFrame.content,
114 | string.format(L.GUIDE_LIST, "|cFFAAAAAA" .. G.GUIDE_LIST_URL),
115 | 550, nil, function()
116 | InterfaceOptionsFrame:Hide()
117 | F.showUrlPopup(G.GUIDE_LIST_URL)
118 | end)
119 |
120 | prev = scrollFrame
121 |
122 | G.guidesFrame.text3 = G.guidesFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
123 | G.guidesFrame.text3:SetText(L.DETAILS .. ":\n")
124 | G.guidesFrame.text3:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -20)
125 | prev = G.guidesFrame.text3
126 |
127 | scrollFrame = CreateFrame("ScrollFrame", nil, G.guidesFrame, "UIPanelScrollFrameTemplate")
128 | scrollFrame:SetPoint("TOPLEFT", prev, "TOPLEFT", 0, -20)
129 | scrollFrame:SetPoint("RIGHT", G.guidesFrame, "RIGHT", -30, 0)
130 | scrollFrame:SetPoint("BOTTOM", G.guidesFrame, "BOTTOM", 0, 60)
131 |
132 | local content = CreateFrame("Frame", nil, scrollFrame)
133 | content:SetSize(1, 1)
134 | scrollFrame:SetScrollChild(content)
135 |
136 | G.guidesFrame.textDetails = F.addMultilineText(content, nil, 550, nil, function()
137 | if G.guidesFrame.textDetails.url ~= nil then
138 | InterfaceOptionsFrame:Hide()
139 | F.showUrlPopup(G.guidesFrame.textDetails.url)
140 | end
141 | end)
142 | G.guidesFrame.textDetails:SetPoint("TOPLEFT", content, "BOTTOMLEFT", 0, 0)
143 | G.guidesFrame.textDetails:SetTextColor(1,1,1,1)
144 | if addon.guides[GuidelimeDataChar.currentGuide] ~= nil and addon.guides[GuidelimeDataChar.currentGuide].details ~= nil then
145 | G.guidesFrame.textDetails:SetText(addon.guides[GuidelimeDataChar.currentGuide].details)
146 | G.guidesFrame.textDetails.url = addon.guides[GuidelimeDataChar.currentGuide].detailsUrl or ""
147 | end
148 |
149 | G.guidesFrame.loadBtn = CreateFrame("BUTTON", nil, G.guidesFrame, "UIPanelButtonTemplate")
150 | G.guidesFrame.loadBtn:SetWidth(140)
151 | G.guidesFrame.loadBtn:SetHeight(30)
152 | G.guidesFrame.loadBtn:SetText(L.RESET_GUIDE)
153 | G.guidesFrame.loadBtn:SetPoint("BOTTOMLEFT", G.guidesFrame, "BOTTOMLEFT", 20, 20)
154 | G.guidesFrame.loadBtn:SetScript("OnClick", resetGuide)
155 |
156 | G.guidesFrame.loadBtn = CreateFrame("BUTTON", nil, G.guidesFrame, "UIPanelButtonTemplate")
157 | G.guidesFrame.loadBtn:SetWidth(140)
158 | G.guidesFrame.loadBtn:SetHeight(30)
159 | G.guidesFrame.loadBtn:SetText(L.EDIT_GUIDE)
160 | G.guidesFrame.loadBtn:SetPoint("BOTTOMLEFT", G.guidesFrame, "BOTTOMLEFT", 160, 20)
161 | G.guidesFrame.loadBtn:SetScript("OnClick", E.showEditor)
162 |
163 | end
164 | prev = G.guidesFrame.content
165 |
166 | if addon.debugging then print("LIME:", D.faction, D.race, D.class) end
167 |
168 | local groups = {}
169 | local groupNames = {}
170 | for name, guide in pairs(addon.guides) do
171 | if D.applies(guide) then
172 | if groups[guide.group] == nil then
173 | groups[guide.group] = {}
174 | table.insert(groupNames, guide.group)
175 | end
176 | table.insert(groups[guide.group], name)
177 | end
178 | end
179 | table.sort(groupNames)
180 |
181 | if G.guidesFrame.groups ~= nil then
182 | for _, group in pairs(G.guidesFrame.groups) do
183 | group:Hide()
184 | end
185 | end
186 | if G.guidesFrame.guides ~= nil then
187 | for _, guide in pairs(G.guidesFrame.guides) do
188 | guide:Hide()
189 | end
190 | end
191 | if G.guidesFrame.messages ~= nil then
192 | for _, message in pairs(G.guidesFrame.messages) do
193 | message:Hide()
194 | end
195 | end
196 | G.guidesFrame.groups = {}
197 | G.guidesFrame.guides = {}
198 | G.guidesFrame.messages = {}
199 |
200 | for i, group in ipairs(groupNames) do
201 | local guides = groups[group]
202 | table.sort(guides, function(a, b)
203 | local ga = addon.guides[a]
204 | local gb = addon.guides[b]
205 | if (ga.minLevel ~= nil or gb.minLevel ~= nil) and ga.minLevel ~= gb.minLevel then return (ga.minLevel or 0) < (gb.minLevel or 0) end
206 | if (ga.maxLevel ~= nil or gb.maxLevel ~= nil) and ga.maxLevel ~= gb.maxLevel then return (ga.maxLevel or 0) < (gb.maxLevel or 0) end
207 | return (ga.name or "") < (gb.name or "")
208 | end)
209 |
210 | --[[
211 | local downloadMinLevel, downloadMaxLevel, download, downloadUrl
212 | for j, name in ipairs(guides) do
213 | local guide = addon.guides[name]
214 | --if addon.debugging then print("LIME: guide", group, name) end
215 |
216 | if guide.next ~= nil and #guide.next > 0 and (addon.guides[group .. ' ' .. guide.next[1] ] == nil) and guide.download ~= nil then
217 | downloadMinLevel, downloadMaxLevel, download, downloadUrl = guide.downloadMinLevel, guide.downloadMaxLevel, guide.download, guide.downloadUrl
218 | end
219 | end
220 | if download == nil or GuidelimeData.displayDemoGuides then]]
221 |
222 | G.guidesFrame.groups[group] = G.guidesFrame.content:CreateFontString(nil, "ARTWORK", "GameFontNormal")
223 | if prev == G.guidesFrame.content then
224 | G.guidesFrame.groups[group]:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -10)
225 | else
226 | G.guidesFrame.groups[group]:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", -10, -10)
227 | end
228 | G.guidesFrame.groups[group]:SetText(group)
229 | prev = G.guidesFrame.groups[group]
230 | for j, name in ipairs(guides) do
231 | local guide = addon.guides[name]
232 |
233 | local text = ""
234 | if guide.minLevel ~= nil then
235 | text = text .. MW.getLevelColor(guide.minLevel) .. guide.minLevel .. "|r"
236 | end
237 | if guide.minLevel ~= nil or guide.maxLevel ~= nil then
238 | text = text .. "-"
239 | end
240 | if guide.maxLevel ~= nil then
241 | text = text .. MW.getLevelColor(guide.maxLevel) .. guide.maxLevel .. "|r"
242 | end
243 | if guide.minLevel ~= nil or guide.maxLevel ~= nil then
244 | text = text .. " "
245 | end
246 | if guide.title ~= nil then
247 | if D.hasRequirements(guide) then
248 | text = text .. MW.COLOR_INACTIVE
249 | else
250 | text = text .. MW.COLOR_WHITE
251 | end
252 | text = text .. guide.title .. "|r"
253 | end
254 | G.guidesFrame.guides[name] = F.addMultilineText(G.guidesFrame.content, text, 550, nil, function(self)
255 | selectGuide(self.name)
256 | end)
257 | if j == 1 then
258 | G.guidesFrame.guides[name]:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 10, -5)
259 | else
260 | G.guidesFrame.guides[name]:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, 0)
261 | end
262 | G.guidesFrame.guides[name]:SetTextColor(1,1,1,1)
263 | G.guidesFrame.guides[name]:SetBackdrop({
264 | --bgFile = "Interface\\QuestFrame\\UI-QuestLogTitleHighlight",
265 | bgFile = "Interface\\AddOns\\" .. addonName .. "\\Icons\\TitleHighlight",
266 | tile = false, edgeSize = 1
267 | })
268 | G.guidesFrame.guides[name].name = name
269 | G.guidesFrame.guides[name].guide = guide
270 | if name == GuidelimeDataChar.currentGuide then
271 | G.guidesFrame.guides[name]:SetBackdropColor(1,1,0,1)
272 | else
273 | G.guidesFrame.guides[name]:SetBackdropColor(0,0,0,0)
274 | end
275 | G.guidesFrame.guides[name]:SetScript("OnEnter", function(self)
276 | G.guidesFrame.textDetails:SetText(self.guide.details or "")
277 | G.guidesFrame.textDetails.url = self.guide.detailsUrl or ""
278 | if self.name ~= GuidelimeDataChar.currentGuide then
279 | self:SetBackdropColor(0.5,0.5,1,1)
280 | end
281 | end)
282 | G.guidesFrame.guides[name]:SetScript("OnLeave", function(self)
283 | if GuidelimeDataChar.currentGuide ~= nil and addon.guides[GuidelimeDataChar.currentGuide] ~= nil then
284 | G.guidesFrame.textDetails:SetText(addon.guides[GuidelimeDataChar.currentGuide].details or "")
285 | G.guidesFrame.textDetails.url = addon.guides[GuidelimeDataChar.currentGuide].detailsUrl or ""
286 | end
287 | if self.name ~= GuidelimeDataChar.currentGuide then
288 | self:SetBackdropColor(0,0,0,0)
289 | end
290 | end)
291 | prev = G.guidesFrame.guides[name]
292 | end
293 | --[[if download ~= nil then
294 | G.guidesFrame.messages[group] = F.addMultilineText(G.guidesFrame.content,
295 | string.format(L.DOWNLOAD_FULL_GUIDE, downloadMinLevel, downloadMaxLevel, download, "\n|cFFAAAAAA" .. downloadUrl),
296 | 550, nil, function()
297 | InterfaceOptionsFrame:Hide()
298 | F.showUrlPopup(downloadUrl)
299 | end)
300 | G.guidesFrame.messages[group]:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -5)
301 | prev = G.guidesFrame.messages[group]
302 | end]]
303 | end
304 |
305 | if prev == G.guidesFrame.content then
306 | G.guidesFrame.guideListMessage:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -20)
307 | else
308 | G.guidesFrame.guideListMessage:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", -10, -20)
309 | end
310 |
311 | G.guidesFrame:Show()
312 | end
313 |
314 | function G.isGuidesShowing()
315 | return G.guidesFrame ~= nil and G.guidesFrame:IsVisible()
316 | end
317 |
318 | function G.selectStartGuide()
319 | local raceStarterZoneDict
320 | if select(4, GetBuildInfo()) >= 40000 then
321 | raceStarterZoneDict = {
322 | Dwarf = {'dwarf', 'dunmorogh', 'coldridgevalley'},
323 | Gnome = {'gnome', 'gnomeregan'},
324 | Human = {'human', 'elwynnforest', 'northshireabbey'},
325 | NightElf = {'nightelf', 'shadowglen', 'teldrassil'},
326 | Orc = {'orc', 'durotar', 'valleyoftrials'},
327 | Tauren = {'tauren', 'mulgore', 'campnarache'},
328 | Troll = {'troll', 'durotar', 'echoisles'},
329 | Undead = {'undead', 'tirisfalglades', 'deathknell'},
330 | Draenei = {'draenei', 'azuremystisle'},
331 | BloodElf = {'bloodelf', 'eversongwoods'},
332 | Worgen = {'worgen', 'gilneas'},
333 | Goblin = {'goblin', 'kezan'},
334 | Pandaren = {'pandaren', 'wanderingisle', 'monk'}
335 | }
336 | else
337 | raceStarterZoneDict = {
338 | Dwarf = {'dwarf', 'dunmorogh', 'coldridgevalley'},
339 | Gnome = {'gnome', 'dunmorogh', 'coldridgevalley'},
340 | Human = {'human', 'elwynnforest', 'northshireabbey'},
341 | NightElf = {'nightelf', 'shadowglen', 'teldrassil'},
342 | Orc = {'orc', 'durotar', 'valleyoftrials'},
343 | Tauren = {'tauren', 'mulgore', 'campnarache'},
344 | Troll = {'troll', 'durotar', 'valleyoftrials'},
345 | Undead = {'undead', 'tirisfalglades', 'deathknell'},
346 | Draenei = {'draenei', 'azuremystisle'},
347 | BloodElf = {'bloodelf', 'eversongwoods'},
348 | Worgen = {'worgen', 'gilneas'},
349 | Goblin = {'goblin', 'kezan'},
350 | Pandaren = {'pandaren', 'wanderingisle'}
351 | }
352 | end
353 | local matchingGuides = {}
354 | for name, guide in pairs(addon.guides) do
355 | if guide.minLevel == 1 then
356 | local n = guide.title:lower():gsub("%s+", "")
357 | for _, phrase in ipairs(raceStarterZoneDict[D.race]) do
358 | if string.find(n, phrase) then
359 | table.insert(matchingGuides, name)
360 | break
361 | end
362 | end
363 | end
364 | end
365 | if #matchingGuides > 1 and GuidelimeData.lastGuideGroup ~= nil then
366 | local filteredGuides = {}
367 | for _, name in ipairs(matchingGuides) do
368 | if addon.guides[name].group == GuidelimeData.lastGuideGroup then
369 | table.insert(filteredGuides, name)
370 | end
371 | if #filteredGuides > 0 then matchingGuides = filteredGuides end
372 | end
373 | end
374 | if #matchingGuides > 0 then
375 | return matchingGuides[1]
376 | end
377 | if addon.debugging then print("LIME: no matching starting guide was found") end
378 | end
379 |
--------------------------------------------------------------------------------
/Guidelime/Data/QuestieCorrections.lua:
--------------------------------------------------------------------------------
1 | local addonName, addon = ...
2 |
3 | addon.QUESTIE.correctionsObjectiveOrder = {
4 | -- https://tbc.wowhead.com/quest=10503/the-bladespire-threat
5 | -- objectives switched; first kill credit then creature
6 | [10503] = {2, 1},
7 | -- https://tbc.wowhead.com/quest=10861/veil-lithic-preemptive-strike
8 | -- objectives switched; first object then creature
9 | [10861] = {2, 1},
10 | }
11 |
12 | -- questie reports quests started by an item as started by the npc dropping the item
13 | -- we want to know which quests is started by an item in order to generate the use item button
14 | -- therefore we 'correct' this here
15 | -- ids for items starting a quest exported from wowhead: https://www.wowhead.com/wotlk/items?filter=6;1;0
16 | addon.QUESTIE.correctionsQuestAccept =
17 | {
18 | [13326] = {{Type = "item", Id = 44326}},
19 | [24554] = {{Type = "item", Id = 50380}},
20 | [13375] = {{Type = "item", Id = 44577}},
21 | [11178] = {{Type = "item", Id = 33102}},
22 | [12518] = {{Type = "item", Id = 44148}},
23 | [10413] = {{Type = "item", Id = 29738}},
24 | [13148] = {{Type = "item", Id = 43297}},
25 | [13372] = {{Type = "item", Id = 44569}},
26 | [12517] = {{Type = "item", Id = 37163}},
27 | [14443] = {{Type = "item", Id = 50379}},
28 | [10825] = {{Type = "item", Id = 31489}},
29 | [10938] = {{Type = "item", Id = 31890}},
30 | [11007] = {{Type = "item", Id = 32405}},
31 | [9731] = {{Type = "item", Id = 24330}},
32 | [9861] = {{Type = "item", Id = 24504}},
33 | [485] = {{Type = "item", Id = 8704}},
34 | [12798] = {{Type = "item", Id = 37164}},
35 | [9827] = {{Type = "item", Id = 24483}},
36 | [10941] = {{Type = "item", Id = 31914}},
37 | [10940] = {{Type = "item", Id = 31907}},
38 | [351] = {{Type = "item", Id = 8623}},
39 | [10793] = {{Type = "item", Id = 31345}},
40 | [9373] = {{Type = "item", Id = 23338}},
41 | [13311] = {{Type = "item", Id = 44158}},
42 | [2198] = {{Type = "item", Id = 7666}},
43 | [2766] = {{Type = "item", Id = 8705}},
44 | [10754] = {{Type = "item", Id = 31239}},
45 | [9872] = {{Type = "item", Id = 24558}},
46 | [11002] = {{Type = "item", Id = 32385}},
47 | [10939] = {{Type = "item", Id = 31891}},
48 | [635] = {{Type = "item", Id = 4614}},
49 | [10719] = {{Type = "item", Id = 31120}},
50 | [9764] = {{Type = "item", Id = 24367}},
51 | [10524] = {{Type = "item", Id = 30431}},
52 | [11400] = {{Type = "item", Id = 33978}},
53 | [10395] = {{Type = "item", Id = 29588}},
54 | [10621] = {{Type = "item", Id = 30756}},
55 | [10797] = {{Type = "item", Id = 31363}},
56 | [13622] = {{Type = "item", Id = 45039}},
57 | [2882] = {{Type = "item", Id = 9254}},
58 | [10810] = {{Type = "item", Id = 31384}},
59 | [13325] = {{Type = "item", Id = 44276}},
60 | [11081] = {{Type = "item", Id = 32726}},
61 | [136] = {{Type = "item", Id = 1357}},
62 | [6522] = {{Type = "item", Id = 17008}},
63 | [337] = {{Type = "item", Id = 2794}},
64 | [9871] = {{Type = "item", Id = 24559}},
65 | [13327] = {{Type = "item", Id = 44294}},
66 | [624] = {{Type = "item", Id = 4056}},
67 | [11041] = {{Type = "item", Id = 32621}},
68 | [12888] = {{Type = "item", Id = 41267}},
69 | [9418] = {{Type = "item", Id = 23580}},
70 | [7907] = {{Type = "item", Id = 19228}},
71 | [9911] = {{Type = "item", Id = 25459}},
72 | [10880] = {{Type = "item", Id = 31707}},
73 | [522] = {{Type = "item", Id = 3668}},
74 | [9301] = {{Type = "item", Id = 22970}},
75 | [11419] = {{Type = "item", Id = 34028}},
76 | [6981] = {{Type = "item", Id = 10441}},
77 | [2876] = {{Type = "item", Id = 9250}},
78 | [9247] = {{Type = "item", Id = 22723}},
79 | [13324] = {{Type = "item", Id = 44259}},
80 | [13845] = {{Type = "item", Id = 46004}},
81 | [12491] = {{Type = "item", Id = 38280}},
82 | [1148] = {{Type = "item", Id = 5877}},
83 | [6564] = {{Type = "item", Id = 16790}},
84 | [24431] = {{Type = "item", Id = 49667}},
85 | [10755] = {{Type = "item", Id = 31241}},
86 | [7704] = {{Type = "item", Id = 18950}},
87 | [10134] = {{Type = "item", Id = 29476}},
88 | [11021] = {{Type = "item", Id = 32523}},
89 | [11531] = {{Type = "item", Id = 34469}},
90 | [5202] = {{Type = "item", Id = 13140}},
91 | [9300] = {{Type = "item", Id = 22974}},
92 | [9587] = {{Type = "item", Id = 23890}},
93 | [7929] = {{Type = "item", Id = 19267}},
94 | [9828] = {{Type = "item", Id = 24484}},
95 | [7928] = {{Type = "item", Id = 19257}},
96 | [10623] = {{Type = "item", Id = 30579}},
97 | [897] = {{Type = "item", Id = 5138}},
98 | [13631] = {{Type = "item", Id = 46052}},
99 | [123] = {{Type = "item", Id = 1307}},
100 | [9299] = {{Type = "item", Id = 22972}},
101 | [637] = {{Type = "item", Id = 4433}},
102 | [5083] = {{Type = "item", Id = 12771}},
103 | [6922] = {{Type = "item", Id = 16782}},
104 | [184] = {{Type = "item", Id = 1972}},
105 | [7810] = {{Type = "item", Id = 18706}},
106 | [968] = {{Type = "item", Id = 5352}},
107 | [9302] = {{Type = "item", Id = 22973}},
108 | [11003] = {{Type = "item", Id = 32386}},
109 | [11941] = {{Type = "item", Id = 35648}},
110 | [5123] = {{Type = "item", Id = 12842}},
111 | [7927] = {{Type = "item", Id = 19277}},
112 | [12922] = {{Type = "item", Id = 41556}},
113 | [13604] = {{Type = "item", Id = 45506}},
114 | [9295] = {{Type = "item", Id = 22977}},
115 | [9588] = {{Type = "item", Id = 23892}},
116 | [1918] = {{Type = "item", Id = 16408}},
117 | [2945] = {{Type = "item", Id = 9326}},
118 | [8446] = {{Type = "item", Id = 20644}},
119 | [12492] = {{Type = "item", Id = 38281}},
120 | [8552] = {{Type = "item", Id = 3985}},
121 | [1480] = {{Type = "item", Id = 20310}},
122 | [770] = {{Type = "item", Id = 4854}},
123 | [9304] = {{Type = "item", Id = 22975}},
124 | [654] = {{Type = "item", Id = 8524}},
125 | [10305] = {{Type = "item", Id = 29234}},
126 | [927] = {{Type = "item", Id = 5179}},
127 | [3513] = {{Type = "item", Id = 10621}},
128 | [9514] = {{Type = "item", Id = 23759}},
129 | [10307] = {{Type = "item", Id = 29236}},
130 | [373] = {{Type = "item", Id = 2874}},
131 | [3884] = {{Type = "item", Id = 11116}},
132 | [7495] = {{Type = "item", Id = 18423}},
133 | [7490] = {{Type = "item", Id = 18422}},
134 | [10306] = {{Type = "item", Id = 29235}},
135 | [178] = {{Type = "item", Id = 1962}},
136 | [1649] = {{Type = "item", Id = 6776}},
137 | [3373] = {{Type = "item", Id = 10454}},
138 | [12421] = {{Type = "item", Id = 37737}},
139 | [460] = {{Type = "item", Id = 3317}},
140 | [10182] = {{Type = "item", Id = 29233}},
141 | [10229] = {{Type = "item", Id = 28552}},
142 | [12420] = {{Type = "item", Id = 37736}},
143 | [9455] = {{Type = "item", Id = 23678}},
144 | [14079] = {{Type = "item", Id = 46875}},
145 | [14083] = {{Type = "item", Id = 46877}},
146 | [8470] = {{Type = "item", Id = 20741}},
147 | [12278] = {{Type = "item", Id = 37571}},
148 | [9875] = {{Type = "item", Id = 24407}},
149 | [14203] = {{Type = "item", Id = 48679}},
150 | [7738] = {{Type = "item", Id = 18972}},
151 | [13819] = {{Type = "item", Id = 46053}},
152 | [12839] = {{Type = "item", Id = 40666}},
153 | [832] = {{Type = "item", Id = 4903}},
154 | [2978] = {{Type = "item", Id = 9370}},
155 | [5262] = {{Type = "item", Id = 13250}},
156 | [7501] = {{Type = "item", Id = 18359}},
157 | [8801] = {{Type = "item", Id = 21221}},
158 | [14352] = {{Type = "item", Id = 49205}},
159 | [4451] = {{Type = "item", Id = 11818}},
160 | [4882] = {{Type = "item", Id = 12558}},
161 | [12146] = {{Type = "item", Id = 36855}},
162 | [3181] = {{Type = "item", Id = 10000}},
163 | [7735] = {{Type = "item", Id = 18969}},
164 | [8791] = {{Type = "item", Id = 21220}},
165 | [12055] = {{Type = "item", Id = 36742}},
166 | [12507] = {{Type = "item", Id = 38321}},
167 | [1100] = {{Type = "item", Id = 5791}},
168 | [12631] = {{Type = "item", Id = 38660}},
169 | [7761] = {{Type = "item", Id = 18987}},
170 | [7781] = {{Type = "item", Id = 19003}},
171 | [13204] = {{Type = "item", Id = 43512}},
172 | [14085] = {{Type = "item", Id = 46876}},
173 | [14082] = {{Type = "item", Id = 46879}},
174 | [361] = {{Type = "item", Id = 2839}},
175 | [9616] = {{Type = "item", Id = 23910}},
176 | [11632] = {{Type = "item", Id = 34777}},
177 | [819] = {{Type = "item", Id = 4926}},
178 | [939] = {{Type = "item", Id = 11668}},
179 | [12059] = {{Type = "item", Id = 36746}},
180 | [13420] = {{Type = "item", Id = 44725}},
181 | [883] = {{Type = "item", Id = 5099}},
182 | [885] = {{Type = "item", Id = 5103}},
183 | [7502] = {{Type = "item", Id = 18360}},
184 | [8308] = {{Type = "item", Id = 20461}},
185 | [9564] = {{Type = "item", Id = 23850}},
186 | [12633] = {{Type = "item", Id = 38673}},
187 | [23] = {{Type = "item", Id = 16303}},
188 | [7498] = {{Type = "item", Id = 18356}},
189 | [8887] = {{Type = "item", Id = 21776}},
190 | [10244] = {{Type = "item", Id = 28598}},
191 | [10393] = {{Type = "item", Id = 29590}},
192 | [11729] = {{Type = "item", Id = 34984}},
193 | [12590] = {{Type = "item", Id = 38567}},
194 | [7938] = {{Type = "item", Id = 19424}},
195 | [12306] = {{Type = "item", Id = 37599}},
196 | [14087] = {{Type = "item", Id = 46884}},
197 | [551] = {{Type = "item", Id = 3706}},
198 | [594] = {{Type = "item", Id = 4098}},
199 | [3374] = {{Type = "item", Id = 10589}},
200 | [7787] = {{Type = "item", Id = 19018}},
201 | [4881] = {{Type = "item", Id = 12564}},
202 | [2] = {{Type = "item", Id = 16305}},
203 | [7604] = {{Type = "item", Id = 18628}},
204 | [7650] = {{Type = "item", Id = 18770}},
205 | [7785] = {{Type = "item", Id = 19016}},
206 | [8183] = {{Type = "item", Id = 19802}},
207 | [8778] = {{Type = "item", Id = 21257}},
208 | [9695] = {{Type = "item", Id = 24228}},
209 | [11972] = {{Type = "item", Id = 35723}},
210 | [24] = {{Type = "item", Id = 16304}},
211 | [7649] = {{Type = "item", Id = 18769}},
212 | [8784] = {{Type = "item", Id = 21230}},
213 | [9175] = {{Type = "item", Id = 22597}},
214 | [9181] = {{Type = "item", Id = 22602}},
215 | [9535] = {{Type = "item", Id = 23797}},
216 | [9550] = {{Type = "item", Id = 23837}},
217 | [13043] = {{Type = "item", Id = 42772}},
218 | [14081] = {{Type = "item", Id = 46882}},
219 | [7504] = {{Type = "item", Id = 18362}},
220 | [7632] = {{Type = "item", Id = 18703}},
221 | [8471] = {{Type = "item", Id = 20742}},
222 | [8482] = {{Type = "item", Id = 20765}},
223 | [9985] = {{Type = "item", Id = 25706}},
224 | [12105] = {{Type = "item", Id = 36940}},
225 | [5805] = {{Type = "item", Id = 14646}},
226 | [5843] = {{Type = "item", Id = 14649}},
227 | [6681] = {{Type = "item", Id = 17126}},
228 | [8540] = {{Type = "item", Id = 20939}},
229 | [14086] = {{Type = "item", Id = 46880}},
230 | [830] = {{Type = "item", Id = 4881}},
231 | [1392] = {{Type = "item", Id = 6196}},
232 | [7499] = {{Type = "item", Id = 18357}},
233 | [7651] = {{Type = "item", Id = 18771}},
234 | [7783] = {{Type = "item", Id = 19002}},
235 | [8687] = {{Type = "item", Id = 21251}},
236 | [9120] = {{Type = "item", Id = 22520}},
237 | [11654] = {{Type = "item", Id = 34815}},
238 | [12168] = {{Type = "item", Id = 36958}},
239 | [14084] = {{Type = "item", Id = 46878}},
240 | [24745] = {{Type = "item", Id = 50320}},
241 | [1423] = {{Type = "item", Id = 6172}},
242 | [7500] = {{Type = "item", Id = 18358}},
243 | [8541] = {{Type = "item", Id = 20940}},
244 | [8501] = {{Type = "item", Id = 20941}},
245 | [9613] = {{Type = "item", Id = 23904}},
246 | [11186] = {{Type = "item", Id = 33115}},
247 | [11398] = {{Type = "item", Id = 33962}},
248 | [13817] = {{Type = "item", Id = 45857}},
249 | [14088] = {{Type = "item", Id = 46883}},
250 | [1642] = {{Type = "item", Id = 6775}},
251 | [5582] = {{Type = "item", Id = 13920}},
252 | [8547] = {{Type = "item", Id = 20938}},
253 | [8536] = {{Type = "item", Id = 21751}},
254 | [9250] = {{Type = "item", Id = 22727}},
255 | [8474] = {{Type = "item", Id = 23228}},
256 | [9576] = {{Type = "item", Id = 23870}},
257 | [9672] = {{Type = "item", Id = 24132}},
258 | [9984] = {{Type = "item", Id = 25705}},
259 | [11790] = {{Type = "item", Id = 35120}},
260 | [12979] = {{Type = "item", Id = 42203}},
261 | [1] = {{Type = "item", Id = 6497}},
262 | [7503] = {{Type = "item", Id = 18361}},
263 | [7505] = {{Type = "item", Id = 18363}},
264 | [8497] = {{Type = "item", Id = 20807}},
265 | [8737] = {{Type = "item", Id = 21245}},
266 | [9187] = {{Type = "item", Id = 22608}},
267 | [9205] = {{Type = "item", Id = 22623}},
268 | [9326] = {{Type = "item", Id = 23181}},
269 | [9520] = {{Type = "item", Id = 23777}},
270 | [11185] = {{Type = "item", Id = 33114}},
271 | [11237] = {{Type = "item", Id = 33289}},
272 | [11260] = {{Type = "item", Id = 33345}},
273 | [11452] = {{Type = "item", Id = 34090}},
274 | [12147] = {{Type = "item", Id = 36856}},
275 | [5847] = {{Type = "item", Id = 14651}},
276 | [7506] = {{Type = "item", Id = 18364}},
277 | [7944] = {{Type = "item", Id = 19443}},
278 | [8489] = {{Type = "item", Id = 20798}},
279 | [8738] = {{Type = "item", Id = 21166}},
280 | [8775] = {{Type = "item", Id = 21253}},
281 | [8804] = {{Type = "item", Id = 21378}},
282 | [9197] = {{Type = "item", Id = 22615}},
283 | [9201] = {{Type = "item", Id = 22620}},
284 | [9594] = {{Type = "item", Id = 23900}},
285 | [11189] = {{Type = "item", Id = 33121}},
286 | [11395] = {{Type = "item", Id = 33961}},
287 | [11453] = {{Type = "item", Id = 34091}},
288 | [12057] = {{Type = "item", Id = 36744}},
289 | [12419] = {{Type = "item", Id = 37833}},
290 | [12781] = {{Type = "item", Id = 39713}},
291 | [884] = {{Type = "item", Id = 5102}},
292 | [3482] = {{Type = "item", Id = 10590}},
293 | [4281] = {{Type = "item", Id = 11463}},
294 | [5841] = {{Type = "item", Id = 14647}},
295 | [6662] = {{Type = "item", Id = 17116}},
296 | [7945] = {{Type = "item", Id = 19452}},
297 | [8308] = {{Type = "item", Id = 20460}},
298 | [8338] = {{Type = "item", Id = 20483}},
299 | [8537] = {{Type = "item", Id = 20945}},
300 | [8777] = {{Type = "item", Id = 21256}},
301 | [8809] = {{Type = "item", Id = 21381}},
302 | [9182] = {{Type = "item", Id = 22603}},
303 | [9185] = {{Type = "item", Id = 22606}},
304 | [9325] = {{Type = "item", Id = 23180}},
305 | [9330] = {{Type = "item", Id = 23182}},
306 | [9360] = {{Type = "item", Id = 23249}},
307 | [9798] = {{Type = "item", Id = 24414}},
308 | [10152] = {{Type = "item", Id = 28114}},
309 | [11987] = {{Type = "item", Id = 35744}},
310 | [708] = {{Type = "item", Id = 4613}},
311 | [1646] = {{Type = "item", Id = 6916}},
312 | [5089] = {{Type = "item", Id = 12780}},
313 | [5842] = {{Type = "item", Id = 14648}},
314 | [6661] = {{Type = "item", Id = 17115}},
315 | [1155] = {{Type = "item", Id = 17409}},
316 | [8502] = {{Type = "item", Id = 20942}},
317 | [8498] = {{Type = "item", Id = 20943}},
318 | [8536] = {{Type = "item", Id = 20946}},
319 | [8535] = {{Type = "item", Id = 20947}},
320 | [8575] = {{Type = "item", Id = 20949}},
321 | [8739] = {{Type = "item", Id = 21167}},
322 | [8770] = {{Type = "item", Id = 21246}},
323 | [8771] = {{Type = "item", Id = 21247}},
324 | [8773] = {{Type = "item", Id = 21248}},
325 | [8772] = {{Type = "item", Id = 21250}},
326 | [8774] = {{Type = "item", Id = 21252}},
327 | [8776] = {{Type = "item", Id = 21255}},
328 | [8779] = {{Type = "item", Id = 21259}},
329 | [8781] = {{Type = "item", Id = 21260}},
330 | [8782] = {{Type = "item", Id = 21262}},
331 | [8780] = {{Type = "item", Id = 21263}},
332 | [8783] = {{Type = "item", Id = 21265}},
333 | [8805] = {{Type = "item", Id = 21379}},
334 | [8806] = {{Type = "item", Id = 21380}},
335 | [8808] = {{Type = "item", Id = 21384}},
336 | [8810] = {{Type = "item", Id = 21385}},
337 | [8829] = {{Type = "item", Id = 21514}},
338 | [8770] = {{Type = "item", Id = 21749}},
339 | [8771] = {{Type = "item", Id = 21750}},
340 | [9186] = {{Type = "item", Id = 22607}},
341 | [9188] = {{Type = "item", Id = 22609}},
342 | [9190] = {{Type = "item", Id = 22610}},
343 | [9204] = {{Type = "item", Id = 22622}},
344 | [9206] = {{Type = "item", Id = 22624}},
345 | [9324] = {{Type = "item", Id = 23179}},
346 | [9331] = {{Type = "item", Id = 23183}},
347 | [9332] = {{Type = "item", Id = 23184}},
348 | [11266] = {{Type = "item", Id = 33347}},
349 | [11935] = {{Type = "item", Id = 35568}},
350 | [11933] = {{Type = "item", Id = 35569}},
351 | [12067] = {{Type = "item", Id = 36756}},
352 | [12271] = {{Type = "item", Id = 37432}},
353 | [12423] = {{Type = "item", Id = 37830}},
354 | [14089] = {{Type = "item", Id = 46881}},
355 | [4903] = {{Type = "item", Id = 12563}},
356 | [5844] = {{Type = "item", Id = 14650}},
357 | [8740] = {{Type = "item", Id = 20944}},
358 | [8538] = {{Type = "item", Id = 20948}},
359 | [8534] = {{Type = "item", Id = 21165}},
360 | [8539] = {{Type = "item", Id = 21249}},
361 | [8785] = {{Type = "item", Id = 21258}},
362 | [8786] = {{Type = "item", Id = 21261}},
363 | [8807] = {{Type = "item", Id = 21382}},
364 | [9178] = {{Type = "item", Id = 22600}},
365 | [9179] = {{Type = "item", Id = 22601}},
366 | [9183] = {{Type = "item", Id = 22604}},
367 | [9184] = {{Type = "item", Id = 22605}},
368 | [9194] = {{Type = "item", Id = 22612}},
369 | [9200] = {{Type = "item", Id = 22617}},
370 | [9202] = {{Type = "item", Id = 22618}},
371 | [9278] = {{Type = "item", Id = 22888}},
372 | [10130] = {{Type = "item", Id = 28113}},
373 | [11249] = {{Type = "item", Id = 33314}},
374 | [12021] = {{Type = "item", Id = 35855}},
375 | [14160] = {{Type = "item", Id = 47246}},
376 | [14483] = {{Type = "item", Id = 49641}},
377 | [7937] = {{Type = "item", Id = 19423}},
378 | [8496] = {{Type = "item", Id = 20806}},
379 | [8787] = {{Type = "item", Id = 21264}},
380 | [9191] = {{Type = "item", Id = 22611}},
381 | [9195] = {{Type = "item", Id = 22613}},
382 | [9196] = {{Type = "item", Id = 22614}},
383 | [9198] = {{Type = "item", Id = 22616}},
384 | [9203] = {{Type = "item", Id = 22621}},
385 | [9233] = {{Type = "item", Id = 22719}},
386 | [12085] = {{Type = "item", Id = 36780}},
387 | }
388 |
--------------------------------------------------------------------------------