Inheritance: MonoBehaviour
    // -----------------------------------------------------------------------------------
    // Update
    // -----------------------------------------------------------------------------------
    private void Update()
    {
        Player player = Player.localPlayer;

        if (!player)
        {
            return;
        }

        if (panel.activeSelf)
        {
            List <UCE_Quest> activeQuests = player.UCE_quests.Where(q => !q.completed || (q.repeatable > 0 && !q.completedAgain)).ToList();

            int maxQuests = Mathf.Min(activeQuests.Count, maxActiveQuestsToShow);

            UIUtils.BalancePrefabs(slotPrefab.gameObject, maxQuests, content);

            // -- refresh all
            for (int i = 0; i < maxQuests; ++i)
            {
                int index              = i;
                UCE_UI_QuestSlot slot  = content.GetChild(index).GetComponent <UCE_UI_QuestSlot>();
                UCE_Quest        quest = activeQuests[index];

                // -- check cache
                if (Time.time > cacheTimer[index])
                {
                    // =======================================================================

                    // -- check gathered items
                    int[] gathered = player.checkGatheredItems(quest);

                    // -- check explored areas
                    int explored = 0;
#if _iMMOEXPLORATION
                    foreach (UCE_Area_Exploration area in quest.exploreTarget)
                    {
                        if (player.UCE_HasExploredArea(area))
                        {
                            explored++;
                        }
                    }
#endif

                    // -- check faction requirement
                    bool factionRequirementsMet = true;
#if _iMMOFACTIONS
                    factionRequirementsMet = player.UCE_CheckFactionRating(quest.factionRequirement);
#endif

                    // =======================================================================

                    // name button
                    GameObject descriptionPanel = slot.descriptionText.gameObject;
                    string     prefix           = descriptionPanel.activeSelf ? hidePrefix : expandPrefix;
                    slot.nameButton.GetComponentInChildren <Text>().text = prefix + quest.name;
                    slot.nameButton.onClick.SetListener(() =>
                    {
                        descriptionPanel.SetActive(!descriptionPanel.activeSelf);
                    });

                    if (quest.IsFulfilled(gathered, explored, factionRequirementsMet))
                    {
                        slot.nameButton.GetComponent <Image>().color = fulfilledQuestColor;
                    }
                    else
                    {
                        slot.nameButton.GetComponent <Image>().color = inprogressQuestColor;
                    }

                    // -- cancel button
                    slot.cancelButton.gameObject.SetActive(false);

                    // -- update cache
                    cacheTooltip[index] = quest.TrackerTip(gathered, explored, factionRequirementsMet, player.level);
                    cacheTimer[index]   = Time.time + cacheInterval;

                    // -- update description
                    slot.descriptionText.text = cacheTooltip[index];
                }
            }
        }
    }
    // -----------------------------------------------------------------------------------
    // Update
    // -----------------------------------------------------------------------------------
    private void Update()
    {
        Player player = Player.localPlayer;

        if (!player)
        {
            return;
        }

        // hotkey (not while typing in chat, etc.)
        if (Input.GetKeyDown(hotKey) && !UIUtils.AnyInputActive())
        {
            panel.SetActive(!panel.activeSelf);
        }

        if (panel.activeSelf)
        {
            List <UCE_Quest> activeQuests = new List <UCE_Quest>();

            if (showActiveQuests)
            {
                activeQuests = player.UCE_quests.Where(q => !q.completed || (q.repeatable > 0 && !q.completedAgain)).ToList();
            }
            else
            {
                activeQuests = player.UCE_quests.Where(q => q.completed).ToList();
            }

            if (cacheTimer == null || cacheTimer.Length != activeQuests.Count)
            {
                cacheTooltip = new string[player.UCE_quests.Count];
                cacheTimer   = new float[player.UCE_quests.Count];
            }

            UIUtils.BalancePrefabs(slotPrefab.gameObject, activeQuests.Count, content);

            // -- refresh all
            for (int i = 0; i < activeQuests.Count; ++i)
            {
                int index              = i;
                UCE_UI_QuestSlot slot  = content.GetChild(index).GetComponent <UCE_UI_QuestSlot>();
                UCE_Quest        quest = activeQuests[index];

                // -- check cache
                if (Time.time > cacheTimer[index])
                {
                    // =======================================================================

                    // -- check gathered items
                    int[] gathered = player.checkGatheredItems(quest);

                    // -- check explored areas
                    int explored = 0;
#if _iMMOEXPLORATION
                    foreach (UCE_Area_Exploration area in quest.exploreTarget)
                    {
                        if (player.UCE_HasExploredArea(area))
                        {
                            explored++;
                        }
                    }
#endif

                    // -- check faction requirement
                    bool factionRequirementsMet = true;
#if _iMMOFACTIONS
                    factionRequirementsMet = player.UCE_CheckFactionRating(quest.factionRequirement);
#endif

                    // =======================================================================

                    // name button
                    GameObject descriptionPanel = slot.descriptionText.gameObject;
                    string     prefix           = descriptionPanel.activeSelf ? hidePrefix : expandPrefix;
                    slot.nameButton.GetComponentInChildren <Text>().text = prefix + quest.name;

                    if (showActiveQuests)
                    {
                        if (quest.IsFulfilled(gathered, explored, factionRequirementsMet))
                        {
                            slot.nameButton.GetComponent <Image>().color = fulfilledQuestColor;
                        }
                        else
                        {
                            slot.nameButton.GetComponent <Image>().color = inprogressQuestColor;
                        }
                    }
                    else
                    {
                        slot.nameButton.GetComponent <Image>().color = fulfilledQuestColor;
                    }

                    slot.nameButton.onClick.SetListener(() =>
                    {
                        descriptionPanel.SetActive(!descriptionPanel.activeSelf);
                    });

                    // -- cancel button
                    if (showActiveQuests)
                    {
                        slot.cancelButton.gameObject.SetActive(true);
                        slot.cancelButton.onClick.SetListener(() =>
                        {
                            cancelQuestPanel.Show(quest.name);
                        });
                    }
                    else
                    {
                        slot.cancelButton.gameObject.SetActive(false);
                    }

                    // -- update cache
                    cacheTooltip[index] = quest.ToolTip(gathered, explored, factionRequirementsMet);
                    cacheTimer[index]   = Time.time + cacheInterval;

                    // -- update description
                    slot.descriptionText.text = cacheTooltip[index];
                }
            }
        }
    }