public override IEnumerator <RST> Run(Sandbox sb) { var attribs = sb.AttribManager.TakeAttribs(); // Skip if chance doesn't fall within range if (attribs.Chance < 100 && sb.RNG.NextDouble(0, 100) > attribs.Chance) { yield break; } int next = -1; int reps = attribs.RepEach ? _elements.Count : attribs.Repetitions; var block = new BlockState(reps, attribs); double weightSum = _constantWeightSum; if (attribs.Start != null) { yield return(attribs.Start); } if (_weighted && attribs.Sync == null) { if (_dynamicWeights != null) { foreach (var dw in _dynamicWeights) { sb.AddOutputWriter(); yield return(dw.Item2); string strWeight = sb.Return().Main; if (string.IsNullOrEmpty(strWeight)) { _weights[dw.Item1] = 0.0; } else if (!Util.ParseDouble(strWeight, out _weights[dw.Item1])) { throw new RantRuntimeException(sb, dw.Item2.Location, GetString("err-runtime-invalid-dynamic-weight", strWeight)); } weightSum += _weights[dw.Item1]; } } } if (attribs.Sync?.Index == 0 && attribs.StartIndex >= 0) { attribs.Sync.Index = attribs.StartIndex; } sb.Blocks.Push(block); for (int i = 0; i < reps; i++) { if (i == 0 && attribs.StartIndex >= 0 && attribs.Sync == null) { next = attribs.StartIndex > _count ? _count - 1 : attribs.StartIndex; } else if (_weighted) { double choice = sb.RNG.NextDouble(weightSum); for (int j = 0; j < _count; j++) { if (choice < _weights[j]) { next = j; break; } choice -= _weights[j]; } } else { next = attribs.NextIndex(_count, sb.RNG); } if (next == -1) { break; } block.Next(next); // Set next block index sb.Blocks.Pop(); // Don't allow separator to access block state // Separator if (i > 0 && attribs.Separator != null) { if (attribs.IsSeries) { // Check if we're on the last separator in a series if (i == reps - 1) { // Add the oxford comma if specified if (attribs.EndSeparator != null) { if (reps > 2) { yield return(attribs.EndSeparator); } } sb.Print(sb.Format.WritingSystem.Space); // Add conjunction if specified (it normally should be, if it's a series) if (attribs.EndConjunction != null) { yield return(attribs.EndConjunction); sb.Print(sb.Format.WritingSystem.Space); } } else if (reps > 2) { yield return(attribs.Separator); sb.Print(sb.Format.WritingSystem.Space); } } else { yield return(attribs.Separator); } } sb.Blocks.Push(block); // Now put it back // Prefix if (attribs.Before != null) { yield return(attribs.Before); } // Content // Redirect output if requested if (attribs.Redirect != null) { sb.AddOutputWriter(); } sb.Objects.EnterScope(); yield return(_elements[next]); sb.Objects.ExitScope(); // Retrieve redirected output if (attribs.Redirect != null) { sb.PushRedirectedOutput(); yield return(attribs.Redirect); sb.PopRedirectedOutput(); } // Affix if (attribs.After != null) { yield return(attribs.After); } } sb.Blocks.Pop(); if (attribs.End != null) { yield return(attribs.End); } }
public override IEnumerator <RantAction> Run(Sandbox sb) { var attribs = sb.NextAttribs(this); // Skip if chance doesn't fall within range if (attribs.Chance < 100 && sb.RNG.NextDouble(0, 100) > attribs.Chance) { yield break; } int next = -1; int reps = attribs.RepEach ? _items.Count : attribs.Repetitions; var block = new BlockState(attribs.Repetitions); double weightSum = _constantWeightSum; if (attribs.Start != null) { yield return(attribs.Start); } if (_weighted && attribs.Sync == null) { foreach (var dw in _dynamicWeights) { sb.AddOutputWriter(); yield return(dw.Item2); var strWeight = sb.Return().Main; if (!Double.TryParse(strWeight, out _weights[dw.Item1])) { throw new RantRuntimeException(sb.Pattern, dw.Item2.Range, $"Dynamic weight returned invalid weight value: '{strWeight}'"); } weightSum += _weights[dw.Item1]; } } sb.Blocks.Push(block); for (int i = 0; i < reps; i++) { if (_weighted) { double choice = sb.RNG.NextDouble(weightSum); for (int j = 0; j < _count; j++) { if (choice < _weights[j]) { next = j; break; } choice -= _weights[j]; } } else { next = attribs.NextIndex(_count, sb.RNG); } if (next == -1) { break; } block.Next(next); sb.Blocks.Pop(); // Don't allow separator to access block state // Separator if (i > 0 && attribs.Separator != null) { if (attribs.IsSeries) { // Check if we're on the last separator in a series if (i == reps - 1) { // Add the oxford comma if specified if (attribs.EndSeparator != null) { // If there are more than two items, print it! if (reps > 2) { yield return(attribs.EndSeparator); } } sb.Print(sb.Format.StandardSpace); // Add conjunction if specified (it normally should be, if it's a series) if (attribs.EndConjunction != null) { yield return(attribs.EndConjunction); sb.Print(sb.Format.StandardSpace); } } else if (reps > 2) { yield return(attribs.Separator); sb.Print(sb.Format.StandardSpace); } } else { yield return(attribs.Separator); } } sb.Blocks.Push(block); // Now put it back // Prefix if (attribs.Before != null) { yield return(attribs.Before); } // Content sb.Objects.EnterScope(); yield return(_items[next]); sb.Objects.ExitScope(); // Affix if (attribs.After != null) { yield return(attribs.After); } } sb.Blocks.Pop(); if (attribs.End != null) { yield return(attribs.End); } }