public JediComponent() { memory = new JediMemory(); settings = new JediSettings(); control = new JediMasterControl(); splitCollection = new SplitCollection(); }
/// <summary> /// Constructs the class. This constructor is useful for saving settings. /// </summary> public EndIsNighSettings(SplitCollection splitCollection, SplitCollectionControl collectionControl, SettingsControl settingsControl) { this.settingsControl = settingsControl; this.splitCollection = splitCollection; this.collectionControl = collectionControl; settingsControl.Settings = this; }
/// <summary> /// Constructs the component. /// </summary> public EndIsNighComponent() { memory = new EndIsNighMemory(); dataClasses = new AutosplitDataClass[] { new MapGrid(memory), new TumorCollection(memory), new BodyPartCollection(memory), new CartridgeCollection(memory), new WorldEventCollection(memory), }; splitCollection = new SplitCollection(this, dataClasses); settingsControl = new EndIsNighControl(); settingsControl.CollectionControl.SplitCollection = splitCollection; settings = new EndIsNighSettings(splitCollection, settingsControl.CollectionControl, settingsControl.SettingsControl); textComponent = new InfoTextComponent("Death Count", "0") { LongestString = int.MaxValue.ToString() }; }
/// <summary> /// Perform an intersection operation between two islands using a reflow strategy. /// </summary> /// <param name="dst">The destination of where the intersected path will be placed.</param> /// <param name="islandSegsA">A list of all the nodes in island A.</param> /// <param name="islandSegsB">A list of all the nodes in island B.</param> /// <param name="onIslA">If the islands were processed, this output parameter contains a node /// on the new shape.</param> /// <returns>The results from the operation.</returns> /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not /// be confused with all nodes in the parent loop.</remarks> public static BoundingMode Intersection( BLoop dst, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA) { // For the intersection, if there's ANY geometry return, it's a copy. // It's expected after this operation that the original islands will // be destroyed and only the copies will be left. float leftWinding = BNode.CalculateWinding(islandSegsA[0].Travel()); float rightWinding = BNode.CalculateWinding(islandSegsB[0].Travel()); if (leftWinding > 0.0f != rightWinding > 0.0f) { islandSegsB[0].ReverseChainOrder(); } onIslA = null; List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>(); GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions); Utils.BezierSubdivSample.CleanIntersectionList(delCollisions); if (delCollisions.Count == 0) { BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB); if (bm == BoundingMode.NoCollision) { return(BoundingMode.NoCollision); } else if (bm == BoundingMode.RightSurroundsLeft) { // If the right is fully surrounded, the right is kept. Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsA, false); foreach (BNode bn in insertCloneMap.Values) { onIslA = bn; bn.SetParent(dst); } onIslA = islandSegsA[0]; return(BoundingMode.RightSurroundsLeft); } else if (bm == BoundingMode.LeftSurroundsRight) { // If the left is fully surrounded, the left is kept. Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsB, false); foreach (BNode bn in insertCloneMap.Values) { onIslA = bn; bn.SetParent(dst); } onIslA = islandSegsA[0]; return(BoundingMode.LeftSurroundsRight); } } // Make copies of both, remap and keep their intersection. Dictionary <BNode, BNode> cloneMapA = BNode.CloneNodes(islandSegsA, false); Dictionary <BNode, BNode> cloneMapB = BNode.CloneNodes(islandSegsB, false); foreach (BNode bn in cloneMapA.Values) { bn.SetParent(dst); } foreach (BNode bn in cloneMapB.Values) { bn.SetParent(dst); } for (int i = 0; i < delCollisions.Count; ++i) { Utils.BezierSubdivSample bss = delCollisions[i]; bss.a.node = cloneMapA[bss.a.node]; bss.b.node = cloneMapB[bss.b.node]; delCollisions[i] = bss; } Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions); Dictionary <Utils.NodeTPos, BNode> createdSubdivs = new Dictionary <Utils.NodeTPos, BNode>(); SplitCollection splitCol = new SplitCollection(dst, delCollisions, createdSubdivs); //left.nodes.Clear(); //foreach(BNode bn in islandSegsA) // bn.SetParent(null, false); // ////right.DumpInto(dst); // Move everything in from the other loop foreach (BNode bn in islandSegsB) { bn.SetParent(dst, false); } HashSet <BNode> looseEnds = new HashSet <BNode>(); foreach (Utils.BezierSubdivSample bss in delCollisions) { BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()]; BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()]; float wind = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut); BNode colNode = createdSubdivs[bss.GetTPosA()]; onIslA = colNode; if (wind < 0.0f != leftWinding < 0.0f) { BNode nA = splitCol.GetNextTo(bss.GetTPosA()); BNode nB = splitCol.GetPreviousTo(bss.GetTPosB()); nA.TanIn = sdiA.nextIn; nB.TanOut = sdiB.prevOut; colNode.UseTanIn = bss.b.node.IsLine() == false; colNode.UseTanOut = bss.a.node.IsLine() == false; colNode.TanIn = sdiB.subIn; colNode.TanOut = sdiA.subOut; nB.next = colNode; colNode.prev = nB; nA.prev = colNode; colNode.next = nA; looseEnds.Add(bss.a.node); looseEnds.Add(splitCol.GetSplitInfo(bss.b.node).origNext); } else { BNode nA = splitCol.GetPreviousTo(bss.GetTPosA()); BNode nB = splitCol.GetNextTo(bss.GetTPosB()); nA.TanOut = sdiA.prevOut; nB.TanIn = sdiB.nextIn; colNode.UseTanIn = bss.a.node.IsLine() == false; colNode.UseTanOut = bss.b.node.IsLine() == false; colNode.TanIn = sdiA.subIn; colNode.TanOut = sdiB.subOut; nA.next = colNode; colNode.prev = nA; nB.prev = colNode; colNode.next = nB; looseEnds.Add(splitCol.GetSplitInfo(bss.a.node).origNext); looseEnds.Add(bss.b.node); } } // Figure out what internal items need to be removed by // checking which nodes have unmatching connectivity. ClipLooseEnds(looseEnds); return(BoundingMode.Collision); }
/// <summary> /// Perform a difference operation between two islands using a reflow strategy. /// </summary> /// <param name="dstloop">The destination of where the differenced path will be placed.</param> /// <param name="islandSegsA">A list of all the nodes in island A.</param> /// <param name="islandSegsB">A list of all the nodes in island B.</param> /// <param name="onIslA">A node on the resulting path.</param> /// <param name="processFullOverlaps">If true, check and handle is one of the parameter islands /// completly wraps around the other island.</param> /// <returns>The results from the operation.</returns> /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not /// be confused with all nodes in the parent loop.</remarks> public static BoundingMode Difference(BLoop dstloop, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA, bool processFullOverlaps) { // If there is any interaction, a copy of islandB is made and used - this means // if islandB should be removed, it is up to the caller to remove it themselves. // // This is done because we don't know if the shape being subtracted is part of a // bigger operation where it's subtracted against multiple islands for multi-island // loop subtraction, or shape subtraction. float leftWind = BNode.CalculateWinding(islandSegsA[0].Travel()); float rightWind = BNode.CalculateWinding(islandSegsB[0].Travel()); // They need to have opposite windings. if (leftWind > 0.0f == rightWind > 0.0f) { islandSegsB[0].ReverseChainOrder(); } List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>(); GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions); Utils.BezierSubdivSample.CleanIntersectionList(delCollisions); onIslA = null; // If we have an odd number of collisions, we have a problem because we have // any entry without an exit which will lead to corrupt topology. For now we // don't have a way to resolve that, but at least we can exit on 1 without // causing issues (in theory at least). if (delCollisions.Count == 1) { return(BoundingMode.NoCollision); } if (delCollisions.Count == 0) { BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB); if (processFullOverlaps == false) { onIslA = islandSegsA[0]; return(bm); } if (bm == BoundingMode.NoCollision) { onIslA = islandSegsA[0]; return(BoundingMode.NoCollision); } else if (bm == BoundingMode.RightSurroundsLeft) { // Everything was subtracted out foreach (BNode bn in islandSegsA) { onIslA = bn; bn.SetParent(null); } return(BoundingMode.RightSurroundsLeft); } else if (bm == BoundingMode.LeftSurroundsRight) { // Leave the reverse winding inside as a hollow cavity - and // nothing needs to be changed. Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsB, false); foreach (BNode bn in insertCloneMap.Values) { bn.SetParent(dstloop); } onIslA = islandSegsA[0]; return(BoundingMode.LeftSurroundsRight); } } // Make sure we have no overlaps of intersections. We currently // can't handle such things. HashSet <Utils.NodeTPos> intersections = new HashSet <Utils.NodeTPos>(); foreach (Utils.BezierSubdivSample bss in delCollisions) { if (intersections.Add(bss.a.TPos()) == false) { return(BoundingMode.Degenerate); } if (intersections.Add(bss.b.TPos()) == false) { return(BoundingMode.Degenerate); } } // Add everything in the copy in. We'll clip loose ends later to get // rid of the trash. Dictionary <BNode, BNode> cloneMap = BNode.CloneNodes(islandSegsB, false); foreach (BNode bn in cloneMap.Values) { bn.SetParent(dstloop); } // Well, if we're going to make a copy in its place, that means we need to remap // all references... for (int i = 0; i < delCollisions.Count; ++i) { Utils.BezierSubdivSample bss = delCollisions[i]; bss.b.node = cloneMap[bss.b.node]; delCollisions[i] = bss; } Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions); Dictionary <Utils.NodeTPos, BNode> createdSubdivs = new Dictionary <Utils.NodeTPos, BNode>(); SplitCollection splitCol = new SplitCollection(dstloop, delCollisions, createdSubdivs); HashSet <BNode> looseEnds = new HashSet <BNode>(); // Note that nothing from B will be tagged as a loose end. Instead, we're // forcing the entire island of B to be removed after the Per-Island // processing. foreach (Utils.BezierSubdivSample bss in delCollisions) { BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()]; BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()]; float wind = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut); BNode colNode = createdSubdivs[bss.GetTPosA()]; onIslA = colNode; if (leftWind > 0 == wind > 0.0f) { // A CCW transition will go from A to B. BNode nA = splitCol.GetPreviousTo(bss.GetTPosA()); BNode nB = splitCol.GetNextTo(bss.GetTPosB()); nA.TanOut = sdiA.prevOut; nB.TanIn = sdiB.nextIn; colNode.UseTanIn = bss.a.node.IsLine() == false; colNode.UseTanOut = bss.b.node.IsLine() == false; colNode.TanIn = sdiA.subIn; colNode.TanOut = sdiB.subOut; nA.next = colNode; colNode.prev = nA; nB.prev = colNode; colNode.next = nB; looseEnds.Add(bss.b.node); looseEnds.Add(bss.a.node.next); } else { // A CW transition will go from the other to it. BNode nA = splitCol.GetNextTo(bss.GetTPosA()); BNode nB = splitCol.GetPreviousTo(bss.GetTPosB()); nA.TanIn = sdiA.nextIn; nB.TanOut = sdiB.prevOut; colNode.UseTanIn = bss.b.node.IsLine() == false; colNode.UseTanOut = bss.a.node.IsLine() == false; colNode.TanIn = sdiB.subIn; colNode.TanOut = sdiA.subOut; nB.next = colNode; colNode.prev = nB; nA.prev = colNode; colNode.next = nA; looseEnds.Add(bss.b.node.next); looseEnds.Add(bss.a.node); } } // Figure out what internal items need to be removed by // checking which nodes have unmatching connectivity. ClipLooseEnds(looseEnds); return(BoundingMode.Collision); }
/// <summary> /// Perform a union operation between two islands using a reflow strategy. /// </summary> /// <param name="dst">The destination of where the conjoined path will be placed.</param> /// <param name="islandSegsA">A list of all the nodes in island A. </param> /// <param name="islandSegsB">A list of all the nodes in island B.</param> /// <param name="onIslA">If the islands were processed, this output parameter contains a node /// on the new shape.</param> /// <param name="mergeNonCol">If true, other parts that didn't collide (and weren't merged) will be /// moved into the final output destination (dst).</param> /// <returns>The results from the operation.</returns> /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not /// be confused with all nodes in the parent loop.</remarks> public static BoundingMode Union(BLoop dst, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA, bool mergeNonCol) { float leftWind = BNode.CalculateWinding(islandSegsA[0].Travel()); float rightWind = BNode.CalculateWinding(islandSegsB[0].Travel()); onIslA = islandSegsA[0]; // It can be either winding, but they must be the same - since islandB is going to be used up in the process, // we'll modify that winding to keep islandA the same. if (leftWind > 0 != rightWind > 0) { islandSegsB[0].ReverseChainOrder(); } List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>(); GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions); Utils.BezierSubdivSample.CleanIntersectionList(delCollisions); // If we didn't find any collisions, it's either because they don't overlap // at all, or one island fully wraps around another island. if (delCollisions.Count == 0) { BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB); // If an island is completely surrounded by another island, one of the // islands gets "smothered out of existence." if (bm == BoundingMode.LeftSurroundsRight) { // Just remember everything from the right out of existence. foreach (BNode bn in islandSegsB) { bn.SetParent(null); } return(BoundingMode.LeftSurroundsRight); } else if (bm == BoundingMode.RightSurroundsLeft) { // Remove everything from the left out of existence, and // move everything from the right island into the left; foreach (BNode bn in islandSegsA) { bn.SetParent(null, false); } foreach (BNode bn in islandSegsB) { onIslA = bn; bn.SetParent(dst, false); } return(BoundingMode.RightSurroundsLeft); } if (mergeNonCol == true) { foreach (BNode bn in islandSegsB) { bn.SetParent(dst, false); } } return(BoundingMode.NoCollision); } // Dump B into A, and if there's anything straggling, // we'll clip it as a loose end afterwards. foreach (BNode bn in islandSegsB) { bn.SetParent(dst, false); } Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions); Dictionary <Utils.NodeTPos, BNode> createdSubdivs = new Dictionary <Utils.NodeTPos, BNode>(); SplitCollection splitCol = new SplitCollection(dst, delCollisions, createdSubdivs); HashSet <BNode> looseEnds = new HashSet <BNode>(); foreach (Utils.BezierSubdivSample bss in delCollisions) { BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()]; BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()]; float wind = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut); BNode colNode = createdSubdivs[bss.GetTPosA()]; onIslA = colNode; if (leftWind <= 0.0f != wind <= 0.0f) { // A CCW transition will go from A to B. BNode nA = splitCol.GetPreviousTo(bss.GetTPosA()); BNode nB = splitCol.GetNextTo(bss.GetTPosB()); nA.TanOut = sdiA.prevOut; nB.TanIn = sdiB.nextIn; colNode.UseTanIn = bss.a.node.IsLine() == false; colNode.UseTanOut = bss.b.node.IsLine() == false; colNode.TanIn = sdiA.subIn; colNode.TanOut = sdiB.subOut; nA.next = colNode; colNode.prev = nA; nB.prev = colNode; colNode.next = nB; looseEnds.Add(bss.b.node); looseEnds.Add(splitCol.GetSplitInfo(bss.a.node).origNext); } else { // A CW transition will go from the other to it. BNode nA = splitCol.GetNextTo(bss.GetTPosA()); BNode nB = splitCol.GetPreviousTo(bss.GetTPosB()); nA.TanIn = sdiA.nextIn; nB.TanOut = sdiB.prevOut; colNode.UseTanIn = bss.b.node.IsLine() == false; colNode.UseTanOut = bss.a.node.IsLine() == false; colNode.TanIn = sdiB.subIn; colNode.TanOut = sdiA.subOut; nB.next = colNode; colNode.prev = nB; nA.prev = colNode; colNode.next = nA; looseEnds.Add(splitCol.GetSplitInfo(bss.b.node).origNext); looseEnds.Add(bss.a.node); } } // Figure out what internal items need to be removed by // checking which nodes have unmatching connectivity. ClipLooseEnds(looseEnds); return(BoundingMode.Collision); }
public SoulsComponent() { splitCollection = new SplitCollection(); memory = new SoulsMemory(); masterControl = new SoulsMasterControl(); run = new RunState(); splitFunctions = new Dictionary <SplitTypes, Func <int[], bool> > { { SplitTypes.Bonfire, ProcessBonfire }, { SplitTypes.Boss, ProcessBoss }, { SplitTypes.Covenant, ProcessCovenant }, { SplitTypes.Event, ProcessEvent }, { SplitTypes.Flag, ProcessFlag }, { SplitTypes.Item, ProcessItem }, { SplitTypes.Quitout, ProcessQuitout }, { SplitTypes.Zone, ProcessZone } }; // This array is used for covenant discovery splits. Discovery occurs when the player is prompted to join a // covenant via a yes/no confirmation box. That prompt's appearance can be detected through memory, but // it's shared among all covenants. As such, position is used to narrow down the covenant. covenantLocations = new [] { new Vector3(-28, -53, 87), // Way of White (in front of Petrus) new Vector3(9, 29, 121), // Way of White (beside Rhea) new Vector3(622, 164, 255), // Princess (in front of Gwynevere) new Vector3(36, 12, -32), // Sunlight (in front of the sunlight altar) new Vector3(93, -311, 4), // Darkwraith (in front of Kaathe) new Vector3(-702, -412, -333), // Dragon (in front of the everlasting dragon) new Vector3(-161, -265, -32), // Gravelord (below Nito) new Vector3(285, -3, -105), // Forest (below Alvina) new Vector3(430, 60, 255), // Darkmoon (just outside Gwyndolin's boss arena) new Vector3(138, -252, 94) // Chaos (in front of the fair lady) }; // Previously, bonfire resting was determined by reading the last bonfire from memory (i.e. the bonfire to // which the player will warp on death or when using an item). That approach works great for most cases, // but there's a catch in that the last bonfire seems to update a frame after the resting animation is // detected. As a result, it was possible for the autosplitter to split incorrectly if that last bonfire // value is already set to the target (in which case you'd split at any bonfire, not just the target). // There's unfortunately no foolproof solution to this problem using that last bonfire data alone, which is // why position data is used instead. // // Also note that each position is taken from the player standing right next to the bonfire (rounded to the // nearest integer). Positions don't need to be exact (just close enough that, on rest, the target bonfire // will be the closest). bonfireLocations = new [] { new Vector3(171, 173, 255), // Anor Londo - Entrance new Vector3(504, 135, 175), // Anor Londo - Interior new Vector3(593, 161, 254), // Anor Londo - Princess new Vector3(391, 70, 255), // Anor Londo - Tomb new Vector3(-388, -408, 156), // Ash Lake - Entrance new Vector3(-700, -414, -323), // Ash Lake - Dragon new Vector3(-277, -137, 73), // Blighttown - Bridge new Vector3(-198, -215, 100), // Blighttown - Swamp new Vector3(44, -119, 204), // Catacombs - Entrance new Vector3(48, -112, 301), // Catacombs - Illusion new Vector3(854, -577, 849), // Chasm of the Abyss new Vector3(96, 134, 864), // Crystal Caves new Vector3(165, -77, -55), // Darkroot Basin new Vector3(257, -3, -12), // Darkroot Garden new Vector3(141, -253, 94), // Daughter of Chaos new Vector3(253, -334, 22), // Demon Ruins - Central new Vector3(194, -267, 130), // Demon Ruins - Entrance new Vector3(118, -357, 139), // Demon Ruins - Firesage new Vector3(349, 278, 594), // Duke's Archives - Balcony new Vector3(230, 200, 481), // Duke's Archives - Entrance new Vector3(378, 270, 552), // Duke's Archives - Prison new Vector3(52, -64, 106), // Firelink Altar new Vector3(-51, -61, 55), // Firelink Shrine new Vector3(-318, -236, 123), // Great Hollow new Vector3(581, -444, 444), // Lost Izalith - Bed of Chaos new Vector3(456, -380, 170), // Lost Izalith - Illusion new Vector3(229, -384, 91), // Lost Izalith - Lava Field new Vector3(972, -314, 583), // Oolacile Sanctuary new Vector3(863, -448, 912), // Oolacile Township - Dungeon new Vector3(1041, -332, 875), // Oolacile Township - Entrance new Vector3(-24, 52, 944), // Painted World new Vector3(897, -329, 452), // Sanctuary Garden new Vector3(73, 60, 301), // Sen's Fortress new Vector3(85, -311, 3), // The Abyss new Vector3(-121, -74, 13), // The Depths new Vector3(77, -214, 45), // Tomb of the Giants - Alcove new Vector3(-159, -265, -34), // Tomb of the Giants - Nito new Vector3(97, -200, 104), // Tomb of the Giants - Patches new Vector3(3, 196, 7), // Undead Asylum - Courtyard new Vector3(34, 193, -26), // Undead Asylum - Interior new Vector3(3, -10, -61), // Undead Burg new Vector3(88, 15, 107), // Undead Parish - Andre new Vector3(24, 10, -23) // Undead Parish - Sunlight }; Zone abyss = new Zone(16, 0); Zone anorLondo = new Zone(15, 1); Zone asylum = new Zone(18, 1); Zone basin = new Zone(12, 0); Zone firelinkAltar = new Zone(18, 0); Zone firelinkShrine = new Zone(10, 2); Zone paintedWorld = new Zone(11, 0); Zone sanctuaryGarden = new Zone(12, 1); Zone sensRoof = new Zone(15, 0); // Since zones are designed to capture transitions between distinct areas, each zone (from the enumeration) // maps to both its associated zone (the first item in the list) and a list of neighboring zones (the // remaining items). zoneMap = new Dictionary <Zones, List <Zone> > { { Zones.AnorLondo, new List <Zone> { anorLondo, paintedWorld, sensRoof } }, { Zones.FirelinkAltar, new List <Zone> { firelinkAltar, abyss, firelinkShrine } }, { Zones.FirelinkShrine, new List <Zone> { firelinkShrine, asylum, firelinkAltar } }, { Zones.PaintedWorld, new List <Zone> { paintedWorld, anorLondo } }, { Zones.SanctuaryGarden, new List <Zone> { sanctuaryGarden, basin } }, { Zones.SensFortressRoof, new List <Zone> { sensRoof, anorLondo } }, { Zones.TheAbyss, new List <Zone> { abyss, firelinkAltar } }, { Zones.UndeadAsylum, new List <Zone> { asylum, firelinkShrine } } }; // This list was previously used in the memory class when setting items. It makes more sense to store it // here instead. List <int> keys = new List <int>(); keys.AddRange(ItemFlags.OrderedKeys); keys.AddRange(ItemFlags.OrderedEmbers); keys.AddRange(ItemFlags.OrderedBonfireItems); keys.Add((int)SoulFlags.BequeathedLordSoulShardFourKings); keys.Add((int)SoulFlags.BequeathedLordSoulShardSeath); keys.Add((int)SoulFlags.LordSoulBedOfChaos); keys.Add((int)SoulFlags.LordSoulNito); for (int i = 0; i < keys.Count; i++) { keys[i] = Utilities.StripHighestDigit(keys[i], out int digit); } keyItems = keys.ToArray(); }
public SheepoComponent() { memory = new SheepoMemory(); masterControl = new SheepoMasterControl(); splitCollection = new SplitCollection(); }