public class Mapgen_Octopus : Mapgen_Base { /* This map type was suggested by Tadrinth on the forums. He couched it as "Spiral galaxy: large cluster in the middle (Body), 8 arms coming off, each arm is a series of linked small clusters. " which made me think of an octopus. So variables are named like it's an octopus It was initially coded by BadgerBadger, and then Tadrinth made some welcome tweaks. Original modification notes for Tadrinth: We figure out how many planets belong in each arm and how many planets go in the body. Planets are allocated via the "addPointsInCircle" because that's easily implemented (and because Keith had already written most of the pieces, so I could steal it readily). If you want two clusters per arm and a bit of a spiral then I suggest you allocate more planets per Arm (note the minPlanetsPerArm and maxPlanetsPerArm variables at the top), then allocate a second armCenter that's a bit further away from the body and at a slightly different angle You can connect gruops of planets via the linkPlanetLists function, so just call that first to link the two clusters in each arm Tadrinth update notes: We seed the core of the galaxy as either one big center cluster, or a ring of smaller clusters. Then we seed pairs of arms; each arm is made up of an middle cluster and an outer cluster. Then we link everything together. */ private readonly bool debug = false; public override void Generate(Galaxy galaxy, ArcenSimContext Context, MapConfiguration mapConfig, MapTypeData mapType) { int symmetryFactor = 2; int numberToSeed = mapConfig.GetClampedNumberOfPlanetsForMapType( mapType ); int userSetSymmetry = BadgerUtilityMethods.getSettingValueInt_Expensive( mapConfig, mapType, "OctopusNumArms" ); int radius = 100; // base radius for each cluster (center is twice as large) int distForThisArm = 105; // base distance for how far out each arm should be placed int minDistanceBetweenPlanets = 45; // planets will be packed more densely than this if needed. int alignmentNumber = 10; //align all planets on points divisible by this value. It makes things look more organized if (numberToSeed < 20) { radius = 70; distForThisArm = 80; symmetryFactor = 1; } else if (numberToSeed < 60) { radius = 90; distForThisArm = 100; symmetryFactor = 2; } else if (numberToSeed < 80) { radius = 100; distForThisArm = 120; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(2,3); } else if(numberToSeed < 110) { radius = 130; distForThisArm = 145; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(2, 4); } else if (numberToSeed < 200) { radius = 200; distForThisArm = 205; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(3, 5); } else if (numberToSeed < 300) { radius = 220; distForThisArm = 270; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(4, 6); } else if(numberToSeed < 400) { radius = 250; distForThisArm = 315; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(8, 10); } else { radius = 350; distForThisArm = 415; symmetryFactor = Context.RandomToUse.NextWithInclusiveUpperBound(10, 12); } if(userSetSymmetry != 0) symmetryFactor = userSetSymmetry; // need at least symmetry three for multi-cluster method to look decent bool singleLargeCentralCluster = (symmetryFactor < 3) || Context.RandomToUse.NextBool(); if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine(string.Format("Generating a spiral galaxy with symmetry {0} and {1}", symmetryFactor, singleLargeCentralCluster ? "one large central cluster" : "a ring of small central clusters"), Verbosity.ShowAsInfo); ArcenPoint galacticCenter = Engine_AIW2.Instance.GalaxyMapOnly_GalaxyCenter; List centerClusterSizes = new List(); List innerArmClusterSizes = new List(); List outerArmClusterSizes = new List(); // spread half the planets evenly across the clusters int minPlanetsPerCluster = Math.Max(numberToSeed / (symmetryFactor * 5) / 2, 2); for (int i = 0; i < symmetryFactor; i++) { centerClusterSizes.Add(minPlanetsPerCluster); innerArmClusterSizes.Add(minPlanetsPerCluster); innerArmClusterSizes.Add(minPlanetsPerCluster); outerArmClusterSizes.Add(minPlanetsPerCluster); outerArmClusterSizes.Add(minPlanetsPerCluster); } int planetsRemaining = numberToSeed - symmetryFactor * minPlanetsPerCluster * 5; // spread rest of planets randomly across the clusters // have to adjust ratios a bit depending on number of arms; the middle feels a little small with few arms and too big with many, otherwise int outerClusterRatio = 25; int innerClusterRatio = 30; if(symmetryFactor > 2) { outerClusterRatio = 35; innerClusterRatio = 30; } else if(symmetryFactor > 4) { outerClusterRatio = 50; innerClusterRatio = 40; } while (planetsRemaining > 0) { int percent = Context.RandomToUse.NextWithInclusiveUpperBound(1, 100); List clusterSizesListToAddTo; if (percent > 100 - outerClusterRatio) { clusterSizesListToAddTo = outerArmClusterSizes; } else if (percent > 100 - outerClusterRatio - innerClusterRatio) { clusterSizesListToAddTo = innerArmClusterSizes; } else { clusterSizesListToAddTo = centerClusterSizes; } int i = Context.RandomToUse.Next(0, clusterSizesListToAddTo.Count); clusterSizesListToAddTo[i] += 1; planetsRemaining -= 1; } if (this.debug) { ArcenDebugging.ArcenDebugLogSingleLine("center " + String.Join(", ", centerClusterSizes), Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine(" inner " + String.Join(", ", innerArmClusterSizes), Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine(" outer " + String.Join(", ", outerArmClusterSizes), Verbosity.DoNotShow); } //allocate the points for the body List allPoints = new List(); List bodyCenters = new List(); List> bodyPlanets = new List>(); //Figure out where to place the Arms; we split them evenly around the body //note that we update the armAngle for each iteration. AngleDegrees startingAngle = AngleDegrees.Create((float)Context.RandomToUse.NextWithInclusiveUpperBound(10, 350)); // randomly spin before we start AngleDegrees anglePerArm = AngleDegrees.Create((float)360 / (float)symmetryFactor); AngleDegrees subAnglePerArm = AngleDegrees.Create((float)360 / (float)symmetryFactor / (float)3); AngleDegrees spiralizeAngle = AngleDegrees.Create((float)360 / (float)symmetryFactor / (float)6); // spin things slightly more as we go outward so it looks a little like a spiral galaxy AngleDegrees armAngle = startingAngle; List bodyCluster = new List(); ArcenPoint center; // randomize linking method for large central cluster; usually densely connected since arms are sparse int percentGabriel = 80; int percentRNG = 10; int percentSpanningTree = 10; int percentSpanningTreeWithConnections = 0; LinkMethod linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); if (singleLargeCentralCluster) { int totalCentralPlanets = 0; for(int i = 0; i < centerClusterSizes.Count; i++) { totalCentralPlanets += centerClusterSizes[i]; } CreateClusterOfPlanets(bodyCluster, galaxy, Context, radius * 2, galacticCenter, minDistanceBetweenPlanets, alignmentNumber, totalCentralPlanets, ref allPoints, armAngle, linkingMethod, 0); if (true) { ArcenDebugging.ArcenDebugLogSingleLine("total central planets: " + totalCentralPlanets, Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine("created central planets: " + bodyCluster.Count, Verbosity.DoNotShow); } } for (int i = 0; i < symmetryFactor; i++) { if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine(string.Format("creating cluster {0}", i), Verbosity.DoNotShow); armAngle = armAngle.Add(anglePerArm); AngleDegrees firstArmAngle = armAngle.Add(spiralizeAngle); AngleDegrees secondArmAngle = firstArmAngle.Add(subAnglePerArm); if( this.debug) { ArcenDebugging.ArcenDebugLogSingleLine(string.Format("armAngle {0}", armAngle), Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine(string.Format("first arm angle {0}", firstArmAngle), Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine(string.Format("second arm angle {0}", secondArmAngle), Verbosity.DoNotShow); } //pick random method for linking clusters making up the central ring, again usually dense percentGabriel = 75; percentRNG = 15; percentSpanningTree = 10; percentSpanningTreeWithConnections = 0; linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); if (!singleLargeCentralCluster) { bodyCluster = new List(); center = CreateClusterOfPlanets(bodyCluster, galaxy, Context, radius, galacticCenter, minDistanceBetweenPlanets, alignmentNumber, centerClusterSizes[i], ref allPoints, armAngle, linkingMethod, distForThisArm); bodyPlanets.Add(bodyCluster); bodyCenters.Add(center); } if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine(string.Format("creating inner arm clusters {0}", i), Verbosity.DoNotShow); // random link method for inner parts of the arms; these can be anything. percentGabriel = 50; percentRNG = 35; percentSpanningTree = 10; percentSpanningTreeWithConnections = 5; linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); List innerArm1 = new List(); ArcenPoint innerArm1Center = CreateClusterOfPlanets(innerArm1, galaxy, Context, radius, galacticCenter, minDistanceBetweenPlanets+15, alignmentNumber, innerArmClusterSizes[2 * i], ref allPoints, firstArmAngle, linkingMethod, distForThisArm * 2+20); if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine(string.Format("creating second inner arm clusters {0}", i), Verbosity.DoNotShow); List innerArm2 = new List(); ArcenPoint innerArm2Center = CreateClusterOfPlanets(innerArm2, galaxy, Context, radius, galacticCenter, minDistanceBetweenPlanets+15, alignmentNumber, innerArmClusterSizes[2 * i + 1], ref allPoints, secondArmAngle, linkingMethod, distForThisArm * 2+35); // random link method for outer parts of arms; prefer sparse for good defense percentGabriel = 15; percentRNG = 15; percentSpanningTree = 60; percentSpanningTreeWithConnections = 10; linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine(string.Format("creating outer arm clusters {0}", i), Verbosity.DoNotShow); linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); List outerArm1 = new List(); ArcenPoint outerArm1Center = CreateClusterOfPlanets(outerArm1, galaxy, Context, radius + 30, galacticCenter, minDistanceBetweenPlanets + 40, alignmentNumber, outerArmClusterSizes[2 * i], ref allPoints, firstArmAngle.Add(spiralizeAngle), LinkMethod.SpanningTreeWithConnections, distForThisArm * 4); if( this.debug) { ArcenDebugging.ArcenDebugLogSingleLine(string.Format("linking outer arm clusters {0}", i), Verbosity.DoNotShow); ArcenDebugging.ArcenDebugLogSingleLine(string.Format("creating second outer arm clusters {0}", i), Verbosity.DoNotShow); } linkingMethod = BadgerUtilityMethods.getRandomLinkMethod(percentSpanningTree, percentGabriel, percentRNG, percentSpanningTreeWithConnections, Context); List outerArm2 = new List(); ArcenPoint outerArm2Center = CreateClusterOfPlanets(outerArm2, galaxy, Context, radius+30, galacticCenter, minDistanceBetweenPlanets + 40, alignmentNumber, outerArmClusterSizes[2 * i + 1], ref allPoints, secondArmAngle.Add(spiralizeAngle), linkingMethod, distForThisArm * 4 + 30); // Link clusters together - inner to outer, body to inner BadgerUtilityMethods.linkPlanetLists(innerArm1, outerArm1, outerArm1Center, false); BadgerUtilityMethods.linkPlanetLists(bodyCluster, innerArm1, innerArm1Center, false); BadgerUtilityMethods.linkPlanetLists(innerArm2, outerArm2, outerArm2Center, false); BadgerUtilityMethods.linkPlanetLists(bodyCluster, innerArm2, innerArm2Center, false); } if (!singleLargeCentralCluster) { if( this.debug) ArcenDebugging.ArcenDebugLogSingleLine("linking central clusters together", Verbosity.DoNotShow); for (int i = 0; i < symmetryFactor - 1; i++) { BadgerUtilityMethods.linkPlanetLists(bodyPlanets[i], bodyPlanets[i + 1], bodyCenters[i + 1], false); } if (Context.RandomToUse.NextBool() || Context.RandomToUse.NextBool()) // occasionally skip completing the ring for spice { if (this.debug) ArcenDebugging.ArcenDebugLogSingleLine("linking last two central clusters together to make a ring", Verbosity.DoNotShow); BadgerUtilityMethods.linkPlanetLists(bodyPlanets[0], bodyPlanets[bodyPlanets.Count - 1], bodyCenters[bodyPlanets.Count - 1], false); } } }