Agent behaviors are no longer hard coded but instead created by equippable control modules Game Context has a job system which assigns appropriate tasks to drone control modules
226 lines
No EOL
9.8 KiB
C#
226 lines
No EOL
9.8 KiB
C#
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using JsonKnownTypes;
|
|
using MessagePack;
|
|
using Newtonsoft.Json;
|
|
using Unity.Mathematics;
|
|
using static Unity.Mathematics.math;
|
|
|
|
[MessagePackObject,
|
|
Union(0, typeof(Ship)),
|
|
Union(1, typeof(OrbitalEntity)),
|
|
JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter<Entity>))]
|
|
public abstract class Entity : DatabaseEntry, IMessagePackSerializationCallbackReceiver
|
|
{
|
|
[JsonProperty("temperature"), Key(1)] public float Temperature;
|
|
[JsonProperty("energy"), Key(2)] public float Energy;
|
|
[JsonProperty("position"), Key(3)] public float2 Position;
|
|
[JsonProperty("direction"), Key(4)] public float2 Direction = float2(0,1);
|
|
[JsonProperty("velocity"), Key(5)] public float2 Velocity;
|
|
[JsonProperty("cargo"), Key(6)] public readonly List<Guid> Cargo; // Type: ItemInstance
|
|
[JsonProperty("equipment"), Key(7)] public readonly List<Guid> EquippedItems; // Type: Gear
|
|
[JsonProperty("parent"), Key(8)] public Guid Parent; // Type: Entity
|
|
[JsonProperty("children"), Key(9)] public List<Guid> Children = new List<Guid>(); // Type: Entity
|
|
[JsonProperty("hull"), Key(10)] public Guid Hull; // Type: Gear
|
|
[JsonProperty("persistedBehaviorData"), Key(11)] public Dictionary<Guid, PersistentBehaviorData[]> PersistedBehaviors;
|
|
[JsonProperty("corporation"), Key(12)] public Guid Corporation;
|
|
|
|
// [IgnoreMember] protected List<IBehavior> Behaviors;
|
|
[IgnoreMember] public Guid Zone;
|
|
[IgnoreMember] public Dictionary<Gear, List<IBehavior>> ItemBehaviors;
|
|
[IgnoreMember] public Dictionary<Gear, List<IActivatedBehavior>> Bindings;
|
|
[IgnoreMember] public Dictionary<IAnalogBehavior, float> Axes;
|
|
[IgnoreMember] public Dictionary<IAnalogBehavior, float> AxisOverrides = new Dictionary<IAnalogBehavior, float>();
|
|
[IgnoreMember] public readonly Dictionary<object, float> VisibilitySources = new Dictionary<object, float>();
|
|
|
|
private Dictionary<Gear, EquippableItemData> GearData;
|
|
|
|
[IgnoreMember] public float Mass { get; private set; }
|
|
[IgnoreMember] public float SpecificHeat { get; private set; }
|
|
[IgnoreMember] public float Visibility => VisibilitySources.Values.Sum();
|
|
|
|
|
|
public Entity(GameContext context, Guid hull, IEnumerable<Guid> items, IEnumerable<Guid> cargo, Guid zone)
|
|
{
|
|
ID = Guid.NewGuid();
|
|
Context = context;
|
|
Zone = zone;
|
|
|
|
EquippedItems = items.ToList();
|
|
Cargo = cargo.ToList();
|
|
Hull = hull;
|
|
|
|
RecalculateMass();
|
|
|
|
AddItemBehaviors();
|
|
}
|
|
|
|
public void AddItemBehaviors()
|
|
{
|
|
var gear = EquippedItems.Append(Hull);
|
|
var gearData = gear.ToDictionary(id => id, id => Context.Cache.Get<Gear>(id));
|
|
GearData = gearData.Values.ToDictionary(g => g, g => g.ItemData);
|
|
|
|
ItemBehaviors = gear
|
|
.Where(i=> gearData[i].ItemData.Behaviors?.Any()??false)
|
|
.ToDictionary(i=> gearData[i], i => gearData[i].ItemData.Behaviors
|
|
.Select(bd => bd.CreateInstance(Context, this, gearData[i]))
|
|
.OrderBy(b => b.GetType().GetCustomAttribute<UpdateOrderAttribute>()?.Order ?? 0)
|
|
.ToList());
|
|
|
|
Bindings = ItemBehaviors
|
|
.Where(x => x.Value.Any(g => g is IActivatedBehavior))
|
|
.Select(x=> new {gear = x.Key, behaviors = x.Value
|
|
.Where(g => g is IActivatedBehavior)
|
|
.Cast<IActivatedBehavior>().ToList()})
|
|
.ToDictionary(x => x.gear, x => x.behaviors);
|
|
|
|
Axes = ItemBehaviors.Values
|
|
.SelectMany(behaviors=>behaviors)
|
|
.Where(b => b is IAnalogBehavior)
|
|
.ToDictionary(b => b as IAnalogBehavior, b => 0f);
|
|
|
|
foreach (var behavior in ItemBehaviors.Values
|
|
.SelectMany(behaviors=>behaviors)) behavior.Initialize();
|
|
}
|
|
|
|
public void RecalculateMass()
|
|
{
|
|
Mass = Context.Cache.Get<Gear>(Hull).Mass +
|
|
EquippedItems.Select(i => Context.Cache.Get<Gear>(i)).Sum(i => i.Mass) +
|
|
Cargo.Select(i => Context.Cache.Get<ItemInstance>(i)).Sum(ii => ii.Mass) +
|
|
Children.Select(i => Context.Cache.Get<Entity>(i)).Sum(c=>c.Mass);
|
|
|
|
SpecificHeat = Context.Cache.Get<Gear>(Hull).HeatCapacity +
|
|
EquippedItems.Select(i => Context.Cache.Get<Gear>(i)).Sum(i => i.HeatCapacity) +
|
|
Cargo.Select(i => Context.Cache.Get<ItemInstance>(i)).Sum(ii => ii.HeatCapacity);
|
|
}
|
|
|
|
public IEnumerable<T> GetBehaviors<T>() where T : class, IBehavior
|
|
{
|
|
foreach (var behaviors in ItemBehaviors.Values)
|
|
foreach (var behavior in behaviors)
|
|
if (behavior is T b)
|
|
yield return b;
|
|
}
|
|
|
|
public IEnumerable<T> GetBehaviorData<T>() where T : BehaviorData
|
|
{
|
|
foreach (var behaviors in ItemBehaviors.Values)
|
|
foreach (var behavior in behaviors)
|
|
if (behavior.Data is T b)
|
|
yield return b;
|
|
}
|
|
|
|
public void AddHeat(float heat)
|
|
{
|
|
Temperature += heat / SpecificHeat;
|
|
}
|
|
|
|
public virtual void Update(float delta)
|
|
{
|
|
foreach(var analogBehavior in Axes.Keys)
|
|
analogBehavior.SetAxis(AxisOverrides.ContainsKey(analogBehavior) ? AxisOverrides[analogBehavior] : Axes[analogBehavior]);
|
|
|
|
foreach (var behavior in ItemBehaviors.SelectMany(item => item.Value))
|
|
behavior.Update(delta);
|
|
|
|
foreach (var staleItem in GearData
|
|
.Where(kvp=>kvp.Key.ItemData!=kvp.Value)
|
|
.Select(kvp=>kvp.Key))
|
|
{
|
|
// Item is stale, there's an updated one in the database cache
|
|
//var actualItem = Context.Cache.Get<Gear>(staleItem.ID);
|
|
var staleBehaviors = ItemBehaviors[staleItem];
|
|
ItemBehaviors.Remove(staleItem);
|
|
|
|
// We'll be reloading the item along with its behaviors, some data must be persisted
|
|
var persistentData = staleBehaviors
|
|
.Where(b => b is IPersistentBehavior)
|
|
.Cast<IPersistentBehavior>()
|
|
.Select(b => b.Store());
|
|
|
|
// Regenerate the behavior list for the updated item
|
|
ItemBehaviors[staleItem] = staleItem.ItemData.Behaviors
|
|
.Select(bd => bd.CreateInstance(Context, this, staleItem))
|
|
.OrderBy(b => b.GetType().GetCustomAttribute<UpdateOrderAttribute>()?.Order ?? 0).ToList();
|
|
|
|
foreach(var behavior in ItemBehaviors[staleItem])
|
|
behavior.Initialize();
|
|
|
|
// Restore state for persisted behaviors
|
|
foreach(var persistentBehaviorData in ItemBehaviors[staleItem]
|
|
.Where(b => b is IPersistentBehavior)
|
|
.Cast<IPersistentBehavior>()
|
|
.Zip(persistentData, (behavior, data) => new {behavior, data}))
|
|
persistentBehaviorData.behavior.Restore(persistentBehaviorData.data);
|
|
|
|
// Regenerate activated behavior bindings
|
|
if (Bindings.ContainsKey(staleItem))
|
|
{
|
|
Bindings.Remove(staleItem);
|
|
Bindings[staleItem] = ItemBehaviors[staleItem]
|
|
.Where(b => b is IActivatedBehavior)
|
|
.Cast<IActivatedBehavior>().ToList();
|
|
}
|
|
|
|
// Remove axis bindings and overrides for analog behaviors
|
|
foreach (var axis in staleBehaviors.Where(b => b is IAnalogBehavior).Cast<IAnalogBehavior>())
|
|
{
|
|
Axes.Remove(axis);
|
|
if (AxisOverrides.ContainsKey(axis))
|
|
AxisOverrides.Remove(axis);
|
|
}
|
|
|
|
// Regenerate axis bindings for analog behaviors
|
|
foreach (var axis in ItemBehaviors[staleItem].Where(b => b is IAnalogBehavior).Cast<IAnalogBehavior>())
|
|
Axes[axis] = 0;
|
|
}
|
|
|
|
foreach (var item in EquippedItems
|
|
.Select(item => Context.Cache.Get<Gear>(item))
|
|
.Where(item => item.ItemData.Performance(Temperature) < .01f))
|
|
item.Durability -= delta;
|
|
|
|
if (Parent != Guid.Empty)
|
|
{
|
|
var parent = Context.Cache.Get<Entity>(Parent);
|
|
Position = parent.Position;
|
|
Velocity = parent.Velocity;
|
|
}
|
|
}
|
|
|
|
public void OnBeforeSerialize()
|
|
{
|
|
// Filter item behavior collections by those with any persistent behaviors
|
|
// For each item create an object containing the item ID and a list of persistent behaviors
|
|
// Then turn that into a dictionary mapping from item ID to an array of every behaviors persistent data
|
|
PersistedBehaviors = ItemBehaviors
|
|
.Where(x => x.Value.Any(b=>b is IPersistentBehavior))
|
|
.Select(x => new {gear=x.Key, behaviors = x.Value
|
|
.Where(b=>b is IPersistentBehavior)
|
|
.Cast<IPersistentBehavior>()})
|
|
.ToDictionary(x=> x.gear.ID, x=>x.behaviors.Select(b => b.Store()).ToArray());
|
|
}
|
|
|
|
public void OnAfterDeserialize()
|
|
{
|
|
RecalculateMass();
|
|
|
|
AddItemBehaviors();
|
|
|
|
var index = 0;
|
|
// Iterate only over the behaviors of items which contain persistent data
|
|
// Filter the behaviors for each item to get the persistent ones, then cast them and combine with the persisted data array for that item
|
|
foreach (var persistentBehaviorData in ItemBehaviors
|
|
.Where(itemBehaviors => PersistedBehaviors.ContainsKey(itemBehaviors.Key.ID)).
|
|
SelectMany(itemBehaviors => itemBehaviors.Value
|
|
.Where(b=> b is IPersistentBehavior)
|
|
.Cast<IPersistentBehavior>()
|
|
.Zip(PersistedBehaviors[itemBehaviors.Key.ID], (behavior, data) => new{behavior, data})))
|
|
persistentBehaviorData.behavior.Restore(persistentBehaviorData.data);
|
|
}
|
|
} |