public void ProcessWarps(Dictionary <string, StaticRoom> mapRooms)
        {
            foreach (var node in this.Nodes.Values)
            {
                foreach (var conf in node.WarpConfig)
                {
                    if (!mapRooms.TryGetValue(conf.Warp, out StaticRoom toRoom))
                    {
                        throw new Exception($"{this.Name}: could not find warp target {conf.Warp}");
                    }
                    if (!toRoom.Nodes.TryGetValue(conf.To ?? "main", out StaticNode toNode))
                    {
                        throw new Exception($"{this.Name}: warp target {conf.Warp} has no node {conf.To ?? "main"}");
                    }

                    var edge = new StaticEdge {
                        FromNode   = node,
                        NodeTarget = toNode,
                        ReqOut     = this.ProcessReqs(conf.ReqOut, null, true),
                        ReqIn      = this.ProcessReqs(conf.ReqIn, null, false),
                    };
                    node.Edges.Add(edge);
                    var reverse = new StaticEdgeReversed(edge, node);
                    toNode.Edges.Add(reverse);
                }
            }
        }
 public StaticEdgeReversed(StaticEdge Child, StaticNode target)
 {
     this.Child      = Child;
     this.NodeTarget = target;
     this.FromNode   = Child.FromNode;
 }
        private void ProcessSubroom(StaticNode node, RandoConfigRoom config)
        {
            if (node.Name != "main" && config.Tweaks != null)
            {
                throw new Exception("Config error: you have a subroom with tweaks in it");
            }

            foreach (RandoConfigHole holeConfig in config.Holes ?? new List <RandoConfigHole>())
            {
                Hole matchedHole      = null;
                int  remainingMatches = holeConfig.Idx;
                foreach (Hole hole in this.Holes)
                {
                    if (hole.Side == holeConfig.Side)
                    {
                        if (remainingMatches == 0)
                        {
                            matchedHole = hole;
                            break;
                        }
                        else
                        {
                            remainingMatches--;
                        }
                    }
                }

                if (matchedHole == null)
                {
                    throw new Exception($"Could not find the hole identified by area:{this.Area} room:{config.Room} side:{holeConfig.Side} idx:{holeConfig.Idx}");
                }

                //Logger.Log("randomizer", $"Matching {roomConfig.Room} {holeConfig.Side} {holeConfig.Idx} to {matchedHole}");
                matchedHole.Kind   = holeConfig.Kind;
                matchedHole.Launch = holeConfig.Launch;
                if (holeConfig.LowBound != null)
                {
                    matchedHole.LowBound = (int)holeConfig.LowBound;
                }
                if (holeConfig.HighBound != null)
                {
                    matchedHole.HighBound = (int)holeConfig.HighBound;
                }
                if (holeConfig.HighOpen != null)
                {
                    matchedHole.HighOpen = (bool)holeConfig.HighOpen;
                }

                if (holeConfig.Kind != HoleKind.None)
                {
                    node.Edges.Add(new StaticEdge()
                    {
                        FromNode   = node,
                        HoleTarget = matchedHole,
                        ReqIn      = this.ProcessReqs(holeConfig.ReqIn, matchedHole, false),
                        ReqOut     = this.ProcessReqs(holeConfig.ReqOut, matchedHole, true)
                    });
                }
            }

            foreach (var edge in config.InternalEdges ?? new List <RandoConfigInternalEdge>())
            {
                StaticNode toNode;
                if (edge.To != null)
                {
                    toNode = this.Nodes[edge.To];
                }
                else if (edge.Split != null)
                {
                    if (node.Edges.Count != 2)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Cannot split: must have exactly two edges");
                    }

                    toNode = new StaticNode()
                    {
                        Name       = node.Name + "_autosplit",
                        ParentRoom = node.ParentRoom
                    };
                    if (node.ParentRoom.Nodes.ContainsKey(toNode.Name))
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] You may only autosplit a room once");
                    }
                    node.ParentRoom.Nodes[toNode.Name] = toNode;

                    bool firstMain;
                    var  first  = node.Edges[0].HoleTarget;
                    var  second = node.Edges[1].HoleTarget;
                    switch (edge.Split)
                    {
                    case RandoConfigInternalEdge.SplitKind.BottomToTop:
                        firstMain = first.Side == ScreenDirection.Down || second.Side == ScreenDirection.Up ||
                                    (first.Side != ScreenDirection.Up && second.Side != ScreenDirection.Down && first.HighBound > second.HighBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.TopToBottom:
                        firstMain = first.Side == ScreenDirection.Up || second.Side == ScreenDirection.Down ||
                                    (first.Side != ScreenDirection.Down && second.Side != ScreenDirection.Up && first.LowBound < second.LowBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.RightToLeft:
                        firstMain = first.Side == ScreenDirection.Right || second.Side == ScreenDirection.Left ||
                                    (first.Side != ScreenDirection.Left && second.Side != ScreenDirection.Right && first.HighBound > second.HighBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.LeftToRight:
                    default:
                        firstMain = first.Side == ScreenDirection.Left || second.Side == ScreenDirection.Right ||
                                    (first.Side != ScreenDirection.Right && second.Side != ScreenDirection.Left && first.LowBound < second.LowBound);
                        break;
                    }

                    var secondary = firstMain ? node.Edges[1] : node.Edges[0];
                    node.Edges.Remove(secondary);
                    toNode.Edges.Add(secondary);
                    secondary.FromNode = toNode;
                }
                else if (edge.Collectable != null)
                {
                    toNode = new StaticNode()
                    {
                        Name       = node.Name + "_coll" + edge.Collectable.ToString(),
                        ParentRoom = node.ParentRoom
                    };
                    if (node.ParentRoom.Nodes.ContainsKey(toNode.Name))
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] You may only autosplit a room once");
                    }
                    node.ParentRoom.Nodes[toNode.Name] = toNode;

                    var thing = this.Collectables[edge.Collectable.Value];
                    if (thing.ParentNode != null)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Can only assign a collectable to one owner");
                    }
                    thing.ParentNode = toNode;
                    toNode.Collectables.Add(thing);
                }
                else
                {
                    throw new Exception($"[{this.Name}.{node.Name}] Internal edge must have either To or Split or Collectable");
                }

                var reqIn  = this.ProcessReqs(edge.ReqIn, null, false);
                var reqOut = this.ProcessReqs(edge.ReqOut, null, true);

                var forward = new StaticEdge()
                {
                    FromNode   = node,
                    NodeTarget = toNode,
                    ReqIn      = reqIn,
                    ReqOut     = reqOut
                };
                var reverse = new StaticEdgeReversed(forward, node);

                node.Edges.Add(forward);
                toNode.Edges.Add(reverse);
            }

            foreach (var col in config.Collectables)
            {
                if (col.Idx != null)
                {
                    var thing = this.Collectables[col.Idx.Value];
                    if (thing.ParentNode != null)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Can only assign a collectable to one owner");
                    }
                    thing.ParentNode = node;
                    thing.MustFly    = col.MustFly;
                    node.Collectables.Add(thing);
                }
                else if (col.X != null && col.Y != null)
                {
                    node.Collectables.Add(new StaticCollectable {
                        ParentNode = node,
                        Position   = new Vector2((float)col.X.Value, (float)col.Y.Value),
                        MustFly    = col.MustFly
                    });
                }
                else
                {
                    throw new Exception($"[{this.Name}.{node.Name}] Collectable must specify Idx or X/Y");
                }
            }
        }
Exemple #4
0
 public UnlinkedEdge(LinkedNode node, StaticEdge edge)
 {
     this.Static = edge;
     this.Node   = node;
 }
Exemple #5
0
            public static ConnectAndMapReceipt Do(RandoLogic logic, UnlinkedEdge fromEdge, StaticEdge toEdge, bool isBacktrack = false)
            {
                var toRoomStatic = toEdge.FromNode.ParentRoom;
                var fromRoom     = fromEdge.Node.Room;

                if (fromEdge.Static.HoleTarget == null || toEdge.HoleTarget == null)
                {
                    return(null);
                }

                var newOffset = fromEdge.Static.HoleTarget.Compatible(toEdge.HoleTarget);

                if (newOffset == Hole.INCOMPATIBLE)
                {
                    return(null);
                }

                var newPosition = toRoomStatic.AdjacentPosition(fromRoom.Bounds, fromEdge.Static.HoleTarget.Side, newOffset);
                var toRoom      = new LinkedRoom(toRoomStatic, newPosition);

                if (!logic.Map.AreaFree(toRoom))
                {
                    return(null);
                }
                toRoom.IsBacktrack = isBacktrack;
                logic.Map.AddRoom(toRoom);

                var extras = WarpClosure(logic, toRoom.Nodes[toEdge.FromNode.Name], isBacktrack);

                if (extras == null)
                {
                    logic.Map.RemoveRoom(toRoom);
                    return(null);
                }

                var newEdge = new LinkedEdge {
                    NodeA   = fromEdge.Node,
                    NodeB   = toRoom.Nodes[toEdge.FromNode.Name],
                    StaticA = fromEdge.Static,
                    StaticB = toEdge,
                };

                newEdge.NodeA.Edges.Add(newEdge);
                newEdge.NodeB.Edges.Add(newEdge);

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

                Logger.Log("randomizer", $"Adding room {toRoomStatic.Name} at {newPosition} ({logic.Map.Count})");
                return(new ConnectAndMapReceipt {
                    NewRoom = toRoom,
                    Logic = logic,
                    Edge = newEdge,
                    EntryNode = toRoom.Nodes[toEdge.FromNode.Name],
                    ExtraRooms = extras,
                });
            }
Exemple #6
0
        private void ProcessSubroom(StaticNode node, RandoConfigRoom config)
        {
            if (node.Name != "main" && config.Tweaks != null)
            {
                throw new Exception("Config error: you have a subroom with tweaks in it");
            }

            foreach (RandoConfigHole holeConfig in config.Holes ?? new List <RandoConfigHole>())
            {
                if (holeConfig.New)
                {
                    if (holeConfig.LowBound == null || holeConfig.HighBound == null)
                    {
                        throw new Exception("Config error: new hole missing LowBound/HighBound");
                    }

                    if (holeConfig.Kind == HoleKind.None)
                    {
                        throw new Exception("You probably didn't mean to add a new hole with kind None");
                    }

                    var hole = new Hole(holeConfig.Side, holeConfig.LowBound.Value, holeConfig.HighBound.Value, holeConfig.HighOpen ?? false)
                    {
                        Launch = holeConfig.Launch,
                        Kind   = holeConfig.Kind,
                    };
                    this.Holes.Add(hole);
                    node.Edges.Add(new StaticEdge {
                        FromNode   = node,
                        HoleTarget = hole,
                        ReqIn      = this.ProcessReqs(holeConfig.ReqIn, hole, false),
                        ReqOut     = this.ProcessReqs(holeConfig.ReqOut, hole, true),
                    });
                    continue;
                }
                Hole matchedHole      = null;
                var  remainingMatches = holeConfig.Idx;
                foreach (var hole in this.Holes.Where(hole => hole.Side == holeConfig.Side))
                {
                    if (remainingMatches == 0)
                    {
                        matchedHole = hole;
                        break;
                    }
                    remainingMatches--;
                }

                if (matchedHole == null)
                {
                    throw new Exception($"Could not find the hole identified by area:{this.Area} room:{config.Room} side:{holeConfig.Side} idx:{holeConfig.Idx}");
                }

                //Logger.Log("randomizer", $"Matching {roomConfig.Room} {holeConfig.Side} {holeConfig.Idx} to {matchedHole}");
                matchedHole.Kind   = holeConfig.Kind;
                matchedHole.Launch = holeConfig.Launch;
                if (holeConfig.LowBound != null)
                {
                    matchedHole.LowBound = holeConfig.LowBound.Value;
                }
                if (holeConfig.HighBound != null)
                {
                    matchedHole.HighBound = holeConfig.HighBound.Value;
                }
                if (holeConfig.HighOpen != null)
                {
                    matchedHole.HighOpen = holeConfig.HighOpen.Value;
                }

                if (holeConfig.Kind != HoleKind.None)
                {
                    node.Edges.Add(new StaticEdge {
                        FromNode   = node,
                        HoleTarget = matchedHole,
                        ReqIn      = this.ProcessReqs(holeConfig.ReqIn, matchedHole, false),
                        ReqOut     = this.ProcessReqs(holeConfig.ReqOut, matchedHole, true),
                    });
                }

                if (holeConfig.Split != null)
                {
                    matchedHole.HighOpen = true;

                    var hole = new Hole(matchedHole.Side, 0, matchedHole.HighBound, false)
                    {
                        Launch = holeConfig.Split.Launch,
                        Kind   = holeConfig.Split.Kind,
                    };
                    this.Holes.Add(hole);
                    node.Edges.Add(new StaticEdge {
                        FromNode   = node,
                        HoleTarget = hole,
                        ReqIn      = this.ProcessReqs(holeConfig.Split.ReqIn, hole, false),
                        ReqOut     = this.ProcessReqs(holeConfig.Split.ReqOut, hole, true),
                    });
                }
            }

            foreach (var edge in config.InternalEdges ?? new List <RandoConfigInternalEdge>())
            {
                StaticNode toNode;
                if (edge.Warp != null)
                {
                    // deal with this later
                    node.WarpConfig.Add(edge);
                    continue;
                }
                else if (edge.To != null)
                {
                    if (!this.Nodes.ContainsKey(edge.To))
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] \"To\" edge says \"{edge.To}\" but no such subroom exists");
                    }
                    toNode = this.Nodes[edge.To];
                }
                else if (edge.CustomWarp)
                {
                    toNode = null;
                }
                else if (edge.Split != null)
                {
                    if (node.Edges.Count != 2)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Cannot split: must have exactly two edges");
                    }

                    toNode = new StaticNode()
                    {
                        Name       = node.Name + "_autosplit",
                        ParentRoom = node.ParentRoom
                    };
                    if (node.ParentRoom.Nodes.ContainsKey(toNode.Name))
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] You may only autosplit a room once");
                    }
                    node.ParentRoom.Nodes[toNode.Name] = toNode;

                    bool firstMain;
                    var  first  = node.Edges[0].HoleTarget;
                    var  second = node.Edges[1].HoleTarget;
                    switch (edge.Split)
                    {
                    case RandoConfigInternalEdge.SplitKind.BottomToTop:
                        firstMain = first.Side == ScreenDirection.Down || second.Side == ScreenDirection.Up ||
                                    (first.Side != ScreenDirection.Up && second.Side != ScreenDirection.Down && first.HighBound > second.HighBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.TopToBottom:
                        firstMain = first.Side == ScreenDirection.Up || second.Side == ScreenDirection.Down ||
                                    (first.Side != ScreenDirection.Down && second.Side != ScreenDirection.Up && first.LowBound < second.LowBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.RightToLeft:
                        firstMain = first.Side == ScreenDirection.Right || second.Side == ScreenDirection.Left ||
                                    (first.Side != ScreenDirection.Left && second.Side != ScreenDirection.Right && first.HighBound > second.HighBound);
                        break;

                    case RandoConfigInternalEdge.SplitKind.LeftToRight:
                    default:
                        firstMain = first.Side == ScreenDirection.Left || second.Side == ScreenDirection.Right ||
                                    (first.Side != ScreenDirection.Right && second.Side != ScreenDirection.Left && first.LowBound < second.LowBound);
                        break;
                    }

                    var secondary = firstMain ? node.Edges[1] : node.Edges[0];
                    node.Edges.Remove(secondary);
                    toNode.Edges.Add(secondary);
                    secondary.FromNode = toNode;
                }
                else if (edge.Collectable != null)
                {
                    toNode = new StaticNode()
                    {
                        Name       = node.Name + "_coll" + edge.Collectable.ToString(),
                        ParentRoom = node.ParentRoom
                    };
                    if (node.ParentRoom.Nodes.ContainsKey(toNode.Name))
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] You may only autosplit a room once");
                    }
                    node.ParentRoom.Nodes[toNode.Name] = toNode;

                    var thing = this.Collectables[edge.Collectable.Value];
                    if (thing.ParentNode != null)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Can only assign a collectable to one owner");
                    }
                    thing.ParentNode = toNode;
                    toNode.Collectables.Add(thing);
                }
                else
                {
                    throw new Exception($"[{this.Name}.{node.Name}] Internal edge must have either To or Split or Collectable or CustomWarp");
                }

                var reqIn  = this.ProcessReqs(edge.ReqIn, null, false);
                var reqOut = this.ProcessReqs(edge.ReqOut, null, true);

                var forward = new StaticEdge()
                {
                    FromNode   = node,
                    NodeTarget = toNode,
                    ReqIn      = reqIn,
                    ReqOut     = reqOut,
                    CustomWarp = edge.CustomWarp,
                };

                node.Edges.Add(forward);
                if (toNode != null)
                {
                    var reverse = new StaticEdgeReversed(forward, node);
                    toNode.Edges.Add(reverse);
                }
            }

            foreach (var col in config.Collectables)
            {
                if (col.Idx != null)
                {
                    var thing = this.Collectables[col.Idx.Value];
                    if (thing.ParentNode != null)
                    {
                        throw new Exception($"[{this.Name}.{node.Name}] Can only assign a collectable to one owner");
                    }
                    thing.ParentNode = node;
                    thing.MustFly    = col.MustFly;
                    node.Collectables.Add(thing);
                }
                else if (col.X != null && col.Y != null)
                {
                    node.Collectables.Add(new StaticCollectable {
                        ParentNode = node,
                        Position   = new Vector2(col.X.Value, col.Y.Value),
                        MustFly    = col.MustFly
                    });
                }
                else
                {
                    throw new Exception($"[{this.Name}.{node.Name}] Collectable must specify Idx or X/Y");
                }
            }

            foreach (var flagStr in config.Flags ?? new List <string>())
            {
                var name = flagStr;
                var val  = true;
                if (name.Contains(":"))
                {
                    var split = flagStr.Split(':');
                    name = split[0];
                    var v = split[1].ToLower();
                    val = v == "on" || v == "set" || v == "true" || v == "yes";
                }
                node.FlagSetters.Add(Tuple.Create(name, val));
            }
        }