diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs index 4418b477..66492598 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Sandbox.ModAPI.Ingame; using Sandbox.ModAPI.Interfaces; @@ -9,7 +9,7 @@ namespace CoreSystems.Api { /// - /// https://github.com/sstixrud/CoreSystems/blob/master/BaseData/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs + /// https://github.com/Ash-LikeSnow/WeaponCore/blob/master/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs /// public class WcPbApi { @@ -56,6 +56,19 @@ public class WcPbApi private Func> _isInRange; private Action> _monitorEvents; private Action> _unmonitorEvents; + + // Descriptions made by Aristeas, with Sigmund Froid's https://steamcommunity.com/sharedfiles/filedetails/?id=2178802013 as a reference. + // PR accepted after prolific begging by Aryx + + /// + /// Activates the WcPbAPI using . + /// + /// + /// Recommended to use 'Me' in for simplicity. + /// + /// + /// if all methods assigned correctly, otherwise + /// Throws exception if WeaponCore is not present public bool Activate(Sandbox.ModAPI.Ingame.IMyTerminalBlock pbBlock) { var dict = pbBlock.GetProperty("WcPbAPI")?.As>().GetValue(pbBlock); @@ -63,6 +76,14 @@ public bool Activate(Sandbox.ModAPI.Ingame.IMyTerminalBlock pbBlock) return ApiAssign(dict); } + /// + /// Bulk calls for all WcPbAPI methods. + /// + /// + /// Not useful for most scripts, but is public nonetheless. + /// + /// + /// if all methods assigned correctly, otherwise public bool ApiAssign(IReadOnlyDictionary delegates) { if (delegates == null) @@ -114,9 +135,21 @@ public bool ApiAssign(IReadOnlyDictionary delegates) return true; } + /// + /// Links method to internal API method of name + /// + /// + /// Not useful for most scripts, but is public nonetheless. + /// + /// + /// + /// + /// + /// private void AssignMethod(IReadOnlyDictionary delegates, string name, ref T field) where T : class { - if (delegates == null) { + if (delegates == null) + { field = null; return; } @@ -131,112 +164,533 @@ private void AssignMethod(IReadOnlyDictionary delegates, st $"{GetType().Name} :: Delegate {name} is not type {typeof(T)}, instead it's: {del.GetType()}"); } + /// + /// Populates with of all loaded WeaponCore weapons. + /// + /// + /// + /// public void GetAllCoreWeapons(ICollection collection) => _getCoreWeapons?.Invoke(collection); + /// + /// Populates with of all loaded WeaponCore fixed weapons. + /// + /// + /// + /// public void GetAllCoreStaticLaunchers(ICollection collection) => _getCoreStaticLaunchers?.Invoke(collection); + /// + /// Populates with of all loaded WeaponCore turret weapons. + /// + /// + /// + /// public void GetAllCoreTurrets(ICollection collection) => _getCoreTurrets?.Invoke(collection); + /// + /// Populates with of contents: + /// + /// Key: Name of weapon. + /// Value: ID of weapon within . + /// + /// + /// + /// + /// public bool GetBlockWeaponMap(Sandbox.ModAPI.Ingame.IMyTerminalBlock weaponBlock, IDictionary collection) => _getBlockWeaponMap?.Invoke(weaponBlock, collection) ?? false; + /// + /// Returns a containing information about projectiles targeting . + /// + /// + /// + /// with contents: + /// + /// Is being locked? + /// Number of locked projectiles. + /// Time (in ticks) locked. + /// + /// public MyTuple GetProjectilesLockedOn(long victim) => _getProjectilesLockedOn?.Invoke(victim) ?? new MyTuple(); + /// + /// Populates with contents: + /// + /// Key: Hostile within targeting range of 's grid + /// Value: Threat level of Key + /// + /// + /// + /// public void GetSortedThreats(Sandbox.ModAPI.Ingame.IMyTerminalBlock pBlock, IDictionary collection) => _getSortedThreats?.Invoke(pBlock, collection); + + /// + /// Populates with contents: + /// + /// Friendly within targeting range of 's + /// + /// + /// + /// public void GetObstructions(Sandbox.ModAPI.Ingame.IMyTerminalBlock pBlock, ICollection collection) => _getObstructions?.Invoke(pBlock, collection); + + /// + /// Returns the GridAi Target with priority of with EntityID . + /// + /// + /// If the grid is valid but does not have a target, an empty is returned. + /// + /// Default = 0 returns the player-selected target. + /// + /// + /// + /// + /// Nullable . Null if does not exist or lacks GridAi. public MyDetectedEntityInfo? GetAiFocus(long shooter, int priority = 0) => _getAiFocus?.Invoke(shooter, priority); + /// + /// Sets the GridAi Target of 's to EntityID . + /// + /// + /// Default = 0 sets the player-visible target. + /// + /// + /// + /// + /// if successful, otherwise. public bool SetAiFocus(Sandbox.ModAPI.Ingame.IMyTerminalBlock pBlock, long target, int priority = 0) => _setAiFocus?.Invoke(pBlock, target, priority) ?? false; + + /// + /// Unsets the GridAi Target of 's . + /// + /// + /// may be set to 0. + /// + /// + /// + /// if successful, otherwise. public bool ReleaseAiFocus(Sandbox.ModAPI.Ingame.IMyTerminalBlock pBlock, long playerId) => _releaseAiFocus?.Invoke(pBlock, playerId) ?? false; + + /// + /// Returns the WeaponAi target of on . + /// + /// + /// Seems to always return null for static weapons. + /// + /// + /// + /// Nullable . public MyDetectedEntityInfo? GetWeaponTarget(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId = 0) => _getWeaponTarget?.Invoke(weapon, weaponId); + /// + /// Sets the WeaponAi target of on to EntityID . + /// + /// + /// + /// public void SetWeaponTarget(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long target, int weaponId = 0) => _setWeaponTarget?.Invoke(weapon, target, weaponId); + /// + /// Fires on once. + /// + /// + /// uses all weapons on . + /// + /// + /// + /// public void FireWeaponOnce(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, bool allWeapons = true, int weaponId = 0) => _fireWeaponOnce?.Invoke(weapon, allWeapons, weaponId); + /// + /// Sets the Shoot On/Off toggle of on to . + /// + /// + /// uses all weapons on . + /// + /// + /// + /// + /// public void ToggleWeaponFire(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, bool on, bool allWeapons, int weaponId = 0) => _toggleWeaponFire?.Invoke(weapon, on, allWeapons, weaponId); + /// + /// Returns whether or not on is ready to fire. + /// + /// + /// uses all weapons on . + /// + /// + /// + /// + /// + /// if ready to fire, otherwise. public bool IsWeaponReadyToFire(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId = 0, bool anyWeaponReady = true, bool shootReady = false) => _isWeaponReadyToFire?.Invoke(weapon, weaponId, anyWeaponReady, shootReady) ?? false; + /// + /// Returns the current Aiming Radius of on . + /// + /// + /// + /// range in meters. public float GetMaxWeaponRange(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getMaxWeaponRange?.Invoke(weapon, weaponId) ?? 0f; + /// + /// Populates with contents: + /// + /// Allowed target type name for on . + /// + /// + /// + /// + /// + /// true if and are valid, false otherwise. public bool GetTurretTargetTypes(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, IList collection, int weaponId = 0) => _getTurretTargetTypes?.Invoke(weapon, collection, weaponId) ?? false; + /// + /// Sets allowed target types for on to . + /// + /// + /// Invalid target types are ignored. + /// + /// + /// + /// public void SetTurretTargetTypes(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, IList collection, int weaponId = 0) => _setTurretTargetTypes?.Invoke(weapon, collection, weaponId); + /// + /// Sets the current Aiming Range of to . + /// + /// + /// Values over the maximum possible Aiming Range of will set it to the maximum possible. + /// + /// + /// public void SetBlockTrackingRange(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, float range) => _setBlockTrackingRange?.Invoke(weapon, range); + /// + /// Returns whether or not on is aligned with EntityID . + /// + /// + /// + /// + /// true if aligned and valid, false otherwise. public bool IsTargetAligned(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long targetEnt, int weaponId) => _isTargetAligned?.Invoke(weapon, targetEnt, weaponId) ?? false; + /// + /// Returns whether or not on is aligned with EntityID . + /// + /// + /// + /// + /// + /// with contents: + /// + /// Is aligned? False if invalid. + /// Position of target. Null if invalid. + /// + /// public MyTuple IsTargetAlignedExtended(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long targetEnt, int weaponId) => _isTargetAlignedExtended?.Invoke(weapon, targetEnt, weaponId) ?? new MyTuple(); + /// + /// Returns whether or not on is aligned with EntityID . + /// + /// + /// Like , but takes target velocity and acceleration into account. + /// + /// + /// + /// + /// true if aligned and valid, false otherwise public bool CanShootTarget(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long targetEnt, int weaponId) => _canShootTarget?.Invoke(weapon, targetEnt, weaponId) ?? false; + /// + /// Returns the lead position of on , with target EntityId . + /// + /// + /// + /// + /// Nullable target lead position. Null if target or weapon invalid. public Vector3D? GetPredictedTargetPosition(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long targetEnt, int weaponId) => _getPredictedTargetPos?.Invoke(weapon, targetEnt, weaponId) ?? null; + /// + /// Returns the heat level of . + /// + /// + /// If is invalid or does not have heat, returns 0f. Heat may exceed 1.0f in case of overheat. + /// + /// + /// heat as percentage between 0.0f and 1.0f public float GetHeatLevel(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon) => _getHeatLevel?.Invoke(weapon) ?? 0f; + + /// + /// Returns current power consumption of . + /// + /// + /// Power in MW public float GetCurrentPower(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon) => _currentPowerConsumption?.Invoke(weapon) ?? 0f; + + /// + /// Returns maximum power consumption of . + /// + /// + /// Power in MW public float GetMaxPower(MyDefinitionId weaponDef) => _getMaxPower?.Invoke(weaponDef) ?? 0f; + + /// + /// Returns whether or not EntityId has a GridAi. + /// + /// + /// true if GridAi present, false otherwise. public bool HasGridAi(long entity) => _hasGridAi?.Invoke(entity) ?? false; + + /// + /// Returns whether or not has a WeaponCore weapon. + /// + /// + /// true if weapon present, false otherwise. public bool HasCoreWeapon(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon) => _hasCoreWeapon?.Invoke(weapon) ?? false; + + /// + /// Returns the total optimal DPS of with EntityId . + /// + /// + /// DPS. public float GetOptimalDps(long entity) => _getOptimalDps?.Invoke(entity) ?? 0f; + /// + /// Returns the active ammo name of on . + /// + /// + /// + /// AmmoName public string GetActiveAmmo(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getActiveAmmo?.Invoke(weapon, weaponId) ?? null; + /// + /// Sets the active ammo name of on to . + /// + /// + /// Does nothing if is invalid. + /// + /// + /// public void SetActiveAmmo(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId, string ammoType) => _setActiveAmmo?.Invoke(weapon, weaponId, ammoType); + /// + /// Assigns projectile callback to on . + /// + /// + /// has parameters: + /// + /// Parent weapon EntityId? + /// Parent weapon partId + /// Projectile EntityId + /// Target EntityId + /// ProjectilePosition if active, LastHit if destroyed + /// ProjectileExists? + /// + /// + /// + /// + /// public void MonitorProjectileCallback(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId, Action action) => _monitorProjectile?.Invoke(weapon, weaponId, action); + /// + /// Unassigns projectile callback to on . + /// + /// + /// has parameters: + /// + /// Parent weapon EntityId? + /// Parent weapon partId + /// Projectile EntityId + /// Target EntityId + /// ProjectilePosition if active, LastHit if destroyed + /// ProjectileExists? + /// + /// + /// + /// + /// public void UnMonitorProjectileCallback(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId, Action action) => _unMonitorProjectile?.Invoke(weapon, weaponId, action); + /// + /// Returns ProjectileState of . + /// + /// + /// + /// with contents: + /// + /// Position + /// Velocity + /// BaseDamagePool + /// BaseHealthPool + /// Target EntityId + /// AmmoRound Name + /// + /// public MyTuple GetProjectileState(ulong projectileId) => _getProjectileState?.Invoke(projectileId) ?? new MyTuple(); - + + /// + /// Returns the total effective DPS of with EntityId . + /// + /// + /// DPS. public float GetConstructEffectiveDps(long entity) => _getConstructEffectiveDps?.Invoke(entity) ?? 0f; + /// + /// Returns the Id of 's controlling player. + /// + /// + /// PlayerId. -1 if invalid or uncontrolled. public long GetPlayerController(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon) => _getPlayerController?.Invoke(weapon) ?? -1; + /// + /// Returns the rotation matrix of on 's azimuth part. + /// + /// + /// + /// AzimuthMatrix public Matrix GetWeaponAzimuthMatrix(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getWeaponAzimuthMatrix?.Invoke(weapon, weaponId) ?? Matrix.Zero; + /// + /// Returns the rotation matrix of on 's elevation part. + /// + /// + /// + /// ElevationMatrix public Matrix GetWeaponElevationMatrix(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getWeaponElevationMatrix?.Invoke(weapon, weaponId) ?? Matrix.Zero; + /// + /// Returns whether or not is a valid target for . + /// + /// + /// will return false if 's threat score is zero. + /// will return false if is non-hostile. + /// + /// + /// + /// + /// + /// public bool IsTargetValid(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long targetId, bool onlyThreats, bool checkRelations) => _isTargetValid?.Invoke(weapon, targetId, onlyThreats, checkRelations) ?? false; + /// + /// Returns scope information of on . + /// + /// + /// + /// + /// with contents: + /// + /// Position + /// Direction + /// + /// public MyTuple GetWeaponScope(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getWeaponScope?.Invoke(weapon, weaponId) ?? new MyTuple(); + // terminalBlock, Threat, Other, Something + /// + /// Returns whether or not 's 's GridAi's PrimaryTarget is in range. + /// + /// + /// The second value in the returned might be legacy from when players could select two targets. + /// + /// + /// + /// with contents: + /// + /// PrimaryTarget In Range? + /// OtherTarget In Range? + /// + /// public MyTuple IsInRange(Sandbox.ModAPI.Ingame.IMyTerminalBlock block) => _isInRange?.Invoke(block) ?? new MyTuple(); + + /// + /// Adds event monitor to weapon on . + /// + /// + /// has parameters: + /// + /// State + /// Active + /// + /// + /// + /// List of event triggers:
+ /// 0 Reloading
+ /// 1 Firing
+ /// 2 Tracking
+ /// 3 Overheated
+ /// 4 TurnOn
+ /// 5 TurnOff
+ /// 6 BurstReload
+ /// 7 NoMagsToLoad
+ /// 8 PreFire
+ /// 9 EmptyOnGameLoad
+ /// 10 StopFiring
+ /// 11 StopTracking
+ /// 12 LockDelay
+ /// 13 Init
+ /// 14 Homing
+ /// 15 TargetAligned
+ /// 16 WhileOn
+ /// 17 TargetRanged100
+ /// 18 TargetRanged75
+ /// 19 TargetRanged50
+ /// 20 TargetRanged25 + ///
+ ///
+ /// + /// + /// public void MonitorEvents(Sandbox.ModAPI.Ingame.IMyTerminalBlock entity, int partId, Action action) => _monitorEvents?.Invoke(entity, partId, action); + /// + /// Removes event monitor from weapon on . + /// + /// + /// has parameters: + /// + /// State + /// Active + /// + /// + /// + /// + /// public void UnMonitorEvents(Sandbox.ModAPI.Ingame.IMyTerminalBlock entity, int partId, Action action) => _unmonitorEvents?.Invoke(entity, partId, action); diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index 2e4ab009..1085c15d 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -1309,7 +1309,19 @@ internal void AvClose() var particle = AmmoDef.AmmoGraphics.Particles.Hit; var keenStrikesAgain = particle.Offset == Vector3D.MaxValue; - var matrix = !keenStrikesAgain ? MatrixD.CreateTranslation(pos) : MatrixD.CreateWorld(pos, VisualDir, OriginUp); + MatrixD matrix = MatrixD.CreateTranslation(pos); + if(keenStrikesAgain) + { + matrix = MatrixD.CreateWorld(pos, VisualDir, OriginUp); + } + else if (particle.Offset == Vector3D.MinValue) + { + float interference; + Vector3D localGrav = Session.I.Physics.CalculateNaturalGravityAt(pos, out interference); + localGrav.Normalize(); + if(localGrav != Vector3D.Zero) + matrix = MatrixD.CreateWorld(pos, Vector3D.CalculatePerpendicularVector(localGrav), -localGrav); + } MyParticleEffect detEffect; if (MyParticlesManager.TryCreateParticleEffect(a.Const.DetParticleStr, ref matrix, ref pos, uint.MaxValue, out detEffect)) { diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 3b35ecf7..53c7882a 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -146,7 +146,19 @@ internal void End() var pos = Session.I.Tick - av.Hit.HitTick <= 1 && !MyUtils.IsZero(av.Hit.SurfaceHit) ? av.Hit.SurfaceHit : av.TracerFront; var particle = av.AmmoDef.AmmoGraphics.Particles.Hit; var keenStrikesAgain = particle.Offset == Vector3D.MaxValue; - var matrix = !keenStrikesAgain ? MatrixD.CreateTranslation(pos) : MatrixD.CreateWorld(pos, av.VisualDir, av.OriginUp); + MatrixD matrix = MatrixD.CreateTranslation(pos); + if (keenStrikesAgain) + { + matrix = MatrixD.CreateWorld(pos, av.VisualDir, av.OriginUp); + } + else if (particle.Offset == Vector3D.MinValue) + { + float interference; + Vector3D localGrav = Session.I.Physics.CalculateNaturalGravityAt(pos, out interference); + localGrav.Normalize(); + if (localGrav != Vector3D.Zero) + matrix = MatrixD.CreateWorld(pos, Vector3D.CalculatePerpendicularVector(localGrav), -localGrav); + } MyParticleEffect hitEffect; if (MyParticlesManager.TryCreateParticleEffect(av.AmmoDef.Const.HitParticleStr, ref matrix, ref pos, uint.MaxValue, out hitEffect)) @@ -799,7 +811,8 @@ internal void RunAvEffects1() var weapon = avEffect.Weapon; var muzzle = avEffect.Muzzle; var ticksAgo = Session.I.Tick - avEffect.StartTick; - var bAv = weapon.System.Values.HardPoint.Graphics.Effect1; + var ammoParticleOverride = weapon.ActiveAmmoDef.AmmoDef.Const.OverrideWeaponEffect; + var bAv = ammoParticleOverride ? weapon.ActiveAmmoDef.AmmoDef.AmmoGraphics.Particles.WeaponEffect1Override : weapon.System.Values.HardPoint.Graphics.Effect1; var effect = weapon.Effects1[muzzle.MuzzleId]; var effectExists = effect != null; @@ -833,18 +846,17 @@ internal void RunAvEffects1() } - var particles = weapon.System.Values.HardPoint.Graphics.Effect1; var renderId = info.Entity.Render.GetRenderObjectID(); var matrix = info.DummyMatrix; var pos = info.Position; - matrix.Translation = info.LocalPosition + particles.Offset; + matrix.Translation = info.LocalPosition + bAv.Offset; if (!effectExists && ticksAgo <= 0) { MyParticleEffect newEffect; - if (MyParticlesManager.TryCreateParticleEffect(particles.Name, ref matrix, ref pos, renderId, out newEffect)) + if (MyParticlesManager.TryCreateParticleEffect(bAv.Name, ref matrix, ref pos, renderId, out newEffect)) { - newEffect.UserScale = particles.Extras.Scale; + newEffect.UserScale = bAv.Extras.Scale; if (newEffect.Loop) { weapon.Effects1[muzzle.MuzzleId] = newEffect; @@ -908,18 +920,17 @@ internal void RunAvEffects2() weapon.Comp.Ai.VelocityUpdateTick = Session.I.Tick; } - var particles = weapon.System.Values.HardPoint.Graphics.Effect2; var renderId = info.Entity.Render.GetRenderObjectID(); var matrix = info.DummyMatrix; var pos = info.Position; - matrix.Translation = info.LocalPosition + particles.Offset; + matrix.Translation = info.LocalPosition + bAv.Offset; if (!effectExists && ticksAgo <= 0) { MyParticleEffect newEffect; - if (MyParticlesManager.TryCreateParticleEffect(particles.Name, ref matrix, ref pos, renderId, out newEffect)) + if (MyParticlesManager.TryCreateParticleEffect(bAv.Name, ref matrix, ref pos, renderId, out newEffect)) { - newEffect.UserScale = particles.Extras.Scale; + newEffect.UserScale = bAv.Extras.Scale; if (newEffect.Loop) { diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index 26043637..0ef684d7 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -515,7 +515,9 @@ public struct LoadingDef [ProtoMember(23)] internal bool GoHomeToReload; [ProtoMember(24)] internal bool DropTargetUntilLoaded; [ProtoMember(25)] internal bool ProhibitCoolingWhenOff; - + [ProtoMember(26)] internal float InventoryFillAmount; + [ProtoMember(27)] internal float InventoryLowAmount; + [ProtoMember(28)] internal bool UseWorldInventoryVolumeMultiplier; } @@ -656,6 +658,8 @@ public class AmmoDef [ProtoMember(29)] internal bool NpcSafe; [ProtoMember(30)] internal SynchronizeDef Sync; [ProtoMember(31)] internal bool NoGridOrArmorScaling; + [ProtoMember(32)] internal string TerminalName; + [ProtoContract] public struct SynchronizeDef @@ -814,6 +818,7 @@ public struct AmmoParticleDef [ProtoMember(1)] internal ParticleDef Ammo; [ProtoMember(2)] internal ParticleDef Hit; [ProtoMember(3)] internal ParticleDef Eject; + [ProtoMember(4)] internal ParticleDef WeaponEffect1Override; } [ProtoContract] @@ -1005,6 +1010,11 @@ public enum SpawnType [ProtoMember(2)] internal float SpawnChance; [ProtoMember(3)] internal SpawnType Type; [ProtoMember(4)] internal ComponentDef CompDef; + [ProtoMember(5)] internal Randomize SpeedVariance; + [ProtoMember(6)] internal Randomize DirectionVariance; + [ProtoMember(7)] internal Vector3D Rotation; + [ProtoMember(8)] internal Randomize RotationVariance; + [ProtoContract] public struct ComponentDef @@ -1295,6 +1305,7 @@ internal enum GuidanceType [ProtoMember(15)] internal ApproachDef[] Approaches; [ProtoMember(16)] internal double TotalAcceleration; [ProtoMember(17)] internal OnHitDef OnHit; + [ProtoMember(18)] internal float DragPerSecond; [ProtoContract] public struct SmartsDef diff --git a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs index f1cd9c31..923d5574 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs @@ -1,4 +1,5 @@ using Sandbox.Game.Entities; +using Sandbox.ModAPI; using System; using System.Collections.Generic; using VRage.Game; @@ -232,6 +233,7 @@ public AmmoType(AmmoDef ammoDef, MyDefinitionId ammoDefinitionId, MyDefinitionId public readonly float MaxTargetRadius; public readonly float MaxAmmoVolume; public readonly float FullAmmoVolume; + public readonly float LowAmmoVolume; public readonly float FiringSoundDistSqr; public readonly float ReloadSoundDistSqr; public readonly float BarrelSoundDistSqr; @@ -277,8 +279,9 @@ public WeaponSystem(WeaponStructure structure, MyStringHash partNameIdHash, MySt WeaponId = weaponId; PartName = partName; AmmoTypes = weaponAmmoTypes; - MaxAmmoVolume = Values.HardPoint.HardWare.InventorySize; - FullAmmoVolume = MaxAmmoVolume * 0.75f; + MaxAmmoVolume = Values.HardPoint.HardWare.InventorySize * (values.HardPoint.Loading.UseWorldInventoryVolumeMultiplier ? MyAPIGateway.Session.BlocksInventorySizeMultiplier : 1); + FullAmmoVolume = MaxAmmoVolume * (values.HardPoint.Loading.InventoryFillAmount > 0 ? values.HardPoint.Loading.InventoryFillAmount : 0.75f); + LowAmmoVolume = MaxAmmoVolume * (values.HardPoint.Loading.InventoryLowAmount > 0 ? values.HardPoint.Loading.InventoryLowAmount : 0.25f); CeaseFireDelay = values.HardPoint.DelayCeaseFire; DelayCeaseFire = CeaseFireDelay > 0; DelayToFire = values.HardPoint.Loading.DelayUntilFire; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index b74402a4..a80f5067 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -80,6 +80,7 @@ public enum Texture public readonly string DetParticleStr; public readonly string DetSoundStr; public readonly string ShotSoundStr; + public readonly string TerminalName; public readonly int ApproachesCount; public readonly int MaxObjectsHit; public readonly int TargetLossTime; @@ -138,6 +139,7 @@ public enum Texture public readonly bool CustomDetParticle; public readonly bool FieldParticle; public readonly bool AmmoSkipAccel; + public readonly bool AmmoUseDrag; public readonly bool LineWidthVariance; public readonly bool LineColorVariance; public readonly bool SegmentWidthVariance; @@ -243,6 +245,7 @@ public enum Texture public readonly bool ZeroEffortNav; public readonly bool ProjectilesFirst; public readonly bool OnHit; + public readonly bool OverrideWeaponEffect; public readonly float LargeGridDmgScale; public readonly float SmallGridDmgScale; public readonly float OffsetRatio; @@ -292,6 +295,7 @@ public enum Texture public readonly float ByBlockHitDepth; public readonly float DetonationSoundDistSqr; public readonly float BackKickForce; + public readonly float DragPerTick; public readonly double MinTurnSpeedSqr; public readonly double Aggressiveness; public readonly double NavAcceleration; @@ -392,6 +396,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon IsDrone = ammo.AmmoDef.Trajectory.Guidance == TrajectoryDef.GuidanceType.DroneAdvanced; TravelTo = ammo.AmmoDef.Trajectory.Guidance == TrajectoryDef.GuidanceType.TravelTo; IsTurretSelectable = !ammo.IsShrapnel && ammo.AmmoDef.HardPointUsable; + TerminalName = string.IsNullOrEmpty(ammo.AmmoDef.TerminalName) ? ammo.AmmoDef.AmmoRound : ammo.AmmoDef.TerminalName; ComputeSmarts(ammo, out IsSmart, out Roam, out NoTargetApproach, out AccelClearance, out OverrideTarget, out TargetOffSet, out FocusOnly, out FocusEviction, out NoSteering, out AdvancedSmartSteering, out KeepAliveAfterTargetLoss, out NoTargetExpire, out ZeroEffortNav, out ScanRange, out OffsetMinRangeSqr, @@ -410,6 +415,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon HitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); HitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name; EndOfLifeAv = !ammo.AmmoDef.AreaOfDamage.EndOfLife.NoVisuals && ammo.AmmoDef.AreaOfDamage.EndOfLife.Enable; + OverrideWeaponEffect = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.WeaponEffect1Override.Name); DrawLine = ammo.AmmoDef.AmmoGraphics.Lines.Tracer.Enable; @@ -444,6 +450,9 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon ArmorCoreActive = Session.I.ArmorCoreActive; AmmoSkipAccel = ammo.AmmoDef.Trajectory.AccelPerSec <= 0; + AmmoUseDrag = ammo.AmmoDef.Trajectory.DragPerSecond > 0; + DragPerTick = AmmoUseDrag ? ammo.AmmoDef.Trajectory.DragPerSecond / 60 : 0; + FeelsGravity = GravityMultiplier > 0; StoreGravity = FeelsGravity || fragHasGravity; SmartOffsetSqr = ammo.AmmoDef.Trajectory.Smarts.Inaccuracy * ammo.AmmoDef.Trajectory.Smarts.Inaccuracy; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs index 67d17648..58fa3e15 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs @@ -209,6 +209,8 @@ public class ArmorOverride [ProtoMember(20)] public bool UnsupportedMode; [ProtoMember(21)] public bool DisableSmallVsLargeBuff = false; [ProtoMember(22)] public Overrides DefinitionOverrides; + [ProtoMember(23)] public float LargeGridDamageMultiplier = 1; + [ProtoMember(24)] public float SmallGridDamageMultiplier = 1; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs index f0c0e8a3..efdc2bb6 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs @@ -284,6 +284,7 @@ public void Sync(Weapon w, ProtoWeaponReload sync, bool force) if (newReload) { w.AmmoName = w.System.AmmoTypes[AmmoTypeId].AmmoDef.AmmoRound; + w.AmmoNameTerminal = w.System.AmmoTypes[AmmoTypeId].AmmoDef.Const.TerminalName; w.DelayedCycleId = -1; } diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index 9d35f701..6b5bdfd0 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -734,7 +734,7 @@ internal static void AmmoSelectionWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready || comp.ConsumableSelectionPartIds.Count == 0) return; var w = comp.Collection[comp.ConsumableSelectionPartIds[0]]; - sb.Append(w.AmmoName); + sb.Append(w.AmmoNameTerminal); } internal static void RepelWriter(IMyTerminalBlock blk, StringBuilder sb) diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 3beccbd2..161f7bc1 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -223,8 +223,9 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str try { var comp = ((Weapon.WeaponComponent)this); + var collection = comp.HasAlternateUi ? SortAndGetTargetTypes() : TypeSpecific != CompTypeSpecific.Phantom ? Platform.Weapons : Platform.Phantoms; - if (comp.Data.Repo.Values.Set.Overrides.LeadGroup == 0 && !comp.IsBomb && (!comp.HasTurret && !comp.OverrideLeads || comp.HasTurret && comp.OverrideLeads)) + if (comp.Data.Repo.Values.Set.Overrides.LeadGroup == 0 && !comp.IsBomb && (!comp.HasTurret && !comp.OverrideLeads || comp.HasTurret && comp.OverrideLeads) && !collection[0].ActiveAmmoDef.AmmoDef.Const.IsSmart) stringBuilder.Append("\nWARNING: fixed weapon detected\n") .Append(" - without a Target Lead Group set!\n\n"); @@ -278,7 +279,6 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str if (!comp.HasAlternateUi) stringBuilder.Append($"\n\n{Localization.GetText("WeaponInfoDividerLineWeapon")}"); - var collection = comp.HasAlternateUi ? SortAndGetTargetTypes() : TypeSpecific != CompTypeSpecific.Phantom ? Platform.Weapons : Platform.Phantoms; for (int i = 0; i < collection.Count; i++) { var w = collection[i]; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs index 2bad497e..ef1a1a5b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs @@ -175,20 +175,35 @@ internal void OnAddedToSceneWeaponTasks(bool firstRun) { w.ChangeActiveAmmoClient(); w.AmmoName = w.ActiveAmmoDef.AmmoDef.AmmoRound; + w.AmmoNameTerminal = w.ActiveAmmoDef.AmmoDef.Const.TerminalName; } if (w.ActiveAmmoDef.AmmoDef == null || !w.ActiveAmmoDef.AmmoDef.Const.IsTurretSelectable && w.System.AmmoTypes.Length > 1) { - //additional logging formatting - string errorString; - if (w.ActiveAmmoDef.AmmoDef != null) + w.ProposedAmmoId = 0; + if (Session.I.IsServer) + w.ChangeActiveAmmoServer(); + else { - errorString = w.ActiveAmmoDef.AmmoDef.AmmoRound + " TurretSelectable:" + w.ActiveAmmoDef.AmmoDef.Const.IsTurretSelectable + " IsShrapnel:" + w.ActiveAmmoDef.IsShrapnel + " HardPointUsable:" + w.ActiveAmmoDef.AmmoDef.HardPointUsable; + w.ChangeActiveAmmoClient(); + w.AmmoName = w.ActiveAmmoDef.AmmoDef.AmmoRound; + w.AmmoNameTerminal = w.ActiveAmmoDef.AmmoDef.Const.TerminalName; } - else errorString = "ActiveAmmoDef was null"; + Log.Line($"Ammo type was invalid, forced {w.System.PartName} back to default"); - Platform.PlatformCrash(this, false, true, $"[{w.System.PartName}] heyyyyyy sweetie this gun is broken. Your first ammoType is broken ({errorString}), I am crashing now Dave."); - return; + if (w.ActiveAmmoDef.AmmoDef == null || !w.ActiveAmmoDef.AmmoDef.Const.IsTurretSelectable && w.System.AmmoTypes.Length > 1) + { + //additional logging formatting + string errorString; + if (w.ActiveAmmoDef.AmmoDef != null) + { + errorString = w.ActiveAmmoDef.AmmoDef.AmmoRound + " TurretSelectable:" + w.ActiveAmmoDef.AmmoDef.Const.IsTurretSelectable + " IsShrapnel:" + w.ActiveAmmoDef.IsShrapnel + " HardPointUsable:" + w.ActiveAmmoDef.AmmoDef.HardPointUsable; + } + else errorString = "ActiveAmmoDef was null"; + + Platform.PlatformCrash(this, false, true, $"[{w.System.PartName}] heyyyyyy sweetie this gun is broken. Your active ammoType is broken ({errorString}), I am crashing now Dave."); + return; + } } w.UpdateWeaponRange(); @@ -1241,9 +1256,9 @@ internal void UpdateAmmoList() { var ammo = ammos[j]; if (!ammo.AmmoDef.Const.IsTurretSelectable) continue; - var ammoStr = ammo.AmmoDef.AmmoRound; + var ammoStr = ammo.AmmoDef.Const.TerminalName; if (wep.DelayedCycleId != -1 && wep.AmmoName.EndsWith(ammo.AmmoDef.AmmoRound)) - ammoStr = wep.AmmoName; + ammoStr = "*" + ammo.AmmoDef.Const.TerminalName; BlockUi.AmmoList.Add(new MyTerminalControlComboBoxItem { Key = j, Value = MyStringId.GetOrCompute(ammoStr) }); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index cdf11609..78717f9f 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -125,6 +125,7 @@ public partial class Weapon : Part internal string FriendlyNameNoSubsystem = string.Empty; internal string AmmoName = ""; + internal string AmmoNameTerminal = ""; internal ProtoWeaponPartState PartState; internal ProtoWeaponReload Reload; internal ProtoWeaponTransferTarget TargetData; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs index e34c671a..ce08fa8b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs @@ -33,6 +33,7 @@ internal void ChangeActiveAmmoServer() Reload.CurrentMags = Comp.TypeSpecific != CompTypeSpecific.Phantom ? Comp.CoreInventory.GetItemAmount(ActiveAmmoDef.AmmoDefinitionId).ToIntSafe() : int.MaxValue; AmmoName = ActiveAmmoDef.AmmoDef.AmmoRound; + AmmoNameTerminal = ActiveAmmoDef.AmmoDef.Const.TerminalName; CheckInventorySystem = true; @@ -79,6 +80,7 @@ internal void QueueAmmoChange(int newAmmoId) { DelayedCycleId = newAmmoId; AmmoName = System.AmmoTypes[newAmmoId].AmmoNameQueued; + AmmoNameTerminal = "*" + System.AmmoTypes[newAmmoId].AmmoDef.Const.TerminalName; if (Session.I.IsClient && !System.DesignatorWeapon) ChangeAmmo(newAmmoId); @@ -184,6 +186,8 @@ internal bool ClientReload(bool networkCaller = false) return true; } + //TODO account for float rounding errors to int here and in later inv pulling + internal bool ComputeServerStorage(bool calledFromReload = false) { var s = Session.I; @@ -197,17 +201,15 @@ internal bool ComputeServerStorage(bool calledFromReload = false) { Comp.CurrentInventoryVolume = (float)Comp.CoreInventory.CurrentVolume; var freeVolume = System.MaxAmmoVolume - Comp.CurrentInventoryVolume; - var spotsFree = (int)(freeVolume / ActiveAmmoDef.AmmoDef.Const.MagVolume); + var spotsFree = (int)(freeVolume / ActiveAmmoDef.AmmoDef.Const.MagVolume + .0001f); Reload.CurrentMags = Comp.CoreInventory.GetItemAmount(ActiveAmmoDef.AmmoDefinitionId).ToIntSafe(); CurrentAmmoVolume = Reload.CurrentMags * ActiveAmmoDef.AmmoDef.Const.MagVolume; - var magsRequested = (int)((System.FullAmmoVolume - CurrentAmmoVolume) / ActiveAmmoDef.AmmoDef.Const.MagVolume); + var magsRequested = (int)((System.FullAmmoVolume - CurrentAmmoVolume) / ActiveAmmoDef.AmmoDef.Const.MagVolume + .0001f); var magsGranted = magsRequested > spotsFree ? spotsFree : magsRequested; var requestedVolume = ActiveAmmoDef.AmmoDef.Const.MagVolume * magsGranted; - var spaceAvailable = freeVolume > requestedVolume; - var lowThreshold = System.MaxAmmoVolume * 0.25f; - - var pullAmmo = magsGranted > 0 && CurrentAmmoVolume < lowThreshold && spaceAvailable; + var spaceAvailable = freeVolume >= requestedVolume; + var pullAmmo = magsGranted > 0 && CurrentAmmoVolume < System.LowAmmoVolume && spaceAvailable; var failSafeTimer = s.Tick - LastInventoryTick > 600; @@ -384,6 +386,7 @@ internal void Reloaded(object o = null) if (DelayedCycleId == ActiveAmmoDef.AmmoDef.Const.AmmoIdxPos) { AmmoName = ActiveAmmoDef.AmmoDef.AmmoRound; + AmmoNameTerminal = ActiveAmmoDef.AmmoDef.Const.TerminalName; DelayedCycleId = -1; } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs index ad507320..4903a7bf 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs @@ -465,9 +465,46 @@ private void SetSpeed(object o) var entity = (MyEntity)o; if (entity?.Physics != null && ActiveAmmoDef?.AmmoDef != null && !entity.MarkedForClose) { - + var ejectDef = ActiveAmmoDef.AmmoDef.Ejection; - entity.Physics.SetSpeeds(Ejector.CachedDir * (ejectDef.Speed), Vector3.Zero); + var speedMin = ejectDef.SpeedVariance.Start; + var speedMax = ejectDef.SpeedVariance.End; + var randSpeed = speedMin != 0f || speedMax != 0f; + var dirMin = ejectDef.DirectionVariance.Start; + var dirMax = ejectDef.DirectionVariance.End; + var randDir = dirMin != 0f || dirMax != 0f; + var rotMin = ejectDef.RotationVariance.Start; + var rotMax = ejectDef.RotationVariance.End; + var randRot = rotMin != 0f || rotMax != 0f; + var needsRand = randSpeed || randDir || randRot; + + var speedVariance = 0f; + var rotation = ejectDef.Rotation; + var direction = Ejector.CachedDir; + var speed = ejectDef.Speed; + + var parentSpeed = Vector3.Zero; + if(Comp.TypeSpecific == CoreComponent.CompTypeSpecific.Rifle && Comp.GetTopEntity().Physics != null) + parentSpeed = Comp.GetTopEntity().Physics.LinearVelocity; + else if (Comp.Cube?.Physics != null) + parentSpeed = Comp.Cube.Physics.LinearVelocity; + + if (needsRand) + { + var rand = new XorShiftRandomStruct((ulong)entity.EntityId); + if (randSpeed) + speedVariance = (float)rand.NextDouble() * (speedMax - speedMin) + speedMin; + if (randDir) + { + direction += new Vector3D(rand.NextDouble() * (dirMax - dirMin) + dirMin, rand.NextDouble() * (dirMax - dirMin) + dirMin, rand.NextDouble() * (dirMax - dirMin) + dirMin); + direction.Normalize(); + } + if (randRot) + { + rotation += new Vector3D(rand.NextDouble() * (rotMax - rotMin) + rotMin, rand.NextDouble() * (rotMax - rotMin) + rotMin, rand.NextDouble() * (rotMax - rotMin) + rotMin); + } + } + entity.Physics.SetSpeeds(parentSpeed + direction * (speed + speedVariance), rotation); } } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index 30b0bef2..1ff46d64 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -31,7 +31,7 @@ internal static bool CanShootTarget(Weapon weapon, ref Vector3D targetCenter, Ve if (Vector3D.IsZero(targetAccel, 5E-03)) targetAccel = Vector3.Zero; var validEstimate = true; - if (prediction != Prediction.Off && !weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult > 0) + if (prediction != Prediction.Off && !weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) targetPos = TrajectoryEstimation(weapon, targetCenter, targetLinVel, targetAccel, weapon.MyPivotPos, out validEstimate, true); else targetPos = targetCenter; @@ -99,7 +99,7 @@ internal static Vector3D LeadTargetAiBlock(Weapon weapon, MyEntity target) var vel = targParent.Physics.LinearVelocity; var accel = targParent.Physics.LinearAcceleration; bool validEstimate; - if (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult > 0) + if (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) targetPos = TrajectoryEstimation(weapon, targetPos, vel, accel, weapon.Comp.Cube.PositionComp.WorldAABB.Center, out validEstimate, false, weapon.Comp.Data.Repo.Values.Set.Overrides.AngularTracking); } return targetPos; @@ -121,8 +121,8 @@ internal static void LeadTarget(Weapon weapon, MyEntity target, out Vector3D tar var validEstimate = true; - if (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult > 0) - targetPos = TrajectoryEstimation(weapon, obb.Center, vel, accel, weapon.MyPivotPos, out validEstimate, false, weapon.Comp.Data.Repo.Values.Set.Overrides.AngularTracking); + if (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) + targetPos = TrajectoryEstimation(weapon, obb.Center, vel, accel, weapon.MyPivotPos, out validEstimate, false, weapon.Comp.Data.Repo.Values.Set.Overrides.AngularTracking); else targetPos = obb.Center; @@ -189,7 +189,7 @@ internal static bool CanShootTargetObb(Weapon weapon, MyEntity entity, Vector3D var tempObb = obb; var validEstimate = true; - if (prediction != Prediction.Off && !weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult > 0) + if (prediction != Prediction.Off && !weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) targetPos = TrajectoryEstimation(weapon, obb.Center, targetLinVel, targetAccel, weapon.MyPivotPos, out validEstimate, true); else targetPos = obb.Center; @@ -264,7 +264,7 @@ internal static bool TargetAligned(Weapon weapon, Target target, out Vector3D ta targetCenter = Vector3D.Zero; var validEstimate = true; - if (weapon.System.Prediction != Prediction.Off && (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult > 0)) + if (weapon.System.Prediction != Prediction.Off && (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0)) { if (fakeTargetInfo != null) @@ -340,7 +340,7 @@ internal static bool TrackingTarget(Weapon w, Target target, out bool targetLock targetCenter = Vector3D.Zero; var validEstimate = true; - if (w.System.Prediction != Prediction.Off && !w.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && w.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed * w.VelocityMult > 0) + if (w.System.Prediction != Prediction.Off && !w.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && w.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) { if (fakeTargetInfo != null) @@ -648,7 +648,7 @@ internal static Vector3D TrajectoryEstimationOld(Weapon weapon, Vector3D targetP shooterPos = MyUtils.IsZero(shooterPos) ? weapon.MyPivotPos : shooterPos; var shooterVel = (Vector3D)weapon.Comp.Ai.TopEntityVel; - var projectileMaxSpeed = ammoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult; + var projectileMaxSpeed = ammoDef.Const.DesiredProjectileSpeed; var projectileInitSpeed = ammoDef.Trajectory.AccelPerSec * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS; var projectileAccMag = ammoDef.Trajectory.AccelPerSec; var basic = weapon.System.Prediction != Prediction.Advanced && !overrideMode || overrideMode && !setAdvOverride; @@ -916,27 +916,22 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, { valid = false; Vector3D aimPoint; - - if (weapon == null || weapon.Comp == null || weapon.ActiveAmmoDef == null || weapon.ActiveAmmoDef.AmmoDef == null) - return Vector3D.Zero; - var comp = weapon.Comp; var ai = comp.Ai; var session = Session.I; - var ammoDef = weapon.ActiveAmmoDef.AmmoDef; - var shooterVel = ai != null ? (Vector3D)ai.TopEntityVel : Vector3D.Zero; - var projectileMaxSpeed = ammoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult; - var updateGravity = ammoDef.Const.FeelsGravity && ai != null && ai.InPlanetGravity; - var useSimple = basicPrediction || ammoDef.Const.AmmoSkipAccel || targetAcc.LengthSquared() < 2.5; //equal to approx 1.58 m/s - #region Must Have Updates - if (ai != null && comp.TopEntity != null && comp.TopEntity.PositionComp != null && ai.VelocityUpdateTick != session.Tick) + if (ai.VelocityUpdateTick != session.Tick) { ai.TopEntityVolume.Center = comp.TopEntity.PositionComp.WorldVolume.Center; - ai.TopEntityVel = comp.TopEntity.Physics?.LinearVelocity ?? Vector3D.Zero; + ai.TopEntityVel = comp.TopEntity.Physics?.LinearVelocity ?? Vector3.Zero; ai.IsStatic = comp.TopEntity.Physics?.IsStatic ?? false; ai.VelocityUpdateTick = session.Tick; } + var shooterVel = (Vector3D)weapon.Comp.Ai.TopEntityVel; + var ammoDef = weapon.ActiveAmmoDef.AmmoDef; + var projectileMaxSpeed = ammoDef.Const.DesiredProjectileSpeed; + var updateGravity = ammoDef.Const.FeelsGravity && ai.InPlanetGravity; + var useSimple = basicPrediction || ammoDef.Const.AmmoSkipAccel || targetAcc.LengthSquared() < 2.5; //equal to approx 1.58 m/s if (updateGravity && session.Tick - weapon.GravityTick > 119) { @@ -958,13 +953,13 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, Vector3D deltaPos = targetPos - shooterPos; if (Vector3D.IsZero(deltaPos)) return targetPos; - + Vector3D deltaPosNorm; var targCube = weapon.Target?.TargetObject as MyCubeBlock; - if (!basicPrediction && trackAngular && targCube?.CubeGrid?.Physics != null && targCube.CubeGrid.Physics.AngularVelocity.LengthSquared() > 0.0014) + if (!basicPrediction && trackAngular && targCube != null && targCube.CubeGrid.Physics != null && targCube.CubeGrid.Physics.AngularVelocity.LengthSquared() > 0.0014) { - if (!ComputeAngular(targCube.CubeGrid, ai, weapon, ammoDef, ref targetPos, ref shooterPos, ref targetAcc, ref deltaVel, projectileMaxSpeed, out deltaLength, out initialTti, out deltaPos, out deltaPosNorm)) + if (!ComputeAngular(targCube.CubeGrid, ai, ammoDef, ref targetPos, ref shooterPos, ref targetAcc, ref deltaVel, projectileMaxSpeed, out deltaLength, out initialTti, out deltaPos, out deltaPosNorm)) return targetPos; } else @@ -1004,7 +999,7 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, { var advTti = initialTti; var projAccelTime = ammoDef.Const.DesiredProjectileSpeed / ammoDef.Const.AccelInMetersPerSec; - var usedTti = QuarticSolver(ref advTti, deltaPos, deltaVel, targetAcc, ammoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult, ai?.QuadraticCoefficientsStorage) ? advTti : initialTti; + var usedTti = QuarticSolver(ref advTti, deltaPos, deltaVel, targetAcc, ammoDef.Const.DesiredProjectileSpeed, ai.QuadraticCoefficientsStorage) ? advTti : initialTti; aimPoint = targetPos + (usedTti + (ammoDef.Const.AmmoSkipAccel ? 0 : (projAccelTime / usedTti))) * (targetVel - shooterVel); } Vector3D gravityOffset = Vector3D.Zero; @@ -1049,12 +1044,12 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, var targetDirection = targetAimPoint - shooterPos; bool isTracking; - if (!weapon.RotorTurretTracking && weapon.TurretController != null && !WeaponLookAt(weapon, ref targetDirection, deltaLength * deltaLength, false, true, DebugCaller.TrajectoryEstimation, out isTracking)) //Angle 2 obscured, switch to angle 1 + if (!weapon.RotorTurretTracking && weapon.TurretController && !WeaponLookAt(weapon, ref targetDirection, deltaLength * deltaLength, false, true, DebugCaller.TrajectoryEstimation, out isTracking)) //Angle 2 obscured, switch to angle 1 { verticalDistance = Math.Tan(angle1) * horizontalDistance; gravityOffset = new Vector3D((verticalDistance + Math.Abs(elevationDifference)) * -weapon.GravityUnitDir); } - else if (weapon.RotorTurretTracking && weapon.Comp?.Ai?.ControlComp?.Platform?.Control != null && !RotorTurretLookAt(weapon.Comp.Ai.ControlComp.Platform.Control, ref targetDirection, deltaLength * deltaLength)) + else if (weapon.RotorTurretTracking && weapon.Comp.Ai.ControlComp != null && !RotorTurretLookAt(weapon.Comp.Ai.ControlComp.Platform.Control, ref targetDirection, deltaLength * deltaLength)) { verticalDistance = Math.Tan(angle1) * horizontalDistance; gravityOffset = new Vector3D((verticalDistance + Math.Abs(elevationDifference)) * -weapon.GravityUnitDir); @@ -1069,7 +1064,8 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, return aimPoint + gravityOffset; } - private static bool ComputeAngular(MyCubeGrid grid, Ai ai, Weapon weapon, WeaponDefinition.AmmoDef ammoDef, ref Vector3D targetPos, ref Vector3D shooterPos, ref Vector3D targetAcc, ref Vector3D deltaVel, double projectileMaxSpeed, out double deltaLength, out double initialTti, out Vector3D deltaPos, out Vector3D deltaPosNorm) + + private static bool ComputeAngular(MyCubeGrid grid, Ai ai, WeaponDefinition.AmmoDef ammoDef, ref Vector3D targetPos, ref Vector3D shooterPos, ref Vector3D targetAcc, ref Vector3D deltaVel, double projectileMaxSpeed, out double deltaLength, out double initialTti, out Vector3D deltaPos, out Vector3D deltaPosNorm) { // deltaPos computed twice, once before and once after Angular estimation. We just return usedTti as initialTti since it should always be superior. deltaPos = targetPos - shooterPos; @@ -1100,7 +1096,7 @@ private static bool ComputeAngular(MyCubeGrid grid, Ai ai, Weapon weapon, Weapon return false; var advTti = initialTti; - var usedTti = QuarticSolver(ref advTti, deltaPos, deltaVel, targetAcc, ammoDef.Const.DesiredProjectileSpeed * weapon.VelocityMult, ai.QuadraticCoefficientsStorage) ? advTti : initialTti; + var usedTti = QuarticSolver(ref advTti, deltaPos, deltaVel, targetAcc, ammoDef.Const.DesiredProjectileSpeed, ai.QuadraticCoefficientsStorage) ? advTti : initialTti; var targCom = grid.Physics.CenterOfMassWorld; var targAngVel = grid.Physics.AngularVelocity; //Radians per second var targAngVelLen = targAngVel.Normalize(); diff --git a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs index a8dca7ae..e7f50ece 100644 --- a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs +++ b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs @@ -199,7 +199,7 @@ private PlatformState WeaponParts() if (!Parts.NameToEntity.TryGetValue(muzzlePartName, out muzzlePartEntity)) { - return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Invalid muzzlePart, I am crashing now Dave."); + return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Weapon: {system.PartName} Invalid muzzlePart, I am crashing now Dave. {muzzlePartName} was not found. Ensure you do not include subpart_ in the Id fields in the weapon definition"); } foreach (var part in Parts.NameToEntity) @@ -210,11 +210,11 @@ private PlatformState WeaponParts() MyEntity azimuthPart; if (!Parts.NameToEntity.TryGetValue(azimuthPartName, out azimuthPart)) - return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Weapon: {system.PartName} Invalid azimuthPart, I am crashing now Dave."); + return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Weapon: {system.PartName} Invalid azimuthPart, I am crashing now Dave. {azimuthPartName} was not found. Ensure you do not include subpart_ in the Id fields in the weapon definition"); MyEntity elevationPart; if (!Parts.NameToEntity.TryGetValue(elevationPartName, out elevationPart)) - return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Invalid elevationPart, I am crashing now Dave."); + return PlatformCrash(Comp, true, true, $"Your block subTypeId ({Comp.SubtypeName}) Weapon: {system.PartName} Invalid elevationPart, I am crashing now Dave. {elevationPartName} was not found. Ensure you do not include subpart_ in the Id fields in the weapon definition"); MyEntity spinPart = null; if (system.HasBarrelRotation) @@ -773,7 +773,10 @@ internal PlatformState PlatformCrash(CoreComponent comp, bool markInvalid, bool if (Session.I.HandlesInput) { if (suppress) - MyAPIGateway.Utilities.ShowNotification($"CoreSystems hard crashed during block init, shutting down\n Send log files to server admin or submit a bug report to mod author:\n {comp.Platform?.Structure?.ModPath} - {comp.SubtypeName}", 10000); + MyAPIGateway.Utilities.ShowNotification($"CoreSystems hard crashed during block init, shutting down\n" + + $"Ensure you don't have duplicate weapons from different mods.\n" + + $"Send log files to server admin or submit a bug report to mod author.\n" + + $"Crashed mod ID and block name: {comp.Platform?.Structure?.ModId} - {comp.SubtypeName}", 10000); } Log.Line($"PlatformCrash: {Comp.SubtypeName} - {message}"); diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index 78917224..d31957e0 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -765,6 +765,12 @@ internal void RunSmart() // this is grossly inlined thanks to mod profiler... th MaxSpeed = DesiredSpeed; var speedCap = speedCapMulti * MaxSpeed; + if (aConst.AmmoUseDrag) + { + speedCap -= Info.Age * aConst.DragPerTick; + if (speedCap < 0) + speedCap = 0; + } if (VelocityLengthSqr > speedCap * speedCap) { VelocityLengthSqr = proposedVel.LengthSquared(); proposedVel = Direction * speedCap; @@ -3094,7 +3100,7 @@ internal bool TrajectoryEstimation(WeaponDefinition.AmmoDef ammoDef, Weapon weap var targetPos = eTarget != null ? eTarget.PositionComp.WorldAABB.Center : pTarget.Position; - if (aConst.FragPointType == PointTypes.Direct) + if (aConst.TimedFragments && aConst.FragPointType == PointTypes.Direct) { targetDirection = Vector3D.Normalize(targetPos - Position); estimatedPosition = targetPos; diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectiles.cs b/Data/Scripts/CoreSystems/Projectiles/Projectiles.cs index ce3f0918..192431ef 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectiles.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectiles.cs @@ -218,6 +218,14 @@ private void UpdateState(int end = 0) var accelThisTick = p.Direction * (aConst.DeltaVelocityPerTick * Session.I.DeltaTimeRatio); var maxSpeedSqr = p.MaxSpeed * p.MaxSpeed; + var curMaxSpeed = p.MaxSpeed; + if (aConst.AmmoUseDrag) + { + curMaxSpeed -= p.Info.Age * aConst.DragPerTick; + if (curMaxSpeed < 0) + curMaxSpeed = 0; + maxSpeedSqr = curMaxSpeed * curMaxSpeed; + } if (p.DeaccelRate > 0) { var distToMax = info.MaxTrajectory - info.DistanceTraveled; @@ -242,7 +250,7 @@ private void UpdateState(int end = 0) p.VelocityLengthSqr = newVel.LengthSquared(); if (p.VelocityLengthSqr > maxSpeedSqr) - newVel = p.Direction * p.MaxSpeed; + newVel = p.Direction * curMaxSpeed; else info.TotalAcceleration += (newVel - p.PrevVelocity); @@ -256,6 +264,14 @@ private void UpdateState(int end = 0) if (aConst.AmmoSkipAccel || p.VelocityLengthSqr > 0) p.LastPosition = p.Position; + if (aConst.AmmoSkipAccel && aConst.AmmoUseDrag) + { + var dragMaxSpeed = p.MaxSpeed - p.Info.Age * aConst.DragPerTick; + if (dragMaxSpeed < 0) + dragMaxSpeed = 0; + p.Velocity = p.Direction * dragMaxSpeed; + } + p.TravelMagnitude = info.Age != 0 ? p.Velocity * Session.I.DeltaStepConst : p.TravelMagnitude; p.Position += p.TravelMagnitude; diff --git a/Data/Scripts/CoreSystems/Session/SessionCleanUp.cs b/Data/Scripts/CoreSystems/Session/SessionCleanUp.cs index cfc2439f..097120da 100644 --- a/Data/Scripts/CoreSystems/Session/SessionCleanUp.cs +++ b/Data/Scripts/CoreSystems/Session/SessionCleanUp.cs @@ -306,7 +306,6 @@ internal void PurgeAll() UiInput = null; TargetUi = null; Placer = null; - TargetGps = null; SApi.Unload(); if (WaterApiLoaded) WApi.Unregister(); diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index dd70b809..a925944a 100644 --- a/Data/Scripts/CoreSystems/Session/SessionControls.cs +++ b/Data/Scripts/CoreSystems/Session/SessionControls.cs @@ -8,7 +8,6 @@ using Sandbox.ModAPI; using Sandbox.ModAPI.Interfaces.Terminal; using SpaceEngineers.Game.ModAPI; -using VRage.ModAPI; using VRage.Utils; using static CoreSystems.Support.CoreComponent.Trigger; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; @@ -19,6 +18,8 @@ public partial class Session #region UI Config public static void CreateDecoyTerminalUi(Session session) where T : IMyTerminalBlock { + if (session.PurgedAll) + return; session.MainThreadId = Environment.CurrentManagedThreadId; if (ControlsAlreadyExist(session)) return; @@ -28,6 +29,8 @@ public static void CreateDecoyTerminalUi(Session session) where T : IMyTermin public static void CreateCameraTerminalUi(Session session) where T : IMyTerminalBlock { + if (session.PurgedAll) + return; session.MainThreadId = Environment.CurrentManagedThreadId; if (ControlsAlreadyExist(session)) return; @@ -124,6 +127,8 @@ public static bool ControlsAlreadyExist(Session session) public static void CreateTerminalUi(Session session) where T : IMyTerminalBlock { + if (session.PurgedAll) + return; session.MainThreadId = Environment.CurrentManagedThreadId; if (ControlsAlreadyExist(session)) { @@ -164,6 +169,8 @@ public static void CreateTerminalUi(Session session) where T : IMyTerminalBlo public static void CombatBlockUi(Session session) where T : IMyTerminalBlock { + if (session.PurgedAll) + return; session.MainThreadId = Environment.CurrentManagedThreadId; if (ControlsAlreadyExist(session)) { @@ -357,27 +364,30 @@ internal static void AlterActions(Session session) { List actions; MyAPIGateway.TerminalControls.GetActions(out actions); - for (int i = 0; i < actions.Count; i++) { - + for (int i = 0; i < actions.Count; i++) + { var a = actions[i]; - if (a.Id.Equals(ShootOnceModeStr) || !a.Id.Contains(ShootModeStr) && !a.Id.Contains("OnOff") && !a.Id.Contains("WC_") && !a.Id.Contains("Control")) { a.Enabled = TerminalHelpers.NotWcBlock; // dont think this is needed and its really expensive session.AlteredActions.Add(a); } - else if (a.Id.Equals("Control")) { + else if (a.Id.Equals("Control")) + { a.Enabled = TerminalHelpers.NotWcOrIsTurret; session.AlteredActions.Add(a); } - else if (a.Id.Equals("Shoot")) { + else if (a.Id.Equals("Shoot")) + { var oldAction = a.Action; - a.Action = blk => { + a.Action = blk => + { var comp = blk?.Components?.Get() as Weapon.WeaponComponent; - if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) { + if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) + { if (comp?.Data?.Repo == null) oldAction(blk); return; @@ -388,10 +398,12 @@ internal static void AlterActions(Session session) }; var oldWriter = a.Writer; - a.Writer = (blk, sb) => { + a.Writer = (blk, sb) => + { var comp = blk?.Components?.Get() as Weapon.WeaponComponent; - if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) { + if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) + { oldWriter(blk, sb); return; } @@ -402,13 +414,16 @@ internal static void AlterActions(Session session) }; session.AlteredActions.Add(a); } - else if (a.Id.Equals("Shoot_On")) { + else if (a.Id.Equals("Shoot_On")) + { var oldAction = a.Action; - a.Action = blk => { + a.Action = blk => + { var comp = blk?.Components?.Get() as Weapon.WeaponComponent; - if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) { + if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) + { if (comp?.Data?.Repo == null) oldAction(blk); return; } @@ -432,14 +447,17 @@ internal static void AlterActions(Session session) }; session.AlteredActions.Add(a); } - else if (a.Id.Equals("Shoot_Off")) { + else if (a.Id.Equals("Shoot_Off")) + { var oldAction = a.Action; - a.Action = blk => { + a.Action = blk => + { var comp = blk?.Components?.Get() as Weapon.WeaponComponent; - if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) { - if (comp?.Data?.Repo == null) oldAction(blk); + if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) + { + if (comp?.Data?.Repo == null) oldAction(blk); return; } if (comp.Data.Repo.Values.State.Trigger != Off) @@ -447,10 +465,12 @@ internal static void AlterActions(Session session) }; var oldWriter = a.Writer; - a.Writer = (blk, sb) => { + a.Writer = (blk, sb) => + { var comp = blk?.Components?.Get() as Weapon.WeaponComponent; - if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) { + if (comp?.Data?.Repo == null || comp.Platform.State != CorePlatform.PlatformState.Ready) + { oldWriter(blk, sb); return; } diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index a50055e3..0aa10bf9 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -436,6 +436,11 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) smallVsLargeBuff = 0.25f; } } + var gridSizeBuff = 1f; + if (grid.GridSizeEnum == MyCubeSize.Large) + gridSizeBuff = Settings.Enforcement.LargeGridDamageMultiplier; + else + gridSizeBuff = Settings.Enforcement.SmallGridDamageMultiplier; for (int i = 0; i < blockCount; i++) { @@ -675,9 +680,9 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) var rootStep = k == 0 && j == 0 && !detActive; var primaryDamage = rootStep && block == rootBlock && !detActive;//limits application to first run w/AOE, suppresses with detonation - var baseScale = damageScale * directDamageScale * smallVsLargeBuff; + var baseScale = damageScale * directDamageScale * smallVsLargeBuff * gridSizeBuff; var scaledDamage = (float)(basePool * baseScale); - var aoeScaledDmg = (float)((aoeDamageFall * (detActive ? detDamageScale : areaDamageScale)) * damageScale); + var aoeScaledDmg = (float)((aoeDamageFall * (detActive ? detDamageScale : areaDamageScale)) * damageScale * gridSizeBuff); bool deadBlock = false; //Check for end of primary life diff --git a/Data/Scripts/CoreSystems/Session/SessionFields.cs b/Data/Scripts/CoreSystems/Session/SessionFields.cs index b1cc0649..f0e955c5 100644 --- a/Data/Scripts/CoreSystems/Session/SessionFields.cs +++ b/Data/Scripts/CoreSystems/Session/SessionFields.cs @@ -46,7 +46,7 @@ public partial class Session internal const int VersionControl = 34; internal const int AwakeBuckets = 60; internal const int AsleepBuckets = 180; - internal const int ModVersion = 28; + internal const int ModVersion = 29; internal const int ClientCfgVersion = 9; internal const string ServerCfgName = "CoreSystemsServer.cfg"; internal const string ClientCfgName = "CoreSystemsClient.cfg"; @@ -319,7 +319,6 @@ public partial class Session internal ControlQuery ControlRequest; internal IMyPhysics Physics; internal IMyCamera Camera; - internal IMyGps TargetGps; internal IMyBlockPlacerBase Placer; internal IMyTerminalBlock LastTerminal; internal IMyCharacter LocalCharacter; @@ -413,6 +412,7 @@ public partial class Session internal ulong MuzzleIdCounter; internal ulong PhantomIdCounter; + internal long PreFetchMaxDist; internal long PlayerId; internal int ClientAvDivisor = 1; internal double SyncDistSqr; diff --git a/Data/Scripts/CoreSystems/Session/SessionInit.cs b/Data/Scripts/CoreSystems/Session/SessionInit.cs index adb6a86d..4f59de66 100644 --- a/Data/Scripts/CoreSystems/Session/SessionInit.cs +++ b/Data/Scripts/CoreSystems/Session/SessionInit.cs @@ -65,9 +65,10 @@ private void BeforeStartInit() SyncBufferedDistSqr = (SyncDist + 500) * (SyncDist + 500); } + PreFetchMaxDist = MyAPIGateway.Session.SessionSettings.PrefetchShapeRayLengthLimit - 1; + Physics = MyAPIGateway.Physics; Camera = MyAPIGateway.Session.Camera; - TargetGps = MyAPIGateway.Session.GPS.Create("WEAPONCORE", "", Vector3D.MaxValue, true); CheckDirtyGridInfos(true); GenerateButtonMap(); diff --git a/Data/Scripts/CoreSystems/Session/SessionInventory.cs b/Data/Scripts/CoreSystems/Session/SessionInventory.cs index d360fd7c..394dd73d 100644 --- a/Data/Scripts/CoreSystems/Session/SessionInventory.cs +++ b/Data/Scripts/CoreSystems/Session/SessionInventory.cs @@ -106,7 +106,7 @@ internal void ConsumablePull() // In Thread var defId = part.ActiveAmmoDef.AmmoDefinitionId; var freeSpace = part.System.MaxAmmoVolume - part.BaseComp.CurrentInventoryVolume; var spotsFree = (int)(freeSpace / part.ActiveAmmoDef.AmmoDef.Const.MagVolume); - var magsNeeded = (int)((part.System.FullAmmoVolume - part.CurrentAmmoVolume) / part.ActiveAmmoDef.AmmoDef.Const.MagVolume); + var magsNeeded = (int)((part.System.FullAmmoVolume - part.CurrentAmmoVolume) / part.ActiveAmmoDef.AmmoDef.Const.MagVolume + 0.0001f); magsNeeded = magsNeeded > spotsFree ? spotsFree : magsNeeded; var consumablePullRequests = InventoryMoveRequestPool.Get(); diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index ca9909d2..6dcd839b 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -1194,7 +1194,7 @@ private void ModChecker() ShieldMod = true; else if (mod.PublishedFileId == 1931509062 || mod.PublishedFileId == 1995197719 || mod.PublishedFileId == 2006751214 || mod.PublishedFileId == 2015560129) ReplaceVanilla = true; - else if (mod.GetPath().Contains("AppData\\Roaming\\SpaceEngineers\\Mods\\VanillaReplacement") || mod.Name.StartsWith("WCVanilla") || mod.FriendlyName.StartsWith("WCVanilla")) + else if (mod.GetPath().Contains("AppData\\Roaming\\SpaceEngineers\\Mods\\VanillaReplacement") || mod.Name.Contains("WCVanilla") || mod.FriendlyName.Contains("WCVanilla")) ReplaceVanilla = true; else if (mod.PublishedFileId == 2189703321 || mod.PublishedFileId == 2496225055 || mod.PublishedFileId == 2726343161 || mod.PublishedFileId == 2734980390) { diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index e35f20ef..831d0092 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -15,6 +15,7 @@ using SpaceEngineers.Game.ModAPI; using VRage.Game.Entity; using VRage.Game; +using VRage.Game.ModAPI; namespace CoreSystems { @@ -627,6 +628,7 @@ private void AiLoop() var weaponAcquires = ai.AcquireTargets && (aConst.RequiresTarget || w.RotorTurretTracking || w.ShootRequest.AcquireTarget); var eTarget = w.Target.TargetObject as MyEntity; var pTarget = w.Target.TargetObject as Projectile; + var cTarget = w.Target.TargetObject as IMyCharacter; if (!IsClient) { if (w.Target.HasTarget) @@ -639,7 +641,7 @@ private void AiLoop() { w.Target.Reset(Tick, States.Expired, !wComp.ManualMode); } - else if (eTarget != null && (eTarget.MarkedForClose || !rootConstruct.HadFocus && weaponAcquires && aConst.SkipAimChecks && !w.RotorTurretTracking || wComp.UserControlled && !w.System.SuppressFire)) + else if (eTarget != null && (eTarget.MarkedForClose || (cTarget!= null && (cTarget.IsDead || cTarget.Integrity <= 0)) || !rootConstruct.HadFocus && weaponAcquires && aConst.SkipAimChecks && !w.RotorTurretTracking || wComp.UserControlled && !w.System.SuppressFire)) { w.Target.Reset(Tick, States.Expired); } diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 31d5c05c..c90a3be2 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -61,7 +61,7 @@ public static class Localization { "TerminalGridsTitle", "Target Grids" }, { "TerminalGridsTooltip", "Target Grids" }, { "TerminalFocusFireTitle", "Target FocusFire" }, - { "TerminalFocusFireTooltip", "Focus all fire on the specified target" }, + { "TerminalFocusFireTooltip", "Only fire on the hud-selected target" }, { "TerminalSubSystemsTitle", "Target SubSystems" }, { "TerminalSubSystemsTooltip", "Target specific SubSystems of a target" }, { "TerminalRepelTitle", "Repel Mode" }, diff --git a/Data/Scripts/CoreSystems/Support/VersionControl.cs b/Data/Scripts/CoreSystems/Support/VersionControl.cs index 4061f8bb..cec10a7c 100644 --- a/Data/Scripts/CoreSystems/Support/VersionControl.cs +++ b/Data/Scripts/CoreSystems/Support/VersionControl.cs @@ -191,6 +191,8 @@ private void RebuildConfig(CoreSettings.ServerSettings oldSettings) Core.Enforcement.AdvancedProjectileSync = oldPointDefenseSyncMonitor; Core.Enforcement.UnsupportedMode = oldUnsupportedMode; Core.Enforcement.DisableSmallVsLargeBuff = oldDisableSmallVsLargeBuff; + Core.Enforcement.LargeGridDamageMultiplier = 1f; + Core.Enforcement.SmallGridDamageMultiplier = 1f; } private void CorruptionCheck(bool write = false) @@ -204,6 +206,12 @@ private void CorruptionCheck(bool write = false) if (Core.Enforcement.ShieldDamageModifer < 0) Core.Enforcement.ShieldDamageModifer = 1f; + if (Core.Enforcement.LargeGridDamageMultiplier < 0) + Core.Enforcement.LargeGridDamageMultiplier = 1f; + + if (Core.Enforcement.SmallGridDamageMultiplier < 0) + Core.Enforcement.SmallGridDamageMultiplier = 1f; + if (Core.Enforcement.ShipSizes == null) { Core.Enforcement.ShipSizes = Array.Empty(); diff --git a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs index 9eed0e61..b5ea4d07 100644 --- a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs +++ b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs @@ -147,7 +147,7 @@ internal bool SelectTarget(bool manualSelect = true, bool firstStage = false, bo MyEntity rootEntity = null; if (ai.MyPlanet != null && Session.I.Tick90 && s.UiInput.AltPressed) { - var rayLine = new LineD(AimPosition, ai.MaxTargetingRange > 14999 ? AimPosition + AimDirection * 14999 : end); //Prefetch will return nothing if dist >= 15000 + var rayLine = new LineD(AimPosition, ai.MaxTargetingRange > s.PreFetchMaxDist ? AimPosition + AimDirection * s.PreFetchMaxDist : end); ai.MyPlanet.PrefetchShapeOnRay(ref rayLine); } Session.I.Physics.CastRay(AimPosition, end, _hitInfo);