private bool AddRoot(out DiscoProgram instance, DiscoSequenceAction action)
        {
            instance = null;
            var programDef = action.GetProgram(Stand.FloorBounds.Width, Stand.FloorBounds.Height);

            if (programDef != null && action.onlyIfMeetsSize && !programDef.CanRunOn(Stand.FloorBounds.Width, Stand.FloorBounds.Height))
            {
                return(true);
            }
            instance = programDef?.MakeProgram(Stand, action.overrides);
            if (instance == null)
            {
                return(true);
            }
            instance.OneMinus      = action.oneMinus;
            instance.OneMinusAlpha = action.oneMinusAlpha;
            instance.Tint          = action.Tint;
            lastAddedProgram       = instance;
            if (action.addToMemory)
            {
                memory.Push(instance);
            }

            switch (action.onEndAction)
            {
            case OnEndAction.None:
                break;

            case OnEndAction.EndSequence:
                instance.OnDisposeEvent += () =>
                {
                    waitForLast = false;
                    toWaitFor   = null;
                    ticksToWait = 0;
                    actionQueue.Clear();
                    Tick();
                };
                break;
            }

            return(false);
        }
        public virtual bool ExecuteAction(DiscoSequenceAction action)
        {
            if (action == null)
            {
                return(true);
            }

            bool CheckMem()
            {
                if (memory.Count == 0)
                {
                    Core.Warn("Tried to do a memory action, but there is nothing on the memory stack.");
                    return(true);
                }

                if (memory.Peek().ShouldRemove)
                {
                    Core.Warn("Executing a memory action, but the last program on the memory stack has already been removed.");
                    return(false);
                }
                return(false);
            }

            switch (action.type)
            {
            case DiscoSequenceActionType.Clear:
                Stand.SetProgramStack(null);
                return(true);

            case DiscoSequenceActionType.Wait:
                if (action.ticks > 0)
                {
                    ticksToWait = action.ticks;
                }
                return(false);

            case DiscoSequenceActionType.WaitMem:
                if (CheckMem())
                {
                    return(true);
                }
                toWaitFor = memory.Peek();
                return(false);

            case DiscoSequenceActionType.WaitLast:
                if (lastAddedProgram == null || lastAddedProgram.ShouldRemove)
                {
                    Core.Warn("Started WaitLast action but there is no previous program, or that program has already ended.");
                    return(true);
                }
                waitForLast = true;
                return(false);

            case DiscoSequenceActionType.Start:
                if (AddRoot(out var instance, action))
                {
                    return(true);
                }
                Stand.SetProgramStack(instance);
                return(true);

            case DiscoSequenceActionType.Add:
                if (AddRoot(out instance, action))
                {
                    return(true);
                }
                Stand.AddProgramStack(instance, action.blend, action.atBottom ? 0 : (int?)null);
                return(true);

            case DiscoSequenceActionType.MemAdd:
                if (lastAddedProgram == null || lastAddedProgram.ShouldRemove)
                {
                    Core.Warn("Started MemAdd action, but there is no previous program, or that program has already ended.");
                    return(true);
                }
                memory.Push(lastAddedProgram);
                return(true);

            case DiscoSequenceActionType.MemRemove:
                if (memory.Count == 0)
                {
                    Core.Warn("Started MemRemove action, but the memory stack is empty!");
                    return(true);
                }
                memory.Pop();
                return(true);

            case DiscoSequenceActionType.TintMem:
                if (CheckMem())
                {
                    return(true);
                }
                Color?tint         = action.Tint;
                memory.Peek().Tint = tint;
                return(true);

            case DiscoSequenceActionType.DestroyMem:
                if (CheckMem())
                {
                    return(true);
                }
                memory.Peek().Remove();
                memory.Pop();
                return(true);
            }
            return(true);
        }