Factory Changes
Factory now has the ability to persist its state Added exponents to control speed/quality tradeoff and quality randomness
This commit is contained in:
parent
967b146d87
commit
8986575c59
14 changed files with 173 additions and 273 deletions
|
|
@ -1752,7 +1752,6 @@
|
|||
</e>
|
||||
<e p="UnityExtensions.cs" t="Include" />
|
||||
<e p="UnityFormatters.cs" t="Include" />
|
||||
<e p="ZoneGeneratorTest.cs" t="Include" />
|
||||
</e>
|
||||
<e p="Shaders" t="Include">
|
||||
<e p="AdditiveUI.shader" t="Include" />
|
||||
|
|
|
|||
|
|
@ -918,7 +918,6 @@
|
|||
<Compile Include="Assets\Scripts\UI\ZoneShip.cs" />
|
||||
<Compile Include="Assets\Scripts\UnityExtensions.cs" />
|
||||
<Compile Include="Assets\Scripts\UnityFormatters.cs" />
|
||||
<Compile Include="Assets\Scripts\ZoneGeneratorTest.cs" />
|
||||
<None Include="Assets\Shaders\AdditiveUI.shader" />
|
||||
<None Include="Assets\Shaders\Background.shader" />
|
||||
<None Include="Assets\TextMesh Pro\Resources\Shaders\TMP_SDF-Surface-Mobile.shader" />
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ public interface IAnalogBehavior : IBehavior
|
|||
|
||||
public interface IPersistentBehavior//<T> where T : IBehavior
|
||||
{
|
||||
object Store();
|
||||
IBehavior Restore(GameContext context, Entity entity, Gear item, Guid data);
|
||||
PersistentBehaviorData Store();
|
||||
void Restore(PersistentBehaviorData data);
|
||||
}
|
||||
[Union(0, typeof(FactoryPersistence)),
|
||||
JsonConverter(typeof(JsonKnownTypesConverter<PersistentBehaviorData>))]
|
||||
public class PersistentBehaviorData { }
|
||||
|
||||
[InspectableField,
|
||||
Union(0, typeof(ProjectileWeaponData)),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using MessagePack;
|
|||
using Newtonsoft.Json;
|
||||
using Unity.Mathematics;
|
||||
using static Unity.Mathematics.math;
|
||||
using Random = Unity.Mathematics.Random;
|
||||
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class FactoryData : IBehaviorData
|
||||
|
|
@ -31,12 +32,13 @@ public class Factory : IBehavior, IPersistentBehavior//<Factory>
|
|||
|
||||
private FactoryData _data;
|
||||
|
||||
private BlueprintData _blueprint;
|
||||
private Guid _blueprint;
|
||||
private float _remainingManHours;
|
||||
private bool _retooling;
|
||||
private bool _producing;
|
||||
private List<ItemInstance> _reservedStock = new List<ItemInstance>();
|
||||
private int _assignedPopulation;
|
||||
private int _productionQuality;
|
||||
private float _productionQuality;
|
||||
|
||||
public Factory(GameContext context, FactoryData data, Entity entity, Gear item)
|
||||
{
|
||||
|
|
@ -52,58 +54,91 @@ public class Factory : IBehavior, IPersistentBehavior//<Factory>
|
|||
|
||||
public void Update(float delta)
|
||||
{
|
||||
if (_retooling || _reservedStock.Count > 0)
|
||||
var blueprint = Context.Cache.Get<BlueprintData>(_blueprint);
|
||||
if (_retooling || _producing)
|
||||
{
|
||||
_remainingManHours -= delta / 3600 * (_assignedPopulation + _data.AutomationPoints) / _productionQuality;
|
||||
_remainingManHours -= delta / 3600 * (_assignedPopulation + _data.AutomationPoints) /
|
||||
pow(lerp(blueprint.QualityFloor, 1, saturate(_productionQuality)),
|
||||
blueprint.ProductionExponent);
|
||||
}
|
||||
|
||||
if (_remainingManHours < 0)
|
||||
{
|
||||
if (_retooling)
|
||||
_retooling = false;
|
||||
else if (_reservedStock.Count > 0)
|
||||
else if (_producing)
|
||||
{
|
||||
var blueprintItemData = Context.Cache.Get(_blueprint.Item);
|
||||
var blueprintItemData = Context.Cache.Get(blueprint.Item);
|
||||
|
||||
for (int i = 0; i < _blueprint.Quantity; i++)
|
||||
if (blueprintItemData is CraftedItemData)
|
||||
{
|
||||
CraftedItemInstance newItem;
|
||||
|
||||
if (blueprintItemData is EquippableItemData equippableItemData)
|
||||
for (int i = 0; i < blueprint.Quantity; i++)
|
||||
{
|
||||
var newGear = new Gear
|
||||
CraftedItemInstance newItem;
|
||||
|
||||
// Applying exponents to two random numbers and adding them produces a range of interesting probability distributions
|
||||
var quality = blueprint.Quality *
|
||||
pow(_productionQuality, blueprint.QualityExponent) *
|
||||
(pow(Context.Random.NextFloat(), blueprint.RandomExponent) +
|
||||
pow(Context.Random.NextFloat(), blueprint.RandomExponent)) / 2;
|
||||
|
||||
if (blueprintItemData is EquippableItemData equippableItemData)
|
||||
{
|
||||
var newGear = new Gear
|
||||
{
|
||||
Context = Context,
|
||||
Data = blueprintItemData.ID,
|
||||
ID = Guid.NewGuid(),
|
||||
Ingredients = _reservedStock.Select(ii=>ii.ID).ToList(),
|
||||
Quality = quality,
|
||||
Blueprint = _blueprint
|
||||
};
|
||||
newGear.Durability = Context.Evaluate(equippableItemData.Durability, newGear);
|
||||
newItem = newGear;
|
||||
}
|
||||
else
|
||||
{
|
||||
newItem = new CompoundCommodity
|
||||
{
|
||||
Context = Context,
|
||||
Data = blueprintItemData.ID,
|
||||
ID = Guid.NewGuid(),
|
||||
Ingredients = _reservedStock.Select(ii=>ii.ID).ToList(),
|
||||
Quality = quality,
|
||||
Blueprint = _blueprint
|
||||
};
|
||||
}
|
||||
Context.Cache.Add(newItem);
|
||||
Entity.Cargo.Add(newItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var simpleCommodityData = blueprintItemData as SimpleCommodityData;
|
||||
var simpleCommodityInstance = Entity.Cargo.FirstOrDefault(i => i.Data == simpleCommodityData.ID) as SimpleCommodity;
|
||||
if (simpleCommodityInstance == null)
|
||||
{
|
||||
simpleCommodityInstance = new SimpleCommodity
|
||||
{
|
||||
Context = Context,
|
||||
Data = blueprintItemData.ID,
|
||||
Data = simpleCommodityData.ID,
|
||||
ID = Guid.NewGuid(),
|
||||
Ingredients = _reservedStock.Select(ii=>ii.ID).ToList(),
|
||||
Quality = _productionQuality * _blueprint.Quality,
|
||||
Blueprint = _blueprint
|
||||
Quantity = blueprint.Quantity
|
||||
};
|
||||
newGear.Durability = Context.Evaluate(equippableItemData.Durability, newGear);
|
||||
newItem = newGear;
|
||||
Context.Cache.Add(simpleCommodityInstance);
|
||||
Entity.Cargo.Add(simpleCommodityInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
newItem = new CompoundCommodity
|
||||
{
|
||||
Context = Context,
|
||||
Data = blueprintItemData.ID,
|
||||
ID = Guid.NewGuid(),
|
||||
Ingredients = _reservedStock.Select(ii=>ii.ID).ToList(),
|
||||
Quality = _productionQuality * _blueprint.Quality,
|
||||
Blueprint = _blueprint
|
||||
};
|
||||
simpleCommodityInstance.Quantity += blueprint.Quantity;
|
||||
}
|
||||
Context.Cache.Add(newItem);
|
||||
Entity.Cargo.Add(newItem);
|
||||
}
|
||||
Entity.RecalculateMass();
|
||||
|
||||
var simpleIngredients = new List<SimpleCommodity>();
|
||||
var compoundIngredients = new List<CompoundCommodity>();
|
||||
var hasAllIngredients = true;
|
||||
foreach (var kvp in _blueprint.Ingredients)
|
||||
foreach (var kvp in blueprint.Ingredients)
|
||||
{
|
||||
var itemData = Context.Cache.Get(kvp.Key);
|
||||
if (itemData is SimpleCommodityData)
|
||||
|
|
@ -137,7 +172,7 @@ public class Factory : IBehavior, IPersistentBehavior//<Factory>
|
|||
}
|
||||
foreach (var simpleCommodity in simpleIngredients)
|
||||
{
|
||||
var blueprintQuantity = _blueprint.Ingredients
|
||||
var blueprintQuantity = blueprint.Ingredients
|
||||
.First(ingredient => ingredient.Key == simpleCommodity.ID).Value;
|
||||
if (simpleCommodity.Quantity == blueprintQuantity)
|
||||
{
|
||||
|
|
@ -157,35 +192,49 @@ public class Factory : IBehavior, IPersistentBehavior//<Factory>
|
|||
_reservedStock.Add(newSimpleCommodity);
|
||||
}
|
||||
}
|
||||
Entity.RecalculateMass();
|
||||
_remainingManHours = blueprint.ProductionTime;
|
||||
_producing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object Store()
|
||||
public PersistentBehaviorData Store()
|
||||
{
|
||||
return new FactoryPersistence
|
||||
{
|
||||
Blueprint = _blueprint.ID,
|
||||
Blueprint = _blueprint,
|
||||
RemainingManHours = _remainingManHours,
|
||||
Retooling = _retooling,
|
||||
ReservedStock = _reservedStock.Select(i=>i.ID).ToArray()
|
||||
Producing = _producing,
|
||||
ReservedStock = _reservedStock.Select(i=>i.ID).ToArray(),
|
||||
AssignedPopulation = _assignedPopulation,
|
||||
ProductionQuality = _productionQuality
|
||||
};
|
||||
}
|
||||
|
||||
public IBehavior Restore(GameContext context, Entity entity, Gear item, Guid data)
|
||||
public void Restore(PersistentBehaviorData data)
|
||||
{
|
||||
var equippable = item.ItemData;
|
||||
var behaviorData = (FactoryData) equippable.Behaviors.First(b => b is FactoryData);
|
||||
return new Factory(context, behaviorData, entity, item);
|
||||
var factoryPersistence = data as FactoryPersistence;
|
||||
_blueprint = factoryPersistence.Blueprint;
|
||||
_remainingManHours = factoryPersistence.RemainingManHours;
|
||||
_retooling = factoryPersistence.Retooling;
|
||||
_producing = factoryPersistence.Producing;
|
||||
_reservedStock = factoryPersistence.ReservedStock.Select(id => Context.Cache.Get<ItemInstance>(id)).ToList();
|
||||
_assignedPopulation = factoryPersistence.AssignedPopulation;
|
||||
_productionQuality = factoryPersistence.ProductionQuality;
|
||||
}
|
||||
}
|
||||
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class FactoryPersistence
|
||||
public class FactoryPersistence : PersistentBehaviorData
|
||||
{
|
||||
public Guid Blueprint;
|
||||
public float RemainingManHours;
|
||||
public bool Retooling;
|
||||
public bool Producing;
|
||||
public Guid[] ReservedStock;
|
||||
public int AssignedPopulation;
|
||||
public float ProductionQuality;
|
||||
}
|
||||
|
|
@ -27,16 +27,28 @@ public class BlueprintData : DatabaseEntry, INamedEntry
|
|||
[InspectableField, JsonProperty("quality"), Key(6)]
|
||||
public float Quality;
|
||||
|
||||
[InspectableField, JsonProperty("statEffects"), Key(7)]
|
||||
[InspectableField, JsonProperty("qualityExponent"), Key(7)]
|
||||
public float QualityExponent = 1;
|
||||
|
||||
[InspectableField, JsonProperty("productionExponent"), Key(8)]
|
||||
public float ProductionExponent = 1;
|
||||
|
||||
[InspectableField, JsonProperty("randomExponent"), Key(9)]
|
||||
public float RandomExponent = 1;
|
||||
|
||||
[InspectableField, JsonProperty("qualityFloor"), Key(10)]
|
||||
public float QualityFloor = .25f;
|
||||
|
||||
[InspectableField, JsonProperty("statEffects"), Key(11)]
|
||||
public List<BlueprintStatEffect> StatEffects = new List<BlueprintStatEffect>();
|
||||
|
||||
[InspectableField, JsonProperty("researchTime"), Key(8)]
|
||||
[InspectableField, JsonProperty("researchTime"), Key(12)]
|
||||
public float ResearchTime;
|
||||
|
||||
[InspectableDatabaseLink(typeof(BlueprintData)), JsonProperty("researchDependencies"), Key(9)]
|
||||
[InspectableDatabaseLink(typeof(BlueprintData)), JsonProperty("researchDependencies"), Key(13)]
|
||||
public List<Guid> Dependencies = new List<Guid>();
|
||||
|
||||
[InspectableDatabaseLink(typeof(CraftedItemData)), JsonProperty("factoryItem"), Key(11)]
|
||||
[InspectableDatabaseLink(typeof(CraftedItemData)), JsonProperty("factoryItem"), Key(14)]
|
||||
public Guid FactoryItem;
|
||||
|
||||
[IgnoreMember] public string EntryName
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class Entity : DatabaseEntry, IMessagePackSerializationCallbackReceiver
|
|||
[IgnoreMember] public float SpecificHeat { get; private set; }
|
||||
[IgnoreMember] public float Visibility => VisibilitySources.Values.Sum();
|
||||
|
||||
public object[] PersistedBehaviors;
|
||||
public PersistentBehaviorData[] PersistedBehaviors;
|
||||
|
||||
public Entity(GameContext context, Gear hull, IEnumerable<Gear> items, IEnumerable<ItemInstance> cargo)
|
||||
{
|
||||
|
|
@ -129,6 +129,14 @@ public class Entity : DatabaseEntry, IMessagePackSerializationCallbackReceiver
|
|||
.SelectMany(i => i.ItemData.Behaviors
|
||||
.Select(bd => bd.CreateInstance(Context, this, i)))
|
||||
.OrderBy(b => b.GetType().GetCustomAttribute<UpdateOrderAttribute>()?.Order ?? 0).ToList();
|
||||
|
||||
var index = 0;
|
||||
foreach (var persistentBehavior in Behaviors
|
||||
.Where(b => b is IPersistentBehavior)
|
||||
.Cast<IPersistentBehavior>())
|
||||
{
|
||||
persistentBehavior.Restore(PersistedBehaviors[index++]);
|
||||
}
|
||||
|
||||
Bindings = Behaviors
|
||||
.Where(b => b is IActivatedBehavior)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Unity.Mathematics.math;
|
||||
using Random = Unity.Mathematics.Random;
|
||||
|
||||
public class GameContext
|
||||
{
|
||||
public Random Random = new Random((uint) (DateTime.Now.Ticks%uint.MaxValue));
|
||||
public List<AgentController> Agents = new List<AgentController>();
|
||||
public Dictionary<ZoneData, Dictionary<DatabaseEntry, Entity>> ZoneContents = new Dictionary<ZoneData, Dictionary<DatabaseEntry, Entity>>();
|
||||
public Dictionary<string, GalaxyMapLayerData> MapLayers = new Dictionary<string, GalaxyMapLayerData>();
|
||||
|
|
@ -197,8 +199,8 @@ public class GameContext
|
|||
// Determine quality of either the item itself or the specific ingredient this stat depends on
|
||||
public float Quality(PerformanceStat stat, Gear item)
|
||||
{
|
||||
|
||||
var activeEffects = item.Blueprint.StatEffects.Where(x => GetAffectedStat(item.Blueprint, x) == stat).ToArray();
|
||||
var blueprint = Cache.Get<BlueprintData>(item.Blueprint);
|
||||
var activeEffects = blueprint.StatEffects.Where(x => GetAffectedStat(blueprint, x) == stat).ToArray();
|
||||
float quality;
|
||||
if (!activeEffects.Any())
|
||||
quality = item.CompoundQuality();
|
||||
|
|
@ -309,7 +311,7 @@ public class GameContext
|
|||
ID = Guid.NewGuid(),
|
||||
Ingredients = ingredients.Select(i=>i.ID).ToList(),
|
||||
Quality = quality,
|
||||
Blueprint = blueprint
|
||||
Blueprint = blueprint.ID
|
||||
};
|
||||
newGear.Durability = Evaluate(equippableItemData.Durability, newGear);
|
||||
Cache.Add(newGear);
|
||||
|
|
@ -323,7 +325,7 @@ public class GameContext
|
|||
ID = Guid.NewGuid(),
|
||||
Ingredients = ingredients.Select(i=>i.ID).ToList(),
|
||||
Quality = quality,
|
||||
Blueprint = blueprint
|
||||
Blueprint = blueprint.ID
|
||||
};
|
||||
Cache.Add(newCommodity);
|
||||
return newCommodity;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public abstract class CraftedItemInstance : ItemInstance
|
|||
|
||||
[JsonProperty("ingredients"), Key(3)] public List<Guid> Ingredients = new List<Guid>();
|
||||
|
||||
[JsonProperty("blueprint"), Key(4)] public BlueprintData Blueprint;
|
||||
[JsonProperty("blueprint"), Key(4)] public Guid Blueprint;
|
||||
}
|
||||
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ using static Unity.Mathematics.math;
|
|||
[MessagePackObject]
|
||||
public class Ship : Entity
|
||||
{
|
||||
[Key("hull")]
|
||||
public Gear Hull;
|
||||
[Key("hardpoints")]
|
||||
public List<Hardpoint> Hardpoints = new List<Hardpoint>();
|
||||
|
||||
|
|
|
|||
|
|
@ -12,29 +12,29 @@ public class ZoneGenerator
|
|||
{
|
||||
|
||||
public static void GenerateZone(
|
||||
GlobalData global,
|
||||
GameContext context,
|
||||
ZoneData zone,
|
||||
IEnumerable<GalaxyMapLayerData> mapLayers,
|
||||
IEnumerable<SimpleCommodityData> resources,
|
||||
out OrbitData[] orbitData,
|
||||
out PlanetData[] planetsData)
|
||||
{
|
||||
var zoneRadius = lerp(global.MinimumZoneRadius,
|
||||
global.MaximumZoneRadius,
|
||||
var zoneRadius = lerp(context.GlobalData.MinimumZoneRadius,
|
||||
context.GlobalData.MaximumZoneRadius,
|
||||
mapLayers.First(m => m.Name == "Radius")
|
||||
.Evaluate(zone.Position,
|
||||
global));
|
||||
context.GlobalData));
|
||||
zone.Radius = zoneRadius;
|
||||
|
||||
var zoneMass = lerp(global.MinimumZoneMass,
|
||||
global.MaximumZoneMass,
|
||||
var zoneMass = lerp(context.GlobalData.MinimumZoneMass,
|
||||
context.GlobalData.MaximumZoneMass,
|
||||
mapLayers.First(m => m.Name == "Mass")
|
||||
.Evaluate(zone.Position,
|
||||
global));
|
||||
context.GlobalData));
|
||||
|
||||
//Debug.Log($"Generating zone at position {zone.Position} with radius {zoneRadius} and mass {zoneMass}");
|
||||
|
||||
var planets = GenerateEntities(global, zone, zoneMass, zoneRadius);
|
||||
var planets = GenerateEntities(context, zone, zoneMass, zoneRadius);
|
||||
|
||||
// Create collections to map between zone generator output and database entries
|
||||
var orbitMap = new Dictionary<Planet, OrbitData>();
|
||||
|
|
@ -63,9 +63,7 @@ public class ZoneGenerator
|
|||
: Guid.Empty;
|
||||
|
||||
// Cache resource densities
|
||||
var resourceMaps = mapLayers.ToDictionary(m => m.ID, m => m.Evaluate(zone.Position, global));
|
||||
|
||||
var random = new Random((uint) (DateTime.Now.Ticks%uint.MaxValue));
|
||||
var resourceMaps = mapLayers.ToDictionary(m => m.ID, m => m.Evaluate(zone.Position, context.GlobalData));
|
||||
|
||||
planetsData = planets.Where(p=>!p.Empty).Select(planet =>
|
||||
{
|
||||
|
|
@ -80,15 +78,15 @@ public class ZoneGenerator
|
|||
// Filter resources where the body type matches the current planet
|
||||
.Where(r =>
|
||||
((planet.Belt ? BodyType.Asteroid :
|
||||
planet.Mass > global.SunMass ? BodyType.Sun :
|
||||
planet.Mass > global.GasGiantMass ? BodyType.GasGiant :
|
||||
planet.Mass > global.PlanetMass ? BodyType.Planet : BodyType.Planetoid) & r.ResourceBodyType) != 0)
|
||||
planet.Mass > context.GlobalData.SunMass ? BodyType.Sun :
|
||||
planet.Mass > context.GlobalData.GasGiantMass ? BodyType.GasGiant :
|
||||
planet.Mass > context.GlobalData.PlanetMass ? BodyType.Planet : BodyType.Planetoid) & r.ResourceBodyType) != 0)
|
||||
// NextUnbounded is our extension function containing resource placement formula
|
||||
// Obtain bias variable by multiplying all resource maps together
|
||||
.Select(r => new
|
||||
{
|
||||
resource = r,
|
||||
quantity = random.NextUnbounded(
|
||||
quantity = context.Random.NextUnbounded(
|
||||
r.ResourceDensity.Aggregate(1f, (m, rdm) => m * resourceMaps[rdm]),
|
||||
r.ResourceDensityBiasPower)
|
||||
})
|
||||
|
|
@ -104,23 +102,22 @@ public class ZoneGenerator
|
|||
zone.Orbits = orbitData.Select(od => od.ID).ToList();
|
||||
}
|
||||
|
||||
public static Planet[] GenerateEntities(GlobalData global, ZoneData data, float mass, float radius)
|
||||
public static Planet[] GenerateEntities(GameContext context, ZoneData data, float mass, float radius)
|
||||
{
|
||||
var random = new Random();
|
||||
random.InitState(unchecked((uint)data.Name.GetHashCode()));
|
||||
|
||||
var root = new Planet
|
||||
{
|
||||
Random = random,
|
||||
Global = global,
|
||||
Context = context,
|
||||
Mass = mass,
|
||||
ChildDistanceMaximum = radius * .75f,
|
||||
ChildDistanceMinimum = global.PlanetRadius(mass)
|
||||
ChildDistanceMinimum = context.GlobalData.PlanetRadius(mass)
|
||||
};
|
||||
|
||||
// There is some chance of generating a rosette or binary system
|
||||
// Probabilities which are fixed for the entire galaxy are in GlobalData, contained in the GameContext
|
||||
var rosette = random.NextFloat() < global.RosetteProbability;
|
||||
var rosette = random.NextFloat() < context.GlobalData.RosetteProbability;
|
||||
|
||||
if (rosette)
|
||||
{
|
||||
|
|
@ -138,7 +135,7 @@ public class ZoneGenerator
|
|||
massFraction: .1f);
|
||||
|
||||
var averageChildMass = root.Children.Sum(p => p.Mass) / root.Children.Count;
|
||||
foreach(var p in root.Children.Where(c=>c.Mass>global.GasGiantMass))
|
||||
foreach(var p in root.Children.Where(c=>c.Mass>context.GlobalData.GasGiantMass))
|
||||
{
|
||||
var m = p.Mass / averageChildMass;
|
||||
// Give each child in the rosette its own mini solar system
|
||||
|
|
@ -169,24 +166,24 @@ public class ZoneGenerator
|
|||
|
||||
// Get all children that are above the satellite creation mass floor and not rosette members
|
||||
var satelliteCandidates = rosette
|
||||
? root.AllPlanets().Where(p => p != root && p.Parent != root && p.Mass > global.SatelliteCreationMassFloor)
|
||||
: root.AllPlanets().Where(p => p != root && p.Mass > global.SatelliteCreationMassFloor);
|
||||
? root.AllPlanets().Where(p => p != root && p.Parent != root && p.Mass > context.GlobalData.SatelliteCreationMassFloor)
|
||||
: root.AllPlanets().Where(p => p != root && p.Mass > context.GlobalData.SatelliteCreationMassFloor);
|
||||
|
||||
var binaries = new List<Planet>();
|
||||
foreach (var planet in satelliteCandidates)
|
||||
{
|
||||
// There's a chance of generating satellites for each qualified planet
|
||||
if (random.NextFloat() < global.SatelliteCreationProbability)
|
||||
if (random.NextFloat() < context.GlobalData.SatelliteCreationProbability)
|
||||
{
|
||||
// Sometimes the satellite is so massive that it forms a binary system (like Earth!)
|
||||
if (random.NextFloat() < global.BinaryCreationProbability)
|
||||
if (random.NextFloat() < context.GlobalData.BinaryCreationProbability)
|
||||
{
|
||||
planet.ExpandRosette(2);
|
||||
binaries.AddRange(planet.Children);
|
||||
}
|
||||
// Otherwise, terrestrial planets get a couple satellites while gas giants get many
|
||||
else planet.ExpandSolar(
|
||||
count: planet.Mass < global.GasGiantMass ? random.NextInt(1,3) : random.NextInt(4,10),
|
||||
count: planet.Mass < context.GlobalData.GasGiantMass ? random.NextInt(1,3) : random.NextInt(4,10),
|
||||
massMulMin: .75f,
|
||||
massMulMax: 1.5f,
|
||||
distMulMin: 1.05f,
|
||||
|
|
@ -198,10 +195,10 @@ public class ZoneGenerator
|
|||
|
||||
// Get all children that are below the belt creation mass floor and not rosette members, also exclude binaries
|
||||
var beltCandidates = rosette
|
||||
? root.AllPlanets().Where(p => p != root && p.Parent != root && p.Mass < global.BeltMassCeiling && !binaries.Contains(p))
|
||||
: root.AllPlanets().Where(p => p != root && p.Mass < global.BeltMassCeiling && !binaries.Contains(p));
|
||||
? root.AllPlanets().Where(p => p != root && p.Parent != root && p.Mass < context.GlobalData.BeltMassCeiling && !binaries.Contains(p))
|
||||
: root.AllPlanets().Where(p => p != root && p.Mass < context.GlobalData.BeltMassCeiling && !binaries.Contains(p));
|
||||
foreach(var planet in beltCandidates.Reverse())
|
||||
if (random.NextFloat() < global.BeltProbability && !planet.Parent.Children.Any(p=>p.Belt))
|
||||
if (random.NextFloat() < context.GlobalData.BeltProbability && !planet.Parent.Children.Any(p=>p.Belt))
|
||||
planet.Belt = true;
|
||||
|
||||
return root.AllPlanets().ToArray();
|
||||
|
|
@ -213,8 +210,7 @@ public class ZoneGenerator
|
|||
// Because they were objects we observed to move in the sky instead of remaining fixed like the stars
|
||||
public class Planet
|
||||
{
|
||||
public Random Random;
|
||||
public GlobalData Global;
|
||||
public GameContext Context;
|
||||
public float Distance;
|
||||
public float Phase;
|
||||
public float Mass;
|
||||
|
|
@ -245,7 +241,7 @@ public class Planet
|
|||
var sharedMass = Mass / vertices * 2;
|
||||
|
||||
// Proportion of the masses of sequential pairs is random, but only for even vertex counts
|
||||
var proportion = vertices % 2 == 0 ? Random.NextFloat(.5f,.95f) : .5f;
|
||||
var proportion = vertices % 2 == 0 ? Context.Random.NextFloat(.5f,.95f) : .5f;
|
||||
|
||||
// Place children at a fixed distance in the center of the range
|
||||
var dist = (ChildDistanceMinimum + ChildDistanceMaximum) / 2;
|
||||
|
|
@ -257,23 +253,22 @@ public class Planet
|
|||
var p1 = OrbitData.Evaluate(1.0f / vertices) * dist;
|
||||
|
||||
// Maximum child distance is half the distance to the neighbor minus the neighbor's radius
|
||||
var p0ChildDist = (distance(p0, p1) * proportion - Global.PlanetRadius(sharedMass * (1 - proportion))) * .75f;
|
||||
var p1ChildDist = (distance(p0, p1) * (1 - proportion) - Global.PlanetRadius(sharedMass * proportion)) * .75f;
|
||||
var p0ChildDist = (distance(p0, p1) * proportion - Context.GlobalData.PlanetRadius(sharedMass * (1 - proportion))) * .75f;
|
||||
var p1ChildDist = (distance(p0, p1) * (1 - proportion) - Context.GlobalData.PlanetRadius(sharedMass * proportion)) * .75f;
|
||||
|
||||
for (int i = 0; i < vertices; i++)
|
||||
{
|
||||
var child = new Planet
|
||||
{
|
||||
Random = Random,
|
||||
Global = Global,
|
||||
Context = Context,
|
||||
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 = Global.PlanetRadius(child.Mass) * 2;
|
||||
child.Period = pow(child.Distance, Global.OrbitPeriodExponent) * Global.OrbitPeriodMultiplier;
|
||||
child.ChildDistanceMinimum = Context.GlobalData.PlanetRadius(child.Mass) * 2;
|
||||
child.Period = pow(child.Distance, Context.GlobalData.OrbitPeriodExponent) * Context.GlobalData.OrbitPeriodMultiplier;
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
|
|
@ -300,13 +295,13 @@ public class Planet
|
|||
// jupiterJump is an additional mass multiplier applied after half of the planets
|
||||
for (var i = 1; i < count; i++)
|
||||
{
|
||||
massTotal += masses[i] = masses[i - 1] * Random.NextFloat(massMulMin, massMulMax) * (count/2==i ? jupiterJump : 1);
|
||||
distances[i] = distances[i - 1] * Random.NextFloat(distMulMin, distMulMax);
|
||||
massTotal += masses[i] = masses[i - 1] * Context.Random.NextFloat(massMulMin, massMulMax) * (count/2==i ? jupiterJump : 1);
|
||||
distances[i] = distances[i - 1] * Context.Random.NextFloat(distMulMin, distMulMax);
|
||||
}
|
||||
|
||||
// Add some randomness to child masses
|
||||
for (var i = 0; i < masses.Length; i++)
|
||||
masses[i] *= Random.NextFloat(.1f, 1f);
|
||||
masses[i] *= Context.Random.NextFloat(.1f, 1f);
|
||||
|
||||
// Normalize the masses of the children
|
||||
for (var i = 0; i < count; i++)
|
||||
|
|
@ -318,25 +313,24 @@ public class Planet
|
|||
var oldDistances = (float[]) distances.Clone();
|
||||
for (var i = 0; i < count; i++)
|
||||
distances[i] = lerp(ChildDistanceMinimum, ChildDistanceMaximum,
|
||||
(oldDistances[i] - oldDistances[0]) / (oldDistances[count - 1] - oldDistances[0])) + Global.PlanetRadius(masses[i]);
|
||||
(oldDistances[i] - oldDistances[0]) / (oldDistances[count - 1] - oldDistances[0])) + Context.GlobalData.PlanetRadius(masses[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
// Only instantiate children above the mass floor
|
||||
if (masses[i] > Global.MassFloor)
|
||||
if (masses[i] > Context.GlobalData.MassFloor)
|
||||
{
|
||||
var child = new Planet
|
||||
{
|
||||
Random = Random,
|
||||
Global = Global,
|
||||
Context = Context,
|
||||
Parent = this,
|
||||
Mass = masses[i],
|
||||
Distance = distances[i],
|
||||
Phase = Random.NextFloat()
|
||||
Phase = Context.Random.NextFloat()
|
||||
};
|
||||
child.Period = pow(child.Distance, Global.OrbitPeriodExponent) * Global.OrbitPeriodMultiplier;
|
||||
child.ChildDistanceMinimum = Global.PlanetRadius(child.Mass) * 2;
|
||||
child.Period = pow(child.Distance, Context.GlobalData.OrbitPeriodExponent) * Context.GlobalData.OrbitPeriodMultiplier;
|
||||
child.ChildDistanceMinimum = Context.GlobalData.PlanetRadius(child.Mass) * 2;
|
||||
// Maximum child distance of child is the smallest distance to either of its neighbors
|
||||
child.ChildDistanceMaximum = min(i == 0 ? child.Distance - ChildDistanceMinimum : child.Distance - distances[i - 1],
|
||||
i < count - 1 ? distances[i + 1] - child.Distance : float.PositiveInfinity);
|
||||
|
|
|
|||
|
|
@ -422,7 +422,7 @@ public class StrategyGameManager : MonoBehaviour
|
|||
OrbitData[] orbits;
|
||||
PlanetData[] planets;
|
||||
ZoneGenerator.GenerateZone(
|
||||
global: _context.GlobalData,
|
||||
context: _context,
|
||||
zone: zoneData,
|
||||
mapLayers: _context.MapLayers.Values,
|
||||
resources: _cache.GetAll<SimpleCommodityData>().Where(i=>i.ResourceDensity.Any()),
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using static Unity.Mathematics.math;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class ZoneGeneratorTest : MonoBehaviour
|
||||
{
|
||||
public Galaxy Galaxy;
|
||||
public MeshRenderer ZoneBackground;
|
||||
public MeshRenderer ZoneBoundary;
|
||||
public ZoneObject ZoneObjectPrefab;
|
||||
public ParticleSystem BeltPrefab;
|
||||
public Texture2D PlanetSprite;
|
||||
public Texture2D GasGiantSprite;
|
||||
public Texture2D SunSprite;
|
||||
public float ZoneSizeScale = .01f;
|
||||
public float ZoneMassScale = .5f;
|
||||
public float ZoneMassPower = .33f;
|
||||
public float ZoneOrbitSpeed = 10;
|
||||
|
||||
private DatabaseCache _cache;
|
||||
private Dictionary<Guid, ZoneObject> _zoneObjects = new Dictionary<Guid, ZoneObject>();
|
||||
private Dictionary<Guid, ParticleSystem> _zoneBelts = new Dictionary<Guid, ParticleSystem>();
|
||||
private List<ZoneObject> _wormholes = new List<ZoneObject>();
|
||||
private Dictionary<Guid, float2> _orbitPositions = new Dictionary<Guid, float2>();
|
||||
private GameContext _context;
|
||||
private Material _boundaryMaterial;
|
||||
|
||||
// Determine orbital position recursively, caching parent positions to avoid repeated calculations
|
||||
private float2 GetOrbitPosition(Guid orbit)
|
||||
{
|
||||
// Root orbit is fixed at center
|
||||
if(orbit==Guid.Empty)
|
||||
return Unity.Mathematics.float2.zero;
|
||||
|
||||
if (!_orbitPositions.ContainsKey(orbit))
|
||||
{
|
||||
var orbitData = _cache.Get<OrbitData>(orbit);
|
||||
_orbitPositions[orbit] = GetOrbitPosition(orbitData.Parent) + (orbitData.Period < .01f ? Unity.Mathematics.float2.zero :
|
||||
OrbitData.Evaluate(Time.time * ZoneOrbitSpeed / -orbitData.Period + orbitData.Phase) *
|
||||
orbitData.Distance);
|
||||
}
|
||||
|
||||
var position = _orbitPositions[orbit];
|
||||
if (float.IsNaN(position.x))
|
||||
{
|
||||
Debug.Log("Orbit position is NaN, something went very wrong!");
|
||||
return Unity.Mathematics.float2.zero;
|
||||
}
|
||||
return _orbitPositions[orbit];
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_cache = new DatabaseCache();
|
||||
_context = new GameContext(_cache, Debug.Log);
|
||||
_boundaryMaterial = ZoneBoundary.material;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
_orbitPositions.Clear();
|
||||
foreach (var planet in _zoneObjects)
|
||||
{
|
||||
var planetData = _cache.Get<PlanetData>(planet.Key);
|
||||
|
||||
planet.Value.transform.position = (Vector2) GetOrbitPosition(planetData.Orbit) * ZoneSizeScale;
|
||||
}
|
||||
|
||||
foreach (var belt in _zoneBelts)
|
||||
{
|
||||
var planetData = _cache.Get<PlanetData>(belt.Key);
|
||||
|
||||
belt.Value.transform.position = float3(GetOrbitPosition(_cache.Get<OrbitData>(planetData.Orbit).Parent) * ZoneSizeScale, .15f);
|
||||
}
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
PopulateZone();
|
||||
}
|
||||
}
|
||||
|
||||
void PopulateZone()
|
||||
{
|
||||
// Delete all the existing zone contents
|
||||
foreach (var zoneObject in _zoneObjects.Values) Destroy(zoneObject.gameObject);//zoneObject.GetComponent<Prototype>().ReturnToPool();
|
||||
_zoneObjects.Clear();
|
||||
foreach (var zoneObject in _wormholes) Destroy(zoneObject.gameObject); //zoneObject.GetComponent<Prototype>().ReturnToPool();
|
||||
_wormholes.Clear();
|
||||
foreach (var belt in _zoneBelts.Values) Destroy(belt.gameObject);
|
||||
_zoneBelts.Clear();
|
||||
|
||||
float2 position;
|
||||
do
|
||||
{
|
||||
position = float2(Random.value, Random.value);
|
||||
} while (Galaxy.MapData.StarDensity.Evaluate(position, Galaxy.MapData.GlobalData) < .1f);
|
||||
|
||||
var zone = new ZoneData{Context = _context, Name = Guid.NewGuid().ToString(), Position = position};
|
||||
OrbitData[] orbits;
|
||||
PlanetData[] planets;
|
||||
ZoneGenerator.GenerateZone(
|
||||
global: Galaxy.MapData.GlobalData,
|
||||
zone: zone,
|
||||
mapLayers: Galaxy.MapData.ResourceDensities,
|
||||
resources: Array.Empty<SimpleCommodityData>(),
|
||||
orbitData: out orbits,
|
||||
planetsData: out planets);
|
||||
_cache.AddAll(orbits);
|
||||
_cache.AddAll(planets);
|
||||
_cache.Add(zone);
|
||||
|
||||
float zoneDepth = 0;
|
||||
foreach (var planet in planets)
|
||||
{
|
||||
if (!planet.Belt)
|
||||
{
|
||||
var planetObject = Instantiate(ZoneObjectPrefab, transform);
|
||||
_zoneObjects[planet.ID] = planetObject;
|
||||
planetObject.Label.text = planet.Name;
|
||||
planetObject.GravityMesh.gameObject.SetActive(true);
|
||||
planetObject.GravityMesh.transform.localScale =
|
||||
Vector3.one * (pow(planet.Mass, Galaxy.MapData.GlobalData.GravityRadiusExponent) *
|
||||
Galaxy.MapData.GlobalData.GravityRadiusMultiplier * 2 * ZoneSizeScale);
|
||||
var depth = pow(planet.Mass, ZoneMassPower) * ZoneMassScale;
|
||||
if (depth > zoneDepth)
|
||||
zoneDepth = depth;
|
||||
planetObject.GravityMesh.material.SetFloat("_Depth", depth);
|
||||
planetObject.Icon.material.SetTexture("_MainTex",
|
||||
planet.Mass > Galaxy.MapData.GlobalData.SunMass ? SunSprite :
|
||||
planet.Mass > Galaxy.MapData.GlobalData.GasGiantMass ? GasGiantSprite :
|
||||
PlanetSprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
var beltObject = Instantiate(BeltPrefab, transform);
|
||||
_zoneBelts[planet.ID] = beltObject;
|
||||
var orbit = _cache.Get<OrbitData>(planet.Orbit);
|
||||
var beltEmission = beltObject.emission;
|
||||
beltEmission.rateOverTime = new ParticleSystem.MinMaxCurve(pow(planet.Mass,Galaxy.MapData.GlobalData.BeltMassExponent) / Galaxy.MapData.GlobalData.BeltMassRatio * orbit.Distance);
|
||||
var beltShape = beltObject.shape;
|
||||
beltShape.radius = orbit.Distance * ZoneSizeScale;
|
||||
beltShape.donutRadius = orbit.Distance * ZoneSizeScale / 2;
|
||||
var beltVelocity = beltObject.velocityOverLifetime;
|
||||
beltVelocity.orbitalZ = orbit.Distance * ZoneSizeScale / orbit.Period * ZoneOrbitSpeed;
|
||||
beltObject.Simulate(60);
|
||||
beltObject.Play();
|
||||
}
|
||||
}
|
||||
|
||||
var zoneRadius = (zone.Radius * ZoneSizeScale * 2);
|
||||
ZoneBackground.material.SetFloat("_ClipDistance", zoneRadius);
|
||||
ZoneBackground.material.SetFloat("_HeightRange", zoneDepth + _boundaryMaterial.GetFloat("_Depth"));
|
||||
ZoneBoundary.transform.localScale = Vector3.one * zoneRadius;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0ae6f9307731b8f469a018c69d95d2ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -6,4 +6,10 @@ r.db('Aetheria').table('Galaxy').filter({$type:'OrbitData'}).delete()
|
|||
r.db('Aetheria').table('Galaxy').filter({$type:'PlanetData'}).delete()
|
||||
|
||||
Find Unassigned Items
|
||||
r.db('Aetheria').table('Items').filter({mass:0}).pluck('$type','name')
|
||||
r.db('Aetheria').table('Items').filter({mass:0}).pluck('$type','name')
|
||||
|
||||
Find Unassigned Blueprints
|
||||
r.db('Aetheria').table('Items').filter({researchTime:0}).pluck('name')
|
||||
|
||||
Find Unassigned Stats
|
||||
r.db('Aetheria').table('Items').filter({durabilityStat:{min:0}}).pluck('$type','name')
|
||||
Loading…
Reference in a new issue