-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRandomCollectionGenerator.cs
More file actions
104 lines (92 loc) · 3.73 KB
/
Copy pathRandomCollectionGenerator.cs
File metadata and controls
104 lines (92 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System.Collections.Generic;
namespace LevelCollections;
/// <summary>
/// Builds a random collection from the config's RandomLevelCount +
/// RandomLevelPool settings. Used by the "lc random" console command.
/// The generated collection is transient — it is never written back to
/// the config file and only exists for the duration of its run.
/// </summary>
internal static class RandomCollectionGenerator
{
/// <summary>
/// Fallback level count when the config doesn't specify one (or the
/// value is invalid). Also the default written to new config files.
/// </summary>
public const int DefaultLevelCount = 5;
/// <summary>
/// Fallback pool when the config's RandomLevelPool is missing/empty:
/// the 12 regular BuiltIn levels (excludes the Intro_Reprise / Credits
/// specials).
/// </summary>
private static readonly string[] _defaultLevelPool =
{
"Intro", "Train", "Carry", "Climb", "Break", "Siege", "Water",
"Power", "Aztec", "Halloween", "Steam", "Ice",
};
/// <summary>
/// Generate a random collection by drawing <c>RandomLevelCount</c> levels
/// (without replacement) from <c>RandomLevelPool</c>, filtering out levels
/// that are not currently available (e.g. unsubscribed workshop levels).
///
/// On success returns the collection and sets <paramref name="message"/>
/// to a comma-separated list of the drawn levels. On failure returns null
/// and sets <paramref name="message"/> to a description of the problem.
/// </summary>
public static CollectionDefinition Generate(out string message)
{
message = null;
int count = ConfigLoader.RandomLevelCount;
if (count <= 0)
count = DefaultLevelCount;
List<string> pool = null;
var configPool = ConfigLoader.RandomLevelPool;
if (configPool != null && configPool.Count > 0)
pool = new List<string>(configPool);
else
pool = new List<string>(_defaultLevelPool);
// Keep only levels that are currently playable (available content).
// Duplicate IDs in the pool are drawn at most once.
var seen = new HashSet<string>();
var available = new List<string>(pool.Count);
foreach (var id in pool)
{
if (string.IsNullOrEmpty(id) || !seen.Add(id))
continue;
bool isMissing;
if (CollectionManager.ValidateLevelId(id, out isMissing))
available.Add(id);
else
Plugin.Logger.LogWarning(
$"RandomCollection: skipping unavailable level '{id}' in the level pool.");
}
if (available.Count == 0)
{
message = "random level pool is empty or contains no available levels. " +
"Check RandomLevelPool in LevelCollections.json.";
return null;
}
if (count > available.Count)
{
Plugin.Logger.LogWarning(
$"RandomCollection: requested {count} levels but only {available.Count} " +
$"available; drawing {available.Count} instead.");
count = available.Count;
}
// Fisher-Yates shuffle, then take the first `count` entries.
var rng = new System.Random();
for (int i = available.Count - 1; i > 0; i--)
{
int j = rng.Next(i + 1);
string tmp = available[i];
available[i] = available[j];
available[j] = tmp;
}
var levels = available.GetRange(0, count);
message = string.Join(", ", levels.ToArray());
return new CollectionDefinition
{
Name = "Random Collection",
Levels = levels,
};
}
}