Aetheria/Assets/Scripts/ZoneManager.cs
hyperion51 c58ba0b1da Copy Aetheria Code
Added a bunch of code from the old Aetheria project
2020-02-27 18:51:07 +00:00

236 lines
7 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using MessagePack;
using Unity.Mathematics;
using UnityEngine;
using Random = UnityEngine.Random;
// TODO: USE THIS EVERYWHERE
using static Unity.Mathematics.math;
public class ZoneManager
{
// public float BinaryFloor = 100;
// public float BinaryProbability = .1f;
// public float MassPowerMinimum = 1.35f;
// public float MassPowerMaximum = 1.75f;
// public float DistancePowerMinimum = 1.35f;
// public float DistancePowerMaximum = 1.75f;
// public float Radius = 500;
// public float Mass = 100000;
// public float MassFloor = 1;
// public float SunMass = 10000;
// public float GasGiantMass = 2000;
// public float RadiusPower = 1.75f;
public static Planet[] GenerateEntities(ZoneData data)
{
Random.InitState(data.Entry.Name.GetHashCode());
Planet.MassFloor = data.MassFloor;
Planet.RadiusPower = data.RadiusPower;
var root = new Planet
{
Mass = data.Mass,
ChildDistanceMaximum = data.Radius,
ChildDistanceMinimum = Planet.Radius(data.Mass)
};
root.ExpandRosette(6);
root.ExpandSolar(4, .6f, .8f, 1.25f, 1.75f);
var averageChildMass = root.Children.Sum(p => p.Mass) / root.Children.Count;
foreach(var p in root.Children.Where(c=>c.Mass>data.GasGiantMass))
{
var m = p.Mass / averageChildMass;
p.ExpandSolar((int) (m * 5), 1.5f, 2.5f, 1 + m * .05f, 1.05f + m * .15f);
}
// foreach (var p in root.Children.Concat(root.Children.SelectMany(c => c.Children))
// .Where(p => p.Mass > BinaryFloor && Random.value < BinaryProbability))
// p.ExpandBinary();
return root.AllPlanets().ToArray();
}
}
public class Planet
{
public static float MassFloor;
public static float RadiusPower;
public float Distance;
public float Phase;
public float Mass;
public float Period;
public float ChildDistanceMinimum;
public float ChildDistanceMaximum;
public bool Empty = false;
public List<Planet> Children = new List<Planet>();
public Planet Parent;
public Orbit Instance;
public static float Radius(float mass)
{
return pow(mass, 1 / RadiusPower);
}
public IEnumerable<Planet> AllPlanets()
{
return new[]{this}.Concat(Children.SelectMany(c=>c.AllPlanets()));
}
public void ExpandRosette(int vertices)
{
//Empty = true;
vertices = vertices / 2 * 2;
// Masses in a rosette alternate, so every sequential pair has the same shared mass
var sharedMass = Mass / (vertices / 2);
var proportion = Random.Range(.5f,.9f);
var dist = (ChildDistanceMinimum + ChildDistanceMaximum) / 2;
var p0 = new float2(0, dist);
var p1 = Orbit.Evaluate(1.0f / vertices) * dist;
var totalChildDist = distance(p0, p1) - Radius(sharedMass * proportion) - Radius(sharedMass * (1 - proportion));
var p0ChildDist = totalChildDist * proportion;
var p1ChildDist = totalChildDist * (1 - proportion);
for (int i = 0; i < vertices; i++)
{
var child = new Planet
{
Parent = this,
Mass = sharedMass * (i % 2 == 0 ? proportion : 1 - proportion), // Masses alternate
Distance = dist,
Phase = (float) i / vertices,
ChildDistanceMaximum = (i % 2 == 0 ? p0ChildDist : p1ChildDist)
};
child.ChildDistanceMinimum = Radius(child.Mass) * 2;
child.Period = child.Distance * child.Distance / 100;
Children.Add(child);
}
ChildDistanceMinimum = dist + p0ChildDist;
}
public void ExpandSolar(int count, float massPowMin, float massPowMax, float distPowMin, float distPowMax)
{
if (count == 0 || ChildDistanceMaximum < ChildDistanceMinimum)
return;
var masses = new float[count];
var distances = new float[count];
float massTotal = distances[0] = masses[0] = 1;
for (var i = 1; i < count; i++)
{
massTotal += masses[i] = masses[i - 1] * Random.Range(massPowMin, massPowMax);
distances[i] = distances[i - 1] * Random.Range(distPowMin, distPowMax);
}
for (var i = 0; i < masses.Length; i++)
masses[i] *= Random.Range(.1f, 1f);
for (var i = 0; i < count; i++)
{
Func<int,float> mass = d => masses[d] / massTotal * Mass;
Func<int,float> realDist = d => lerp(ChildDistanceMinimum, ChildDistanceMaximum,
(distances[d] - distances[0]) / (distances[count - 1] - distances[0]) * .9f) + Radius(mass(d));
if (mass(i) > MassFloor)
{
var child = new Planet
{
Parent = this,
Mass = mass(i),
Distance = realDist(i),
Phase = Random.value
};
child.Period = child.Distance * child.Distance / 100;
child.ChildDistanceMinimum = Radius(child.Mass) * 2;
child.ChildDistanceMaximum = min(i > 0 ? child.Distance - realDist(i - 1) : child.Distance - ChildDistanceMinimum,
i < count - 1 ? realDist(i + 1) - child.Distance : float.PositiveInfinity);
if (float.IsNaN(child.Distance)) {
Debug.Log($"Planet created with NaN distance, something went very wrong!");
return;
}
if (child.ChildDistanceMaximum > child.ChildDistanceMinimum)
Children.Add(child);
}
}
}
public void ExpandBinary()
{
Empty = true;
var proportion = Random.Range(.5f,.99f);
var dist = Random.Range(.1f * ChildDistanceMaximum, .5f * ChildDistanceMaximum);
var big = new Planet
{
Mass = Mass * proportion,
Distance = dist * (1 - proportion)
};
big.ChildDistanceMinimum = Radius(big.Mass);
big.Period = dist * dist / 100;
big.ChildDistanceMaximum = dist * proportion;
Children.Add(big);
var little = new Planet
{
Parent = this,
Mass = Mass * (1 - proportion),
Distance = dist * proportion
};
little.ChildDistanceMinimum = Radius(little.Mass);
little.Period = dist * dist / 100;
little.ChildDistanceMaximum = dist * (1 - proportion);
little.Phase = .5f;
Children.Add(little);
ChildDistanceMaximum = little.Distance + little.ChildDistanceMaximum;
}
public void Expand(ExpansionType type)
{
switch (type)
{
case ExpansionType.Rosette:
// Choose an even number of vertices between 4 and 12 inclusive
ExpandRosette((Random.Range(2, 7) * Random.Range(2, 7)) / 2 * 2);
break;
case ExpansionType.Solar:
ExpandSolar(Random.Range(1, 6) + Random.Range(1, 6), 1.2f, 2f, 1.2f, 2f);
break;
case ExpansionType.Binary:
ExpandBinary();
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}
public enum ExpansionType
{
Rosette,
Solar,
Binary
}
[MessagePackObject]
public class ZoneData : IDatabaseEntry
{
[Inspectable] [Key("entry")] public DatabaseEntry DatabaseEntry = new DatabaseEntry();
[Inspectable] [Key("radius")] public float Radius = 500;
[Inspectable] [Key("radiusPower")] public float RadiusPower = 1.75f;
[Inspectable] [Key("mass")] public float Mass = 100000;
[Inspectable] [Key("massFloor")] public float MassFloor = 1;
[Inspectable] [Key("sunMass")] public float SunMass = 10000;
[Inspectable] [Key("gasGiantMass")] public float GasGiantMass = 2000;
[InspectableDatabaseLink(typeof(ZoneData))] [Key("wormholes")]
public List<Guid> Wormholes = new List<Guid>();
[IgnoreMember] public DatabaseEntry Entry => DatabaseEntry;
}