vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
-- MetaMapQST
-- Written by MetaHawk - aka Urshurak

QST_DELAY                               = 0.5;
QST_BUTTONWIDTH = 400;

QST_HEADERCOLOUR    = "|cFFBFBFFF";
QST_TITLECOLOUR     = "|cFFFFFFFF";
QST_OVERVIEWCOLOUR  = "|cFF7F7F7F";
QST_SPECIALCOLOUR   = "|cFFFFFF00";
QST_INITIALCOLOUR   = "|cFFD82619";
QST_MIDDLECOLOUR    = "|cFFFFFF00";
QST_COMPLETEDCOLOUR     = "|cFF00FF19";

QST_Options       = {};
QST_QuestLog     = {};
QST_QuestBase     = {};
QST_QuestList     = {};
QST_ItemList      = {};
QST_AcceptQuest   = nil;
QST_FinishQuest   = nil;
QST_AbandonQuest   = nil;

QST_Default = {
        ["LoadAlways"]  = false,
        ["ShowPlayers"] = false,
        ["ShowAll"]     = false,
        ["ShowActive"]  = false,
        ["SaveHistory"] = true,
        ["SaveDesc"]    = true,
        ["SaveRew"]     = true,
        ["Padding"]     = 10,
        ["SortOrder"]   = "logtime",
}

QST_VarsLoaded      = false;
QST_ButtonTotal     = 0;
QST_ActivePlayer    = "";
QST_SelectedPlayer  = "";
QST_LastRefresh     = 0.0;
QST_QuestLogUpdate  = false;
QST_ShowHeader      = true;
QST_LastSearch      = "";
QST_QuestDetail     = nil;

QST_FilterMenu = {
        [1] = {QST_SORT_TITLE,  "SortOrdertitle"},
        [2] = {QST_SORT_LEVEL,  "SortOrderlevel"},
        [3] = {QST_SORT_LOGGED, "SortOrderlogtime"},
        [4] = {"", "", 1},
        [5] = {QST_FILTER_ALL, "ShowAll"},
        [6] = {QST_FILTER_MAP, "ShowPlayers"},
        [7] = {QST_FILTER_ACT, "ShowActive"},
        [8] = {"", "", 1},
}

function QST_OnLoad()
        this:RegisterEvent("ADDON_LOADED");
        this:RegisterEvent("QUEST_LOG_UPDATE");
        this:RegisterEvent("QUEST_DETAIL");
        this:RegisterEvent("QUEST_FINISHED");
        this:RegisterEvent("UNIT_NAME_UPDATE");
        this:RegisterEvent("PLAYER_LEVEL_UP");
        this:RegisterEvent("WORLD_MAP_UPDATE");
        this:RegisterEvent("ZONE_CHANGED_NEW_AREA");
        this:RegisterEvent("UPDATE_MOUSEOVER_UNIT");
end

function QST_OnEvent(event)
        if(event == "ADDON_LOADED" and arg1 == "MetaMapQST") then
                QST_LoadConfig();
        elseif(event == "QUEST_DETAIL") then
                QST_QuestDetail = GetTitleText();
        elseif(event == "ZONE_CHANGED_NEW_AREA") then
                QST_RefreshDisplay();
        elseif(event == "WORLD_MAP_UPDATE" and QST_DisplayFrame:IsVisible()) then
                QST_RefreshDisplay();
        elseif(event == "INSTANCE_MAP_UPDATE" and QST_DisplayFrame:IsVisible()) then
                QST_RefreshDisplay();
        elseif(event == "PLAYER_LEVEL_UP" and QST_DisplayFrame:IsVisible()) then
                QST_ShowDisplay();
        elseif(event == "UNIT_NAME_UPDATE" and QST_DisplayFrame:IsVisible()) then
                QST_ShowDisplay();
        elseif(event == "UPDATE_MOUSEOVER_UNIT") then
                QST_EnhanceTooltip();
        elseif(event == "QUEST_LOG_UPDATE") then
                QST_QuestLogUpdate = true;
                QST_LastRefresh = 0.0;
        end
end

function QST_ToggleFrame(mode)
        if(mode == 2) then return; end
        if(QST_DisplayFrame:IsVisible()) then
                MetaMapContainer_ShowFrame();
                if(mode == 1) then
                        MetaMap_ToggleFrame(WorldMapFrame);
                end
        else
                if(not WorldMapFrame:IsVisible()) then
                        MetaMap_ToggleFrame(WorldMapFrame);
                end
                MetaMapContainer_ShowFrame(QST_DisplayFrame);
        end
end

function QST_LoadConfig()
        --- Hooks
        QST_Orig_afTooltip = aftt_setName;
        aftt_setName = QST_afTooltip;
        QST_Orig_ContainerButtonOnEnter = ContainerFrameItemButton_OnEnter;
        ContainerFrameItemButton_OnEnter = QST_ContainerButtonOnEnter;
        Orig_QuestAcceptOnClick = QuestDetailAcceptButton_OnClick;
        QuestDetailAcceptButton_OnClick = QST_QuestAcceptOnClick;
        Orig_RewardCompleteOnClick = QuestRewardCompleteButton_OnClick;
        QuestRewardCompleteButton_OnClick = QST_QuestCompleteOnClick;
        Orig_AbandonQuest = AbandonQuest;
        AbandonQuest = QST_AbandonQuestOnClick;
        if(IsAddOnLoaded("Sanity")) then
                QST_OrigSanityButtonOnEnter = SanityItemButton_OnEnter;
                SanityItemButton_OnEnter = QST_SanityButtonOnEnter;
        end
        --- End Hooks
        for option, value in QST_Default do
                if(QST_Options[option] == nil) then QST_Options[option] = value; end
        end
        if(QST_QuestLog == nil) then QST_QuestLog = {}; end
        QST_ActivePlayer = UnitName("player").." of "..GetCVar("realmName");
        QST_SelectedPlayer = QST_ActivePlayer;
        QST_UpdateQuests();
        QST_UpdatePlayer();
        UIDropDownMenu_Initialize(QST_FilterSelectMenu, QST_FilterMenuInit, "MENU");
        UIDropDownMenu_SetText(QST_SelectedPlayer, QST_FilterSelect)
        QST_VarsLoaded = true;
end

function QST_OnUpdate(arg1)
        if(not QST_VarsLoaded) then return; end
        if(QST_QuestLogUpdate == true) then
                QST_LastRefresh = QST_LastRefresh + arg1;
                if(QST_LastRefresh > QST_DELAY) then
                        QST_UpdateQuests();
                        QST_LastRefresh = 0.0;
                        QST_QuestLogUpdate = false;
                end
        end
end

function QST_UpdatePlayer()
        local tempPlayers = {};
        local t = table.getn(QST_FilterMenu)+1;
        for index, qData in QST_QuestLog do
                for player, value in qData.qPlayer do
                        if(tempPlayers[player] == nil) then
                                tempPlayers[player] = 1;
                        end
                end
        end
        for index, qData in QST_QuestBase do
                for player, value in qData.qPlayer do
                        if(tempPlayers[player] == nil) then
                                tempPlayers[player] = 1;
                        end
                end
        end
        for player, value in tempPlayers do
                QST_FilterMenu[t] = {player, "player"};
                t = t +1;
        end
end

function QST_UpdateQuests()
        local qData, qZone;
        for qIndex=1, GetNumQuestLogEntries(), 1 do
                local QuestTitle, _, _, isHeader = GetQuestLogTitle(qIndex);
                if(isHeader) then
                        qZone = QuestTitle;
                else
                        SelectQuestLogEntry(qIndex);
                        for index, qQuests in QST_QuestLog do
                                if(qQuests.qTitle == QuestTitle and qQuests.qPlayer[QST_ActivePlayer]) then 
                                        qData = qQuests; break;
                                end
                        end
                        if(qData) then
                                QST_UpdateDataBase(qData, qIndex);
                        else
                                local n = table.getn(QST_QuestLog)+1;
                                QST_QuestLog[n] = {};
                                QST_CreateDatabase(QST_QuestLog[n], qZone, qIndex);
                        end
                        qData = nil;
                end
        end
        QST_SetLogOrder();
        if(QST_DisplayFrame:IsVisible()) then
                QST_RefreshDisplay();
        end
end

function QST_SetLogOrder()
        local TmpData = {};
        local new = 1;
        for index, value in QST_QuestLog do
                TmpData[new] = {};
                TmpData[new] = value;
                new = new +1;
        end
        QST_QuestLog = {};
        QST_QuestLog = TmpData;
end

function QST_UpdateDataBase(qData, qIndex)
        local QuestTitle, QuestLevel, QuestTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(qIndex);
        local Description, Objectives = GetQuestLogQuestText();
        local status = "";
        if(isComplete == nil) then status = QST_MIDDLECOLOUR..QST_QUEST_ACTIVE;
        elseif(isComplete < 0) then status = QST_INITIALCOLOUR..QST_QUEST_FAILED;
        elseif(isComplete == 1) then status = QST_COMPLETEDCOLOUR..QST_QUEST_DONE; end

        qData.qIndex = qIndex;
        qData["qPlayer"][QST_ActivePlayer]["qStatus"] = status;
        if(QST_AcceptQuest and QST_AcceptQuest.Title == QuestTitle) then
                qData["qNPC"][1] = {};
                qData["qNPC"][1].qName = QST_AcceptQuest.NPC;
                qData["qNPC"][1].qZone = QST_AcceptQuest.zone;
                qData["qNPC"][1].qX = QST_AcceptQuest.xPos;
                qData["qNPC"][1].qY = QST_AcceptQuest.yPos;
                QST_AcceptQuest = nil;
        end
        QST_UpdateQuestItems(QuestTitle, qData);
end

function QST_CreateDatabase(qData, qZone, qIndex)
        local QuestTitle, QuestLevel, QuestTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(qIndex);
        local Description, Objectives = GetQuestLogQuestText();
        local continent, zone = MetaMap_NameToZoneID(qZone);
        local status = "";

        if(isComplete == nil) then status = QST_MIDDLECOLOUR..QST_QUEST_ACTIVE;
        elseif(isComplete < 0) then status = QST_INITIALCOLOUR..QST_QUEST_FAILED;
        elseif(isComplete == 1) then status = QST_COMPLETEDCOLOUR..QST_QUEST_DONE; end
        if(zone == 0) then qData["qArea"] = GetRealZoneText(); end

        qData["qTitle"] = QuestTitle;
        qData["qZone"] = qZone;
        qData["qObj"] = Objectives;
        qData["qDesc"] = Description;
        qData["qLogtime"] = GetTime();
        qData["qNote"] = "";
        qData["qMoney"] = GetQuestLogRewardMoney();
        qData["qLevel"] = QuestLevel;
        qData["qTag"] = QuestTag;
        qData["qIndex"] = qIndex;
        qData["qPlayer"] = {};
        qData["qNPC"] = {};
        qData["qPlayer"][QST_ActivePlayer] = {};
        qData["qPlayer"][QST_ActivePlayer]["qStatus"] = status;
                
        if(GetNumQuestLogRewards() > 0) then
                qData["qReward"] = {};
                for index=1, GetNumQuestLogRewards(), 1 do
                        local name, texture, numItems, quality, isUsable = GetQuestLogRewardInfo(index);
                        qData["qReward"][index] = {};
                        qData["qReward"][index]["qAmount"] = numItems;
                        qData["qReward"][index]["qLink"] = GetQuestLogItemLink("reward", index);
                        qData["qReward"][index]["qTex"] = texture;
                end
        end
        if(GetNumQuestLogChoices() > 0) then
                qData["qChoice"] = {};
                for index=1, GetNumQuestLogChoices(), 1 do
                        local name, texture, numItems, quality, isUsable = GetQuestLogChoiceInfo(index);
                        qData["qChoice"][index] = {};
                        qData["qChoice"][index]["qAmount"] = numItems;
                        qData["qChoice"][index]["qLink"] = GetQuestLogItemLink("choice", index);
                        qData["qChoice"][index]["qTex"] = texture;
                end
        end
        if(GetQuestLogRewardSpell()) then
                qData["qSpell"] = {};
                local texture, name = GetQuestLogRewardSpell();
                qData["qSpell"] = {};
                qData["qSpell"]["qLink"] = name;
                qData["qSpell"]["qTex"] = texture;
        end
        if(QST_AcceptQuest and QST_AcceptQuest.Title == QuestTitle) then
                qData["qNPC"][1] = {};
                qData["qNPC"][1].qName = QST_AcceptQuest.NPC;
                qData["qNPC"][1].qZone = QST_AcceptQuest.zone;
                qData["qNPC"][1].qX = QST_AcceptQuest.xPos;
                qData["qNPC"][1].qY = QST_AcceptQuest.yPos;
                QST_AcceptQuest = nil;
        end
        QST_UpdateQuestItems(QuestTitle, qData);
end

function QST_UpdateQuestItems(QuestTitle, qData)
        if(GetNumQuestLeaderBoards() > 0) then
                qData["qItems"] = {};
                for index=1, GetNumQuestLeaderBoards(), 1 do
                        local LeaderBoardText, iType, Finished = GetQuestLogLeaderBoard(index);
                        local i, j, ItemName, NumItems, NumNeeded = string.find(LeaderBoardText, "(.*):%s*([-%d]+)%s*/%s*([-%d]+)%s*$");
                        if(NumItems ~= nil) then
                                local slain = "";
                                i, j = string.find(ItemName, QST_TOOLTIP_SLAIN);
                                if(i ~= nil) then
                                        ItemName = string.sub(ItemName, 1, i - 2);
                                        slain = " "..QST_TOOLTIP_SLAIN;
                                end
                                qData["qItems"][index] = {};
                                qData["qItems"][index] = ItemName..slain..": "..NumItems.."/"..NumNeeded;
                                QST_ItemList[ItemName] = {};
                                QST_ItemList[ItemName]["qTitle"] = QuestTitle;
                                QST_ItemList[ItemName]["qItems"] = NumItems.."/"..NumNeeded;
                        end
                end
        end
end

function QST_RefreshDisplay()
        QST_QuestList = {};
        local qCount2, qTotal2, aCount2, aTotal2 = QST_DisplayInit("QST_QuestBase");
        local qCount1, qTotal1, aCount1, aTotal1 = QST_DisplayInit("QST_QuestLog");
        QST_InfoText1:SetText(QST_QUEST_TEXT..": "..qCount1 + qCount2.."/"..qTotal1 + qTotal2);
        QST_InfoText2:SetText(QST_QUEST_ACTIVE..": "..aCount1 + aCount2.."/"..aTotal1 + aTotal2);
        QST_SortQuestList(QST_Options.SortOrder);
        QST_ShowDisplay();
end

function QST_DisplayInit(qDB)
        QST_ShowHeader = true;
        local qBase = getglobal(qDB);
        local questCount, questTotal, activeCount, activeTotal = 0,0,0,0;
        local mapContinent, mapZone, _, mapName = MetaMap_GetCurrentMapInfo();
        QST_HeaderText:SetText(mapName);

        for index, qData in qBase do
                local showThis = false;
                local continent, zone;
                questTotal = questTotal +1;
                if(qData.qIndex) then activeTotal = activeTotal +1; end
                if(qData.qArea) then
                        if(QST_GetLandMark(qData.qZone)) then showThis = true;
                        elseif(qData.qArea == mapName) then showThis = true;
                        else continent, zone = MetaMap_NameToZoneID(qData.qArea); end
                else
                        continent, zone = MetaMap_NameToZoneID(qData.qZone);
                end
                if(mapName == "World") then showThis = true; end
                if(continent == mapContinent and mapName == "Kalimdor") then showThis = true; end
                if(continent == mapContinent and mapName == "Eastern Kingdoms") then showThis = true; end
                if(continent == mapContinent and zone == mapZone) then showThis = true; end
                if(not QST_Options.ShowPlayers and not qData["qPlayer"][QST_SelectedPlayer]) then showThis = false; end
                if(QST_Options.ShowActive and not qData.qIndex) then showThis = false; end
                if(qData.qZone == mapName and not QST_Options.ShowAll) then QST_ShowHeader = false; end
                if(qData.qArea and qData.qArea == mapName and not QST_Options.ShowAll) then QST_ShowHeader = false; end
                        if(showThis or QST_Options.ShowAll) then
                        if(string.find(string.lower(qData.qTitle),string.lower(QST_LastSearch),1,true)~=nil
                                or string.find(string.lower(qData.qDesc),string.lower(QST_LastSearch),1,true)~=nil
                                or string.find(string.lower(qData.qObj),string.lower(QST_LastSearch),1,true)~=nil
                                or string.find(string.lower(qData.qZone),string.lower(QST_LastSearch),1,true)~=nil
                                or string.find(string.lower(qData.qNote),string.lower(QST_LastSearch),1,true)~=nil) then
                                tinsert(QST_QuestList, {qTitle = qData.qTitle, qZone = qData.qZone, qLevel = qData.qLevel, qLogtime = qData.qLogtime, qDB = qDB, qIndex = index});
                                if(qData["qPlayer"][QST_SelectedPlayer]) then
                                        if(string.find(qData["qPlayer"][QST_SelectedPlayer]["qStatus"], "Active")) then activeCount = activeCount +1; end
                                end
                                questCount = questCount + 1;
                        end
                end
        end
        return questCount, questTotal, activeCount, activeTotal;
end

function QST_SortQuestList(sort)
        local tmpList = {};
        local name = "";
        local tmp = MetaMap_sortType;
        MetaMap_sortType = METAMAP_SORTBY_NAME;
        for index, value in QST_QuestList do
                if(sort == "title") then
                        name = value.qZone..value.qTitle..value.qLevel;
                elseif(sort == "level") then
                        name = value.qZone..value.qLevel..value.qTitle;
                else
                        name = value.qZone..value.qLogtime;
                end
                tinsert(tmpList, {qTitle = value.qTitle, qZone = value.qZone, qLevel = value.qLevel, qLogtime = value.qLogtime, qDB = value.qDB, qIndex = value.qIndex, name = name});
        end
        QST_QuestList = {};
  table.sort(tmpList, MetaMap_SortCriteria);
        for index, value in tmpList do
                tinsert(QST_QuestList, {qTitle = value.qTitle, qZone = value.qZone, qLevel = value.qLevel, qLogtime = value.qLogtime, qDB = value.qDB, qIndex = value.qIndex});
        end
        tmpList = nil;
        MetaMap_sortType = tmp;
end

function QST_ShowDisplay()
        local questInfo = "";
        local buttonID = 1;
        local setHeader = "";
        local ScrollHeight = 0;

        for index, qList in QST_QuestList do
                local qData = getglobal(qList.qDB)[qList.qIndex];
                if(QST_ShowHeader and setHeader ~= qData.qZone) then
                        local area = "";
                        local button = QST_CreateButton(buttonID);
                        local buttontext = getglobal("QSTButton"..buttonID.."Text");
                        if(qData.qArea) then area = " ("..qData.qArea..")"; end
                        buttontext:SetText(format(QST_HEADERCOLOUR.."%s|r", qData.qZone..area).."\n");
                        if(buttonID == 1) then
                                button:SetPoint("TOPLEFT", "QST_ScrollChild", "TOPLEFT", 10, -10)
                        else
                                button:SetPoint("TOP", getglobal("QSTButton"..buttonID-1), "BOTTOM", 0, 0)
                        end
                        if(qData.qArea) then
                                button.qMap = qData.qArea;
                        else
                                button.qMap = qData.qZone;
                        end
                        button.qDB = qList.qDB;
                        button.qIndex = qList.qIndex;
                        buttontext:SetWidth(QST_BUTTONWIDTH);
                        button:SetHeight(buttontext:GetHeight() + QST_Options.Padding);
                        button:SetParent("QST_ScrollChild");
                        button:Show();
                        ScrollHeight = ScrollHeight + button:GetHeight();
                        setHeader = qData.qZone;
                        buttonID = buttonID +1;
                end
                local button = QST_CreateButton(buttonID);
                local buttontext = getglobal("QSTButton"..buttonID.."Text");
                if(buttonID == 1) then
                        button:SetPoint("TOPLEFT", "QST_ScrollChild", "TOPLEFT", 10, -10)
                else
                        button:SetPoint("TOP", getglobal("QSTButton"..buttonID-1), "BOTTOM", 0, 0)
                end
                local col = GetDifficultyColor(qData.qLevel);
                local cText = "|c%02X%02X%02X%02X%s|r";
                local R = col.r * 255; local G = col.g * 255; local B = col.b * 255;
                questInfo = questInfo.."  ";
                if(qData.qTag == ELITE) then
                        questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."+] ");
                elseif(qData.qTag == QST_DUNGEON) then
                        questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."d] ");
                elseif(qData.qTag == RAID) then
                        questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."r] ");
                elseif(qData.qTag == QST_PVP) then
                        questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."p] ");
                else
                        questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."] ");
                end
                questInfo = questInfo..format(QST_TITLECOLOUR.."%s|r", qData.qTitle).."\n";
                if(qData.qItems) then
                        for x, value in qData.qItems, 1 do
                                questInfo = questInfo.."    "..QST_GetColourString(value)..value.."\n";
                        end
                else
                        questInfo = questInfo.."    "..format(QST_OVERVIEWCOLOUR.."%s|r", qData.qObj).."\n";
                end
                if(qData.qIndex) then
                        for index, name in QST_FilterMenu do
                                if(qData.qPlayer[name[1]]) then
                                        getglobal("QSTButton"..buttonID.."Status"):SetText(qData.qPlayer[name[1]].qStatus);
                                end
                        end
                else
                        getglobal("QSTButton"..buttonID.."Status"):SetText(QST_QUEST_HISTORY);
                end
                if(strlen(qData.qNote) > 0) then
                        getglobal("QSTButton"..buttonID.."Noted"):SetText(QST_HEADERCOLOUR..QST_NOTE_FLAG);
                end
                button.qDB = qList.qDB;
                button.qIndex = qList.qIndex;
                buttontext:SetText(questInfo);
                buttontext:SetWidth(QST_BUTTONWIDTH);
                button:SetHeight(buttontext:GetHeight() + QST_Options.Padding);
                button:SetParent("QST_ScrollChild");
                buttontext:Show();
                button:Show();
                questInfo = "";
                buttonID = buttonID + 1;
                ScrollHeight = ScrollHeight + button:GetHeight();
        end
        for i=buttonID, QST_ButtonTotal, 1 do
                getglobal("QSTButton"..i):Hide()
        end
        QST_ScrollChild:SetHeight(ScrollHeight);
        QST_ScrollFrame:UpdateScrollChildRect()
end

function QST_FilterMenuInit()
        for index, menuItem in QST_FilterMenu do
                local check = nil;
                local spacer = nil;
                if(menuItem[3]) then spacer = 1; end
                if(menuItem[2] == "player") then
                        if(QST_Options.ShowPlayers) then
                                check = 1;
                        elseif(menuItem[1] == QST_SelectedPlayer) then
                                check = 1;
                        end
                elseif(QST_Options.SortOrder == string.gsub(menuItem[2], "SortOrder", "")) then
                        check = 1;
                elseif(QST_Options[menuItem[2]]) then
                        check = 1;
                end
                local info = {};
                info.isTitle = spacer;
                info.notClickable = spacer;
                info.checked = check;
                info.text = menuItem[1];
                info.value = menuItem[2];
                info.func = QST_FilterMenuOnClick;
                UIDropDownMenu_AddButton(info);
        end
end

function QST_FilterMenuOnClick()
        if(this.value == "player") then
                QST_SelectedPlayer = this:GetText();
                QST_Options.ShowPlayers = false;
        elseif(string.find(this.value, "SortOrder")) then
                QST_Options.SortOrder = string.gsub(this.value, "SortOrder", "");
                QST_SortQuestList(QST_Options.SortOrder);
                QST_ShowDisplay();
                return;
        else
                QST_ToggleOptions(this.value);
        end
  UIDropDownMenu_SetText(this:GetText(), QST_FilterSelect);
        QST_RefreshDisplay();
end

function QSTButton_OnEnter()
        local qData = getglobal(this.qDB)[this.qIndex];
        if(not qData or QST_RewardFrame:IsVisible() or QST_EditorFrame:IsVisible()) then return; end
        WorldMapTooltip:SetOwner(QST_DisplayFrame, "ANCHOR_BOTTOMRIGHT", 0, QST_DisplayFrame:GetHeight()* QST_DisplayFrame:GetEffectiveScale());
        if(IsControlKeyDown()) then
                WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
                WorldMapTooltip:AddLine(qData.qNote, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
        elseif(IsShiftKeyDown()) then
                WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
                if(qData.qNPC[1]) then
                        WorldMapTooltip:AddDoubleLine("QuestStart:", QST_COMPLETEDCOLOUR..qData.qNPC[1].qName, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
                end
                if(qData.qNPC[2]) then
                        WorldMapTooltip:AddDoubleLine("QuestEnd:", QST_COMPLETEDCOLOUR..qData.qNPC[2].qName, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
                end
                WorldMapTooltip:AddLine(" ", 0, 0, 0, 0);
                for name, value in qData.qPlayer do
                        WorldMapTooltip:AddDoubleLine(name, value.qStatus, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
                end
        else
                WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
                WorldMapTooltip:AddLine(qData.qObj, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
                WorldMapTooltip:AddLine(" ", 0, 0, 0, 0);
                if(qData.qDesc) then
                        WorldMapTooltip:AddLine(qData.qDesc, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
                end
                WorldMapTooltip:AddLine(qData.qTag, 1, 0, 1, 1);
                if(qData.qIndex) then
                        SelectQuestLogEntry(qData.qIndex);
                        for i = 1, GetNumPartyMembers() do
                                if(IsUnitOnQuest(qData.qIndex, "party"..i)) then
                                        WorldMapTooltip:AddLine(QST_COMPLETEDCOLOUR..UnitName("party"..i));
                                else
                                        WorldMapTooltip:AddLine(QST_INITIALCOLOUR..UnitName("party"..i));
                                end
                        end
                end
        end
        WorldMapTooltip:SetPoint("TOPRIGHT", QST_DisplayFrame, "TOPRIGHT", 0, 0);
        WorldMapTooltip:Show();
end

function QSTButton_OnClick(button)
        local qData = getglobal(this.qDB)[this.qIndex];
        QST_EditorFrame:Hide()
        if(this.qMap) then
                local continent, zone = MetaMap_NameToZoneID(this.qMap, 1);
                if(continent == 0 and zone > 0) then
                        MetaMap_CurrentMap = zone;
                        MetaMap_Toggle(true);
                else
                        SetMapZoom(continent, zone);
                end
                return;
        end
        if(IsShiftKeyDown() and ChatFrameEditBox:IsVisible()) then
                if(button == "LeftButton") then
                        if(qData.qTag == ELITE) then
                                ChatFrameEditBox:Insert("["..qData.qLevel.."+] "..qData.qTitle.." ");
                        elseif(qData.qTag == QST_DUNGEON) then
                                ChatFrameEditBox:Insert("["..qData.qLevel.."d] "..qData.qTitle.." ");
                        elseif(qData.qTag == RAID) then
                                ChatFrameEditBox:Insert("["..qData.qLevel.."r] "..qData.qTitle.." ");
                        elseif(qData.qTag == QST_PVP) then
                                ChatFrameEditBox:Insert("["..qData.qLevel.."p] "..qData.qTitle.." ");
                        else
                                ChatFrameEditBox:Insert("["..qData.qLevel.."] "..qData.qTitle.." ");
                        end
                else
                        local ChatItems = "";
                        if(qData.qItems) then
                                for i, value in qData.qItems, 1 do
                                        ChatItems = ChatItems.."{"..qData.qItems[i].."} ";
                                end
                        else
                                ChatItems = ChatItems.."{"..qData.qObj.."} ";
                        end
                        ChatFrameEditBox:Insert(ChatItems);
                end
                return;
        elseif(IsControlKeyDown() and qData.qIndex) then
                if(button == "LeftButton") then
                        SelectQuestLogEntry(qData.qIndex);
                        if(GetQuestLogPushable() and GetNumPartyMembers() > 0) then
                                QuestLogPushQuest();
                        end
                else
                        SelectQuestLogEntry(qData.qIndex);
                        SetAbandonQuest();
                        StaticPopup_Show("ABANDON_QUEST", GetAbandonQuestName());
                        QST_AbandonQuest = GetAbandonQuestName();
                end
                return;
        else
                if(button == "LeftButton") then
                        QST_RewardFrameShow(this.qDB, this.qIndex);
                else
                        QST_QuestNoteShow(this.qDB, this.qIndex);
                end
        end
end

function QST_RewardFrameShow(qDB, qIndex)
        local qData = getglobal(qDB)[qIndex];
        local height = 100;
        local rewardCount, choiceCount, spellCount = 0,0,0;
        local receiveText = getglobal("QST_RewardItemReceiveText");
        local chooseText = getglobal("QST_RewardItemChooseText");
        
        if(qData.qChoice) then
                chooseText:Show();
                height = height + 14
                for i, choice in qData.qChoice do       
                        local _, _, itemLink, itemName = string.find(choice.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
                        rewardItem = getglobal("QST_RewardItem"..i);
                        rewardItem.type = "choice";
                        rewardItem.rewardType = "item";
                        rewardItem.link = choice.qLink;
                        rewardItem.itemID = itemLink;
                        rewardItem:Show();
                        SetItemButtonCount(rewardItem, choice.qAmount);
                        SetItemButtonTexture(rewardItem, choice.qTex);
                        if(qData.qIndex and qData.qPlayer[QST_ActivePlayer]) then
                                SelectQuestLogEntry(qData.qIndex);
                                _, _, _, _, isUsable = GetQuestLogChoiceInfo(i);
                                if(isUsable) then
                                        SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
                                else
                                        SetItemButtonTextureVertexColor(rewardItem, 0.9, 0, 0);
                                end
                        else
                                SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
                        end
                        if(i > 1) then
                                if(mod(i,2) == 1) then
                                        rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..(i - 2), "BOTTOMLEFT", 0, -2);
                                        height = height + 40
                                else
                                        rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..(i - 1), "TOPRIGHT", 20, 0);
                                end
                        else
                                rewardItem:SetPoint("TOPLEFT", "QST_RewardItemChooseText", "BOTTOMLEFT", 20, -5);
                                height = height + 40
                        end
                        choiceCount = i;
                end
        else
                chooseText:Hide();
        end
        if(qData.qReward or qData.qSpell or (qData.qMoney and qData.qMoney > 0)) then
                height = height + 16;
                receiveText:Show();
                if(qData.qChoice) then
                        local index = choiceCount;
                        if(mod(index, 2) == 0 ) then
                                index = index - 1;
                        end
                        receiveText:SetText(TEXT(REWARD_ITEMS));
                        receiveText:SetPoint("TOPLEFT", "QST_RewardItem"..index, "BOTTOMLEFT", -20, -5);
                else 
                        receiveText:SetText(TEXT(REWARD_ITEMS_ONLY));
                        receiveText:SetPoint("TOPLEFT", "QST_RewardFrame", "TOPLEFT", 20, -45);
                end
                if(qData.qReward) then
                        getglobal("QST_RewardItemReceiveText"):Show();
                        for i, reward in qData.qReward do
                                local _, _, itemLink, itemName = string.find(reward.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
                                rewardItem = getglobal("QST_RewardItem"..(i + choiceCount));
                                rewardItem.type = "reward";
                                rewardItem.rewardType = "item";
                                rewardItem.link = reward.qLink;
                                rewardItem.itemID = itemLink;
                                rewardItem:Show();
                                SetItemButtonCount(rewardItem, reward.qAmount);
                                SetItemButtonTexture(rewardItem, reward.qTex);
                                if(qData.qIndex and qData.qPlayer[QST_ActivePlayer]) then
                                        SelectQuestLogEntry(qData.qIndex);
                                        _, _, _, _, isUsable = GetQuestLogRewardInfo(i);
                                        if(isUsable) then
                                                SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
                                        else
                                                SetItemButtonTextureVertexColor(rewardItem, 0.9, 0, 0);
                                        end
                                else
                                        SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
                                end
                                if(i > 1) then
                                        if(mod(i,2) == 1) then
                                                rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((i + choiceCount) - 2), "BOTTOMLEFT", 0, -2);
                                                height = height + 40
                                        else
                                                rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((i + choiceCount) - 1), "TOPRIGHT", 20, 0);
                                        end
                                else
                                        rewardItem:SetPoint("TOPLEFT", "QST_RewardItemReceiveText", "BOTTOMLEFT", 20, -5);
                                        height = height + 40
                                end
                                rewardCount = i;
                        end
                end
                if(qData.qSpell) then
                        local _, _, itemLink, itemName = string.find(qData.qSpell.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
                        rewardItem = getglobal("QST_RewardItem"..(rewardCount + choiceCount + 1));
                        rewardItem.rewardType = "spell";
                        rewardItem.name = itemName;
                        rewardItem.itemID = itemLink;
                        rewardItem:Show();
                        SetItemButtonTexture(rewardItem, qData.qSpell.qTex);
                        if(rewardsCount > 0) then
                                if(mod(rewardCount,2) == 0) then
                                        rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((rewardCount + choiceCount) - 1), "BOTTOMLEFT", 0, -2);
                                        height = height + 40
                                else
                                        rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((rewardCount + choiceCount)), "TOPRIGHT", 20, 0);
                                end
                        else
                                rewardItem:SetPoint("TOPLEFT", "QST_RewardItemReceiveText", "BOTTOMLEFT", 20, -5);
                                height = height + 40
                        end
                        spellCount = 1;
                end
        else    
                receiveText:Hide();
        end
        local totalRewards = rewardCount + choiceCount + spellCount;
        for i=totalRewards + 1, 10, 1 do
                getglobal("QST_RewardItem"..i):Hide();
        end
        if(totalRewards > 0 or (qData.qMoney and qData.qMoney > 0)) then
                MoneyFrame_Update("QST_RewardMoneyFrame", qData.qMoney);
                local x, y = GetCursorPosition();
                x = x / UIParent:GetEffectiveScale();
                y = y / UIParent:GetEffectiveScale();
                QST_RewardFrame:SetPoint("LEFT", "UIParent", "BOTTOMLEFT", x-20, y);
                QST_RewardFrame:SetHeight(height);
                QST_RewardFrame:Show();
        end
end

function QST_RewardItemOnClick()
        if(IsControlKeyDown()) then
                if(this.rewardType ~= "spell") then
                        MetaMap_ToggleDR(1);
                        DressUpItemLink(this.itemID);
                        DressUpFrame:Show();
                        DressUpItemLink(this.itemID);
                end
        elseif(IsShiftKeyDown()) then
                if(ChatFrameEditBox:IsVisible()) then
                        ChatFrameEditBox:Insert(this.link);
                end
        end
end

function QST_QuestNoteShow(qDB, qIndex)
        local qData = getglobal(qDB)[qIndex];
        QST_SaveNote.qDB = qDB;
        QST_SaveNote.qIndex = qIndex;
        QST_NotesEditBox:SetText(qData.qNote);
        QST_EditorFrame:Show();
        QST_NotesEditBox:SetFocus();
end

function QST_QuestNoteUpdate()
        local qData = getglobal(this.qDB)[this.qIndex];
        qData.qNote = QST_NotesEditBox:GetText();
        QST_EditorFrame:Hide();
        QST_ShowDisplay();
end

function QST_QuestAcceptOnClick()
        local x, y = GetPlayerMapPosition("player");
        local questStart = UnitName("target");
        if(questStart == nil) then questStart = "Quest Item"; end
        QST_AcceptQuest = {};
        QST_AcceptQuest.Title = QST_QuestDetail;
        QST_AcceptQuest.NPC = questStart;
        QST_AcceptQuest.zone = GetRealZoneText();
        QST_AcceptQuest.xPos = x;
        QST_AcceptQuest.yPos = y;
        QST_QuestDetail = nil;
        Orig_QuestAcceptOnClick();
end

function QST_QuestCompleteOnClick()
        local qIndex, qLog;
        local x, y = GetPlayerMapPosition("player");
        local questStart = UnitName("target");
        if(questStart == nil) then questStart = "Unknown"; end
        for index, qQuests in QST_QuestLog do
                if(qQuests.qTitle == GetTitleText() and qQuests.qPlayer[QST_ActivePlayer]) then
                        qIndex = index; qLog = qQuests; break;
                end
        end
        if(qIndex) then
                if(QST_Options.SaveHistory) then
                        qLog.qIndex = nil;
                        qLog.qPlayer[QST_ActivePlayer].qStatus = QST_COMPLETEDCOLOUR..QST_QUEST_DONE;
                        qLog.qNPC[2] = {};
                        qLog.qNPC[2]["qName"] = questStart;
                        qLog.qNPC[2]["qZone"] = GetRealZoneText;
                        qLog.qNPC[2]["qX"] = x;
                        qLog.qNPC[2]["qY"] = y;
                        for index, qQuests in QST_QuestBase do
                                if(qLog.qTitle == qQuests.qTitle and qLog.qObj == qQuests.qObj) then
                                        qBase = qQuests; break;
                                end
                        end
                        if(qBase) then
                                qBase.qNote = qBase.qNote.."\n\n"..qLog.qNote;
                                qBase.qPlayer[QST_ActivePlayer] = {};
                                qBase.qPlayer[QST_ActivePlayer] = qLog.qPlayer[QST_ActivePlayer];
                                qBase.qNPC[2] = qLog.qNPC[2];
                        else
                                if(not QST_Options.SaveDesc) then
                                        qLog.qDesc = nil;
                                end
                                if(not QST_Options.SaveRew) then
                                        qLog.qReward = nil; qLog.qChoice = nil; qLog.qSpell = nil;
                                end
                                QST_QuestBase[table.getn(QST_QuestBase)+1] = qLog;
                        end
                end
                QST_QuestLog[qIndex] = nil;
        end
        Orig_RewardCompleteOnClick();
end

function QST_AbandonQuestOnClick()
        local qIndex, qLog;
        for index, qQuests in QST_QuestLog do
                if(qQuests.qTitle == QST_AbandonQuest and qQuests.qPlayer[QST_ActivePlayer]) then
                        qIndex = index; qLog = qQuests; break;
                end
        end
        if(qIndex) then
                if(QST_Options.SaveHistory) then
                        qLog.qIndex = nil;
                        qLog.qPlayer[QST_ActivePlayer].qStatus = QST_INITIALCOLOUR..QST_QUEST_ABANDON;
                        for index, qQuests in QST_QuestBase do
                                if(qLog.qTitle == qQuests.qTitle and qLog.qObj == qQuests.qObj) then
                                        qBase = qQuests; break;
                                end
                        end
                        if(qBase) then
                                qBase.qNote = qBase.qNote.."\n\n"..qLog.qNote;
                                qBase.qPlayer[QST_ActivePlayer] = {};
                                qBase.qPlayer[QST_ActivePlayer] = qLog.qPlayer[QST_ActivePlayer];
                        else
                                if(not QST_Options.SaveDesc) then
                                        qLog.qDesc = nil;
                                end
                                if(not QST_Options.SaveRew) then
                                        qLog.qReward = nil; qLog.qChoice = nil; qLog.qSpell = nil;
                                end
                                QST_QuestBase[table.getn(QST_QuestBase)+1] = qLog;
                        end
                end
                QST_QuestLog[qIndex] = nil;
        end
        Orig_AbandonQuest();
end

function QST_afTooltip(unit)
        QST_Orig_afTooltip(unit);
        QST_EnhanceTooltip();
end

function QST_ContainerButtonOnEnter()
        QST_Orig_ContainerButtonOnEnter();
        QST_EnhanceTooltip();
end

function QST_SanityButtonOnEnter()
        QST_OrigSanityButtonOnEnter();
        QST_EnhanceTooltip();
end

function QST_EnhanceTooltip()
        if(GameTooltip == nil) then return; end
        if(not QST_ItemList) then return; end
        if(getglobal('GameTooltipTextLeft1'):GetText() ~= nil) then
                local itemName = string.gsub(getglobal('GameTooltipTextLeft1'):GetText(),"|c........(.*)|?r?","%1");
                if(QST_ItemList[itemName] == nil) then return; end
                if(getglobal('GameTooltipTextLeft2'):GetText() ~= ITEM_BIND_QUEST) then
                        GameTooltip:AddLine(ITEM_BIND_QUEST, 1, 1, 1, 1);
                        GameTooltip:SetHeight(GameTooltip:GetHeight() + 14);
                end
                GameTooltip:AddLine(QST_ItemList[itemName].qTitle..": "..QST_ItemList[itemName].qItems, 1, 0, 1, 1);
                length = getglobal(GameTooltip:GetName() .. "TextLeft" .. GameTooltip:NumLines()):GetStringWidth();
                length = length + 22;
                GameTooltip:SetHeight(GameTooltip:GetHeight() + 14);
                if(length > GameTooltip:GetWidth()) then
                        GameTooltip:SetWidth(length);
                end
        end
end

function QST_HintTooltip()
        WorldMapTooltip:SetOwner(this, "ANCHOR_TOPLEFT");
        WorldMapTooltip:SetText(QST_TTHINT_H0, 0.2, 0.5, 1, 1);
        WorldMapTooltip:AddLine(QST_TTHINT_H1, 0, 1, 0, 1);
        WorldMapTooltip:AddDoubleLine("Shift-onEnter", QST_TTHINT_T0, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Ctrl-onEnter", QST_TTHINT_T1, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Left-Click", QST_TTHINT_T2, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Right-Click", QST_TTHINT_T3, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Shift-Left", QST_TTHINT_T4, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Shift-Right", QST_TTHINT_T5, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Ctrl-Left", QST_TTHINT_T6, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Ctrl-Right", QST_TTHINT_T7, 1, 1, 1, 1);
        WorldMapTooltip:AddLine(QST_TTHINT_H2, 0, 1, 0, 1);
        WorldMapTooltip:AddDoubleLine("Shift-Left", QST_TTHINT_T8, 1, 1, 1, 1);
        WorldMapTooltip:AddDoubleLine("Ctrl-Left", QST_TTHINT_T9, 1, 1, 1, 1);
        WorldMapTooltip:Show();
end

function QST_ToggleOptions(key, value)
        if(value) then
                QST_Options[key] = value;
        else
                QST_Options[key] = not QST_Options[key];
        end
        return QST_Options[key];
end

function QST_CreateButton(id)
        local button;
        if(getglobal("QSTButton"..id)) then
                button = getglobal("QSTButton"..id);
                button.qMap = nil;
                button.qTitle = nil;
                button.qData = nil;
                getglobal("QSTButton"..id.."Text"):SetText("");
                getglobal("QSTButton"..id.."Status"):SetText("");
                getglobal("QSTButton"..id.."Noted"):SetText("");
        else
                button = CreateFrame("Button" ,"QSTButton"..id, QST_ScrollChild, "QST_ButtonTemplate");
                button:SetWidth(QST_ScrollChild:GetWidth());
                button:SetID(id);
                QST_ButtonTotal = QST_ButtonTotal +1;
        end
        return button;
end

function QST_GetLandMark(zoneText)
        for landmarkIndex = 1, GetNumMapLandmarks(), 1 do
                local name = GetMapLandmarkInfo(landmarkIndex);
                if(strlower(name) == strlower(zoneText)) then return true; end
        end
        return false;
end

function QST_GetColourString(Text)
        local i, j, ItemName, NumItems, NumNeeded = string.find(Text, "(.*):%s*([-%d]+)%s*/%s*([-%d]+)%s*$");
        local colour = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
        local colourInitial = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
        local colourMid = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
        local colourComplete = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
        colourInitial.r, colourInitial.g, colourInitial.b, colourInitial.a = QST_TextToRGB(QST_INITIALCOLOUR);
        colourMid.r, colourMid.g, colourMid.b, colourMid.a = QST_TextToRGB(QST_MIDDLECOLOUR);
        colourComplete.r, colourComplete.g, colourComplete.b, colourComplete.a = QST_TextToRGB(QST_COMPLETEDCOLOUR);

        local colourDelta1 = {
                a = (colourMid.a - colourInitial.a),
                r = (colourMid.r - colourInitial.r),
                g = (colourMid.g - colourInitial.g),
                b = (colourMid.b - colourInitial.b)
        };
        local colourDelta2 = {
                a = (colourComplete.a - colourMid.a),
                r = (colourComplete.r - colourMid.r),
                g = (colourComplete.g - colourMid.g),
                b = (colourComplete.b - colourMid.b)
        };
        if (NumItems ~= nil) then
                if ((NumItems / NumNeeded) < 0.5) then
                        colour.r = colourInitial.r + ((NumItems / (NumNeeded / 2)) * colourDelta1.r);
                        colour.g = colourInitial.g + ((NumItems / (NumNeeded / 2)) * colourDelta1.g);
                        colour.b = colourInitial.b + ((NumItems / (NumNeeded / 2)) * colourDelta1.b);
                else
                        colour.r = colourMid.r + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.r);
                        colour.g = colourMid.g + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.g);
                        colour.b = colourMid.b + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.b);
                end
        else
                i, j, ItemName, NumItems, NumNeeded = string.find(Text, "(.*):%s*([-%a]+)%s*/%s*([-%a]+)%s*$");
                if (Items ~= nil) then
                        if (NumItems == NumNeeded) then
                                return QST_COMPLETEDCOLOUR;
                        else
                                return QST_INITIALCOLOUR;
                        end
                else
                        return QST_SPECIALCOLOUR;
                end
        end

        if(colour.r > 1.0) then colour.r = 1.0; end
        if(colour.g > 1.0) then colour.g = 1.0; end
        if(colour.b > 1.0) then colour.b = 1.0; end
        if(colour.r < 0.0) then colour.r = 0.0; end
        if(colour.g < 0.0) then colour.g = 0.0; end
        if(colour.b < 0.0) then colour.b = 0.0; end

        return QST_RGBtoText(colour.r, colour.g, colour.b, colour.a);
end

function QST_TextToRGB(strColour)
        local i = 3;
        local iAlpha = tonumber(string.sub(strColour, i, i + 1), 16);
        local iRed = tonumber(string.sub(strColour, i + 2, i + 3), 16);
        local iGreen = tonumber(string.sub(strColour, i + 4, i + 5), 16);
        local iBlue = tonumber(string.sub(strColour, i + 6, i + 7), 16);
        iAlpha = iAlpha / 255;
        iRed = iRed / 255;
        iGreen = iGreen / 255;
        iBlue = iBlue / 255;
        return iRed, iGreen, iBlue, iAlpha;
end

function QST_RGBtoText(iRed, iGreen, iBlue, iAlpha)
        local strColour;
        iAlpha = floor(iAlpha * 255);
        iRed = floor(iRed * 255);
        iGreen = floor(iGreen * 255);
        iBlue = floor(iBlue * 255);
        strColour = format("|c%2x%2x%2x%2x", iAlpha, iRed, iGreen, iBlue);
        return strColour;
end