/// <summary> /// Attempts to satisfy one connector in the current phase or advance to the next phase /// Returns true if generation is still in progress, false if it is complete or has failed /// </summary> public bool StepGeneration() { if (Generating) { var oldRandomState = Random.state; Random.state = randomState; if (currentPhaseStatus.RemainingPartCount > 0 && currentPhaseStatus.OpenOutbound.Count > 0) { //DungeonConnector outboundConnector = currentPhaseStatus.OpenOutbound.RandomElement(); DungeonConnector outboundConnector = currentPhaseStatus.OpenOutbound[0]; // Try to place each potentially connectable part (in random order) currentPhaseStatus.PartPool.Shuffle(); foreach (var candPart in currentPhaseStatus.PartPool) { if (candPart.HasInboundConnector(outboundConnector.ConnectionTag) && candPart.CanCombineWith(outboundConnector.ParentPart)) { if (TryPlacePart(candPart, outboundConnector)) { currentPhaseStatus.RemainingPartCount--; break; } } } // Store failed connectors to be attempted in later phases if (currentPhaseStatus.OpenOutbound.Remove(outboundConnector)) { currentPhaseStatus.FailedOutbound.Add(outboundConnector); } } else { //var placedPhaseParts = currentPhaseStatus.Config.PartLimit - currentPhaseStatus.RemainingPartCount; //Debug.Log($"Phase '{currentPhaseStatus.Config.PhaseName}' complete: {placedPhaseParts} parts placed, " + // $"{currentPhaseStatus.FailedOutbound.Count + currentPhaseStatus.OpenOutbound.Count} open connectors"); Generating = AdvanceGenerationPhase(); } randomState = Random.state; Random.state = oldRandomState; if (!Generating) { if (OnGenerationComplete != null) { OnGenerationComplete.Invoke(); } CurrentDungeonBounds.Expand(BoundsPadding); } } return(Generating); }
public bool CanConnectTo(DungeonConnector other) { return(other != this && (ConnectionTag == other.ConnectionTag) && ((AllowOutbound && other.AllowInbound) || (AllowInbound && other.AllowOutbound))); }
public List <DungeonConnector> InboundConnectorsFor(DungeonConnector outboundConnector) { return(connectors.Where(c => c.AllowInbound && c.CanConnectTo(outboundConnector)).ToList()); }
/// <summary> /// Attempts to attach the specified part to the specified outbound connector, performing bounds /// checks as appropriate. Will attempt placement with all valid matching inbound connectors /// Returns true if the part was placed successfully, false if not /// </summary> private bool TryPlacePart(DungeonPart partPrefab, DungeonConnector outboundConnector) { Quaternion targetOrientation = new Quaternion(); Vector3 targetPosition = Vector3.zero; int? inboundConnectorId = null; var prefabICs = partPrefab.InboundConnectorsFor(outboundConnector); prefabICs.Shuffle(); foreach (var prefabIC in prefabICs) { // calculate prospective orientation and position for part instance targetOrientation = Quaternion.AngleAxis(180, outboundConnector.transform.up) * outboundConnector.transform.rotation * Quaternion.Inverse(prefabIC.transform.localRotation); targetPosition = outboundConnector.transform.position - targetOrientation * prefabIC.transform.localPosition; bool canPlace = partPrefab.SkipBoundsCheck || partPrefab.BoundsCheck(outboundConnector.transform, prefabIC.ConnectorId); if (canPlace) { inboundConnectorId = prefabIC.ConnectorId; break; } else { //Debug.Log($"Bounds check failed! Can't connect {outboundConnector.gameObject.name} (outbound) to {partPrefab.PartName} {prefabIC.gameObject.name} (inbound)"); } } if (!inboundConnectorId.HasValue) { return(false); } var partInstance = Instantiate(partPrefab.gameObject, transform).GetComponent <DungeonPart>(); currentDungeonPartInstances.Add(partInstance); var inboundConnector = partInstance.GetConnector(inboundConnectorId.Value); // random rotation added separately from bounds check so that bounds check is deterministic if (inboundConnector.RandomRotation || outboundConnector.RandomRotation) { float rotateDegrees = Random.Range(0, 360); targetOrientation = Quaternion.AngleAxis(180, outboundConnector.transform.up) * Quaternion.AngleAxis(rotateDegrees, outboundConnector.transform.forward) * outboundConnector.transform.rotation * Quaternion.Inverse(inboundConnector.transform.localRotation); targetPosition = outboundConnector.transform.position - targetOrientation * inboundConnector.transform.localPosition; } partInstance.transform.localRotation = targetOrientation; partInstance.transform.position = targetPosition; //Debug.Log($"Connected {outboundConnector.gameObject.name} (outbound) to {partPrefab.PartName} {inboundConnector.gameObject.name} (inbound)"); var nextOutboundConnectors = partInstance.OutboundConnectors(); nextOutboundConnectors.Remove(inboundConnector); currentPhaseStatus.OpenOutbound.Remove(outboundConnector); // check whether this part makes additional connections to other open connectors for (var i = nextOutboundConnectors.Count - 1; i > 0; i--) { var connA = nextOutboundConnectors[i]; for (var j = currentPhaseStatus.OpenOutbound.Count - 1; j > 0; j--) { var connB = currentPhaseStatus.OpenOutbound[j]; if (connA.CanConnectTo(connB)) { if ((connA.transform.position - connB.transform.position).sqrMagnitude < 0.01f) { nextOutboundConnectors.RemoveAt(i); currentPhaseStatus.OpenOutbound.RemoveAt(j); //Debug.Log($"Detected additional connection at {connA.transform.position} between {connA.gameObject.name} and {connB.gameObject.name}, closing both."); } } } } currentPhaseStatus.OpenOutbound.AddRange(nextOutboundConnectors); return(true); }