2020-04-09 10:33:16 +01:00
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
2020-04-29 23:36:00 +01:00
|
|
|
using JsonKnownTypes;
|
|
|
|
|
using MessagePack;
|
|
|
|
|
using Newtonsoft.Json;
|
2020-04-09 10:33:16 +01:00
|
|
|
using Unity.Mathematics;
|
|
|
|
|
using static Unity.Mathematics.math;
|
|
|
|
|
|
2020-04-29 23:36:00 +01:00
|
|
|
[MessagePackObject,
|
2020-05-04 00:07:36 +01:00
|
|
|
Union(0, typeof(Ship)),
|
|
|
|
|
Union(1, typeof(OrbitalEntity)),
|
2020-04-29 23:36:00 +01:00
|
|
|
JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter<Entity>))]
|
2020-05-25 03:05:30 +01:00
|
|
|
public abstract class Entity : DatabaseEntry, IMessagePackSerializationCallbackReceiver, INamedEntry
|
2020-04-09 10:33:16 +01:00
|
|
|
{
|
2020-06-01 03:11:04 +01:00
|
|
|
[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)]
|
2020-06-05 04:16:33 +01:00
|
|
|
public readonly HashSet<Guid> Cargo; // Type: ItemInstance
|
|
|
|
|
|
|
|
|
|
[JsonProperty("gear"), Key(7)]
|
|
|
|
|
public readonly List<Guid> Gear;
|
2020-06-01 03:11:04 +01:00
|
|
|
|
|
|
|
|
[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;
|
|
|
|
|
|
|
|
|
|
[JsonProperty("name"), Key(13)]
|
|
|
|
|
public string Name;
|
|
|
|
|
|
|
|
|
|
[JsonProperty("population"), Key(14)]
|
|
|
|
|
public int Population;
|
|
|
|
|
|
|
|
|
|
[JsonProperty("personality"), Key(15)]
|
|
|
|
|
public Dictionary<Guid, float> Personality = new Dictionary<Guid, float>();
|
2020-05-06 04:56:36 +01:00
|
|
|
|
2020-06-02 07:01:03 +01:00
|
|
|
[JsonProperty("incompleteGear"), Key(16)]
|
|
|
|
|
public Dictionary<Guid, double> IncompleteGear = new Dictionary<Guid, double>();
|
|
|
|
|
//public List<IncompleteItem> IncompleteGear = new List<IncompleteItem>();
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
[JsonProperty("incompleteCargo"), Key(17)]
|
2020-06-02 07:01:03 +01:00
|
|
|
public Dictionary<Guid, double> IncompleteCargo = new Dictionary<Guid, double>();
|
|
|
|
|
//public List<IncompleteItem> IncompleteCargo = new List<IncompleteItem>();
|
|
|
|
|
|
2020-05-06 04:56:36 +01:00
|
|
|
[IgnoreMember] public Guid Zone;
|
2020-06-05 04:16:33 +01:00
|
|
|
[IgnoreMember] public List<Hardpoint> Hardpoints;
|
2020-04-29 23:36:00 +01:00
|
|
|
[IgnoreMember] public readonly Dictionary<object, float> VisibilitySources = new Dictionary<object, float>();
|
2020-05-29 23:06:35 +01:00
|
|
|
[IgnoreMember] public readonly Dictionary<string, float> Messages = new Dictionary<string, float>();
|
2020-06-05 04:16:33 +01:00
|
|
|
// [IgnoreMember] public Dictionary<Gear, EquippableItemData> GearData;
|
|
|
|
|
// [IgnoreMember] public Dictionary<Gear, List<IBehavior>> ItemBehaviors;
|
|
|
|
|
|
2020-06-08 01:01:33 +01:00
|
|
|
private List<IPopulationAssignment> PopulationAssignments = new List<IPopulationAssignment>();
|
|
|
|
|
|
|
|
|
|
[IgnoreMember] public int AssignedPopulation => PopulationAssignments.Sum(pa => pa.AssignedPopulation);
|
2020-04-29 23:36:00 +01:00
|
|
|
[IgnoreMember] public float Mass { get; private set; }
|
2020-05-26 01:12:06 +01:00
|
|
|
[IgnoreMember] public float OccupiedCapacity { get; private set; }
|
2020-06-05 04:16:33 +01:00
|
|
|
[IgnoreMember] public float ThermalMass { get; private set; }
|
2020-04-29 23:36:00 +01:00
|
|
|
[IgnoreMember] public float Visibility => VisibilitySources.Values.Sum();
|
2020-06-02 07:01:03 +01:00
|
|
|
|
2020-06-10 02:45:10 +01:00
|
|
|
public class ChangeEvent : IChangeSource
|
2020-06-06 03:21:03 +01:00
|
|
|
{
|
|
|
|
|
public event Action OnChanged;
|
|
|
|
|
|
|
|
|
|
public void Change()
|
|
|
|
|
{
|
|
|
|
|
OnChanged?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private bool _cargoChanged;
|
2020-06-10 02:45:10 +01:00
|
|
|
public ChangeEvent CargoEvent = new ChangeEvent();
|
|
|
|
|
|
2020-06-06 03:21:03 +01:00
|
|
|
private bool _gearChanged;
|
2020-06-10 02:45:10 +01:00
|
|
|
public ChangeEvent GearEvent = new ChangeEvent();
|
|
|
|
|
|
|
|
|
|
public ChangeEvent ChildEvent = new ChangeEvent();
|
2020-06-06 03:21:03 +01:00
|
|
|
|
2020-05-28 04:50:40 +01:00
|
|
|
public float Capacity
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var hull = Context.Cache.Get<Gear>(Hull);
|
|
|
|
|
var hullData = Context.Cache.Get<HullData>(hull.Data);
|
|
|
|
|
return Context.Evaluate(hullData.Capacity, hull, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-29 23:36:00 +01:00
|
|
|
|
2020-05-25 03:05:30 +01:00
|
|
|
[IgnoreMember] public string EntryName
|
|
|
|
|
{
|
|
|
|
|
get => Name;
|
|
|
|
|
set => Name = value;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public Entity(GameContext context, Guid hull, IEnumerable<Guid> gear, IEnumerable<Guid> cargo, Guid zone, Guid corporation)
|
2020-04-09 10:33:16 +01:00
|
|
|
{
|
|
|
|
|
Context = context;
|
2020-05-07 03:53:54 +01:00
|
|
|
Zone = zone;
|
2020-05-27 02:23:25 +01:00
|
|
|
Corporation = corporation;
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
Gear = gear.ToList();
|
|
|
|
|
Cargo = new HashSet<Guid>(cargo);
|
2020-04-23 03:31:17 +01:00
|
|
|
Hull = hull;
|
2020-04-25 00:01:13 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
Hydrate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Hydrate()
|
|
|
|
|
{
|
|
|
|
|
//var gearIDs = EquippedItems.Append(Hull);
|
|
|
|
|
//var gear = gearIDs.ToDictionary(id => id, id => Context.Cache.Get<Gear>(id));
|
|
|
|
|
//GearData = gear.Values.ToDictionary(g => g, g => g.ItemData);
|
|
|
|
|
|
|
|
|
|
Hardpoints = new List<Hardpoint>();
|
|
|
|
|
Hardpoints.Add(new Hardpoint
|
|
|
|
|
{
|
|
|
|
|
HardpointData = new HardpointData{ Type = HardpointType.Hull }
|
|
|
|
|
});
|
|
|
|
|
var hull = Context.Cache.Get<Gear>(Hull);
|
|
|
|
|
Equip(hull, Hardpoints[0]);
|
|
|
|
|
|
|
|
|
|
var hullData = Context.Cache.Get<HullData>(hull.Data);
|
|
|
|
|
Hardpoints.AddRange(hullData.Hardpoints.Select(hd => new Hardpoint {HardpointData = hd}));
|
|
|
|
|
|
|
|
|
|
// Create a copy of the gear list and remove every item actually equipped
|
|
|
|
|
var remainingGear = new List<Guid>(Gear);
|
|
|
|
|
foreach (var gearID in Gear)
|
|
|
|
|
if (Equip(gearID))
|
|
|
|
|
remainingGear.Remove(gearID);
|
2020-04-28 01:50:50 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var remaining in remainingGear)
|
|
|
|
|
{
|
|
|
|
|
Context.Log($"Item {Context.Cache.Get<Gear>(remaining).Name} equipped on entity {Name} has no compatible hardpoint, moved to cargo.");
|
|
|
|
|
Gear.Remove(remaining);
|
|
|
|
|
Cargo.Add(remaining);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var behavior in hardpoint.Behaviors
|
|
|
|
|
.Where(behavior => behavior is IInitializableBehavior))
|
|
|
|
|
((IInitializableBehavior) behavior).Initialize();
|
|
|
|
|
|
|
|
|
|
RecalculateMass();
|
2020-05-06 04:56:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public void HydrateHardpoint(Hardpoint hardpoint)
|
2020-05-06 04:56:36 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
if (hardpoint.Gear == null)
|
|
|
|
|
{
|
|
|
|
|
Context.Log("Attempted to hydrate hardpoint with no gear on it!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-06 03:21:03 +01:00
|
|
|
hardpoint.ItemData = hardpoint.Gear.ItemData;
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
hardpoint.Behaviors = hardpoint.Gear.ItemData.Behaviors
|
|
|
|
|
.Select(bd => bd.CreateInstance(Context, this, hardpoint.Gear))
|
|
|
|
|
.ToArray();
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
hardpoint.BehaviorGroups = hardpoint.Behaviors
|
|
|
|
|
.GroupBy(b => b.Data.Group)
|
|
|
|
|
.OrderBy(g=>g.Key)
|
|
|
|
|
.Select(g=>new BehaviorGroup {
|
|
|
|
|
Behaviors = g.ToArray(),
|
|
|
|
|
//Axis = (IAnalogBehavior) g.FirstOrDefault(b=>b is IAnalogBehavior),
|
|
|
|
|
Switch = (Switch) g.FirstOrDefault(b=>b is Switch),
|
|
|
|
|
Trigger = (Trigger) g.FirstOrDefault(b=>b is Trigger)
|
|
|
|
|
})
|
|
|
|
|
.ToArray();
|
2020-06-08 01:01:33 +01:00
|
|
|
|
|
|
|
|
foreach(var behavior in hardpoint.Behaviors)
|
|
|
|
|
if(behavior is IPopulationAssignment populationAssignment)
|
|
|
|
|
PopulationAssignments.Add(populationAssignment);
|
2020-06-05 04:16:33 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-10 02:45:10 +01:00
|
|
|
public void Unequip(Hardpoint hardpoint)
|
2020-06-05 04:16:33 +01:00
|
|
|
{
|
|
|
|
|
if (hardpoint.Gear != null)
|
|
|
|
|
{
|
|
|
|
|
Gear.Remove(hardpoint.Gear.ID);
|
|
|
|
|
Cargo.Add(hardpoint.Gear.ID);
|
2020-06-10 02:45:10 +01:00
|
|
|
hardpoint.Gear = null;
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-10 02:45:10 +01:00
|
|
|
_gearChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
}
|
2020-06-10 02:45:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Equip(Gear gear, Hardpoint hardpoint)
|
|
|
|
|
{
|
|
|
|
|
// Hardpoint is occupied, remove existing item from gear list
|
|
|
|
|
if (hardpoint.Gear != null) Unequip(hardpoint);
|
|
|
|
|
if (Cargo.Contains(gear.ID)) Cargo.Remove(gear.ID);
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
hardpoint.Gear = gear;
|
|
|
|
|
HydrateHardpoint(hardpoint);
|
2020-06-06 03:21:03 +01:00
|
|
|
_gearChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Equip(Guid gearID, bool force = false)
|
|
|
|
|
{
|
|
|
|
|
// Check all portions of inventory for a matching item
|
|
|
|
|
if (Cargo.Contains(gearID)) Cargo.Remove(gearID);
|
|
|
|
|
else if (IncompleteGear.ContainsKey(gearID))
|
|
|
|
|
IncompleteGear.Remove(gearID);
|
|
|
|
|
else if(!Gear.Contains(gearID))
|
|
|
|
|
throw new ArgumentException("Attempted to equip gear which is not present in inventory!");
|
2020-04-09 10:33:16 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
var gear = Context.Cache.Get<Gear>(gearID);
|
|
|
|
|
var gearData = Context.Cache.Get<GearData>(gear.Data);
|
2020-05-12 01:42:13 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
// Find an empty hardpoint of the correct type
|
|
|
|
|
var hardpoint = Hardpoints
|
|
|
|
|
.FirstOrDefault(hp => hp.Gear == null && hp.HardpointData.Type == gearData.HardpointType);
|
|
|
|
|
|
|
|
|
|
// With force option enabled, we'll try to find an empty hardpoint but otherwise any will do
|
|
|
|
|
if (hardpoint == null && force)
|
|
|
|
|
hardpoint = Hardpoints
|
|
|
|
|
.FirstOrDefault(hp => hp.HardpointData.Type == gearData.HardpointType);
|
|
|
|
|
|
|
|
|
|
// No compatible hardpoint found, return false
|
|
|
|
|
if (hardpoint == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Equip(gear, hardpoint);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CraftedItemInstance AddCargo(CraftedItemInstance item)
|
|
|
|
|
{
|
|
|
|
|
Cargo.Add(item.ID);
|
|
|
|
|
Mass += item.Mass;
|
|
|
|
|
ThermalMass += item.ThermalMass;
|
|
|
|
|
OccupiedCapacity += item.Size;
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CraftedItemInstance RemoveCargo(CraftedItemInstance item)
|
|
|
|
|
{
|
|
|
|
|
if(!Cargo.Contains(item.ID))
|
|
|
|
|
throw new ArgumentException("Attempted to remove an item which is not in cargo!");
|
|
|
|
|
Cargo.Remove(item.ID);
|
|
|
|
|
Mass -= item.Mass;
|
|
|
|
|
ThermalMass -= item.ThermalMass;
|
|
|
|
|
OccupiedCapacity -= item.Size;
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SimpleCommodity AddCargo(SimpleCommodity item)
|
|
|
|
|
{
|
|
|
|
|
// Regardless of whether a match is found, the item's mass and size are added to the entity
|
|
|
|
|
Mass += item.Mass;
|
|
|
|
|
ThermalMass += item.ThermalMass;
|
|
|
|
|
OccupiedCapacity += item.Size;
|
2020-05-11 01:17:10 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
// Try to find an matching instance of the same commodity in cargo
|
|
|
|
|
var existingCommodityID = Cargo.FirstOrDefault(id =>
|
|
|
|
|
Context.Cache.Get<ItemInstance>(id) is SimpleCommodity simpleCommodity &&
|
|
|
|
|
simpleCommodity.ItemData == item.ItemData);
|
2020-05-06 04:56:36 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
// A match was found, delete the original item and increment the quantity of the match
|
|
|
|
|
if (existingCommodityID != Guid.Empty)
|
|
|
|
|
{
|
|
|
|
|
var existingCommodity = Context.Cache.Get<SimpleCommodity>(existingCommodityID);
|
|
|
|
|
existingCommodity.Quantity += item.Quantity;
|
|
|
|
|
Context.Cache.Delete(item);
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
return existingCommodity;
|
|
|
|
|
}
|
2020-04-28 01:50:50 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
// No match was found, simply add the item instance to the cargo
|
|
|
|
|
Cargo.Add(item.ID);
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
return item;
|
2020-04-28 01:50:50 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public SimpleCommodity RemoveCargo(SimpleCommodity item, int quantity)
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
if(!Cargo.Contains(item.ID))
|
|
|
|
|
throw new ArgumentException("Attempted to remove an item which is not in cargo!");
|
|
|
|
|
|
|
|
|
|
// If the quantity matches exactly, huzzah, just remove the instance from cargo
|
|
|
|
|
if (item.Quantity == quantity)
|
|
|
|
|
Cargo.Remove(item.ID);
|
2020-06-02 07:01:03 +01:00
|
|
|
else
|
2020-06-05 04:16:33 +01:00
|
|
|
{
|
|
|
|
|
// Quantity does not match, create new item instance and decrement source quantity
|
|
|
|
|
var newSimpleCommodity = new SimpleCommodity
|
|
|
|
|
{
|
|
|
|
|
Context = Context,
|
|
|
|
|
Data = item.Data,
|
|
|
|
|
Quantity = quantity
|
|
|
|
|
};
|
2020-06-10 02:45:10 +01:00
|
|
|
Context.Cache.Add(newSimpleCommodity);
|
2020-06-05 04:16:33 +01:00
|
|
|
item.Quantity -= quantity;
|
|
|
|
|
item = newSimpleCommodity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mass -= item.Mass;
|
|
|
|
|
ThermalMass -= item.ThermalMass;
|
|
|
|
|
OccupiedCapacity -= item.Size;
|
|
|
|
|
|
2020-06-06 03:21:03 +01:00
|
|
|
_cargoChanged = true;
|
2020-06-05 04:16:33 +01:00
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddChild(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
Mass += entity.Mass;
|
|
|
|
|
ThermalMass += entity.ThermalMass;
|
|
|
|
|
Children.Add(entity.ID);
|
2020-06-10 02:45:10 +01:00
|
|
|
ChildEvent.Change();
|
2020-06-05 04:16:33 +01:00
|
|
|
}
|
2020-06-02 07:01:03 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public void RemoveChild(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
Mass -= entity.Mass;
|
|
|
|
|
ThermalMass -= entity.ThermalMass;
|
|
|
|
|
Children.Remove(entity.ID);
|
2020-06-10 02:45:10 +01:00
|
|
|
ChildEvent.Change();
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public Guid Build(BlueprintData blueprint, float quality, string name, bool direct = false)
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
if (direct)
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
|
|
|
|
if(blueprint.FactoryItem!=Guid.Empty)
|
|
|
|
|
throw new ArgumentException("Attempted to directly build a blueprint which requires a factory!");
|
|
|
|
|
var blueprintItem = Context.Cache.Get<GearData>(blueprint.Item);
|
|
|
|
|
if(blueprintItem == null)
|
|
|
|
|
throw new ArgumentException("Attempted to directly build a blueprint for a missing or incompatible item!");
|
|
|
|
|
}
|
2020-06-05 04:16:33 +01:00
|
|
|
|
|
|
|
|
//var newItemID = Guid.Empty;
|
|
|
|
|
|
|
|
|
|
// GetBlueprintIngredients will assign the output lists with the items matching the blueprint, and return false if they are not found
|
|
|
|
|
if (!GetBlueprintIngredients(blueprint, out var simpleIngredients, out var compoundIngredients))
|
|
|
|
|
return Guid.Empty;
|
2020-06-02 07:01:03 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
// These will be bundled with the data for the compound commodity instance
|
|
|
|
|
var ingredients = new List<ItemInstance>();
|
|
|
|
|
ingredients.AddRange(compoundIngredients.Select(RemoveCargo));
|
|
|
|
|
ingredients.AddRange(simpleIngredients.Select(sc => RemoveCargo(sc, blueprint.Ingredients[sc.Data])));
|
2020-06-02 07:01:03 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
var blueprintItemData = Context.Cache.Get(blueprint.Item);
|
|
|
|
|
|
|
|
|
|
// Creating new crafted item instances is a bit more complicated
|
|
|
|
|
if (blueprintItemData is CraftedItemData)
|
|
|
|
|
{
|
|
|
|
|
CraftedItemInstance newItem;
|
2020-06-02 07:01:03 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
if (blueprintItemData is EquippableItemData equippableItemData)
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
var newGear = new Gear
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
Context = Context,
|
|
|
|
|
Data = blueprintItemData.ID,
|
|
|
|
|
Ingredients = ingredients.Select(ii=>ii.ID).ToList(),
|
|
|
|
|
Quality = quality,
|
|
|
|
|
Blueprint = blueprint.ID,
|
|
|
|
|
Name = name,
|
|
|
|
|
SourceEntity = ID
|
|
|
|
|
};
|
|
|
|
|
newGear.Durability = Context.Evaluate(equippableItemData.Durability, newGear);
|
|
|
|
|
newItem = newGear;
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
newItem = new CompoundCommodity
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
|
|
|
|
Context = Context,
|
|
|
|
|
Data = blueprintItemData.ID,
|
2020-06-05 04:16:33 +01:00
|
|
|
Ingredients = ingredients.Select(ii=>ii.ID).ToList(),
|
|
|
|
|
Quality = quality,
|
|
|
|
|
Blueprint = blueprint.ID,
|
|
|
|
|
Name = name,
|
|
|
|
|
SourceEntity = ID
|
2020-06-02 07:01:03 +01:00
|
|
|
};
|
|
|
|
|
}
|
2020-06-05 04:16:33 +01:00
|
|
|
Context.Cache.Add(newItem);
|
|
|
|
|
if (direct)
|
|
|
|
|
IncompleteGear[newItem.ID] = blueprint.ProductionTime;
|
|
|
|
|
else
|
|
|
|
|
IncompleteCargo[newItem.ID] = blueprint.ProductionTime;
|
|
|
|
|
return newItem.ID;
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
var newSimpleCommodity = new SimpleCommodity
|
|
|
|
|
{
|
|
|
|
|
Context = Context,
|
|
|
|
|
Data = blueprintItemData.ID,
|
|
|
|
|
Quantity = 1
|
|
|
|
|
};
|
|
|
|
|
Context.Cache.Add(newSimpleCommodity);
|
|
|
|
|
IncompleteCargo[newSimpleCommodity.ID] = blueprint.ProductionTime;
|
|
|
|
|
return newSimpleCommodity.ID;
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool GetBlueprintIngredients(BlueprintData blueprint, out List<SimpleCommodity> simpleIngredients,
|
|
|
|
|
out List<CompoundCommodity> compoundIngredients)
|
|
|
|
|
{
|
|
|
|
|
simpleIngredients = new List<SimpleCommodity>();
|
|
|
|
|
compoundIngredients = new List<CompoundCommodity>();
|
|
|
|
|
var hasAllIngredients = true;
|
|
|
|
|
var cargoInstances = Cargo.Select(c => Context.Cache.Get<ItemInstance>(c));
|
|
|
|
|
foreach (var kvp in blueprint.Ingredients)
|
|
|
|
|
{
|
|
|
|
|
var itemData = Context.Cache.Get(kvp.Key);
|
|
|
|
|
if (itemData is SimpleCommodityData)
|
|
|
|
|
{
|
|
|
|
|
var matchingItem = cargoInstances.FirstOrDefault(ii =>
|
|
|
|
|
{
|
|
|
|
|
if (!(ii is SimpleCommodity simpleCommodity)) 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 =
|
|
|
|
|
cargoInstances.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.Take(kvp.Value));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hasAllIngredients;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-28 01:50:50 +01:00
|
|
|
public void RecalculateMass()
|
|
|
|
|
{
|
2020-05-06 04:56:36 +01:00
|
|
|
Mass = Context.Cache.Get<Gear>(Hull).Mass +
|
2020-06-05 04:16:33 +01:00
|
|
|
Gear.Select(i => Context.Cache.Get<Gear>(i)).Sum(i => i.Mass) +
|
2020-06-02 07:01:03 +01:00
|
|
|
IncompleteGear.Select(i => Context.Cache.Get<Gear>(i.Key)).Sum(i => i.Mass) +
|
2020-05-06 04:56:36 +01:00
|
|
|
Cargo.Select(i => Context.Cache.Get<ItemInstance>(i)).Sum(ii => ii.Mass) +
|
2020-06-02 07:01:03 +01:00
|
|
|
IncompleteCargo.Select(i => Context.Cache.Get<ItemInstance>(i.Key)).Sum(ii => ii.Mass) +
|
2020-05-06 04:56:36 +01:00
|
|
|
Children.Select(i => Context.Cache.Get<Entity>(i)).Sum(c=>c.Mass);
|
2020-04-28 01:50:50 +01:00
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
ThermalMass = Context.Cache.Get<Gear>(Hull).ThermalMass +
|
|
|
|
|
Gear.Select(i => Context.Cache.Get<Gear>(i)).Sum(i => i.ThermalMass) +
|
|
|
|
|
IncompleteGear.Select(i => Context.Cache.Get<Gear>(i.Key)).Sum(i => i.ThermalMass) +
|
|
|
|
|
Cargo.Select(i => Context.Cache.Get<ItemInstance>(i)).Sum(ii => ii.ThermalMass) +
|
|
|
|
|
IncompleteCargo.Select(i => Context.Cache.Get<ItemInstance>(i.Key)).Sum(ii => ii.ThermalMass) +
|
|
|
|
|
Children.Select(i => Context.Cache.Get<Entity>(i)).Sum(c=>c.ThermalMass);
|
|
|
|
|
|
|
|
|
|
OccupiedCapacity = Gear.Select(i => Context.Cache.Get<Gear>(i)).Sum(i => i.Size) +
|
|
|
|
|
Cargo.Select(i => Context.Cache.Get<ItemInstance>(i)).Sum(ii => ii.Size);
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public T GetBehavior<T>() where T : class, IBehavior
|
2020-04-09 10:33:16 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var behavior in hardpoint.Behaviors)
|
|
|
|
|
if (behavior is T b)
|
|
|
|
|
return b;
|
|
|
|
|
return null;
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public IEnumerable<T> GetBehaviors<T>() where T : class, IBehavior
|
2020-05-11 01:17:10 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var behavior in hardpoint.Behaviors)
|
|
|
|
|
if (behavior is T b)
|
|
|
|
|
yield return b;
|
2020-05-11 01:17:10 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public IEnumerable<T> GetBehaviorData<T>() where T : BehaviorData
|
2020-05-11 01:17:10 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var behavior in hardpoint.Behaviors)
|
|
|
|
|
if (behavior.Data is T b)
|
|
|
|
|
yield return b;
|
2020-05-11 01:17:10 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public Switch GetSwitch<T>() where T : class, IBehavior
|
2020-05-28 04:50:40 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var group in hardpoint.BehaviorGroups)
|
|
|
|
|
foreach(var behavior in group.Behaviors)
|
|
|
|
|
if (behavior is T)
|
|
|
|
|
return group.Switch;
|
|
|
|
|
return null;
|
2020-05-28 04:50:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public Trigger GetTrigger<T>() where T : class, IBehavior
|
2020-05-28 04:50:40 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints)
|
|
|
|
|
if(hardpoint.Gear!=null)
|
|
|
|
|
foreach (var group in hardpoint.BehaviorGroups)
|
|
|
|
|
foreach(var behavior in group.Behaviors)
|
|
|
|
|
if (behavior is T)
|
|
|
|
|
return group.Trigger;
|
|
|
|
|
return null;
|
2020-05-28 04:50:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-09 10:33:16 +01:00
|
|
|
public void AddHeat(float heat)
|
|
|
|
|
{
|
2020-05-26 01:12:06 +01:00
|
|
|
if (Parent != Guid.Empty)
|
|
|
|
|
{
|
2020-06-04 01:36:55 +01:00
|
|
|
// var parent = Context.Cache.Get<Entity>(Parent);
|
|
|
|
|
// parent.AddHeat(heat);
|
2020-05-26 01:12:06 +01:00
|
|
|
}
|
|
|
|
|
else
|
2020-06-05 04:16:33 +01:00
|
|
|
Temperature += heat / ThermalMass;
|
2020-05-26 01:12:06 +01:00
|
|
|
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void Update(float delta)
|
|
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var hardpoint in Hardpoints.Where(hp=>hp.Gear!=null))
|
|
|
|
|
{
|
|
|
|
|
foreach (var group in hardpoint.BehaviorGroups)
|
|
|
|
|
foreach(var behavior in group.Behaviors)
|
|
|
|
|
if(!behavior.Update(delta))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
foreach (var behavior in hardpoint.Behaviors.Where(b => b is IAlwaysUpdatedBehavior).Cast<IAlwaysUpdatedBehavior>())
|
2020-05-29 23:06:35 +01:00
|
|
|
behavior.AlwaysUpdate(delta);
|
2020-06-05 04:16:33 +01:00
|
|
|
|
|
|
|
|
if(hardpoint.Gear.ItemData.Performance(Temperature) < .01f)
|
|
|
|
|
hardpoint.Gear.Durability -= delta;
|
|
|
|
|
}
|
2020-05-04 00:07:36 +01:00
|
|
|
|
2020-05-06 04:56:36 +01:00
|
|
|
if (Parent != Guid.Empty)
|
2020-05-04 00:07:36 +01:00
|
|
|
{
|
2020-05-06 04:56:36 +01:00
|
|
|
var parent = Context.Cache.Get<Entity>(Parent);
|
|
|
|
|
Position = parent.Position;
|
|
|
|
|
Velocity = parent.Velocity;
|
2020-05-26 01:12:06 +01:00
|
|
|
Temperature = parent.Temperature;
|
2020-05-04 00:07:36 +01:00
|
|
|
}
|
2020-05-11 01:17:10 +01:00
|
|
|
|
|
|
|
|
// Processing stale item data properly was a pain in my ass, so the shortcut is just persisting and restoring everything
|
2020-06-05 04:16:33 +01:00
|
|
|
// if (Hardpoints.Any(hp => hp.Gear.ItemData != hp.ItemData))
|
|
|
|
|
// {
|
|
|
|
|
// OnBeforeSerialize();
|
|
|
|
|
// OnAfterDeserialize();
|
|
|
|
|
// }
|
2020-05-29 23:06:35 +01:00
|
|
|
|
|
|
|
|
foreach (var message in Messages.Keys.ToArray())
|
|
|
|
|
{
|
|
|
|
|
Messages[message] = Messages[message] - delta;
|
|
|
|
|
if (Messages[message] < 0)
|
|
|
|
|
Messages.Remove(message);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 07:01:03 +01:00
|
|
|
foreach (var incompleteGear in IncompleteGear.Keys.ToArray())
|
|
|
|
|
{
|
|
|
|
|
IncompleteGear[incompleteGear] = IncompleteGear[incompleteGear] - delta;
|
2020-06-05 04:16:33 +01:00
|
|
|
if (IncompleteGear[incompleteGear] < 0) Equip(incompleteGear);
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-11 01:17:10 +01:00
|
|
|
// Here's the PITA way
|
|
|
|
|
// 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;
|
|
|
|
|
// }
|
2020-06-06 03:21:03 +01:00
|
|
|
|
|
|
|
|
if (_cargoChanged)
|
|
|
|
|
{
|
|
|
|
|
CargoEvent.Change();
|
|
|
|
|
_cargoChanged = false;
|
|
|
|
|
}
|
|
|
|
|
if (_gearChanged)
|
|
|
|
|
{
|
|
|
|
|
GearEvent.Change();
|
|
|
|
|
_gearChanged = false;
|
|
|
|
|
}
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|
2020-04-29 23:36:00 +01:00
|
|
|
|
2020-05-29 23:06:35 +01:00
|
|
|
public void SetMessage(string message)
|
|
|
|
|
{
|
|
|
|
|
Messages[message] = Context.GlobalData.MessageDuration;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-29 23:36:00 +01:00
|
|
|
public void OnBeforeSerialize()
|
|
|
|
|
{
|
2020-05-06 04:56:36 +01:00
|
|
|
// 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
|
2020-06-05 04:16:33 +01:00
|
|
|
PersistedBehaviors = Hardpoints
|
|
|
|
|
.Where(hp => hp.Behaviors.Any(b=>b is IPersistentBehavior))
|
|
|
|
|
.Select(hp => new {gear=hp.Gear, behaviors = hp.Behaviors
|
2020-05-06 04:56:36 +01:00
|
|
|
.Where(b=>b is IPersistentBehavior)
|
|
|
|
|
.Cast<IPersistentBehavior>()})
|
|
|
|
|
.ToDictionary(x=> x.gear.ID, x=>x.behaviors.Select(b => b.Store()).ToArray());
|
2020-04-29 23:36:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnAfterDeserialize()
|
|
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
Hydrate();
|
2020-04-30 02:54:31 +01:00
|
|
|
|
2020-05-06 04:56:36 +01:00
|
|
|
// 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
|
2020-06-05 04:16:33 +01:00
|
|
|
foreach (var persistentBehaviorData in Hardpoints
|
|
|
|
|
.Where(hardpoint => PersistedBehaviors.ContainsKey(hardpoint.Gear.ID)).
|
|
|
|
|
SelectMany(hardpoint => hardpoint.Behaviors
|
2020-05-06 04:56:36 +01:00
|
|
|
.Where(b=> b is IPersistentBehavior)
|
|
|
|
|
.Cast<IPersistentBehavior>()
|
2020-06-05 04:16:33 +01:00
|
|
|
.Zip(PersistedBehaviors[hardpoint.Gear.ID], (behavior, data) => new{behavior, data})))
|
2020-05-06 04:56:36 +01:00
|
|
|
persistentBehaviorData.behavior.Restore(persistentBehaviorData.data);
|
2020-04-29 23:36:00 +01:00
|
|
|
}
|
2020-05-11 01:17:10 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
//[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
public class Hardpoint
|
2020-05-11 01:17:10 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
public Gear Gear;
|
|
|
|
|
public EquippableItemData ItemData;
|
|
|
|
|
public HardpointData HardpointData;
|
|
|
|
|
public IBehavior[] Behaviors;
|
|
|
|
|
public BehaviorGroup[] BehaviorGroups;
|
2020-06-02 07:01:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-05 04:16:33 +01:00
|
|
|
public class BehaviorGroup
|
2020-06-02 07:01:03 +01:00
|
|
|
{
|
2020-06-05 04:16:33 +01:00
|
|
|
public IBehavior[] Behaviors;
|
|
|
|
|
public Trigger Trigger;
|
|
|
|
|
public Switch Switch;
|
|
|
|
|
//public IAnalogBehavior Axis;
|
2020-04-09 10:33:16 +01:00
|
|
|
}
|