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"); } } }
public UnlinkedEdge(LinkedNode node, StaticEdge edge) { this.Static = edge; this.Node = node; }
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, }); }
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)); } }