public GasGiant(PlanetSettings settings, GasGiantData data, Orbit orbit) : base(settings, data, orbit) { GasGiantData = data; GravityWavesDepth = new ReadOnlyReactiveProperty <float>( data.Mass.CombineLatest(data.WaveDepthMultiplier, (mass, depth) => settings.WaveDepth.Evaluate(mass) * depth)); GravityWavesRadius = new ReadOnlyReactiveProperty <float>( data.Mass.CombineLatest(data.WaveRadiusMultiplier, (mass, radius) => settings.WaveRadius.Evaluate(mass) * radius)); GravityWavesSpeed = new ReadOnlyReactiveProperty <float>( data.Mass.CombineLatest(data.WaveSpeedMultiplier, (mass, speed) => settings.WaveSpeed.Evaluate(mass) * speed)); }
public static ZonePack GenerateZone( ItemManager itemManager, ZoneGenerationSettings zoneSettings, Galaxy galaxy, GalaxyZone galaxyZone, bool isTutorial = false) { var pack = new ZonePack(); var random = new Random(unchecked ((uint)galaxyZone.Name.GetHashCode()) ^ hash(galaxyZone.Position)); var density = saturate(galaxy.Background.CloudDensity(galaxyZone.Position) / 2); pack.Radius = zoneSettings.ZoneRadius.Evaluate(density); pack.Mass = zoneSettings.ZoneMass.Evaluate(density); var targetSubzoneCount = zoneSettings.SubZoneCount.Evaluate(density); //Debug.Log($"Generating zone at position {zone.Position} with radius {zoneRadius} and mass {zoneMass}"); var planets = new List <GeneratorPlanet>(); if (targetSubzoneCount > 1) { var zoneBoundary = new Circle(float2.zero, pack.Radius * zoneSettings.ZoneBoundaryRadius); float boundaryTangentRadius(float2 point) => - zoneBoundary.DistanceTo(point); var occupiedAreas = new List <Circle>(); float tangentRadius(float2 point) => min(boundaryTangentRadius(point), occupiedAreas.Min(circle => circle.DistanceTo(point))); var startPosition = random.NextFloat(pack.Radius * .25f, pack.Radius * .5f) * random.NextFloat2Direction(); occupiedAreas.Add(new Circle(startPosition, boundaryTangentRadius(startPosition))); int samples = 0; while (occupiedAreas.Count < targetSubzoneCount && samples < MaximumPlacementSamples) { samples = 0; for (int i = 0; i < MaximumPlacementSamples; i++) { var samplePos = random.NextFloat2(-pack.Radius, pack.Radius); var rad = tangentRadius(samplePos); if (rad > 0) { occupiedAreas.Add(new Circle(samplePos, rad)); break; } samples++; } } var totalArea = occupiedAreas.Sum(c => c.Area); foreach (var c in occupiedAreas) { planets.AddRange(GenerateEntities(zoneSettings, ref random, c.Area / totalArea * pack.Mass, c.Radius, c.Center)); } } else { planets.AddRange(GenerateEntities(zoneSettings, ref random, pack.Mass, pack.Radius, float2.zero)); } // Create collections to map between zone generator output and database entries var orbitMap = new Dictionary <GeneratorPlanet, OrbitData>(); var orbitInverseMap = new Dictionary <OrbitData, GeneratorPlanet>(); // Create orbit database entries pack.Orbits = planets.Select(planet => { var data = new OrbitData { FixedPosition = planet.FixedPosition, Distance = new ReactiveProperty <float>(planet.Distance), //Period = planet.Period, Phase = planet.Phase }; orbitMap[planet] = data; orbitInverseMap[data] = planet; return(data); }).ToList(); // Link OrbitData parents to database GUIDs foreach (var data in pack.Orbits) { data.Parent = orbitInverseMap[data].Parent != null ? orbitMap[orbitInverseMap[data].Parent].ID : Guid.Empty; } // Cache resource densities // var resourceMaps = mapLayers.Values // .ToDictionary(m => m.ID, m => m.Evaluate(zone.Position, settings.ShapeSettings)); pack.Planets = planets.Where(p => !p.Empty).Select(planet => { // Dictionary<Guid, float> planetResources = new Dictionary<Guid, float>(); BodyType bodyType = planet.Belt ? BodyType.Asteroid : planet.Mass > zoneSettings.SunMass ? BodyType.Sun : planet.Mass > zoneSettings.GasGiantMass ? BodyType.GasGiant : planet.Mass > zoneSettings.PlanetMass ? BodyType.Planet : BodyType.Planetoid; // foreach (var r in resources) // { // if ((bodyType & r.ResourceBodyType) != 0) // { // float quantity = ResourceValue(ref random, settings, r, r.ResourceDensity.Aggregate(1f, (m, rdm) => m * resourceMaps[rdm])); // if (r.Floor < quantity) planetResources.Add(r.ID, quantity); // } // } BodyData planetData; switch (bodyType) { case BodyType.Asteroid: planetData = new AsteroidBeltData(); break; case BodyType.Planetoid: case BodyType.Planet: planetData = new PlanetData(); break; case BodyType.GasGiant: planetData = new GasGiantData(); break; case BodyType.Sun: planetData = new SunData(); break; default: throw new ArgumentOutOfRangeException(); } planetData.Mass.Value = planet.Mass; planetData.Orbit = orbitMap[planet].ID; // planetData.Resources = planetResources; planetData.Name.Value = planetData.ID.ToString().Substring(0, 8); if (planetData is AsteroidBeltData beltData) { beltData.Asteroids = Enumerable.Range(0, (int)(zoneSettings.AsteroidCount.Evaluate(beltData.Mass.Value * orbitMap[planet].Distance.Value))) .Select(_ => new Asteroid { Distance = orbitMap[planet].Distance.Value + random.NextFloat() * (random.NextFloat() - .5f) * zoneSettings.AsteroidBeltWidth.Evaluate(orbitMap[planet].Distance.Value), Phase = random.NextFloat(), Size = random.NextFloat(), RotationSpeed = zoneSettings.AsteroidRotationSpeed.Evaluate(random.NextFloat()) }) //.OrderByDescending(a=>a.Size) .ToArray(); } else if (planetData is GasGiantData gas) { if (gas is SunData sun) { float primary = random.NextFloat(); float secondary = frac(primary + 1 + zoneSettings.SunSecondaryColorDistance * (random.NextFloat() > .5 ? 1 : -1)); gas.Colors.Value = new [] { float4(ColorMath.HsvToRgb(float3(primary, zoneSettings.SunColorSaturation, .5f)), 0), float4(ColorMath.HsvToRgb(float3(secondary, zoneSettings.SunColorSaturation, 1)), 1) }; sun.FogTintColor.Value = ColorMath.HsvToRgb(float3(primary, zoneSettings.SunFogTintSaturation, 1)); sun.LightColor.Value = ColorMath.HsvToRgb(float3(primary, zoneSettings.SunLightSaturation, 1)); gas.FirstOffsetDomainRotationSpeed.Value = 5; } else { // Define primary color and two adjacent colors float primary = random.NextFloat(); float right = frac(primary + zoneSettings.GasGiantBandColorSeparation); float left = frac(primary + 1 - zoneSettings.GasGiantBandColorSeparation); // Create n time keys from 0 to 1 var bandCount = (int)(zoneSettings.GasGiantBandCount.Evaluate(random.NextFloat()) + .5f); var times = Enumerable.Range(0, bandCount) .Select(i => (float)i / (bandCount - 1)); // Each band has a chance of being either the primary or one of the adjacent hues // Saturation and Value are random with curves applied gas.Colors.Value = times .Select(time => float4(ColorMath.HsvToRgb(float3( random.NextFloat() > zoneSettings.GasGiantBandAltColorChance ? primary : (random.NextFloat() > .5f ? right : left), zoneSettings.GasGiantBandSaturation.Evaluate(random.NextFloat()), zoneSettings.GasGiantBandSaturation.Evaluate(random.NextFloat()))), time)) .ToArray(); gas.FirstOffsetDomainRotationSpeed.Value = 0; } gas.AlbedoRotationSpeed.Value = -3; gas.FirstOffsetRotationSpeed.Value = 5; gas.SecondOffsetRotationSpeed.Value = 10; gas.SecondOffsetDomainRotationSpeed.Value = -25; } return(planetData); }).ToList(); var nearestFaction = galaxy.Factions.MinBy(f => galaxy.HomeZones[f].Distance[galaxyZone]); var nearestFactionHomeZone = galaxy.HomeZones[nearestFaction]; var factionPresence = nearestFaction.InfluenceDistance - nearestFactionHomeZone.Distance[galaxyZone] + 1; var storyStations = galaxyZone.Locations.Where(story => story.Type == LocationType.Station).ToArray(); var stationCount = (int)(random.NextFloat() * (factionPresence + 1)) + storyStations.Length; var potentialLagrangePoints = planets .Where(p => p.Parent != null && p.Parent.Children .TrueForAll(c => !(c != p && abs(c.Distance - p.Distance) < .1f))) // Filter Rosettes .OrderBy(p => p.Distance) .ToArray(); // Pick a selection from the middle of the distribution var selectedStationOrbits = potentialLagrangePoints .Skip(potentialLagrangePoints.Length / 2) .Take(stationCount) .Select(p => orbitMap[p]) .ToArray(); var loadoutGenerators = new Dictionary <Faction, LoadoutGenerator>(); LoadoutGenerator GetLoadoutGenerator(Faction faction) { if (!loadoutGenerators.ContainsKey(faction)) { loadoutGenerators[faction] = isTutorial ? new LoadoutGenerator(ref random, itemManager, faction, .5f) : new LoadoutGenerator(ref random, itemManager, galaxy, galaxyZone, faction, .5f); } return(loadoutGenerators[faction]); } OrbitData CreateLagrangeOrbit(OrbitData baseOrbit) { var lagrangeOrbit = new OrbitData { ID = Guid.NewGuid(), Parent = baseOrbit.Parent, Distance = new ReactiveProperty <float>(baseOrbit.Distance.Value), Phase = baseOrbit.Phase + PI / 3 * sign(random.NextFloat() - .5f) }; pack.Orbits.Add(lagrangeOrbit); return(lagrangeOrbit); } void PlaceTurret(OrbitData orbit, LoadoutGenerator loadoutGenerator, int distanceMultiplier) { var phase = 20f * distanceMultiplier / orbit.Distance.Value; var turretOrbit = new OrbitData { ID = Guid.NewGuid(), Parent = orbit.Parent, Distance = new ReactiveProperty <float>(orbit.Distance.Value), Phase = orbit.Phase + phase }; pack.Orbits.Add(turretOrbit); var turret = loadoutGenerator.GenerateTurretLoadout(); turret.Orbit = turretOrbit.ID; pack.Entities.Add(turret); } void PlaceTurrets(OrbitData orbit, LoadoutGenerator loadoutGenerator, int count) { for (int t = 0; t < count; t++) { var dist = t / 2; if (t % 2 == 0) { dist = -dist; } PlaceTurret(orbit, loadoutGenerator, dist); } } for (var i = 0; i < storyStations.Length; i++) { var story = storyStations[i]; var orbit = selectedStationOrbits[i]; var lagrangeOrbit = CreateLagrangeOrbit(orbit); var station = GetLoadoutGenerator(story.Faction).GenerateStationLoadout(); station.Orbit = lagrangeOrbit.ID; station.SecurityLevel = story.Security; station.SecurityRadius = pack.Radius; station.Story = i; pack.Entities.Add(station); PlaceTurrets(lagrangeOrbit, GetLoadoutGenerator(story.Faction), story.Turrets); } for (var i = storyStations.Length; i < selectedStationOrbits.Length; i++) { var orbit = selectedStationOrbits[i]; var security = (SecurityLevel)((int)((1f - pow(random.NextFloat(), factionPresence / 2f)) * 3f)); var lagrangeOrbit = CreateLagrangeOrbit(orbit); var station = GetLoadoutGenerator(nearestFaction).GenerateStationLoadout(); station.Orbit = lagrangeOrbit.ID; station.SecurityLevel = security; station.SecurityRadius = pack.Radius; pack.Entities.Add(station); PlaceTurrets(lagrangeOrbit, GetLoadoutGenerator(nearestFaction), 2); } var enemyCount = (int)(random.NextFloat() * factionPresence * 2) + stationCount; for (int i = 0; i < enemyCount; i++) { pack.Entities.Add(GetLoadoutGenerator(nearestFaction).GenerateShipLoadout()); } return(pack); }