2020-03-22 19:05:26 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Linq ;
2020-05-04 00:07:36 +01:00
using Unity.Mathematics ;
2020-03-22 19:05:26 +00:00
using static Unity . Mathematics . math ;
2020-05-04 00:07:36 +01:00
using float2 = Unity . Mathematics . float2 ;
2020-04-30 02:54:31 +01:00
using Random = Unity . Mathematics . Random ;
2020-05-10 00:16:33 +01:00
using JM.LinqFaster ;
2020-05-30 02:44:22 +01:00
using float4 = Unity . Mathematics . float4 ;
2020-03-22 19:05:26 +00:00
public class GameContext
{
2020-04-30 02:54:31 +01:00
public Random Random = new Random ( ( uint ) ( DateTime . Now . Ticks % uint . MaxValue ) ) ;
2020-05-04 00:07:36 +01:00
public Dictionary < Guid , Guid [ ] > ZonePlanets = new Dictionary < Guid , Guid [ ] > ( ) ;
public Dictionary < Guid , Dictionary < Guid , Entity > > ZoneEntities = new Dictionary < Guid , Dictionary < Guid , Entity > > ( ) ;
2020-04-23 03:31:17 +01:00
public Dictionary < string , GalaxyMapLayerData > MapLayers = new Dictionary < string , GalaxyMapLayerData > ( ) ;
2020-05-07 03:53:54 +01:00
public Dictionary < Guid , List < IController > > CorporationControllers = new Dictionary < Guid , List < IController > > ( ) ;
2020-05-10 00:16:33 +01:00
public Dictionary < Guid , SimplifiedZoneData > GalaxyZones ;
2020-04-23 03:31:17 +01:00
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-05-10 00:16:33 +01:00
private HashSet < Guid > _loadedZones = new HashSet < Guid > ( ) ;
2020-05-04 00:07:36 +01:00
private HashSet < Guid > _orbits = new HashSet < Guid > ( ) ;
private Dictionary < Guid , float2 > _orbitVelocities = new Dictionary < Guid , float2 > ( ) ;
private Dictionary < Guid , float2 > _orbitPositions = new Dictionary < Guid , float2 > ( ) ;
private Dictionary < Guid , float2 > _previousOrbitPositions = new Dictionary < Guid , float2 > ( ) ;
2020-05-28 04:50:40 +01:00
private Dictionary < Guid , float4 [ ] > _asteroidTransforms = new Dictionary < Guid , float4 [ ] > ( ) ; // x, y, rotation, scale
private Dictionary < Guid , float4 [ ] > _previousAsteroidTransforms = new Dictionary < Guid , float4 [ ] > ( ) ; // x, y, rotation, scale
private Dictionary < ( Guid , int ) , float > _asteroidRespawnTimers = new Dictionary < ( Guid , int ) , float > ( ) ;
private Dictionary < ( Guid , int ) , float > _asteroidDamage = new Dictionary < ( Guid , int ) , float > ( ) ;
2020-05-29 23:06:35 +01:00
private Dictionary < ( Guid , Guid , int ) , float > _asteroidMiningAccumulator = new Dictionary < ( Guid , Guid , int ) , float > ( ) ;
2020-05-10 00:16:33 +01:00
private Guid _forceLoadZone ;
2020-05-12 01:42:13 +01:00
private Type [ ] _statObjects ;
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 ; }
2020-05-12 01:42:13 +01:00
public Type [ ] StatObjects = > _statObjects ? ? ( _statObjects = typeof ( BehaviorData ) . GetAllChildClasses ( )
. Concat ( typeof ( EquippableItemData ) . GetAllChildClasses ( ) ) . ToArray ( ) ) ;
2020-05-10 00:16:33 +01:00
public Guid ForceLoadZone
{
get = > _forceLoadZone ;
set
{
if ( _forceLoadZone ! = Guid . Empty & & ZoneEntities [ _forceLoadZone ] . Count = = 0 )
UnloadZone ( _forceLoadZone ) ;
_forceLoadZone = value ;
if ( ! _loadedZones . Contains ( _forceLoadZone ) )
LoadZone ( _forceLoadZone ) ;
}
}
2020-04-16 03:08:18 +01:00
public double Time
{
get = > _time ;
set
{
_deltaTime = ( float ) ( value - _time ) ;
_time = value ;
2020-05-07 03:53:54 +01:00
//Log($"GameContext delta time: {_deltaTime}");
2020-04-16 03:08:18 +01:00
}
}
2020-05-04 00:07:36 +01:00
public float2 WormholePosition ( Guid zone , Guid other )
{
var zoneData = Cache . Get < ZoneData > ( zone ) ;
var direction = Cache . Get < ZoneData > ( other ) . Position - zoneData . Position ;
return normalize ( direction ) * zoneData . Radius * . 95f ;
}
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 ) ;
}
public void Update ( )
{
2020-05-04 00:07:36 +01:00
_previousOrbitPositions = _orbitPositions ;
_orbitPositions = new Dictionary < Guid , float2 > ( _orbits . Count ) ;
foreach ( var orbit in _orbits )
{
_orbitPositions [ orbit ] = GetOrbitPosition ( orbit ) ;
_orbitVelocities [ orbit ] = _previousOrbitPositions . ContainsKey ( orbit )
2020-05-07 03:53:54 +01:00
? ( _orbitPositions [ orbit ] - _previousOrbitPositions [ orbit ] ) / _deltaTime
2020-05-04 00:07:36 +01:00
: float2 . zero ;
}
2020-05-07 03:53:54 +01:00
2020-05-28 04:50:40 +01:00
foreach ( var asteroids in _asteroidTransforms . Keys )
2020-05-30 02:44:22 +01:00
UpdateAsteroidTransforms ( asteroids ) ;
2020-05-25 03:05:30 +01:00
2020-05-07 03:53:54 +01:00
foreach ( var corporation in Cache . GetAll < Corporation > ( ) )
2020-04-23 03:31:17 +01:00
{
2020-05-10 00:16:33 +01:00
foreach ( var tasks in corporation . Tasks
2020-05-07 03:53:54 +01:00
. Select ( id = > Cache . Get < AgentTask > ( id ) ) // Fetch the tasks from the database cache
2020-05-10 00:16:33 +01:00
. Where ( task = > ! task . Reserved ) // Filter out tasks that have already been reserved
. GroupBy ( task = > task . Type ) ) // Group tasks by type
2020-05-07 03:53:54 +01:00
{
2020-05-10 00:16:33 +01:00
// Create a list of available controllers for this task type
var availableControllers = CorporationControllers [ corporation . ID ]
. Where ( controller = > controller . TaskType = = tasks . Key & & controller . Available ) . ToList ( ) ;
// Iterate over the highest priority tasks for which controllers are available
foreach ( var task in tasks . OrderByDescending ( task = > task . Priority ) . Take ( availableControllers . Count ) )
{
// Find the nearest controller for this task
IController nearestController = availableControllers [ 0 ] ;
List < SimplifiedZoneData > nearestControllerPath = FindPath ( GalaxyZones [ availableControllers . First ( ) . Zone ] , GalaxyZones [ task . Zone ] , true ) ;
foreach ( var controller in availableControllers . Skip ( 1 ) )
{
var path = FindPath ( GalaxyZones [ controller . Zone ] , GalaxyZones [ task . Zone ] , true ) ;
if ( path . Count < nearestControllerPath . Count )
{
nearestControllerPath = path ;
nearestController = controller ;
}
}
task . Reserved = true ;
2020-05-27 02:23:25 +01:00
nearestController . AssignTask ( task . ID ) ;
2020-05-10 00:16:33 +01:00
}
2020-05-07 03:53:54 +01:00
}
2020-04-23 03:31:17 +01:00
}
2020-05-04 00:07:36 +01:00
foreach ( var kvp in ZoneEntities )
{
foreach ( var entity in kvp . Value . Values ) entity . Update ( _deltaTime ) ;
}
}
2020-05-10 00:16:33 +01:00
public IEnumerable < Entity > GetEntities ( Guid zone )
{
return ZoneEntities [ zone ] . Values ;
}
public void LoadZone ( Guid zone )
{
var zoneData = Cache . Get < ZoneData > ( zone ) ;
_loadedZones . Add ( zone ) ;
foreach ( var orbit in zoneData . Orbits ) _orbits . Add ( orbit ) ;
// TODO: Associate planets with stored entities for planetary colonies
ZonePlanets [ zone ] = zoneData . Planets ;
2020-05-30 02:44:22 +01:00
foreach ( var belt in zoneData . Planets
. Select ( id = > Cache . Get < PlanetData > ( id ) )
. Where ( p = > p . Belt ) )
{
_asteroidTransforms [ belt . ID ] = new float4 [ belt . Asteroids . Length ] ;
_previousAsteroidTransforms [ belt . ID ] = new float4 [ belt . Asteroids . Length ] ;
}
2020-05-10 00:16:33 +01:00
// TODO: Load stored entities
ZoneEntities [ zone ] = new Dictionary < Guid , Entity > ( ) ;
}
2020-05-04 00:07:36 +01:00
public void UnloadZone ( Guid zone )
{
var zoneData = Cache . Get < ZoneData > ( zone ) ;
2020-05-10 00:16:33 +01:00
_loadedZones . Remove ( zone ) ;
2020-05-04 00:07:36 +01:00
ZoneEntities . Remove ( zone ) ;
2020-05-10 00:16:33 +01:00
ZonePlanets . Remove ( zone ) ;
foreach ( var orbit in zoneData . Orbits ) _orbits . Remove ( orbit ) ;
2020-05-30 02:44:22 +01:00
foreach ( var belt in zoneData . Planets
. Select ( id = > Cache . Get < PlanetData > ( id ) )
. Where ( p = > p . Belt ) )
{
_asteroidTransforms . Remove ( belt . ID ) ;
_previousAsteroidTransforms . Remove ( belt . ID ) ;
}
2020-05-10 00:16:33 +01:00
}
public void Warp ( Entity entity , Guid targetZone )
{
if ( length ( entity . Position - WormholePosition ( entity . Zone , targetZone ) ) < GlobalData . WarpDistance )
{
var sourceZone = entity . Zone ;
// Remove entity from source zone
ZoneEntities [ sourceZone ] . Remove ( entity . ID ) ;
// Unload source zone if empty and unobserved
if ( ForceLoadZone ! = sourceZone & & ZoneEntities [ sourceZone ] . Count = = 0 )
UnloadZone ( sourceZone ) ;
// Load target zone if not yet loaded
if ( ! _loadedZones . Contains ( targetZone ) )
LoadZone ( targetZone ) ;
entity . Zone = targetZone ;
entity . Position = WormholePosition ( targetZone , sourceZone ) ;
ZoneEntities [ targetZone ] [ entity . ID ] = entity ;
}
2020-05-04 00:07:36 +01:00
}
// Determine orbital position recursively, caching parent positions to avoid repeated calculations
public float2 GetOrbitPosition ( Guid orbit )
{
// Root orbit is fixed at center
if ( orbit = = Guid . Empty )
return float2 . zero ;
if ( ! _orbitPositions . ContainsKey ( orbit ) )
{
var orbitData = Cache . Get < OrbitData > ( orbit ) ;
2020-05-27 03:43:30 +01:00
float2 pos = float2 . zero ;
if ( orbitData . Period > . 01f )
{
var phase = ( float ) frac ( Time / orbitData . Period * GlobalData . OrbitSpeedMultiplier ) ;
pos = OrbitData . Evaluate ( frac ( phase + orbitData . Phase ) ) * orbitData . Distance ;
}
_orbitPositions [ orbit ] = GetOrbitPosition ( orbitData . Parent ) + pos ;
2020-05-04 00:07:36 +01:00
}
var position = _orbitPositions [ orbit ] ;
if ( float . IsNaN ( position . x ) )
2020-04-16 03:08:18 +01:00
{
2020-05-04 00:07:36 +01:00
Log ( "Orbit position is NaN, something went very wrong!" ) ;
return float2 . zero ;
2020-03-27 01:53:03 +00:00
}
2020-05-04 00:07:36 +01:00
return _orbitPositions [ orbit ] ;
2020-03-27 01:53:03 +00:00
}
2020-04-16 03:08:18 +01:00
2020-05-04 00:07:36 +01:00
public float2 GetOrbitVelocity ( Guid orbit )
2020-04-16 03:08:18 +01:00
{
2020-05-04 00:07:36 +01:00
return _orbitVelocities . ContainsKey ( orbit ) ? _orbitVelocities [ orbit ] : float2 . zero ;
2020-04-16 03:08:18 +01:00
}
2020-05-10 00:16:33 +01:00
2020-05-28 04:50:40 +01:00
public int NearestAsteroid ( Guid planetDataID , float2 position )
2020-05-25 03:05:30 +01:00
{
var planetData = Cache . Get < PlanetData > ( planetDataID ) ;
2020-05-28 04:50:40 +01:00
var asteroidPositions = GetAsteroidTransforms ( planetDataID ) ;
int nearest = 0 ;
float nearestDistance = Single . MaxValue ;
for ( int i = 0 ; i < planetData . Asteroids . Length ; i + + )
2020-05-25 03:05:30 +01:00
{
2020-05-28 04:50:40 +01:00
var dist = lengthsq ( asteroidPositions [ i ] . xy - position ) ;
if ( AsteroidExists ( planetDataID , i ) & & dist < nearestDistance )
{
nearest = i ;
nearestDistance = dist ;
}
}
return nearest ;
}
public bool AsteroidExists ( Guid planetDataID , int asteroid ) = >
! _asteroidRespawnTimers . ContainsKey ( ( planetDataID , asteroid ) ) ;
public float2 GetAsteroidVelocity ( Guid planetDataID , int asteroid )
{
return ( _asteroidTransforms [ planetDataID ] [ asteroid ] . xy - _previousAsteroidTransforms [ planetDataID ] [ asteroid ] . xy ) / _deltaTime ;
}
public float4 GetAsteroidTransform ( Guid planetDataID , int asteroid )
{
return _asteroidTransforms [ planetDataID ] [ asteroid ] ;
}
public float4 [ ] GetAsteroidTransforms ( Guid planetDataID )
{
return _asteroidTransforms [ planetDataID ] ;
}
private void UpdateAsteroidTransforms ( Guid planetDataID )
{
var planetData = Cache . Get < PlanetData > ( planetDataID ) ;
Array . Copy ( _asteroidTransforms [ planetDataID ] , _previousAsteroidTransforms [ planetDataID ] , planetData . Asteroids . Length ) ;
var orbitData = Cache . Get < OrbitData > ( planetData . Orbit ) ;
var orbitPosition = GetOrbitPosition ( orbitData . Parent ) ;
2020-05-25 03:05:30 +01:00
for ( var i = 0 ; i < planetData . Asteroids . Length ; i + + )
{
2020-05-29 23:06:35 +01:00
var size = planetData . Asteroids [ i ] . Size ;
if ( _asteroidRespawnTimers . ContainsKey ( ( planetDataID , i ) ) ) size = 0 ;
else if ( _asteroidDamage . ContainsKey ( ( planetDataID , i ) ) )
{
var hpLerp = unlerp ( GlobalData . AsteroidSizeMin , GlobalData . AsteroidSizeMax , size ) ;
var asteroidHitpoints = lerp ( GlobalData . AsteroidHitpointsMin , GlobalData . AsteroidHitpointsMax , pow ( hpLerp , GlobalData . AsteroidHitpointsPower ) ) ;
var sizeLerp = ( asteroidHitpoints - _asteroidDamage [ ( planetDataID , i ) ] ) / asteroidHitpoints ;
size = lerp ( GlobalData . AsteroidSizeMin , size , sizeLerp ) ;
}
2020-05-28 04:50:40 +01:00
_asteroidTransforms [ planetDataID ] [ i ] = float4 (
OrbitData . Evaluate ( ( float ) frac ( Time / OrbitalPeriod ( planetData . Asteroids [ i ] . Distance ) * GlobalData . OrbitSpeedMultiplier +
planetData . Asteroids [ i ] . Phase ) ) * planetData . Asteroids [ i ] . Distance + orbitPosition ,
2020-05-29 23:06:35 +01:00
( float ) ( Time * planetData . Asteroids [ i ] . RotationSpeed % 360.0 ) , size ) ;
2020-05-25 03:05:30 +01:00
}
}
2020-05-29 23:06:35 +01:00
public OrbitData CreateOrbit ( Guid zone , Guid parent , float2 position )
2020-05-10 00:16:33 +01:00
{
var parentPosition = GetOrbitPosition ( parent ) ;
var delta = position - parentPosition ;
var distance = length ( delta ) ;
var period = OrbitalPeriod ( distance ) ;
var phase = atan2 ( delta . y , delta . x ) / ( PI * 2 ) ;
2020-05-27 03:43:30 +01:00
var currentPhase = frac ( Time / period * GlobalData . OrbitSpeedMultiplier ) ;
2020-05-27 02:23:25 +01:00
var storedPhase = ( float ) frac ( phase - currentPhase ) ;
2020-05-10 00:16:33 +01:00
var orbit = new OrbitData
{
Context = this ,
Distance = distance ,
Parent = parent ,
Period = period ,
Phase = storedPhase ,
2020-05-29 23:06:35 +01:00
Zone = zone
2020-05-10 00:16:33 +01:00
} ;
Cache . Add ( orbit ) ;
2020-05-29 23:06:35 +01:00
_orbits . Add ( orbit . ID ) ;
2020-05-10 00:16:33 +01:00
return orbit ;
}
public float OrbitalPeriod ( float distance )
{
return pow ( distance , GlobalData . OrbitPeriodExponent ) * GlobalData . OrbitPeriodMultiplier ;
}
2020-05-21 01:53:06 +01:00
public void PlaceMegas ( )
{
var megas = Cache . GetAll < MegaCorporation > ( ) ;
var availableZones = Cache . GetAll < ZoneData > ( )
. Where ( z = > megas . All ( m = > m . HomeZone ! = z . ID ) )
. ToList ( ) ;
foreach ( var megaPlacement in megas
. OrderBy ( m = > m . PlacementType )
. GroupBy ( m = > m . PlacementType ) )
switch ( megaPlacement . Key )
{
case MegaPlacementType . Mass :
foreach ( var mega in megaPlacement )
{
var zone = availableZones
. MaxBy ( z = > z . Mass ) ;
availableZones . Remove ( zone ) ;
mega . HomeZone = zone . ID ;
}
break ;
case MegaPlacementType . Planets :
foreach ( var mega in megaPlacement )
{
var zone = availableZones
. MaxBy ( z = > z . Planets . Length ) ;
availableZones . Remove ( zone ) ;
mega . HomeZone = zone . ID ;
}
break ;
case MegaPlacementType . Resources :
foreach ( var mega in megaPlacement )
{
var zone = availableZones
. MaxBy ( z = > z . Planets
. SelectMany ( id = > Cache . Get < PlanetData > ( id ) . Resources
. Select ( r = > r . Key ) )
. Distinct ( )
. Count ( ) ) ;
availableZones . Remove ( zone ) ;
mega . HomeZone = zone . ID ;
}
break ;
case MegaPlacementType . Connected :
foreach ( var mega in megaPlacement )
{
var zone = availableZones
. OrderByDescending ( z = > z . Wormholes . Count )
. ThenBy ( z = > megas
. Sum ( m = > m . HomeZone = = Guid . Empty ? 0 : lengthsq ( Cache . Get < ZoneData > ( m . HomeZone ) . Position - z . Position ) ) )
. First ( ) ;
availableZones . Remove ( zone ) ;
mega . HomeZone = zone . ID ;
}
break ;
case MegaPlacementType . Isolated :
foreach ( var mega in megaPlacement )
{
var zone = availableZones
. Where ( z = > z . Wormholes . Count = = 1 )
. MaxBy ( z = >
{
int soloChain = 0 ;
var previous = z ;
var next = Cache . Get < ZoneData > ( z . Wormholes . First ( ) ) ;
while ( next . Wormholes . Count = = 2 )
{
var temp = next ;
next = Cache . Get < ZoneData > ( next . Wormholes . First ( w = > w ! = previous . ID ) ) ;
previous = temp ;
soloChain + + ;
}
return soloChain ;
} ) ;
availableZones . Remove ( zone ) ;
mega . HomeZone = zone . ID ;
}
break ;
default :
throw new ArgumentOutOfRangeException ( ) ;
}
}
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 ;
}
2020-05-26 01:12:06 +01:00
public float GetSize ( ItemInstance item )
{
var data = GetData ( item ) ;
switch ( item )
{
case CraftedItemInstance _ :
return data . Size ;
case SimpleCommodity commodity :
return data . Size * commodity . Quantity ;
}
return 0 ;
}
2020-06-05 04:16:33 +01:00
public float GetThermalMass ( ItemInstance item )
2020-03-22 19:05:26 +00:00
{
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-05-12 01:42:13 +01:00
var effectObject = effect . StatReference . Target = = blueprintItem . Name ? ( object ) blueprintItem :
blueprintItem . Behaviors . FirstOrDefault ( b = > b . GetType ( ) . Name = = effect . StatReference . Target ) ;
2020-04-16 03:08:18 +01:00
if ( effectObject = = null )
2020-04-09 07:48:45 +01:00
{
2020-05-12 01:42:13 +01:00
_logger ( $"Attempted to get stat effect for Blueprint {blueprint.ID} but stat references missing behavior \" { effect . StatReference . Target } \ "!" ) ;
2020-04-09 07:48:45 +01:00
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-05-12 01:42:13 +01:00
_logger ( $"Attempted to get stat effect for Blueprint {blueprint.ID} but object {effect.StatReference.Target} 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-05-22 00:23:34 +01:00
private readonly Dictionary < CraftedItemInstance , float > ItemQuality = new Dictionary < CraftedItemInstance , float > ( ) ;
2020-05-27 03:43:30 +01:00
2020-05-22 00:23:34 +01:00
public float CompoundQuality ( CraftedItemInstance item )
{
if ( ItemQuality . ContainsKey ( item ) ) return ItemQuality [ item ] ;
var quality = item . Quality ;
var craftedIngredients = item . Ingredients . Where ( i = > i is CraftedItemInstance ) . ToArray ( ) ;
if ( craftedIngredients . Length > 0 )
{
var ingredientQualityWeight = Cache . Get < CraftedItemData > ( item . Data ) . IngredientQualityWeight ;
quality = quality * ( 1 - ingredientQualityWeight ) +
craftedIngredients . Cast < CraftedItemInstance > ( ) . Average ( CompoundQuality ) * ingredientQualityWeight ;
}
ItemQuality [ item ] = quality ;
return ItemQuality [ item ] ;
}
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-30 02:54:31 +01:00
var blueprint = Cache . Get < BlueprintData > ( item . Blueprint ) ;
var activeEffects = blueprint . StatEffects . Where ( x = > GetAffectedStat ( 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-05-22 00:23:34 +01:00
quality = CompoundQuality ( item ) ;
2020-03-22 19:05:26 +00:00
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-06-05 04:16:33 +01:00
var distinctIngredients = ingredients . Select ( i = > i . Data ) . Distinct ( ) ;
if ( distinctIngredients . Count ( ) ! = activeEffects . Length )
2020-04-09 07:48:45 +01:00
{
_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 )
2020-05-22 00:23:34 +01:00
sum + = CompoundQuality ( ci ) ;
2020-04-09 07:48:45 +01:00
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 ;
}
2020-05-25 03:05:30 +01:00
public Entity CreateEntity ( Guid zone , Guid corporation , Guid loadout )
{
if ( ! ( Cache . Get ( loadout ) is LoadoutData loadoutData ) )
{
_logger ( "Attempted to spawn invalid loadout ID" ) ;
return null ;
}
if ( ! ( Cache . Get ( loadoutData . Hull ) is HullData hullData ) )
{
_logger ( "Attempted to spawn loadout with invalid hull ID" ) ;
return null ;
}
if ( ! ( Cache . Get ( zone ) is ZoneData zoneData ) )
{
_logger ( "Attempted to spawn entity with invalid zone ID" ) ;
return null ;
}
if ( ! ( Cache . Get ( corporation ) is Corporation corpData ) )
{
_logger ( "Attempted to spawn entity with invalid corporation ID" ) ;
return null ;
}
var gearData = loadoutData . Items
. Take ( hullData . Hardpoints . Count )
. Where ( id = > id ! = Guid . Empty )
. Select ( id = > Cache . Get < GearData > ( id ) )
. ToArray ( ) ;
if ( gearData . Any ( g = > g = = null ) )
{
_logger ( "Attempted to spawn loadout with invalid gear ID" ) ;
return null ;
}
var cargoData = loadoutData . Items
. Skip ( hullData . Hardpoints . Count )
. Where ( id = > id ! = Guid . Empty )
. Select ( id = > Cache . Get < CraftedItemData > ( id ) )
. ToArray ( ) ;
if ( cargoData . Any ( g = > g = = null ) )
{
_logger ( "Attempted to spawn loadout with invalid cargo ID" ) ;
return null ;
}
var gear = gearData
. Select ( gd = > CreateInstance ( gd . ID , GlobalData . StartingGearQualityMin , GlobalData . StartingGearQualityMax ) )
. ToArray ( ) ;
if ( gear . Any ( g = > g = = null ) )
return null ;
foreach ( var g in gear )
Cache . Add ( g ) ;
var cargo = cargoData
. Select ( gd = > CreateInstance ( gd . ID , GlobalData . StartingGearQualityMin , GlobalData . StartingGearQualityMax ) )
. ToArray ( ) ;
if ( cargo . Any ( g = > g = = null ) )
return null ;
foreach ( var c in cargo )
Cache . Add ( c ) ;
var simpleCargo = loadoutData . SimpleCargo ?
. Select ( x = > CreateInstance ( x . Key , x . Value ) )
. ToArray ( ) ? ? new SimpleCommodity [ 0 ] ;
if ( simpleCargo . Any ( g = > g = = null ) )
return null ;
var hull = CreateInstance ( loadoutData . Hull , GlobalData . StartingGearQualityMin , GlobalData . StartingGearQualityMax ) ;
if ( hull = = null )
return null ;
// Load target zone if not yet loaded
if ( ! _loadedZones . Contains ( zone ) )
LoadZone ( zone ) ;
if ( hullData . HullType = = HullType . Ship )
{
var entity = new Ship ( this , hull . ID , gear . Select ( g = > g . ID ) ,
2020-05-27 02:23:25 +01:00
cargo . Select ( c = > c . ID ) . Concat ( simpleCargo . Select ( c = > c . ID ) ) , zone , corporation )
2020-05-25 03:05:30 +01:00
{
Name = $"{loadoutData.Name} {Random.NextInt(1,255):X}"
} ;
Cache . Add ( entity ) ;
ZoneEntities [ zone ] [ entity . ID ] = entity ;
return entity ;
}
if ( hullData . HullType = = HullType . Station )
{
var distance = lerp ( . 2f , . 8f , Random . NextFloat ( ) ) * zoneData . Radius ;
var orbit = new OrbitData
{
Context = this ,
Distance = distance ,
Parent = _orbits
. Select ( id = > Cache . Get < OrbitData > ( id ) )
. First ( orbitData = > orbitData . Zone = = zone & & orbitData . Parent = = Guid . Empty ) . ID ,
Period = OrbitalPeriod ( distance ) ,
Phase = Random . NextFloat ( ) ,
Zone = zone
} ;
Cache . Add ( orbit ) ;
2020-05-29 23:06:35 +01:00
_orbits . Add ( orbit . ID ) ;
2020-05-25 03:05:30 +01:00
var entity = new OrbitalEntity ( this , hull . ID , gear . Select ( g = > g . ID ) ,
2020-05-27 02:23:25 +01:00
cargo . Select ( c = > c . ID ) . Concat ( simpleCargo . Select ( c = > c . ID ) ) , orbit . ID , zone , corporation )
2020-05-25 03:05:30 +01:00
{
2020-05-26 01:12:06 +01:00
Name = $"{loadoutData.Name} {Random.NextInt(1,255):X}" ,
2020-06-01 03:11:04 +01:00
Temperature = 293 ,
2020-06-08 01:01:33 +01:00
Population = 4
2020-05-25 03:05:30 +01:00
} ;
2020-06-01 03:11:04 +01:00
var parentCorp = Cache . Get < MegaCorporation > ( corpData . Parent ) ;
foreach ( var attribute in parentCorp . Personality )
entity . Personality [ attribute . Key ] = attribute . Value ;
2020-05-25 03:05:30 +01:00
Cache . Add ( entity ) ;
ZoneEntities [ zone ] [ entity . ID ] = entity ;
return entity ;
}
return null ;
}
2020-03-22 19:05:26 +00:00
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 )
2020-06-02 07:01:03 +01:00
{
var newItem = new SimpleCommodity
2020-03-22 19:05:26 +00:00
{
2020-06-02 07:01:03 +01:00
Context = this ,
2020-03-22 19:05:26 +00:00
Data = data ,
2020-05-25 03:05:30 +01:00
Quantity = count
2020-03-22 19:05:26 +00:00
} ;
2020-06-02 07:01:03 +01:00
Cache . Add ( newItem ) ;
return newItem ;
}
2020-03-22 19:05:26 +00:00
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
}
2020-05-25 03:05:30 +01:00
public CraftedItemInstance CreateInstance ( Guid data , float qualityMin , float qualityMax )
2020-03-22 19:05:26 +00:00
{
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 ) }
2020-05-25 03:05:30 +01:00
: Enumerable . Range ( 0 , ci . Value ) . Select ( i = > CreateInstance ( ci . Key , qualityMin , qualityMax ) ) ;
2020-03-22 19:05:26 +00:00
} )
. 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 ,
2020-04-28 01:50:50 +01:00
Ingredients = ingredients . Select ( i = > i . ID ) . ToList ( ) ,
2020-05-25 03:05:30 +01:00
Quality = Random . NextFloat ( qualityMin , qualityMax ) ,
2020-06-02 07:01:03 +01:00
Blueprint = blueprint . ID ,
2020-06-07 03:21:47 +01:00
Name = $"{item.Name}"
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 ,
2020-04-28 01:50:50 +01:00
Ingredients = ingredients . Select ( i = > i . ID ) . ToList ( ) ,
2020-05-25 03:05:30 +01:00
Quality = Random . NextFloat ( qualityMin , qualityMax ) ,
2020-06-02 07:01:03 +01:00
Blueprint = blueprint . ID ,
2020-06-07 03:21:47 +01:00
Name = $"{item.Name}"
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-05-10 00:16:33 +01:00
class DijkstraNode
{
public float Cost ;
public DijkstraNode Parent ;
public SimplifiedZoneData Zone ;
}
public List < SimplifiedZoneData > FindPath ( SimplifiedZoneData source , SimplifiedZoneData target , bool bestFirst = false )
{
SortedList < float , DijkstraNode > members = new SortedList < float , DijkstraNode > { { 0 , new DijkstraNode { Zone = source } } } ;
List < DijkstraNode > searched = new List < DijkstraNode > ( ) ;
while ( true )
{
var s = members . FirstOrDefault ( m = > ! searched . Contains ( m . Value ) ) . Value ; // Lowest cost unsearched node
if ( s = = null ) return null ; // No vertices left unsearched
if ( s . Zone = = target ) // We found the path
{
Stack < DijkstraNode > path = new Stack < DijkstraNode > ( ) ; // Since we start at the end, use a LIFO collection
path . Push ( s ) ;
while ( path . Peek ( ) . Parent ! = null ) // Keep pushing until we reach the start, which has no parent
path . Push ( path . Peek ( ) . Parent ) ;
return path . Select ( dv = > dv . Zone ) . ToList ( ) ;
}
// For each adjacent star (filter already visited zones unless heuristic is in use)
foreach ( var dijkstraStar in s . Zone . Links . WhereSelectF ( i = > ! bestFirst | | members . All ( m = > m . Value . Zone ! = GalaxyZones [ i ] ) ,
// Cost is parent cost plus distance
i = > new DijkstraNode { Parent = s , Zone = GalaxyZones [ i ] , Cost = s . Cost + length ( s . Zone . Position - GalaxyZones [ i ] . Position ) } ) )
// Add new member to list, sorted by cost plus optional heuristic
members . Add ( bestFirst ? dijkstraStar . Cost + length ( dijkstraStar . Zone . Position - target . Position ) : dijkstraStar . Cost , dijkstraStar ) ;
searched . Add ( s ) ;
}
}
2020-05-25 03:05:30 +01:00
public void SetParent ( Entity child , Entity parent )
{
child . Parent = parent . ID ;
2020-06-05 04:16:33 +01:00
parent . AddChild ( child ) ;
2020-05-25 03:05:30 +01:00
}
public void RemoveParent ( Entity child )
{
if ( child . Parent = = Guid . Empty )
return ;
var parent = Cache . Get < Entity > ( child . Parent ) ;
2020-06-05 04:16:33 +01:00
parent . RemoveChild ( child ) ;
2020-05-25 03:05:30 +01:00
child . Parent = Guid . Empty ;
}
2020-05-28 04:50:40 +01:00
public void MineAsteroid ( Entity miner , Guid asteroidBelt , int asteroid , float damage , float efficiency , float penetration )
{
var planetData = Cache . Get < PlanetData > ( asteroidBelt ) ;
var asteroidTransform = GetAsteroidTransform ( asteroidBelt , asteroid ) ;
var hpLerp = unlerp ( GlobalData . AsteroidSizeMin , GlobalData . AsteroidSizeMax , asteroidTransform . w ) ;
var asteroidHitpoints = lerp ( GlobalData . AsteroidHitpointsMin , GlobalData . AsteroidHitpointsMax , pow ( hpLerp , GlobalData . AsteroidHitpointsPower ) ) ;
2020-05-29 23:06:35 +01:00
2020-05-28 04:50:40 +01:00
if ( ! _asteroidDamage . ContainsKey ( ( asteroidBelt , asteroid ) ) )
_asteroidDamage [ ( asteroidBelt , asteroid ) ] = 0 ;
_asteroidDamage [ ( asteroidBelt , asteroid ) ] = _asteroidDamage [ ( asteroidBelt , asteroid ) ] + damage ;
2020-05-29 23:06:35 +01:00
if ( ! _asteroidMiningAccumulator . ContainsKey ( ( asteroidBelt , miner . ID , asteroid ) ) )
_asteroidMiningAccumulator [ ( asteroidBelt , miner . ID , asteroid ) ] = 0 ;
_asteroidMiningAccumulator [ ( asteroidBelt , miner . ID , asteroid ) ] = _asteroidMiningAccumulator [ ( asteroidBelt , miner . ID , asteroid ) ] + damage ;
2020-05-28 04:50:40 +01:00
if ( _asteroidDamage [ ( asteroidBelt , asteroid ) ] > asteroidHitpoints )
2020-05-29 23:06:35 +01:00
{
2020-05-28 04:50:40 +01:00
_asteroidRespawnTimers [ ( asteroidBelt , asteroid ) ] =
lerp ( GlobalData . AsteroidRespawnMin , GlobalData . AsteroidRespawnMax , hpLerp ) ;
2020-05-29 23:06:35 +01:00
_asteroidDamage . Remove ( ( asteroidBelt , asteroid ) ) ;
_asteroidMiningAccumulator . Remove ( ( asteroidBelt , miner . ID , asteroid ) ) ;
return ;
}
var resourceCount = planetData . Resources . Sum ( x = > x . Value ) ;
2020-05-28 04:50:40 +01:00
var resource = planetData . Resources . MaxBy ( x = > pow ( x . Value , 1f / penetration ) * Random . NextFloat ( ) ) ;
2020-05-29 23:06:35 +01:00
if ( efficiency * Random . NextFloat ( ) * _asteroidMiningAccumulator [ ( asteroidBelt , miner . ID , asteroid ) ] * resourceCount / GlobalData . MiningDifficulty > 1 & & miner . OccupiedCapacity < miner . Capacity - 1 )
2020-05-28 04:50:40 +01:00
{
2020-05-29 23:06:35 +01:00
_asteroidMiningAccumulator . Remove ( ( asteroidBelt , miner . ID , asteroid ) ) ;
2020-06-05 04:16:33 +01:00
var newSimpleCommodity = new SimpleCommodity
2020-05-28 04:50:40 +01:00
{
2020-06-05 04:16:33 +01:00
Context = this ,
Data = resource . Key ,
Quantity = 1
} ;
Cache . Add ( newSimpleCommodity ) ;
miner . AddCargo ( newSimpleCommodity ) ;
2020-05-28 04:50:40 +01:00
}
}
2020-06-05 04:16:33 +01:00
// public bool MoveCargo(Entity source, Entity target, ItemInstance item, int quantity = int.MaxValue)
// {
// if (item is CraftedItemInstance craftedItemInstance)
// {
// var craftedItemData = Cache.Get<CraftedItemData>(craftedItemInstance.Data);
// if (target.Capacity - target.OccupiedCapacity > craftedItemData.Size)
// {
// // Target has the cargo capacity for the item, simply move the instance
// source.Cargo.Remove(item.ID);
// target.Cargo.Add(item.ID);
// source.RecalculateMass();
// target.RecalculateMass();
// return true;
// }
// return false;
// }
//
// if (item is SimpleCommodity simpleCommoditySource)
// {
// quantity = min(quantity, simpleCommoditySource.Quantity);
// var simpleCommodityData = Cache.Get<SimpleCommodityData>(simpleCommoditySource.Data);
// var spareCapacity = (int) (target.Capacity - target.OccupiedCapacity);
// var simpleCommodityTarget = target.Cargo
// .Select(i => Cache.Get<ItemInstance>(i))
// .FirstOrDefault(i => i.Data == simpleCommodityData.ID) as SimpleCommodity;
// var success = spareCapacity >= quantity;
// quantity = min(quantity, spareCapacity);
// if (simpleCommodityTarget == null)
// {
// if (quantity == simpleCommoditySource.Quantity)
// {
// // Target has the cargo capacity to hold the full quantity,
// // and the target has no matching item instance;
// // simply move the existing item instance to the target
// source.Cargo.Remove(simpleCommoditySource.ID);
// target.Cargo.Add(simpleCommoditySource.ID);
// source.RecalculateMass();
// target.RecalculateMass();
// return success;
// }
// else
// {
// // Target has the cargo capacity to hold the desired quantity
// // and the target has no matching item instance;
// // Create a new item instance on the target and decrement the quantity of the source instance
// var newSimpleCommodity = new SimpleCommodity
// {
// Context = this,
// Data = simpleCommodityData.ID,
// Quantity = simpleCommoditySource.Quantity
// };
// Cache.Add(newSimpleCommodity);
// target.Cargo.Add(newSimpleCommodity.ID);
// simpleCommoditySource.Quantity -= quantity;
// source.RecalculateMass();
// target.RecalculateMass();
// return success;
// }
// }
// else
// {
// if (quantity == simpleCommoditySource.Quantity)
// {
// // Target has the cargo capacity to hold the full quantity,
// // and there is already a matching item instance;
// // delete the source entity's item instance and increment the target's quantity
// source.Cargo.Remove(simpleCommoditySource.ID);
// Cache.Delete(simpleCommoditySource);
// simpleCommodityTarget.Quantity += quantity;
// source.RecalculateMass();
// target.RecalculateMass();
// return success;
// }
// else
// {
// // Target has the cargo capacity to hold the desired quantity,
// // and there is already a matching item instance;
// // decrement the source's quantity and increment the target's quantity
// simpleCommoditySource.Quantity -= quantity;
// simpleCommodityTarget.Quantity += quantity;
// source.RecalculateMass();
// target.RecalculateMass();
// return success;
// }
// }
//
// }
//
// return false;
// }
2020-04-16 03:08:18 +01:00
}