private ParticleEventSequence ProcessEventSequenceBlock(ref List <ParticleEmitterToken> .Enumerator tokenIter)
        {
            bool startedProcessingEvents = false;
            ParticleEventSequence seq    = new ParticleEventSequence();

            seq.Reset();

            // move past the event sequence keyword...
            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            // next token should be the name of the sequence...
            if (tokenIter.Current.Type != TokenType.Quote)
            {
                throw new Exception("Must name particle sequence block!");
            }

            seq.Name = tokenIter.Current.StringValue;

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            // next token should be opening brace...
            if (tokenIter.Current.Type != TokenType.OpenBrace)
            {
                throw new Exception("Expected opening brace for particle sequence block!");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            while (tokenIter.Current.Type != TokenType.CloseBrace)
            {
                ParticleEmitterToken savedtoken;
                savedtoken = tokenIter.Current;

                // the first token here can be a SysNumberProperty, SysAlphaBlendModeProperty, SysVectorProperty,
                // or an EventSequence.
                switch (tokenIter.Current.Type)
                {
                case TokenType.SeqNumericProp:
                {
                    if (startedProcessingEvents)
                    {
                        throw new Exception("Cannot specify any sequence properties after specifying events.");
                    }

                    MinMax <float> number;

                    // the next 2 tokens should be an equals, and a number.
                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Equals)
                    {
                        throw new Exception("Expected equals sign!");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    number = ParticleEmitterTokenizer.ProcessNumber(ref tokenIter);

                    if (savedtoken.IsEmitRate())
                    {
                        seq.EmitRate = number;
                    }
                    else if (savedtoken.IsLifeTime())
                    {
                        seq.Lifetime = number;
                    }
                    else if (savedtoken.IsNumParticles())
                    {
                        seq.NumParticles = (int)number.Max;
                    }
                    else if (savedtoken.IsLoops())
                    {
                        seq.Loops = (int)number.Max;
                    }
                    else
                    {
                        throw new Exception("Unknown sequence numeric property!");
                    }
                }

                break;

                case TokenType.SeqVectorProp:
                {
                    if (startedProcessingEvents)
                    {
                        throw new Exception("Cannot specify any sequence properties after specifying events.");
                    }

                    MinMax <Vector3> v;

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Equals)
                    {
                        throw new Exception("Expected equals sign!");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    v = ParticleEmitterTokenizer.ProcessVector(ref tokenIter);

                    if (savedtoken.IsSpawnDir())
                    {
                        seq.SpawnDir = v;
                    }
                    else if (savedtoken.IsEmitRadius())
                    {
                        seq.EmitRadius = v;
                    }
                    else if (savedtoken.IsGravity())
                    {
                        seq.Gravity = v;
                    }
                    else
                    {
                        throw new Exception("Unknown sequence vector property!");
                    }
                }

                break;

                case TokenType.SeqAlphaBlendModeProp:
                {
                    if (startedProcessingEvents)
                    {
                        throw new Exception("Cannot specify any sequence properties after specifying events.");
                    }

                    LocalBlendState alphablendmode;
                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Equals)
                    {
                        throw new Exception("Expected equals sign!");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    alphablendmode = ParticleEmitterTokenizer.ProcessAlphaBlendMode(ref tokenIter);

                    if (savedtoken.IsSrcBlendMode())
                    {
                        seq.SrcBlendMode = (Blend)alphablendmode;
                    }
                    else if (savedtoken.IsDestBlendMode())
                    {
                        seq.DestBlendMode = (Blend)alphablendmode;
                    }
                    else
                    {
                        throw new Exception("Unknown sequence alpha blending mode property!");
                    }
                }

                break;

                case TokenType.KeywordTexture:
                {
                    // TODO
                    if (startedProcessingEvents)
                    {
                        throw new Exception("Cannot specify any sequence properties after specifying events.");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Equals)
                    {
                        throw new Exception("Expected equals sign!");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Quote)
                    {
                        throw new Exception("Expected filename after texture sequence property.");
                    }

                    seq.TextureFilename = tokenIter.Current.StringValue.RemoveQuotes();
                    ParticleEmitterTokenizer.NextToken(ref tokenIter);
                }

                break;

                case TokenType.KeywordFade:
                case TokenType.KeywordAt:
                case TokenType.KeywordInitial:
                case TokenType.KeywordFinal:
                {
                    startedProcessingEvents = true;

                    bool           isFade    = false;
                    MinMax <float> timeRange = new MinMax <float>();

                    // parse the time range section of the event line
                    ParticleEmitterTokenizer.ProcessTime(ref timeRange, ref isFade, 0, seq.Lifetime.Max, ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.ParticleNumericProp && tokenIter.Current.Type != TokenType.ParticleVectorProp &&
                        tokenIter.Current.Type != TokenType.ParticleColorProp)
                    {
                        throw new Exception("Expecting particle property after time specifier!");
                    }

                    ParticleEvent newEvent = null;

                    try
                    {
                        // create the appropriate event
                        newEvent = EventFactory(tokenIter.Current.StringValue);

                        if (newEvent == null)
                        {
                            throw new Exception("Unknown event type, or there was an error creating this event.");
                        }

                        // let the event parse the rest of its properties from the token stream.
                        if (isFade && !newEvent.FadeAllowed())
                        {
                            throw new Exception("Fading is not supported on this event.");
                        }

                        newEvent.ProcessTokenStream(ref tokenIter);
                        newEvent.TimeRange = timeRange;
                        newEvent.SetFade(isFade);
                        seq.Events.Add(newEvent);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }

                break;

                default:
                {
                    throw new Exception("Unexpected " + tokenIter.Current.StringValue + " in Sequence Block!");
                }
                }
            }

            seq.NailDownRandomTimes();
            seq.SortEvents();
            seq.CreateFadeLists();
            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            return(seq);
        }
        private void ProcessParticleSystemBlock(ref List <ParticleEmitterToken> .Enumerator tokenIter)
        {
            var startedProcessingSequences = false;

            // move past the opening brace...
            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            while (tokenIter.Current.Type != TokenType.CloseBrace)
            {
                ParticleEmitterToken savedtoken;
                savedtoken = tokenIter.Current;

                // the first token here can be a SysNumberProperty, SysAlphaBlendModeProperty, SysVectorProperty,
                // or an EventSequence.
                switch (tokenIter.Current.Type)
                {
                case TokenType.SysVectorProp:
                {
                    if (startedProcessingSequences)
                    {
                        throw new Exception("Cannot specify any particle system properties after specifying sequences.");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    if (tokenIter.Current.Type != TokenType.Equals)
                    {
                        throw new Exception("Expected equals sign!");
                    }

                    ParticleEmitterTokenizer.NextToken(ref tokenIter);

                    MinMax <Vector3> v = ParticleEmitterTokenizer.ProcessVector(ref tokenIter);

                    if (savedtoken.IsPosition())
                    {
                        PositionRange = v;
                    }
                    else
                    {
                        throw new Exception("Unknown particle system property!");
                    }
                }

                break;

                case TokenType.KeywordEventSequence:
                {
                    startedProcessingSequences = true;

                    ParticleEventSequence newseq = ProcessEventSequenceBlock(ref tokenIter);
                    sequences.Add(newseq);
                }

                break;

                default:
                {
                    throw new Exception("Unexpected " + tokenIter.Current.StringValue + " in Particle System Block!");
                }
                }
            }

            var ended = tokenIter.MoveNext();

            if (ended)
            {
                throw new Exception("end - of - file expected");
            }
        }
        public void Compile(string script)
        {
            lastError = "0 error(s), you're good to go!";

            try
            {
                ParticleEmitterTokenizer tokenizer = new ParticleEmitterTokenizer();

                Init();

                // parse the character string into tokens.
                tokenizer.Tokenize(script);

                var tokenIter = tokenizer.TokenVector.GetEnumerator();

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenizer.TokenVector.Count < 2)
                {
                    throw new Exception("This script is too small to be valid.");
                }

                // the first three tokens out of the gate should be
                // ParticleSystem, followed by a name and version number, then
                // an open brace.
                if (tokenIter.Current.Type != TokenType.KeywordParticleSystem)
                {
                    throw new Exception("First word must be ParticleSystem");
                }

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.Quote)
                {
                    throw new Exception("Must name particle system");
                }

                name = tokenIter.Current.StringValue.RemoveQuotes();
                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.RealNumber)
                {
                    throw new Exception("Must have version number");
                }

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.OpenBrace)
                {
                    throw new Exception("Missing opening brace for ParticleSystem block");
                }

                ProcessParticleSystemBlock(ref tokenIter);
            }
            catch (Exception ex)
            {
                lastError = ex.Message;
                Init();
                Debug.WriteLine(ex);
            }

            // do misc. processing and calcuations
            pos = posRange.GetRandomNumInRange();
        }