Added Console

Debug commands can now be executed using an in-game console
This commit is contained in:
hyperion51 2020-04-28 01:50:50 +01:00
commit 75eb534983
305 changed files with 2975 additions and 17480 deletions

View file

@ -15,14 +15,7 @@ public class AgentController
private Locomotion _locomotion;
private VelocityMatch _velocityMatch;
private Random _random = new Random((uint) (DateTime.Now.Ticks%uint.MaxValue));
private float _stoppingTime;
private float _stoppingDistance;
private float _turnaroundDistance;
private float _turnaroundTime;
private IAnalogBehavior _thrust;
private IAnalogBehavior _turning;
private KeyValuePair<DatabaseEntry, Entity> _objective;
private float _velocityLimit;
public AgentController(GameContext context, ZoneData zone, Entity entity)
{
@ -39,41 +32,31 @@ public class AgentController
};
EntityAgent.CurrentBehavior = _locomotion;
RandomTarget();
_thrust = entity.Axes.Keys.FirstOrDefault(x => x is Thruster);
_turning = entity.Axes.Keys.FirstOrDefault(x => x is Turning);
var velocityLimitData = entity.GetBehaviorData<VelocityLimitData>().FirstOrDefault();
_velocityLimit = Context.Evaluate(velocityLimitData.TopSpeed, entity.Hull, entity);
_stoppingTime = _velocityLimit / Context.Evaluate((_thrust.Data as ThrusterData).Thrust, _thrust.Item, Entity) * Entity.Mass;
_stoppingDistance = _stoppingTime * _velocityLimit / 2;
_turnaroundTime = PI / Context.Evaluate((_turning.Data as TurningData).Torque, _turning.Item, entity) * entity.Mass;
_turnaroundDistance = _turnaroundTime * _velocityLimit;
_locomotion.LeadTime = _stoppingTime;
}
public void Update(float delta)
{
EntityAgent.Update(delta);
var distance = length(_locomotion.Objective.Position - Entity.Position);
Context.Log(
$"Agent Mode: {EntityAgent.CurrentBehavior.GetType()} " +
$"Agent Distance: {distance} " +
$"Turnaround Distance: {_stoppingDistance + _turnaroundDistance} " +
$"Objective: {_objective.Key.ID.ToString().Substring(0, 8)}");
// Context.Log(
// $"Agent Mode: {EntityAgent.CurrentBehavior.GetType()} " +
// $"Agent Distance: {distance} " +
// $"Turnaround Distance: {_stoppingDistance + _turnaroundDistance} " +
// $"Objective: {_objective.Key.ID.ToString().Substring(0, 8)}");
if (EntityAgent.CurrentBehavior == _locomotion)
{
var velocity = length(Entity.Velocity);
_stoppingTime = velocity / Context.Evaluate((_thrust.Data as ThrusterData).Thrust, _thrust.Item, Entity) * Entity.Mass;
_stoppingDistance = _stoppingTime * velocity / 2;
if (distance < _stoppingDistance + _turnaroundDistance)
EntityAgent.CurrentBehavior = _velocityMatch;
_velocityMatch.Objective = _locomotion.Objective;
var matchDistanceTime = _velocityMatch.MatchDistanceTime;
_locomotion.LeadTime = matchDistanceTime.y;
if (distance < matchDistanceTime.x)
EntityAgent.CurrentBehavior = _velocityMatch;
}
}
private void RandomTarget()
{
var entities = Context.ZoneContents[Zone].ToArray();
var entities = Context.ZoneContents[Zone].Where(kvp=>!(kvp.Key is ShipData)).ToArray();
_objective = entities[_random.NextInt(entities.Length)];
_locomotion.Objective = _objective.Value;
}

View file

@ -35,4 +35,22 @@ public class VelocityMatch : AgentBehavior
_entity.Axes[_thrust] = saturate(length(pow(saturate(dot(normalize(_entity.Direction),normalize(deltaV))), 8)*length(deltaV))*ThrustSensitivity);
}
}
public float2 MatchDistanceTime
{
get
{
var velocity = length(_entity.Velocity);
var deltaV = Objective.Velocity - _entity.Velocity;
var stoppingTime = length(deltaV) / _entity.Context.Evaluate((_thrust.Data as ThrusterData).Thrust, _thrust.Item, _entity) * _entity.Mass;
var stoppingDistance = stoppingTime * velocity / 2;
var angleDiff = _entity.Direction.AngleDiff(deltaV);
var turnaroundTime = angleDiff / _entity.Context.Evaluate((_turning.Data as TurningData).Torque, _turning.Item, _entity) * _entity.Mass;
var turnaroundDistance = turnaroundTime * velocity;
return float2(stoppingDistance + turnaroundDistance, stoppingTime + turnaroundTime);
}
}
}

View file

@ -30,6 +30,12 @@ public interface IAnalogBehavior : IBehavior
void SetAxis(float value);
}
public interface IPersistentBehavior//<T> where T : IBehavior
{
object Store();
IBehavior Restore(GameContext context, Entity entity, Gear item, Guid data);
}
[InspectableField,
Union(0, typeof(ProjectileWeaponData)),
Union(1, typeof(LauncherData)),
@ -43,6 +49,7 @@ public interface IAnalogBehavior : IBehavior
Union(9, typeof(TurningData)),
Union(10, typeof(VelocityConversionData)),
Union(11, typeof(VelocityLimitData)),
Union(12, typeof(FactoryData)),
JsonConverter(typeof(JsonKnownTypesConverter<IBehaviorData>)), JsonObject(MemberSerialization.OptIn)]
public interface IBehaviorData
{

View file

@ -0,0 +1,191 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MessagePack;
using Newtonsoft.Json;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class FactoryData : IBehaviorData
{
[InspectableField, JsonProperty("toolingTime"), Key(0)]
public PerformanceStat ToolingTime = new PerformanceStat();
[InspectableField, JsonProperty("automation"), Key(1)]
public int AutomationPoints;
public IBehavior CreateInstance(GameContext context, Entity entity, Gear item)
{
return new Factory(context, this, entity, item);
}
}
public class Factory : IBehavior, IPersistentBehavior//<Factory>
{
public Entity Entity { get; }
public Gear Item { get; }
public GameContext Context { get; }
public IBehaviorData Data => _data;
private FactoryData _data;
private BlueprintData _blueprint;
private float _remainingManHours;
private bool _retooling;
private List<ItemInstance> _reservedStock = new List<ItemInstance>();
private int _assignedPopulation;
private int _productionQuality;
public Factory(GameContext context, FactoryData data, Entity entity, Gear item)
{
Context = context;
_data = data;
Entity = entity;
Item = item;
}
public void Initialize()
{
}
public void Update(float delta)
{
if (_retooling || _reservedStock.Count > 0)
{
_remainingManHours -= delta / 3600 * (_assignedPopulation + _data.AutomationPoints) / _productionQuality;
}
if (_remainingManHours < 0)
{
if (_retooling)
_retooling = false;
else if (_reservedStock.Count > 0)
{
var blueprintItemData = Context.Cache.Get(_blueprint.Item);
for (int i = 0; i < _blueprint.Quantity; i++)
{
CraftedItemInstance newItem;
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 = _productionQuality * _blueprint.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 = _productionQuality * _blueprint.Quality,
Blueprint = _blueprint
};
}
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)
{
var itemData = Context.Cache.Get(kvp.Key);
if (itemData is SimpleCommodityData)
{
var matchingItem = Entity.Cargo.FirstOrDefault(ii =>
{
var simpleCommodity = ii as SimpleCommodity;
if (simpleCommodity == null) return false;
return simpleCommodity.Data == itemData.ID && simpleCommodity.Quantity >= kvp.Value;
}) as SimpleCommodity;
hasAllIngredients = hasAllIngredients && matchingItem != null;
if(matchingItem != null)
simpleIngredients.Add(matchingItem);
}
else
{
var matchingItems =
Entity.Cargo.Where(ii => (ii as CompoundCommodity)?.Data == itemData.ID).Cast<CompoundCommodity>().ToArray();
hasAllIngredients = hasAllIngredients && matchingItems.Length >= kvp.Value;
if(matchingItems.Length >= kvp.Value)
compoundIngredients.AddRange(matchingItems);
}
}
if (hasAllIngredients)
{
_reservedStock.AddRange(compoundIngredients);
foreach (var compoundCommodity in compoundIngredients)
{
Entity.Cargo.Remove(compoundCommodity);
}
foreach (var simpleCommodity in simpleIngredients)
{
var blueprintQuantity = _blueprint.Ingredients
.First(ingredient => ingredient.Key == simpleCommodity.ID).Value;
if (simpleCommodity.Quantity == blueprintQuantity)
{
_reservedStock.Add(simpleCommodity);
Entity.Cargo.Remove(simpleCommodity);
}
else
{
var newSimpleCommodity = new SimpleCommodity
{
Context = Context,
Data = simpleCommodity.Data,
ID = Guid.NewGuid(),
Quantity = blueprintQuantity
};
simpleCommodity.Quantity -= blueprintQuantity;
_reservedStock.Add(newSimpleCommodity);
}
}
}
}
}
}
public object Store()
{
return new FactoryPersistence
{
Blueprint = _blueprint.ID,
RemainingManHours = _remainingManHours,
Retooling = _retooling,
ReservedStock = _reservedStock.Select(i=>i.ID).ToArray()
};
}
public IBehavior Restore(GameContext context, Entity entity, Gear item, Guid data)
{
var equippable = item.ItemData;
var behaviorData = (FactoryData) equippable.Behaviors.First(b => b is FactoryData);
return new Factory(context, behaviorData, entity, item);
}
}
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class FactoryPersistence
{
public Guid Blueprint;
public float RemainingManHours;
public bool Retooling;
public Guid[] ReservedStock;
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f6378c7d7a9be24c9830679aabe55ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -35,6 +35,9 @@ public class BlueprintData : DatabaseEntry, INamedEntry
[InspectableDatabaseLink(typeof(BlueprintData)), JsonProperty("researchDependencies"), Key(9)]
public List<Guid> Dependencies = new List<Guid>();
[InspectableDatabaseLink(typeof(CraftedItemData)), JsonProperty("factoryItem"), Key(11)]
public Guid FactoryItem;
[IgnoreMember] public string EntryName
{

View file

@ -17,11 +17,11 @@ public class Entity
public float2 Velocity;
public Dictionary<Guid, List<IActivatedBehavior>> Bindings;
public Dictionary<IAnalogBehavior, float> Axes;
public readonly GameContext Context;
public readonly List<ItemInstance> Cargo;
public readonly List<Gear> EquippedItems;
protected readonly GameContext Context;
protected readonly List<IBehavior> Behaviors;
protected readonly List<Gear> Items;
protected readonly List<ItemInstance> Cargo;
public Gear Hull { get; }
@ -33,19 +33,13 @@ public class Entity
{
Context = context;
Items = items.ToList();
EquippedItems = items.ToList();
Cargo = cargo.ToList();
Hull = hull;
Mass = hull?.Mass ?? 0 +
Items.Sum(i => i.Mass) +
Cargo.Sum(ii => ii.Mass);
SpecificHeat = (hull?.HeatCapacity ?? 0) +
Items.Sum(i => i.HeatCapacity) +
Cargo.Sum(ii => ii.HeatCapacity);
var activeItems = hull == null ? Items : Items.Append(hull);
RecalculateMass();
var activeItems = Hull == null ? EquippedItems : EquippedItems.Append(Hull);
Behaviors = activeItems
.Where(i=>i.ItemData.Behaviors?.Any()??false)
@ -61,6 +55,22 @@ public class Entity
Axes = Behaviors
.Where(b => b is IAnalogBehavior)
.ToDictionary(b => b as IAnalogBehavior, b => 0f);
foreach (var behavior in Behaviors)
{
behavior.Initialize();
}
}
public void RecalculateMass()
{
Mass = Hull?.Mass ?? 0 +
EquippedItems.Sum(i => i.Mass) +
Cargo.Sum(ii => ii.Mass);
SpecificHeat = (Hull?.HeatCapacity ?? 0) +
EquippedItems.Sum(i => i.HeatCapacity) +
Cargo.Sum(ii => ii.HeatCapacity);
}
public IEnumerable<T> GetBehaviors<T>() where T : class, IBehavior
@ -90,7 +100,7 @@ public class Entity
foreach(var behavior in Behaviors)
behavior.Update(delta);
foreach (var item in Items.Where(item => item.ItemData.Performance(Temperature) < .01f))
foreach (var item in EquippedItems.Where(item => item.ItemData.Performance(Temperature) < .01f))
item.Durability -= delta;
}
}

View file

@ -20,7 +20,8 @@ public enum HardpointType
Tool,
Energy,
Ballistic,
Launcher
Launcher,
Infrastructure
}
public enum DamageType

View file

@ -223,11 +223,11 @@ public static class Extensions
public static float AngleDiff(this float2 a, float2 b)
{
var directionAngle = atan2(b.y, b.x) / PI;
var currentAngle = atan2(a.y, a.x) / PI;
var directionAngle = atan2(b.y, b.x);
var currentAngle = atan2(a.y, a.x);
var d1 = directionAngle - currentAngle;
var d2 = directionAngle - 2 - currentAngle;
var d3 = directionAngle + 2 - currentAngle;
var d2 = directionAngle - 2 * PI - currentAngle;
var d3 = directionAngle + 2 * PI - currentAngle;
return abs(d1) < abs(d2) ? abs(d1) < abs(d3) ? d1 : d3 : abs(d2) < abs(d3) ? d2 : d3;
}
}

View file

@ -204,7 +204,8 @@ public class GameContext
quality = item.CompoundQuality();
else
{
var ingredients = item.Ingredients.Where(i => activeEffects.Any(e => e.Ingredient == i.Data)).ToArray();
var ingredientInstances = item.Ingredients.Select(i => Cache.Get<ItemInstance>(i)).ToArray();
var ingredients = ingredientInstances.Where(i => activeEffects.Any(e => e.Ingredient == i.Data)).ToArray();
if(ingredients.Length != activeEffects.Length)
{
_logger($"Item {item.ID} does not have the ingredients specified by the stat effects of its blueprint!");
@ -297,6 +298,8 @@ public class GameContext
})
.ToList();
Cache.AddAll(ingredients);
if (item is EquippableItemData equippableItemData)
{
var newGear = new Gear
@ -304,22 +307,25 @@ public class GameContext
Context = this,
Data = data,
ID = Guid.NewGuid(),
Ingredients = ingredients,
Ingredients = ingredients.Select(i=>i.ID).ToList(),
Quality = quality,
Blueprint = blueprint
};
newGear.Durability = Evaluate(equippableItemData.Durability, newGear);
Cache.Add(newGear);
return newGear;
}
return new CompoundCommodity
var newCommodity = new CompoundCommodity
{
Context = this,
Data = data,
ID = Guid.NewGuid(),
Ingredients = ingredients,
Ingredients = ingredients.Select(i=>i.ID).ToList(),
Quality = quality,
Blueprint = blueprint
};
Cache.Add(newCommodity);
return newCommodity;
}
}

View file

@ -97,6 +97,9 @@ public class GlobalData : DatabaseEntry
[InspectableField, JsonProperty("planetMass"), Key(28)]
public float PlanetMass = 100f;
[InspectableField, JsonProperty("orbitSpeedMultiplier"), Key(29)]
public float OrbitSpeedMultiplier = .25f;
public float PlanetRadius(float mass)
{

View file

@ -33,7 +33,7 @@ public abstract class CraftedItemInstance : ItemInstance
{
[JsonProperty("quality"), Key(2)] public float Quality;
[JsonProperty("ingredients"), Key(3)] public List<ItemInstance> Ingredients = new List<ItemInstance>();
[JsonProperty("ingredients"), Key(3)] public List<Guid> Ingredients = new List<Guid>();
[JsonProperty("blueprint"), Key(4)] public BlueprintData Blueprint;
}

View file

@ -31,7 +31,7 @@ public class OrbitalEntity : Entity
{
var orbitData = Context.Cache.Get<OrbitData>(orbit);
_orbitPositions[orbit] = GetOrbitPosition(orbitData.Parent) + (orbitData.Period < .01f ? float2.zero :
OrbitData.Evaluate((float) (Context.Time / -orbitData.Period + orbitData.Phase)) *
OrbitData.Evaluate((float) (Context.Time / -orbitData.Period * Context.GlobalData.OrbitSpeedMultiplier + orbitData.Phase)) *
orbitData.Distance);
}