private static List <string> GetProgressionItems(int reachableCount) { List <string> progression = new List <string>(); string[] obtained = new string[_obtainedItems.Count + 1]; _obtainedItems.CopyTo(obtained); foreach (string str in _unobtainedItems) { if (!LogicManager.GetItemDef(str).progression) { continue; } obtained[obtained.Length - 1] = str; int hypothetical = 0; foreach (string item in _unobtainedLocations) { if (LogicManager.ParseLogic(item, obtained)) { hypothetical++; } } if (hypothetical > reachableCount) { progression.Add(str); } } return(progression); }
public static void Randomize() { SetupVariables(); RandomizerMod.Instance.Settings.ResetItemPlacements(); Log("Randomizing with seed: " + RandomizerMod.Instance.Settings.Seed); Log("Mode - " + (RandomizerMod.Instance.Settings.NoClaw ? "No Claw" : "Standard")); Log("Shade skips - " + RandomizerMod.Instance.Settings.ShadeSkips); Log("Acid skips - " + RandomizerMod.Instance.Settings.AcidSkips); Log("Spike tunnel skips - " + RandomizerMod.Instance.Settings.SpikeTunnels); Log("Misc skips - " + RandomizerMod.Instance.Settings.MiscSkips); Log("Fireball skips - " + RandomizerMod.Instance.Settings.FireballSkips); Log("Mag skips - " + RandomizerMod.Instance.Settings.MagSkips); Random rand = new Random(RandomizerMod.Instance.Settings.Seed); // For use in weighting item placement Dictionary <string, int> locationDepths = new Dictionary <string, int>(); int currentDepth = 1; _unobtainedItems.Remove("Wayward_Compass"); _unobtainedLocations.Remove("Grubberfly's_Elegy"); LogItemPlacement("Wayward_Compass", "Grubberfly's_Elegy"); // Early game sucks too much if you don't get any geo, and the fury spot is weird anyway // Two birds with one stone Log("Placing initial geo pickup"); string[] furyGeoContenders = _unobtainedItems.Where(item => LogicManager.GetItemDef(item).type == ItemType.Geo && LogicManager.GetItemDef(item).geo > 100) .ToArray(); string furyGeoItem = furyGeoContenders[rand.Next(furyGeoContenders.Length)]; _unobtainedItems.Remove(furyGeoItem); _unobtainedLocations.Remove("Fury_of_the_Fallen"); LogItemPlacement(furyGeoItem, "Fury_of_the_Fallen"); Log("Beginning first pass of progression item placement"); // Choose where to place progression items while (true) { // Get currently reachable locations List <string> reachableLocations = new List <string>(); string[] obtained = _obtainedItems.ToArray(); int reachableCount = 0; foreach (string loc in _unobtainedLocations) { if (!LogicManager.ParseLogic(loc, obtained)) { continue; } if (!locationDepths.ContainsKey(loc)) { locationDepths[loc] = currentDepth; } // This way further locations will be more likely to be picked for (int j = 0; j < currentDepth; j++) { reachableLocations.Add(loc); } reachableCount++; } List <string> progressionItems = GetProgressionItems(reachableCount); // We only need complex randomization until all progression items are placed // After that everything can just be placed completely randomly if (progressionItems.Count == 0) { break; } string placeLocation = reachableLocations[rand.Next(reachableLocations.Count)]; string placeItem = progressionItems[rand.Next(progressionItems.Count)]; _unobtainedLocations.Remove(placeLocation); _unobtainedItems.Remove(placeItem); _obtainedItems.Add(placeItem); LogItemPlacement(placeItem, placeLocation); if (_shopItems.ContainsKey(placeLocation)) { _shopItems[placeLocation].Add(placeItem); } currentDepth++; } Log("Beginning second pass of progression item placement"); // Place remaining potential progression items List <string> unusedProgressionItems = new List <string>(); foreach (string str in _unobtainedItems) { if (LogicManager.GetItemDef(str).progression) { unusedProgressionItems.Add(str); } } List <string> weightedLocations = new List <string>(); foreach (string str in _unobtainedLocations) { // Items tagged as requiring "EVERYTHING" will not be in this dict if (!locationDepths.ContainsKey(str)) { continue; } // Using weight^2 to heavily bias towards late locations for (int i = 0; i < locationDepths[str] * locationDepths[str]; i++) { weightedLocations.Add(str); } } while (unusedProgressionItems.Count > 0) { string placeLocation = weightedLocations[rand.Next(weightedLocations.Count)]; string placeItem = unusedProgressionItems[rand.Next(unusedProgressionItems.Count)]; _unobtainedLocations.Remove(placeLocation); unusedProgressionItems.Remove(placeItem); _unobtainedItems.Remove(placeItem); _obtainedItems.Add(placeItem); weightedLocations.RemoveAll(str => str == placeLocation); LogItemPlacement(placeItem, placeLocation); if (_shopItems.ContainsKey(placeLocation)) { _shopItems[placeLocation].Add(placeItem); } } Log("Beginning placement of good items into remaining shops"); // Place good items into shops that lack progression items List <string> goodItems = new List <string>(); foreach (string str in _unobtainedItems) { if (LogicManager.GetItemDef(str).isGoodItem) { goodItems.Add(str); } } foreach (string shopName in _shopItems.Keys.ToList()) { if (_shopItems[shopName].Count != 0) { continue; } string placeItem = goodItems[rand.Next(goodItems.Count)]; _unobtainedItems.Remove(placeItem); goodItems.Remove(placeItem); _obtainedItems.Add(placeItem); LogItemPlacement(placeItem, shopName); _shopItems[shopName].Add(placeItem); _unobtainedLocations.Remove(shopName); } // Place geo drops first to guarantee they don't end up in shops Log("Beginning placement of geo drops"); List <string> geoItems = _unobtainedItems.Where(name => LogicManager.GetItemDef(name).type == ItemType.Geo) .ToList(); List <string> geoLocations = _unobtainedLocations.Where(name => LogicManager.GetItemDef(name).cost == 0).ToList(); foreach (string geoItem in geoItems) { string placeLocation = geoLocations[rand.Next(geoLocations.Count)]; _unobtainedLocations.Remove(placeLocation); geoLocations.Remove(placeLocation); _unobtainedItems.Remove(geoItem); _obtainedItems.Add(geoItem); LogItemPlacement(geoItem, placeLocation); } Log("Beginning full random placement into remaining locations"); // Randomly place into remaining locations while (_unobtainedLocations.Count > 0) { string placeLocation = _unobtainedLocations[rand.Next(_unobtainedLocations.Count)]; string placeItem = _unobtainedItems[rand.Next(_unobtainedItems.Count)]; _unobtainedLocations.Remove(placeLocation); _unobtainedItems.Remove(placeItem); _obtainedItems.Add(placeItem); LogItemPlacement(placeItem, placeLocation); if (_shopItems.ContainsKey(placeLocation)) { _shopItems[placeLocation].Add(placeItem); } } Log("Beginning placement of leftover items into shops"); string[] shopNames = _shopItems.Keys.ToArray(); // Put remaining items in shops while (_unobtainedItems.Count > 0) { string placeLocation = shopNames[rand.Next(shopNames.Length)]; string placeItem = _unobtainedItems[rand.Next(_unobtainedItems.Count)]; _unobtainedItems.Remove(placeItem); _obtainedItems.Add(placeItem); LogItemPlacement(placeItem, placeLocation); _shopItems[placeLocation].Add(placeItem); } Log("Randomization complete"); }