void HandleLine(string rawLine)
    {
        ChapterLineManager.LINE line = ChapterLineManager.Interpret(rawLine);

        // now we have to handle the line. this requires a loop full of waiting for input since the
        // line consists of multiple segments that have to be handled individually.
        StopHandlingLine();
        handlingLine = StartCoroutine(HandlingLine(line));
    }
    IEnumerator HandlingLine(ChapterLineManager.LINE line)
    {
        // since the "next" trigger controls the flow of a chapter by moving through lines and yet also
        // controls the progression through a line by its segments, it must be reset.
        _next = false;
        int lineProgress = 0; // progress through the segments of a line.

        while (lineProgress < line.segments.Count)
        {
            _next = false; // reset at the start of each loop.
            ChapterLineManager.LINE.SEGMENT segment = line.segments[lineProgress];

            // always run the first segment automatically. But wait for the trigger on all proceding segments.
            if (lineProgress > 0)
            {
                if (segment.trigger == ChapterLineManager.LINE.SEGMENT.TRIGGER.autoDelay)
                {
                    for (float timer = segment.autoDelay; timer >= 0; timer -= Time.deltaTime)
                    {
                        yield return(new WaitForEndOfFrame());

                        if (_next)
                        {
                            break; // allow termination of a delay when "next" is triggered. Prevents unskippable wait timers.
                        }
                    }
                }
                else
                {
                    while (!_next)
                    {
                        yield return(new WaitForEndOfFrame()); // wait until the player says to move to the next segment.
                    }
                }
            }
            _next = false; // next could have been triggered during an event above.

            // the segment now needs to build and run.
            segment.Run();

            while (segment.isRunning)
            {
                yield return(new WaitForEndOfFrame());

                // allow for auto completion of the current segment for skipping purposes.
                if (_next)
                {
                    // rapidly complete the text on first advance, force it to finish on the second.
                    if (!segment.architect.skip)
                    {
                        segment.architect.skip = true;
                    }
                    else
                    {
                        segment.ForceFinish();
                    }
                    _next = false;
                }
            }

            lineProgress++;

            yield return(new WaitForEndOfFrame());
        }

        // Line is finished. Handle all the actions set at the end of the line.
        for (int i = 0; i < line.actions.Count; i++)
        {
            HandleAction(line.actions[i]);
        }
        handlingLine = null;
    }