2020-03-22 19:05:26 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Linq ;
using static Unity . Mathematics . math ;
public class GameContext
{
2020-04-23 03:31:17 +01:00
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 > ( ) ;
2020-04-09 07:48:45 +01:00
private Action < string > _logger ;
2020-04-16 03:08:18 +01:00
private Dictionary < BlueprintStatEffect , PerformanceStat > AffectedStats =
new Dictionary < BlueprintStatEffect , PerformanceStat > ( ) ;
private double _time ;
private float _deltaTime ;
2020-04-23 03:31:17 +01:00
private GlobalData _globalData ;
2020-04-16 03:08:18 +01:00
2020-04-23 03:31:17 +01:00
public GlobalData GlobalData = > _globalData ? ? ( _globalData = Cache . GetAll < GlobalData > ( ) . FirstOrDefault ( ) ) ;
2020-04-16 03:08:18 +01:00
public DatabaseCache Cache { get ; }
public double Time
{
get = > _time ;
set
{
_deltaTime = ( float ) ( value - _time ) ;
_time = value ;
}
}
2020-04-09 07:48:45 +01:00
// private readonly Dictionary<CraftedItemData, int> Tier = new Dictionary<CraftedItemData, int>();
2020-03-27 01:53:03 +00:00
2020-04-09 07:48:45 +01:00
public GameContext ( DatabaseCache cache , Action < string > logger )
2020-03-27 01:53:03 +00:00
{
2020-04-16 03:08:18 +01:00
Cache = cache ;
2020-04-09 07:48:45 +01:00
_logger = logger ;
2020-04-23 03:31:17 +01:00
// var globalData = Cache.GetAll<GlobalData>().FirstOrDefault();
// if (globalData == null)
// {
// globalData = new GlobalData();
// Cache.Add(globalData);
// }
2020-04-16 03:08:18 +01:00
}
public void Log ( string s )
{
_logger ( s ) ;
}
2020-04-23 03:31:17 +01:00
public IEnumerable < Entity > ZoneEntities ( ZoneData zone )
{
return ZoneContents [ zone ] . Values ;
}
2020-04-16 03:08:18 +01:00
public Dictionary < DatabaseEntry , Entity > InitializeZone ( Guid zoneID )
{
var contents = new Dictionary < DatabaseEntry , Entity > ( ) ;
var zoneData = Cache . Get < ZoneData > ( zoneID ) ;
var planetsData = zoneData . Planets . Select ( id = > Cache . Get < PlanetData > ( id ) ) . ToArray ( ) ;
var planetEntities = planetsData . ToDictionary ( p = > p ,
2020-04-23 03:31:17 +01:00
p = > new OrbitalEntity ( this , null , Enumerable . Empty < Gear > ( ) , Enumerable . Empty < ItemInstance > ( ) , p . Belt ? Cache . Get < OrbitData > ( p . Orbit ) . Parent : p . Orbit ) ) ;
2020-04-16 03:08:18 +01:00
foreach ( var kvp in planetEntities )
contents . Add ( kvp . Key , kvp . Value ) ;
var stationsData = zoneData . Stations . Select ( id = > Cache . Get < StationData > ( id ) ) ;
var stationEntities = stationsData . ToDictionary ( s = > s ,
2020-04-23 03:31:17 +01:00
s = > new OrbitalEntity ( this , null , Enumerable . Empty < Gear > ( ) , Enumerable . Empty < ItemInstance > ( ) , s . Parent ) ) ;
2020-04-16 03:08:18 +01:00
foreach ( var kvp in stationEntities )
contents . Add ( kvp . Key , kvp . Value ) ;
ZoneContents . Add ( zoneData , contents ) ;
return contents ;
}
public void Update ( )
{
2020-04-23 03:31:17 +01:00
foreach ( var agent in Agents )
{
agent . Update ( _deltaTime ) ;
}
2020-04-17 01:38:48 +01:00
OrbitalEntity . ClearOrbits ( ) ;
2020-04-16 03:08:18 +01:00
foreach ( var kvp in ZoneContents )
{
var zone = kvp . Key ;
var entities = kvp . Value . Values ;
foreach ( var entity in entities )
{
entity . Update ( _deltaTime ) ;
}
2020-03-27 01:53:03 +00:00
}
}
2020-04-16 03:08:18 +01:00
public void UnloadZone ( ZoneData zone )
{
ZoneContents . Remove ( zone ) ;
}
2020-03-22 19:05:26 +00:00
2020-04-09 07:48:45 +01:00
// public int ItemTier(CraftedItemData itemData)
// {
// if (Tier.ContainsKey(itemData)) return Tier[itemData];
//
// Tier[itemData] = itemData.Ingredients.Keys.Max(ci => _cache.Get<ItemData>(ci) is CraftedItemData craftableIngredient ? ItemTier(craftableIngredient) : 0);
//
// return Tier[itemData];
// }
2020-03-22 19:05:26 +00:00
public ItemData GetData ( ItemInstance item )
{
2020-04-16 03:08:18 +01:00
return Cache . Get < ItemData > ( item . Data ) ;
2020-03-22 19:05:26 +00:00
}
public SimpleCommodityData GetData ( SimpleCommodity item )
{
2020-04-16 03:08:18 +01:00
return Cache . Get < SimpleCommodityData > ( item . Data ) ;
2020-03-22 19:05:26 +00:00
}
public CraftedItemData GetData ( CraftedItemInstance item )
{
2020-04-16 03:08:18 +01:00
return Cache . Get < CraftedItemData > ( item . Data ) ;
2020-03-22 19:05:26 +00:00
}
public EquippableItemData GetData ( Gear gear )
{
2020-04-16 03:08:18 +01:00
return Cache . Get < EquippableItemData > ( gear . Data ) ;
2020-03-22 19:05:26 +00:00
}
public float GetMass ( ItemInstance item )
{
var data = GetData ( item ) ;
switch ( item )
{
case CraftedItemInstance _ :
return data . Mass ;
case SimpleCommodity commodity :
return data . Mass * commodity . Quantity ;
}
return 0 ;
}
public float GetHeatCapacity ( ItemInstance item )
{
var data = GetData ( item ) ;
switch ( item )
{
case CraftedItemInstance _ :
return data . Mass * data . SpecificHeat ;
case SimpleCommodity commodity :
return data . Mass * data . SpecificHeat * commodity . Quantity ;
}
return 0 ;
}
2020-04-09 07:48:45 +01:00
public PerformanceStat GetAffectedStat ( BlueprintData blueprint , BlueprintStatEffect effect )
{
// We've already cached this effect's stat, return it directly
if ( AffectedStats . ContainsKey ( effect ) ) return AffectedStats [ effect ] ;
// Get the data for the item the blueprint is for, return null if not found or not equippable
2020-04-16 03:08:18 +01:00
var blueprintItem = Cache . Get < EquippableItemData > ( blueprint . Item ) ;
2020-04-09 07:48:45 +01:00
if ( blueprintItem = = null )
{
_logger ( $"Attempted to get stat effect but Blueprint {blueprint.ID} is not for an equippable item!" ) ;
return AffectedStats [ effect ] = null ;
}
// Get the first behavior of the type specified in the blueprint stat effect, return null if not found
2020-04-16 03:08:18 +01:00
var effectObject = effect . StatReference . Behavior = = blueprintItem . Name ? ( object ) blueprintItem :
2020-04-09 07:48:45 +01:00
blueprintItem . Behaviors . FirstOrDefault ( b = > b . GetType ( ) . Name = = effect . StatReference . Behavior ) ;
2020-04-16 03:08:18 +01:00
if ( effectObject = = null )
2020-04-09 07:48:45 +01:00
{
_logger ( $"Attempted to get stat effect for Blueprint {blueprint.ID} but stat references missing behavior \" { effect . StatReference . Behavior } \ "!" ) ;
return AffectedStats [ effect ] = null ;
}
// Get the first field in the behavior matching the name specified in the stat effect, return null if not found
2020-04-16 03:08:18 +01:00
var type = effectObject . GetType ( ) ;
2020-04-09 07:48:45 +01:00
var field = type . GetField ( effect . StatReference . Stat ) ;
if ( field = = null )
{
2020-04-16 03:08:18 +01:00
_logger ( $"Attempted to get stat effect for Blueprint {blueprint.ID} but object {effect.StatReference.Behavior} does not have a stat named \" { effect . StatReference . Stat } \ "!" ) ;
2020-04-09 07:48:45 +01:00
return AffectedStats [ effect ] = null ;
}
// Finally we've confirmed the stat effect is valid, return the affected stat
2020-04-16 03:08:18 +01:00
return AffectedStats [ effect ] = field . GetValue ( effectObject ) as PerformanceStat ;
2020-04-09 07:48:45 +01:00
}
2020-03-22 19:05:26 +00:00
// Determine quality of either the item itself or the specific ingredient this stat depends on
public float Quality ( PerformanceStat stat , Gear item )
{
2020-04-09 07:48:45 +01:00
var activeEffects = item . Blueprint . StatEffects . Where ( x = > GetAffectedStat ( item . Blueprint , x ) = = stat ) . ToArray ( ) ;
2020-03-22 19:05:26 +00:00
float quality ;
2020-04-09 07:48:45 +01:00
if ( ! activeEffects . Any ( ) )
2020-03-22 19:05:26 +00:00
quality = item . CompoundQuality ( ) ;
else
{
2020-04-28 01:50:50 +01:00
var ingredientInstances = item . Ingredients . Select ( i = > Cache . Get < ItemInstance > ( i ) ) . ToArray ( ) ;
var ingredients = ingredientInstances . Where ( i = > activeEffects . Any ( e = > e . Ingredient = = i . Data ) ) . ToArray ( ) ;
2020-04-09 07:48:45 +01:00
if ( ingredients . Length ! = activeEffects . Length )
{
_logger ( $"Item {item.ID} does not have the ingredients specified by the stat effects of its blueprint!" ) ;
return 0 ;
}
float sum = 0 ;
foreach ( var i in ingredients )
{
if ( i is CraftedItemInstance ci )
sum + = ci . CompoundQuality ( ) ;
else _logger ( $"Blueprint stat effect for item {item.ID} specifies invalid (non crafted) ingredient!" ) ;
}
quality = sum / ingredients . Length ;
2020-03-22 19:05:26 +00:00
}
return quality ;
}
// Returns stat when not equipped
public float Evaluate ( PerformanceStat stat , Gear item )
{
var quality = pow ( Quality ( stat , item ) , stat . QualityExponent ) ;
var result = lerp ( stat . Min , stat . Max , quality ) ;
if ( float . IsNaN ( result ) )
return stat . Min ;
return result ;
}
// Returns stat using ship temperature and modifiers
2020-04-09 10:33:16 +01:00
public float Evaluate ( PerformanceStat stat , Gear item , Entity entity )
2020-03-22 19:05:26 +00:00
{
var itemData = GetData ( item ) ;
2020-04-09 10:33:16 +01:00
var heat = ! stat . HeatDependent ? 1 : pow ( itemData . Performance ( entity . Temperature ) , Evaluate ( itemData . HeatExponent , item ) ) ;
2020-04-16 03:08:18 +01:00
var durability = ! stat . DurabilityDependent ? 1 : pow ( item . Durability / Evaluate ( itemData . Durability , item ) , Evaluate ( itemData . DurabilityExponent , item ) ) ;
2020-03-22 19:05:26 +00:00
var quality = pow ( Quality ( stat , item ) , stat . QualityExponent ) ;
2020-04-09 10:33:16 +01:00
var scaleModifier = stat . GetScaleModifiers ( entity ) . Values . Aggregate ( 1.0f , ( current , mod ) = > current * mod ) ;
2020-03-22 19:05:26 +00:00
2020-04-09 10:33:16 +01:00
var constantModifier = stat . GetConstantModifiers ( entity ) . Values . Sum ( ) ;
2020-03-22 19:05:26 +00:00
var result = lerp ( stat . Min , stat . Max , heat * durability * quality ) * scaleModifier + constantModifier ;
if ( float . IsNaN ( result ) )
return stat . Min ;
return result ;
}
public SimpleCommodity CreateInstance ( Guid data , int count )
{
2020-04-16 03:08:18 +01:00
var item = Cache . Get < SimpleCommodityData > ( data ) ;
2020-03-22 19:05:26 +00:00
if ( item ! = null )
return new SimpleCommodity
{
Data = data ,
Quantity = count ,
ID = Guid . NewGuid ( )
} ;
2020-04-09 07:48:45 +01:00
_logger ( "Attempted to create Simple Commodity instance using missing or incorrect item id" ) ;
return null ;
2020-03-22 19:05:26 +00:00
}
public CraftedItemInstance CreateInstance ( Guid data , float quality )
{
2020-04-16 03:08:18 +01:00
var item = Cache . Get < CraftedItemData > ( data ) ;
2020-03-22 19:05:26 +00:00
if ( item = = null )
{
2020-04-09 07:48:45 +01:00
_logger ( "Attempted to create crafted item instance using missing or incorrect item id!" ) ;
return null ;
2020-03-22 19:05:26 +00:00
}
2020-04-16 03:08:18 +01:00
var blueprint = Cache . GetAll < BlueprintData > ( ) . FirstOrDefault ( b = > b . Item = = data ) ;
2020-04-09 07:48:45 +01:00
if ( blueprint = = null )
{
_logger ( "Attempted to create crafted item instance which has no blueprint!" ) ;
return null ;
}
var ingredients = blueprint . Ingredients . SelectMany ( ci = >
2020-03-22 19:05:26 +00:00
{
2020-04-16 03:08:18 +01:00
var ingredient = Cache . Get ( ci . Key ) ;
2020-03-22 19:05:26 +00:00
return ingredient is SimpleCommodityData
? ( IEnumerable < ItemInstance > ) new [ ] { CreateInstance ( ci . Key , ci . Value ) }
: Enumerable . Range ( 0 , ci . Value ) . Select ( i = > CreateInstance ( ci . Key , quality ) ) ;
} )
. ToList ( ) ;
2020-04-28 01:50:50 +01:00
Cache . AddAll ( ingredients ) ;
2020-03-22 19:05:26 +00:00
if ( item is EquippableItemData equippableItemData )
{
2020-04-16 03:08:18 +01:00
var newGear = new Gear
2020-03-22 19:05:26 +00:00
{
Context = this ,
Data = data ,
ID = Guid . NewGuid ( ) ,
2020-04-28 01:50:50 +01:00
Ingredients = ingredients . Select ( i = > i . ID ) . ToList ( ) ,
2020-04-23 03:31:17 +01:00
Quality = quality ,
Blueprint = blueprint
2020-03-22 19:05:26 +00:00
} ;
2020-04-16 03:08:18 +01:00
newGear . Durability = Evaluate ( equippableItemData . Durability , newGear ) ;
2020-04-28 01:50:50 +01:00
Cache . Add ( newGear ) ;
2020-04-16 03:08:18 +01:00
return newGear ;
2020-03-22 19:05:26 +00:00
}
2020-04-28 01:50:50 +01:00
var newCommodity = new CompoundCommodity
2020-03-22 19:05:26 +00:00
{
Context = this ,
Data = data ,
ID = Guid . NewGuid ( ) ,
2020-04-28 01:50:50 +01:00
Ingredients = ingredients . Select ( i = > i . ID ) . ToList ( ) ,
2020-04-23 03:31:17 +01:00
Quality = quality ,
Blueprint = blueprint
2020-03-22 19:05:26 +00:00
} ;
2020-04-28 01:50:50 +01:00
Cache . Add ( newCommodity ) ;
return newCommodity ;
2020-03-22 19:05:26 +00:00
}
2020-04-16 03:08:18 +01:00
}