예제 #1
0
            public override bool Next()
            {
                var caps    = this.Logic.Caps.WithoutKey();
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, true);

                foreach (var edge in closure.UnlinkedEdges())
                {
                    if (!this.Logic.Map.HoleFree(this.Node.Room, edge.Static.HoleTarget))
                    {
                        continue;
                    }
                    if (this.Logic.Random.Next(5) != 0)
                    {
                        continue;
                    }

                    var possibilities = this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.Collectables.Count != 0);
                    foreach (var newEdge in possibilities)
                    {
                        var receipt = ConnectAndMapReceipt.Do(this.Logic, edge, newEdge);
                        if (receipt == null)
                        {
                            continue;
                        }

                        var closure2 = LinkedNodeSet.Closure(receipt.EntryNode, caps, caps, true);
                        var seen     = new HashSet <UnlinkedCollectable>();
                        var options  = new List <Tuple <UnlinkedCollectable, bool> >();
                        foreach (var spot in closure2.UnlinkedCollectables())
                        {
                            seen.Add(spot);
                            options.Add(Tuple.Create(spot, false));
                        }

                        closure2.Extend(caps, null, true);
                        foreach (var spot in closure2.UnlinkedCollectables())
                        {
                            if (seen.Contains(spot))
                            {
                                continue;
                            }
                            options.Add(Tuple.Create(spot, true));
                        }

                        if (options.Count == 0)
                        {
                            receipt.Undo();
                            continue;
                        }

                        var pickedSpotTup = options[this.Logic.Random.Next(options.Count)];
                        var pickedSpot    = pickedSpotTup.Item1;
                        var berry         = pickedSpot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : this.Logic.Settings.Algorithm == LogicType.Endless ? LinkedNode.LinkedCollectable.LifeBerry : LinkedNode.LinkedCollectable.Strawberry;
                        pickedSpot.Node.Collectables[pickedSpot.Static] = Tuple.Create(berry, pickedSpotTup.Item2);
                        break;
                    }
                }

                return(true);
            }
예제 #2
0
            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
                bool extendingMap = this.Logic.Random.Next(5) > this.Tries + 1;

                this.Tries++;

                var caps    = this.Logic.Caps.WithoutKey();
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly);

                closure.Shuffle(this.Logic.Random);

                if (!extendingMap)
                {
                    // just try to place a key
                    foreach (var spot in closure.UnlinkedCollectables())
                    {
                        if (spot.Static.MustFly)
                        {
                            continue;
                        }
                        this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key));
                        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, (StaticEdge e) => !e.FromNode.ParentRoom.End))
                    {
                        var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge);
                        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());
            }
예제 #3
0
            private ConnectAndMapReceipt WorkingPossibility(UnlinkedEdge fromEdge)
            {
                var capsNoKey = this.Logic.Caps.WithoutKey();

                foreach (var toEdge in this.Logic.AvailableNewEdges(capsNoKey, capsNoKey, (StaticEdge e) => !e.FromNode.ParentRoom.End))
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, fromEdge, toEdge);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                return(null);
            }
예제 #4
0
            private ConnectAndMapReceipt WorkingPossibility()
            {
                var possibilities = this.Logic.AvailableNewEdges(this.Logic.Caps.WithoutKey(), this.Logic.Caps.WithoutKey(), (StaticEdge edge) => !edge.FromNode.ParentRoom.End && !this.TriedEdges.Contains(edge));

                foreach (var edge in possibilities)
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, this.Edge, edge);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                return(null);
            }
            private ConnectAndMapReceipt WorkingPossibility()
            {
                var caps          = this.Logic.Caps.WithoutKey(); // don't try to enter a door locked from the other side
                var possibilities = this.Logic.AvailableNewEdges(caps, null, (StaticEdge e) => !this.TriedRooms.Contains(e.FromNode.ParentRoom) && this.IsEnd == e.FromNode.ParentRoom.End);

                foreach (var edge in possibilities)
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, this.Edge, edge);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                return(null);
            }
예제 #6
0
            private ConnectAndMapReceipt WorkingWarpPossibility()
            {
                var allrooms = new List <StaticRoom>(this.Logic.RemainingRooms.Where(RoomFilter));

                allrooms.Shuffle(this.Logic.Random);

                foreach (var room in allrooms)
                {
                    var result = ConnectAndMapReceipt.DoWarp(this.Logic, this.Edge, room);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                return(null);
            }
예제 #7
0
            private ConnectAndMapReceipt WorkingPossibility()
            {
                var caps          = this.Logic.Caps.WithoutKey(); // don't try to enter a door locked from the other side
                var possibilities = this.Logic.AvailableNewEdges(caps, null, e => RoomFilter(e.FromNode.ParentRoom));

                if (possibilities.Count == 0 && this.IsEnd)
                {
                    throw new GenerationError("No ending rooms available");
                }

                foreach (var edge in possibilities)
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, this.Edge, edge);
                    if (result != null)
                    {
                        var defaultBerry = this.Logic.Settings.Algorithm == LogicType.Endless ? LinkedNode.LinkedCollectable.LifeBerry : LinkedNode.LinkedCollectable.Strawberry;
                        var closure      = LinkedNodeSet.Closure(result.EntryNode, caps, caps, true);
                        var seen         = new HashSet <UnlinkedCollectable>();
                        foreach (var spot in closure.UnlinkedCollectables())
                        {
                            seen.Add(spot);
                            if (!spot.Static.MustFly && this.Logic.Random.Next(5) == 0)
                            {
                                spot.Node.Collectables[spot.Static] = Tuple.Create(defaultBerry, false);
                            }
                        }

                        closure.Extend(caps, null, true);
                        foreach (var spot in closure.UnlinkedCollectables())
                        {
                            if (!seen.Contains(spot) && !spot.Static.MustFly && this.Logic.Random.Next(10) == 0)
                            {
                                spot.Node.Collectables[spot.Static] = Tuple.Create(defaultBerry, true);
                            }
                        }

                        return(result);
                    }
                }

                return(null);
            }
예제 #8
0
            private ConnectAndMapReceipt WorkingPossibility()
            {
                var caps          = this.Logic.Caps.WithoutKey(); // don't try to enter a door locked from the other side
                var possibilities = this.Logic.AvailableNewEdges(caps, null, e => RoomFilter(e.FromNode.ParentRoom));

                if (possibilities.Count == 0 && this.IsEnd)
                {
                    throw new GenerationError("No ending rooms available");
                }

                foreach (var edge in possibilities)
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, this.Edge, edge);
                    if (result != null)
                    {
                        return(result);
                    }
                }

                return(null);
            }
예제 #9
0
            public static StartRoomReceipt Do(RandoLogic logic, StaticRoom newRoomStatic)
            {
                Logger.Log("randomizer", $"Adding room {newRoomStatic.Name} at start");
                var newRoom = new LinkedRoom(newRoomStatic, Vector2.Zero);
                var extras  = ConnectAndMapReceipt.WarpClosure(logic, newRoom.Nodes["main"]);

                logic.Map.AddRoom(newRoom);

                if (!logic.Settings.RepeatRooms)
                {
                    logic.RemainingRooms.Remove(newRoomStatic);
                    foreach (var extra in extras)
                    {
                        logic.RemainingRooms.Remove(extra.Static);
                    }
                }

                return(new StartRoomReceipt {
                    Logic = logic,
                    NewRoom = newRoom,
                    ExtraRooms = extras,
                });
            }
예제 #10
0
        private void GenerateLabyrinth()
        {
            this.Caps             = this.Caps.WithoutKey();
            this.StartingGemCount = this.Settings.Length == MapLength.Short ? 3 : 0;

            void retry()
            {
                throw new RetryException();
            }

            foreach (var room in RandoLogic.AllRooms)
            {
                if (room.Name == "Celeste/6-Reflection/A/b-00")
                {
                    var lroom = new LabyrinthStartRoom(room);
                    this.Map.AddRoom(lroom);
                    this.RemainingRooms.Remove(room);
                    this.PossibleContinuations.AddRange(LinkedNodeSet.Closure(lroom.Nodes["main"], this.Caps, this.Caps, true).UnlinkedEdges());
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"status: rooms={this.Map.Count} queue={this.PossibleContinuations.Count}");
                int idx       = this.Random.Next(this.PossibleContinuations.Count);
                var startEdge = this.PossibleContinuations[idx];
                this.PossibleContinuations.RemoveAt(idx);

                foreach (var toEdge in this.AvailableNewEdges(this.Caps, this.Caps,
                                                              (edge) => edge.FromNode.ParentRoom.ReqEnd is Impossible &&
                                                              edge.FromNode.ParentRoom.Name != "Celeste/7-Summit/A/g-00b" &&
                                                              edge.FromNode.ParentRoom.Nodes.Values.All(node => node.FlagSetters.Count == 0)))
                {
                    var result = ConnectAndMapReceipt.Do(this, startEdge, toEdge);
                    if (result != null)
                    {
                        var closure     = LinkedNodeSet.Closure(result.EntryNode, this.Caps, this.Caps, true);
                        var ue          = closure.UnlinkedEdges();
                        var uc          = new List <Tuple <UnlinkedCollectable, bool> >();
                        var alreadySeen = new HashSet <UnlinkedCollectable>();
                        foreach (var c in closure.UnlinkedCollectables())
                        {
                            alreadySeen.Add(c);
                            uc.Add(Tuple.Create(c, false));
                        }
                        closure.Extend(this.Caps, null, true);
                        foreach (var c in closure.UnlinkedCollectables())
                        {
                            if (alreadySeen.Contains(c))
                            {
                                continue;
                            }
                            uc.Add(Tuple.Create(c, true));
                        }
                        if (ue.Count == 0 && uc.Count == 0)
                        {
                            result.Undo();
                            continue;
                        }
                        else if (ue.Count == 0)
                        {
                            this.PriorityCollectables.AddRange(uc);
                        }
                        else
                        {
                            this.PossibleContinuations.AddRange(ue);
                            this.PossibleCollectables.AddRange(uc);
                        }
                        break;
                    }
                }

                if (this.Map.Count >= LabyrinthMaximums[(int)this.Settings.Length])
                {
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"Pruning - {this.PossibleContinuations.Count} remaining");
                var startEdge = this.PossibleContinuations.Last();
                this.PossibleContinuations.RemoveAt(this.PossibleContinuations.Count - 1);

                var closure     = LinkedNodeSet.Closure(startEdge.Node, this.Caps, this.Caps, true);
                var uc          = new List <Tuple <UnlinkedCollectable, bool> >();
                var alreadySeen = new HashSet <UnlinkedCollectable>();
                foreach (var c in closure.UnlinkedCollectables())
                {
                    alreadySeen.Add(c);
                    uc.Add(Tuple.Create(c, false));
                }
                closure.Extend(this.Caps, null, true);
                foreach (var c in closure.UnlinkedCollectables())
                {
                    if (alreadySeen.Contains(c))
                    {
                        continue;
                    }
                    uc.Add(Tuple.Create(c, true));
                }

                if (uc.Count == 0)
                {
                    if (startEdge.Node.Room.Static.Name == "Celeste/6-Reflection/A/b-00")
                    {
                        // DON'T REMOVE THE STARTING ROOM OMG
                        continue;
                    }
                    var edgeCount = 0;
                    foreach (var node in startEdge.Node.Room.Nodes.Values)
                    {
                        edgeCount += node.Edges.Count;
                    }
                    if (edgeCount <= 1)
                    {
                        var nodeNames = new List <string>(startEdge.Node.Room.Nodes.Keys);
                        nodeNames.Sort();
                        foreach (var nodeName in nodeNames)
                        {
                            var node = startEdge.Node.Room.Nodes[nodeName];
                            foreach (var edge in node.Edges)
                            {
                                var otherNode = edge.OtherNode(node);
                                var otherEdge = edge.OtherEdge(node);
                                otherNode.Edges.Remove(edge);
                                this.PossibleContinuations.Add(new UnlinkedEdge(otherNode, otherEdge));
                            }
                        }
                        this.Map.RemoveRoom(startEdge.Node.Room);
                    }
                }
                else
                {
                    this.PriorityCollectables.AddRange(uc);
                    foreach (var c in uc)
                    {
                        this.PossibleCollectables.Remove(c);
                    }
                }
            }

            if (this.Map.Count < LabyrinthMinimums[(int)this.Settings.Length])
            {
                //Logger.Log("DEBUG", "retrying - too short");
                retry();
            }

            if (this.PossibleCollectables.Count + this.PriorityCollectables.Count < (6 - this.StartingGemCount))
            {
                //Logger.Log("DEBUG", "retrying - not enough spots");
                retry();
            }

            for (var gem = LinkedCollectable.Gem1 + this.StartingGemCount; gem <= LinkedCollectable.Gem6; gem++)
            {
                //Logger.Log("DEBUG", $"Placing {gem}");
                var collection = this.PriorityCollectables.Count != 0 ? this.PriorityCollectables : this.PossibleCollectables;
                if (collection.Count == 0)    // just in case
                {
                    retry();
                }
                var idx        = this.Random.Next(collection.Count);
                var spot       = collection[idx].Item1;
                var autoBubble = collection[idx].Item2;
                collection.RemoveAt(idx);

                if (spot.Static.MustFly)
                {
                    //Logger.Log("DEBUG", "...winged berry");
                    if (this.Settings.Strawberries != StrawberryDensity.None)
                    {
                        spot.Node.Collectables[spot.Static] = Tuple.Create(LinkedCollectable.WingedStrawberry, autoBubble);
                    }
                    gem--;
                }
                else
                {
                    spot.Node.Collectables[spot.Static] = Tuple.Create(gem, autoBubble);
                    //Logger.Log("DEBUG", $"Adding gem to {spot}");

                    if (collection == this.PriorityCollectables)
                    {
                        for (int i = 0; i < collection.Count; i++)
                        {
                            if (collection[i].Item1.Node.Room == spot.Node.Room)
                            {
                                collection.RemoveAt(i);
                                i--;
                            }
                        }
                    }
                }
            }

            var defaultBerry = this.Settings.HasLives ? LinkedCollectable.LifeBerry : LinkedCollectable.Strawberry;

            while (this.Settings.Strawberries != StrawberryDensity.None && this.PriorityCollectables.Count != 0)
            {
                //Logger.Log("DEBUG", $"Pruning priority collectables - {this.PriorityCollectables.Count} remaining");
                var spot       = this.PriorityCollectables.Last().Item1;
                var autoBubble = this.PriorityCollectables.Last().Item2;
                this.PriorityCollectables.RemoveAt(this.PriorityCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = Tuple.Create(spot.Static.MustFly ? LinkedCollectable.WingedStrawberry : defaultBerry, autoBubble);
            }

            int targetCount = 0;

            switch (this.Settings.Strawberries)
            {
            case StrawberryDensity.High:
                targetCount = 0;
                break;

            case StrawberryDensity.Low:
                targetCount = this.PossibleCollectables.Count / 3 * 2;
                break;

            case StrawberryDensity.None:
                targetCount = this.PossibleCollectables.Count;
                break;
            }
            this.PossibleCollectables.Shuffle(this.Random);
            while (this.PossibleCollectables.Count > targetCount)
            {
                //Logger.Log("DEBUG", $"Pruning collectables - {this.PossibleContinuations.Count - targetCount} remaining");
                var spot       = this.PossibleCollectables.Last().Item1;
                var autoBubble = this.PossibleCollectables.Last().Item2;
                this.PossibleCollectables.RemoveAt(this.PossibleCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = Tuple.Create(spot.Static.MustFly ? LinkedCollectable.WingedStrawberry : defaultBerry, autoBubble);
            }
        }
예제 #11
0
        private void GenerateLabyrinth()
        {
            this.Caps             = this.Caps.WithoutKey();
            this.StartingGemCount = this.Settings.Length == MapLength.Short ? 3 : 0;

            void retry()
            {
                this.PossibleCollectables.Clear();
                this.PriorityCollectables.Clear();
                this.PossibleContinuations.Clear();
                this.ResetRooms();
                this.Map.Clear();
            }

tryagain:

            foreach (var room in this.RemainingRooms)
            {
                if (room.Name == "Celeste/6-Reflection/A/b-00")
                {
                    var lroom = new LabyrinthStartRoom(room);
                    this.Map.AddRoom(lroom);
                    this.RemainingRooms.Remove(room);
                    this.PossibleContinuations.AddRange(LinkedNodeSet.Closure(lroom.Nodes["main"], this.Caps, this.Caps, true).UnlinkedEdges());
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"status: rooms={this.Map.Count} queue={this.PossibleContinuations.Count}");
                int idx       = this.Random.Next(this.PossibleContinuations.Count);
                var startEdge = this.PossibleContinuations[idx];
                this.PossibleContinuations.RemoveAt(idx);

                foreach (var toEdge in this.AvailableNewEdges(this.Caps, this.Caps, (edge) => !edge.FromNode.ParentRoom.End))
                {
                    var result = ConnectAndMapReceipt.Do(this, startEdge, toEdge);
                    if (result != null)
                    {
                        var closure = LinkedNodeSet.Closure(result.EntryNode, this.Caps, this.Caps, true);
                        var ue      = closure.UnlinkedEdges();
                        var uc      = closure.UnlinkedCollectables();
                        if (ue.Count == 0 && uc.Count == 0)
                        {
                            result.Undo();
                            continue;
                        }
                        else if (ue.Count == 0)
                        {
                            this.PriorityCollectables.AddRange(uc);
                        }
                        else
                        {
                            this.PossibleContinuations.AddRange(ue);
                            this.PossibleCollectables.AddRange(uc);
                        }
                        break;
                    }
                }

                if (this.Map.Count >= LabyrinthMaximums[(int)this.Settings.Length])
                {
                    break;
                }
            }

            if (this.Map.Count < LabyrinthMinimums[(int)this.Settings.Length])
            {
                //Logger.Log("DEBUG", "retrying - too short");
                retry();
                goto tryagain;
            }

            if (this.PossibleCollectables.Count + this.PriorityCollectables.Count < (6 - this.StartingGemCount))
            {
                //Logger.Log("DEBUG", "retrying - not enough spots");
                retry();
                goto tryagain;
            }

            for (var gem = LinkedNode.LinkedCollectable.Gem1 + this.StartingGemCount; gem <= LinkedNode.LinkedCollectable.Gem6; gem++)
            {
                var collection = this.PriorityCollectables.Count != 0 ? this.PriorityCollectables : this.PossibleCollectables;
                if (collection.Count == 0)    // just in case
                {
                    retry();
                    goto tryagain;
                }
                var idx  = this.Random.Next(collection.Count);
                var spot = collection[idx];
                collection.RemoveAt(idx);

                if (spot.Static.MustFly)
                {
                    spot.Node.Collectables[spot.Static] = LinkedNode.LinkedCollectable.WingedStrawberry;
                    gem--;
                }
                else
                {
                    spot.Node.Collectables[spot.Static] = gem;
                }
            }

            while (this.PriorityCollectables.Count != 0)
            {
                var spot = this.PriorityCollectables[this.PriorityCollectables.Count - 1];
                this.PriorityCollectables.RemoveAt(this.PriorityCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = spot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : LinkedNode.LinkedCollectable.Strawberry;
            }

            var targetCount = this.PossibleCollectables.Count / 3 * 2;

            this.PossibleCollectables.Shuffle(this.Random);
            while (this.PossibleCollectables.Count > targetCount)
            {
                var spot = this.PossibleCollectables[this.PossibleCollectables.Count - 1];
                this.PossibleCollectables.RemoveAt(this.PossibleCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = spot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : LinkedNode.LinkedCollectable.Strawberry;
            }
        }
예제 #12
0
            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());
            }
예제 #13
0
            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());
            }