vanilla-wow-addons – Rev 1
?pathlinks?
------------------------------------------------------
-- Linkerator.lua
-- Link scanning code based on LootLink 1.9. Thanks Telo!
------------------------------------------------------
FLT_VERSION = "11000.2";
------------------------------------------------------
FLT_ItemLinks = { };
local ChatMessageTypes = {
"CHAT_MSG_SYSTEM",
"CHAT_MSG_SAY",
"CHAT_MSG_TEXT_EMOTE",
"CHAT_MSG_YELL",
"CHAT_MSG_WHISPER",
"CHAT_MSG_PARTY",
"CHAT_MSG_GUILD",
"CHAT_MSG_OFFICER",
"CHAT_MSG_CHANNEL",
"CHAT_MSG_RAID",
"CHAT_MSG_LOOT",
};
local INVENTORY_SLOT_LIST = {
{ name = "HeadSlot" },
{ name = "NeckSlot" },
{ name = "ShoulderSlot" },
{ name = "BackSlot" },
{ name = "ChestSlot" },
{ name = "ShirtSlot" },
{ name = "TabardSlot" },
{ name = "WristSlot" },
{ name = "HandsSlot" },
{ name = "WaistSlot" },
{ name = "LegsSlot" },
{ name = "FeetSlot" },
{ name = "Finger0Slot" },
{ name = "Finger1Slot" },
{ name = "Trinket0Slot" },
{ name = "Trinket1Slot" },
{ name = "MainHandSlot" },
{ name = "SecondaryHandSlot" },
{ name = "RangedSlot" },
};
-- Anti-freeze code borrowed from ReagentInfo (in turn, from Quest-I-On):
-- keeps WoW from locking up if we try to scan the tradeskill window too fast.
FLT_TradeSkillLock = { };
FLT_TradeSkillLock.NeedScan = false;
FLT_TradeSkillLock.Locked = false;
FLT_TradeSkillLock.EventTimer = 0;
FLT_TradeSkillLock.EventCooldown = 0;
FLT_TradeSkillLock.EventCooldownTime = 1;
FLT_CraftLock = { };
FLT_CraftLock.NeedScan = false;
FLT_CraftLock.Locked = false;
FLT_CraftLock.EventTimer = 0;
FLT_CraftLock.EventCooldown = 0;
FLT_CraftLock.EventCooldownTime = 1;
function FLT_OnLoad()
--GFWUtils.Debug = 1;
-- Register Slash Commands
SLASH_FLT1 = "/linkerator";
SLASH_FLT2 = "/link";
SlashCmdList["FLT"] = function(msg)
FLT_ChatCommandHandler(msg);
end
for index, value in ChatMessageTypes do
this:RegisterEvent(value);
end
for index = 1, getn(INVENTORY_SLOT_LIST), 1 do
INVENTORY_SLOT_LIST[index].id = GetInventorySlotInfo(INVENTORY_SLOT_LIST[index].name);
end
this:RegisterEvent("PLAYER_ENTERING_WORLD");
this:RegisterEvent("PLAYER_LEAVING_WORLD");
this:RegisterEvent("PLAYER_TARGET_CHANGED");
this:RegisterEvent("BANKFRAME_OPENED");
this:RegisterEvent("MERCHANT_SHOW");
this:RegisterEvent("TRADE_SKILL_SHOW");
this:RegisterEvent("TRADE_SKILL_UPDATE");
this:RegisterEvent("CRAFT_SHOW");
this:RegisterEvent("CRAFT_UPDATE");
this:RegisterEvent("QUEST_COMPLETE");
this:RegisterEvent("QUEST_DETAIL");
this:RegisterEvent("QUEST_FINISHED");
this:RegisterEvent("QUEST_PROGRESS");
this:RegisterEvent("QUEST_GREETING");
this:RegisterEvent("AUCTION_ITEM_LIST_UPDATE");
GFWUtils.Print("Fizzwidget Linkerator "..FLT_VERSION.." initialized!");
end
function FLT_OnUpdate(elapsed)
-- If it's been more than a second since our last tradeskill update,
-- we can allow the event to process again.
FLT_TradeSkillLock.EventTimer = FLT_TradeSkillLock.EventTimer + elapsed;
if (FLT_TradeSkillLock.Locked) then
FLT_TradeSkillLock.EventCooldown = FLT_TradeSkillLock.EventCooldown + elapsed;
if (FLT_TradeSkillLock.EventCooldown > FLT_TradeSkillLock.EventCooldownTime) then
FLT_TradeSkillLock.EventCooldown = 0;
FLT_TradeSkillLock.Locked = false;
end
end
FLT_CraftLock.EventTimer = FLT_CraftLock.EventTimer + elapsed;
if (FLT_CraftLock.Locked) then
FLT_CraftLock.EventCooldown = FLT_CraftLock.EventCooldown + elapsed;
if (FLT_CraftLock.EventCooldown > FLT_CraftLock.EventCooldownTime) then
FLT_CraftLock.EventCooldown = 0;
FLT_CraftLock.Locked = false;
end
end
if (FLT_TradeSkillLock.NeedScan) then
FLT_TradeSkillLock.NeedScan = false;
FLT_ScanTradeSkill();
end
if (FLT_CraftLock.NeedScan) then
FLT_CraftLock.NeedScan = false;
FLT_ScanCraft();
end
end
function FLT_OnEvent(event)
if( event == "PLAYER_TARGET_CHANGED" ) then
if( UnitIsUnit("target", "player") ) then
return;
elseif( UnitIsPlayer("target") ) then
FLT_Inspect("target");
end
elseif( event == "PLAYER_ENTERING_WORLD" ) then
FLT_ScanInventory();
FLT_Inspect("player");
this:RegisterEvent("UNIT_INVENTORY_CHANGED");
elseif( event == "PLAYER_LEAVING_WORLD" ) then
this:UnregisterEvent("UNIT_INVENTORY_CHANGED");
elseif( event == "UNIT_INVENTORY_CHANGED" ) then
if( arg1 == "player" ) then
FLT_ScanInventory();
FLT_Inspect("player");
end
elseif( event == "MERCHANT_SHOW" ) then
for i=1, GetMerchantNumItems() do
local link = GetMerchantItemLink(i);
if ( link ) then
FLT_ProcessLinks(link);
end
end
elseif( event == "BANKFRAME_OPENED" ) then
FLT_ScanBank();
elseif( event == "AUCTION_ITEM_LIST_UPDATE" ) then
FLT_ScanAuction();
elseif ( event == "QUEST_COMPLETE" or event == "QUEST_DETAIL" or event == "QUEST_FINISHED" or event == "QUEST_PROGRESS" or event == "QUEST_GREETING") then
FLT_ScanQuestgiver();
elseif ( event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_UPDATE" ) then
FLT_ScanTradeSkill();
elseif ( event == "CRAFT_SHOW" or event == "CRAFT_UPDATE" ) then
FLT_ScanCraft();
elseif (event == "CHAT_MSG_CHANNEL") then
--DevTools_Dump({event=event, args={arg1=arg1,arg2=arg2,arg3=arg3,arg4=arg4,arg5=arg5,arg6=arg6,arg7=arg7,arg8=arg8,arg9=arg9,}});
if (FLT_Debug) then
debugprofilestart();
end
local gotLink = FLT_ProcessLinks(arg1);
if (FLT_Debug) then
local parseTime = debugprofilestop();
if (gotLink) then
FLT_MaxFoundTime = math.max((FLT_MaxFoundTime or 0), parseTime);
else
FLT_MaxNotFoundTime = math.max((FLT_MaxNotFoundTime or 0), parseTime);
end
end
else
FLT_ProcessLinks(arg1);
end
end
function FLT_ChatCommandHandler(msg)
-- Print Help
if ( msg == "help" ) or ( msg == "" ) then
GFWUtils.Print("Fizzwidget Linkerator "..FLT_VERSION..":");
GFWUtils.Print("/linkerator (or /link) <command>");
GFWUtils.Print("- "..GFWUtils.Hilite("help").." - Print this helplist.");
GFWUtils.Print("- "..GFWUtils.Hilite("<item name>").." - Print a hyperlink to the chat window for an item known by name.");
GFWUtils.Print("- "..GFWUtils.Hilite("<item id #>").." - Print a hyperlink to the chat window for a generic item whose ID number is known.");
GFWUtils.Print("- "..GFWUtils.Hilite("<code>").." - Print a hyperlink to the chat window for an item whose complete link code is known.");
return;
end
if (msg == "version") then
GFWUtils.Print("Fizzwidget Linkerator "..FLT_VERSION);
return;
end
if (msg == "list") then
local totalCount, knownCount = 0, 0;
for name, linkInfo in FLT_ItemLinks do
if (type(linkInfo) == "string") then
totalCount = totalCount + 1;
local link = FLT_GetItemLink(linkInfo);
if (link) then
GFWUtils.Print(link);
knownCount = knownCount + 1;
else
GFWUtils.Print(name.." ("..linkInfo.."; not cached)");
end
elseif (type(linkInfo) == "table") then
for _, linkID in linkInfo do
totalCount = totalCount + 1;
local link = FLT_GetItemLink(linkID);
if (link) then
GFWUtils.Print(link);
knownCount = knownCount + 1;
else
GFWUtils.Print(name.." ("..linkID.."; not cached)");
end
end
end
end
GFWUtils.Print(GFWUtils.Hilite(totalCount).." items in history, "..GFWUtils.Hilite(knownCount).." known to client.");
return;
end
if (msg == "count") then
local totalCount, knownCount = 0, 0;
for name, linkInfo in FLT_ItemLinks do
if (type(linkInfo) == "string") then
totalCount = totalCount + 1;
local link = FLT_GetItemLink(linkInfo);
if (link) then
knownCount = knownCount + 1;
end
elseif (type(linkInfo) == "table") then
for _, linkID in linkInfo do
totalCount = totalCount + 1;
local link = FLT_GetItemLink(linkID);
if (link) then
knownCount = knownCount + 1;
end
end
end
end
GFWUtils.Print(GFWUtils.Hilite(totalCount).." items in history, "..GFWUtils.Hilite(knownCount).." known to client.");
return;
end
if (FLT_Debug and msg == "time") then
GFWUtils.Print(string.format("Max parse time "..GFWUtils.Hilite("%.2f").." ms (no links found)", FLT_MaxNotFoundTime * 1000));
GFWUtils.Print(string.format("Max parse time "..GFWUtils.Hilite("%.2f").." ms (links found)", FLT_MaxFoundTime * 1000));
return;
end
if (msg == "debug") then
if (FLT_Debug) then
FLT_Debug = nil;
GFWUtils.Print("Linkerator debugging messages off.");
else
FLT_Debug = 1;
GFWUtils.Print("Linkerator debugging messages on.");
end
return;
end
if (msg == "import") then
local lootLinkCount = 0;
if (ItemLinks and type(ItemLinks) == "table") then
for itemName, lootLinkInfo in ItemLinks do
if (lootLinkInfo.i and type(lootLinkInfo.i) == "string") then
local itemLink = "item:"..lootLinkInfo.i;
local addedLink = FLT_AddLink(itemName, itemLink);
if (addedLink) then
lootLinkCount = lootLinkCount + 1;
end
end
end
end
GFWUtils.Print(GFWUtils.Hilite(lootLinkCount).. " items imported from LootLink.");
end
local itemLink;
if (msg and msg ~= "") then
_, _, itemLink = string.find(msg, "(item:%d+:%d+:%d+:%d+)");
end
if (tonumber(msg)) then
--DevTools_Dump({msg=msg});
local link = FLT_GetItemLink(msg, 1);
if (link) then
GFWUtils.Print("Item ID "..msg..": "..link);
else
GFWUtils.Print("Item ID "..msg.." is unknown to this WoW client.");
end
return;
elseif (itemLink) then
--DevTools_Dump({msg=msg, itemLink=itemLink});
local link = FLT_GetItemLink(itemLink, 1);
if (link) then
GFWUtils.Print(itemLink..": "..link);
else
GFWUtils.Print(itemLink.." is unknown to this WoW client.");
end
return;
elseif (msg and msg ~= "") then
local foundLinks = FLT_GetLinkByName(msg, true);
if (foundLinks) then
if (type(foundLinks) == "string") then
if (FLT_Debug) then
local _, _, linkInfo = string.find(foundLinks, "(item:%d+:%d+:%d+:%d+)");
GFWUtils.Print(foundLinks.." ("..linkInfo..")");
else
GFWUtils.Print(foundLinks);
end
elseif (type(foundLinks) == "table") then
for _, aLink in foundLinks do
if (FLT_Debug) then
local _, _, linkInfo = string.find(aLink, "(item:%d+:%d+:%d+:%d+)");
GFWUtils.Print(aLink.." ("..linkInfo..")");
else
GFWUtils.Print(aLink);
end
end
else
GFWUtils.Print(GFWUtils.Red("Linkerator error: ").."Unexpected result from FLT_GetLinkByName().");
end
else
local foundCount = 0;
msg = string.lower(msg);
for itemName, linkInfo in FLT_ItemLinks do
if (string.find(itemName, msg)) then
if (type(linkInfo) == "string") then
local link = FLT_GetItemLink(linkInfo);
if (link) then
if (FLT_Debug) then
GFWUtils.Print(link.." ("..linkInfo..")");
else
GFWUtils.Print(link);
end
foundCount = foundCount + 1;
end
elseif (type(linkInfo) == "table") then
for _, aLink in linkInfo do
local link = FLT_GetItemLink(aLink);
if (link) then
if (FLT_Debug) then
GFWUtils.Print(link.." ("..aLink..")");
else
GFWUtils.Print(link);
end
foundCount = foundCount + 1;
end
end
end
end
end
if (foundCount > 0) then
GFWUtils.Print(GFWUtils.Hilite(foundCount).." links found for '"..GFWUtils.Hilite(msg).."'");
else
GFWUtils.Print("Could not find '"..GFWUtils.Hilite(msg).."' in Linkerator's item history.");
end
end
return;
end
-- If we're this far, we probably have bad input.
FDP_ChatCommandHandler("help");
end
function FLT_GetItemLink(linkInfo, shouldAdd)
local sName, sLink, iQuality, iLevel, sType, sSubType, iCount = GetItemInfo(linkInfo);
if (sName) then
local _, _, _, color = GetItemQualityColor(iQuality);
local linkFormat = "%s|H%s|h[%s]|h|r";
if (shouldAdd) then
FLT_AddLink(sName, sLink); -- add it to our name index if we're getting it from another source
end
return string.format(linkFormat, color, sLink, sName);
else
return nil;
end
end
function FLT_GetLinkByName(text, returnAll)
local _, _, name, property = string.find(text, "(.+)%((.-)%)" );
local linkInfo = FLT_ItemLinks[string.lower(text)];
local allResults = {};
if (linkInfo == nil and name and property) then
name = string.lower(string.gsub(name, " +$", ""));
property = string.lower(property);
linkInfo = FLT_ItemLinks[string.lower(name)];
end
if (linkInfo) then
if (type(linkInfo) == "string") then
local link = FLT_GetItemLink(linkInfo);
if (link) then return link; end
elseif (type(linkInfo) == "table" and name and property) then
for _, itemLink in linkInfo do
local _, _, _, _, type, subType, _, equipLoc = GetItemInfo(itemLink);
if ((type and string.find(string.lower(type), property))
or (subType and string.find(string.lower(subType), property))
or (getglobal(equipLoc) and string.find(string.lower(getglobal(equipLoc)), property))) then
local link = FLT_GetItemLink(itemLink);
if (link and returnAll) then
table.insert(allResults, link);
elseif (link) then
return link;
end
end
end
for _, itemLink in linkInfo do
if (FLT_FindInItemTooltip(property, itemLink)) then
if (returnAll) then
table.insert(allResults, FLT_GetItemLink(itemLink));
else
return FLT_GetItemLink(itemLink);
end
end
end
elseif (type(linkInfo) == "table") then
for _, itemLink in linkInfo do
local link = FLT_GetItemLink(itemLink);
if (link and returnAll) then
table.insert(allResults, link);
elseif (link) then
return link;
end
end
else
GFWUtils.Print(GFWUtils.Red("Linkerator error: ").."Corrupt table entry for "..GFWUtils.Hilite(name)..".");
end
elseif (string.find(text, "^(item:%d+:%d+:%d+:%d+)$")) then
local link = FLT_GetItemLink(text);
if (link) then return link; end
elseif (string.find(text, "^#%d+$")) then
local link = FLT_GetItemLink(string.sub(text,2));
if (link) then return link; end
end
if (returnAll and table.getn(allResults) > 0) then
return allResults;
end
end
function FLT_ProcessLinks(text)
local lastLink;
if ( text ) then
local link, name;
for link, name in string.gfind(text, "|c%x+|H(item:%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r") do
if (link and name and name ~= "") then
lastLink = FLT_AddLink(name, link);
end
end
end
return lastLink;
end
function FLT_AddLink(name, link)
local cleanLink = string.gsub(link, "item:(%d+):%d+:(%d+):%d+", "item:%1:0:%2:0"); -- strip uniqID, enchant
name = string.lower(name); -- so we can do case-insensitive lookups
local existingLink = FLT_ItemLinks[name];
if (existingLink and existingLink ~= cleanLink) then
if (type(existingLink) == "string") then
FLT_ItemLinks[name] = {};
table.insert(FLT_ItemLinks[name], existingLink);
table.insert(FLT_ItemLinks[name], cleanLink);
if (FLT_Debug) then GFWUtils.Print("Added "..(FLT_GetItemLink(cleanLink) or name).." ("..cleanLink.."); changed to table"); end
elseif (type(existingLink) == "table") then
if (GFWTable.KeyOf(existingLink, cleanLink) == nil) then
FLT_ItemLinks[name] = GFWTable.Merge(existingLink, {cleanLink});
if (FLT_Debug) then GFWUtils.Print("Added "..(FLT_GetItemLink(cleanLink) or name).." ("..cleanLink.."); table count is now "..GFWTable.Count(FLT_ItemLinks[name])); end
end
else
GFWUtils.PrintOnce("Corrupt entry for "..GFWUtils.Hilite(name).."; replacing with "..FLT_GetItemLink(cleanLink)..".", 60);
FLT_ItemLinks[name] = cleanLink;
end
elseif (existingLink == nil) then
FLT_ItemLinks[name] = cleanLink;
if (FLT_Debug) then GFWUtils.Print("Added "..(FLT_GetItemLink(cleanLink) or name).." ("..cleanLink..")"); end
end
return cleanLink;
end
function FLT_InspectSlot(unit, id)
local link = GetInventoryItemLink(unit, id);
if ( link ) then
FLT_ProcessLinks(link);
end
end
function FLT_Inspect(who)
local index;
for index = 1, getn(INVENTORY_SLOT_LIST), 1 do
FLT_InspectSlot(who, INVENTORY_SLOT_LIST[index].id)
end
end
function FLT_ScanTradeSkill()
if (not TradeSkillFrame or not TradeSkillFrame:IsVisible() or FLT_TradeSkillLock.Locked) then return; end
-- This prevents further update events from being handled if we're already processing one.
-- This is done to prevent the game from freezing under certain conditions.
FLT_TradeSkillLock.Locked = true;
local skillLineName, skillLineRank, skillLineMaxRank = GetTradeSkillLine();
if not (skillLineName) then
FLT_TradeSkillLock.NeedScan = true;
return; -- apparently sometimes we're called too early, this is nil, and all hell breaks loose.
end
for id = 1, GetNumTradeSkills() do
local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(id);
if ( skillType ~= "header" ) then
local itemLink = GetTradeSkillItemLink(id);
if (itemLink == nil) then
FLT_TradeSkillLock.NeedScan = true;
else
FLT_ProcessLinks(itemLink);
for i=1, GetTradeSkillNumReagents(id), 1 do
local link = GetTradeSkillReagentItemLink(id, i);
if (link == nil) then
FLT_TradeSkillLock.NeedScan = true;
break;
else
FLT_ProcessLinks(link);
end
end
end
end
end
end
function FLT_ScanCraft()
if (not CraftFrame or not CraftFrame:IsVisible() or FLT_CraftLock.Locked) then return; end
-- This prevents further update events from being handled if we're already processing one.
-- This is done to prevent the game from freezing under certain conditions.
FLT_CraftLock.Locked = true;
-- This is used only for Enchanting
local skillLineName, rank, maxRank = GetCraftDisplaySkillLine();
if not (skillLineName) then
return; -- Hunters' Beast Training also uses the CraftFrame, but doesn't have a SkillLine.
end
for id = GetNumCrafts(), 1, -1 do
if ( craftType ~= "header" ) then
local itemLink = GetCraftItemLink(id);
if (itemLink == nil) then
FLT_TradeSkillLock.NeedScan = true;
else
FLT_ProcessLinks(itemLink);
for i=1, GetCraftNumReagents(id), 1 do
local link = GetCraftReagentItemLink(id, i);
if (link == nil) then
FLT_CraftLock.NeedScan = true;
break;
else
FLT_ProcessLinks(link);
end
end
end
end
end
end
function FLT_ScanQuestgiver()
local link;
for i = 1, GetNumQuestItems() do
link = GetQuestItemLink("required", i);
if (link) then
FLT_ProcessLinks(link);
end
end
for i = 1, GetNumQuestChoices() do
link = GetQuestItemLink("choice", i);
if (link) then
FLT_ProcessLinks(link);
end
end
for i = 1, GetNumQuestRewards() do
link = GetQuestItemLink("reward", i);
if (link) then
FLT_ProcessLinks(link);
end
end
end
function FLT_ScanInventory()
local bagid, size, slotid, link;
for bagid = 0, 4, 1 do
size = GetContainerNumSlots(bagid);
if( size ) then
for slotid = size, 1, -1 do
link = GetContainerItemLink(bagid, slotid);
if( link ) then
FLT_ProcessLinks(link);
end
end
end
end
end
function FLT_ScanAuction()
local numBatchAuctions, totalAuctions = GetNumAuctionItems("list");
local auctionid, link;
if( numBatchAuctions > 0 ) then
for auctionid = 1, numBatchAuctions do
link = GetAuctionItemLink("list", auctionid);
if( link ) then
FLT_ProcessLinks(link);
end
end
end
end
function FLT_ScanBank()
local index, bagid, size, slotid, link;
local lBankBagIDs = { BANK_CONTAINER, 5, 6, 7, 8, 9, 10, };
for index, bagid in lBankBagIDs do
size = GetContainerNumSlots(bagid);
if( size ) then
for slotid = size, 1, -1 do
link = GetContainerItemLink(bagid, slotid);
if( link ) then
FLT_ProcessLinks(link);
end
end
end
end
end
function FLT_LinkifyName(head, text, tail)
if (head ~= "|h" and tail ~= "|h") then -- only linkify things text that isn't linked already
local link = FLT_GetLinkByName(text);
if (link) then return link; end
end
return head.."["..text.."]"..tail;
end
function FLT_FindInItemTooltip(text, link)
LinkeratorTip:ClearLines();
LinkeratorTip:SetHyperlink(link);
for lineNum = 1, LinkeratorTip:NumLines() do
local leftText = getglobal("LinkeratorTipTextLeft"..lineNum):GetText();
if (leftText and string.find(string.lower(leftText), text)) then return true; end
local rightText = getglobal("LinkeratorTipTextRight"..lineNum):GetText();
if (rightText and string.find(string.lower(rightText), text)) then return true; end
end
for lineNum = 1, LinkeratorTip:NumLines() do
-- for some reason ClearLines alone isn't clearing the right-side text
getglobal("LinkeratorTipTextLeft"..lineNum):SetText(nil);
getglobal("LinkeratorTipTextRight"..lineNum):SetText(nil);
end
end
function FLT_ParseChatMessage(text)
return string.gsub(text, "([|]?[h]?)%[(.-)%]([|]?[h]?)", FLT_LinkifyName);
end
-- Hooks
FLT_Orig_ChatEdit_OnTextChanged = ChatEdit_OnTextChanged;
function ChatEdit_OnTextChanged()
local text = this:GetText();
if (string.find(text, "^/script") or string.find(text, "^/dump")) then
-- don't parse
else
text = FLT_ParseChatMessage(text);
this:SetText(text);
end
FLT_Orig_ChatEdit_OnTextChanged(this);
end
FLT_Orig_QuestLog_UpdateQuestDetails = QuestLog_UpdateQuestDetails;
function QuestLog_UpdateQuestDetails()
for i = 1, GetNumQuestLogChoices() do
link = GetQuestLogItemLink("choice", i);
if (link) then
FLT_ProcessLinks(link);
end
end
for i = 1, GetNumQuestLogRewards() do
link = GetQuestLogItemLink("reward", i);
if (link) then
FLT_ProcessLinks(link);
end
end
FLT_Orig_QuestLog_UpdateQuestDetails();
end