public override bool Next() { if (this.Tries >= 5) { Logger.Log("randomizer", $"Failure: took too many tries to satisfy {this.Req} from {Node.Room.Static.Name}:{Node.Static.Name}"); return(false); } // each attempt we should back further away from the idea that we might add a new room int roll = this.Logic.Random.Next(5); bool extendingMap = roll > this.Tries; this.Tries++; var caps = this.Logic.Caps.WithoutKey().WithFlags(this.State); int maxSteps = 99999; if (!InternalOnly) { maxSteps = this.Logic.Random.Next(1, 20); } var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly, maxSteps); closure.Shuffle(this.Logic.Random); if (this.Req is FlagRequirement fr) { var curval = this.State.TryGetValue(fr.Flag, out var x) ? x : FlagState.Unset; if (fr.Set && (curval == FlagState.Both || curval == FlagState.Set || curval == FlagState.UnsetToSet)) { return(true); } if (!fr.Set && (curval == FlagState.Both || curval == FlagState.Unset || curval == FlagState.SetToUnset)) { return(true); } } if (!extendingMap) { switch (this.Req) { case KeyRequirement keyReq: { // just try to place a key foreach (var spot in closure.UnlinkedCollectables()) { if (spot.Static.MustFly) { continue; } if (spot.Node.Room == this.OriginalNode.Room) { // don't be boring! continue; } this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedCollectable.Key, false, keyReq.KeyholeID, this.OriginalNode.Room)); return(true); } // try again for things that we need to bubble back from var newClosure = new LinkedNodeSet(closure); newClosure.Extend(caps, null, true); foreach (var spot in newClosure.UnlinkedCollectables()) { if (spot.Static.MustFly) { continue; } if (spot.Node.Room == this.OriginalNode.Room) { // don't be boring! continue; } this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedCollectable.Key, true, keyReq.KeyholeID, this.OriginalNode.Room)); return(true); } // third try: place a room which has a reachable spot var appropriateNodes = this.Logic.RemainingRooms .Where(x => x.ReqEnd is Impossible) .SelectMany(r => r.Nodes.Values) .Where(n => n.Collectables.Count(c => !c.MustFly) != 0) .ToList(); appropriateNodes.Shuffle(this.Logic.Random); foreach (var n in appropriateNodes) { // hack: make a linkednode for each staticnode so that the closure methods can work on it... var edges = LinkedNodeSet .Closure(new LinkedRoom(n.ParentRoom, Vector2.Zero).Nodes[n.Name], caps, caps, true) .Shuffle(this.Logic.Random) .UnlinkedEdges() .Select(e => e.Static); foreach (var edge in edges) { foreach (var startEdge in closure.UnlinkedEdges()) { var receipt = ConnectAndMapReceipt.Do(this.Logic, startEdge, edge, true); if (receipt != null) { this.AddReceipt(receipt); var cols = n.Collectables.Where(c => !c.MustFly).ToList(); cols.Shuffle(this.Logic.Random); this.AddReceipt(PlaceCollectableReceipt.Do(receipt.NewRoom.Nodes[n.Name], cols[this.Logic.Random.Next(cols.Count)], LinkedCollectable.Key, false, keyReq.KeyholeID, this.OriginalNode.Room)); return(true); } } } } break; } case FlagRequirement flagReq: { var appropriateNodes = this.Logic.RemainingRooms .SelectMany(r => r.Nodes.Values) .Where(n => n.FlagSetters.Any(s => s.Item1 == flagReq.Flag && s.Item2 == flagReq.Set)) .ToList(); appropriateNodes.Shuffle(this.Logic.Random); foreach (var n in appropriateNodes) { // hack: make a linkednode for each staticnode so that the closure methods can work on it... var edges = LinkedNodeSet .Closure(new LinkedRoom(n.ParentRoom, Vector2.Zero).Nodes[n.Name], caps, caps, true) .Shuffle(this.Logic.Random) .UnlinkedEdges() .Select(e => e.Static); foreach (var edge in edges) { foreach (var startEdge in closure.UnlinkedEdges()) { var receipt = ConnectAndMapReceipt.Do(this.Logic, startEdge, edge, true); if (receipt != null) { this.AddReceipt(receipt); // TODO: check for reverse traversability back to orig node return(true); } } } } break; } default: { throw new Exception($"Don't know how to satisfy {this.Req}. What?"); } } } // see if we can find somewhere to extend the map foreach (var outEdge in closure.UnlinkedEdges()) { if (!this.Logic.Map.HoleFree(outEdge.Node.Room, outEdge.Static.HoleTarget)) { continue; } foreach (var toEdge in this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.ReqEnd is Impossible)) { var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge, isBacktrack: true); if (mapped == null) { continue; } this.AddReceipt(mapped); this.AddNextTask(new TaskPathwaySatisfyRequirement(this.Logic, mapped.EntryNode, this.Req, this.State, this.OriginalNode, true, this.Tries)); return(true); } } // if we failed to both extend the map or place the capability at the same time, we're f****d! if (!extendingMap) { return(false); } // try again return(this.Next()); }
public override bool Next() { if (this.Tries >= 5) { Logger.Log("randomizer", $"Failure: took too many tries to place key from {Node.Room.Static.Name}:{Node.Static.Name}"); return(false); } // each attempt we should back further away from the idea that we might add a new room int roll = this.Logic.Random.Next(5); bool extendingMap = roll > this.Tries; this.Tries++; var caps = this.Logic.Caps.WithoutKey(); int maxSteps = 99999; if (!InternalOnly) { maxSteps = this.Logic.Random.Next(6, 20); } var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly, maxSteps); closure.Shuffle(this.Logic.Random); if (!extendingMap) { // just try to place a key foreach (var spot in closure.UnlinkedCollectables()) { if (spot.Static.MustFly) { continue; } if (spot.Node.Room == this.OriginalNode.Room) { // don't be boring! continue; } this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key, false)); this.OriginalNode.Room.UsedKeyholes.Add(this.KeyholeID); return(true); } // try again for things that we need to bubble back from var newClosure = new LinkedNodeSet(closure); newClosure.Extend(caps, null, true); foreach (var spot in newClosure.UnlinkedCollectables()) { if (spot.Static.MustFly) { continue; } if (spot.Node.Room == this.OriginalNode.Room) { // don't be boring! continue; } this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key, true)); this.OriginalNode.Room.UsedKeyholes.Add(this.KeyholeID); return(true); } } // see if we can find somewhere to extend the map foreach (var outEdge in closure.UnlinkedEdges()) { if (!this.Logic.Map.HoleFree(outEdge.Node.Room, outEdge.Static.HoleTarget)) { continue; } foreach (var toEdge in this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.ReqEnd is Impossible)) { var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge, isBacktrack: true); if (mapped == null) { continue; } this.AddReceipt(mapped); this.AddNextTask(new TaskPathwayPlaceKey(this.Logic, mapped.EntryNode, this.KeyholeID, this.OriginalNode, true, this.Tries)); return(true); } } // try again return(this.Next()); }