using System; using System.Collections; using System.Collections.Generic; using System.Linq; //using MessagePack; using UnityEngine; using NaughtyAttributes; using JM.LinqFaster; using Unity.Mathematics; using static Unity.Mathematics.math; using static Unity.Mathematics.noise; using static NoiseFbm; [CreateAssetMenu(menuName = "Aetheria/Galaxy")] public class Galaxy : ScriptableObject { public GalaxyMapData MapData; } [Serializable] public class GalaxyMapData { public int Arms = 4; public float Twist = 10; public float TwistPower = 2; [HideInInspector] public GalaxyMapLayerData StarDensity; [HideInInspector] public Dictionary ResourceDensities = new Dictionary(); //[HideInInspector] public List Stars = new List(); } public static class StarListExtensions { class DijkstraStar { public float Cost; public DijkstraStar Parent; public StarData Star; } public static List FindPath(this List stars, StarData source, StarData target, bool bestFirst = false) { SortedList members = new SortedList{{0,new DijkstraStar{Star = source}}}; List searched = new List(); while (true) { var s = members.FirstOrDefault(m => !searched.Contains(m.Value)).Value; // Lowest cost unsearched node if (s == null) return null; // No vertices left unsearched if (s.Star == target) // We found the path { Stack path = new Stack(); // Since we start at the end, use a LIFO collection path.Push(s); while(path.Peek().Parent!=null) // Keep pushing until we reach the start, which has no parent path.Push(path.Peek().Parent); return path.Select(dv => dv.Star).ToList(); } // foreach (var dijkstraStar in s.Star.Links.Select(i => stars[i]) // All adjacent stars // .Where(n => members.All(m => m.Value.Star != n)) // That are not members // .Select(n => new DijkstraStar {Parent = s, Star = n, Cost = s.Cost + (s.Star.Position - n.Position).magnitude})) // For each adjacent star (filter already visited stars unless heuristic is in use) foreach (var dijkstraStar in s.Star.Links.WhereSelectF(i => !bestFirst || members.All(m => m.Value.Star != stars[i]), // Cost is parent cost plus distance i => new DijkstraStar {Parent = s, Star = stars[i], Cost = s.Cost + (s.Star.Position - stars[i].Position).magnitude})) // Add new member to list, sorted by cost plus optional heuristic members.Add(bestFirst ? dijkstraStar.Cost + (dijkstraStar.Star.Position - target.Position).magnitude : dijkstraStar.Cost, dijkstraStar); searched.Add(s); } } } [Serializable] public class StarData { public Vector2 Position; public List Links = new List(); } public static class NoiseFbm { public static float fBm(float2 p, int octaves, float frequency, float offset, float amplitude, float lacunarity, float gain) { float freq = frequency, amp = .5f; float sum = 0; for(int i = 0; i < octaves; i++) { sum += snoise(p * freq) * amp; freq *= lacunarity; amp *= gain; } return (sum + offset)*amplitude; } } [Serializable] public class GalaxyMapLayerData { public float CoreBoost = 1.05f; public float CoreBoostOffset = .1f; public float CoreBoostPower = 2.25f; public float SpokeScale = 1; public float SpokeOffset = 0; public float EdgeReduction = 3; public float NoiseOffset = 0; public float NoiseAmplitude = 1.5f; public float NoiseGain = .7f; public float NoiseLacunarity = 2; public int NoiseOctaves = 7; public float NoiseFrequency = 1; public float NoisePosition = 1337; public float Evaluate(float2 uv, GalaxyMapData galaxy) { float2 offset = -float2(.5f, .5f)+uv; float circle = (.5f-length(offset))*2; float angle = pow(length(offset)*2,galaxy.TwistPower) * galaxy.Twist; float2 twist = float2(offset.x*cos(angle) - offset.y*sin(angle), offset.x*sin(angle) + offset.y*cos(angle)); float atan = atan2(twist.y,twist.x); float spokes = (sin(atan*galaxy.Arms) + SpokeOffset) * SpokeScale; float noise = fBm(uv + float2(NoisePosition), NoiseOctaves, NoiseFrequency, NoiseOffset, NoiseAmplitude, NoiseLacunarity, NoiseGain); float shape = lerp(spokes - EdgeReduction * length(offset), 1, pow(circle + CoreBoostOffset, CoreBoostPower) * CoreBoost); float gal = max(shape - noise * saturate(circle), 0); return gal; } } public static class Vector2Extension { public static Vector2 Rotate(this Vector2 v, float degrees) { float sin = Mathf.Sin(degrees * Mathf.Deg2Rad); float cos = Mathf.Cos(degrees * Mathf.Deg2Rad); float tx = v.x; float ty = v.y; v.x = (cos * tx) - (sin * ty); v.y = (sin * tx) + (cos * ty); return v; } } [Serializable] public class NameData { public Dictionary>> Names = new Dictionary>>(); public void Add(string dataString) { //Debug.Log("Adding " + dataString); var all = dataString.Split(','); var name = all[0].Split(" -.".ToCharArray(),StringSplitOptions.RemoveEmptyEntries).OrderByDescending(s=>s.Length).First(); var region = all[1].Split('/'); var continent = region[0]; var capital = region[1]; if(!Names.ContainsKey(continent)) Names[continent] = new Dictionary>(); if(!Names[continent].ContainsKey(capital)) Names[continent][capital] = new List(); Names[continent][capital].Add(name); } } [Serializable] public class ContinentData { public string Name; public List Regions; } [Serializable] public class RegionData { public string Name; public List Towns; } public class VoronoiLink { public Vertex2 point1; public Vertex2 point2; public float Length { get { return (point1.ToVector2() - point2.ToVector2()).magnitude; } } public VoronoiLink(Vertex2 p1, Vertex2 p2) { point1 = p1; point2 = p2; } public bool ContainsPoint(Vertex2 p) { return point1 == p || point2 == p; } } public static class VoronoiLinkExtensions { class DijkstraVertex { public double Cost; public double Heuristic; public DijkstraVertex Parent; public Vertex2 Vertex; } public static bool ContainsLine(this IEnumerable data, VoronoiLink l) { return data.Any( line => (line.point1 == l.point1 && line.point2 == l.point2) || (line.point1 == l.point2 && line.point2 == l.point1)); } public static IEnumerable FindPath(this IEnumerable data, Vertex2 source, Vertex2 target, bool bestFirst = true) { List members = new List{new DijkstraVertex{Vertex = source}}; List searched = new List(); while (true) { var v = members.Where(m=>!searched.Contains(m.Vertex)).OrderBy(m=>bestFirst?m.Heuristic:m.Cost).FirstOrDefault(); if (v == null) return null; // No vertices left unsearched if (v.Vertex == target) { Stack path = new Stack(); path.Push(v); while(path.Peek().Parent!=null) path.Push(path.Peek().Parent); return path.Select(dv => dv.Vertex).ToList(); } members.AddRange(data.Where(l => l.point1 == v.Vertex).Select(l => l.point2).Concat(data.Where(l => l.point2 == v.Vertex).Select(l => l.point1)) .Where(n => members.All(m => m.Vertex != n)).Select(n => { var c = new DijkstraVertex {Parent = v, Vertex = n, Cost = v.Cost + v.Vertex.Distance(n)}; if (bestFirst) c.Heuristic = c.Cost + c.Vertex.Distance(target); return c; })); searched.Add(v.Vertex); } } public static IEnumerable ConnectedRegion(this IEnumerable data, Vertex2 v) { List members = new List(); members.Add(v); while (true) { int lastCount = members.Count; // For each member, add all vertices that are connected to it via a line but are not already a member foreach (Vertex2 m in members.ToArray()) members.AddRange(data.Where(l => l.point1 == m).Select(l => l.point2) .Concat(data.Where(l => l.point2 == m).Select(l => l.point1)) .Where(n => !members.Contains(n))); // If we have stopped finding neighbors, stop traversing if (members.Count == lastCount) return members; } } }