public static void Update()
        {
            float newWidestText = 0;

            if (!initialised)
            {
                Initialise();
            }

            // First step is to add actions to the queue based on newly added events.
            CreateNewText();

            try
            {
                activeEffectsText.Update(-((actionQueueTexts.Count + cooldownTexts.Count + customTimerTexts.Count - 1) * ActualTimerTextHeight()) - 5 * ActualTimerHeadingHeight());
                cooldownText.Update(-((actionQueueTexts.Count + cooldownTexts.Count - 1) * ActualTimerTextHeight()) - 3 * ActualTimerHeadingHeight());
                queueText.Update(-((actionQueueTexts.Count - 1) * ActualTimerTextHeight() + ActualTimerHeadingHeight()));

                if (activeEffectsText.getTextWidth() > newWidestText)
                {
                    newWidestText = activeEffectsText.getTextWidth();
                }
                if (cooldownText.getTextWidth() > newWidestText)
                {
                    newWidestText = cooldownText.getTextWidth();
                }
                if (queueText.getTextWidth() > newWidestText)
                {
                    newWidestText = queueText.getTextWidth();
                }
            }
            catch (Exception)
            {
                Initialise();
                activeEffectsText.Update(-((actionQueueTexts.Count + cooldownTexts.Count + customTimerTexts.Count - 1) * ActualTimerTextHeight()) - 5 * ActualTimerHeadingHeight());
                cooldownText.Update(-((actionQueueTexts.Count + cooldownTexts.Count - 1) * ActualTimerTextHeight()) - 3 * ActualTimerHeadingHeight());
                queueText.Update(-((actionQueueTexts.Count - 1) * ActualTimerTextHeight() + ActualTimerHeadingHeight()));
            }

            // Redemption messages
            if (currentRedemptionText != null)
            {
                currentRedemptionText.Update(-((actionQueueTexts.Count + cooldownTexts.Count + customTimerTexts.Count) * ActualTimerTextHeight()) - 6 * ActualTimerHeadingHeight());

                if (currentRedemptionText.IsFinished())
                {
                    currentRedemptionText.Destroy();
                    currentRedemptionText = null;
                }
            }
            if (currentRedemptionText == null)
            {
                string newRedemptionText;
                if (redemptionTexts.TryDequeue(out newRedemptionText))
                {
                    currentRedemptionText = new CustomText(newRedemptionText, 5, 0, false, false);

                    // Only do this in the main thread, to hopefully prevent errors
                    if (MainPatcher.secrets.saveRedemptionMessages)
                    {
                        string filePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "redemptions.txt");
                        using (StreamWriter sw = File.AppendText(filePath))
                        {
                            sw.WriteLine(newRedemptionText);
                        }
                    }
                }
            }

            // Active effects
            for (int i = 0; i < customTimerTexts.Count; i++)
            {
                customTimerTexts[i].Update(-((actionQueueTexts.Count + cooldownTexts.Count + i) * ActualTimerTextHeight()) - 4 * ActualTimerHeadingHeight());

                if (customTimerTexts[i].getTextWidth() > newWidestText)
                {
                    newWidestText = customTimerTexts[i].getTextWidth();
                }

                if (customTimerTexts[i].IsFinished())
                {
                    if (customTimerTexts[i].HasTimedEvent())
                    {
                        Tuple <string, string, TimedEventInfo> timedEvent = customTimerTexts[i].GetTimedEvent();
                        EventLookup.RunningEventIDs.Remove(timedEvent.Item1);
                        EventLookup.TimedActionsQueue.Add(timedEvent);
                        EventLookup.Cooldowns.Add(timedEvent.Item1, Time.time);
                        AddCooldownText(timedEvent.Item1, timedEvent.Item3.CooldownSeconds, new Tuple <string, string, EventInfo>(timedEvent.Item1, timedEvent.Item2, timedEvent.Item3));
                    }

                    customTimerTexts[i].Destroy();
                    customTimerTexts.RemoveAt(i);
                    i--;
                }
            }

            // Queue
            int j = 0;

            foreach (KeyValuePair <string, CustomText> actionQueueText in actionQueueTexts)
            {
                actionQueueText.Value.Update(-(j * ActualTimerTextHeight()));
                j++;

                if (actionQueueText.Value.getTextWidth() > newWidestText)
                {
                    newWidestText = actionQueueText.Value.getTextWidth();
                }
            }

            // Cooldown
            List <string> finishedCooldowns = new List <string>();

            int k = 0;

            foreach (KeyValuePair <string, CustomText> cooldownText in cooldownTexts)
            {
                cooldownText.Value.Update(-((actionQueueTexts.Count + k) * ActualTimerTextHeight()) - 2 * ActualTimerHeadingHeight());
                k++;

                Tuple <string, string, EventInfo> eventInfo = cooldownText.Value.GetEvent();
                float currentCooldownDuration = Time.time - EventLookup.Cooldowns[eventInfo.Item1];
                if (currentCooldownDuration >= eventInfo.Item3.CooldownSeconds)
                {
                    finishedCooldowns.Add(eventInfo.Item1);
                }

                if (cooldownText.Value.getTextWidth() > newWidestText)
                {
                    newWidestText = cooldownText.Value.getTextWidth();
                }
            }

            foreach (string finishedCooldown in finishedCooldowns)
            {
                RemoveCooldownText(finishedCooldown);
                EventLookup.Cooldowns.Remove(finishedCooldown);
            }

            widestText = newWidestText;
        }
        public static void Update()
        {
            timerTextHeight    = (int)(20 * (Screen.width / 1920f));
            timerHeadingHeight = (int)(32 * (Screen.width / 1920f));

            float newWidestText = 0;

            if (!initialised)
            {
                Initialise();
            }

            // First step is to add actions to the queue based on newly added events.
            CreateNewText();

            try
            {
                activeEffectsText.Update(-((actionQueueTexts.Count + cooldownTexts.Count + customTimerTexts.Count - 1) * timerTextHeight) - 5 * timerHeadingHeight);
                cooldownText.Update(-((actionQueueTexts.Count + cooldownTexts.Count - 1) * timerTextHeight) - 3 * timerHeadingHeight);
                queueText.Update(-((actionQueueTexts.Count - 1) * timerTextHeight + timerHeadingHeight));

                if (activeEffectsText.getTextWidth() > newWidestText)
                {
                    newWidestText = activeEffectsText.getTextWidth();
                }
                if (cooldownText.getTextWidth() > newWidestText)
                {
                    newWidestText = cooldownText.getTextWidth();
                }
                if (queueText.getTextWidth() > newWidestText)
                {
                    newWidestText = queueText.getTextWidth();
                }
            }
            catch (Exception)
            {
                Initialise();
                activeEffectsText.Update(-((actionQueueTexts.Count + cooldownTexts.Count + customTimerTexts.Count - 1) * timerTextHeight) - 5 * timerHeadingHeight);
                cooldownText.Update(-((actionQueueTexts.Count + cooldownTexts.Count - 1) * timerTextHeight) - 3 * timerHeadingHeight);
                queueText.Update(-((actionQueueTexts.Count - 1) * timerTextHeight + timerHeadingHeight));
            }

            for (int i = 0; i < customTimerTexts.Count; i++)
            {
                customTimerTexts[i].Update(-((actionQueueTexts.Count + cooldownTexts.Count + i) * timerTextHeight) - 4 * timerHeadingHeight);

                if (customTimerTexts[i].getTextWidth() > newWidestText)
                {
                    newWidestText = customTimerTexts[i].getTextWidth();
                }

                if (customTimerTexts[i].IsFinished())
                {
                    if (customTimerTexts[i].HasTimedEvent())
                    {
                        KeyValuePair <string, TimedEventInfo> timedEvent = customTimerTexts[i].GetTimedEvent();
                        EventLookup.RunningEventIDs.Remove(timedEvent.Key);
                        EventLookup.TimedActionsQueue.Add(timedEvent.Value.TimedAction);
                        EventLookup.Cooldowns.Add(timedEvent.Key, Time.time);
                        AddCooldownText(timedEvent.Key, timedEvent.Value.CooldownSeconds, timedEvent.Value);
                    }

                    customTimerTexts[i].Destroy();
                    customTimerTexts.RemoveAt(i);
                    i--;
                }
            }

            int j = 0;

            foreach (KeyValuePair <string, CustomText> actionQueueText in actionQueueTexts)
            {
                actionQueueText.Value.Update(-(j * timerTextHeight));
                j++;

                if (actionQueueText.Value.getTextWidth() > newWidestText)
                {
                    newWidestText = actionQueueText.Value.getTextWidth();
                }
            }

            List <string> finishedCooldowns = new List <string>();

            int k = 0;

            foreach (KeyValuePair <string, CustomText> cooldownText in cooldownTexts)
            {
                cooldownText.Value.Update(-((actionQueueTexts.Count + k) * timerTextHeight) - 2 * timerHeadingHeight);
                k++;

                KeyValuePair <string, EventInfo> eventInfo = cooldownText.Value.GetEvent();
                float currentCooldownDuration = Time.time - EventLookup.Cooldowns[eventInfo.Key];
                if (currentCooldownDuration >= eventInfo.Value.CooldownSeconds)
                {
                    finishedCooldowns.Add(eventInfo.Key);
                }

                if (cooldownText.Value.getTextWidth() > newWidestText)
                {
                    newWidestText = cooldownText.Value.getTextWidth();
                }
            }

            foreach (string finishedCooldown in finishedCooldowns)
            {
                RemoveCooldownText(finishedCooldown);
                EventLookup.Cooldowns.Remove(finishedCooldown);
            }

            widestText = newWidestText;
        }