├── db ├── refloot-epoch.lua ├── enUS │ ├── professions-epoch.lua │ └── objects-epoch.lua ├── areatrigger-epoch.lua ├── minimap-epoch.lua └── quests-itemreq-epoch.lua ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── object-issue.yml │ ├── item-issue.yml │ ├── unit-issue.yml │ └── quest-issue.yml └── workflows │ └── auto_release_on_db_change.yml ├── .gitattributes ├── init ├── enUS-epoch.xml └── data-epoch.xml ├── pfQuest-epoch.toc ├── README.md ├── LICENSE ├── Contribute.md ├── Db.md ├── pfQuest-announce.lua ├── pfQuest-updater.lua ├── patchtable.lua └── pfQuest-worldmap.lua /db/refloot-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["refloot"]["data-epoch"] = { 2 | } 3 | -------------------------------------------------------------------------------- /db/enUS/professions-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["professions"]["enUS-epoch"] = { 2 | } 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: [] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /init/enUS-epoch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /init/data-epoch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /pfQuest-epoch.toc: -------------------------------------------------------------------------------- 1 | ## Interface: 30300 2 | ## Title: |cff33ffccpf|cffffffffQuest |cffcccccc[Project Epoch DB] 3 | ## Author: Bennylavaa 4 | # Contributors: Exuria, Shagu, snifflewow, skmd0, trav346 5 | ## Notes: A Project Epoch DB extension for pfQuest 6 | ## Version: 2.22.2 7 | ## Dependencies: pfQuest-wotlk 8 | ## SavedVariables: pfQuest_epochcount, pfQuest_CompletedQuestData 9 | 10 | init\data-epoch.xml 11 | init\enUS-epoch.xml 12 | 13 | overwrites.lua 14 | patchtable.lua 15 | pfQuest-updater.lua 16 | pfQuest-announce.lua 17 | pfQuest-worldmap.lua 18 | -------------------------------------------------------------------------------- /.github/workflows/auto_release_on_db_change.yml: -------------------------------------------------------------------------------- 1 | name: Release pfQuest-epoch on changes 2 | on: 3 | workflow_dispatch: 4 | permissions: 5 | contents: write 6 | actions: read 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Extract version from TOC file 15 | id: get_version 16 | run: | 17 | VERSION=$(grep "## Version:" pfQuest-epoch.toc | sed 's/## Version: //') 18 | echo "version=$VERSION" >> $GITHUB_OUTPUT 19 | echo "Found version: $VERSION" 20 | 21 | - name: Create pfQuest-epoch zip 22 | run: | 23 | mkdir pfQuest-epoch 24 | rsync -av --exclude='.git' --exclude='.github' --exclude='*.md' --exclude='LICENSE' --exclude='.gitattributes' --exclude='pfQuest-epoch' . pfQuest-epoch/ 25 | zip -r pfQuest-epoch.zip pfQuest-epoch/ 26 | 27 | - name: Create Versioned Release and Upload Asset 28 | uses: softprops/action-gh-release@v2 29 | with: 30 | tag_name: pfQuest-epoch-v${{ steps.get_version.outputs.version }} 31 | name: pfQuest-epoch v${{ steps.get_version.outputs.version }} 32 | files: pfQuest-epoch.zip 33 | make_latest: true 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/object-issue.yml: -------------------------------------------------------------------------------- 1 | name: Object Issue 2 | description: Report an issue with a game object 3 | title: "[Object] " 4 | labels: ["object", "bug"] 5 | body: 6 | - type: input 7 | id: object_id 8 | attributes: 9 | label: Object ID (If known) 10 | description: The ID of the object (optional if unknown) 11 | placeholder: "e.g., 1234" 12 | validations: 13 | required: false 14 | 15 | - type: input 16 | id: object_name 17 | attributes: 18 | label: Object Name 19 | description: The name of the object 20 | placeholder: "e.g., Chest of Booty" 21 | validations: 22 | required: true 23 | 24 | - type: input 25 | id: object_zone 26 | attributes: 27 | label: Object Zone 28 | description: The zone where the object is located 29 | placeholder: "e.g., The Barrens" 30 | validations: 31 | required: true 32 | 33 | - type: input 34 | id: object_coords 35 | attributes: 36 | label: Object Coordinates (X, Y) 37 | description: The X and Y coordinates of the object 38 | placeholder: "e.g., 52.3, 41.7" 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | id: issue_details 44 | attributes: 45 | label: Details of the Issue 46 | description: Please provide a detailed description of the issue 47 | placeholder: "Describe what's wrong with the object..." 48 | validations: 49 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/item-issue.yml: -------------------------------------------------------------------------------- 1 | name: Item Issue 2 | description: Report an issue with an item 3 | title: "[Item] " 4 | labels: ["item", "bug"] 5 | body: 6 | - type: input 7 | id: item_id 8 | attributes: 9 | label: Item ID 10 | description: The ID of the item 11 | placeholder: "e.g., 1234" 12 | validations: 13 | required: true 14 | 15 | - type: input 16 | id: item_name 17 | attributes: 18 | label: Item Name 19 | description: The name of the item 20 | placeholder: "e.g., Linen Cloth" 21 | validations: 22 | required: true 23 | 24 | - type: dropdown 25 | id: drops_from_type 26 | attributes: 27 | label: Drops From Type 28 | description: Type of entity that drops this item 29 | options: 30 | - Unit 31 | - Object 32 | - Nothing 33 | validations: 34 | required: true 35 | 36 | - type: input 37 | id: drops_from_id 38 | attributes: 39 | label: Drops From ID 40 | description: ID of the Unit or Object that drops this item 41 | placeholder: "e.g., 5678" 42 | validations: 43 | required: false 44 | 45 | - type: input 46 | id: drops_from_name 47 | attributes: 48 | label: Drops From Name 49 | description: Name of the Unit or Object that drops this item 50 | placeholder: "e.g., Hogger" 51 | validations: 52 | required: false 53 | 54 | - type: textarea 55 | id: issue_details 56 | attributes: 57 | label: Details of the Issue 58 | description: Please provide a detailed description of the issue 59 | placeholder: "Describe what's wrong with the item..." 60 | validations: 61 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/unit-issue.yml: -------------------------------------------------------------------------------- 1 | name: Unit Issue 2 | description: Report an issue with a unit (NPC/Mob) 3 | title: "[Unit] " 4 | labels: ["unit", "bug"] 5 | body: 6 | - type: input 7 | id: unit_id 8 | attributes: 9 | label: Unit ID 10 | description: The ID of the unit 11 | placeholder: "e.g., 1234" 12 | validations: 13 | required: true 14 | 15 | - type: input 16 | id: unit_name 17 | attributes: 18 | label: Unit Name 19 | description: The name of the unit 20 | placeholder: "e.g., Hogger" 21 | validations: 22 | required: true 23 | 24 | - type: dropdown 25 | id: unit_faction 26 | attributes: 27 | label: Unit Faction 28 | description: The faction of the unit 29 | options: 30 | - Horde 31 | - Alliance 32 | - Neutral 33 | validations: 34 | required: true 35 | 36 | - type: input 37 | id: unit_level 38 | attributes: 39 | label: Unit Level 40 | description: The level of the unit 41 | placeholder: "e.g., 10" 42 | validations: 43 | required: true 44 | 45 | - type: input 46 | id: unit_zone 47 | attributes: 48 | label: Unit Zone 49 | description: The zone where the unit is located 50 | placeholder: "e.g., Elwynn Forest" 51 | validations: 52 | required: true 53 | 54 | - type: input 55 | id: unit_coords 56 | attributes: 57 | label: Unit Coordinates (X, Y) 58 | description: The X and Y coordinates of the unit 59 | placeholder: "e.g., 45.2, 67.8" 60 | validations: 61 | required: true 62 | 63 | - type: textarea 64 | id: issue_details 65 | attributes: 66 | label: Details of the Issue 67 | description: Please provide a detailed description of the issue 68 | placeholder: "Describe what's wrong with the unit..." 69 | validations: 70 | required: true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pfQuest [Project Epoch DB] 2 | 3 | **NOTE: I do not give permission for this addon to be hosted in any servers launchers of any kind unless directly pulled from my repo and you must contact me before doing so.** 4 | 5 | An extension for [pfQuest-wotlk](https://github.com/shagu/pfQuest) which adds support for [Project Epoch](https://www.project-epoch.net/). 6 | The latest version of [pfQuest-wotlk](https://github.com/shagu/pfQuest) is required and only enUS-Gameclients are supported at the time. 7 | 8 | ## Installation 9 | pfQuest-epoch is dependant on pfQuest to work. 10 | 11 | 1. Download the latest release **[pfQuest-wotlk](https://github.com/shagu/pfQuest/releases/latest/download/pfQuest-full-wotlk.zip)** 12 | 2. Unzip it and place the "pfQuest-wotlk" folder into Wow-Directory\Interface\AddOns 13 | 3. Download the latest release **[pfQuest-epoch](https://github.com/Bennylavaa/pfQuest-epoch/releases/latest/download/pfQuest-epoch.zip)** 14 | 4. Unzip it and place the "pfQuest-epoch" folder into Wow-Directory\Interface\AddOns 15 | 16 | ## Contribute 17 | To my knowledge there is no way to automate the data gathering so I urge 18 | as many as possible to read the contribution guide and help as this is not 19 | a one man job. 20 | [How to contribute](Contribute.md) 21 | 22 | ## Progress 23 | 24 | [Google Sheet](https://docs.google.com/spreadsheets/d/1uTlB9E-YUPJxO96Kn9RCpvx76kmhvHEs-i7XV8rPpW8/edit?usp=sharing) 25 | 26 | ## License ![License](https://img.shields.io/badge/License-Custom-blue.svg) 27 | This project is licensed under a custom license that allows personal use and GitHub forks only. Redistribution or rehosting elsewhere is not permitted. See the [LICENSE](LICENSE) file for details. 28 | 29 | ## Special Thank You 30 | - Shagu: for making the base addon and creating pfQuest-turtle which this is based off of. 31 | - snifflewow: for the auto release workflow idea as well as the cleaned up readme documentation. 32 | - Exuria: for their countless hours spent contrubuting. 33 | - trav346: for the foundation of the overworld map markers. 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # pfQuest-epoch License 2 | 3 | Copyright (c) 2021 Eric Mauser (Shagu) 4 | Copyright (c) 2021-2025 Bennylavaa 5 | 6 | Permission is hereby granted to use this software for personal use only, subject to the following conditions: 7 | 8 | ## PERMITTED USES: 9 | - Personal use and modification of the software 10 | - Creating forks on GitHub for development and contribution purposes 11 | - Contributing improvements back to the original repository via pull requests 12 | - Using the software for personal World of Warcraft gameplay 13 | 14 | ## PROHIBITED USES: 15 | - Redistributing, rehosting, or republishing this software on any platform other than GitHub forks 16 | - Including this software in game launchers, modpacks, or any distribution collections 17 | - Commercial use, sale, or monetization of this software in any form 18 | - Claiming this work as your own or removing attribution 19 | - Hosting modified versions outside of the GitHub fork system 20 | 21 | ## FORK REQUIREMENTS: 22 | Any forks of this repository must: 23 | - Clearly indicate in the repository name and description that it is derived from pfQuest-epoch 24 | - Retain this license file and all copyright notices 25 | - Include a prominent link back to the original repository (https://github.com/Bennylavaa/pfQuest-epoch) 26 | - Not misrepresent the fork as the original or official version 27 | 28 | ## DISCLAIMER: 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ## ADDITIONAL NOTES: 32 | This license applies only to the pfQuest-epoch code and modifications. World of Warcraft and related assets remain the property of Blizzard Entertainment and are subject to their Terms of Service and End User License Agreement. 33 | -------------------------------------------------------------------------------- /db/areatrigger-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["areatrigger"]["data-epoch"] = { 2 | [2726] = { 3 | ["coords"] = { 4 | [1] = { 23.9, 68.4, 139 }, 5 | }, 6 | }, 7 | [10000] = { 8 | ["coords"] = { 9 | [1] = { 55.6, 35.2, 12 }, 10 | }, 11 | }, 12 | [4000000] = { --Revils Camp 13 | ["coords"] = { 14 | [1] = { 74.7, 62.8, 10 }, 15 | }, 16 | }, 17 | [4000001] = { --Manor Mistmantle 18 | ["coords"] = { 19 | [1] = { 77.1, 35.8, 10 }, 20 | }, 21 | }, 22 | [4000002] = { --Rolands Doom 23 | ["coords"] = { 24 | [1] = { 73.4, 84.3, 10 }, 25 | }, 26 | }, 27 | [4000003] = { --Blazing Hills 28 | ["coords"] = { 29 | [1] = { 78.91, 74.50, 45 }, 30 | }, 31 | }, 32 | [4000004] = { --Hillsbrad Foothills Central Tower 33 | ["coords"] = { 34 | [1] = { 55.60, 34.73, 267 }, 35 | }, 36 | }, 37 | [4000005] = { --Durnhold Escape 38 | ["coords"] = { 39 | [1] = { 76.47, 46.64, 267 }, 40 | }, 41 | }, 42 | [4000006] = { --Minkrik\'s Village 43 | ["coords"] = { 44 | [1] = { 61.5, 23.8, 17 }, 45 | }, 46 | }, 47 | [4000007] = { --Blazing Hills 48 | ["coords"] = { 49 | [1] = { 82.8, 57.3, 45 }, 50 | }, 51 | }, 52 | [4000008] = { --Stonetalon Caverns 53 | ["coords"] = { 54 | [1] = { 30.7, 48.3, 406 }, 55 | }, 56 | }, 57 | [4000009] = { --Shadowforge Excavation 58 | ["coords"] = { 59 | [1] = { 81.0, 33.1, 3 }, 60 | }, 61 | }, 62 | [4000010] = { --Centar Artifact 63 | ["coords"] = { 64 | [1] = { 42.15, 34.78, 400 }, 65 | }, 66 | }, 67 | [4000011] = { --Aru-Tails Site One 68 | ["coords"] = { 69 | [1] = { 38.8, 87.2, 490 }, 70 | }, 71 | }, 72 | [4000012] = { --Meeting Location 73 | ["coords"] = { 74 | [1] = { 67.1, 78.7, 85 }, 75 | }, 76 | }, 77 | [4000013] = { --Odor\'s Source 78 | ["coords"] = { 79 | [1] = { 60.6, 15.1, 618 }, 80 | }, 81 | }, 82 | [4000014] = { --Blackmaw Village Overlook 83 | ["coords"] = { 84 | [1] = { 52.4, 13.1, 16, 0 }, 85 | }, 86 | }, 87 | [4000015] = { --Tul\'Mari\'s Tomb 88 | ["coords"] = { 89 | [1] = { 46.3, 90.2, 440, 0 }, 90 | }, 91 | }, 92 | [4000016] = { --Northshore Mine 93 | ["coords"] = { 94 | [1] = { 22.4, 50.9, 85, 0 }, 95 | }, 96 | }, 97 | [4000017] = { --Stillwater Pond 98 | ["coords"] = { 99 | [1] = { 49.75, 52.0, 85, 0 }, 100 | }, 101 | }, 102 | } 103 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/quest-issue.yml: -------------------------------------------------------------------------------- 1 | name: Quest Issue 2 | description: Report an issue with a quest 3 | title: "[Quest] " 4 | labels: ["quest", "bug"] 5 | body: 6 | - type: input 7 | id: quest_id 8 | attributes: 9 | label: Quest ID 10 | description: The ID of the quest 11 | placeholder: "e.g., 1234" 12 | validations: 13 | required: true 14 | 15 | - type: input 16 | id: quest_name 17 | attributes: 18 | label: Quest Name 19 | description: The name of the quest 20 | placeholder: "e.g., The Missing Diplomat" 21 | validations: 22 | required: true 23 | 24 | - type: input 25 | id: starting_id 26 | attributes: 27 | label: Starting ID 28 | description: ID of the Unit, Object, or Item that starts the quest 29 | placeholder: "e.g., 5678" 30 | validations: 31 | required: true 32 | 33 | - type: input 34 | id: starting_name 35 | attributes: 36 | label: Starting Name 37 | description: Name of the Unit, Object, or Item that starts the quest 38 | placeholder: "e.g., Guard Thomas" 39 | validations: 40 | required: true 41 | 42 | - type: input 43 | id: ending_id 44 | attributes: 45 | label: Ending ID 46 | description: ID of the Unit, Object, or Item where the quest ends 47 | placeholder: "e.g., 9012" 48 | validations: 49 | required: true 50 | 51 | - type: input 52 | id: ending_name 53 | attributes: 54 | label: Ending Name 55 | description: Name of the Unit, Object, or Item where the quest ends 56 | placeholder: "e.g., Marshal Dughan" 57 | validations: 58 | required: true 59 | 60 | - type: dropdown 61 | id: objective_type 62 | attributes: 63 | label: Objective Type 64 | description: Type of objective for this quest 65 | options: 66 | - Item 67 | - Unit 68 | - Object 69 | - Nothing 70 | validations: 71 | required: true 72 | 73 | - type: input 74 | id: objective_id 75 | attributes: 76 | label: Objective ID 77 | description: ID of the objective (Item, Unit, or Object) 78 | placeholder: "e.g., 3456" 79 | validations: 80 | required: false 81 | 82 | - type: textarea 83 | id: issue_details 84 | attributes: 85 | label: Details of the Issue 86 | description: Please provide a detailed description of the issue 87 | placeholder: "Describe what's wrong with the quest..." 88 | validations: 89 | required: true -------------------------------------------------------------------------------- /Contribute.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | - [Introduction](#introduction) 4 | - [Macros](#macros) 5 | - [Example](#example) 6 | - [Issue](#issue) 7 | - [Pull request](#pull-request) 8 | 9 | 10 | --- 11 | 12 | ## Introduction 13 | 14 | To help include or improve data in this project you can either open an issue 15 | or create a pull request. 16 | 17 | Gather as much data as you can about the thing you want to add. 18 | For quests this will be, starting entity(npc or object or item), ending entity 19 | objectives, english quest text (see macros below). 20 | 21 | [Zone IDs](https://github.com/Bennylavaa/wowchat-epoch/blob/main/src/main/resources/pre_cata_areas.csv) 22 | 23 | ## Macros 24 | 25 | Your Current Cords: 26 | ```lua 27 | /script SetMapToCurrentZone() local x,y=GetPlayerMapPosition("player") DEFAULT_CHAT_FRAME:AddMessage(format("%s, %s: %.1f, %.1f",GetZoneText(),GetSubZoneText(),x*100,y*100)) 28 | ``` 29 | 30 | 31 | Targeted Unit Information: 32 | 33 | ```lua 34 | /run local guid = UnitGUID("target"); local npcId = tonumber(string.sub(guid, 8, 12), 16); local npcName = UnitName("target"); print("NPC ID:", npcId, "NPC Name:", npcName) 35 | ``` 36 | 37 | Selected QuestLog Data: 38 | 39 | ```lua 40 | /run local t, l, _, _, _, _, _, _, i = GetQuestLogTitle(GetQuestLogSelection()); print("\nID:"..i.."\nLevel:"..l.."\n[\"T\"] "..t.."\n[\"O\"] "..QuestInfoObjectivesText:GetText().."\n[\"D\"] "..QuestInfoDescriptionText:GetText()) 41 | ``` 42 | 43 | Hover Over Item ID: 44 | ```lua 45 | /run local _, link = GameTooltip:GetItem(); if link then local itemID = tonumber(link:match("item:(%d+):")); if itemID then print("Item ID:", itemID) end end 46 | ``` 47 | 48 | Object ID: 49 | If you know of a way to extract this from the client make an issue 50 | 51 | ## Example 52 | Demo quest commit: [Do Slavers Keep Records? 53 | ](https://github.com/Bennylavaa/pfQuest-epoch/commit/39abc567413a0c004ea22ec38fed4eb2e486e9d6) 54 | 55 | ### Issue 56 | First search in issues to see if there already is one for whatever addition you 57 | want to report. 58 | Gather as much information you can about what you want to add. 59 | This can be npc names, items, quest objectives, coordinates, zone, and so on. 60 | You can use the macros from the section above to help. 61 | Make a issue over at [issues](https://github.com/Bennylavaa/pfQuest-epoch/issues) 62 | 63 | ### Pull request 64 | If you have the technical knowhow to edit and make a pull request then please 65 | checkout the [db spec](Db.md) to learn more about the structure of the addon. 66 | 67 | Note that not all id's are included in this project. Try to make sure the 68 | things you are adding like objects/items/npcs are not in the original pfquest adddon 69 | since they exist in vanilla. 70 | -------------------------------------------------------------------------------- /db/minimap-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["minimap-epoch"] = { 2 | [0] = { 36799.8, 24533.2 }, 3 | [1] = { 4925.0, 3283.34 }, 4 | [3] = { 2487.5, 1658.34 }, 5 | [4] = { 3350.0, 2233.3 }, 6 | [8] = { 2293.75, 1529.17 }, 7 | [10] = { 2700.003, 1800.03 }, 8 | [11] = { 4135.417, 2756.25 }, 9 | [12] = { 3470.84, 2314.62 }, 10 | [14] = { 5287.5, 3525.0 }, 11 | [15] = { 5250.0, 3500.0 }, 12 | [16] = { 5070.84, 3381.25 }, 13 | [17] = { 10133.34, 6756.25 }, 14 | [28] = { 4299.997, 2866.67 }, 15 | [33] = { 6381.25, 4254.1 }, 16 | [36] = { 2800.003, 1866.667 }, 17 | [38] = { 2758.33, 1839.58 }, 18 | [40] = { 3500.003, 2333.3 }, 19 | [41] = { 2499.997, 1666.63 }, 20 | [44] = { 2170.84, 1447.9 }, 21 | [45] = { 3600.003, 2399.997 }, 22 | [46] = { 2929.163, 1952.08 }, 23 | [47] = { 3850.0, 2566.67 }, 24 | [51] = { 2231.253, 1487.5 }, 25 | [65] = { 5608.33, 3739.58 }, 26 | [66] = { 4993.75, 3329.17 }, 27 | [67] = { 7112.5, 4741.65 }, 28 | [85] = { 4518.75, 3012.5 }, 29 | [130] = { 4200.0, 2800.0 }, 30 | [139] = { 4031.25, 2687.5 }, 31 | [141] = { 5091.66, 3393.7 }, 32 | [148] = { 6550.0, 4366.66 }, 33 | [206] = { 0.0, 0.0 }, 34 | [210] = { 6270.833, 4181.25 }, 35 | [215] = { 5137.5, 3425.003 }, 36 | [267] = { 3200.0, 2133.33 }, 37 | [331] = { 5766.67, 3843.753 }, 38 | [357] = { 6950.0, 4633.33 }, 39 | [361] = { 5750.0, 3833.33 }, 40 | [394] = { 5250.0, 3500.0 }, 41 | [400] = { 4399.997, 2933.33 }, 42 | [405] = { 4495.83, 2997.913 }, 43 | [406] = { 4883.33, 3256.253 }, 44 | [440] = { 6900.0, 4600.0 }, 45 | [490] = { 3700.003, 2466.66 }, 46 | [493] = { 2308.33, 1539.59 }, 47 | [495] = { 6045.83, 4031.253 }, 48 | [618] = { 7100.003, 4733.33 }, 49 | [1196] = { 6550.0, 4366.67 }, 50 | [1377] = { 3483.334, 2322.92 }, 51 | [1497] = { 959.3754, 640.1 }, 52 | [1519] = { 1737.5033, 1158.34 }, 53 | [1537] = { 790.629, 527.61 }, 54 | [1637] = { 1402.61, 935.42 }, 55 | [1638] = { 1043.75, 695.83 }, 56 | [1657] = { 1058.33, 705.71 }, 57 | [2597] = { 4237.5, 2825.0 }, 58 | [2817] = { 2722.92, 1814.58 }, 59 | [3277] = { 1145.837, 764.58 }, 60 | [3358] = { 1756.247, 1170.83 }, 61 | [3456] = { 1856.25, 1237.5 }, 62 | [3483] = { 5164.58, 3443.75 }, 63 | [3518] = { 5524.97, 3683.3367 }, 64 | [3519] = { 5400.0, 3600.0 }, 65 | [3520] = { 5500.0, 3666.66 }, 66 | [3521] = { 5027.08, 3352.09 }, 67 | [3522] = { 5425.0, 3616.663 }, 68 | [3523] = { 5574.9967, 3716.67 }, 69 | [3537] = { 5764.58, 3843.75 }, 70 | [3703] = { 1306.25, 870.84 }, 71 | [3711] = { 4356.25, 2904.17 }, 72 | [3820] = { 2270.837, 1514.58 }, 73 | [4100] = { 1824.997, 1216.67 }, 74 | [4196] = { 627.087, 418.75 }, 75 | [4197] = { 2975.0, 1983.34 }, 76 | [4228] = { 2600.0, 1733.333 }, 77 | [4264] = { 3400.003, 2266.6667 }, 78 | [4265] = { 0.0, 0.0 }, 79 | [4272] = { 3400.0, 2266.6667 }, 80 | [4273] = { 3287.5, 2191.67 }, 81 | [4277] = { 1072.9133, 714.584 }, 82 | [4298] = { 3162.5, 2108.333 }, 83 | [4384] = { 1743.75, 1162.497 }, 84 | [4395] = { 0.0, 0.0 }, 85 | [4415] = { 383.333, 256.25 }, 86 | [4416] = { 1143.753, 762.5 }, 87 | [4493] = { 1162.4967, 775.0 }, 88 | [4494] = { 972.917, 647.917 }, 89 | [4500] = { 3400.003, 2266.6667 }, 90 | [4603] = { 2600.0, 1733.33 }, 91 | [4710] = { 2650.0, 1766.6633 }, 92 | [4722] = { 2600.0, 1733.333 }, 93 | [4723] = { 2600.0, 1733.333 }, 94 | [4742] = { 3677.087, 2452.03 }, 95 | [4809] = { 11400.0, 7600.0 }, 96 | [4812] = { 12200.0, 8133.33 }, 97 | [4813] = { 1533.333, 1022.917 }, 98 | [4820] = { 13000.0, 8666.67 }, 99 | [4987] = { 752.083, 502.09 }, 100 | [5016] = { 1378.95, 911.12 }, 101 | } 102 | -------------------------------------------------------------------------------- /Db.md: -------------------------------------------------------------------------------- 1 | # Db Structure 2 | 3 | **Short explanation of each file:** 4 | 5 | - **fac** -- Faction by letter: A (Alliance), H (Horde), AH (both) 6 | - **U** -- Unit 7 | - **O** -- Object 8 | - **I** -- Item 9 | - **IR** -- Itemreq, see quests-itemreq-epoch 10 | 11 | --- 12 | 13 | ## `db/` 14 | 15 | ### `quests-epoch.lua` 16 | Contains quest information, including start and end points, level, and the next quest in a chain. 17 | Not all fields are mandatory. 18 | 19 | **Format:** 20 | ```lua 21 | [QUESTID#] = { 22 | ["start"] = { 23 | ["U"] = { UNITID# }, -- Quest giver unit ID 24 | }, 25 | ["end"] = { 26 | ["U"] = { UNITID# }, -- Quest turn-in unit ID 27 | }, 28 | ["lvl"] = quest_level, -- Recommended quest level 29 | ["min"] = min_level, -- Minimum level to accept 30 | ["next"] = next_quest_id, -- Next quest in the chain 31 | ["pre"] = prev_quest_id, -- Previous quest in the chain 32 | ["close"] = { conflicting_quest_ids }, -- Quests that cannot be taken together (e.g., profession specializations) 33 | ["skill"] = skill_id, -- Required profession/skill ID (e.g., 165 (Leatherworking)) 34 | ["race"] = race_requirement, -- Race bitflag, advanced see pfQuest code https://github.com/shagu/pfQuest/blob/104f35678ca39ab1fb78b655f815cc7016f5e0c8/database.lua#L333 35 | ["class"] = class_requirement, -- see https://github.com/shagu/pfQuest/blob/104f35678ca39ab1fb78b655f815cc7016f5e0c8/database.lua#L351 36 | ["event"] = event_id, -- Event id 37 | ["obj"] = { -- Quest objectives 38 | ["I"] = { item_ids_to_collect }, -- Items to collect 39 | ["U"] = { unit_ids_to_kill }, -- Units to kill 40 | ["O"] = { object_ids }, -- Objects to interact with 41 | ["IR"] = { item_req_ids }, -- see quests-itemreq-epoch.lua 42 | }, 43 | } 44 | ``` 45 | 46 | **Example:** 47 | ```lua 48 | [27246] = { 49 | ["start"] = { 50 | ["U"] = { 14624 }, 51 | }, 52 | ["end"] = { 53 | ["U"] = { 46164 }, 54 | }, 55 | ["obj"] = { 56 | ["I"] = { 8165, 8203, 8204 }, 57 | }, 58 | ["lvl"] = 50, 59 | ["min"] = 50, 60 | ["next"] = 27242, 61 | }, 62 | ``` 63 | 64 | --- 65 | 66 | ### `units-epoch.lua` 67 | Contains unit information such as level, ID, coordinates, and faction. 68 | Both quest NPCs and mobs are in this file. 69 | 70 | **Format:** 71 | ```lua 72 | [UNITID#] = { 73 | ["coords"] = { -- List of spawn locations and respawn timer 74 | [1] = { x, y, zoneid, respawn }, 75 | [2] = { x, y, zoneid, respawn }, 76 | }, 77 | ["fac"] = faction, 78 | ["lvl"] = level, -- can be ranges like 21-23 79 | ["rnk"] = rank -- 1 = elite?, 2 = rare elite?, no rank = normal mob 80 | }, 81 | ``` 82 | 83 | **Example:** 84 | ```lua 85 | [61100] = { 86 | ["coords"] = { 87 | [1] = { 96.5, 66.8, 440, 280 }, 88 | [2] = { 52.7, 80.7, 5121, 280 }, 89 | }, 90 | ["fac"] = "AH", 91 | ["lvl"] = "55", 92 | }, 93 | ``` 94 | 95 | --- 96 | 97 | ### `items-epoch.lua` 98 | Contains item information, including which units (`"U"`) and objects (`"O"`) they drop from. An item can drop from multiple units or objects. 99 | 100 | **Format:** 101 | ```lua 102 | [ITEMID#] = { 103 | ["O"] = { -- object 104 | [OBJECTID#] = drop%number, -- first object it drops from 105 | [OBJECTID#] = drop%number, -- second object it drops from 106 | }, 107 | ["U"] = { -- unit 108 | [UNITID#] = drop%number, -- first NPC it drops from 109 | [UNITID#] = drop%number, -- second NPC it drops from 110 | }, 111 | ["V"] = { -- vendor 112 | [UNITID#] = 0, 113 | } 114 | }, 115 | ``` 116 | 117 | **Example:** 118 | ```lua 119 | [4606] = { 120 | ["O"] = { 121 | [153462] = 4.17, 122 | }, 123 | ["U"] = { 124 | [3] = 4.73, 125 | [48] = 4.55, 126 | }, 127 | }, 128 | ``` 129 | 130 | --- 131 | 132 | ### `areatrigger-epoch.lua` 133 | Contains area trigger locations for exploration quests. 134 | 135 | **Format:** 136 | ```lua 137 | [AREATRIGGERID#] = { 138 | ["coords"] = { 139 | [1] = { x, y, zoneid }, 140 | }, 141 | }, 142 | ``` 143 | 144 | **Example:** 145 | ```lua 146 | [1] = { 147 | ["coords"] = { 148 | [1] = { 35.8, 62.1, 11 }, 149 | }, 150 | }, 151 | ``` 152 | 153 | --- 154 | 155 | ### `objects-epoch.lua` 156 | Contains object information, such as coordinates and ID. 157 | 158 | **Format:** 159 | ```lua 160 | [OBJECTID#] = { 161 | ["coords"] = { 162 | [1] = { x, y, zoneid, respawn }, 163 | }, 164 | ["fac"] = faction_letters 165 | }, 166 | ``` 167 | 168 | **Example:** 169 | ```lua 170 | [34] = { 171 | ["coords"] = { 172 | [1] = { 40.6, 17, 40, 2700 }, 173 | }, 174 | }, 175 | ``` 176 | 177 | --- 178 | 179 | ### `meta-epoch.lua` 180 | Contains meta-relations, such as lists of game objects that are ores, unit IDs that are flight points, etc. 181 | 182 | **Format:** 183 | ```lua 184 | ["chests"] = { 185 | [-CHESTID#] = 0, 186 | }, 187 | ["flight"] = { 188 | [UNITID#] = "FACTIONLETTER", 189 | [UNITID#] = "FACTIONLETTER", 190 | [UNITID#] = "FACTIONLETTER", 191 | }, 192 | ["rares"] = { 193 | [UNITID#] = LEVEL, 194 | [UNITID#] = LEVEL, 195 | }, 196 | ``` 197 | 198 | **Example:** 199 | ```lua 200 | ["chests"] = { 201 | [-3000248] = 0, 202 | [-3000247] = 0, 203 | }, 204 | ["flight"] = { 205 | [352] = "A", 206 | [1233] = "AH", 207 | [1387] = "H", 208 | }, 209 | ["rares"] = { 210 | [61] = 11, 211 | [79] = 10, 212 | }, 213 | ``` 214 | 215 | --- 216 | 217 | ### `minimap-epoch.lua` 218 | Contains minimap scale factors for specific areas, which helps to correctly display dots on the minimap inside buildings. 219 | 220 | **Format:** 221 | ```lua 222 | [MAPID] = { xsize, ysize }, 223 | ``` 224 | 225 | **Example:** 226 | ```lua 227 | [25] = { 711.56, 468.68 }, 228 | ``` 229 | 230 | --- 231 | 232 | ### `quests-itemreq-epoch.lua` 233 | Contains a list of items required for a quest that are usable in the floating quest log UI. 234 | 235 | ### `refloot-epoch.lua` 236 | Contains item requirements for specific quests, such as listing all anvil objects for the "Broken Tools" quest. 237 | 238 | ### `zones-epoch.lua` 239 | Contains information about zones and their positions on maps, as well as the maps themselves. 240 | *(No format or example provided in the original text)* 241 | 242 | --- 243 | 244 | ## `enUS/` (Localization Folder) 245 | 246 | ### `items-epoch.lua` 247 | Contains item IDs and their corresponding names. 248 | 249 | **Format:** 250 | ```lua 251 | [ITEMID#] = ITEMNAME, 252 | ``` 253 | 254 | --- 255 | 256 | ### `objects-epoch.lua` 257 | Contains object IDs and their corresponding names. 258 | 259 | **Format:** 260 | ```lua 261 | [OBJECTID#] = OBJECTNAME, 262 | ``` 263 | 264 | --- 265 | 266 | ### `professions-epoch.lua` 267 | Contains IDs and names for custom professions only. 268 | 269 | **Format:** 270 | ```lua 271 | [PROFESSIONID#] = PROFESSIONNAME, 272 | ``` 273 | 274 | --- 275 | 276 | ### `quests-epoch.lua` 277 | Contains quest IDs and their title, objective, and description. 278 | 279 | **Format:** 280 | ```lua 281 | [QUESTID#] = { 282 | ["T"] = quest_title 283 | ["O"] = quest_ojective, 284 | ["D"] = quest_description, 285 | }, 286 | ``` 287 | 288 | --- 289 | 290 | ### `units-epoch.lua` 291 | Contains unit IDs and their corresponding names. 292 | 293 | **Format:** 294 | ```lua 295 | [UNITID#] = unit_name, 296 | ``` 297 | 298 | --- 299 | 300 | ### `zones-epoch.lua` 301 | Contains zone IDs and their corresponding names. 302 | 303 | **Format:** 304 | ```lua 305 | [ZONEID#] = zone_name, 306 | ``` 307 | -------------------------------------------------------------------------------- /pfQuest-announce.lua: -------------------------------------------------------------------------------- 1 | local questAnnounceFrame = CreateFrame("Frame", "pfQuestEpochAnnounce") 2 | questAnnounceFrame:RegisterEvent("UI_INFO_MESSAGE") 3 | questAnnounceFrame:SetScript("OnEvent", function(self, event, message) 4 | if event == "UI_INFO_MESSAGE" and message then 5 | pfQuestEpoch_OnQuestUpdate(message) 6 | end 7 | end) 8 | 9 | function pfQuestEpoch_OnQuestUpdate(message) 10 | if GetNumPartyMembers() == 0 then 11 | return 12 | end 13 | 14 | if not message or type(message) ~= "string" then 15 | return 16 | end 17 | 18 | local itemName, numItems, numNeeded = string.match(message, "(.*):%s*([-%d]+)%s*/%s*([-%d]+)%s*$") 19 | 20 | if itemName and numItems and numNeeded then 21 | local iNumItems = tonumber(numItems) 22 | local iNumNeeded = tonumber(numNeeded) 23 | local stillNeeded = iNumNeeded - iNumItems 24 | local questName = pfQuestEpoch_GetQuestNameForObjective(itemName) 25 | local outMessage 26 | 27 | if stillNeeded < 1 then 28 | if pfQuest_config["epochannounceFinished"] == "1" then 29 | if questName then 30 | outMessage = "Finished " .. questName .. "." 31 | else 32 | outMessage = "I have finished " .. itemName .. "." 33 | end 34 | end 35 | else 36 | if pfQuest_config["epochannounceRemaining"] == "1" then 37 | if questName then 38 | outMessage = "" .. itemName .. " for " .. questName .. " (" .. stillNeeded .. " left)" 39 | else 40 | outMessage = "" .. itemName .. " (" .. stillNeeded .. " left)" 41 | end 42 | end 43 | end 44 | 45 | if outMessage and outMessage ~= "" then 46 | SendChatMessage(outMessage, "PARTY") 47 | end 48 | end 49 | end 50 | 51 | function pfQuestEpoch_GetQuestNameForObjective(objectiveName) 52 | local numQuestLogEntries = GetNumQuestLogEntries() 53 | 54 | for i = 1, numQuestLogEntries do 55 | local questTitle, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete = GetQuestLogTitle(i) 56 | 57 | if not isHeader and questTitle then 58 | SelectQuestLogEntry(i) 59 | local numObjectives = GetNumQuestLeaderBoards() 60 | 61 | for j = 1, numObjectives do 62 | local description, type, finished = GetQuestLogLeaderBoard(j) 63 | 64 | if description then 65 | local objName = string.match(description, "(.*):%s*[-%d]+%s*/%s*[-%d]+%s*$") 66 | 67 | if objName and string.find(string.lower(objName), string.lower(objectiveName), 1, true) then 68 | return GetQuestLink(i) 69 | end 70 | end 71 | end 72 | end 73 | end 74 | 75 | return nil 76 | end 77 | 78 | local function ExtendPfQuestConfig() 79 | if not pfQuest_defconfig or not pfQuestConfig then 80 | return false 81 | end 82 | 83 | local foundHeader, foundFinished, foundRemaining = false, false, false 84 | for _, entry in pairs(pfQuest_defconfig) do 85 | if entry.text == "Announce" and entry.type == "header" then 86 | foundHeader = true 87 | elseif entry.config == "epochannounceFinished" then 88 | foundFinished = true 89 | elseif entry.config == "epochannounceRemaining" then 90 | foundRemaining = true 91 | end 92 | end 93 | 94 | if foundHeader and foundFinished and foundRemaining then 95 | return true 96 | end 97 | 98 | if not foundHeader then 99 | table.insert(pfQuest_defconfig, { 100 | text = "Announce", 101 | default = nil, 102 | type = "header" 103 | }) 104 | end 105 | 106 | if not foundFinished then 107 | table.insert(pfQuest_defconfig, { 108 | text = "Announce Finished Quest Objectives", 109 | default = "0", 110 | type = "checkbox", 111 | config = "epochannounceFinished" 112 | }) 113 | end 114 | 115 | if not foundRemaining then 116 | table.insert(pfQuest_defconfig, { 117 | text = "Announce Remaining Quest Objectives", 118 | default = "0", 119 | type = "checkbox", 120 | config = "epochannounceRemaining" 121 | }) 122 | end 123 | 124 | if not pfQuest_config["epochannounceFinished"] then 125 | pfQuest_config["epochannounceFinished"] = "0" 126 | end 127 | if not pfQuest_config["epochannounceRemaining"] then 128 | pfQuest_config["epochannounceRemaining"] = "0" 129 | end 130 | 131 | if pfQuestConfig.CreateConfigEntries then 132 | for i = 1, 50 do 133 | local frame = getglobal("pfQuestConfig" .. i) 134 | if frame then 135 | frame:Hide() 136 | frame:SetParent(nil) 137 | else 138 | break 139 | end 140 | end 141 | 142 | pfQuestConfig.vpos = 40 143 | 144 | pfQuestConfig:CreateConfigEntries(pfQuest_defconfig) 145 | end 146 | 147 | return true 148 | end 149 | 150 | local function CheckAndHandleVersionUpdate() 151 | -- Only disable on version 2.22.1, one time only 152 | local currentVersion = GetAddOnMetadata("pfQuest-epoch", "Version") or "0.0.0" 153 | 154 | if currentVersion == "2.22.1" and not pfQuest_config["epochannounceForcedDisableOnce"] then 155 | pfQuest_config["epochannounceFinished"] = "0" 156 | pfQuest_config["epochannounceForcedDisableOnce"] = "1" 157 | return true 158 | end 159 | 160 | return false 161 | end 162 | 163 | local configExtenderFrame = CreateFrame("Frame") 164 | configExtenderFrame:RegisterEvent("ADDON_LOADED") 165 | configExtenderFrame:SetScript("OnEvent", function(self, event, addonName) 166 | if addonName == "pfQuest-epoch" then 167 | local timer = 0 168 | self:SetScript("OnUpdate", function() 169 | timer = timer + 1 170 | if timer > 10 then 171 | if ExtendPfQuestConfig() then 172 | local versionUpdated = CheckAndHandleVersionUpdate() 173 | if versionUpdated then 174 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Updated - Finished quest announcements have been disabled. Re-enable manually if desired.") 175 | end 176 | self:SetScript("OnUpdate", nil) 177 | self:UnregisterAllEvents() 178 | elseif timer > 300 then 179 | self:SetScript("OnUpdate", nil) 180 | self:UnregisterAllEvents() 181 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Config integration failed") 182 | end 183 | end 184 | end) 185 | end 186 | end) 187 | 188 | local function SetupAnnounceCommands() 189 | local currentHandler = SlashCmdList["PFDB"] 190 | 191 | SlashCmdList["PFDB"] = function(input, editbox) 192 | local commandlist = {} 193 | local command 194 | 195 | local compat = pfQuestCompat 196 | if compat and compat.gfind then 197 | for command in compat.gfind(input, "[^ ]+") do 198 | table.insert(commandlist, command) 199 | end 200 | else 201 | for word in string.gmatch(input, "[^%s]+") do 202 | table.insert(commandlist, word) 203 | end 204 | end 205 | 206 | local arg1 = commandlist[1] and string.lower(commandlist[1]) 207 | local arg2 = commandlist[2] and string.lower(commandlist[2]) 208 | local arg3 = commandlist[3] and string.lower(commandlist[3]) 209 | 210 | if arg1 == "announce" then 211 | if arg2 == "finished" then 212 | if arg3 == "on" then 213 | pfQuest_config["epochannounceFinished"] = "1" 214 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Finished quest announcements enabled") 215 | return 216 | elseif arg3 == "off" then 217 | pfQuest_config["epochannounceFinished"] = "0" 218 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Finished quest announcements disabled") 219 | return 220 | end 221 | elseif arg2 == "remaining" then 222 | if arg3 == "on" then 223 | pfQuest_config["epochannounceRemaining"] = "1" 224 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Remaining quest announcements enabled") 225 | return 226 | elseif arg3 == "off" then 227 | pfQuest_config["epochannounceRemaining"] = "0" 228 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest |cffcccccc[Epoch]|r: Remaining quest announcements disabled") 229 | return 230 | end 231 | end 232 | end 233 | 234 | if currentHandler then 235 | currentHandler(input, editbox) 236 | end 237 | end 238 | end 239 | 240 | local commandSetupFrame = CreateFrame("Frame") 241 | commandSetupFrame:RegisterEvent("PLAYER_ENTERING_WORLD") 242 | commandSetupFrame:SetScript("OnEvent", function() 243 | local timer = 0 244 | this:SetScript("OnUpdate", function() 245 | timer = timer + 1 246 | if timer > 60 then 247 | SetupAnnounceCommands() 248 | this:SetScript("OnUpdate", nil) 249 | this:UnregisterAllEvents() 250 | end 251 | end) 252 | end) -------------------------------------------------------------------------------- /db/quests-itemreq-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["quests-itemreq"]["data-epoch"] = { 2 | [6436] = { 3 | [4663] = "7914", 4 | [4664] = "7914", 5 | [4665] = "7914", 6 | [4666] = "7914", 7 | [4667] = "7914", 8 | }, 9 | [6635] = { 10 | [-100035] = "8202", 11 | [-63674] = "8202", 12 | [100028] = "8202", 13 | [101749] = "8202", 14 | [-5000041] = 0, 15 | }, 16 | [7866] = { 17 | [2726] = 0, 18 | }, 19 | [9466] = { 20 | [-144050] = "11757", 21 | }, 22 | [9618] = { 23 | [5268] = 0, 24 | [5286] = 0, 25 | }, 26 | [12815] = { 27 | [10902] = 0, 28 | [10903] = 0, 29 | [10904] = 0, 30 | [10905] = 0, 31 | }, 32 | [10687] = { 33 | [-152598] = 0, 34 | }, 35 | [10688] = { 36 | [-152604] = 0, 37 | }, 38 | [10689] = { 39 | [-152605] = 0, 40 | }, 41 | [10690] = { 42 | [-152606] = 0, 43 | }, 44 | [11914] = { 45 | [7086] = 0, 46 | }, 47 | [11948] = { 48 | [7092] = 0, 49 | }, 50 | [11953] = { 51 | [6559] = 0, 52 | [6556] = 0, 53 | }, 54 | [15766] = { 55 | [-177673] = 0, 56 | }, 57 | [15826] = { 58 | [12296] = 0, 59 | }, 60 | [60767] = { 61 | [-250073] = 0, 62 | }, 63 | [62811] = { 64 | [-250365] = 0, 65 | }, 66 | [62596] = { 67 | [45776] = 0, 68 | }, 69 | [62590] = { 70 | [-250316] = 0, 71 | }, 72 | [62605] = { 73 | [-250320] = 0, 74 | }, 75 | [62531] = { 76 | [-250286] = 0, 77 | }, 78 | [62498] = { 79 | [45694] = 0, 80 | }, 81 | [62875] = { 82 | [45955] = 0, 83 | }, 84 | [62885] = { 85 | [45969] = 0, 86 | }, 87 | [62880] = { 88 | [-250604] = 0, 89 | [-250605] = 0, 90 | [-250606] = 0, 91 | }, 92 | [62331] = { 93 | [45552] = 0, 94 | }, 95 | [62333] = { 96 | [-250224] = 0, 97 | }, 98 | [62335] = { 99 | [45557] = 0, 100 | }, 101 | [62336] = { 102 | [45568] = 0, 103 | [45569] = 0, 104 | [45570] = 0, 105 | }, 106 | [62655] = { 107 | [-250594] = 0, 108 | }, 109 | [62667] = { 110 | [-250332] = 0, 111 | }, 112 | [62482] = { 113 | [12047] = 0, 114 | [13076] = 0, 115 | }, 116 | [62484] = { 117 | [-5000002] = 0, 118 | }, 119 | [62488] = { 120 | [-250584] = 0, 121 | }, 122 | [62487] = { 123 | [-250263] = 0, 124 | }, 125 | [62625] = { 126 | [45801] = 0, 127 | }, 128 | [62641] = { 129 | [-250330] = 0, 130 | }, 131 | [63278] = { 132 | [-250473] = 0, 133 | }, 134 | [63277] = { 135 | [-250471] = 0, 136 | }, 137 | [63355] = { 138 | [-250499] = 0, 139 | }, 140 | [63367] = { 141 | [46270] = 0, 142 | }, 143 | [62783] = { 144 | [45875] = 0, 145 | [45876] = 0, 146 | [11198] = 0, 147 | [45878] = 0, 148 | [45881] = 0, 149 | [1776] = 0, 150 | [11874] = 0, 151 | }, 152 | [63138] = { 153 | [3130] = 0, 154 | [3131] = 0, 155 | }, 156 | [63052] = { 157 | [-5000003] = 0, 158 | }, 159 | [63065] = { 160 | [7452] = 0, 161 | [7453] = 0, 162 | [7454] = 0, 163 | }, 164 | [62403] = { 165 | [45610] = 0, 166 | }, 167 | [63207] = { 168 | [-250491] = 0, 169 | }, 170 | [62720] = { 171 | [-5000037] = 0, 172 | }, 173 | [63044] = { 174 | [3240] = 0, 175 | [3239] = 0, 176 | }, 177 | [63108] = { 178 | [46080] = 0, 179 | }, 180 | [63109] = { 181 | [4664] = 0, 182 | [4663] = 0, 183 | [4666] = 0, 184 | [4665] = 0, 185 | }, 186 | [62632] = { 187 | [-250594] = 0, 188 | }, 189 | [62903] = { 190 | [-5000038] = 0, 191 | }, 192 | [62938] = { 193 | [-250392] = 0, 194 | }, 195 | [63163] = { 196 | [46119] = 0, 197 | }, 198 | [62895] = { 199 | [12856] = 0, 200 | }, 201 | [62896] = { 202 | [45987] = 0, 203 | }, 204 | [60106] = { 205 | [46116] = 0, 206 | }, 207 | [60090] = { 208 | [2657] = 0, 209 | }, 210 | [60086] = { 211 | [5635] = 0, 212 | }, 213 | [62760] = { 214 | [-250350] = 0, 215 | }, 216 | [60094] = { 217 | [2505] = 0, 218 | }, 219 | [63334] = { 220 | [46238] = 0, 221 | }, 222 | [63335] = { 223 | [-250451] = 0, 224 | }, 225 | [63338] = { 226 | [-250492] = 0, 227 | }, 228 | [63311] = { 229 | [-250487] = 0, 230 | }, 231 | [63344] = { 232 | [46249] = 0, 233 | }, 234 | [63216] = { 235 | [-5000050] = 0, 236 | }, 237 | [63288] = { 238 | [46208] = 0, 239 | }, 240 | [63289] = { 241 | [5456] = 0, 242 | [5457] = 0, 243 | }, 244 | [63281] = { 245 | [-250475] = 0, 246 | }, 247 | [63296] = { 248 | [-250478] = 0, 249 | }, 250 | [63340] = { 251 | [6517] = 0, 252 | [6527] = 0, 253 | [6518] = 0, 254 | [6519] = 0, 255 | }, 256 | [63299] = { 257 | [46216] = 0, 258 | }, 259 | [63307] = { 260 | [47001] = 0, 261 | }, 262 | [63302] = { 263 | [46216] = 0, 264 | }, 265 | [62456] = { 266 | [-250255] = 0, 267 | }, 268 | [62458] = { 269 | [-250256] = 0, 270 | }, 271 | [62460] = { 272 | [9691] = 0, 273 | [9695] = 0, 274 | [9698] = 0, 275 | }, 276 | [62440] = { 277 | [-250246] = 0, 278 | }, 279 | [63390] = { 280 | [46277] = 0, 281 | }, 282 | [63388] = { 283 | [-181632] = 0, 284 | }, 285 | [61939] = { 286 | [5287] = 0, 287 | [5272] = 0, 288 | [5308] = 0, 289 | }, 290 | [63195] = { 291 | [5262] = 0, 292 | }, 293 | [61945] = { 294 | [45212] = 0, 295 | [45213] = 0, 296 | }, 297 | [63433] = { 298 | [7524] = 0, 299 | [7523] = 0, 300 | }, 301 | [63432] = { 302 | [46295] = 0, 303 | }, 304 | [63459] = { 305 | [7458] = 0, 306 | [7460] = 0, 307 | [7459] = 0, 308 | }, 309 | [63450] = { 310 | [-250518] = 0, 311 | }, 312 | [63452] = { 313 | [-250518] = 0, 314 | }, 315 | [62829] = { 316 | [1806] = 0, 317 | [1808] = 0, 318 | }, 319 | [62838] = { 320 | [-250371] = 0, 321 | }, 322 | [62839] = { 323 | [-250371] = 0, 324 | }, 325 | [62840] = { 326 | [-250371] = 0, 327 | }, 328 | [63409] = { 329 | [6519] = 0, 330 | [6518] = 0, 331 | [6517] = 0, 332 | [6527] = 0, 333 | }, 334 | [63408] = { 335 | [46282] = 0, 336 | [46281] = 0, 337 | }, 338 | [63410] = { 339 | [6520] = 0, 340 | [6521] = 0, 341 | }, 342 | [63412] = { 343 | [45762] = 0, 344 | [45761] = 0, 345 | }, 346 | [63411] = { 347 | [6560] = 0, 348 | }, 349 | [63414] = { 350 | [-250508] = 0, 351 | }, 352 | [63420] = { 353 | [-250509] = 0, 354 | }, 355 | [63421] = { 356 | [-250510] = 0, 357 | }, 358 | [63422] = { 359 | [-250511] = 0, 360 | }, 361 | [63423] = { 362 | [-250512] = 0, 363 | }, 364 | [62577] = { 365 | [-5000005] = 0, 366 | [-5000006] = 0, 367 | [-5000007] = 0, 368 | }, 369 | [62551] = { 370 | [-250299] = 0, 371 | }, 372 | [62888] = { 373 | [-250384] = 0, 374 | }, 375 | [62541] = { 376 | [-250295] = 0, 377 | }, 378 | [62538] = { 379 | [-250292] = 0, 380 | }, 381 | [62762] = { 382 | [1557] = 0, 383 | }, 384 | [63155] = { 385 | [-250047] = 0, 386 | }, 387 | [63316] = { 388 | [-250488] = 0, 389 | }, 390 | [62252] = { 391 | [524] = 0, 392 | }, 393 | [62253] = { 394 | [118] = 0, 395 | }, 396 | [62254] = { 397 | [822] = 0, 398 | }, 399 | [62271] = { 400 | [45501] = 0, 401 | }, 402 | [62344] = { 403 | [-250227] = 0, 404 | }, 405 | [62345] = { 406 | [-250227] = 0, 407 | }, 408 | [62528] = { 409 | [5635] = 0, 410 | }, 411 | [63241] = { 412 | [-250465] = 0, 413 | }, 414 | [63237] = { 415 | [-250462] = 0, 416 | }, 417 | [63427] = { 418 | [-250515] = 0, 419 | }, 420 | [63481] = { 421 | [-250530] = 0, 422 | }, 423 | [63483] = { 424 | [-250531] = 0, 425 | }, 426 | [63457] = { 427 | [-250521] = 0, 428 | }, 429 | [63458] = { 430 | [10619] = 0, 431 | }, 432 | [62284] = { 433 | [-250210] = 0, 434 | }, 435 | [62285] = { 436 | [-250211] = 0, 437 | }, 438 | [62286] = { 439 | [-250212] = 0, 440 | }, 441 | [63166] = { 442 | [-250443] = 0, 443 | }, 444 | [63169] = { 445 | [-250443] = 0, 446 | }, 447 | [63796] = { 448 | [-250541] = 0, 449 | }, 450 | [63797] = { 451 | [-250545] = 0, 452 | }, 453 | [63798] = { 454 | [-250547] = 0, 455 | }, 456 | [63799] = { 457 | [-250549] = 0, 458 | }, 459 | [63824] = { 460 | [46378] = 0, 461 | }, 462 | [63810] = { 463 | [-176361] = 0, 464 | [-176392] = 0, 465 | [-176393] = 0, 466 | [-177289] = 0, 467 | }, 468 | [63786] = { 469 | [8540] = 0, 470 | [8541] = 0, 471 | [8542] = 0, 472 | }, 473 | [63787] = { 474 | [46340] = 0, 475 | }, 476 | [63792] = { 477 | [-250538] = 0, 478 | }, 479 | [63793] = { 480 | [-250536] = 0, 481 | }, 482 | [62476] = { 483 | [-184164] = 0, 484 | }, 485 | [62477] = { 486 | [-176694] = 0, 487 | }, 488 | [62314] = { 489 | [45531] = 0, 490 | }, 491 | [62822] = { 492 | [45908] = 0, 493 | }, 494 | [65326] = { 495 | [11736] = 0, 496 | }, 497 | } 498 | -------------------------------------------------------------------------------- /pfQuest-updater.lua: -------------------------------------------------------------------------------- 1 | function hcstrsplit(delimiter, subject) 2 | if not subject then return nil end 3 | local delimiter, fields = delimiter or ":", {} 4 | local pattern = string.format("([^%s]+)", delimiter) 5 | string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end) 6 | return unpack(fields) 7 | end 8 | 9 | function formatVersion(versionNum) 10 | local major = math.floor(versionNum / 10000) 11 | local minor = math.floor((versionNum % 10000) / 100) 12 | local fix = versionNum % 100 13 | return major .. "." .. minor .. "." .. fix 14 | end 15 | 16 | local major, minor, fix = hcstrsplit(".", tostring(GetAddOnMetadata("pfQuest-epoch", "Version"))) 17 | fix = fix or 0 18 | local alreadyshown = false 19 | local localversion = tonumber(major*10000 + minor*100 + fix) 20 | local remoteversion = tonumber(gpiupdateavailable) or 0 21 | local loginchannels = { "BATTLEGROUND", "RAID", "GUILD", "PARTY" } 22 | local groupchannels = { "BATTLEGROUND", "RAID", "PARTY" } 23 | local partyVersions = {} 24 | local manualPings = {} 25 | 26 | local function StripRealmName(fullName) 27 | if fullName and fullName:find("-") then 28 | return fullName:match("^([^-]+)") 29 | end 30 | return fullName 31 | end 32 | 33 | local function UpdatePartyVersionDisplay() 34 | if UnitName("player") ~= "Bennylava" then 35 | return 36 | end 37 | 38 | for i = 1, GetNumPartyMembers() do 39 | local memberName = UnitName("party" .. i) 40 | local stripMemberName = StripRealmName(memberName) 41 | local version = partyVersions[memberName] or partyVersions[stripMemberName] 42 | 43 | if memberName and version then 44 | local frame = _G["ElvUF_PartyGroup1UnitButton" .. i] 45 | if not frame then 46 | frame = _G["PartyMemberFrame" .. i] 47 | end 48 | 49 | if frame then 50 | local labelName = "pfQuestVersionLabel" .. i 51 | local label = _G[labelName] 52 | 53 | local parent = frame.RaisedElementParent or frame 54 | 55 | if not label then 56 | label = parent:CreateFontString(labelName, "OVERLAY", "GameFontNormalSmall") 57 | label:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE") 58 | label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 15) 59 | end 60 | 61 | label:SetText("v" .. formatVersion(version)) 62 | label:SetTextColor(0.4, 1, 1) 63 | label:Show() 64 | end 65 | end 66 | end 67 | end 68 | 69 | local function UpdateTargetVersionDisplay() 70 | if UnitName("player") ~= "Bennylava" then 71 | return 72 | end 73 | 74 | if not UnitExists("target") then 75 | local label = _G["pfQuestVersionLabelTarget"] 76 | if label then 77 | label:Hide() 78 | end 79 | return 80 | end 81 | 82 | local targetName = UnitName("target") 83 | if not targetName then return end 84 | 85 | local stripTargetName = StripRealmName(targetName) 86 | local version = partyVersions[targetName] or partyVersions[stripTargetName] 87 | 88 | if version then 89 | local frame = _G["ElvUF_Target"] 90 | if not frame then 91 | frame = _G["TargetFrame"] 92 | end 93 | 94 | if frame then 95 | local labelName = "pfQuestVersionLabelTarget" 96 | local label = _G[labelName] 97 | 98 | local parent = frame.RaisedElementParent or frame 99 | 100 | if not label then 101 | label = parent:CreateFontString(labelName, "OVERLAY", "GameFontNormalSmall") 102 | label:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE") 103 | label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 15) 104 | end 105 | 106 | label:SetText("v" .. formatVersion(version)) 107 | label:SetTextColor(0.4, 1, 1) 108 | label:Show() 109 | end 110 | else 111 | local label = _G["pfQuestVersionLabelTarget"] 112 | if label then 113 | label:Hide() 114 | end 115 | end 116 | end 117 | 118 | gpiupdater = CreateFrame("Frame") 119 | gpiupdater:RegisterEvent("CHAT_MSG_ADDON") 120 | gpiupdater:RegisterEvent("PLAYER_ENTERING_WORLD") 121 | gpiupdater:RegisterEvent("PARTY_MEMBERS_CHANGED") 122 | gpiupdater:RegisterEvent("PLAYER_TARGET_CHANGED") 123 | gpiupdater:SetScript("OnEvent", function(_, event, ...) 124 | if event == "CHAT_MSG_ADDON" then 125 | local arg1, arg2, arg3, arg4 = ... 126 | if arg1 == "pfqe" then 127 | local v, remoteversion = hcstrsplit(":", arg2) 128 | remoteversion = tonumber(remoteversion) 129 | if v == "VERSION" and remoteversion then 130 | local strippedName = StripRealmName(arg4) 131 | partyVersions[strippedName] = remoteversion 132 | if remoteversion > localversion then 133 | gpiupdateavailable = remoteversion 134 | if not alreadyshown then 135 | local currentVer = formatVersion(localversion) 136 | local availableVer = formatVersion(remoteversion) 137 | print("|cff33ffccpfQuest |cffcccccc[Project Epoch DB]|r New version available!") 138 | print("Current: |cff66ccff" .. currentVer .. "|r -> Available: |cff66ccff" .. availableVer .. "|r") 139 | print("|cff66ccffhttps://github.com/Bennylavaa/pfQuest-epoch/releases|r") 140 | alreadyshown = true 141 | end 142 | end 143 | end 144 | if v == "PING?" then 145 | if arg3 == "WHISPER" then 146 | SendAddonMessage("pfqe", "PONG!:"..GetAddOnMetadata("pfQuest-epoch", "Version"), "WHISPER", arg4) 147 | else 148 | for _, chan in ipairs(loginchannels) do 149 | SendAddonMessage("pfqe", "PONG!:"..GetAddOnMetadata("pfQuest-epoch", "Version"), chan) 150 | end 151 | end 152 | end 153 | if v == "PONG!" then 154 | if UnitName("player") == "Bennylava" then 155 | local pongCmd, pongversion = hcstrsplit(":", arg2) 156 | local pmajor, pminor, pfix = hcstrsplit(".", tostring(pongversion)) 157 | pfix = pfix or 0 158 | pongversion = tonumber(pmajor*10000 + pminor*100 + pfix) 159 | local strippedName = StripRealmName(arg4) 160 | partyVersions[strippedName] = pongversion 161 | 162 | if manualPings[strippedName] then 163 | print("|cffff8000"..arg4.."|r - |cff66ccffv"..formatVersion(pongversion).."|r") 164 | manualPings[strippedName] = nil 165 | end 166 | 167 | UpdatePartyVersionDisplay() 168 | UpdateTargetVersionDisplay() 169 | end 170 | end 171 | end 172 | elseif event == "PARTY_MEMBERS_CHANGED" then 173 | local groupsize = GetNumRaidMembers() > 0 and GetNumRaidMembers() or GetNumPartyMembers() > 0 and GetNumPartyMembers() or 0 174 | if (gpiupdater.group or 0) < groupsize then 175 | for _, chan in ipairs(groupchannels) do 176 | SendAddonMessage("pfqe", "VERSION:" .. localversion, chan) 177 | end 178 | end 179 | gpiupdater.group = groupsize 180 | UpdatePartyVersionDisplay() 181 | elseif event == "PLAYER_ENTERING_WORLD" then 182 | if not alreadyshown and localversion < remoteversion then 183 | local currentVer = formatVersion(localversion) 184 | local availableVer = formatVersion(remoteversion) 185 | print("|cff33ffccpfQuest |cffcccccc[Project Epoch DB]|r New version available!") 186 | print("Current: |cff66ccff" .. currentVer .. "|r -> Available: |cff66ccff" .. availableVer .. "|r") 187 | print("|cff66ccffhttps://github.com/Bennylavaa/pfQuest-epoch/releases|r") 188 | gpiupdateavailable = localversion 189 | alreadyshown = true 190 | end 191 | for _, chan in ipairs(loginchannels) do 192 | SendAddonMessage("pfqe", "VERSION:" .. localversion, chan) 193 | end 194 | elseif event == "PLAYER_TARGET_CHANGED" then 195 | if UnitName("player") == "Bennylava" then 196 | local targetName = UnitName("target") 197 | if targetName and UnitIsPlayer("target") then 198 | SendAddonMessage("pfqe", "PING?", "WHISPER", targetName) 199 | end 200 | end 201 | UpdateTargetVersionDisplay() 202 | end 203 | end) 204 | 205 | SLASH_PFQEPING1 = "/pfqe" 206 | SlashCmdList["PFQEPING"] = function(msg) 207 | if msg == "" then 208 | print("Usage: /pfqe PLAYERNAME") 209 | return 210 | end 211 | local strippedName = StripRealmName(msg) 212 | manualPings[strippedName] = true 213 | SendAddonMessage("pfqe", "PING?", "WHISPER", msg) 214 | end -------------------------------------------------------------------------------- /db/enUS/objects-epoch.lua: -------------------------------------------------------------------------------- 1 | pfDB["objects"]["enUS-epoch"] = { 2 | [175802] = "Small Lockbox", 3 | [176495] = "Zeppelin (The Purple Princess)", 4 | [178824] = "Meeting Stone (Razorfen Downs)", 5 | [178825] = "Meeting Stone (Razorfen Kraul)", 6 | [178826] = "Meeting Stone (Dire Maul)", 7 | [178827] = "Meeting Stone (Maraudon)", 8 | [178828] = "Meeting Stone (Blackfathom Deeps)", 9 | [178829] = "Meeting Stone (Zul\'Farrak)", 10 | [178831] = "Meeting Stone (Strathholme)", 11 | [178832] = "Meeting Stone (Scholomance)", 12 | [178833] = "Meeting Stone (Uldaman)", 13 | [178834] = "Meeting Stone (The Deadmines)", 14 | [178844] = "Meeting Stone (Scarlet Monastery)", 15 | [178845] = "Meeting Stone (Shadowfang Keep)", 16 | [178884] = "Meeting Stone (Wailing Caverns)", 17 | [179554] = "Meeting Stone (The Temple of Atal\'Hakkar)", 18 | [179555] = "Meeting Stone (Gnomeregan)", 19 | [179584] = "Meeting Stone (Blackrock Depths)", 20 | [179585] = "Meeting Stone (Blackrock Spire)", 21 | [179595] = "Meeting Stone (The Stockade)", 22 | [179596] = "Meeting Stone (Ragefire Chasm)", 23 | [250000] = "Alteraci Shilling", 24 | [250001] = "Aqiri Ynusi", 25 | [250002] = "Atal\'ai Real", 26 | [250003] = "Azsharan Agorot", 27 | [250004] = "Azsharan Shekel", 28 | [250005] = "Kezan Penny", 29 | [250006] = "Kezan Dollar", 30 | [250007] = "Kezan Quarter", 31 | [250008] = "Dark Iron Drachma", 32 | [250009] = "Dark Iron Mina", 33 | [250010] = "Dark Iron Obol", 34 | [250011] = "Dark Iron Stater", 35 | [250012] = "Defias Ducat", 36 | [250013] = "Gnomish Guinea", 37 | [250014] = "Ironforge Florin", 38 | [250015] = "Ironforge Shilling", 39 | [250016] = "Kaldorei Lune", 40 | [250017] = "Kaldorei Sol", 41 | [250018] = "Kaldorei Star", 42 | [250019] = "Kul\'Tiran Crown", 43 | [250020] = "Kul\'Tiran Farthing", 44 | [250021] = "Legion Paisa", 45 | [250022] = "Legion Rupee", 46 | [250023] = "Lordaeron Crown", 47 | [250024] = "Lordaeron Farthing", 48 | [250025] = "Lordaeron Penny", 49 | [250026] = "Dalaran Shilling", 50 | [250027] = "Gilnean Crown", 51 | [250028] = "Naga Bigshell", 52 | [250029] = "Naga Smallshell", 53 | [250030] = "Pandaren Yuen", 54 | [250031] = "Stormwind Crown", 55 | [250032] = "Stormwind Shilling", 56 | [250033] = "Stormwind Farthing", 57 | [250034] = "Stormwind Sixpence", 58 | [250035] = "Stromgarde Shilling", 59 | [250036] = "A Coin of Unknown Origin", 60 | [250037] = "Venture Co. IOU", 61 | [250038] = "Venture Co. Voucher", 62 | [250039] = "Wildhammer Florin", 63 | [250040] = "Shadraspawn Sack", 64 | [250041] = "Gryphon Nest", 65 | [250042] = "Waterlogged Chest", 66 | [250043] = "Falstad Wildhammer", 67 | [250044] = "Royal Bite Reed", 68 | [250045] = "Turtle \"Nugget\"", 69 | [250046] = "Ubo\'s Chest", 70 | [250047] = "Rirrek\'s Effigy", 71 | [250048] = "Wanted: Foulcrest", 72 | [250049] = "Wanted Poster", -- Badlands 73 | [250050] = "Wildhammer Bones", 74 | [250051] = "Job Opening: Captain of the Guard", 75 | [250052] = "Howin\'s Fishing Chair", 76 | [250053] = "Howin\'s Tackle Box", 77 | [250054] = "Driftwood", 78 | [250055] = "Waterlogged Sword", 79 | [250056] = "Relic of the Past", 80 | [250057] = "Smokywood Pastures Shipping Crate", 81 | [250058] = "Mecha-Chicken Parts", 82 | [250059] = "Cauldron", 83 | [250060] = "Prism Prison", 84 | [250061] = "Crystal Lock", 85 | [250062] = "Crystal Lock", 86 | [250063] = "Crystal Lock", 87 | [250064] = "Crystal Lock", 88 | [250065] = "Milhouse\'s Belongings", 89 | [250066] = "Ghost Gate", 90 | [250067] = "Cache of the Warchief", 91 | [250068] = "Solid Chest", 92 | [250069] = "Balefire Draught", 93 | [250070] = "Crate of Felstone Grog", 94 | [250071] = "Felstone Grog", 95 | [250072] = "Kuzhir Atramentum", 96 | [250073] = "High Altar of Felstone Fortress", 97 | [250074] = "Felstone - Fel Mark", 98 | [250075] = "Highborne Relic Chest", 99 | [250076] = "Starfall Crystal", 100 | [250077] = "Eleanor\'s Crate", 101 | [250078] = "Starlight Shrine", 102 | [250079] = "Arcanaeum Mooncrystal Altar", 103 | [250080] = "Immaculate Scroll", 104 | [250081] = "Broodmother\'s Lair", 105 | [250082] = "Rune of Warding", 106 | [250083] = "Small Fleshwerks Fire", 107 | [250084] = "Ghost Gate", 108 | [250085] = "Arena Spoils", 109 | [250086] = "Anvilrage Mole Machine", 110 | [250087] = "Anvilrage Mole Machine", 111 | [250088] = "Doodad_BOSSGATE02", 112 | [250089] = "Defias Gunpowder", 113 | [250090] = "Defias Cannon", 114 | [250091] = "Factory Door", 115 | [250092] = "Mast Room Door", 116 | [250093] = "Foundry Door", 117 | [250094] = "Iron Clad Door", 118 | [250095] = "Vacant Minecart", 119 | [250096] = "Excavated Artifact", 120 | [250097] = "Crystal Miner", 121 | [250098] = "Crystal Miner", 122 | [250099] = "Crystal Miner", 123 | [250100] = "Crystal Miner", 124 | [250101] = "Crystal Miner", 125 | [250102] = "Crystal Miner", 126 | [250103] = "Warning!", 127 | [250104] = "No blasting!", 128 | [250105] = "Turn away!", 129 | [250106] = "Do not wake the beast!", 130 | [250107] = "Eye\'s closed!", 131 | [250108] = "Warning!!!", 132 | [250109] = "Danger!", 133 | [250110] = "Run!!", 134 | [250111] = "I\'m not joking!", 135 | [250112] = "Don\'t be a fool!", 136 | [250113] = "No! No! No!", 137 | [250114] = "DANGER!", 138 | [250115] = "No Overtime!", 139 | [250116] = "STOP!", 140 | [250117] = "NO BLAST MINING!", 141 | [250118] = "Stop!", 142 | [250119] = "Do not enter!", 143 | [250120] = "Mine closed!", 144 | [250121] = "Silithid Metal Alloy", 145 | [250122] = "Wetland Hemp", 146 | [250123] = "Garr\'s Core", 147 | [250124] = "Spiked Earth Pillar", 148 | [250125] = "Lava Bubbles", 149 | [250126] = "Active Rune of Warding", 150 | [250127] = "Molten Bridge Lava Flow", 151 | [250128] = "Onyxia\'s Gate", 152 | [250129] = "Onyxia Room Encloser", 153 | [250130] = "Onyxia Egg", 154 | [250131] = "Frozen Onyxia Egg", 155 | [250132] = "Portal Visual", 156 | [250133] = "Blood Filled Orb", 157 | [250134] = "The Pervasive Argument for Laws", 158 | [250135] = "Is Eating Tauren Wrong: A Case Study", 159 | [250136] = "The Troll Wars & The Troll Warcrimes", 160 | [250137] = "Crate of Blessed Ingots", 161 | [250138] = "Sothos and Jarien\'s Heirlooms", 162 | [250139] = "Atal\'ai Statue", 163 | [250140] = "Atal\'ai Statue", 164 | [250141] = "Atal\'ai Statue", 165 | [250142] = "Atal\'ai Statue", 166 | [250143] = "Atal\'ai Statue", 167 | [250144] = "Atal\'ai Statue", 168 | [250145] = "Pedestal of Blood", 169 | [250146] = "The Tablet of Alani", 170 | [250147] = "The Tablet of Jakari", 171 | [250148] = "Royal Troll Mummy", 172 | [250149] = "Falrin Forcefield", 173 | [250150] = "A History of Baradin Hold", 174 | [250151] = "Captain\'s Log", 175 | [250152] = "Origin of the Kirin Tor", 176 | [250153] = "The Eternal Woods Manuscript", 177 | [250154] = "Reflections on Celestial Immortality", 178 | [250155] = "Solarsong: A Novel", 179 | [250156] = "A Short History of the High Elf Migration", 180 | [250157] = "Hidden Compartment", 181 | [250158] = "An Undisturbed Pile of Dirt", 182 | [250159] = "An Undisturbed Pile of Dirt", 183 | [250160] = "Spectral Treasure Chest", 184 | [250161] = "Jailor\'s Journal", 185 | [250162] = "Special Brew", 186 | [250163] = "Arcanite Lode", 187 | [250164] = "Ooze Covered Arcanite Lode", 188 | [250165] = "Call to Skirmish", -- Horde 189 | [250166] = "Call to Skirmish", -- Alliance 190 | [250167] = "Call to Skirmish: Stonetalon Mountains", 191 | [250168] = "Call to Skirmish: Ashenvale", 192 | [250169] = "Call to Skirmish: Hillsbrad Foothills", 193 | [250170] = "Call to Skirmish: Thousand Needles", 194 | [250171] = "Call to Skirmish: Alterac Mountains", 195 | [250172] = "Call to Skirmish: Desolace", 196 | [250173] = "Call to Skirmish: Arathi Highlands", 197 | [250174] = "Call to Skirmish: Swamp of Sorrows", 198 | [250175] = "Call to Skirmish: Badlands", 199 | [250176] = "Call to Skirmish: The Hinterlands", 200 | [250177] = "Call to Skirmish: Stranglethorn Vale", 201 | [250178] = "Call to Skirmish: Tanaris", 202 | [250179] = "Call to Skirmish: Felwood", 203 | [250180] = "Call to Skirmish: Azshara", 204 | [250181] = "Call to Skirmish: Un\'Goro Crater", 205 | [250182] = "Call to Skirmish: Searing Gorge", 206 | [250183] = "Call to Skirmish: Western Plaguelands", 207 | [250184] = "Call to Skirmish: Burning Steppes", 208 | [250185] = "Call to Skirmish: Eastern Plaguelands", 209 | [250186] = "Call to Skirmish: Silithus", 210 | [250187] = "Call to Skirmish: Feralas", 211 | [250188] = "Call to Skirmish: Winterspring", 212 | [250189] = "Call to Skirmish: Dustwallow Marsh", 213 | [250190] = "Call to Skirmish: Blasted Lands", 214 | [250191] = "Aid Station Banner", 215 | [250192] = "Aid Station Banner", 216 | [250193] = "Aid Station Banner", 217 | [250194] = "Aid Station Banner", 218 | [250195] = "Aid Station Banner", 219 | [250196] = "Aid Station Banner", 220 | [250197] = "Aid Station Banner", 221 | [250198] = "Alliance Banner", 222 | [250199] = "Contested Banner", 223 | [250200] = "Flag Pole", 224 | [250201] = "Horde Banner", 225 | [250202] = "Alliance Banner", 226 | [250203] = "Neutral Banner", 227 | [250204] = "Dark Portal Fragment", 228 | [250205] = "the basement of Foothold Citadel", 229 | [250206] = "the southern orc burrow outside of Stonard", 230 | [250207] = "Anvil", 231 | [250208] = "Caer Darrow", 232 | [250209] = "Brazier of the Dormant Flame", 233 | [250210] = "Natural Spring Fissure", 234 | [250211] = "Stonetalon Spring Fissure", 235 | [250212] = "Raven Hill Well", 236 | [250213] = "Brazier of Springfont", 237 | [250214] = "Gravestone", 238 | [250215] = "Frost Thistle", 239 | [250216] = "Magically-Sealed Strongbox", 240 | [250217] = "A Weathered Headstone", 241 | [250218] = "Uplands Intelligence Drop", 242 | [250219] = "Dandred\'s Fold Intelligence Drop", 243 | [250220] = "Teleportation Crystal", 244 | [250221] = "Barony Mordis Chest", 245 | [250222] = "Apple Bucket", 246 | [250223] = "Juicy Watermelon", 247 | [250224] = "Steam Vent", 248 | [250225] = "Shadowberry Bush", 249 | [250226] = "Oversized Shadowberry Bush", 250 | [250227] = "Fireplume\'s Roost", 251 | [250228] = "Strong Smelling Bait", 252 | [250229] = "Falstad Wildhammer", 253 | [250230] = "Battered Journal", 254 | [250231] = "Sunken Shaman Shrine", 255 | [250232] = "Shadowforge Treasure", 256 | [250233] = "Case of Mithril", 257 | [250234] = "Crate of Indurium Ore", 258 | [250235] = "Indurium Matrix", 259 | [250236] = "Wanted Poster", -- Darkshore 260 | [250237] = "Submerged Necklace", 261 | [250238] = "Lava-Scarred Chest", 262 | [250239] = "Mound of Clay", 263 | [250240] = "Ore Crate", 264 | [250241] = "Gold Ore Crate", 265 | [250242] = "Sunken Treasure", 266 | [250243] = "WANTED!", 267 | [250244] = "WANTED!", 268 | [250245] = "Izalnir\'s Belongings", 269 | [250246] = "Dreadmaul Peak Altar", 270 | [250247] = "Frozen Rookery Egg", 271 | [250248] = "Abandoned Herb Pouch", 272 | [250249] = "Torn Shirt", 273 | [250250] = "Subtly Writhing Sack", 274 | [250251] = "Woven Basket", 275 | [250252] = "Ornate Wardrobe", 276 | [250253] = "Metal Box", 277 | [250254] = "Knapsack", 278 | [250255] = "Lava Pool", 279 | [250256] = "Altar of Storms", 280 | [250257] = "Karazhan Front Door", 281 | [250258] = "Karazhan Valuables", 282 | [250259] = "Karazhan Valuables", 283 | [250260] = "Karazhan Valuables", 284 | [250261] = "Karazhan Valuables", 285 | [250262] = "Old Cabinet", 286 | [250263] = "Frostmane Armaments", 287 | [250264] = "Frostmane Armaments", 288 | [250265] = "the deepest part of Ironforge Airfield\'s lake", 289 | [250266] = "Bucket of Tools", 290 | [250267] = "Sudden Fire", 291 | [250268] = "Drum of Lubricant", 292 | [250269] = "Metal Plating", 293 | [250270] = "Trash Part", 294 | [250271] = "Trash Part", 295 | [250272] = "Trash Part", 296 | [250273] = "Trash Part", 297 | [250274] = "Cage", 298 | [250275] = "Frostmane Cage", 299 | [250276] = "Anvil", 300 | [250277] = "Amberstill Ranch Dummy", 301 | [250278] = "Kinetic Ore", 302 | [250279] = "Lost Tablet", 303 | [250280] = "Blood Petal Bush", 304 | [250281] = "Bloody Note", 305 | [250282] = "Pile of Dust", 306 | [250283] = "Revil\'s Notes", 307 | [250284] = "Lost Shipment", 308 | [250285] = "Brightwood Bloom", 309 | [250286] = "Twilight Grove Moonwell", 310 | [250287] = "Sarae\'s Experiment", 311 | [250288] = "Sarae\'s Experiment", 312 | [250289] = "Nelle\'s Diary", 313 | [250290] = "Leftover Bread", 314 | [250291] = "Cask of Brightwood White", 315 | [250292] = "Darkshire Hay Field", 316 | [250293] = "Picnic Basket", 317 | [250294] = "Cleansing Clam", 318 | [250295] = "Faye Underhills Home", 319 | [250296] = "Jacks Letter", 320 | [250297] = "Wanted: Plagued Shambler", 321 | [250298] = "Comfortably Large Crate", 322 | [250299] = "Blackrock Mountain", 323 | [250300] = "Cenarion Ritual Altar", 324 | [250301] = "Old Lantern", 325 | [250302] = "Empty Jug", 326 | [250303] = "Old Bottle", 327 | [250304] = "Logging Equipment", 328 | [250305] = "Old Book", 329 | [250306] = "Old Bucket", 330 | [250307] = "Discarded Doll", 331 | [250308] = "Old Clothes", 332 | [250309] = "Discarded Spool", 333 | [250310] = "A Dusty Letter", 334 | [250311] = "A Dirty Letter", 335 | [250312] = "A Torn Letter", 336 | [250313] = "Box of Salvaged Goods", 337 | [250314] = "Egg Sac", 338 | [250315] = "Scourge Cauldron Focus", 339 | [250316] = "Tower of Azora Well Dummy", 340 | [250317] = "Mining Equipment", 341 | [250318] = "Soaked Barrel", 342 | [250319] = "Northshire Reed", 343 | [250320] = "Northshire Falls Dummy", 344 | [250321] = "Alteraci Legion Banner of Honor", -- Alterac Mountains - Alterac 345 | [250322] = "Alteraci Legion Banner of Honor", -- Alterac Mountains - Dalaran 346 | [250323] = "Alteraci Legion Banner of Honor", -- Arathi Highlands 347 | [250324] = "Wanted: Big Blue", 348 | [250325] = "Syndicate Food Supplies Dummy", 349 | [250326] = "Apple", 350 | [250327] = "Apple Cider Barrel", 351 | [250328] = "Apple Press", 352 | [250329] = "Crates of Armaments", 353 | [250330] = "Ghost-Touched Ore", 354 | [250331] = "WANTED", -- Crossroads 355 | [250332] = "Thelsamar Graveyard Dummy", 356 | [250333] = "Lost Artifact", 357 | [250334] = "Loch Weed", 358 | [250335] = "WANTED", -- Hillsbrad Foothills 359 | [250336] = "Crate of Stolen Horde Supplies", 360 | [250337] = "Ritual Altar", 361 | [250338] = "Crate of Artifacts", 362 | [250339] = "Makeshift Ambermill Cage", 363 | [250340] = "Historian\'s Bookcase", 364 | [250341] = "Looted Book", 365 | [250342] = "Looted Perfume", 366 | [250343] = "Looted Lantern", 367 | [250344] = "Looted Jewelry", 368 | [250345] = "Bloodscalp Ritual Altar", 369 | [250346] = "Blood Brazier", 370 | [250347] = "Waterlogged Weapons Cache", 371 | [250348] = "Sidis Faintsnipe Dummy", 372 | [250349] = "Zul\'Kunda Cage", 373 | [250350] = "Zul\'Kunda Cage", 374 | [250351] = "Failed Experiment", 375 | [250352] = "The Tablet of Zuuldaia", 376 | [250353] = "Conspicuous Sand Pile", 377 | [250354] = "Conspicuous Sand Pile", 378 | [250355] = "Conspicuous Sand Pile", 379 | [250356] = "Tulip\'s Final Entry", 380 | [250357] = "Ocniir\'s Chest", 381 | [250358] = "Fire Snap Chili", 382 | [250359] = "Swamp Reed", 383 | [250360] = "WANTED", -- Loch Modan 384 | [250361] = "WANTED", -- Swamp of Sorrows 385 | [250362] = "Box of Collected Relics", 386 | [250363] = "Case of Ore", 387 | [250364] = "Zapetta\'s Stash", 388 | [250365] = "Joseph\'s Grave Dummy", 389 | [250366] = "Winterspring Temporal Disturbance", 390 | [250367] = "Pile of Correspondence", 391 | [250368] = "Old Woodpile", 392 | [250369] = "Pure Elwynn Soil Sample", 393 | [250370] = "Anvil", 394 | [250371] = "Prepared Soil", 395 | [250372] = "Barov Sepulcher Plaque", 396 | [250373] = "Scarlet Outpost", 397 | [250374] = "Prank Bag", --Bullwork 398 | [250375] = "Prank Bag", --Chillind Camp 399 | [250376] = "Prank Bag", --Uther\'s Tomb 400 | [250377] = "WANTED", -- Feralas 401 | [250378] = "Dragonmaw Weapon Rack", 402 | [250379] = "Box of Assorted Parts", 403 | [250380] = "Highlands Corn", 404 | [250381] = "Conspicuous Pile Of Dirt", 405 | [250382] = "Ameth\'Aran Focus", 406 | [250383] = "Ameth\'Aran Focus Ward", 407 | [250384] = "Ruins of Stardust Dummy", 408 | [250385] = "Sturdy Reed", 409 | [250386] = "Tremormatic MK I", 410 | [250387] = "Tremormatic MK II", 411 | [250388] = "New Demon Seed", 412 | [250389] = "", 413 | [250390] = "", 414 | [250391] = "Valormok Mine Vein", 415 | [250392] = "Lake Mennar", 416 | [250393] = "West Legashi Effigy", 417 | [250394] = "East Legashi Effigy", 418 | [250395] = "South Legashi Effigy", 419 | [250396] = "Thunderhead Hippogryph Feather", 420 | [250397] = "Naga Shrine", 421 | [250398] = "Watery Highborne Relic", 422 | [250399] = "Shipwrecked Part", 423 | [250400] = "Wreck of the Spirit of Gnomeregan", 424 | [250401] = "Wreck of the Pine Duck", 425 | [250402] = "Wreck of the Gnomeforce One", 426 | [250403] = "Wreck of the Microtanic", 427 | [250404] = "Shipwrecked Supplies", 428 | [250405] = "Whitetide Lotus", 429 | [250406] = "Deviate Feast", 430 | [250407] = "Medical Supply Crate", 431 | [250408] = "Wobble Hollow Wanted Poster", 432 | [250409] = "Wobble Hollow Wanted Poster", 433 | [250410] = "Cage", 434 | [250411] = "Anvil", 435 | [250412] = "The Beast\'s Room", 436 | [250413] = "Smokywood Lockbox", 437 | [250414] = "Smokywood Lockbox", 438 | [250415] = "Quilboar Dagger", 439 | [250416] = "Looted Lorespeaker Crate", 440 | [250417] = "WANTED", -- Durotar 441 | [250418] = "Torn Travel Pamphlet", 442 | [250419] = "Clue 1", 443 | [250420] = "Clue 2", 444 | [250421] = "Clue 3", 445 | [250422] = "Defias Strongbox", 446 | [250423] = "Bottle of Raven\'s Dark Ale", 447 | [250424] = "Everit\'s Canteen", 448 | [250425] = "Everit\'s Notebook", 449 | [250426] = "Everit\'s Lantern", 450 | [250427] = "Everit\'s Laundry", 451 | [250428] = "Sunken Chest", 452 | [250429] = "Sunken Treasure", 453 | [250430] = "Magical Barrier", 454 | [250431] = "Ceremonial Bonfire", 455 | [250432] = "Grimtotem Treasured Possession", 456 | [250433] = "Fossilised Gallbladder", 457 | [250434] = "Wanted: Keiko", 458 | [250435] = "Wanted: Wilfiz Silverbit", 459 | [250436] = "Thistleweed", 460 | [250437] = "WANTED", -- Westfall 461 | [250438] = "Cage", 462 | [250439] = "Wanted: Dragon Killers", 463 | [250440] = "Wanted: King Krool", 464 | [250441] = "Wanted: King Krool", 465 | [250442] = "Zeppelin Cargo", 466 | [250443] = "Bloodvenom Falls Dock", 467 | [250444] = "Memento of Kil\'jaeden", 468 | [250445] = "Memento of Sargeras", 469 | [250446] = "Memento of Kil\'Jaeden", 470 | [250447] = "Memento of Tichondrius", 471 | [250448] = "Ogre Corpse", 472 | [250449] = "WANTED", -- Swamp of Sorrows 473 | [250450] = "Palemane Cage", 474 | [250451] = "Oil Spill", 475 | [250452] = "Inn Entrance", 476 | [250453] = "Calf Bait", 477 | [250454] = "Wanted: Jasone", 478 | [250455] = "Ancient Reports", 479 | [250456] = "Silithus Crystal Pile", 480 | [250457] = "Silithus Wasp Swarm", 481 | [250458] = "Lower Twilight Tablet", 482 | [250459] = "Upper Twilight Tablet", 483 | [250460] = "Silithus Crystal Fragment", 484 | [250461] = "Cult Plans", 485 | [250462] = "Pile of Meat", 486 | [250463] = "Pile of Meat", 487 | [250464] = "Anvil", 488 | [250465] = "Heart of Hive\'Regal", 489 | [250466] = "Valor\'s Rest Supplies", 490 | [250467] = "Palladium Ore", 491 | [250468] = "Coprolite Node", 492 | [250469] = "Tremormatic MK II", 493 | [250470] = "Water Vine", 494 | [250471] = "Venture Co. Supplies", 495 | [250472] = "Venture Co. Supplies", 496 | [250473] = "Kaela\'s Campfire Dummy", 497 | [250474] = "Alter of Gral", 498 | [250475] = "Altar of Gral", 499 | [250476] = "Gral Offering", 500 | [250477] = "Sandshore Seagrass", 501 | [250478] = "Thistleshrub Valley Moonwell", 502 | [250479] = "Roknar", 503 | [250480] = "Empowered Kezan\'s Premium Refrigerator", 504 | [250481] = "Ancient Troll Pot", 505 | [250482] = "Ancient Troll Pot", 506 | [250483] = "Ancient Troll Pot", 507 | [250484] = "Gnome Racer Parts", 508 | [250485] = "Gnome Racer Parts", 509 | [250486] = "Gnome Racer Parts", 510 | [250487] = "Oil Platform Base", 511 | [250488] = "Narain Southfancy\'s House", 512 | [250489] = "Rocket Detonator", 513 | [250490] = "Ancient Chest", 514 | [250491] = "Oil Spill", 515 | [250492] = "Slightly-Open Oil Barrel", 516 | [250493] = "Timed Explosive", 517 | [250494] = "Filter Parts", 518 | [250495] = "Filter Parts", 519 | [250496] = "Zeppelin Cargo", 520 | [250497] = "Wanted Poster", -- Tanaris 521 | [250498] = "Bloodfeather Egg", 522 | [250499] = "Dolanaar Moonwell Dummy", 523 | [250500] = "Lily Whip", 524 | [250501] = "Ancient Tauren Totem", 525 | [250502] = "Weaveleaf", 526 | [250503] = "Thalanaar Moonwell", 527 | [250504] = "Weathered Totem", 528 | [250505] = "Carrion Vulture Egg", 529 | [250506] = "Golakka Hot Springs", 530 | [250507] = "Aru-Talis Artifact", 531 | [250508] = "Area of Interest Focus", 532 | [250509] = "Aru-Talis Site One", 533 | [250510] = "Aru-Talis Site Two", 534 | [250511] = "Aru-Talis Site Three", 535 | [250512] = "Aru-Talis Site Four", 536 | [250513] = "Fine River Sand", 537 | [250514] = "Anvil", 538 | [250515] = "Southern Pylon", 539 | [250516] = "How to Train Your Dinosaur", 540 | [250517] = "Anvil", 541 | [250518] = "Heap of Flesh", 542 | [250519] = "Heap of Flesh", 543 | [250520] = "A Treatise on Wintersaber Training", 544 | [250521] = "Frostfire Hot Springs", 545 | [250522] = "Icesap", 546 | [250523] = "Frost Crystal", 547 | [250524] = "Arcanaeum Door", 548 | [250525] = "Arcanaeum Door Rune", 549 | [250526] = "Arcanaeum Vault", 550 | [250527] = "Quel\'dorei Tablet", 551 | [250528] = "Nightmare Seed", 552 | [250529] = "Anvil", 553 | [250530] = "Malvor\'s Moonwell", 554 | [250531] = "Soft Soil", 555 | [250532] = "Snow Cherry Bush", 556 | [250533] = "Heart of An\'she", 557 | [250534] = "Heart of Mu\'sha", 558 | [250535] = "Anvil", 559 | [250536] = "Earthmother\'s Shrine", -- The Barrens 560 | [250537] = "Anvil", 561 | [250538] = "Earthmother\'s Shrine", -- Stonetalon Mountains 562 | [250539] = "Anvil", 563 | [250540] = "Bones of the Earthmother", 564 | [250541] = "Monument Rock", 565 | [250542] = "Anvil", 566 | [250543] = "Resting Place of Ancestors", 567 | [250544] = "Anvil", 568 | [250545] = "Resting Place of Ancestors", -- Dun Morogh 569 | [250546] = "Anvil", 570 | [250547] = "Resting Place of Ancestors", -- Ashenvale 571 | [250548] = "Anvil", 572 | [250549] = "Resting Place of Ancestors", -- Duskwood 573 | [250550] = "Anvil", 574 | [250551] = "Resting Place of Ancestors", 575 | [250552] = "Anvil", 576 | [250553] = "Resting Place of Ancestors", 577 | [250554] = "Anvil", 578 | [250555] = "Resting Place of Ancestors", 579 | [250556] = "Jug of Healing Water", 580 | [250557] = "Anvil", 581 | [250558] = "Durnholde Keep Gallows", 582 | [250559] = "Anvil", 583 | [250560] = "Plaguelands Cauldrons Dummy", 584 | [250561] = "Plagued Weed", 585 | [250562] = "Firebrew\'s Keg", 586 | [250563] = "Wiggi\'s Terminal", 587 | [250564] = "", 588 | [250565] = "Frozen Prey", 589 | [250566] = "Gnomish Components", 590 | [250567] = "Anvil", 591 | [250568] = "Shatterspear Axe Throwing Tournament", 592 | [250569] = "Cache of Courage", 593 | [250570] = "Anvil", 594 | [250571] = "Shatterspear Cave", 595 | [250572] = "Nitrous Power-Up", 596 | [250573] = "Red Rocket Power-Up", 597 | [250574] = "Green Rocket Power-Up", 598 | [250575] = "Jump Power-Up", 599 | [250576] = "Tar Puddle Power-Up", 600 | [250577] = "Cage", 601 | [250578] = "Summoning Crystal", 602 | [250579] = "Blue Sigil", 603 | [250580] = "Ice Block", 604 | [250581] = "Crescent Vent", 605 | [250582] = "Uldaman", 606 | [250583] = "Mailbox", 607 | [250584] = "Bonfire", 608 | [250585] = "Wildhammer Clan Headquarters", 609 | [250586] = "Ironforge Airfield Guard Post", 610 | [250587] = "Corrupted Dirt", 611 | [250588] = "Sarae\'s Experiment", 612 | [250589] = "The Bearded Barmaid", 613 | [250590] = "The Citrine Eagle", 614 | [250591] = "Durnholde Armaments", 615 | [250592] = "Tog\'thar Shackles", 616 | [250593] = "Vernado Shackles", 617 | [250594] = "Syndicate Food Supplies", 618 | [250595] = "Bhondur\'s Bones", 619 | [250596] = "The Sleeper\'s Bed", 620 | [250597] = "The Sleeper\'s Bed", 621 | [250598] = "The Sleeper\'s Bed", 622 | [250599] = "Deathguard Chunks", 623 | [250600] = "Joseph\'s Watch", 624 | [250601] = "Psathshroom", 625 | [250602] = "Rusty Metal Doors", 626 | [250603] = "Sarcophagus of an Unknown Human", 627 | [250604] = "Dry Hay Bail", 628 | [250605] = "Dry Hay Bail", 629 | [250606] = "Dry Hay Bail", 630 | [250607] = "Grim Batol Instance Portal", 631 | [250608] = "Box of Treasure", 632 | [250609] = "Crate of Assorted Armor", 633 | [250610] = "Shrine of Sha\'gri", 634 | [250611] = "Venture Co. Debris", 635 | [250612] = "Venture Co. Debris", 636 | [250613] = "Venture Co. Debris", 637 | [250614] = "Blacksmith", 638 | [250615] = "Engineering", 639 | [250616] = "Mining", 640 | [250617] = "Invigilator Watchtower Transporter", 641 | [250618] = "Invigilator Watchtower Transporter", 642 | [250619] = "Invigilator Watchtower Transporter", 643 | [250620] = "Invigilator Watchtower Transporter", 644 | [250621] = "Parachute Crate", 645 | [250622] = "Ancient Smithing Tome", 646 | [250623] = "Bubbly Fissure", 647 | [250624] = "Highlands Nook", 648 | [250625] = "Crambers Ranch", 649 | [250626] = "Burndural", 650 | [250627] = "Dun Guldar", 651 | [250628] = "Zeppelin Fire", 652 | [250629] = "Zeppelin Bonfire", 653 | [250630] = "", 654 | [250631] = "", 655 | [250632] = "Meeting Stone", 656 | [250633] = "Meeting Stone (Baradin Hold)", 657 | [250634] = "Meeting Stone (Glittermurk Mines)", 658 | [250635] = "Meeting Stone (Uldum)", 659 | [250636] = "Ssaggh ez oou Uul\'gwa Ryiu", 660 | [250637] = "Ssaggh ez oou Par\'okoth Vwyq", 661 | [250638] = "Ssagh ez oou Yyg\'far Vwah", 662 | [250639] = "Ssaggh ez oou Lwhuk Ak\'agthshi", 663 | [250640] = "Westfall", 664 | [250641] = "Northshire Abbey", 665 | [250642] = "Stormwind City", 666 | [250643] = "Northshire Abbey", 667 | [250644] = "Redridge", 668 | [250645] = "Goldshire", 669 | [250646] = "Southshore Sentinel", 670 | [250647] = "Southshore Sentinel", 671 | [250648] = "Karazhan Alchemy Tome", 672 | [250649] = "How to Cook Forty Humans", 673 | [250650] = "Unscrying Glyph", 674 | [250651] = "Tackle Box", 675 | [250652] = "Mycobloom", 676 | [250653] = "Black Forge Lava", 677 | [250654] = "Seething Ore", 678 | [250655] = "Buzzing Ore", 679 | [250656] = "Grundel\'s Bracers", 680 | [250657] = "Grundel\'s Chestpiece", 681 | [250658] = "Grundel\'s Hammer", 682 | [250659] = "Felix\'s Box", 683 | [250660] = "Felix\'s Chest", 684 | [250661] = "Felix\'s Bucket of Bolts", 685 | [250662] = "Relic of the Past", 686 | [250663] = "Sun-Ripened Banana", 687 | [250664] = "Tropical Seashell", 688 | [250665] = "Palm Frond Scroll", 689 | [250666] = "First Tablet of Bwonsamdi", 690 | [250667] = "Second Tablet of Bwonsamdi", 691 | [250668] = "Darkspear Isle Guardpost", 692 | [250669] = "Empty Signpost", 693 | [250670] = "Sen\'jin Village", 694 | [250671] = "Empty Signpost", 695 | [250672] = "Darkspear Isle", 696 | [250673] = "Sen\'jin Village", 697 | [250674] = "Gnomish Emergency Assembly Route", 698 | [250675] = "Banner of Heritage", 699 | [250676] = "Back Door", 700 | [250677] = "Springsocket Guard Post", 701 | [250678] = "Thorium Brotherhood Guard Post", 702 | [250679] = "Volchan\'s Heart", 703 | [250680] = "Abandoned Lockbox", 704 | -- Placeholder IDs 705 | [4000077] = "Memento of Archimonde", 706 | [5000001] = "Mana Berry Bush", 707 | [5000002] = "Ironforge Airfield Lake", 708 | [5000003] = "Corehound Manure", 709 | [5000004] = "Sander\'s Chest", 710 | [5000005] = "North Plague Cauldron", 711 | [5000006] = "South Plague Cauldron", 712 | [5000007] = "South West Plague Cauldron", 713 | [5000008] = "Noxious Glade Cauldron", 714 | [5000009] = "Fungal Vale Cauldron", 715 | [5000010] = "Strange Pylon Tanaris", 716 | [5000011] = "Strange Pylon Silithus", 717 | [5000012] = "Valley of Honor Pond", 718 | [5000013] = "Lake Elune\'ara", 719 | [5000014] = "Deadwind Pass River", 720 | [5000015] = "Winterspring Waters", 721 | [5000016] = "Blackwood Lake", 722 | [5000017] = "Thoridal River", 723 | [5000018] = "Blackrock Magma", 724 | [5000019] = "Un\'goro Waters", 725 | [5000020] = "Blasted Lands Coast", 726 | [5000021] = "Azshara Coast", 727 | [5000022] = "The Slag Pit", 728 | [5000023] = "Sandsorrow Pond", 729 | [5000024] = "Quel\'Danil Pond", 730 | [5000025] = "The Forgotten Coast", 731 | [5000026] = "Swamp Water", 732 | [5000027] = "Springsocket Pond", 733 | [5000028] = "Dustwallow Bay", 734 | [5000029] = "Lake Nazferiti", 735 | [5000030] = "The Veiled Sea", 736 | [5000031] = "Arathi Waters", 737 | [5000032] = "Darkcloud Pinnacle Pond", 738 | [5000033] = "Menethil Bay", 739 | [5000034] = "Western Strand", 740 | [5000035] = "Ashenvale Lake", 741 | [5000036] = "Mirkfallon Lake", 742 | [5000037] = "The Sepulcher", 743 | [5000038] = "Splintertree Mine", 744 | [5000039] = "Crystal Cave", 745 | [5000040] = "Fire Plume Ridge", 746 | [5000041] = "Shaman Shrine", -- Darkspear Isle 747 | [5000050] = "Bloodhoof Village Inn Entrance", 748 | [5000051] = "Call to Skirmish: Hillsbrad Foothills", 749 | [5000052] = "Call to Skirmish: Stonetalon Mountains", 750 | [5000053] = "Call to Skirmish: Ashenvale", 751 | [5000054] = "Call to Skirmish: Thousand Needles", 752 | [5000055] = "Call to Skirmish: Alterac Mountains", 753 | [5000056] = "Call to Skirmish: Desolace", 754 | [5000057] = "Call to Skirmish: Arathi Highlands", 755 | [5000058] = "Call to Skirmish: Swamp of Sorrows", 756 | [5000059] = "Call to Skirmish: Badlands", 757 | [5000060] = "Call to Skirmish: The Hinterlands", 758 | [5000061] = "Call to Skirmish: Stranglethorn Vale", 759 | [5000062] = "Call to Skirmish: Tanaris", 760 | [5000063] = "Call to Skirmish: Felwood", 761 | [5000064] = "Call to Skirmish: Azshara", 762 | [5000065] = "Call to Skirmish: Un\'Goro Crater", 763 | [5000066] = "Call to Skirmish: Searing Gorge", 764 | [5000067] = "Call to Skirmish: Western Plaguelands", 765 | [5000068] = "Call to Skirmish: Burning Steppes", 766 | [5000069] = "Call to Skirmish: Eastern Plaguelands", 767 | [5000070] = "Call to Skirmish: Feralas", 768 | [5000071] = "Call to Skirmish: Winterspring", 769 | [5000072] = "Call to Skirmish: Dustwallow Marsh", 770 | [5000073] = "Call to Skirmish: Blasted Lands", 771 | [5000074] = "Call to Skirmish: Silithus", 772 | } -------------------------------------------------------------------------------- /patchtable.lua: -------------------------------------------------------------------------------- 1 | local loc = GetLocale() 2 | local dbs = { "items", "quests", "quests-itemreq", "objects", "units", "zones", "professions", "areatrigger", "refloot" } 3 | local noloc = { "items", "quests", "objects", "units" } 4 | 5 | -- Patch databases to merge ProjectEpoch data 6 | local function patchtable(base, diff) 7 | for k, v in pairs(diff) do 8 | if type(v) == "string" and v == "_" then 9 | base[k] = nil 10 | else 11 | base[k] = v 12 | end 13 | end 14 | end 15 | 16 | local loc_core, loc_update 17 | for _, db in pairs(dbs) do 18 | if pfDB[db]["data-epoch"] then 19 | patchtable(pfDB[db]["data"], pfDB[db]["data-epoch"]) 20 | end 21 | 22 | for loc, _ in pairs(pfDB.locales) do 23 | if pfDB[db][loc] and pfDB[db][loc.."-epoch"] then 24 | loc_update = pfDB[db][loc.."-epoch"] or pfDB[db]["enUS-epoch"] 25 | patchtable(pfDB[db][loc], loc_update) 26 | end 27 | end 28 | end 29 | 30 | loc_core = pfDB["professions"][loc] or pfDB["professions"]["enUS"] 31 | loc_update = pfDB["professions"][loc.."-epoch"] or pfDB["professions"]["enUS-epoch"] 32 | if loc_update then patchtable(loc_core, loc_update) end 33 | 34 | if pfDB["minimap-epoch"] then patchtable(pfDB["minimap"], pfDB["minimap-epoch"]) end 35 | if pfDB["meta-epoch"] then patchtable(pfDB["meta"], pfDB["meta-epoch"]) end 36 | 37 | -- Update bitmasks to include custom races if needed 38 | -- if pfDB.bitraces then 39 | -- pfDB.bitraces[256] = "Goblin" 40 | -- pfDB.bitraces[512] = "BloodElf" 41 | -- end 42 | 43 | -- Use wowhead database url for now 44 | pfQuest.dburl = "https://epochhead.com/?quest=" 45 | 46 | -- Disable Minimap in custom dungeon maps 47 | function pfMap:HasMinimap(map_id) 48 | -- disable dungeon minimap 49 | local has_minimap = not IsInInstance() 50 | 51 | -- enable dungeon minimap if continent is less then 3 (e.g AV) 52 | if IsInInstance() and GetCurrentMapContinent() < 3 then 53 | has_minimap = true 54 | end 55 | 56 | return has_minimap 57 | end 58 | 59 | -- Reload all pfQuest internal database shortcuts 60 | pfDatabase:Reload() 61 | 62 | -- Automatically clear quest cache if new quests have been found 63 | local updatecheck = CreateFrame("Frame") 64 | updatecheck:RegisterEvent("PLAYER_ENTERING_WORLD") 65 | updatecheck:SetScript("OnEvent", function() 66 | if pfDB["quests"]["data-epoch"] then 67 | -- count all known epoch quests 68 | local count = 0 69 | for k, v in pairs(pfDB["quests"]["data-epoch"]) do 70 | count = count + 1 71 | end 72 | 73 | pfQuest:Debug("Project Epoch loaded with |cff33ffcc" .. count .. "|r quests.") 74 | 75 | -- check if the last count differs to the current amount of quests 76 | if not pfQuest_epochcount or pfQuest_epochcount ~= count then 77 | -- remove quest cache to force reinitialisation of all quests. 78 | pfQuest:Debug("New quests found. Reloading |cff33ffccCache|r") 79 | pfQuest_questcache = {} 80 | end 81 | 82 | -- write current count to the saved variable 83 | pfQuest_epochcount = count 84 | end 85 | end) 86 | 87 | local originalSlashHandler = SlashCmdList["PFDB"] 88 | 89 | SlashCmdList["PFDB"] = originalSlashHandler 90 | 91 | function pfDatabase:QueryServer() 92 | if not QueryQuestsCompleted then 93 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Option is not available on your server.") 94 | return 95 | end 96 | QueryQuestsCompleted() 97 | local frame = CreateFrame("Frame") 98 | frame:RegisterEvent("QUEST_QUERY_COMPLETE") 99 | local function OnQuestQueryComplete() 100 | frame:UnregisterEvent("QUEST_QUERY_COMPLETE") 101 | local completedQuests = GetQuestsCompleted() 102 | if type(completedQuests) == "table" then 103 | local count = 0 104 | local closed = 0 105 | 106 | -- First pass: mark all completed quests 107 | for questID, _ in pairs(completedQuests) do 108 | pfQuest_history[questID] = { time(), UnitLevel("player") } 109 | count = count + 1 110 | end 111 | 112 | -- Second pass: auto-close mutually exclusive quests 113 | for questID, _ in pairs(pfQuest_history) do 114 | local questData = pfDB["quests"]["data"][questID] 115 | if questData and questData["close"] then 116 | for _, closedQuestID in pairs(questData["close"]) do 117 | if not pfQuest_history[closedQuestID] then 118 | pfQuest_history[closedQuestID] = { time(), UnitLevel("player") } 119 | closed = closed + 1 120 | end 121 | end 122 | end 123 | end 124 | 125 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Found " .. count .. " completed quests.") 126 | if closed > 0 then 127 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Auto-closed " .. closed .. " mutually exclusive quests.") 128 | end 129 | pfQuest:ResetAll() 130 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Query Complete. Please /reload to save the changes.") 131 | elseif completedQuests == nil then 132 | print("Error: GetQuestsCompleted() returned nil.") 133 | else 134 | print("Error: GetQuestsCompleted() did not return a valid table. Value: ", completedQuests) 135 | end 136 | end 137 | frame:SetScript("OnEvent", OnQuestQueryComplete) 138 | end 139 | 140 | function pfDatabase:PrintQuestData() 141 | local completedQuests = GetQuestsCompleted() 142 | if completedQuests then 143 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Raw quest data:") 144 | for questID, data in pairs(completedQuests) do 145 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuestID: " .. questID .. " = " .. tostring(data)) 146 | end 147 | else 148 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: No quest data available. Try running QueryQuestsCompleted() first.") 149 | end 150 | end 151 | 152 | pfQuest_CompletedQuestData = pfQuest_CompletedQuestData or {} 153 | 154 | function pfDatabase:SaveCompletedQuests() 155 | if not QueryQuestsCompleted then 156 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Option is not available on your server.") 157 | return 158 | end 159 | 160 | QueryQuestsCompleted() 161 | local frame = CreateFrame("Frame") 162 | frame:RegisterEvent("QUEST_QUERY_COMPLETE") 163 | 164 | local function OnQuestQueryComplete() 165 | frame:UnregisterEvent("QUEST_QUERY_COMPLETE") 166 | 167 | local completedQuests = GetQuestsCompleted() 168 | if type(completedQuests) == "table" then 169 | pfQuest_CompletedQuestData = { 170 | data = completedQuests, 171 | timestamp = time(), 172 | characterName = UnitName("player"), 173 | realm = GetRealmName() 174 | } 175 | 176 | local count = 0 177 | for questID, _ in pairs(completedQuests) do 178 | count = count + 1 179 | end 180 | 181 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Saved " .. count .. " completed quests to SavedVariables.") 182 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Data will persist between sessions.") 183 | else 184 | DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffQuest: Error - Could not retrieve quest data.") 185 | end 186 | end 187 | 188 | frame:SetScript("OnEvent", OnQuestQueryComplete) 189 | end 190 | 191 | -- Function to calculate gray level (no XP level) based on WoW's system 192 | local function GetGrayLevel(charLevel) 193 | if charLevel <= 5 then 194 | return 0 -- all mobs give XP 195 | elseif charLevel <= 49 then 196 | return charLevel - math.floor(charLevel / 10) - 5 197 | elseif charLevel == 50 then 198 | return 40 -- charLevel - 10 199 | elseif charLevel <= 59 then 200 | return charLevel - math.floor(charLevel / 5) - 1 201 | else -- level 60-70 202 | return charLevel - 9 203 | end 204 | end 205 | 206 | function pfDatabase:QuestFilter(id, plevel, pclass, prace) 207 | -- hide active quest 208 | if pfQuest.questlog[id] then return end 209 | -- hide completed quests 210 | if pfQuest_history[id] then return end 211 | 212 | -- hide broken quests without names 213 | if not pfDB.quests.loc[id] or not pfDB.quests.loc[id].T then return end 214 | 215 | local quest = pfDB["quests"]["data"][id] 216 | if not quest then return end 217 | 218 | -- hide missing pre-quests 219 | if quest["pre"] then 220 | -- check all pre-quests for one to be completed 221 | local one_complete = nil 222 | for _, prequest in pairs(quest["pre"]) do 223 | if pfQuest_history[prequest] then 224 | one_complete = true 225 | end 226 | end 227 | -- hide if none of the pre-quests has been completed 228 | if not one_complete then return end 229 | end 230 | 231 | -- hide non-available quests for your race 232 | if quest["race"] and not ( bit.band(quest["race"], prace) == prace ) then return end 233 | -- hide non-available quests for your class 234 | if quest["class"] and not ( bit.band(quest["class"], pclass) == pclass ) then return end 235 | -- hide non-available quests for your profession 236 | if quest["skill"] then 237 | local playerSkillLevel = pfDatabase:GetPlayerSkill(quest["skill"]) 238 | if not playerSkillLevel or quest["skillmin"] and playerSkillLevel < quest["skillmin"] then return end 239 | end 240 | -- hide lowlevel quests using WoW's gray level system 241 | if quest["lvl"] and quest["lvl"] <= GetGrayLevel(plevel) and pfQuest_config["showlowlevel"] == "0" then return end 242 | -- hide highlevel quests (or show those that are 3 levels above) 243 | if quest["min"] and quest["min"] > plevel + ( pfQuest_config["showhighlevel"] == "1" and 3 or 0 ) then return end 244 | -- hide event quests 245 | if quest["event"] and pfQuest_config["showfestival"] == "0" then return end 246 | 247 | -- hide PvP quests 248 | if pfQuest_config["epochHidePvPQuests"] == "1" then 249 | local title = pfDB.quests.loc[id].T 250 | if title and not string.find(title, "Alteraci Shilling") and ( 251 | string.find(title, "Warsong") or 252 | string.find(title, "Arathi") or 253 | string.find(title, "Alterac") or 254 | string.find(title, "Battleground") or 255 | string.find(title, "Call to Skirmish") 256 | ) then 257 | return 258 | end 259 | end 260 | 261 | -- hide Commission quests 262 | if pfQuest_config["epochHideCommissionQuests"] == "1" then 263 | local title = pfDB.quests.loc[id].T 264 | if title and string.find(title, "Commission for") then 265 | return 266 | end 267 | end 268 | 269 | -- hide chicken quests 270 | if pfQuest_config["epochHideChickenQuests"] == "1" then 271 | local title = pfDB.quests.loc[id].T 272 | if title and string.find(title, "CLUCK!") then 273 | return 274 | end 275 | end 276 | 277 | -- hide Felwood flowers 278 | if pfQuest_config["epochHideFelwoodFlowers"] == "1" then 279 | local title = pfDB.quests.loc[id].T 280 | if title and ( 281 | title == "Corrupted Windblossom" or 282 | title == "Corrupted Whipper Root" or 283 | title == "Corrupted Songflower" or 284 | title == "Corrupted Night Dragon" 285 | ) then 286 | return 287 | end 288 | end 289 | 290 | return true 291 | end 292 | 293 | -- fix wotlk linking from db menu 294 | pfQuestCompat.InsertQuestLink = function(questid, name) 295 | local questid = questid or 0 296 | local fallback = name or UNKNOWN 297 | local level = pfDB["quests"]["data"][questid] and pfDB["quests"]["data"][questid]["lvl"] or 0 298 | local name = pfDB["quests"]["loc"][questid] and pfDB["quests"]["loc"][questid]["T"] or fallback 299 | local hex = pfUI.api.rgbhex(pfQuestCompat.GetDifficultyColor(level)) 300 | 301 | -- Use the correct editbox for 3.3.5 302 | local editBox = ChatFrame1EditBox or ChatFrameEditBox 303 | 304 | if editBox then 305 | editBox:Show() 306 | if pfQuest_config["questlinks"] == "1" then 307 | -- seems server blocks the other method so I used this 308 | editBox:Insert("\124cffffff00\124Hquest:" .. questid .. ":" .. level .. "\124h[" .. name .. "]\124h\124r") 309 | else 310 | editBox:Insert("[" .. name .. "]") 311 | end 312 | end 313 | end 314 | 315 | local corpseMessage = "" 316 | local wasDeadLastFrame = false 317 | 318 | pfQuest.route.arrow:SetScript("OnUpdate", function() 319 | if not this.parent then return end 320 | 321 | local isCurrentlyDead = UnitIsDead("player") or UnitIsGhost("player") 322 | 323 | if isCurrentlyDead then 324 | if not wasDeadLastFrame then 325 | local corpseMessages = { 326 | "Skill Issue, have fun running back", 327 | "Git Gud Scrub", 328 | "You Died LOL", 329 | "Walk of Shame Initiated", 330 | "Corpse Run Express", 331 | "Better Luck Next Time", 332 | "RIP Your Repair Bill", 333 | "Death Tax Collector Awaits", 334 | "Your Body is Over There Dummy", 335 | "Congratulations, You're Dead", 336 | "Achievement Unlocked: Floor Tank", 337 | "Press F to Pay Respects", 338 | "This is Why We Can't Have Nice Things", 339 | "Maybe Try Reading the Tactics Next Time", 340 | "Outstanding Move, Chief", 341 | "Welcome to the Spirit World", 342 | "Ghost Mode: ACTIVATED", 343 | "That Went Well", 344 | "Professional Grave Digger", 345 | "Another Happy Landing", 346 | "Task Failed Successfully", 347 | "Speedrun: Any% Death Category", 348 | "You've Been Disconnected from Life", 349 | "Error 404: HP Not Found", 350 | "Critical Hit: Your Pride", 351 | "Respawn Timer: Your Dignity", 352 | "New Personal Best: Worst Decision", 353 | "Plot Twist: You're the Bad Guy", 354 | "Congratulations, You Played Yourself", 355 | "Tutorial Complete: How to Die", 356 | "Achievement: First Time?", 357 | "Pro Tip: Don't Die Next Time", 358 | "Your Performance Review: Needs Improvement", 359 | "Status Update: Currently Deceased", 360 | "That's a Bold Strategy Cotton", 361 | "The Afterlife Called, They're Expecting You", 362 | "Death Certificate: Cause of Death - Bad Decision" 363 | } 364 | corpseMessage = corpseMessages[math.random(1, #corpseMessages)] 365 | wasDeadLastFrame = true 366 | end 367 | 368 | local cx, cy = GetCorpseMapPosition() 369 | -- corpse coords are 0–1; ignore if invalid (0,0) 370 | if cx and cy and (cx > 0 or cy > 0) then 371 | local xplayer, yplayer = GetPlayerMapPosition("player") 372 | local dx = (cx - xplayer) * 100 * 1.5 373 | local dy = (cy - yplayer) * 100 374 | 375 | -- Calculate distance to corpse 376 | local corpseDistance = ceil(math.sqrt(dx*dx + dy*dy)*100)/100 377 | 378 | local dir = atan2(dx, -dy) 379 | dir = dir > 0 and (2*math.pi) - dir or -dir 380 | if dir < 0 then dir = dir + 360 end 381 | local angle = math.rad(dir) - pfQuestCompat.GetPlayerFacing() 382 | 383 | -- rotate the arrow model to point at corpse 384 | local cell = modulo(floor(angle / (2*math.pi) * 108 + .5), 108) 385 | local col = modulo(cell, 9) 386 | local row = floor(cell / 9) 387 | this.model:SetTexCoord( 388 | (col * 56)/512, ((col+1)*56)/512, 389 | (row * 42)/512, ((row+1)*42)/512 390 | ) 391 | 392 | this.title:SetText("Corpse") 393 | this.description:SetText("|cffff0000" .. corpseMessage .. "|r") 394 | this.distance:SetText("|cffaaaaaa" .. (pfQuest_Loc["Distance"] or "Distance") .. ": " .. string.format("%.1f", corpseDistance)) 395 | 396 | this:Show() 397 | return 398 | end 399 | else 400 | 401 | if wasDeadLastFrame then 402 | wasDeadLastFrame = false 403 | corpseMessage = "" 404 | end 405 | end 406 | 407 | xplayer, yplayer = GetPlayerMapPosition("player") 408 | wrongmap = xplayer == 0 and yplayer == 0 and true or nil 409 | target = this.parent.coords and this.parent.coords[1] and this.parent.coords[1][4] and this.parent.coords[1] or nil 410 | 411 | if not target or wrongmap or pfQuest_config["arrow"] == "0" then 412 | if invalid and invalid < GetTime() then 413 | this:Hide() 414 | elseif not invalid then 415 | invalid = GetTime() + 1 416 | end 417 | 418 | return 419 | else 420 | invalid = nil 421 | end 422 | 423 | xDelta = (target[1] - xplayer*100)*1.5 424 | yDelta = (target[2] - yplayer*100) 425 | dir = atan2(xDelta, -(yDelta)) 426 | dir = dir > 0 and (math.pi*2) - dir or -dir 427 | if dir < 0 then dir = dir + 360 end 428 | angle = math.rad(dir) 429 | 430 | player = pfQuestCompat.GetPlayerFacing() 431 | angle = angle - player 432 | perc = math.abs(((math.pi - math.abs(angle)) / math.pi)) 433 | r, g, b = pfUI.api.GetColorGradient(floor(perc*100)/100) 434 | cell = modulo(floor(angle / (math.pi*2) * 108 + 0.5), 108) 435 | column = modulo(cell, 9) 436 | row = floor(cell / 9) 437 | xstart = (column * 56) / 512 438 | ystart = (row * 42) / 512 439 | xend = ((column + 1) * 56) / 512 440 | yend = ((row + 1) * 42) / 512 441 | 442 | area = target[3].priority and target[3].priority or 1 443 | area = max(1, area) 444 | area = min(20, area) 445 | area = (area / 10) + 1 446 | 447 | alpha = target[4] - area 448 | alpha = alpha > 1 and 1 or alpha 449 | alpha = alpha < .5 and .5 or alpha 450 | 451 | texalpha = (1 - alpha) * 2 452 | texalpha = texalpha > 1 and 1 or texalpha 453 | texalpha = texalpha < 0 and 0 or texalpha 454 | 455 | r, g, b = r + texalpha, g + texalpha, b + texalpha 456 | 457 | this.model:SetTexCoord(xstart,xend,ystart,yend) 458 | this.model:SetVertexColor(r,g,b) 459 | 460 | if target ~= lasttarget then 461 | color = defcolor 462 | if tonumber(target[3]["qlvl"]) then 463 | color = pfMap:HexDifficultyColor(tonumber(target[3]["qlvl"])) 464 | end 465 | 466 | if target[3].texture then 467 | this.texture:SetTexture(target[3].texture) 468 | 469 | if target[3].vertex and ( target[3].vertex[1] > 0 470 | or target[3].vertex[2] > 0 471 | or target[3].vertex[3] > 0 ) 472 | then 473 | this.texture:SetVertexColor(unpack(target[3].vertex)) 474 | else 475 | this.texture:SetVertexColor(1,1,1,1) 476 | end 477 | else 478 | this.texture:SetTexture(pfQuestConfig.path.."\\img\\node") 479 | this.texture:SetVertexColor(pfMap.str2rgb(target[3].title)) 480 | end 481 | 482 | local level = target[3].qlvl and "[" .. target[3].qlvl .. "] " or "" 483 | this.title:SetText(color..level..target[3].title.."|r") 484 | local desc = target[3].description or "" 485 | if not pfUI or not pfUI.uf then 486 | this.description:SetTextColor(1,.9,.7,1) 487 | desc = string.gsub(desc, "ff33ffcc", "ffffffff") 488 | end 489 | this.description:SetText(desc.."|r.") 490 | end 491 | 492 | local distance = floor(target[4]*10)/10 493 | if distance ~= this.distance.number then 494 | this.distance:SetText("|cffaaaaaa" .. pfQuest_Loc["Distance"] .. ": "..string.format("%.1f", distance)) 495 | this.distance.number = distance 496 | end 497 | 498 | this.texture:SetAlpha(texalpha) 499 | this.model:SetAlpha(alpha) 500 | end) 501 | 502 | -- Item Drop System 503 | local originalSearchQuestID = pfDatabase.SearchQuestID 504 | pfDatabase.SearchQuestID = function(self, id, meta, maps) 505 | 506 | maps = originalSearchQuestID(self, id, meta, maps) 507 | 508 | local quests = pfDB["quests"]["data"] 509 | local items = pfDB["items"]["data"] 510 | local units = pfDB["units"]["data"] 511 | local objects = pfDB["objects"]["data"] 512 | local refloot = pfDB["refloot"]["data"] 513 | 514 | if not quests[id] then return maps end 515 | 516 | if pfQuest_config["epochHideItemDrops"] == "1" then 517 | return maps 518 | end 519 | 520 | if pfQuest_config["currentquestgivers"] == "1" then 521 | if quests[id]["start"] and not meta["qlogid"] then 522 | if quests[id]["start"]["I"] then 523 | for _, item in pairs(quests[id]["start"]["I"]) do 524 | if items[item] then 525 | local drop_sources = {} 526 | local sources_with_levels = {} 527 | 528 | if items[item]["U"] then 529 | for unit, chance in pairs(items[item]["U"]) do 530 | local unit_name = pfDB["units"]["loc"][unit] 531 | if unit_name and not drop_sources[unit_name] then 532 | drop_sources[unit_name] = true 533 | local unit_level = units[unit] and units[unit]["lvl"] or "?" 534 | sources_with_levels[unit_name] = {level = unit_level, chance = chance} 535 | end 536 | end 537 | end 538 | 539 | if items[item]["O"] then 540 | for object, chance in pairs(items[item]["O"]) do 541 | local obj_name = pfDB["objects"]["loc"][object] 542 | if obj_name and not drop_sources[obj_name] then 543 | drop_sources[obj_name] = true 544 | sources_with_levels[obj_name] = {level = "Object", chance = chance} 545 | end 546 | end 547 | end 548 | 549 | if items[item]["R"] then 550 | for ref, chance in pairs(items[item]["R"]) do 551 | if refloot[ref] then 552 | if refloot[ref]["U"] then 553 | for unit in pairs(refloot[ref]["U"]) do 554 | local unit_name = pfDB["units"]["loc"][unit] 555 | if unit_name and not drop_sources[unit_name] then 556 | drop_sources[unit_name] = true 557 | local unit_level = units[unit] and units[unit]["lvl"] or "?" 558 | sources_with_levels[unit_name] = {level = unit_level, chance = chance} 559 | end 560 | end 561 | end 562 | 563 | if refloot[ref]["O"] then 564 | for object in pairs(refloot[ref]["O"]) do 565 | local obj_name = pfDB["objects"]["loc"][object] 566 | if obj_name and not drop_sources[obj_name] then 567 | drop_sources[obj_name] = true 568 | sources_with_levels[obj_name] = {level = "Object", chance = chance} 569 | end 570 | end 571 | end 572 | end 573 | end 574 | end 575 | 576 | local sources_text = "" 577 | local source_count = 0 578 | local display_count = 0 579 | for source_name in pairs(drop_sources) do 580 | source_count = source_count + 1 581 | end 582 | 583 | for source_name in pairs(drop_sources) do 584 | display_count = display_count + 1 585 | if display_count > 1 then sources_text = sources_text .. ", " end 586 | sources_text = sources_text .. source_name 587 | if display_count >= 3 then 588 | if source_count > 3 then 589 | sources_text = sources_text .. " (+" .. (source_count - 3) .. " more)" 590 | end 591 | break 592 | end 593 | end 594 | 595 | local best_source = nil 596 | local best_count = 0 597 | 598 | if items[item]["U"] then 599 | for unit, chance in pairs(items[item]["U"]) do 600 | if units[unit] and units[unit]["coords"] then 601 | local count = table.getn(units[unit]["coords"]) 602 | if count > best_count then 603 | best_count = count 604 | best_source = {type = "unit", id = unit} 605 | end 606 | end 607 | end 608 | end 609 | 610 | if items[item]["O"] then 611 | for object, chance in pairs(items[item]["O"]) do 612 | if objects[object] and objects[object]["coords"] then 613 | local count = table.getn(objects[object]["coords"]) 614 | if count > best_count then 615 | best_count = count 616 | best_source = {type = "object", id = object} 617 | end 618 | end 619 | end 620 | end 621 | 622 | if items[item]["R"] then 623 | for ref, chance in pairs(items[item]["R"]) do 624 | if refloot[ref] then 625 | if refloot[ref]["U"] then 626 | for unit in pairs(refloot[ref]["U"]) do 627 | if units[unit] and units[unit]["coords"] then 628 | local count = table.getn(units[unit]["coords"]) 629 | if count > best_count then 630 | best_count = count 631 | best_source = {type = "unit", id = unit} 632 | end 633 | end 634 | end 635 | end 636 | 637 | if refloot[ref]["O"] then 638 | for object in pairs(refloot[ref]["O"]) do 639 | if objects[object] and objects[object]["coords"] then 640 | local count = table.getn(objects[object]["coords"]) 641 | if count > best_count then 642 | best_count = count 643 | best_source = {type = "object", id = object} 644 | end 645 | end 646 | end 647 | end 648 | end 649 | end 650 | end 651 | 652 | if best_source then 653 | local coords_table = best_source.type == "unit" and units[best_source.id]["coords"] or objects[best_source.id]["coords"] 654 | 655 | local zones_coords = {} 656 | for _, data in pairs(coords_table) do 657 | local x, y, zone = unpack(data) 658 | if zone > 0 and not zones_coords[zone] then 659 | zones_coords[zone] = {x, y} 660 | end 661 | end 662 | 663 | for zone, coords in pairs(zones_coords) do 664 | local item_meta = {} 665 | for k, v in pairs(meta or {}) do item_meta[k] = v end 666 | 667 | item_meta["QTYPE"] = "ITEM_START" 668 | item_meta["layer"] = 4 669 | item_meta["texture"] = pfQuestConfig.path.."\\img\\available" 670 | --item_meta["vertex"] = { 0.7, 0.4, 1 } 671 | local plevel = UnitLevel("player") 672 | if quests[id]["min"] and quests[id]["min"] > plevel then 673 | item_meta["vertex"] = { 1, .6, .6 } 674 | item_meta["layer"] = 2 675 | elseif quests[id]["lvl"] and quests[id]["lvl"] <= GetGrayLevel(plevel) then 676 | item_meta["vertex"] = { 1, 1, 1 } 677 | item_meta["layer"] = 2 678 | elseif quests[id]["event"] then 679 | item_meta["vertex"] = { .2, .8, 1 } 680 | item_meta["layer"] = 2 681 | end 682 | 683 | item_meta["spawn"] = pfDB["items"]["loc"][item] or UNKNOWN 684 | item_meta["spawnid"] = item 685 | item_meta["item"] = pfDB["items"]["loc"][item] 686 | item_meta["dropsources"] = sources_text 687 | item_meta["dropsources_levels"] = sources_with_levels 688 | item_meta["title"] = item_meta["quest"] or item_meta["spawn"] 689 | item_meta["zone"] = zone 690 | item_meta["x"] = coords[1] 691 | item_meta["y"] = coords[2] 692 | item_meta["level"] = pfQuest_Loc["N/A"] 693 | item_meta["spawntype"] = pfQuest_Loc["Item Drop"] 694 | item_meta["respawn"] = pfQuest_Loc["N/A"] 695 | item_meta["description"] = pfDatabase:BuildQuestDescription(item_meta) 696 | 697 | maps = maps or {} 698 | maps[zone] = maps[zone] and maps[zone] + 0 or 0 699 | pfMap:AddNode(item_meta) 700 | end 701 | end 702 | end 703 | end 704 | end 705 | end 706 | end 707 | 708 | return maps 709 | end 710 | 711 | local originalSearchQuests = pfDatabase.SearchQuests 712 | pfDatabase.SearchQuests = function(self, meta, maps) 713 | maps = originalSearchQuests(self, meta, maps) 714 | 715 | local quests = pfDB["quests"]["data"] 716 | local items = pfDB["items"]["data"] 717 | local units = pfDB["units"]["data"] 718 | local objects = pfDB["objects"]["data"] 719 | local refloot = pfDB["refloot"]["data"] 720 | 721 | if pfQuest_config["epochHideItemDrops"] == "1" then 722 | return maps 723 | end 724 | 725 | local plevel = UnitLevel("player") 726 | local pfaction = UnitFactionGroup("player") 727 | pfaction = pfaction == "Horde" and "H" or pfaction == "Alliance" and "A" or "GM" 728 | 729 | local _, race = UnitRace("player") 730 | local prace = pfDatabase:GetBitByRace(race) 731 | local _, class = UnitClass("player") 732 | local pclass = pfDatabase:GetBitByClass(class) 733 | 734 | for id in pairs(quests) do 735 | if pfDatabase:QuestFilter(id, plevel, pclass, prace) then 736 | -- Additional faction check for quest enders 737 | local validFaction = true 738 | if quests[id]["end"] and quests[id]["end"]["U"] then 739 | validFaction = false 740 | for _, unit in pairs(quests[id]["end"]["U"]) do 741 | if pfDatabase:IsFriendly(unit) then 742 | validFaction = true 743 | break 744 | end 745 | end 746 | end 747 | 748 | if validFaction and quests[id]["start"] and quests[id]["start"]["I"] then 749 | for _, item in pairs(quests[id]["start"]["I"]) do 750 | if items[item] then 751 | local drop_sources = {} 752 | local sources_with_levels = {} 753 | 754 | if items[item]["U"] then 755 | for unit, chance in pairs(items[item]["U"]) do 756 | local unit_name = pfDB["units"]["loc"][unit] 757 | if unit_name and not drop_sources[unit_name] then 758 | drop_sources[unit_name] = true 759 | local unit_level = units[unit] and units[unit]["lvl"] or "?" 760 | sources_with_levels[unit_name] = {level = unit_level, chance = chance} 761 | end 762 | end 763 | end 764 | 765 | if items[item]["O"] then 766 | for object, chance in pairs(items[item]["O"]) do 767 | local obj_name = pfDB["objects"]["loc"][object] 768 | if obj_name and not drop_sources[obj_name] then 769 | drop_sources[obj_name] = true 770 | sources_with_levels[obj_name] = {level = "Object", chance = chance} 771 | end 772 | end 773 | end 774 | 775 | if items[item]["R"] then 776 | for ref, chance in pairs(items[item]["R"]) do 777 | if refloot[ref] then 778 | if refloot[ref]["U"] then 779 | for unit in pairs(refloot[ref]["U"]) do 780 | local unit_name = pfDB["units"]["loc"][unit] 781 | if unit_name and not drop_sources[unit_name] then 782 | drop_sources[unit_name] = true 783 | local unit_level = units[unit] and units[unit]["lvl"] or "?" 784 | sources_with_levels[unit_name] = {level = unit_level, chance = chance} 785 | end 786 | end 787 | end 788 | 789 | if refloot[ref]["O"] then 790 | for object in pairs(refloot[ref]["O"]) do 791 | local obj_name = pfDB["objects"]["loc"][object] 792 | if obj_name and not drop_sources[obj_name] then 793 | drop_sources[obj_name] = true 794 | sources_with_levels[obj_name] = {level = "Object", chance = chance} 795 | end 796 | end 797 | end 798 | end 799 | end 800 | end 801 | 802 | local sources_text = "" 803 | local source_count = 0 804 | local display_count = 0 805 | for source_name in pairs(drop_sources) do 806 | source_count = source_count + 1 807 | end 808 | 809 | for source_name in pairs(drop_sources) do 810 | display_count = display_count + 1 811 | if display_count > 1 then sources_text = sources_text .. ", " end 812 | sources_text = sources_text .. source_name 813 | if display_count >= 3 then 814 | if source_count > 3 then 815 | sources_text = sources_text .. " (+" .. (source_count - 3) .. " more)" 816 | end 817 | break 818 | end 819 | end 820 | 821 | local best_source = nil 822 | local best_count = 0 823 | 824 | if items[item]["U"] then 825 | for unit, chance in pairs(items[item]["U"]) do 826 | if units[unit] and units[unit]["coords"] then 827 | local count = table.getn(units[unit]["coords"]) 828 | if count > best_count then 829 | best_count = count 830 | best_source = {type = "unit", id = unit} 831 | end 832 | end 833 | end 834 | end 835 | 836 | if items[item]["O"] then 837 | for object, chance in pairs(items[item]["O"]) do 838 | if objects[object] and objects[object]["coords"] then 839 | local count = table.getn(objects[object]["coords"]) 840 | if count > best_count then 841 | best_count = count 842 | best_source = {type = "object", id = object} 843 | end 844 | end 845 | end 846 | end 847 | 848 | if items[item]["R"] then 849 | for ref, chance in pairs(items[item]["R"]) do 850 | if refloot[ref] then 851 | if refloot[ref]["U"] then 852 | for unit in pairs(refloot[ref]["U"]) do 853 | if units[unit] and units[unit]["coords"] then 854 | local count = table.getn(units[unit]["coords"]) 855 | if count > best_count then 856 | best_count = count 857 | best_source = {type = "unit", id = unit} 858 | end 859 | end 860 | end 861 | end 862 | 863 | if refloot[ref]["O"] then 864 | for object in pairs(refloot[ref]["O"]) do 865 | if objects[object] and objects[object]["coords"] then 866 | local count = table.getn(objects[object]["coords"]) 867 | if count > best_count then 868 | best_count = count 869 | best_source = {type = "object", id = object} 870 | end 871 | end 872 | end 873 | end 874 | end 875 | end 876 | end 877 | 878 | if best_source then 879 | local coords_table = best_source.type == "unit" and units[best_source.id]["coords"] or objects[best_source.id]["coords"] 880 | 881 | local zones_coords = {} 882 | for _, data in pairs(coords_table) do 883 | local x, y, zone = unpack(data) 884 | if zone > 0 and not zones_coords[zone] then 885 | zones_coords[zone] = {x, y} 886 | end 887 | end 888 | 889 | for zone, coords in pairs(zones_coords) do 890 | local item_meta = {} 891 | for k, v in pairs(meta or {}) do item_meta[k] = v end 892 | 893 | item_meta["quest"] = pfDB["quests"]["loc"][id] and pfDB["quests"]["loc"][id].T or UNKNOWN 894 | item_meta["questid"] = id 895 | item_meta["QTYPE"] = "ITEM_START" 896 | item_meta["layer"] = 3 897 | item_meta["texture"] = pfQuestConfig.path.."\\img\\available" 898 | --item_meta["vertex"] = { 0.7, 0.4, 1 } 899 | item_meta["spawn"] = pfDB["items"]["loc"][item] or UNKNOWN 900 | item_meta["spawnid"] = item 901 | item_meta["item"] = pfDB["items"]["loc"][item] 902 | item_meta["dropsources"] = sources_text 903 | item_meta["dropsources_levels"] = sources_with_levels 904 | item_meta["title"] = item_meta["quest"] 905 | item_meta["zone"] = zone 906 | item_meta["x"] = coords[1] 907 | item_meta["y"] = coords[2] 908 | item_meta["level"] = pfQuest_Loc["N/A"] 909 | item_meta["spawntype"] = pfQuest_Loc["Item Drop"] 910 | item_meta["respawn"] = pfQuest_Loc["N/A"] 911 | item_meta["qlvl"] = quests[id]["lvl"] 912 | item_meta["qmin"] = quests[id]["min"] 913 | 914 | if quests[id]["min"] and quests[id]["min"] > plevel then 915 | item_meta["vertex"] = { 1, .6, .6 } 916 | item_meta["layer"] = 2 917 | elseif quests[id]["lvl"] and quests[id]["lvl"] <= GetGrayLevel(plevel) then 918 | item_meta["vertex"] = { 1, 1, 1 } 919 | item_meta["layer"] = 2 920 | elseif quests[id]["event"] then 921 | item_meta["vertex"] = { .2, .8, 1 } 922 | item_meta["layer"] = 2 923 | end 924 | 925 | item_meta["description"] = pfDatabase:BuildQuestDescription(item_meta) 926 | 927 | maps = maps or {} 928 | maps[zone] = maps[zone] and maps[zone] + 1 or 1 929 | pfMap:AddNode(item_meta) 930 | end 931 | end 932 | end 933 | end 934 | end 935 | end 936 | end 937 | 938 | return maps 939 | end 940 | 941 | local originalNodeEnter = pfMap.NodeEnter 942 | pfMap.NodeEnter = function() 943 | if not this or not this.node then 944 | if originalNodeEnter then originalNodeEnter() end 945 | return 946 | end 947 | 948 | local hasItemStart = false 949 | local itemStartMeta = nil 950 | for title, meta in pairs(this.node) do 951 | if meta.QTYPE == "ITEM_START" and meta.dropsources_levels then 952 | hasItemStart = true 953 | itemStartMeta = meta 954 | break 955 | end 956 | end 957 | 958 | if hasItemStart and itemStartMeta then 959 | if pfQuestCompat and pfQuestCompat.client and pfQuestCompat.client >= 30300 then 960 | WorldMapPOIFrame.allowBlobTooltip = false 961 | end 962 | 963 | local tooltip = this:GetParent() == WorldMapButton and WorldMapTooltip or GameTooltip 964 | tooltip:SetOwner(this, "ANCHOR_LEFT") 965 | this.spawn = this.spawn or UNKNOWN 966 | tooltip:SetText(this.spawn..(pfQuest_config.showids == "1" and " |cffcccccc("..this.spawnid..")|r" or ""), .3, 1, .8) 967 | tooltip:AddDoubleLine(pfQuest_Loc["Type"] .. ":", (this.spawntype or UNKNOWN), .8,.8,.8, 1,1,1) 968 | 969 | if itemStartMeta.dropsources_levels then 970 | tooltip:AddLine(" ") 971 | tooltip:AddLine("Drops from:", .8,.8,.8) 972 | 973 | local sorted_sources = {} 974 | for source_name, data in pairs(itemStartMeta.dropsources_levels) do 975 | table.insert(sorted_sources, {name = source_name, level = data.level, chance = data.chance or 0}) 976 | end 977 | 978 | table.sort(sorted_sources, function(a, b) 979 | return a.chance > b.chance 980 | end) 981 | 982 | local count = 0 983 | for _, source in ipairs(sorted_sources) do 984 | count = count + 1 985 | if count <= 5 then 986 | tooltip:AddLine(" " .. source.name .. " (" .. source.level .. ") - " .. source.chance .. "%", 1,1,1) 987 | end 988 | end 989 | 990 | if table.getn(sorted_sources) > 5 then 991 | tooltip:AddLine(" (+" .. (table.getn(sorted_sources) - 5) .. " more)", .7,.7,.7) 992 | end 993 | end 994 | 995 | tooltip:AddLine(" ") 996 | 997 | for title, meta in pairs(this.node) do 998 | pfMap:ShowTooltip(meta, tooltip) 999 | end 1000 | 1001 | if pfQuest_config["tooltiphelp"] == "1" then 1002 | local text = pfQuest_Loc["Use -Click To Mark Quest As Done"] 1003 | tooltip:AddLine(text, .6, .6, .6) 1004 | tooltip:Show() 1005 | end 1006 | 1007 | pfMap.highlight = pfQuest_config["mouseover"] == "1" and this.title 1008 | else 1009 | if originalNodeEnter then 1010 | originalNodeEnter() 1011 | end 1012 | end 1013 | end 1014 | 1015 | -- Yoinked from https://github.com/shagu/pfQuest/pull/301 props to BlacRyu 1016 | local original_ResizeNode = pfMap.ResizeNode 1017 | 1018 | -- Make mainmap_inversescale global so we can control it across files 1019 | mainmap_inversescale = 1.0 1020 | 1021 | -- Override ResizeNode to use our global mainmap_inversescale 1022 | function pfMap:ResizeNode(frame, obj) 1023 | local highlight = frame.texture and pfMap.highlightdb[frame][pfMap.highlight] and true or nil 1024 | local target = frame.texture and pfQuest.route and pfQuest.route.IsTarget(frame) or nil 1025 | 1026 | -- set default sizes for different node types 1027 | frame.defsize = (frame.cluster or frame.layer == 4) and 18 or 14 1028 | -- Adjust node size if main map is zoomed in/out 1029 | if (obj ~= "minimap") then 1030 | if (frame.title and pfQuest.icons[frame.title]) or frame.icon then 1031 | -- Adjust for icons being 1 unit smaller than their parent frame 1032 | -- Looks better to keep the icon size constant even if the frame grows a bit. 1033 | frame.defsize = (frame.defsize - 2) * (mainmap_inversescale) + 2 1034 | else 1035 | frame.defsize = frame.defsize * mainmap_inversescale 1036 | end 1037 | end 1038 | 1039 | -- make the current route target visible 1040 | if target then frame.hl:Show() else frame.hl:Hide() end 1041 | 1042 | -- reset frame size except for highlights 1043 | if not highlight then 1044 | frame:SetWidth(frame.defsize) 1045 | frame:SetHeight(frame.defsize) 1046 | end 1047 | end 1048 | 1049 | -- Define ResizeNodes function (doesn't exist in base pfQuest) 1050 | function pfMap:ResizeNodes() 1051 | local i = 1 1052 | if pfMap.pins then 1053 | while pfMap.pins[i] and pfMap.pins[i]:IsShown() do 1054 | pfMap:ResizeNode(pfMap.pins[i]) 1055 | i = i + 1 1056 | end 1057 | end 1058 | end 1059 | 1060 | -- Handle map scale changes 1061 | function pfMap:OnMapScaleChanged(frame, scale, hookedfunction) 1062 | hookedfunction(frame, scale) 1063 | 1064 | local new_inversescale = 1.0 / WorldMapButton:GetEffectiveScale() 1065 | if (mainmap_inversescale ~= new_inversescale) then 1066 | mainmap_inversescale = new_inversescale 1067 | pfMap:ResizeNodes() 1068 | end 1069 | end 1070 | 1071 | -- Listen for WorldMapFrame scale changes 1072 | local pfHookWorldMapFrame_SetScale = WorldMapFrame.SetScale 1073 | WorldMapFrame.SetScale = function(frame, scale) pfMap:OnMapScaleChanged(frame, scale, pfHookWorldMapFrame_SetScale) end 1074 | 1075 | -- Listen for WorldMapDetailFrame scale changes 1076 | local pfHookWorldMapDetailFrame_SetScale = WorldMapDetailFrame.SetScale 1077 | WorldMapDetailFrame.SetScale = function(frame, scale) pfMap:OnMapScaleChanged(frame, scale, pfHookWorldMapDetailFrame_SetScale) end 1078 | 1079 | -- Listen for WorldMapButton scale changes 1080 | local pfHookWorldMapButton_SetScale = WorldMapButton.SetScale 1081 | WorldMapButton.SetScale = function(frame, scale) pfMap:OnMapScaleChanged(frame, scale, pfHookWorldMapButton_SetScale) end 1082 | 1083 | function pfDatabase:BuildQuestDescription(meta) 1084 | if not meta.title or not meta.quest or not meta.QTYPE then return meta.description end 1085 | 1086 | if meta.QTYPE == "NPC_START" then 1087 | return string.format(pfQuest_Loc["Speak with |cff33ffcc%s|r to obtain |cffffcc00[!]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), (meta.quest or UNKNOWN)) 1088 | elseif meta.QTYPE == "OBJECT_START" then 1089 | return string.format(pfQuest_Loc["Interact with |cff33ffcc%s|r to obtain |cff66ff66[!]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), (meta.quest or UNKNOWN)) 1090 | elseif meta.QTYPE == "ITEM_START" then 1091 | if meta.dropsources and meta.dropsources ~= "" then 1092 | return string.format(pfQuest_Loc["Loot |cff33ffcc[%s]|r from |cff33ffcc%s|r to obtain |cff66ff66[!]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), meta.dropsources, (meta.quest or UNKNOWN)) 1093 | else 1094 | return string.format(pfQuest_Loc["Loot |cff33ffcc[%s]|r to obtain |cff66ff66[!]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), (meta.quest or UNKNOWN)) 1095 | end 1096 | elseif meta.QTYPE == "NPC_END" then 1097 | return string.format(pfQuest_Loc["Speak with |cff33ffcc%s|r to complete |cffffcc00[?]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), (meta.quest or UNKNOWN)) 1098 | elseif meta.QTYPE == "OBJECT_END" then 1099 | return string.format(pfQuest_Loc["Interact with |cff33ffcc%s|r to complete |cffffcc00[?]|cff33ffcc %s|r"], (meta.spawn or UNKNOWN), (meta.quest or UNKNOWN)) 1100 | elseif meta.QTYPE == "UNIT_OBJECTIVE" then 1101 | if pfDatabase:IsFriendly(meta.spawnid) then 1102 | return string.format(pfQuest_Loc["Talk to |cff33ffcc%s|r"], (meta.spawn or UNKNOWN)) 1103 | else 1104 | return string.format(pfQuest_Loc["Kill |cff33ffcc%s|r"], (meta.spawn or UNKNOWN)) 1105 | end 1106 | elseif meta.QTYPE == "UNIT_OBJECTIVE_ITEMREQ" then 1107 | return string.format(pfQuest_Loc["Use |cff33ffcc%s|r on |cff33ffcc%s|r"], (meta.itemreq or UNKNOWN), (meta.spawn or UNKNOWN)) 1108 | elseif meta.QTYPE == "OBJECT_OBJECTIVE" then 1109 | return string.format(pfQuest_Loc["Interact with |cff33ffcc%s|r"], (meta.spawn or UNKNOWN)) 1110 | elseif meta.QTYPE == "OBJECT_OBJECTIVE_ITEMREQ" then 1111 | return string.format(pfQuest_Loc["Use |cff33ffcc%s|r at |cff33ffcc%s|r"], (meta.itemreq or UNKNOWN), (meta.spawn or UNKNOWN)) 1112 | elseif meta.QTYPE == "ITEM_OBJECTIVE_LOOT" then 1113 | return string.format(pfQuest_Loc["Loot |cff33ffcc[%s]|r from |cff33ffcc%s|r"], (meta.item or UNKNOWN), (meta.spawn or UNKNOWN)) 1114 | elseif meta.QTYPE == "ITEM_OBJECTIVE_USE" then 1115 | return string.format(pfQuest_Loc["Loot and/or Use |cff33ffcc[%s]|r from |cff33ffcc%s|r"], (meta.item or UNKNOWN), (meta.spawn or UNKNOWN)) 1116 | elseif meta.QTYPE == "AREATRIGGER_OBJECTIVE" then 1117 | return string.format(pfQuest_Loc["Explore |cff33ffcc%s|r"], (meta.spawn or UNKNOWN)) 1118 | elseif meta.QTYPE == "ZONE_OBJECTIVE" then 1119 | return string.format(pfQuest_Loc["Use Quest Item at |cff33ffcc%s|r"], (meta.spawn or UNKNOWN)) 1120 | end 1121 | end -------------------------------------------------------------------------------- /pfQuest-worldmap.lua: -------------------------------------------------------------------------------- 1 | local original_UpdateNodes = pfMap.UpdateNodes 2 | local continentPins = {} 3 | local maxContinentPins = 2000 -- guessing here but more is better if possible 4 | 5 | -- ============================================================================ 6 | -- WorldMapArea.dbc to mapData Conversion Formula 7 | -- ============================================================================ 8 | -- 9 | -- WorldMapArea Format: LocLeft, LocRight, LocTop, LocBottom 10 | -- mapData Format: {width, height, left, top} 11 | -- 12 | -- Conversion: || means absolute value 13 | -- width = |LocRight - LocLeft| 14 | -- height = |LocTop - LocBottom| 15 | -- left = LocLeft 16 | -- top = LocTop 17 | -- 18 | -- Example: 19 | -- MPQ: "2938.36","1880.03","10238.3","9532.59" 20 | -- Result: {1058.33, 705.71, 2938.36, 10238.3} 21 | -- ============================================================================ 22 | 23 | local mapData = { 24 | -- Eastern Kingdoms zones (instance 0) 25 | -- Format: {width, height, left, top} in world coordinates 26 | [1429] = {3470.84, 2314.62, 1535.42, -7939.58}, -- Elwynn Forest 27 | [1436] = {3500.00, 2333.3, 3016.67, -9400}, -- Westfall 28 | [1433] = {2170.84, 1447.9, -1570.83, -8575}, -- Redridge Mountains 29 | [1431] = {2700.00, 1800.03, 833.333, -9716.67}, -- Duskwood 30 | [1434] = {6381.25, 4254.1, 2220.83, -11168.8}, -- Stranglethorn Vale 31 | [1453] = {1737.50, 1158.34, 1722.92, -7995.83}, -- Stormwind City 32 | [1426] = {4925.00, 3283.34, 1802.08, -3877.08}, -- Dun Morogh 33 | [1455] = {790.63, 527.61, -713.591, -4569.24}, -- Ironforge 34 | [1432] = {2758.33, 1839.58, -1993.75, -4487.5}, -- Loch Modan 35 | [1437] = {4135.42, 2756.25, -389.583, -2147.92}, -- Wetlands 36 | [1424] = {3200.00, 2133.33, 1066.67, 400}, -- Hillsbrad Foothills 37 | [1416] = {2800.00, 1866.667, 783.333, 1500}, -- Alterac Mountains 38 | [1417] = {3600.00, 2400.00, -866.667, -133.333}, -- Arathi Highlands 39 | [1425] = {3850.00, 2566.67, -1575, 1466.67}, -- The Hinterlands 40 | [1420] = {4518.75, 3012.5, 3033.33, 3837.5}, -- Tirisfal Glades 41 | [1421] = {4200.00, 2800.00, 3450, 1666.67}, -- Silverpine Forest 42 | [1458] = {959.38, 640.1, 873.193, 1877.94}, -- Undercity 43 | [1422] = {4300.00, 2866.67, 416.667, 3366.67}, -- Western Plaguelands 44 | [1423] = {4031.25, 2687.5, -2287.5, 3704.17}, -- Eastern Plaguelands 45 | [1418] = {2487.50, 1658.34, -2079.17, -5889.58}, -- Badlands 46 | [1427] = {2231.253, 1487.5, -322.917, -6100}, -- Searing Gorge 47 | [1428] = {2929.163, 1952.08, -266.667, -7031.25}, -- Burning Steppes 48 | [1435] = {2293.75, 1529.17, -2222.92, -9620.83}, -- Swamp of Sorrows 49 | [1419] = {3350.00, 2233.30, -1241.67, -10566.7}, -- Blasted Lands 50 | [1430] = {2500.00, 1666.63, -833.333, -9866.67}, -- Deadwind Pass 51 | -- Kalimdor zones (instance 1) 52 | -- Format: {width, height, left, top} in world coordinates 53 | [1438] = {5091.66, 3393.7, 3814.58, 11831.2}, -- Teldrassil (zone 141) 54 | [1457] = {1058.33, 705.71, 2938.36, 10238.3}, -- Darnassus (zone 1657) 55 | [1439] = {6550.00, 4366.66, 2941.67, 8333.33}, -- Darkshore (zone 148) 56 | [1440] = {5766.67, 3843.75, 1700, 4672.92}, -- Ashenvale (zone 331) 57 | [1442] = {4883.33, 3256.25, 3245.83, 2916.67}, -- Stonetalon Mountains (zone 406) 58 | [1413] = {10133.34, 6756.25, 2622.92, 1612.5}, -- The Barrens (zone 17) 59 | [1411] = {5287.5, 3525, -1962.5, 1808.33}, -- Durotar (zone 14) 60 | [1454] = {1402.61, 935.42, -3680.6, 2273.88}, -- Orgrimmar (zone 1637) 61 | [1412] = {5137.5, 3425.00, 2047.92, -272.917}, -- Mulgore (zone 215) 62 | [1456] = {1043.75, 695.83, 516.667, -850}, -- Thunder Bluff (zone 1638) 63 | [1443] = {4495.83, 2997.91, 4233.33, 452.083}, -- Desolace (zone 405) 64 | [1444] = {6950.00, 4633.33, 5441.67, -2366.67}, -- Feralas (zone 357) 65 | [1441] = {4400.00, 2933.33, -433.333, -3966.67}, -- Thousand Needles (zone 400) 66 | [1446] = {6900.00, 4600.00, -218.75, -5875}, -- Tanaris (zone 440) 67 | [1449] = {3700.00, 2466.66, 533.333, -5966.67}, -- Un'Goro Crater (zone 490) 68 | [1451] = {3483.33, 2322.92, 2537.5, -5958.33}, -- Silithus (zone 1377) 69 | [1445] = {5250.00, 3500.00, -975, -2033.33}, -- Dustwallow Marsh (zone 15) 70 | [1452] = {7100.00, 4733.33, -316.667, 8533.33}, -- Winterspring (zone 618) 71 | [1447] = {5070.84, 3381.25, -3277.08, 5341.67}, -- Azshara (zone 16) 72 | [1448] = {5750.00, 3833.33, 1641.67, 7133.33}, -- Felwood (zone 361) 73 | [1450] = {2308.33, 1539.59, -1381.25, 8491.67}, -- Moonglade (zone 493) 74 | -- Eastern Kingdoms 75 | [1415] = {40741.18, 27149.69, 18171.97, 11176.34}, -- Eastern Kingdoms continent 76 | -- Kalimdor continent 77 | [1414] = {36799.81, 24533.20, 17066.60, 12799.90} -- Kalimdor continent 78 | } 79 | 80 | local zoneToUiMapID = { 81 | -- Eastern Kingdoms 82 | [12] = 1429, 83 | [40] = 1436, 84 | [44] = 1433, 85 | [10] = 1431, 86 | [33] = 1434, 87 | [1519] = 1453, 88 | [1] = 1426, 89 | [1537] = 1455, 90 | [38] = 1432, 91 | [11] = 1437, 92 | [267] = 1424, 93 | [36] = 1416, 94 | [45] = 1417, 95 | [47] = 1425, 96 | [85] = 1420, 97 | [130] = 1421, 98 | [1497] = 1458, 99 | [28] = 1422, 100 | [139] = 1423, 101 | [3] = 1418, 102 | [51] = 1427, 103 | [46] = 1428, 104 | [8] = 1435, 105 | [4] = 1419, 106 | [41] = 1430, 107 | -- Kalimdor 108 | [141] = 1438, 109 | [1657] = 1457, 110 | [148] = 1439, 111 | [331] = 1440, 112 | [406] = 1442, 113 | [17] = 1413, 114 | [14] = 1411, 115 | [1637] = 1454, 116 | [215] = 1412, 117 | [1638] = 1456, 118 | [405] = 1443, 119 | [357] = 1444, 120 | [400] = 1441, 121 | [440] = 1446, 122 | [490] = 1449, 123 | [1377] = 1451, 124 | [15] = 1445, 125 | [618] = 1452, 126 | [16] = 1447, 127 | [361] = 1448, 128 | [493] = 1450 129 | } 130 | 131 | -- Get zone data from either main or epoch databases 132 | local function GetZoneData(zoneID) 133 | local zoneData = pfDB and pfDB["zones"] and pfDB["zones"]["data"] and pfDB["zones"]["data"][zoneID] 134 | if not zoneData then 135 | zoneData = pfDB and pfDB["zones"] and pfDB["zones"]["data-epoch"] and pfDB["zones"]["data-epoch"][zoneID] 136 | end 137 | return zoneData 138 | end 139 | 140 | -- Continent assignments 141 | local zoneContinent = { 142 | -- Eastern Kingdoms = 2 (continent 0 in game) 143 | [1] = 2, 144 | [3] = 2, 145 | [4] = 2, 146 | [8] = 2, 147 | [10] = 2, 148 | [11] = 2, 149 | [12] = 2, 150 | [28] = 2, 151 | [33] = 2, 152 | [36] = 2, 153 | [38] = 2, 154 | [40] = 2, 155 | [41] = 2, 156 | [44] = 2, 157 | [45] = 2, 158 | [46] = 2, 159 | [47] = 2, 160 | [51] = 2, 161 | [85] = 2, 162 | [130] = 2, 163 | [139] = 2, 164 | [267] = 2, 165 | [1497] = 2, 166 | [1519] = 2, 167 | [1537] = 2, 168 | -- Kalimdor = 1 (continent 1 in game) 169 | [14] = 1, 170 | [15] = 1, 171 | [16] = 1, 172 | [17] = 1, 173 | [141] = 1, 174 | [148] = 1, 175 | [215] = 1, 176 | [331] = 1, 177 | [357] = 1, 178 | [361] = 1, 179 | [400] = 1, 180 | [405] = 1, 181 | [406] = 1, 182 | [440] = 1, 183 | [490] = 1, 184 | [493] = 1, 185 | [618] = 1, 186 | [1377] = 1, 187 | [1637] = 1, 188 | [1638] = 1, 189 | [1657] = 1 190 | } 191 | 192 | local function GetZoneGroup(zoneID) 193 | -- Group related zones together to prevent duplicate NPCs 194 | local zoneGroups = { 195 | teldrassil = {141, 1657}, -- Teldrassil + Darnassus 196 | stormwind = {12, 1519}, -- Elwynn + Stormwind City 197 | ironforge = {1, 1537}, -- Dun Morogh + Ironforge 198 | orgrimmar = {14, 1637}, -- Durotar + Orgrimmar 199 | thunderbluff = {215, 1638}, -- Mulgore + Thunder Bluff 200 | undercity = {85, 1497} -- Tirisfal + Undercity 201 | } 202 | 203 | for group, zones in pairs(zoneGroups) do 204 | for _, zID in pairs(zones) do 205 | if zID == zoneID then 206 | return group 207 | end 208 | end 209 | end 210 | return zoneID 211 | end 212 | 213 | local function GetZoneContinent(zoneID) 214 | if zoneContinent[zoneID] then 215 | return zoneContinent[zoneID] 216 | end 217 | 218 | local zoneData = GetZoneData(zoneID) 219 | if zoneData and zoneData[1] then 220 | local continent = zoneData[1] 221 | if continent == 0 then 222 | continent = 2 223 | elseif continent == 1 then 224 | continent = 1 225 | else 226 | return nil 227 | end 228 | 229 | zoneContinent[zoneID] = continent 230 | return continent 231 | end 232 | 233 | return nil 234 | end 235 | 236 | local function ZoneToWorld(x, y, zoneID) 237 | local uiMapID = zoneToUiMapID[zoneID] 238 | if not uiMapID then 239 | return nil, nil 240 | end 241 | local data = mapData[uiMapID] 242 | if not data then 243 | return nil, nil 244 | end 245 | 246 | local worldX = data[3] - data[1] * (x / 100) 247 | local worldY = data[4] - data[2] * (y / 100) 248 | 249 | return worldX, worldY 250 | end 251 | 252 | local function WorldToContinent(worldX, worldY, continent) 253 | local contData = mapData[continent == 1 and 1414 or 1415] 254 | if not contData then 255 | return nil, nil 256 | end 257 | 258 | local x = (contData[3] - worldX) / contData[1] 259 | local y = (contData[4] - worldY) / contData[2] 260 | 261 | return x, y 262 | end 263 | 264 | local function NodeAnimate(self, max) 265 | return 266 | end 267 | 268 | local inverseMapScale = 1.0 269 | local function ResizeContinentNode(frame) 270 | if not frame.icon then 271 | -- Use config value for regular nodes, fallback to 12 if not set 272 | frame.defsize = tonumber(pfQuest_config["continentNodeSize"]) or 12 273 | frame.defsize = frame.defsize * inverseMapScale 274 | else 275 | -- Use config value for utility NPCs, fallback to 14 if not set 276 | frame.defsize = tonumber(pfQuest_config["continentUtilityNodeSize"]) or 14 277 | -- Compensate for icon's 1 pixel padding so it doesn't shrink down to nothing 278 | frame.defsize = (frame.defsize - 2) * inverseMapScale + 2 279 | end 280 | frame:SetWidth(frame.defsize) 281 | frame:SetHeight(frame.defsize) 282 | frame.hl:SetWidth(frame.defsize) 283 | frame.hl:SetHeight(frame.defsize) 284 | end 285 | 286 | local function ResizeContinentNodes() 287 | local i = 1 288 | if continentPins then 289 | while continentPins[i] and continentPins[i]:IsShown() do 290 | ResizeContinentNode(continentPins[i]) 291 | i = i + 1 292 | end 293 | end 294 | end 295 | 296 | -- Resize icons on map zoom change 297 | local function OnMapScaleChanged(frame, scale, originalfunction) 298 | originalfunction(frame, scale) 299 | 300 | local newInverseScale = 1.0 / WorldMapButton:GetEffectiveScale() 301 | if (inverseMapScale ~= newInverseScale) then 302 | inverseMapScale = newInverseScale 303 | ResizeContinentNodes() 304 | end 305 | end 306 | -- Listen for WorldMapFrame scale changes 307 | local originalWorldMapFrame_SetScale = WorldMapFrame.SetScale 308 | WorldMapFrame.SetScale = function(frame, scale) 309 | OnMapScaleChanged(frame, scale, originalWorldMapFrame_SetScale) 310 | end 311 | -- Listen for WorldMapDetailFrame scale changes 312 | local originalWorldMapDetailFrame_SetScale = WorldMapDetailFrame.SetScale 313 | WorldMapDetailFrame.SetScale = function(frame, scale) 314 | OnMapScaleChanged(frame, scale, originalWorldMapDetailFrame_SetScale) 315 | end 316 | -- Listen for WorldMapButton scale changes 317 | local originalWorldMapButton_SetScale = WorldMapButton.SetScale 318 | WorldMapButton.SetScale = function(frame, scale) 319 | OnMapScaleChanged(frame, scale, originalWorldMapButton_SetScale) 320 | end 321 | 322 | local function CreateContinentPin(index) 323 | if not continentPins[index] then 324 | local pin = CreateFrame("Button", "pfQuestContinentPin" .. index, WorldMapButton) 325 | pin:SetFrameLevel(WorldMapButton:GetFrameLevel() + 10) 326 | pin:SetFrameStrata("DIALOG") 327 | 328 | pin.tex = pin:CreateTexture(nil, "BACKGROUND") 329 | pin.tex:SetAllPoints(pin) 330 | 331 | pin.pic = pin:CreateTexture(nil, "BORDER") 332 | pin.pic:SetPoint("TOPLEFT", pin, "TOPLEFT", 1, -1) 333 | pin.pic:SetPoint("BOTTOMRIGHT", pin, "BOTTOMRIGHT", -1, 1) 334 | 335 | pin.hl = pin:CreateTexture(nil, "OVERLAY") 336 | pin.hl:SetTexture(pfQuestConfig.path .. "\\img\\track") 337 | pin.hl:SetPoint("TOPLEFT", pin, "TOPLEFT", -5, 5) 338 | pin.hl:Hide() 339 | 340 | pin.defalpha = 1 341 | pin.Animate = NodeAnimate 342 | pin.dt = 0 343 | 344 | if pfQuest_config["continentClickThrough"] == "1" then 345 | pin.tooltipTimer = 0 346 | pin.wasMouseOver = false 347 | 348 | local function CheckTooltip(self, elapsed) 349 | if not self:IsVisible() then return end 350 | 351 | local x, y = GetCursorPosition() 352 | local scale = self:GetEffectiveScale() 353 | x = x / scale 354 | y = y / scale 355 | 356 | local left = self:GetLeft() 357 | local right = self:GetRight() 358 | local top = self:GetTop() 359 | local bottom = self:GetBottom() 360 | 361 | local isMouseOver = false 362 | if left and right and top and bottom then 363 | isMouseOver = (x >= left and x <= right and y >= bottom and y <= top) 364 | end 365 | 366 | if isMouseOver and not self.wasMouseOver then 367 | if self.node then 368 | pfMap.NodeEnter(self) 369 | end 370 | self.wasMouseOver = true 371 | elseif not isMouseOver and self.wasMouseOver then 372 | self.pulse = 1 373 | self.mod = 1 374 | self:SetWidth(self.defsize) 375 | self:SetHeight(self.defsize) 376 | pfMap.NodeLeave(self) 377 | self.wasMouseOver = false 378 | end 379 | end 380 | 381 | pin:SetScript("OnUpdate", function(self, elapsed) 382 | if IsControlKeyDown() then 383 | -- Enable full mouse interaction when Ctrl is held 384 | if not self.mouseEnabled then 385 | self:EnableMouse(true) 386 | self:RegisterForClicks("LeftButtonUp", "RightButtonUp") 387 | self.mouseEnabled = true 388 | end 389 | else 390 | -- Disable mouse interaction when Ctrl is not held, but keep tooltips 391 | if self.mouseEnabled ~= false then 392 | self:EnableMouse(false) 393 | self:RegisterForClicks() 394 | self.mouseEnabled = false 395 | end 396 | end 397 | 398 | CheckTooltip(self, elapsed) 399 | end) 400 | 401 | pin:SetScript( 402 | "OnClick", 403 | function(self, button) 404 | if IsControlKeyDown() and self.node then 405 | if pfMap.NodeClick then 406 | pfMap.NodeClick(self, button) 407 | end 408 | end 409 | end 410 | ) 411 | else 412 | pin:EnableMouse(true) 413 | pin:RegisterForClicks("LeftButtonUp", "RightButtonUp") 414 | 415 | pin:SetScript( 416 | "OnEnter", 417 | function(self) 418 | if self.node then 419 | pfMap.NodeEnter(self) 420 | end 421 | end 422 | ) 423 | 424 | pin:SetScript( 425 | "OnLeave", 426 | function(self) 427 | self.pulse = 1 428 | self.mod = 1 429 | self:SetWidth(self.defsize) 430 | self:SetHeight(self.defsize) 431 | pfMap.NodeLeave(self) 432 | end 433 | ) 434 | 435 | pin:SetScript( 436 | "OnClick", 437 | function(self, button) 438 | if self.node then 439 | if pfMap.NodeClick then 440 | pfMap.NodeClick(self, button) 441 | end 442 | end 443 | end 444 | ) 445 | end 446 | 447 | continentPins[index] = pin 448 | end 449 | return continentPins[index] 450 | end 451 | 452 | function pfMap:UpdateNodes() 453 | local continent = GetCurrentMapContinent() 454 | local zone = GetCurrentMapZone() 455 | 456 | original_UpdateNodes(self) 457 | 458 | local mapName = GetMapInfo() 459 | local isContinent = 460 | (mapName == "Kalimdor" and zone == 0) or (mapName == "Azeroth" and zone == 0) or 461 | (mapName == nil and continent == 0 and zone == 0) 462 | 463 | -- Function to calculate gray level (no XP level) based on WoW's system 464 | local function GetGrayLevel(charLevel) 465 | if charLevel <= 5 then 466 | return 0 -- all mobs give XP 467 | elseif charLevel <= 49 then 468 | return charLevel - math.floor(charLevel / 10) - 5 469 | elseif charLevel == 50 then 470 | return 40 -- charLevel - 10 471 | elseif charLevel <= 59 then 472 | return charLevel - math.floor(charLevel / 5) - 1 473 | else -- level 60-70 474 | return charLevel - 9 475 | end 476 | end 477 | 478 | for i = 1, maxContinentPins do 479 | if continentPins[i] then 480 | continentPins[i]:Hide() 481 | continentPins[i].node = nil 482 | continentPins[i].sourceContinent = nil 483 | end 484 | end 485 | 486 | if pfQuest_config["epochContinentPins"] == "0" then 487 | return 488 | end 489 | 490 | if zone > 0 and not isContinent then 491 | return 492 | end 493 | 494 | for _, pin in pairs(pfMap.pins) do 495 | if pin then 496 | pin:Hide() 497 | end 498 | end 499 | 500 | if continent == 0 then 501 | local pinCount = 0 502 | local playerLevel = UnitLevel("player") 503 | local processedZones = {} 504 | local processedQuests = {} 505 | 506 | for targetContinent = 1, 2 do 507 | for addon, addonData in pairs(pfMap.nodes) do 508 | for zID, zoneNodes in pairs(addonData) do 509 | local zoneCont = GetZoneContinent(zID) 510 | 511 | if zoneCont == targetContinent then 512 | processedZones[zID] = true 513 | local uiMapID = zoneToUiMapID[zID] 514 | if uiMapID and mapData[uiMapID] then 515 | for coords, node in pairs(zoneNodes) do 516 | local skipNode = false 517 | local questKey = nil 518 | 519 | for title, data in pairs(node) do 520 | local needsDeduplication = false 521 | local isUtilityNPC = false 522 | 523 | if data.addon == "PFDB" then 524 | local utilityTypes = { 525 | "flight", 526 | "auctioneer", 527 | "banker", 528 | "battlemaster", 529 | "innkeeper", 530 | "mailbox", 531 | "stablemaster", 532 | "spirithealer", 533 | "meetingstone" 534 | } 535 | 536 | for _, utilityType in pairs(utilityTypes) do 537 | if pfDB["meta-epoch"] and pfDB["meta-epoch"][utilityType] then 538 | for objectId, faction in pairs(pfDB["meta-epoch"][utilityType]) do 539 | if data.id and tonumber(data.id) == objectId then 540 | isUtilityNPC = true 541 | break 542 | end 543 | end 544 | if isUtilityNPC then 545 | break 546 | end 547 | end 548 | end 549 | 550 | -- Block unwanted PFDB trackables (herbs, mines, chests, etc.) 551 | if not isUtilityNPC then 552 | local blockedTypes = {"herbs", "mines", "chests", "fish", "rares"} 553 | for _, blockedType in pairs(blockedTypes) do 554 | if pfDB["meta-epoch"] and pfDB["meta-epoch"][blockedType] then 555 | for objectId, faction in pairs(pfDB["meta-epoch"][blockedType]) do 556 | if data.id and tonumber(data.id) == objectId then 557 | skipNode = true 558 | break 559 | end 560 | end 561 | if skipNode then 562 | break 563 | end 564 | end 565 | end 566 | end 567 | elseif data.addon and string.find(data.addon, "TRACK_") then 568 | local allowedTracks = { 569 | "TRACK_FLIGHT", 570 | "TRACK_AUCTIONEER", 571 | "TRACK_BANKER", 572 | "TRACK_BATTLEMASTER", 573 | "TRACK_INNKEEPER", 574 | "TRACK_MAILBOX", 575 | "TRACK_STABLEMASTER", 576 | "TRACK_SPIRITHEALER", 577 | "TRACK_MEETINGSTONE" 578 | } 579 | 580 | local isAllowed = false 581 | for _, track in pairs(allowedTracks) do 582 | if string.find(data.addon, track) then 583 | isAllowed = true 584 | break 585 | end 586 | end 587 | 588 | if isAllowed then 589 | isUtilityNPC = true 590 | else 591 | skipNode = true 592 | end 593 | end 594 | 595 | if 596 | (zID == 141 or zID == 1657) or (zID == 12 or zID == 1519) or 597 | (zID == 1 or zID == 1537) or 598 | (zID == 14 or zID == 1637) or 599 | (zID == 215 or zID == 1638) or 600 | (zID == 85 or zID == 1497) 601 | then 602 | needsDeduplication = true 603 | 604 | if zID == 141 or zID == 1657 then 605 | questKey = title .. "_teldrassil" 606 | elseif zID == 12 or zID == 1519 then 607 | questKey = title .. "_stormwind" 608 | elseif zID == 1 or zID == 1537 then 609 | questKey = title .. "_ironforge" 610 | elseif zID == 14 or zID == 1637 then 611 | questKey = title .. "_orgrimmar" 612 | elseif zID == 215 or zID == 1638 then 613 | questKey = title .. "_thunderbluff" 614 | elseif zID == 85 or zID == 1497 then 615 | questKey = title .. "_undercity" 616 | end 617 | end 618 | 619 | if needsDeduplication and questKey and processedQuests[questKey] then 620 | skipNode = true 621 | break 622 | end 623 | 624 | local questLevel = tonumber(data.qlvl) or tonumber(data.lvl) or 0 625 | local minLevel = tonumber(data.min) or 0 626 | 627 | if not isUtilityNPC then 628 | if pfQuest_config["showlowlevel"] == "0" then 629 | if questLevel > 0 and questLevel <= GetGrayLevel(playerLevel) then 630 | if not (data.texture and string.find(data.texture, "complete")) then 631 | skipNode = true 632 | break 633 | end 634 | end 635 | end 636 | end 637 | 638 | -- Skip quests that are way too high level (red quests) - only if high level display is disabled 639 | if not isUtilityNPC then 640 | if minLevel > playerLevel + (pfQuest_config["showhighlevel"] == "1" and 3 or 0) then 641 | if not (data.texture and string.find(data.texture, "complete")) then 642 | skipNode = true 643 | break 644 | end 645 | end 646 | end 647 | 648 | -- Special filter for quests with suspiciously low min level - only if low level display is disabled 649 | if not isUtilityNPC then 650 | if pfQuest_config["showlowlevel"] == "0" then 651 | if minLevel <= 1 and questLevel <= GetGrayLevel(playerLevel) then 652 | if not (data.texture and string.find(data.texture, "complete")) then 653 | skipNode = true 654 | break 655 | end 656 | end 657 | end 658 | end 659 | 660 | if needsDeduplication and questKey and not skipNode then 661 | processedQuests[questKey] = true 662 | end 663 | end 664 | 665 | if not skipNode then 666 | local _, _, strx, stry = strfind(coords, "(.*)|(.*)") 667 | local zoneX = tonumber(strx) 668 | local zoneY = tonumber(stry) 669 | 670 | if zoneX and zoneY then 671 | local worldX, worldY = ZoneToWorld(zoneX, zoneY, zID) 672 | 673 | if worldX and worldY then 674 | local contX, contY = WorldToContinent(worldX, worldY, targetContinent) 675 | 676 | if 677 | contX and contY and contX >= 0 and contX <= 1 and contY >= 0 and 678 | contY <= 1 679 | then 680 | local worldMapX, worldMapY 681 | 682 | if targetContinent == 1 then 683 | worldMapX = contX * 0.90 - 0.22 684 | worldMapY = contY * 0.85 + 0.05 685 | else 686 | worldMapX = 0.33 + (contX * 0.90) 687 | worldMapY = contY * 0.90 - 0.04 688 | end 689 | 690 | if 691 | worldMapX >= 0 and worldMapX <= 1 and worldMapY >= 0 and 692 | worldMapY <= 1 693 | then 694 | pinCount = pinCount + 1 695 | if pinCount > maxContinentPins then 696 | break 697 | end 698 | 699 | local pin = CreateContinentPin(pinCount) 700 | pin.node = node 701 | pin.sourceContinent = targetContinent 702 | 703 | pfMap:UpdateNode(pin, node, nil, nil, nil) 704 | 705 | ResizeContinentNode(pin) 706 | 707 | pin:ClearAllPoints() 708 | pin:SetPoint( 709 | "CENTER", 710 | WorldMapButton, 711 | "TOPLEFT", 712 | worldMapX * WorldMapButton:GetWidth(), 713 | -worldMapY * WorldMapButton:GetHeight() 714 | ) 715 | pin:Show() 716 | end 717 | end 718 | end 719 | end 720 | end 721 | end 722 | if pinCount >= maxContinentPins then 723 | break 724 | end 725 | end 726 | end 727 | end 728 | if pinCount >= maxContinentPins then 729 | break 730 | end 731 | end 732 | if pinCount >= maxContinentPins then 733 | break 734 | end 735 | end 736 | 737 | for i = pinCount + 1, maxContinentPins do 738 | if continentPins[i] then 739 | continentPins[i]:Hide() 740 | end 741 | end 742 | return 743 | end 744 | 745 | if continent > 2 or continent < 1 then 746 | return 747 | end 748 | 749 | local pinCount = 0 750 | local playerLevel = UnitLevel("player") 751 | local processedZones = {} 752 | local processedQuests = {} 753 | 754 | local function GetGrayLevel(charLevel) 755 | if charLevel <= 5 then 756 | return 0 757 | elseif charLevel <= 49 then 758 | return charLevel - math.floor(charLevel / 10) - 5 759 | elseif charLevel == 50 then 760 | return 40 761 | elseif charLevel <= 59 then 762 | return charLevel - math.floor(charLevel / 5) - 1 763 | else 764 | return charLevel - 9 765 | end 766 | end 767 | 768 | for addon, addonData in pairs(pfMap.nodes) do 769 | for zID, zoneNodes in pairs(addonData) do 770 | local zoneCont = GetZoneContinent(zID) 771 | if not zoneCont or zoneCont ~= continent then 772 | else 773 | processedZones[zID] = true 774 | local uiMapID = zoneToUiMapID[zID] 775 | if uiMapID and mapData[uiMapID] then 776 | for coords, node in pairs(zoneNodes) do 777 | local skipNode = false 778 | local questKey = nil 779 | 780 | for title, data in pairs(node) do 781 | local needsDeduplication = false 782 | local isUtilityNPC = false 783 | 784 | if data.addon == "PFDB" then 785 | local utilityTypes = { 786 | "flight", 787 | "auctioneer", 788 | "banker", 789 | "battlemaster", 790 | "innkeeper", 791 | "mailbox", 792 | "stablemaster", 793 | "spirithealer", 794 | "meetingstone" 795 | } 796 | 797 | for _, utilityType in pairs(utilityTypes) do 798 | if pfDB["meta-epoch"] and pfDB["meta-epoch"][utilityType] then 799 | for objectId, faction in pairs(pfDB["meta-epoch"][utilityType]) do 800 | if data.id and tonumber(data.id) == objectId then 801 | isUtilityNPC = true 802 | break 803 | end 804 | end 805 | if isUtilityNPC then 806 | break 807 | end 808 | end 809 | end 810 | 811 | -- Block unwanted PFDB trackables (herbs, mines, chests, etc.) 812 | if not isUtilityNPC then 813 | local blockedTypes = {"herbs", "mines", "chests", "fish", "rares"} 814 | for _, blockedType in pairs(blockedTypes) do 815 | if pfDB["meta-epoch"] and pfDB["meta-epoch"][blockedType] then 816 | for objectId, faction in pairs(pfDB["meta-epoch"][blockedType]) do 817 | if data.id and tonumber(data.id) == objectId then 818 | skipNode = true 819 | break 820 | end 821 | end 822 | if skipNode then 823 | break 824 | end 825 | end 826 | end 827 | end 828 | elseif data.addon and string.find(data.addon, "TRACK_") then 829 | local allowedTracks = { 830 | "TRACK_FLIGHT", 831 | "TRACK_AUCTIONEER", 832 | "TRACK_BANKER", 833 | "TRACK_BATTLEMASTER", 834 | "TRACK_INNKEEPER", 835 | "TRACK_MAILBOX", 836 | "TRACK_STABLEMASTER", 837 | "TRACK_SPIRITHEALER", 838 | "TRACK_MEETINGSTONE" 839 | } 840 | 841 | local isAllowed = false 842 | for _, track in pairs(allowedTracks) do 843 | if string.find(data.addon, track) then 844 | isAllowed = true 845 | break 846 | end 847 | end 848 | 849 | if isAllowed then 850 | isUtilityNPC = true 851 | else 852 | skipNode = true 853 | end 854 | end 855 | 856 | -- Only apply deduplication to specific problem zones 857 | if 858 | (zID == 141 or zID == 1657) or -- Teldrassil/Darnassus 859 | (zID == 12 or zID == 1519) or -- Elwynn/Stormwind 860 | (zID == 1 or zID == 1537) or -- Dun Morogh/Ironforge 861 | (zID == 14 or zID == 1637) or -- Durotar/Orgrimmar 862 | (zID == 215 or zID == 1638) or -- Mulgore/Thunder Bluff 863 | (zID == 85 or zID == 1497) -- Tirisfal/Undercity 864 | then 865 | needsDeduplication = true 866 | 867 | -- Create zone-pair specific keys 868 | if zID == 141 or zID == 1657 then 869 | questKey = title .. "_teldrassil" 870 | elseif zID == 12 or zID == 1519 then 871 | questKey = title .. "_stormwind" 872 | elseif zID == 1 or zID == 1537 then 873 | questKey = title .. "_ironforge" 874 | elseif zID == 14 or zID == 1637 then 875 | questKey = title .. "_orgrimmar" 876 | elseif zID == 215 or zID == 1638 then 877 | questKey = title .. "_thunderbluff" 878 | elseif zID == 85 or zID == 1497 then 879 | questKey = title .. "_undercity" 880 | end 881 | end 882 | 883 | -- Only check for duplicates in problem zones 884 | if needsDeduplication and questKey and processedQuests[questKey] then 885 | skipNode = true 886 | break 887 | end 888 | 889 | local questLevel = tonumber(data.qlvl) or tonumber(data.lvl) or 0 890 | local minLevel = tonumber(data.min) or 0 891 | 892 | if not isUtilityNPC then 893 | if pfQuest_config["showlowlevel"] == "0" then 894 | if questLevel > 0 and questLevel <= GetGrayLevel(playerLevel) then 895 | if not (data.texture and string.find(data.texture, "complete")) then 896 | skipNode = true 897 | break 898 | end 899 | end 900 | end 901 | end 902 | 903 | -- Skip quests that are way too high level (red quests) - only if high level display is disabled 904 | if not isUtilityNPC then 905 | if minLevel > playerLevel + (pfQuest_config["showhighlevel"] == "1" and 3 or 0) then 906 | if not (data.texture and string.find(data.texture, "complete")) then 907 | skipNode = true 908 | break 909 | end 910 | end 911 | end 912 | 913 | -- Special filter for quests with suspiciously low min level - only if low level display is disabled 914 | if not isUtilityNPC then 915 | if pfQuest_config["showlowlevel"] == "0" then 916 | if minLevel <= 1 and questLevel <= GetGrayLevel(playerLevel) then 917 | if not (data.texture and string.find(data.texture, "complete")) then 918 | skipNode = true 919 | break 920 | end 921 | end 922 | end 923 | end 924 | 925 | if needsDeduplication and questKey and not skipNode then 926 | processedQuests[questKey] = true 927 | end 928 | end 929 | 930 | if not skipNode then 931 | local _, _, strx, stry = strfind(coords, "(.*)|(.*)") 932 | local zoneX = tonumber(strx) 933 | local zoneY = tonumber(stry) 934 | 935 | if zoneX and zoneY then 936 | local worldX, worldY = ZoneToWorld(zoneX, zoneY, zID) 937 | if worldX and worldY then 938 | local contX, contY = WorldToContinent(worldX, worldY, continent) 939 | if contX and contY and contX >= 0 and contX <= 1 and contY >= 0 and contY <= 1 then 940 | pinCount = pinCount + 1 941 | if pinCount > maxContinentPins then 942 | break 943 | end 944 | 945 | local pin = CreateContinentPin(pinCount) 946 | pin.node = node 947 | pin.sourceContinent = continent 948 | 949 | pfMap:UpdateNode(pin, node, nil, nil, nil) 950 | 951 | ResizeContinentNode(pin) 952 | 953 | pin:ClearAllPoints() 954 | pin:SetPoint( 955 | "CENTER", 956 | WorldMapButton, 957 | "TOPLEFT", 958 | contX * WorldMapButton:GetWidth(), 959 | -contY * WorldMapButton:GetHeight() 960 | ) 961 | pin:Show() 962 | end 963 | end 964 | end 965 | end 966 | end 967 | if pinCount >= maxContinentPins then 968 | break 969 | end 970 | end 971 | end 972 | end 973 | if pinCount >= maxContinentPins then 974 | break 975 | end 976 | end 977 | 978 | for i = pinCount + 1, maxContinentPins do 979 | if continentPins[i] then 980 | continentPins[i]:Hide() 981 | end 982 | end 983 | end 984 | 985 | local originalWorldMapButton_OnUpdate = WorldMapButton:GetScript("OnUpdate") 986 | WorldMapButton:SetScript( 987 | "OnUpdate", 988 | function(self, elapsed) 989 | if originalWorldMapButton_OnUpdate then 990 | originalWorldMapButton_OnUpdate(self, elapsed) 991 | end 992 | 993 | local currentContinent = GetCurrentMapContinent() 994 | local currentZone = GetCurrentMapZone() 995 | 996 | if self.lastContinent ~= currentContinent or self.lastZone ~= currentZone then 997 | self.lastContinent = currentContinent 998 | self.lastZone = currentZone 999 | 1000 | if currentZone == 0 then 1001 | pfMap:UpdateNodes() 1002 | end 1003 | end 1004 | end 1005 | ) 1006 | 1007 | local function ExtendPfQuestConfig() 1008 | table.insert( 1009 | pfQuest_defconfig, 1010 | { 1011 | text = "|cff33ffccContinent Map|r", 1012 | type = "header" 1013 | } 1014 | ) 1015 | table.insert( 1016 | pfQuest_defconfig, 1017 | { 1018 | text = "Display Continent Pins", 1019 | default = "1", 1020 | type = "checkbox", 1021 | config = "epochContinentPins" 1022 | } 1023 | ) 1024 | table.insert( 1025 | pfQuest_defconfig, 1026 | { 1027 | text = "Require Ctrl+Click for Pin Interaction", 1028 | default = "0", 1029 | type = "checkbox", 1030 | config = "continentClickThrough" 1031 | } 1032 | ) 1033 | table.insert( 1034 | pfQuest_defconfig, 1035 | { 1036 | text = "Continent Node Size", 1037 | default = "12", 1038 | type = "text", 1039 | config = "continentNodeSize" 1040 | } 1041 | ) 1042 | table.insert( 1043 | pfQuest_defconfig, 1044 | { 1045 | text = "Continent Utility Node Size", 1046 | default = "14", 1047 | type = "text", 1048 | config = "continentUtilityNodeSize" 1049 | } 1050 | ) 1051 | table.insert( 1052 | pfQuest_defconfig, 1053 | { 1054 | text = "Hide Chicken Quests (CLUCK!)", 1055 | default = "1", 1056 | type = "checkbox", 1057 | config = "epochHideChickenQuests" 1058 | } 1059 | ) 1060 | table.insert( 1061 | pfQuest_defconfig, 1062 | { 1063 | text = "Hide Felwood Corrupted Flowers", 1064 | default = "1", 1065 | type = "checkbox", 1066 | config = "epochHideFelwoodFlowers" 1067 | } 1068 | ) 1069 | table.insert( 1070 | pfQuest_defconfig, 1071 | { 1072 | text = "Hide PvP/Battleground Quests", 1073 | default = "1", 1074 | type = "checkbox", 1075 | config = "epochHidePvPQuests" 1076 | } 1077 | ) 1078 | table.insert( 1079 | pfQuest_defconfig, 1080 | { 1081 | text = "Hide Commission Quests", 1082 | default = "0", 1083 | type = "checkbox", 1084 | config = "epochHideCommissionQuests" 1085 | } 1086 | ) 1087 | table.insert( 1088 | pfQuest_defconfig, 1089 | { 1090 | text = "Hide Item Drop Quest Starters", 1091 | default = "0", 1092 | type = "checkbox", 1093 | config = "epochHideItemDrops" 1094 | } 1095 | ) 1096 | 1097 | -- Initialize the config values with defaults 1098 | pfQuest_config["epochContinentPins"] = pfQuest_config["epochContinentPins"] or "1" 1099 | pfQuest_config["continentClickThrough"] = pfQuest_config["continentClickThrough"] or "0" 1100 | pfQuest_config["continentNodeSize"] = pfQuest_config["continentNodeSize"] or "12" 1101 | pfQuest_config["continentUtilityNodeSize"] = pfQuest_config["continentUtilityNodeSize"] or "14" 1102 | pfQuest_config["epochHideChickenQuests"] = pfQuest_config["epochHideChickenQuests"] or "1" 1103 | pfQuest_config["epochHideFelwoodFlowers"] = pfQuest_config["epochHideFelwoodFlowers"] or "1" 1104 | pfQuest_config["epochHidePvPQuests"] = pfQuest_config["epochHidePvPQuests"] or "1" 1105 | pfQuest_config["epochHideCommissionQuests"] = pfQuest_config["epochHideCommissionQuests"] or "0" 1106 | pfQuest_config["epochHideItemDrops"] = pfQuest_config["epochHideItemDrops"] or "0" 1107 | end 1108 | 1109 | local f = CreateFrame("Frame") 1110 | f:RegisterEvent("VARIABLES_LOADED") 1111 | f:SetScript( 1112 | "OnEvent", 1113 | function() 1114 | ExtendPfQuestConfig() 1115 | end 1116 | ) 1117 | --------------------------------------------------------------------------------