public override IEnumerator<RantAction> Run(Sandbox sb) { var attribs = sb.NextAttribs(this); 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) 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; }
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; }