2020-12-22 03:13:43 +00:00
/ * This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at https : //mozilla.org/MPL/2.0/. */
2020-04-09 10:33:16 +01:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2020-12-10 23:20:38 +00:00
using UniRx ;
2020-04-09 10:33:16 +01:00
using Unity.Mathematics ;
using static Unity . Mathematics . math ;
2020-12-10 23:20:38 +00:00
using int2 = Unity . Mathematics . int2 ;
2020-04-09 10:33:16 +01:00
2020-12-10 23:20:38 +00:00
public abstract class Entity
2020-04-09 10:33:16 +01:00
{
2020-12-10 23:20:38 +00:00
public Zone Zone ;
2021-03-20 04:20:17 +00:00
public Faction Faction ;
2020-12-10 23:20:38 +00:00
public EquippableItem Hull ;
2021-04-18 05:13:20 +01:00
public EquippedItem EquippedHull ;
2020-06-01 03:11:04 +01:00
2021-01-06 14:56:39 +00:00
public float3 Position ;
2020-06-01 03:11:04 +01:00
public float2 Direction = float2 ( 0 , 1 ) ;
public float2 Velocity ;
2020-12-10 23:20:38 +00:00
public float [ , ] Temperature ;
2021-01-17 23:26:53 +00:00
public float [ , ] NewTemperature ;
2021-01-17 02:45:52 +00:00
public bool2 [ , ] HullConductivity ;
2020-12-27 22:18:22 +00:00
public float [ , ] ThermalMass ;
2020-06-05 04:16:33 +01:00
2020-12-19 02:37:13 +00:00
public readonly ReactiveCollection < EquippedItem > Equipment = new ReactiveCollection < EquippedItem > ( ) ;
public readonly ReactiveCollection < EquippedCargoBay > CargoBays = new ReactiveCollection < EquippedCargoBay > ( ) ;
public readonly ReactiveCollection < EquippedDockingBay > DockingBays = new ReactiveCollection < EquippedDockingBay > ( ) ;
2021-02-26 19:01:47 +00:00
public readonly ReactiveCollection < Entity > VisibleEntities = new ReactiveCollection < Entity > ( ) ;
2021-05-11 04:45:42 +01:00
public readonly ReactiveDictionary < Entity , bool > EntityHostility = new ReactiveDictionary < Entity , bool > ( ) ;
public readonly ReactiveCollection < Entity > VisibleEnemies = new ReactiveCollection < Entity > ( ) ;
public readonly ReactiveCollection < Entity > VisibleFriendlies = new ReactiveCollection < Entity > ( ) ;
2020-12-10 23:20:38 +00:00
public Entity Parent ;
public List < Entity > Children = new List < Entity > ( ) ;
2021-01-10 03:59:47 +00:00
public ReactiveProperty < Entity > Target = new ReactiveProperty < Entity > ( ( Entity ) null ) ;
2021-01-05 02:56:17 +00:00
public float3 LookDirection ;
2020-06-01 03:11:04 +01:00
public string Name ;
public int Population ;
public Dictionary < Guid , float > Personality = new Dictionary < Guid , float > ( ) ;
2020-05-06 04:56:36 +01:00
2020-12-10 23:20:38 +00:00
public readonly Dictionary < string , float > Messages = new Dictionary < string , float > ( ) ;
public readonly Dictionary < object , float > VisibilitySources = new Dictionary < object , float > ( ) ;
2021-02-26 19:01:47 +00:00
public readonly ReactiveDictionary < Entity , float > EntityInfoGathered = new ReactiveDictionary < Entity , float > ( ) ;
2021-01-08 12:44:10 +00:00
public readonly Dictionary < HardpointData , ( float3 position , float3 direction ) > HardpointTransforms =
2021-01-15 20:07:39 +00:00
new Dictionary < HardpointData , ( float3 position , float3 direction ) > ( ) ;
2020-06-02 07:01:03 +01:00
2021-05-02 20:59:45 +01:00
public ( List < Weapon > weapons , List < EquippedItem > items ) [ ] WeaponGroups ;
2020-12-31 19:53:16 +00:00
public List < IPopulationAssignment > PopulationAssignments = new List < IPopulationAssignment > ( ) ;
2020-12-27 22:18:22 +00:00
2020-12-12 03:42:58 +00:00
public EquippedItem [ , ] GearOccupancy ;
public HardpointData [ , ] Hardpoints ;
2021-01-08 12:44:10 +00:00
public float [ , ] Armor ;
2021-02-01 08:13:14 +00:00
public float [ , ] MaxArmor ;
2020-12-10 23:20:38 +00:00
2020-12-27 22:18:22 +00:00
private EquippedItem [ ] _orderedEquipment ;
2021-02-11 03:14:20 +00:00
private List < Weapon > _weapons = new List < Weapon > ( ) ;
2021-03-02 23:02:19 +00:00
private List < Capacitor > _capacitors = new List < Capacitor > ( ) ;
private List < Reactor > _reactors = new List < Reactor > ( ) ;
2021-03-04 03:58:46 +00:00
private List < Radiator > _heatsinks = new List < Radiator > ( ) ;
2021-04-18 05:13:20 +01:00
private List < ConsumableItemEffect > _activeConsumables = new List < ConsumableItemEffect > ( ) ;
2021-01-05 02:56:17 +00:00
protected bool _active ;
2021-03-04 03:58:46 +00:00
private bool _heatsinksEnabled = true ;
public bool HeatsinksEnabled
{
get = > _heatsinksEnabled ;
set
{
if ( value = = _heatsinksEnabled ) return ;
_heatsinksEnabled = value ;
foreach ( var heatsink in _heatsinks )
2021-03-07 01:10:31 +00:00
heatsink . Item . Enabled . Value = value ;
2021-03-04 03:58:46 +00:00
}
}
2021-03-20 04:20:17 +00:00
public HullData HullData { get ; }
2021-03-14 03:56:49 +00:00
public EntitySettings Settings { get ; }
2021-03-07 01:10:31 +00:00
public bool OverrideShutdown { get ; set ; }
2021-04-15 04:25:22 +01:00
public float TractorPower { get ; set ; }
2021-03-04 03:58:46 +00:00
public bool Active
{
get = > _active ;
}
2020-12-27 22:18:22 +00:00
2021-02-11 03:14:20 +00:00
public IEnumerable < Weapon > Weapons
{
get = > _weapons ;
}
2021-03-06 01:04:26 +00:00
public Shield Shield { get ; private set ; }
2021-03-06 02:42:19 +00:00
public Cockpit Cockpit { get ; private set ; }
2021-03-21 01:55:25 +00:00
public Sensor Sensor { get ; private set ; }
2021-03-06 02:42:19 +00:00
public float Heatstroke { get ; private set ; }
2021-04-10 01:47:16 +01:00
public float Hypothermia { get ; private set ; }
2021-03-06 01:04:26 +00:00
2021-02-26 19:01:47 +00:00
public float TargetRange { get ; private set ; }
2021-01-27 00:05:30 +00:00
public float MaxTemp { get ; private set ; }
public float MinTemp { get ; private set ; }
2020-12-10 23:20:38 +00:00
public ItemManager ItemManager { get ; }
public int AssignedPopulation = > PopulationAssignments . Sum ( pa = > pa . AssignedPopulation ) ;
public float Mass { get ; private set ; }
public float Visibility = > VisibilitySources . Values . Sum ( ) ;
2021-04-27 16:44:44 +01:00
public Subject < Entity > IncomingHit = new Subject < Entity > ( ) ;
2021-01-08 12:44:10 +00:00
public Subject < ( int2 pos , float damage ) > ArmorDamage = new Subject < ( int2 , float ) > ( ) ;
public Subject < ( EquippedItem item , float damage ) > ItemDamage = new Subject < ( EquippedItem , float ) > ( ) ;
2021-03-07 01:10:31 +00:00
// public Subject<EquippedItem> ItemOffline = new Subject<EquippedItem>();
// public Subject<EquippedItem> ItemOnline = new Subject<EquippedItem>();
2021-01-08 12:44:10 +00:00
public Subject < float > HullDamage = new Subject < float > ( ) ;
2021-02-26 19:01:47 +00:00
public Subject < Entity > Docked = new Subject < Entity > ( ) ;
2021-03-06 02:42:19 +00:00
public Subject < Unit > HeatstrokeRisk = new Subject < Unit > ( ) ;
2021-03-09 02:26:58 +00:00
public Subject < Unit > HeatstrokeDeath = new Subject < Unit > ( ) ;
2021-04-10 01:47:16 +01:00
public Subject < Unit > HypothermiaRisk = new Subject < Unit > ( ) ;
public Subject < Unit > HypothermiaDeath = new Subject < Unit > ( ) ;
2021-04-30 11:19:25 +01:00
public Subject < Entity > TargetedBy = new Subject < Entity > ( ) ;
public ReactiveProperty < int > TargetedByCount = new ReactiveProperty < int > ( 0 ) ;
2021-05-11 04:45:42 +01:00
public ReactiveProperty < SecurityLevel > CurrentSecurityLevel = new ReactiveProperty < SecurityLevel > ( SecurityLevel . Open ) ;
public ReadOnlyReactiveProperty < bool > PresencePermitted ;
2021-01-10 03:59:47 +00:00
public UniRx . IObservable < EquippedItem > ItemDestroyed ;
public UniRx . IObservable < int2 > HullArmorDepleted ;
public UniRx . IObservable < HardpointData > HardpointArmorDepleted ;
2021-02-26 19:01:47 +00:00
public UniRx . IObservable < Weapon > WeaponDestroyed ;
2021-04-10 01:47:16 +01:00
public UniRx . IObservable < CauseOfDeath > Death ;
2021-01-08 12:44:10 +00:00
2021-02-26 19:01:47 +00:00
private List < IDisposable > _subscriptions = new List < IDisposable > ( ) ;
2021-05-11 04:45:42 +01:00
private Dictionary < Entity , List < IDisposable > > _watchedEntitySubscriptions = new Dictionary < Entity , List < IDisposable > > ( ) ;
2021-02-26 19:01:47 +00:00
public virtual void Activate ( )
2020-07-03 01:33:42 +01:00
{
2021-03-21 01:55:25 +00:00
//ItemManager.Log($"Entity {Name} is activating!");
2021-02-26 19:01:47 +00:00
_active = true ;
2021-03-06 02:42:19 +00:00
Heatstroke = 0 ;
2021-02-26 19:01:47 +00:00
foreach ( var item in Equipment )
foreach ( var behavior in item . Behaviors )
2020-07-03 01:33:42 +01:00
{
2021-02-26 19:01:47 +00:00
if ( behavior is IInitializableBehavior initializableBehavior )
initializableBehavior . Initialize ( ) ;
2020-07-03 01:33:42 +01:00
}
2021-05-11 04:45:42 +01:00
foreach ( var entity in Zone . Entities )
{
EntityInfoGathered [ entity ] = 0 ;
EntityHostility [ entity ] = IsHostileTo ( entity ) ;
}
_subscriptions . Add ( Zone . Entities . ObserveAdd ( ) . Subscribe ( add = >
{
EntityInfoGathered [ add . Value ] = 0 ;
EntityHostility [ add . Value ] = IsHostileTo ( add . Value ) ;
} ) ) ;
2021-02-26 19:01:47 +00:00
_subscriptions . Add ( Zone . Entities . ObserveRemove ( ) . Subscribe ( remove = >
{
if ( Target . Value = = remove . Value ) Target . Value = null ;
EntityInfoGathered . Remove ( remove . Value ) ;
2021-05-11 04:45:42 +01:00
EntityHostility . Remove ( remove . Value ) ;
2021-02-26 19:01:47 +00:00
VisibleEntities . Remove ( remove . Value ) ;
2021-05-11 04:45:42 +01:00
VisibleEnemies . Remove ( remove . Value ) ;
VisibleFriendlies . Remove ( remove . Value ) ;
2021-02-26 19:01:47 +00:00
} ) ) ;
2021-05-11 04:45:42 +01:00
_subscriptions . Add ( VisibleEnemies . ObserveRemove ( ) . Subscribe ( remove = >
2021-02-26 19:01:47 +00:00
{
if ( Target . Value = = remove . Value ) Target . Value = null ;
} ) ) ;
2021-05-11 04:45:42 +01:00
_subscriptions . Add ( Target . Subscribe ( entity = > entity ? . TargetedBy . OnNext ( this ) ) ) ;
_subscriptions . Add ( TargetedBy . Subscribe ( enemy = >
{
TargetedByCount . Value + + ;
enemy . Target . Where ( t = > t ! = this ) . Take ( 1 ) . Subscribe ( _ = > TargetedByCount . Value - - ) ;
} ) ) ;
//
_subscriptions . Add ( EntityInfoGathered . ObserveReplace ( ) . Subscribe ( replace = >
{
if ( replace . OldValue < ItemManager . GameplaySettings . TargetDetectionInfoThreshold & &
replace . NewValue > ItemManager . GameplaySettings . TargetDetectionInfoThreshold )
{
VisibleEntities . Add ( replace . Key ) ;
if ( EntityHostility [ replace . Key ] )
VisibleEnemies . Add ( replace . Key ) ;
else VisibleFriendlies . Add ( replace . Key ) ;
}
if ( replace . OldValue > ItemManager . GameplaySettings . TargetDetectionInfoThreshold & &
replace . NewValue < ItemManager . GameplaySettings . TargetDetectionInfoThreshold )
{
VisibleEntities . Remove ( replace . Key ) ;
VisibleEnemies . Remove ( replace . Key ) ;
VisibleFriendlies . Remove ( replace . Key ) ;
}
} ) ) ;
// Create subscriptions to events occurring on any visible entity
_subscriptions . Add ( VisibleEntities . ObserveAdd ( )
. Select ( add = > add . Value )
. Subscribe ( entity = >
{
var newVisibleEntitySubscriptions = new List < IDisposable >
{
// Respond to changes in the target's hostility status by updating the contents of VisibleEnemies and VisibleFriendlies
EntityHostility . ObserveReplace ( )
. Where ( replace = > replace . Key = = entity )
. Select ( replace = > replace . NewValue )
. Subscribe ( isHostile = >
{
( isHostile ? VisibleEnemies : VisibleFriendlies ) . Add ( entity ) ;
( isHostile ? VisibleFriendlies : VisibleEnemies ) . Remove ( entity ) ;
} )
} ;
_watchedEntitySubscriptions [ entity ] = newVisibleEntitySubscriptions ;
} ) ) ;
// Cleanup visible entity subscriptions
_subscriptions . Add ( VisibleEntities . ObserveRemove ( ) . Subscribe ( disappearingEntity = >
{
foreach ( var subscription in _watchedEntitySubscriptions [ disappearingEntity . Value ] ) subscription . Dispose ( ) ;
_watchedEntitySubscriptions . Remove ( disappearingEntity . Value ) ;
} ) ) ;
2021-05-02 20:59:45 +01:00
if ( WeaponGroups . All ( wg = > ! wg . items . Any ( ) ) )
GenerateWeaponGroups ( ) ;
2020-07-03 01:33:42 +01:00
}
2020-05-25 03:05:30 +01:00
2021-02-26 19:01:47 +00:00
public virtual void Deactivate ( )
{
2021-03-21 01:55:25 +00:00
//ItemManager.Log($"Entity {Name} is deactivating!");
2021-02-26 19:01:47 +00:00
foreach ( var s in _subscriptions ) s . Dispose ( ) ;
_subscriptions . Clear ( ) ;
2021-05-11 04:45:42 +01:00
foreach ( var ss in _watchedEntitySubscriptions . Values ) foreach ( var s in ss ) s . Dispose ( ) ;
_watchedEntitySubscriptions . Clear ( ) ;
2021-02-26 19:01:47 +00:00
_active = false ;
EntityInfoGathered . Clear ( ) ;
VisibleEntities . Clear ( ) ;
2021-05-11 04:45:42 +01:00
VisibleEnemies . Clear ( ) ;
VisibleFriendlies . Clear ( ) ;
2021-02-26 19:01:47 +00:00
}
2021-01-05 02:56:17 +00:00
2021-03-14 03:56:49 +00:00
public Entity ( ItemManager itemManager , Zone zone , EquippableItem hull , EntitySettings settings )
2020-04-09 10:33:16 +01:00
{
2021-03-14 03:56:49 +00:00
Settings = settings ;
2020-12-10 23:20:38 +00:00
ItemManager = itemManager ;
2020-05-07 03:53:54 +01:00
Zone = zone ;
2020-04-23 03:31:17 +01:00
Hull = hull ;
2021-03-20 04:20:17 +00:00
HullData = itemManager . GetData ( hull ) as HullData ;
2021-03-22 05:22:31 +00:00
Name = HullData . Name ;
2021-01-08 12:44:10 +00:00
MapEntity ( ) ;
2021-05-02 20:59:45 +01:00
WeaponGroups = new ( List < Weapon > weapons , List < EquippedItem > items ) [ itemManager . GameplaySettings . WeaponGroupCount ] ;
for ( int i = 0 ; i < itemManager . GameplaySettings . WeaponGroupCount ; i + + )
WeaponGroups [ i ] = ( new List < Weapon > ( ) , new List < EquippedItem > ( ) ) ;
2021-01-10 03:59:47 +00:00
ItemDestroyed = ItemDamage . Where ( x = > x . item . EquippableItem . Durability < . 01f ) . Select ( x = > x . item ) ;
2021-02-26 19:01:47 +00:00
WeaponDestroyed = ItemDestroyed . Select ( x = > x . Behaviors . FirstOrDefault ( b = > b is Weapon ) as Weapon ) . Where ( x = > x ! = null ) ;
2021-01-10 03:59:47 +00:00
HullArmorDepleted = ArmorDamage . Where ( x = > Armor [ x . pos . x , x . pos . y ] < . 01f ) . Select ( x = > x . pos ) ;
2021-04-10 01:47:16 +01:00
Death = HullDamage . Where ( _ = > Hull . Durability < . 01f ) . Select ( _ = > CauseOfDeath . HullDestroyed )
. Merge ( HeatstrokeDeath . Select ( _ = > CauseOfDeath . Heatstroke ) )
. Merge ( HypothermiaDeath . Select ( _ = > CauseOfDeath . Hypothermia ) )
. Merge ( ItemDestroyed . Where ( i = > i . GetBehavior < Cockpit > ( ) ! = null ) . Select ( _ = > CauseOfDeath . CockpitDestroyed ) ) ;
2021-02-26 19:01:47 +00:00
2021-05-11 04:45:42 +01:00
// The entity's presence is permitted when its faction owns the zone, or the current security level at its location is low enough
PresencePermitted = new ReadOnlyReactiveProperty < bool > ( CurrentSecurityLevel . Select ( security = >
IsPresencePermitted ( GetFactionRelationship ( Zone . SectorZone . Owner ) , security ) ) , true ) ;
}
2021-04-30 11:19:25 +01:00
2021-05-11 04:45:42 +01:00
public bool IsHostileTo ( Entity other , bool recursive = false )
{
if ( Faction = = null )
return ! recursive & & other . Faction ! = null & & other . IsHostileTo ( this , true ) ;
// TODO: Inter-faction hostility
// When the entity faction owns the zone, they are hostile to trespassers or those hostile to them
if ( Faction . ID = = Zone . SectorZone . Owner . ID )
return recursive ? ! other . PresencePermitted . Value : ! other . PresencePermitted . Value | | other . IsHostileTo ( this , true ) ;
return ! recursive & & other . IsHostileTo ( this , true ) ;
2021-02-26 19:01:47 +00:00
}
2021-05-11 04:45:42 +01:00
// TODO: Inter-faction relationships
public FactionRelationship GetFactionRelationship ( Faction faction ) = >
this is Ship { IsPlayerShip : true } ? Zone . Sector . FactionRelationships [ faction ] : faction = = Faction ? FactionRelationship . Beloved : FactionRelationship . Neutral ;
2021-05-12 00:15:11 +01:00
public static bool IsPresencePermitted ( FactionRelationship relationship , SecurityLevel securityLevel ) = >
( int ) relationship - ( int ) securityLevel > 0 ;
public static bool IsDockingPermitted ( FactionRelationship relationship , SecurityLevel securityLevel ) = >
( int ) relationship - ( int ) securityLevel > 1 ;
2021-05-11 04:45:42 +01:00
2021-04-18 05:13:20 +01:00
public void ActivateConsumable ( ConsumableItem item )
{
_activeConsumables . Add ( new ConsumableItemEffect ( item , this ) ) ;
}
public ConsumableItemEffect FindActiveConsumable ( ConsumableItemData data )
{
return _activeConsumables . FirstOrDefault ( ac = > ac . Data . ID = = data . ID ) ;
}
public bool CanActivateConsumable ( ConsumableItemData data )
{
return data . Stackable | | FindActiveConsumable ( data ) = = null ;
}
public bool TryActivateConsumable ( ConsumableItemData data )
{
if ( ! CanActivateConsumable ( data ) ) return false ;
var bay = FindItemInCargo ( data . ID ) ;
if ( bay = = null ) return false ;
var item = ( ConsumableItem ) bay . ItemsOfType [ data . ID ] . First ( ) ;
ActivateConsumable ( item ) ;
bay . Remove ( item ) ;
return true ;
}
2021-01-08 12:44:10 +00:00
private void MapEntity ( )
2020-06-05 04:16:33 +01:00
{
2020-12-10 23:20:38 +00:00
var hullData = ItemManager . GetData ( Hull ) as HullData ;
2021-04-18 05:13:20 +01:00
EquippedHull = new EquippedItem ( ItemManager , Hull , int2 . zero , this ) ;
Equipment . Add ( EquippedHull ) ;
2020-12-22 03:13:43 +00:00
Mass = hullData . Mass ;
2020-12-27 22:18:22 +00:00
Temperature = new float [ hullData . Shape . Width , hullData . Shape . Height ] ;
2021-01-17 23:26:53 +00:00
NewTemperature = new float [ hullData . Shape . Width , hullData . Shape . Height ] ;
2021-01-17 02:45:52 +00:00
HullConductivity = new bool2 [ hullData . Shape . Width , hullData . Shape . Height ] ;
2020-12-27 22:18:22 +00:00
ThermalMass = new float [ hullData . Shape . Width , hullData . Shape . Height ] ;
2021-01-08 12:44:10 +00:00
Armor = new float [ hullData . Shape . Width , hullData . Shape . Height ] ;
2021-02-01 08:13:14 +00:00
MaxArmor = new float [ hullData . Shape . Width , hullData . Shape . Height ] ;
2020-12-12 03:42:58 +00:00
Hardpoints = new HardpointData [ hullData . Shape . Width , hullData . Shape . Height ] ;
2020-12-10 23:20:38 +00:00
foreach ( var hardpoint in hullData . Hardpoints )
2020-06-05 04:16:33 +01:00
{
2020-12-10 23:20:38 +00:00
foreach ( var hardpointCoord in hardpoint . Shape . Coordinates )
{
var hullCoord = hardpoint . Position + hardpointCoord ;
2020-12-12 03:42:58 +00:00
Hardpoints [ hullCoord . x , hullCoord . y ] = hardpoint ;
2020-12-10 23:20:38 +00:00
}
2020-07-03 01:33:42 +01:00
}
2021-02-01 08:13:14 +00:00
var cellCount = hullData . Shape . Coordinates . Length ;
foreach ( var v in hullData . Shape . Coordinates )
{
Armor [ v . x , v . y ] = hullData . Armor ;
MaxArmor [ v . x , v . y ] = hullData . Armor ;
if ( Hardpoints [ v . x , v . y ] ! = null )
{
Armor [ v . x , v . y ] + = Hardpoints [ v . x , v . y ] . Armor ;
MaxArmor [ v . x , v . y ] + = Hardpoints [ v . x , v . y ] . Armor ;
}
Temperature [ v . x , v . y ] = 280 ;
ThermalMass [ v . x , v . y ] = hullData . Mass * hullData . SpecificHeat / cellCount ;
}
GearOccupancy = new EquippedItem [ hullData . Shape . Width , hullData . Shape . Height ] ;
2020-05-06 04:56:36 +01:00
}
2021-05-02 20:59:45 +01:00
public void GenerateWeaponGroups ( )
2021-02-26 19:01:47 +00:00
{
foreach ( var group in Weapons
2021-04-18 05:13:20 +01:00
. GroupBy ( w = > w . Item . EquippableItem . Data . LinkID )
. OrderBy ( wg = > wg . Average ( w = > w . Range ) )
2021-02-26 19:01:47 +00:00
. Select ( ( weapons , index ) = > ( weapons , index ) ) )
{
2021-05-02 20:59:45 +01:00
WeaponGroups [ group . index ] . weapons = group . weapons . ToList ( ) ;
WeaponGroups [ group . index ] . items = group . weapons . Select ( w = > w . Item ) . ToList ( ) ;
2021-02-26 19:01:47 +00:00
}
}
2021-02-01 08:13:14 +00:00
public void AddHeat ( int2 position , float heat , bool ignoreThermalMass = false )
2020-05-06 04:56:36 +01:00
{
2021-02-01 08:13:14 +00:00
if ( ignoreThermalMass )
Temperature [ position . x , position . y ] + = heat ;
else
Temperature [ position . x , position . y ] + = heat / ThermalMass [ position . x , position . y ] ;
2020-12-31 19:53:16 +00:00
}
2020-06-11 02:55:11 +01:00
2020-12-31 19:53:16 +00:00
public int CountItemsInCargo ( Guid itemDataID )
{
int sum = 0 ;
foreach ( var x in CargoBays )
2020-07-03 01:33:42 +01:00
{
2020-12-31 19:53:16 +00:00
if ( x . ItemsOfType . ContainsKey ( itemDataID ) )
{
foreach ( var i in x . ItemsOfType [ itemDataID ] ) sum + = i is SimpleCommodity simpleCommodity ? simpleCommodity . Quantity : 1 ;
}
2020-07-03 01:33:42 +01:00
}
2020-06-05 04:16:33 +01:00
2020-12-31 19:53:16 +00:00
return sum ;
2020-12-27 22:18:22 +00:00
}
2020-12-10 23:20:38 +00:00
public EquippedCargoBay FindItemInCargo ( Guid itemDataID )
2020-06-05 04:16:33 +01:00
{
2020-12-19 02:37:13 +00:00
return CargoBays . FirstOrDefault ( c = > c . ItemsOfType . ContainsKey ( itemDataID ) ) ;
2020-06-10 02:45:10 +01:00
}
2021-03-20 04:20:17 +00:00
public Shape UnoccupiedSpace
{
get
{
var emptyShape = new Shape ( HullData . Shape . Width , HullData . Shape . Height ) ;
foreach ( var v in HullData . Shape . Coordinates )
{
if ( HullData . InteriorCells [ v ] & & GearOccupancy [ v . x , v . y ] = = null & & Hardpoints [ v . x , v . y ] = = null )
emptyShape [ v ] = true ;
}
return emptyShape ;
}
}
2020-12-10 23:20:38 +00:00
// Attempts to move a given number of items of the given type to the target Entity
// Returns the number of items successfully transferred
public int TryTransferItems ( Entity target , Guid itemDataID , int quantity )
2020-06-10 02:45:10 +01:00
{
2020-12-10 23:20:38 +00:00
int quantityTransferred = 0 ;
while ( quantityTransferred < quantity )
{
2020-12-19 02:37:13 +00:00
EquippedCargoBay originInventory = CargoBays . FirstOrDefault ( c = > c . ItemsOfType . ContainsKey ( itemDataID ) ) ;
2020-12-10 23:20:38 +00:00
if ( originInventory = = null ) break ;
var itemInstance = originInventory . ItemsOfType [ itemDataID ] [ 0 ] ;
if ( itemInstance is SimpleCommodity simpleCommodity )
{
var targetQuantity = min ( simpleCommodity . Quantity , quantity - quantityTransferred ) ;
2020-12-19 02:37:13 +00:00
if ( ! target . CargoBays . Any ( c = > originInventory . TryTransferItem ( c , simpleCommodity , targetQuantity ) ) )
2020-12-10 23:20:38 +00:00
{
quantityTransferred + = targetQuantity - simpleCommodity . Quantity ;
break ;
}
quantityTransferred + = targetQuantity ;
}
else if ( itemInstance is CraftedItemInstance craftedItemInstance )
{
2020-12-19 02:37:13 +00:00
if ( ! target . CargoBays . Any ( c = > originInventory . TryTransferItem ( c , craftedItemInstance ) ) )
2020-12-10 23:20:38 +00:00
break ;
quantityTransferred + + ;
}
}
2020-06-10 02:45:10 +01:00
2020-12-10 23:20:38 +00:00
return quantityTransferred ;
2020-06-05 04:16:33 +01:00
}
2021-03-04 03:58:46 +00:00
public EquippableItem TryUnequip ( EquippedItem item )
2020-06-05 04:16:33 +01:00
{
2021-03-04 03:58:46 +00:00
// Don't allow unequipping when the entity is active
if ( _active ) return null ;
2020-12-10 23:20:38 +00:00
if ( item . EquippableItem = = null )
{
ItemManager . Log ( "Attempted to remove equipped item with no equippable item on it! This should be impossible!" ) ;
return null ;
}
2020-06-05 04:16:33 +01:00
2020-12-10 23:20:38 +00:00
if ( item is EquippedCargoBay cargoBay )
{
if ( cargoBay . Cargo . Count > 0 )
{
ItemManager . Log ( "Attempted to remove cargo bay that is not empty! Please check first before doing this!" ) ;
return null ;
}
2020-06-05 04:16:33 +01:00
2020-12-19 02:37:13 +00:00
CargoBays . Remove ( cargoBay ) ;
2020-12-10 23:20:38 +00:00
}
2020-12-19 02:37:13 +00:00
Equipment . Remove ( item ) ;
2020-12-27 22:18:22 +00:00
_orderedEquipment = Equipment . OrderBy ( x = > x . SortPosition ) . ToArray ( ) ;
2020-12-10 23:20:38 +00:00
2020-12-20 03:04:52 +00:00
var hullData = ItemManager . GetData ( Hull ) as HullData ;
2020-12-10 23:20:38 +00:00
var itemData = ItemManager . GetData ( item . EquippableItem ) ;
2020-12-20 03:04:52 +00:00
foreach ( var i in hullData . Shape . Coordinates )
2021-01-17 02:45:52 +00:00
if ( GearOccupancy [ i . x , i . y ] = = item )
2020-12-27 22:18:22 +00:00
{
ThermalMass [ i . x , i . y ] - = ItemManager . GetThermalMass ( item . EquippableItem ) / itemData . Shape . Coordinates . Length ;
2021-01-17 02:45:52 +00:00
GearOccupancy [ i . x , i . y ] = null ;
2020-12-27 22:18:22 +00:00
}
2020-12-22 03:13:43 +00:00
Mass - = itemData . Mass ;
2021-02-11 03:14:20 +00:00
foreach ( var b in item . Behaviors )
2021-03-02 23:02:19 +00:00
{
2021-02-11 03:14:20 +00:00
if ( b is Weapon weapon )
2021-03-02 23:02:19 +00:00
_weapons . Remove ( weapon ) ;
2021-03-06 01:04:26 +00:00
if ( b is Capacitor capacitor )
2021-03-02 23:02:19 +00:00
_capacitors . Remove ( capacitor ) ;
2021-03-06 01:04:26 +00:00
if ( b is Reactor reactor )
2021-03-02 23:02:19 +00:00
_reactors . Remove ( reactor ) ;
2021-03-06 01:04:26 +00:00
if ( b is Radiator heatsink )
2021-03-04 03:58:46 +00:00
_heatsinks . Remove ( heatsink ) ;
2021-03-06 01:04:26 +00:00
if ( b is Shield )
Shield = null ;
2021-03-06 02:42:19 +00:00
if ( b is Cockpit )
Cockpit = null ;
2021-03-21 01:55:25 +00:00
if ( b is Sensor )
Sensor = null ;
2021-03-02 23:02:19 +00:00
}
2020-06-05 04:16:33 +01:00
2020-12-10 23:20:38 +00:00
return item . EquippableItem ;
2020-06-05 04:16:33 +01:00
}
2020-12-10 23:20:38 +00:00
// Check whether the given item will fit when its origin is placed at the given coordinate
private bool ItemFits ( EquippableItemData itemData , HullData hullData , EquippableItem item , int2 hullCoord )
2020-06-05 04:16:33 +01:00
{
2020-12-20 03:04:52 +00:00
// If the given coordinate isn't even in the ship it obviously won't fit
if ( ! hullData . Shape [ hullCoord ] ) return false ;
2020-12-10 23:20:38 +00:00
// Items without specific hardpoints on the ship can be freely rotated and placed anywhere
2021-01-17 02:45:52 +00:00
if ( itemData . HardpointType = = HardpointType . Tool )
2020-12-10 23:20:38 +00:00
{
// Check every cell of the item's shape
foreach ( var i in itemData . Shape . Coordinates )
{
// If there is any gear already occupying that space, it won't fit
2021-01-05 02:56:17 +00:00
// If there's a hardpoint there, it won't fit
2020-12-10 23:20:38 +00:00
// Thermal items have their own layer and do not collide with gear
var itemCoord = hullCoord + itemData . Shape . Rotate ( i , item . Rotation ) ;
2021-01-17 02:45:52 +00:00
if ( ! hullData . InteriorCells [ itemCoord ] | |
itemData . HardpointType = = HardpointType . Tool & & Hardpoints [ itemCoord . x , itemCoord . y ] ! = null | |
GearOccupancy [ itemCoord . x , itemCoord . y ] ! = null )
return false ;
2020-12-10 23:20:38 +00:00
}
}
else
{
2020-12-12 03:42:58 +00:00
var hardpoint = Hardpoints [ hullCoord . x , hullCoord . y ] ;
2020-12-10 23:20:38 +00:00
// If there's no hardpoint there, it won't fit
if ( hardpoint = = null ) return false ;
// If the hardpoint type doesn't match the item, it won't fit
if ( hardpoint . Type ! = itemData . HardpointType ) return false ;
2020-12-20 03:04:52 +00:00
// Items placed in hardpoints are automatically aligned to hardpoint rotation
item . Rotation = hardpoint . Rotation ;
// Inset the shapes of both item and hardpoint
var itemShapeInset = hullData . Shape . Inset ( itemData . Shape , hullCoord , item . Rotation ) ;
var hardpointShapeInset = hullData . Shape . Inset ( hardpoint . Shape , hardpoint . Position ) ;
2021-03-18 00:36:34 +00:00
// Check every cell of the hardpoint shape for existing items
2021-01-08 12:44:10 +00:00
foreach ( var v in hardpointShapeInset . Coordinates )
if ( GearOccupancy [ v . x , v . y ] ! = null )
return false ;
2020-12-10 23:20:38 +00:00
// Check every cell of the item's shape
2020-12-20 03:04:52 +00:00
foreach ( var i in itemShapeInset . Coordinates )
2020-12-10 23:20:38 +00:00
{
// If the hardpoint does not have a matching cell, it wont fit
2020-12-20 03:04:52 +00:00
if ( ! hardpointShapeInset [ i ] ) return false ;
2020-12-10 23:20:38 +00:00
// If there is any gear already occupying that space, it won't fit
2020-12-20 03:04:52 +00:00
if ( GearOccupancy [ i . x , i . y ] ! = null ) return false ;
2020-12-10 23:20:38 +00:00
}
}
return true ;
2020-06-05 04:16:33 +01:00
}
2020-12-10 23:20:38 +00:00
// Check whether the given item will fit when its origin is placed at the given coordinate on the hull
public bool ItemFits ( EquippableItem item , int2 hullCoord )
2020-06-05 04:16:33 +01:00
{
2021-03-04 03:58:46 +00:00
// Don't allow equipping while deployed
if ( _active ) return false ;
2020-12-10 23:20:38 +00:00
var itemData = ItemManager . GetData ( item ) ;
var hullData = ItemManager . GetData ( Hull ) as HullData ;
return ItemFits ( itemData , hullData , item , hullCoord ) ;
2020-06-05 04:16:33 +01:00
}
2020-12-10 23:20:38 +00:00
public bool TryFindSpace ( EquippableItem item , out int2 hullCoord )
2020-06-05 04:16:33 +01:00
{
2021-03-04 03:58:46 +00:00
// Don't allow equipping while deployed
if ( _active )
{
hullCoord = int2 . zero ;
return false ;
}
2020-12-10 23:20:38 +00:00
var itemData = ItemManager . GetData ( item ) ;
var hullData = ItemManager . GetData ( Hull ) as HullData ;
2020-05-11 01:17:10 +01:00
2020-12-10 23:20:38 +00:00
// Tools and thermal equipment can be installed anywhere on the ship
// Search the whole ship for somewhere the item will fit
2021-01-17 02:45:52 +00:00
if ( itemData . HardpointType = = HardpointType . Tool )
2020-12-10 23:20:38 +00:00
{
foreach ( var hullCoord2 in hullData . InteriorCells . Coordinates )
{
if ( ItemFits ( itemData , hullData , item , hullCoord2 ) )
{
hullCoord = hullCoord2 ;
return true ;
}
}
}
2020-05-06 04:56:36 +01:00
2020-12-10 23:20:38 +00:00
// Everything else has to be equipped onto a hardpoint of the same type
// Search the ship for an empty hardpoint that matches the type and shape of the item
else
2020-06-05 04:16:33 +01:00
{
2020-12-10 23:20:38 +00:00
foreach ( var hardpoint in hullData . Hardpoints )
{
if ( hardpoint . Type = = itemData . HardpointType )
{
foreach ( var hardpointCoord in hardpoint . Shape . Coordinates )
{
var hullCoord2 = hardpoint . Position + hardpointCoord ;
if ( ItemFits ( itemData , hullData , item , hullCoord2 ) )
{
hullCoord = hullCoord2 ;
return true ;
}
}
}
}
2020-06-05 04:16:33 +01:00
}
2020-04-28 01:50:50 +01:00
2020-12-10 23:20:38 +00:00
hullCoord = int2 . zero ;
return false ;
2020-04-28 01:50:50 +01:00
}
2020-12-10 23:20:38 +00:00
// Try to equip the given item anywhere it will fit, returns true when the item was successfully equipped
public bool TryEquip ( EquippableItem item ) = > TryFindSpace ( item , out var hullCoord ) & & TryEquip ( item , hullCoord ) ;
// Try to equip the given item to the given location
public bool TryEquip ( EquippableItem item , int2 hullCoord )
2020-06-02 07:01:03 +01:00
{
2021-03-04 03:58:46 +00:00
// Don't allow equipping while deployed
if ( _active ) return false ;
2020-12-10 23:20:38 +00:00
var itemData = ItemManager . GetData ( item ) ;
var hullData = ItemManager . GetData ( Hull ) as HullData ;
if ( ! ItemFits ( itemData , hullData , item , hullCoord ) ) return false ;
2020-06-05 04:16:33 +01:00
2021-01-06 14:56:39 +00:00
EquippedItem equippedItem ;
2021-01-17 02:45:52 +00:00
if ( itemData . HardpointType = = HardpointType . Tool )
2020-12-10 23:20:38 +00:00
{
if ( itemData is CargoBayData )
{
2020-12-19 02:37:13 +00:00
if ( itemData is DockingBayData )
{
2020-12-27 22:18:22 +00:00
equippedItem = new EquippedDockingBay ( ItemManager , item , hullCoord , this , $"{Name} Docking Bay {DockingBays.Count + 1}" ) ;
2020-12-19 02:37:13 +00:00
DockingBays . Add ( ( EquippedDockingBay ) equippedItem ) ;
}
else
{
2020-12-27 22:18:22 +00:00
equippedItem = new EquippedCargoBay ( ItemManager , item , hullCoord , this , $"{Name} Cargo Bay {CargoBays.Count + 1}" ) ;
2020-12-19 02:37:13 +00:00
CargoBays . Add ( ( EquippedCargoBay ) equippedItem ) ;
}
2020-12-10 23:20:38 +00:00
}
else
{
2020-12-27 22:18:22 +00:00
equippedItem = new EquippedItem ( ItemManager , item , hullCoord , this ) ;
2020-12-19 02:37:13 +00:00
Equipment . Add ( equippedItem ) ;
2020-12-10 23:20:38 +00:00
}
}
2020-06-02 07:01:03 +01:00
else
2020-06-05 04:16:33 +01:00
{
2021-01-06 14:56:39 +00:00
equippedItem = new EquippedItem ( ItemManager , item , hullCoord , this ) ;
2020-12-19 02:37:13 +00:00
Equipment . Add ( equippedItem ) ;
2021-01-06 14:56:39 +00:00
}
2021-03-03 01:53:23 +00:00
foreach ( var b in equippedItem . Behaviors )
{
if ( b is Weapon weapon )
_weapons . Add ( weapon ) ;
if ( b is Capacitor capacitor )
_capacitors . Add ( capacitor ) ;
if ( b is Reactor reactor )
_reactors . Add ( reactor ) ;
2021-03-04 03:58:46 +00:00
if ( b is Radiator heatsink )
_heatsinks . Add ( heatsink ) ;
2021-03-06 01:04:26 +00:00
if ( b is Shield shield )
Shield = shield ;
2021-03-06 02:42:19 +00:00
if ( b is Cockpit cockpit )
Cockpit = cockpit ;
2021-03-21 01:55:25 +00:00
if ( b is Sensor sensor )
Sensor = sensor ;
2021-03-03 01:53:23 +00:00
}
2021-03-07 01:10:31 +00:00
// equippedItem.OnOnline += () => ItemOnline.OnNext(equippedItem);
// equippedItem.OnOffline += () => ItemOffline.OnNext(equippedItem);
2020-12-10 23:20:38 +00:00
2021-01-06 14:56:39 +00:00
foreach ( var i in itemData . Shape . Coordinates )
{
var occupiedCoord = hullCoord + itemData . Shape . Rotate ( i , item . Rotation ) ;
// TODO: Track thermal mass of cargo bay contents as reactive property
ThermalMass [ occupiedCoord . x , occupiedCoord . y ] + = ItemManager . GetThermalMass ( item ) / itemData . Shape . Coordinates . Length ;
2021-01-17 02:45:52 +00:00
GearOccupancy [ occupiedCoord . x , occupiedCoord . y ] = equippedItem ;
2020-06-05 04:16:33 +01:00
}
2021-01-06 14:56:39 +00:00
Mass + = itemData . Mass ;
_orderedEquipment = Equipment . OrderBy ( x = > x . SortPosition ) . ToArray ( ) ;
return true ;
2020-12-19 02:37:13 +00:00
}
2020-06-05 04:16:33 +01:00
2020-12-19 02:37:13 +00:00
public EquippedDockingBay TryDock ( Ship ship )
{
2021-05-12 00:15:11 +01:00
if ( ! IsDockingPermitted ( ship . GetFactionRelationship ( Faction ) , CurrentSecurityLevel . Value ) ) return null ;
2020-12-19 02:37:13 +00:00
var bay = DockingBays . FirstOrDefault ( x = > x . DockedShip = = null ) ;
if ( bay ! = null )
{
bay . DockedShip = ship ;
ship . SetParent ( this ) ;
Zone . Entities . Remove ( ship ) ;
2021-03-21 01:55:25 +00:00
ship . Deactivate ( ) ;
2021-02-26 19:01:47 +00:00
ship . Docked . OnNext ( this ) ;
2020-12-19 02:37:13 +00:00
}
return bay ;
}
public bool TryUndock ( Ship ship )
{
var bay = DockingBays . FirstOrDefault ( x = > x . DockedShip = = ship ) ;
if ( bay = = null )
{
ItemManager . Log ( $"Ship {ship.Name} attempted to undock from {Name}, but it was not docked!" ) ;
return false ;
}
if ( bay . Cargo . Any ( ) )
return false ;
bay . DockedShip = null ;
ship . RemoveParent ( ) ;
Zone . Entities . Add ( ship ) ;
2021-02-26 19:01:47 +00:00
ship . Activate ( ) ;
2020-12-19 02:37:13 +00:00
return true ;
2020-06-05 04:16:33 +01:00
}
2021-03-06 01:04:26 +00:00
public bool CanConsumeEnergy ( float energy )
{
var capEnergy = _capacitors . Sum ( cap = > cap . Charge ) ;
2021-03-07 01:10:31 +00:00
int onlineReactors = _reactors . Count ( reactor = > reactor . Item . Online . Value ) ;
2021-03-06 01:04:26 +00:00
return capEnergy > energy | | onlineReactors > 0 ;
}
2021-03-02 23:02:19 +00:00
public bool TryConsumeEnergy ( float energy )
{
if ( energy < . 01f ) return true ;
int chargedCapacitors ;
do
{
chargedCapacitors = _capacitors . Count ( capacitor = > capacitor . Charge > . 01f ) ;
var chargeToRemove = energy ;
foreach ( var cap in _capacitors )
{
if ( cap . Charge > 0.01f )
{
var chargeRemoved = min ( chargeToRemove / chargedCapacitors , cap . Charge ) ;
cap . AddCharge ( - chargeRemoved ) ;
energy - = chargeRemoved ;
}
}
} while ( chargedCapacitors > 0 & & energy > . 01f ) ;
if ( energy < . 01f ) return true ;
2021-03-07 01:10:31 +00:00
int onlineReactors = _reactors . Count ( reactor = > reactor . Item . Online . Value ) ;
2021-03-02 23:02:19 +00:00
foreach ( var reactor in _reactors )
{
2021-03-07 01:10:31 +00:00
if ( reactor . Item . Online . Value )
2021-03-02 23:02:19 +00:00
{
reactor . ConsumeEnergy ( energy / onlineReactors ) ;
}
}
return onlineReactors > 0 ;
}
2021-01-17 02:45:52 +00:00
private void AddChild ( Entity entity )
2020-06-05 04:16:33 +01:00
{
Mass + = entity . Mass ;
2020-12-10 23:20:38 +00:00
Children . Add ( entity ) ;
2020-06-05 04:16:33 +01:00
}
2020-06-02 07:01:03 +01:00
2021-01-17 02:45:52 +00:00
private void RemoveChild ( Entity entity )
2020-06-05 04:16:33 +01:00
{
Mass - = entity . Mass ;
2020-12-10 23:20:38 +00:00
Children . Remove ( entity ) ;
2020-06-02 07:01:03 +01:00
}
2020-12-01 00:16:11 +00:00
public void SetParent ( Entity parent )
{
2020-12-10 23:20:38 +00:00
Parent = parent ;
2020-12-01 00:16:11 +00:00
parent . AddChild ( this ) ;
}
public void RemoveParent ( )
{
2020-12-10 23:20:38 +00:00
if ( Parent = = null )
2020-12-01 00:16:11 +00:00
return ;
2020-12-10 23:20:38 +00:00
Parent . RemoveChild ( this ) ;
Parent = null ;
2020-12-01 00:16:11 +00:00
}
2020-06-02 07:01:03 +01:00
2021-04-18 05:13:20 +01:00
public T GetBehavior < T > ( ) where T : Behavior
2020-06-02 07:01:03 +01:00
{
2020-12-19 02:37:13 +00:00
foreach ( var equippedItem in Equipment )
2020-12-27 22:18:22 +00:00
if ( equippedItem . Behaviors ! = null )
foreach ( var behavior in equippedItem . Behaviors )
if ( behavior is T b )
return b ;
2020-12-10 23:20:38 +00:00
return null ;
}
2020-06-05 04:16:33 +01:00
2021-04-18 05:13:20 +01:00
public IEnumerable < T > GetBehaviors < T > ( ) where T : Behavior
2020-12-10 23:20:38 +00:00
{
2020-12-19 02:37:13 +00:00
foreach ( var equippedItem in Equipment )
2020-12-27 22:18:22 +00:00
if ( equippedItem . Behaviors ! = null )
foreach ( var behavior in equippedItem . Behaviors )
if ( behavior is T b )
yield return b ;
2020-12-10 23:20:38 +00:00
}
2020-06-05 04:16:33 +01:00
2020-12-10 23:20:38 +00:00
public IEnumerable < T > GetBehaviorData < T > ( ) where T : BehaviorData
{
2020-12-19 02:37:13 +00:00
foreach ( var equippedItem in Equipment )
2020-12-10 23:20:38 +00:00
foreach ( var behavior in equippedItem . Behaviors )
if ( behavior . Data is T b )
yield return b ;
}
2021-01-25 05:53:36 +00:00
// public IEnumerable<(T t, Switch s)> GetSwitch<T>() where T : class, IBehavior
// {
// foreach (var equippedItem in Equipment)
// foreach (var group in equippedItem.BehaviorGroups.Values)
// foreach(var behavior in group.Behaviors)
// if (behavior is T t)
// {
// var s = group.GetExposed<Switch>();
// if (s != null) yield return (t, s);
// }
// }
//
// public IEnumerable<(T behavior, Trigger trigger)> GetTrigger<T>() where T : class, IBehavior
// {
// foreach (var equippedItem in Equipment)
// foreach (var group in equippedItem.BehaviorGroups.Values)
// foreach(var behavior in group.Behaviors)
// if (behavior is T t)
// {
// var s = group.GetExposed<Trigger>();
// if (s != null) yield return (t, s);
// }
// }
//
// public IEnumerable<(T behavior, Axis axis)> GetAxis<T>() where T : class, IBehavior
// {
// foreach (var equippedItem in Equipment)
// foreach (var group in equippedItem.BehaviorGroups.Values)
// foreach(var behavior in group.Behaviors)
// if (behavior is T t)
// {
// var s = group.GetExposed<Axis>();
// if (s != null) yield return (t, s);
// }
// }
2020-12-10 23:20:38 +00:00
public virtual void Update ( float delta )
{
2020-12-27 22:18:22 +00:00
var hullData = ItemManager . GetData ( Hull ) as HullData ;
2021-02-26 19:01:47 +00:00
TargetRange = Target . Value = = null ? - 1 : length ( Position - Target . Value . Position ) ;
2021-01-25 05:53:36 +00:00
2021-05-11 04:45:42 +01:00
var localSecurityLevel = Zone . GetSecurityLevel ( Position . xz ) ;
if ( CurrentSecurityLevel . Value ! = localSecurityLevel ) CurrentSecurityLevel . Value = localSecurityLevel ;
2021-01-25 05:53:36 +00:00
foreach ( var v in VisibilitySources . Keys . ToArray ( ) )
{
2021-04-12 04:20:52 +01:00
VisibilitySources [ v ] = AetheriaMath . Decay ( VisibilitySources [ v ] , ItemManager . GameplaySettings . VisibilityDecay , delta ) ;
2021-05-12 00:15:11 +01:00
2021-04-12 04:20:52 +01:00
if ( VisibilitySources [ v ] < 0.1f ) VisibilitySources . Remove ( v ) ;
2021-01-25 05:53:36 +00:00
}
2021-02-26 19:01:47 +00:00
UpdateTemperature ( delta ) ;
2021-04-13 01:50:42 +01:00
foreach ( var item in _orderedEquipment ) item . UpdatePerformance ( ) ;
2021-02-26 19:01:47 +00:00
if ( _active )
{
2021-05-12 00:15:11 +01:00
foreach ( var entity in Zone . Entities )
{
var previousHostility = EntityHostility [ entity ] ;
var newHostility = IsHostileTo ( entity ) ;
if ( newHostility ! = previousHostility )
EntityHostility [ entity ] = newHostility ;
}
2021-03-09 02:26:58 +00:00
if ( Cockpit ! = null )
{
2021-04-18 05:13:20 +01:00
var cockpitTemp = Cockpit . Temperature ;
if ( cockpitTemp > ItemManager . GameplaySettings . HeatstrokeTemperature )
2021-03-09 02:26:58 +00:00
{
var previous = Heatstroke ;
Heatstroke = saturate (
Heatstroke +
2021-04-18 05:13:20 +01:00
pow ( cockpitTemp - ItemManager . GameplaySettings . HeatstrokeTemperature , ItemManager . GameplaySettings . HeatstrokeExponent ) *
2021-03-09 02:26:58 +00:00
ItemManager . GameplaySettings . HeatstrokeMultiplier * delta ) ;
if ( previous < ItemManager . GameplaySettings . SevereHeatstrokeRiskThreshold & & Heatstroke > ItemManager . GameplaySettings . SevereHeatstrokeRiskThreshold )
HeatstrokeRisk . OnNext ( Unit . Default ) ;
2021-04-10 01:47:16 +01:00
if ( Heatstroke > . 99 )
2021-03-09 02:26:58 +00:00
{
HeatstrokeDeath . OnNext ( Unit . Default ) ;
Deactivate ( ) ;
}
}
else
{
Heatstroke = saturate ( Heatstroke - ItemManager . GameplaySettings . HeatstrokeRecoverySpeed * delta ) ;
}
2021-04-18 05:13:20 +01:00
if ( cockpitTemp < ItemManager . GameplaySettings . HypothermiaTemperature )
2021-04-10 01:47:16 +01:00
{
var previous = Hypothermia ;
Hypothermia = saturate (
Hypothermia +
2021-04-18 05:13:20 +01:00
pow ( ItemManager . GameplaySettings . HypothermiaTemperature - cockpitTemp , ItemManager . GameplaySettings . HypothermiaExponent ) *
2021-04-10 01:47:16 +01:00
ItemManager . GameplaySettings . HypothermiaMultiplier * delta ) ;
if ( previous < ItemManager . GameplaySettings . SevereHeatstrokeRiskThreshold & & Heatstroke > ItemManager . GameplaySettings . SevereHeatstrokeRiskThreshold )
HypothermiaRisk . OnNext ( Unit . Default ) ;
if ( Hypothermia > . 99 )
{
HypothermiaDeath . OnNext ( Unit . Default ) ;
Deactivate ( ) ;
}
}
else
{
Hypothermia = saturate ( Hypothermia - ItemManager . GameplaySettings . HypothermiaRecoverySpeed * delta ) ;
}
2021-03-09 02:26:58 +00:00
}
2021-04-18 05:13:20 +01:00
for ( var i = 0 ; i < _activeConsumables . Count ; i + + )
{
_activeConsumables [ i ] . Update ( delta ) ;
if ( _activeConsumables [ i ] . RemainingDuration < 0 ) _activeConsumables . RemoveAt ( i - - ) ;
}
2021-02-26 19:01:47 +00:00
foreach ( var equippedItem in _orderedEquipment )
{
equippedItem . Update ( delta ) ;
}
foreach ( var message in Messages . Keys . ToArray ( ) )
{
Messages [ message ] = Messages [ message ] - delta ;
if ( Messages [ message ] < 0 )
Messages . Remove ( message ) ;
}
}
foreach ( var child in Children )
child . Update ( delta ) ;
if ( Parent ! = null )
{
Position = Parent . Position ;
Velocity = Parent . Velocity ;
}
else Position . y = Zone . GetHeight ( Position . xz ) + hullData . GridOffset ;
}
private void UpdateTemperature ( float delta )
{
var hullData = ItemManager . GetData ( Hull ) as HullData ;
2021-01-25 05:53:36 +00:00
2021-01-27 00:05:30 +00:00
MaxTemp = Single . MinValue ;
MinTemp = Single . MaxValue ;
2021-01-17 23:26:53 +00:00
//float[,] newTemp = new float[hullData.Shape.Width,hullData.Shape.Height];
2020-12-27 22:18:22 +00:00
var radiation = 0f ;
foreach ( var v in hullData . Shape . Coordinates )
{
var temp = Temperature [ v . x , v . y ] ;
2021-01-27 00:05:30 +00:00
var totalTemp = temp / ItemManager . GameplaySettings . HeatConductionMultiplier ;
var totalConductivity = 1f / ItemManager . GameplaySettings . HeatConductionMultiplier ;
2020-12-27 22:18:22 +00:00
2021-01-17 02:45:52 +00:00
if ( hullData . Shape [ int2 ( v . x - 1 , v . y ) ] )
2021-01-27 00:05:30 +00:00
{
var conductivity = ( GearOccupancy [ v . x , v . y ] ? . Conductivity ? ? 1 ) *
( GearOccupancy [ v . x - 1 , v . y ] ? . Conductivity ? ? 1 ) *
( HullConductivity [ v . x - 1 , v . y ] . x ? hullData . Conductivity : 1 / hullData . Conductivity ) *
( ThermalMass [ v . x - 1 , v . y ] / ThermalMass [ v . x , v . y ] ) ;
totalConductivity + = conductivity ;
totalTemp + = Temperature [ v . x - 1 , v . y ] * conductivity ;
}
2021-01-17 02:45:52 +00:00
if ( hullData . Shape [ int2 ( v . x + 1 , v . y ) ] )
2021-01-27 00:05:30 +00:00
{
var conductivity = ( GearOccupancy [ v . x , v . y ] ? . Conductivity ? ? 1 ) *
( GearOccupancy [ v . x + 1 , v . y ] ? . Conductivity ? ? 1 ) *
( HullConductivity [ v . x , v . y ] . x ? hullData . Conductivity : 1 / hullData . Conductivity ) *
( ThermalMass [ v . x + 1 , v . y ] / ThermalMass [ v . x , v . y ] ) ;
totalConductivity + = conductivity ;
totalTemp + = Temperature [ v . x + 1 , v . y ] * conductivity ;
}
2021-01-17 02:45:52 +00:00
if ( hullData . Shape [ int2 ( v . x , v . y - 1 ) ] )
2021-01-27 00:05:30 +00:00
{
var conductivity = ( GearOccupancy [ v . x , v . y ] ? . Conductivity ? ? 1 ) *
( GearOccupancy [ v . x , v . y - 1 ] ? . Conductivity ? ? 1 ) *
( HullConductivity [ v . x , v . y - 1 ] . y ? hullData . Conductivity : 1 / hullData . Conductivity ) *
( ThermalMass [ v . x , v . y - 1 ] / ThermalMass [ v . x , v . y ] ) ;
totalConductivity + = conductivity ;
totalTemp + = Temperature [ v . x , v . y - 1 ] * conductivity ;
}
2020-12-27 22:18:22 +00:00
2021-01-27 00:05:30 +00:00
if ( hullData . Shape [ int2 ( v . x , v . y + 1 ) ] )
{
var conductivity = ( GearOccupancy [ v . x , v . y ] ? . Conductivity ? ? 1 ) *
( GearOccupancy [ v . x , v . y + 1 ] ? . Conductivity ? ? 1 ) *
( HullConductivity [ v . x , v . y ] . y ? hullData . Conductivity : 1 / hullData . Conductivity ) *
( ThermalMass [ v . x , v . y + 1 ] / ThermalMass [ v . x , v . y ] ) ;
totalConductivity + = conductivity ;
totalTemp + = Temperature [ v . x , v . y + 1 ] * conductivity ;
}
2020-12-27 22:18:22 +00:00
2021-01-27 00:05:30 +00:00
NewTemperature [ v . x , v . y ] = totalTemp / totalConductivity ;
var r = 0f ;
2020-12-27 22:18:22 +00:00
// For all cells on the border of the entity, radiate some heat into space, increasing the visibility of the ship
2021-01-17 02:45:52 +00:00
if ( Parent = = null & & ! hullData . InteriorCells [ v ] )
2020-12-27 22:18:22 +00:00
{
2021-03-01 01:53:56 +00:00
var rad = pow ( NewTemperature [ v . x , v . y ] , ItemManager . GameplaySettings . HeatRadiationExponent ) *
ItemManager . GameplaySettings . HeatRadiationMultiplier ;
NewTemperature [ v . x , v . y ] - = rad * delta ;
r + = rad ;
2020-12-27 22:18:22 +00:00
}
2021-01-27 00:05:30 +00:00
radiation + = r ;
2021-01-17 02:45:52 +00:00
2021-01-31 03:52:32 +00:00
if ( float . IsNaN ( NewTemperature [ v . x , v . y ] ) | | NewTemperature [ v . x , v . y ] < 0 )
2021-01-17 02:45:52 +00:00
ItemManager . Log ( "HOUSTON, WE HAVE A PROBLEM!" ) ;
2021-01-27 00:05:30 +00:00
if ( NewTemperature [ v . x , v . y ] < MinTemp )
MinTemp = NewTemperature [ v . x , v . y ] ;
if ( NewTemperature [ v . x , v . y ] > MaxTemp )
MaxTemp = NewTemperature [ v . x , v . y ] ;
2020-12-27 22:18:22 +00:00
}
VisibilitySources [ this ] = radiation ;
2021-01-17 23:26:53 +00:00
var swap = Temperature ;
Temperature = NewTemperature ;
NewTemperature = swap ;
2020-06-02 07:01:03 +01:00
}
2020-12-10 23:20:38 +00:00
public void SetMessage ( string message )
2020-04-28 01:50:50 +01:00
{
2020-12-10 23:20:38 +00:00
Messages [ message ] = ItemManager . GameplaySettings . MessageDuration ;
2020-06-02 07:01:03 +01:00
}
2020-12-10 23:20:38 +00:00
}
2021-04-18 05:13:20 +01:00
public class ConsumableItemEffect
{
public float RemainingDuration { get ; private set ; }
public Entity Entity { get ; }
public ConsumableItem Item { get ; }
public ConsumableItemData Data { get ; }
public Behavior [ ] Behaviors { get ; }
public ConsumableItemEffect ( ConsumableItem item , Entity entity )
{
Item = item ;
Entity = entity ;
Data = ( ConsumableItemData ) item . Data . Value ;
RemainingDuration = Data . Duration ;
2021-04-21 01:49:07 +01:00
Behaviors = Data . Behaviors
2021-04-18 05:13:20 +01:00
. Select ( bd = > bd . CreateInstance ( this ) )
. ToArray ( ) ;
}
public void Update ( float delta )
{
foreach ( var behavior in Behaviors )
if ( behavior is IAlwaysUpdatedBehavior alwaysUpdatedBehavior ) alwaysUpdatedBehavior . Update ( delta ) ;
foreach ( var behavior in Behaviors )
{
if ( ! behavior . Execute ( delta ) )
break ;
}
RemainingDuration - = delta ;
}
public float Evaluate ( PerformanceStat stat )
{
var effectiveness = Data . Effectiveness . Evaluate ( ( Data . Duration - RemainingDuration ) / Data . Duration ) ;
var quality = pow ( Item . Quality , stat . QualityExponent ) ;
var result = lerp ( stat . Min , stat . Max , effectiveness * quality ) ;
if ( float . IsNaN ( result ) )
return stat . Min ;
return result ;
}
}
2020-12-10 23:20:38 +00:00
public class EquippedItem
{
2021-03-07 01:10:31 +00:00
public int SortPosition ;
2021-01-17 02:45:52 +00:00
public EquippableItem EquippableItem ;
public int2 Position ;
2021-02-26 19:01:47 +00:00
2021-03-07 01:10:31 +00:00
private bool _thermalOnline ;
private bool _durabilityOnline ;
2021-04-30 06:39:57 +01:00
public WwiseMetaSoundBank SoundBank ;
2020-12-10 23:20:38 +00:00
2021-04-18 05:13:20 +01:00
public Behavior [ ] Behaviors { get ; }
public Dictionary < int , BehaviorGroup > BehaviorGroups { get ; }
2021-01-17 02:45:52 +00:00
public float Conductivity { get ; }
2021-03-07 01:10:31 +00:00
public float ThermalPerformance { get ; private set ; }
public float ThermalExponent { get ; }
public float DurabilityPerformance { get ; private set ; }
public float DurabilityExponent { get ; }
public float Wear { get ; private set ; }
2020-12-27 22:18:22 +00:00
public Shape InsetShape { get ; }
2021-03-07 01:10:31 +00:00
public Entity Entity { get ; }
public EquippableItemData Data { get ; }
2020-12-27 22:18:22 +00:00
2021-03-07 01:10:31 +00:00
public ReactiveProperty < bool > ThermalOnline { get ; } = new ReactiveProperty < bool > ( false ) ;
public ReactiveProperty < bool > DurabilityOnline { get ; } = new ReactiveProperty < bool > ( false ) ;
public ReadOnlyReactiveProperty < bool > Online { get ; }
public ReactiveProperty < bool > Enabled { get ; } = new ReactiveProperty < bool > ( true ) ;
public ReadOnlyReactiveProperty < bool > Active { get ; }
2021-03-16 16:35:23 +00:00
public ItemManager ItemManager { get ; }
2021-01-10 03:59:47 +00:00
2021-04-30 06:39:57 +01:00
public Subject < uint > AudioEvents { get ; } = new Subject < uint > ( ) ;
public Subject < ( uint id , float v ) > AudioParameters { get ; } = new Subject < ( uint id , float v ) > ( ) ;
public Dictionary < uint , float > AudioParameterValues { get ; } = new Dictionary < uint , float > ( ) ;
2020-12-27 22:18:22 +00:00
public float Temperature
{
get
{
float sum = 0 ;
2021-03-07 01:10:31 +00:00
foreach ( var x in InsetShape . Coordinates ) sum + = Entity . Temperature [ x . x , x . y ] ;
2020-12-27 22:18:22 +00:00
return sum / InsetShape . Coordinates . Length ;
}
}
2021-04-30 06:39:57 +01:00
public void FireAudioEvent ( uint eventId , bool skipVerify = false )
{
if ( SoundBank ! = null & & ( skipVerify | | SoundBank . IncludedEvents . Any ( o = > o . Id = = eventId ) ) )
AudioEvents . OnNext ( eventId ) ;
}
public void FireAudioEvent ( WwiseSoundBinding soundBinding )
{
FireAudioEvent ( soundBinding . PlayEvent ) ;
}
public void PlaySound ( WwiseLoopingSoundBinding soundBinding )
{
FireAudioEvent ( soundBinding . PlayEvent ) ;
}
public void StopSound ( WwiseLoopingSoundBinding soundBinding )
{
FireAudioEvent ( soundBinding . StopEvent ) ;
}
public void FireAudioEvent ( WeaponAudioEvent weaponAudioEvent )
{
if ( SoundBank = = null ) return ;
var eventObject = SoundBank . GetEvent ( weaponAudioEvent ) ;
if ( eventObject = = null )
{
ItemManager . Log ( $"Attempted to trigger {Enum.GetName(typeof(WeaponAudioEvent), weaponAudioEvent)} weapon audio event, but the soundbank doesn't support it!" ) ;
return ;
}
FireAudioEvent ( eventObject . Id , true ) ;
}
public void FireAudioEvent ( ChargedWeaponAudioEvent weaponAudioEvent )
{
if ( SoundBank = = null ) return ;
var eventObject = SoundBank . GetEvent ( weaponAudioEvent ) ;
if ( eventObject = = null )
{
ItemManager . Log ( $"Attempted to trigger {Enum.GetName(typeof(ChargedWeaponAudioEvent), weaponAudioEvent)} weapon audio event, but the soundbank doesn't support it!" ) ;
return ;
}
FireAudioEvent ( eventObject . Id , true ) ;
}
public void SetAudioParameter ( uint id , float v , bool skipVerify = false )
{
if ( SoundBank ! = null & & ( skipVerify | | SoundBank . GameParameters . Any ( o = > o . Id = = id ) ) )
{
AudioParameterValues [ id ] = v ;
AudioParameters . OnNext ( ( id , v ) ) ;
}
}
// public void SetAudioParameter(WwiseParameterBinding binding)
// {
// FireAudioEvent(binding.Parameter);
// }
public void SetAudioParameter ( SpecialAudioParameter p , float v )
{
if ( SoundBank = = null ) return ;
var metaObject = SoundBank . GetParameter ( p ) ;
if ( metaObject = = null )
{
ItemManager . Log ( $"Attempted to set {Enum.GetName(typeof(ChargedWeaponAudioEvent), p)} audio parameter, but the soundbank doesn't support it!" ) ;
return ;
}
SetAudioParameter ( metaObject . Id , v , true ) ;
}
2020-12-10 23:20:38 +00:00
2020-12-27 22:18:22 +00:00
public EquippedItem ( ItemManager itemManager , EquippableItem item , int2 position , Entity entity )
2020-04-09 10:33:16 +01:00
{
2021-03-16 16:35:23 +00:00
ItemManager = itemManager ;
Data = ItemManager . GetData ( item ) ;
2021-03-07 01:10:31 +00:00
Entity = entity ;
2020-12-10 23:20:38 +00:00
EquippableItem = item ;
Position = position ;
2021-03-07 01:10:31 +00:00
Conductivity = Data . Conductivity ;
2021-03-22 05:22:31 +00:00
ThermalExponent = lerp (
ItemManager . GameplaySettings . ThermalQualityMin ,
ItemManager . GameplaySettings . ThermalQualityMax ,
pow ( item . Quality , ItemManager . GameplaySettings . ThermalQualityExponent ) ) ;
DurabilityExponent = lerp (
ItemManager . GameplaySettings . DurabilityQualityMin ,
ItemManager . GameplaySettings . DurabilityQualityMax ,
pow ( item . Quality , ItemManager . GameplaySettings . DurabilityQualityExponent ) ) ;
2020-12-27 22:18:22 +00:00
var hullData = itemManager . GetData ( entity . Hull ) ;
2021-03-07 01:10:31 +00:00
InsetShape = hullData . Shape . Inset ( Data . Shape , position , item . Rotation ) ;
2020-12-31 19:53:16 +00:00
2021-03-07 01:10:31 +00:00
Online = new ReadOnlyReactiveProperty < bool > ( ThermalOnline
. CombineLatest ( DurabilityOnline , ( thermal , durability ) = > thermal & & durability ) . DistinctUntilChanged ( ) ) ;
Active = new ReadOnlyReactiveProperty < bool > ( Enabled
. CombineLatest ( Online , ( enabled , online ) = > enabled & & online ) . DistinctUntilChanged ( ) ) ;
Behaviors = Data . Behaviors
2021-04-12 04:20:52 +01:00
. Select ( bd = > bd . CreateInstance ( this ) )
2020-12-31 19:53:16 +00:00
. ToArray ( ) ;
2021-01-25 05:53:36 +00:00
2020-12-31 19:53:16 +00:00
BehaviorGroups = Behaviors
. GroupBy ( b = > b . Data . Group )
2021-01-25 05:53:36 +00:00
. OrderBy ( g = > g . Key )
. ToDictionary ( g = > g . Key , g = > new BehaviorGroup
{
2021-03-07 01:10:31 +00:00
Behaviors = g . ToArray ( )
2021-01-25 05:53:36 +00:00
} ) ;
2020-12-31 19:53:16 +00:00
foreach ( var behavior in Behaviors )
{
if ( behavior is IOrderedBehavior orderedBehavior )
SortPosition = orderedBehavior . Order ;
if ( behavior is IPopulationAssignment populationAssignment )
entity . PopulationAssignments . Add ( populationAssignment ) ;
}
2020-12-27 22:18:22 +00:00
}
2021-03-07 01:10:31 +00:00
public float Evaluate ( PerformanceStat stat )
{
2021-03-22 05:22:31 +00:00
var heat = pow ( ThermalPerformance , ThermalExponent * stat . HeatExponentMultiplier ) ;
var durability = pow ( DurabilityPerformance , DurabilityExponent * stat . DurabilityExponentMultiplier ) ;
var quality = pow ( EquippableItem . Quality , stat . QualityExponent ) ;
2021-03-07 01:10:31 +00:00
var scaleModifier = 1.0f ;
2021-04-21 01:49:07 +01:00
var scaleModifiers = stat . GetScaleModifiers ( Entity ) . Values ;
foreach ( var value in scaleModifiers ) scaleModifier * = value ;
2021-03-07 01:10:31 +00:00
float constantModifier = 0 ;
foreach ( var value in stat . GetConstantModifiers ( Entity ) . Values ) constantModifier + = value ;
var result = lerp ( stat . Min , stat . Max , durability * quality * heat ) * scaleModifier + constantModifier ;
if ( float . IsNaN ( result ) )
return stat . Min ;
return result ;
}
2021-02-01 08:13:14 +00:00
public void AddHeat ( float heat , bool ignoreThermalMass = false )
2020-12-27 22:18:22 +00:00
{
foreach ( var hullCoord in InsetShape . Coordinates )
2021-03-07 01:10:31 +00:00
Entity . AddHeat ( hullCoord , heat / InsetShape . Coordinates . Length , ignoreThermalMass ) ;
2020-04-09 10:33:16 +01:00
}
2021-04-13 01:50:42 +01:00
public void UpdatePerformance ( )
2020-05-11 01:17:10 +01:00
{
2021-03-07 01:10:31 +00:00
var temp = Temperature ;
ThermalPerformance = Data . Performance ( temp ) ;
DurabilityPerformance = EquippableItem . Durability / Data . Durability ;
2021-03-14 03:56:49 +00:00
var performanceThreshold = Entity . Settings . ShutdownPerformance ;
2021-04-22 02:53:06 +01:00
Wear = ( 1 - pow ( ThermalPerformance ,
2021-03-22 05:22:31 +00:00
( 1 - pow ( EquippableItem . Quality , ItemManager . GameplaySettings . QualityWearExponent ) ) *
2021-03-16 16:35:23 +00:00
ItemManager . GameplaySettings . ThermalWearExponent )
2021-03-07 01:10:31 +00:00
) * Data . Durability / Data . ThermalResilience ;
ThermalOnline . Value = ThermalPerformance > performanceThreshold | | Entity . OverrideShutdown & & EquippableItem . OverrideShutdown ;
DurabilityOnline . Value = EquippableItem . Durability > . 01f ;
2021-04-13 01:50:42 +01:00
}
public void Update ( float delta )
{
2021-04-30 06:39:57 +01:00
foreach ( var audioStat in Data . AudioStats )
{
SetAudioParameter ( audioStat . Parameter , Evaluate ( audioStat . Stat ) ) ;
}
2021-03-07 01:10:31 +00:00
if ( Active . Value )
2020-12-10 23:20:38 +00:00
{
2021-01-25 05:53:36 +00:00
foreach ( var group in BehaviorGroups . Values )
2020-12-10 23:20:38 +00:00
{
2021-01-10 03:59:47 +00:00
foreach ( var behavior in group . Behaviors )
{
if ( ! behavior . Execute ( delta ) )
break ;
}
2020-12-10 23:20:38 +00:00
}
}
2021-04-21 01:49:07 +01:00
foreach ( var behavior in Behaviors )
if ( behavior is IAlwaysUpdatedBehavior alwaysUpdatedBehavior ) alwaysUpdatedBehavior . Update ( delta ) ;
2021-03-02 23:02:19 +00:00
}
2021-04-21 01:49:07 +01:00
public T GetBehavior < T > ( ) where T : class
2021-03-02 23:02:19 +00:00
{
foreach ( var behavior in Behaviors )
if ( behavior is T b )
return b ;
return null ;
2020-05-11 01:17:10 +01:00
}
2020-12-10 23:20:38 +00:00
}
2020-05-11 01:17:10 +01:00
2020-12-10 23:20:38 +00:00
public class EquippedCargoBay : EquippedItem
{
public readonly ReactiveDictionary < ItemInstance , int2 > Cargo = new ReactiveDictionary < ItemInstance , int2 > ( ) ;
public readonly ItemInstance [ , ] Occupancy ;
public readonly Dictionary < Guid , List < ItemInstance > > ItemsOfType = new Dictionary < Guid , List < ItemInstance > > ( ) ;
2020-12-14 02:56:35 +00:00
public readonly CargoBayData Data ;
2020-12-10 23:20:38 +00:00
public float Mass { get ; private set ; }
public float ThermalMass { get ; private set ; }
2020-12-19 02:37:13 +00:00
public string Name { get ; }
2021-03-20 04:20:17 +00:00
public Shape UnoccupiedSpace
{
get
{
var unoccupied = new Shape ( Data . InteriorShape . Width , Data . InteriorShape . Height ) ;
foreach ( var v in unoccupied . AllCoordinates )
unoccupied [ v ] = Occupancy [ v . x , v . y ] = = null ;
return unoccupied ;
}
}
2020-12-27 22:18:22 +00:00
public EquippedCargoBay ( ItemManager itemManager , EquippableItem item , int2 position , Entity entity , string name ) : base ( itemManager , item , position , entity )
2020-05-11 01:17:10 +01:00
{
2021-03-16 16:35:23 +00:00
Data = ItemManager . GetData ( EquippableItem ) as CargoBayData ;
2020-12-19 02:37:13 +00:00
Name = name ;
2020-12-10 23:20:38 +00:00
2020-12-14 02:56:35 +00:00
Mass = Data . Mass ;
ThermalMass = Data . Mass * Data . SpecificHeat ;
2020-12-10 23:20:38 +00:00
2020-12-14 02:56:35 +00:00
Occupancy = new ItemInstance [ Data . InteriorShape . Width , Data . InteriorShape . Height ] ;
2020-05-11 01:17:10 +01:00
}
2020-12-10 23:20:38 +00:00
// Check whether the given item will fit when its origin is placed at the given coordinate
2020-12-20 03:04:52 +00:00
public bool ItemFits ( ItemInstance item , int2 cargoCoord )
2020-05-28 04:50:40 +01:00
{
2021-04-18 05:13:20 +01:00
var itemData = item . Data . Value ;
2020-12-10 23:20:38 +00:00
// Check every cell of the item's shape
foreach ( var i in itemData . Shape . Coordinates )
{
// If there is an item already occupying that space, it won't fit
var itemCargoCoord = cargoCoord + itemData . Shape . Rotate ( i , item . Rotation ) ;
2020-12-20 03:04:52 +00:00
if ( ! Data . InteriorShape [ itemCargoCoord ] | | ( Occupancy [ itemCargoCoord . x , itemCargoCoord . y ] ! = null & & Occupancy [ itemCargoCoord . x , itemCargoCoord . y ] ! = item ) ) return false ;
2020-12-10 23:20:38 +00:00
}
return true ;
2020-05-28 04:50:40 +01:00
}
2021-03-04 03:58:46 +00:00
public bool TryFindSpace ( ItemInstance item )
{
if ( item is SimpleCommodity simpleCommodity )
return TryFindSpace ( simpleCommodity , out _ ) ;
if ( item is CraftedItemInstance craftedItem )
return TryFindSpace ( craftedItem , out _ ) ;
return false ;
}
2020-05-28 04:50:40 +01:00
2020-12-10 23:20:38 +00:00
// Tries to find a place to put the given items in the inventory
// Will attempt to fill existing item stacks first
// Returns true only when ALL of the items have places to go
public bool TryFindSpace ( SimpleCommodity item , out List < int2 > positions )
2020-05-28 04:50:40 +01:00
{
2020-12-10 23:20:38 +00:00
positions = new List < int2 > ( ) ;
2021-03-16 16:35:23 +00:00
var itemData = ItemManager . GetData ( item ) ;
2020-12-10 23:20:38 +00:00
var remainingQuantity = item . Quantity ;
// For simple commodities, search for existing item stacks to add to
foreach ( var cargoItem in Cargo . Keys )
{
if ( item . Data ! = cargoItem . Data ) continue ;
var cargoCommodity = ( SimpleCommodity ) cargoItem ;
if ( cargoCommodity . Quantity > = itemData . MaxStack ) continue ;
// Subtract remaining space in existing stack from remaining quantity
remainingQuantity - = min ( itemData . MaxStack - cargoCommodity . Quantity , remainingQuantity ) ;
positions . Add ( Cargo [ cargoItem ] ) ;
// If we've moved all of the items into existing stacks, no need to search for empty space!
if ( remainingQuantity = = 0 ) return true ;
}
2021-03-20 04:20:17 +00:00
// TODO: Try alternate item rotations / use Shape.FitsWithin
2020-12-10 23:20:38 +00:00
// Search all the space in the cargo bay for an empty space where the item fits
2020-12-14 02:56:35 +00:00
foreach ( var cargoCoord in Data . InteriorShape . Coordinates )
2020-12-10 23:20:38 +00:00
{
if ( ItemFits ( item , cargoCoord ) )
{
positions . Add ( cargoCoord ) ;
return true ;
}
}
return false ;
2020-05-28 04:50:40 +01:00
}
2021-03-04 03:58:46 +00:00
// Searches the cargo bay for a position where the item will fit, returns true when found
public bool TryFindSpace ( CraftedItemInstance item , out int2 position )
{
// Search all the space in the cargo bay for an empty space where the item fits
foreach ( var cargoCoord in Data . InteriorShape . Coordinates )
{
if ( ItemFits ( item , cargoCoord ) )
{
position = cargoCoord ;
return true ;
}
}
position = int2 . zero ;
return false ;
}
2021-01-27 16:14:41 +00:00
public bool TryStore ( ItemInstance item )
{
if ( item is SimpleCommodity simpleCommodity )
return TryStore ( simpleCommodity ) ;
if ( item is CraftedItemInstance craftedItem )
return TryStore ( craftedItem ) ;
return false ;
}
2020-05-28 04:50:40 +01:00
2020-12-20 03:04:52 +00:00
public bool TryStore ( ItemInstance item , int2 cargoCoord )
{
if ( item is SimpleCommodity simpleCommodity )
return TryStore ( simpleCommodity , cargoCoord ) ;
if ( item is CraftedItemInstance craftedItem )
return TryStore ( craftedItem , cargoCoord ) ;
return false ;
}
2020-12-10 23:20:38 +00:00
// Attempts to store all of the given item anywhere in the inventory
// Will attempt to fill existing item stacks first
// Returns true only when ALL of the items are successfully stored
public bool TryStore ( SimpleCommodity item )
2020-04-09 10:33:16 +01:00
{
2020-12-10 23:20:38 +00:00
TryFindSpace ( item , out var positions ) ;
foreach ( var position in positions )
2020-05-26 01:12:06 +01:00
{
2020-12-10 23:20:38 +00:00
if ( TryStore ( item , position ) ) return true ;
2020-05-26 01:12:06 +01:00
}
2020-12-10 23:20:38 +00:00
return false ;
2020-04-09 10:33:16 +01:00
}
2020-12-10 23:20:38 +00:00
// Try to store the given commodity at the given position
// If there's a stack at the given position it will be added to
// Returns true only when ALL of the items are successfully stored
public bool TryStore ( SimpleCommodity item , int2 cargoCoord )
2020-04-09 10:33:16 +01:00
{
2021-03-16 16:35:23 +00:00
var itemData = ItemManager . GetData ( item ) ;
2020-12-10 23:20:38 +00:00
if ( ItemFits ( item , cargoCoord ) )
2020-06-05 04:16:33 +01:00
{
2020-12-10 23:20:38 +00:00
foreach ( var p in itemData . Shape . Coordinates )
2020-06-12 01:57:15 +01:00
{
2020-12-10 23:20:38 +00:00
var pos = cargoCoord + itemData . Shape . Rotate ( p , item . Rotation ) ;
Occupancy [ pos . x , pos . y ] = item ;
}
Cargo [ item ] = cargoCoord ;
2020-06-05 04:16:33 +01:00
2021-04-18 05:13:20 +01:00
if ( ! ItemsOfType . ContainsKey ( item . Data . LinkID ) )
ItemsOfType [ item . Data . LinkID ] = new List < ItemInstance > ( ) ;
ItemsOfType [ item . Data . LinkID ] . Add ( item ) ;
2020-12-10 23:20:38 +00:00
}
else if ( Occupancy [ cargoCoord . x , cargoCoord . y ] is SimpleCommodity cargoCommodity & & cargoCommodity . Data = = item . Data )
{
if ( cargoCommodity . Quantity + item . Quantity < = itemData . MaxStack )
{
cargoCommodity . Quantity + = item . Quantity ;
2020-06-12 01:57:15 +01:00
}
2020-12-10 23:20:38 +00:00
else
2020-06-12 01:57:15 +01:00
{
2020-12-10 23:20:38 +00:00
var quantityTransferred = itemData . MaxStack - cargoCommodity . Quantity ;
item . Quantity - = quantityTransferred ;
cargoCommodity . Quantity = itemData . MaxStack ;
Mass + = itemData . Mass * quantityTransferred ;
ThermalMass + = itemData . Mass * itemData . SpecificHeat * quantityTransferred ;
return false ;
2020-06-12 01:57:15 +01:00
}
2020-12-10 23:20:38 +00:00
}
else return false ;
2021-03-16 16:35:23 +00:00
Mass + = ItemManager . GetMass ( item ) ;
ThermalMass + = ItemManager . GetThermalMass ( item ) ;
2020-12-10 23:20:38 +00:00
return true ;
}
2020-06-12 01:57:15 +01:00
2020-12-10 23:20:38 +00:00
// Try to store the given item anywhere it will fit, returns true when the item was successfully stored
public bool TryStore ( CraftedItemInstance item ) = > TryFindSpace ( item , out var position ) & & TryStore ( item , position ) ;
// Try to store the given item at the given position, returns true when the item was successfully stored
public bool TryStore ( CraftedItemInstance item , int2 cargoCoord )
{
if ( ! ItemFits ( item , cargoCoord ) ) return false ;
2021-03-16 16:35:23 +00:00
var itemData = ItemManager . GetData ( item ) ;
2020-12-10 23:20:38 +00:00
foreach ( var p in itemData . Shape . Coordinates )
2020-05-04 00:07:36 +01:00
{
2020-12-10 23:20:38 +00:00
var pos = cargoCoord + itemData . Shape . Rotate ( p , item . Rotation ) ;
Occupancy [ pos . x , pos . y ] = item ;
2020-05-04 00:07:36 +01:00
}
2020-12-10 23:20:38 +00:00
Cargo [ item ] = cargoCoord ;
2021-04-18 05:13:20 +01:00
if ( ! ItemsOfType . ContainsKey ( item . Data . LinkID ) )
ItemsOfType [ item . Data . LinkID ] = new List < ItemInstance > ( ) ;
ItemsOfType [ item . Data . LinkID ] . Add ( item ) ;
2020-12-10 23:20:38 +00:00
2021-03-16 16:35:23 +00:00
Mass + = ItemManager . GetMass ( item ) ;
ThermalMass + = ItemManager . GetThermalMass ( item ) ;
2020-12-10 23:20:38 +00:00
return true ;
}
2020-05-11 01:17:10 +01:00
2020-12-10 23:20:38 +00:00
public SimpleCommodity Remove ( SimpleCommodity item , int quantity )
{
if ( ! Cargo . ContainsKey ( item ) )
2020-06-06 03:21:03 +01:00
{
2021-03-16 16:35:23 +00:00
ItemManager . Log ( "Attempted to remove item from a cargo bay that it wasn't even in! Something went wrong here!" ) ;
2020-12-10 23:20:38 +00:00
return null ;
2020-06-06 03:21:03 +01:00
}
2021-03-16 16:35:23 +00:00
var itemData = ItemManager . GetData ( item ) ;
2020-12-10 23:20:38 +00:00
if ( quantity > = item . Quantity )
2020-06-06 03:21:03 +01:00
{
2020-12-20 03:04:52 +00:00
foreach ( var v in Data . InteriorShape . Coordinates )
if ( Occupancy [ v . x , v . y ] = = item )
Occupancy [ v . x , v . y ] = null ;
2020-12-10 23:20:38 +00:00
Cargo . Remove ( item ) ;
2021-04-18 05:13:20 +01:00
ItemsOfType [ item . Data . LinkID ] . Remove ( item ) ;
if ( ! ItemsOfType [ item . Data . LinkID ] . Any ( ) )
ItemsOfType . Remove ( item . Data . LinkID ) ;
2020-12-10 23:20:38 +00:00
2021-03-16 16:35:23 +00:00
Mass - = ItemManager . GetMass ( item ) ;
ThermalMass - = ItemManager . GetThermalMass ( item ) ;
2020-12-10 23:20:38 +00:00
return item ;
2020-06-06 03:21:03 +01:00
}
2020-04-29 23:36:00 +01:00
2020-12-10 23:20:38 +00:00
item . Quantity - = quantity ;
Mass - = itemData . Mass * quantity ;
ThermalMass - = itemData . Mass * itemData . SpecificHeat * quantity ;
return new SimpleCommodity { Data = item . Data , Quantity = quantity , Rotation = item . Rotation } ;
2020-05-29 23:06:35 +01:00
}
2020-12-10 23:20:38 +00:00
public void Remove ( CraftedItemInstance item )
2020-04-29 23:36:00 +01:00
{
2020-12-10 23:20:38 +00:00
if ( ! Cargo . ContainsKey ( item ) )
{
2021-03-16 16:35:23 +00:00
ItemManager . Log ( "Attempted to remove item from a cargo bay that it wasn't even in! Something went wrong here!" ) ;
2020-12-10 23:20:38 +00:00
return ;
}
2021-03-16 16:35:23 +00:00
var itemData = ItemManager . GetData ( item ) ;
2020-12-20 03:04:52 +00:00
foreach ( var v in Data . InteriorShape . Coordinates )
if ( Occupancy [ v . x , v . y ] = = item )
Occupancy [ v . x , v . y ] = null ;
2020-12-10 23:20:38 +00:00
Cargo . Remove ( item ) ;
2021-04-18 05:13:20 +01:00
ItemsOfType [ item . Data . LinkID ] . Remove ( item ) ;
if ( ! ItemsOfType [ item . Data . LinkID ] . Any ( ) )
ItemsOfType . Remove ( item . Data . LinkID ) ;
2020-12-10 23:20:38 +00:00
2021-03-16 16:35:23 +00:00
Mass - = ItemManager . GetMass ( item ) ;
ThermalMass - = ItemManager . GetThermalMass ( item ) ;
2020-04-29 23:36:00 +01:00
}
2020-12-20 03:04:52 +00:00
public void Remove ( ItemInstance item )
{
if ( item is SimpleCommodity simpleCommodity )
Remove ( simpleCommodity , simpleCommodity . Quantity ) ;
if ( item is CraftedItemInstance craftedItem )
Remove ( craftedItem ) ;
}
2020-12-10 23:20:38 +00:00
public bool TryTransferItem ( EquippedCargoBay target , SimpleCommodity item , int quantity )
2020-04-29 23:36:00 +01:00
{
2020-12-10 23:20:38 +00:00
if ( ! Cargo . ContainsKey ( item ) )
{
2021-03-16 16:35:23 +00:00
ItemManager . Log ( "Attempted to remove item from a cargo bay that it wasn't even in! Something went wrong here!" ) ;
2020-12-10 23:20:38 +00:00
return false ;
}
2020-04-30 02:54:31 +01:00
2020-12-10 23:20:38 +00:00
var oldPos = Cargo [ item ] ;
var newItem = Remove ( item , quantity ) ;
if ( target . TryStore ( item ) ) return true ;
// Failed to transfer full quantity, move the remaining items back to their old slot
TryStore ( newItem , oldPos ) ;
return false ;
}
public bool TryTransferItem ( EquippedCargoBay target , CraftedItemInstance item )
{
if ( ! Cargo . ContainsKey ( item ) )
{
2021-03-16 16:35:23 +00:00
ItemManager . Log ( "Attempted to remove item from a cargo bay that it wasn't even in! Something went wrong here!" ) ;
2020-12-10 23:20:38 +00:00
return false ;
}
if ( ! target . TryStore ( item ) ) return false ;
Remove ( item ) ;
return true ;
2020-04-29 23:36:00 +01:00
}
2020-06-02 07:01:03 +01:00
}
2020-12-19 02:37:13 +00:00
public class EquippedDockingBay : EquippedCargoBay
{
public Ship DockedShip ;
public int2 MaxSize = > _data . MaxSize ;
private DockingBayData _data ;
2020-12-27 22:18:22 +00:00
public EquippedDockingBay ( ItemManager itemManager , EquippableItem item , int2 position , Entity entity , string name ) : base ( itemManager , item , position , entity , name )
2020-12-19 02:37:13 +00:00
{
2021-03-16 16:35:23 +00:00
_data = ItemManager . GetData ( EquippableItem ) as DockingBayData ;
2020-12-19 02:37:13 +00:00
}
}
2020-06-05 04:16:33 +01:00
public class BehaviorGroup
2020-06-02 07:01:03 +01:00
{
2021-04-18 05:13:20 +01:00
public Behavior [ ] Behaviors ;
2021-01-25 05:53:36 +00:00
2021-04-18 05:13:20 +01:00
public T GetBehavior < T > ( ) where T : Behavior
2021-01-25 05:53:36 +00:00
{
foreach ( var b in Behaviors )
{
if ( ! ( b is T s ) ) continue ;
return s ;
}
return null ;
}
2021-04-18 05:13:20 +01:00
public T GetExposed < T > ( ) where T : Behavior , IInteractiveBehavior
2021-01-25 05:53:36 +00:00
{
foreach ( var b in Behaviors )
{
if ( ! ( b is T s ) | | ! ( ( IInteractiveBehavior ) b ) . Exposed ) continue ;
return s ;
}
return null ;
}
2020-06-05 04:16:33 +01:00
//public IAnalogBehavior Axis;
2020-04-09 10:33:16 +01:00
}