2026-06-13 17:22:03 +01:00
/ * This Source Code Form is subject to the terms of the Mozilla Public
2020-12-22 03:13:43 +00:00
* 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/. */
using System ;
2020-03-22 19:05:26 +00:00
using System.Collections.Generic ;
using System.Linq ;
2026-06-14 04:35:51 +01:00
using GameCult.Aetheria.State.Unity ;
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-12-01 00:16:11 +00:00
using UniRx ;
2020-05-30 02:44:22 +01:00
using float4 = Unity . Mathematics . float4 ;
2020-03-22 19:05:26 +00:00
2020-12-10 23:20:38 +00:00
public class ItemManager
2020-03-22 19:05:26 +00:00
{
2020-04-30 02:54:31 +01:00
public Random Random = new Random ( ( uint ) ( DateTime . Now . Ticks % uint . MaxValue ) ) ;
2020-12-10 23:20:38 +00:00
// public Dictionary<Guid, List<IController>> CorporationControllers = new Dictionary<Guid, List<IController>>();
// public Dictionary<Guid, ZoneDefinition> 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 double _time ;
private float _deltaTime ;
2020-11-27 22:47:33 +00:00
private Dictionary < Guid , Zone > _zones = new Dictionary < Guid , Zone > ( ) ;
2020-12-20 03:04:52 +00:00
2020-11-27 22:47:33 +00:00
// private Guid _forceLoadZone;
2020-05-12 01:42:13 +01:00
2026-06-14 09:19:53 +01:00
private readonly IRuntimeItemCatalogReader _runtimeItems ;
2026-06-14 10:54:17 +01:00
2020-12-10 23:20:38 +00:00
public GameplaySettings GameplaySettings { get ; }
2020-05-10 00:16:33 +01:00
2021-04-09 14:01:34 +01:00
// public double Time
// {
// get => _time;
// set
// {
// _deltaTime = (float) (value - _time);
// _time = value;
// //Log($"GameContext delta time: {_deltaTime}");
// }
// }
2020-04-16 03:08:18 +01:00
2026-06-14 09:19:53 +01:00
public ItemManager ( IRuntimeItemCatalogReader runtimeItems , GameplaySettings settings , Action < string > logger )
2020-03-27 01:53:03 +00:00
{
2026-06-14 09:19:53 +01:00
_runtimeItems = runtimeItems ;
2020-12-20 03:04:52 +00:00
GameplaySettings = settings ;
2020-04-09 07:48:45 +01:00
_logger = logger ;
2020-04-16 03:08:18 +01:00
}
2026-06-14 04:35:51 +01:00
public AetheriaRuntimeCatalogItem GetRuntimeItem ( ItemInstance item )
{
2026-06-14 22:58:02 +01:00
return item = = null
? null
: ! string . IsNullOrWhiteSpace ( item . ItemKey )
? _runtimeItems . GetRuntimeItem ( item . ItemKey )
2026-06-15 01:31:16 +01:00
: null ;
2026-06-14 04:35:51 +01:00
}
2026-06-14 10:29:17 +01:00
public Behavior [ ] CreateRuntimeBehaviors ( EquippedItem item )
{
return CreateRuntimeBehaviorConfigs ( item ? . EquippableItem )
. Select ( config = > config . CreateInstance ( item ) )
. ToArray ( ) ;
}
public Behavior [ ] CreateRuntimeBehaviors ( ConsumableItemEffect effect )
{
return CreateRuntimeBehaviorConfigs ( effect ? . Item )
. Select ( config = > config . CreateInstance ( effect ) )
. ToArray ( ) ;
}
2026-06-14 21:48:43 +01:00
private IReadOnlyList < RuntimeBehaviorConfig > CreateRuntimeBehaviorConfigs ( ItemInstance item )
2026-06-14 10:29:17 +01:00
{
2026-06-14 10:54:17 +01:00
var typedItem = GetRuntimeItem ( item ) ;
return typedItem = = null
2026-06-14 21:48:43 +01:00
? Array . Empty < RuntimeBehaviorConfig > ( )
2026-06-14 10:54:17 +01:00
: BuildBehaviorConfigs ( typedItem ) ;
}
2026-06-14 21:48:43 +01:00
private static RuntimeBehaviorConfig [ ] BuildBehaviorConfigs ( AetheriaRuntimeCatalogItem item )
2026-06-14 10:54:17 +01:00
{
return item . BehaviorPayloads
. Select ( BuildBehaviorConfig )
. Where ( behavior = > behavior ! = null )
. ToArray ( ) ;
}
2026-06-14 21:48:43 +01:00
private static RuntimeBehaviorConfig BuildBehaviorConfig ( AetheriaRuntimeBehaviorPayload payload )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
switch ( payload . Kind )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
case "AetherDrive" : return Configure ( new AetherDriveConfig ( ) , payload , ApplyAetherDriveConfig ) ;
case "AutoWeapon" : return Configure ( new AutoWeaponConfig ( ) , payload , ApplyInstantWeaponConfig ) ;
case "Capacitor" : return Configure ( new CapacitorConfig ( ) , payload , ApplyCapacitorConfig ) ;
case "ChargedWeapon" : return Configure ( new ChargedWeaponConfig ( ) , payload , ApplyChargedWeaponConfig ) ;
case "Cockpit" : return Configure ( new CockpitConfig ( ) , payload , null ) ;
case "ConstantWeapon" : return Configure ( new ConstantWeaponConfig ( ) , payload , ApplyConstantWeaponConfig ) ;
case "Cooldown" : return Configure ( new CooldownConfig ( ) , payload , ApplyCooldownConfig ) ;
case "EnergyDraw" : return Configure ( new EnergyDrawConfig ( ) , payload , ApplyEnergyDrawConfig ) ;
case "GuidedWeapon" : return Configure ( new GuidedWeaponConfig ( ) , payload , ApplyGuidedWeaponConfig ) ;
case "Heat" : return Configure ( new HeatConfig ( ) , payload , ApplyHeatConfig ) ;
case "HeatStorage" : return Configure ( new HeatStorageConfig ( ) , payload , null ) ;
case "InstantWeapon" : return Configure ( new InstantWeaponConfig ( ) , payload , ApplyInstantWeaponConfig ) ;
case "ItemUsage" : return Configure ( new ItemUsageConfig ( ) , payload , ApplyItemUsageConfig ) ;
case "Launcher" : return Configure ( new LauncherConfig ( ) , payload , ApplyLauncherConfig ) ;
case "MiningTool" : return Configure ( new MiningToolConfig ( ) , payload , ApplyMiningToolConfig ) ;
case "Radiator" : return Configure ( new RadiatorConfig ( ) , payload , ApplyRadiatorConfig ) ;
case "Reactor" : return Configure ( new ReactorConfig ( ) , payload , ApplyReactorConfig ) ;
case "Reflector" : return Configure ( new ReflectorConfig ( ) , payload , ApplyReflectorConfig ) ;
case "ResourceScanner" : return Configure ( new ResourceScannerConfig ( ) , payload , ApplyResourceScannerConfig ) ;
case "Sensor" : return Configure ( new SensorConfig ( ) , payload , ApplySensorConfig ) ;
case "Shield" : return Configure ( new ShieldConfig ( ) , payload , ApplyShieldConfig ) ;
case "StatModifier" : return Configure ( new StatModifierConfig ( ) , payload , ApplyStatModifierConfig ) ;
case "Switch" : return Configure ( new SwitchConfig ( ) , payload , null ) ;
case "Thermotoggle" : return Configure ( new ThermotoggleConfig ( ) , payload , ApplyThermotoggleConfig ) ;
case "Thruster" : return Configure ( new ThrusterConfig ( ) , payload , ApplyThrusterConfig ) ;
case "Trigger" : return Configure ( new TriggerConfig ( ) , payload , null ) ;
case "TurretController" : return Configure ( new TurretControllerConfig ( ) , payload , null ) ;
case "VelocityConversion" : return Configure ( new VelocityConversionConfig ( ) , payload , ApplyVelocityConversionConfig ) ;
case "VelocityLimit" : return Configure ( new VelocityLimitConfig ( ) , payload , ApplyVelocityLimitConfig ) ;
case "Visibility" : return Configure ( new VisibilityConfig ( ) , payload , ApplyVisibilityConfig ) ;
case "Wear" : return Configure ( new WearConfig ( ) , payload , ApplyWearConfig ) ;
default : return null ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static T Configure < T > ( T config , AetheriaRuntimeBehaviorPayload payload , Action < T , BehaviorPayloadReader > apply )
where T : RuntimeBehaviorConfig
{
var reader = new BehaviorPayloadReader ( payload ) ;
config . Kind = payload . Kind ;
config . Group = payload . Group ;
apply ? . Invoke ( config , reader ) ;
return config ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyAetherDriveConfig ( AetherDriveConfig config , BehaviorPayloadReader reader )
{
config . RotorDiameter = reader . Float3 ( 1 , config . RotorDiameter ) ;
config . RotorMass = reader . Float3 ( 2 , config . RotorMass ) ;
config . MaximumRpm = reader . PerformanceStat ( 3 , config . MaximumRpm ) ;
config . CouplingLambda = reader . Float3 ( 4 , config . CouplingLambda ) ;
config . LambdaMultiplier = reader . PerformanceStat ( 5 , config . LambdaMultiplier ) ;
config . CouplingEfficiency = reader . PerformanceStat ( 6 , config . CouplingEfficiency ) ;
config . Torque = reader . PerformanceStat ( 7 , config . Torque ) ;
config . TorqueProfile = reader . BezierCurve ( 8 , config . TorqueProfile ) ;
config . EnergyDraw = reader . PerformanceStat ( 9 , config . EnergyDraw ) ;
config . PassiveCoupling = reader . PerformanceStat ( 10 , config . PassiveCoupling ) ;
config . RpmAudioParameter = reader . UInt ( 11 , config . RpmAudioParameter ) ;
config . TorqueRatioAudioParameter = reader . UInt ( 12 , config . TorqueRatioAudioParameter ) ;
config . Particles = reader . String ( 13 , config . Particles ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyCapacitorConfig ( CapacitorConfig config , BehaviorPayloadReader reader )
{
config . Capacity = reader . PerformanceStat ( 1 , config . Capacity ) ;
config . Efficiency = reader . PerformanceStat ( 2 , config . Efficiency ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyCooldownConfig ( CooldownConfig config , BehaviorPayloadReader reader )
{
config . Cooldown = reader . PerformanceStat ( 1 , config . Cooldown ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyEnergyDrawConfig ( EnergyDrawConfig config , BehaviorPayloadReader reader )
{
config . EnergyDraw = reader . PerformanceStat ( 1 , config . EnergyDraw ) ;
config . PerSecond = reader . Bool ( 2 , config . PerSecond ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyHeatConfig ( HeatConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
config . Heat = reader . PerformanceStat ( 1 , config . Heat ) ;
config . PerSecond = reader . Bool ( 2 , config . PerSecond ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyItemUsageConfig ( ItemUsageConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 23:31:54 +01:00
config . ItemKey = reader . ItemKey ( 1 , config . ItemKey ) ;
2026-06-14 22:07:05 +01:00
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyMiningToolConfig ( MiningToolConfig config , BehaviorPayloadReader reader )
{
config . DamagePerSecond = reader . PerformanceStat ( 1 , config . DamagePerSecond ) ;
config . Efficiency = reader . PerformanceStat ( 2 , config . Efficiency ) ;
config . Penetration = reader . PerformanceStat ( 3 , config . Penetration ) ;
config . Range = reader . PerformanceStat ( 4 , config . Range ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyRadiatorConfig ( RadiatorConfig config , BehaviorPayloadReader reader )
{
config . Emissivity = reader . PerformanceStat ( 1 , config . Emissivity ) ;
config . PumpedHeat = reader . PerformanceStat ( 2 , config . PumpedHeat ) ;
config . TemperatureFloor = reader . Float ( 3 , config . TemperatureFloor ) ;
config . WasteHeat = reader . PerformanceStat ( 4 , config . WasteHeat ) ;
config . EnergyUsage = reader . PerformanceStat ( 5 , config . EnergyUsage ) ;
config . ThermalMass = reader . PerformanceStat ( 6 , config . ThermalMass ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyReactorConfig ( ReactorConfig config , BehaviorPayloadReader reader )
{
config . Charge = reader . PerformanceStat ( 1 , config . Charge ) ;
config . Efficiency = reader . PerformanceStat ( 2 , config . Efficiency ) ;
config . OverloadEfficiency = reader . PerformanceStat ( 3 , config . OverloadEfficiency ) ;
config . ThrottlingFactor = reader . PerformanceStat ( 4 , config . ThrottlingFactor ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyReflectorConfig ( ReflectorConfig config , BehaviorPayloadReader reader )
{
config . CrossSection = reader . PerformanceStat ( 1 , config . CrossSection ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyResourceScannerConfig ( ResourceScannerConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
config . Range = reader . PerformanceStat ( 1 , config . Range ) ;
config . MinimumDensity = reader . PerformanceStat ( 2 , config . MinimumDensity ) ;
config . ScanDuration = reader . PerformanceStat ( 3 , config . ScanDuration ) ;
}
private static void ApplySensorConfig ( SensorConfig config , BehaviorPayloadReader reader )
{
config . Sensitivity = reader . PerformanceStat ( 3 , config . Sensitivity ) ;
config . SensitivityCurve = reader . BezierCurve ( 4 , config . SensitivityCurve ) ;
config . PingBoost = reader . PerformanceStat ( 5 , config . PingBoost ) ;
config . PingEnergy = reader . PerformanceStat ( 6 , config . PingEnergy ) ;
config . PingVisibility = reader . PerformanceStat ( 7 , config . PingVisibility ) ;
config . PingRange = reader . PerformanceStat ( 8 , config . PingRange ) ;
config . PingCooldown = reader . PerformanceStat ( 9 , config . PingCooldown ) ;
config . PingDuration = reader . Float ( 10 , config . PingDuration ) ;
config . PingRadiusExponent = reader . Float ( 11 , config . PingRadiusExponent ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyShieldConfig ( ShieldConfig config , BehaviorPayloadReader reader )
{
config . Efficiency = reader . PerformanceStat ( 1 , config . Efficiency ) ;
config . EnergyUsage = reader . PerformanceStat ( 2 , config . EnergyUsage ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyStatModifierConfig ( StatModifierConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
config . Stat = reader . StatReference ( 1 , config . Stat ) ;
config . Modifier = reader . PerformanceStat ( 2 , config . Modifier ) ;
config . Type = reader . Enum ( 3 , config . Type ) ;
config . RequireBehavior = reader . String ( 4 , config . RequireBehavior ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyThermotoggleConfig ( ThermotoggleConfig config , BehaviorPayloadReader reader )
{
config . TargetTemperature = reader . Float ( 1 , config . TargetTemperature ) ;
config . HighPass = reader . Bool ( 2 , config . HighPass ) ;
config . Adjustable = reader . Bool ( 3 , config . Adjustable ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyThrusterConfig ( ThrusterConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
config . Thrust = reader . PerformanceStat ( 1 , config . Thrust ) ;
config . Visibility = reader . PerformanceStat ( 2 , config . Visibility ) ;
config . Heat = reader . PerformanceStat ( 3 , config . Heat ) ;
config . EnergyUsage = reader . PerformanceStat ( 4 , config . EnergyUsage ) ;
config . ParticlesPrefab = reader . String ( 5 , config . ParticlesPrefab ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyVelocityConversionConfig ( VelocityConversionConfig config , BehaviorPayloadReader reader )
{
config . Lambda = reader . PerformanceStat ( 1 , config . Lambda ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyVelocityLimitConfig ( VelocityLimitConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
config . TopSpeed = reader . PerformanceStat ( 1 , config . TopSpeed ) ;
}
private static void ApplyVisibilityConfig ( VisibilityConfig config , BehaviorPayloadReader reader )
{
config . Visibility = reader . PerformanceStat ( 1 , config . Visibility ) ;
config . VisibilityDecay = reader . PerformanceStat ( 2 , config . VisibilityDecay ) ;
}
private static void ApplyWearConfig ( WearConfig config , BehaviorPayloadReader reader )
{
config . PerSecond = reader . Bool ( 1 , config . PerSecond ) ;
}
2026-06-14 10:54:17 +01:00
2026-06-14 22:07:05 +01:00
private static void ApplyWeaponConfig ( WeaponConfig config , BehaviorPayloadReader reader )
{
config . DamageType = reader . Enum ( 1 , config . DamageType ) ;
config . Damage = reader . PerformanceStat ( 2 , config . Damage ) ;
config . Penetration = reader . PerformanceStat ( 3 , config . Penetration ) ;
config . DamageSpread = reader . PerformanceStat ( 4 , config . DamageSpread ) ;
config . MinRange = reader . PerformanceStat ( 5 , config . MinRange ) ;
config . Range = reader . PerformanceStat ( 6 , config . Range ) ;
config . DamageCurve = reader . BezierCurve ( 7 , config . DamageCurve ) ;
config . EffectPrefab = reader . String ( 8 , config . EffectPrefab ) ;
config . Energy = reader . PerformanceStat ( 9 , config . Energy ) ;
config . Heat = reader . PerformanceStat ( 10 , config . Heat ) ;
config . Visibility = reader . PerformanceStat ( 11 , config . Visibility ) ;
2026-06-14 23:31:54 +01:00
config . AmmoItemKey = reader . ItemKey ( 12 , config . AmmoItemKey ) ;
2026-06-14 22:07:05 +01:00
config . MagazineSize = reader . Int ( 13 , config . MagazineSize ) ;
config . ReloadTime = reader . Float ( 14 , config . ReloadTime ) ;
config . Spread = reader . PerformanceStat ( 15 , config . Spread ) ;
config . Velocity = reader . PerformanceStat ( 16 , config . Velocity ) ;
}
private static void ApplyInstantWeaponConfig ( InstantWeaponConfig config , BehaviorPayloadReader reader )
{
ApplyWeaponConfig ( config , reader ) ;
config . Count = reader . PerformanceStat ( 17 , config . Count ) ;
config . BurstTime = reader . PerformanceStat ( 18 , config . BurstTime ) ;
config . Cooldown = reader . PerformanceStat ( 19 , config . Cooldown ) ;
config . SingleAmmoBurst = reader . Bool ( 20 , config . SingleAmmoBurst ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyConstantWeaponConfig ( ConstantWeaponConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
ApplyWeaponConfig ( config , reader ) ;
config . AmmoInterval = reader . Float ( 17 , config . AmmoInterval ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyChargedWeaponConfig ( ChargedWeaponConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
ApplyInstantWeaponConfig ( config , reader ) ;
config . ChargeTime = reader . PerformanceStat ( 21 , config . ChargeTime ) ;
config . ChargeEnergy = reader . PerformanceStat ( 22 , config . ChargeEnergy ) ;
config . ChargeHeat = reader . PerformanceStat ( 23 , config . ChargeHeat ) ;
config . CanFireEarly = reader . Bool ( 24 , config . CanFireEarly ) ;
config . FailureCharge = reader . Float ( 25 , config . FailureCharge ) ;
config . FailureDamage = reader . Float ( 26 , config . FailureDamage ) ;
config . ChargeFiringDamageMultiplier = reader . Float ( 27 , config . ChargeFiringDamageMultiplier ) ;
config . ChargeFiringSpreadMultiplier = reader . Float ( 28 , config . ChargeFiringSpreadMultiplier ) ;
config . ChargeFiringBurstCountMultiplier = reader . Float ( 29 , config . ChargeFiringBurstCountMultiplier ) ;
config . ChargeFiringVisibilityMultiplier = reader . Float ( 30 , config . ChargeFiringVisibilityMultiplier ) ;
config . ChargeFiringVelocityMultiplier = reader . Float ( 31 , config . ChargeFiringVelocityMultiplier ) ;
config . ChargeFiringHeatMultiplier = reader . Float ( 32 , config . ChargeFiringHeatMultiplier ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyLockWeaponConfig ( LockWeaponConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
ApplyInstantWeaponConfig ( config , reader ) ;
config . LockSpeed = reader . PerformanceStat ( 21 , config . LockSpeed ) ;
config . SensorImpact = reader . PerformanceStat ( 22 , config . SensorImpact ) ;
config . LockAngle = reader . PerformanceStat ( 23 , config . LockAngle ) ;
config . DirectionImpact = reader . PerformanceStat ( 24 , config . DirectionImpact ) ;
config . Decay = reader . PerformanceStat ( 25 , config . Decay ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyLauncherConfig ( LauncherConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
ApplyLockWeaponConfig ( config , reader ) ;
config . GuidanceCurve = reader . Float4Array ( 26 , config . GuidanceCurve ) ;
config . ThrustCurve = reader . Float4Array ( 27 , config . ThrustCurve ) ;
config . LiftCurve = reader . Float4Array ( 28 , config . LiftCurve ) ;
config . Thrust = reader . PerformanceStat ( 29 , config . Thrust ) ;
config . DodgeFrequency = reader . Float ( 30 , config . DodgeFrequency ) ;
config . MissileVelocity = reader . PerformanceStat ( 31 , config . MissileVelocity ) ;
2026-06-14 10:54:17 +01:00
}
2026-06-14 22:07:05 +01:00
private static void ApplyGuidedWeaponConfig ( GuidedWeaponConfig config , BehaviorPayloadReader reader )
2026-06-14 10:54:17 +01:00
{
2026-06-14 22:07:05 +01:00
ApplyInstantWeaponConfig ( config , reader ) ;
config . GuidanceCurve = reader . Float4Array ( 21 , config . GuidanceCurve ) ;
config . ThrustCurve = reader . Float4Array ( 22 , config . ThrustCurve ) ;
config . LiftCurve = reader . Float4Array ( 23 , config . LiftCurve ) ;
config . Thrust = reader . PerformanceStat ( 24 , config . Thrust ) ;
config . DodgeFrequency = reader . Float ( 25 , config . DodgeFrequency ) ;
config . MissileVelocity = reader . PerformanceStat ( 26 , config . MissileVelocity ) ;
}
private sealed class BehaviorPayloadReader
{
private readonly IReadOnlyDictionary < int , AetheriaRuntimeBehaviorValue > _fields ;
public BehaviorPayloadReader ( AetheriaRuntimeBehaviorPayload payload )
{
var fields = new Dictionary < int , AetheriaRuntimeBehaviorValue > ( ) ;
foreach ( var field in payload . Fields )
{
if ( ! fields . ContainsKey ( field . Key ) )
{
fields . Add ( field . Key , field . Value ) ;
}
}
_fields = fields ;
}
public string String ( int key , string fallback = "" )
{
return _fields . TryGetValue ( key , out var value ) ? value . StringValue ? ? "" : fallback ;
}
public bool Bool ( int key , bool fallback = false )
{
return _fields . TryGetValue ( key , out var value ) ? value . BoolValue : fallback ;
}
public float Float ( int key , float fallback = 0 )
{
return _fields . TryGetValue ( key , out var value ) ? ( float ) value . NumberValue : fallback ;
}
public int Int ( int key , int fallback = 0 )
{
return _fields . TryGetValue ( key , out var value ) ? checked ( ( int ) value . NumberValue ) : fallback ;
}
public uint UInt ( int key , uint fallback = 0 )
{
return _fields . TryGetValue ( key , out var value ) ? checked ( ( uint ) value . NumberValue ) : fallback ;
}
public Guid Guid ( int key , Guid fallback = default )
{
return _fields . TryGetValue ( key , out var value ) & & System . Guid . TryParse ( value . LegacyIdValue , out var result )
? result
: fallback ;
}
2026-06-14 23:31:54 +01:00
public string ItemKey ( int key , string fallback = "" )
{
return Guid ( key ) is var itemId & & itemId ! = System . Guid . Empty
? AetheriaRuntimeItemReference . FromLegacyId ( itemId )
: fallback ;
}
2026-06-14 22:07:05 +01:00
public T Enum < T > ( int key , T fallback ) where T : struct
{
if ( ! _fields . TryGetValue ( key , out var value ) )
{
return fallback ;
}
if ( ! string . IsNullOrWhiteSpace ( value . StringValue ) & &
System . Enum . TryParse ( value . StringValue , true , out T parsed ) )
{
return parsed ;
}
return ( T ) System . Enum . ToObject ( typeof ( T ) , checked ( ( int ) value . NumberValue ) ) ;
}
public float3 Float3 ( int key , float3 fallback = default )
{
return _fields . TryGetValue ( key , out var value ) ? ToFloat3 ( value ) : fallback ;
}
public float4 [ ] Float4Array ( int key , float4 [ ] fallback )
{
return _fields . TryGetValue ( key , out var value )
? value . Children . Select ( ToFloat4 ) . ToArray ( )
: fallback ;
}
public PerformanceStat PerformanceStat ( int key , PerformanceStat fallback )
{
return _fields . TryGetValue ( key , out var value ) ? ToPerformanceStat ( value ) : fallback ;
}
public BezierCurve BezierCurve ( int key , BezierCurve fallback )
{
if ( ! _fields . TryGetValue ( key , out var value ) )
{
return fallback ;
}
return new BezierCurve
{
Keys = value . Children . Count > 0
? value . Children [ 0 ] . Children . Select ( ToFloat4 ) . ToArray ( )
: Array . Empty < float4 > ( )
} ;
}
public StatReference StatReference ( int key , StatReference fallback )
{
if ( ! _fields . TryGetValue ( key , out var value ) )
{
return fallback ;
}
return new StatReference
{
Target = ChildString ( value , 1 ) ,
Stat = ChildString ( value , 2 )
} ;
}
private static PerformanceStat ToPerformanceStat ( AetheriaRuntimeBehaviorValue value )
{
return new PerformanceStat
{
Min = ChildFloat ( value , 0 ) ,
Max = ChildFloat ( value , 1 ) ,
HeatExponentMultiplier = ChildFloat ( value , 2 ) ,
DurabilityExponentMultiplier = ChildFloat ( value , 3 ) ,
QualityExponent = ChildFloat ( value , 4 )
} ;
}
private static float3 ToFloat3 ( AetheriaRuntimeBehaviorValue value )
{
return new float3 ( ChildFloat ( value , 0 ) , ChildFloat ( value , 1 ) , ChildFloat ( value , 2 ) ) ;
}
private static float4 ToFloat4 ( AetheriaRuntimeBehaviorValue value )
{
return new float4 ( ChildFloat ( value , 0 ) , ChildFloat ( value , 1 ) , ChildFloat ( value , 2 ) , ChildFloat ( value , 3 ) ) ;
}
private static float ChildFloat ( AetheriaRuntimeBehaviorValue value , int index )
{
return value . Children . Count > index ? ( float ) value . Children [ index ] . NumberValue : 0 ;
}
private static string ChildString ( AetheriaRuntimeBehaviorValue value , int index )
{
return value . Children . Count > index ? value . Children [ index ] . StringValue ? ? "" : "" ;
}
2026-06-14 05:51:15 +01:00
}
2026-06-14 05:15:18 +01:00
public Shape GetRuntimeShape ( ItemInstance item )
{
var typedItem = GetRuntimeItem ( item ) ;
return ToShape ( typedItem ? . ShapeWidth ? ? 1 , typedItem ? . ShapeHeight ? ? 1 , typedItem ? . ShapeCells ) ;
}
private static Shape ToShape ( int width , int height , IReadOnlyList < AetheriaRuntimeShapeCell > cells )
{
var shape = new Shape ( Math . Max ( width , 1 ) , Math . Max ( height , 1 ) ) ;
if ( cells = = null ) return shape ;
foreach ( var cell in cells )
shape [ new int2 ( cell . X , cell . Y ) ] = true ;
return shape ;
}
2026-06-14 22:58:02 +01:00
public AetheriaRuntimeItemReference CreateReference ( AetheriaRuntimeCatalogItem item )
2026-06-14 07:27:23 +01:00
{
2026-06-14 22:58:02 +01:00
return new AetheriaRuntimeItemReference ( ToItemKey ( item ) ) ;
}
private static string ToItemKey ( AetheriaRuntimeCatalogItem item )
{
2026-06-15 01:31:16 +01:00
return item ? . ItemKey ? ? "" ;
2026-06-14 07:27:23 +01:00
}
2020-04-16 03:08:18 +01:00
public void Log ( string s )
{
_logger ( s ) ;
}
2020-03-22 19:05:26 +00:00
public float GetMass ( ItemInstance item )
{
2026-06-14 04:35:51 +01:00
var typedItem = GetRuntimeItem ( item ) ;
if ( typedItem = = null )
{
return 0 ;
}
2021-04-18 05:13:20 +01:00
return item switch
2020-03-22 19:05:26 +00:00
{
2026-06-14 04:35:51 +01:00
CraftedItemInstance _ = > ( float ) typedItem . Mass ,
SimpleCommodity commodity = > ( float ) typedItem . Mass * commodity . Quantity ,
2021-04-18 05:13:20 +01:00
_ = > 0
} ;
2020-03-22 19:05:26 +00:00
}
2020-06-05 04:16:33 +01:00
public float GetThermalMass ( ItemInstance item )
2020-03-22 19:05:26 +00:00
{
2026-06-14 04:35:51 +01:00
var typedItem = GetRuntimeItem ( item ) ;
if ( typedItem = = null )
{
return 0 ;
}
2021-04-18 05:13:20 +01:00
return item switch
2020-03-22 19:05:26 +00:00
{
2026-06-14 04:35:51 +01:00
CraftedItemInstance _ = > ( float ) ( typedItem . Mass * typedItem . SpecificHeat ) ,
SimpleCommodity commodity = > ( float ) ( typedItem . Mass * typedItem . SpecificHeat * commodity . Quantity ) ,
2021-04-18 05:13:20 +01:00
_ = > 0
} ;
2020-03-22 19:05:26 +00:00
}
// Returns stat when not equipped
2020-12-10 23:20:38 +00:00
public float Evaluate ( PerformanceStat stat , EquippableItem item )
2020-03-22 19:05:26 +00:00
{
2026-06-14 06:23:45 +01:00
var typedItem = GetRuntimeItem ( item ) ;
var maxDurability = ( float ) ( typedItem ? . Durability ? ? 0 ) ;
if ( maxDurability < = 0 )
{
maxDurability = Math . Max ( item . Durability , 1f ) ;
}
2021-03-22 05:22:31 +00:00
var quality = pow ( item . Quality , stat . QualityExponent ) ;
var durabilityExponent = lerp (
GameplaySettings . DurabilityQualityMin ,
GameplaySettings . DurabilityQualityMax ,
pow ( item . Quality , GameplaySettings . DurabilityQualityExponent ) ) ;
2026-06-14 06:23:45 +01:00
var durability = pow ( item . Durability / maxDurability , durabilityExponent * stat . DurabilityExponentMultiplier ) ;
2021-03-22 05:22:31 +00:00
var result = lerp ( stat . Min , stat . Max , quality * durability ) ;
2021-03-27 10:03:24 +00:00
if ( float . IsNaN ( result ) )
2026-06-14 11:12:48 +01:00
throw new InvalidOperationException ( $"Performance Stat on {typedItem?.Name ?? item.ItemId.ToString()} evaluating as NaN: input data is invalid! Durability: {item.Durability} / {maxDurability}" ) ;
2020-03-22 19:05:26 +00:00
return result ;
2021-03-22 05:22:31 +00:00
2020-03-22 19:05:26 +00:00
}
2020-05-25 03:05:30 +01:00
2021-03-20 04:20:17 +00:00
public int GetPrice ( CraftedItemInstance item )
{
2026-06-14 04:35:51 +01:00
var typedItem = GetRuntimeItem ( item ) ;
return typedItem = = null
? 0
: ( int ) ( GameplaySettings . QualityPriceModifier . Evaluate ( item . Quality ) * typedItem . Price ) ;
2021-03-20 04:20:17 +00:00
}
2026-06-14 05:56:41 +01:00
public int GetPrice ( ItemInstance item )
{
var typedItem = GetRuntimeItem ( item ) ;
if ( typedItem = = null )
{
return 0 ;
}
return item switch
{
CraftedItemInstance crafted = > ( int ) ( GameplaySettings . QualityPriceModifier . Evaluate ( crafted . Quality ) * typedItem . Price ) ,
SimpleCommodity commodity = > typedItem . Price * commodity . Quantity ,
_ = > typedItem . Price
} ;
}
2026-06-14 07:41:32 +01:00
public SimpleCommodity CreateSimpleCommodityInstance ( AetheriaRuntimeCatalogItem item , int count )
2020-03-22 19:05:26 +00:00
{
2026-06-15 01:31:16 +01:00
if ( ! string . IsNullOrWhiteSpace ( item ? . ItemKey ) )
2020-06-02 07:01:03 +01:00
{
2026-06-14 07:41:32 +01:00
return new SimpleCommodity
2020-03-22 19:05:26 +00:00
{
2026-06-14 22:58:02 +01:00
Reference = CreateReference ( item ) ,
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
}
2026-06-14 07:41:32 +01:00
_logger ( "Attempted to create simple commodity instance using missing typed item data" ) ;
2020-04-09 07:48:45 +01:00
return null ;
2020-03-22 19:05:26 +00:00
}
2021-01-19 01:22:56 +00:00
public ItemInstance Instantiate ( ItemInstance item )
{
2026-06-14 07:41:32 +01:00
var typedItem = GetRuntimeItem ( item ) ;
if ( typedItem = = null )
2021-01-27 00:05:30 +00:00
{
2026-06-14 11:12:48 +01:00
_logger ( $"Attempted to instantiate missing item id {item?.ItemId}" ) ;
2026-06-14 07:41:32 +01:00
return null ;
2021-01-27 00:05:30 +00:00
}
2026-06-14 07:41:32 +01:00
ItemInstance instance = item switch
2021-01-27 00:05:30 +00:00
{
2026-06-14 07:41:32 +01:00
SimpleCommodity simple = > CreateSimpleCommodityInstance ( typedItem , simple . Quantity ) ,
CraftedItemInstance = > CreateCraftedInstance ( typedItem ) ,
_ = > null
} ;
if ( instance ! = null )
instance . Rotation = item . Rotation ;
return instance ;
2021-01-19 01:22:56 +00:00
}
2021-03-27 10:03:24 +00:00
2026-06-14 07:41:32 +01:00
public CraftedItemInstance CreateCraftedInstance ( AetheriaRuntimeCatalogItem item , float quality )
2021-03-27 10:03:24 +00:00
{
2026-06-14 07:41:32 +01:00
if ( IsEquippable ( item ) )
return CreateEquippableInstance ( item , quality ) ;
2026-06-15 01:31:16 +01:00
if ( string . IsNullOrWhiteSpace ( item ? . ItemKey ) )
2021-03-27 10:03:24 +00:00
{
2026-06-14 07:41:32 +01:00
throw new NullReferenceException ( "Attempted to create crafted item instance using missing typed item data!" ) ;
}
2026-06-14 09:38:55 +01:00
if ( string . Equals ( item . Category , AetheriaRuntimeItemCategories . Consumable , StringComparison . Ordinal ) )
2026-06-14 07:41:32 +01:00
{
return new ConsumableItem
2021-03-27 10:03:24 +00:00
{
2026-06-14 22:58:02 +01:00
Reference = CreateReference ( item ) ,
2026-06-14 07:41:32 +01:00
Quality = quality
2021-03-27 10:03:24 +00:00
} ;
}
2026-06-14 07:41:32 +01:00
return new CompoundCommodity
2021-03-27 10:03:24 +00:00
{
2026-06-14 22:58:02 +01:00
Reference = CreateReference ( item ) ,
2021-03-27 10:03:24 +00:00
Quality = quality
} ;
2026-06-14 07:41:32 +01:00
}
public CraftedItemInstance CreateCraftedInstance ( AetheriaRuntimeCatalogItem item )
{
return CreateCraftedInstance ( item , SelectCraftedQuality ( ) ) ;
2021-03-27 10:03:24 +00:00
}
2026-06-14 06:35:34 +01:00
2026-06-14 07:27:23 +01:00
public EquippableItem CreateEquippableInstance ( AetheriaRuntimeCatalogItem item , float quality )
{
2026-06-15 01:31:16 +01:00
if ( string . IsNullOrWhiteSpace ( item ? . ItemKey ) )
2026-06-14 07:27:23 +01:00
{
throw new NullReferenceException ( "Attempted to create equippable item instance using missing typed item data!" ) ;
}
return new EquippableItem
{
2026-06-14 22:58:02 +01:00
Reference = CreateReference ( item ) ,
2026-06-14 07:27:23 +01:00
Quality = quality ,
Durability = item . Durability > 0 ? ( float ) item . Durability : 1f
} ;
}
public EquippableItem CreateEquippableInstance ( AetheriaRuntimeCatalogItem item )
{
return CreateEquippableInstance ( item , SelectCraftedQuality ( ) ) ;
}
private float SelectCraftedQuality ( )
{
2021-03-08 02:57:38 +00:00
var quality = Random . NextFloat ( ) ;
var tier = GameplaySettings . Tiers [ 0 ] ;
foreach ( var t in GameplaySettings . Tiers )
{
if ( t . Rarity > quality )
tier = t ;
}
2020-03-22 19:05:26 +00:00
2026-06-14 07:27:23 +01:00
return tier . Quality ;
}
2026-06-14 07:41:32 +01:00
private static bool IsEquippable ( AetheriaRuntimeCatalogItem item )
{
return ! string . IsNullOrWhiteSpace ( item ? . HardpointType ) | |
2026-06-14 09:38:55 +01:00
string . Equals ( item ? . Category , AetheriaRuntimeItemCategories . Hull , StringComparison . Ordinal ) ;
2026-06-14 07:41:32 +01:00
}
2021-03-08 02:57:38 +00:00
public ( RarityTier tier , int upgrades ) GetTier ( CraftedItemInstance item )
{
var tier = GameplaySettings . Tiers [ 0 ] ;
foreach ( var t in GameplaySettings . Tiers )
if ( item . Quality + . 001f > t . Quality )
tier = t ;
int upgrades = ( int ) ( ( item . Quality - tier . Quality ) / . 0499f ) ;
return ( tier , upgrades ) ;
}
2026-06-13 07:11:16 +01:00
}