From 46705f5c11efd583fa6870d77c3d4a629c91e46b Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:32:56 -0600 Subject: [PATCH 01/77] Targeting optimization --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 79 +++++++++++++--------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index 7a7a52ae..cdd487ab 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -1145,50 +1145,53 @@ private static bool FindRandomBlock(Weapon w, Target target, TargetInfo info, Co var lowFiVoxels = distSqr > oneHalfKmSqr && (ai.PlanetSurfaceInRange || ai.ClosestVoxelSqr <= oneHalfKmSqr); var filter = lowFiVoxels ? CollisionLayers.DefaultCollisionLayer : CollisionLayers.VoxelLod1CollisionLayer; - physics.CastRay(testPos, blockPos, hitTmpList, filter); - for (int j = 0; j < hitTmpList.Count; j++) + IHitInfo iHitInfo; + MyCubeGrid rayGrid = null; + if (ai.AiType == AiTypes.Grid && physics.CastRay(testPos, blockPos, out iHitInfo, CollisionLayers.NoVoxelCollisionLayer)) + { + rayGrid = iHitInfo.HitEntity?.GetTopMostParent() as MyCubeGrid; + if (rayGrid != null && rayGrid.IsSameConstructAs(ai.GridEntity)) + continue; + } + if (rayGrid != null && block.CubeGrid == rayGrid) + { + acquire = true; + } + else { - var hitInfo = hitTmpList[j]; + physics.CastRay(testPos, blockPos, hitTmpList, filter); + for (int j = 0; j < hitTmpList.Count; j++) + { + var hitInfo = hitTmpList[j]; - var entity = hitInfo.HitEntity as MyEntity; - var hitGrid = entity as MyCubeGrid; - var voxel = entity as MyVoxelBase; - var character = entity as IMyCharacter; - var dist = hitInfo.Fraction * targetDist; + var entity = hitInfo.HitEntity as MyEntity; + var hitGrid = entity as MyCubeGrid; + var voxel = entity as MyVoxelBase; + var character = entity as IMyCharacter; + var dist = hitInfo.Fraction * targetDist; - if (character == null && hitGrid == null && voxel == null || dist >= closest || hitGrid != null && (hitGrid.MarkedForClose || hitGrid.Physics == null || hitGrid.IsPreview)) - continue; + if (character == null && hitGrid == null && voxel == null || dist >= closest || hitGrid != null && (hitGrid.MarkedForClose || hitGrid.Physics == null || hitGrid.IsPreview)) + continue; - TargetInfo otherInfo; - var knownTarget = ai.Targets.TryGetValue(entity, out otherInfo) && (otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies || otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.Neutral || otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.NoOwnership); + TargetInfo otherInfo; + var knownTarget = ai.Targets.TryGetValue(entity, out otherInfo) && (otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies || otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.Neutral || otherInfo.EntInfo.Relationship == MyRelationsBetweenPlayerAndBlock.NoOwnership); - var enemyCharacter = character != null && knownTarget; + var enemyCharacter = character != null && knownTarget; - if (character != null && !enemyCharacter) - { - if (dist < closest) + if (character != null && !enemyCharacter && dist < closest) { closest = dist; acquire = false; } - } - if (voxel != null) - { - if (dist < closest) + else if (voxel != null && dist < closest) { closest = dist; acquire = false; } - } - else if (hitGrid != null) - { - var bigOwners = hitGrid.BigOwners; - var noOwner = bigOwners.Count == 0; - var validTarget = noOwner || knownTarget; - - if (dist < closest) + else if (hitGrid != null && dist < closest) { + var validTarget = hitGrid.BigOwners.Count == 0 || knownTarget; closest = dist; acquire = validTarget; } @@ -1198,17 +1201,29 @@ private static bool FindRandomBlock(Weapon w, Target target, TargetInfo info, Co else { IHitInfo iHitInfo; + MyCubeGrid rayGrid = null; if (ai.AiType == AiTypes.Grid && physics.CastRay(testPos, testPos + (targetDirNorm * (ai.TopEntityVolume.Radius * 2)), out iHitInfo, CollisionLayers.NoVoxelCollisionLayer)) { - var rayGrid = iHitInfo.HitEntity?.GetTopMostParent() as MyCubeGrid; + rayGrid = iHitInfo.HitEntity?.GetTopMostParent() as MyCubeGrid; if (rayGrid != null && rayGrid.IsSameConstructAs(ai.GridEntity)) continue; } - var checkLine = new LineD(testPos, testPos + (targetDirNorm * w.MaxTargetDistance), w.MaxTargetDistance); + s.OverlapResultTmp.Clear(); - var queryType = ai.StaticEntityInRange ? MyEntityQueryType.Both : MyEntityQueryType.Dynamic; - MyGamePruningStructure.GetTopmostEntitiesOverlappingRay(ref checkLine, s.OverlapResultTmp, queryType); + var checkLine = new LineD(testPos, testPos + (targetDirNorm * targetDist), targetDist); + + if (rayGrid != null && block.CubeGrid == rayGrid) + { + var hit = new MyLineSegmentOverlapResult() {Element = rayGrid}; + s.OverlapResultTmp.Add(hit); + } + else + { + var queryType = ai.StaticEntityInRange ? MyEntityQueryType.Both : MyEntityQueryType.Dynamic; + MyGamePruningStructure.GetTopmostEntitiesOverlappingRay(ref checkLine, s.OverlapResultTmp, queryType); + } + for (int j = 0; j < s.OverlapResultTmp.Count; j++) { var entity = s.OverlapResultTmp[j].Element; From dd6c0f07e0291ae8452a0ff2d9ae7541d35b7e7f Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:14:07 -0600 Subject: [PATCH 02/77] Remove angular tracking button from fixed weps, limit logs to current and previous session --- .../EntityComp/Controls/TerminalHelpers.cs | 2 +- Data/Scripts/CoreSystems/Support/Log.cs | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 1b5e294a..25c53008 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -209,7 +209,7 @@ internal static void CreateGenericControls(Session session) where T : IMyTerm AddOnOffSwitchNoAction(session, "Shoot", Localization.GetText("TerminalShootTitle"), Localization.GetText("TerminalShootTooltip"), BlockUi.GetShoot, BlockUi.RequestSetShoot, true, IsNotBomb); AddOnOffSwitchNoAction(session, "Override", Localization.GetText("TerminalOverrideTitle"), Localization.GetText("TerminalOverrideTooltip"), BlockUi.GetOverride, BlockUi.RequestOverride, true, OverrideTarget); - AddOnOffSwitchNoAction(session, "AngularTracking", Localization.GetText("TerminalAngularTitle"), Localization.GetText("TerminalAngularTooltip"), BlockUi.GetAngularTracking, BlockUi.RequestAngularTracking, true, IsNotBomb); + AddOnOffSwitchNoAction(session, "AngularTracking", Localization.GetText("TerminalAngularTitle"), Localization.GetText("TerminalAngularTooltip"), BlockUi.GetAngularTracking, BlockUi.RequestAngularTracking, true, HasTracking); } internal static void CreateGenericArmor(Session session) where T : IMyTerminalBlock diff --git a/Data/Scripts/CoreSystems/Support/Log.cs b/Data/Scripts/CoreSystems/Support/Log.cs index bc786c77..ef54d460 100644 --- a/Data/Scripts/CoreSystems/Support/Log.cs +++ b/Data/Scripts/CoreSystems/Support/Log.cs @@ -101,7 +101,7 @@ public static void Init(string name, Session session, bool defaultInstance = tru { var filename = name + ".log"; if (_instances.ContainsKey(name)) return; - RenameFileInLocalStorage(filename, name + $"-{DateTime.Now:MM-dd-yy_HH-mm-ss}.log", typeof(LogInstance)); + RenameFileInLocalStorageLimited(filename, name + $"_1.log", typeof(LogInstance)); if (defaultInstance) _defaultInstance = name; var instance = _logPool.Get(); @@ -138,6 +138,30 @@ public static void Init(string name, Session session, bool defaultInstance = tru } } + public static void RenameFileInLocalStorageLimited(string oldName, string newName, Type anyObjectInYourMod) + { + if (!MyAPIGateway.Utilities.FileExistsInLocalStorage(oldName, anyObjectInYourMod)) + return; + + if (MyAPIGateway.Utilities.FileExistsInLocalStorage(newName, anyObjectInYourMod)) + MyAPIGateway.Utilities.DeleteFileInLocalStorage(newName, anyObjectInYourMod); + + using (var read = MyAPIGateway.Utilities.ReadFileInLocalStorage(oldName, anyObjectInYourMod)) + { + var sb = new StringBuilder(newName); + SUtils.ReplaceAll(sb, Path.GetInvalidFileNameChars(), '_'); + + using (var write = MyAPIGateway.Utilities.WriteFileInLocalStorage(sb.ToString(), anyObjectInYourMod)) + { + write.Write(read.ReadToEnd()); + write.Flush(); + write.Dispose(); + } + } + + MyAPIGateway.Utilities.DeleteFileInLocalStorage(oldName, anyObjectInYourMod); + } + public static void RenameFileInLocalStorage(string oldName, string newName, Type anyObjectInYourMod) { if (!MyAPIGateway.Utilities.FileExistsInLocalStorage(oldName, anyObjectInYourMod)) From 28fa16221b75a36cbe825cec6c7cb754d1384386 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:54:44 -0600 Subject: [PATCH 03/77] Ammo/Wep cfg minimization, aconst note update --- .../Coreparts/Definitions/AmmoTypes.cs | 2938 +---------------- .../Coreparts/Definitions/Artillery.cs | 178 - .../Coreparts/Definitions/Assault Cannons.cs | 261 -- .../Coreparts/Definitions/Autocannons.cs | 156 - .../Coreparts/Definitions/Gatlings.cs | 255 -- .../Coreparts/Definitions/InteriorTurret.cs | 78 - .../Coreparts/Definitions/Missiles.cs | 506 --- .../Coreparts/Definitions/Railguns.cs | 211 +- .../Coreparts/Definitions/SearchLight.cs | 132 - .../SerializedConfigs/AmmoConstants.cs | 2 +- 10 files changed, 132 insertions(+), 4585 deletions(-) diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs index 6591afe3..34757ce7 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs @@ -36,6 +36,7 @@ partial class Parts AmmoRound = "SpotLight", BaseDamage = 0, NoGridOrArmorScaling = true, + EnergyMagazineSize = 1, Trajectory = new TrajectoryDef { MaxLifeTime = 3600, @@ -47,82 +48,15 @@ partial class Parts { AmmoMagazine = "NATO_25x184mm", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "25mm NATO", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 90f, // Direct damage; one steel plate is worth 100. Mass = 1f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 200f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. - HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, + HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. DamageScales = new DamageScaleDef { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. HealthHitModifier = 1, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. Characters = 0.33f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 1000f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, Grids = new GridSizeDef { Large = -1f, // Multiplier for damage against large grids. @@ -153,164 +87,15 @@ partial class Parts DeformType = HitBlock, DeformDelay = 30, }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. }, Trajectory = new TrajectoryDef { Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 400, // voxel phasing if you go above 5100 MaxTrajectory = 800f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. SpeedVariance = Random(start: -5, end: 5), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, }, AmmoGraphics = new GraphicDef { @@ -336,15 +121,6 @@ partial class Parts }, Particles = new AmmoParticleDef { - Ammo = new ParticleDef - { - Name = "", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, Hit = new ParticleDef { Name = "MaterialHit_Metal_GatlingGun", @@ -356,17 +132,6 @@ partial class Parts HitPlayChance = 0.5f, }, }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, }, Lines = new LineDef { @@ -384,36 +149,6 @@ partial class Parts "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, }, OffsetEffect = new OffsetEffectDef { @@ -425,110 +160,27 @@ partial class Parts }, AmmoAudio = new AmmoAudioDef { - TravelSound = "", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 0.5f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef InteriorAmmo => new AmmoDef // Your ID, for slotting into the Weapon CS { AmmoMagazine = "RapidFireAutomaticRifleGun_Mag_50rd", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "MR-50A", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 30f, // Direct damage; one steel plate is worth 100. Mass = 0.1f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 1f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, DamageScales = new DamageScaleDef { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. HealthHitModifier = 0.5, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. Characters = -1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 0f, // Distance at which damage begins falling off. - MinMultipler = 1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, Grids = new GridSizeDef { Large = -1f, // Multiplier for damage against large grids. @@ -559,168 +211,15 @@ partial class Parts DeformType = HitBlock, DeformDelay = 30, }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. }, Trajectory = new TrajectoryDef { - Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 100, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 600, // voxel phasing if you go above 5100 MaxTrajectory = 400f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, }, AmmoGraphics = new GraphicDef { - ModelName = "", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" VisualProbability = 1f, // % ShieldHitDraw = false, Decals = new DecalDef @@ -742,15 +241,6 @@ partial class Parts }, Particles = new AmmoParticleDef { - Ammo = new ParticleDef - { - Name = "", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, Hit = new ParticleDef { Name = "MaterialHit_Metal", @@ -762,17 +252,6 @@ partial class Parts HitPlayChance = 0.5f, }, }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, }, Lines = new LineDef { @@ -790,36 +269,6 @@ partial class Parts "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, }, OffsetEffect = new OffsetEffectDef { @@ -831,96 +280,21 @@ partial class Parts }, AmmoAudio = new AmmoAudioDef { - TravelSound = "", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 0.5f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef InteriorAmmoOld => new AmmoDef // Your ID, for slotting into the Weapon CS { AmmoMagazine = "NATO_5p56x45mm", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "5.56x45mm", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 30f, // Direct damage; one steel plate is worth 100. Mass = 0.1f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 1f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, DamageScales = new DamageScaleDef { MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. @@ -929,12 +303,6 @@ partial class Parts HealthHitModifier = 0.5, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. Characters = -1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 0f, // Distance at which damage begins falling off. - MinMultipler = 1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, Grids = new GridSizeDef { Large = -1f, // Multiplier for damage against large grids. @@ -965,181 +333,27 @@ partial class Parts DeformType = HitBlock, DeformDelay = 30, }, - Custom = new CustomScalesDef + }, + Trajectory = new TrajectoryDef + { + MaxLifeTime = 100, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. + DesiredSpeed = 600, // voxel phasing if you go above 5100 + MaxTrajectory = 400f, // Max Distance the projectile or beam can Travel. + }, + AmmoGraphics = new GraphicDef + { + VisualProbability = 1f, // % + Decals = new DecalDef { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. + MaxAge = 3600, + Map = new[] { - new CustomBlocksDef + new TextureMapDef { - SubTypeId = "Test1", - Modifier = -1f, + HitMaterial = "Metal", + DecalMaterial = "GunBullet", }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, - Trajectory = new TrajectoryDef - { - Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - MaxLifeTime = 100, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. - DesiredSpeed = 600, // voxel phasing if you go above 5100 - MaxTrajectory = 400f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0.5f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, - }, - AmmoGraphics = new GraphicDef - { - ModelName = "", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" - VisualProbability = 1f, // % - ShieldHitDraw = false, - Decals = new DecalDef - { - MaxAge = 3600, - Map = new[] - { - new TextureMapDef - { - HitMaterial = "Metal", - DecalMaterial = "GunBullet", - }, - new TextureMapDef + new TextureMapDef { HitMaterial = "Glass", DecalMaterial = "GunBullet", @@ -1148,15 +362,6 @@ partial class Parts }, Particles = new AmmoParticleDef { - Ammo = new ParticleDef - { - Name = "", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, Hit = new ParticleDef { Name = "MaterialHit_Metal", @@ -1168,17 +373,6 @@ partial class Parts HitPlayChance = 0.5f, }, }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, }, Lines = new LineDef { @@ -1196,36 +390,6 @@ partial class Parts "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, }, OffsetEffect = new OffsetEffectDef { @@ -1237,28 +401,11 @@ partial class Parts }, AmmoAudio = new AmmoAudioDef { - TravelSound = "", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 0.5f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef MissileAmmo => new AmmoDef // Your ID, for slotting into the Weapon CS @@ -1266,67 +413,15 @@ partial class Parts AmmoMagazine = "Missile200mm", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "200mm Missile", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 1f, // Direct damage; one steel plate is worth 100. Mass = 45f, // In kilograms; how much force the impact will apply to the target. Health = 2, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. - BackKickForce = 0f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. { Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. Diameter = 1, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, DamageScales = new DamageScaleDef { MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. @@ -1391,22 +486,6 @@ partial class Parts }, AreaOfDamage = new AreaOfDamageDef { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, EndOfLife = new EndOfLifeDef { Enable = true, @@ -1415,143 +494,25 @@ partial class Parts Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. Falloff = Linear, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. MinArmingTime = 1, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. }, }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, Trajectory = new TrajectoryDef { Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed TargetLossDegree = 15, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 1200, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. AccelPerSec = 600, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 200, // voxel phasing if you go above 5100 MaxTrajectory = 800f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 2.5f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1, // controls how responsive tracking is. - MaxLateralThrust = 0.2f, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = false, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 1, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.0025f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 120, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, }, AmmoGraphics = new GraphicDef { - ModelName = "Models\\Weapons\\Projectile_Missile.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" + ModelName = "Models\\Weapons\\Projectile_Missile.mwm", // Model Path goes here. VisualProbability = 1f, // % ShieldHitDraw = true, - Decals = new DecalDef - { - MaxAge = 3600, - Map = new[] - { - new TextureMapDef - { - HitMaterial = "Metal", - DecalMaterial = "Missile", - }, - new TextureMapDef - { - HitMaterial = "Glass", - DecalMaterial = "Missile", - }, - }, - }, Particles = new AmmoParticleDef { Ammo = new ParticleDef @@ -1563,176 +524,22 @@ partial class Parts Scale = 1, }, }, - Hit = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - }, - Lines = new LineDef - { - ColorVariance = Random(start: 0.8f, end: 1.2f), // multiply the color by random values within range. - WidthVariance = Random(start: 0f, end: 0f), // adds random value to default width (negatives shrinks width) - Tracer = new TracerBaseDef - { - Enable = false, - Length = 5f, // - Width = 0.1f, // - Color = Color(red: 3, green: 2, blue: 1f, alpha: 1), // RBG 255 is Neon Glowing, 100 is Quite Bright. - VisualFadeStart = 0, // Number of ticks the weapon has been firing before projectiles begin to fade their color - VisualFadeEnd = 0, // How many ticks after fade began before it will be invisible. - Textures = new[] {// WeaponLaser, ProjectileTrailLine, WarpBubble, etc.. - "WeaponLaser", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, - }, - OffsetEffect = new OffsetEffectDef - { - MaxOffset = 0,// 0 offset value disables this effect - MinLength = 0.2f, - MaxLength = 3, - }, }, }, AmmoAudio = new AmmoAudioDef { TravelSound = "MissileFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight - HitSound = "", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", - VoxelHitSound = "", - FloatingHitSound = "", - HitPlayChance = 0.5f, - HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef AutocannonShell => new AmmoDef // Your ID, for slotting into the Weapon CS { AmmoMagazine = "AutocannonClip", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "Autocannon Shell", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 500f, // Direct damage; one steel plate is worth 100. Mass = 3f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 250f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, DamageScales = new DamageScaleDef { MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. @@ -1741,12 +548,6 @@ partial class Parts HealthHitModifier = 2, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. Characters = 0.2f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 1500f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, Grids = new GridSizeDef { Large = -1f, // Multiplier for damage against large grids. @@ -1795,152 +596,18 @@ partial class Parts }, }, }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, Trajectory = new TrajectoryDef { Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 420, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 400, // voxel phasing if you go above 5100 MaxTrajectory = 800f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. SpeedVariance = Random(start: -5, end: 5), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, - }, + }, AmmoGraphics = new GraphicDef { - ModelName = "", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" VisualProbability = 1f, // % - ShieldHitDraw = false, Decals = new DecalDef { MaxAge = 3600, @@ -1960,19 +627,10 @@ partial class Parts }, Particles = new AmmoParticleDef { - Ammo = new ParticleDef + Hit = new ParticleDef { - Name = "", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, - Hit = new ParticleDef - { - Name = "MaterialHit_Metal_GatlingGun", - ApplyToShield = true, + Name = "MaterialHit_Metal_GatlingGun", + ApplyToShield = true, Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { @@ -1980,17 +638,6 @@ partial class Parts HitPlayChance = 0.5f, }, }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, }, Lines = new LineDef { @@ -2008,36 +655,6 @@ partial class Parts "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, }, OffsetEffect = new OffsetEffectDef { @@ -2049,304 +666,35 @@ partial class Parts }, AmmoAudio = new AmmoAudioDef { - TravelSound = "", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 1f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef AssaultCannonShell => new AmmoDef // Your ID, for slotting into the Weapon CS { AmmoMagazine = "MediumCalibreAmmo", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. AmmoRound = "Assault Cannon Shell", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 4000f, // Direct damage; one steel plate is worth 100. Mass = 300f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 10000f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, - DamageScales = new DamageScaleDef - { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. - HealthHitModifier = 3, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. - VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. - Characters = 1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 2000f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, - Grids = new GridSizeDef - { - Large = -1f, // Multiplier for damage against large grids. - Small = -1f, // Multiplier for damage against small grids. - }, - Armor = new ArmorDef - { - Armor = -1f, // Multiplier for damage against all armor. This is multiplied with the specific armor type multiplier (light, heavy). - Light = -1f, // Multiplier for damage against light armor. - Heavy = -1f, // Multiplier for damage against heavy armor. - NonArmor = -1f, // Multiplier for damage against every else. - }, - Shields = new ShieldDef - { - Modifier = -1f, // Multiplier for damage against shields. - Type = Default, // Damage vs healing against shields; Default, Heal - BypassModifier = -1f, // If greater than zero, the percentage of damage that will penetrate the shield. - }, - DamageType = new DamageTypes // Damage type of each element of the projectile's damage; Kinetic, Energy - { - Base = Kinetic, // Base Damage uses this - AreaEffect = Kinetic, - Detonation = Kinetic, - Shield = Kinetic, // Damage against shields is currently all of one type per projectile. Shield Bypass Weapons, always Deal Energy regardless of this line - }, - Deform = new DeformDef - { - DeformType = HitBlock, - DeformDelay = 30, - }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, + NoGridOrArmorScaling = true, Trajectory = new TrajectoryDef { Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 480, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 500, // voxel phasing if you go above 5100 MaxTrajectory = 1400f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) GravityMultiplier = 1f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, }, AmmoGraphics = new GraphicDef { ModelName = "Models\\Weapons\\MediumCalibreShell.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" VisualProbability = 1f, // % - ShieldHitDraw = false, Decals = new DecalDef { MaxAge = 3600, @@ -2354,809 +702,68 @@ partial class Parts { new TextureMapDef { - HitMaterial = "Metal", - DecalMaterial = "GunBullet", - }, - new TextureMapDef - { - HitMaterial = "Glass", - DecalMaterial = "GunBullet", - }, - }, - }, - Particles = new AmmoParticleDef - { - Ammo = new ParticleDef - { - Name = "MediumCalibreGun_Tracer", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, - Hit = new ParticleDef - { - Name = "MaterialHit_Metal_GatlingGun", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - }, - Lines = new LineDef - { - ColorVariance = Random(start: 0.5f, end: 2.5f), // multiply the color by random values within range. - WidthVariance = Random(start: 0f, end: 0.05f), // adds random value to default width (negatives shrinks width) - Tracer = new TracerBaseDef - { - Enable = false, - Length = 5f, // - Width = 0.2f, // - Color = Color(red: 2.5f, green: 2, blue: 1f, alpha: 1), // RBG 255 is Neon Glowing, 100 is Quite Bright. - VisualFadeStart = 0, // Number of ticks the weapon has been firing before projectiles begin to fade their color - VisualFadeEnd = 240, // How many ticks after fade began before it will be invisible. - Textures = new[] {// WeaponLaser, ProjectileTrailLine, WarpBubble, etc.. - "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, - }, - OffsetEffect = new OffsetEffectDef - { - MaxOffset = 0,// 0 offset value disables this effect - MinLength = 0.2f, - MaxLength = 3, - }, - }, - }, - AmmoAudio = new AmmoAudioDef - { - TravelSound = "MediumShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight - HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", - VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", - HitPlayChance = 1f, - HitPlayShield = true, - }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line - }; - - private AmmoDef ArtilleryShell => new AmmoDef // Your ID, for slotting into the Weapon CS - { - AmmoMagazine = "LargeCalibreAmmo", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. - AmmoRound = "Artillery Shell", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = false, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. - BaseDamage = 17000f, // Direct damage; one steel plate is worth 100. - Mass = 800f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. - BackKickForce = 30000f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. - HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, - DamageScales = new DamageScaleDef - { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. - HealthHitModifier = 4, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. - VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. - Characters = 1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 2500f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, - Grids = new GridSizeDef - { - Large = -1f, // Multiplier for damage against large grids. - Small = -1f, // Multiplier for damage against small grids. - }, - Armor = new ArmorDef - { - Armor = -1f, // Multiplier for damage against all armor. This is multiplied with the specific armor type multiplier (light, heavy). - Light = -1f, // Multiplier for damage against light armor. - Heavy = -1f, // Multiplier for damage against heavy armor. - NonArmor = -1f, // Multiplier for damage against every else. - }, - Shields = new ShieldDef - { - Modifier = -1f, // Multiplier for damage against shields. - Type = Default, // Damage vs healing against shields; Default, Heal - BypassModifier = -1f, // If greater than zero, the percentage of damage that will penetrate the shield. - }, - DamageType = new DamageTypes // Damage type of each element of the projectile's damage; Kinetic, Energy - { - Base = Kinetic, // Base Damage uses this - AreaEffect = Kinetic, - Detonation = Kinetic, - Shield = Kinetic, // Damage against shields is currently all of one type per projectile. Shield Bypass Weapons, always Deal Energy regardless of this line - }, - Deform = new DeformDef - { - DeformType = HitBlock, - DeformDelay = 30, - }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, - Trajectory = new TrajectoryDef - { - Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - MaxLifeTime = 600, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. - DesiredSpeed = 500, // voxel phasing if you go above 5100 - MaxTrajectory = 2000f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 0.5f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, - }, - AmmoGraphics = new GraphicDef - { - ModelName = "Models\\Weapons\\LargeCalibreShell.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" - VisualProbability = 1f, // % - ShieldHitDraw = false, - Decals = new DecalDef - { - MaxAge = 3600, - Map = new[] - { - new TextureMapDef - { - HitMaterial = "Metal", - DecalMaterial = "LargeShell", - }, - new TextureMapDef - { - HitMaterial = "Glass", - DecalMaterial = "LargeShell", - }, - }, - }, - Particles = new AmmoParticleDef - { - Ammo = new ParticleDef - { - Name = "LargeCalibreGun_Tracer", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, - Hit = new ParticleDef - { - Name = "MaterialHit_Metal_GatlingGun", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, - }, - Lines = new LineDef - { - ColorVariance = Random(start: 0.5f, end: 2.5f), // multiply the color by random values within range. - WidthVariance = Random(start: 0f, end: 0.05f), // adds random value to default width (negatives shrinks width) - Tracer = new TracerBaseDef - { - Enable = false, - Length = 5f, // - Width = 0.2f, // - Color = Color(red: 2.5f, green: 2, blue: 1f, alpha: 1), // RBG 255 is Neon Glowing, 100 is Quite Bright. - VisualFadeStart = 0, // Number of ticks the weapon has been firing before projectiles begin to fade their color - VisualFadeEnd = 240, // How many ticks after fade began before it will be invisible. - Textures = new[] {// WeaponLaser, ProjectileTrailLine, WarpBubble, etc.. - "ProjectileTrailLine", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } - }, - Trail = new TrailDef - { - Enable = false, - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - TextureMode = Normal, - DecayTime = 3, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. - Color = Color(red: 0, green: 0, blue: 1, alpha: 1), - Back = false, - CustomWidth = 0, - UseWidthVariance = false, - UseColorFade = true, - }, - OffsetEffect = new OffsetEffectDef - { - MaxOffset = 0,// 0 offset value disables this effect - MinLength = 0.2f, - MaxLength = 3, - }, - }, - }, - AmmoAudio = new AmmoAudioDef - { - TravelSound = "HeavyShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight - HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", - VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", - HitPlayChance = 1f, - HitPlayShield = true, - }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line - }; - - private AmmoDef LargeRailgunSabot => new AmmoDef // Your ID, for slotting into the Weapon CS - { - AmmoMagazine = "LargeRailgunAmmo", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. - AmmoRound = "Large Railgun Slug", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. - HybridRound = true, // Use both a physical ammo magazine and energy per shot. - EnergyCost = 0.1368f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. - BaseDamage = 50000f, // Direct damage; one steel plate is worth 100. - Mass = 100f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. - BackKickForce = 120000f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. - HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, - DamageScales = new DamageScaleDef - { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. - HealthHitModifier = 10, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. - VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. - Characters = 1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 3500f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, - Grids = new GridSizeDef - { - Large = -1f, // Multiplier for damage against large grids. - Small = -1f, // Multiplier for damage against small grids. - }, - Armor = new ArmorDef - { - Armor = -1f, // Multiplier for damage against all armor. This is multiplied with the specific armor type multiplier (light, heavy). - Light = -1f, // Multiplier for damage against light armor. - Heavy = -1f, // Multiplier for damage against heavy armor. - NonArmor = -1f, // Multiplier for damage against every else. - }, - Shields = new ShieldDef - { - Modifier = -1f, // Multiplier for damage against shields. - Type = Default, // Damage vs healing against shields; Default, Heal - BypassModifier = -1f, // If greater than zero, the percentage of damage that will penetrate the shield. - }, - DamageType = new DamageTypes // Damage type of each element of the projectile's damage; Kinetic, Energy - { - Base = Kinetic, // Base Damage uses this - AreaEffect = Kinetic, - Detonation = Kinetic, - Shield = Kinetic, // Damage against shields is currently all of one type per projectile. Shield Bypass Weapons, always Deal Energy regardless of this line - }, - Deform = new DeformDef - { - DeformType = HitBlock, - DeformDelay = 30, - }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, + HitMaterial = "Metal", + DecalMaterial = "GunBullet", + }, + new TextureMapDef + { + HitMaterial = "Glass", + DecalMaterial = "GunBullet", + }, + }, }, - Field = new FieldDef + Particles = new AmmoParticleDef { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. + Ammo = new ParticleDef + { + Name = "MediumCalibreGun_Tracer", //ShipWelderArc + Extras = new ParticleOptionDef + { + Scale = 1, + }, + }, + Hit = new ParticleDef { - Name = "", // SubtypeId of field particle effect. + Name = "MaterialHit_Metal_GatlingGun", + ApplyToShield = true, Extras = new ParticleOptionDef { - Scale = 1, // Scale of effect. + Scale = 1, + HitPlayChance = 1f, }, }, }, }, - Beams = new BeamDef + AmmoAudio = new AmmoAudioDef { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. + TravelSound = "MediumShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight + HitSound = "ImpMetalMetalCat0", + VoxelHitSound = "ImpMetalRockCat0", + HitPlayChance = 1f, + HitPlayShield = true, }, + }; + + private AmmoDef ArtilleryShell => new AmmoDef // Your ID, for slotting into the Weapon CS + { + AmmoMagazine = "LargeCalibreAmmo", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. + AmmoRound = "Artillery Shell", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. + BaseDamage = 17000f, // Direct damage; one steel plate is worth 100. + Mass = 800f, // In kilograms; how much force the impact will apply to the target. + BackKickForce = 30000f, // Recoil. This is applied to the Parent Grid. + HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. + NoGridOrArmorScaling = true, Trajectory = new TrajectoryDef { Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. - DesiredSpeed = 2000, // voxel phasing if you go above 5100 + MaxLifeTime = 600, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. + DesiredSpeed = 500, // voxel phasing if you go above 5100 MaxTrajectory = 2000f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 1f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, + GravityMultiplier = 0.5f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. }, AmmoGraphics = new GraphicDef { - ModelName = "Models\\Weapons\\RailgunAmmoLarge.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" + ModelName = "Models\\Weapons\\LargeCalibreShell.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" VisualProbability = 1f, // % ShieldHitDraw = false, Decals = new DecalDef @@ -3166,12 +773,12 @@ partial class Parts { new TextureMapDef { - HitMaterial = "Glass", + HitMaterial = "Metal", DecalMaterial = "LargeShell", }, new TextureMapDef { - HitMaterial = "Metal", + HitMaterial = "Glass", DecalMaterial = "LargeShell", }, }, @@ -3180,7 +787,7 @@ partial class Parts { Ammo = new ParticleDef { - Name = "", //ShipWelderArc + Name = "LargeCalibreGun_Tracer", //ShipWelderArc Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { @@ -3198,11 +805,65 @@ partial class Parts HitPlayChance = 1f, }, }, - Eject = new ParticleDef + }, + }, + AmmoAudio = new AmmoAudioDef + { + TravelSound = "HeavyShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight + HitSound = "ImpMetalMetalCat0", + VoxelHitSound = "ImpMetalRockCat0", + HitPlayChance = 1f, + HitPlayShield = true, + }, + }; + + private AmmoDef LargeRailgunSabot => new AmmoDef // Your ID, for slotting into the Weapon CS + { + AmmoMagazine = "LargeRailgunAmmo", // SubtypeId of physical ammo magazine. Use "Energy" for weapons without physical ammo. + AmmoRound = "Large Railgun Slug", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. + HybridRound = true, // Use both a physical ammo magazine and energy per shot. + EnergyCost = 0.1368f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. + BaseDamage = 50000f, // Direct damage; one steel plate is worth 100. + Mass = 100f, // In kilograms; how much force the impact will apply to the target. + BackKickForce = 120000f, // Recoil. This is applied to the Parent Grid. + HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. + NoGridOrArmorScaling = true, + Trajectory = new TrajectoryDef + { + MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. + DesiredSpeed = 2000, // voxel phasing if you go above 5100 + MaxTrajectory = 2000f, // Max Distance the projectile or beam can Travel. + SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. + RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory + MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. + }, + AmmoGraphics = new GraphicDef + { + ModelName = "Models\\Weapons\\RailgunAmmoLarge.mwm", // Model Path goes here. "\\Models\\Ammo\\Starcore_Arrow_Missile_Large" + VisualProbability = 1f, // % + Decals = new DecalDef + { + MaxAge = 3600, + Map = new[] + { + new TextureMapDef + { + HitMaterial = "Glass", + DecalMaterial = "LargeShell", + }, + new TextureMapDef + { + HitMaterial = "Metal", + DecalMaterial = "LargeShell", + }, + }, + }, + Particles = new AmmoParticleDef + { + Hit = new ParticleDef { - Name = "", + Name = "MaterialHit_Metal_GatlingGun", ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Scale = 1, @@ -3220,28 +881,11 @@ partial class Parts Length = 80f, // Width = 0.3f, // Color = Color(red: 20f, green: 20f, blue: 40f, alpha: 1f), // RBG 255 is Neon Glowing, 100 is Quite Bright. - VisualFadeStart = 0, // Number of ticks the weapon has been firing before projectiles begin to fade their color VisualFadeEnd = 240, // How many ticks after fade began before it will be invisible. Textures = new[] {// WeaponLaser, ProjectileTrailLine, WarpBubble, etc.. "WeaponLaser", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } }, Trail = new TrailDef { @@ -3252,9 +896,7 @@ partial class Parts TextureMode = Normal, DecayTime = 30, // In Ticks. 1 = 1 Additional Tracer generated per motion, 33 is 33 lines drawn per projectile. Keep this number low. Color = Color(red: 15f, green: 15f, blue: 30f, alpha: 1f), - Back = false, CustomWidth = 0.3f, - UseWidthVariance = false, UseColorFade = true, }, OffsetEffect = new OffsetEffectDef @@ -3269,26 +911,10 @@ partial class Parts { TravelSound = "HeavyShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 1f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; private AmmoDef SmallRailgunSabot => new AmmoDef // Your ID, for slotting into the Weapon CS @@ -3299,266 +925,14 @@ partial class Parts EnergyCost = 0.081f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. BaseDamage = 8000f, // Direct damage; one steel plate is worth 100. Mass = 25f, // In kilograms; how much force the impact will apply to the target. - Health = 0, // How much damage the projectile can take from other projectiles (base of 1 per hit) before dying; 0 disables this and makes the projectile untargetable. BackKickForce = 30000f, // Recoil. This is applied to the Parent Grid. - DecayPerShot = 0f, // Damage to the firing weapon itself. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. - EnergyMagazineSize = 0, // For energy weapons, how many shots to fire before reloading. - IgnoreWater = false, // Whether the projectile should be able to penetrate water when using WaterMod. - IgnoreVoxels = false, // Whether the projectile should be able to penetrate voxels. - Synchronize = false, // For future use - HeatModifier = -1f, // Allows this ammo to modify the amount of heat the weapon produces per shot. - Shape = new ShapeDef // Defines the collision shape of the projectile, defaults to LineShape and uses the visual Line Length if set to 0. - { - Shape = LineShape, // LineShape or SphereShape. Do not use SphereShape for fast moving projectiles if you care about precision. - Diameter = 0, // Diameter is minimum length of LineShape or minimum diameter of SphereShape. - }, - ObjectsHit = new ObjectsHitDef - { - MaxObjectsHit = 0, // Limits the number of entities (grids, players, projectiles) the projectile can penetrate; 0 = unlimited. - CountBlocks = false, // Counts individual blocks, not just entities hit. - }, - Fragment = new FragmentDef // Formerly known as Shrapnel. Spawns specified ammo fragments on projectile death (via hit or detonation). - { - AmmoRound = "", // AmmoRound field of the ammo to spawn. - Fragments = 0, // Number of projectiles to spawn. - Degrees = 0, // Cone in which to randomize direction of spawned projectiles. - Reverse = false, // Spawn projectiles backward instead of forward. - DropVelocity = false, // fragments will not inherit velocity from parent. - Offset = 0f, // Offsets the fragment spawn by this amount, in meters (positive forward, negative for backwards), value is read from parent ammo type. - Radial = 0f, // Determines starting angle for Degrees of spread above. IE, 0 degrees and 90 radial goes perpendicular to travel path - MaxChildren = 0, // number of maximum branches for fragments from the roots point of view, 0 is unlimited - IgnoreArming = true, // If true, ignore ArmOnHit or MinArmingTime in EndOfLife definitions - AdvOffset = Vector(x: 0, y: 0, z: 0), // advanced offsets the fragment by xyz coordinates relative to parent, value is read from fragment ammo type. - TimedSpawns = new TimedSpawnDef // disables FragOnEnd in favor of info specified below - { - Enable = false, // Enables TimedSpawns mechanism - Interval = 0, // Time between spawning fragments, in ticks, 0 means every tick, 1 means every other - StartTime = 0, // Time delay to start spawning fragments, in ticks, of total projectile life - MaxSpawns = 1, // Max number of fragment children to spawn - Proximity = 1000, // Starting distance from target bounding sphere to start spawning fragments, 0 disables this feature. No spawning outside this distance - ParentDies = true, // Parent dies once after it spawns its last child. - PointAtTarget = true, // Start fragment direction pointing at Target - PointType = Predict, // Point accuracy, Direct, Lead (always fire), Predict (only fire if it can hit) - GroupSize = 5, // Number of spawns in each group - GroupDelay = 120, // Delay between each group. - }, - }, - Pattern = new PatternDef - { - Patterns = new[] { // If enabled, set of multiple ammos to fire in order instead of the main ammo. - "", - }, - Mode = Fragment, // Select when to activate this pattern, options: Never, Weapon, Fragment, Both - TriggerChance = 1f, // This is % - Random = false, // This randomizes the number spawned at once, NOT the list order. - RandomMin = 1, - RandomMax = 1, - SkipParent = false, // Skip the Ammo itself, in the list - PatternSteps = 1, // Number of Ammos activated per round, will progress in order and loop. Ignored if Random = true. - }, - DamageScales = new DamageScaleDef - { - MaxIntegrity = 0f, // Blocks with integrity higher than this value will be immune to damage from this projectile; 0 = disabled. - DamageVoxels = false, // Whether to damage voxels. - SelfDamage = false, // Whether to damage the weapon's own grid. - HealthHitModifier = 2.5, // How much Health to subtract from another projectile on hit; defaults to 1 if zero or less. - VoxelHitModifier = 1, // Voxel damage multiplier; defaults to 1 if zero or less. - Characters = 1f, // Character damage multiplier; defaults to 1 if zero or less. - // For the following modifier values: -1 = disabled (higher performance), 0 = no damage, 0.01f = 1% damage, 2 = 200% damage. - FallOff = new FallOffDef - { - Distance = 2500f, // Distance at which damage begins falling off. - MinMultipler = -1f, // Value from 0.0001f to 1f where 0.1f would be a min damage of 10% of base damage. - }, - Grids = new GridSizeDef - { - Large = -1f, // Multiplier for damage against large grids. - Small = -1f, // Multiplier for damage against small grids. - }, - Armor = new ArmorDef - { - Armor = -1f, // Multiplier for damage against all armor. This is multiplied with the specific armor type multiplier (light, heavy). - Light = -1f, // Multiplier for damage against light armor. - Heavy = -1f, // Multiplier for damage against heavy armor. - NonArmor = -1f, // Multiplier for damage against every else. - }, - Shields = new ShieldDef - { - Modifier = -1f, // Multiplier for damage against shields. - Type = Default, // Damage vs healing against shields; Default, Heal - BypassModifier = -1f, // If greater than zero, the percentage of damage that will penetrate the shield. - }, - DamageType = new DamageTypes // Damage type of each element of the projectile's damage; Kinetic, Energy - { - Base = Kinetic, // Base Damage uses this - AreaEffect = Kinetic, - Detonation = Kinetic, - Shield = Kinetic, // Damage against shields is currently all of one type per projectile. Shield Bypass Weapons, always Deal Energy regardless of this line - }, - Deform = new DeformDef - { - DeformType = HitBlock, - DeformDelay = 30, - }, - Custom = new CustomScalesDef - { - SkipOthers = NoSkip, // Controls how projectile interacts with other blocks in relation to those defined here, NoSkip, Exclusive, Inclusive. - Types = new[] // List of blocks to apply custom damage multipliers to. - { - new CustomBlocksDef - { - SubTypeId = "Test1", - Modifier = -1f, - }, - new CustomBlocksDef - { - SubTypeId = "Test2", - Modifier = -1f, - }, - }, - }, - }, - AreaOfDamage = new AreaOfDamageDef - { - ByBlockHit = new ByBlockHitDef - { - Enable = false, - Radius = 5f, // Meters - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Pooled, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - EndOfLife = new EndOfLifeDef - { - Enable = false, - Radius = 5f, // Radius of AOE effect, in meters. - Damage = 5f, - Depth = 1f, // Max depth of AOE effect, in meters. 0=disabled, and AOE effect will reach to a depth of the radius value - MaxAbsorb = 0f, // Soft cutoff for damage, except for pooled falloff. If pooled falloff, limits max damage per block. - Falloff = Squeeze, //.NoFalloff applies the same damage to all blocks in radius - //.Linear drops evenly by distance from center out to max radius - //.Curve drops off damage sharply as it approaches the max radius - //.InvCurve drops off sharply from the middle and tapers to max radius - //.Squeeze does little damage to the middle, but rapidly increases damage toward max radius - //.Pooled damage behaves in a pooled manner that once exhausted damage ceases. - //.Exponential drops off exponentially. Does not scale to max radius - ArmOnlyOnHit = false, // Detonation only is available, After it hits something, when this is true. IE, if shot down, it won't explode. - MinArmingTime = 100, // In ticks, before the Ammo is allowed to explode, detonate or similar; This affects shrapnel spawning. - NoVisuals = false, - NoSound = false, - ParticleScale = 1, - CustomParticle = "", // Particle SubtypeID, from your Particle SBC - CustomSound = "", // SubtypeID from your Audio SBC, not a filename - Shape = Diamond, // Round or Diamond shape. Diamond is more performance friendly. - }, - }, - Ewar = new EwarDef - { - Enable = false, // Enables EWAR effects AND DISABLES BASE DAMAGE AND AOE DAMAGE!! - Type = EnergySink, // EnergySink, Emp, Offense, Nav, Dot, AntiSmart, JumpNull, Anchor, Tractor, Pull, Push, - Mode = Effect, // Effect , Field - Strength = 100f, - Radius = 5f, // Meters - Duration = 100, // In Ticks - StackDuration = true, // Combined Durations - Depletable = true, - MaxStacks = 10, // Max Debuffs at once - NoHitParticle = false, - /* - EnergySink : Targets & Shutdowns Power Supplies, such as Batteries & Reactor - Emp : Targets & Shutdown any Block capable of being powered - Offense : Targets & Shutdowns Weaponry - Nav : Targets & Shutdown Gyros or Locks them down - Dot : Deals Damage to Blocks in radius - AntiSmart : Effects & Scrambles the Targeting List of Affected Missiles - JumpNull : Shutdown & Stops any Active Jumps, or JumpDrive Units in radius - Tractor : Affects target with Physics - Pull : Affects target with Physics - Push : Affects target with Physics - Anchor : Targets & Shutdowns Thrusters - - */ - Force = new PushPullDef - { - ForceFrom = ProjectileLastPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - ForceTo = HitPosition, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - Position = TargetCenterOfMass, // ProjectileLastPosition, ProjectileOrigin, HitPosition, TargetCenter, TargetCenterOfMass - DisableRelativeMass = false, - TractorRange = 0, - ShooterFeelsForce = false, - }, - Field = new FieldDef - { - Interval = 0, // Time between each pulse, in game ticks (60 == 1 second), starts at 0 (59 == tick 60). - PulseChance = 0, // Chance from 0 - 100 that an entity in the field will be hit by any given pulse. - GrowTime = 0, // How many ticks it should take the field to grow to full size. - HideModel = false, // Hide the default bubble, or other model if specified. - ShowParticle = true, // Show Block damage effect. - TriggerRange = 250f, //range at which fields are triggered - Particle = new ParticleDef // Particle effect to generate at the field's position. - { - Name = "", // SubtypeId of field particle effect. - Extras = new ParticleOptionDef - { - Scale = 1, // Scale of effect. - }, - }, - }, - }, - Beams = new BeamDef - { - Enable = false, // Enable beam behaviour. Please have 3600 RPM, when this Setting is enabled. Please do not fire Beams into Voxels. - VirtualBeams = false, // Only one damaging beam, but with the effectiveness of the visual beams combined (better performance). - ConvergeBeams = false, // When using virtual beams, converge the visual beams to the location of the real beam. - RotateRealBeam = false, // The real beam is rotated between all visual beams, instead of centered between them. - OneParticle = false, // Only spawn one particle hit per beam weapon. - }, + NoGridOrArmorScaling = true, Trajectory = new TrajectoryDef { - Guidance = None, // None, Remote, TravelTo, Smart, DetectTravelTo, DetectSmart, DetectFixed - TargetLossDegree = 180f, // Degrees, Is pointed forward - TargetLossTime = 0, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - AccelPerSec = 0f, // Meters Per Second. This is the spawning Speed of the Projectile, and used by turning. DesiredSpeed = 1000, // voxel phasing if you go above 5100 MaxTrajectory = 1400f, // Max Distance the projectile or beam can Travel. - DeaccelTime = 0, // 0 is disabled, a value causes the projectile to come to rest overtime, (Measured in game ticks, 60 = 1 second) - GravityMultiplier = 1f, // Gravity multiplier, influences the trajectory of the projectile, value greater than 0 to enable. Natural Gravity Only. - SpeedVariance = Random(start: 0, end: 0), // subtracts value from DesiredSpeed. Be warned, you can make your projectile go backwards. - RangeVariance = Random(start: 0, end: 0), // subtracts value from MaxTrajectory - MaxTrajectoryTime = 0, // How long the weapon must fire before it reaches MaxTrajectory. - Smarts = new SmartsDef - { - Inaccuracy = 0f, // 0 is perfect, hit accuracy will be a random num of meters between 0 and this value. - Aggressiveness = 1f, // controls how responsive tracking is. - MaxLateralThrust = 0.5, // controls how sharp the trajectile may turn - TrackingDelay = 0, // Measured in Shape diameter units traveled. - MaxChaseTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - OverideTarget = true, // when set to true ammo picks its own target, does not use hardpoint's. - MaxTargets = 0, // Number of targets allowed before ending, 0 = unlimited - NoTargetExpire = false, // Expire without ever having a target at TargetLossTime - Roam = false, // Roam current area after target loss - KeepAliveAfterTargetLoss = false, // Whether to stop early death of projectile on target loss - OffsetRatio = 0.05f, // The ratio to offset the random direction (0 to 1) - OffsetTime = 60, // how often to offset degree, measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..) - }, - Mines = new MinesDef // Note: This is being investigated. Please report to Github, any issues. - { - DetectRadius = 0, - DeCloakRadius = 0, - FieldTime = 0, - Cloak = false, - Persist = false, - }, }, AmmoGraphics = new GraphicDef { @@ -3584,15 +958,6 @@ partial class Parts }, Particles = new AmmoParticleDef { - Ammo = new ParticleDef - { - Name = "", //ShipWelderArc - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - }, - }, Hit = new ParticleDef { Name = "MaterialHit_Metal_GatlingGun", @@ -3604,17 +969,6 @@ partial class Parts HitPlayChance = 1f, }, }, - Eject = new ParticleDef - { - Name = "", - ApplyToShield = true, - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Scale = 1, - HitPlayChance = 1f, - }, - }, }, Lines = new LineDef { @@ -3632,22 +986,6 @@ partial class Parts "WeaponLaser", // Please always have this Line set, if this Section is enabled. }, TextureMode = Normal, // Normal, Cycle, Chaos, Wave - Segmentation = new SegmentDef - { - Enable = false, // If true Tracer TextureMode is ignored - Textures = new[] { - "", // Please always have this Line set, if this Section is enabled. - }, - SegmentLength = 0f, // Uses the values below. - SegmentGap = 0f, // Uses Tracer textures and values - Speed = 1f, // meters per second - Color = Color(red: 1, green: 2, blue: 2.5f, alpha: 1), - WidthMultiplier = 1f, - Reverse = false, - UseLineVariance = true, - WidthVariance = Random(start: 0f, end: 0f), - ColorVariance = Random(start: 0f, end: 0f) - } }, Trail = new TrailDef { @@ -3675,26 +1013,10 @@ partial class Parts { TravelSound = "MediumShellFlightSound", // SubtypeID for your Sound File. Travel, is sound generated around your Projectile in flight HitSound = "ImpMetalMetalCat0", - ShotSound = "", - ShieldHitSound = "", - PlayerHitSound = "", VoxelHitSound = "ImpMetalRockCat0", - FloatingHitSound = "", HitPlayChance = 1f, HitPlayShield = true, }, - Ejection = new EjectionDef // Optional Component, allows generation of Particle or Item (Typically magazine), on firing, to simulate Tank shell ejection - { - Type = Particle, // Particle or Item (Inventory Component) - Speed = 100f, // Speed inventory is ejected from in dummy direction - SpawnChance = 0.5f, // chance of triggering effect (0 - 1) - CompDef = new ComponentDef - { - ItemName = "", //InventoryComponent name - ItemLifeTime = 0, // how long item should exist in world - Delay = 0, // delay in ticks after shot before ejected - } - }, // Don't edit below this line }; } } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs index 4f420456..ebe9a7e8 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "muzzle_missile_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_missile_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -41,13 +40,6 @@ partial class Parts { SubSystems = new[] { Power, Utility, Offense, Thrust, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 2, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -57,96 +49,26 @@ partial class Parts { PartName = "Artillery", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.15f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.3f, // Inventory capacity in kL. IdlePower = 0.0025f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 80, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 720, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepLargeCalibreShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -154,34 +76,16 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_LargeCalibre", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, }, }, Ammos = new[] { ArtilleryShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition LargeBlockArtilleryTurret => new WeaponDefinition @@ -204,7 +108,6 @@ partial class Parts { "muzzle_missile_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A "muzzle_missile_002", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "camera", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -215,13 +118,6 @@ partial class Parts { SubSystems = new[] { Power, Utility, Offense, Thrust, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 2, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -232,26 +128,12 @@ partial class Parts { DeviateShotAngle = 0.3f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Advanced, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -261,66 +143,24 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -15, MaxElevation = 60, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.6f, // Inventory capacity in kL. IdlePower = 0.02f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 80, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 720, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 2, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepLargeCalibreShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -328,34 +168,16 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_LargeCalibre", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, }, }, Ammos = new[] { ArtilleryShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs index e9c78fcb..0d67d1dd 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs @@ -31,7 +31,6 @@ partial class Parts { "Muzzle_Missile_Left", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A "Muzzle_Missile_Right", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "Muzzle_Missile_Left", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -42,13 +41,6 @@ partial class Parts { SubSystems = new[] { Utility, Offense, Thrust, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -59,26 +51,12 @@ partial class Parts { DeviateShotAngle = 0.35f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Advanced, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -88,66 +66,24 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -20, MaxElevation = 75, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.18f, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 180, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 2, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepMediumCalibreShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -155,34 +91,16 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_MediumCalibre", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, }, }, Ammos = new[] { AssaultCannonShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallBlockAssaultCannon => new WeaponDefinition @@ -204,7 +122,6 @@ partial class Parts { Muzzles = new[] { "Muzzle_Missile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "Muzzle_Missile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -215,13 +132,6 @@ partial class Parts { SubSystems = new[] { Utility, Offense, Thrust, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -231,96 +141,25 @@ partial class Parts { PartName = "Assault Cannon", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.2f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.09f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 200, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepMediumCalibreShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -328,34 +167,16 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_MediumCalibre", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, }, }, Ammos = new[] { AssaultCannonShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallBlockAssaultCannonTurret => new WeaponDefinition @@ -377,7 +198,6 @@ partial class Parts { Muzzles = new[] { "Muzzle_Missile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "Muzzle_Missile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -388,13 +208,6 @@ partial class Parts { SubSystems = new[] { Utility, Offense, Thrust, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -405,26 +218,12 @@ partial class Parts { DeviateShotAngle = 0.5f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Advanced, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -434,66 +233,24 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -10, MaxElevation = 50, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.09f, // Inventory capacity in kL. IdlePower = 0.005f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 200, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepMediumCalibreShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -501,25 +258,9 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_MediumCalibre", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { - Loop = false, // Set this to the same as in the particle sbc! Restart = false, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, }, }, }, @@ -527,8 +268,6 @@ partial class Parts { Ammos = new[] { AssaultCannonShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs index 5d017910..ac247cf2 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -41,13 +40,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -57,95 +49,25 @@ partial class Parts { PartName = "Autocannon", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.15f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.072f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 200, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 1, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipAutocannonShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, @@ -154,24 +76,18 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Autocannon", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, Effect2 = new ParticleDef { Name = "Smoke_Autocannon", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -203,8 +119,6 @@ partial class Parts { Muzzles = new[] { "muzzle_missile_01", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. - Scope = "", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef { @@ -214,13 +128,6 @@ partial class Parts { SubSystems = new[] { Utility, Offense, Thrust, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -231,26 +138,12 @@ partial class Parts { DeviateShotAngle = 0.25f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -260,66 +153,25 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -10, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle HomeElevation = 1, // Default resting elevation InventorySize = 0.072f, // Inventory capacity in kL. IdlePower = 0.005f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 200, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipAutocannonShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, Graphics = new HardPointParticleDef @@ -327,24 +179,18 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Autocannon", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. Scale = 1f, // Scale of effect. }, }, Effect2 = new ParticleDef { Name = "Smoke_Autocannon", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -353,8 +199,6 @@ partial class Parts { Ammos = new[] { AutocannonShell, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs index ecea09c1..8b678d31 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -41,13 +40,6 @@ partial class Parts { SubSystems = new[] { Offense, Thrust, Utility, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -59,25 +51,12 @@ partial class Parts { AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -87,62 +66,27 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -40, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.658f, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, }, Other = new OtherDef { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. RotateBarrelAxis = 3, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 700, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 240, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). SpinFree = true, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipGatlingShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. BarrelRotationSound = "WepShipGatlingRotation", @@ -154,8 +98,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Large", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! @@ -166,12 +108,9 @@ partial class Parts { Effect2 = new ParticleDef { Name = "Smoke_LargeGunShot_WC", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -180,8 +119,6 @@ partial class Parts { Ammos = new[] { GatlingAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallGatlingGun => new WeaponDefinition @@ -212,7 +149,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -223,13 +159,6 @@ partial class Parts { SubSystems = new[] { Offense, Thrust, Utility, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -239,94 +168,31 @@ partial class Parts { PartName = "Small Gatling Gun", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.3f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.064f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, }, Other = new OtherDef { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. RotateBarrelAxis = 3, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 700, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 1, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). SpinFree = true, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipGatlingShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. BarrelRotationSound = "WepShipGatlingRotation", FireSoundEndDelay = 10, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. @@ -336,8 +202,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Large", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! @@ -348,12 +212,9 @@ partial class Parts { Effect2 = new ParticleDef { Name = "Smoke_LargeGunShot_WC", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -362,8 +223,6 @@ partial class Parts { Ammos = new[] { GatlingAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallGatlingTurret => new WeaponDefinition @@ -396,13 +255,6 @@ partial class Parts { SubSystems = new[] { Offense, Thrust, Utility, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -414,25 +266,12 @@ partial class Parts { AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -442,62 +281,27 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -10, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.36f, // Inventory capacity in kL. IdlePower = 0.005f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, }, Other = new OtherDef { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. RotateBarrelAxis = 3, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 700, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). SpinFree = true, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipGatlingShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. BarrelRotationSound = "WepShipGatlingRotation", @@ -509,8 +313,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Large", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! @@ -521,12 +323,9 @@ partial class Parts { Effect2 = new ParticleDef { Name = "Smoke_LargeGunShot_WC", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -535,8 +334,6 @@ partial class Parts { Ammos = new[] { GatlingAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition LargeGatlingTurretReskin => new WeaponDefinition @@ -558,7 +355,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -569,13 +365,6 @@ partial class Parts { SubSystems = new[] { Offense, Thrust, Utility, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -602,21 +391,9 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -40, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.658f, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, }, Loading = new LoadingDef { @@ -628,10 +405,8 @@ partial class Parts { }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipGatlingShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. BarrelRotationSound = "WepShipGatlingRotation", @@ -643,7 +418,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Large", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. Offset = Vector(x: 0, y: 0, z: -1.1f), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { @@ -655,12 +429,9 @@ partial class Parts { Effect2 = new ParticleDef { Name = "Smoke_LargeGunShot_WC", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -669,11 +440,8 @@ partial class Parts { Ammos = new[] { GatlingAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; - WeaponDefinition SmallGatlingTurretReskin => new WeaponDefinition { Assignments = new ModelAssignmentsDef @@ -693,7 +461,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -730,21 +497,9 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -10, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.36f, // Inventory capacity in kL. IdlePower = 0.005f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, }, Loading = new LoadingDef { @@ -756,10 +511,8 @@ partial class Parts { }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipGatlingShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. BarrelRotationSound = "WepShipGatlingRotation", @@ -771,7 +524,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash_Large", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. Offset = Vector(x: 0, y: 0, z: 0.3f), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { @@ -783,12 +535,9 @@ partial class Parts { Effect2 = new ParticleDef { Name = "Smoke_LargeGunShot_WC", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! - Restart = false, Scale = 1f, }, }, @@ -797,10 +546,6 @@ partial class Parts { Ammos = new[] { GatlingAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; - - // Don't edit below this line. } } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs index 24224bd6..5d5fe2af 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "muzzle_projectile", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_projectile", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -42,12 +41,6 @@ partial class Parts { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, ClosestFirst = true, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -59,25 +52,12 @@ partial class Parts { AimingTolerance = 1f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -87,65 +67,23 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -76, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.08f, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 600, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 180, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepTurretInteriorFire", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state - NoAmmoSound = "", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", FireSoundEndDelay = 10, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, @@ -154,8 +92,6 @@ partial class Parts { Effect1 = new ParticleDef { Name = "Muzzle_Flash", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. Extras = new ParticleOptionDef { Loop = true, // Set this to the same as in the particle sbc! @@ -163,26 +99,12 @@ partial class Parts { Scale = 3f, // Scale of effect. }, }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 3f, - }, - }, }, }, Ammos = new[] { InteriorAmmo, InteriorAmmoOld // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs index 75d92d89..1c76cfa5 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs @@ -48,7 +48,6 @@ partial class Parts { "muzzle_missile_018", "muzzle_missile_019", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_missile_010", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -59,13 +58,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -75,131 +67,32 @@ partial class Parts { PartName = "Large Missile Launcher", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.1f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = true, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 1.14f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 120, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 240, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 19, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipSmallMissileShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = true, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { MissileAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition LargeMissileTurret => new WeaponDefinition @@ -235,7 +128,6 @@ partial class Parts { "muzzle_missile_005", "muzzle_missile_006", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_missile_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -246,13 +138,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -263,26 +148,12 @@ partial class Parts { DeviateShotAngle = 0.5f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = true, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -292,101 +163,30 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -58, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.768f, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 90, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 240, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 6, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepTurretMissileShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = true, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { MissileAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallMissileLauncher => new WeaponDefinition @@ -419,7 +219,6 @@ partial class Parts { "muzzle_missile_003", "muzzle_missile_004", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_missile_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -430,13 +229,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -446,131 +238,32 @@ partial class Parts { PartName = "Small Missile Launcher", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.1f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = true, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.24f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 60, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 60, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 4, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipSmallMissileShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = true, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { MissileAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallRocketLauncherReload => new WeaponDefinition @@ -595,7 +288,6 @@ partial class Parts { "muzzle_missile_003", "muzzle_missile_004", }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "muzzle_missile_002", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -606,13 +298,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -622,131 +307,32 @@ partial class Parts { PartName = "Reloadable Missile Launcher", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.1f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = true, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.245f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 60, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 60, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 4, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepShipSmallMissileShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = true, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { MissileAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallMissileTurret => new WeaponDefinition @@ -789,13 +375,6 @@ partial class Parts { SubSystems = new[] { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 1000, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -806,26 +385,12 @@ partial class Parts { DeviateShotAngle = 0.5f, // Projectile inaccuracy in degrees. AimingTolerance = 4f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = true, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, Ai = new AiDef { TrackTargets = true, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. }, HardWare = new HardwareDef { @@ -835,101 +400,30 @@ partial class Parts { MaxAzimuth = 180, MinElevation = -8, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.36f, // Inventory capacity in kL. IdlePower = 0.005f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 90, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 360, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 2, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { - PreFiringSound = "", // Audio for warmup effect. FiringSound = "WepTurretMissileShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", HardPointRotationSound = "WepTurretGatlingRotate", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = true, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { MissileAmmo, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs index 8359ef3e..f2e9eebc 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "barrel_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "barrel_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -41,13 +40,6 @@ partial class Parts { SubSystems = new[] { Power, Utility, Offense, Thrust, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 2, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -57,84 +49,20 @@ partial class Parts { PartName = "Large Railgun", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.24f, // Inventory capacity in kL. IdlePower = 0.001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 20, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 3600, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. DelayUntilFire = 120, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. StayCharged = true, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef @@ -142,46 +70,14 @@ partial class Parts { PreFiringSound = "WepRailgunLargeCharge", // Audio for warmup effect. FiringSound = "WepRailgunLargeShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 1, green: 1, blue: 1, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { LargeRailgunSabot, // Must list all primary, shrapnel, and pattern ammos. }, Animations = LargeRailgunAnimation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallBlockRailgun => new WeaponDefinition @@ -203,7 +99,6 @@ partial class Parts { Muzzles = new[] { "barrel_001", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "barrel_001", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -214,13 +109,6 @@ partial class Parts { SubSystems = new[] { Power, Utility, Offense, Thrust, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, - ClosestFirst = false, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. - MaxTargetDistance = 0, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 2, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. StopTrackingSpeed = 500, // Do not track threats traveling faster than this speed; 0 = unlimited. @@ -230,133 +118,36 @@ partial class Parts { PartName = "Small Railgun", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0f, // Projectile inaccuracy in degrees. AimingTolerance = 2f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Off, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 0, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - - Ui = new UiDef - { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. - }, - Ai = new AiDef - { - TrackTargets = false, // Whether this weapon tracks its own targets, or (for multiweapons) relies on the weapon with PrimaryTracking enabled for target designation. Turrets Need this set to True. - TurretAttached = false, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. - TurretController = false, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. - PrimaryTracking = false, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. - }, HardWare = new HardwareDef { - RotateRate = 0f, // Max traversal speed of azimuth subpart in radians per tick (0.1 is approximately 360 degrees per second). - ElevateRate = 0f, // Max traversal speed of elevation subpart in radians per tick. - MinAzimuth = 0, - MaxAzimuth = 0, - MinElevation = 0, - MaxElevation = 0, - HomeAzimuth = 0, // Default resting rotation angle - HomeElevation = 0, // Default resting elevation InventorySize = 0.048f, // Inventory capacity in kL. IdlePower = 0.0001f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom - CriticalReaction = new CriticalDef - { - Enable = false, // Enables Warhead behaviour. - DefaultArmedTimer = 120, // Sets default countdown duration. - PreArmed = false, // Whether the warhead is armed by default when placed. Best left as false. - TerminalControls = true, // Whether the warhead should have terminal controls for arming and detonation. - AmmoRound = "", // Optional. If specified, the warhead will always use this ammo on detonation rather than the currently selected ammo. - }, - }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. }, Loading = new LoadingDef { RateOfFire = 20, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. TrajectilesPerBarrel = 1, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. ReloadTime = 1200, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). MagsToLoad = 1, // Number of physical magazines to consume on reload. DelayUntilFire = 30, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. + StayCharged = true, // Will start recharging whenever power cap is not full. }, Audio = new HardPointAudioDef { PreFiringSound = "WepRailgunSmallCharge", // Audio for warmup effect. FiringSound = "WepRailgunSmallShot", // Audio for firing. FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state NoAmmoSound = "WepShipGatlingNoAmmo", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 0, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, - Graphics = new HardPointParticleDef - { - Effect1 = new ParticleDef - { - Name = "", // SubtypeId of muzzle particle effect. - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), // Deprecated, set color in particle sbc. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the effect from the muzzle empty. - Extras = new ParticleOptionDef - { - Loop = false, // Set this to the same as in the particle sbc! - Restart = false, // Whether to end a looping effect instantly when firing stops. - Scale = 1f, // Scale of effect. - }, - }, - Effect2 = new ParticleDef - { - Name = "", - Color = Color(red: 0, green: 0, blue: 0, alpha: 1), - Offset = Vector(x: 0, y: 0, z: 0), - Extras = new ParticleOptionDef - { - Loop = true, // Set this to the same as in the particle sbc! - Restart = false, - Scale = 1f, - }, - }, - }, }, Ammos = new[] { SmallRailgunSabot, // Must list all primary, shrapnel, and pattern ammos. }, Animations = SmallRailgunAnimation, - //Upgrades = UpgradeModules, }; - // Don't edit below this line. } } diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs index 26cf8f82..8915520d 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs @@ -30,7 +30,6 @@ partial class Parts { Muzzles = new[] { "spotlight", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "spotlight", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -42,12 +41,7 @@ partial class Parts { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, ClosestFirst = true, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. MaxTargetDistance = 2500, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. ShootBlanks = true, @@ -57,17 +51,8 @@ partial class Parts { PartName = "Searchlight", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.1f, // Projectile inaccuracy in degrees. AimingTolerance = 1f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - Ui = new UiDef { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. DisableStatus = true, // Do not display weapon status NoTarget, Reloading, NoAmmo, etc.. }, Ai = new AiDef @@ -76,9 +61,6 @@ partial class Parts { TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. TargetGridCenter = true, // Does not target blocks, instead it targets grid center. }, HardWare = new HardwareDef @@ -87,68 +69,20 @@ partial class Parts { ElevateRate = 0.04f, // Max traversal speed of elevation subpart in radians per tick. MinAzimuth = -180, MaxAzimuth = 180, - MinElevation = 0, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle HomeElevation = 45, // Default resting elevation - InventorySize = 0, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. - }, Loading = new LoadingDef { RateOfFire = 600, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. - TrajectilesPerBarrel = 0, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. - ReloadTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - MagsToLoad = 0, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. - }, - Audio = new HardPointAudioDef - { - PreFiringSound = "", // Audio for warmup effect. - FiringSound = "", // Audio for firing. - FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state - NoAmmoSound = "", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 10, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). - FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, }, Ammos = new[] { SpotLight, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; WeaponDefinition SmallSearchlight => new WeaponDefinition { @@ -169,7 +103,6 @@ partial class Parts { Muzzles = new[] { "spotlight", // Where your Projectiles spawn. Use numbers not Letters. IE Muzzle_01 not Muzzle_A }, - Ejector = "", // Optional; empty from which to eject "shells" if specified. Scope = "spotlight", // Where line of sight checks are performed from. Must be clear of block collision. }, Targeting = new TargetingDef @@ -181,12 +114,7 @@ partial class Parts { Thrust, Utility, Offense, Power, Production, Any, // Subsystem targeting priority: Offense, Utility, Power, Production, Thrust, Jumping, Steering, Any }, ClosestFirst = true, // Tries to pick closest targets first (blocks on grids, projectiles, etc...). - IgnoreDumbProjectiles = false, // Don't fire at non-smart projectiles. - LockedSmartOnly = false, // Only fire at smart projectiles that are locked on to parent grid. - MinimumDiameter = 0, // Minimum radius of threat to engage. - MaximumDiameter = 0, // Maximum radius of threat to engage; 0 = unlimited. MaxTargetDistance = 1500, // Maximum distance at which targets will be automatically shot at; 0 = unlimited. - MinTargetDistance = 0, // Minimum distance at which targets will be automatically shot at. TopTargets = 4, // Maximum number of targets to randomize between; 0 = unlimited. TopBlocks = 4, // Maximum number of blocks to randomize between; 0 = unlimited. ShootBlanks = true, @@ -196,17 +124,8 @@ partial class Parts { PartName = "Searchlight", // Name of the weapon in terminal, should be unique for each weapon definition that shares a SubtypeId (i.e. multiweapons). DeviateShotAngle = 0.1f, // Projectile inaccuracy in degrees. AimingTolerance = 1f, // How many degrees off target a turret can fire at. 0 - 180 firing angle. - AimLeadingPrediction = Accurate, // Level of turret aim prediction; Off, Basic, Accurate, Advanced - DelayCeaseFire = 10, // Measured in game ticks (6 = 100ms, 60 = 1 second, etc..). Length of time the weapon continues firing after trigger is released. - AddToleranceToTracking = false, // Allows turret to track to the edge of the AimingTolerance cone instead of dead centre. - CanShootSubmerged = false, // Whether the weapon can be fired underwater when using WaterMod. - Ui = new UiDef { - RateOfFire = false, // Enables terminal slider for changing rate of fire. - DamageModifier = false, // Enables terminal slider for changing damage per shot. - ToggleGuidance = false, // Enables terminal option to disable smart projectile guidance. - EnableOverload = false, // Enables terminal option to turn on Overload; this allows energy weapons to double damage per shot, at the cost of quadrupled power draw and heat gain, and 2% self damage on overheat. DisableStatus = true, // Do not display weapon status NoTarget, Reloading, NoAmmo, etc.. }, Ai = new AiDef @@ -215,9 +134,6 @@ partial class Parts { TurretAttached = true, // Whether this weapon is a turret and should have the UI and API options for such. Turrets Need this set to True. TurretController = true, // Whether this weapon can physically control the turret's movement. Turrets Need this set to True. PrimaryTracking = true, // For multiweapons: whether this weapon should designate targets for other weapons on the platform without their own tracking. - LockOnFocus = false, // If enabled, weapon will only fire at targets that have been HUD selected AND locked onto by pressing Numpad 0. - SuppressFire = false, // If enabled, weapon can only be fired manually. - OverrideLeads = false, // Disable target leading on fixed weapons, or allow it for turrets. TargetGridCenter = true, // Does not target blocks, instead it targets grid center. }, HardWare = new HardwareDef @@ -226,68 +142,20 @@ partial class Parts { ElevateRate = 0.04f, // Max traversal speed of elevation subpart in radians per tick. MinAzimuth = -180, MaxAzimuth = 180, - MinElevation = 0, MaxElevation = 90, - HomeAzimuth = 0, // Default resting rotation angle HomeElevation = 45, // Default resting elevation - InventorySize = 0, // Inventory capacity in kL. IdlePower = 0.01f, // Constant base power draw in MW. - FixedOffset = false, // Deprecated. - Offset = Vector(x: 0, y: 0, z: 0), // Offsets the aiming/firing line of the weapon, in metres. Type = BlockWeapon, // What type of weapon this is; BlockWeapon, HandWeapon, Phantom }, - Other = new OtherDef - { - ConstructPartCap = 0, // Maximum number of blocks with this weapon on a grid; 0 = unlimited. - RotateBarrelAxis = 0, // For spinning barrels, which axis to spin the barrel around; 0 = none. - EnergyPriority = 0, // Deprecated. - MuzzleCheck = false, // Whether the weapon should check LOS from each individual muzzle in addition to the scope. - Debug = false, // Force enables debug mode. - RestrictionRadius = 0, // Prevents other blocks of this type from being placed within this distance of the centre of the block. - CheckInflatedBox = false, // If true, the above distance check is performed from the edge of the block instead of the centre. - CheckForAnyWeapon = false, // If true, the check will fail if ANY weapon is present, not just weapons of the same subtype. - }, Loading = new LoadingDef { RateOfFire = 600, // Set this to 3600 for beam weapons. This is how fast your Gun fires. BarrelsPerShot = 1, // How many muzzles will fire a projectile per fire event. - TrajectilesPerBarrel = 0, // Number of projectiles per muzzle per fire event. - SkipBarrels = 0, // Number of muzzles to skip after each fire event. - ReloadTime = 0, // Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - MagsToLoad = 0, // Number of physical magazines to consume on reload. - DelayUntilFire = 0, // How long the weapon waits before shooting after being told to fire. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - HeatPerShot = 0, // Heat generated per shot. - MaxHeat = 100, // Max heat before weapon enters cooldown (70% of max heat). - Cooldown = .95f, // Percentage of max heat to be under to start firing again after overheat; accepts 0 - 0.95 - HeatSinkRate = 0, // Amount of heat lost per second. - DegradeRof = false, // Progressively lower rate of fire when over 80% heat threshold (80% of max heat). - ShotsInBurst = 0, // Use this if you don't want the weapon to fire an entire physical magazine in one go. Should not be more than your magazine capacity. - DelayAfterBurst = 0, // How long to spend "reloading" after each burst. Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). - FireFull = false, // Whether the weapon should fire the full magazine (or the full burst instead if ShotsInBurst > 0), even if the target is lost or the player stops firing prematurely. - GiveUpAfter = false, // Whether the weapon should drop its current target and reacquire a new target after finishing its magazine or burst. - BarrelSpinRate = 0, // Visual only, 0 disables and uses RateOfFire. - DeterministicSpin = false, // Spin barrel position will always be relative to initial / starting positions (spin will not be as smooth). - SpinFree = false, // Spin barrel while not firing. - StayCharged = false, // Will start recharging whenever power cap is not full. - }, - Audio = new HardPointAudioDef - { - PreFiringSound = "", // Audio for warmup effect. - FiringSound = "", // Audio for firing. - FiringSoundPerShot = true, // Whether to replay the sound for each shot, or just loop over the entire track while firing. - ReloadSound = "", // Sound SubtypeID, for when your Weapon is in a reloading state - NoAmmoSound = "", - HardPointRotationSound = "", // Audio played when turret is moving. - BarrelRotationSound = "", - FireSoundEndDelay = 10, // How long the firing audio should keep playing after firing stops. Measured in game ticks(6 = 100ms, 60 = 1 seconds, etc..). - FireSoundNoBurst = true, // Don't stop firing sound from looping when delaying after burst. }, }, Ammos = new[] { SpotLight, // Must list all primary, shrapnel, and pattern ammos. }, - //Animations = Weapon75_Animation, - //Upgrades = UpgradeModules, }; // Don't edit below this line. diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 9ce41767..8ea71ab7 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -498,7 +498,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon MagazineSize = EnergyAmmo ? EnergyMagSize : MagazineDef.Capacity; if (EnergyAmmo && MagazineSize == 0) - Log.Line($"{ammo.AmmoDef.AmmoRound} has a magazine capacity of zero, Girax error detected! Check your magazine sbc entry for a tag"); + Log.Line($"{ammo.AmmoDef.AmmoRound} has a magazine capacity of zero, Girax error detected! Check your magazine sbc entry for a tag or ammo for EnergyMagazineSize"); MagsToLoad = wDef.HardPoint.Loading.MagsToLoad > 0 ? wDef.HardPoint.Loading.MagsToLoad : 1; MaxAmmo = MagsToLoad * MagazineSize; From b65052eca7d8c117b9fd6b50743c8be8c5fa4bfa Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:55:19 -0600 Subject: [PATCH 04/77] Additional sharpdx logging --- Data/Scripts/CoreSystems/AudioVisual/AvShot.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index 1085c15d..8c2481b9 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -1051,8 +1051,8 @@ internal void SetupSounds(double distanceFromCameraSqr) } catch (Exception e) { - MyLog.Default.Error($"Sound error with ammo: {AmmoDef.AmmoRound} from {Weapon.Comp.TerminalBlock.DisplayName} soundID {AmmoDef.Const.ShotSoundPair.SoundId} cuename {AmmoDef.Const.ShotSoundPair.GetCueName()}" + - $"FireEmitter.Entity null? {FireEmitter?.Entity == null} \n Origin: {Origin}"); + MyLog.Default.Error($"Sound error with ammo: {AmmoDef.AmmoRound} from {Weapon.Comp.TerminalBlock.CustomName} soundID {AmmoDef.Const.ShotSoundPair.SoundId} cuename {AmmoDef.Const.ShotSoundPair.GetCueName()}" + + $"\n FireEmitter.Entity null? {FireEmitter?.Entity == null} \n Origin: {Origin} \n Marked for Close? {Weapon.Comp.FunctionalBlock.MarkedForClose}"); throw e; } } From e9718203cb7157fd6a421ca9d3ca36fd91b911b1 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:34:04 -0600 Subject: [PATCH 05/77] Debugging Temp catches --- .../EntityComp/Parts/Weapon/WeaponTypes.cs | 199 +++++++++++------- .../CoreSystems/Session/SessionSupport.cs | 116 ++++++---- 2 files changed, 196 insertions(+), 119 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs index 4e1c91f3..46897c01 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs @@ -5,6 +5,7 @@ using Sandbox.Game.Entities; using VRage.Game.Entity; using VRage.Game.ModAPI; +using VRage.Utils; using VRageMath; namespace CoreSystems.Platform @@ -22,106 +23,152 @@ internal ParallelRayCallBack(Weapon weapon) public void NormalShootRayCallBack(IHitInfo hitInfo) { - var pTarget = Weapon.Target.TargetObject as Projectile; - var eTarget = Weapon.Target.TargetObject as MyEntity; - if (pTarget == null && eTarget == null) - return; + int crumb = 0; + try + { + var pTarget = Weapon.Target.TargetObject as Projectile; + var eTarget = Weapon.Target.TargetObject as MyEntity; + if (pTarget == null && eTarget == null) + return; + crumb = 1; + Weapon.Casting = false; + Weapon.PauseShoot = false; + var masterWeapon = Weapon.System.TrackTargets ? Weapon : Weapon.Comp.PrimaryWeapon; + var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || Weapon.Target.TargetObject is IMyCharacter; + var scope = Weapon.GetScope; + var trackingCheckPosition = scope.CachedPos; + double rayDist = 0; + crumb = 2; + + if (Session.I.DebugLos) + { + var hitPos = hitInfo.Position; + if (rayDist <= 0) Vector3D.Distance(ref trackingCheckPosition, ref hitPos, out rayDist); - Weapon.Casting = false; - Weapon.PauseShoot = false; - var masterWeapon = Weapon.System.TrackTargets ? Weapon : Weapon.Comp.PrimaryWeapon; - var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || Weapon.Target.TargetObject is IMyCharacter; - var scope = Weapon.GetScope; - var trackingCheckPosition = scope.CachedPos; - double rayDist = 0; + Session.I.AddLosCheck(new Session.LosDebug { Part = Weapon, HitTick = Session.I.Tick, Line = new LineD(trackingCheckPosition, hitPos) }); + } + crumb = 3; - if (Session.I.DebugLos) - { - var hitPos = hitInfo.Position; - if (rayDist <= 0) Vector3D.Distance(ref trackingCheckPosition, ref hitPos, out rayDist); + if (Weapon.Comp.Ai.ShieldNear) + { + crumb = 4; - Session.I.AddLosCheck(new Session.LosDebug { Part = Weapon, HitTick = Session.I.Tick, Line = new LineD(trackingCheckPosition, hitPos) }); - } + var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldMatrixRef.Translation; + var targetDir = targetPos - trackingCheckPosition; + if (Weapon.HitFriendlyShield(trackingCheckPosition, targetPos, targetDir)) + { + crumb = 51; - - if (Weapon.Comp.Ai.ShieldNear) - { + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + return; + } + crumb = 6; - var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldMatrixRef.Translation; - var targetDir = targetPos - trackingCheckPosition; - if (Weapon.HitFriendlyShield(trackingCheckPosition, targetPos, targetDir)) - { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - return; } - } - var hitTopEnt = (MyEntity)hitInfo?.HitEntity?.GetTopMostParent(); - if (hitTopEnt == null) - { - if (ignoreTargets) - return; - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); - return; - } + var hitTopEnt = (MyEntity)hitInfo?.HitEntity?.GetTopMostParent(); + if (hitTopEnt == null) + { + crumb = 7; - var targetTopEnt = eTarget?.GetTopMostParent(); - if (targetTopEnt == null) - return; + if (ignoreTargets) + return; + crumb = 8; - var unexpectedHit = ignoreTargets || targetTopEnt != hitTopEnt; - var topAsGrid = hitTopEnt as MyCubeGrid; + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); + crumb = 9; - if (unexpectedHit) - { - if (hitTopEnt is MyVoxelBase && !Weapon.System.ScanNonThreats) - { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); return; } + crumb = 10; - if (topAsGrid == null) + var targetTopEnt = eTarget?.GetTopMostParent(); + if (targetTopEnt == null) return; - if (Weapon.Comp.Ai.AiType == Ai.AiTypes.Grid && topAsGrid.IsSameConstructAs(Weapon.Comp.Ai.GridEntity)) + crumb = 11; + + var unexpectedHit = ignoreTargets || targetTopEnt != hitTopEnt; + var topAsGrid = hitTopEnt as MyCubeGrid; + crumb = 12; + + if (unexpectedHit) { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); - Weapon.PauseShoot = true; + crumb = 13; + + if (hitTopEnt is MyVoxelBase && !Weapon.System.ScanNonThreats) + { + crumb = 14; + + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); + return; + } + + if (topAsGrid == null) + return; + if (Weapon.Comp.Ai.AiType == Ai.AiTypes.Grid && topAsGrid.IsSameConstructAs(Weapon.Comp.Ai.GridEntity)) + { + crumb = 15; + + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); + Weapon.PauseShoot = true; + return; + } + if (!Weapon.System.ScanNonThreats && (!topAsGrid.DestructibleBlocks || topAsGrid.Immune || topAsGrid.GridGeneralDamageModifier <= 0 || !Session.GridEnemy(Weapon.Comp.Ai.AiOwner, topAsGrid))) + { + crumb = 16; + + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + return; + } + crumb = 17; + return; + } - if (!Weapon.System.ScanNonThreats && (!topAsGrid.DestructibleBlocks || topAsGrid.Immune || topAsGrid.GridGeneralDamageModifier <= 0 || !Session.GridEnemy(Weapon.Comp.Ai.AiOwner, topAsGrid))) + if (Weapon.System.ClosestFirst && topAsGrid != null && topAsGrid == targetTopEnt) { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - return; + crumb = 18; + + var halfExtMin = topAsGrid.PositionComp.LocalAABB.HalfExtents.Min(); + var minSize = topAsGrid.GridSizeR * 8; + var maxChange = halfExtMin > minSize ? halfExtMin : minSize; + var targetPos = eTarget.PositionComp.WorldAABB.Center; + var weaponPos = trackingCheckPosition; + + if (rayDist <= 0) Vector3D.Distance(ref weaponPos, ref targetPos, out rayDist); + var newHitShortDist = rayDist * (1 - hitInfo.Fraction); + var distanceToTarget = rayDist * hitInfo.Fraction; + crumb = 19; + + var shortDistExceed = newHitShortDist - Weapon.Target.HitShortDist > maxChange; + var escapeDistExceed = distanceToTarget - Weapon.Target.OrigDistance > Weapon.Target.OrigDistance; + if (shortDistExceed || escapeDistExceed) + { + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); + crumb = 20; + + } } - return; } - if (Weapon.System.ClosestFirst && topAsGrid != null && topAsGrid == targetTopEnt) + catch (Exception ex) { - var halfExtMin = topAsGrid.PositionComp.LocalAABB.HalfExtents.Min(); - var minSize = topAsGrid.GridSizeR * 8; - var maxChange = halfExtMin > minSize ? halfExtMin : minSize; - var targetPos = eTarget.PositionComp.WorldAABB.Center; - var weaponPos = trackingCheckPosition; - - if (rayDist <= 0) Vector3D.Distance(ref weaponPos, ref targetPos, out rayDist); - var newHitShortDist = rayDist * (1 - hitInfo.Fraction); - var distanceToTarget = rayDist * hitInfo.Fraction; - - var shortDistExceed = newHitShortDist - Weapon.Target.HitShortDist > maxChange; - var escapeDistExceed = distanceToTarget - Weapon.Target.OrigDistance > Weapon.Target.OrigDistance; - if (shortDistExceed || escapeDistExceed) - { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); - } + var pTarget = Weapon?.Target?.TargetObject as Projectile; + var eTarget = Weapon?.Target?.TargetObject as MyEntity; + var msg = $"Emperor Palpatine, your bug is back!\n" + + $"Crumb: {crumb} eTarget: {eTarget == null} pTarget: {pTarget == null} WepNull: {Weapon == null} Wep.Targ Null: {Weapon.Target == null}"; + MyLog.Default.WriteLine(msg); + Log.Line(msg); + throw ex; } } + } internal class Muzzle diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index ad008cda..fe570a6d 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -874,48 +874,57 @@ private void ChatMessageSet(string message, ref bool sendToOthers) private uint _lastIncompatibleMessageTick = uint.MaxValue; internal void RemoveIncompatibleBlock(object o) { - var cube = o as MyCubeBlock; - if (cube != null) + try { - - var processCube = !cube.MarkedForClose && !cube.Closed && cube.SlimBlock != null && cube.BlockDefinition != null && cube.CubeGrid != null && !cube.CubeGrid.IsPreview && cube.CubeGrid.Physics != null && !cube.CubeGrid.MarkedForClose; - if (processCube) + var cube = o as MyCubeBlock; + if (cube != null) { - if (BrokenMod(cube)) - return; - - if (_lastIncompatibleMessageTick == uint.MaxValue || Tick - _lastIncompatibleMessageTick > 600) + var processCube = !cube.MarkedForClose && !cube.Closed && cube.SlimBlock != null && cube.BlockDefinition != null && cube.CubeGrid != null && !cube.CubeGrid.IsPreview && cube.CubeGrid.Physics != null && !cube.CubeGrid.MarkedForClose; + if (processCube) { - _lastIncompatibleMessageTick = Tick; - var skipMessage = IsClient && Settings.Enforcement.UnsupportedMode && Tick > 600; - if (!skipMessage) - FutureEvents.Schedule(ReportIncompatibleBlocks, null, 10); - } + if (BrokenMod(cube)) + return; - if (cube.BlockDefinition?.Id.SubtypeName != null) - _unsupportedBlockNames.Add(cube.BlockDefinition.Id.SubtypeName); - - if (DedicatedServer) - { - if (!Settings.Enforcement.UnsupportedMode) - cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); - } - else if (!Settings.Enforcement.UnsupportedMode) - { - if (!_removeComplete) - _unsupportedBlocks.Add(cube); + if (_lastIncompatibleMessageTick == uint.MaxValue || Tick - _lastIncompatibleMessageTick > 600) + { + _lastIncompatibleMessageTick = Tick; + var skipMessage = IsClient && Settings.Enforcement.UnsupportedMode && Tick > 600; + if (!skipMessage) + FutureEvents.Schedule(ReportIncompatibleBlocks, null, 10); + } - if (_removeComplete || DedicatedServer) - cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); + if (cube.BlockDefinition?.Id.SubtypeName != null) + _unsupportedBlockNames.Add(cube.BlockDefinition.Id.SubtypeName); - if (!_removeScheduled) + if (DedicatedServer) + { + if (!Settings.Enforcement.UnsupportedMode) + cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); + } + else if (!Settings.Enforcement.UnsupportedMode) { - _removeScheduled = true; - FutureEvents.Schedule(RemoveIncompatibleBlocks, null, 3600); + if (!_removeComplete) + _unsupportedBlocks.Add(cube); + + if (_removeComplete || DedicatedServer) + cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); + + if (!_removeScheduled) + { + _removeScheduled = true; + FutureEvents.Schedule(RemoveIncompatibleBlocks, null, 3600); + } } } } } + catch (Exception ex) + { + var errorMsg = $"WC exception in RemoveIncompatibleBlock. Obj null: {o == null} Settings null: {Settings?.Enforcement == null} Unsupported mode: {Settings?.Enforcement.UnsupportedMode} \n{ex}"; + Log.Line(errorMsg); + MyLog.Default.WriteLineAndConsole(errorMsg); + throw ex; + } } private readonly HashSet _unsupportedBlockNames = new HashSet(); @@ -1592,6 +1601,9 @@ public bool IsPartAreaRestricted(MyStringHash subtype, MyOrientedBoundingBoxD cu var checkSphere = restriction.RestrictionRadius > 0; var querySphere = new BoundingSphereD(cubeBoundingBox.Center, queryRadius); + if (myGrid.Hierarchy == null) + return false; + myGrid.Hierarchy.QuerySphere(ref querySphere, _tmpNearByBlocks); foreach (var grid in ai.SubGridCache) { @@ -1700,17 +1712,20 @@ internal void LoadVanillaData() VanillaIds[largeMissileMedCalId] = largeMissileMedCal; VanillaCoreIds[largeMissileMedCal] = largeMissileMedCalId; - var smallLargeMissile = MyStringHash.GetOrCompute("LargeMissileLauncher"); var smallLargeMissileId = new MyDefinitionId(typeof(MyObjectBuilder_SmallMissileLauncher), "LargeMissileLauncher"); VanillaIds[smallLargeMissileId] = smallLargeMissile; VanillaCoreIds[smallLargeMissile] = smallLargeMissileId; + var largeRailgun = MyStringHash.GetOrCompute("LargeRailgun"); + var largeRailgunId = new MyDefinitionId(typeof(MyObjectBuilder_ConveyorSorter), "LargeRailgun"); + VanillaIds[largeRailgunId] = largeRailgun; + VanillaCoreIds[largeRailgun] = largeRailgunId; - var smallLargeMissileReload = MyStringHash.GetOrCompute("LargeRailgun"); - var smallLargeMissileReloadId = new MyDefinitionId(typeof(MyObjectBuilder_SmallMissileLauncherReload), "LargeRailgun"); - VanillaIds[smallLargeMissileReloadId] = smallLargeMissileReload; - VanillaCoreIds[smallLargeMissileReload] = smallLargeMissileReloadId; + var SmallRailgun = MyStringHash.GetOrCompute("SmallRailgun"); + var SmallRailgunId = new MyDefinitionId(typeof(MyObjectBuilder_ConveyorSorter), "SmallRailgun"); + VanillaIds[SmallRailgunId] = SmallRailgun; + VanillaCoreIds[SmallRailgun] = SmallRailgunId; var smallLargeMissileLargeCal = MyStringHash.GetOrCompute("LargeBlockLargeCalibreGun"); var smallLargeMissileLargeCalId = new MyDefinitionId(typeof(MyObjectBuilder_SmallMissileLauncher), "LargeBlockLargeCalibreGun"); @@ -1732,16 +1747,31 @@ internal void LoadVanillaData() VanillaIds[smallAutoTurretId] = smallAutoTurret; VanillaCoreIds[smallAutoTurret] = smallAutoTurretId; + var smallMissileTurret = MyStringHash.GetOrCompute("SmallMissileTurret"); + var smallMissileTurretId = new MyDefinitionId(typeof(MyObjectBuilder_LargeMissileTurret), "SmallMissileTurret"); + VanillaIds[smallMissileTurretId] = smallMissileTurret; + VanillaCoreIds[smallMissileTurret] = smallMissileTurretId; + var smallMedCalGun = MyStringHash.GetOrCompute("SmallBlockMediumCalibreGun"); var smallMedCalGunId = new MyDefinitionId(typeof(MyObjectBuilder_SmallMissileLauncherReload), "SmallBlockMediumCalibreGun"); VanillaIds[smallMedCalGunId] = smallMedCalGun; VanillaCoreIds[smallMedCalGun] = smallMedCalGunId; + var smallMedCalTurret = MyStringHash.GetOrCompute("SmallBlockMediumCalibreTurret"); + var smallMedCalTurretId = new MyDefinitionId(typeof(MyObjectBuilder_LargeMissileTurret), "SmallBlockMediumCalibreTurret"); + VanillaIds[smallMedCalTurretId] = smallMedCalTurret; + VanillaCoreIds[smallMedCalTurret] = smallMedCalTurretId; + var smallGatAuto = MyStringHash.GetOrCompute("SmallBlockAutocannon"); var smallGatAutoId = new MyDefinitionId(typeof(MyObjectBuilder_SmallGatlingGun), "SmallBlockAutocannon"); VanillaIds[smallGatAutoId] = smallGatAuto; VanillaCoreIds[smallGatAuto] = smallGatAutoId; + var smallGatReskin = MyStringHash.GetOrCompute("SmallGatlingTurretReskin"); + var smallGatReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeGatlingTurret), "SmallGatlingTurretReskin"); + VanillaIds[smallGatReskinId] = smallGatReskin; + VanillaCoreIds[smallGatReskin] = smallGatReskinId; + var smallRocketReload = MyStringHash.GetOrCompute("SmallRocketLauncherReload"); var smallRocketReloadId = new MyDefinitionId(typeof(MyObjectBuilder_SmallMissileLauncherReload), "SmallRocketLauncherReload"); VanillaIds[smallRocketReloadId] = smallRocketReload; @@ -1752,6 +1782,11 @@ internal void LoadVanillaData() VanillaIds[smallGat2Id] = smallGat2; VanillaCoreIds[smallGat2] = smallGat2Id; + var smallGatTurret = MyStringHash.GetOrCompute("SmallGatlingTurret"); + var smallGatTurretId = new MyDefinitionId(typeof(MyObjectBuilder_LargeGatlingTurret), "SmallGatlingTurret"); + VanillaIds[smallGatTurretId] = smallGatTurret; + VanillaCoreIds[smallGatTurret] = smallGatTurretId; + var largeSearch = MyStringHash.GetOrCompute("LargeSearchlight"); var largeSearchId = new MyDefinitionId(typeof(MyObjectBuilder_SearchlightDefinition), "LargeSearchlight"); VanillaIds[largeSearchId] = largeSearch; @@ -1767,18 +1802,13 @@ internal void LoadVanillaData() VanillaIds[largeGatReskinId] = largeGatReskin; VanillaCoreIds[largeGatReskin] = largeGatReskinId; - var smallGatReskin = MyStringHash.GetOrCompute("SmallGatlingTurretReskin"); - var smallGatReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeTurretBaseDefinition), "SmallGatlingTurretReskin"); - VanillaIds[smallGatReskinId] = smallGatReskin; - VanillaCoreIds[smallGatReskin] = smallGatReskinId; - var largeMissileReskin = MyStringHash.GetOrCompute("LargeMissileTurretReskin"); - var largeMissileReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeTurretBaseDefinition), "LargeMissileTurretReskin"); + var largeMissileReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeMissileTurret), "LargeMissileTurretReskin"); VanillaIds[largeMissileReskinId] = largeMissileReskin; VanillaCoreIds[largeMissileReskin] = largeMissileReskinId; var smallMissileReskin = MyStringHash.GetOrCompute("SmallMissileTurretReskin"); - var smallMissileReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeTurretBaseDefinition), "SmallMissileTurretReskin"); + var smallMissileReskinId = new MyDefinitionId(typeof(MyObjectBuilder_LargeMissileTurret), "SmallMissileTurretReskin"); VanillaIds[smallMissileReskinId] = smallMissileReskin; VanillaCoreIds[smallMissileReskin] = smallMissileReskinId; From 3ea29fa12128631847b0709a82bd4b6a57f71667 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 2 Feb 2025 13:02:31 -0600 Subject: [PATCH 06/77] Localization- first pass at adding fields --- .../Controls/CreateCustomActions.cs | 6 +++--- .../EntityComp/Controls/TerminalHelpers.cs | 10 +++++----- .../CoreSystems/Support/Localization.cs | 20 +++++++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index 9a1a4682..f1f0bf60 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -374,7 +374,7 @@ public static void CreateLargeGrid(Session session) { var action = MyAPIGateway.TerminalControls.CreateAction("LargeGrid"); action.Icon = @"Textures\GUI\Icons\Actions\Toggle.dds"; - action.Name = new StringBuilder("Target Large Grids"); + action.Name = new StringBuilder(Localization.GetText("ActionTargetLargeGrids")); action.Action = CustomActions.TerminalActionToggleLargeGrid; action.Writer = CustomActions.LargeGridWriter; action.Enabled = TerminalHelpers.HasTracking; @@ -388,7 +388,7 @@ public static void CreateSmallGrid(Session session) { var action = MyAPIGateway.TerminalControls.CreateAction("SmallGrid"); action.Icon = @"Textures\GUI\Icons\Actions\Toggle.dds"; - action.Name = new StringBuilder("Target Small Grids"); + action.Name = new StringBuilder(Localization.GetText("ActionTargetSmallGrids")); action.Action = CustomActions.TerminalActionToggleSmallGrid; action.Writer = CustomActions.SmallGridWriter; action.Enabled = TerminalHelpers.HasTracking; @@ -415,7 +415,7 @@ internal static void CreateObjectiveMode(Session session) { var action = MyAPIGateway.TerminalControls.CreateAction("ObjectiveMode"); action.Icon = @"Textures\GUI\Icons\Actions\Toggle.dds"; - action.Name = new StringBuilder("Cycle Objective Mode");//Need localization input + action.Name = new StringBuilder(Localization.GetText("ActionCycleObjective")); action.Action = CustomActions.TerminActionCycleObjectiveMode; action.Writer = CustomActions.ObjectiveModeWriter; action.Enabled = TerminalHelpers.HasTracking; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 25c53008..9f59dbbb 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -38,7 +38,7 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I { AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); - AddComboboxNoAction(session, "ObjectiveMode", "Objective Mode", "Select when to cease firing at a block", BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); + AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); @@ -74,9 +74,9 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); - AddOnOffSwitchNoAction(session, "LargeGrid", "Large Grid", "Target large grids", BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", "Small Grid", "Target small grids", BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); Separator(session, "WC_sep3", IsTrue); @@ -135,9 +135,9 @@ internal static void AddTurretControlBlockControls(Session session) where T : CtcAddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepelControl, BlockUi.RequestSetRepelControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "LargeGrid", "Large Grid", "Target large grids", BlockUi.GetLargeGridControl, BlockUi.RequestSetLargeGridControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGridControl, BlockUi.RequestSetLargeGridControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "SmallGrid", "Small Grid", "Target small grids", BlockUi.GetSmallGridControl, BlockUi.RequestSetSmallGridControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGridControl, BlockUi.RequestSetSmallGridControl, true, CtcIsReady); Separator(session, "WC_sep3", IsTrue); diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index c90a3be2..050bceaa 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -78,26 +78,18 @@ public static class Localization { "TerminalCameraChannelTooltip", "Assign this weapon to a camera channel" }, { "TerminalBurstShotsTitle", "Burst Shot Count" }, { "TerminalBurstShotsTooltip", "The number of shots to burst at a time" }, - { "TerminalBurstDelayTitle", "Shot Delay" }, { "TerminalBurstDelayTooltip", "The number game ticks (60 per second) to delay between shots" }, - - { "TerminalSequenceIdTitle", "Weapon Sequence id" }, { "TerminalSequenceIdTooltip", "Assign this weapon a unique sequence id per weapon group, used for sequence firing" }, - { "TerminalWeaponGroupIdTitle", "Weapon Group id" }, { "TerminalWeaponGroupIdTooltip", "Assign this weapon to a sequence group, used for sequence firing" }, - { "TerminalShootModeTitle", "Shoot Mode" }, { "TerminalShootModeTooltip", "Set the weapon's mode, fire once, burst fire, mouse click, key toggle mode" }, - { "TerminalAiEnabledTitle", "Enable AI" }, { "TerminalAiEnabledTooltip", "Automatically aim and fire at targets" }, - { "TerminalShareFireControlTitle", "Share Control" }, { "TerminalShareFireControlTooltip", "Weapons with manual/painter/mousecontrol enabled will respond when using the Control button above" }, - { "TerminalTargetGroupTitle", "Target Lead Group" }, { "TerminalTargetGroupTooltip", "Assign this weapon to target lead group" }, { "TerminalDecoyPickSubSystemTitle", "Pick SubSystem" }, @@ -105,7 +97,6 @@ public static class Localization { "TerminalCameraCameraChannelTitle", "Camera Channel" }, { "TerminalCameraCameraChannelTooltip", "Assign the camera weapon channel to this camera" }, { "TerminalDebugTitle", "Debug" }, - { "TerminalDebugTooltip", "Debug On/Off" }, { "TerminalAdvancedTitle", "Advanced Features" }, { "TerminalAdvancedTooltip", "This enables more advanced UI features that tend to be confusing to new users" }, @@ -145,10 +136,11 @@ public static class Localization { "ActionWC_Decrease_CameraChannel", "Previous Camera Channel" }, { "ActionWC_Increase_LeadGroup", "Next Lead Group" }, { "ActionWC_Decrease_LeadGroup", "Previous Lead Group" }, - + { "ActionTargetLargeGrids", "Target Large Grids" }, + { "ActionTargetSmallGrids", "Target Small Grids" }, + { "ActionCycleObjective", "Cycle Objective Mode" }, { "ActionWCAiEnabled", "Enable AI On/Off" }, { "ActionShareFireControl", "Share Control On/Off" }, - { "ActionMask", "Select Mask Type" }, { "SystemStatusFault", "[Fault]" }, { "SystemStatusOffline", "[Offline]" }, @@ -180,6 +172,12 @@ public static class Localization { "TerminalOverrideTooltip", "Turns off targeting and tracking so you can forcibly fire the weapon- for practice use only" }, { "TerminalAngularTitle", "Track Angular Motion" }, { "TerminalAngularTooltip", "Adjust aim to account for angular motion of the target" }, + { "TerminalLGTitle", "Large Grid" }, + { "TerminalLGTooltip", "Target large grids" }, + { "TerminalSGTitle", "Small Grid" }, + { "TerminalSGTooltip", "Target small grids" }, + { "TerminalObjectiveTitle", "Objective Mode" }, + { "TerminalObjectiveTooltip", "Select when to cease firing at a block" }, { "WeaponInfoHasTarget", "HasTarget" }, { "WeaponInfoTargetState", "TargetType" }, From 41600202254b75566ad39778254ae6d90c5fe805 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:48:56 -0600 Subject: [PATCH 07/77] Localization and label updates --- .../SerializedConfigs/Weapon/ProtoWeapon.cs | 1 - .../Controls/Weapon/WeaponActions.cs | 2 +- .../EntityComp/Controls/Weapon/WeaponUi.cs | 57 +++--- .../CoreSystems/EntityComp/EntityEvents.cs | 34 ++-- .../CoreSystems/Support/Localization.cs | 178 +++++++++++++++--- 5 files changed, 199 insertions(+), 73 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs index 1cf29cbe..4dc35002 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs @@ -595,7 +595,6 @@ public enum MoveModes { Any, Moving, - Mobile, Moored, } diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index d066249f..bd7c64d6 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -150,7 +150,7 @@ internal static void TerminalActionMovementMode(IMyTerminalBlock blk) return; var numValue = (int)comp.Data.Repo.Values.Set.Overrides.MoveMode; - var value = numValue + 1 <= 3 ? numValue + 1 : 0; + var value = numValue + 1 <= 2 ? numValue + 1 : 0; Weapon.WeaponComponent.RequestSetValue(comp, "MovementModes", value, Session.I.PlayerId); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs index 8cae6999..4dec0d3a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs @@ -621,10 +621,10 @@ internal static void ListShootModes(List shootMod private static readonly List ShootModeList = new List { - new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute($"{(Weapon.ShootManager.ShootModes)0}") }, - new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute($"{(Weapon.ShootManager.ShootModes)1}") }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute($"{(Weapon.ShootManager.ShootModes)2}") }, - new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute($"{(Weapon.ShootManager.ShootModes)3}") }, + new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("ShootAiShoot")) }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("ShootMouse")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("ShootKeyToggle")) }, + new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute(Localization.GetText("ShootKey")) }, }; internal static long GetSubSystem(IMyTerminalBlock block) @@ -649,14 +649,14 @@ internal static void ListSubSystems(List subSyste private static readonly List SubList = new List { - new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)0}") }, - new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)1}") }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)2}") }, - new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)3}") }, - new MyTerminalControlComboBoxItem { Key = 4, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)4}") }, - new MyTerminalControlComboBoxItem { Key = 5, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)5}") }, - new MyTerminalControlComboBoxItem { Key = 6, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)6}") }, - new MyTerminalControlComboBoxItem { Key = 7, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)7}") }, + new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeAny")) }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeOffense")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeUtility")) }, + new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypePower")) }, + new MyTerminalControlComboBoxItem { Key = 4, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeProduction")) }, + new MyTerminalControlComboBoxItem { Key = 5, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeThrust")) }, + new MyTerminalControlComboBoxItem { Key = 6, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeJumping")) }, + new MyTerminalControlComboBoxItem { Key = 7, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeSteering")) }, }; internal static long GetMovementMode(IMyTerminalBlock block) @@ -681,10 +681,9 @@ internal static void ListMovementModes(List moveL private static readonly List MoveList = new List { - new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.MoveModes)0}") }, - new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.MoveModes)1}") }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.MoveModes)2}") }, - new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.MoveModes)3}") }, + new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("MoveAny")) }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMoving")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMoored")) }, }; internal static long GetObjectiveMode(IMyTerminalBlock block) @@ -709,9 +708,9 @@ internal static void ListObjectiveModes(List move private static readonly List ObjectiveList = new List { - new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ObjectiveModes)0}") }, - new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ObjectiveModes)1}") }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ObjectiveModes)2}") }, + new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("ObjDefault")) }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("ObjDisabled")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("ObjDestroyed")) }, }; internal static long GetControlMode(IMyTerminalBlock block) @@ -1131,9 +1130,9 @@ internal static void ListControlModes(List contro private static readonly List ControlList = new List { - new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ControlModes)0}") }, - new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ControlModes)1}") }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute($"{(ProtoWeaponOverrides.ControlModes)2}") }, + new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("ControlAuto")) }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("ControlManual")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("ControlPainter")) }, }; internal static void ListDecoySubSystems(List subSystemList) @@ -1143,13 +1142,13 @@ internal static void ListDecoySubSystems(List sub private static readonly List DecoySubList = new List() { - new MyTerminalControlComboBoxItem() { Key = 1, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)1}") }, - new MyTerminalControlComboBoxItem() { Key = 2, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)2}") }, - new MyTerminalControlComboBoxItem() { Key = 3, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)3}") }, - new MyTerminalControlComboBoxItem() { Key = 4, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)4}") }, - new MyTerminalControlComboBoxItem() { Key = 5, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)5}") }, - new MyTerminalControlComboBoxItem() { Key = 6, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)6}") }, - new MyTerminalControlComboBoxItem() { Key = 7, Value = MyStringId.GetOrCompute($"{(WeaponDefinition.TargetingDef.BlockTypes)7}") }, + new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeOffense")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeUtility")) }, + new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypePower")) }, + new MyTerminalControlComboBoxItem { Key = 4, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeProduction")) }, + new MyTerminalControlComboBoxItem { Key = 5, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeThrust")) }, + new MyTerminalControlComboBoxItem { Key = 6, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeJumping")) }, + new MyTerminalControlComboBoxItem { Key = 7, Value = MyStringId.GetOrCompute(Localization.GetText("SubtypeSteering")) }, }; } } diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index c48bc659..86149001 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -236,12 +236,12 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str var debug = Debug || comp.Data.Repo.Values.Set.Overrides.Debug; var advanced = (I.Settings.ClientConfig.AdvancedMode || debug) && !comp.HasAlternateUi; if (HasServerOverrides) - stringBuilder.Append($"\nWeapon modified by server!\n") - .Append($"Report issues to server admins.\n"); + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoServerModdedLine1")}\n") + .Append($"\n{Localization.GetText("WeaponInfoServerModdedLine2")}"); //Start of new formatting if (IdlePower > 0.01) - stringBuilder.Append($"\nIdle Power: " + IdlePower.ToString("0.00") + " MW"); + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoIdlePower")}: {IdlePower:0.00} {Localization.GetText("WeaponInfoMWLabel")}"); for (int i = 0; i < collection.Count; i++) { @@ -250,8 +250,8 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str if ((w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo || w.ActiveAmmoDef.AmmoDef.Const.IsHybrid) && !comp.HasAlternateUi) { var chargeTime = w.AssignedPower > 0 ? (int)((w.MaxCharge - w.ProtoWeaponAmmo.CurrentCharge) / w.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; - shots += "\nDraw/Max: " + (SinkPower - IdlePower).ToString("0.00") + "/" + w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick.ToString("0.00") + " MW" + - $"\n{(chargeTime == 0 ? "Power: Charged" : "Power: Charged in " + chargeTime + "s")}"; + shots += $"\n{Localization.GetText("WeaponInfoDrawOverMax")}: {SinkPower - IdlePower:0.00}/ {w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick:0.00} {Localization.GetText("WeaponInfoMWLabel")}" + + $"\n{(chargeTime == 0 ? Localization.GetText("WeaponInfoPowerCharged") : Localization.GetText("WeaponInfoPowerChargedIn") + chargeTime + Localization.GetText("WeaponInfoSeconds"))}"; } var endReturn = i + 1 != collection.Count ? "\n" : string.Empty; @@ -260,37 +260,37 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str var displayName = showName ? w.ActiveAmmoDef.AmmoDef.Const.TerminalName + " (" + w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : w.ActiveAmmoDef.AmmoDef.Const.TerminalName; stringBuilder.Append($"\n\n" + w.System.PartName + shots + - $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : "\nAmmo: " + (w.Loading ? timeToLoad < 0 ? "Waiting on charge" : "Loaded in " + timeToLoad + "s": w.ProtoWeaponAmmo.CurrentAmmo > 0 ? "Loaded " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : "No Ammo"))}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoadedIn") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + w.Target.HasTarget : string.Empty)}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : "No Target") : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); } if (HeatPerSecond > 0) - stringBuilder.Append($"\nHeat per Sec/Max: {HeatPerSecond}/{MaxHeat}" + + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoHeatPerSecOverMax")}: {HeatPerSecond}/{MaxHeat}" + $"\n{Localization.GetText("WeaponInfoCurrentHeat")}: {CurrentHeat:0.} W ({(CurrentHeat / MaxHeat):P})"); if (advanced) { - stringBuilder.Append($"\n\n--- Stats ---" + - $"\nDPS: {comp.PeakDps:0.}"); + stringBuilder.Append($"\n\n{Localization.GetText("WeaponInfoStatsHeader")}" + + $"\n{Localization.GetText("WeaponInfoDPSLabel")}: {comp.PeakDps:0.}"); for (int i = 0; i < collection.Count; i++) { var w = collection[i]; stringBuilder.Append($" {(collection.Count > 1 ? "\n{w.FriendlyName}" : string.Empty)}" + - $"{(w.MinTargetDistance > 0 ? "\nMin Range: " + w.MinTargetDistance + "m" : string.Empty)}" + - $"\nMax Range: {w.MaxTargetDistance}m" + - $"\nRoF: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin}/min"); + $"{(w.MinTargetDistance > 0 ? $"\n{Localization.GetText("WeaponInfoMinRange")}: {w.MinTargetDistance}{Localization.GetText("WeaponInfoMeter")}" : string.Empty)}" + + $"\n{Localization.GetText("WeaponInfoMaxRange")}: {w.MaxTargetDistance}{Localization.GetText("WeaponInfoMeter")}" + + $"\n{Localization.GetText("WeaponInfoROF")}: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin}{Localization.GetText("WeaponInfoPerMin")}"); if(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget) { - var targ = "Target: "; + var targ = $"{Localization.GetText("WeaponInfoTargetLabel")}: "; if (w.Target.HasTarget && w.Target.TargetObject != null) { var pTarg = w.Target.TargetObject as Projectile; var eTarg = w.Target.TargetObject as MyEntity; if(pTarg != null) { - targ += "Projectile"; + targ += Localization.GetText("WeaponInfoProjectileLabel"); } else if (eTarg != null) { @@ -304,7 +304,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str } } else - targ += "none"; + targ += Localization.GetText("WeaponInfoNoneTarget"); stringBuilder.Append($"\n{targ}"); } @@ -318,7 +318,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str continue; if (otherAmmo == null) - otherAmmo = "\n\nAmmo Types (Mag if different):"; + otherAmmo = $"\n\n{Localization.GetText("WeaponInfoAmmoType")}:"; var showName = ammo.AmmoDef.Const.TerminalName != ammo.AmmoDef.Const.MagazineDef.DisplayNameText && ammo.AmmoDef.Const.MagazineDef.DisplayNameText != "Energy"; otherAmmo += $"\n{ammo.AmmoDef.Const.TerminalName} {(showName ? "(" + ammo.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : "")}"; } diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 050bceaa..08ce4a82 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -29,7 +29,7 @@ public static class Localization { "TerminalWeaponROFTitle", "Change Rate of Fire" }, { "TerminalWeaponROFTooltip", "Change rate of fire" }, { "TerminalOverloadTitle", "Overload Damage" }, - { "TerminalOverloadTooltip", "Overload damage" }, + { "TerminalOverloadTooltip", "For energy weapons- double damage output, quadruple energy and heat, will damage weapon if overheated" }, { "TerminalDetonationTitle", "Detonation time" }, { "TerminalDetonationTooltip", "Detonation time" }, { "TerminalGravityTitle", "Gravity Offset" }, @@ -180,7 +180,54 @@ public static class Localization { "TerminalObjectiveTooltip", "Select when to cease firing at a block" }, { "WeaponInfoHasTarget", "HasTarget" }, { "WeaponInfoTargetState", "TargetType" }, - + //new + { "WeaponInfoIdlePower", "Idle Power" }, + { "WeaponInfoMWLabel", "MW" }, + { "WeaponInfoStatsHeader", "--- Stats ---" }, + { "WeaponInfoDPSLabel", "DPS" }, + { "WeaponInfoMinRange", "Min Range" }, + { "WeaponInfoMaxRange", "Max Range" }, + { "WeaponInfoROF", "RoF" }, + { "WeaponInfoMeter", "m" }, + { "WeaponInfoTargetLabel", "Target" }, + { "WeaponInfoPerMin", "/min" }, + { "WeaponInfoProjectileLabel", "Projectile" }, + { "WeaponInfoNoneTarget", "None" }, + { "WeaponInfoAmmoType", "Ammo Types (Mag if different)" }, + { "WeaponInfoServerModdedLine1", "Weapon modified by server!" }, + { "WeaponInfoServerModdedLine2", "Report issues to server admins." }, + { "WeaponInfoDrawOverMax", "Draw/Max" }, + { "WeaponInfoPowerCharged", "Power: Charged" }, + { "WeaponInfoPowerChargedIn", "Power: Charged in" }, + { "WeaponInfoSeconds", "s" }, + { "WeaponInfoAmmoLabel", "Ammo" }, + { "WeaponInfoWaitingCharge", "Waiting on charge" }, + { "WeaponInfoLoadedIn", "Loaded in" }, + { "WeaponInfoLoaded", "Loaded" }, + { "WeaponInfoNoammo", "No Ammo" }, + { "WeaponInfoNoTarget", "No Target" }, + { "WeaponInfoHeatPerSecOverMax", "Heat per Sec/Max" }, + { "SubtypeAny", "Any" }, + { "SubtypeOffense", "Offense" }, + { "SubtypeUtility", "Utility" }, + { "SubtypePower", "Power" }, + { "SubtypeProduction", "Production" }, + { "SubtypeThrust", "Thrust" }, + { "SubtypeJumping", "Jumping" }, + { "SubtypeSteering", "Steering" }, + { "ShootAiShoot", "AiShoot" }, + { "ShootMouse", "MouseControl" }, + { "ShootKeyToggle", "KeyToggle" }, + { "ShootKey", "KeyFire" }, + { "ObjDefault", "Default" }, + { "ObjDisabled", "Disabled" }, + { "ObjDestroyed", "Destroyed" }, + { "ControlAuto", "Auto" }, + { "ControlManual", "Manual" }, + { "ControlPainter", "Painter" }, + { "MoveAny", "Any" }, + { "MoveMoving", "Ship" }, + { "MoveMoored", "Station" }, } }, { @@ -193,21 +240,21 @@ public static class Localization { "Decrease Weapon Damage", "Уменьшить Наносимый урон" }, { "Increase Weapon ROF", "Увеличить Скорострельность" }, { "Decrease Weapon ROF", "Уменьшить Скорострельность" }, - { "Overload Toggle On/Off", "Переключить Перегрузку Вкл/Выкл " }, - { "Overload On", "Переключить Перегрузку Вкл" }, - { "Overload Off", "Переключить Перегрузку Выкл" }, + { "Overload Toggle On/Off", "Переключить Усиление Вкл/Выкл" }, + { "Overload On", "Переключить Усиление Вкл" }, + { "Overload Off", "Переключить Усиление Выкл" }, { "TerminalSwitchOn", "Вкл" }, { "TerminalSwitchOff", "Выкл" }, { "TerminalReportTargetTitle", "Включить Наведение" }, - { "TerminalReportTargetTooltip", "Включить наводку турелей" }, + { "TerminalReportTargetTooltip", "Отображать на HUD наличие захваченной цели" }, { "TerminalWeaponROFTitle", "Изменить скорострельность" }, { "TerminalWeaponROFTooltip", "Изменить скорострельность" }, - { "TerminalOverloadTitle", "Получаемый урон от перегрузки" }, - { "TerminalOverloadTooltip", "Получаемый урон от перегрузки" }, + { "TerminalOverloadTitle", "Получаемый урон от усиления" }, + { "TerminalOverloadTooltip", "Включение усиления увеличит урон энергетического(!) оружия в 2 раза, но потребление энергии и перегрев возрастут в 4 раза, перегрев будет наносить 2% урона cfvjve орудию" }, { "TerminalDetonationTitle", "Время детонации" }, { "TerminalDetonationTooltip", "Время детонации" }, { "TerminalGravityTitle", "Коррекция гравитации" }, - { "TerminalGravityTooltip", "Фактор коррекции гравитации, более низкие значения уменьшают дальность, более высокие значения увеличивают её" }, + { "TerminalGravityTooltip", "Фактор коррекции гравитационного воздействия, более низкие значения уменьшают дальность, более высокие значения увеличивают её" }, { "TerminalStartCountTitle", "Запустить отсчет" }, { "TerminalStartCountTooltip", "Запустить отсчет" }, { "TerminalStopCountTitle", "Остановить отсчет" }, @@ -219,23 +266,27 @@ public static class Localization { "TerminalWeaponRangeTitle", "Радиус прицеливания" }, { "TerminalWeaponRangeTooltip", "Радиус прицеливания" }, { "TerminalNeutralsTitle", "Наводить на нейтральных" }, - { "TerminalNeutralsTooltip", "Стрелять по нейтральным" }, + { "TerminalNeutralsTooltip", "Открывать огонь по нейтральным целям" }, { "TerminalUnownedTitle", "Наводить на цели без владельца" }, - { "TerminalUnownedTooltip", "Стрелять по целям без владельца" }, + { "TerminalUnownedTooltip", "Открывать огонь по целям без владельца" }, + { "TerminalFriendlyTitle", "Наводить на союзников" }, + { "TerminalFriendlyTooltip", "Открывать огонь по союзным целям" }, { "TerminalBiologicalsTitle", "Наводить на живую силу" }, - { "TerminalBiologicalsTooltip", "Огонь по игрокам и NPC" }, + { "TerminalBiologicalsTooltip", "Открывать огонь по игрокам и NPC" }, { "TerminalProjectilesTitle", "Наводить на снаряды" }, { "TerminalProjectilesTooltip", "Огонь по приближающимся снарядам" }, + { "TerminalSupportingPDTitle", "Вспомогательная оборона" }, + { "TerminalSupportingPDTooltip", "Автоматически открывать огонь по снарядам, вне завистимости от наличия захваченной цели" }, { "TerminalMeteorsTitle", "Наводить на метеориты" }, { "TerminalMeteorsTooltip", "Огонь по приближающимся метеоритам" }, { "TerminalGridsTitle", "Наводить на большие/малые блоки" }, - { "TerminalGridsTooltip", "Огонь по большим/малым блокам" }, + { "TerminalGridsTooltip", "Открывать огонь по большим/малым блокам" }, { "TerminalFocusFireTitle", "Фокусировать огонь" }, - { "TerminalFocusFireTooltip", "Сосредоточивать весь огонь на указанной цели" }, + { "TerminalFocusFireTooltip", "Сосредоточивать весь огонь на захваченной цели" }, { "TerminalSubSystemsTitle", "Наводить на подсистемы" }, - { "TerminalSubSystemsTooltip", "Огонь по подсистемам цели" }, + { "TerminalSubSystemsTooltip", "Открывать огонь по выбранным подсистемам цели" }, { "TerminalRepelTitle", "Режим Подавления" }, - { "TerminalRepelTooltip", "Сосредоточить огонь на приближающихся малых объектах" }, + { "TerminalRepelTooltip", "Сосредоточить огонь на приближающихся малых объектах с низким уровнем угрозы (не на снарядах)" }, { "TerminalPickAmmoTitle", "Вид боеприпасов" }, { "TerminalPickAmmoTooltip", "Выберите тип боеприпасов для использования" }, { "TerminalPickSubSystemTitle", "Подсистема для наведения" }, @@ -243,34 +294,51 @@ public static class Localization { "TerminalTrackingModeTitle", "Отслеживать цели" }, { "TerminalTrackingModeTooltip", "Стрелять только по Двигающимся/Всем, кроме неподвижных/Неподвижным" }, { "TerminalControlModesTitle", "Управление прицелом" }, - { "TerminalControlModesTooltip", "Выберите режим управления прицелом для оружия." }, + { "TerminalControlModesTooltip", "Выберите режим управления прицелом для оружия" }, { "TerminalCameraChannelTitle", "Канал оружейной камеры" }, - { "TerminalCameraChannelTooltip", "На камере с таким же каналом, будет проецироваться прицел с опережением" }, + { "TerminalCameraChannelTooltip", "На камере с таким же каналом, будет проецироваться прицел с упреждением" }, { "TerminalBurstShotsTitle", "Размер очереди" }, { "TerminalBurstShotsTooltip", "Кол-во выстрелов в 1 очереди" }, - { "TerminalTargetGroupTitle", "Ведущая оружейная группа" }, - { "TerminalTargetGroupTooltip", "Установите идентичные цифры на камере м орудиях для проекции прицела" }, + { "TerminalBurstDelayTitle", "Задержка перед выстрелом" }, + { "TerminalBurstDelayTooltip", "Количество игровых тиков(60 в секунду), которое должно пройти перед выстрелом" }, + { "TerminalSequenceIdTitle", "Номер орудия в очереди" }, + { "TerminalSequenceIdTooltip", "Назначьте орудию уникальный номер, использующийся в стрельбе группой" }, + { "TerminalWeaponGroupIdTitle", "Номер оружейной группы" }, + { "TerminalWeaponGroupIdTooltip", "Назначьте орудию номер оружейной группы, использующийся в стрельбе группой" }, + { "TerminalShootModeTitle", "Режим огня" }, + { "TerminalShootModeTooltip", "Назначьте режим ведения огня: стрельба одиночными, стрельба очередями, нажатие мыши, переключение" }, + { "TerminalAiEnabledTitle", "Включить ИИ" }, + { "TerminalAiEnabledTooltip", "Разрешить автоматический захват, наведение и огонь по цели" }, + { "TerminalShareFireControlTitle", "Разделить управление" }, + { "TerminalShareFireControlTooltip", "Оружие с управлением прицеливанием ручной/точка будут получать цель от других орудий, если у последних включено разделение управления" }, + { "TerminalTargetGroupTitle", "Оружейная группа упреждения" }, + { "TerminalTargetGroupTooltip", "Установите индентичные цифры на камере и оружейной группе для проекции точки упреждения" }, { "TerminalDecoyPickSubSystemTitle", "Имитация подсистемы" }, { "TerminalDecoyPickSubSystemTooltip", "Выберите, какую подсистему будет имитировать эта приманка" }, { "TerminalCameraCameraChannelTitle", "Канал камеры" }, - { "TerminalCameraCameraChannelTooltip", "Установите индентичные цифры на ведущей группе орудий для проекции прицела" }, + { "TerminalCameraCameraChannelTooltip", "Установите индентичные цифры на камере и оружейной группе для проекции точки упреждения" }, { "TerminalDebugTitle", "Отладка" }, { "TerminalDebugTooltip", "Отладка Вкл/Выкл" }, + { "TerminalAdvancedTitle", "Продвинутые настройки" }, + { "TerminalAdvancedTooltip", "Включить показ продвинутых опций настройки оружия (может быть сложно для начинающий)" }, { "TerminalShootTitle", "Стрелять" }, { "TerminalShootTooltip", "Стрелять Вкл/Выкл" }, { "ActionStateOn", "Вкл" }, { "ActionStateOff", "Выкл" }, - { "ActionWCShootMode", "Огонь по нажатию" }, + { "ActionWCShootMode", "Переключить режим огня" }, + { "ActionWCMouseToggle", "Нажатие мыши" }, { "ActionFire", "Выстрелить один раз" }, + { "ForceReload", "Принудительная перезарядка" }, { "ActionShoot", "Стрелять Вкл/Выкл" }, { "ActionShoot_Off", "Стрелять Выкл" }, { "ActionShoot_On", "Стрелять Вкл" }, - { "ActionSubSystems", "Выбрать Подсистему" }, + { "ActionSubSystems", "Переключить подсистемы" }, { "ActionNextCameraChannel", "Следующий канал" }, { "ActionPreviousCameraChannel", "Предыдущий оружейный канал" }, { "ActionControlModes", "Управление прицелом" }, { "ActionNeutrals", "Наводить на Нейтральных Вкл/Выкл" }, { "ActionProjectiles", "Наводить на Снаряды Вкл/Выкл" }, + { "ActionSupportingPD", "Вспомогательная оборона Вкл/Выкл" }, { "ActionBiologicals", "Наводить на Живую силу Вкл/Выкл" }, { "ActionMeteors", "Наводить на Метеориты Вкл/Выкл" }, { "ActionGrids", "Наводить на Большие/малые блоки Вкл/Выкл" }, @@ -289,6 +357,11 @@ public static class Localization { "ActionWC_Decrease_CameraChannel", "Предыдущий канал оружейной камеры" }, { "ActionWC_Increase_LeadGroup", "Следующая ведущая оружейная группа" }, { "ActionWC_Decrease_LeadGroup", "Предыдущая ведущая оружейная группа" }, + { "ActionTargetLargeGrids", "Наводить на большие структуры" }, + { "ActionTargetSmallGrids", "Наводить на малые структуры" }, + { "ActionCycleObjective", "Переключить задачу" }, + { "ActionWCAiEnabled", "ИИ Вкл/Выкл" }, + { "ActionShareFireControl", "Разделить управление Вкл/Выкл" }, { "ActionMask", "Имитировать подсистему" }, { "SystemStatusFault", "[Повреждено]" }, { "SystemStatusOffline", "[Выкл]" }, @@ -308,7 +381,6 @@ public static class Localization { "WeaponInfoCurrentHeat", "Текущий нагрев" }, { "WeaponInfoCurrentDraw", "Макс. потребление" }, { "WeaponInfoRequiredPower", "Требуемое потребление" }, - { "WeaponInfoDividerLineWeapon", "==== Оружие ====" }, { "WeaponInfoName", "Название" }, { "WeaponInfoBurst", "Очередь" }, { "WeaponInfoDelay", "Задержка" }, @@ -317,8 +389,64 @@ public static class Localization { "WeaponTotalEffect", "Повреждать" }, { "WeaponTotalEffectAvgDps", "Средний урон в секунду" }, { "TerminalOverrideTitle", "Переопределие" }, - { "TerminalOverrideTooltip", "Разрешить стрельбу орудиям, иначе требующие цель, только для тестирования цели" }, + { "TerminalOverrideTooltip", "Разрешить стрельбу орудиям, иначе требующим цель, только для тестирования!" }, + { "TerminalAngularTitle", "Отслеживать угловое движение" }, + { "TerminalAngularTooltip", "Вносить корректировки для учёта углового движения цели" }, + { "TerminalLGTitle", "Большие Структуры" }, + { "TerminalLGTooltip", "Наводить на Большие Структуры" }, + { "TerminalSGTitle", "Малые Структуры" }, + { "TerminalSGTooltip", "Наводить на Малые Структуры" }, + { "TerminalObjectiveTitle", "Выбор задачи" }, + { "TerminalObjectiveTooltip", "Выберите условие, когда прекращается огонь по блоку" }, { "WeaponInfoHasTarget", "Имеет цель" }, + { "WeaponInfoTargetState", "Тип цели" }, + { "WeaponInfoIdlePower", "Пассивное потребление" }, + { "WeaponInfoMWLabel", "МВ" }, + { "WeaponInfoStatsHeader", "-- Параметры --" }, + { "WeaponInfoDPSLabel", "Урон в секунду" }, + { "WeaponInfoMinRange", "Минимальная дистанция" }, + { "WeaponInfoMaxRange", "Максимальная дистанция" }, + { "WeaponInfoROF", "Скорострельность" }, + { "WeaponInfoMeter", "м" }, + { "WeaponInfoTargetLabel", "Цель" }, + { "WeaponInfoPerMin", "/мин" }, + { "WeaponInfoProjectileLabel", "Боеприпас" }, + { "WeaponInfoNoneTarget", "Нет" }, + { "WeaponInfoAmmoType", "Типы боеприпасов(Боеприпас, если задан)" }, + { "WeaponInfoServerModdedLine1", "Оружие изменено сервером!" }, + { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, + { "WeaponInfoDrawOverMax", "Потребление/Максимальное" }, + { "WeaponInfoPowerCharged", "Заряд: накопленный" }, + { "WeaponInfoPowerChargedIn", "Заряд: осталось накопить" }, + { "WeaponInfoSeconds", "c" }, + { "WeaponInfoAmmoLabel", "Боеприпас" }, + { "WeaponInfoWaitingCharge", "Ожидание зарядки" }, + { "WeaponInfoLoadedIn", "Перезарядка еще" }, + { "WeaponInfoLoaded", "Заряжено" }, + { "WeaponInfoNoammo", "Нет боеприпасов" }, + { "WeaponInfoNoTarget", "Нет цели" }, + { "WeaponInfoHeatPerSecOverMax", "Нагрев в Сек/Макс" }, + { "SubtypeAny", "Любой" }, + { "SubtypeOffense", "Вооружение" }, + { "SubtypeUtility", "Другое" }, + { "SubtypePower", "Энергия" }, + { "SubtypeProduction", "Производство" }, + { "SubtypeThrust", "Двигатели" }, + { "SubtypeJumping", "Прыжковые двигатели" }, + { "SubtypeSteering", "Гироскопы" }, + { "ShootAiShoot", "ИИ-Выстрел" }, + { "ShootMouse", "Нажатие мыши" }, + { "ShootKeyToggle", "Переключение" }, + { "ShootKey", "Нажатие" }, + { "ObjDefault", "Стандарт" }, + { "ObjDisabled", "Отключено" }, + { "ObjDestroyed", "Уничтожено" }, + { "ControlAuto", "Авто" }, + { "ControlManual", "Ручной" }, + { "ControlPainter", "Точка" }, + { "MoveAny", "Любой" }, + { "MoveMoving", "Корабль" }, + { "MoveMoored", "Станция" }, } }, { From 71fc9ce58fd1269170c75fdba3d91c75ef3cfa5f Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:21:28 +0300 Subject: [PATCH 08/77] Update Localization.cs Fixed: TerminalReportTargetTitle Ru loc --- Data/Scripts/CoreSystems/Support/Localization.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 08ce4a82..4e3b2375 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Sandbox.ModAPI; using VRage; @@ -245,7 +245,7 @@ public static class Localization { "Overload Off", "Переключить Усиление Выкл" }, { "TerminalSwitchOn", "Вкл" }, { "TerminalSwitchOff", "Выкл" }, - { "TerminalReportTargetTitle", "Включить Наведение" }, + { "TerminalReportTargetTitle", "Отображать состояние захвата" }, { "TerminalReportTargetTooltip", "Отображать на HUD наличие захваченной цели" }, { "TerminalWeaponROFTitle", "Изменить скорострельность" }, { "TerminalWeaponROFTooltip", "Изменить скорострельность" }, @@ -412,7 +412,7 @@ public static class Localization { "WeaponInfoPerMin", "/мин" }, { "WeaponInfoProjectileLabel", "Боеприпас" }, { "WeaponInfoNoneTarget", "Нет" }, - { "WeaponInfoAmmoType", "Типы боеприпасов(Боеприпас, если задан)" }, + { "WeaponInfoAmmoType", "Типы боеприпасов(Магазин, если задан)" }, { "WeaponInfoServerModdedLine1", "Оружие изменено сервером!" }, { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, { "WeaponInfoDrawOverMax", "Потребление/Максимальное" }, @@ -623,4 +623,4 @@ internal static string GetTextWithoutFallback(string text) return I18NDictionary.TryGetValue(text, out value) ? value : text; } } -} \ No newline at end of file +} From 8b0bee0b078e74e1f771178d4deacc269468fe99 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:31:37 -0600 Subject: [PATCH 09/77] Localization bug pass an movemode enum catch on load --- .../CoreSystems/EntityComp/Controls/Control/ControlActions.cs | 2 +- Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs index a655ac6f..403dd86a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs @@ -65,7 +65,7 @@ internal static void TerminalActionMovementModeControl(IMyTerminalBlock blk) return; var numValue = (int)comp.Data.Repo.Values.Set.Overrides.MoveMode; - var value = numValue + 1 <= 3 ? numValue + 1 : 0; + var value = numValue + 1 <= 2 ? numValue + 1 : 0; ControlSys.ControlComponent.RequestSetValue(comp, "MovementModes", value, Session.I.PlayerId); } diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 86149001..9e07443c 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -260,7 +260,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str var displayName = showName ? w.ActiveAmmoDef.AmmoDef.Const.TerminalName + " (" + w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : w.ActiveAmmoDef.AmmoDef.Const.TerminalName; stringBuilder.Append($"\n\n" + w.System.PartName + shots + - $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoadedIn") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + w.Target.HasTarget : string.Empty)}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 69b10782..199e89a7 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -43,6 +43,10 @@ internal void Load() if (Session.I.IsServer) Repo.Values.Targets = new ProtoWeaponTransferTarget[collection.Count]; + //Decrement from prev max move mode to account for removal of an enum option and clarification to Any/Ship/Station + if ((int)Repo.Values.Set.Overrides.MoveMode == 3) + Repo.Values.Set.Overrides.MoveMode = (ProtoWeaponOverrides.MoveModes)2; + for (int i = 0; i < collection.Count; i++) { var w = collection[i]; From a17803b8adf760b2e1fc370acbd44a67fc5b8d79 Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:54:09 +0300 Subject: [PATCH 10/77] Update Localization.cs --- .../CoreSystems/Support/Localization.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 08ce4a82..ea394938 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Sandbox.ModAPI; using VRage; @@ -245,11 +245,11 @@ public static class Localization { "Overload Off", "Переключить Усиление Выкл" }, { "TerminalSwitchOn", "Вкл" }, { "TerminalSwitchOff", "Выкл" }, - { "TerminalReportTargetTitle", "Включить Наведение" }, + { "TerminalReportTargetTitle", "Отображать состояние захвата" }, { "TerminalReportTargetTooltip", "Отображать на HUD наличие захваченной цели" }, { "TerminalWeaponROFTitle", "Изменить скорострельность" }, { "TerminalWeaponROFTooltip", "Изменить скорострельность" }, - { "TerminalOverloadTitle", "Получаемый урон от усиления" }, + { "TerminalOverloadTitle", "Усиление" }, { "TerminalOverloadTooltip", "Включение усиления увеличит урон энергетического(!) оружия в 2 раза, но потребление энергии и перегрев возрастут в 4 раза, перегрев будет наносить 2% урона cfvjve орудию" }, { "TerminalDetonationTitle", "Время детонации" }, { "TerminalDetonationTooltip", "Время детонации" }, @@ -412,16 +412,16 @@ public static class Localization { "WeaponInfoPerMin", "/мин" }, { "WeaponInfoProjectileLabel", "Боеприпас" }, { "WeaponInfoNoneTarget", "Нет" }, - { "WeaponInfoAmmoType", "Типы боеприпасов(Боеприпас, если задан)" }, + { "WeaponInfoAmmoType", "Типы боеприпасов(Магазин, если задан)" }, { "WeaponInfoServerModdedLine1", "Оружие изменено сервером!" }, { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, - { "WeaponInfoDrawOverMax", "Потребление/Максимальное" }, - { "WeaponInfoPowerCharged", "Заряд: накопленный" }, - { "WeaponInfoPowerChargedIn", "Заряд: осталось накопить" }, + { "WeaponInfoDrawOverMax", "Потребление: Текущее/Максимальное" }, + { "WeaponInfoPowerCharged", "Заряд: Готово" }, + { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё" }, { "WeaponInfoSeconds", "c" }, { "WeaponInfoAmmoLabel", "Боеприпас" }, { "WeaponInfoWaitingCharge", "Ожидание зарядки" }, - { "WeaponInfoLoadedIn", "Перезарядка еще" }, + { "WeaponInfoLoadedIn", "Перезарядка ещё " }, { "WeaponInfoLoaded", "Заряжено" }, { "WeaponInfoNoammo", "Нет боеприпасов" }, { "WeaponInfoNoTarget", "Нет цели" }, @@ -623,4 +623,4 @@ internal static string GetTextWithoutFallback(string text) return I18NDictionary.TryGetValue(text, out value) ? value : text; } } -} \ No newline at end of file +} From 10d4bdf7fe28753a7be2c790a9893279570253a5 Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:55:23 +0300 Subject: [PATCH 11/77] Update Localization.cs --- Data/Scripts/CoreSystems/Support/Localization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index ea394938..edc11846 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -417,7 +417,7 @@ public static class Localization { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, { "WeaponInfoDrawOverMax", "Потребление: Текущее/Максимальное" }, { "WeaponInfoPowerCharged", "Заряд: Готово" }, - { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё" }, + { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё " }, { "WeaponInfoSeconds", "c" }, { "WeaponInfoAmmoLabel", "Боеприпас" }, { "WeaponInfoWaitingCharge", "Ожидание зарядки" }, From e14fe6b1f5c50c16b53852feb8588a9c0b221b5b Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:58:05 +0300 Subject: [PATCH 12/77] Update Localization.cs --- Data/Scripts/CoreSystems/Support/Localization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index edc11846..b1928a38 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -320,7 +320,7 @@ public static class Localization { "TerminalDebugTitle", "Отладка" }, { "TerminalDebugTooltip", "Отладка Вкл/Выкл" }, { "TerminalAdvancedTitle", "Продвинутые настройки" }, - { "TerminalAdvancedTooltip", "Включить показ продвинутых опций настройки оружия (может быть сложно для начинающий)" }, + { "TerminalAdvancedTooltip", "Включить показ продвинутых опций настройки оружия (может быть сложно для начинающих)" }, { "TerminalShootTitle", "Стрелять" }, { "TerminalShootTooltip", "Стрелять Вкл/Выкл" }, { "ActionStateOn", "Вкл" }, From 6fba72401b826c1d3f594b52611eec9cfe24a1e3 Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:01:52 +0300 Subject: [PATCH 13/77] Update Localization.cs --- Data/Scripts/CoreSystems/Support/Localization.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index b1928a38..60e717fc 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -417,7 +417,7 @@ public static class Localization { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, { "WeaponInfoDrawOverMax", "Потребление: Текущее/Максимальное" }, { "WeaponInfoPowerCharged", "Заряд: Готово" }, - { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё " }, + { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё" }, { "WeaponInfoSeconds", "c" }, { "WeaponInfoAmmoLabel", "Боеприпас" }, { "WeaponInfoWaitingCharge", "Ожидание зарядки" }, @@ -425,7 +425,7 @@ public static class Localization { "WeaponInfoLoaded", "Заряжено" }, { "WeaponInfoNoammo", "Нет боеприпасов" }, { "WeaponInfoNoTarget", "Нет цели" }, - { "WeaponInfoHeatPerSecOverMax", "Нагрев в Сек/Макс" }, + { "WeaponInfoHeatPerSecOverMax", "Нагрев: в Сек/Макс" }, { "SubtypeAny", "Любой" }, { "SubtypeOffense", "Вооружение" }, { "SubtypeUtility", "Другое" }, From 5169fc423c6e3cdedf4aa1be85aeddc23889264c Mon Sep 17 00:00:00 2001 From: Senpai-ZER0 <82382920+Senpai-ZER0@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:02:43 +0300 Subject: [PATCH 14/77] Update Localization.cs --- Data/Scripts/CoreSystems/Support/Localization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 60e717fc..e4d2a5e3 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -417,7 +417,7 @@ public static class Localization { "WeaponInfoServerModdedLine2", "Проинформируйте администрацию о проблемах" }, { "WeaponInfoDrawOverMax", "Потребление: Текущее/Максимальное" }, { "WeaponInfoPowerCharged", "Заряд: Готово" }, - { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё" }, + { "WeaponInfoPowerChargedIn", "Заряд: Зарядка ещё " }, { "WeaponInfoSeconds", "c" }, { "WeaponInfoAmmoLabel", "Боеприпас" }, { "WeaponInfoWaitingCharge", "Ожидание зарядки" }, From f788370ac5c19cb058a7fa3e325518a1eb7344b7 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:23:18 -0600 Subject: [PATCH 15/77] Localization additional --- .../Controls/Control/ControlActions.cs | 26 ++++++++----------- .../Controls/CreateCustomActions.cs | 2 +- .../EntityComp/Controls/TerminalHelpers.cs | 4 +-- .../Controls/Weapon/WeaponActions.cs | 21 +++++++-------- .../CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../CoreSystems/Support/Localization.cs | 16 ++++++++++-- 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs index 403dd86a..39c47a0e 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs @@ -3,8 +3,6 @@ using CoreSystems.Platform; using CoreSystems.Support; using Sandbox.ModAPI; -using VRageMath; -using static CoreSystems.Support.CoreComponent.Trigger; namespace CoreSystems.Control { @@ -47,16 +45,6 @@ internal static void TerminActionCycleShootModeControl(IMyTerminalBlock blk) ControlSys.ControlComponent.RequestSetValue(comp, "ShootMode", value, Session.I.PlayerId); } - internal static void ShootModeWriterControl(IMyTerminalBlock blk, StringBuilder sb) - { - var comp = blk.Components.Get() as ControlSys.ControlComponent; - if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - - var altAiControlName = !comp.HasAim && comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.AiShoot ? InActive : comp.Data.Repo.Values.Set.Overrides.ShootMode.ToString(); - sb.Append(altAiControlName); - } - - internal static void TerminalActionMovementModeControl(IMyTerminalBlock blk) { @@ -301,6 +289,14 @@ internal static void TerminalActionToggleSmallGridControl(IMyTerminalBlock blk) #endregion #region Writters + internal static void ShootModeWriterControl(IMyTerminalBlock blk, StringBuilder sb) + { + var comp = blk.Components.Get() as ControlSys.ControlComponent; + if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; + + var altAiControlName = !comp.HasAim && comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.AiShoot ? Localization.GetText("ControlsInactive") : Localization.GetText("Shoot" + comp.Data.Repo.Values.Set.Overrides.ShootMode); + sb.Append(altAiControlName); + } internal static void ShareFireControlWriterControl(IMyTerminalBlock blk, StringBuilder sb) { @@ -439,7 +435,7 @@ internal static void ControlStateWriterControl(IMyTerminalBlock blk, StringBuild { var comp = blk.Components.Get() as ControlSys.ControlComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.Control); + sb.Append(Localization.GetText("Control" + comp.Data.Repo.Values.Set.Overrides.Control)); } internal static void MovementModeWriterControl(IMyTerminalBlock blk, StringBuilder sb) @@ -447,7 +443,7 @@ internal static void MovementModeWriterControl(IMyTerminalBlock blk, StringBuild var comp = blk.Components.Get() as ControlSys.ControlComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.MoveMode); + sb.Append(Localization.GetText("Move" + comp.Data.Repo.Values.Set.Overrides.MoveMode)); } internal static void SubSystemWriterControl(IMyTerminalBlock blk, StringBuilder sb) @@ -455,7 +451,7 @@ internal static void SubSystemWriterControl(IMyTerminalBlock blk, StringBuilder var comp = blk.Components.Get() as ControlSys.ControlComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.SubSystem); + sb.Append(Localization.GetText("Subtype" + comp.Data.Repo.Values.Set.Overrides.SubSystem)); } internal static void RepelWriterControl(IMyTerminalBlock blk, StringBuilder sb) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index f1f0bf60..2c63f210 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -401,7 +401,7 @@ public static void CreateAngularTracking(Session session) { var action = MyAPIGateway.TerminalControls.CreateAction("AngularTracking"); action.Icon = @"Textures\GUI\Icons\Actions\Toggle.dds"; - action.Name = new StringBuilder("Predict Targets Angular Motion"); + action.Name = new StringBuilder(Localization.GetText("TrackAngular")); action.Action = CustomActions.TerminalActionToggleAngularTracking; action.Writer = CustomActions.AngularTrackingWriter; action.Enabled = TerminalHelpers.HasTracking; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 9f59dbbb..08a926cf 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -176,9 +176,9 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - AddOnOffSwitchNoAction(session, "LargeGrid", "Large Grid", "Target large grids", BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", "Small Grid", "Target small grids", BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index bd7c64d6..55e5daf4 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -506,9 +506,9 @@ internal static void ArmWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; if (comp.Data.Repo.Values.Set.Overrides.Armed) - sb.Append("Armed"); + sb.Append(Localization.GetText("BombArmed")); else - sb.Append("Disarmed"); + sb.Append(Localization.GetText("BombDisarmed")); } internal static void ShootStateWriter(IMyTerminalBlock blk, StringBuilder sb) @@ -667,7 +667,7 @@ internal static void ControlStateWriter(IMyTerminalBlock blk, StringBuilder sb) { var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.Control); + sb.Append(Localization.GetText("Control" + comp.Data.Repo.Values.Set.Overrides.Control)); } internal static void MovementModeWriter(IMyTerminalBlock blk, StringBuilder sb) @@ -675,7 +675,7 @@ internal static void MovementModeWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.MoveMode); + sb.Append(Localization.GetText("Move" + comp.Data.Repo.Values.Set.Overrides.MoveMode)); } internal static void SubSystemWriter(IMyTerminalBlock blk, StringBuilder sb) @@ -683,7 +683,7 @@ internal static void SubSystemWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.SubSystem); + sb.Append(Localization.GetText("Subtype" + comp.Data.Repo.Values.Set.Overrides.SubSystem)); } internal static void ShootModeWriter(IMyTerminalBlock blk, StringBuilder sb) @@ -691,7 +691,7 @@ internal static void ShootModeWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - var altAiControlName = !comp.HasAim && comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.AiShoot ? InActive : comp.Data.Repo.Values.Set.Overrides.ShootMode.ToString(); + var altAiControlName = !comp.HasAim && comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.AiShoot ? Localization.GetText("ControlsInactive") : Localization.GetText("Shoot" + comp.Data.Repo.Values.Set.Overrides.ShootMode); sb.Append(altAiControlName); } @@ -700,16 +700,15 @@ internal static void ObjectiveModeWriter(IMyTerminalBlock blk, StringBuilder sb) var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.ObjectiveMode); + sb.Append(Localization.GetText("Obj" + comp.Data.Repo.Values.Set.Overrides.ObjectiveMode)); } - private const string InActive = "Inactive"; internal static void MouseToggleWriter(IMyTerminalBlock blk, StringBuilder sb) { var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - var message = comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.MouseControl ? comp.Data.Repo.Values.Set.Overrides.ShootMode.ToString() : InActive; + var message = comp.Data.Repo.Values.Set.Overrides.ShootMode == Weapon.ShootManager.ShootModes.MouseControl ? Localization.GetText("ShootMouse") : Localization.GetText("ControlsInactive"); sb.Append(message); } @@ -719,7 +718,7 @@ internal static void DecoyWriter(IMyTerminalBlock blk, StringBuilder sb) long value; if (long.TryParse(blk.CustomData, out value)) { - sb.Append(((WeaponDefinition.TargetingDef.BlockTypes)value).ToString()); + sb.Append(Localization.GetText("Subtype" + (WeaponDefinition.TargetingDef.BlockTypes)value)); } } @@ -728,7 +727,7 @@ internal static void CameraWriter(IMyTerminalBlock blk, StringBuilder sb) long value; if (long.TryParse(blk.CustomData, out value)) { - var group = $"Camera Channel {value}"; + var group = $"{Localization.GetText("TerminalCameraCameraChannelTitle")} {value}"; sb.Append(group); } } diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 9e07443c..e1438748 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -261,7 +261,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str stringBuilder.Append($"\n\n" + w.System.PartName + shots + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + w.Target.HasTarget : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); } diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index e4d2a5e3..d6b45c55 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -180,7 +180,6 @@ public static class Localization { "TerminalObjectiveTooltip", "Select when to cease firing at a block" }, { "WeaponInfoHasTarget", "HasTarget" }, { "WeaponInfoTargetState", "TargetType" }, - //new { "WeaponInfoIdlePower", "Idle Power" }, { "WeaponInfoMWLabel", "MW" }, { "WeaponInfoStatsHeader", "--- Stats ---" }, @@ -228,6 +227,13 @@ public static class Localization { "MoveAny", "Any" }, { "MoveMoving", "Ship" }, { "MoveMoored", "Station" }, + { "WeaponInfoTrue", "True" }, + { "WeaponInfoFalse", "False" }, + { "ControlsInactive", "Inactive" }, + { "BombArmed", "Armed" }, + { "BombDisarmed", "Disarmed" }, + { "TrackAngular", "Predict Targets Angular Motion"} + } }, { @@ -388,7 +394,7 @@ public static class Localization { "WeaponInfoLoS", "Прямая видимость" }, { "WeaponTotalEffect", "Повреждать" }, { "WeaponTotalEffectAvgDps", "Средний урон в секунду" }, - { "TerminalOverrideTitle", "Переопределие" }, + { "TerminalOverrideTitle", "Усиление" }, { "TerminalOverrideTooltip", "Разрешить стрельбу орудиям, иначе требующим цель, только для тестирования!" }, { "TerminalAngularTitle", "Отслеживать угловое движение" }, { "TerminalAngularTooltip", "Вносить корректировки для учёта углового движения цели" }, @@ -447,6 +453,12 @@ public static class Localization { "MoveAny", "Любой" }, { "MoveMoving", "Корабль" }, { "MoveMoored", "Станция" }, + { "WeaponTargTrue", "Да" },//Contextual to targets + { "WeaponTargFalse", "Нет" },//Contextual to targets, use Правда/Ложь for logic + { "ControlsInactive", "Неактивно" }, + { "BombArmed", "Взведено" }, + { "BombDisarmed", "Предохранитель" }, + { "TrackAngular", "Отслеживать угловое движение"} } }, { From 9b892f8b40197534c0e422b6cd4244482a06f4a1 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 16 Feb 2025 22:34:23 -0600 Subject: [PATCH 16/77] Mobility enhancements --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 3 ++- .../SerializedConfigs/Weapon/ProtoWeapon.cs | 2 ++ .../EntityComp/Controls/Control/ControlActions.cs | 2 +- .../EntityComp/Controls/Weapon/WeaponActions.cs | 2 +- .../CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs | 5 ++++- .../CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs | 4 ---- Data/Scripts/CoreSystems/Support/Localization.cs | 10 ++++++---- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index cdd487ab..9a7b43f0 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -133,6 +133,7 @@ private static bool AcquireTopMostEntity(Weapon w, ProtoWeaponOverrides overRide var movingMode = moveMode == ProtoWeaponOverrides.MoveModes.Moving; var fireOnStation = moveMode == ProtoWeaponOverrides.MoveModes.Any || moveMode == ProtoWeaponOverrides.MoveModes.Moored; var stationOnly = moveMode == ProtoWeaponOverrides.MoveModes.Moored; + var shipOnly = moveMode == ProtoWeaponOverrides.MoveModes.ShipAny; BoundingSphereD waterSphere = new BoundingSphereD(Vector3D.Zero, 1f); WaterData water = null; if (session.WaterApiLoaded && !ammoDef.IgnoreWater && ai.InPlanetGravity && ai.MyPlanet != null && session.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) @@ -223,7 +224,7 @@ private static bool AcquireTopMostEntity(Weapon w, ProtoWeaponOverrides overRide if (w.System.ScanTrackOnly && !ValidScanEntity(w, info.EntInfo, info.Target, true)) continue; - if (movingMode && info.VelLenSqr < 1 || !fireOnStation && info.IsStatic || stationOnly && !info.IsStatic) + if (movingMode && info.VelLenSqr < 1 || !fireOnStation && info.IsStatic || stationOnly && !info.IsStatic || shipOnly && info.IsStatic) continue; var character = info.Target as IMyCharacter; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs index 4dc35002..5bd3015f 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Weapon/ProtoWeapon.cs @@ -595,7 +595,9 @@ public enum MoveModes { Any, Moving, + Mobile, Moored, + ShipAny } public enum ControlModes diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs index 39c47a0e..5b1c3325 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs @@ -53,7 +53,7 @@ internal static void TerminalActionMovementModeControl(IMyTerminalBlock blk) return; var numValue = (int)comp.Data.Repo.Values.Set.Overrides.MoveMode; - var value = numValue + 1 <= 2 ? numValue + 1 : 0; + var value = numValue + 1 <= 4 ? numValue + 1 : 0; ControlSys.ControlComponent.RequestSetValue(comp, "MovementModes", value, Session.I.PlayerId); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index 55e5daf4..eb2e113a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -150,7 +150,7 @@ internal static void TerminalActionMovementMode(IMyTerminalBlock blk) return; var numValue = (int)comp.Data.Repo.Values.Set.Overrides.MoveMode; - var value = numValue + 1 <= 2 ? numValue + 1 : 0; + var value = numValue + 1 <= 4 ? numValue + 1 : 0; Weapon.WeaponComponent.RequestSetValue(comp, "MovementModes", value, Session.I.PlayerId); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs index 4dec0d3a..265a7227 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs @@ -683,7 +683,10 @@ internal static void ListMovementModes(List moveL { new MyTerminalControlComboBoxItem { Key = 0, Value = MyStringId.GetOrCompute(Localization.GetText("MoveAny")) }, new MyTerminalControlComboBoxItem { Key = 1, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMoving")) }, - new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMoored")) }, + new MyTerminalControlComboBoxItem { Key = 2, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMobile")) }, + new MyTerminalControlComboBoxItem { Key = 3, Value = MyStringId.GetOrCompute(Localization.GetText("MoveMoored")) }, + new MyTerminalControlComboBoxItem { Key = 4, Value = MyStringId.GetOrCompute(Localization.GetText("MoveShipAny")) }, + }; internal static long GetObjectiveMode(IMyTerminalBlock block) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 199e89a7..69b10782 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -43,10 +43,6 @@ internal void Load() if (Session.I.IsServer) Repo.Values.Targets = new ProtoWeaponTransferTarget[collection.Count]; - //Decrement from prev max move mode to account for removal of an enum option and clarification to Any/Ship/Station - if ((int)Repo.Values.Set.Overrides.MoveMode == 3) - Repo.Values.Set.Overrides.MoveMode = (ProtoWeaponOverrides.MoveModes)2; - for (int i = 0; i < collection.Count; i++) { var w = collection[i]; diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index d6b45c55..ed919b62 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -225,15 +225,17 @@ public static class Localization { "ControlManual", "Manual" }, { "ControlPainter", "Painter" }, { "MoveAny", "Any" }, - { "MoveMoving", "Ship" }, - { "MoveMoored", "Station" }, + { "MoveMoving", "Moving ship" }, + { "MoveMoored", "Stations" }, { "WeaponInfoTrue", "True" }, { "WeaponInfoFalse", "False" }, { "ControlsInactive", "Inactive" }, { "BombArmed", "Armed" }, { "BombDisarmed", "Disarmed" }, - { "TrackAngular", "Predict Targets Angular Motion"} - + { "TrackAngular", "Predict Targets Angular Motion"}, + //new + { "MoveMobile", "Immobile ship" }, + { "MoveShipAny", "Ships" }, } }, { From 80ddd6f47aa844d2d1518a3532b0e3edb6e71068 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 24 Feb 2025 18:07:02 -0600 Subject: [PATCH 17/77] Moar localization --- Data/Scripts/CoreSystems/Support/Localization.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index ed919b62..02b17452 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -453,14 +453,16 @@ public static class Localization { "ControlManual", "Ручной" }, { "ControlPainter", "Точка" }, { "MoveAny", "Любой" }, - { "MoveMoving", "Корабль" }, - { "MoveMoored", "Станция" }, + { "MoveMoving", "Движущиеся корабли" }, + { "MoveMoored", "Станции" }, { "WeaponTargTrue", "Да" },//Contextual to targets { "WeaponTargFalse", "Нет" },//Contextual to targets, use Правда/Ложь for logic { "ControlsInactive", "Неактивно" }, { "BombArmed", "Взведено" }, { "BombDisarmed", "Предохранитель" }, - { "TrackAngular", "Отслеживать угловое движение"} + { "TrackAngular", "Отслеживать угловое движение"}, + { "MoveMobile", "Неподвижный корабль" }, + { "MoveShipAny", "Любой корабль" }, } }, { From fee134d400267501c5141321511190b93179c169 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:50:38 -0500 Subject: [PATCH 18/77] Update - Added option to disable HUD painter - Made max burst delay time more dynamic --- .../CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs | 2 ++ .../Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs | 3 ++- .../CoreSystems/EntityComp/Parts/Control/ControlData.cs | 2 +- .../Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs | 2 +- Data/Scripts/CoreSystems/Session/SessionRun.cs | 2 +- Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs index 58fa3e15..b7bbac47 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/CoreSettings.cs @@ -211,6 +211,8 @@ public class ArmorOverride [ProtoMember(22)] public Overrides DefinitionOverrides; [ProtoMember(23)] public float LargeGridDamageMultiplier = 1; [ProtoMember(24)] public float SmallGridDamageMultiplier = 1; + [ProtoMember(25)] public bool ProhibitHUDPainter = false; + } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs index 265a7227..d58408c3 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs @@ -1029,7 +1029,8 @@ internal static float GetMaxBurstDelay(IMyTerminalBlock block) var comp = block?.Components?.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return 0; - return 300; + var priReload = comp.Collection[0].System.WConst.ReloadTime * 3; + return priReload < 600 ? 600 : priReload; } internal static float GetMinSequenceId(IMyTerminalBlock block) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs index 7f3bc9f9..9d1ced4b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs @@ -91,7 +91,7 @@ internal bool ValidFakeTargetInfo(long playerId, out Ai.FakeTarget.FakeWorldTarg if (Session.I.PlayerDummyTargets.TryGetValue(playerId, out fakeTargets)) { var validManual = Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Manual && Comp.Data.Repo.Values.State.TrackingReticle && fakeTargets.ManualTarget.FakeInfo.WorldPosition != Vector3D.Zero; - var validPainter = Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Painter && fakeTargets.PaintedTarget.LocalPosition != Vector3D.Zero; + var validPainter = !Session.I.Settings.Enforcement.ProhibitHUDPainter && Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Painter && fakeTargets.PaintedTarget.LocalPosition != Vector3D.Zero; var fakeTarget = validPainter && preferPainted ? fakeTargets.PaintedTarget : validManual ? fakeTargets.ManualTarget : null; if (fakeTarget == null) return false; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs index b8263607..c228b11b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs @@ -53,7 +53,7 @@ internal bool ValidFakeTargetInfo(long playerId, out Ai.FakeTarget.FakeWorldTarg if (Session.I.PlayerDummyTargets.TryGetValue(playerId, out fakeTargets)) { var validManual = Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Manual && Comp.Data.Repo.Values.State.TrackingReticle && fakeTargets.ManualTarget.FakeInfo.WorldPosition != Vector3D.Zero; - var validPainter = Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Painter && fakeTargets.PaintedTarget.LocalPosition != Vector3D.Zero; + var validPainter = !Session.I.Settings.Enforcement.ProhibitHUDPainter && Comp.Data.Repo.Values.Set.Overrides.Control == ProtoWeaponOverrides.ControlModes.Painter && fakeTargets.PaintedTarget.LocalPosition != Vector3D.Zero; var fakeTarget = validPainter && preferPainted ? fakeTargets.PaintedTarget : validManual ? fakeTargets.ManualTarget : null; if (fakeTarget == null) { diff --git a/Data/Scripts/CoreSystems/Session/SessionRun.cs b/Data/Scripts/CoreSystems/Session/SessionRun.cs index bddf2076..f2b85654 100644 --- a/Data/Scripts/CoreSystems/Session/SessionRun.cs +++ b/Data/Scripts/CoreSystems/Session/SessionRun.cs @@ -243,7 +243,7 @@ public override void UpdateAfterSimulation() if (WaterApiLoaded && (Tick3600 || WaterMap.IsEmpty)) UpdateWaters(); - if (HandlesInput && Tick30) + if (!Settings.Enforcement.ProhibitHUDPainter && HandlesInput && Tick30) UpdatePlayerPainters(); if (DebugLos && Tick1800) { diff --git a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs index 9cdaedb0..4ea08c2e 100644 --- a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs +++ b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs @@ -336,7 +336,7 @@ internal bool ActivateDroneNotice() internal bool ActivateMarks() { var s = Session.I; - var mark = s.TrackingAi.AiType != Ai.AiTypes.Phantom && s.ActiveMarks.Count > 0; + var mark = !s.Settings.Enforcement.ProhibitHUDPainter && s.TrackingAi.AiType != Ai.AiTypes.Phantom && s.ActiveMarks.Count > 0; var showAlert = mark && !(s.HudHandlers.Count > 0 && s.HudUi.RestrictHudHandlers(s.TrackingAi, s.PlayerId, Hud.Hud.HudMode.PainterMarks)); return showAlert; } From fb3897db47d36376606e6e3db2010f74e4fb348c Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 16 Mar 2025 21:52:43 -0500 Subject: [PATCH 19/77] Steam offline mode fix --- Data/Scripts/CoreSystems/Session/SessionSupport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index fe570a6d..f2561bf3 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -1200,7 +1200,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.Contains("WCVanilla") || mod.FriendlyName.Contains("WCVanilla")) + else if (mod.GetPath().Contains("AppData\\Roaming\\SpaceEngineers\\Mods\\VanillaReplacement") || mod.Name.Contains("WCVanilla") || mod.FriendlyName != null && mod.FriendlyName.Contains("WCVanilla")) ReplaceVanilla = true; else if (mod.PublishedFileId == 2189703321 || mod.PublishedFileId == 2496225055 || mod.PublishedFileId == 2726343161 || mod.PublishedFileId == 2734980390) { From 5c8b9634895c24033211dc9843df5ee6496919a4 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 16 Mar 2025 23:11:51 -0500 Subject: [PATCH 20/77] Update SessionRun.cs --- Data/Scripts/CoreSystems/Session/SessionRun.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionRun.cs b/Data/Scripts/CoreSystems/Session/SessionRun.cs index f2b85654..0fc13f2e 100644 --- a/Data/Scripts/CoreSystems/Session/SessionRun.cs +++ b/Data/Scripts/CoreSystems/Session/SessionRun.cs @@ -243,7 +243,7 @@ public override void UpdateAfterSimulation() if (WaterApiLoaded && (Tick3600 || WaterMap.IsEmpty)) UpdateWaters(); - if (!Settings.Enforcement.ProhibitHUDPainter && HandlesInput && Tick30) + if (HandlesInput && Tick30 && (Settings?.Enforcement?.ProhibitHUDPainter != null ? !Settings.Enforcement.ProhibitHUDPainter : true )) UpdatePlayerPainters(); if (DebugLos && Tick1800) { From 635308df25254d4c89878b247d53e4cc7ebd8fac Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 17 Mar 2025 00:36:48 -0500 Subject: [PATCH 21/77] Offline suppress fix and localization missing EN --- Data/Scripts/CoreSystems/Session/SessionSupport.cs | 2 +- Data/Scripts/CoreSystems/Support/Localization.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index f2561bf3..dd0abd58 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -1210,7 +1210,7 @@ private void ModChecker() WaterMod = true; } - SuppressWc = !SUtils.ModActivate(ModContext, Session); + SuppressWc = LocalVersion ? false : !SUtils.ModActivate(ModContext, Session); if (!SuppressWc && !ReplaceVanilla) { diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 02b17452..6b54b88e 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -236,6 +236,8 @@ public static class Localization //new { "MoveMobile", "Immobile ship" }, { "MoveShipAny", "Ships" }, + { "WeaponTargTrue", "True" }, + { "WeaponTargFalse", "False" }, } }, { From 4d36b20746dfe392939f50f1cf3c84d7a66f9370 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 17 Mar 2025 02:25:42 -0500 Subject: [PATCH 22/77] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 8c9236b1..7afa1e16 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,3 @@ Modder documentation is currently in progress, and viewable on the [Wiki](https: Thanks to Ash for server override enhancements and quality of life work on v2. Thanks to CriegwareFare for his work on the block animations, heat mechanic and assisting with weapon reloading hud! Thanks to [NukeGuard](https://github.com/nukeguard) for the awesome GUI/HUD design and textures. Thanks to Derek for general bug fixing and weapon area placement limit feature. Special thanks to the math wizard himself [WhipLash141](https://github.com/Whiplash141) for all of his help with the mind bending math required to pull off the advanced target tracking and smart projectile guidance systems is this mod. - -# Patreon - -## If you appreciate WeaponCore please consider donating: - -[![](https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png)](https://www.patreon.com/user?u=14228932) From 0bdaf5897b8cc51c4ec243a5d9dac44886a94a19 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 18 Mar 2025 08:55:36 -0500 Subject: [PATCH 23/77] AI Block trigger fix --- Data/Scripts/CoreSystems/Ai/AiConstruct.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs index a7489ce4..b7d3edc4 100644 --- a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs +++ b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs @@ -477,8 +477,8 @@ internal static void CombatBlockUpdates(Ai cAi)//Obligatory comment to annoy com } } } - if (aCB.MarkedForClose) checkAi.Construct.ActiveCombatBlock = null; - if (aFB.MarkedForClose) checkAi.Construct.ActiveFlightBlock = null; //Placed these removals here so we can flip triggers "off" the last time the method runs + if (aCB.MarkedForClose || !aCB.IsWorking) checkAi.Construct.ActiveCombatBlock = null; + if (aFB.MarkedForClose || !aFB.IsWorking) checkAi.Construct.ActiveFlightBlock = null; //Placed these removals here so we can flip triggers "off" the last time the method runs } } From 1b0bb7a8619836bcb0e1b842e26e9993962c6f93 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:15:35 -0500 Subject: [PATCH 24/77] =?UTF-8?q?=C4=B1nsets=20georg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Data/Scripts/CoreSystems/Session/SessionUpdate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index d1237bb5..76130db3 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -914,7 +914,7 @@ private void ShootWeapons() var w = ShootingWeapons[i]; var invalidWeapon = w.Comp.CoreEntity.MarkedForClose || w.Comp.Ai == null || w.Comp.Ai.Concealed || w.Comp.Ai.MarkedForClose || w.Comp.TopEntity.MarkedForClose || w.Comp.Platform.State != CorePlatform.PlatformState.Ready; var smartTimer = w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance == Smart && w.System.TurretMovement == WeaponSystem.TurretType.Fixed && (QCount == w.ShortLoadId && w.Target.HasTarget && Tick - w.LastSmartLosCheck > 240 || Tick - w.LastSmartLosCheck > 1200); - var quickSkip = invalidWeapon || w.Comp.IsBlock && smartTimer && !w.SmartLos() || w.PauseShoot || w.LiveSmarts >= w.System.MaxActiveProjectiles || (w.ProtoWeaponAmmo.CurrentAmmo == 0 && w.ClientMakeUpShots == 0) && w.ActiveAmmoDef.AmmoDef.Const.Reloadable; + var quickSkip = invalidWeapon || w.Comp.IsBlock && smartTimer && !w.System.DisableLosCheck && !w.SmartLos() || w.PauseShoot || w.LiveSmarts >= w.System.MaxActiveProjectiles || (w.ProtoWeaponAmmo.CurrentAmmo == 0 && w.ClientMakeUpShots == 0) && w.ActiveAmmoDef.AmmoDef.Const.Reloadable; if (quickSkip) continue; w.Shoot(); From 5caf6b9960be0f53a5937ea4a2be605c8b578048 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:31:22 -0500 Subject: [PATCH 25/77] UI Remix --- .../EntityComp/Controls/TerminalHelpers.cs | 98 ++++++++++------- .../CoreSystems/Session/SessionControls.cs | 77 ++++++++------ .../CoreSystems/Support/Localization.cs | 100 +++++++++--------- 3 files changed, 152 insertions(+), 123 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 08a926cf..4f46594b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -36,67 +36,83 @@ internal static void AddUiControls(Session session) where T : IMyTerminalBloc internal static void AddTurretOrTrackingControls(Session session) where T : IMyTerminalBlock { - AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); + //Enenenenennennnenennnennennnnnnenenenenennenera's order + AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); - - AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); - - AddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystem, BlockUi.RequestSubSystem, BlockUi.ListSubSystems, HasTracking); - AddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementMode, BlockUi.RequestMovementMode, BlockUi.ListMovementModes, HasTracking); - + AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); AddWeaponRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRange, BlockUi.RequestSetRange, BlockUi.ShowRange, BlockUi.GetMinRange, BlockUi.GetMaxRange, true, false); Separator(session, "WC_sep2", HasTracking); - AddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTarget, BlockUi.RequestSetReportTarget, true, UiReportTarget); + AddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystems, BlockUi.RequestSetSubSystems, true, HasTracking); + AddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystem, BlockUi.RequestSubSystem, BlockUi.ListSubSystems, HasTracking); - AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); + AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTracking); + AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); + AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); - - //AddOnOffSwitchNoAction(session, "Friendly", Localization.GetText("TerminalFriendlyTitle"), Localization.GetText("TerminalFriendlyTooltip"), BlockUi.GetFriendly, BlockUi.RequestSetFriendly, true, HasTrackingAndTrackFriendly); - + AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); - - AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); - + AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); AddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPD, BlockUi.RequestSetSupportingPD, true, UiDisableSupportingPD); - AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); - AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); + Separator(session, "WC_sep3", IsTrue); - AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTracking); + AddWeaponBurstCountSliderRange(session, "Burst Count", Localization.GetText("TerminalBurstShotsTitle"), Localization.GetText("TerminalBurstShotsTooltip"), BlockUi.GetBurstCount, BlockUi.RequestSetBurstCount, CanBurstIsNotBomb, BlockUi.GetMinBurstCount, BlockUi.GetMaxBurstCount, true); + AddWeaponBurstDelaySliderRange(session, "Burst Delay", Localization.GetText("TerminalBurstDelayTitle"), Localization.GetText("TerminalBurstDelayTooltip"), BlockUi.GetBurstDelay, BlockUi.RequestSetBurstDelay, AllowShotDelay, BlockUi.GetMinBurstDelay, BlockUi.GetMaxBurstDelay, true); + AddWeaponSequenceIdSliderRange(session, "Sequence Id", Localization.GetText("TerminalSequenceIdTitle"), Localization.GetText("TerminalSequenceIdTooltip"), BlockUi.GetSequenceId, BlockUi.RequestSetSequenceId, IsNotBomb, BlockUi.GetMinSequenceId, BlockUi.GetMaxSequenceId, false); + AddWeaponGroupIdIdSliderRange(session, "Weapon Group Id", Localization.GetText("TerminalWeaponGroupIdTitle"), Localization.GetText("TerminalWeaponGroupIdTooltip"), BlockUi.GetWeaponGroupId, BlockUi.RequestSetWeaponGroupId, IsNotBomb, BlockUi.GetMinWeaponGroupId, BlockUi.GetMaxWeaponGroupId, true); - AddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystems, BlockUi.RequestSetSubSystems, true, HasTracking); + Separator(session, "WC_sep4", IsTrue); - AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); + AddLeadGroupSliderRange(session, "Target Group", Localization.GetText("TerminalTargetGroupTitle"), Localization.GetText("TerminalTargetGroupTooltip"), BlockUi.GetLeadGroup, BlockUi.RequestSetLeadGroup, TargetLead, BlockUi.GetMinLeadGroup, BlockUi.GetMaxLeadGroup, true); + AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); + AddListBoxNoAction(session, "Friend", "Friend", "Friend list", BlockUi.FriendFill, BlockUi.FriendSelect, IsDrone, 1, true, true); + AddListBoxNoAction(session, "Enemy", "Enemy", "Enemy list", BlockUi.EnemyFill, BlockUi.EnemySelect, IsDrone, 1, true, true); + AddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTarget, BlockUi.RequestSetReportTarget, true, UiReportTarget); - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); + /* + AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); + AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); + AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); + AddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystems, BlockUi.RequestSetSubSystems, true, HasTracking); + AddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystem, BlockUi.RequestSubSystem, BlockUi.ListSubSystems, HasTracking); + AddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementMode, BlockUi.RequestMovementMode, BlockUi.ListMovementModes, HasTracking); + AddWeaponRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRange, BlockUi.RequestSetRange, BlockUi.ShowRange, BlockUi.GetMinRange, BlockUi.GetMaxRange, true, false); + Separator(session, "WC_sep2", HasTracking); + AddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTarget, BlockUi.RequestSetReportTarget, true, UiReportTarget); + AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); + AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); + AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); + AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); + AddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPD, BlockUi.RequestSetSupportingPD, true, UiDisableSupportingPD); + AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); + AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTracking); + AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); + AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); - - Separator(session, "WC_sep3", IsTrue); - AddWeaponBurstCountSliderRange(session, "Burst Count", Localization.GetText("TerminalBurstShotsTitle"), Localization.GetText("TerminalBurstShotsTooltip"), BlockUi.GetBurstCount, BlockUi.RequestSetBurstCount, CanBurstIsNotBomb, BlockUi.GetMinBurstCount, BlockUi.GetMaxBurstCount, true); AddWeaponBurstDelaySliderRange(session, "Burst Delay", Localization.GetText("TerminalBurstDelayTitle"), Localization.GetText("TerminalBurstDelayTooltip"), BlockUi.GetBurstDelay, BlockUi.RequestSetBurstDelay, AllowShotDelay, BlockUi.GetMinBurstDelay, BlockUi.GetMaxBurstDelay, true); AddWeaponSequenceIdSliderRange(session, "Sequence Id", Localization.GetText("TerminalSequenceIdTitle"), Localization.GetText("TerminalSequenceIdTooltip"), BlockUi.GetSequenceId, BlockUi.RequestSetSequenceId, IsNotBomb, BlockUi.GetMinSequenceId, BlockUi.GetMaxSequenceId, false); AddWeaponGroupIdIdSliderRange(session, "Weapon Group Id", Localization.GetText("TerminalWeaponGroupIdTitle"), Localization.GetText("TerminalWeaponGroupIdTooltip"), BlockUi.GetWeaponGroupId, BlockUi.RequestSetWeaponGroupId, IsNotBomb, BlockUi.GetMinWeaponGroupId, BlockUi.GetMaxWeaponGroupId, true); - Separator(session, "WC_sep4", IsTrue); - AddLeadGroupSliderRange(session, "Target Group", Localization.GetText("TerminalTargetGroupTitle"), Localization.GetText("TerminalTargetGroupTooltip"), BlockUi.GetLeadGroup, BlockUi.RequestSetLeadGroup, TargetLead, BlockUi.GetMinLeadGroup, BlockUi.GetMaxLeadGroup, true); - AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); - + AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); AddListBoxNoAction(session, "Friend", "Friend", "Friend list", BlockUi.FriendFill, BlockUi.FriendSelect, IsDrone, 1, true, true); AddListBoxNoAction(session, "Enemy", "Enemy", "Enemy list", BlockUi.EnemyFill, BlockUi.EnemySelect, IsDrone, 1, true, true); //AddListBoxNoAction(session, "Position", "Position", "Position list", BlockUi.PositionFill, BlockUi.PositionSelect, IsDrone, 1, true, true); Suppressed for now as it's inop - - Separator(session, "WC_sep5", HasTracking); + //AddOnOffSwitchNoAction(session, "Friendly", Localization.GetText("TerminalFriendlyTitle"), Localization.GetText("TerminalFriendlyTooltip"), BlockUi.GetFriendly, BlockUi.RequestSetFriendly, true, HasTrackingAndTrackFriendly); + */ } @@ -127,22 +143,22 @@ internal static void AddTurretControlBlockControls(Session session) where T : CtcAddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteorsControl, BlockUi.RequestSetMeteorsControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGridsControl, BlockUi.RequestSetGridsControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFireControl, BlockUi.RequestSetFocusFireControl, true, CtcIsReady); CtcAddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystemsControl, BlockUi.RequestSetSubSystemsControl, true, CtcIsReady); + CtcAddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystemControl, BlockUi.RequestSubSystemControl, BlockUi.ListSubSystems, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepelControl, BlockUi.RequestSetRepelControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGridsControl, BlockUi.RequestSetGridsControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGridControl, BlockUi.RequestSetLargeGridControl, true, CtcIsReady); CtcAddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGridControl, BlockUi.RequestSetSmallGridControl, true, CtcIsReady); Separator(session, "WC_sep3", IsTrue); - CtcAddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystemControl, BlockUi.RequestSubSystemControl, BlockUi.ListSubSystems, CtcIsReady); - CtcAddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementModeControl, BlockUi.RequestMovementModeControl, BlockUi.ListMovementModes, CtcIsReady); //CtcAddComboboxNoAction(session, "Shoot Mode", Localization.GetText("TerminalShootModeTitle"), Localization.GetText("TerminalShootModeTooltip"), BlockUi.CtcGetShootModes, BlockUi.CtcRequestShootModes, BlockUi.CtcListShootModes, CtcIsReady); @@ -166,6 +182,12 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); + AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); + + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); + + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); + //AddOnOffSwitchNoAction(session, "Friendly", Localization.GetText("TerminalFriendlyTitle"), Localization.GetText("TerminalFriendlyTooltip"), BlockUi.GetFriendly, BlockUi.RequestSetFriendly, true, HasTrackingAndTrackFriendly); AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); @@ -174,12 +196,6 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); - AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - - AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); - AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); } @@ -207,7 +223,7 @@ internal static void CreateGenericControls(Session session) where T : IMyTerm Separator(session, "WC_sep4", IsTrue); AddOnOffSwitchNoAction(session, "ShareFireControlEnabled", Localization.GetText("TerminalShareFireControlTitle"), Localization.GetText("TerminalShareFireControlTooltip"), BlockUi.GetShareFireControl, BlockUi.RequestShareFireControl, true, HasTracking); - AddOnOffSwitchNoAction(session, "Shoot", Localization.GetText("TerminalShootTitle"), Localization.GetText("TerminalShootTooltip"), BlockUi.GetShoot, BlockUi.RequestSetShoot, true, IsNotBomb); + AddOnOffSwitchNoAction(session, "Shoot", Localization.GetText("TerminalShootTitle"), Localization.GetText("TerminalShootTooltip"), BlockUi.GetShoot, BlockUi.RequestSetShoot, true, IsNotBomb); AddOnOffSwitchNoAction(session, "Override", Localization.GetText("TerminalOverrideTitle"), Localization.GetText("TerminalOverrideTooltip"), BlockUi.GetOverride, BlockUi.RequestOverride, true, OverrideTarget); AddOnOffSwitchNoAction(session, "AngularTracking", Localization.GetText("TerminalAngularTitle"), Localization.GetText("TerminalAngularTooltip"), BlockUi.GetAngularTracking, BlockUi.RequestAngularTracking, true, HasTracking); } diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index 72903deb..42b3b50d 100644 --- a/Data/Scripts/CoreSystems/Session/SessionControls.cs +++ b/Data/Scripts/CoreSystems/Session/SessionControls.cs @@ -215,39 +215,48 @@ internal static void CreateCustomActionSet(Session session) where T : IMyTerm { CreateCustomActions.CreateArmReaction(session); CreateCustomActions.CreateTriggerNow(session); + + + CreateCustomActions.CreateKeyShoot(session); + CreateCustomActions.CreateMouseToggle(session); + CreateCustomActions.CreateShootToggle(session); CreateCustomActions.CreateShootOn(session); CreateCustomActions.CreateShootOff(session); CreateCustomActions.CreateShootMode(session); - CreateCustomActions.CreateKeyShoot(session); - CreateCustomActions.CreateMouseToggle(session); - CreateCustomActions.CreateSubSystems(session); CreateCustomActions.CreateControlModes(session); - CreateCustomActions.CreateCycleAmmo(session); + CreateCustomActions.CreateObjectiveMode(session); CreateCustomActions.CreateMovementState(session); + + CreateCustomActions.CreateCycleAmmo(session); CreateCustomActions.CreateForceReload(session); - CreateCustomActions.CreateFocusTargets(session); + + CreateCustomActions.CreateAngularTracking(session); + CreateCustomActions.CreateFocusSubSystem(session); - CreateCustomActions.CreateGrids(session); + CreateCustomActions.CreateSubSystems(session); + + CreateCustomActions.CreateFocusTargets(session); + CreateCustomActions.CreateRepelMode(session); + CreateCustomActions.CreateNeutrals(session); - CreateCustomActions.CreateFriendly(session); CreateCustomActions.CreateUnowned(session); + CreateCustomActions.CreateGrids(session); + CreateCustomActions.CreateLargeGrid(session); + CreateCustomActions.CreateSmallGrid(session); + CreateCustomActions.CreateBiologicals(session); CreateCustomActions.CreateProjectiles(session); CreateCustomActions.CreateSupportingPD(session); - CreateCustomActions.CreateBiologicals(session); CreateCustomActions.CreateMeteors(session); - CreateCustomActions.CreateWeaponCameraChannels(session); + CreateCustomActions.CreateLeadGroups(session); - CreateCustomActions.CreateRepelMode(session); - CreateCustomActions.CreateMaxSize(session); - CreateCustomActions.CreateMinSize(session); + CreateCustomActions.CreateWeaponCameraChannels(session); CreateCustomActions.CreateSelectFriend(session); CreateCustomActions.CreateSelectEnemy(session); + CreateCustomActions.CreateMinSize(session); + CreateCustomActions.CreateMaxSize(session); + CreateCustomActions.CreateFriendly(session); //CreateCustomActions.CreateSelectPosition(session); Suppressed for now as it's inop - CreateCustomActions.CreateLargeGrid(session); - CreateCustomActions.CreateSmallGrid(session); - CreateCustomActions.CreateAngularTracking(session); - CreateCustomActions.CreateObjectiveMode(session); } internal static void CreateTurretControllerActions(Session session) where T : IMyTerminalBlock @@ -281,14 +290,16 @@ internal static void CreateSearchlightActions(Session session) where T : IMyT CreateCustomActions.CreateNeutralsControl(session); //CreateCustomActions.CreateFriendlyControl(session); //This even work for a "turret"? CreateCustomActions.CreateUnownedControl(session); - CreateCustomActions.CreateProjectilesControl(session); + CreateCustomActions.CreateGridsControl(session); + CreateCustomActions.CreateLargeGridControl(session); + CreateCustomActions.CreateSmallGridControl(session); CreateCustomActions.CreateBiologicalsControl(session); + CreateCustomActions.CreateProjectilesControl(session); CreateCustomActions.CreateMeteorsControl(session); - CreateCustomActions.CreateGridsControl(session); + CreateCustomActions.CreateMaxSizeControl(session); CreateCustomActions.CreateMinSizeControl(session); - CreateCustomActions.CreateLargeGridControl(session); - CreateCustomActions.CreateSmallGridControl(session); + CreateCustomActions.CreateWeaponCameraChannels(session); } @@ -549,13 +560,14 @@ internal static void AlterActions(Session session) { "WC_Shoot", "WC_AngularTracking", - "WC_Override", + "WC_Repel", + "WC_Unowned", + "WC_Supporting PD", "WC_ShareFireControlEnabled", - "WC_ControlModes", + "WC_ObjectiveMode", "WC_TrackingMode", "WC_ReportTarget", "WC_FocusFire", - "WC_Repel", "Camera Channel", "Weapon Group Id", "Sequence Id", @@ -573,21 +585,23 @@ internal static void AlterActions(Session session) { "WC_Shoot", "AngularTracking", - "ShootToggle", - "MinSize Decrease", - "MinSize Increase", - "MaxSize Decrease", - "MaxSize Increase", "WC_RepelMode", + //"ShootToggle", + "ObjectiveMode", + "SupportingPD", + "Friendly", "WC_Decrease_LeadGroup", "WC_Increase_LeadGroup", "WC_Decrease_CameraChannel", "WC_Increase_CameraChannel", - "FocusSubSystem", + //"FocusSubSystem", "FocusTargets", "TrackingMode", - "ControlModes", - + //"ControlModes", + "MinSize Decrease", + "MinSize Increase", + "MaxSize Decrease", + "MaxSize Increase", }; private static readonly HashSet HideCombatControls = new HashSet() @@ -597,7 +611,6 @@ internal static void AlterActions(Session session) "OffensiveCombatCircleOrbit_SelectedToolsList", "OffensiveCombatCircleOrbit_AvailableWeapons", - "OffensiveCombatStayAtRange_SelectedWeapons", "OffensiveCombatStayAtRange_AddSelectedTool", "OffensiveCombatStayAtRange_SelectedToolsList", diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 6b54b88e..584de436 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -25,7 +25,7 @@ public static class Localization { "TerminalSwitchOn", "On" }, { "TerminalSwitchOff", "Off" }, { "TerminalReportTargetTitle", "Enable Report Target" }, - { "TerminalReportTargetTooltip", "Report if weapon has target on HUD" }, + { "TerminalReportTargetTooltip", "Report if weapon has target on right hand HUD\nTurn off to suppress 'No Target' text" }, { "TerminalWeaponROFTitle", "Change Rate of Fire" }, { "TerminalWeaponROFTooltip", "Change rate of fire" }, { "TerminalOverloadTitle", "Overload Damage" }, @@ -39,39 +39,39 @@ public static class Localization { "TerminalStopCountTitle", "Stop Countdown" }, { "TerminalStopCountTooltip", "Stop Countdown" }, { "TerminalArmTitle", "Arm Reaction" }, - { "TerminalArmTooltip", "When checked, the warhead can be detonated manually or by unwary handle." }, + { "TerminalArmTooltip", "When checked, the warhead can be detonated manually or by unwary handling" }, { "TerminalTriggerTitle", "Trigger" }, { "TerminalTriggerTooltip", "Trigger" }, - { "TerminalWeaponRangeTitle", "Aiming Radius" }, - { "TerminalWeaponRangeTooltip", "Change the min/max targeting range" }, + { "TerminalWeaponRangeTitle", "Aiming Range" }, + { "TerminalWeaponRangeTooltip", "Set the max targeting range for Auto (AI Control) mode" }, { "TerminalNeutralsTitle", "Target Neutrals" }, - { "TerminalNeutralsTooltip", "Fire on targets that are neutral" }, + { "TerminalNeutralsTooltip", "Engage targets that are neutral" }, { "TerminalUnownedTitle", "Target Unowned" }, - { "TerminalUnownedTooltip", "Fire on targets with no owner" }, + { "TerminalUnownedTooltip", "Engage targets with no owner" }, { "TerminalFriendlyTitle", "Track NonThreats" }, { "TerminalFriendlyTooltip", "Track non-threating objects" }, { "TerminalBiologicalsTitle", "Target Biologicals" }, - { "TerminalBiologicalsTooltip", "Fire on players and biological NPCs" }, + { "TerminalBiologicalsTooltip", "Engage players and creatures" }, { "TerminalProjectilesTitle", "Target Projectiles" }, - { "TerminalProjectilesTooltip", "Fire on incoming projectiles" }, + { "TerminalProjectilesTooltip", "Fire on projectiles" }, { "TerminalSupportingPDTitle", "Supportive Point Defense" }, - { "TerminalSupportingPDTooltip", "Fire on all enemy projectiles, regardless of target" }, + { "TerminalSupportingPDTooltip", "Fire on all enemy projectiles in range, regardless of target\nTurn off to only engage projectiles targeting your grid" }, { "TerminalMeteorsTitle", "Target Meteors" }, - { "TerminalMeteorsTooltip", "Target Meteors" }, + { "TerminalMeteorsTooltip", "Target meteors" }, { "TerminalGridsTitle", "Target Grids" }, - { "TerminalGridsTooltip", "Target Grids" }, + { "TerminalGridsTooltip", "Target grids, based on size toggles below" }, { "TerminalFocusFireTitle", "Target FocusFire" }, - { "TerminalFocusFireTooltip", "Only fire on the hud-selected target" }, + { "TerminalFocusFireTooltip", "Only fire on the hud-selected target\nWILL NOT AUTO ENGAGE OTHER TARGETS" }, { "TerminalSubSystemsTitle", "Target SubSystems" }, - { "TerminalSubSystemsTooltip", "Target specific SubSystems of a target" }, + { "TerminalSubSystemsTooltip", "Only target selected subsystems on enemy grid, if present" }, { "TerminalRepelTitle", "Repel Mode" }, { "TerminalRepelTooltip", "Aggressively focus on and repel small threats (not projectiles)" }, { "TerminalPickAmmoTitle", "Pick Ammo" }, { "TerminalPickAmmoTooltip", "Select the ammo type to use" }, - { "TerminalPickSubSystemTitle", "Pick SubSystem" }, - { "TerminalPickSubSystemTooltip", "Select the target subsystem to focus fire on" }, - { "TerminalTrackingModeTitle", "Tracking Mode" }, - { "TerminalTrackingModeTooltip", "Movement fire control requirements" }, + { "TerminalPickSubSystemTitle", "Targeted SubSystem" }, + { "TerminalPickSubSystemTooltip", "Select subsystem to target (When Target SubSystems is on)" }, + { "TerminalTrackingModeTitle", "Movement Mode" }, + { "TerminalTrackingModeTooltip", "Filters for grids in motion, stopped, or stations" }, { "TerminalControlModesTitle", "Control Mode" }, { "TerminalControlModesTooltip", "Select the aim control mode for the weapon" }, { "TerminalCameraChannelTitle", "Weapon Camera Channel" }, @@ -84,12 +84,12 @@ public static class Localization { "TerminalSequenceIdTooltip", "Assign this weapon a unique sequence id per weapon group, used for sequence firing" }, { "TerminalWeaponGroupIdTitle", "Weapon Group id" }, { "TerminalWeaponGroupIdTooltip", "Assign this weapon to a sequence group, used for sequence firing" }, - { "TerminalShootModeTitle", "Shoot Mode" }, - { "TerminalShootModeTooltip", "Set the weapon's mode, fire once, burst fire, mouse click, key toggle mode" }, + { "TerminalShootModeTitle", "Trigger Mode" }, + { "TerminalShootModeTooltip", "Select what will make the weapon fire" }, { "TerminalAiEnabledTitle", "Enable AI" }, { "TerminalAiEnabledTooltip", "Automatically aim and fire at targets" }, { "TerminalShareFireControlTitle", "Share Control" }, - { "TerminalShareFireControlTooltip", "Weapons with manual/painter/mousecontrol enabled will respond when using the Control button above" }, + { "TerminalShareFireControlTooltip", "Other weapons with manual/painter/mousecontrol enabled will follow\nthe aimpoint and fire when using the Control button above" }, { "TerminalTargetGroupTitle", "Target Lead Group" }, { "TerminalTargetGroupTooltip", "Assign this weapon to target lead group" }, { "TerminalDecoyPickSubSystemTitle", "Pick SubSystem" }, @@ -98,40 +98,40 @@ public static class Localization { "TerminalCameraCameraChannelTooltip", "Assign the camera weapon channel to this camera" }, { "TerminalDebugTitle", "Debug" }, { "TerminalDebugTooltip", "Debug On/Off" }, - { "TerminalAdvancedTitle", "Advanced Features" }, - { "TerminalAdvancedTooltip", "This enables more advanced UI features that tend to be confusing to new users" }, + { "TerminalAdvancedTitle", "Advanced WC Features" }, + { "TerminalAdvancedTooltip", "This enables more advanced WC UI features/options" }, { "TerminalShootTitle", "Shoot" }, - { "TerminalShootTooltip", "Shoot On/Off Toggle, this will be retired, replaced by Shoot Mode KeyToggle" }, + { "TerminalShootTooltip", "Shoot On/Off toggle" }, { "ActionStateOn", "On" }, { "ActionStateOff", "Off" }, - { "ActionWCShootMode", "Cycle Shoot Modes" }, - { "ActionWCMouseToggle", "Toggle Mouse Shoot (Mode: MouseControl)" }, - { "ActionFire", "Shoot (Modes: KeyToggle/KeyFire)" }, + { "ActionWCShootMode", "Cycle Trigger Mode" }, + { "ActionWCMouseToggle", "Toggle Mouse Control Trigger Mode" }, + { "ActionFire", "Shoot (Trigger Modes: Key Toggle & Key Fire)" }, { "ForceReload", "Force Reload" }, { "ActionShoot", "Shoot On/Off" }, { "ActionShoot_Off", "Shoot Off" }, { "ActionShoot_On", "Shoot On" }, - { "ActionSubSystems", "Cycle SubSystems" }, + { "ActionSubSystems", "Cycle Targeted SubSystems" }, { "ActionNextCameraChannel", "Next Channel" }, { "ActionPreviousCameraChannel", "Previous Channel" }, - { "ActionControlModes", "Control Mode" }, - { "ActionNeutrals", "Neutrals On/Off" }, - { "ActionProjectiles", "Projectiles On/Off" }, + { "ActionControlModes", "Cycle Control Mode" }, + { "ActionNeutrals", "Target Neutrals On/Off" }, + { "ActionProjectiles", "Target Projectiles On/Off" }, { "ActionSupportingPD", "Supporting PD On/Off" }, - { "ActionBiologicals", "Biologicals On/Off" }, - { "ActionMeteors", "Meteors On/Off" }, - { "ActionGrids", "Grids On/Off" }, - { "ActionFriendly", "Friendly On/Off" }, - { "ActionUnowned", "Unowned On/Off" }, - { "ActionFocusTargets", "FocusTargets On/Off" }, - { "ActionFocusSubSystem", "FocusSubSystem On/Off" }, + { "ActionBiologicals", "Target Biologicals On/Off" }, + { "ActionMeteors", "Target Meteors On/Off" }, + { "ActionGrids", "Target Grids On/Off" }, + { "ActionFriendly", "Target Friendly On/Off" }, + { "ActionUnowned", "Target Unowned On/Off" }, + { "ActionFocusTargets", "Focus Fire On/Off" }, + { "ActionFocusSubSystem", "Target SubSystems On/Off" }, { "ActionMaxSizeIncrease", "MaxSize Increase" }, { "ActionMaxSizeDecrease", "MaxSize Decrease" }, { "ActionMinSizeIncrease", "MinSize Increase" }, { "ActionMinSizeDecrease", "MinSize Decrease" }, - { "ActionTrackingMode", "Tracking Mode" }, - { "ActionWC_CycleAmmo", "Cycle Consumable" }, - { "ActionWC_RepelMode", "Repel Mode" }, + { "ActionTrackingMode", "Cycle Movement Mode" }, + { "ActionWC_CycleAmmo", "Cycle Ammo" }, + { "ActionWC_RepelMode", "Repel Mode On/Off" }, { "ActionWC_Increase_CameraChannel", "Next Camera Channel" }, { "ActionWC_Decrease_CameraChannel", "Previous Camera Channel" }, { "ActionWC_Increase_LeadGroup", "Next Lead Group" }, @@ -168,10 +168,10 @@ public static class Localization { "WeaponInfoLoS", "LoS" }, { "WeaponTotalEffect", "Damage" }, { "WeaponTotalEffectAvgDps", "AvgDps" }, - { "TerminalOverrideTitle", "Override" }, - { "TerminalOverrideTooltip", "Turns off targeting and tracking so you can forcibly fire the weapon- for practice use only" }, + { "TerminalOverrideTitle", "Test Mode" }, + { "TerminalOverrideTooltip", "When test mode is on guided weapons and turrets\ncan be forced to fire without a target - for practice use only" }, { "TerminalAngularTitle", "Track Angular Motion" }, - { "TerminalAngularTooltip", "Adjust aim to account for angular motion of the target" }, + { "TerminalAngularTooltip", "Adjust aim to account for helical motion of the target\nonly needed against those who spin to dodge" }, { "TerminalLGTitle", "Large Grid" }, { "TerminalLGTooltip", "Target large grids" }, { "TerminalSGTitle", "Small Grid" }, @@ -214,16 +214,16 @@ public static class Localization { "SubtypeThrust", "Thrust" }, { "SubtypeJumping", "Jumping" }, { "SubtypeSteering", "Steering" }, - { "ShootAiShoot", "AiShoot" }, - { "ShootMouse", "MouseControl" }, - { "ShootKeyToggle", "KeyToggle" }, - { "ShootKey", "KeyFire" }, + { "ShootAiShoot", "Auto (AI Controlled)" }, + { "ShootMouse", "Mouse Control (Click)" }, + { "ShootKeyToggle", "Key Toggle (Shoot On/Off)" }, + { "ShootKey", "Key Fire (Shoot Once)" }, { "ObjDefault", "Default" }, { "ObjDisabled", "Disabled" }, { "ObjDestroyed", "Destroyed" }, - { "ControlAuto", "Auto" }, - { "ControlManual", "Manual" }, - { "ControlPainter", "Painter" }, + { "ControlAuto", "Auto (AI Controlled)" }, + { "ControlManual", "Manual (Cursor Following)" }, + { "ControlPainter", "Painter (Mark targets for AI)" }, { "MoveAny", "Any" }, { "MoveMoving", "Moving ship" }, { "MoveMoored", "Stations" }, From 0af75a98b314875befd6b85c9f7262d49f668669 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 26 Mar 2025 15:18:24 -0500 Subject: [PATCH 26/77] Force Reload Terminal Button --- .../EntityComp/Controls/TerminalHelpers.cs | 19 +++++++++++++++++++ .../EntityComp/Controls/Weapon/WeaponUi.cs | 11 +++++++++++ .../CoreSystems/Support/Localization.cs | 3 ++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 4f46594b..67d67f68 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -42,6 +42,7 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); AddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementMode, BlockUi.RequestMovementMode, BlockUi.ListMovementModes, HasTracking); AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); + AddButtonNoAction(session, "ForceReload", Localization.GetText("ForceReload"), Localization.GetText("TerminalForceReloadTooltip"), BlockUi.ForceReload, EnableForceReload, AmmoSelection); AddWeaponRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRange, BlockUi.RequestSetRange, BlockUi.ShowRange, BlockUi.GetMinRange, BlockUi.GetMaxRange, true, false); Separator(session, "WC_sep2", HasTracking); @@ -329,6 +330,24 @@ internal static bool AmmoSelection(IMyTerminalBlock block) return comp.ConsumableSelectionPartIds.Count > 0; } + internal static bool EnableForceReload(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + var valid = comp != null && comp.Platform.State == CorePlatform.PlatformState.Ready && comp.Type == CoreComponent.CompType.Weapon && comp.Data?.Repo != null; + if (!valid || Session.I.PlayerId != comp.Data.Repo.Values.State.PlayerId && !comp.TakeOwnerShip()) + return false; + bool enable = false; + for (int i = 0; i < comp.Collection.Count; i++) + { + var wep = comp.Collection[i]; + if (!wep.System.HasAmmoSelection) + continue; + if (wep.DelayedCycleId != -1 && wep.ActiveAmmoDef != wep.System.AmmoTypes[wep.DelayedCycleId]) + enable = true; + } + return enable; + } + internal static bool CanBurst(IMyTerminalBlock block) { var comp = block?.Components?.Get() as Weapon.WeaponComponent; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs index d58408c3..fe685d77 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs @@ -587,6 +587,17 @@ internal static void RequestSetAmmo(IMyTerminalBlock block, long newValue) wep.QueueAmmoChange((int)newValue); } + block.ShowInToolbarConfig = !block.ShowInToolbarConfig; //#keen + block.ShowInToolbarConfig = !block.ShowInToolbarConfig; + } + + internal static void ForceReload(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; + comp.RequestForceReload(); + block.ShowInToolbarConfig = !block.ShowInToolbarConfig; //#keen + block.ShowInToolbarConfig = !block.ShowInToolbarConfig; } diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 584de436..2c94d628 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -67,7 +67,7 @@ public static class Localization { "TerminalRepelTitle", "Repel Mode" }, { "TerminalRepelTooltip", "Aggressively focus on and repel small threats (not projectiles)" }, { "TerminalPickAmmoTitle", "Pick Ammo" }, - { "TerminalPickAmmoTooltip", "Select the ammo type to use" }, + { "TerminalPickAmmoTooltip", "Select the ammo type to load next\nTo swap immediately, click 'Force Reload' below" }, { "TerminalPickSubSystemTitle", "Targeted SubSystem" }, { "TerminalPickSubSystemTooltip", "Select subsystem to target (When Target SubSystems is on)" }, { "TerminalTrackingModeTitle", "Movement Mode" }, @@ -108,6 +108,7 @@ public static class Localization { "ActionWCMouseToggle", "Toggle Mouse Control Trigger Mode" }, { "ActionFire", "Shoot (Trigger Modes: Key Toggle & Key Fire)" }, { "ForceReload", "Force Reload" }, + { "TerminalForceReloadTooltip", "Forces weapon to swap to newly selected ammo type\nWill return ammo to weapon inventory if there is space" }, { "ActionShoot", "Shoot On/Off" }, { "ActionShoot_Off", "Shoot Off" }, { "ActionShoot_On", "Shoot On" }, From 8221584e7a5e354af7c991e91a4a412fe62ab870 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 31 Mar 2025 15:24:55 -0500 Subject: [PATCH 27/77] Updates - CTC will now use a camera, if selected, for LOS and aiming checks - CTC's controlling smarts will now share the target selected to all mounted smart weapons - LOS checks through friendlies and terrain should work somewhat better, but weapon firing events will be delayed a few milliseconds as they wait to ensure LOS is clear --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 3 ++- .../CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../Parts/Control/ControlTracking.cs | 8 ++++++- .../EntityComp/Parts/Weapon/WeaponFields.cs | 1 + .../CoreSystems/Session/SessionUpdate.cs | 22 +++++++++++++++---- 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index 9a7b43f0..f12e6d28 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -1130,7 +1130,8 @@ private static bool FindRandomBlock(Weapon w, Target target, TargetInfo info, Co w.AcquiredBlock = true; var targetDirNorm = Vector3D.Normalize(blockPos - w.BarrelOrigin); - var testPos = w.BarrelOrigin + (targetDirNorm * w.MuzzleDistToBarrelCenter); + var ctcCam = w.RotorTurretTracking && w.Comp.Ai.ControlComp?.Controller.Camera != null; + var testPos = ctcCam ? w.Comp.Ai.ControlComp.Controller.Camera.GetPosition() : w.BarrelOrigin + (targetDirNorm * w.MuzzleDistToBarrelCenter); var targetDist = Vector3D.Distance(testPos, blockPos); var fakeCheck = w.System.NoVoxelLosCheck; diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index e1438748..8387f3fc 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -251,7 +251,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str { var chargeTime = w.AssignedPower > 0 ? (int)((w.MaxCharge - w.ProtoWeaponAmmo.CurrentCharge) / w.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; shots += $"\n{Localization.GetText("WeaponInfoDrawOverMax")}: {SinkPower - IdlePower:0.00}/ {w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick:0.00} {Localization.GetText("WeaponInfoMWLabel")}" + - $"\n{(chargeTime == 0 ? Localization.GetText("WeaponInfoPowerCharged") : Localization.GetText("WeaponInfoPowerChargedIn") + chargeTime + Localization.GetText("WeaponInfoSeconds"))}"; + $"\n{(chargeTime == 0 ? Localization.GetText("WeaponInfoPowerCharged") : Localization.GetText("WeaponInfoPowerChargedIn") + " " + chargeTime + Localization.GetText("WeaponInfoSeconds"))}"; } var endReturn = i + 1 != collection.Count ? "\n" : string.Empty; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs index b20d8583..d4ede967 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs @@ -64,7 +64,13 @@ internal static bool TrajectoryEstimation(ControlSys control, out Vector3D targe var maxRangeSqr = fakeTargetInfo != null && topAi.Construct.RootAi != null ? topAi.Construct.RootAi.MaxTargetingRangeSqr : cValues.Set.Range * cValues.Set.Range; bool valid; - topAi.RotorTargetPosition = Weapon.TrajectoryEstimation(weapon, targetCenter, targetVel, targetAcc, shooterPos, out valid, false, cValues.Set.Overrides.AngularTracking); + if (weapon.ActiveAmmoDef.AmmoDef.Const.IsSmart || weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon) + { + valid = true; + topAi.RotorTargetPosition = targetCenter; + } + else + topAi.RotorTargetPosition = Weapon.TrajectoryEstimation(weapon, targetCenter, targetVel, targetAcc, shooterPos, out valid, false, cValues.Set.Overrides.AngularTracking); targetDirection = Vector3D.Normalize(topAi.RotorTargetPosition - shooterPos); return valid && Vector3D.DistanceSquared(topAi.RotorTargetPosition, shooterPos) < maxRangeSqr; } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index c90215cc..5223f757 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -235,6 +235,7 @@ public partial class Weapon : Part internal bool PlayingHardPointSound; internal bool VanillaTracking; internal bool RotorTurretTracking; + internal bool RotorTurretSlaving; internal bool ShotReady { diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index 76130db3..7fec0e02 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -369,7 +369,11 @@ private void AiLoop() { cComp.ToolsAndWeapons.Clear(); foreach (var comp in cPart.TopAi.WeaponComps) + { cComp.ToolsAndWeapons.Add(comp.CoreEntity); + if(comp != cPart.TopAi.RootComp) + comp.PrimaryWeapon.RotorTurretSlaving = true; + } foreach (var tool in cPart.TopAi.Tools) cComp.ToolsAndWeapons.Add((MyEntity)tool); } @@ -409,12 +413,12 @@ private void AiLoop() var primaryWeapon = cPart.TopAi.RootComp.PrimaryWeapon; primaryWeapon.RotorTurretTracking = true; + primaryWeapon.RotorTurretSlaving = false; if (IsServer && cValues.Set.Range < 0 && primaryWeapon.MaxTargetDistance > 0) BlockUi.RequestSetRangeControl(cComp.TerminalBlock, (float) primaryWeapon.MaxTargetDistance); var validTarget = primaryWeapon.Target.TargetState == TargetStates.IsEntity || primaryWeapon.Target.TargetState == TargetStates.IsFake || primaryWeapon.Target.TargetState == TargetStates.IsProjectile; - var noTarget = false; var desiredDirection = Vector3D.Zero; @@ -438,7 +442,7 @@ private void AiLoop() continue; } - if (!cComp.TrackTarget(cComp.Platform.Control.BaseMap, cComp.Platform.Control.OtherMap, ref desiredDirection)) + if (!cComp.TrackTarget(cComp.Platform.Control.BaseMap, cComp.Platform.Control.OtherMap, ref desiredDirection)) continue; } @@ -647,7 +651,7 @@ private void AiLoop() { w.Target.Reset(Tick, States.Expired, !wComp.ManualMode); } - 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)) + else if (eTarget != null && (eTarget.MarkedForClose || (cTarget!= null && (cTarget.IsDead || cTarget.Integrity <= 0)) || !rootConstruct.HadFocus && weaponAcquires && aConst.SkipAimChecks && !w.RotorTurretTracking && !w.RotorTurretSlaving || wComp.UserControlled && !w.System.SuppressFire)) { w.Target.Reset(Tick, States.Expired); } @@ -706,6 +710,8 @@ private void AiLoop() if (wValues.State.Control == ControlMode.Camera && UiInput.MouseButtonPressed) w.Target.TargetPos = Vector3D.Zero; + //if (w.RotorTurretTracking) MyAPIGateway.Utilities.ShowNotification($"{w.Comp.Cube.DisplayNameText} {weaponAcquires} {w.TargetAcquireTick} {(!w.System.DropTargetUntilLoaded || w.ProtoWeaponAmmo.CurrentAmmo > 0)} {(!wComp.UserControlled || wComp.FakeMode || wValues.State.Trigger == On)}", 16); + /// /// Queue for target acquire or set to tracking weapon. /// @@ -723,6 +729,14 @@ private void AiLoop() Dictionary masterTargets; var seek = weaponReady && (acquireReady || w.ProjectilesNear) && (!w.System.TargetSlaving || rootConstruct.TrackedTargets.TryGetValue(w.System.StorageLocation, out masterTargets) && masterTargets.Count > 0); var fakeRequest = wComp.FakeMode && w.Target.TargetState != TargetStates.IsFake && wComp.UserControlled; + var syncCTC = ai.ControlComp != null && w.RotorTurretSlaving && (bool)ai.RootComp.PrimaryWeapon?.Target?.HasTarget && w.Target.TopEntityId != ai.RootComp.PrimaryWeapon.Target.TopEntityId; + + if (syncCTC) + { + var ctcPriTarg = ai.RootComp.PrimaryWeapon.Target; + w.Target.Set(ctcPriTarg.TargetObject, ctcPriTarg.TargetPos, ctcPriTarg.HitShortDist, ctcPriTarg.OrigDistance, ctcPriTarg.TopEntityId); + w.Target.TargetChanged = true; + } if (seek || fakeRequest) { @@ -750,7 +764,7 @@ private void AiLoop() var delayedFire = w.System.DelayCeaseFire && !w.Target.IsAligned && Tick - w.CeaseFireDelayTick <= w.System.CeaseFireDelay; var finish = w.FinishShots || delayedFire; - var shootRequest = (anyShot || finish); + var shootRequest = (anyShot || finish) && !w.Casting; var shotReady = canShoot && shootRequest; var shoot = shotReady && ai.CanShoot && (!aConst.RequiresTarget || w.Target.HasTarget || finish || overRide || wComp.ShootManager.Signal == Weapon.ShootManager.Signals.Manual); From 4c8be4fe57c05c10f8ac4f48140faaa2560eea88 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:32:10 -0500 Subject: [PATCH 28/77] PD ray change --- Data/Scripts/CoreSystems/Session/SessionUpdate.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index 7fec0e02..f77d5622 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -764,7 +764,8 @@ private void AiLoop() var delayedFire = w.System.DelayCeaseFire && !w.Target.IsAligned && Tick - w.CeaseFireDelayTick <= w.System.CeaseFireDelay; var finish = w.FinishShots || delayedFire; - var shootRequest = (anyShot || finish) && !w.Casting; + var pauseForRay = w.Target.TargetState == TargetStates.IsProjectile ? false : w.Casting; + var shootRequest = (anyShot || finish) && !pauseForRay; var shotReady = canShoot && shootRequest; var shoot = shotReady && ai.CanShoot && (!aConst.RequiresTarget || w.Target.HasTarget || finish || overRide || wComp.ShootManager.Signal == Weapon.ShootManager.Signals.Manual); From 7ea0ee4ce0d699e81945ca978131fa87eaee9587 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:51:51 -0500 Subject: [PATCH 29/77] LOS pause touchups --- Data/Scripts/CoreSystems/Session/SessionUpdate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index f77d5622..d951332d 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -764,7 +764,7 @@ private void AiLoop() var delayedFire = w.System.DelayCeaseFire && !w.Target.IsAligned && Tick - w.CeaseFireDelayTick <= w.System.CeaseFireDelay; var finish = w.FinishShots || delayedFire; - var pauseForRay = w.Target.TargetState == TargetStates.IsProjectile ? false : w.Casting; + var pauseForRay = w.Target.TargetState != TargetStates.IsProjectile && w.Casting && !w.PreFired && !finish; var shootRequest = (anyShot || finish) && !pauseForRay; var shotReady = canShoot && shootRequest; From 662f0323811d6df421d2f7dc52c1a474df49ad44 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:13:20 -0500 Subject: [PATCH 30/77] CTC NRE --- Data/Scripts/CoreSystems/Session/SessionUpdate.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index d951332d..932a3f17 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -710,8 +710,6 @@ private void AiLoop() if (wValues.State.Control == ControlMode.Camera && UiInput.MouseButtonPressed) w.Target.TargetPos = Vector3D.Zero; - //if (w.RotorTurretTracking) MyAPIGateway.Utilities.ShowNotification($"{w.Comp.Cube.DisplayNameText} {weaponAcquires} {w.TargetAcquireTick} {(!w.System.DropTargetUntilLoaded || w.ProtoWeaponAmmo.CurrentAmmo > 0)} {(!wComp.UserControlled || wComp.FakeMode || wValues.State.Trigger == On)}", 16); - /// /// Queue for target acquire or set to tracking weapon. /// @@ -729,7 +727,7 @@ private void AiLoop() Dictionary masterTargets; var seek = weaponReady && (acquireReady || w.ProjectilesNear) && (!w.System.TargetSlaving || rootConstruct.TrackedTargets.TryGetValue(w.System.StorageLocation, out masterTargets) && masterTargets.Count > 0); var fakeRequest = wComp.FakeMode && w.Target.TargetState != TargetStates.IsFake && wComp.UserControlled; - var syncCTC = ai.ControlComp != null && w.RotorTurretSlaving && (bool)ai.RootComp.PrimaryWeapon?.Target?.HasTarget && w.Target.TopEntityId != ai.RootComp.PrimaryWeapon.Target.TopEntityId; + var syncCTC = w.RotorTurretSlaving && ai.ControlComp != null && ai.RootComp?.PrimaryWeapon != null && (bool)ai.RootComp.PrimaryWeapon.Target?.HasTarget && w.Target.TopEntityId != ai.RootComp.PrimaryWeapon.Target.TopEntityId; if (syncCTC) { From 499c4f6603fa317886c4b77975d2c88d79120b2b Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:11:58 -0500 Subject: [PATCH 31/77] Updates - Reduced planetary voxel prefetch time from 90 to 10 ticks, should eliminate "twitching" weapons when they are following your mouse across voxel - Weapons in Test Mode (Override) will no longer wait for LOS ray check callbacks - Default character damage fixed to match notes in DamageScales. Would erroneously default to zero instead of 1 --- .../CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs | 2 +- Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs | 2 +- Data/Scripts/CoreSystems/Session/SessionUpdate.cs | 2 +- Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 8ea71ab7..edee4329 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -497,7 +497,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon MagazineSize = EnergyAmmo ? EnergyMagSize : MagazineDef.Capacity; - if (EnergyAmmo && MagazineSize == 0) + if (EnergyAmmo && MagazineSize == 0 && ammo.AmmoDef.HardPointUsable) Log.Line($"{ammo.AmmoDef.AmmoRound} has a magazine capacity of zero, Girax error detected! Check your magazine sbc entry for a tag or ammo for EnergyMagazineSize"); MagsToLoad = wDef.HardPoint.Loading.MagsToLoad > 0 ? wDef.HardPoint.Loading.MagsToLoad : 1; diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 97824a26..ad33c534 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -947,7 +947,7 @@ private void DamageDestObj(HitEntity hitEnt, ProInfo info) var character = hitEnt.Entity as IMyCharacter; float damageScale = 1; if (info.AmmoDef.Const.VirtualBeams) damageScale *= info.Weapon.WeaponCache.Hits; - if (character != null && info.AmmoDef.DamageScales.Characters >= 0) + if (character != null && info.AmmoDef.DamageScales.Characters > 0) damageScale *= info.AmmoDef.DamageScales.Characters; var areaEffect = info.AmmoDef.AreaOfDamage; diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index 932a3f17..1634418c 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -762,7 +762,7 @@ private void AiLoop() var delayedFire = w.System.DelayCeaseFire && !w.Target.IsAligned && Tick - w.CeaseFireDelayTick <= w.System.CeaseFireDelay; var finish = w.FinishShots || delayedFire; - var pauseForRay = w.Target.TargetState != TargetStates.IsProjectile && w.Casting && !w.PreFired && !finish; + var pauseForRay = w.Target.TargetState != TargetStates.IsProjectile && !wComp.Data.Repo.Values.Set.Overrides.Override && w.Casting && !w.PreFired && !finish; var shootRequest = (anyShot || finish) && !pauseForRay; var shotReady = canShoot && shootRequest; diff --git a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs index 4ea08c2e..e56298cf 100644 --- a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs +++ b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs @@ -145,7 +145,7 @@ internal bool SelectTarget(bool manualSelect = true, bool firstStage = false, bo var advanced = s.Settings.ClientConfig.AdvancedMode || s.UiInput.IronLock; MyEntity closestEnt = null; MyEntity rootEntity = null; - if (ai.MyPlanet != null && Session.I.Tick90 && s.UiInput.AltPressed) + if (ai.MyPlanet != null && s.UiInput.AltPressed && Session.I.Tick10) { var rayLine = new LineD(AimPosition, ai.MaxTargetingRange > s.PreFetchMaxDist ? AimPosition + AimDirection * s.PreFetchMaxDist : end); ai.MyPlanet.PrefetchShapeOnRay(ref rayLine); From de00d593e03f9ddb6d056e1721b12ec8b2c39eca Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 14 Apr 2025 18:18:03 -0500 Subject: [PATCH 32/77] Updates - Added new weapon cfg option in "Other": AllowScopeOutsideObb. By default the LoS check origin is adjusted to be inside the weapon block bounds, setting this true uses the actual scope position. (coreparts structure update needed for modders) - Changed CastRayParallel calls to CastRay. Delays for LoS checks should be eliminated and false-positive firing at terrain or friendlies should be further reduced - Fixed a bug with LoS on weapons that target grid center, if there was nothing at the center of the grid the ray would return a false miss and the weapon would give up that grid target - Added additional weapon debug draws for the scope and scope forward (yellow ball at scope and line) --- .../Definitions/CoreDefinitions.cs | 1 + .../CoreSystems/Definitions/CoreSystems.cs | 2 + .../CoreSystems/EntityComp/EntityRun.cs | 6 +- .../EntityComp/Parts/Weapon/WeaponTracking.cs | 13 +- .../EntityComp/Parts/Weapon/WeaponTypes.cs | 195 +++++++----------- .../CoreSystems/Session/SessionSupport.cs | 3 + 6 files changed, 98 insertions(+), 122 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index 4281815e..49e27a19 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -615,6 +615,7 @@ public struct OtherDef [ProtoMember(8)] internal bool CheckForAnyWeapon; [ProtoMember(9)] internal bool DisableLosCheck; [ProtoMember(10)] internal bool NoVoxelLosCheck; + [ProtoMember(11)] internal bool AllowScopeOutsideObb; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs index 5826a78f..315ecc06 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs @@ -222,6 +222,7 @@ public AmmoType(AmmoDef ammoDef, MyDefinitionId ammoDefinitionId, MyDefinitionId public readonly bool GoHomeToReload; public readonly bool DropTargetUntilLoaded; public readonly bool NoVoxelLosCheck; + public readonly bool AllowScopeOutsideObb; public readonly double MaxTargetSpeed; public readonly double AzStep; public readonly double ElStep; @@ -301,6 +302,7 @@ public WeaponSystem(WeaponStructure structure, MyStringHash partNameIdHash, MySt GoHomeToReload = Values.HardPoint.Loading.GoHomeToReload; DropTargetUntilLoaded = Values.HardPoint.Loading.DropTargetUntilLoaded; NoVoxelLosCheck = Values.HardPoint.Other.NoVoxelLosCheck; + AllowScopeOutsideObb = Values.HardPoint.Other.AllowScopeOutsideObb; TopTargets = Values.Targeting.TopTargets; CycleTargets = Values.Targeting.CycleTargets; diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs b/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs index 35b6641b..545b9273 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs @@ -6,6 +6,7 @@ using VRageMath; using static CoreSystems.Session; using static CoreSystems.Support.Ai; +using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Support { @@ -227,7 +228,10 @@ internal void OnAddedToSceneTasks(bool firstRun) foreach (var weapon in Platform.Weapons) { var scopeInfo = weapon.GetScope.Info; - if (!obb.Contains(ref scopeInfo.Position)) + + if (weapon.Comp.PrimaryWeapon.System.AllowScopeOutsideObb) + weapon.ScopeDistToCheckPos = 0; + else if (!obb.Contains(ref scopeInfo.Position)) { var rayBack = new RayD(scopeInfo.Position, -scopeInfo.Direction); weapon.ScopeDistToCheckPos = obb.Intersects(ref rayBack) ?? 0; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index de611a88..07f39ae5 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -1245,7 +1245,11 @@ private bool RayCheckTest(double rangeToTargetSqr) if (Target.TargetState == Target.TargetStates.IsFake) { Casting = true; - Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref Target.TargetPos, filter, ManualShootRayCallBack); + //Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref Target.TargetPos, filter, ManualShootRayCallBack); + + IHitInfo fakeHitInfo; + Session.I.Physics.CastRay(trackingCheckPosition, Target.TargetPos, out fakeHitInfo, filter); + ManualShootRayCallBack(fakeHitInfo); return true; } @@ -1319,7 +1323,12 @@ private bool RayCheckTest(double rangeToTargetSqr) } } Casting = true; - Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref targetPos, filter, RayCallBack.NormalShootRayCallBack); + //Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref targetPos, filter, RayCallBack.NormalShootRayCallBack); + + IHitInfo rayHitInfo; + Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); + RayCallBack.NormalShootRayCallBack(rayHitInfo); + return true; } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs index 46897c01..156ca0fc 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs @@ -23,149 +23,106 @@ internal ParallelRayCallBack(Weapon weapon) public void NormalShootRayCallBack(IHitInfo hitInfo) { - int crumb = 0; - try + var pTarget = Weapon.Target.TargetObject as Projectile; + var eTarget = Weapon.Target.TargetObject as MyEntity; + if (pTarget == null && eTarget == null) + return; + Weapon.Casting = false; + Weapon.PauseShoot = false; + var masterWeapon = Weapon.System.TrackTargets ? Weapon : Weapon.Comp.PrimaryWeapon; + var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || Weapon.Target.TargetObject is IMyCharacter; + var scope = Weapon.GetScope; + var trackingCheckPosition = scope.CachedPos; + double rayDist = 0; + + if (Session.I.DebugLos) { - var pTarget = Weapon.Target.TargetObject as Projectile; - var eTarget = Weapon.Target.TargetObject as MyEntity; - if (pTarget == null && eTarget == null) - return; - crumb = 1; - Weapon.Casting = false; - Weapon.PauseShoot = false; - var masterWeapon = Weapon.System.TrackTargets ? Weapon : Weapon.Comp.PrimaryWeapon; - var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || Weapon.Target.TargetObject is IMyCharacter; - var scope = Weapon.GetScope; - var trackingCheckPosition = scope.CachedPos; - double rayDist = 0; - crumb = 2; - - if (Session.I.DebugLos) - { - var hitPos = hitInfo.Position; - if (rayDist <= 0) Vector3D.Distance(ref trackingCheckPosition, ref hitPos, out rayDist); - - Session.I.AddLosCheck(new Session.LosDebug { Part = Weapon, HitTick = Session.I.Tick, Line = new LineD(trackingCheckPosition, hitPos) }); - } - crumb = 3; + var hitPos = hitInfo.Position; + if (rayDist <= 0) Vector3D.Distance(ref trackingCheckPosition, ref hitPos, out rayDist); + Session.I.AddLosCheck(new Session.LosDebug { Part = Weapon, HitTick = Session.I.Tick, Line = new LineD(trackingCheckPosition, hitPos) }); + } - if (Weapon.Comp.Ai.ShieldNear) + if (Weapon.Comp.Ai.ShieldNear) + { + var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldMatrixRef.Translation; + var targetDir = targetPos - trackingCheckPosition; + if (Weapon.HitFriendlyShield(trackingCheckPosition, targetPos, targetDir)) { - crumb = 4; - - var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldMatrixRef.Translation; - var targetDir = targetPos - trackingCheckPosition; - if (Weapon.HitFriendlyShield(trackingCheckPosition, targetPos, targetDir)) - { - crumb = 51; - - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - return; - } - crumb = 6; - + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + return; } + } - var hitTopEnt = (MyEntity)hitInfo?.HitEntity?.GetTopMostParent(); - if (hitTopEnt == null) + var hitTopEnt = (MyEntity)hitInfo?.HitEntity?.GetTopMostParent(); + if (hitTopEnt == null) + { + if (Weapon.System.TargetGridCenter && eTarget != null) + hitTopEnt = eTarget; + else { - crumb = 7; - if (ignoreTargets) return; - crumb = 8; - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); - crumb = 9; - return; } - crumb = 10; - - var targetTopEnt = eTarget?.GetTopMostParent(); - if (targetTopEnt == null) - return; - crumb = 11; - - var unexpectedHit = ignoreTargets || targetTopEnt != hitTopEnt; - var topAsGrid = hitTopEnt as MyCubeGrid; - crumb = 12; - - if (unexpectedHit) - { - crumb = 13; - - if (hitTopEnt is MyVoxelBase && !Weapon.System.ScanNonThreats) - { - crumb = 14; + } - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); - return; - } + var targetTopEnt = eTarget?.GetTopMostParent(); + if (targetTopEnt == null) + return; - if (topAsGrid == null) - return; - if (Weapon.Comp.Ai.AiType == Ai.AiTypes.Grid && topAsGrid.IsSameConstructAs(Weapon.Comp.Ai.GridEntity)) - { - crumb = 15; + var unexpectedHit = ignoreTargets || targetTopEnt != hitTopEnt; + var topAsGrid = hitTopEnt as MyCubeGrid; - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); - Weapon.PauseShoot = true; - return; - } - if (!Weapon.System.ScanNonThreats && (!topAsGrid.DestructibleBlocks || topAsGrid.Immune || topAsGrid.GridGeneralDamageModifier <= 0 || !Session.GridEnemy(Weapon.Comp.Ai.AiOwner, topAsGrid))) - { - crumb = 16; - - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); - return; - } - crumb = 17; + if (unexpectedHit) + { + if (hitTopEnt is MyVoxelBase && !Weapon.System.ScanNonThreats) + { + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); return; + } + if (topAsGrid == null) + return; + if (Weapon.Comp.Ai.AiType == Ai.AiTypes.Grid && topAsGrid.IsSameConstructAs(Weapon.Comp.Ai.GridEntity)) + { + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckSelfHit); + Weapon.PauseShoot = true; + return; } - if (Weapon.System.ClosestFirst && topAsGrid != null && topAsGrid == targetTopEnt) + if (!Weapon.System.ScanNonThreats && (!topAsGrid.DestructibleBlocks || topAsGrid.Immune || topAsGrid.GridGeneralDamageModifier <= 0 || !Session.GridEnemy(Weapon.Comp.Ai.AiOwner, topAsGrid))) { - crumb = 18; - - var halfExtMin = topAsGrid.PositionComp.LocalAABB.HalfExtents.Min(); - var minSize = topAsGrid.GridSizeR * 8; - var maxChange = halfExtMin > minSize ? halfExtMin : minSize; - var targetPos = eTarget.PositionComp.WorldAABB.Center; - var weaponPos = trackingCheckPosition; - - if (rayDist <= 0) Vector3D.Distance(ref weaponPos, ref targetPos, out rayDist); - var newHitShortDist = rayDist * (1 - hitInfo.Fraction); - var distanceToTarget = rayDist * hitInfo.Fraction; - crumb = 19; - - var shortDistExceed = newHitShortDist - Weapon.Target.HitShortDist > maxChange; - var escapeDistExceed = distanceToTarget - Weapon.Target.OrigDistance > Weapon.Target.OrigDistance; - if (shortDistExceed || escapeDistExceed) - { - masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); - if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); - crumb = 20; - - } + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + return; } + return; + } - catch (Exception ex) + if (Weapon.System.ClosestFirst && topAsGrid != null && topAsGrid == targetTopEnt) { - var pTarget = Weapon?.Target?.TargetObject as Projectile; - var eTarget = Weapon?.Target?.TargetObject as MyEntity; - var msg = $"Emperor Palpatine, your bug is back!\n" + - $"Crumb: {crumb} eTarget: {eTarget == null} pTarget: {pTarget == null} WepNull: {Weapon == null} Wep.Targ Null: {Weapon.Target == null}"; - MyLog.Default.WriteLine(msg); - Log.Line(msg); - throw ex; + var halfExtMin = topAsGrid.PositionComp.LocalAABB.HalfExtents.Min(); + var minSize = topAsGrid.GridSizeR * 8; + var maxChange = halfExtMin > minSize ? halfExtMin : minSize; + var targetPos = eTarget.PositionComp.WorldAABB.Center; + var weaponPos = trackingCheckPosition; + + if (rayDist <= 0) Vector3D.Distance(ref weaponPos, ref targetPos, out rayDist); + var newHitShortDist = rayDist * (1 - hitInfo.Fraction); + var distanceToTarget = rayDist * hitInfo.Fraction; + var shortDistExceed = newHitShortDist - Weapon.Target.HitShortDist > maxChange; + var escapeDistExceed = distanceToTarget - Weapon.Target.OrigDistance > Weapon.Target.OrigDistance; + if (shortDistExceed || escapeDistExceed) + { + masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); + if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); + } } } diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index dd0abd58..a0188244 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -1171,6 +1171,9 @@ public void WeaponDebug(Weapon w) DsDebugDraw.DrawLine(w.MyAimTestLine, Color.Black, 0.07f); DsDebugDraw.DrawSingleVec(w.MyPivotPos, 1f, Color.White); DsDebugDraw.DrawLine(w.AzimuthFwdLine.From, w.AzimuthFwdLine.To, Color.Cyan, 0.05f); + DsDebugDraw.DrawLine(w.GetScope.Info.Position, w.GetScope.Info.Position + w.GetScope.Info.Direction * 10, Color.Yellow, 0.05f); + DsDebugDraw.DrawSingleVec(w.GetScope.Info.Position, 0.5f, Color.Yellow); + //DsDebugDraw.DrawLine(w.MyCenterTestLine, Color.Green, 0.05f); //DsDebugDraw.DrawBox(w.targetBox, Color.Plum); //DsDebugDraw.DrawLine(w.LimitLine.From, w.LimitLine.To, Color.Orange, 0.05f); From 8d6fd0a1f86ae907154f12685689d8506a158b11 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 16 Apr 2025 17:48:10 -0500 Subject: [PATCH 33/77] Axed --- CoreSystems.csproj | 6 - .../CoreSystems/Ai/AiComp/AiCompRun.cs | 3 +- Data/Scripts/CoreSystems/Ai/AiConstruct.cs | 11 +- Data/Scripts/CoreSystems/Ai/AiData.cs | 2 - Data/Scripts/CoreSystems/Ai/AiDatabase.cs | 112 +- Data/Scripts/CoreSystems/Ai/AiEvents.cs | 5 - Data/Scripts/CoreSystems/Ai/AiFields.cs | 3 +- Data/Scripts/CoreSystems/Ai/AiSupport.cs | 3 +- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 1 - Data/Scripts/CoreSystems/Ai/AiTypes.cs | 8 +- .../CoreSystems/Api/CoreSystemsApiBase.cs | 1 - .../CoreSystems/Api/CoreSystemsApiBlocks.cs | 4 - .../CoreSystems/Api/CoreSystemsApiDefs.cs | 3 - .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 3 +- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 7 +- Data/Scripts/CoreSystems/Comms/Spectrum.cs | 1 - .../Coreparts/Definitions/AmmoTypes.cs | 12 - .../Coreparts/Definitions/Artillery.cs | 1 - .../Coreparts/Definitions/Assault Cannons.cs | 1 - .../Coreparts/Definitions/Autocannons.cs | 1 - .../Coreparts/Definitions/Gatlings.cs | 1 - .../Coreparts/Definitions/InteriorTurret.cs | 1 - .../Coreparts/Definitions/MasterConfig.cs | 5 - .../Coreparts/Definitions/Missiles.cs | 1 - .../Definitions/RailgunAnimations.cs | 2 - .../Coreparts/Definitions/Railguns.cs | 2 - .../Coreparts/Definitions/SearchLight.cs | 2 - .../CoreSystems/Definitions/CoreSystems.cs | 12 - .../CoreSystems/Definitions/PartAnimation.cs | 1 - .../Definitions/SerializedConfigs/AiValues.cs | 3 +- .../SerializedConfigs/ConstructValues.cs | 1 - .../SerializedConfigs/Control/ProtoControl.cs | 3 - .../Definitions/SerializedConfigs/Misc.cs | 7 +- .../SerializedConfigs/PacketTypes.cs | 22 +- .../SerializedConfigs/Upgrade/ProtoUpgrade.cs | 1 - .../Controls/Control/ControlActions.cs | 3 +- .../EntityComp/Controls/Control/ControlUi.cs | 2 - .../Controls/CreateCustomActions.cs | 37 - .../EntityComp/Controls/TerminalHelpers.cs | 86 -- .../EntityComp/Controls/Weapon/WeaponUi.cs | 2 - .../CoreSystems/EntityComp/EntityEvents.cs | 291 ++-- .../CoreSystems/EntityComp/EntityInit.cs | 6 +- .../CoreSystems/EntityComp/EntityRun.cs | 4 +- .../CoreSystems/EntityComp/EntityState.cs | 58 +- .../CoreSystems/EntityComp/EntitySupport.cs | 1 - .../ModelSupport/RecursiveSubparts.cs | 1 - .../EntityComp/Parts/Control/ControlComp.cs | 1 - .../EntityComp/Parts/Control/ControlData.cs | 1 - .../EntityComp/Parts/Control/ControlFields.cs | 1 - .../EntityComp/Parts/PartFields.cs | 9 +- .../EntityComp/Parts/Support/SupportCharge.cs | 4 +- .../EntityComp/Parts/Support/SupportComp.cs | 1 - .../EntityComp/Parts/Support/SupportMisc.cs | 1 - .../EntityComp/Parts/Upgrade/UpgradeComp.cs | 1 - .../EntityComp/Parts/Weapon/WeaponAv.cs | 1 - .../EntityComp/Parts/Weapon/WeaponComp.cs | 1 - .../Parts/Weapon/WeaponController.cs | 110 +- .../EntityComp/Parts/Weapon/WeaponFields.cs | 83 -- .../EntityComp/Parts/Weapon/WeaponHand.cs | 3 +- .../EntityComp/Parts/Weapon/WeaponState.cs | 1 - .../EntityComp/Parts/Weapon/WeaponTracking.cs | 288 +--- .../EntityComp/Parts/Weapon/WeaponTypes.cs | 19 +- .../CoreSystems/EntityComp/PlatformInit.cs | 1 - Data/Scripts/CoreSystems/Projectiles/Dtree.cs | 11 - .../CoreSystems/Projectiles/Projectile.cs | 1 - .../CoreSystems/Projectiles/ProjectileGen.cs | 1 - .../CoreSystems/Projectiles/ProjectileHits.cs | 210 ++- .../Projectiles/ProjectileTypes.cs | 5 - .../CoreSystems/Session/SessionCompMgr.cs | 5 +- .../CoreSystems/Session/SessionControls.cs | 1 - .../CoreSystems/Session/SessionDamageMgr.cs | 38 - .../CoreSystems/Session/SessionEvents.cs | 2 - .../CoreSystems/Session/SessionEwar.cs | 1 - .../CoreSystems/Session/SessionInit.cs | 1 - .../CoreSystems/Session/SessionLocalPlayer.cs | 2 - .../CoreSystems/Session/SessionModHandlers.cs | 3 - .../CoreSystems/Session/SessionNetwork.cs | 5 - .../Session/SessionNetworkCMethods.cs | 17 +- .../Session/SessionNetworkSMethods.cs | 1 - .../Session/SessionNetworkSupport.cs | 22 - .../CoreSystems/Session/SessionSupport.cs | 72 - .../CoreSystems/Session/SessionUpdate.cs | 3 +- .../CoreSystems/Support/Draw/DrawExts.cs | 1 - .../CoreSystems/Support/DsAutoResetEvent.cs | 52 - .../CoreSystems/Support/GridIntersect.cs | 14 - Data/Scripts/CoreSystems/Support/Log.cs | 58 - Data/Scripts/CoreSystems/Support/MathFuncs.cs | 641 --------- Data/Scripts/CoreSystems/Support/Spawn.cs | 282 ---- .../CoreSystems/Support/StaticUtils.cs | 267 ---- Data/Scripts/CoreSystems/Support/Utils.cs | 1171 ++--------------- .../CoreSystems/Support/VersionControl.cs | 2 - Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs | 4 - Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs | 1 - Data/Scripts/CoreSystems/Ui/Hud/HudSupport.cs | 3 +- Data/Scripts/CoreSystems/Ui/Hud/HudText.cs | 1 - .../Ui/Targeting/TargetUiSelect.cs | 2 - Data/Scripts/CoreSystems/Ui/UiInput.cs | 4 +- 97 files changed, 421 insertions(+), 3752 deletions(-) delete mode 100644 Data/Scripts/CoreSystems/Support/DsAutoResetEvent.cs delete mode 100644 Data/Scripts/CoreSystems/Support/Spawn.cs diff --git a/CoreSystems.csproj b/CoreSystems.csproj index 4c21e724..7c7ce43b 100644 --- a/CoreSystems.csproj +++ b/CoreSystems.csproj @@ -430,9 +430,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -532,9 +529,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/Data/Scripts/CoreSystems/Ai/AiComp/AiCompRun.cs b/Data/Scripts/CoreSystems/Ai/AiComp/AiCompRun.cs index 0e6bcea3..99fead1d 100644 --- a/Data/Scripts/CoreSystems/Ai/AiComp/AiCompRun.cs +++ b/Data/Scripts/CoreSystems/Ai/AiComp/AiCompRun.cs @@ -1,5 +1,4 @@ -using System; -using VRage.Game.Components; +using VRage.Game.Components; namespace CoreSystems.Support { diff --git a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs index b7d3edc4..383257b5 100644 --- a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs +++ b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using CoreSystems.Platform; using CoreSystems.Projectiles; using Sandbox.Game.Entities; @@ -855,13 +854,9 @@ internal void Clean() { if (TotalEffect > 0) { - try - { - PlayerMap player; - var playerName = Session.I.Players.TryGetValue(Ai.AiOwner, out player) ? player.Player.DisplayName ?? string.Empty : string.Empty; - Log.Stats($"{Ai.TopEntity?.DisplayName}, {playerName}, {(long)TotalEffect}, {TotalPrimaryEffect}, {TotalAoeEffect}, {TotalShieldEffect}, {TotalProjectileEffect}", "griddmgstats"); - } - catch (Exception ex) { Log.Line($"Exception in ConstructClean: {ex}", null, true); } + PlayerMap player; + var playerName = Session.I.Players.TryGetValue(Ai.AiOwner, out player) ? player.Player.DisplayName ?? string.Empty : string.Empty; + Log.Stats($"{Ai.TopEntity?.DisplayName}, {playerName}, {(long)TotalEffect}, {TotalPrimaryEffect}, {TotalAoeEffect}, {TotalShieldEffect}, {TotalProjectileEffect}", "griddmgstats"); } if (WeaponGroups.Count > 0) diff --git a/Data/Scripts/CoreSystems/Ai/AiData.cs b/Data/Scripts/CoreSystems/Ai/AiData.cs index 19d2625b..4d305235 100644 --- a/Data/Scripts/CoreSystems/Ai/AiData.cs +++ b/Data/Scripts/CoreSystems/Ai/AiData.cs @@ -5,8 +5,6 @@ namespace CoreSystems { - using static Session; - public class AiData { public Ai Ai; diff --git a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs index 722ea21f..e832f22f 100644 --- a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs +++ b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs @@ -1,5 +1,4 @@ using System; -using CoreSystems.Platform; using Sandbox.Common.ObjectBuilders; using Sandbox.Game.Entities; using Sandbox.ModAPI; @@ -362,80 +361,75 @@ private bool TouchingSafeZone(MySafeZone safeZone) internal bool CreateEntInfo(MyEntity entity, long gridOwner, out Sandbox.ModAPI.Ingame.MyDetectedEntityInfo entInfo) { - - try + MyRelationsBetweenPlayerAndBlock relationship = MyRelationsBetweenPlayerAndBlock.Neutral; + if (entity == null) + { + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(); + return false; + } + var grid = entity.GetTopMostParent() as MyCubeGrid; + if (grid != null) { - MyRelationsBetweenPlayerAndBlock relationship = MyRelationsBetweenPlayerAndBlock.Neutral; - if (entity == null) + if (!grid.DestructibleBlocks || grid.Immune || grid.GridGeneralDamageModifier <= 0) { entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(); return false; } - var grid = entity.GetTopMostParent() as MyCubeGrid; - if (grid != null) - { - if (!grid.DestructibleBlocks || grid.Immune || grid.GridGeneralDamageModifier <= 0) - { - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(); - return false; - } - var bigOwners = grid.BigOwners; - var topOwner = bigOwners.Count > 0 ? bigOwners[0] : long.MaxValue; + var bigOwners = grid.BigOwners; + var topOwner = bigOwners.Count > 0 ? bigOwners[0] : long.MaxValue; - relationship = topOwner != long.MaxValue ? MyIDModule.GetRelationPlayerBlock(gridOwner, topOwner, MyOwnershipShareModeEnum.Faction) : MyRelationsBetweenPlayerAndBlock.NoOwnership; - var type = grid.GridSizeEnum != MyCubeSize.Small ? Sandbox.ModAPI.Ingame.MyDetectedEntityType.LargeGrid : Sandbox.ModAPI.Ingame.MyDetectedEntityType.SmallGrid; - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(grid.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); - return true; - } + relationship = topOwner != long.MaxValue ? MyIDModule.GetRelationPlayerBlock(gridOwner, topOwner, MyOwnershipShareModeEnum.Faction) : MyRelationsBetweenPlayerAndBlock.NoOwnership; + var type = grid.GridSizeEnum != MyCubeSize.Small ? Sandbox.ModAPI.Ingame.MyDetectedEntityType.LargeGrid : Sandbox.ModAPI.Ingame.MyDetectedEntityType.SmallGrid; + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(grid.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); + return true; + } - var myCharacter = entity as IMyCharacter; - if (myCharacter != null) - { - var type = !myCharacter.IsPlayer ? Sandbox.ModAPI.Ingame.MyDetectedEntityType.CharacterOther : Sandbox.ModAPI.Ingame.MyDetectedEntityType.CharacterHuman; + var myCharacter = entity as IMyCharacter; + if (myCharacter != null) + { + var type = !myCharacter.IsPlayer ? Sandbox.ModAPI.Ingame.MyDetectedEntityType.CharacterOther : Sandbox.ModAPI.Ingame.MyDetectedEntityType.CharacterHuman; - var getComponentOwner = entity as IMyComponentOwner; + var getComponentOwner = entity as IMyComponentOwner; - long playerId; - MyIDModule targetIdModule; - if (getComponentOwner != null && getComponentOwner.GetComponent(out targetIdModule)) - playerId = targetIdModule.Owner; - else { - var controllingId = myCharacter.ControllerInfo?.ControllingIdentityId; - playerId = controllingId ?? 0; - } + long playerId; + MyIDModule targetIdModule; + if (getComponentOwner != null && getComponentOwner.GetComponent(out targetIdModule)) + playerId = targetIdModule.Owner; + else { + var controllingId = myCharacter.ControllerInfo?.ControllingIdentityId; + playerId = controllingId ?? 0; + } - relationship = MyIDModule.GetRelationPlayerBlock(gridOwner, playerId, MyOwnershipShareModeEnum.Faction); - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); + relationship = MyIDModule.GetRelationPlayerBlock(gridOwner, playerId, MyOwnershipShareModeEnum.Faction); + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); - return !myCharacter.IsDead && myCharacter.Integrity > 0; - } + return !myCharacter.IsDead && myCharacter.Integrity > 0; + } - var myPlanet = entity as MyPlanet; + var myPlanet = entity as MyPlanet; - if (myPlanet != null) - { - const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Planet; - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); - return true; - } - if (entity is MyVoxelMap) - { - const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Asteroid; - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); - return true; - } - if (entity is MyMeteor) - { - const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Meteor; - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, MyRelationsBetweenPlayerAndBlock.Enemies, new BoundingBoxD(), Session.I.Tick); - return true; - } + if (myPlanet != null) + { + const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Planet; + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); + return true; + } + if (entity is MyVoxelMap) + { + const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Asteroid; + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, relationship, new BoundingBoxD(), Session.I.Tick); + return true; + } + if (entity is MyMeteor) + { + const Sandbox.ModAPI.Ingame.MyDetectedEntityType type = Sandbox.ModAPI.Ingame.MyDetectedEntityType.Meteor; + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(entity.EntityId, string.Empty, type, null, MatrixD.Zero, Vector3.Zero, MyRelationsBetweenPlayerAndBlock.Enemies, new BoundingBoxD(), Session.I.Tick); + return true; } - catch (Exception ex) { Log.Line($"Exception in CreateEntInfo: {ex}", null, true); } - entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(); - return false; + entInfo = new Sandbox.ModAPI.Ingame.MyDetectedEntityInfo(); + return false; } } } diff --git a/Data/Scripts/CoreSystems/Ai/AiEvents.cs b/Data/Scripts/CoreSystems/Ai/AiEvents.cs index 4b6d57b9..a808a82f 100644 --- a/Data/Scripts/CoreSystems/Ai/AiEvents.cs +++ b/Data/Scripts/CoreSystems/Ai/AiEvents.cs @@ -2,13 +2,11 @@ using System.Collections.Generic; using Sandbox.Game; using Sandbox.Game.Entities; -using Sandbox.Game.Entities.Interfaces; using Sandbox.ModAPI; using VRage; using VRage.Collections; using VRage.Game.Entity; using VRage.Game.ModAPI; -using VRage.Utils; using VRageMath; namespace CoreSystems.Support @@ -185,10 +183,7 @@ internal void FatBlockAdded(MyCubeBlock cube) if (!ModOverride && Session.I.IsPartAreaRestricted(cube.BlockDefinition.Id.SubtypeId, blockBox, cube.CubeGrid, cube.EntityId, null, out b, out s)) { if (Session.I.IsServer) - { Session.I.FutureEvents.Schedule(QueuedBlockRemoval, cube, 10); - //cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); - } } //Projected block ammo removal diff --git a/Data/Scripts/CoreSystems/Ai/AiFields.cs b/Data/Scripts/CoreSystems/Ai/AiFields.cs index 230bf5a9..6c1fe363 100644 --- a/Data/Scripts/CoreSystems/Ai/AiFields.cs +++ b/Data/Scripts/CoreSystems/Ai/AiFields.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using CoreSystems.Platform; using CoreSystems.Projectiles; diff --git a/Data/Scripts/CoreSystems/Ai/AiSupport.cs b/Data/Scripts/CoreSystems/Ai/AiSupport.cs index 6fa1ae08..a49b6e5f 100644 --- a/Data/Scripts/CoreSystems/Ai/AiSupport.cs +++ b/Data/Scripts/CoreSystems/Ai/AiSupport.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using CoreSystems.Platform; using CoreSystems.Projectiles; using Sandbox.Game.EntityComponents; @@ -68,7 +67,7 @@ internal void CompChange(bool add, CoreComponent comp) WeaponIdx[WeaponComps[weaponIdx]] = weaponIdx; WeaponIdx.Remove(wComp); - if (wCompMaxWepRange >= (MaxTargetingRange - TopEntity.PositionComp.LocalVolume.Radius) * 0.95) //Filter so that only the longest ranged weps force a recalc + if (wCompMaxWepRange >= (MaxTargetingRange - TopEntity.PositionComp.LocalVolume.Radius) * 0.95) UpdateMaxTargetingRange(); if (wComp.Data.Repo.Values.Set.Overrides.WeaponGroupId > 0) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index f12e6d28..5fe79c06 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using System.Xml; using CoreSystems.Platform; using CoreSystems.Projectiles; using Sandbox.Game.Entities; diff --git a/Data/Scripts/CoreSystems/Ai/AiTypes.cs b/Data/Scripts/CoreSystems/Ai/AiTypes.cs index 874af0d1..e05287fa 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTypes.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTypes.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using CoreSystems.Platform; using Sandbox.Game.Entities; -using Sandbox.Game.World; -using Sandbox.ModAPI; using Sandbox.ModAPI.Ingame; using VRage.Collections; using VRage.Game; @@ -12,7 +9,6 @@ using VRage.Game.ModAPI; using VRage.Utils; using VRageMath; -using CollisionLayers = Sandbox.Engine.Physics.MyPhysics.CollisionLayers; namespace CoreSystems.Support { public partial class Ai @@ -366,7 +362,7 @@ internal void Clean(Ai ai) DroneCount = 0; var rootConstruct = ai.Construct.RootAi.Construct; - if (rootConstruct.DroneCount != 0 && Session.I.Tick - rootConstruct.LastDroneTick > 30) //Was 200, dropped for faster updates on current threats + if (rootConstruct.DroneCount != 0 && Session.I.Tick - rootConstruct.LastDroneTick > 30) rootConstruct.DroneCleanup(); } } diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs index 94222dbe..dca1bc67 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using CoreSystems.Platform; using Sandbox.ModAPI; using VRage; using VRage.Collections; diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBlocks.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBlocks.cs index ac20ccce..ba582a31 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBlocks.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBlocks.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; using Sandbox.ModAPI; -using VRage; -using VRage.Game.Entity; -using VRage.ModAPI; -using VRageMath; namespace CoreSystems.Api { diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiDefs.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiDefs.cs index c8328ac2..e0a169bf 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiDefs.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiDefs.cs @@ -1,9 +1,6 @@ using System.Collections.Generic; using ProtoBuf; using VRageMath; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.LineDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.TrajectoryDef.ApproachDef; -using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; namespace CoreSystems.Api { diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index 8c2481b9..c1786877 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -959,7 +959,7 @@ internal void HitEffects(bool force = false) if (Hit.EventType == HitEntity.Type.Water) { - HitParticleActive = true;//FML... didn't know there was rand for impacts. + HitParticleActive = true; } if (OnScreen == Screen.Tracer || AmmoDef.Const.HitParticleNoCull || distToCameraSqr < 360000) { @@ -1410,7 +1410,6 @@ internal void UpdateCache(AvInfoCache avInfoCache) internal void Close() { - // Reset only vars that are not always set Hit = new Hit(); EndState = new AvClose(); diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 85aa356a..96cf2909 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -173,7 +173,6 @@ internal void End() } if (av.Hit.Entity != null && av.AmmoDef.AmmoGraphics.Decals.MaxAge > 0 && !Vector3D.IsZero(av.Hit.SurfaceHit) && av.AmmoDef.Const.TextureHitMap.Count > 0 && !av.Hit.Entity.MarkedForClose && av.Hit.Entity.InScene) { - //Starcore bug report on decals, parent "not found" and world position goes to NaN var shield = av.Hit.Entity as IMyUpgradeModule; var floating = av.Hit.Entity as MyFloatingObject; if (shield == null && floating == null) @@ -201,10 +200,6 @@ internal void End() Position = av.Hit.SurfaceHit + (av.Direction * 0.01), Normal = av.Direction, }; - - - //Log.Line($"Decal: {av.Hit.Entity.DebugName} mat{materialType} closed?{av.Hit.Entity.Closed} posCompNull?{av.Hit.Entity.PositionComp != null}"); - MyDecals.HandleAddDecal(av.Hit.Entity, hitInfo, Vector3.Zero, materialType, projectileMaterial, null, -1, voxelMaterial, false, MyDecalFlags.IgnoreOffScreenDeletion, MyAPIGateway.Session.GameplayFrameCounter + av.AmmoDef.AmmoGraphics.Decals.MaxAge); } } @@ -212,7 +207,7 @@ internal void End() if (av.Hit.EventType == HitEntity.Type.Water) { - var splashHit = av.Hit.SurfaceHit;//Hopefully we can get a more precise surface intercept or correction? + var splashHit = av.Hit.SurfaceHit; var ammoInfo = av.AmmoDef; var radius = ammoInfo.Const.CollisionSize > ammoInfo.Const.LargestHitSize ? (float)ammoInfo.Const.CollisionSize : (float)ammoInfo.Const.LargestHitSize; if (radius < 3) diff --git a/Data/Scripts/CoreSystems/Comms/Spectrum.cs b/Data/Scripts/CoreSystems/Comms/Spectrum.cs index 8b8c8e0c..c01d3dfc 100644 --- a/Data/Scripts/CoreSystems/Comms/Spectrum.cs +++ b/Data/Scripts/CoreSystems/Comms/Spectrum.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using CoreSystems; using VRage.Utils; using WeaponCore.Data.Scripts.CoreSystems.Support; diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs index 34757ce7..3fafc203 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs @@ -1,16 +1,9 @@ using static CoreSystems.Support.WeaponDefinition; using static CoreSystems.Support.WeaponDefinition.AmmoDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EjectionDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EjectionDef.SpawnType; using static CoreSystems.Support.WeaponDefinition.AmmoDef.ShapeDef.Shapes; using static CoreSystems.Support.WeaponDefinition.AmmoDef.DamageScaleDef.CustomScalesDef.SkipMode; using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef; using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.DecalDef; - -using static CoreSystems.Support.WeaponDefinition.AmmoDef.FragmentDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.PatternDef.PatternModes; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.FragmentDef.TimedSpawnDef.PointTypes; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.TrajectoryDef; using static CoreSystems.Support.WeaponDefinition.AmmoDef.TrajectoryDef.GuidanceType; using static CoreSystems.Support.WeaponDefinition.AmmoDef.DamageScaleDef; using static CoreSystems.Support.WeaponDefinition.AmmoDef.DamageScaleDef.ShieldDef.ShieldType; @@ -18,12 +11,7 @@ using static CoreSystems.Support.WeaponDefinition.AmmoDef.AreaOfDamageDef; using static CoreSystems.Support.WeaponDefinition.AmmoDef.AreaOfDamageDef.Falloff; using static CoreSystems.Support.WeaponDefinition.AmmoDef.AreaOfDamageDef.AoeShape; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EwarDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EwarDef.EwarMode; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EwarDef.EwarType; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.EwarDef.PushPullDef.Force; using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.LineDef; -using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.LineDef.TracerBaseDef; using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.LineDef.Texture; using static CoreSystems.Support.WeaponDefinition.AmmoDef.DamageScaleDef.DamageTypes.Damage; namespace Scripts diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs index ebe9a7e8..3a56215a 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Artillery.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs index 0d67d1dd..8851e0bf 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Assault Cannons.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs index ac247cf2..4b6b2484 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Autocannons.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs index 8b678d31..ae203a13 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Gatlings.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs index 5d5fe2af..cfd72ecb 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/InteriorTurret.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/MasterConfig.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/MasterConfig.cs index 2b4b5d23..4cfe0b1f 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/MasterConfig.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/MasterConfig.cs @@ -5,11 +5,6 @@ partial class Parts { internal Parts() { - // naming convention: WeaponDefinition Name - // - // Enable your definitions using the follow syntax: - // PartDefinitions(Your1stDefinition, Your2ndDefinition, Your3rdDefinition); - // PartDefinitions includes both weapons and phantoms PartDefinitions(LargeGatlingTurret, SmallGatlingGun, SmallGatlingTurret, diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs index 1c76cfa5..0c8ae9d2 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Missiles.cs @@ -5,7 +5,6 @@ using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/RailgunAnimations.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/RailgunAnimations.cs index fde06b65..09b323a9 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/RailgunAnimations.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/RailgunAnimations.cs @@ -2,8 +2,6 @@ using static CoreSystems.Support.WeaponDefinition; using static CoreSystems.Support.WeaponDefinition.AnimationDef; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef.EventTriggers; -using static CoreSystems.Support.WeaponDefinition.AnimationDef.RelMove.MoveType; -using static CoreSystems.Support.WeaponDefinition.AnimationDef.RelMove; namespace Scripts { // Don't edit above this line partial class Parts diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs index f2e9eebc..d839f6b2 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/Railguns.cs @@ -2,10 +2,8 @@ using static CoreSystems.Support.WeaponDefinition; using static CoreSystems.Support.WeaponDefinition.ModelAssignmentsDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs index 8915520d..4ffbe471 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/SearchLight.cs @@ -2,10 +2,8 @@ using static CoreSystems.Support.WeaponDefinition; using static CoreSystems.Support.WeaponDefinition.ModelAssignmentsDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.Prediction; using static CoreSystems.Support.WeaponDefinition.TargetingDef.BlockTypes; using static CoreSystems.Support.WeaponDefinition.TargetingDef.Threat; -using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef; using static CoreSystems.Support.WeaponDefinition.HardPointDef.HardwareDef.HardwareType; namespace Scripts { diff --git a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs index 315ecc06..f5dd722d 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs @@ -781,22 +781,10 @@ internal WeaponConstants(WeaponDefinition values) if (wO.MinTargetDistance.HasValue) MinTargetDistance = Math.Max(wO.MinTargetDistance.Value, 0f); if (wO.RateOfFire.HasValue) RateOfFire = Math.Max(wO.RateOfFire.Value, 0); if (wO.ReloadTime.HasValue) ReloadTime = Math.Max(wO.ReloadTime.Value, 0); - if (wO.DeviateShotAngle.HasValue) DeviateShotAngleRads = MathHelper.ToRadians(Math.Max(wO.DeviateShotAngle.Value, 0f)); if (wO.AimingTolerance.HasValue) AimingToleranceRads = MathHelperD.ToRadians(wO.AimingTolerance.Value <= 0 ? 180 : wO.AimingTolerance.Value); - //if (wO.InventorySize.HasValue) - if (wO.HeatPerShot.HasValue) HeatPerShot = Math.Max(wO.HeatPerShot.Value, 0); - //if (wO.MaxHeat.HasValue) if (wO.HeatSinkRate.HasValue) HeatSinkRate = Math.Max(wO.HeatSinkRate.Value, 0); - //if (wO.Cooldown.HasValue) - - //if (wO.ConstructPartCap.HasValue) - //if (wO.RestrictionRadius.HasValue) - //if (wO.CheckInflatedBox.HasValue) - //if (wO.CheckForAnyWeapon.HasValue) - //if (wO.MuzzleCheck.HasValue) - if (wO.IdlePower.HasValue) IdlePower = Math.Max(wO.IdlePower.Value, 0.001f); } } diff --git a/Data/Scripts/CoreSystems/Definitions/PartAnimation.cs b/Data/Scripts/CoreSystems/Definitions/PartAnimation.cs index 193b3fcc..b390cda7 100644 --- a/Data/Scripts/CoreSystems/Definitions/PartAnimation.cs +++ b/Data/Scripts/CoreSystems/Definitions/PartAnimation.cs @@ -61,7 +61,6 @@ public struct EmissiveState internal bool Running; internal bool Triggered; internal bool CanPlay; - //internal bool Paused; internal uint StartTick; internal List PlayTicks; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AiValues.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AiValues.cs index 97c3dad6..17a905d8 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AiValues.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AiValues.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using ProtoBuf; +using ProtoBuf; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/ConstructValues.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/ConstructValues.cs index 7c94db5d..17d56e20 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/ConstructValues.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/ConstructValues.cs @@ -6,7 +6,6 @@ namespace CoreSystems [ProtoContract] public class ConstructDataValues { - //[ProtoMember(1)] public int Version = Session.VersionControl; [ProtoMember(2)] public FocusData FocusData; public bool Sync(Constructs construct, ConstructDataValues sync, bool localCall = false) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Control/ProtoControl.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Control/ProtoControl.cs index 17dcccf4..d9243301 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Control/ProtoControl.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Control/ProtoControl.cs @@ -1,11 +1,8 @@ using CoreSystems.Platform; using CoreSystems.Support; using ProtoBuf; -using Sandbox.ModAPI; -using System; using System.ComponentModel; using static CoreSystems.Support.CoreComponent; -using static CoreSystems.Support.WeaponDefinition.TargetingDef; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Misc.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Misc.cs index b7e817ac..010108ad 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Misc.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Misc.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using CoreSystems.Platform; using CoreSystems.Support; using ProtoBuf; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems { @@ -63,9 +61,6 @@ internal class PlayerMouseData [ProtoContract] public class WeaponRandomGenerator { - //[ProtoMember(1)] public int TurretCurrentCounter; - //[ProtoMember(2)] public int ClientProjectileCurrentCounter; - //[ProtoMember(3)] public int AcquireCurrentCounter; [ProtoMember(4)] public int CurrentSeed; public XorShiftRandomStruct TurretRandom; public XorShiftRandomStruct AcquireRandom; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/PacketTypes.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/PacketTypes.cs index 68461593..1bb080ef 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/PacketTypes.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/PacketTypes.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; using CoreSystems.Settings; -using CoreSystems.Support; using ProtoBuf; using VRageMath; using static CoreSystems.Support.CoreComponent; @@ -106,7 +104,7 @@ public enum PacketType [ProtoInclude(40, typeof(ControlStatePacket))] [ProtoInclude(41, typeof(BlackListPacket))] [ProtoInclude(42, typeof(DronePacket))] - [ProtoInclude(43, typeof(HandWeaponDebugPacket))] + //[ProtoInclude(43, typeof(HandWeaponDebugPacket))] [ProtoInclude(44, typeof(PingPacket))] [ProtoInclude(45, typeof(ProjectileSyncTargetPacket))] @@ -155,22 +153,6 @@ public override void CleanUp() } } - [ProtoContract] - public class HandWeaponDebugPacket : Packet - { - [ProtoMember(1)] internal uint LastHitTick = uint.MaxValue; - [ProtoMember(2)] internal uint LastShootTick = uint.MaxValue; - [ProtoMember(3)] internal Vector3D ShootStart; - [ProtoMember(4)] internal Vector3D ShootEnd; - [ProtoMember(5)] internal Vector3D HitStart; - [ProtoMember(6)] internal Vector3D HitEnd; - - public override void CleanUp() - { - base.CleanUp(); - } - } - [ProtoContract] public class ProjectileSyncPositionPacket : Packet { diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Upgrade/ProtoUpgrade.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Upgrade/ProtoUpgrade.cs index 0ac7037a..627141bf 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Upgrade/ProtoUpgrade.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/Upgrade/ProtoUpgrade.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel; using CoreSystems.Platform; -using CoreSystems.Support; using ProtoBuf; using static CoreSystems.Support.WeaponDefinition.TargetingDef; using static CoreSystems.Support.CoreComponent; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs index 5b1c3325..a6d50366 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlActions.cs @@ -1,5 +1,4 @@ -using System; -using System.Text; +using System.Text; using CoreSystems.Platform; using CoreSystems.Support; using Sandbox.ModAPI; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlUi.cs index 8e98baea..fe404444 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Control/ControlUi.cs @@ -4,11 +4,9 @@ using CoreSystems.Support; using Sandbox.Game.Entities; using Sandbox.ModAPI; -using VRage.Game; using VRage.Game.Entity; using VRage.ModAPI; using VRage.Utils; -using VRageMath; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index 2c63f210..6f96a3ca 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -609,43 +609,6 @@ public static void CreateDecoy(Session session) session.CustomActions.Add(action); } - internal static void CreateOnOffActionSet(Session session, IMyTerminalControlOnOffSwitch tc, string name, Func enabler, bool group = false) - { - var action0 = MyAPIGateway.TerminalControls.CreateAction($"WC_{name}_Toggle"); - action0.Icon = @"Textures\GUI\Icons\Actions\Toggle.dds"; - action0.Name = new StringBuilder(Localization.GetTextWithoutFallback($"{name} Toggle On/Off")); - action0.Action = b => tc.Setter(b, !tc.Getter(b)); - action0.Writer = (b, t) => t.Append(tc.Getter(b) ? tc.OnText : tc.OffText); - action0.Enabled = enabler; - action0.ValidForGroups = group; - - MyAPIGateway.TerminalControls.AddAction(action0); - session.CustomActions.Add(action0); - - var action1 = MyAPIGateway.TerminalControls.CreateAction($"WC_{name}_Toggle_On"); - action1.Icon = @"Textures\GUI\Icons\Actions\SwitchOn.dds"; - action1.Name = new StringBuilder(Localization.GetTextWithoutFallback($"{name} On")); - action1.Action = b => tc.Setter(b, true); - action1.Writer = (b, t) => t.Append(tc.Getter(b) ? tc.OnText : tc.OffText); - action1.Enabled = enabler; - action1.ValidForGroups = group; - - MyAPIGateway.TerminalControls.AddAction(action1); - session.CustomActions.Add(action1); - - var action2 = MyAPIGateway.TerminalControls.CreateAction($"WC_{name}_Toggle_Off"); - action2.Icon = @"Textures\GUI\Icons\Actions\SwitchOff.dds"; - action2.Name = new StringBuilder(Localization.GetTextWithoutFallback($"{name} Off")); - action2.Action = b => tc.Setter(b, true); - action2.Writer = (b, t) => t.Append(tc.Getter(b) ? tc.OnText : tc.OffText); - action2.Enabled = enabler; - action2.ValidForGroups = group; - - MyAPIGateway.TerminalControls.AddAction(action2); - session.CustomActions.Add(action2); - - } - internal static void CreateOnOffActionSet(Session session, IMyTerminalControlCheckbox tc, string name, Func enabler, bool group = false) { var action0 = MyAPIGateway.TerminalControls.CreateAction($"WC_{name}_Toggle"); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 67d67f68..431cca51 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -6,10 +6,8 @@ using Sandbox.ModAPI; using Sandbox.ModAPI.Interfaces.Terminal; using SpaceEngineers.Game.ModAPI; -using VRage.Game.Entity; using VRage.ModAPI; using VRage.Utils; -using VRageMath; namespace CoreSystems.Control { @@ -17,15 +15,9 @@ public static class TerminalHelpers { internal static void AddUiControls(Session session) where T : IMyTerminalBlock { - - //AddComboboxNoAction(session, "Shoot Mode", Localization.GetText("TerminalShootModeTitle"), Localization.GetText("TerminalShootModeTooltip"), BlockUi.GetShootModes, BlockUi.RequestShootModes, BlockUi.ListShootModesNoBurst, Istrue); AddComboboxNoAction(session, "Shoot Mode", Localization.GetText("TerminalShootModeTitle"), Localization.GetText("TerminalShootModeTooltip"), BlockUi.GetShootModes, BlockUi.RequestShootModes, BlockUi.ListShootModes, IsNotBomb); - AddSliderRof(session, "Weapon ROF", Localization.GetText("TerminalWeaponROFTitle"), Localization.GetText("TerminalWeaponROFTooltip"), BlockUi.GetRof, BlockUi.RequestSetRof, UiRofSlider, BlockUi.GetMinRof, BlockUi.GetMaxRof); - AddCheckbox(session, "Overload", Localization.GetText("TerminalOverloadTitle"), Localization.GetText("TerminalOverloadTooltip"), BlockUi.GetOverload, BlockUi.RequestSetOverload, true, UiOverLoad); - - AddWeaponCrticalTimeSliderRange(session, "Detonation", Localization.GetText("TerminalDetonationTitle"), Localization.GetText("TerminalDetonationTooltip"), BlockUi.GetArmedTimer, BlockUi.RequestSetArmedTimer, NotCounting, CanBeArmed, BlockUi.GetMinCriticalTime, BlockUi.GetMaxCriticalTime, true); AddButtonNoAction(session, "StartCount", Localization.GetText("TerminalStartCountTitle"), Localization.GetText("TerminalStartCountTooltip"), BlockUi.StartCountDown, NotCounting, CanBeArmed); AddButtonNoAction(session, "StopCount", Localization.GetText("TerminalStopCountTitle"), Localization.GetText("TerminalStopCountTooltip"), BlockUi.StopCountDown, IsCounting, CanBeArmed); @@ -36,8 +28,6 @@ internal static void AddUiControls(Session session) where T : IMyTerminalBloc internal static void AddTurretOrTrackingControls(Session session) where T : IMyTerminalBlock { - //Enenenenennennnenennnennennnnnnenenenenennenera's order - AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); AddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementMode, BlockUi.RequestMovementMode, BlockUi.ListMovementModes, HasTracking); @@ -77,43 +67,6 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I AddListBoxNoAction(session, "Friend", "Friend", "Friend list", BlockUi.FriendFill, BlockUi.FriendSelect, IsDrone, 1, true, true); AddListBoxNoAction(session, "Enemy", "Enemy", "Enemy list", BlockUi.EnemyFill, BlockUi.EnemySelect, IsDrone, 1, true, true); AddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTarget, BlockUi.RequestSetReportTarget, true, UiReportTarget); - - - /* - AddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlMode, BlockUi.RequestControlMode, BlockUi.ListControlModes, TurretOrGuidedAmmo); - AddComboboxNoAction(session, "ObjectiveMode", Localization.GetText("TerminalObjectiveTitle"), Localization.GetText("TerminalObjectiveTooltip"), BlockUi.GetObjectiveMode, BlockUi.RequestObjectiveMode, BlockUi.ListObjectiveModes, HasTracking); - AddComboboxNoAction(session, "PickAmmo", Localization.GetText("TerminalPickAmmoTitle"), Localization.GetText("TerminalPickAmmoTooltip"), BlockUi.GetAmmos, BlockUi.RequestSetAmmo, BlockUi.ListAmmos, AmmoSelection); - AddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystems, BlockUi.RequestSetSubSystems, true, HasTracking); - AddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystem, BlockUi.RequestSubSystem, BlockUi.ListSubSystems, HasTracking); - AddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementMode, BlockUi.RequestMovementMode, BlockUi.ListMovementModes, HasTracking); - AddWeaponRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRange, BlockUi.RequestSetRange, BlockUi.ShowRange, BlockUi.GetMinRange, BlockUi.GetMaxRange, true, false); - Separator(session, "WC_sep2", HasTracking); - AddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTarget, BlockUi.RequestSetReportTarget, true, UiReportTarget); - AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); - AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); - AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); - AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); - AddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPD, BlockUi.RequestSetSupportingPD, true, UiDisableSupportingPD); - AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); - AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTracking); - AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); - AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); - Separator(session, "WC_sep3", IsTrue); - AddWeaponBurstCountSliderRange(session, "Burst Count", Localization.GetText("TerminalBurstShotsTitle"), Localization.GetText("TerminalBurstShotsTooltip"), BlockUi.GetBurstCount, BlockUi.RequestSetBurstCount, CanBurstIsNotBomb, BlockUi.GetMinBurstCount, BlockUi.GetMaxBurstCount, true); - AddWeaponBurstDelaySliderRange(session, "Burst Delay", Localization.GetText("TerminalBurstDelayTitle"), Localization.GetText("TerminalBurstDelayTooltip"), BlockUi.GetBurstDelay, BlockUi.RequestSetBurstDelay, AllowShotDelay, BlockUi.GetMinBurstDelay, BlockUi.GetMaxBurstDelay, true); - AddWeaponSequenceIdSliderRange(session, "Sequence Id", Localization.GetText("TerminalSequenceIdTitle"), Localization.GetText("TerminalSequenceIdTooltip"), BlockUi.GetSequenceId, BlockUi.RequestSetSequenceId, IsNotBomb, BlockUi.GetMinSequenceId, BlockUi.GetMaxSequenceId, false); - AddWeaponGroupIdIdSliderRange(session, "Weapon Group Id", Localization.GetText("TerminalWeaponGroupIdTitle"), Localization.GetText("TerminalWeaponGroupIdTooltip"), BlockUi.GetWeaponGroupId, BlockUi.RequestSetWeaponGroupId, IsNotBomb, BlockUi.GetMinWeaponGroupId, BlockUi.GetMaxWeaponGroupId, true); - Separator(session, "WC_sep4", IsTrue); - AddLeadGroupSliderRange(session, "Target Group", Localization.GetText("TerminalTargetGroupTitle"), Localization.GetText("TerminalTargetGroupTooltip"), BlockUi.GetLeadGroup, BlockUi.RequestSetLeadGroup, TargetLead, BlockUi.GetMinLeadGroup, BlockUi.GetMaxLeadGroup, true); - AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); - AddListBoxNoAction(session, "Friend", "Friend", "Friend list", BlockUi.FriendFill, BlockUi.FriendSelect, IsDrone, 1, true, true); - AddListBoxNoAction(session, "Enemy", "Enemy", "Enemy list", BlockUi.EnemyFill, BlockUi.EnemySelect, IsDrone, 1, true, true); - //AddListBoxNoAction(session, "Position", "Position", "Position list", BlockUi.PositionFill, BlockUi.PositionSelect, IsDrone, 1, true, true); Suppressed for now as it's inop - Separator(session, "WC_sep5", HasTracking); - //AddOnOffSwitchNoAction(session, "Friendly", Localization.GetText("TerminalFriendlyTitle"), Localization.GetText("TerminalFriendlyTooltip"), BlockUi.GetFriendly, BlockUi.RequestSetFriendly, true, HasTrackingAndTrackFriendly); - */ } @@ -162,14 +115,8 @@ internal static void AddTurretControlBlockControls(Session session) where T : CtcAddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementModeControl, BlockUi.RequestMovementModeControl, BlockUi.ListMovementModes, CtcIsReady); - //CtcAddComboboxNoAction(session, "Shoot Mode", Localization.GetText("TerminalShootModeTitle"), Localization.GetText("TerminalShootModeTooltip"), BlockUi.CtcGetShootModes, BlockUi.CtcRequestShootModes, BlockUi.CtcListShootModes, CtcIsReady); - CtcAddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlModeControl, BlockUi.RequestControlModeControl, BlockUi.ListControlModes, CtcIsReady); - //AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); - - //AddLeadGroupSliderRange(session, "Target Group", Localization.GetText("TerminalTargetGroupTitle"), Localization.GetText("TerminalTargetGroupTooltip"), BlockUi.GetLeadGroup, BlockUi.RequestSetLeadGroup, TargetLead, BlockUi.GetMinLeadGroup, BlockUi.GetMaxLeadGroup, true); - Separator(session, "WC_sep4", IsTrue); } @@ -189,8 +136,6 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); - //AddOnOffSwitchNoAction(session, "Friendly", Localization.GetText("TerminalFriendlyTitle"), Localization.GetText("TerminalFriendlyTooltip"), BlockUi.GetFriendly, BlockUi.RequestSetFriendly, true, HasTrackingAndTrackFriendly); - AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); @@ -519,17 +464,6 @@ internal static bool HasTrackingNeutrals(IMyTerminalBlock block) return (comp.HasTracking || comp.HasGuidance) && !comp.HasAlternateUi; } - internal static bool HasTrackingAndTrackFriendly(IMyTerminalBlock block) - { - var comp = block?.Components?.Get() as Weapon.WeaponComponent; - - var valid = comp != null && comp.Platform.State == CorePlatform.PlatformState.Ready && comp.Data?.Repo != null; - - if (!valid || Session.I.PlayerId != comp.Data.Repo.Values.State.PlayerId && !comp.TakeOwnerShip()) - return false; - - return (comp.HasTracking || comp.HasGuidance || comp.HasAlternateUi); - } internal static bool IsArmed(IMyTerminalBlock block) { @@ -903,26 +837,6 @@ internal static IMyTerminalControlSlider AddLeadGroupSliderRange(Session sess return c; } - internal static IMyTerminalControlOnOffSwitch AddWeaponOnOff(Session session, string name, string title, string tooltip, string onText, string offText, Func getter, Action setter, Func visibleGetter) where T : IMyTerminalBlock - { - var c = MyAPIGateway.TerminalControls.CreateControl($"WC_Enable"); - - c.Title = MyStringId.GetOrCompute(title); - c.Tooltip = MyStringId.GetOrCompute(tooltip); - c.OnText = MyStringId.GetOrCompute(onText); - c.OffText = MyStringId.GetOrCompute(offText); - c.Enabled = IsReady; - c.Visible = visibleGetter; - c.Getter = IsReady; - c.Setter = setter; - MyAPIGateway.TerminalControls.AddControl(c); - session.CustomControls.Add(c); - - CreateCustomActions.CreateOnOffActionSet(session, c, name, visibleGetter); - - return c; - } - internal static IMyTerminalControlSeparator Separator(Session session, string name, Func visibleGettter) where T : IMyTerminalBlock { var c = MyAPIGateway.TerminalControls.CreateControl(name); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs index fe685d77..71618102 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponUi.cs @@ -9,8 +9,6 @@ using VRage.ModAPI; using VRage.Utils; using VRageMath; -using WeaponCore.Data.Scripts.CoreSystems.Ui.Targeting; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 8387f3fc..8bc04864 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; -using CoreSystems.Control; using CoreSystems.Platform; using CoreSystems.Projectiles; using Sandbox.Game.Entities; @@ -12,10 +11,8 @@ using VRage.Game; using VRage.Game.Entity; using VRage.Game.ModAPI; -using VRageMath; using static CoreSystems.Platform.CorePlatform; using static CoreSystems.Session; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Support { @@ -146,12 +143,6 @@ private void OnContentsChanged(MyInventoryBase inv, MyPhysicalInventoryItem item } } - private static void OnMarkForClose(MyEntity myEntity) - { - - } - - private void IsWorkingChanged(MyCubeBlock myCubeBlock) { var wasFunctional = IsFunctional; @@ -221,227 +212,111 @@ internal string GetSystemStatus() private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder stringBuilder) { - try + var comp = ((Weapon.WeaponComponent)this); + /* + var r = "[color=#DDFF0000]"; //ARGB in hex values + var y = "[color=#DDFFFF00]"; + var g = "[color=#DD00FF00]"; + var e = "[/color]"; + stringBuilder.Append($"{r}Red{e} {y}Yellow{e} {g}Green{e}"); + */ + + var collection = comp.HasAlternateUi ? SortAndGetTargetTypes() : TypeSpecific != CompTypeSpecific.Phantom ? Platform.Weapons : Platform.Phantoms; + var debug = Debug || comp.Data.Repo.Values.Set.Overrides.Debug; + var advanced = (I.Settings.ClientConfig.AdvancedMode || debug) && !comp.HasAlternateUi; + if (HasServerOverrides) + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoServerModdedLine1")}\n") + .Append($"\n{Localization.GetText("WeaponInfoServerModdedLine2")}"); + + //Start of new formatting + if (IdlePower > 0.01) + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoIdlePower")}: {IdlePower:0.00} {Localization.GetText("WeaponInfoMWLabel")}"); + + for (int i = 0; i < collection.Count; i++) { - var comp = ((Weapon.WeaponComponent)this); - /* - var r = "[color=#DDFF0000]"; //ARGB in hex values - var y = "[color=#DDFFFF00]"; - var g = "[color=#DD00FF00]"; - var e = "[/color]"; - stringBuilder.Append($"{r}Red{e} {y}Yellow{e} {g}Green{e}"); - */ - - var collection = comp.HasAlternateUi ? SortAndGetTargetTypes() : TypeSpecific != CompTypeSpecific.Phantom ? Platform.Weapons : Platform.Phantoms; - var debug = Debug || comp.Data.Repo.Values.Set.Overrides.Debug; - var advanced = (I.Settings.ClientConfig.AdvancedMode || debug) && !comp.HasAlternateUi; - if (HasServerOverrides) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoServerModdedLine1")}\n") - .Append($"\n{Localization.GetText("WeaponInfoServerModdedLine2")}"); - - //Start of new formatting - if (IdlePower > 0.01) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoIdlePower")}: {IdlePower:0.00} {Localization.GetText("WeaponInfoMWLabel")}"); - - for (int i = 0; i < collection.Count; i++) + var w = collection[i]; + string shots = ""; + if ((w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo || w.ActiveAmmoDef.AmmoDef.Const.IsHybrid) && !comp.HasAlternateUi) { - var w = collection[i]; - string shots = ""; - if ((w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo || w.ActiveAmmoDef.AmmoDef.Const.IsHybrid) && !comp.HasAlternateUi) - { - var chargeTime = w.AssignedPower > 0 ? (int)((w.MaxCharge - w.ProtoWeaponAmmo.CurrentCharge) / w.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; - shots += $"\n{Localization.GetText("WeaponInfoDrawOverMax")}: {SinkPower - IdlePower:0.00}/ {w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick:0.00} {Localization.GetText("WeaponInfoMWLabel")}" + - $"\n{(chargeTime == 0 ? Localization.GetText("WeaponInfoPowerCharged") : Localization.GetText("WeaponInfoPowerChargedIn") + " " + chargeTime + Localization.GetText("WeaponInfoSeconds"))}"; - } - - var endReturn = i + 1 != collection.Count ? "\n" : string.Empty; - var timeToLoad = (int)(w.ReloadEndTick - Session.I.Tick) / 60; - var showName = w.ActiveAmmoDef.AmmoDef.Const.TerminalName != w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText; - var displayName = showName ? w.ActiveAmmoDef.AmmoDef.Const.TerminalName + " (" + w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : w.ActiveAmmoDef.AmmoDef.Const.TerminalName; - stringBuilder.Append($"\n\n" + w.System.PartName + - shots + - $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + - endReturn); + var chargeTime = w.AssignedPower > 0 ? (int)((w.MaxCharge - w.ProtoWeaponAmmo.CurrentCharge) / w.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; + shots += $"\n{Localization.GetText("WeaponInfoDrawOverMax")}: {SinkPower - IdlePower:0.00}/ {w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick:0.00} {Localization.GetText("WeaponInfoMWLabel")}" + + $"\n{(chargeTime == 0 ? Localization.GetText("WeaponInfoPowerCharged") : Localization.GetText("WeaponInfoPowerChargedIn") + " " + chargeTime + Localization.GetText("WeaponInfoSeconds"))}"; } + + var endReturn = i + 1 != collection.Count ? "\n" : string.Empty; + var timeToLoad = (int)(w.ReloadEndTick - Session.I.Tick) / 60; + var showName = w.ActiveAmmoDef.AmmoDef.Const.TerminalName != w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText; + var displayName = showName ? w.ActiveAmmoDef.AmmoDef.Const.TerminalName + " (" + w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : w.ActiveAmmoDef.AmmoDef.Const.TerminalName; + stringBuilder.Append($"\n\n" + w.System.PartName + + shots + + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + + endReturn); + } - if (HeatPerSecond > 0) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoHeatPerSecOverMax")}: {HeatPerSecond}/{MaxHeat}" + - $"\n{Localization.GetText("WeaponInfoCurrentHeat")}: {CurrentHeat:0.} W ({(CurrentHeat / MaxHeat):P})"); + if (HeatPerSecond > 0) + stringBuilder.Append($"\n{Localization.GetText("WeaponInfoHeatPerSecOverMax")}: {HeatPerSecond}/{MaxHeat}" + + $"\n{Localization.GetText("WeaponInfoCurrentHeat")}: {CurrentHeat:0.} W ({(CurrentHeat / MaxHeat):P})"); - if (advanced) + if (advanced) + { + stringBuilder.Append($"\n\n{Localization.GetText("WeaponInfoStatsHeader")}" + + $"\n{Localization.GetText("WeaponInfoDPSLabel")}: {comp.PeakDps:0.}"); + for (int i = 0; i < collection.Count; i++) { - stringBuilder.Append($"\n\n{Localization.GetText("WeaponInfoStatsHeader")}" + - $"\n{Localization.GetText("WeaponInfoDPSLabel")}: {comp.PeakDps:0.}"); - for (int i = 0; i < collection.Count; i++) + var w = collection[i]; + stringBuilder.Append($" {(collection.Count > 1 ? "\n{w.FriendlyName}" : string.Empty)}" + + $"{(w.MinTargetDistance > 0 ? $"\n{Localization.GetText("WeaponInfoMinRange")}: {w.MinTargetDistance}{Localization.GetText("WeaponInfoMeter")}" : string.Empty)}" + + $"\n{Localization.GetText("WeaponInfoMaxRange")}: {w.MaxTargetDistance}{Localization.GetText("WeaponInfoMeter")}" + + $"\n{Localization.GetText("WeaponInfoROF")}: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin}{Localization.GetText("WeaponInfoPerMin")}"); + if(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget) { - var w = collection[i]; - stringBuilder.Append($" {(collection.Count > 1 ? "\n{w.FriendlyName}" : string.Empty)}" + - $"{(w.MinTargetDistance > 0 ? $"\n{Localization.GetText("WeaponInfoMinRange")}: {w.MinTargetDistance}{Localization.GetText("WeaponInfoMeter")}" : string.Empty)}" + - $"\n{Localization.GetText("WeaponInfoMaxRange")}: {w.MaxTargetDistance}{Localization.GetText("WeaponInfoMeter")}" + - $"\n{Localization.GetText("WeaponInfoROF")}: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin}{Localization.GetText("WeaponInfoPerMin")}"); - if(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget) + var targ = $"{Localization.GetText("WeaponInfoTargetLabel")}: "; + if (w.Target.HasTarget && w.Target.TargetObject != null) { - var targ = $"{Localization.GetText("WeaponInfoTargetLabel")}: "; - if (w.Target.HasTarget && w.Target.TargetObject != null) + var pTarg = w.Target.TargetObject as Projectile; + var eTarg = w.Target.TargetObject as MyEntity; + if(pTarg != null) { - var pTarg = w.Target.TargetObject as Projectile; - var eTarg = w.Target.TargetObject as MyEntity; - if(pTarg != null) - { - targ += Localization.GetText("WeaponInfoProjectileLabel"); - } - else if (eTarg != null) - { - var topEnt = eTarg.GetTopMostParent(); - var grid = topEnt as MyCubeGrid; - var suit = eTarg as IMyCharacter; - if (grid != null) - targ += topEnt.DisplayName; - else if (suit != null) - targ += suit.DisplayName; - } + targ += Localization.GetText("WeaponInfoProjectileLabel"); } - else - targ += Localization.GetText("WeaponInfoNoneTarget"); - stringBuilder.Append($"\n{targ}"); - } - - string otherAmmo = null; - if (!comp.HasAlternateUi) - { - for (int j = 0; j < w.System.AmmoTypes.Length; j++) + else if (eTarg != null) { - var ammo = w.System.AmmoTypes[j]; - if (!ammo.AmmoDef.Const.IsTurretSelectable || string.IsNullOrEmpty(ammo.AmmoDef.AmmoRound) || ammo.AmmoDef.AmmoRound == "Energy") - continue; - - if (otherAmmo == null) - otherAmmo = $"\n\n{Localization.GetText("WeaponInfoAmmoType")}:"; - var showName = ammo.AmmoDef.Const.TerminalName != ammo.AmmoDef.Const.MagazineDef.DisplayNameText && ammo.AmmoDef.Const.MagazineDef.DisplayNameText != "Energy"; - otherAmmo += $"\n{ammo.AmmoDef.Const.TerminalName} {(showName ? "(" + ammo.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : "")}"; + var topEnt = eTarg.GetTopMostParent(); + var grid = topEnt as MyCubeGrid; + var suit = eTarg as IMyCharacter; + if (grid != null) + targ += topEnt.DisplayName; + else if (suit != null) + targ += suit.DisplayName; } - - if (otherAmmo != null) - stringBuilder.Append(otherAmmo); } + else + targ += Localization.GetText("WeaponInfoNoneTarget"); + stringBuilder.Append($"\n{targ}"); } - } - /////// - - /* OLD STUFF - - if (advanced) - { - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoConstructDPS")}: " + Ai.EffectiveDps.ToString("e2")) - .Append($"\n{Localization.GetText("WeaponInfoPeakDps")}: " + comp.PeakDps.ToString("0.0")) - .Append($"\n{Localization.GetText("WeaponInfoBaseDps")}: " + comp.BaseDps.ToString("0.0")) - .Append($"\n{Localization.GetText("WeaponInfoAreaDps")}: " + comp.AreaDps.ToString("0.0")) - .Append($"\n{Localization.GetText("WeaponInfoExplode")}: " + comp.DetDps.ToString("0.0")) - .Append("\n") - .Append($"\n{Localization.GetText("WeaponTotalEffect")}: " + comp.TotalEffect.ToString("e2")) - .Append($"\n " + Ai.Construct.RootAi?.Construct.TotalEffect.ToString("e2")) - .Append($"\n{Localization.GetText("WeaponTotalEffectAvgDps")}: " + comp.AverageEffect.ToString("N0") + " - (" + comp.AddEffect.ToString("N0") + ")") - .Append($"\n " + Ai.Construct.RootAi?.Construct.AverageEffect.ToString("N0") + " - (" + Ai.Construct.RootAi?.Construct.AddEffect.ToString("N0") + ")"); - } - else - { - if (!comp.HasAlternateUi) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoPeakDps")}: " + comp.PeakDps.ToString("0.0")); - } - - - if (HeatPerSecond > 0 && advanced) - stringBuilder.Append("\n__________________________________" ) - .Append($"\n{Localization.GetText("WeaponInfoHeatGenerated")}: {HeatPerSecond:0.0} W ({(HeatPerSecond / MaxHeat) :P}/s)") - .Append($"\n{Localization.GetText("WeaponInfoHeatDissipated")}: {HeatSinkRate:0.0} W ({(HeatSinkRate / MaxHeat):P}/s)") - .Append($"\n{Localization.GetText("WeaponInfoCurrentHeat")}: {CurrentHeat:0.0} J ({(CurrentHeat / MaxHeat):P})"); - - if (!comp.HasAlternateUi && advanced) - { - stringBuilder.Append(advanced ? "\n__________________________________\n" : string.Empty) - .Append($"\n{Localization.GetText("WeaponInfoShotsPerSec")}: " + comp.RealShotsPerSec.ToString("0.00"));//+ " (" + comp.ShotsPerSec.ToString("0.00") + ")") - - if(SinkPower > 0.01) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoCurrentDraw")}: " + SinkPower.ToString("0.00") + " MW"); - } - - if (comp.HasEnergyWeapon && advanced) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoRequiredPower")}: " + Platform.Structure.ActualPeakPowerCombined.ToString("0.00") + " MW"); + string otherAmmo = null; if (!comp.HasAlternateUi) - stringBuilder.Append($"\n\n{Localization.GetText("WeaponInfoDividerLineWeapon")}"); - - for (int i = 0; i < collection.Count; i++) { - var w = collection[i]; - string shots; - if ((w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo || w.ActiveAmmoDef.AmmoDef.Const.IsHybrid) && !comp.HasAlternateUi) + for (int j = 0; j < w.System.AmmoTypes.Length; j++) { - var chargeTime = w.AssignedPower > 0 ? (int)((w.MaxCharge - w.ProtoWeaponAmmo.CurrentCharge) / w.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; - - shots = $"\nCharge: {(chargeTime == 0 ? "Charged" : "" + chargeTime + " s remaining" )}"; //(w.Comp.ModOverride ? "NPC Override" : w.Charging.ToString()) + (chargeTime == 0f ? " Charged" : " ("+ chargeTime+")"); - shots += "\nCurrent/Max(MW): " + (SinkPower - IdlePower).ToString("0.00") + "/" + w.ActiveAmmoDef.AmmoDef.Const.PowerPerTick.ToString("0.00"); - - if (w.ActiveAmmoDef.AmmoDef.Const.IsHybrid) shots += "\n" + w.ActiveAmmoDef.AmmoDef.AmmoRound + ": " + w.ProtoWeaponAmmo.CurrentAmmo; + var ammo = w.System.AmmoTypes[j]; + if (!ammo.AmmoDef.Const.IsTurretSelectable || string.IsNullOrEmpty(ammo.AmmoDef.AmmoRound) || ammo.AmmoDef.AmmoRound == "Energy") + continue; + + if (otherAmmo == null) + otherAmmo = $"\n\n{Localization.GetText("WeaponInfoAmmoType")}:"; + var showName = ammo.AmmoDef.Const.TerminalName != ammo.AmmoDef.Const.MagazineDef.DisplayNameText && ammo.AmmoDef.Const.MagazineDef.DisplayNameText != "Energy"; + otherAmmo += $"\n{ammo.AmmoDef.Const.TerminalName} {(showName ? "(" + ammo.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : "")}"; } - else shots = "\n" + w.ActiveAmmoDef.AmmoDef.AmmoRound + ": " + w.ProtoWeaponAmmo.CurrentAmmo; - - var burst = advanced && w.ActiveAmmoDef.AmmoDef.Const.BurstMode && w.System.ShotsPerBurst > 1 && !comp.HasAlternateUi ? $"\nShootMode: " + w.ShotsFired + "(" + w.System.ShotsPerBurst + $") - {Localization.GetText("WeaponInfoDelay")}: " + w .System.Values.HardPoint.Loading.DelayAfterBurst : string.Empty; - - var endReturn = i + 1 != collection.Count ? "\n" : string.Empty; - - if (!comp.HasAlternateUi) - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoName")}: " + w.System.PartName + shots + burst + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + w.Target.HasTarget.ToString() : "")}" + // \n{Localization.GetText("WeaponInfoHasTarget")}: " + (w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? w.Target.HasTarget.ToString() : "n/a") + - $"\n{Localization.GetText("WeaponInfoReloading")}: " + w.Loading + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + !w.PauseShoot : "")}" + - endReturn); - else - stringBuilder.Append($"\n{Localization.GetText("WeaponInfoName")}: " + w.System.PartName + (w.Target.HasTarget ? $"\n{Localization.GetText("WeaponInfoTargetState")}: " + w.Target.CurrentState : string.Empty)); - - string otherAmmo = null; - if (!comp.HasAlternateUi) - { - for (int j = 0; j < w.System.AmmoTypes.Length; j++) - { - var ammo = w.System.AmmoTypes[j]; - if (ammo == w.ActiveAmmoDef || !ammo.AmmoDef.Const.IsTurretSelectable || string.IsNullOrEmpty(ammo.AmmoDef.AmmoRound) || ammo.AmmoDef.AmmoRound == "Energy") - continue; - - if (otherAmmo == null) - otherAmmo = "\n\nAlternate Magazines:"; - - otherAmmo += $"\n{ammo.AmmoDef.AmmoRound}"; - } - - if (otherAmmo != null) - stringBuilder.Append(otherAmmo); - } + if (otherAmmo != null) + stringBuilder.Append(otherAmmo); } - - if (advanced) - { - foreach (var weapon in collection) - { - var chargeTime = weapon.AssignedPower > 0 ? (int)((weapon.MaxCharge - weapon.ProtoWeaponAmmo.CurrentCharge) / weapon.AssignedPower * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS) : 0; - stringBuilder.Append($"\n\nWeapon: {weapon.System.PartName} - Enabled: {IsWorking}"); - stringBuilder.Append($"\nTargetState: {weapon.Target.CurrentState} - Manual: {weapon.BaseComp.UserControlled || weapon.Target.TargetState == Target.TargetStates.IsFake}"); - stringBuilder.Append($"\nEvent: {weapon.LastEvent} - ProtoWeaponAmmo :{!weapon.NoMagsToLoad}"); - stringBuilder.Append($"\nOverHeat: {weapon.PartState.Overheated} - Shooting: {weapon.IsShooting}"); - stringBuilder.Append($"\nisAligned: {weapon.Target.IsAligned}"); - stringBuilder.Append($"\nCanShoot: {weapon.ShotReady} - Charging: {weapon.Charging}"); - stringBuilder.Append($"\nAiShooting: {weapon.AiShooting}"); - stringBuilder.Append($"\n{(weapon.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? "ChargeSize: " + weapon.ActiveAmmoDef.AmmoDef.Const.ChargSize : "MagSize: " + weapon.ActiveAmmoDef.AmmoDef.Const.MagazineSize)} ({weapon.ProtoWeaponAmmo.CurrentCharge})"); - stringBuilder.Append($"\nChargeTime: {chargeTime}"); - stringBuilder.Append($"\nCharging: {weapon.Charging}({weapon.ActiveAmmoDef.AmmoDef.Const.MustCharge})"); - } - } - */ + } } - catch (Exception ex) { Log.Line($"Exception in Weapon AppendingCustomInfo: {ex}", null, true); } } private List SortAndGetTargetTypes() diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityInit.cs b/Data/Scripts/CoreSystems/EntityComp/EntityInit.cs index 34807914..0bb1d80b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityInit.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityInit.cs @@ -1,10 +1,6 @@ -using System; -using CoreSystems.Platform; +using CoreSystems.Platform; using Sandbox.Game; -using Sandbox.Game.Entities; using Sandbox.ModAPI; -using VRage; -using VRage.Game.ModAPI; using static CoreSystems.CompData; namespace CoreSystems.Support { diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs b/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs index 545b9273..8aca676d 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityRun.cs @@ -1,12 +1,10 @@ -using System; -using CoreSystems.Platform; +using CoreSystems.Platform; using Sandbox.Game.Entities; using Sandbox.ModAPI; using VRage.Game.Components; using VRageMath; using static CoreSystems.Session; using static CoreSystems.Support.Ai; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Support { diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityState.cs b/Data/Scripts/CoreSystems/EntityComp/EntityState.cs index 53a417f6..f662d0fb 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityState.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityState.cs @@ -3,7 +3,6 @@ using Sandbox.Game.Entities; using VRage.Game.Entity; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Support { @@ -59,47 +58,42 @@ internal void WakeupComp() internal void SubpartClosed(MyEntity ent) { - try + if (ent == null) { - if (ent == null) - { - Log.Line($"SubpartClosed had null entity"); - return; - } + Log.Line($"SubpartClosed had null entity"); + return; + } - using (CoreEntity.Pin()) + using (CoreEntity.Pin()) + { + ent.OnClose -= SubpartClosed; + if (!CoreEntity.MarkedForClose && Platform.State == CorePlatform.PlatformState.Ready) { - ent.OnClose -= SubpartClosed; - if (!CoreEntity.MarkedForClose && Platform.State == CorePlatform.PlatformState.Ready) + if (Type == CompType.Weapon) + Platform.ResetParts(); + Status = Start.Started; + + foreach (var w in Platform.Weapons) { - if (Type == CompType.Weapon) - Platform.ResetParts(); - Status = Start.Started; + w.Azimuth = 0; + w.Elevation = 0; + w.Elevation = 0; + + if (w.ActiveAmmoDef.AmmoDef.Const.MustCharge) + w.ExitCharger = true; - foreach (var w in Platform.Weapons) + if (!FunctionalBlock.Enabled) + w.EventTriggerStateChanged(EventTriggers.TurnOff, true); + else if (w.AnimationsSet.ContainsKey(EventTriggers.TurnOn)) + Session.I.FutureEvents.Schedule(w.TurnOnAV, null, 100); + + if (w.ProtoWeaponAmmo.CurrentAmmo == 0) { - w.Azimuth = 0; - w.Elevation = 0; - w.Elevation = 0; - - if (w.ActiveAmmoDef.AmmoDef.Const.MustCharge) - w.ExitCharger = true; - - if (!FunctionalBlock.Enabled) - w.EventTriggerStateChanged(EventTriggers.TurnOff, true); - else if (w.AnimationsSet.ContainsKey(EventTriggers.TurnOn)) - Session.I.FutureEvents.Schedule(w.TurnOnAV, null, 100); - - if (w.ProtoWeaponAmmo.CurrentAmmo == 0) - { - w.EventTriggerStateChanged(EventTriggers.EmptyOnGameLoad, true); - } + w.EventTriggerStateChanged(EventTriggers.EmptyOnGameLoad, true); } } } } - catch (Exception ex) { Log.Line($"Exception in SubpartClosed: {ex}", null, true); - } } internal void ForceClose(object o) diff --git a/Data/Scripts/CoreSystems/EntityComp/EntitySupport.cs b/Data/Scripts/CoreSystems/EntityComp/EntitySupport.cs index d5e2a4c6..f44f2830 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntitySupport.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntitySupport.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.ObjectModel; using CoreSystems.Platform; using Sandbox.Game.Entities; using Sandbox.ModAPI.Weapons; diff --git a/Data/Scripts/CoreSystems/EntityComp/ModelSupport/RecursiveSubparts.cs b/Data/Scripts/CoreSystems/EntityComp/ModelSupport/RecursiveSubparts.cs index b60a690f..6367f1cf 100644 --- a/Data/Scripts/CoreSystems/EntityComp/ModelSupport/RecursiveSubparts.cs +++ b/Data/Scripts/CoreSystems/EntityComp/ModelSupport/RecursiveSubparts.cs @@ -5,7 +5,6 @@ using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.ModAPI; -using VRage.Utils; using VRageMath; namespace CoreSystems.Support diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlComp.cs index 99ef6403..cda70389 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlComp.cs @@ -7,7 +7,6 @@ using VRage.Game.Entity; using VRage.Utils; using VRageMath; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Platform { diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs index 9d1ced4b..3c4e489a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlData.cs @@ -2,7 +2,6 @@ using CoreSystems.Support; using Sandbox.ModAPI; using VRageMath; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Platform { diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlFields.cs index 66a55d8e..4cb86b71 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlFields.cs @@ -1,6 +1,5 @@ using CoreSystems.Support; using Sandbox.ModAPI; -using System.Runtime.Remoting.Metadata.W3cXsd2001; using VRage.Utils; using VRageMath; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/PartFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/PartFields.cs index 4daf36ff..4641041e 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/PartFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/PartFields.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using CoreSystems.Support; -using VRageMath; +using CoreSystems.Support; namespace CoreSystems.Platform { public partial class Part { - //internal readonly List> Monitors = new List>(); - internal CoreComponent BaseComp; internal CoreSystem CoreSystem; internal PartAcquire Acquire; @@ -40,8 +35,6 @@ internal void Init(CoreComponent comp, CoreSystem system, int partId) Acquire = new PartAcquire(this); UniquePartId = Session.I.UniquePartId; ShortLoadId = Session.I.ShortLoadAssigner(); - //for (int i = 0; i < BaseComp.Monitors[PartId].Count; i++) - // Monitors.Add(BaseComp.Monitors[PartId][i]); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportCharge.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportCharge.cs index 99cadf0c..af5bd09c 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportCharge.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportCharge.cs @@ -1,6 +1,4 @@ -using Sandbox.Game.Entities; - -namespace CoreSystems.Platform +namespace CoreSystems.Platform { public partial class SupportSys { diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportComp.cs index ff6d30a3..158e3362 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportComp.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using CoreSystems.Support; -using Sandbox.Game.Entities; using VRage.Game; using VRage.Game.Entity; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportMisc.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportMisc.cs index 89d33059..114dcaed 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportMisc.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Support/SupportMisc.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using Sandbox.Game.Entities; using VRage.Game.ModAPI; using static CoreSystems.Support.SupportDefinition.SupportEffect.Protections; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Upgrade/UpgradeComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Upgrade/UpgradeComp.cs index 27ffb003..818049ee 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Upgrade/UpgradeComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Upgrade/UpgradeComp.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using CoreSystems.Support; -using Sandbox.Game.Entities; using VRage.Game; using VRage.Game.Entity; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponAv.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponAv.cs index 8da79651..f1ca7056 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponAv.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponAv.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using CoreSystems.Support; -using Sandbox.ModAPI; using VRage.Game; using VRageMath; using static CoreSystems.Support.PartAnimation; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs index 857d9c9b..e6512410 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs @@ -9,7 +9,6 @@ using Sandbox.ModAPI.Weapons; using VRage.Game; using VRage.Game.Entity; -using VRage.Game.ModAPI; using VRage.ModAPI; using VRage.ObjectBuilders; using VRage.Utils; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponController.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponController.cs index ca7a7fb5..d1692ff5 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponController.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponController.cs @@ -1,11 +1,9 @@ using System; using CoreSystems.Support; -using VRage.Game.Entity; using VRage.Utils; using VRageMath; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; using static CoreSystems.Support.CoreComponent; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Platform { @@ -257,79 +255,75 @@ internal void UpdatePivotPos() internal void UpdateWeaponHeat(object o = null) { - try + if (!System.ProhibitCoolingWhenOff || System.ProhibitCoolingWhenOff && Comp.Cube.IsWorking) { - if (!System.ProhibitCoolingWhenOff || System.ProhibitCoolingWhenOff && Comp.Cube.IsWorking) - { - var hsRateMod = HsRate + (float)Comp.HeatLoss; - Comp.CurrentHeat = Comp.CurrentHeat >= hsRateMod ? Comp.CurrentHeat - hsRateMod : 0; - PartState.Heat = PartState.Heat >= hsRateMod ? PartState.Heat - hsRateMod : 0; - Comp.HeatLoss = 0; - } - - var set = PartState.Heat - LastHeat > 0.001 || PartState.Heat - LastHeat < 0.001; - - LastHeatUpdateTick = Session.I.Tick; + var hsRateMod = HsRate + (float)Comp.HeatLoss; + Comp.CurrentHeat = Comp.CurrentHeat >= hsRateMod ? Comp.CurrentHeat - hsRateMod : 0; + PartState.Heat = PartState.Heat >= hsRateMod ? PartState.Heat - hsRateMod : 0; + Comp.HeatLoss = 0; + } - if (!Session.I.DedicatedServer) - { - var heatOffset = HeatPerc = PartState.Heat / System.MaxHeat; + var set = PartState.Heat - LastHeat > 0.001 || PartState.Heat - LastHeat < 0.001; - if (set && heatOffset > .33) - { - if (heatOffset > 1) heatOffset = 1; + LastHeatUpdateTick = Session.I.Tick; - heatOffset -= .33f; + if (!Session.I.DedicatedServer) + { + var heatOffset = HeatPerc = PartState.Heat / System.MaxHeat; - var intensity = .7f * heatOffset; + if (set && heatOffset > .33) + { + if (heatOffset > 1) heatOffset = 1; - var color = Session.I.HeatEmissives[(int)(heatOffset * 100)]; + heatOffset -= .33f; - for(int i = 0; i < HeatingParts.Count; i++) - HeatingParts[i]?.SetEmissiveParts("Heating", color, intensity); - } - else if (set) - for(int i = 0; i < HeatingParts.Count; i++) - HeatingParts[i]?.SetEmissiveParts("Heating", Color.Transparent, 0); + var intensity = .7f * heatOffset; - LastHeat = PartState.Heat; - } + var color = Session.I.HeatEmissives[(int)(heatOffset * 100)]; - if (set && System.DegRof && PartState.Heat >= (System.MaxHeat * .8)) - { - CurrentlyDegrading = true; - UpdateRof(); + for(int i = 0; i < HeatingParts.Count; i++) + HeatingParts[i]?.SetEmissiveParts("Heating", color, intensity); } - else if (set && CurrentlyDegrading) - { - if (PartState.Heat <= (System.MaxHeat * .4)) - CurrentlyDegrading = false; + else if (set) + for(int i = 0; i < HeatingParts.Count; i++) + HeatingParts[i]?.SetEmissiveParts("Heating", Color.Transparent, 0); - UpdateRof(); - } + LastHeat = PartState.Heat; + } - if (PartState.Overheated && PartState.Heat <= (System.MaxHeat * System.WepCoolDown)) - { - EventTriggerStateChanged(EventTriggers.Overheated, false); - if (Session.I.IsServer) - { - PartState.Overheated = false; - OverHeatCountDown = 0; - if (Session.I.MpActive) - Session.I.SendState(Comp); - } + if (set && System.DegRof && PartState.Heat >= (System.MaxHeat * .8)) + { + CurrentlyDegrading = true; + UpdateRof(); + } + else if (set && CurrentlyDegrading) + { + if (PartState.Heat <= (System.MaxHeat * .4)) + CurrentlyDegrading = false; - } + UpdateRof(); + } - if (PartState.Heat > 0) - Session.I.FutureEvents.Schedule(UpdateWeaponHeat, null, 20); - else + if (PartState.Overheated && PartState.Heat <= (System.MaxHeat * System.WepCoolDown)) + { + EventTriggerStateChanged(EventTriggers.Overheated, false); + if (Session.I.IsServer) { - HeatLoopRunning = false; - LastHeatUpdateTick = 0; + PartState.Overheated = false; + OverHeatCountDown = 0; + if (Session.I.MpActive) + Session.I.SendState(Comp); } + + } + + if (PartState.Heat > 0) + Session.I.FutureEvents.Schedule(UpdateWeaponHeat, null, 20); + else + { + HeatLoopRunning = false; + LastHeatUpdateTick = 0; } - catch (Exception ex) { Log.Line($"Exception in UpdateWeaponHeat: {ex} - {System == null}- BaseComp:{Comp == null} - ProtoRepo:{Comp?.Data.Repo == null} - Weapons:{Comp.Data.Repo?.Values.State.Weapons[PartId] == null}", null, true); } } internal void UpdateRof() diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index 5223f757..d419cac9 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -8,19 +8,16 @@ using VRage.Game; using VRage.Game.Entity; using VRage.Game.ModAPI; -using VRage.ObjectBuilders; using VRage.Utils; using VRageMath; using VRageRender.Lights; using static CoreSystems.Session; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems.Platform { public partial class Weapon : Part { - internal volatile bool Casting; private readonly int _numOfMuzzles; private readonly int _numModelBarrels; private readonly HashSet _muzzlesToFire = new HashSet(); @@ -438,88 +435,8 @@ internal Weapon(MyEntity entity, WeaponSystem system, int partId, WeaponComponen if (System.HasAntiSmart) Session.I.AntiSmartActive = true; - - if (Session.I.HandlesInput && Comp.IsBlock) - { - //InitLight(Color.Red, 99, 1, out Light); - } - } - - private void InitLight(Vector4 color, float radius, float falloff, out MyLight light) - { - var cube = Comp.Cube; - - light = new MyLight(); - light.Start(color, cube.CubeGrid.GridScale * radius, cube.DisplayNameText); - light.ReflectorOn = true; - light.LightType = MyLightType.SPOTLIGHT; - light.ReflectorTexture = @"Textures\Lights\reflector_large.dds"; ; - light.Falloff = 0.3f; - light.GlossFactor = 0.0f; - light.ReflectorGlossFactor = 1f; - light.ReflectorFalloff = 0.5f; - light.GlareOn = light.LightOn; - light.GlareQuerySize = GlareQuerySizeDef; - light.GlareType = MyGlareTypeEnum.Directional; - light.GlareSize = _flare.Size; - light.SubGlares = _flare.SubGlares; - - //light.ReflectorIntensity = 10f; - //light.ReflectorRange = 100; // how far the projected light goes - //light.ReflectorConeDegrees = 90; // projected light angle in degrees, max 179. - //light.CastShadows = true; - - - cube.Render.NeedsDrawFromParent = true; - light.Position = Scope.Info.Position + (Scope.Info.Direction * 1); - light.UpdateLight(); - - //light.GlareSize = new Vector2(1, 1); // glare size in X and Y. - //light.GlareIntensity = 2; - //light.GlareMaxDistance = 50; - //light.SubGlares = GetFlareDefinition("InteriorLight").SubGlares; // subtype name from flares.sbc - //light.GlareType = MyGlareTypeEnum.Normal; // usable values: MyGlareTypeEnum.Normal, MyGlareTypeEnum.Distant, MyGlareTypeEnum.Directional - //light.GlareQuerySize = 0.5f; // glare "box" size, affects occlusion and fade occlussion - //light.GlareQueryShift = 1f; // no idea - //light.ParentID = cube.Render.GetRenderObjectID(); - //this.UpdateIntensity(); } - - public static MyFlareDefinition GetFlareDefinition(string flareSubtypeId) - { - if (string.IsNullOrEmpty(flareSubtypeId)) - throw new ArgumentException("flareSubtypeId must not be null or empty!"); - var flareDefId = new MyDefinitionId(typeof(MyObjectBuilder_FlareDefinition), flareSubtypeId); - var flareDef = MyDefinitionManager.Static.GetDefinition(flareDefId) as MyFlareDefinition; - - if (flareDef == null) - throw new Exception($"Couldn't find flare subtype {flareSubtypeId}"); - - return flareDef; - } - - private readonly MyFlareDefinition _flare = new MyFlareDefinition() ; - private float GlareQuerySizeDef => Comp.Cube.CubeGrid.GridScale * (true ? 0.5f : 0.1f); - - /* - private void UpdateIntensity() - { - float num1 = this.m_lightingLogic.CurrentLightPower * this.m_lightingLogic.Intensity; - foreach (MyLight light in this.m_lightingLogic.Lights) - { - light.ReflectorIntensity = num1 * 8f; - light.Intensity = num1 * 0.3f; - float num2 = num1 / this.m_lightingLogic.IntensityBounds.Max; - float num3 = this.m_flare.Intensity * num1; - if (num3 < (double)this.m_flare.Intensity) - num3 = this.m_flare.Intensity; - light.GlareIntensity = num3; - light.GlareSize = this.m_flare.Size * (float)((double)num2 / 2.0 + 0.5); - this.m_lightingLogic.BulbColor = this.m_lightingLogic.ComputeBulbColor(); - } - } - */ private void FuckMyLife() { var azPartMatrix = AzimuthPart.Entity.PositionComp.LocalMatrixRef; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponHand.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponHand.cs index 6b835fd1..22dcc127 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponHand.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponHand.cs @@ -1,5 +1,4 @@ -using CoreSystems.Projectiles; -using CoreSystems.Support; +using CoreSystems.Support; using Sandbox.Common.ObjectBuilders; using Sandbox.Game.Entities; using Sandbox.Game.Entities.Character.Components; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs index c228b11b..ed267df3 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs @@ -1,5 +1,4 @@ using System; -using CoreSystems.Projectiles; using System.Collections.Generic; using CoreSystems.Support; using Sandbox.Game.Entities; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index 07f39ae5..e4020a86 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -14,7 +14,6 @@ using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; using CollisionLayers = Sandbox.Engine.Physics.MyPhysics.CollisionLayers; using static CoreSystems.Support.MathFuncs; -using VRage.Game.ObjectBuilders.Components; namespace CoreSystems.Platform { @@ -128,20 +127,9 @@ internal static void LeadTarget(Weapon weapon, MyEntity target, out Vector3D tar obb.Center = targetPos; weapon.TargetBox = obb; - - //var obbAbsMax = obb.HalfExtent.AbsMax(); - //var maxRangeSqr = obbAbsMax + weapon.MaxTargetDistance; - //var minRangeSqr = obbAbsMax + weapon.MinTargetDistance; - - //maxRangeSqr *= maxRangeSqr; - //minRangeSqr *= minRangeSqr; - //double rangeToTarget; - //Vector3D.DistanceSquared(ref targetPos, ref weapon.MyPivotPos, out rangeToTarget); - //couldHit = validEstimate && rangeToTarget <= maxRangeSqr && rangeToTarget >= minRangeSqr; couldHit = validEstimate; bool canTrack = false; - //if (validEstimate && rangeToTarget <= maxRangeSqr && rangeToTarget >= minRangeSqr) if (validEstimate) { var targetDir = targetPos - weapon.MyPivotPos; @@ -448,7 +436,7 @@ internal static bool TrackingTarget(Weapon w, Target target, out bool targetLock if (baseData.State.Control == ProtoWeaponState.ControlMode.Camera || w.Comp.FakeMode || session.IsServer && baseData.Set.Overrides.Repel && ai.DetectionInfo.DroneInRange && target.IsDrone && (session.AwakeCount == w.Acquire.SlotId || ai.Construct.RootAi.Construct.LastDroneTick == session.Tick) && Ai.SwitchToDrone(w)) return true; - var rayCheckTest = isTracking && (isAligned || locked) && baseData.State.Control != ProtoWeaponState.ControlMode.Camera && (w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance != TrajectoryDef.GuidanceType.Smart && w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance != TrajectoryDef.GuidanceType.DroneAdvanced) && !w.System.DisableLosCheck && (!w.Casting && session.Tick - w.Comp.LastRayCastTick > 29 || w.System.Values.HardPoint.Other.MuzzleCheck && session.Tick - w.LastMuzzleCheck > 29); + var rayCheckTest = isTracking && (isAligned || locked) && baseData.State.Control != ProtoWeaponState.ControlMode.Camera && (w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance != TrajectoryDef.GuidanceType.Smart && w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance != TrajectoryDef.GuidanceType.DroneAdvanced) && !w.System.DisableLosCheck && (session.Tick - w.Comp.LastRayCastTick > 29 || w.System.Values.HardPoint.Other.MuzzleCheck && session.Tick - w.LastMuzzleCheck > 29); var trackingTimeLimit = w.System.MaxTrackingTime && session.Tick - w.Target.ChangeTick > w.System.MaxTrackingTicks; if (session.IsServer && (rayCheckTest && !w.RayCheckTest(rangeToTargetSqr) || trackingTimeLimit)) @@ -608,269 +596,6 @@ internal void SmartLosDebug() DsDebugDraw.DrawLine(line, hit ? Color.Red : Color.Blue, 0.05f); } } - - internal static Vector3D TrajectoryEstimationOld(Weapon weapon, Vector3D targetPos, Vector3D targetVel, Vector3D targetAcc, Vector3D shooterPos, bool trackAngular, out bool valid, bool overrideMode = false, bool setAdvOverride = false, bool skipAccel = false) - { - valid = true; - var comp = weapon.Comp; - var ai = comp.Ai; - var session = Session.I; - var ammoDef = weapon.ActiveAmmoDef.AmmoDef; - var origTargetPos = targetPos; //Need these original values for debug draws later - var origTargetVel = targetVel; - - if (ai.VelocityUpdateTick != session.Tick) - { - ai.TopEntityVolume.Center = comp.TopEntity.PositionComp.WorldVolume.Center; - ai.TopEntityVel = comp.TopEntity.Physics?.LinearVelocity ?? Vector3D.Zero; - ai.IsStatic = comp.TopEntity.Physics?.IsStatic ?? false; - ai.VelocityUpdateTick = session.Tick; - } - - var updateGravity = ammoDef.Const.FeelsGravity && ai.InPlanetGravity; - - if (updateGravity && session.Tick - weapon.GravityTick > 119) - { - weapon.GravityTick = session.Tick; - float interference; - weapon.GravityPoint = session.Physics.CalculateNaturalGravityAt(weapon.MyPivotPos, out interference); - weapon.GravityUnitDir = weapon.GravityPoint; - weapon.GravityLength = weapon.GravityUnitDir.Normalize(); - } - else if (!updateGravity) - weapon.GravityPoint = Vector3D.Zero; - - var gravityMultiplier = ammoDef.Const.FeelsGravity && !MyUtils.IsZero(weapon.GravityPoint) ? ammoDef.Const.GravityMultiplier : 0f; - bool hasGravity = gravityMultiplier > 1e-6 && !MyUtils.IsZero(weapon.GravityPoint); - - var targetMaxSpeed = Session.I.MaxEntitySpeed; - shooterPos = MyUtils.IsZero(shooterPos) ? weapon.MyPivotPos : shooterPos; - - var shooterVel = (Vector3D)weapon.Comp.Ai.TopEntityVel; - 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; - - if (basic && weapon.System.Prediction == Prediction.Accurate && hasGravity && ai.InPlanetGravity) - { - basic = false; - skipAccel = true; - } - - Vector3D deltaPos = targetPos - shooterPos; - Vector3D deltaVel = targetVel - shooterVel; - Vector3D deltaPosNorm; - double deltaLength = 0; - if (Vector3D.IsZero(deltaPos)) - { - deltaPosNorm = Vector3D.Zero; - } - else if (Vector3D.IsUnit(ref deltaPos)) - { - deltaPosNorm = deltaPos; - deltaLength = 1; - } - else - { - deltaPosNorm = deltaPos; - deltaLength = deltaPosNorm.Normalize(); - } - - double closingSpeed; - Vector3D.Dot(ref deltaVel, ref deltaPosNorm, out closingSpeed); - - Vector3D closingVel = closingSpeed * deltaPosNorm; - Vector3D lateralVel = deltaVel - closingVel; - double projectileMaxSpeedSqr = projectileMaxSpeed * projectileMaxSpeed; - double ttiDiff = projectileMaxSpeedSqr - lateralVel.LengthSquared(); - - if (ttiDiff < 0) - { - valid = false; - return targetPos; - } - - double projectileClosingSpeed = Math.Sqrt(ttiDiff) - closingSpeed; - - double closingDistance; - Vector3D.Dot(ref deltaPos, ref deltaPosNorm, out closingDistance); - - double timeToIntercept = ttiDiff < 0 ? 0 : closingDistance / projectileClosingSpeed; - - if (timeToIntercept < 0) - { - valid = false; - return targetPos; - } - - double maxSpeedSqr = targetMaxSpeed * targetMaxSpeed; - double shooterVelScaleFactor = 1; - bool projectileAccelerates = projectileAccMag > 1e-6; - - if (!basic && projectileAccelerates) - shooterVelScaleFactor = Math.Min(1, (projectileMaxSpeed - projectileInitSpeed) / projectileAccMag); - - Vector3D estimatedImpactPoint = targetPos + timeToIntercept * (targetVel - shooterVel * shooterVelScaleFactor); - - if (basic) - return estimatedImpactPoint; - - Vector3D aimDirection = estimatedImpactPoint - shooterPos; - - Vector3D projectileVel = shooterVel; - Vector3D projectilePos = shooterPos; - - Vector3D aimDirectionNorm; - if (projectileAccelerates) - { - - if (Vector3D.IsZero(deltaPos)) aimDirectionNorm = Vector3D.Zero; - else if (Vector3D.IsUnit(ref deltaPos)) aimDirectionNorm = aimDirection; - else aimDirectionNorm = Vector3D.Normalize(aimDirection); - projectileVel += aimDirectionNorm * projectileInitSpeed; - } - else - { - - if (targetAcc.LengthSquared() < 1 && !hasGravity) - return estimatedImpactPoint; - - if (Vector3D.IsZero(deltaPos)) aimDirectionNorm = Vector3D.Zero; - else if (Vector3D.IsUnit(ref deltaPos)) aimDirectionNorm = aimDirection; - else Vector3D.Normalize(ref aimDirection, out aimDirectionNorm); - projectileVel += aimDirectionNorm * projectileMaxSpeed; - } - - var deepSim = projectileAccelerates || hasGravity; - var count = deepSim ? 320 : 60; - - double dt = Math.Max(MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS, timeToIntercept / count); // This can be a const somewhere - double dtSqr = dt * dt; - Vector3D targetAccStep = targetAcc * dt; - Vector3D projectileAccStep = aimDirectionNorm * projectileAccMag * dt; - - Vector3D aimOffset = Vector3D.Zero; - - //BD Todo: Clamp this for projectiles OR targets that don't accelerate - if (!skipAccel && (projectileAccelerates || targetVel.LengthSquared() >= 0.01)) - { - for (int i = 0; i < count; ++i) - { - - targetVel += targetAccStep; - - if (targetVel.LengthSquared() > maxSpeedSqr) - { - Vector3D targetNormVel; - Vector3D.Normalize(ref targetVel, out targetNormVel); - targetVel = targetNormVel * targetMaxSpeed; - } - - targetPos += targetVel * dt; - if (projectileAccelerates) - { - - projectileVel += projectileAccStep; - if (projectileVel.LengthSquared() > projectileMaxSpeedSqr) - { - Vector3D pNormVel; - Vector3D.Normalize(ref projectileVel, out pNormVel); - projectileVel = pNormVel * projectileMaxSpeed; - } - } - - projectilePos += projectileVel * dt; - Vector3D diff = (targetPos - projectilePos); - double diffLenSq = diff.LengthSquared(); - aimOffset = diff; - if (diffLenSq < projectileMaxSpeedSqr * dtSqr || Vector3D.Dot(diff, aimDirectionNorm) < 0) - break; - } - } - - Vector3D perpendicularAimOffset = !skipAccel ? aimOffset - Vector3D.Dot(aimOffset, aimDirectionNorm) * aimDirectionNorm : Vector3D.Zero; - - Vector3D gravityOffset = Vector3D.Zero; - //gravity nonsense for differing elevations - if (hasGravity && ai.InPlanetGravity) - { - var targetAngle = Math.Acos(Vector3D.Dot(weapon.GravityPoint, deltaPos) / (weapon.GravityLength * deltaLength)); - double elevationDifference; - if (targetAngle >= 1.5708) //Target is above weapon - { - targetAngle -= 1.5708; //angle-90 - elevationDifference = -Math.Sin(targetAngle) * deltaLength; - } - else //Target is below weapon - { - targetAngle = 1.5708 - targetAngle; //90-angle - elevationDifference = -Math.Sin(targetAngle) * deltaLength; - } - var horizontalDistance = Math.Sqrt(deltaLength * deltaLength - elevationDifference * elevationDifference); - - //Minimized for my sanity - var g = -(weapon.GravityLength * gravityMultiplier); - var v = projectileMaxSpeed; - var h = elevationDifference; - var d = horizontalDistance; - - var angleCheck = (v * v * v * v) - 2 * (v * v) * -h * g - (g * g) * (d * d); - - if (angleCheck <= 0) - { - valid = false; - return estimatedImpactPoint + perpendicularAimOffset + gravityOffset; - - } - - //lord help me - var angleSqrt = Math.Sqrt(angleCheck); - var angle1 = -Math.Atan((v * v + angleSqrt) / (g * d));//Higher angle - var angle2 = -Math.Atan((v * v - angleSqrt) / (g * d));//Lower angle //Try angle 2 first (the lower one) - - var verticalDistance = Math.Tan(angle2) * horizontalDistance; //without below-the-horizon modifier - gravityOffset = new Vector3D((verticalDistance + Math.Abs(elevationDifference)) * -weapon.GravityUnitDir); - if (angle1 > 1.57) - { - return estimatedImpactPoint + perpendicularAimOffset + gravityOffset; - } - - var targetAimPoint = estimatedImpactPoint + perpendicularAimOffset + gravityOffset; - var targetDirection = targetAimPoint - shooterPos; - - bool isTracking; - 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 != 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); - } - } - - if (false) - { - //OldAdvanced - DsDebugDraw.DrawLine(new LineD(origTargetPos, estimatedImpactPoint + perpendicularAimOffset + gravityOffset), Color.Yellow, 2f); - - //OldBasic - Vector3D estimatedImpactPointbasicOld = origTargetPos + (ttiDiff < 0 ? 0 : closingDistance / projectileClosingSpeed) * (origTargetVel - shooterVel); - DsDebugDraw.DrawLine(new LineD(origTargetPos, estimatedImpactPointbasicOld), Color.Green, 2f); - - //New algo - var tempCoord = TrajectoryEstimation(weapon, origTargetPos, origTargetVel, targetAcc, shooterPos, out valid, false, trackAngular); - DsDebugDraw.DrawLine(new LineD(origTargetPos, tempCoord), Color.Red, 2); - return tempCoord; - } - MyAPIGateway.Utilities.ShowNotification($"Old Mode: {(basic ? " basic" : " advanced")} {(updateGravity ? " w/ grav" : " no grav")} {(skipAccel ? "" : " w/ proj accel")} ", 16); - - return estimatedImpactPoint + perpendicularAimOffset + gravityOffset; - } - public static bool QuarticSolver(ref double timeToIntercept, Vector3D relativePosition, Vector3D relativeVelocity, Vector3D acceleration, double projectileSpeed, double[] coefficients, double tolerance = 1e-3, int maxIterations = 10) { var oneOverSpeedSq = projectileSpeed > 0 ? 1.0 / (projectileSpeed * projectileSpeed) : 0; @@ -1056,10 +781,6 @@ internal static Vector3D TrajectoryEstimation(Weapon weapon, Vector3D targetPos, } } } - - //DsDebugDraw.DrawLine(new LineD(targetPos, aimPoint + gravityOffset), Color.Red, 1); - //MyAPIGateway.Utilities.ShowNotification($"New Mode: {(useSimple ? "Simple" : "Advanced")} {(updateGravity ? " w/ grav" : " no grav")} {(ammoDef.Const.AmmoSkipAccel ? " no proj accel" : "w/ proj accel")}",16); - return aimPoint + gravityOffset; } @@ -1125,7 +846,6 @@ private static bool ComputeAngular(MyCubeGrid grid, Ai ai, WeaponDefinition.Ammo public void ManualShootRayCallBack(IHitInfo hitInfo) { - Casting = false; var masterWeapon = System.TrackTargets ? this : Comp.PrimaryWeapon; var grid = hitInfo.HitEntity as MyCubeGrid; @@ -1244,9 +964,6 @@ private bool RayCheckTest(double rangeToTargetSqr) if (Target.TargetState == Target.TargetStates.IsFake) { - Casting = true; - //Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref Target.TargetPos, filter, ManualShootRayCallBack); - IHitInfo fakeHitInfo; Session.I.Physics.CastRay(trackingCheckPosition, Target.TargetPos, out fakeHitInfo, filter); ManualShootRayCallBack(fakeHitInfo); @@ -1322,9 +1039,6 @@ private bool RayCheckTest(double rangeToTargetSqr) return false; } } - Casting = true; - //Session.I.Physics.CastRayParallel(ref trackingCheckPosition, ref targetPos, filter, RayCallBack.NormalShootRayCallBack); - IHitInfo rayHitInfo; Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); RayCallBack.NormalShootRayCallBack(rayHitInfo); diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs index 156ca0fc..8b310a28 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; -using CoreSystems.Projectiles; +using CoreSystems.Projectiles; using CoreSystems.Support; using Sandbox.Game.Entities; +using System; using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.Utils; @@ -27,15 +26,14 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) var eTarget = Weapon.Target.TargetObject as MyEntity; if (pTarget == null && eTarget == null) return; - Weapon.Casting = false; Weapon.PauseShoot = false; var masterWeapon = Weapon.System.TrackTargets ? Weapon : Weapon.Comp.PrimaryWeapon; - var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || Weapon.Target.TargetObject is IMyCharacter; + var ignoreTargets = Weapon.Target.TargetState == Target.TargetStates.IsProjectile || (eTarget != null && Weapon.Target.TargetObject is IMyCharacter); var scope = Weapon.GetScope; var trackingCheckPosition = scope.CachedPos; double rayDist = 0; - if (Session.I.DebugLos) + if (Session.I.DebugLos && hitInfo != null) { var hitPos = hitInfo.Position; if (rayDist <= 0) Vector3D.Distance(ref trackingCheckPosition, ref hitPos, out rayDist); @@ -45,7 +43,7 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) if (Weapon.Comp.Ai.ShieldNear) { - var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldMatrixRef.Translation; + var targetPos = pTarget?.Position ?? eTarget.PositionComp.WorldVolume.Center; var targetDir = targetPos - trackingCheckPosition; if (Weapon.HitFriendlyShield(trackingCheckPosition, targetPos, targetDir)) { @@ -112,10 +110,9 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) var maxChange = halfExtMin > minSize ? halfExtMin : minSize; var targetPos = eTarget.PositionComp.WorldAABB.Center; var weaponPos = trackingCheckPosition; - if (rayDist <= 0) Vector3D.Distance(ref weaponPos, ref targetPos, out rayDist); - var newHitShortDist = rayDist * (1 - hitInfo.Fraction); - var distanceToTarget = rayDist * hitInfo.Fraction; + var newHitShortDist = hitInfo == null ? rayDist : rayDist * (1 - hitInfo.Fraction); + var distanceToTarget = hitInfo == null ? rayDist : rayDist * hitInfo.Fraction; var shortDistExceed = newHitShortDist - Weapon.Target.HitShortDist > maxChange; var escapeDistExceed = distanceToTarget - Weapon.Target.OrigDistance > Weapon.Target.OrigDistance; if (shortDistExceed || escapeDistExceed) @@ -124,8 +121,8 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistOffset); } } + } - } internal class Muzzle diff --git a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs index b04cfa49..e4e66729 100644 --- a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs +++ b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs @@ -8,7 +8,6 @@ using static CoreSystems.Support.CoreComponent.Start; using static CoreSystems.Support.CoreComponent.CompTypeSpecific; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; -using Sandbox.Game.World; namespace CoreSystems.Platform { diff --git a/Data/Scripts/CoreSystems/Projectiles/Dtree.cs b/Data/Scripts/CoreSystems/Projectiles/Dtree.cs index e23edac5..d46e64f6 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Dtree.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Dtree.cs @@ -26,17 +26,6 @@ internal static void UnregisterProjectile(Projectile projectile) s.ProjectileTree.RemoveProxy(projectile.PruningProxyId); projectile.PruningProxyId = -1; } - - internal static void OnProjectileMoved(Projectile projectile, ref Vector3D velocity) - { - if (projectile.PruningProxyId == -1) - return; - BoundingSphereD sphere = new BoundingSphereD(projectile.Position, projectile.Info.AmmoDef.Const.LargestHitSize); - BoundingBoxD result; - BoundingBoxD.CreateFromSphere(ref sphere, out result); - Session.I.ProjectileTree.MoveProxy(projectile.PruningProxyId, ref result, velocity); - } - internal static void GetAllProjectilesInSphere(Session session, ref BoundingSphereD sphere, List result, bool clearList = true) { session.ProjectileTree.OverlapAllBoundingSphere(ref sphere, result, clearList); diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index 310fa68a..12f05d41 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using CoreSystems.Support; using Jakaria.API; using Sandbox.Game.Entities; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileGen.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileGen.cs index 2f6f8703..6b596f7b 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileGen.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileGen.cs @@ -1,6 +1,5 @@ using CoreSystems.Support; using Sandbox.Game.Entities; -using System; using VRage.Game.Entity; using VRageMath; using static CoreSystems.Support.NewProjectile; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 89f06d5e..2e2ff4cb 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -5,7 +5,6 @@ using Sandbox.Game.Entities; using Sandbox.ModAPI; using VRage; -using VRage.Game.Components; using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.Game.ModAPI.Interfaces; @@ -18,7 +17,6 @@ using Jakaria.API; using static CoreSystems.Projectiles.Projectile; using static CoreSystems.Support.VoxelIntersect; -using System.Runtime.CompilerServices; namespace CoreSystems.Projectiles { @@ -379,15 +377,7 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) hitEntity.EventType = info.FirstWaterHitTick != tick ? Voxel : Water; } else if (voxelState == VoxelIntersectBranch.DeferedMissUpdate || voxelState == VoxelIntersectBranch.DeferFullCheck) - { FullVoxelCheck(p, voxel, voxelState, lineCheck); - /* - lock (DeferedVoxels) - { - DeferedVoxels.Add(new DeferedVoxels { Projectile = p, Branch = voxelState, Voxel = voxel, LineCheck = lineCheck}); - } - */ - } } else if (ent.Physics != null && !ent.Physics.IsPhantom && !ent.IsPreview && grid != null) { @@ -633,133 +623,117 @@ internal void ProjectileHit(Projectile p, Projectile target, bool lineCheck, ref internal bool GenerateHitInfo(Projectile p) { - try + var info = p.Info; + var count = info.HitList.Count; + if (count > 1) { - var info = p.Info; - var count = info.HitList.Count; - if (count > 1) - { - try { info.HitList.Sort((x, y) => GetEntityCompareDist(x, y, info)); } // Unable to sort because the IComparer.Compare() method returns inconsistent results - catch (Exception ex) { Log.Line($"p.Info.HitList.Sort failed: {ex} - weapon:{info.Weapon.System.PartName} - ammo:{info.AmmoDef.AmmoRound} - hitCount:{info.HitList.Count}", null, true); } - } - else GetEntityCompareDist(info.HitList[0], null, info); - - try - { - var pulseTrigger = false; - var voxelFound = false; - for (int i = info.HitList.Count - 1; i >= 0; i--) - { - var ent = info.HitList[i]; - if (ent.EventType == Voxel) - voxelFound = true; - - if (!ent.Hit) { - if (ent.PulseTrigger) pulseTrigger = true; - info.HitList.RemoveAtFast(i); - ent.Clean(); - } - else break; - } - - if (pulseTrigger) - { + try { info.HitList.Sort((x, y) => GetEntityCompareDist(x, y, info)); } // Unable to sort because the IComparer.Compare() method returns inconsistent results + catch (Exception ex) { Log.Line($"p.Info.HitList.Sort failed: {ex} - weapon:{info.Weapon.System.PartName} - ammo:{info.AmmoDef.AmmoRound} - hitCount:{info.HitList.Count}", null, true); } + } + else GetEntityCompareDist(info.HitList[0], null, info); - info.ExpandingEwarField = true; - p.DistanceToTravelSqr = info.DistanceTraveled * info.DistanceTraveled; - p.PrevVelocity = p.Velocity; - p.Velocity = Vector3D.Zero; - info.ProHit.LastHit = p.Position; - info.HitList.Clear(); - return false; - } - var finalCount = info.HitList.Count; - try - { - if (finalCount > 0) - { - var aConst = info.AmmoDef.Const; - try - { - if (voxelFound && info.HitList[0].EventType != Voxel && aConst.IsBeamWeapon) - info.VoxelCache.HitRefreshed = 0; - } - catch (Exception ex) { Log.Line($"Exception in HitRefreshed finalCount: {ex}", null, true); } + var pulseTrigger = false; + var voxelFound = false; + for (int i = info.HitList.Count - 1; i >= 0; i--) + { + var ent = info.HitList[i]; + if (ent.EventType == Voxel) + voxelFound = true; + + if (!ent.Hit) { + if (ent.PulseTrigger) pulseTrigger = true; + info.HitList.RemoveAtFast(i); + ent.Clean(); + } + else break; + } - var checkHit = (!aConst.IsBeamWeapon || !info.ShieldBypassed || finalCount > 1); + if (pulseTrigger) + { - var blockingEnt = !info.ShieldBypassed || finalCount == 1 ? 0 : 1; - var hitEntity = info.HitList[blockingEnt]; - if (hitEntity == null) - { - Log.Line($"null hitEntity"); - return false; - } + info.ExpandingEwarField = true; + p.DistanceToTravelSqr = info.DistanceTraveled * info.DistanceTraveled; + p.PrevVelocity = p.Velocity; + p.Velocity = Vector3D.Zero; + info.ProHit.LastHit = p.Position; + info.HitList.Clear(); + return false; + } - if (!checkHit) - hitEntity.HitPos = p.Beam.To; + var finalCount = info.HitList.Count; - info.ProHit.Entity = hitEntity.Entity; - info.ProHit.LastHit = hitEntity.HitPos ?? p.Beam.To; + if (finalCount > 0) + { + var aConst = info.AmmoDef.Const; + if (voxelFound && info.HitList[0].EventType != Voxel && aConst.IsBeamWeapon) + info.VoxelCache.HitRefreshed = 0; - if (aConst.OnHit && Session.I.Tick >= info.ProHit.EndTick) - { - info.ProHit.EndTick = Session.I.Tick + aConst.OnHitDuration; - } + var checkHit = (!aConst.IsBeamWeapon || !info.ShieldBypassed || finalCount > 1); - if (p.EnableAv || aConst.VirtualBeams) - { - Vector3D lastHitVel = Vector3D.Zero; - if (hitEntity.EventType == Shield) - { - var cube = hitEntity.Entity as MyCubeBlock; - if (cube?.CubeGrid?.Physics != null) - lastHitVel = cube.CubeGrid.Physics.LinearVelocity; - } - else if (hitEntity.Projectile != null) - lastHitVel = hitEntity.Projectile?.Velocity ?? Vector3D.Zero; - else if (hitEntity.Entity?.Physics != null) - lastHitVel = hitEntity.Entity?.Physics.LinearVelocity ?? Vector3D.Zero; - else lastHitVel = Vector3D.Zero; - - Vector3D visualHitPos; - if (hitEntity.Entity is MyCubeGrid) - { - IHitInfo hitInfo = null; - if (Session.I.HandlesInput && hitEntity.HitPos.HasValue && Vector3D.DistanceSquared(hitEntity.HitPos.Value, Session.I.CameraPos) < 22500 && Session.I.CameraFrustrum.Contains(hitEntity.HitPos.Value) != ContainmentType.Disjoint) - { - var entSphere = hitEntity.Entity.PositionComp.WorldVolume; - var from = hitEntity.Intersection.From + (hitEntity.Intersection.Direction * MyUtils.GetSmallestDistanceToSphereAlwaysPositive(ref hitEntity.Intersection.From, ref entSphere)); - var to = hitEntity.HitPos.Value + (hitEntity.Intersection.Direction * 3f); - Session.I.Physics.CastRay(from, to, out hitInfo, CollisionLayers.NoVoxelCollisionLayer); - } - visualHitPos = hitInfo?.HitEntity != null ? hitInfo.Position : hitEntity.HitPos ?? p.Beam.To; - } - else visualHitPos = hitEntity.HitPos ?? p.Beam.To; + var blockingEnt = !info.ShieldBypassed || finalCount == 1 ? 0 : 1; + var hitEntity = info.HitList[blockingEnt]; + if (hitEntity == null) + { + Log.Line($"null hitEntity"); + return false; + } - if (p.EnableAv) { - info.AvShot.LastHitShield = hitEntity.EventType == Shield; - info.AvShot.Hit = new Hit { Entity = hitEntity.Entity, EventType = hitEntity.EventType, HitTick = Session.I.Tick, HitVelocity = lastHitVel, LastHit = visualHitPos, SurfaceHit = visualHitPos }; - } - else if (aConst.VirtualBeams) - AvShot.UpdateVirtualBeams(p, info, hitEntity, visualHitPos, lastHitVel, true); + if (!checkHit) + hitEntity.HitPos = p.Beam.To; - if (info.AimedShot && Session.I.TrackingAi != null && Session.I.TargetUi.HitIncrease < 0.1d && info.Ai.ControlComp == null && (aConst.FixedFireAmmo || info.Weapon.Comp.Data.Repo.Values.Set.Overrides.Control != ProtoWeaponOverrides.ControlModes.Auto)) - Session.I.TargetUi.SetHit(info); - } + info.ProHit.Entity = hitEntity.Entity; + info.ProHit.LastHit = hitEntity.HitPos ?? p.Beam.To; + if (aConst.OnHit && Session.I.Tick >= info.ProHit.EndTick) + { + info.ProHit.EndTick = Session.I.Tick + aConst.OnHitDuration; + } - return true; + if (p.EnableAv || aConst.VirtualBeams) + { + Vector3D lastHitVel = Vector3D.Zero; + if (hitEntity.EventType == Shield) + { + var cube = hitEntity.Entity as MyCubeBlock; + if (cube?.CubeGrid?.Physics != null) + lastHitVel = cube.CubeGrid.Physics.LinearVelocity; + } + else if (hitEntity.Projectile != null) + lastHitVel = hitEntity.Projectile?.Velocity ?? Vector3D.Zero; + else if (hitEntity.Entity?.Physics != null) + lastHitVel = hitEntity.Entity?.Physics.LinearVelocity ?? Vector3D.Zero; + else lastHitVel = Vector3D.Zero; + + Vector3D visualHitPos; + if (hitEntity.Entity is MyCubeGrid) + { + IHitInfo hitInfo = null; + if (Session.I.HandlesInput && hitEntity.HitPos.HasValue && Vector3D.DistanceSquared(hitEntity.HitPos.Value, Session.I.CameraPos) < 22500 && Session.I.CameraFrustrum.Contains(hitEntity.HitPos.Value) != ContainmentType.Disjoint) + { + var entSphere = hitEntity.Entity.PositionComp.WorldVolume; + var from = hitEntity.Intersection.From + (hitEntity.Intersection.Direction * MyUtils.GetSmallestDistanceToSphereAlwaysPositive(ref hitEntity.Intersection.From, ref entSphere)); + var to = hitEntity.HitPos.Value + (hitEntity.Intersection.Direction * 3f); + Session.I.Physics.CastRay(from, to, out hitInfo, CollisionLayers.NoVoxelCollisionLayer); } + visualHitPos = hitInfo?.HitEntity != null ? hitInfo.Position : hitEntity.HitPos ?? p.Beam.To; + } + else visualHitPos = hitEntity.HitPos ?? p.Beam.To; + if (p.EnableAv) { + info.AvShot.LastHitShield = hitEntity.EventType == Shield; + info.AvShot.Hit = new Hit { Entity = hitEntity.Entity, EventType = hitEntity.EventType, HitTick = Session.I.Tick, HitVelocity = lastHitVel, LastHit = visualHitPos, SurfaceHit = visualHitPos }; } - catch (Exception ex) { Log.Line($"Exception in GenerateHitInfo1: {ex}", null, true); } + else if (aConst.VirtualBeams) + AvShot.UpdateVirtualBeams(p, info, hitEntity, visualHitPos, lastHitVel, true); + + if (info.AimedShot && Session.I.TrackingAi != null && Session.I.TargetUi.HitIncrease < 0.1d && info.Ai.ControlComp == null && (aConst.FixedFireAmmo || info.Weapon.Comp.Data.Repo.Values.Set.Overrides.Control != ProtoWeaponOverrides.ControlModes.Auto)) + Session.I.TargetUi.SetHit(info); } - catch (Exception ex) { Log.Line($"Exception in GenerateHitInfo2: {ex}", null, true); } + + return true; } - catch (Exception ex) { Log.Line($"Exception in GenerateHitInfo3: {ex}", null, true); } return false; } diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs index 3c1960a3..35a2f756 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs @@ -749,10 +749,5 @@ internal void GrowPlanetCache(Vector3D hitPos) Vector3D.Distance(ref PlanetSphere.Center, ref hitPos, out dist); PlanetSphere = new BoundingSphereD(PlanetSphere.Center, dist); } - - internal void DebugDraw() - { - DsDebugDraw.DrawSphere(HitSphere, Color.Red); - } } } diff --git a/Data/Scripts/CoreSystems/Session/SessionCompMgr.cs b/Data/Scripts/CoreSystems/Session/SessionCompMgr.cs index 7537a7b1..060a0764 100644 --- a/Data/Scripts/CoreSystems/Session/SessionCompMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionCompMgr.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Remoting.Metadata.W3cXsd2001; +using System.Collections.Generic; using CoreSystems.Platform; using CoreSystems.Support; using Sandbox.Game.Entities; @@ -10,7 +8,6 @@ using VRage.Game.Entity; using VRage.Utils; using VRageMath; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index 42b3b50d..f9280435 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.Utils; using static CoreSystems.Support.CoreComponent.Trigger; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; namespace CoreSystems diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index ad33c534..828b1242 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -1017,13 +1017,6 @@ private void DamageProjectile(HitEntity hitEnt, ProInfo attacker) scaledDamage *= fallOffMultipler; } - //Rework of projectile on projectile damage calcs, as previously you could end up with a high primary damage projectile - //unintentionally surviving multiple hits and doing nearly infinite damage against other projectiles. This was more apparent - //with smarts that would select and chase a new target. Projectiles with EOL detonations could also pop multiple times without dying. - //If your attacking projectile has a health > 0, it will deduct the HealthHitModifier damage done to the target from its own health. It will die once health hits zero or less - //If your attacking projectile has a health = 0, the HealthHitModifier damage it does will be deducted from the primary damage field. It will die once damage hits zero or less - //In either case, a projectile with EOL will detonate on hitting another projectile and die - var deductFromAttackerHealth = attacker.AmmoDef.Health > 0; if (scaledDamage >= objHp) { @@ -1211,16 +1204,11 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl foundSomething = false; return; } - - //Log.Line($"Start"); - //var watch = System.Diagnostics.Stopwatch.StartNew(); var rootHitPos = rootInfo.QueryPos; //local cube grid var localfrom = grid.WorldToGridScaledLocal(direction.From); var localto = grid.WorldToGridScaledLocal(direction.To); var gridsize = grid.GridSizeR; aoeHits = 0; - - //Log.Line($"Raw rootpos{root.Position} localctr{rootInfo.QueryPos} rootpos {rootPos} localfrom{localfrom} localto{localto} min{root.Min} max{root.Max}"); radius *= gridsize; //GridSizeR is 0.4 for LG, 2.0 for SG depth *= gridsize; var gmin = grid.Min; @@ -1250,12 +1238,8 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl var xhit = (hitray.Intersects(xplane) ?? 0) + (hitray.Intersects(xmplane) ?? 0); var yhit = (hitray.Intersects(yplane) ?? 0) + (hitray.Intersects(ymplane) ?? 0); var zhit = (hitray.Intersects(zplane) ?? 0) + (hitray.Intersects(zmplane) ?? 0); - //Log.Line($"localto{localto} rootpos{rootPos} rootmin{root.Min} rootmax{root.Max}"); - //Log.Line($"xhit {xhit} yhit {yhit} zhit{zhit}"); var axishit = new Vector3D(xhit, yhit, zhit); - // Log.Line($"Hitvec x{hitray.Intersects(xplane)} y{hitray.Intersects(yplane)} xm{hitray.Intersects(xmplane)} ym{hitray.Intersects(ymplane)}"); - switch (axishit.AbsMaxComponent())//sort out which "face" was hit and coming/going along that axis { case 1://hit face perp to y @@ -1366,8 +1350,6 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl } } } - //watch.Stop(); - //Log.Line($"End {watch.ElapsedMilliseconds}"); } public static void GetBlocksInsideSphereFast(MyCubeGrid grid, ref BoundingSphereD sphere, bool checkDestroyed, List blocks) @@ -1458,25 +1440,5 @@ internal bool RayAccuracyCheck(HitEntity hitEnt, IMySlimBlock block) } return false; } - - private bool RayAccuracyCheck(HitEntity hitEnt, IMyCharacter character) - { - var box = character.PositionComp.WorldAABB; - var ray = new RayD(ref hitEnt.Intersection.From, ref hitEnt.Intersection.Direction); - var rayHit = ray.Intersects(box); - if (rayHit != null) - { - var hitPos = hitEnt.Intersection.From + (hitEnt.Intersection.Direction * (rayHit.Value - 0.1f)); - IHitInfo hitInfo; - if (Physics.CastRay(hitPos, hitEnt.Intersection.To, out hitInfo, 15)) - { - var hit = (MyEntity)hitInfo.HitEntity; - var hitPoint = hitInfo.Position + (hitEnt.Intersection.Direction * 0.1f); - var rayHitTarget = box.Contains(hitPoint) != ContainmentType.Disjoint && hit == character; - return rayHitTarget; - } - } - return false; - } } } diff --git a/Data/Scripts/CoreSystems/Session/SessionEvents.cs b/Data/Scripts/CoreSystems/Session/SessionEvents.cs index ea339761..cec3cde7 100644 --- a/Data/Scripts/CoreSystems/Session/SessionEvents.cs +++ b/Data/Scripts/CoreSystems/Session/SessionEvents.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Text; using CoreSystems.Platform; using CoreSystems.Support; @@ -13,7 +12,6 @@ using VRage.Collections; using VRage.Game.Entity; using VRage.Game.ModAPI; -using VRageMath; using static CoreSystems.Support.Ai; using IMyControllableEntity = VRage.Game.ModAPI.Interfaces.IMyControllableEntity; diff --git a/Data/Scripts/CoreSystems/Session/SessionEwar.cs b/Data/Scripts/CoreSystems/Session/SessionEwar.cs index 50cf26b8..ebe70785 100644 --- a/Data/Scripts/CoreSystems/Session/SessionEwar.cs +++ b/Data/Scripts/CoreSystems/Session/SessionEwar.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Text; using CoreSystems.Support; using Sandbox.Game.Entities; using Sandbox.ModAPI; diff --git a/Data/Scripts/CoreSystems/Session/SessionInit.cs b/Data/Scripts/CoreSystems/Session/SessionInit.cs index b66f3851..60bb806e 100644 --- a/Data/Scripts/CoreSystems/Session/SessionInit.cs +++ b/Data/Scripts/CoreSystems/Session/SessionInit.cs @@ -71,7 +71,6 @@ private void BeforeStartInit() GenerateButtonMap(); Settings = new CoreSettings(this); ReallyStupidKeenShit(); - CounterKeenLogMessage(); var control = MyAPIGateway.Input.GetGameControl(MyStringId.GetOrCompute("RELOAD")); UiInput.ReloadKey = control.GetKeyboardControl(); diff --git a/Data/Scripts/CoreSystems/Session/SessionLocalPlayer.cs b/Data/Scripts/CoreSystems/Session/SessionLocalPlayer.cs index 4a628ec2..ddcea868 100644 --- a/Data/Scripts/CoreSystems/Session/SessionLocalPlayer.cs +++ b/Data/Scripts/CoreSystems/Session/SessionLocalPlayer.cs @@ -8,11 +8,9 @@ using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.Input; -using VRage.ObjectBuilders; using VRage.Utils; using VRageMath; using static CoreSystems.Support.Ai; -using static VRage.Game.MyObjectBuilder_ControllerSchemaDefinition; namespace CoreSystems { diff --git a/Data/Scripts/CoreSystems/Session/SessionModHandlers.cs b/Data/Scripts/CoreSystems/Session/SessionModHandlers.cs index b674a735..d16a5983 100644 --- a/Data/Scripts/CoreSystems/Session/SessionModHandlers.cs +++ b/Data/Scripts/CoreSystems/Session/SessionModHandlers.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using CoreSystems.Support; using Sandbox.Definitions; using Sandbox.ModAPI; -using VRage; using VRage.Utils; namespace CoreSystems diff --git a/Data/Scripts/CoreSystems/Session/SessionNetwork.cs b/Data/Scripts/CoreSystems/Session/SessionNetwork.cs index 53216a3b..c388e109 100644 --- a/Data/Scripts/CoreSystems/Session/SessionNetwork.cs +++ b/Data/Scripts/CoreSystems/Session/SessionNetwork.cs @@ -105,11 +105,6 @@ private bool ProccessClientPacket(PacketObj packetObj, bool firstRun = true) ClientProjectileTargetSyncs(packetObj); break; } - case PacketType.HandWeaponDebug: - { - ClientHandDebug(packetObj); - break; - } case PacketType.AimTargetUpdate: { ClientFakeTargetUpdate(packetObj); diff --git a/Data/Scripts/CoreSystems/Session/SessionNetworkCMethods.cs b/Data/Scripts/CoreSystems/Session/SessionNetworkCMethods.cs index 3c328e6f..797c9704 100644 --- a/Data/Scripts/CoreSystems/Session/SessionNetworkCMethods.cs +++ b/Data/Scripts/CoreSystems/Session/SessionNetworkCMethods.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; -using CoreSystems.Platform; +using CoreSystems.Platform; using CoreSystems.Projectiles; using CoreSystems.Support; using Sandbox.Game.Entities; -using Sandbox.ModAPI; using VRage.Game.Entity; -using static CoreSystems.Session; using static CoreSystems.Support.Ai; namespace CoreSystems { @@ -456,18 +453,6 @@ private bool ClientSentReport(PacketObj data) } - private bool ClientHandDebug(PacketObj data) - { - var packet = data.Packet; - var debugPacket = (HandWeaponDebugPacket)packet; - if (debugPacket == null) return Error(data, Msg("HandDebug")); - { - DrawHandDebug(debugPacket); - } - data.Report.PacketValid = true; - return true; - } - private bool ClientProjectilePosSyncs(PacketObj data) { var packet = data.Packet; diff --git a/Data/Scripts/CoreSystems/Session/SessionNetworkSMethods.cs b/Data/Scripts/CoreSystems/Session/SessionNetworkSMethods.cs index 2f1665b3..03cf0cc2 100644 --- a/Data/Scripts/CoreSystems/Session/SessionNetworkSMethods.cs +++ b/Data/Scripts/CoreSystems/Session/SessionNetworkSMethods.cs @@ -2,7 +2,6 @@ using CoreSystems.Support; using Sandbox.Game.Entities; using Sandbox.ModAPI; -using VRage.Game.Entity; using VRageMath; using static CoreSystems.Platform.ControlSys; using static CoreSystems.Support.Focus; diff --git a/Data/Scripts/CoreSystems/Session/SessionNetworkSupport.cs b/Data/Scripts/CoreSystems/Session/SessionNetworkSupport.cs index e1b3591d..1a04d5f8 100644 --- a/Data/Scripts/CoreSystems/Session/SessionNetworkSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionNetworkSupport.cs @@ -1,13 +1,9 @@ using System; using CoreSystems.Platform; using CoreSystems.Support; -using Sandbox.ModAPI; -using VRage; using VRage.Game.Entity; -using VRage.Game.ModAPI; using VRageMath; using static CoreSystems.Platform.ControlSys; -using static CoreSystems.Platform.Weapon.ShootManager; using static CoreSystems.Support.CoreComponent; namespace CoreSystems @@ -103,24 +99,6 @@ public void CleanUp() #endregion #region ServerOnly - - internal readonly HandWeaponDebugPacket HandDebugPacketPacket = new HandWeaponDebugPacket {PType = PacketType.HandWeaponDebug}; - private void SendHandDebugInfo(Weapon weapon) - { - PlayerMap player; - if (Players.TryGetValue(weapon.Comp.Data.Repo.Values.State.PlayerId, out player)) - { - HandDebugPacketPacket.SenderId = player.Player.SteamUserId; - - PacketsToClient.Add(new PacketInfo - { - SingleClient = true, - Unreliable = true, - Packet = HandDebugPacketPacket - }); - } - } - private void SendProjectilePosSyncs() { var packet = ProtoWeaponProPosPacketPool.Count > 0 ? ProtoWeaponProPosPacketPool.Pop() : new ProjectileSyncPositionPacket (); diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index a0188244..b64a5d84 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -83,18 +83,7 @@ internal void Timings() if (IsServer && PbActivate && !PbApiInited) Api.PbInit(); if (HandlesInput && !ClientCheck && Tick > 1200) - { - if (IsClient) - { - if (ServerVersion != ModContext.ModName) - { - var message = $"::CoreSystems Version Mismatch:: Server:{ServerVersion} - Client:{ModContext.ModName} - Unexpected behavior may occur."; - Log.Line(message); - //MyAPIGateway.Utilities.ShowNotification(message, 10000, "Red"); - } - } ClientCheck = true; - } } LCount++; @@ -146,8 +135,6 @@ internal void Timings() if (!PlayersLoaded && KeenFuckery()) PlayersLoaded = true; - - //Api.PbHackDetection(); } internal void AddLosCheck(LosDebug debug) @@ -257,45 +244,6 @@ internal void VisualDebuging() else ShowLocalNotify($"[Approach] Completed on stage:{ApproachDebug.Stage} - {ApproachDebug.Start1}:{ApproachDebug.Start2}:{ApproachDebug.End1}:{ApproachDebug.End2} - {ApproachDebug.TimeSinceSpawn}:{ApproachDebug.NextSpawn}", 2000, "White"); } - - /* - if (Tick - _clientHandDebug.LastHitTick < 1200 || Tick - _clientHandDebug.LastShootTick < 1200) - { - if (_clientHandDebug.ShootStart != Vector3D.Zero) - DsDebugDraw.DrawLine(_clientHandDebug.ShootStart, _clientHandDebug.ShootEnd, Color.Blue, 0.2f); - if (_clientHandDebug.HitStart != Vector3D.Zero) - DsDebugDraw.DrawLine(_clientHandDebug.HitStart, _clientHandDebug.HitEnd, Color.Red, 0.2f); - } - - if (Tick - _clientHandDebug2.LastHitTick < 1200 || Tick - _clientHandDebug2.LastShootTick < 1200) - { - if (_clientHandDebug2.ShootStart != Vector3D.Zero) - DsDebugDraw.DrawLine(_clientHandDebug2.ShootStart, _clientHandDebug2.ShootEnd, Color.Green, 0.2f); - if (_clientHandDebug2.HitStart != Vector3D.Zero) - DsDebugDraw.DrawLine(_clientHandDebug2.HitStart, _clientHandDebug2.HitEnd, Color.Yellow, 0.2f); - } - */ - } - - public void AddHandHitDebug(Vector3D start, Vector3D end, bool shoot) - { - var packet = HandlesInput ? _clientHandDebug2 : HandDebugPacketPacket; - if (shoot) - { - packet.LastShootTick = Tick + 1; - packet.LastHitTick = uint.MaxValue; - packet.ShootStart = start; - packet.ShootEnd = end; - packet.HitStart = Vector3D.Zero; - packet.HitEnd = Vector3D.Zero; - } - else - { - packet.LastHitTick = Tick + 1; - packet.HitStart = start; - packet.HitEnd = end; - } - } private double _drawCpuTime; @@ -1134,26 +1082,6 @@ internal void CheckToolbarForVanilla(MyCubeBlock cube) } } - private readonly HandWeaponDebugPacket _clientHandDebug = new HandWeaponDebugPacket(); - private readonly HandWeaponDebugPacket _clientHandDebug2 = new HandWeaponDebugPacket(); - - private void DrawHandDebug(HandWeaponDebugPacket hDebug) - { - _clientHandDebug.ShootStart = hDebug.ShootStart; - _clientHandDebug.ShootEnd = hDebug.ShootEnd; - _clientHandDebug.HitStart = hDebug.HitStart; - _clientHandDebug.HitEnd = hDebug.HitEnd; - _clientHandDebug.LastHitTick = Tick; - _clientHandDebug.LastShootTick = Tick; - } - - private static void CounterKeenLogMessage(bool console = true) - { - var message = "\n***\n [CoreSystems] Ignore log messages from keen stating 'Mod CoreSystems is accessing physics from parallel threads'\n CS is using a thread safe parallel.for, not a parallel task\n***"; - if (console) MyLog.Default.WriteLineAndConsole(message); - else MyLog.Default.WriteLine(message); - } - internal static double ModRadius(double radius, bool largeBlock) { if (largeBlock && radius < 3) radius = 3; diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index 1634418c..b4a04d84 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -762,8 +762,7 @@ private void AiLoop() var delayedFire = w.System.DelayCeaseFire && !w.Target.IsAligned && Tick - w.CeaseFireDelayTick <= w.System.CeaseFireDelay; var finish = w.FinishShots || delayedFire; - var pauseForRay = w.Target.TargetState != TargetStates.IsProjectile && !wComp.Data.Repo.Values.Set.Overrides.Override && w.Casting && !w.PreFired && !finish; - var shootRequest = (anyShot || finish) && !pauseForRay; + var shootRequest = (anyShot || finish); var shotReady = canShoot && shootRequest; var shoot = shotReady && ai.CanShoot && (!aConst.RequiresTarget || w.Target.HasTarget || finish || overRide || wComp.ShootManager.Signal == Weapon.ShootManager.Signals.Manual); diff --git a/Data/Scripts/CoreSystems/Support/Draw/DrawExts.cs b/Data/Scripts/CoreSystems/Support/Draw/DrawExts.cs index f9bedb99..d2791a9f 100644 --- a/Data/Scripts/CoreSystems/Support/Draw/DrawExts.cs +++ b/Data/Scripts/CoreSystems/Support/Draw/DrawExts.cs @@ -2,7 +2,6 @@ using VRage.Game; using VRage.Utils; using VRageMath; -using VRageRender; using BlendTypeEnum = VRageRender.MyBillboard.BlendTypeEnum; namespace CoreSystems.Support diff --git a/Data/Scripts/CoreSystems/Support/DsAutoResetEvent.cs b/Data/Scripts/CoreSystems/Support/DsAutoResetEvent.cs deleted file mode 100644 index e758e0c7..00000000 --- a/Data/Scripts/CoreSystems/Support/DsAutoResetEvent.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading; -using VRage; - -namespace CoreSystems.Support -{ - internal class DsAutoResetEvent - { - private readonly FastResourceLock _lock = new FastResourceLock(); - private int _waiters; - - public void WaitOne() - { - _lock.AcquireExclusive(); - _waiters = 1; - _lock.AcquireExclusive(); - _lock.ReleaseExclusive(); - } - - public void Set() - { - if (Interlocked.Exchange(ref _waiters, 0) > 0) - _lock.ReleaseExclusive(); - } - } - - internal class DsPulseEvent - { - private readonly object _signal = new object(); - private bool _signalSet; - - public void WaitOne() - { - lock (_signal) - { - while (!_signalSet) - { - Monitor.Wait(_signal); - } - _signalSet = false; - } - } - - public void Set() - { - lock (_signal) - { - _signalSet = true; - Monitor.Pulse(_signal); - } - } - } -} diff --git a/Data/Scripts/CoreSystems/Support/GridIntersect.cs b/Data/Scripts/CoreSystems/Support/GridIntersect.cs index 5134f7c7..ae62d4b5 100644 --- a/Data/Scripts/CoreSystems/Support/GridIntersect.cs +++ b/Data/Scripts/CoreSystems/Support/GridIntersect.cs @@ -141,20 +141,6 @@ internal static bool BresenhamGridIntersection(MyCubeGrid grid, ref Vector3D wor } return false; } - - public static IMySlimBlock FirstBlock(this IMyCubeGrid grid, Vector3D worldStart, Vector3D worldEnd, - Func pred = null, Vector3I? gridSizeInflate = null) - { - for (var itr = CellEnumerator.EnumerateGridCells(grid, worldStart, worldEnd, gridSizeInflate); - itr.IsValid; - itr.MoveNext()) - { - var block = grid.GetCubeBlock(itr.Current); - if (block != null && (pred == null || pred.Invoke(block))) - return block; - } - return null; - } } public struct CellEnumerator diff --git a/Data/Scripts/CoreSystems/Support/Log.cs b/Data/Scripts/CoreSystems/Support/Log.cs index ef54d460..5a9e4568 100644 --- a/Data/Scripts/CoreSystems/Support/Log.cs +++ b/Data/Scripts/CoreSystems/Support/Log.cs @@ -288,39 +288,6 @@ public static void LineShortDate(string text, string instanceName = null) } } - public static void NetLog(string text, Session session, int logLevel) - { - var set = session.AuthorSettings; - var netEnabled = session.AuthLogging && set[4] >= 0 && logLevel >= set[5]; - if (netEnabled) - NetLogger(session, "[R-LOG] " + text, string.Empty); - } - - public static void Chars(string text, string instanceName = null) - { - try - { - var name = instanceName ?? _defaultInstance; - var instance = _instances[name]; - if (instance.TextWriter != null) { - - if (name == _defaultInstance && !instance.Session.LocalVersion && instance.Paused()) - return; - - instance.TextWriter.Write(text); - instance.TextWriter.Flush(); - - var set = instance.Session.AuthorSettings; - var netEnabled = instance.Session.AuthLogging && name == _defaultInstance && set[0] >= 0 || name == "perf" && set[1] >= 0 || name == "stats" && set[2] >= 0 || name == "net" && set[3] >= 0; - if (netEnabled) - NetLogger(instance.Session, "[R-LOG] " + text, name); - } - } - catch (Exception e) - { - } - } - public static void CleanLine(string text, string instanceName = null) { try @@ -346,31 +313,6 @@ public static void CleanLine(string text, string instanceName = null) } } - public static void ThreadedWrite(string logLine) - { - _threadedLineQueue.Enqueue(new[] { $"Threaded Time: {DateTime.Now:HH-mm-ss-fff} - ", logLine }); - MyAPIGateway.Utilities.InvokeOnGameThread(WriteLog); - } - - private static void WriteLog() { - string[] line; - - var instance = _instances[_defaultInstance]; - if (instance.TextWriter != null) - Init("debugdevelop.log", null); - - instance = _instances[_defaultInstance]; - - while (_threadedLineQueue.TryDequeue(out line)) - { - if (instance.TextWriter != null) - { - instance.TextWriter.WriteLine(line[0] + line[1]); - instance.TextWriter.Flush(); - } - } - } - public static void Close() { try diff --git a/Data/Scripts/CoreSystems/Support/MathFuncs.cs b/Data/Scripts/CoreSystems/Support/MathFuncs.cs index 908fb75c..429618cf 100644 --- a/Data/Scripts/CoreSystems/Support/MathFuncs.cs +++ b/Data/Scripts/CoreSystems/Support/MathFuncs.cs @@ -5,235 +5,6 @@ namespace CoreSystems.Support { - #region MissileGuidanceBase - - internal class ProNavGuidanceInlined - { - private readonly double _updatesPerSecond; - - public Vector3D? LastVelocity; - - public ProNavGuidanceInlined(double updatesPerSecond) - { - _updatesPerSecond = updatesPerSecond; - - } - - public void ClearAcceleration() - { - LastVelocity = null; - } - - public Vector3D Update(Vector3D missilePosition, Vector3D missileVelocity, double missileAcceleration, Vector3D targetPosition, Vector3D targetVelocity, Vector3D? gravity = null, double navConstant = 3, double maxLateralThrustProportion = 1, double navAccelConstant = 0) - { - Vector3D targetAcceleration = Vector3D.Zero; - if (LastVelocity.HasValue) - targetAcceleration = (targetVelocity - LastVelocity.Value) * _updatesPerSecond; - LastVelocity = targetVelocity; - - Vector3D missileToTarget = targetPosition - missilePosition; - Vector3D missileToTargetNorm = Vector3D.Normalize(missileToTarget); - Vector3D relativeVelocity = targetVelocity - missileVelocity; - Vector3D lateralTargetAcceleration = (targetAcceleration - Vector3D.Dot(targetAcceleration, missileToTargetNorm) * missileToTargetNorm); - - Vector3D omega = Vector3D.Cross(missileToTarget, relativeVelocity) / Math.Max(missileToTarget.LengthSquared(), 1); //to combat instability at close range - var lateralAcceleration = navConstant * relativeVelocity.Length() * Vector3D.Cross(omega, missileToTargetNorm) + navAccelConstant * lateralTargetAcceleration; - - - Vector3D pointingVector; - if (Vector3D.IsZero(lateralAcceleration)) - { - pointingVector = missileToTargetNorm * missileAcceleration; - } - else - { - double maxLateralThrust = missileAcceleration * Math.Min(1, Math.Max(0, maxLateralThrustProportion)); - if (lateralAcceleration.LengthSquared() > maxLateralThrust * maxLateralThrust) - { - Vector3D.Normalize(ref lateralAcceleration, out lateralAcceleration); - lateralAcceleration *= maxLateralThrust; - } - - - var diff = missileAcceleration * missileAcceleration - lateralAcceleration.LengthSquared(); - pointingVector = diff < 0 ? Vector3D.Normalize(lateralAcceleration) * missileAcceleration : lateralAcceleration + Math.Sqrt(diff) * missileToTargetNorm; - } - - if (gravity.HasValue && gravity.Value.LengthSquared() > 1e-3) - { - - if (!Vector3D.IsZero(pointingVector)) - { - var directionNorm = Vector3D.IsUnit(ref pointingVector) ? pointingVector : Vector3D.Normalize(pointingVector); - Vector3D gravityCompensationVec; - - if (Vector3D.IsZero(gravity.Value) || Vector3D.IsZero(pointingVector)) - gravityCompensationVec = Vector3D.Zero; - else - gravityCompensationVec = (gravity.Value - gravity.Value.Dot(pointingVector) / pointingVector.LengthSquared() * pointingVector); - - var diffSq = missileAcceleration * missileAcceleration - gravityCompensationVec.LengthSquared(); - pointingVector = diffSq < 0 ? pointingVector - gravity.Value : directionNorm * Math.Sqrt(diffSq) + gravityCompensationVec; - } - - } - - return pointingVector; - } - } - - - internal abstract class MissileGuidanceBase - { - protected double _deltaTime; - protected double _updatesPerSecond; - - Vector3D? _lastVelocity; - - protected MissileGuidanceBase(double updatesPerSecond) - { - _updatesPerSecond = updatesPerSecond; - _deltaTime = 1.0 / _updatesPerSecond; - } - - public void ClearAcceleration() - { - _lastVelocity = null; - } - - public Vector3D Update(Vector3D missilePosition, Vector3D missileVelocity, double missileAcceleration, Vector3D targetPosition, Vector3D targetVelocity, Vector3D? gravity = null) - { - Vector3D targetAcceleration = Vector3D.Zero; - if (_lastVelocity.HasValue) - targetAcceleration = (targetVelocity - _lastVelocity.Value) * _updatesPerSecond; - _lastVelocity = targetVelocity; - - Vector3D pointingVector = GetPointingVector(missilePosition, missileVelocity, missileAcceleration, targetPosition, targetVelocity, targetAcceleration); - - if (gravity.HasValue && gravity.Value.LengthSquared() > 1e-3) - { - pointingVector = GravityCompensation(missileAcceleration, pointingVector, gravity.Value); - } - return pointingVector; - } - - public static Vector3D GravityCompensation(double missileAcceleration, Vector3D desiredDirection, Vector3D gravity) - { - Vector3D directionNorm = MathFuncs.SafeNormalize(desiredDirection); - Vector3D gravityCompensationVec = -(MathFuncs.Rejection(gravity, desiredDirection)); - - double diffSq = missileAcceleration * missileAcceleration - gravityCompensationVec.LengthSquared(); - if (diffSq < 0) // Impossible to hover - { - return desiredDirection - gravity; // We will sink, but at least approach the target. - } - - return directionNorm * Math.Sqrt(diffSq) + gravityCompensationVec; - } - - protected abstract Vector3D GetPointingVector(Vector3D missilePosition, Vector3D missileVelocity, double missileAcceleration, Vector3D targetPosition, Vector3D targetVelocity, Vector3D targetAcceleration); - } - - internal abstract class RelNavGuidance : MissileGuidanceBase - { - public double NavConstant; - public double NavAccelConstant; - - protected RelNavGuidance(double updatesPerSecond, double navConstant, double navAccelConstant = 0) : base(updatesPerSecond) - { - NavConstant = navConstant; - NavAccelConstant = navAccelConstant; - } - - protected abstract Vector3D GetLatax(Vector3D missileToTarget, Vector3D missileToTargetNorm, Vector3D relativeVelocity, Vector3D lateralTargetAcceleration); - - protected override Vector3D GetPointingVector(Vector3D missilePosition, Vector3D missileVelocity, double missileAcceleration, Vector3D targetPosition, Vector3D targetVelocity, Vector3D targetAcceleration) - { - Vector3D missileToTarget = targetPosition - missilePosition; - Vector3D missileToTargetNorm = Vector3D.Normalize(missileToTarget); - Vector3D relativeVelocity = targetVelocity - missileVelocity; - Vector3D lateralTargetAcceleration = (targetAcceleration - Vector3D.Dot(targetAcceleration, missileToTargetNorm) * missileToTargetNorm); - - Vector3D lateralAcceleration = GetLatax(missileToTarget, missileToTargetNorm, relativeVelocity, lateralTargetAcceleration); - - if (Vector3D.IsZero(lateralAcceleration)) - return missileToTargetNorm * missileAcceleration; - - double diff = missileAcceleration * missileAcceleration - lateralAcceleration.LengthSquared(); - if (diff < 0) - return Vector3D.Normalize(lateralAcceleration) * missileAcceleration; //fly parallel to the target - return lateralAcceleration + Math.Sqrt(diff) * missileToTargetNorm; - } - } - - /// - /// Whip's Proportional Navigation Intercept - /// Derived from: https://en.wikipedia.org/wiki/Proportional_navigation - /// And: http://www.moddb.com/members/blahdy/blogs/gamedev-introduction-to-proportional-navigation-part-i - /// And: http://www.dtic.mil/dtic/tr/fulltext/u2/a556639.pdf - /// And: http://nptel.ac.in/courses/101108054/module8/lecture22.pdf - /// - internal class ProNavGuidance : RelNavGuidance - { - public ProNavGuidance(double updatesPerSecond, double navConstant, double navAccelConstant = 0) : base(updatesPerSecond, navConstant, navAccelConstant) { } - - protected override Vector3D GetLatax(Vector3D missileToTarget, Vector3D missileToTargetNorm, Vector3D relativeVelocity, Vector3D lateralTargetAcceleration) - { - Vector3D omega = Vector3D.Cross(missileToTarget, relativeVelocity) / Math.Max(missileToTarget.LengthSquared(), 1); //to combat instability at close range - return NavConstant * relativeVelocity.Length() * Vector3D.Cross(omega, missileToTargetNorm) - + NavAccelConstant * lateralTargetAcceleration; - } - } - - internal class WhipNavGuidance : RelNavGuidance - { - public WhipNavGuidance(double updatesPerSecond, double navConstant, double navAccelConstant = 0) : base(updatesPerSecond, navConstant, navAccelConstant) { } - - protected override Vector3D GetLatax(Vector3D missileToTarget, Vector3D missileToTargetNorm, Vector3D relativeVelocity, Vector3D lateralTargetAcceleration) - { - Vector3D parallelVelocity = relativeVelocity.Dot(missileToTargetNorm) * missileToTargetNorm; //bootleg vector projection - Vector3D normalVelocity = (relativeVelocity - parallelVelocity); - return NavConstant * 0.1 * normalVelocity - + NavAccelConstant * lateralTargetAcceleration; - } - } - - internal class HybridNavGuidance : RelNavGuidance - { - public HybridNavGuidance(double updatesPerSecond, double navConstant, double navAccelConstant = 0) : base(updatesPerSecond, navConstant, navAccelConstant) { } - - protected override Vector3D GetLatax(Vector3D missileToTarget, Vector3D missileToTargetNorm, Vector3D relativeVelocity, Vector3D lateralTargetAcceleration) - { - Vector3D omega = Vector3D.Cross(missileToTarget, relativeVelocity) / Math.Max(missileToTarget.LengthSquared(), 1); //to combat instability at close range - Vector3D parallelVelocity = relativeVelocity.Dot(missileToTargetNorm) * missileToTargetNorm; //bootleg vector projection - Vector3D normalVelocity = (relativeVelocity - parallelVelocity); - return NavConstant * (relativeVelocity.Length() * Vector3D.Cross(omega, missileToTargetNorm) + 0.1 * normalVelocity) - + NavAccelConstant * lateralTargetAcceleration; - } - } - - /// - /// Zero Effort Miss Intercept - /// Derived from: https://doi.org/10.2514/1.26948 - /// - internal class ZeroEffortMissGuidance : RelNavGuidance - { - public ZeroEffortMissGuidance(double updatesPerSecond, double navConstant) : base(updatesPerSecond, navConstant, 0) { } - protected override Vector3D GetLatax(Vector3D missileToTarget, Vector3D missileToTargetNorm, Vector3D relativeVelocity, Vector3D lateralTargetAcceleration) - { - double distToTarget = Vector3D.Dot(missileToTarget, missileToTargetNorm); - double closingSpeed = Vector3D.Dot(relativeVelocity, missileToTargetNorm); - // Equation (8) with sign modification to keep time positive and not NaN - double tau = distToTarget / Math.Max(1, Math.Abs(closingSpeed)); - // Equation (6) - Vector3D z = missileToTarget + relativeVelocity * tau; - // Equation (7) - return NavConstant * z / (tau * tau) - + NavAccelConstant * lateralTargetAcceleration; - } - } - #endregion - internal static class MathFuncs { internal struct Cone @@ -262,69 +33,6 @@ internal static bool TargetSphereInCone(ref BoundingSphereD targetSphere, ref Co return true; } - public static bool IntersectEllipsoidEllipsoid(Ellipsoid ellipsoid1, Quaternion rotation1, Ellipsoid ellipsoid2, Quaternion rotation2, out Vector3 collisionPoint) - { - collisionPoint = Vector3.Zero; - - // Create transform matrices for the ellipsoids - Matrix transform1 = Matrix.CreateFromTransformScale(rotation1, ellipsoid1.Center, Vector3.One); - Matrix transform2 = Matrix.CreateFromTransformScale(rotation2, ellipsoid2.Center, Vector3.One); - - // Transform the ellipsoid centers and radii into a common space - Matrix transform1Inv = Matrix.Invert(transform1); - Matrix transform2Inv = Matrix.Invert(transform2); - var center1 = Vector3.Transform(ellipsoid2.Center, transform1Inv); - var center2 = Vector3.Transform(ellipsoid1.Center, transform2Inv); - var radii1 = Vector3.TransformNormal(ellipsoid2.Radii, transform1Inv); - var radii2 = Vector3.TransformNormal(ellipsoid1.Radii, transform2Inv); - - //Vector3 center1 = transform1.inverse.MultiplyPoint(ellipsoid2.Center); - //Vector3 center2 = transform2.inverse.MultiplyPoint(ellipsoid1.Center); - //Vector3 radii1 = transform1.inverse.MultiplyVector(ellipsoid2.Radii); - //Vector3 radii2 = transform2.inverse.MultiplyVector(ellipsoid1.Radii); - - // Calculate the distance between the transformed ellipsoid centers - Vector3 centerDistance = center1 - center2; - float distance = centerDistance.Length(); - - // Calculate the sum of the transformed ellipsoid radii - Vector3 radiiSum = radii1 + radii2; - - // Check if the distance between the transformed ellipsoid centers is less than or equal to the sum of the transformed ellipsoid radii in all dimensions - if (distance <= radiiSum.X && distance <= radiiSum.Y && distance <= radiiSum.Z) - { - // The ellipsoids intersect, so find the surface collision points - Vector3 surfacePoint1 = center1 + centerDistance.Normalize() * radii1; - Vector3 surfacePoint2 = center2 - centerDistance.Normalize() * radii2; - - // Calculate the collision distance - float collisionDistance = (radiiSum - centerDistance).Length() / 2; - - // Set the collision point to the midpoint between the surface collision points, minus the collision distance - collisionPoint = (surfacePoint1 + surfacePoint2) / 2 - centerDistance.Normalize() * collisionDistance; - // Transform the collision point back into world space - collisionPoint = Vector3.Transform(collisionPoint, transform1); - //collisionPoint = transform1.MultiplyPoint(collisionPoint); - return true; - } - else - { - return false; - } - } - - public struct Ellipsoid - { - public Vector3 Center; - public Vector3 Radii; - - public Ellipsoid(Vector3 center, Vector3 radii) - { - Center = center; - Radii = radii; - } - } - internal static double? IntersectEllipsoid(MatrixD ellipsoidMatrixInv, MatrixD ellipsoidMatrix, RayD ray) { var normSphere = new BoundingSphereD(Vector3.Zero, 1f); @@ -353,11 +61,6 @@ public Ellipsoid(Vector3 center, Vector3 radii) return (double.IsNaN(dist) ? (double?) null : dist); } - public static bool PointInEllipsoid(Vector3D point, MatrixD ellipsoidMatrixInv) - { - return Vector3D.Transform(point, ellipsoidMatrixInv).LengthSquared() <= 1; - } - internal static bool IsDotProductWithinTolerance(ref Vector3D targetDir, ref Vector3D refDir, double tolerance) { double dot = Vector3D.Dot(targetDir, refDir); @@ -365,77 +68,6 @@ internal static bool IsDotProductWithinTolerance(ref Vector3D targetDir, ref Vec return Math.Abs(dot) * dot > num; } - /* - This sausage of a method was made by combining my MissileGuidance class into a single call - because... because Keen won't let us have nice things. - - This is derived using a lateral acceleration (latax) that is normal to the line of sight vector. - This is a much more stable solution than using a latax that is normal to the velocity vector - because I don't need to screw with the dozen or so edge cases you get with that geometry. - - - Whiplash141 the Salty - */ - internal static Vector3D ProNavGuidanceButAllInOneMethod( - double NavConstant, // Nominally: 3-5 - double NavAccelConstant, // Nominally: NavConstant / 2 - ref Vector3D targetPosition, - ref Vector3D targetVelocity, - ref Vector3D targetAcceleration, - ref Vector3D missilePosition, - ref Vector3D missileVelocity, - ref Vector3D gravity, // Plug in Vector3D.Zero if you don't give a shit about gravity - double missileAccelerationMagnitude, // m/s^2 - double updateIntervalInSeconds) - { - Vector3D missileToTarget = targetPosition - missilePosition; - Vector3D missileToTargetNorm = Vector3D.Normalize(missileToTarget); - Vector3D relativeVelocity = targetVelocity - missileVelocity; - Vector3D lateralTargetAcceleration = (targetAcceleration - Vector3D.Dot(targetAcceleration, missileToTargetNorm) * missileToTargetNorm); - Vector3D gravityCompensationTerm = 1.1 * -(gravity - Vector3D.Dot(gravity, missileToTargetNorm) * missileToTargetNorm); - /* - This is where the magic happens. Here we are using the angular rate of change as the "error" - that we are going to slap into an augmented PROPORTIONAL controller. That is where the name - comes from. - */ - Vector3D omega = Vector3D.Cross(missileToTarget, relativeVelocity) / Math.Max(missileToTarget.LengthSquared(), 1); // To combat instability at close range - Vector3D lateralAcceleration = NavConstant * relativeVelocity.Length() * Vector3D.Cross(omega, missileToTargetNorm) - + NavAccelConstant * lateralTargetAcceleration - + gravityCompensationTerm; // Normal to LOS - - if (Vector3D.IsZero(lateralAcceleration)) - return missileToTarget; - - double diff = missileAccelerationMagnitude * missileAccelerationMagnitude - lateralAcceleration.LengthSquared(); - if (diff < 0) - return lateralAcceleration; // Fly parallel to the target if we run out of acceleration budget - return lateralAcceleration + Math.Sqrt(diff) * missileToTargetNorm; - } - - //Relative velocity proportional navigation - //aka: Whip-Nav - internal static Vector3D CalculateMissileIntercept(Vector3D targetPosition, Vector3D targetVelocity, Vector3D missilePos, Vector3D missileVelocity, double missileAcceleration, double compensationFactor = 1, double maxLateralThrustProportion = 0.5) - { - var missileToTarget = Vector3D.Normalize(targetPosition - missilePos); - var relativeVelocity = targetVelocity - missileVelocity; - var parallelVelocity = relativeVelocity.Dot(missileToTarget) * missileToTarget; - var normalVelocity = (relativeVelocity - parallelVelocity); - - var normalMissileAcceleration = normalVelocity * compensationFactor; - - if (Vector3D.IsZero(normalMissileAcceleration)) - return missileToTarget * missileAcceleration; - - double maxLateralThrust = missileAcceleration * Math.Min(1, Math.Max(0, maxLateralThrustProportion)); - if (normalMissileAcceleration.LengthSquared() > maxLateralThrust * maxLateralThrust) - { - Vector3D.Normalize(ref normalMissileAcceleration, out normalMissileAcceleration); - normalMissileAcceleration *= maxLateralThrust; - } - double diff = missileAcceleration * missileAcceleration - normalMissileAcceleration.LengthSquared(); - var maxedDiff = Math.Max(0, diff); - return Math.Sqrt(maxedDiff) * missileToTarget + normalMissileAcceleration; - } - public enum DebugCaller { TrajectoryEstimation, @@ -671,28 +303,6 @@ internal static double AngleBetween(Vector3D a, Vector3D b) //returns radians return Math.Acos(MathHelperD.Clamp(a.Dot(b) / Math.Sqrt(a.LengthSquared() * b.LengthSquared()), -1, 1)); } - internal static long UniqueId(int left, int right) - { - long uniqueId = left; - uniqueId <<= 32; - uniqueId += right; - return uniqueId; - } - - internal static void GetRotationAnglesOld(ref Vector3D targetVector, ref MatrixD matrix, out double yaw, out double pitch) - { - var localTargetVector = Vector3D.TransformNormal(targetVector, MatrixD.Transpose(matrix)); - var flattenedTargetVector = new Vector3D(localTargetVector.X, 0, localTargetVector.Z); - yaw = AngleBetween(Vector3D.Forward, flattenedTargetVector) * -Math.Sign(localTargetVector.X); //right is positive - if (Math.Abs(yaw) < 1E-6 && localTargetVector.Z > 0) //check for straight back case - yaw = Math.PI; - - if (Vector3D.IsZero(flattenedTargetVector)) //check for straight up case - pitch = MathHelper.PiOver2 * Math.Sign(localTargetVector.Y); - else - pitch = AngleBetween(localTargetVector, flattenedTargetVector) * Math.Sign(localTargetVector.Y); //up is positive - } - internal static void GetRotationAngles(ref Vector3D targetVector, ref MatrixD matrix, out double yaw, out double pitch) { yaw = 0; @@ -734,234 +344,6 @@ internal static double Intercept(Vector3D deltaPos, Vector3D deltaVel, double pr return 2.0 * num3 / (Math.Sqrt(d) - num2); } - internal static void WrapAngleAroundPI(ref float angle) - { - angle %= MathHelper.TwoPi; - if (angle > Math.PI) - angle = -MathHelper.TwoPi + angle; - else if (angle < -Math.PI) - angle = MathHelper.TwoPi + angle; - } - - internal static void DotRepairAndReport(double dot, out double newDot) - { - if (dot > 1) - newDot = 1; - else if (dot < -1) - newDot = -1; - else - newDot = dot; - - Log.Line($"dot was invalid: {dot}"); - } - - internal static double CalculateRotorDeviationAngle(Vector3D forwardVector, MatrixD lastOrientation) - { - var flattenedForwardVector = Rejection(forwardVector, lastOrientation.Up); - return AngleBetween(flattenedForwardVector, lastOrientation.Forward) * Math.Sign(flattenedForwardVector.Dot(lastOrientation.Left)); - } - - internal static void GetAzimuthAngle(ref Vector3D targetVector, ref MatrixD matrix, out double azimuth) - { - var localTargetVector = Vector3D.TransformNormal(targetVector, MatrixD.Transpose(matrix)); - var flattenedTargetVector = new Vector3D(localTargetVector.X, 0, localTargetVector.Z); - azimuth = AngleBetween(Vector3D.Forward, flattenedTargetVector) * -Math.Sign(localTargetVector.X); //right is positive - if (Math.Abs(azimuth) < 1E-6 && localTargetVector.Z > 0) //check for straight back case - azimuth = Math.PI; - } - internal static void GetElevationAngle(ref Vector3D targetVector, ref MatrixD matrix, out double pitch) - { - var localTargetVector = Vector3D.TransformNormal(targetVector, MatrixD.Transpose(matrix)); - var flattenedTargetVector = new Vector3D(localTargetVector.X, 0, localTargetVector.Z); - if (Vector3D.IsZero(flattenedTargetVector)) //check for straight up case - pitch = MathHelper.PiOver2 * Math.Sign(localTargetVector.Y); - else - pitch = AngleBetween(localTargetVector, flattenedTargetVector) * Math.Sign(localTargetVector.Y); //up is positive - } - - internal static Vector3D SafeNormalize(Vector3D a) - { - if (Vector3D.IsZero(a)) return Vector3D.Zero; - if (Vector3D.IsUnit(ref a)) return a; - return Vector3D.Normalize(a); - } - - internal static Vector3D Reflection(Vector3D a, Vector3D b, double rejectionFactor = 1) //reflect a over b - { - Vector3D project_a = Projection(a, b); - Vector3D reject_a = a - project_a; - return project_a - reject_a * rejectionFactor; - } - - internal static Vector3D Rejection(Vector3D a, Vector3D b) //reject a on b - { - if (Vector3D.IsZero(a) || Vector3D.IsZero(b)) - return Vector3D.Zero; - return a - a.Dot(b) / b.LengthSquared() * b; - } - - internal static Vector3D Projection(Vector3D a, Vector3D b) - { - if (Vector3D.IsZero(a) || Vector3D.IsZero(b)) - return Vector3D.Zero; - if (Vector3D.IsUnit(ref b)) - return a.Dot(b) * b; - - return a.Dot(b) / b.LengthSquared() * b; - } - - internal static double ScalarProjection(Vector3D a, Vector3D b) - { - if (Vector3D.IsZero(a) || Vector3D.IsZero(b)) return 0; - if (Vector3D.IsUnit(ref b)) - return a.Dot(b); - return a.Dot(b) / b.Length(); - } - - - - public static double CosBetween(Vector3D a, Vector3D b) - { - if (Vector3D.IsZero(a) || Vector3D.IsZero(b)) - return 0; - return MathHelper.Clamp(a.Dot(b) / Math.Sqrt(a.LengthSquared() * b.LengthSquared()), -1, 1); - } - - - public static Vector3D NearestPointOnLine(Vector3D start, Vector3D end, Vector3D pnt) - { - var line = (end - start); - var len = line.Length(); - line.Normalize(); - - var v = pnt - start; - var d = Vector3.Dot(v, line); - MathHelper.Clamp(d, 0f, len); - return start + line * d; - } - - /* - ** Returns the point on the line formed by (point1 + dir1 * x) that is closest to the point - ** on the line formed by line (point2 + dir2 * t) - */ - - public static Vector3D GetClosestPointOnLine1(Vector3D point1, Vector3D dir1, Vector3D point2, Vector3D dir2) - { - Vector3D axis = Vector3D.Cross(dir1, dir2); - if (Vector3D.IsZero(axis)) - return point1; - Vector3D perpDir2 = Vector3D.Cross(dir2, axis); - Vector3D point1To2 = point2 - point1; - return point1 + Vector3D.Dot(point1To2, perpDir2) / Vector3D.Dot(dir1, perpDir2) * dir1; - } - - /* - ** Returns the point on the line1 that is closest to the point on line2 - */ - - public static Vector3D GetClosestPointOnLine2(Vector3D line1Start, Vector3D line1End, Vector3D line2Start, Vector3D line2End) - { - Vector3D dir1 = line1End - line1Start; - Vector3D dir2 = line2End - line2Start; - Vector3D axis = Vector3D.Cross(dir1, dir2); - if (Vector3D.IsZero(axis)) - return line1Start; - Vector3D perpDir2 = Vector3D.Cross(dir2, axis); - Vector3D point1To2 = line2Start - line1Start; - return line1Start + Vector3D.Dot(point1To2, perpDir2) / Vector3D.Dot(dir1, perpDir2) * dir1; - } - - public static Vector3D VectorProjection(Vector3D a, Vector3D b) - { - if (Vector3D.IsZero(b)) - return Vector3D.Zero; - - return a.Dot(b) / b.LengthSquared() * b; - } - - public static bool SameSign(float num1, double num2) - { - if (num1 > 0 && num2 < 0) - return false; - if (num1 < 0 && num2 > 0) - return false; - return true; - } - - public static bool NearlyEqual(double f1, double f2) - { - // Equal if they are within 0.00001 of each other - return Math.Abs(f1 - f2) < 0.00001; - } - - - public static double InverseSqrDist(Vector3D source, Vector3D target, double range) - { - var rangeSq = range * range; - var distSq = (target - source).LengthSquared(); - if (distSq > rangeSq) - return 0.0; - return 1.0 - (distSq / rangeSq); - } - - public static double GetIntersectingSurfaceArea(MatrixD matrix, Vector3D hitPosLocal) - { - var surfaceArea = -1d; - - var boxMax = matrix.Backward + matrix.Right + matrix.Up; - var boxMin = -boxMax; - var box = new BoundingBoxD(boxMin, boxMax); - - var maxWidth = box.Max.LengthSquared(); - var testLine = new LineD(Vector3D.Zero, Vector3D.Normalize(hitPosLocal) * maxWidth); - LineD testIntersection; - box.Intersect(ref testLine, out testIntersection); - - var intersection = testIntersection.To; - - var epsilon = 1e-6; - var projFront = VectorProjection(intersection, matrix.Forward); - if (Math.Abs(projFront.LengthSquared() - matrix.Forward.LengthSquared()) < epsilon) - { - var a = Vector3D.Distance(matrix.Left, matrix.Right); - var b = Vector3D.Distance(matrix.Up, matrix.Down); - surfaceArea = a * b; - } - - var projLeft = VectorProjection(intersection, matrix.Left); - if (Math.Abs(projLeft.LengthSquared() - matrix.Left.LengthSquared()) < epsilon) - { - var a = Vector3D.Distance(matrix.Forward, matrix.Backward); - var b = Vector3D.Distance(matrix.Up, matrix.Down); - surfaceArea = a * b; - } - - var projUp = VectorProjection(intersection, matrix.Up); - if (Math.Abs(projUp.LengthSquared() - matrix.Up.LengthSquared()) < epsilon) - { - var a = Vector3D.Distance(matrix.Forward, matrix.Backward); - var b = Vector3D.Distance(matrix.Left, matrix.Right); - surfaceArea = a * b; - } - return surfaceArea; - } - - public static void FibonacciSeq(int magicNum) - { - var root5 = Math.Sqrt(5); - var phi = (1 + root5) / 2; - - var n = 0; - int Fn; - do - { - Fn = (int)((Math.Pow(phi, n) - Math.Pow(-phi, -n)) / ((2 * phi) - 1)); - //Console.Write("{0} ", Fn); - ++n; - } - while (Fn < magicNum); - } - public static double LargestCubeInSphere(double r) { @@ -974,34 +356,11 @@ public static double LargestCubeInSphere(double r) return a; } - public static double AreaCube(double a) - { - return (a * a * a); - } - - public static double SurfaceCube(double a) - { - return (6 * a * a); - } - public static double VolumeCube(double len) { return Math.Pow(len, 3); } - public static double Percentile(double[] sequence, double excelPercentile) - { - Array.Sort(sequence); - int N = sequence.Length; - double n = (N - 1) * excelPercentile + 1; - // Another method: double n = (N + 1) * excelPercentile; - if (n == 1d) return sequence[0]; - if (n == N) return sequence[N - 1]; - int k = (int)n; - double d = n - k; - return sequence[k - 1] + d * (sequence[k] - sequence[k - 1]); - } - public static double GetMedian(int[] array) { int[] tempArray = array; diff --git a/Data/Scripts/CoreSystems/Support/Spawn.cs b/Data/Scripts/CoreSystems/Support/Spawn.cs deleted file mode 100644 index cb51d243..00000000 --- a/Data/Scripts/CoreSystems/Support/Spawn.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Collections.Generic; -using Sandbox.Common.ObjectBuilders; -using Sandbox.Definitions; -using Sandbox.Game.Entities; -using Sandbox.Game.EntityComponents; -using Sandbox.ModAPI; -using VRage; -using VRage.Game; -using VRage.Game.Entity; -using VRage.Game.GUI.TextPanel; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRage.ObjectBuilders; -using VRageMath; - -namespace CoreSystems.Support -{ - internal class Spawn - { - private static readonly SerializableBlockOrientation EntityOrientation = new SerializableBlockOrientation(Base6Directions.Direction.Forward, Base6Directions.Direction.Up); - - private static readonly MyObjectBuilder_CubeGrid CubeGridBuilder = new MyObjectBuilder_CubeGrid - { - EntityId = 0, - GridSizeEnum = MyCubeSize.Large, - IsStatic = true, - Skeleton = new List(), - LinearVelocity = Vector3.Zero, - AngularVelocity = Vector3.Zero, - ConveyorLines = new List(), - BlockGroups = new List(), - Handbrake = false, - XMirroxPlane = null, - YMirroxPlane = null, - ZMirroxPlane = null, - PersistentFlags = MyPersistentEntityFlags2.InScene, - Name = "ArtificialCubeGrid", - DisplayName = "FieldEffect", - CreatePhysics = false, - DestructibleBlocks = true, - PositionAndOrientation = new MyPositionAndOrientation(Vector3D.Zero, Vector3D.Forward, Vector3D.Up), - - CubeBlocks = new List - { - new MyObjectBuilder_CubeBlock - { - EntityId = 0, - BlockOrientation = EntityOrientation, - SubtypeName = string.Empty, - Name = string.Empty, - Min = Vector3I.Zero, - Owner = 0, - ShareMode = MyOwnershipShareModeEnum.None, - DeformationRatio = 0, - } - } - }; - - public static readonly MyObjectBuilder_Meteor GravityMissile = new MyObjectBuilder_Meteor - { - EntityId = 0, - LinearVelocity = Vector3.One * 10, - AngularVelocity = Vector3.Zero, - PersistentFlags = MyPersistentEntityFlags2.InScene, - Name = "GravityMissile", - PositionAndOrientation = new MyPositionAndOrientation(Vector3D.Zero, Vector3D.Forward, Vector3D.Up) - }; - - public static readonly MyObjectBuilder_FloatingObject FloatingObject = new MyObjectBuilder_FloatingObject - { - EntityId = 0, - PersistentFlags = MyPersistentEntityFlags2.InScene, - Name = "CustomFloater", - PositionAndOrientation = new MyPositionAndOrientation(Vector3D.Zero, Vector3D.Forward, Vector3D.Up) - }; - - public static MyEntity EmptyEntity(string displayName, string model, MyEntity parent, bool parented = false) - { - try - { - var ent = new MyEntity(); - ent.Init(null, model, null, null); - ent.Render.CastShadows = false; - ent.IsPreview = true; - ent.Render.Visible = true; - ent.Save = false; - ent.SyncFlag = false; - ent.NeedsWorldMatrix = false; - ent.Flags |= EntityFlags.IsNotGamePrunningStructureObject; - MyEntities.Add(ent); - return ent; - } - catch (Exception ex) { Log.Line($"Exception in EmptyEntity: {ex}"); return null; } - } - - public static MyEntity SpawnBlock(string subtypeId, string name, bool isVisible = false, bool hasPhysics = false, bool isStatic = false, bool toSave = false, bool destructible = false, long ownerId = 0) - { - try - { - CubeGridBuilder.Name = name; - CubeGridBuilder.CubeBlocks[0].SubtypeName = subtypeId; - CubeGridBuilder.CreatePhysics = hasPhysics; - CubeGridBuilder.IsStatic = isStatic; - CubeGridBuilder.DestructibleBlocks = destructible; - var ent = (MyEntity)MyAPIGateway.Entities.CreateFromObjectBuilder(CubeGridBuilder); - - ent.Flags &= ~EntityFlags.Save; - ent.Render.Visible = isVisible; - MyAPIGateway.Entities.AddEntity(ent); - - return ent; - } - catch (Exception ex) - { - Log.Line("Exception in Spawn"); - Log.Line($"{ex}"); - return null; - } - } - - internal static MyEntity SpawnPrefab(string name, out IMyTextPanel lcd, bool isDisplay = false) - { - try - { - if (string.IsNullOrEmpty(name)) - { - lcd = null; - return null; - } - - PrefabBuilder.CubeBlocks.Clear(); // need no leftovers from previous spawns - - if (isDisplay) - { - PrefabTextPanel.SubtypeName = name; - PrefabTextPanel.FontColor = new Vector4(1, 1, 1, 1); - PrefabTextPanel.BackgroundColor = new Vector4(0,0,0, 0); - PrefabTextPanel.FontSize = DISPLAY_FONT_SIZE; - PrefabBuilder.CubeBlocks.Add(PrefabTextPanel); - var def = MyDefinitionManager.Static.GetCubeBlockDefinition(new MyDefinitionId(typeof(MyObjectBuilder_TextPanel), name)) as MyTextPanelDefinition; - - if (def != null) - def.TextureResolution = 256; - } - else - { - PrefabCubeBlock.SubtypeName = name; - PrefabBuilder.CubeBlocks.Add(PrefabCubeBlock); - } - - MyEntities.RemapObjectBuilder(PrefabBuilder); - var ent = MyEntities.CreateFromObjectBuilder(PrefabBuilder, true); - ent.IsPreview = true; // don't sync on MP - ent.SyncFlag = false; // don't sync on MP - ent.Save = false; // don't save this entity - - MyEntities.Add(ent); - var lcdSlim = ((IMyCubeGrid)ent).GetCubeBlock(Vector3I.Zero); - lcd = lcdSlim.FatBlock as IMyTextPanel; - //lcd.Render.CastShadows = false; - //lcd.Render.Transparency = 0.5f; - //lcd.Render.NeedsResolveCastShadow = false; - //lcd.Render.EnableColorMaskHsv = false; - //lcd.Render.DrawInAllCascades = false; - - //lcd.Render.UpdateRenderObject(false, true); - //lcd.Render.UpdateRenderObject(true, true); - //lcd.Render.RemoveRenderObjects(); - //lcd.Render.AddRenderObjects(); - lcd.SetEmissiveParts("ScreenArea", Color.White, 1f); - lcd.SetEmissiveParts("Edges", Color.Teal, 0.5f); - - lcd.ContentType = ContentType.TEXT_AND_IMAGE; - - return ent; - } - catch (Exception ex) { Log.Line($"Exception in SpawnPrefab: {ex}"); } - - lcd = null; - return null; - } - - internal static MyEntity SpawnCamera(string name, out MyCameraBlock camera) - { - try - { - if (string.IsNullOrEmpty(name)) - { - camera = null; - return null; - } - PrefabCamera.SubtypeName = name; - PrefabBuilder.CubeBlocks.Clear(); // need no leftovers from previous spawns - PrefabBuilder.CubeBlocks.Add(PrefabCamera); - MyEntities.RemapObjectBuilder(PrefabBuilder); - var ent = MyEntities.CreateFromObjectBuilder(PrefabBuilder, false); - ent.Render.CastShadows = false; - ent.Render.Visible = false; - ent.IsPreview = true; - ent.Save = false; - ent.SyncFlag = false; - ent.NeedsWorldMatrix = false; - ent.Flags |= EntityFlags.IsNotGamePrunningStructureObject; - ent.Render.RemoveRenderObjects(); - MyEntities.Add(ent, false); - var cameraSlim = ((IMyCubeGrid)ent).GetCubeBlock(Vector3I.Zero); - var gId = MyResourceDistributorComponent.ElectricityId; - camera = (MyCameraBlock)cameraSlim.FatBlock; - camera.ResourceSink.SetInputFromDistributor(gId, 0, true); - camera.RefreshModels(null, null); - return ent; - } - catch (Exception ex) { Log.Line($"Exception in SpawnPrefab: {ex}"); } - - camera = null; - return null; - } - - private static SerializableVector3 PrefabVector0 = new SerializableVector3(0, 0, 0); - private static SerializableVector3I PrefabVectorI0 = new SerializableVector3I(0, 0, 0); - private static SerializableBlockOrientation PrefabOrientation = new SerializableBlockOrientation(Base6Directions.Direction.Forward, Base6Directions.Direction.Up); - private const float DISPLAY_FONT_SIZE = 1f; - - private static MyObjectBuilder_CubeBlock PrefabCubeBlock = new MyObjectBuilder_CubeBlock - { - EntityId = 1, - SubtypeName = "", - Min = PrefabVectorI0, - BlockOrientation = PrefabOrientation, - DeformationRatio = 0, - ShareMode = MyOwnershipShareModeEnum.None, - }; - - private static MyObjectBuilder_CubeGrid PrefabBuilder = new MyObjectBuilder_CubeGrid - { - EntityId = 0, - GridSizeEnum = MyCubeSize.Small, - IsStatic = true, - Skeleton = new List(), - LinearVelocity = PrefabVector0, - AngularVelocity = PrefabVector0, - ConveyorLines = new List(), - BlockGroups = new List(), - Handbrake = false, - XMirroxPlane = null, - YMirroxPlane = null, - ZMirroxPlane = null, - PersistentFlags = MyPersistentEntityFlags2.InScene, - Name = "SpamCamGrid", - DisplayName = "SpamCamGrid", - CreatePhysics = false, - PositionAndOrientation = new MyPositionAndOrientation(Vector3D.Zero, Vector3D.Forward, Vector3D.Up), - CubeBlocks = new List(), - }; - - private static MyObjectBuilder_TextPanel PrefabTextPanel = new MyObjectBuilder_TextPanel - { - EntityId = 1, - Min = PrefabVectorI0, - BlockOrientation = PrefabOrientation, - ShareMode = MyOwnershipShareModeEnum.None, - DeformationRatio = 0, - ShowOnHUD = false, - //ShowText = ShowTextOnScreenFlag.PUBLIC, // HACK not whitelisted anymore... - FontSize = DISPLAY_FONT_SIZE, - }; - - private static MyObjectBuilder_CameraBlock PrefabCamera = new MyObjectBuilder_CameraBlock - { - EntityId = 1, - Min = PrefabVectorI0, - BlockOrientation = PrefabOrientation, - ShareMode = MyOwnershipShareModeEnum.None, - DeformationRatio = 0, - ShowOnHUD = false, - //IsActive = true, - Name = null, - CustomName = null, - }; - } -} diff --git a/Data/Scripts/CoreSystems/Support/StaticUtils.cs b/Data/Scripts/CoreSystems/Support/StaticUtils.cs index 39e1ad15..81dc3f90 100644 --- a/Data/Scripts/CoreSystems/Support/StaticUtils.cs +++ b/Data/Scripts/CoreSystems/Support/StaticUtils.cs @@ -1,14 +1,10 @@ using System; using System.Collections.Generic; using System.Text; -using CoreSystems.Projectiles; -using Sandbox.Game; using Sandbox.Game.Entities; -using Sandbox.Game.Entities.Cube; using Sandbox.ModAPI; using Sandbox.ModAPI.Interfaces.Terminal; using VRage.Game; -using VRage.Game.Entity; using VRage.Game.ModAPI; using VRageMath; @@ -63,31 +59,6 @@ public static bool ModActivate(IMyModContext context, IMySession session) return validP3; } - public static void GetFourInt16FromLong(long id, out int w, out int x, out int y, out int z) - { - - w = (int)(id >> 48); - - x = (int)((id << 16) >> 48); - y = (int)((id << 32) >> 48); - z = (int)((id << 48) >> 48); - - } - - public static void FourInt16ToLong(int w, int x, int y, int z, out long id) - { - - id = ((long)(w << 16 | x) << 32) | (uint)(y << 16 | z); - - } - - public static double Clamp01(double value) - { - if (value < 0.0) - return 0.0d; - return value > 1.0 ? 1d : value; - } - public static void ReplaceAll(StringBuilder sb, char[] charlist, char replacewith) { for (int i = 0; i < sb.Length; i++) @@ -96,47 +67,11 @@ public static void ReplaceAll(StringBuilder sb, char[] charlist, char replacewit sb[i] = replacewith; } } - - public static double Lerp(double a, double b, double t) => a + (b - a) * Clamp01(t); - - public static double InverseLerp(double a, double b, double value) => a != b ? Clamp01((value - a) / (b - a)) : 0.0f; - public static Vector3 ColorToHSVOffset(Color color) { return MyColorPickerConstants.HSVToHSVOffset(color.ColorToHSV()); } - public static long MakeLong(int left, int right) - { - long res = left; - res <<= 32; - res |= (uint)right; //uint first to prevent loss of signed bit - return res; - } - - static void ShellSort(List list, Vector3D weaponPos) - { - int length = list.Count; - - for (int h = length / 2; h > 0; h /= 2) - { - for (int i = h; i < length; i += 1) - { - var tempValue = list[i]; - double temp; - Vector3D.DistanceSquared(ref list[i].Position, ref weaponPos, out temp); - - int j; - for (j = i; j >= h && Vector3D.DistanceSquared(list[j - h].Position, weaponPos) > temp; j -= h) - { - list[j] = list[j - h]; - } - - list[j] = tempValue; - } - } - } - public static IMyTerminalControlOnOffSwitch RefreshToggle; public static MyCubeBlock RefreshToggleCube; @@ -208,208 +143,6 @@ public static void RefreshTerminalControls(IMyTerminalBlock b) } } - - /* - public static void UpdateTerminal(this MyCubeBlock block) - { - MyOwnershipShareModeEnum shareMode; - long ownerId; - if (block.IDModule != null) - { - ownerId = block.IDModule.Owner; - shareMode = block.IDModule.ShareMode; - } - else - { - var sorter = block as IMyTerminalBlock; - if (sorter != null) - { - sorter.ShowOnHUD = !sorter.ShowOnHUD; - sorter.ShowOnHUD = !sorter.ShowOnHUD; - } - return; - } - block.ChangeOwner(ownerId, shareMode == MyOwnershipShareModeEnum.None ? MyOwnershipShareModeEnum.Faction : MyOwnershipShareModeEnum.None); - block.ChangeOwner(ownerId, shareMode); - } - */ - public static void SphereCloud(int pointLimit, Vector3D[] physicsArray, MyEntity shieldEnt, bool transformAndScale, bool debug, Random rnd = null) - { - if (pointLimit > 10000) pointLimit = 10000; - if (rnd == null) rnd = new Random(0); - - var sPosComp = shieldEnt.PositionComp; - var unscaledPosWorldMatrix = MatrixD.Rescale(MatrixD.CreateTranslation(sPosComp.WorldAABB.Center), sPosComp.WorldVolume.Radius); - var radius = sPosComp.WorldVolume.Radius; - for (int i = 0; i < pointLimit; i++) - { - var value = rnd.Next(0, physicsArray.Length - 1); - var phi = 2 * Math.PI * i / pointLimit; - var x = (float)(radius * Math.Sin(phi) * Math.Cos(value)); - var z = (float)(radius * Math.Sin(phi) * Math.Sin(value)); - var y = (float)(radius * Math.Cos(phi)); - var v = new Vector3D(x, y, z); - - if (transformAndScale) v = Vector3D.Transform(Vector3D.Normalize(v), unscaledPosWorldMatrix); - if (debug) DsDebugDraw.DrawX(v, sPosComp.LocalMatrix, 0.5); - physicsArray[i] = v; - } - } - - public static void UnitSphereCloudQuick(int pointLimit, ref Vector3D[] physicsArray, MyEntity shieldEnt, bool translateAndScale, bool debug, Random rnd = null) - { - if (pointLimit > 10000) pointLimit = 10000; - if (rnd == null) rnd = new Random(0); - - var sPosComp = shieldEnt.PositionComp; - var radius = sPosComp.WorldVolume.Radius; - var center = sPosComp.WorldAABB.Center; - var v = Vector3D.Zero; - - for (int i = 0; i < pointLimit; i++) - { - while (true) - { - v.X = (rnd.NextDouble() * 2) - 1; - v.Y = (rnd.NextDouble() * 2) - 1; - v.Z = (rnd.NextDouble() * 2) - 1; - var len2 = v.LengthSquared(); - if (len2 < .0001) continue; - v *= radius / Math.Sqrt(len2); - break; - } - - if (translateAndScale) physicsArray[i] = v += center; - else physicsArray[i] = v; - if (debug) DsDebugDraw.DrawX(v, sPosComp.LocalMatrix, 0.5); - } - } - - public static void UnitSphereRandomOnly(ref Vector3D[] physicsArray, Random rnd = null) - { - if (rnd == null) rnd = new Random(0); - var v = Vector3D.Zero; - - for (int i = 0; i < physicsArray.Length; i++) - { - v.X = 0; - v.Y = 0; - v.Z = 0; - while ((v.X * v.X) + (v.Y * v.Y) + (v.Z * v.Z) < 0.0001) - { - v.X = (rnd.NextDouble() * 2) - 1; - v.Y = (rnd.NextDouble() * 2) - 1; - v.Z = (rnd.NextDouble() * 2) - 1; - } - v.Normalize(); - physicsArray[i] = v; - } - } - - public static void UnitSphereTranslateScale(int pointLimit, ref Vector3D[] physicsArray, ref Vector3D[] scaledCloudArray, MyEntity shieldEnt, bool debug) - { - var sPosComp = shieldEnt.PositionComp; - var radius = sPosComp.WorldVolume.Radius; - var center = sPosComp.WorldAABB.Center; - - for (int i = 0; i < pointLimit; i++) - { - var v = physicsArray[i]; - scaledCloudArray[i] = v = center + (radius * v); - if (debug) DsDebugDraw.DrawX(v, sPosComp.LocalMatrix, 0.5); - } - } - - public static void UnitSphereTranslateScaleList(int pointLimit, ref Vector3D[] physicsArray, ref List scaledCloudList, MyEntity shieldEnt, bool debug, MyEntity grid, bool rotate = true) - { - var sPosComp = shieldEnt.PositionComp; - var radius = sPosComp.WorldVolume.Radius; - var center = sPosComp.WorldAABB.Center; - var gMatrix = grid.WorldMatrix; - for (int i = 0; i < pointLimit; i++) - { - var v = physicsArray[i]; - if (rotate) Vector3D.Rotate(ref v, ref gMatrix, out v); - v = center + (radius * v); - scaledCloudList.Add(v); - if (debug) DsDebugDraw.DrawX(v, sPosComp.LocalMatrix, 0.5); - } - } - - public static void DetermisticSphereCloud(List physicsArray, int pointsInSextant) - { - physicsArray.Clear(); - int stepsPerCoord = (int)Math.Sqrt(pointsInSextant); - double radPerStep = MathHelperD.PiOver2 / stepsPerCoord; - - for (double az = -MathHelperD.PiOver4; az < MathHelperD.PiOver4; az += radPerStep) - { - for (double el = -MathHelperD.PiOver4; el < MathHelperD.PiOver4; el += radPerStep) - { - Vector3D vec; - Vector3D.CreateFromAzimuthAndElevation(az, el, out vec); - Vector3D vec2 = new Vector3D(vec.Z, vec.X, vec.Y); - Vector3D vec3 = new Vector3D(vec.Y, vec.Z, vec.X); - physicsArray.Add(vec); //first sextant - physicsArray.Add(vec2); //2nd sextant - physicsArray.Add(vec3); //3rd sextant - physicsArray.Add(-vec); //4th sextant - physicsArray.Add(-vec2); //5th sextant - physicsArray.Add(-vec3); //6th sextant - } - } - } - - public static Vector3D? GetLineIntersectionExactAll(MyCubeGrid grid, ref LineD line, out double distance, out IMySlimBlock intersectedBlock) - { - intersectedBlock = null; - distance = 3.40282346638529E+38; - Vector3I? nullable = new Vector3I?(); - Vector3I zero = Vector3I.Zero; - double distanceSquared = double.MaxValue; - if (grid.GetLineIntersectionExactGrid(ref line, ref zero, ref distanceSquared)) - { - distanceSquared = Math.Sqrt(distanceSquared); - nullable = zero; - } - if (!nullable.HasValue) - return new Vector3D?(); - distance = distanceSquared; - intersectedBlock = grid.GetCubeBlock(nullable.Value); - if (intersectedBlock == null) - return new Vector3D?(); - return zero; - } - - public static void CreateVoxelExplosion(Session session, float damage, double radius, Vector3D position, Vector3D direction, MyEntity owner, MyEntity hitEnt, WeaponDefinition.AmmoDef ammoDef, bool forceNoDraw = false) - { - - var sphere = new BoundingSphereD(position, radius); - var eFlags = MyExplosionFlags.AFFECT_VOXELS; - - var explosionInfo = new MyExplosionInfo - { - PlayerDamage = 0.1f, - Damage = damage, - ExplosionType = MyExplosionTypeEnum.MISSILE_EXPLOSION, - ExplosionSphere = sphere, - LifespanMiliseconds = 0, - HitEntity = hitEnt, - OwnerEntity = owner, - Direction = direction, - VoxelExplosionCenter = sphere.Center, - ExplosionFlags = eFlags, - VoxelCutoutScale = 0.3f, - PlaySound = false, - ApplyForceAndDamage = true, - KeepAffectedBlocks = true, - CreateParticleEffect = false, - }; - if (hitEnt?.Physics != null) - explosionInfo.Velocity = hitEnt.Physics.LinearVelocity; - MyExplosions.AddExplosion(ref explosionInfo); - } - public static void GetBlockOrientedBoundingBox(MyCubeBlock block, out MyOrientedBoundingBoxD blockBox) { var quat = Quaternion.CreateFromRotationMatrix(block.PositionComp.WorldMatrixRef); diff --git a/Data/Scripts/CoreSystems/Support/Utils.cs b/Data/Scripts/CoreSystems/Support/Utils.cs index 252fe024..fa864d5d 100644 --- a/Data/Scripts/CoreSystems/Support/Utils.cs +++ b/Data/Scripts/CoreSystems/Support/Utils.cs @@ -1,15 +1,21 @@ using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using VRage; using VRage.Collections; -using VRage.Library.Threading; using VRageMath; namespace CoreSystems.Support { + internal static class ConcurrentQueueExtensions + { + public static void Clear(this ConcurrentQueue queue) + { + T item; + while (queue.TryDequeue(out item)) { } + } + } public class JerkRunningAverage { @@ -139,54 +145,6 @@ public bool NextBoolean() return (_buffer & 0xF000000000000000) == 0; } - /// - /// Generates a pseudorandom byte. - /// - /// - /// A pseudorandom byte. - /// - - public byte NextByte() - { - if (_bufferMask >= 8) - { - byte _ = (byte)_buffer; - _buffer >>= 8; - _bufferMask >>= 8; - return _; - } - - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - _buffer = tempY + _y; - _x = tempX; - _y = tempY; - - _bufferMask = 0x8000000000000; - return (byte)(_buffer >>= 8); - } - - /// - /// Generates a pseudorandom 16-bit signed integer. - /// - /// - /// A pseudorandom 16-bit signed integer. - /// - - public short NextInt16() - { - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - var _ = (short)(tempY + _y); - - _x = tempX; - _y = tempY; - - return _; - } - /// /// Generates a pseudorandom 16-bit unsigned integer. /// @@ -206,63 +164,6 @@ public ushort NextUInt16() return _; } - /// - /// Generates a pseudorandom 32-bit signed integer. - /// - /// - /// A pseudorandom 32-bit signed integer. - /// - public int NextInt32() - { - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - var _ = (int)(tempY + _y); - - _x = tempX; - _y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom 32-bit unsigned integer. - /// - /// - /// A pseudorandom 32-bit unsigned integer. - /// - public uint NextUInt32() - { - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - var _ = (uint)(tempY + _y); - - _x = tempX; - _y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom 64-bit signed integer. - /// - /// - /// A pseudorandom 64-bit signed integer. - /// - public long NextInt64() - { - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - var _ = (long)(tempY + _y); - - _x = tempX; - _y = tempY; - - return _; - } - /// /// Generates a pseudorandom 64-bit unsigned integer. /// @@ -303,36 +204,6 @@ public double NextDouble() return _; } - /// - /// Generates a pseudorandom decimal between - /// 0 and 1 non-inclusive. - /// - /// - /// A pseudorandom decimal. - /// - public decimal NextDecimal() - { - var tempX = _y; - _x ^= _x << 23; var tempY = _x ^ _y ^ (_x >> 17) ^ (_y >> 26); - - var tempZ = tempY + _y; - - var h = (int)(tempZ & 0x1FFFFFFF); - var m = (int)(tempZ >> 16); - var l = (int)(tempZ >> 32); - - var _ = new decimal(l, m, h, false, 28); - - _x = tempX; - _y = tempY; - - return _; - } - - public ulong Range(ulong aMin, ulong aMax) - { - return aMin + NextUInt64() % (aMax - aMin); - } public int Range(int aMin, int aMax) { var rndInt = (int)NextUInt64(); @@ -376,14 +247,6 @@ public ulong FairRange(ulong aRange) v = NextUInt64(); return v % aRange; } - public ulong FairRange(ulong aMin, ulong aMax) - { - return aMin + FairRange(aMax - aMin); - } - public Vector3D Vector(double radius) - { - return new Vector3D(Range(-radius, radius), Range(-radius, radius), Range(-radius, radius)); - } } public class XorShiftRandom { @@ -416,913 +279,108 @@ public XorShiftRandom(ulong seed) var tempX = Y; X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); var newSeed = tempY + Y; X = tempX; Y = tempY; X = newSeed << 3; Y = newSeed >> 3; } + } - /// - /// Reinits existing Random class - /// with the supplied seed. - /// - /// - /// The seed value. - /// - public void Reinit(ulong seed) + internal class RunningAverage + { + private readonly int _size; + private readonly int[] _values; + private int _valuesIndex; + private int _valueCount; + private int _sum; + + internal RunningAverage(int size) { - X = seed << 3; Y = seed >> 3; - Buffer = 0; - BufferMask = 0; + _size = Math.Max(size, 1); + _values = new int[_size]; + } - // - // random isn't very random unless we do the below.... likely because hashes produce incrementing numbers for Int3 conversions. - // + internal int Add(int newValue) + { + // calculate new value to add to sum by subtracting the + // value that is replaced from the new value; + var temp = newValue - _values[_valuesIndex]; + _values[_valuesIndex] = newValue; + _sum += temp; - var temp1 = Y; X ^= X << 23; var temp2 = X ^ Y ^ (X >> 17) ^ (Y >> 26); X = temp1; Y = temp2; + _valuesIndex++; + _valuesIndex %= _size; - var tempX = Y; X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); var newSeed = tempY + Y; X = tempX; Y = tempY; + if (_valueCount < _size) + _valueCount++; - X = newSeed << 3; Y = newSeed >> 3; + return _sum / _valueCount; } + } - public MyTuple GetSeedVaues() - { - return new MyTuple(X, Y); - } + public class NetworkReporter + { + public Dictionary> ReportData = new Dictionary>(); + public readonly MyConcurrentPool ReportPool = new MyConcurrentPool(3600, reprot => reprot.Clean()); - public void SyncSeed(ulong x, ulong y) + public NetworkReporter() { - X = x; - Y = y; + foreach (var suit in (PacketType[])Enum.GetValues(typeof(PacketType))) + ReportData.Add(suit, new List()); } - /// - /// Generates a pseudorandom boolean. - /// - /// - /// A pseudorandom boolean. - /// - public bool NextBoolean() + public class Report { - if (BufferMask > 0) + public enum Received { - var _ = (Buffer & BufferMask) == 0; - BufferMask >>= 1; - return _; + None, + Server, + Client } - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - Buffer = tempY + Y; - X = tempX; - Y = tempY; - - BufferMask = 0x8000000000000000; - return (Buffer & 0xF000000000000000) == 0; - } - - /// - /// Generates a pseudorandom byte. - /// - /// - /// A pseudorandom byte. - /// + public Received Receiver; + public bool PacketValid; + public int PacketSize; - public byte NextByte() - { - if (BufferMask >= 8) + public void Clean() { - byte _ = (byte)Buffer; - Buffer >>= 8; - BufferMask >>= 8; - return _; + Receiver = Received.None; + PacketValid = false; + PacketSize = 0; } - - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - Buffer = tempY + Y; - X = tempX; - Y = tempY; - - BufferMask = 0x8000000000000; - return (byte)(Buffer >>= 8); } + } - /// - /// Generates a pseudorandom 16-bit signed integer. - /// - /// - /// A pseudorandom 16-bit signed integer. - /// + internal class StallReporter + { + private readonly Stopwatch _watch = new Stopwatch(); + internal string Name; + internal double MaxMs; - public short NextInt16() + public void Start(string name, double maxMs) { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = (short)(tempY + Y); - - X = tempX; - Y = tempY; - - return _; + Name = name; + MaxMs = maxMs; + _watch.Restart(); } - /// - /// Generates a pseudorandom 16-bit unsigned integer. - /// - /// - /// A pseudorandom 16-bit unsigned integer. - /// - public ushort NextUInt16() + public void End() { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = (ushort)(tempY + Y); - - X = tempX; - Y = tempY; - - return _; + _watch.Stop(); + var ticks = _watch.ElapsedTicks; + var ns = 1000000000.0 * ticks / Stopwatch.Frequency; + var ms = ns / 1000000.0; + if (ms > MaxMs) + { + var message = $"[Warning] {ms} milisecond delay detected in {Name}: "; + Log.LineShortDate(message, "perf"); + } } + } - /// - /// Generates a pseudorandom 32-bit signed integer. - /// - /// - /// A pseudorandom 32-bit signed integer. - /// - public int NextInt32() + internal class DSUtils + { + internal struct Results { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = (int)(tempY + Y); - - X = tempX; - Y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom 32-bit unsigned integer. - /// - /// - /// A pseudorandom 32-bit unsigned integer. - /// - public uint NextUInt32() - { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = (uint)(tempY + Y); - - X = tempX; - Y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom 64-bit signed integer. - /// - /// - /// A pseudorandom 64-bit signed integer. - /// - public long NextInt64() - { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = (long)(tempY + Y); - - X = tempX; - Y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom 64-bit unsigned integer. - /// - /// - /// A pseudorandom 64-bit unsigned integer. - /// - public ulong NextUInt64() - { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var _ = tempY + Y; - - X = tempX; - Y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom double between - /// 0 and 1 non-inclusive. - /// - /// - /// A pseudorandom double. - /// - public double NextDouble() - { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var tempZ = tempY + Y; - var _ = DoubleUnit * (0x7FFFFFFF & tempZ); - - X = tempX; - Y = tempY; - - return _; - } - - /// - /// Generates a pseudorandom decimal between - /// 0 and 1 non-inclusive. - /// - /// - /// A pseudorandom decimal. - /// - public decimal NextDecimal() - { - var tempX = Y; - X ^= X << 23; var tempY = X ^ Y ^ (X >> 17) ^ (Y >> 26); - - var tempZ = tempY + Y; - - var h = (int)(tempZ & 0x1FFFFFFF); - var m = (int)(tempZ >> 16); - var l = (int)(tempZ >> 32); - - var _ = new decimal(l, m, h, false, 28); - - X = tempX; - Y = tempY; - - return _; - } - - public ulong Range(ulong aMin, ulong aMax) - { - return aMin + NextUInt64() % (aMax - aMin); - } - public int Range(int aMin, int aMax) - { - var rndInt = (int)NextUInt64(); - var value = aMin + rndInt % (aMax - aMin); - - if (value < aMin || value > aMax) - value *= -1; - - return value; - } - - public double Range(double aMin, double aMax) - { - var value = aMin + NextDouble() * (aMax - aMin); - if (value < aMin || value > aMax) - value *= -1; - - return value; - } - - public float Range(float aMin, float aMax) - { - var value = aMin + NextDouble() * (aMax - aMin); - if (value < aMin || value > aMax) - value *= -1; - - return (float)value; - } - - // corrects bit alignment which might shift the probability slightly to the - // lower numbers based on the choosen range. - public ulong FairRange(ulong aRange) - { - ulong dif = ulong.MaxValue % aRange; - // if aligned or range too big, just pick a number - if (dif == 0 || ulong.MaxValue / (aRange / 4UL) < 2UL) - return NextUInt64() % aRange; - ulong v = NextUInt64(); - // avoid the last incomplete set - while (ulong.MaxValue - v < dif) - v = NextUInt64(); - return v % aRange; - } - public ulong FairRange(ulong aMin, ulong aMax) - { - return aMin + FairRange(aMax - aMin); - } - public Vector3D Vector(double radius) - { - return new Vector3D(Range(-radius, radius), Range(-radius, radius), Range(-radius, radius)); - } - } - - internal class RunningAverage - { - private readonly int _size; - private readonly int[] _values; - private int _valuesIndex; - private int _valueCount; - private int _sum; - - internal RunningAverage(int size) - { - _size = Math.Max(size, 1); - _values = new int[_size]; - } - - internal int Add(int newValue) - { - // calculate new value to add to sum by subtracting the - // value that is replaced from the new value; - var temp = newValue - _values[_valuesIndex]; - _values[_valuesIndex] = newValue; - _sum += temp; - - _valuesIndex++; - _valuesIndex %= _size; - - if (_valueCount < _size) - _valueCount++; - - return _sum / _valueCount; - } - } - - internal static class ConcurrentQueueExtensions - { - public static void Clear(this ConcurrentQueue queue) - { - T item; - while (queue.TryDequeue(out item)) { } - } - } - - class FiniteFifoQueueSet - { - private readonly T1[] _nodes; - private readonly Dictionary _backingDict; - private int _nextSlotToEvict; - - public FiniteFifoQueueSet(int size) - { - _nodes = new T1[size]; - _backingDict = new Dictionary(size + 1); - _nextSlotToEvict = 0; - } - - public void Enqueue(T1 key, T2 value) - { - try - { - _backingDict.Remove(_nodes[_nextSlotToEvict]); - _nodes[_nextSlotToEvict] = key; - _backingDict.Add(key, value); - - _nextSlotToEvict++; - if (_nextSlotToEvict >= _nodes.Length) _nextSlotToEvict = 0; - } - catch (Exception ex) { Log.Line($"Exception in Enqueue: {ex}"); } - } - - public bool Contains(T1 value) - { - return _backingDict.ContainsKey(value); - } - - public bool TryGet(T1 value, out T2 hostileEnt) - { - return _backingDict.TryGetValue(value, out hostileEnt); - } - } - - public class NetworkReporter - { - public Dictionary> ReportData = new Dictionary>(); - public readonly MyConcurrentPool ReportPool = new MyConcurrentPool(3600, reprot => reprot.Clean()); - - public NetworkReporter() - { - foreach (var suit in (PacketType[])Enum.GetValues(typeof(PacketType))) - ReportData.Add(suit, new List()); - } - - public class Report - { - public enum Received - { - None, - Server, - Client - } - - public Received Receiver; - public bool PacketValid; - public int PacketSize; - - public void Clean() - { - Receiver = Received.None; - PacketValid = false; - PacketSize = 0; - } - } - } - - public class DsUniqueListFastRemove - { - private List _list; - private Dictionary _dictionary; - - public DsUniqueListFastRemove(int capacity) - { - _list = new List(capacity); - _dictionary = new Dictionary(capacity); - } - - public DsUniqueListFastRemove() - { - _list = new List(); - _dictionary = new Dictionary(); - } - - /// O(1) - public int Count - { - get - { - return _list.Count; - } - } - - /// O(1) - public T this[int index] - { - get - { - return _list[index]; - } - } - - /// O(1) - public bool Add(T item) - { - if (_dictionary.ContainsKey(item)) - return false; - _list.Add(item); - _dictionary.Add(item, _list.Count - 1); - return true; - } - - /// O(1) - public bool Remove(T item) - { - int oldPos; - if (_dictionary.TryGetValue(item, out oldPos)) - { - - _dictionary.Remove(item); - _list.RemoveAtFast(oldPos); - var count = _list.Count; - if (count > 0) - { - count--; - if (oldPos <= count) - _dictionary[_list[oldPos]] = oldPos; - else - _dictionary[_list[count]] = count; - } - - return true; - } - return false; - } - - public void Clear() - { - _list.Clear(); - _dictionary.Clear(); - } - - /// O(1) - public bool Contains(T item) - { - return _dictionary.ContainsKey(item); - } - - public UniqueListReader Items - { - get - { - return new UniqueListReader(); - } - } - - public List ItemList - { - get - { - return new List(_list); - } - } - - public List.Enumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - } - - public class DsUniqueConcurrentListFastRemove - { - private MyConcurrentList _list = new MyConcurrentList(); - private ConcurrentDictionary _dictionary = new ConcurrentDictionary(); - - /// O(1) - public int Count - { - get - { - return _list.Count; - } - } - - /// O(1) - public T this[int index] - { - get - { - return _list[index]; - } - } - - /// O(1) - public bool Add(T item) - { - if (_dictionary.ContainsKey(item)) - return false; - _list.Add(item); - - return _dictionary.TryAdd(item, _list.Count - 1); - - } - - /// O(1) - public bool Remove(T item) - { - int oldPos; - if (_dictionary.TryGetValue(item, out oldPos)) - { - - _dictionary.Remove(item); - _list.RemoveAtFast(oldPos); - var count = _list.Count; - if (count > 0) - { - count--; - if (oldPos <= count) - _dictionary[_list[oldPos]] = oldPos; - else - _dictionary[_list[count]] = count; - } - - return true; - } - return false; - } - - public void Clear() - { - _list.Clear(); - _dictionary.Clear(); - } - - /// O(1) - public bool Contains(T item) - { - return _dictionary.ContainsKey(item); - } - - public UniqueListReader Items - { - get - { - return new UniqueListReader(); - } - } - - public ListReader ItemList - { - get - { - return new ListReader(new List(_list)); - } - } - - public Object GetEnumerator() - { - return _list.GetEnumerator(); - } - } - - public class DsUniqueList - { - private List _list = new List(); - private HashSet _hashSet = new HashSet(); - - /// O(1) - public int Count - { - get - { - return _list.Count; - } - } - - /// O(1) - public T this[int index] - { - get - { - return _list[index]; - } - } - - /// O(1) - public bool Add(T item) - { - if (!_hashSet.Add(item)) - return false; - _list.Add(item); - return true; - } - - /// O(n) - public bool Insert(int index, T item) - { - if (_hashSet.Add(item)) - { - _list.Insert(index, item); - return true; - } - _list.Remove(item); - _list.Insert(index, item); - return false; - } - - /// O(n) - public bool Remove(T item) - { - if (!_hashSet.Remove(item)) - return false; - _list.Remove(item); - return true; - } - - public void Clear() - { - _list.Clear(); - _hashSet.Clear(); - } - - /// O(1) - public bool Contains(T item) - { - return _hashSet.Contains(item); - } - - public UniqueListReader Items - { - get - { - return new UniqueListReader(); - } - } - - public ListReader ItemList - { - get - { - return new ListReader(_list); - } - } - - public List.Enumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - } - - public class ConcurrentUniqueQueue : IEnumerable - { - private readonly MyConcurrentHashSet _hashSet; - private readonly ConcurrentQueue _queue; - private SpinLockRef _lock = new SpinLockRef(); - - public ConcurrentUniqueQueue() - { - _hashSet = new MyConcurrentHashSet(); - _queue = new ConcurrentQueue(); - } - - - public int Count - { - get - { - return _hashSet.Count; - } - } - - public void Clear() - { - _hashSet.Clear(); - _queue.Clear(); - } - - - public bool Contains(T item) - { - return _hashSet.Contains(item); - } - - - public void Enqueue(T item) - { - if (_hashSet.Add(item)) - { - _queue.Enqueue(item); - } - } - - public T Dequeue() - { - T item; - _queue.TryDequeue(out item); - _hashSet.Remove(item); - return item; - } - - - public T Peek() - { - T result; - _queue.TryPeek(out result); - return result; - } - - - public IEnumerator GetEnumerator() - { - return _queue.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _queue.GetEnumerator(); - } - } - - public class DsConcurrentUniqueList - { - private List _list = new List(); - private HashSet _hashSet = new HashSet(); - private SpinLockRef _lock = new SpinLockRef(); - - /// O(1) - public int Count - { - get - { - return _list.Count; - } - } - - /// O(1) - public T this[int index] - { - get - { - return _list[index]; - } - } - - /// O(1) - public bool Add(T item) - { - using (_lock.Acquire()) - { - if (!_hashSet.Add(item)) - return false; - _list.Add(item); - return true; - } - } - - /// O(n) - public bool Insert(int index, T item) - { - using (_lock.Acquire()) - { - if (_hashSet.Add(item)) - { - _list.Insert(index, item); - return true; - } - _list.Remove(item); - _list.Insert(index, item); - return false; - } - } - - /// O(n) - public bool Remove(T item) - { - using (_lock.Acquire()) - { - if (!_hashSet.Remove(item)) - return false; - _list.Remove(item); - return true; - } - } - - public void Clear() - { - _list.Clear(); - _hashSet.Clear(); - } - - /// O(1) - public bool Contains(T item) - { - return _hashSet.Contains(item); - } - - public UniqueListReader Items - { - get - { - return new UniqueListReader(); - } - } - - public ListReader ItemList - { - get - { - return new ListReader(_list); - } - } - - public List.Enumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - } - - internal class StallReporter - { - private readonly Stopwatch _watch = new Stopwatch(); - internal string Name; - internal double MaxMs; - - public void Start(string name, double maxMs) - { - Name = name; - MaxMs = maxMs; - _watch.Restart(); - } - - public void End() - { - _watch.Stop(); - var ticks = _watch.ElapsedTicks; - var ns = 1000000000.0 * ticks / Stopwatch.Frequency; - var ms = ns / 1000000.0; - if (ms > MaxMs) - { - var message = $"[Warning] {ms} milisecond delay detected in {Name}: "; - Log.LineShortDate(message, "perf"); - } - } - } - - internal class DSUtils - { - internal struct Results - { - public double Min; - public double Max; - public double Median; - public uint MaxTick; + public double Min; + public double Max; + public double Median; + public uint MaxTick; } internal class Timings @@ -1443,71 +501,4 @@ public void Complete(string name, bool store, bool display = false) } } } - - public class UniqueQueue : IEnumerable - { - private HashSet hashSet; - private Queue queue; - - - public UniqueQueue() - { - hashSet = new HashSet(); - queue = new Queue(); - } - - - public int Count - { - get - { - return hashSet.Count; - } - } - - public void Clear() - { - hashSet.Clear(); - queue.Clear(); - } - - - public bool Contains(T item) - { - return hashSet.Contains(item); - } - - - public void Enqueue(T item) - { - if (hashSet.Add(item)) - { - queue.Enqueue(item); - } - } - - public T Dequeue() - { - T item = queue.Dequeue(); - hashSet.Remove(item); - return item; - } - - - public T Peek() - { - return queue.Peek(); - } - - - public IEnumerator GetEnumerator() - { - return queue.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return queue.GetEnumerator(); - } - } } diff --git a/Data/Scripts/CoreSystems/Support/VersionControl.cs b/Data/Scripts/CoreSystems/Support/VersionControl.cs index cec10a7c..d4b346e1 100644 --- a/Data/Scripts/CoreSystems/Support/VersionControl.cs +++ b/Data/Scripts/CoreSystems/Support/VersionControl.cs @@ -6,8 +6,6 @@ using CoreSystems.Settings; using CoreSystems.Support; using Sandbox.ModAPI; -using VRage.Utils; -using static VRage.Game.MyObjectBuilder_SessionComponentMission; namespace WeaponCore.Data.Scripts.CoreSystems.Support { diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs index 2d16fb38..30433c7a 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs @@ -1,13 +1,9 @@ using CoreSystems; using CoreSystems.Platform; using CoreSystems.Support; -using System.Runtime.CompilerServices; -using Sandbox.ModAPI; using VRage.Game; using VRage.Utils; using VRageMath; -using WeaponCore.Data.Scripts.CoreSystems.Ui.Targeting; -using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition; namespace WeaponCore.Data.Scripts.CoreSystems.Ui.Hud { diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs index f6eab3c0..cbec3f88 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using CoreSystems; using CoreSystems.Platform; using VRage.Collections; using VRage.Utils; diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudSupport.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudSupport.cs index 4e2ea693..8d64eb11 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudSupport.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudSupport.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using CoreSystems; using CoreSystems.Platform; using CoreSystems.Support; diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudText.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudText.cs index e4e42ba3..92621353 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudText.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudText.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using CoreSystems; -using CoreSystems.Support; using VRage.Game; using VRage.Utils; using VRageMath; diff --git a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs index e56298cf..3ff7ba0c 100644 --- a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs +++ b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiSelect.cs @@ -6,8 +6,6 @@ using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.ModAPI; -using VRage.Noise.Patterns; -using VRage.Utils; using VRageMath; using CollisionLayers = Sandbox.Engine.Physics.MyPhysics.CollisionLayers; diff --git a/Data/Scripts/CoreSystems/Ui/UiInput.cs b/Data/Scripts/CoreSystems/Ui/UiInput.cs index 6265fa9e..68afb975 100644 --- a/Data/Scripts/CoreSystems/Ui/UiInput.cs +++ b/Data/Scripts/CoreSystems/Ui/UiInput.cs @@ -1,11 +1,9 @@ -using System; -using CoreSystems; +using CoreSystems; using CoreSystems.Platform; using CoreSystems.Support; using Sandbox.ModAPI; using VRage.Input; using VRageMath; -using WeaponCore.Data.Scripts.CoreSystems.Ui.Hud; namespace WeaponCore.Data.Scripts.CoreSystems.Ui { From e1d3c540614b91e35976b27f82b55710828d0a24 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sat, 19 Apr 2025 11:46:01 -0500 Subject: [PATCH 34/77] Usings cleanup --- Data/Scripts/CoreSystems/Ai/AiDatabase.cs | 3 +-- Data/Scripts/CoreSystems/EntityComp/EntityState.cs | 3 +-- .../CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs | 3 --- .../Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs | 2 -- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs index e832f22f..72cc393e 100644 --- a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs +++ b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs @@ -1,5 +1,4 @@ -using System; -using Sandbox.Common.ObjectBuilders; +using Sandbox.Common.ObjectBuilders; using Sandbox.Game.Entities; using Sandbox.ModAPI; using VRage.Game; diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityState.cs b/Data/Scripts/CoreSystems/EntityComp/EntityState.cs index f662d0fb..d43248c7 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityState.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityState.cs @@ -1,5 +1,4 @@ -using System; -using CoreSystems.Platform; +using CoreSystems.Platform; using Sandbox.Game.Entities; using VRage.Game.Entity; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index d419cac9..e8c795f4 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -2,15 +2,12 @@ using System.Collections.Generic; using CoreSystems.Projectiles; using CoreSystems.Support; -using Sandbox.Definitions; using Sandbox.Game.Entities; -using Sandbox.Game.Lights; using VRage.Game; using VRage.Game.Entity; using VRage.Game.ModAPI; using VRage.Utils; using VRageMath; -using VRageRender.Lights; using static CoreSystems.Session; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs index 8b310a28..6ce16c40 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs @@ -1,10 +1,8 @@ using CoreSystems.Projectiles; using CoreSystems.Support; using Sandbox.Game.Entities; -using System; using VRage.Game.Entity; using VRage.Game.ModAPI; -using VRage.Utils; using VRageMath; namespace CoreSystems.Platform From 363910fa414b3afd7cdef23ad30dff456cdc9e99 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 4 Mar 2025 15:10:26 -0600 Subject: [PATCH 35/77] First --- .../Coreparts/Definitions/AmmoTypes.cs | 6 +- .../CoreSystems/Projectiles/ProjectileHits.cs | 5 + .../Projectiles/ProjectileTypes.cs | 6 +- .../CoreSystems/Session/SessionDamageMgr.cs | 205 ++++++++++++------ 4 files changed, 156 insertions(+), 66 deletions(-) diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs index 3fafc203..47c6ea83 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs @@ -911,15 +911,15 @@ partial class Parts AmmoRound = "Small Railgun Slug", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. HybridRound = true, // Use both a physical ammo magazine and energy per shot. EnergyCost = 0.081f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. - BaseDamage = 8000f, // Direct damage; one steel plate is worth 100. - Mass = 25f, // In kilograms; how much force the impact will apply to the target. + BaseDamage = 600f, // Direct damage; one steel plate is worth 100. + Mass = 0, //TEMP // In kilograms; how much force the impact will apply to the target. BackKickForce = 30000f, // Recoil. This is applied to the Parent Grid. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. NoGridOrArmorScaling = true, Trajectory = new TrajectoryDef { MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - DesiredSpeed = 1000, // voxel phasing if you go above 5100 + DesiredSpeed = 10000, //TEMP was 1000// voxel phasing if you go above 5100 MaxTrajectory = 1400f, // Max Distance the projectile or beam can Travel. }, AmmoGraphics = new GraphicDef diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 2e2ff4cb..e4208b59 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -830,6 +830,11 @@ internal int GetEntityCompareDist(HitEntity x, HitEntity y, ProInfo info) { lastBlockHit = firstBlock; hitEnt.Blocks.Add(new HitEntity.RootBlocks {Block = firstBlock, QueryPos = posI}); + // + var blockDist = Vector3D.DistanceSquared(grid.GridIntegerToWorld(posI), beam.From); + info.BlockList.Add(new KeyValuePair(firstBlock, blockDist)); + + if (closestBlockFound) continue; MyOrientedBoundingBoxD obb; var fat = firstBlock.FatBlock; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs index 35a2f756..3326a6df 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs @@ -18,6 +18,7 @@ public class ProInfo internal readonly Target Target = new Target(); internal readonly SmartStorage Storage = new SmartStorage(); internal readonly List HitList = new List(); + internal readonly List> BlockList = new List>(); internal readonly ProHit ProHit = new ProHit(); internal List> ProHits; internal int[] PatternShuffle; @@ -113,8 +114,9 @@ internal void Clean() Target.Reset(Session.I.Tick, Target.States.ProjectileClean); HitList.Clear(); - - if(aConst.IsGuided) + BlockList.Clear(); + + if (aConst.IsGuided) Storage.Clean(this); diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 828b1242..0758a398 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using CoreSystems.Projectiles; using CoreSystems.Support; using Sandbox.Definitions; @@ -40,6 +41,68 @@ internal void ProcessHits() var noDamageProjectile = ammoDef.BaseDamage <= 0; var lastIndex = info.HitList.Count - 1; + //Log.Line($"ProcessHits"); + + //var gridList = new List(); + if (info.HitList.Count > 1 && info.BlockList.Count > 1) + info.BlockList.SortNoAlloc((b, a) => b.Value.CompareTo(a.Value)); + /* + //Sort and ID order of grids being hit, incl duplicates + if (info.BlockList.Count > 2) + for (int i = 0; i < info.BlockList.Count; i++) + { + if (i == 0) + { + gridList.Add(info.BlockList[i].Key.CubeGrid); + continue; + } + if (info.BlockList[i - 1].Key.CubeGrid != info.BlockList[i].Key.CubeGrid) + gridList.Add(info.BlockList[i].Key.CubeGrid); + } + */ + + /* + var pool = Projectiles.HitEntityArrayPool[Environment.CurrentManagedThreadId]; + var hitEntity = pool.Count > 0 ? pool.Pop() : new HitEntity(); + info.HitList.Add(hitEntity); + + if (gridList.Count > 1) + { + var blockListPos = 0; + for (int i = 0; i < info.HitList.Count; i++) + { + var hEnt = info.HitList[i]; + if (hEnt.EventType != HitEntity.Type.Grid) + continue; + var grid = hEnt.Entity as IMyCubeGrid; + int lastHit = 0; + for (int j = 0; j < hEnt.Blocks.Count; j++) + { + if (hEnt.Blocks[j].Block == info.BlockList[blockListPos].Key) + { + lastHit++; + blockListPos++; + } + } + + hEnt.Blocks.RemoveRange(lastHit, hEnt.Blocks.Count - lastHit); + Log.Line($"Last hit on {grid.DisplayName}: {lastHit}"); + break; + } + } + + //Logging readout only + Log.Line("Grids hit:"); + for (int i = 0; i < gridList.Count; i++) + Log.Line($"{gridList[i].DisplayName}"); + */ + + Log.Line("Blocks hit:"); + for (int i = 0; i < info.BlockList.Count; i++) + Log.Line($"Blk: {info.BlockList[i].Key.BlockDefinition.DisplayNameText} {info.BlockList[i].Value} {info.BlockList[i].Key.CubeGrid.DisplayName}"); + // + + if (!info.DoDamage && IsServer) info.BaseDamagePool = 0; @@ -350,74 +413,51 @@ private void DamageShield(HitEntity hitEnt, ProInfo info) // silly levels of inl private void DamageGrid(HitEntity hitEnt, ProInfo t) { - var grid = hitEnt.Entity as MyCubeGrid; - if (grid == null || grid.MarkedForClose || !hitEnt.HitPos.HasValue || hitEnt.Blocks == null) + //var grid = hitEnt.Entity as MyCubeGrid; + if (!hitEnt.HitPos.HasValue || hitEnt.Blocks == null) { hitEnt.Blocks?.Clear(); return; } - - if (t.AmmoDef.DamageScales.Shields.Type == ShieldDef.ShieldType.Heal || (!t.AmmoDef.Const.SelfDamage && !t.AmmoDef.Const.IsCriticalReaction && !t.Storage.SmartReady) && t.Ai.AiType == Ai.AiTypes.Grid && t.Ai.GridEntity.IsInSameLogicalGroupAs(grid) || !grid.DestructibleBlocks || grid.Immune || grid.GridGeneralDamageModifier <= 0) + + if (t.AmmoDef.DamageScales.Shields.Type == ShieldDef.ShieldType.Heal || (!t.AmmoDef.Const.SelfDamage && !t.AmmoDef.Const.IsCriticalReaction && !t.Storage.SmartReady) && t.Ai.AiType == Ai.AiTypes.Grid && t.Ai.GridEntity.IsInSameLogicalGroupAs(hitEnt.Blocks[0].Block.CubeGrid) ) { t.BaseDamagePool = 0; return; } //Global & modifiers - var canDamage = t.DoDamage; - - var directDmgGlobal = Settings.Enforcement.DirectDamageModifer * hitEnt.DamageMulti; - var areaDmgGlobal = Settings.Enforcement.AreaDamageModifer * hitEnt.DamageMulti; - var sync = DedicatedServer; - float gridDamageModifier = grid.GridGeneralDamageModifier; - var gridBlockCount = grid.CubeBlocks.Count; IMySlimBlock rootBlock = null; var d = t.AmmoDef.DamageScales; var armor = t.AmmoDef.DamageScales.Armor; - var maxIntegrity = d.MaxIntegrity; //Target/targeting Info - var largeGrid = grid.GridSizeEnum == MyCubeSize.Large; var attackerId = t.Weapon.Comp.CoreEntity.EntityId; var maxObjects = t.AmmoDef.Const.MaxObjectsHit; - var gridMatrix = grid.PositionComp.WorldMatrixRef; var distTraveled = t.AmmoDef.Const.IsBeamWeapon ? hitEnt.HitDist ?? t.DistanceTraveled : t.DistanceTraveled; - - var direction = hitEnt.Intersection; - - var deformType = d.Deform.DeformType; var deformDelay = t.AmmoDef.Const.DeformDelay; - //Ammo properties - var hitMass = t.AmmoDef.Const.Mass; //overall primary falloff scaling var fallOff = t.AmmoDef.Const.FallOffScaling && distTraveled > t.AmmoDef.Const.FallOffDistance; var fallOffMultipler = 1d; if (fallOff) - { fallOffMultipler = (float)MathHelperD.Clamp(1.0 - ((distTraveled - t.AmmoDef.Const.FallOffDistance) / (t.AmmoDef.Const.MaxTrajectory - t.AmmoDef.Const.FallOffDistance)), t.AmmoDef.DamageScales.FallOff.MinMultipler, 1); - } + //hit & damage loop info var basePool = t.BaseDamagePool; var hits = 1; if (t.AmmoDef.Const.VirtualBeams) - { hits = t.Weapon.WeaponCache.Hits; - } - var partialShield = t.ShieldInLine && !t.ShieldBypassed && SApi.MatchEntToShieldFast(grid, true) != null; var objectsHit = t.ObjectsHit; - var blockCount = hitEnt.Blocks.Count; + var blockCount = t.BlockList.Count; var countBlocksAsObjects = t.AmmoDef.ObjectsHit.CountBlocks; - - - //General damage data - + //Generics used for both AOE and detonation var aoeFalloff = Falloff.NoFalloff; var aoeShape = AoeShape.Diamond; var hasAoe = t.AmmoDef.AreaOfDamage.ByBlockHit.Enable; var hasDet = t.AmmoDef.AreaOfDamage.EndOfLife.Enable && t.RelativeAge >= t.AmmoDef.AreaOfDamage.EndOfLife.MinArmingTime; - var damageType = t.ShieldBypassed ? ShieldBypassDamageType : hasAoe || hasDet ? MyDamageType.Explosion : MyDamageType.Bullet; + //Switches and setup for damage types/event loops var detRequested = false; var detActive = false; @@ -429,18 +469,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) var smallVsLargeBuff = 1f; var cutoff = t.AmmoDef.BaseDamageCutoff; var useBaseCutoff = cutoff > 0; - if (!Settings.Enforcement.DisableSmallVsLargeBuff && t.Ai.AiType == Ai.AiTypes.Grid && grid.GridSizeEnum != t.Ai.GridEntity.GridSizeEnum) - { - if (t.Ai.GridEntity.GridSizeEnum == MyCubeSize.Large) { - if (aConst.SmallGridDmgScale < 0 && aConst.LargeGridDmgScale < 0) - smallVsLargeBuff = 0.25f; - } - } - var gridSizeBuff = 1f; - if (grid.GridSizeEnum == MyCubeSize.Large) - gridSizeBuff = Settings.Enforcement.LargeGridDamageMultiplier; - else - gridSizeBuff = Settings.Enforcement.SmallGridDamageMultiplier; + var appliedImpulse = false; for (int i = 0; i < blockCount; i++) @@ -453,6 +482,22 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) else if(hasDet && objectsHit >= maxObjects && t.AmmoDef.ObjectsHit.SkipBlocksForAOE) basePool = 0; + //var rootInfo = hitEnt.Blocks[i]; + rootBlock = t.BlockList[i].Key; + + //Grid specific + var grid = rootBlock.CubeGrid as MyCubeGrid; + if (grid.MarkedForClose || grid.IsPreview || !grid.DestructibleBlocks || grid.Immune || grid.GridGeneralDamageModifier <= 0) + continue; + + float gridDamageModifier = grid.GridGeneralDamageModifier; + var gridBlockCount = grid.CubeBlocks.Count; + var largeGrid = grid.GridSizeEnum == MyCubeSize.Large; + var gridSizeBuff = grid.GridSizeEnum == MyCubeSize.Large ? Settings.Enforcement.LargeGridDamageMultiplier : Settings.Enforcement.SmallGridDamageMultiplier; + var partialShield = t.ShieldInLine && !t.ShieldBypassed && SApi.MatchEntToShieldFast(grid, true) != null; + if (!Settings.Enforcement.DisableSmallVsLargeBuff && t.Ai.AiType == Ai.AiTypes.Grid && grid.GridSizeEnum != t.Ai.GridEntity.GridSizeEnum && t.Ai.GridEntity.GridSizeEnum == MyCubeSize.Large && aConst.SmallGridDmgScale < 0 && aConst.LargeGridDmgScale < 0) + smallVsLargeBuff = 0.25f; + var aoeAbsorb = 0d; var aoeDepth = 0d; var aoeDmgTally = 0d; @@ -461,7 +506,6 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) var aoeIsPool = false; var aoeHits = 0; - if (hasAoe && !detRequested)//load in AOE vars { aoeDamage = aConst.ByBlockHitDamage; @@ -483,8 +527,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) aoeIsPool = aoeFalloff == Falloff.Pooled; } - var rootInfo = hitEnt.Blocks[i]; - rootBlock = rootInfo.Block; + if (!detRequested) { if (IsServer && _destroyedSlims.Contains(rootBlock) || IsClient && _destroyedSlimsClient.Contains(rootBlock)) @@ -524,7 +567,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) if (hasAoe && !detRequested || hasDet && detRequested) { detRequested = false; - RadiantAoe(ref rootInfo, grid, aoeRadius, aoeDepth, direction, ref maxAoeDistance, out foundAoeBlocks, aoeShape, showHits, out aoeHits); + RadiantAoe(rootBlock.Position, grid, aoeRadius, aoeDepth, hitEnt.Intersection, ref maxAoeDistance, out foundAoeBlocks, aoeShape, showHits, out aoeHits); } var blockStages = maxAoeDistance + 1; @@ -585,9 +628,9 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) var blockHp = (double)(!IsClient ? block.Integrity - block.AccumulatedDamage : (_slimHealthClient.TryGetValue(block, out cachedIntegrity) ? cachedIntegrity : block.Integrity)); var blockDmgModifier = cubeBlockDef.GeneralDamageMultiplier; double damageScale = hits; - double directDamageScale = directDmgGlobal; - double areaDamageScale = areaDmgGlobal; - double detDamageScale = areaDmgGlobal; + double directDamageScale = Settings.Enforcement.DirectDamageModifer * hitEnt.DamageMulti; + double areaDamageScale = Settings.Enforcement.AreaDamageModifer * hitEnt.DamageMulti; + double detDamageScale = areaDamageScale; //Damage scaling for blocktypes if (aConst.DamageScaling || !MyUtils.IsEqual(blockDmgModifier, 1f) || !MyUtils.IsEqual(gridDamageModifier, 1f)) @@ -597,7 +640,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) else blockHp = (blockHp / blockDmgModifier / gridDamageModifier); - if (maxIntegrity > 0 && blockHp > maxIntegrity) + if (d.MaxIntegrity > 0 && blockHp > d.MaxIntegrity) { basePool = 0; continue; @@ -769,22 +812,23 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) } } + Log.Line($"Damaged {block.BlockDefinition.DisplayNameText} on {block.CubeGrid.DisplayName}"); //Apply damage - if (canDamage) + if (t.DoDamage) { try { - if (Session.IsServer && !appliedImpulse && primaryDamage && hitMass > 0 ) + if (Session.IsServer && !appliedImpulse && primaryDamage && t.AmmoDef.Const.Mass > 0 ) { appliedImpulse = true; var speed = !t.AmmoDef.Const.IsBeamWeapon && t.AmmoDef.Const.DesiredProjectileSpeed > 0 ? t.AmmoDef.Const.DesiredProjectileSpeed : 1; - ApplyProjectileForce(grid, grid.GridIntegerToWorld(rootBlock.Position), hitEnt.Intersection.Direction, (hitMass * speed)); + ApplyProjectileForce(grid, grid.GridIntegerToWorld(rootBlock.Position), hitEnt.Intersection.Direction, (t.AmmoDef.Const.Mass * speed)); } if (!deadBlock || gridBlockCount < 2500) { - block.DoDamage(scaledDamage, damageType, sync, null, attackerId); + block.DoDamage(scaledDamage, damageType, DedicatedServer, null, attackerId); var remainingHp = blockHp - scaledDamage; @@ -792,13 +836,13 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) { uint lastDeformTick; MyCube myCube; - if (deformType == DeformDef.DeformTypes.HitBlock && primaryDamage && (deformDelay == 1 || !_slimLastDeformTick.TryGetValue(block, out lastDeformTick) || Tick - lastDeformTick >= deformDelay) && grid.TryGetCube(block.Position, out myCube)) + if (d.Deform.DeformType == DeformDef.DeformTypes.HitBlock && primaryDamage && (deformDelay == 1 || !_slimLastDeformTick.TryGetValue(block, out lastDeformTick) || Tick - lastDeformTick >= deformDelay) && grid.TryGetCube(block.Position, out myCube)) { grid.ApplyDestructionDeformation(myCube.CubeBlock, 0f, new MyHitInfo(), attackerId); if (deformDelay > 1) _slimLastDeformTick[block] = Tick; } - else if (deformType == DeformDef.DeformTypes.AllDamagedBlocks && (deformDelay == 1 || !_slimLastDeformTick.TryGetValue(block, out lastDeformTick) || Tick - lastDeformTick >= deformDelay) && grid.TryGetCube(block.Position, out myCube)) + else if (d.Deform.DeformType == DeformDef.DeformTypes.AllDamagedBlocks && (deformDelay == 1 || !_slimLastDeformTick.TryGetValue(block, out lastDeformTick) || Tick - lastDeformTick >= deformDelay) && grid.TryGetCube(block.Position, out myCube)) { grid.ApplyDestructionDeformation(myCube.CubeBlock, 0f, new MyHitInfo(), attackerId); if (deformDelay > 1) @@ -899,7 +943,8 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) Vector3 halfExt; rootBlock.ComputeScaledHalfExtents(out halfExt); var blockBox = new BoundingBoxD(-halfExt, halfExt); - gridMatrix.Translation = grid.GridIntegerToWorld(rootBlock.Position); + var gridMatrix = rootBlock.CubeGrid.PositionComp.WorldMatrixRef; + gridMatrix.Translation = rootBlock.CubeGrid.GridIntegerToWorld(rootBlock.Position); obb = new MyOrientedBoundingBoxD(blockBox, gridMatrix); } @@ -947,7 +992,7 @@ private void DamageDestObj(HitEntity hitEnt, ProInfo info) var character = hitEnt.Entity as IMyCharacter; float damageScale = 1; if (info.AmmoDef.Const.VirtualBeams) damageScale *= info.Weapon.WeaponCache.Hits; - if (character != null && info.AmmoDef.DamageScales.Characters > 0) + if (character != null && info.AmmoDef.DamageScales.Characters >= 0) damageScale *= info.AmmoDef.DamageScales.Characters; var areaEffect = info.AmmoDef.AreaOfDamage; @@ -1017,6 +1062,13 @@ private void DamageProjectile(HitEntity hitEnt, ProInfo attacker) scaledDamage *= fallOffMultipler; } + //Rework of projectile on projectile damage calcs, as previously you could end up with a high primary damage projectile + //unintentionally surviving multiple hits and doing nearly infinite damage against other projectiles. This was more apparent + //with smarts that would select and chase a new target. Projectiles with EOL detonations could also pop multiple times without dying. + //If your attacking projectile has a health > 0, it will deduct the HealthHitModifier damage done to the target from its own health. It will die once health hits zero or less + //If your attacking projectile has a health = 0, the HealthHitModifier damage it does will be deducted from the primary damage field. It will die once damage hits zero or less + //In either case, a projectile with EOL will detonate on hitting another projectile and die + var deductFromAttackerHealth = attacker.AmmoDef.Health > 0; if (scaledDamage >= objHp) { @@ -1196,7 +1248,7 @@ private void DamageVoxel(HitEntity hitEnt, ProInfo info, HitEntity.Type type) } - public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, double radius, double depth, LineD direction, ref int maxDbc, out bool foundSomething, AoeShape shape, bool showHits,out int aoeHits) //added depth and angle + public void RadiantAoe(Vector3I rootInfo, MyCubeGrid grid, double radius, double depth, LineD direction, ref int maxDbc, out bool foundSomething, AoeShape shape, bool showHits,out int aoeHits) //added depth and angle { if (depth <= 0) { @@ -1204,11 +1256,16 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl foundSomething = false; return; } - var rootHitPos = rootInfo.QueryPos; //local cube grid + + //Log.Line($"Start"); + //var watch = System.Diagnostics.Stopwatch.StartNew(); + var rootHitPos = rootInfo; //local cube grid var localfrom = grid.WorldToGridScaledLocal(direction.From); var localto = grid.WorldToGridScaledLocal(direction.To); var gridsize = grid.GridSizeR; aoeHits = 0; + + //Log.Line($"Raw rootpos{root.Position} localctr{rootInfo.QueryPos} rootpos {rootPos} localfrom{localfrom} localto{localto} min{root.Min} max{root.Max}"); radius *= gridsize; //GridSizeR is 0.4 for LG, 2.0 for SG depth *= gridsize; var gmin = grid.Min; @@ -1238,8 +1295,12 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl var xhit = (hitray.Intersects(xplane) ?? 0) + (hitray.Intersects(xmplane) ?? 0); var yhit = (hitray.Intersects(yplane) ?? 0) + (hitray.Intersects(ymplane) ?? 0); var zhit = (hitray.Intersects(zplane) ?? 0) + (hitray.Intersects(zmplane) ?? 0); + //Log.Line($"localto{localto} rootpos{rootPos} rootmin{root.Min} rootmax{root.Max}"); + //Log.Line($"xhit {xhit} yhit {yhit} zhit{zhit}"); var axishit = new Vector3D(xhit, yhit, zhit); + // Log.Line($"Hitvec x{hitray.Intersects(xplane)} y{hitray.Intersects(yplane)} xm{hitray.Intersects(xmplane)} ym{hitray.Intersects(ymplane)}"); + switch (axishit.AbsMaxComponent())//sort out which "face" was hit and coming/going along that axis { case 1://hit face perp to y @@ -1350,6 +1411,8 @@ public void RadiantAoe(ref HitEntity.RootBlocks rootInfo, MyCubeGrid grid, doubl } } } + //watch.Stop(); + //Log.Line($"End {watch.ElapsedMilliseconds}"); } public static void GetBlocksInsideSphereFast(MyCubeGrid grid, ref BoundingSphereD sphere, bool checkDestroyed, List blocks) @@ -1440,5 +1503,25 @@ internal bool RayAccuracyCheck(HitEntity hitEnt, IMySlimBlock block) } return false; } + + private bool RayAccuracyCheck(HitEntity hitEnt, IMyCharacter character) + { + var box = character.PositionComp.WorldAABB; + var ray = new RayD(ref hitEnt.Intersection.From, ref hitEnt.Intersection.Direction); + var rayHit = ray.Intersects(box); + if (rayHit != null) + { + var hitPos = hitEnt.Intersection.From + (hitEnt.Intersection.Direction * (rayHit.Value - 0.1f)); + IHitInfo hitInfo; + if (Physics.CastRay(hitPos, hitEnt.Intersection.To, out hitInfo, 15)) + { + var hit = (MyEntity)hitInfo.HitEntity; + var hitPoint = hitInfo.Position + (hitEnt.Intersection.Direction * 0.1f); + var rayHitTarget = box.Contains(hitPoint) != ContainmentType.Disjoint && hit == character; + return rayHitTarget; + } + } + return false; + } } } From 8bd101adbcc8372266bfeba550934f9d8b97b964 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:54:55 -0500 Subject: [PATCH 36/77] Update --- .../CoreSystems/Session/SessionDamageMgr.cs | 57 +------------------ 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 0758a398..2248ccaa 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -41,65 +41,13 @@ internal void ProcessHits() var noDamageProjectile = ammoDef.BaseDamage <= 0; var lastIndex = info.HitList.Count - 1; - //Log.Line($"ProcessHits"); - - //var gridList = new List(); + // if (info.HitList.Count > 1 && info.BlockList.Count > 1) info.BlockList.SortNoAlloc((b, a) => b.Value.CompareTo(a.Value)); - /* - //Sort and ID order of grids being hit, incl duplicates - if (info.BlockList.Count > 2) - for (int i = 0; i < info.BlockList.Count; i++) - { - if (i == 0) - { - gridList.Add(info.BlockList[i].Key.CubeGrid); - continue; - } - if (info.BlockList[i - 1].Key.CubeGrid != info.BlockList[i].Key.CubeGrid) - gridList.Add(info.BlockList[i].Key.CubeGrid); - } - */ - - /* - var pool = Projectiles.HitEntityArrayPool[Environment.CurrentManagedThreadId]; - var hitEntity = pool.Count > 0 ? pool.Pop() : new HitEntity(); - info.HitList.Add(hitEntity); - - if (gridList.Count > 1) - { - var blockListPos = 0; - for (int i = 0; i < info.HitList.Count; i++) - { - var hEnt = info.HitList[i]; - if (hEnt.EventType != HitEntity.Type.Grid) - continue; - var grid = hEnt.Entity as IMyCubeGrid; - int lastHit = 0; - for (int j = 0; j < hEnt.Blocks.Count; j++) - { - if (hEnt.Blocks[j].Block == info.BlockList[blockListPos].Key) - { - lastHit++; - blockListPos++; - } - } - - hEnt.Blocks.RemoveRange(lastHit, hEnt.Blocks.Count - lastHit); - Log.Line($"Last hit on {grid.DisplayName}: {lastHit}"); - break; - } - } - - //Logging readout only - Log.Line("Grids hit:"); - for (int i = 0; i < gridList.Count; i++) - Log.Line($"{gridList[i].DisplayName}"); - */ - Log.Line("Blocks hit:"); for (int i = 0; i < info.BlockList.Count; i++) Log.Line($"Blk: {info.BlockList[i].Key.BlockDefinition.DisplayNameText} {info.BlockList[i].Value} {info.BlockList[i].Key.CubeGrid.DisplayName}"); + //TODO- stop repeats by flushing BlockList after first grid proc or remove other grid hitEnts // @@ -961,6 +909,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) t.BaseDamagePool = basePool; hitEnt.Blocks.Clear(); + t.BlockList.Clear(); } From b40876b8243a3f4b9787de1cc65bb8e3a528d6b3 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:06:36 -0500 Subject: [PATCH 37/77] Ready? --- .../Coreparts/Definitions/AmmoTypes.cs | 6 ++-- .../CoreSystems/Projectiles/ProjectileHits.cs | 26 ++++++++++++++ .../CoreSystems/Session/SessionDamageMgr.cs | 35 ++----------------- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs index 47c6ea83..6807ac20 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs @@ -911,15 +911,15 @@ partial class Parts AmmoRound = "Small Railgun Slug", // Name of ammo in terminal, should be different for each ammo type used by the same weapon. Is used by Shrapnel. HybridRound = true, // Use both a physical ammo magazine and energy per shot. EnergyCost = 0.081f, // Scaler for energy per shot (EnergyCost * BaseDamage * (RateOfFire / 3600) * BarrelsPerShot * TrajectilesPerBarrel). Uses EffectStrength instead of BaseDamage if EWAR. - BaseDamage = 600f, // Direct damage; one steel plate is worth 100. - Mass = 0, //TEMP // In kilograms; how much force the impact will apply to the target. + BaseDamage = 8000f, // Direct damage; one steel plate is worth 100. + Mass = 25f, // In kilograms; how much force the impact will apply to the target. BackKickForce = 30000f, // Recoil. This is applied to the Parent Grid. HardPointUsable = true, // Whether this is a primary ammo type fired directly by the turret. Set to false if this is a shrapnel ammoType and you don't want the turret to be able to select it directly. NoGridOrArmorScaling = true, Trajectory = new TrajectoryDef { MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - DesiredSpeed = 10000, //TEMP was 1000// voxel phasing if you go above 5100 + DesiredSpeed = 1000, //TEMP was 1000// voxel phasing if you go above 5100 MaxTrajectory = 1400f, // Max Distance the projectile or beam can Travel. }, AmmoGraphics = new GraphicDef diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index e4208b59..97cbae10 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -731,7 +731,33 @@ internal bool GenerateHitInfo(Projectile p) Session.I.TargetUi.SetHit(info); } + if (info.HitList.Count > 1 && info.BlockList.Count > 1) + { + //Sort list of blocks hit by dist in KVP + info.BlockList.SortNoAlloc((b, a) => b.Value.CompareTo(a.Value)); + /* + //Determine order of grids hit, incl repeats + var gridList = new List(); + for (int i = 0; i < info.BlockList.Count; i++) + { + if (i == 0) + { + gridList.Add(info.BlockList[i].Key.CubeGrid); + continue; + } + if (info.BlockList[i - 1].Key.CubeGrid != info.BlockList[i].Key.CubeGrid) + gridList.Add(info.BlockList[i].Key.CubeGrid); + } + //Logging readout only + Log.Line("Grids hit:"); + for (int i = 0; i < gridList.Count; i++) + Log.Line($"{gridList[i].DisplayName}"); + Log.Line("Blocks hit:"); + for (int i = 0; i < info.BlockList.Count; i++) + Log.Line($"Blk: {info.BlockList[i].Key.BlockDefinition.DisplayNameText} {info.BlockList[i].Value} {info.BlockList[i].Key.CubeGrid.DisplayName}"); + */ + } return true; } diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 2248ccaa..50024485 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -41,16 +41,6 @@ internal void ProcessHits() var noDamageProjectile = ammoDef.BaseDamage <= 0; var lastIndex = info.HitList.Count - 1; - // - if (info.HitList.Count > 1 && info.BlockList.Count > 1) - info.BlockList.SortNoAlloc((b, a) => b.Value.CompareTo(a.Value)); - Log.Line("Blocks hit:"); - for (int i = 0; i < info.BlockList.Count; i++) - Log.Line($"Blk: {info.BlockList[i].Key.BlockDefinition.DisplayNameText} {info.BlockList[i].Value} {info.BlockList[i].Key.CubeGrid.DisplayName}"); - //TODO- stop repeats by flushing BlockList after first grid proc or remove other grid hitEnts - // - - if (!info.DoDamage && IsServer) info.BaseDamagePool = 0; @@ -368,7 +358,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) return; } - if (t.AmmoDef.DamageScales.Shields.Type == ShieldDef.ShieldType.Heal || (!t.AmmoDef.Const.SelfDamage && !t.AmmoDef.Const.IsCriticalReaction && !t.Storage.SmartReady) && t.Ai.AiType == Ai.AiTypes.Grid && t.Ai.GridEntity.IsInSameLogicalGroupAs(hitEnt.Blocks[0].Block.CubeGrid) ) + if (t.AmmoDef.DamageScales.Shields.Type == ShieldDef.ShieldType.Heal || (!t.AmmoDef.Const.SelfDamage && !t.AmmoDef.Const.IsCriticalReaction && !t.Storage.SmartReady) && t.Ai.AiType == Ai.AiTypes.Grid && t.Ai.GridEntity.IsInSameLogicalGroupAs(hitEnt.Blocks[0].Block.CubeGrid)) { t.BaseDamagePool = 0; return; @@ -431,6 +421,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) basePool = 0; //var rootInfo = hitEnt.Blocks[i]; + //rootBlock = rootInfo.Block; rootBlock = t.BlockList[i].Key; //Grid specific @@ -760,7 +751,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) } } - Log.Line($"Damaged {block.BlockDefinition.DisplayNameText} on {block.CubeGrid.DisplayName}"); + //Log.Line($"Damaged {block.BlockDefinition.DisplayNameText} on {block.CubeGrid.DisplayName}"); //Apply damage if (t.DoDamage) @@ -1452,25 +1443,5 @@ internal bool RayAccuracyCheck(HitEntity hitEnt, IMySlimBlock block) } return false; } - - private bool RayAccuracyCheck(HitEntity hitEnt, IMyCharacter character) - { - var box = character.PositionComp.WorldAABB; - var ray = new RayD(ref hitEnt.Intersection.From, ref hitEnt.Intersection.Direction); - var rayHit = ray.Intersects(box); - if (rayHit != null) - { - var hitPos = hitEnt.Intersection.From + (hitEnt.Intersection.Direction * (rayHit.Value - 0.1f)); - IHitInfo hitInfo; - if (Physics.CastRay(hitPos, hitEnt.Intersection.To, out hitInfo, 15)) - { - var hit = (MyEntity)hitInfo.HitEntity; - var hitPoint = hitInfo.Position + (hitEnt.Intersection.Direction * 0.1f); - var rayHitTarget = box.Contains(hitPoint) != ContainmentType.Disjoint && hit == character; - return rayHitTarget; - } - } - return false; - } } } From 5e98c6b6384ef8c5f18339187806875cf81c7009 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:08:00 -0500 Subject: [PATCH 38/77] Tidy --- Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs | 2 +- Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs index 6807ac20..3fafc203 100644 --- a/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs +++ b/Data/Scripts/CoreSystems/Coreparts/Definitions/AmmoTypes.cs @@ -919,7 +919,7 @@ partial class Parts Trajectory = new TrajectoryDef { MaxLifeTime = 300, // 0 is disabled, Measured in game ticks (6 = 100ms, 60 = 1 seconds, etc..). time begins at 0 and time must EXCEED this value to trigger "time > maxValue". Please have a value for this, It stops Bad things. - DesiredSpeed = 1000, //TEMP was 1000// voxel phasing if you go above 5100 + DesiredSpeed = 1000, // voxel phasing if you go above 5100 MaxTrajectory = 1400f, // Max Distance the projectile or beam can Travel. }, AmmoGraphics = new GraphicDef diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 50024485..4d5350a9 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using CoreSystems.Projectiles; using CoreSystems.Support; using Sandbox.Definitions; From 9022c3747102b7b42b71849e3ed9d54ce21c39c7 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:02:58 -0500 Subject: [PATCH 39/77] Comm slave focusfire prohibition --- .../EntityComp/Controls/CreateCustomActions.cs | 2 +- .../EntityComp/Controls/TerminalHelpers.cs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index 6f96a3ca..92e9c3b3 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -433,7 +433,7 @@ public static void CreateFocusTargets(Session session) action.Name = new StringBuilder(Localization.GetText("ActionFocusTargets")); action.Action = CustomActions.TerminalActionToggleFocusTargets; action.Writer = CustomActions.FocusTargetsWriter; - action.Enabled = TerminalHelpers.HasTracking; + action.Enabled = TerminalHelpers.HasTrackingExceptCommSlave; action.ValidForGroups = true; MyAPIGateway.TerminalControls.AddAction(action); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 431cca51..9d34f34b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -40,7 +40,7 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I AddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystems, BlockUi.RequestSetSubSystems, true, HasTracking); AddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystem, BlockUi.RequestSubSystem, BlockUi.ListSubSystems, HasTracking); - AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTracking); + AddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFire, BlockUi.RequestSetFocusFire, true, HasTrackingExceptCommSlave); AddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepel, BlockUi.RequestSetRepel, true, HasTracking); AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); @@ -440,6 +440,17 @@ internal static bool HasTracking(IMyTerminalBlock block) return (comp.HasTracking || comp.HasGuidance) && !comp.HasAlternateUi; } + internal static bool HasTrackingExceptCommSlave(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + + var valid = comp != null && comp.Platform.State == CorePlatform.PlatformState.Ready && comp.Data?.Repo != null; + + if (!valid || Session.I.PlayerId != comp.Data.Repo.Values.State.PlayerId && !comp.TakeOwnerShip()) + return false; + + return !comp.PrimaryWeapon.System.TargetSlaving && (comp.HasTracking || comp.HasGuidance) && !comp.HasAlternateUi; + } internal static bool HasTrackingUnowned(IMyTerminalBlock block) { From 59534389733ab999002b5a2b6f3fab5745ea6f96 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 27 Apr 2025 03:33:01 -0500 Subject: [PATCH 40/77] interior turret BP ammo compat --- .../EntityComp/Parts/Weapon/WeaponData.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 69b10782..9c40b83b 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -138,6 +138,20 @@ internal void Load() if (Comp.TypeSpecific == CoreComponent.CompTypeSpecific.Rifle) Comp.AmmoStorage(true); + //Interior turret shenanigans + if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName == "LargeInteriorTurret") + { + var wep = collection[0]; + foreach (var item in wep.BaseComp.CoreInventory.GetItems()) + { + if (wep.System.AmmoTypes[wep.Reload.AmmoTypeId].AmmoDef.AmmoMagazine == item.Content.SubtypeName) + break; + else if (item.Content.SubtypeName == "NATO_5p56x45mm") + wep.ProposedAmmoId = 1; + else if (item.Content.SubtypeName == "RapidFireAutomaticRifleGun_Mag_50rd") + wep.ProposedAmmoId = 0; + } + } } internal void Change(DataState state) From 5f8b7ba97217e2f87957fa336bb698daf846ccff Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 27 Apr 2025 13:09:28 -0500 Subject: [PATCH 41/77] Hud Updates --- .../CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../EntityComp/Parts/Weapon/WeaponData.cs | 2 +- .../EntityComp/Parts/Weapon/WeaponFields.cs | 3 -- .../EntityComp/Parts/Weapon/WeaponState.cs | 30 ++------------ .../CoreSystems/Support/Localization.cs | 3 ++ Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs | 41 ++++++++++--------- Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs | 2 +- 7 files changed, 30 insertions(+), 53 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 8bc04864..b65ebefe 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -250,7 +250,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str stringBuilder.Append($"\n\n" + w.System.PartName + shots + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : w.Target.CurrentState == Target.States.NotSet ? Localization.GetText("WeaponTargNeedSelection") : w.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < w.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < w.MinTargetDistanceSqr) ? Localization.GetText("WeaponTargTooClose") : w.BaseComp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargRange") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 9c40b83b..5da69938 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -139,7 +139,7 @@ internal void Load() Comp.AmmoStorage(true); //Interior turret shenanigans - if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName == "LargeInteriorTurret") + if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName.Contains("LargeInteriorTurret")) { var wep = collection[0]; foreach (var item in wep.BaseComp.CoreInventory.GetItems()) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index e8c795f4..c7ee45ed 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -114,9 +114,6 @@ public partial class Weapon : Part internal Matrix[] BarrelRotationPerShot = new Matrix[10]; internal string FriendlyName = string.Empty; - internal string FriendlyNameNoAmmo = string.Empty; - internal string FriendlyNameNoTarget = string.Empty; - internal string FriendlyNameNoSubsystem = string.Empty; internal string AmmoName = ""; internal string AmmoNameTerminal = ""; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs index ed267df3..d3e884b0 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponState.cs @@ -460,15 +460,7 @@ internal Vector3D PbRandomizePredictedPosition(Vector3D predictedPos) return predictedPos + _cachedPredictedOffset; } - internal enum FriendlyNames - { - Normal, - NoAmmo, - NoSubSystems, - NoTarget, - } - - internal string UpdateAndGetFriendlyName(FriendlyNames type) + internal void UpdateAndGetFriendlyName() { string weaponName; @@ -481,28 +473,12 @@ internal string UpdateAndGetFriendlyName(FriendlyNames type) update = !weaponName.Equals(FriendlyName); } else - { weaponName = System.ShortName; - } if (update) { - FriendlyName = weaponName; - FriendlyNameNoTarget = weaponName + Hud.NoTargetStr; - FriendlyNameNoAmmo = weaponName + Hud.NoAmmoStr; - FriendlyNameNoSubsystem = weaponName + Hud.NoSubSystemStr; - } - - switch (type) - { - case FriendlyNames.NoAmmo: - return FriendlyNameNoAmmo; - case FriendlyNames.NoTarget: - return FriendlyNameNoTarget; - case FriendlyNames.NoSubSystems: - return FriendlyNameNoSubsystem; - default: - return FriendlyName; + var nameLen = weaponName.Length; + FriendlyName = nameLen > 30 ? weaponName.Remove(30, nameLen - 30) : weaponName; } } } diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 2c94d628..23b662c8 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -239,6 +239,9 @@ public static class Localization { "MoveShipAny", "Ships" }, { "WeaponTargTrue", "True" }, { "WeaponTargFalse", "False" }, + { "WeaponTargRange", "Not in range" }, + { "WeaponTargNeedSelection", "Manually select target" }, + { "WeaponTargTooClose", "Too close" }, } }, { diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs index 30433c7a..cde3fb2a 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs @@ -224,10 +224,12 @@ private void BackgroundAdd(Vector2D currWeaponDisplayPos, double bgStartPosX) _textureAddList.Add(backgroundTexture); } - public const string EmptyStr = ""; - public const string NoAmmoStr = ": No Ammo"; - public const string NoTargetStr = ": No Target"; - public const string NoSubSystemStr = ": No Subsystem"; + public const string NoAmmoStr = ": No Ammo"; + public const string NoSubSystemStr = ": No Subsys"; + public const string RotatingStr = ": Rotating"; + public const string NotInRangeStr = ": Too Far"; + public const string InsideMinRangeStr = ": Too Close"; + public const string NoTargetSetStr = ": Pick Targ"; private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgStartPosX) { @@ -246,31 +248,30 @@ private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgSt var notAnyBlock = comp.MasterOverrides.SubSystem != WeaponDefinition.TargetingDef.BlockTypes.Any; var needsTarget = (!weapon.Target.HasTarget || Session.I.Tick - weapon.Target.ChangeTick <= 30) && comp.MasterOverrides.Grids && (comp.DetectOtherSignals && comp.MasterAi.DetectionInfo.OtherInRange || comp.MasterAi.DetectionInfo.PriorityInRange) && report && comp.Data.Repo.Values.Set.ReportTarget && delayNoTarget && comp.MasterAi.DetectionInfo.TargetInRange(weapon); var showReloadIcon = (weapon.Loading || weapon.Reload.WaitForClient || s.Tick - weapon.LastLoadedTick < 60); - - string noTagetReason; - var needNameUpdate = weapon.LastFriendlyNameTick == 0 || s.Tick - weapon.LastFriendlyNameTick > 600; + var displayText = weapon.FriendlyName; + if (weapon.LastFriendlyNameTick == 0 || s.Tick - weapon.LastFriendlyNameTick > 600) + weapon.UpdateAndGetFriendlyName(); if (needsTarget) { if (weapon.OutOfAmmo && !showReloadIcon) - noTagetReason = needNameUpdate ? weapon.UpdateAndGetFriendlyName(Weapon.FriendlyNames.NoAmmo) : weapon.FriendlyNameNoAmmo; - + displayText += NoAmmoStr; + else if (weapon.Target.CurrentState == Target.States.NotSet) + displayText += NoTargetSetStr; else if (comp.MasterOverrides.FocusSubSystem && !showReloadIcon && notAnyBlock && weapon.FoundTopMostTarget) - noTagetReason = needNameUpdate ? weapon.UpdateAndGetFriendlyName(Weapon.FriendlyNames.NoSubSystems) : weapon.FriendlyNameNoSubsystem; - - else - noTagetReason = needNameUpdate ? weapon.UpdateAndGetFriendlyName(Weapon.FriendlyNames.NoTarget) : weapon.FriendlyNameNoTarget; - } - else - { - noTagetReason = needNameUpdate ? weapon.UpdateAndGetFriendlyName(Weapon.FriendlyNames.Normal) : weapon.FriendlyName; + displayText += NoSubSystemStr; + else if (weapon.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < weapon.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < weapon.MinTargetDistanceSqr)) + displayText += InsideMinRangeStr; + else + displayText += NotInRangeStr; } - + else if (weapon.Comp.HasTurret && !weapon.Target.IsAligned) + displayText += RotatingStr; var textOffset = bgStartPosX - _bgWidth + _reloadWidth + _padding; var hasHeat = weapon.HeatPerc > 0; var textInfo = _textDrawPool.Count > 0 ? _textDrawPool.Dequeue() : new TextDrawRequest(); - textInfo.Text = noTagetReason; + textInfo.Text = displayText; var color = new Vector4(1, 1, 1, 1); textInfo.Color = color; textInfo.Position.X = textOffset; @@ -285,7 +286,7 @@ private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgSt textInfo.Text = $"(x{stackedInfo.WeaponStack})"; textInfo.Color = new Vector4(0.5f, 0.5f, 1, 1); - textInfo.Position.X = textOffset + (noTagetReason.Length * ((_textSize * s.AspectRatioInv) * 0.6f) * ShadowSizeScaler); + textInfo.Position.X = textOffset + (displayText.Length * ((_textSize * s.AspectRatioInv) * 0.6f) * ShadowSizeScaler); textInfo.Position.Y = currWeaponDisplayPos.Y; textInfo.FontSize = _sTextSize; diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs index cbec3f88..91d1fa76 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudFields.cs @@ -12,7 +12,7 @@ partial class Hud { private const float MetersInPixel = 0.0002645833f; private const float PaddingConst = 10 * MetersInPixel; - private const float WeaponHudFontSize = 8f; + private const float WeaponHudFontSize = 7f; private const float WeaponHudFontHeight = WeaponHudFontSize * MetersInPixel; private const float ReloadHeightConst = 4f * MetersInPixel; private const float ReloadWidthConst = ReloadHeightConst; From e4a24de57ace9812d4f905693b20fb9c4b8f2606 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 27 Apr 2025 15:28:33 -0500 Subject: [PATCH 42/77] Updates - HUD and terminal status clarifications on weapon status, including why it may not have a target or if alternate ammo is available - Add shield hit particle option to ammo --- Data/Scripts/CoreSystems/AudioVisual/AvShot.cs | 8 ++++++-- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 10 +++++----- .../Scripts/CoreSystems/Definitions/CoreDefinitions.cs | 1 + .../Definitions/SerializedConfigs/AmmoConstants.cs | 5 +++++ Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs | 4 ++-- .../EntityComp/Parts/Weapon/WeaponFields.cs | 1 + Data/Scripts/CoreSystems/Support/Localization.cs | 3 ++- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index c1786877..e5b8da6f 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -58,6 +58,7 @@ public AvShot(Session session) internal bool LastHitShield; internal bool ForceHitParticle; internal bool HitParticleActive; + internal bool ShieldHitParticleActive; internal bool MarkForClose; internal bool ProEnded; internal bool AccelClearance; @@ -127,6 +128,7 @@ internal enum ParticleState { None, Custom, + Shield, Dirty, } @@ -963,8 +965,10 @@ internal void HitEffects(bool force = false) } if (OnScreen == Screen.Tracer || AmmoDef.Const.HitParticleNoCull || distToCameraSqr < 360000) { - if (HitParticleActive && AmmoDef.Const.HitParticle && !(LastHitShield && !AmmoDef.AmmoGraphics.Particles.Hit.ApplyToShield)) - HitParticle = ParticleState.Custom; + if (LastHitShield && ShieldHitParticleActive && AmmoDef.Const.ShieldHitParticle) + HitParticle = ParticleState.Shield; + else if (HitParticleActive && AmmoDef.Const.HitParticle && !(LastHitShield && !AmmoDef.AmmoGraphics.Particles.Hit.ApplyToShield)) + HitParticle = ParticleState.Custom; } diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 96cf2909..0e487d29 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -138,13 +138,12 @@ internal void End() else av.TravelEmitter.SetPosition(av.TracerFront); } - if (av.HitParticle == AvShot.ParticleState.Custom) + if (av.HitParticle == AvShot.ParticleState.Custom || av.HitParticle == AvShot.ParticleState.Shield) { - av.HitParticle = AvShot.ParticleState.Dirty; if (av.OnScreen != AvShot.Screen.None) { 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 particle = av.HitParticle == AvShot.ParticleState.Shield ? av.AmmoDef.AmmoGraphics.Particles.ShieldHit : av.AmmoDef.AmmoGraphics.Particles.Hit; var keenStrikesAgain = particle.Offset == Vector3D.MaxValue; MatrixD matrix = MatrixD.CreateTranslation(pos); if (keenStrikesAgain) @@ -161,15 +160,16 @@ internal void End() } MyParticleEffect hitEffect; - if (MyParticlesManager.TryCreateParticleEffect(av.AmmoDef.Const.HitParticleStr, ref matrix, ref pos, uint.MaxValue, out hitEffect)) + if (MyParticlesManager.TryCreateParticleEffect(av.HitParticle == AvShot.ParticleState.Shield ? av.AmmoDef.Const.ShieldHitParticleStr : av.AmmoDef.Const.HitParticleStr, ref matrix, ref pos, uint.MaxValue, out hitEffect)) { - hitEffect.UserScale = av.AmmoDef.AmmoGraphics.Particles.Hit.Extras.Scale; + hitEffect.UserScale = particle.Extras.Scale; var tickVelo = av.Hit.HitVelocity / 60; HitParticles.Add(new HitParticleEvent(hitEffect, tickVelo)); if (hitEffect.Loop) hitEffect.Stop(); } } + av.HitParticle = AvShot.ParticleState.Dirty; } if (av.Hit.Entity != null && av.AmmoDef.AmmoGraphics.Decals.MaxAge > 0 && !Vector3D.IsZero(av.Hit.SurfaceHit) && av.AmmoDef.Const.TextureHitMap.Count > 0 && !av.Hit.Entity.MarkedForClose && av.Hit.Entity.InScene) { diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index 49e27a19..f626e7de 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -822,6 +822,7 @@ public struct AmmoParticleDef [ProtoMember(2)] internal ParticleDef Hit; [ProtoMember(3)] internal ParticleDef Eject; [ProtoMember(4)] internal ParticleDef WeaponEffect1Override; + [ProtoMember(5)] internal ParticleDef ShieldHit; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index edee4329..57312735 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -77,6 +77,7 @@ public enum Texture public readonly Vector4 LinearTrailColor; public readonly string ModelPath; public readonly string HitParticleStr; + public readonly string ShieldHitParticleStr; public readonly string DetParticleStr; public readonly string DetSoundStr; public readonly string ShotSoundStr; @@ -136,6 +137,7 @@ public enum Texture public readonly bool IsField; public readonly bool AmmoParticle; public readonly bool HitParticle; + public readonly bool ShieldHitParticle; public readonly bool CustomDetParticle; public readonly bool FieldParticle; public readonly bool AmmoSkipAccel; @@ -416,6 +418,9 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon AmmoParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Ammo.Name); HitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); HitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name; + ShieldHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); + ShieldHitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.Name; + EndOfLifeAv = !ammo.AmmoDef.AreaOfDamage.EndOfLife.NoVisuals && ammo.AmmoDef.AreaOfDamage.EndOfLife.Enable; OverrideWeaponEffect = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.WeaponEffect1Override.Name); diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index b65ebefe..9bdead50 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -249,8 +249,8 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str var displayName = showName ? w.ActiveAmmoDef.AmmoDef.Const.TerminalName + " (" + w.ActiveAmmoDef.AmmoDef.Const.MagazineDef.DisplayNameText + ")" : w.ActiveAmmoDef.AmmoDef.Const.TerminalName; stringBuilder.Append($"\n\n" + w.System.PartName + shots + - $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : Localization.GetText("WeaponInfoNoammo")))}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : w.Target.CurrentState == Target.States.NotSet ? Localization.GetText("WeaponTargNeedSelection") : w.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < w.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < w.MinTargetDistanceSqr) ? Localization.GetText("WeaponTargTooClose") : w.BaseComp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargRange") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : w.Comp.CurrentInventoryVolume > 0 ? Localization.GetText("WeaponInfoCheckAmmoType") : Localization.GetText("WeaponInfoNoammo")))}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : w.Target.CurrentState == Target.States.NotSet && w.Comp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargNeedSelection") : w.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < w.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < w.MinTargetDistanceSqr) ? Localization.GetText("WeaponTargTooClose") : w.BaseComp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargRange") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs index c7ee45ed..4c2bad87 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponFields.cs @@ -266,6 +266,7 @@ internal Weapon(MyEntity entity, WeaponSystem system, int partId, WeaponComponen if (c.MustCharge) CanUseChargeAmmo = true; if (c.IsBeamWeapon) CanUseBeams = true; if (c.HitParticle) hitParticle = true; + if (c.ShieldHitParticle) hitParticle = true; } comp.HasEnergyWeapon = comp.HasEnergyWeapon || CanUseEnergyAmmo || CanUseHybridAmmo; diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index 23b662c8..e433149a 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -204,7 +204,7 @@ public static class Localization { "WeaponInfoWaitingCharge", "Waiting on charge" }, { "WeaponInfoLoadedIn", "Loaded in" }, { "WeaponInfoLoaded", "Loaded" }, - { "WeaponInfoNoammo", "No Ammo" }, + { "WeaponInfoNoammo", "No ammo available" }, { "WeaponInfoNoTarget", "No Target" }, { "WeaponInfoHeatPerSecOverMax", "Heat per Sec/Max" }, { "SubtypeAny", "Any" }, @@ -242,6 +242,7 @@ public static class Localization { "WeaponTargRange", "Not in range" }, { "WeaponTargNeedSelection", "Manually select target" }, { "WeaponTargTooClose", "Too close" }, + { "WeaponInfoCheckAmmoType", "No selected ammo, alternatives in inventory" }, } }, { From 462997f00b368ffac71237944825e56742ced362 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 27 Apr 2025 15:28:49 -0500 Subject: [PATCH 43/77] Update --- Data/Scripts/CoreSystems/Projectiles/Projectile.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index 12f05d41..1c1b7d4c 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -308,6 +308,11 @@ internal void Start() var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.Hit.Extras.HitPlayChance; Info.AvShot.HitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); } + if (aConst.ShieldHitParticle && !aConst.IsBeamWeapon || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) + { + var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.ShieldHit.Extras.HitPlayChance; + Info.AvShot.ShieldHitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); + } if (aConst.PrimeModel || aConst.TriggerModel) { From 6c35e944fdcad0bde7f8b7f6c217e1593b5d513d Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 27 Apr 2025 23:11:20 -0500 Subject: [PATCH 44/77] more updates --- .../Controls/Weapon/WeaponActions.cs | 2 ++ .../CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../EntityComp/Parts/Weapon/WeaponData.cs | 21 ++++++++++--------- .../EntityComp/Parts/Weapon/WeaponTracking.cs | 2 ++ .../CoreSystems/Session/SessionUpdate.cs | 2 +- Data/Scripts/CoreSystems/Support/MiscTypes.cs | 1 + Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs | 18 ++++++++++++---- 7 files changed, 32 insertions(+), 16 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index eb2e113a..86ed0197 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -115,6 +115,8 @@ internal static void TerminalActionKeyShoot(IMyTerminalBlock blk) var mode = comp.Data.Repo.Values.Set.Overrides.ShootMode; if (mode == Weapon.ShootManager.ShootModes.KeyToggle || mode == Weapon.ShootManager.ShootModes.KeyFire) { + if (!comp.Data.Repo.Values.Set.Overrides.Override && comp.HasRequireTarget && comp.PrimaryWeapon.Target.CurrentState != Target.States.Acquired) + return; var keyToggle = mode == Weapon.ShootManager.ShootModes.KeyToggle; var signal = keyToggle ? Weapon.ShootManager.Signals.KeyToggle : Weapon.ShootManager.Signals.Once; var on = comp.Data.Repo.Values.State.Trigger == On; diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index 9bdead50..ad23e79c 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -250,7 +250,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str stringBuilder.Append($"\n\n" + w.System.PartName + shots + $" {(w.ActiveAmmoDef.AmmoDef.Const.EnergyAmmo ? string.Empty : $"\n{Localization.GetText("WeaponInfoAmmoLabel")}: " + (w.Loading ? timeToLoad < 0 ? Localization.GetText("WeaponInfoWaitingCharge") : Localization.GetText("WeaponInfoLoadedIn") + " " + timeToLoad + Localization.GetText("WeaponInfoSeconds") : w.ProtoWeaponAmmo.CurrentAmmo > 0 ? Localization.GetText("WeaponInfoLoaded") + " " + w.ProtoWeaponAmmo.CurrentAmmo + "x " + displayName : w.Comp.CurrentInventoryVolume > 0 ? Localization.GetText("WeaponInfoCheckAmmoType") : Localization.GetText("WeaponInfoNoammo")))}" + - $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : w.Target.CurrentState == Target.States.NotSet && w.Comp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargNeedSelection") : w.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < w.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < w.MinTargetDistanceSqr) ? Localization.GetText("WeaponTargTooClose") : w.BaseComp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargRange") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoHasTarget") + ": " + (w.Target.HasTarget ? Localization.GetText("WeaponTargTrue") : w.Comp.MasterAi.DetectionInfo.SomethingInRange && (w.Target.CurrentState == Target.States.NotSet || w.Target.CurrentState == Target.States.Expired) ? Localization.GetText("WeaponTargNeedSelection") : w.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < w.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < w.MinTargetDistanceSqr) ? Localization.GetText("WeaponTargTooClose") : w.BaseComp.MasterAi.DetectionInfo.SomethingInRange ? Localization.GetText("WeaponTargRange") : Localization.GetText("WeaponTargFalse")) : string.Empty)}" + $" {(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget ? "\n" + Localization.GetText("WeaponInfoLoS") + ": " + (w.Target.HasTarget ? "" + !w.PauseShoot : Localization.GetText("WeaponInfoNoTarget")) : string.Empty)}" + endReturn); } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 5da69938..630356f3 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -139,18 +139,19 @@ internal void Load() Comp.AmmoStorage(true); //Interior turret shenanigans - if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName.Contains("LargeInteriorTurret")) + if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName == "LargeInteriorTurret") //"NPCLargeInteriorTurret" { var wep = collection[0]; - foreach (var item in wep.BaseComp.CoreInventory.GetItems()) - { - if (wep.System.AmmoTypes[wep.Reload.AmmoTypeId].AmmoDef.AmmoMagazine == item.Content.SubtypeName) - break; - else if (item.Content.SubtypeName == "NATO_5p56x45mm") - wep.ProposedAmmoId = 1; - else if (item.Content.SubtypeName == "RapidFireAutomaticRifleGun_Mag_50rd") - wep.ProposedAmmoId = 0; - } + if (wep.System.AmmoTypes.Length == 2) + foreach (var item in wep.BaseComp.CoreInventory.GetItems()) + { + if (wep.System.AmmoTypes[wep.Reload.AmmoTypeId].AmmoDef.AmmoMagazine == item.Content.SubtypeName) + break; + else if (item.Content.SubtypeName == "NATO_5p56x45mm") + wep.ProposedAmmoId = 1; + else if (item.Content.SubtypeName == "RapidFireAutomaticRifleGun_Mag_50rd") + wep.ProposedAmmoId = 0; + } } } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index e4020a86..213c111d 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -159,6 +159,7 @@ internal static void LeadTarget(Weapon weapon, MyEntity target, out Vector3D tar canTrack = IsDotProductWithinTolerance(ref weapon.MyPivotFwd, ref targetDir, weapon.AimingTolerance); } willHit = canTrack; + weapon.Target.ValidEstimate = willHit; } internal static bool CanShootTargetObb(Weapon weapon, MyEntity entity, Vector3D targetLinVel, Vector3D targetAccel, out Vector3D targetPos) @@ -356,6 +357,7 @@ internal static bool TrackingTarget(Weapon w, Target target, out bool targetLock if (Vector3D.IsZero(targetAccel, 5E-03)) targetAccel = Vector3.Zero; targetPos = TrajectoryEstimation(w, targetCenter, targetLinVel, targetAccel, w.MyPivotPos, out validEstimate, false, baseData.Set.Overrides.AngularTracking); + w.Target.ValidEstimate = validEstimate; } else targetPos = targetCenter; diff --git a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs index b4a04d84..ecd194a3 100644 --- a/Data/Scripts/CoreSystems/Session/SessionUpdate.cs +++ b/Data/Scripts/CoreSystems/Session/SessionUpdate.cs @@ -618,7 +618,7 @@ private void AiLoop() /// /// Update Weapon Hud Info /// - var addWeaponToHud = HandlesInput && !w.System.DisableStatus && (w.HeatPerc >= 0.01 || (w.ShowReload && (w.Loading || w.Reload.WaitForClient)) || ((aConst.CanReportTargetStatus || ai.ControlComp != null) && wValues.Set.ReportTarget && !w.Target.HasTarget && grids && (wComp.DetectOtherSignals && ai.DetectionInfo.OtherInRange || ai.DetectionInfo.PriorityInRange) && ai.DetectionInfo.TargetInRange(w))); + var addWeaponToHud = HandlesInput && !w.System.DisableStatus && (w.HeatPerc >= 0.01 || (w.ShowReload && (w.Loading || w.Reload.WaitForClient)) || ((aConst.CanReportTargetStatus || ai.ControlComp != null) && wValues.Set.ReportTarget && (!w.Target.HasTarget || w.Target.TargetState == TargetStates.IsFake) && grids && (wComp.DetectOtherSignals && ai.DetectionInfo.OtherInRange || ai.DetectionInfo.PriorityInRange) && ai.DetectionInfo.TargetInRange(w))); if (addWeaponToHud && !Session.Config.MinimalHud && !enforcement.DisableHudReload && !Settings.ClientConfig.HideReload && (ActiveControlBlock != null && ai.SubGridCache.Contains(ActiveControlBlock.CubeGrid) || PlayerHandWeapon != null && IdToCompMap.ContainsKey(((IMyGunBaseUser)PlayerHandWeapon).OwnerId))) { diff --git a/Data/Scripts/CoreSystems/Support/MiscTypes.cs b/Data/Scripts/CoreSystems/Support/MiscTypes.cs index f0f46fbb..7d3db142 100644 --- a/Data/Scripts/CoreSystems/Support/MiscTypes.cs +++ b/Data/Scripts/CoreSystems/Support/MiscTypes.cs @@ -22,6 +22,7 @@ public class Target internal bool TargetChanged; internal bool ClientDirty; internal bool IsDrone; + internal bool ValidEstimate; internal uint ChangeTick; internal uint ProjectileEndTick; internal long TargetId; diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs index cde3fb2a..18fe736a 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs @@ -230,6 +230,8 @@ private void BackgroundAdd(Vector2D currWeaponDisplayPos, double bgStartPosX) public const string NotInRangeStr = ": Too Far"; public const string InsideMinRangeStr = ": Too Close"; public const string NoTargetSetStr = ": Pick Targ"; + public const string FakeOnTarg = ": Aligned"; + public const string FakeCannotHit = ": Cannot hit"; private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgStartPosX) { @@ -255,18 +257,26 @@ private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgSt { if (weapon.OutOfAmmo && !showReloadIcon) displayText += NoAmmoStr; - else if (weapon.Target.CurrentState == Target.States.NotSet) + else if (comp.MasterOverrides.Control != ProtoWeaponOverrides.ControlModes.Manual && (weapon.Target.CurrentState == Target.States.NotSet || weapon.Target.CurrentState == Target.States.Expired)) displayText += NoTargetSetStr; else if (comp.MasterOverrides.FocusSubSystem && !showReloadIcon && notAnyBlock && weapon.FoundTopMostTarget) displayText += NoSubSystemStr; else if (weapon.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < weapon.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < weapon.MinTargetDistanceSqr)) displayText += InsideMinRangeStr; - else + else if (comp.MasterOverrides.Control != ProtoWeaponOverrides.ControlModes.Manual) displayText += NotInRangeStr; } - else if (weapon.Comp.HasTurret && !weapon.Target.IsAligned) + else if (weapon.Comp.HasTurret && !weapon.Target.IsAligned && weapon.Target.ValidEstimate) displayText += RotatingStr; - var textOffset = bgStartPosX - _bgWidth + _reloadWidth + _padding; + else if (weapon.Comp.HasTurret && weapon.Target.CurrentState == Target.States.Fake) + { + if (weapon.Target.IsAligned) + displayText += FakeOnTarg; + else if (!weapon.Target.ValidEstimate) + displayText += FakeCannotHit; + } + + var textOffset = bgStartPosX - _bgWidth + _reloadWidth + _padding; var hasHeat = weapon.HeatPerc > 0; var textInfo = _textDrawPool.Count > 0 ? _textDrawPool.Dequeue() : new TextDrawRequest(); From 6d957c577a39e4454287f6a857127ae4bafa3b10 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 28 Apr 2025 07:50:16 -0500 Subject: [PATCH 45/77] bugfixes --- .../CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs | 4 +--- Data/Scripts/CoreSystems/Ui/Targeting/TargetUiDraw.cs | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs index 630356f3..09846e84 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponData.cs @@ -137,9 +137,7 @@ internal void Load() ProtoRepoBase = Repo; if (Comp.TypeSpecific == CoreComponent.CompTypeSpecific.Rifle) Comp.AmmoStorage(true); - - //Interior turret shenanigans - if (Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName == "LargeInteriorTurret") //"NPCLargeInteriorTurret" + else if (Comp.TypeSpecific != CoreComponent.CompTypeSpecific.Phantom && Comp.Cube != null && Session.I.IsServer && !Session.I.IsCreative && Comp.Cube.BlockDefinition.Id.SubtypeName == "LargeInteriorTurret") //"NPCLargeInteriorTurret" { var wep = collection[0]; if (wep.System.AmmoTypes.Length == 2) diff --git a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiDraw.cs b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiDraw.cs index be816035..82be7162 100644 --- a/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiDraw.cs +++ b/Data/Scripts/CoreSystems/Ui/Targeting/TargetUiDraw.cs @@ -551,11 +551,12 @@ private bool CheckBlockWeaponEntityHit(MyEntity ent, ProInfo info) private void CheckHandWeaponEntityHit(ProInfo info) { + if (Session.I.TrackingAi != null && info.Weapon?.Comp?.Ai?.Construct?.RootAi != Session.I.TrackingAi) + return; foreach (var hitEnt in info.HitList) { - if (info.Weapon.Comp.Ai.Construct.RootAi != Session.I.TrackingAi || hitEnt.Entity is MyVoxelBase) + if (hitEnt.Entity is MyVoxelBase) continue; - HandHitIncrease = HandFullPulseSize - HandCircleSize; HandHitMarkerActive = true; return; From a3b1b63dbd0ec84a13f0b517b51edbdec5680982 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:25:55 -0500 Subject: [PATCH 46/77] Sphere fix --- .../CoreSystems/Projectiles/ProjectileHits.cs | 18 ++++-- .../CoreSystems/Session/SessionSupport.cs | 63 +++++++++---------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 97cbae10..d95f9fa4 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -831,6 +831,19 @@ internal int GetEntityCompareDist(HitEntity x, HitEntity y, ProInfo info) if (hitEnt.SelfHit && (Vector3D.DistanceSquared(hitPos, hitEnt.Info.Origin) <= grid.GridSize * grid.GridSize) && hitEnt.EventType != Field) continue; + IMySlimBlock lastBlockHit = null; + var ewarWeaponDamage = info.EwarActive && aConst.SelfDamage && hitEnt.EventType == Effect; + for (int j = 0; j < hitEnt.Vector3ICache.Count; j++) + { + var posI = hitEnt.Vector3ICache[j]; + var firstBlock = grid.GetCubeBlock(posI) as IMySlimBlock; + if (firstBlock != null && firstBlock != lastBlockHit && !firstBlock.IsDestroyed && (hitEnt.Info.Ai.AiType != Ai.AiTypes.Grid || firstBlock != hitEnt.Info.Weapon.Comp.Cube?.SlimBlock || ewarWeaponDamage && firstBlock == hitEnt.Info.Weapon.Comp.Cube?.SlimBlock)) + { + var blockDist = Vector3D.DistanceSquared(grid.GridIntegerToWorld(posI), beam.From); + info.BlockList.Add(new KeyValuePair(firstBlock, blockDist)); + } + } + if (!ewarActive) GetAndSortBlocksInSphere(hitEnt.Info.AmmoDef, hitEnt.Info.Weapon.System, grid, hitEnt.PruneSphere, false, hitEnt.Blocks); @@ -856,7 +869,6 @@ internal int GetEntityCompareDist(HitEntity x, HitEntity y, ProInfo info) { lastBlockHit = firstBlock; hitEnt.Blocks.Add(new HitEntity.RootBlocks {Block = firstBlock, QueryPos = posI}); - // var blockDist = Vector3D.DistanceSquared(grid.GridIntegerToWorld(posI), beam.From); info.BlockList.Add(new KeyValuePair(firstBlock, blockDist)); @@ -1107,8 +1119,6 @@ internal static void GetAndSortBlocksInSphere(WeaponDefinition.AmmoDef ammoDef, } else { - //usage: - //var dict = (Dictionary)GetHackDict((IMySlimBlock) null); var tmpList = Session.I.SlimPool.Get(); Session.GetBlocksInsideSphereFast(grid, ref sphere, true, tmpList); @@ -1128,7 +1138,5 @@ internal static void GetAndSortBlocksInSphere(WeaponDefinition.AmmoDef ammoDef, return Vector3D.DistanceSquared(aPos, hitPos).CompareTo(Vector3D.DistanceSquared(bPos, hitPos)); }); } - public static object GetHackDict(TVal valueType) => new Dictionary(); - } } diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index b64a5d84..88d71f66 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -825,52 +825,47 @@ internal void RemoveIncompatibleBlock(object o) try { var cube = o as MyCubeBlock; - if (cube != null) + if (cube != null && !cube.MarkedForClose && !cube.Closed && cube.SlimBlock != null && cube.BlockDefinition != null && cube.CubeGrid != null && !cube.CubeGrid.IsPreview && cube.CubeGrid.Physics != null && !cube.CubeGrid.MarkedForClose) { - var processCube = !cube.MarkedForClose && !cube.Closed && cube.SlimBlock != null && cube.BlockDefinition != null && cube.CubeGrid != null && !cube.CubeGrid.IsPreview && cube.CubeGrid.Physics != null && !cube.CubeGrid.MarkedForClose; - if (processCube) - { - if (BrokenMod(cube)) - return; + if (BrokenMod(cube)) + return; - if (_lastIncompatibleMessageTick == uint.MaxValue || Tick - _lastIncompatibleMessageTick > 600) - { - _lastIncompatibleMessageTick = Tick; - var skipMessage = IsClient && Settings.Enforcement.UnsupportedMode && Tick > 600; - if (!skipMessage) - FutureEvents.Schedule(ReportIncompatibleBlocks, null, 10); - } + if (_lastIncompatibleMessageTick == uint.MaxValue || Tick - _lastIncompatibleMessageTick > 600) + { + _lastIncompatibleMessageTick = Tick; + var skipMessage = IsClient && Settings.Enforcement.UnsupportedMode && Tick > 600; + if (!skipMessage) + FutureEvents.Schedule(ReportIncompatibleBlocks, null, 10); + } - if (cube.BlockDefinition?.Id.SubtypeName != null) - _unsupportedBlockNames.Add(cube.BlockDefinition.Id.SubtypeName); + if (cube.BlockDefinition?.Id.SubtypeName != null) + _unsupportedBlockNames.Add(cube.BlockDefinition.Id.SubtypeName); - if (DedicatedServer) - { - if (!Settings.Enforcement.UnsupportedMode) - cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); - } - else if (!Settings.Enforcement.UnsupportedMode) - { - if (!_removeComplete) - _unsupportedBlocks.Add(cube); + if (DedicatedServer) + { + if (!Settings.Enforcement.UnsupportedMode) + cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); + } + else if (!Settings.Enforcement.UnsupportedMode) + { + if (!_removeComplete) + _unsupportedBlocks.Add(cube); - if (_removeComplete || DedicatedServer) - cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); + if (_removeComplete || DedicatedServer) + cube.CubeGrid.RemoveBlock(cube.SlimBlock, true); - if (!_removeScheduled) - { - _removeScheduled = true; - FutureEvents.Schedule(RemoveIncompatibleBlocks, null, 3600); - } + if (!_removeScheduled) + { + _removeScheduled = true; + FutureEvents.Schedule(RemoveIncompatibleBlocks, null, 3600); } } } } catch (Exception ex) { - var errorMsg = $"WC exception in RemoveIncompatibleBlock. Obj null: {o == null} Settings null: {Settings?.Enforcement == null} Unsupported mode: {Settings?.Enforcement.UnsupportedMode} \n{ex}"; - Log.Line(errorMsg); - MyLog.Default.WriteLineAndConsole(errorMsg); + var errorMsg = $"WC exception in RemoveIncompatibleBlock. Settings.Enforcement null: {Settings?.Enforcement == null} Unsupported mode: {Settings?.Enforcement?.UnsupportedMode} \n{ex}"; + MyLog.Default.Error(errorMsg); throw ex; } } From c1b4afef88b0c7e284b606a2bb70499e0c415675 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:24:00 -0500 Subject: [PATCH 47/77] beam updates --- .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 36 ++++++++++--------- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 10 ++++-- .../SerializedConfigs/AmmoConstants.cs | 2 +- .../CoreSystems/Projectiles/Projectile.cs | 4 +-- .../CoreSystems/Session/SessionDamageMgr.cs | 28 +++++++-------- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index e5b8da6f..f1b8f8e1 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -399,19 +399,23 @@ internal static void DeferedAvStateUpdates() { if (a.Tracer == TracerState.Shrink && !a.ShrinkInited) a.Shrink(); - else if (aConst.IsBeamWeapon && aConst.HitParticle && !(a.MuzzleId != 0 && (aConst.ConvergeBeams || aConst.OneHitParticle))) + else if (aConst.IsBeamWeapon && !(a.MuzzleId != 0 && (aConst.ConvergeBeams || aConst.OneHitParticle))) { - MyParticleEffect effect; - if (a.Hitting) + var shieldHit = a.Hit.EventType == HitEntity.Type.Shield && aConst.ShieldHitParticle; + if (shieldHit && a.ShieldHitParticleActive || (!shieldHit && aConst.HitParticle && a.HitParticleActive)) { - ContainmentType containment; - s.CameraFrustrum.Contains(ref a.Hit.SurfaceHit, out containment); - if (containment != ContainmentType.Disjoint) a.RunBeam(); - } - else if (s.Av.BeamEffects.TryGetValue(a.UniqueMuzzleId, out effect)) - { - effect.Stop(); - s.Av.BeamEffects.Remove(a.UniqueMuzzleId); + MyParticleEffect effect; + if (a.Hitting) + { + ContainmentType containment; + s.CameraFrustrum.Contains(ref a.Hit.SurfaceHit, out containment); + if (containment != ContainmentType.Disjoint) a.RunBeam(shieldHit && a.ShieldHitParticleActive); + } + else if (s.Av.BeamEffects.TryGetValue(a.UniqueMuzzleId, out effect)) + { + effect.Stop(); + s.Av.BeamEffects.Remove(a.UniqueMuzzleId); + } } } @@ -1191,22 +1195,22 @@ private void StageChange(int newStageIdx, bool createdPrimeEntity) } - internal void RunBeam() - { + internal void RunBeam(bool shieldHit) + { + var p = AmmoDef.AmmoGraphics.Particles; MyParticleEffect effect; MatrixD matrix; var vel = HitVelocity; if (!Session.Av.BeamEffects.TryGetValue(UniqueMuzzleId, out effect)) { MatrixD.CreateTranslation(ref TracerFront, out matrix); - if (!MyParticlesManager.TryCreateParticleEffect(AmmoDef.AmmoGraphics.Particles.Hit.Name, ref matrix, ref TracerFront, uint.MaxValue, out effect)) { + if (!MyParticlesManager.TryCreateParticleEffect(shieldHit ? p.ShieldHit.Name : p.Hit.Name, ref matrix, ref TracerFront, uint.MaxValue, out effect)) return; - } if (effect.Loop || effect.DurationMax <= 0) Session.Av.BeamEffects[UniqueMuzzleId] = effect; - effect.UserScale = AmmoDef.AmmoGraphics.Particles.Hit.Extras.Scale; + effect.UserScale = shieldHit ? p.ShieldHit.Extras.Scale : p.Hit.Extras.Scale; Vector3D.ClampToSphere(ref vel, (float)MaxSpeed); } diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 0e487d29..5b48a0fb 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -144,9 +144,15 @@ 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.HitParticle == AvShot.ParticleState.Shield ? av.AmmoDef.AmmoGraphics.Particles.ShieldHit : av.AmmoDef.AmmoGraphics.Particles.Hit; - var keenStrikesAgain = particle.Offset == Vector3D.MaxValue; MatrixD matrix = MatrixD.CreateTranslation(pos); - if (keenStrikesAgain) + if (av.HitParticle == AvShot.ParticleState.Shield) + { + var grid = av.Hit.Entity.GetTopMostParent() as IMyCubeGrid; + var lineToShield = pos - grid.PositionComp.WorldAABB.Center; + lineToShield.Normalize(); + matrix = MatrixD.CreateWorld(pos, lineToShield, Vector3D.CalculatePerpendicularVector(lineToShield)); + } + else if (particle.Offset == Vector3D.MaxValue) { matrix = MatrixD.CreateWorld(pos, av.VisualDir, av.OriginUp); } diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 57312735..ba874981 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -418,7 +418,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon AmmoParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Ammo.Name); HitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); HitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name; - ShieldHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); + ShieldHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.Name); ShieldHitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.Name; EndOfLifeAv = !ammo.AmmoDef.AreaOfDamage.EndOfLife.NoVisuals && ammo.AmmoDef.AreaOfDamage.EndOfLife.Enable; diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index 1c1b7d4c..a2a452b5 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -303,12 +303,12 @@ internal void Start() Info.AvShot = session.Av.AvShotPool.Count > 0 ? session.Av.AvShotPool.Pop() : new AvShot(session); Info.AvShot.Init(Info, (aConst.DeltaVelocityPerTick * Session.I.DeltaTimeRatio), MaxSpeed, ref Direction); Info.AvShot.SetupSounds(distanceFromCameraSqr); //Pool initted sounds per Projectile type... this is expensive - if (aConst.HitParticle && !aConst.IsBeamWeapon || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) + if (aConst.HitParticle || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) { var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.Hit.Extras.HitPlayChance; Info.AvShot.HitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); } - if (aConst.ShieldHitParticle && !aConst.IsBeamWeapon || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) + if (aConst.ShieldHitParticle || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) { var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.ShieldHit.Extras.HitPlayChance; Info.AvShot.ShieldHitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 4d5350a9..099d9722 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -1126,38 +1126,37 @@ private void DamageVoxel(HitEntity hitEnt, ProInfo info, HitEntity.Type type) info.ObjectsHit++; float damageScale = 1 * directDmgGlobal; - if (info.AmmoDef.Const.VirtualBeams) damageScale *= info.Weapon.WeaponCache.Hits; + if (info.AmmoDef.Const.VirtualBeams) + damageScale *= info.Weapon.WeaponCache.Hits; var scaledDamage = info.BaseDamagePool * damageScale; - + + //Falloff var distTraveled = info.AmmoDef.Const.IsBeamWeapon ? hitEnt.HitDist ?? info.DistanceTraveled : info.DistanceTraveled; - var fallOff = info.AmmoDef.Const.FallOffScaling && distTraveled > info.AmmoDef.Const.FallOffDistance; - - if (fallOff) - { - var fallOffMultipler = (float)MathHelperD.Clamp(1.0 - ((distTraveled - info.AmmoDef.Const.FallOffDistance) / (info.AmmoDef.Const.MaxTrajectory - info.AmmoDef.Const.FallOffDistance)), info.AmmoDef.DamageScales.FallOff.MinMultipler, 1); - scaledDamage *= fallOffMultipler; - } + if (info.AmmoDef.Const.FallOffScaling && distTraveled > info.AmmoDef.Const.FallOffDistance) + scaledDamage *= (float)MathHelperD.Clamp(1.0 - ((distTraveled - info.AmmoDef.Const.FallOffDistance) / (info.AmmoDef.Const.MaxTrajectory - info.AmmoDef.Const.FallOffDistance)), info.AmmoDef.DamageScales.FallOff.MinMultipler, 1); var oRadius = info.AmmoDef.Const.ByBlockHitRadius; var minTestRadius = distTraveled - info.PrevDistanceTraveled; var tRadius = oRadius < minTestRadius && !info.AmmoDef.Const.IsBeamWeapon ? minTestRadius : oRadius; var objHp = (int)MathHelper.Clamp(MathFuncs.VolumeCube(MathFuncs.LargestCubeInSphere(tRadius)), 5000, double.MaxValue); - if (tRadius > 5) objHp *= 5; + if (tRadius > 5) + objHp *= 5; if (scaledDamage < objHp) { var reduceBy = objHp / scaledDamage; oRadius /= reduceBy; - if (oRadius < 1) oRadius = 1; - + if (oRadius < 1) + oRadius = 1; info.BaseDamagePool = 0; } else { info.BaseDamagePool -= objHp; - if (oRadius < minTestRadius) oRadius = minTestRadius; + if (oRadius < minTestRadius) + oRadius = minTestRadius; } var cut = aConst.FakeVoxelHitTicks == 0 || aConst.FakeVoxelHitTicks == Tick; @@ -1171,7 +1170,8 @@ private void DamageVoxel(HitEntity hitEnt, ProInfo info, HitEntity.Type type) { var dRadius = info.AmmoDef.Const.EndOfLifeRadius; - if (dRadius < 1.5) dRadius = 1.5f; + if (dRadius < 1.5) + dRadius = 1.5f; if (info.DoDamage) destObj.PerformCutOutSphereFast(hitInfo.Position, dRadius, true); From 774ced10bc7f6608e95a02da0e852d071763ba73 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 1 May 2025 13:40:25 -0500 Subject: [PATCH 48/77] shield hit particle orientation --- .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 16 +++++++++----- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 15 ++++++++----- .../SerializedConfigs/AmmoConstants.cs | 1 + .../CoreSystems/Projectiles/ProjectileHits.cs | 8 +++---- .../Projectiles/ProjectileTypes.cs | 1 + .../CoreSystems/Session/SessionDamageMgr.cs | 17 +++++--------- Data/Scripts/CoreSystems/Support/MathFuncs.cs | 22 +++++++++++++++++++ 7 files changed, 52 insertions(+), 28 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index f1b8f8e1..fe376dd7 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -105,6 +105,7 @@ public AvShot(Session session) internal Vector3D ShootVelStep; internal Vector3D TracerFront; internal Vector3D TracerBack; + internal Vector3D ShieldHitAngle; internal Vector4 Color; internal Vector4 SegmentColor; internal Vector4 FgFactionColor; @@ -387,7 +388,7 @@ internal static void DeferedAvStateUpdates() var lineOnScreen = a.OnScreen > (Screen)2; - if (!a.Active && (a.OnScreen != Screen.None || a.HitSoundInitted || a.TravelSound || aConst.AmmoParticleNoCull || saveHit && aConst.HitParticleNoCull || aConst.FieldParticle && aConst.FieldParticleNoCull)) { + if (!a.Active && (a.OnScreen != Screen.None || a.HitSoundInitted || a.TravelSound || aConst.AmmoParticleNoCull || saveHit && (a.Hit.EventType == HitEntity.Type.Shield && aConst.ShieldHitParticleNoCull) || aConst.HitParticleNoCull) || aConst.FieldParticle && aConst.FieldParticleNoCull) { a.Active = true; s.Av.AvShots.Add(a); } @@ -968,10 +969,10 @@ internal void HitEffects(bool force = false) HitParticleActive = true; } - if (OnScreen == Screen.Tracer || AmmoDef.Const.HitParticleNoCull || distToCameraSqr < 360000) { - if (LastHitShield && ShieldHitParticleActive && AmmoDef.Const.ShieldHitParticle) + if (OnScreen == Screen.Tracer) { + if (LastHitShield && ShieldHitParticleActive && AmmoDef.Const.ShieldHitParticle && (AmmoDef.Const.ShieldHitParticleNoCull || distToCameraSqr < 360000)) HitParticle = ParticleState.Shield; - else if (HitParticleActive && AmmoDef.Const.HitParticle && !(LastHitShield && !AmmoDef.AmmoGraphics.Particles.Hit.ApplyToShield)) + else if (HitParticleActive && AmmoDef.Const.HitParticle && !(LastHitShield && !AmmoDef.AmmoGraphics.Particles.Hit.ApplyToShield) && (AmmoDef.Const.HitParticleNoCull || distToCameraSqr < 360000)) HitParticle = ParticleState.Custom; } @@ -1202,8 +1203,10 @@ internal void RunBeam(bool shieldHit) MatrixD matrix; var vel = HitVelocity; if (!Session.Av.BeamEffects.TryGetValue(UniqueMuzzleId, out effect)) { - - MatrixD.CreateTranslation(ref TracerFront, out matrix); + if (shieldHit) + matrix = MatrixD.CreateWorld(TracerFront, ShieldHitAngle, Vector3D.CalculatePerpendicularVector(ShieldHitAngle)); + else + MatrixD.CreateTranslation(ref TracerFront, out matrix); if (!MyParticlesManager.TryCreateParticleEffect(shieldHit ? p.ShieldHit.Name : p.Hit.Name, ref matrix, ref TracerFront, uint.MaxValue, out effect)) return; @@ -1489,6 +1492,7 @@ internal void Close() Session.Av.OffSetLists.Push(Offsets); } + ShieldHitAngle = Vector3D.Zero; HitVelocity = Vector3D.Zero; TracerBack = Vector3D.Zero; TracerFront = Vector3D.Zero; diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 5b48a0fb..066b1578 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -147,15 +147,18 @@ internal void End() MatrixD matrix = MatrixD.CreateTranslation(pos); if (av.HitParticle == AvShot.ParticleState.Shield) { - var grid = av.Hit.Entity.GetTopMostParent() as IMyCubeGrid; - var lineToShield = pos - grid.PositionComp.WorldAABB.Center; - lineToShield.Normalize(); - matrix = MatrixD.CreateWorld(pos, lineToShield, Vector3D.CalculatePerpendicularVector(lineToShield)); + if(av.ShieldHitAngle == Vector3D.Zero) + { + var grid = av.Hit.Entity.GetTopMostParent() as IMyCubeGrid; + var lineToShield = pos - grid.PositionComp.WorldAABB.Center; + lineToShield.Normalize(); + matrix = MatrixD.CreateWorld(pos, lineToShield, Vector3D.CalculatePerpendicularVector(lineToShield)); + } + else + matrix = MatrixD.CreateWorld(pos, av.ShieldHitAngle, Vector3D.CalculatePerpendicularVector(av.ShieldHitAngle)); } else if (particle.Offset == Vector3D.MaxValue) - { matrix = MatrixD.CreateWorld(pos, av.VisualDir, av.OriginUp); - } else if (particle.Offset == Vector3D.MinValue) { float interference; diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index ba874981..84559709 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -163,6 +163,7 @@ public enum Texture public readonly bool AmmoParticleNoCull; public readonly bool FieldParticleNoCull; public readonly bool HitParticleNoCull; + public readonly bool ShieldHitParticleNoCull; public readonly bool DrawLine; public readonly bool Ewar; public readonly bool NonAntiSmartEwar; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index d95f9fa4..2194791a 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -161,7 +161,7 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) var shrapnelSpawn = p.Info.IsFragment && p.Info.PrevRelativeAge <= -1; if (Vector3D.Transform(!shrapnelSpawn ? info.Origin : coreEntity.PositionComp.WorldMatrixRef.Translation, shieldInfo.Value.Item3.Item1).LengthSquared() > 1) { - + Vector3D angle; var dist = MathFuncs.IntersectEllipsoid(shieldInfo.Value.Item3.Item1, shieldInfo.Value.Item3.Item2, new RayD(beamFrom, direction)); if (target.TargetState == Target.TargetStates.IsProjectile && Vector3D.Transform(((Projectile)target.TargetObject).Position, shieldInfo.Value.Item3.Item1).LengthSquared() <= 1) projetileInShield = true; @@ -175,9 +175,9 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) hitEntity.EventType = Shield; var hitPos = beamFrom + (direction * dist.Value); - hitEntity.HitPos = beamFrom + (direction * dist.Value); + hitEntity.HitPos = hitPos; hitEntity.HitDist = dist; - + hitEntity.ShieldHitAngle = MathFuncs.ShieldHitAngle(shieldInfo.Value.Item3.Item1, shieldInfo.Value.Item3.Item2, new RayD(beamFrom, direction)); var weakendShield = shieldInfo.Value.Item4.Item2 || shieldInfo.Value.Item4.Item3 < shieldInfo.Value.Item4.Item4; if (weakendShield || shieldInfo.Value.Item2.Item2) @@ -719,9 +719,9 @@ internal bool GenerateHitInfo(Projectile p) visualHitPos = hitInfo?.HitEntity != null ? hitInfo.Position : hitEntity.HitPos ?? p.Beam.To; } else visualHitPos = hitEntity.HitPos ?? p.Beam.To; - if (p.EnableAv) { info.AvShot.LastHitShield = hitEntity.EventType == Shield; + info.AvShot.ShieldHitAngle = hitEntity.ShieldHitAngle; info.AvShot.Hit = new Hit { Entity = hitEntity.Entity, EventType = hitEntity.EventType, HitTick = Session.I.Tick, HitVelocity = lastHitVel, LastHit = visualHitPos, SurfaceHit = visualHitPos }; } else if (aConst.VirtualBeams) diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs index 3326a6df..b9a171e8 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs @@ -438,6 +438,7 @@ public enum Type public Type EventType; public int DamageMulti = 1; public Stack Pool; + public Vector3D ShieldHitAngle; public void Clean() { Vector3ICache.Clear(); diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 099d9722..76c650eb 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -1163,18 +1163,12 @@ private void DamageVoxel(HitEntity hitEnt, ProInfo info, HitEntity.Type type) if (cut) { var radius = (float)(oRadius * info.AmmoDef.Const.VoxelHitModifier); - destObj.PerformCutOutSphereFast(hitInfo.Position, radius, true); - } - - if (detonateOnEnd && info.BaseDamagePool <= 0 && cut) - { - var dRadius = info.AmmoDef.Const.EndOfLifeRadius; - - if (dRadius < 1.5) + var dRadius = detonateOnEnd ? info.AmmoDef.Const.EndOfLifeDepth < info.AmmoDef.Const.EndOfLifeRadius ? info.AmmoDef.Const.EndOfLifeDepth : info.AmmoDef.Const.EndOfLifeRadius : -1; + if (dRadius != -1 && dRadius < 1.5) dRadius = 1.5f; - - if (info.DoDamage) - destObj.PerformCutOutSphereFast(hitInfo.Position, dRadius, true); + if (detonateOnEnd && info.BaseDamagePool <= 0 && dRadius > radius) + radius = dRadius; + destObj.PerformCutOutSphereFast(hitInfo.Position, radius, true); } if (GlobalDamageHandlerActive) @@ -1184,7 +1178,6 @@ private void DamageVoxel(HitEntity hitEnt, ProInfo info, HitEntity.Type type) } } } - } public void RadiantAoe(Vector3I rootInfo, MyCubeGrid grid, double radius, double depth, LineD direction, ref int maxDbc, out bool foundSomething, AoeShape shape, bool showHits,out int aoeHits) //added depth and angle diff --git a/Data/Scripts/CoreSystems/Support/MathFuncs.cs b/Data/Scripts/CoreSystems/Support/MathFuncs.cs index 429618cf..cafac2db 100644 --- a/Data/Scripts/CoreSystems/Support/MathFuncs.cs +++ b/Data/Scripts/CoreSystems/Support/MathFuncs.cs @@ -33,6 +33,28 @@ internal static bool TargetSphereInCone(ref BoundingSphereD targetSphere, ref Co return true; } + internal static Vector3D ShieldHitAngle(MatrixD ellipsoidMatrixInv, MatrixD ellipsoidMatrix, RayD ray) + { + var dirMat = MatrixD.CreateWorld(ray.Position, ray.Direction, Vector3D.CalculatePerpendicularVector(ray.Direction)); + + var ray1 = new RayD(ray.Position + dirMat.Up * 0.001 + dirMat.Right * 0.001, ray.Direction); + var ray2 = new RayD(ray.Position + dirMat.Down * 0.001 + dirMat.Right * 0.001, ray.Direction); + var ray3 = new RayD(ray.Position + dirMat.Left * 0.001414, ray.Direction); + + var dist1 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray1); + var dist2 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray2); + var dist3 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray3); + if (!dist1.HasValue || !dist2.HasValue || !dist3.HasValue) + return Vector3D.Zero; + + var hitPos1World = ray1.Position + (ray.Direction * dist1.Value); + var hitPos2World = ray2.Position + (ray.Direction * dist2.Value); + var hitPos3World = ray3.Position + (ray.Direction * dist3.Value); + + var plane = new Plane(hitPos1World, hitPos2World, hitPos3World); + return plane.Normal; + } + internal static double? IntersectEllipsoid(MatrixD ellipsoidMatrixInv, MatrixD ellipsoidMatrix, RayD ray) { var normSphere = new BoundingSphereD(Vector3.Zero, 1f); From b2eb846a2bb9e169883de5c513b93ed5f665d167 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 1 May 2025 13:40:36 -0500 Subject: [PATCH 49/77] Aconst --- .../CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 84559709..53ecd061 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -414,6 +414,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon AmmoParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.Ammo.DisableCameraCulling; HitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.Hit.DisableCameraCulling; + ShieldHitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.DisableCameraCulling; FieldParticleNoCull = ammo.AmmoDef.Ewar.Field.Particle.DisableCameraCulling; AmmoParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Ammo.Name); From 6a23392b2e7d5a9a1d9f587e8b23bfc7eae587be Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 1 May 2025 15:51:40 -0500 Subject: [PATCH 50/77] Float Errors are fun --- Data/Scripts/CoreSystems/Support/MathFuncs.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/Support/MathFuncs.cs b/Data/Scripts/CoreSystems/Support/MathFuncs.cs index cafac2db..e75a2a77 100644 --- a/Data/Scripts/CoreSystems/Support/MathFuncs.cs +++ b/Data/Scripts/CoreSystems/Support/MathFuncs.cs @@ -36,10 +36,9 @@ internal static bool TargetSphereInCone(ref BoundingSphereD targetSphere, ref Co internal static Vector3D ShieldHitAngle(MatrixD ellipsoidMatrixInv, MatrixD ellipsoidMatrix, RayD ray) { var dirMat = MatrixD.CreateWorld(ray.Position, ray.Direction, Vector3D.CalculatePerpendicularVector(ray.Direction)); - - var ray1 = new RayD(ray.Position + dirMat.Up * 0.001 + dirMat.Right * 0.001, ray.Direction); - var ray2 = new RayD(ray.Position + dirMat.Down * 0.001 + dirMat.Right * 0.001, ray.Direction); - var ray3 = new RayD(ray.Position + dirMat.Left * 0.001414, ray.Direction); + var ray1 = new RayD(ray.Position + dirMat.Up * 0.01 + dirMat.Right * 0.01, ray.Direction); + var ray2 = new RayD(ray.Position + dirMat.Down * 0.01 + dirMat.Right * 0.01, ray.Direction); + var ray3 = new RayD(ray.Position + dirMat.Left * 0.01414, ray.Direction); var dist1 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray1); var dist2 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray2); From b01d75907293f22c91d2dea31b6f152ddb3c4304 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 2 May 2025 09:40:54 -0500 Subject: [PATCH 51/77] Updates - Bugfixes for new shield particle orientation - Added PBApi method "GetAmmoCount" to see how much ammo a weapon has internally --- Data/Scripts/CoreSystems/Api/ApiBackend.cs | 10 ++++++++++ Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs | 11 +++++++++++ Data/Scripts/CoreSystems/Support/MathFuncs.cs | 11 +++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/Api/ApiBackend.cs b/Data/Scripts/CoreSystems/Api/ApiBackend.cs index 97457462..2e222f74 100644 --- a/Data/Scripts/CoreSystems/Api/ApiBackend.cs +++ b/Data/Scripts/CoreSystems/Api/ApiBackend.cs @@ -190,6 +190,7 @@ internal ApiBackend() ["HasGridAi"] = new Func(PbHasGridAi), ["HasCoreWeapon"] = new Func(PbHasCoreWeapon), ["GetOptimalDps"] = new Func(PbGetOptimalDps), + ["GetAmmoCount"] = new Func(PbGetAmmoCount), ["GetActiveAmmo"] = new Func(PbGetActiveAmmo), ["SetActiveAmmo"] = new Action(PbSetActiveAmmo), ["RegisterProjectileAdded"] = new Action>(RegisterProjectileAddedCallback), @@ -264,6 +265,15 @@ private void PbSetActiveAmmo(object arg1, int arg2, string arg3) SetActiveAmmo((MyEntity) arg1, arg2, arg3); } + private int PbGetAmmoCount(object arg1, int arg2) + { + var weaponBlock = (MyEntity)arg1; + var comp = weaponBlock.Components.Get() as Weapon.WeaponComponent; + if (comp?.Platform != null && comp.Platform.State == Ready && comp.Platform.Weapons.Count > arg2) + return comp.Platform.Weapons[arg2].ProtoWeaponAmmo.CurrentAmmo; + return -1; + } + private string PbGetActiveAmmo(object arg1, int arg2) { return GetActiveAmmo((MyEntity) arg1, arg2); diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs index 7cf8ee10..6e2c06e6 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs @@ -44,6 +44,7 @@ public class WcPbApi private Func _hasCoreWeapon; private Func _getOptimalDps; private Func _getActiveAmmo; + private Func _getAmmoCount; private Action _setActiveAmmo; private Action> _monitorProjectile; private Action> _unMonitorProjectile; @@ -120,6 +121,7 @@ public bool ApiAssign(IReadOnlyDictionary delegates) AssignMethod(delegates, "HasGridAi", ref _hasGridAi); AssignMethod(delegates, "HasCoreWeapon", ref _hasCoreWeapon); AssignMethod(delegates, "GetOptimalDps", ref _getOptimalDps); + AssignMethod(delegates, "GetAmmoCount", ref _getAmmoCount); AssignMethod(delegates, "GetActiveAmmo", ref _getActiveAmmo); AssignMethod(delegates, "SetActiveAmmo", ref _setActiveAmmo); AssignMethod(delegates, "MonitorProjectile", ref _monitorProjectile); @@ -502,6 +504,15 @@ public bool CanShootTarget(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, long t public string GetActiveAmmo(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => _getActiveAmmo?.Invoke(weapon, weaponId) ?? null; + /// + /// Returns the current amount of ammo the weapon has internally (loaded, not inventory) on . + /// + /// + /// + /// Current ammo + public string GetAmmoCount(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon, int weaponId) => + _getActiveAmmo?.Invoke(weapon, weaponId) ?? null; + /// /// Sets the active ammo name of on to . /// diff --git a/Data/Scripts/CoreSystems/Support/MathFuncs.cs b/Data/Scripts/CoreSystems/Support/MathFuncs.cs index e75a2a77..1cf4cb50 100644 --- a/Data/Scripts/CoreSystems/Support/MathFuncs.cs +++ b/Data/Scripts/CoreSystems/Support/MathFuncs.cs @@ -36,9 +36,9 @@ internal static bool TargetSphereInCone(ref BoundingSphereD targetSphere, ref Co internal static Vector3D ShieldHitAngle(MatrixD ellipsoidMatrixInv, MatrixD ellipsoidMatrix, RayD ray) { var dirMat = MatrixD.CreateWorld(ray.Position, ray.Direction, Vector3D.CalculatePerpendicularVector(ray.Direction)); - var ray1 = new RayD(ray.Position + dirMat.Up * 0.01 + dirMat.Right * 0.01, ray.Direction); - var ray2 = new RayD(ray.Position + dirMat.Down * 0.01 + dirMat.Right * 0.01, ray.Direction); - var ray3 = new RayD(ray.Position + dirMat.Left * 0.01414, ray.Direction); + var ray1 = new RayD(ray.Position + dirMat.Up * 0.05 + dirMat.Right * 0.05, ray.Direction); + var ray2 = new RayD(ray.Position + dirMat.Down * 0.05 + dirMat.Right * 0.05, ray.Direction); + var ray3 = new RayD(ray.Position + dirMat.Left * 0.0707106781186548, ray.Direction); var dist1 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray1); var dist2 = IntersectEllipsoid(ellipsoidMatrixInv, ellipsoidMatrix, ray2); @@ -49,8 +49,11 @@ internal static Vector3D ShieldHitAngle(MatrixD ellipsoidMatrixInv, MatrixD elli var hitPos1World = ray1.Position + (ray.Direction * dist1.Value); var hitPos2World = ray2.Position + (ray.Direction * dist2.Value); var hitPos3World = ray3.Position + (ray.Direction * dist3.Value); + var plane = new PlaneD(hitPos1World, hitPos2World, hitPos3World); + + if (!plane.Normal.IsValid() || plane.Normal.AbsMax() == 1) + return Vector3D.Zero; - var plane = new Plane(hitPos1World, hitPos2World, hitPos3World); return plane.Normal; } From d2e40242c8f60dcf0bf95047eda88e404505685b Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 2 May 2025 22:43:39 -0500 Subject: [PATCH 52/77] DLC variant fix --- Data/BlockVariantGroups.sbc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Data/BlockVariantGroups.sbc b/Data/BlockVariantGroups.sbc index 304d4813..824a4998 100644 --- a/Data/BlockVariantGroups.sbc +++ b/Data/BlockVariantGroups.sbc @@ -19,6 +19,8 @@ + + @@ -30,8 +32,12 @@ + + + + From c55bfd943b7ffb1255b39688cf80a1a8e6f46a4d Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sun, 4 May 2025 21:23:03 -0500 Subject: [PATCH 53/77] Shield barticles --- .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 5 ++--- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 2 +- .../CoreSystems/Projectiles/ProjectileHits.cs | 20 ++++++++++++++----- .../Projectiles/ProjectileTypes.cs | 1 + 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index fe376dd7..5d4f71d7 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -1319,11 +1319,10 @@ internal void AvClose() { var particle = AmmoDef.AmmoGraphics.Particles.Hit; - var keenStrikesAgain = particle.Offset == Vector3D.MaxValue; MatrixD matrix = MatrixD.CreateTranslation(pos); - if(keenStrikesAgain) + if (particle.Offset == Vector3D.MaxValue) { - matrix = MatrixD.CreateWorld(pos, VisualDir, OriginUp); + matrix = MatrixD.CreateWorld(pos, VisualDir, Vector3D.CalculatePerpendicularVector(VisualDir)); } else if (particle.Offset == Vector3D.MinValue) { diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 066b1578..f41abe3d 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -158,7 +158,7 @@ internal void End() matrix = MatrixD.CreateWorld(pos, av.ShieldHitAngle, Vector3D.CalculatePerpendicularVector(av.ShieldHitAngle)); } else if (particle.Offset == Vector3D.MaxValue) - matrix = MatrixD.CreateWorld(pos, av.VisualDir, av.OriginUp); + matrix = MatrixD.CreateWorld(pos, av.VisualDir, Vector3D.CalculatePerpendicularVector(av.VisualDir)); else if (particle.Offset == Vector3D.MinValue) { float interference; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 2194791a..8d02da5a 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -161,7 +161,6 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) var shrapnelSpawn = p.Info.IsFragment && p.Info.PrevRelativeAge <= -1; if (Vector3D.Transform(!shrapnelSpawn ? info.Origin : coreEntity.PositionComp.WorldMatrixRef.Translation, shieldInfo.Value.Item3.Item1).LengthSquared() > 1) { - Vector3D angle; var dist = MathFuncs.IntersectEllipsoid(shieldInfo.Value.Item3.Item1, shieldInfo.Value.Item3.Item2, new RayD(beamFrom, direction)); if (target.TargetState == Target.TargetStates.IsProjectile && Vector3D.Transform(((Projectile)target.TargetObject).Position, shieldInfo.Value.Item3.Item1).LengthSquared() <= 1) projetileInShield = true; @@ -214,7 +213,7 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) { //if (((MyCubeGrid)shieldInfo.Value.Item1.CubeGrid).DebugName.Contains("test")) // Log.Line($"resist: RNG:{normalized} <= penChance:{threshold} - ammo:{info.AmmoDef.AmmoRound} - sPerc:{shieldInfo.Value.Item2.Item5} - heat:{shieldInfo.Value.Item2.Item6} - threshold:{shieldInfo.Value.Item2.Item5 / (1 + (shieldInfo.Value.Item2.Item6 * 0.01))}"); - + p.Info.ShieldBypassMod = 1f; } @@ -228,7 +227,8 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) info.ShieldBypassMod = aConst.ShieldDamageBypassMod; } } - else continue; + else + continue; } } else @@ -479,6 +479,10 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) hitEntity.EventType = Destroyable; } + + if (info.ShieldBypassed && hitEntity.EventType != Shield) + info.ShieldBypassedHitOther = true; + if (hitEntity != null) { var hitEnt = hitEntity.EventType != Shield ? ent : (MyEntity)shieldInfo.Value.Item1; @@ -719,10 +723,11 @@ internal bool GenerateHitInfo(Projectile p) visualHitPos = hitInfo?.HitEntity != null ? hitInfo.Position : hitEntity.HitPos ?? p.Beam.To; } else visualHitPos = hitEntity.HitPos ?? p.Beam.To; - if (p.EnableAv) { + if (p.EnableAv) + { info.AvShot.LastHitShield = hitEntity.EventType == Shield; info.AvShot.ShieldHitAngle = hitEntity.ShieldHitAngle; - info.AvShot.Hit = new Hit { Entity = hitEntity.Entity, EventType = hitEntity.EventType, HitTick = Session.I.Tick, HitVelocity = lastHitVel, LastHit = visualHitPos, SurfaceHit = visualHitPos }; + info.AvShot.Hit = new Hit { Entity = hitEntity.Entity, EventType = hitEntity.EventType, HitTick = Session.I.Tick, HitVelocity = lastHitVel, LastHit = visualHitPos, SurfaceHit = visualHitPos}; } else if (aConst.VirtualBeams) AvShot.UpdateVirtualBeams(p, info, hitEntity, visualHitPos, lastHitVel, true); @@ -931,6 +936,11 @@ internal int GetEntityCompareDist(HitEntity x, HitEntity y, ProInfo info) hitEnt.Miss = !closestBlockFound; } } + + if (hitEnt.HitPos != null && info.ShieldBypassedHitOther && info.AvShot.HitParticle == AvShot.ParticleState.Dirty) + { + info.AvShot.HitParticle = AvShot.ParticleState.Custom; + } } else if (voxel != null) { diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs index b9a171e8..b661337f 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs @@ -71,6 +71,7 @@ public class ProInfo internal bool AimedShot; internal bool DoDamage; internal bool ShieldBypassed; + internal bool ShieldBypassedHitOther; internal bool ShieldKeepBypass; internal bool ShieldInLine; internal uint FirstWaterHitTick; From 76a3b77b297764868cf9d5b698e4b902857c82d8 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 6 May 2025 08:40:06 -0500 Subject: [PATCH 54/77] Revert CTC leading for smarts --- .../CoreSystems/EntityComp/Parts/Control/ControlTracking.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs index d4ede967..93ad2e3a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Control/ControlTracking.cs @@ -64,7 +64,7 @@ internal static bool TrajectoryEstimation(ControlSys control, out Vector3D targe var maxRangeSqr = fakeTargetInfo != null && topAi.Construct.RootAi != null ? topAi.Construct.RootAi.MaxTargetingRangeSqr : cValues.Set.Range * cValues.Set.Range; bool valid; - if (weapon.ActiveAmmoDef.AmmoDef.Const.IsSmart || weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon) + if (weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon) { valid = true; topAi.RotorTargetPosition = targetCenter; From 365f55ed7fd98f3619a6bca3e7052d49014f63da Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 6 May 2025 10:39:06 -0500 Subject: [PATCH 55/77] NRE in projectillehits --- Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 8d02da5a..48673dfb 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -479,12 +479,11 @@ internal void InitialHitCheck(Projectile p, bool lineCheck) hitEntity.EventType = Destroyable; } - - if (info.ShieldBypassed && hitEntity.EventType != Shield) - info.ShieldBypassedHitOther = true; - if (hitEntity != null) { + if (info.ShieldBypassed && hitEntity.EventType != Shield) + info.ShieldBypassedHitOther = true; + var hitEnt = hitEntity.EventType != Shield ? ent : (MyEntity)shieldInfo.Value.Item1; if (hitEnt != null) { From 92045cf1e5b487eef45db18d455015cbcc70771f Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 6 May 2025 13:40:11 -0500 Subject: [PATCH 56/77] gat sharpdx fix? --- Audio/ARC/WEP/ArcWepShipGatlingShot3d01.wav | Bin 0 -> 68988 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d02.wav | Bin 0 -> 64792 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d03.wav | Bin 0 -> 64280 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d04.wav | Bin 0 -> 62748 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d05.wav | Bin 0 -> 62296 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d06.wav | Bin 0 -> 62324 bytes Audio/ARC/WEP/ArcWepShipGatlingShot3d07.wav | Bin 0 -> 62748 bytes Data/Audio.sbc | 43 ++++++++++++++++++ .../CoreSystems/Projectiles/ProjectileHits.cs | 4 +- 9 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d01.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d02.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d03.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d04.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d05.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d06.wav create mode 100644 Audio/ARC/WEP/ArcWepShipGatlingShot3d07.wav create mode 100644 Data/Audio.sbc diff --git a/Audio/ARC/WEP/ArcWepShipGatlingShot3d01.wav b/Audio/ARC/WEP/ArcWepShipGatlingShot3d01.wav new file mode 100644 index 0000000000000000000000000000000000000000..9e4cada563cf605a1b50b7a83a31860a54262be8 GIT binary patch literal 68988 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≧@qpm?+_@U5rp& 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 Ht4#j~M^2KL literal 0 HcmV?d00001 diff --git a/Audio/ARC/WEP/ArcWepShipGatlingShot3d04.wav b/Audio/ARC/WEP/ArcWepShipGatlingShot3d04.wav new file mode 100644 index 0000000000000000000000000000000000000000..2a261ba067ad48d922d987085050bbc5e870b00e GIT binary patch literal 62748 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≈=(KeKuS> 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

+ /// Registers an action that tells a given grid's AI available target entities in a given sphere. + /// + /// + public void AddScanTargetsAction(Action> action) => _addScanTargetsAction?.Invoke(action); + + /// + /// Unregisters an action that tells a given grid's AI available target entities in a given sphere. + /// + /// + public void RemoveScanTargetsAction(Action> action) => _removeScanTargetsAction?.Invoke(action); + private const long Channel = 67549756549; private bool _getWeaponDefinitions; private bool _isRegistered; @@ -617,6 +632,9 @@ public void ApiAssign(IReadOnlyDictionary delegates, bool getW AssignMethod(delegates, "SetMagazine", ref _setMagazine); AssignMethod(delegates, "ForceReload", ref _forceReload); + AssignMethod(delegates, "AddScanTargetsAction", ref _addScanTargetsAction); + AssignMethod(delegates, "RemoveScanTargetsAction", ref _removeScanTargetsAction); + // Damage handler AssignMethod(delegates, "DamageHandler", ref _registerDamageEvent); diff --git a/Data/Scripts/CoreSystems/Session/SessionFields.cs b/Data/Scripts/CoreSystems/Session/SessionFields.cs index 55895c2c..774efafd 100644 --- a/Data/Scripts/CoreSystems/Session/SessionFields.cs +++ b/Data/Scripts/CoreSystems/Session/SessionFields.cs @@ -332,6 +332,7 @@ public partial class Session internal Projectiles.Projectiles Projectiles; internal ApiBackend Api; internal Action ProjectileAddedCallback = (location, health) => { }; + internal Action> ScanTargetsAction = null; internal ShieldApi SApi = new ShieldApi(); internal NetworkReporter Reporter = new NetworkReporter(); internal MyStorageData TmpStorage = new MyStorageData(); From e7482dd7b9ce06ba0d2c3fcf261fab96aef7cf24 Mon Sep 17 00:00:00 2001 From: Aristeas <94058548+ari-steas@users.noreply.github.com> Date: Fri, 9 May 2025 17:32:19 -0500 Subject: [PATCH 58/77] Create SetValidateWeaponTargetFunc API Hook --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 6 ++++++ Data/Scripts/CoreSystems/Api/ApiBackend.cs | 6 ++++++ Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs | 9 +++++++++ Data/Scripts/CoreSystems/Session/SessionFields.cs | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index 5fe79c06..5365d67e 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -246,9 +246,15 @@ private static bool AcquireTopMostEntity(Weapon w, ProtoWeaponOverrides overRide Vector3D targetAccel = accelPrediction ? info.Target.Physics?.LinearAcceleration ?? Vector3D.Zero : Vector3.Zero; Vector3D predictedPos; + // WcApi turret target validation + if (Session.I.ValidateWeaponTargetFunc != null && + !Session.I.ValidateWeaponTargetFunc.Invoke(w.Comp.TerminalBlock, w.PartId, info.Target)) + continue; + if (info.IsGrid) { if (!s.TrackGrids || !overRides.Grids || (!overRides.LargeGrid && info.LargeGrid) || (!overRides.SmallGrid && !info.LargeGrid) || !focusTarget && info.FatCount < 2) continue; + if (w.System.TargetGridCenter) { if (!Weapon.CanShootTarget(w, ref targetCenter, targetLinVel, targetAccel, out predictedPos, false, null, MathFuncs.DebugCaller.CanShootTarget2)) continue; diff --git a/Data/Scripts/CoreSystems/Api/ApiBackend.cs b/Data/Scripts/CoreSystems/Api/ApiBackend.cs index 1a00ca87..9b0a8a00 100644 --- a/Data/Scripts/CoreSystems/Api/ApiBackend.cs +++ b/Data/Scripts/CoreSystems/Api/ApiBackend.cs @@ -143,6 +143,7 @@ internal ApiBackend() ["GetConstructEffectiveDps"] = new Func(GetConstructEffectiveDpsLegacy), ["AddScanTargetsAction"] = new Action>>(AddScanTargetsAction), ["RemoveScanTargetsAction"] = new Action>>(RemoveScanTargetsAction), + ["SetValidateWeaponTargetFunc"] = new Action>(SetValidateWeaponTargetFunc), // Phantoms ["GetTargetAssessment"] = new Func>(GetPhantomTargetAssessment), @@ -1532,6 +1533,11 @@ private void RemoveScanTargetsAction(Action func) + { + Session.I.ValidateWeaponTargetFunc = func; + } + /// /// Phantoms diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs index 36d52f9c..30ea9e8e 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs @@ -94,6 +94,7 @@ public partial class WcApi private Func _forceReload; private Action>> _addScanTargetsAction; private Action>> _removeScanTargetsAction; + private Action> _setValidateWeaponTargetFunc; public void SetWeaponTarget(MyEntity weapon, MyEntity target, int weaponId = 0) => _setWeaponTarget?.Invoke(weapon, target, weaponId); @@ -482,6 +483,13 @@ public bool ForceReload(MyEntity weapon, int weaponId) /// public void RemoveScanTargetsAction(Action> action) => _removeScanTargetsAction?.Invoke(action); + /// + /// Assigns a function that determines if a given weapon can target a given entity. + /// + /// Block, PartId, target + public void SetValidateWeaponTargetFunc(Func func) => + _setValidateWeaponTargetFunc?.Invoke(func); + private const long Channel = 67549756549; private bool _getWeaponDefinitions; private bool _isRegistered; @@ -634,6 +642,7 @@ public void ApiAssign(IReadOnlyDictionary delegates, bool getW AssignMethod(delegates, "AddScanTargetsAction", ref _addScanTargetsAction); AssignMethod(delegates, "RemoveScanTargetsAction", ref _removeScanTargetsAction); + AssignMethod(delegates, "SetValidateWeaponTargetFunc", ref _setValidateWeaponTargetFunc); // Damage handler AssignMethod(delegates, "DamageHandler", ref _registerDamageEvent); diff --git a/Data/Scripts/CoreSystems/Session/SessionFields.cs b/Data/Scripts/CoreSystems/Session/SessionFields.cs index 774efafd..88efb8db 100644 --- a/Data/Scripts/CoreSystems/Session/SessionFields.cs +++ b/Data/Scripts/CoreSystems/Session/SessionFields.cs @@ -332,7 +332,14 @@ public partial class Session internal Projectiles.Projectiles Projectiles; internal ApiBackend Api; internal Action ProjectileAddedCallback = (location, health) => { }; + /// + /// WcApi action for feeding targets to grid AI. Overrides default if not null. + /// internal Action> ScanTargetsAction = null; + /// + /// WcApi function for checking if a weapon's target is allowed. Defaults true if null. + /// + internal Func ValidateWeaponTargetFunc = null; internal ShieldApi SApi = new ShieldApi(); internal NetworkReporter Reporter = new NetworkReporter(); internal MyStorageData TmpStorage = new MyStorageData(); From 2f40c18cbb7860b700b6c342a4056f6ea4114c26 Mon Sep 17 00:00:00 2001 From: Aristeas <94058548+ari-steas@users.noreply.github.com> Date: Fri, 9 May 2025 17:37:06 -0500 Subject: [PATCH 59/77] Add networking warning to new API methods --- Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs index 30ea9e8e..01bd9f08 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs @@ -472,19 +472,19 @@ public bool ForceReload(MyEntity weapon, int weaponId) } /// - /// Registers an action that tells a given grid's AI available target entities in a given sphere. + /// Registers an action that tells a given grid's AI available target entities in a given sphere. NOT NETWORKED - make sure this is synced in your mod. /// /// public void AddScanTargetsAction(Action> action) => _addScanTargetsAction?.Invoke(action); /// - /// Unregisters an action that tells a given grid's AI available target entities in a given sphere. + /// Unregisters an action that tells a given grid's AI available target entities in a given sphere. NOT NETWORKED - make sure this is synced in your mod. /// /// public void RemoveScanTargetsAction(Action> action) => _removeScanTargetsAction?.Invoke(action); /// - /// Assigns a function that determines if a given weapon can target a given entity. + /// Assigns a function that determines if a given weapon can target a given entity. NOT NETWORKED - make sure this is synced in your mod. /// /// Block, PartId, target public void SetValidateWeaponTargetFunc(Func func) => From 2686c1bbf5771a6b9dbb95ae6a26c7005800924a Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 9 May 2025 18:09:54 -0500 Subject: [PATCH 60/77] cleanup --- .../CoreSystems/Session/SessionFields.cs | 1 - .../CoreSystems/Session/SessionInit.cs | 18 ++----- .../CoreSystems/Session/SessionSupport.cs | 51 ------------------- 3 files changed, 3 insertions(+), 67 deletions(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionFields.cs b/Data/Scripts/CoreSystems/Session/SessionFields.cs index 88efb8db..3f0b020b 100644 --- a/Data/Scripts/CoreSystems/Session/SessionFields.cs +++ b/Data/Scripts/CoreSystems/Session/SessionFields.cs @@ -425,7 +425,6 @@ public partial class Session internal double SyncDistSqr; internal double SyncBufferedDistSqr; internal double SyncDist; - internal double MaxEntitySpeed; internal double Load; internal double ScaleFov; internal double RayMissAmounts; diff --git a/Data/Scripts/CoreSystems/Session/SessionInit.cs b/Data/Scripts/CoreSystems/Session/SessionInit.cs index 60bb806e..f3e5e421 100644 --- a/Data/Scripts/CoreSystems/Session/SessionInit.cs +++ b/Data/Scripts/CoreSystems/Session/SessionInit.cs @@ -46,21 +46,9 @@ private void BeforeStartInit() if (HandlesInput) MyAPIGateway.Utilities.MessageEntered += ChatMessageSet; - var env = MyDefinitionManager.Static.EnvironmentDefinition; - if (env.LargeShipMaxSpeed > MaxEntitySpeed) MaxEntitySpeed = env.LargeShipMaxSpeed; - else if (env.SmallShipMaxSpeed > MaxEntitySpeed) MaxEntitySpeed = env.SmallShipMaxSpeed; - if (MpActive) - { - SyncDist = MyAPIGateway.Session.SessionSettings.SyncDistance; - SyncDistSqr = SyncDist * SyncDist; - SyncBufferedDistSqr = SyncDistSqr + 250000; - } - else - { - SyncDist = MyAPIGateway.Session.SessionSettings.ViewDistance; - SyncDistSqr = SyncDist * SyncDist; - SyncBufferedDistSqr = (SyncDist + 500) * (SyncDist + 500); - } + SyncDist = MpActive ? MyAPIGateway.Session.SessionSettings.SyncDistance : MyAPIGateway.Session.SessionSettings.ViewDistance; + SyncDistSqr = SyncDist * SyncDist; + SyncBufferedDistSqr = SyncDistSqr + 250000; PreFetchMaxDist = MyAPIGateway.Session.SessionSettings.PrefetchShapeRayLengthLimit - 1; diff --git a/Data/Scripts/CoreSystems/Session/SessionSupport.cs b/Data/Scripts/CoreSystems/Session/SessionSupport.cs index 88d71f66..d963bd30 100644 --- a/Data/Scripts/CoreSystems/Session/SessionSupport.cs +++ b/Data/Scripts/CoreSystems/Session/SessionSupport.cs @@ -1359,13 +1359,6 @@ private void InitDelayedHandWeapons() } } - public enum CubeTypes - { - All, - Slims, - Fats, - } - internal void DeferredPlayerLocks() { foreach (var p in DeferredPlayerLock) @@ -1400,41 +1393,6 @@ internal void UpdateLocalCharacterInfo() PlayerPos = Vector3D.Zero; } - public static void GetCubesInRange(MyCubeGrid grid, MyCubeBlock rootBlock, int cubeDistance, HashSet resultSet, out Vector3I min, out Vector3I max, CubeTypes types = CubeTypes.All) - { - resultSet.Clear(); - min = rootBlock.Min - cubeDistance; - max = rootBlock.Max + cubeDistance; - var gridMin = grid.Min; - var gridMax = grid.Max; - - Vector3I.Max(ref min, ref gridMin, out min); - Vector3I.Min(ref max, ref gridMax, out max); - - var iter = new Vector3I_RangeIterator(ref min, ref max); - - var next = rootBlock.Position; - while (iter.IsValid()) { - - MyCube myCube; - if (grid.TryGetCube(next, out myCube) && myCube.CubeBlock != rootBlock.SlimBlock) { - - var slim = (IMySlimBlock)myCube.CubeBlock; - - if (next == slim.Position) { - - if (types == CubeTypes.Slims && slim.FatBlock == null) - resultSet.Add(myCube); - else if (types == CubeTypes.Fats && slim.FatBlock != null) - resultSet.Add(myCube); - else if (types == CubeTypes.All) - resultSet.Add(myCube); - } - } - iter.GetNext(out next); - } - } - private void ColorAreas() { var color = ColorArmorToggle ? SUtils.ColorToHSVOffset(Color.Black) : SUtils.ColorToHSVOffset(Color.OrangeRed); @@ -1806,15 +1764,6 @@ internal void UpdateEnforcement() } Log.Line($"WC Version: {ModVersion}"); - if (IsClient && Settings.Enforcement.Version > ModVersion) - WarnClientAboutOldVersion(); - } - - private void WarnClientAboutOldVersion() - { - var message = "Your WeaponCore version is [older than the servers]! This is likely due to a [corrupted download], please follow directions on [WC Steam Page] to correct"; - ShowLocalNotify(message, 30000); - Log.Line(message); } } } From b9c16281981782a4020868bdd922ca8dedf48f06 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 13 May 2025 10:20:56 -0500 Subject: [PATCH 61/77] CanTargetSubmerged option --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 8 ++++---- Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs | 1 + .../CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs | 2 +- .../CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs | 2 +- Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs | 3 ++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index 5365d67e..730fe269 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -135,7 +135,7 @@ private static bool AcquireTopMostEntity(Weapon w, ProtoWeaponOverrides overRide var shipOnly = moveMode == ProtoWeaponOverrides.MoveModes.ShipAny; BoundingSphereD waterSphere = new BoundingSphereD(Vector3D.Zero, 1f); WaterData water = null; - if (session.WaterApiLoaded && !ammoDef.IgnoreWater && ai.InPlanetGravity && ai.MyPlanet != null && session.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) + if (session.WaterApiLoaded && !(ammoDef.IgnoreWater || comp.TargetSubmerged) && ai.InPlanetGravity && ai.MyPlanet != null && session.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) waterSphere = new BoundingSphereD(ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); var rootConstruct = ai.Construct.RootAi.Construct; @@ -524,7 +524,7 @@ internal static bool AcquireProjectile(Weapon w, ulong id = ulong.MaxValue) var aConst = w.ActiveAmmoDef.AmmoDef.Const; BoundingSphereD waterSphere = new BoundingSphereD(Vector3D.Zero, 1f); WaterData water = null; - if (Session.I.WaterApiLoaded && !w.ActiveAmmoDef.AmmoDef.IgnoreWater && ai.InPlanetGravity && ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) + if (Session.I.WaterApiLoaded && !(w.ActiveAmmoDef.AmmoDef.IgnoreWater || w.Comp.TargetSubmerged) && ai.InPlanetGravity && ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) waterSphere = new BoundingSphereD(ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); var wepAiOwnerFactionId = w.Comp.MasterAi.AiOwnerFactionId; @@ -741,7 +741,7 @@ internal static bool ReacquireTarget(Projectile p) var previousEntity = info.AcquiredEntity; BoundingSphereD waterSphere = new BoundingSphereD(Vector3D.Zero, 1f); WaterData water = null; - if (Session.I.WaterApiLoaded && !info.AmmoDef.IgnoreWater && ai.InPlanetGravity && ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) + if (Session.I.WaterApiLoaded && !(info.AmmoDef.IgnoreWater || w.Comp.TargetSubmerged) && ai.InPlanetGravity && ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) waterSphere = new BoundingSphereD(ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); TargetInfo alphaInfo = null; int offset = 0; @@ -1773,7 +1773,7 @@ internal static bool SwitchToDrone(Weapon w) var stationOnly = moveMode == ProtoWeaponOverrides.MoveModes.Moored; BoundingSphereD waterSphere = new BoundingSphereD(Vector3D.Zero, 1f); WaterData water = null; - if (session.WaterApiLoaded && !ammoDef.IgnoreWater && ai.InPlanetGravity && ai.MyPlanet != null && session.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) + if (session.WaterApiLoaded && !(ammoDef.IgnoreWater || w.Comp.TargetSubmerged) && ai.InPlanetGravity && ai.MyPlanet != null && session.WaterMap.TryGetValue(ai.MyPlanet.EntityId, out water)) waterSphere = new BoundingSphereD(ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); var numOfTargets = ai.SortedTargets.Count; diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index f626e7de..d88b4233 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -486,6 +486,7 @@ public enum Prediction [ProtoMember(14)] internal bool CanShootSubmerged; [ProtoMember(15)] internal bool NpcSafe; [ProtoMember(16)] internal bool ScanTrackOnly; + [ProtoMember(17)] internal bool CanTargetSubmerged; [ProtoContract] public struct LoadingDef diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs index e6512410..a0a2bb6a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponComp.cs @@ -58,6 +58,7 @@ public partial class WeaponComponent : CoreComponent internal bool HasDisabledBurst; internal bool HasRofSlider; internal bool ShootSubmerged; + internal bool TargetSubmerged; internal bool HasTracking; internal bool HasRequireTarget; internal bool HasDrone; @@ -424,7 +425,6 @@ internal void DetectStateChanges(bool masterChange) Ai.DetectOtherSignals = true; var wasAsleep = IsAsleep; IsAsleep = false; - //IsDisabled = Ai.TouchingWater && !ShootSubmerged && Ai.WaterVolume.Contains(CoreEntity.PositionComp.WorldAABB.Center) != ContainmentType.Disjoint; //submerged wep check if (Ai.TouchingWater && !ShootSubmerged) { var projectedPos = CoreEntity.PositionComp.WorldAABB.Center + (Vector3D.Normalize(CoreEntity.PositionComp.WorldVolume.Center- Ai.ClosestPlanetCenter) * CoreEntity.PositionComp.WorldVolume.Radius); diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index 213c111d..45757705 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -1031,7 +1031,7 @@ private bool RayCheckTest(double rangeToTargetSqr) return false; } WaterData water = null; - if (Session.I.WaterApiLoaded && !ActiveAmmoDef.AmmoDef.IgnoreWater && Comp.Ai.InPlanetGravity && Comp.Ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(Comp.Ai.MyPlanet.EntityId, out water)) + if (Session.I.WaterApiLoaded && !(ActiveAmmoDef.AmmoDef.IgnoreWater && Comp.TargetSubmerged) && Comp.Ai.InPlanetGravity && Comp.Ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(Comp.Ai.MyPlanet.EntityId, out water)) { var waterSphere = new BoundingSphereD(Comp.Ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); if (waterSphere.Contains(targetPos) != ContainmentType.Disjoint) diff --git a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs index e4e66729..65363621 100644 --- a/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs +++ b/Data/Scripts/CoreSystems/EntityComp/PlatformInit.cs @@ -716,13 +716,14 @@ internal void SetupWeaponUi(Weapon w) w.Comp.HasDelayToFire = w.Comp.HasDelayToFire || w.System.DelayToFire > 0; w.Comp.ShootSubmerged = w.Comp.ShootSubmerged || w.System.Values.HardPoint.CanShootSubmerged; + w.Comp.TargetSubmerged = w.Comp.TargetSubmerged || w.System.Values.HardPoint.CanTargetSubmerged; w.Comp.HasDisabledBurst = w.Comp.Structure.MultiParts || w.System.MaxAmmoCount <= 1; w.BaseComp.HasServerOverrides = w.BaseComp.HasServerOverrides || w.System.WConst.HasServerOverrides; if (w.System.MaxAmmoCount > w.Comp.MaxAmmoCount) w.Comp.MaxAmmoCount = w.System.MaxAmmoCount; - if (ui.EnableOverload || ui.RateOfFire || ui.ToggleGuidance) // removed ui.DamageModifier explit + if (ui.EnableOverload || ui.RateOfFire || ui.ToggleGuidance) w.BaseComp.UiEnabled = true; if (w.System.HasAmmoSelection) From 81986211a2cfdc0dfdc19bbd7a54efeabf632b83 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 13 May 2025 10:58:22 -0500 Subject: [PATCH 62/77] Update --- .../CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index 45757705..b5730967 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -1031,7 +1031,7 @@ private bool RayCheckTest(double rangeToTargetSqr) return false; } WaterData water = null; - if (Session.I.WaterApiLoaded && !(ActiveAmmoDef.AmmoDef.IgnoreWater && Comp.TargetSubmerged) && Comp.Ai.InPlanetGravity && Comp.Ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(Comp.Ai.MyPlanet.EntityId, out water)) + if (Session.I.WaterApiLoaded && !(ActiveAmmoDef.AmmoDef.IgnoreWater || Comp.TargetSubmerged) && Comp.Ai.InPlanetGravity && Comp.Ai.MyPlanet != null && Session.I.WaterMap.TryGetValue(Comp.Ai.MyPlanet.EntityId, out water)) { var waterSphere = new BoundingSphereD(Comp.Ai.MyPlanet.PositionComp.WorldAABB.Center, water.MinRadius); if (waterSphere.Contains(targetPos) != ContainmentType.Disjoint) From c99f8f6ed510bf7d8b410b4b17cec401f658af13 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 13 May 2025 13:45:04 -0500 Subject: [PATCH 63/77] Action QC pass --- .../EntityComp/Controls/TerminalHelpers.cs | 52 ++++--------------- .../CoreSystems/Session/SessionControls.cs | 49 ++++++++--------- 2 files changed, 32 insertions(+), 69 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 9d34f34b..66f44c37 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -73,51 +73,31 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I internal static void AddTurretControlBlockControls(Session session) where T : IMyTerminalBlock { CtcAddListBoxNoAction(session, "ToolsAndWeapons", "Available tools and weapons", "Auto populated by weaponcore", BlockUi.ToolWeaponFill, BlockUi.ToolWeaponSelect, CtcIsReady, 4, true); - CtcAddCheckboxNoAction(session, "Advanced", Localization.GetText("TerminalAdvancedTitle"), Localization.GetText("TerminalAdvancedTooltip"), BlockUi.GetAdvancedControl, BlockUi.RequestAdvancedControl, true, CtcIsReady); CtcAddOnOffSwitchNoAction(session, "ShareFireControlEnabled", Localization.GetText("TerminalShareFireControlTitle"), Localization.GetText("TerminalShareFireControlTooltip"), BlockUi.GetShareFireControlControl, BlockUi.RequestShareFireControlControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "WCAiEnabled", Localization.GetText("TerminalAiEnabledTitle"), Localization.GetText("TerminalAiEnabledTooltip"), BlockUi.GetAiEnabledControl, BlockUi.RequestSetAiEnabledControl, true, CtcIsReady); - - Separator(session, "WC_sep2", IsTrue); - + CtcAddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlModeControl, BlockUi.RequestControlModeControl, BlockUi.ListControlModes, CtcIsReady); + CtcAddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementModeControl, BlockUi.RequestMovementModeControl, BlockUi.ListMovementModes, CtcIsReady); AddWeaponCtcRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRangeControl, BlockUi.RequestSetRangeControl, CtcIsReady, BlockUi.GetMinRangeControl, BlockUi.GetMaxRangeControl, true, false); - CtcAddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTargetControl, BlockUi.RequestSetReportTargetControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutralsControl, BlockUi.RequestSetNeutralsControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnownedControl, BlockUi.RequestSetUnownedControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicalsControl, BlockUi.RequestSetBiologicalsControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectilesControl, BlockUi.RequestSetProjectilesControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPDControl, BlockUi.RequestSetSupportingPDControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteorsControl, BlockUi.RequestSetMeteorsControl, true, CtcIsReady); - - CtcAddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFireControl, BlockUi.RequestSetFocusFireControl, true, CtcIsReady); + Separator(session, "WC_sep2", IsTrue); CtcAddOnOffSwitchNoAction(session, "SubSystems", Localization.GetText("TerminalSubSystemsTitle"), Localization.GetText("TerminalSubSystemsTooltip"), BlockUi.GetSubSystemsControl, BlockUi.RequestSetSubSystemsControl, true, CtcIsReady); - CtcAddComboboxNoAction(session, "PickSubSystem", Localization.GetText("TerminalPickSubSystemTitle"), Localization.GetText("TerminalPickSubSystemTooltip"), BlockUi.GetSubSystemControl, BlockUi.RequestSubSystemControl, BlockUi.ListSubSystems, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "FocusFire", Localization.GetText("TerminalFocusFireTitle"), Localization.GetText("TerminalFocusFireTooltip"), BlockUi.GetFocusFireControl, BlockUi.RequestSetFocusFireControl, true, CtcIsReady); CtcAddOnOffSwitchNoAction(session, "Repel", Localization.GetText("TerminalRepelTitle"), Localization.GetText("TerminalRepelTooltip"), BlockUi.GetRepelControl, BlockUi.RequestSetRepelControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutralsControl, BlockUi.RequestSetNeutralsControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnownedControl, BlockUi.RequestSetUnownedControl, true, CtcIsReady); CtcAddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGridsControl, BlockUi.RequestSetGridsControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGridControl, BlockUi.RequestSetLargeGridControl, true, CtcIsReady); - CtcAddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGridControl, BlockUi.RequestSetSmallGridControl, true, CtcIsReady); - - Separator(session, "WC_sep3", IsTrue); - - CtcAddComboboxNoAction(session, "TrackingMode", Localization.GetText("TerminalTrackingModeTitle"), Localization.GetText("TerminalTrackingModeTooltip"), BlockUi.GetMovementModeControl, BlockUi.RequestMovementModeControl, BlockUi.ListMovementModes, CtcIsReady); - - CtcAddComboboxNoAction(session, "ControlModes", Localization.GetText("TerminalControlModesTitle"), Localization.GetText("TerminalControlModesTooltip"), BlockUi.GetControlModeControl, BlockUi.RequestControlModeControl, BlockUi.ListControlModes, CtcIsReady); - - Separator(session, "WC_sep4", IsTrue); + CtcAddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicalsControl, BlockUi.RequestSetBiologicalsControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectilesControl, BlockUi.RequestSetProjectilesControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPDControl, BlockUi.RequestSetSupportingPDControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteorsControl, BlockUi.RequestSetMeteorsControl, true, CtcIsReady); + CtcAddOnOffSwitchNoAction(session, "ReportTarget", Localization.GetText("TerminalReportTargetTitle"), Localization.GetText("TerminalReportTargetTooltip"), BlockUi.GetReportTargetControl, BlockUi.RequestSetReportTargetControl, true, CtcIsReady); } internal static void AddSearchlightControls(Session session) where T : IMyTerminalBlock @@ -125,25 +105,15 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer Separator(session, "WC_sep2", HasTracking); AddWeaponRangeSliderNoAction(session, "Weapon Range", Localization.GetText("TerminalWeaponRangeTitle"), Localization.GetText("TerminalWeaponRangeTooltip"), BlockUi.GetRange, BlockUi.RequestSetRange, BlockUi.ShowRange, BlockUi.GetMinRange, BlockUi.GetMaxRange, true, false); - AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); - AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); - AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); - AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); - AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); - AddWeaponCameraSliderRange(session, "Camera Channel", Localization.GetText("TerminalCameraChannelTitle"), Localization.GetText("TerminalCameraChannelTooltip"), BlockUi.GetWeaponCamera, BlockUi.RequestSetBlockCamera, HasTracking, BlockUi.GetMinCameraChannel, BlockUi.GetMaxCameraChannel, true); - } internal static void AddDecoyControls(Session session) where T : IMyTerminalBlock { diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index f9280435..b383a881 100644 --- a/Data/Scripts/CoreSystems/Session/SessionControls.cs +++ b/Data/Scripts/CoreSystems/Session/SessionControls.cs @@ -212,26 +212,21 @@ internal static void CreateCustomCameraActions(Session session) where T : IMy } internal static void CreateCustomActionSet(Session session) where T : IMyTerminalBlock { + CreateCustomActions.CreateShootMode(session); CreateCustomActions.CreateArmReaction(session); CreateCustomActions.CreateTriggerNow(session); - - CreateCustomActions.CreateKeyShoot(session); CreateCustomActions.CreateMouseToggle(session); - CreateCustomActions.CreateShootToggle(session); CreateCustomActions.CreateShootOn(session); CreateCustomActions.CreateShootOff(session); - CreateCustomActions.CreateShootMode(session); + CreateCustomActions.CreateAngularTracking(session); CreateCustomActions.CreateControlModes(session); CreateCustomActions.CreateObjectiveMode(session); CreateCustomActions.CreateMovementState(session); - CreateCustomActions.CreateCycleAmmo(session); CreateCustomActions.CreateForceReload(session); - CreateCustomActions.CreateAngularTracking(session); - CreateCustomActions.CreateFocusSubSystem(session); CreateCustomActions.CreateSubSystems(session); @@ -252,42 +247,42 @@ internal static void CreateCustomActionSet(Session session) where T : IMyTerm CreateCustomActions.CreateWeaponCameraChannels(session); CreateCustomActions.CreateSelectFriend(session); CreateCustomActions.CreateSelectEnemy(session); + CreateCustomActions.CreateMinSize(session); CreateCustomActions.CreateMaxSize(session); - CreateCustomActions.CreateFriendly(session); - //CreateCustomActions.CreateSelectPosition(session); Suppressed for now as it's inop + //CreateCustomActions.CreateFriendly(session); } internal static void CreateTurretControllerActions(Session session) where T : IMyTerminalBlock { CreateCustomActions.CreateShareFireControlControl(session); CreateCustomActions.CreateAiEnabledControl(session); - CreateCustomActions.CreateNeutralsControl(session); - CreateCustomActions.CreateFriendlyControl(session); - CreateCustomActions.CreateUnownedControl(session); - + CreateCustomActions.CreateControlModesControl(session); CreateCustomActions.CreateMovementStateControl(session); - //CreateCustomActions.CreateShootModeControl(session); + CreateCustomActions.CreateFocusSubSystemControl(session); CreateCustomActions.CreateSubSystemsControl(session); - CreateCustomActions.CreateControlModesControl(session); - CreateCustomActions.CreateProjectilesControl(session); - CreateCustomActions.CreateSupportingPDControl(session); - CreateCustomActions.CreateBiologicalsControl(session); - CreateCustomActions.CreateMeteorsControl(session); - CreateCustomActions.CreateGridsControl(session); + CreateCustomActions.CreateFocusTargetsControl(session); - CreateCustomActions.CreateFocusSubSystemControl(session); - CreateCustomActions.CreateMaxSizeControl(session); - CreateCustomActions.CreateMinSizeControl(session); CreateCustomActions.CreateRepelModeControl(session); + + CreateCustomActions.CreateNeutralsControl(session); + CreateCustomActions.CreateUnownedControl(session); + CreateCustomActions.CreateGridsControl(session); CreateCustomActions.CreateLargeGridControl(session); CreateCustomActions.CreateSmallGridControl(session); + CreateCustomActions.CreateBiologicalsControl(session); + CreateCustomActions.CreateProjectilesControl(session); + CreateCustomActions.CreateSupportingPDControl(session); + CreateCustomActions.CreateMeteorsControl(session); + + CreateCustomActions.CreateMinSizeControl(session); + CreateCustomActions.CreateMaxSizeControl(session); + //CreateCustomActions.CreateFriendlyControl(session); } internal static void CreateSearchlightActions(Session session) where T : IMyTerminalBlock { CreateCustomActions.CreateNeutralsControl(session); - //CreateCustomActions.CreateFriendlyControl(session); //This even work for a "turret"? CreateCustomActions.CreateUnownedControl(session); CreateCustomActions.CreateGridsControl(session); CreateCustomActions.CreateLargeGridControl(session); @@ -295,11 +290,9 @@ internal static void CreateSearchlightActions(Session session) where T : IMyT CreateCustomActions.CreateBiologicalsControl(session); CreateCustomActions.CreateProjectilesControl(session); CreateCustomActions.CreateMeteorsControl(session); - - CreateCustomActions.CreateMaxSizeControl(session); - CreateCustomActions.CreateMinSizeControl(session); - CreateCustomActions.CreateWeaponCameraChannels(session); + CreateCustomActions.CreateMinSizeControl(session); + CreateCustomActions.CreateMaxSizeControl(session); } internal static void CreateCustomActionSetArmorEnhancer(Session session) where T: IMyTerminalBlock From 3ec13817a9daa866763700aae59e664e40068e2b Mon Sep 17 00:00:00 2001 From: Aristeas <94058548+ari-steas@users.noreply.github.com> Date: Wed, 21 May 2025 16:34:40 -0500 Subject: [PATCH 64/77] Prevent WC interference with other mods' hidden controls on sorters --- Data/Scripts/CoreSystems/Session/SessionControls.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index b383a881..8455ff9b 100644 --- a/Data/Scripts/CoreSystems/Session/SessionControls.cs +++ b/Data/Scripts/CoreSystems/Session/SessionControls.cs @@ -625,8 +625,10 @@ internal static void AlterControls(Session session) where T : IMyTerminalBloc var c = controls[i]; if (session.AlteredControls.Contains(c)) continue; - if (!_visibleControls.Contains(c.Id)) { - c.Visible = TerminalHelpers.NotWcBlock; + if (!_visibleControls.Contains(c.Id)) + { + var prevVisibleFunc = c.Visible; // caching to avoid an infinite recursive loop + c.Visible = block => (prevVisibleFunc?.Invoke(block) ?? true) && TerminalHelpers.NotWcBlock(block); session.AlteredControls.Add(c); continue; } From 779dbdadca3ad78ff1967a052abbcbb9afbc6d2f Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Sat, 24 May 2025 18:44:42 -0500 Subject: [PATCH 65/77] Tidy up --- .../SerializedConfigs/AmmoConstants.cs | 28 ------------------- .../CoreSystems/EntityComp/EntityEvents.cs | 2 +- .../EntityComp/Parts/Weapon/WeaponTracking.cs | 8 ++++-- Data/Scripts/CoreSystems/Support/Log.cs | 2 ++ 4 files changed, 9 insertions(+), 31 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 53ecd061..29cf69fd 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -1251,34 +1251,6 @@ private void LoadModifiers(AmmoDef ammoDef, out AmmoOverride overrides, out bool } - //private void GetModifiableValues(AmmoDef ammoDef, out float baseDamage, out float health, out float gravityMultiplier, out float maxTrajectory, out float maxTrajectorySqr, out bool energyBaseDmg, out bool energyAreaDmg, out bool energyDetDmg, out bool energyShieldDmg, out double shieldModifier, out float fallOffDistance, out float fallOffMinMult, out float mass, out float shieldBypassRaw) - //{ - // baseDamage = AmmoModsFound && _modifierMap[BaseDmgStr].HasData() ? _modifierMap[BaseDmgStr].GetAsFloat : ammoDef.BaseDamage; - - // if (baseDamage < 0.000001) - // baseDamage = 0.000001f; - - // health = AmmoModsFound && _modifierMap[HealthStr].HasData() ? _modifierMap[HealthStr].GetAsFloat : ammoDef.Health; - // gravityMultiplier = AmmoModsFound && _modifierMap[GravityStr].HasData() ? _modifierMap[GravityStr].GetAsFloat : ammoDef.Trajectory.GravityMultiplier; - // maxTrajectory = AmmoModsFound && _modifierMap[MaxTrajStr].HasData() ? _modifierMap[MaxTrajStr].GetAsFloat : ammoDef.Trajectory.MaxTrajectory; - // maxTrajectorySqr = maxTrajectory * maxTrajectory; - // energyBaseDmg = AmmoModsFound && _modifierMap[EnergyBaseDmgStr].HasData() ? _modifierMap[EnergyBaseDmgStr].GetAsBool : ammoDef.DamageScales.DamageType.Base != DamageTypes.Damage.Kinetic; - // energyAreaDmg = AmmoModsFound && _modifierMap[EnergyAreaDmgStr].HasData() ? _modifierMap[EnergyAreaDmgStr].GetAsBool : ammoDef.DamageScales.DamageType.AreaEffect != DamageTypes.Damage.Kinetic; - // energyDetDmg = AmmoModsFound && _modifierMap[EnergyDetDmgStr].HasData() ? _modifierMap[EnergyDetDmgStr].GetAsBool : ammoDef.DamageScales.DamageType.Detonation != DamageTypes.Damage.Kinetic; - // energyShieldDmg = AmmoModsFound && _modifierMap[EnergyShieldDmgStr].HasData() ? _modifierMap[EnergyShieldDmgStr].GetAsBool : ammoDef.DamageScales.DamageType.Shield != DamageTypes.Damage.Kinetic; - - // var givenShieldModifier = AmmoModsFound && _modifierMap[ShieldModStr].HasData() ? _modifierMap[ShieldModStr].GetAsDouble : ammoDef.DamageScales.Shields.Modifier; - // shieldModifier = givenShieldModifier < 0 ? 1 : givenShieldModifier; - - // fallOffDistance = AmmoModsFound && _modifierMap[FallOffDistanceStr].HasData() ? _modifierMap[FallOffDistanceStr].GetAsFloat : ammoDef.DamageScales.FallOff.Distance; - // fallOffMinMult = AmmoModsFound && _modifierMap[FallOffMinMultStr].HasData() ? _modifierMap[FallOffMinMultStr].GetAsFloat : ammoDef.DamageScales.FallOff.MinMultipler; - - // mass = AmmoModsFound && _modifierMap[MassStr].HasData() ? _modifierMap[MassStr].GetAsFloat : ammoDef.Mass; - - // shieldBypassRaw = AmmoModsFound && _modifierMap[ShieldBypassStr].HasData() ? _modifierMap[ShieldBypassStr].GetAsFloat : ammoDef.DamageScales.Shields.BypassModifier; - //} - - private int mexLogLevel = 0; private void GetPeakDps(WeaponSystem.AmmoType ammoDef, WeaponSystem system, WeaponDefinition wDef, out float peakDps, out float effectiveDps, out float dpsWoInaccuracy, out float shotsPerSec, out float realShotsPerSec, out float baseDps, out float areaDps, out float detDps, out float realShotsPerMin) { diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index ad23e79c..dfd289cb 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -269,7 +269,7 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str stringBuilder.Append($" {(collection.Count > 1 ? "\n{w.FriendlyName}" : string.Empty)}" + $"{(w.MinTargetDistance > 0 ? $"\n{Localization.GetText("WeaponInfoMinRange")}: {w.MinTargetDistance}{Localization.GetText("WeaponInfoMeter")}" : string.Empty)}" + $"\n{Localization.GetText("WeaponInfoMaxRange")}: {w.MaxTargetDistance}{Localization.GetText("WeaponInfoMeter")}" + - $"\n{Localization.GetText("WeaponInfoROF")}: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin}{Localization.GetText("WeaponInfoPerMin")}"); + $"\n{Localization.GetText("WeaponInfoROF")}: {w.ActiveAmmoDef.AmmoDef.Const.RealShotsPerMin:0.}{Localization.GetText("WeaponInfoPerMin")}"); if(w.ActiveAmmoDef.AmmoDef.Const.RequiresTarget) { var targ = $"{Localization.GetText("WeaponInfoTargetLabel")}: "; diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index b5730967..8f9da23a 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -1024,7 +1024,7 @@ private bool RayCheckTest(double rangeToTargetSqr) var targetPos = pTarget?.Position ?? eTarget?.PositionComp.WorldAABB.Center ?? Vector3D.Zero; var distToTargetSqr = Vector3D.DistanceSquared(targetPos, trackingCheckPosition); - if (distToTargetSqr > MaxTargetDistanceSqr && distToTargetSqr < MinTargetDistanceSqr) + if (distToTargetSqr > MaxTargetDistanceSqr || distToTargetSqr < MinTargetDistanceSqr) //TODO this was &&, will that ever trip for a wep with min and max range? { masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckDistExceeded); if (masterWeapon != this) Target.Reset(Session.I.Tick, Target.States.RayCheckDistExceeded); @@ -1042,7 +1042,11 @@ private bool RayCheckTest(double rangeToTargetSqr) } } IHitInfo rayHitInfo; - Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); + if (distToTargetSqr <= 2500) + Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); + else + Session.I.Physics.CastLongRay(trackingCheckPosition, targetPos, out rayHitInfo, false); //TODO check if this improves voxel detection + RayCallBack.NormalShootRayCallBack(rayHitInfo); return true; diff --git a/Data/Scripts/CoreSystems/Support/Log.cs b/Data/Scripts/CoreSystems/Support/Log.cs index 5a9e4568..8638183c 100644 --- a/Data/Scripts/CoreSystems/Support/Log.cs +++ b/Data/Scripts/CoreSystems/Support/Log.cs @@ -4,6 +4,7 @@ using System.Text; using Sandbox.ModAPI; using VRage.Collections; +using VRage.Utils; namespace CoreSystems.Support { @@ -135,6 +136,7 @@ public static void Init(string name, Session session, bool defaultInstance = tru catch (Exception e) { MyAPIGateway.Utilities.ShowNotification(e.Message, 5000); + MyLog.Default.Error($"WC failed to initialize log {name} \n {e.Message}"); } } From 63b72d589bb65e444511dd08c83279fa910841d4 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 27 May 2025 08:28:20 -0500 Subject: [PATCH 66/77] Updated - Added option in Smarts to "IgnoreAntiSmarts" - Fixed Energy ammo reloading, mags to load was ignored but is now factored in to the amount of energy ammo that is "loaded" per reload event - Fixed corner case of virtual beam updated throwing an exception if the firing grid was deleted while hit checks were waiting on a thread. --- .../CoreSystems/Api/CoreSystemsApiBase.cs | 2 +- .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 2 ++ .../Definitions/CoreDefinitions.cs | 1 + .../SerializedConfigs/AmmoConstants.cs | 6 ++-- .../EntityComp/Parts/Weapon/WeaponReload.cs | 31 +++++++++---------- .../CoreSystems/Projectiles/Projectile.cs | 10 +++--- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs index 01bd9f08..69c0f195 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs @@ -12,7 +12,7 @@ namespace CoreSystems.Api { /// - /// https://github.com/sstixrud/CoreSystems/blob/master/BaseData/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs + /// https://github.com/Ash-LikeSnow/WeaponCore/blob/master/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs /// public partial class WcApi { diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index 5d4f71d7..b824508e 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -1232,6 +1232,8 @@ internal static void UpdateVirtualBeams(Projectile p, ProInfo info, HitEntity hi for (int v = 0; v < p.VrPros.Count; v++) { + if (p.State == ProjectileState.Dead) + break; var vp = p.VrPros[v]; var vs = vp.AvShot; diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index d88b4233..ac63e859 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -1341,6 +1341,7 @@ public struct SmartsDef [ProtoMember(23)] internal double MinTurnSpeed; [ProtoMember(24)] internal bool NoTargetApproach; [ProtoMember(25)] internal bool AltNavigation; + [ProtoMember(26)] internal bool IgnoreAntiSmarts; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 29cf69fd..de9e78a7 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -249,6 +249,7 @@ public enum Texture public readonly bool ProjectilesFirst; public readonly bool OnHit; public readonly bool OverrideWeaponEffect; + public readonly bool IgnoreAntiSmarts; public readonly float LargeGridDmgScale; public readonly float SmallGridDmgScale; public readonly float OffsetRatio; @@ -405,7 +406,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon 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, - out Aggressiveness, out NavAcceleration, out MinTurnSpeedSqr, out OffsetRatio, out MaxChaseTime, out MaxTargets, out OffsetTime); + out Aggressiveness, out NavAcceleration, out MinTurnSpeedSqr, out OffsetRatio, out MaxChaseTime, out MaxTargets, out OffsetTime, out IgnoreAntiSmarts); IsGuided = TravelTo || IsMine || IsDrone || IsSmart; @@ -598,7 +599,7 @@ internal void Purge() private void ComputeSmarts(WeaponSystem.AmmoType ammo, out bool isSmart, out bool roam, out bool noTargetApproach, out bool accelClearance, out bool overrideTarget, out bool targetOffSet, out bool focusOnly, out bool focusEviction, out bool noSteering, out bool advancedSmartSteering, out bool keepAliveAfterTargetLoss, out bool noTargetExpire, out bool zeroEffortNav, out double scanRange, out double offsetMinRangeSqr, - out double aggressiveness, out double navAcceleration, out double minTurnSpeedSqr, out float offsetRatio, out int maxChaseTime, out int maxTargets, out int offsetTime) + out double aggressiveness, out double navAcceleration, out double minTurnSpeedSqr, out float offsetRatio, out int maxChaseTime, out int maxTargets, out int offsetTime, out bool ignoreAntiSmarts) { isSmart = ammo.AmmoDef.Trajectory.Guidance == TrajectoryDef.GuidanceType.Smart || ammo.AmmoDef.Trajectory.Guidance == TrajectoryDef.GuidanceType.DetectSmart; @@ -633,6 +634,7 @@ private void ComputeSmarts(WeaponSystem.AmmoType ammo, out bool isSmart, out boo offsetTime = ammo.AmmoDef.Trajectory.Smarts.OffsetTime; noTargetApproach = ammo.AmmoDef.Trajectory.Smarts.NoTargetApproach; zeroEffortNav = ammo.AmmoDef.Trajectory.Smarts.AltNavigation; + ignoreAntiSmarts = ammo.AmmoDef.Trajectory.Smarts.IgnoreAntiSmarts; } diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs index f6daf7f2..d4e45f06 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponReload.cs @@ -1,5 +1,6 @@ using System; using CoreSystems.Support; +using VRage.Game.Entity; using VRage.Utils; using static CoreSystems.Support.CoreComponent; using static CoreSystems.Support.WeaponDefinition.AnimationDef.PartAnimationSetDef; @@ -255,35 +256,31 @@ internal bool ServerReload() ++ClientStartId; ++Reload.LifetimeLoads; - if (!ActiveAmmoDef.AmmoDef.Const.EnergyAmmo) { - + if (!ActiveAmmoDef.AmmoDef.Const.EnergyAmmo) + { var isPhantom = Comp.TypeSpecific == CompTypeSpecific.Phantom; Reload.MagsLoaded = ActiveAmmoDef.AmmoDef.Const.MagsToLoad <= Reload.CurrentMags || Session.I.IsCreative ? ActiveAmmoDef.AmmoDef.Const.MagsToLoad : Reload.CurrentMags; - - if (!Session.I.IsCreative) + + if (!Session.I.IsCreative && !isPhantom) { - if (!isPhantom && Comp.CoreInventory.ItemsCanBeRemoved(Reload.MagsLoaded, ActiveAmmoDef.AmmoDef.Const.AmmoItem)) + if (Comp.CoreInventory.ItemsCanBeRemoved(Reload.MagsLoaded, ActiveAmmoDef.AmmoDef.Const.AmmoItem)) { - if (System.HasAmmoSelection) { - var magItem = Comp.CoreInventory.FindItem(ActiveAmmoDef.AmmoDefinitionId) ?? ActiveAmmoDef.AmmoDef.Const.AmmoItem; - Comp.CoreInventory.RemoveItems(magItem.ItemId, Reload.MagsLoaded); - } + MyPhysicalInventoryItem magItem; + if (System.HasAmmoSelection) + magItem = Comp.CoreInventory.FindItem(ActiveAmmoDef.AmmoDefinitionId) ?? ActiveAmmoDef.AmmoDef.Const.AmmoItem; else - { - var magItem = Comp.TypeSpecific == CompTypeSpecific.Rifle ? Comp.CoreInventory.FindItem(ActiveAmmoDef.AmmoDefinitionId) ?? ActiveAmmoDef.AmmoDef.Const.AmmoItem : ActiveAmmoDef.AmmoDef.Const.AmmoItem; - Comp.CoreInventory.RemoveItems(magItem.ItemId, Reload.MagsLoaded); - } + magItem = Comp.TypeSpecific == CompTypeSpecific.Rifle ? Comp.CoreInventory.FindItem(ActiveAmmoDef.AmmoDefinitionId) ?? ActiveAmmoDef.AmmoDef.Const.AmmoItem : ActiveAmmoDef.AmmoDef.Const.AmmoItem; + Comp.CoreInventory.RemoveItems(magItem.ItemId, Reload.MagsLoaded); } - else if (!isPhantom && Comp.CoreInventory.ItemCount > 0 && Comp.CoreInventory.ContainItems(Reload.MagsLoaded, ActiveAmmoDef.AmmoDef.Const.AmmoItem.Content)) - { + else if (Comp.CoreInventory.ItemCount > 0 && Comp.CoreInventory.ContainItems(Reload.MagsLoaded, ActiveAmmoDef.AmmoDef.Const.AmmoItem.Content)) Comp.CoreInventory.Remove(ActiveAmmoDef.AmmoDef.Const.AmmoItem, Reload.MagsLoaded); - } } - Reload.CurrentMags = !isPhantom ? Comp.CoreInventory.GetItemAmount(ActiveAmmoDef.AmmoDefinitionId).ToIntSafe() : Reload.CurrentMags - Reload.MagsLoaded; if (Reload.CurrentMags == 0) CheckInventorySystem = true; } + else + Reload.MagsLoaded = ActiveAmmoDef.AmmoDef.Const.MagsToLoad; StartReload(); return true; diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index a2a452b5..a3c8a3ac 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -3399,11 +3399,11 @@ internal void EwarEffects() var nStorage = netted.Info.Storage; var nAconst = netted.Info.AmmoDef.Const; - if (nStorage.RequestedStage >= 0 && nStorage.RequestedStage < nAconst.ApproachesCount && nAconst.Approaches[nStorage.RequestedStage].IgnoreAntiSmart) + if (nAconst.IgnoreAntiSmarts || (nStorage.RequestedStage >= 0 && nStorage.RequestedStage < nAconst.ApproachesCount && nAconst.Approaches[nStorage.RequestedStage].IgnoreAntiSmart)) continue; if (Info.Random.NextDouble() * 100f < aConst.PulseChance || !aConst.EwarField) { - Info.BaseEwarPool -= (float)netted.Info.AmmoDef.Const.HealthHitModifier; + Info.BaseEwarPool -= (float)nAconst.HealthHitModifier; if (Info.BaseEwarPool <= 0 && Info.BaseHealthPool-- > 0) { Info.EwarActive = true; @@ -3432,13 +3432,13 @@ internal void EwarEffects() var nStorage = netted.Info.Storage; var nAconst = netted.Info.AmmoDef.Const; - if (nStorage.RequestedStage >= 0 && nStorage.RequestedStage < nAconst.ApproachesCount && nAconst.Approaches[nStorage.RequestedStage].IgnoreAntiSmart) + if (nAconst.IgnoreAntiSmarts || (nStorage.RequestedStage >= 0 && nStorage.RequestedStage < nAconst.ApproachesCount && nAconst.Approaches[nStorage.RequestedStage].IgnoreAntiSmart)) continue; if (Info.Random.NextDouble() * 100f < aConst.PulseChance || !aConst.EwarField) { - if (Info.BaseEwarPool - netted.Info.AmmoDef.Const.Health >= 0) + if (Info.BaseEwarPool - nAconst.Health >= 0) { - Info.BaseEwarPool -= netted.Info.AmmoDef.Const.Health; + Info.BaseEwarPool -= nAconst.Health; Info.EwarActive = true; netted.Info.Target.TargetObject = this; netted.Info.Target.TargetState = Target.TargetStates.IsProjectile; From 1683860dab9f0b5e0b34bb48d203c295548cf0a9 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 28 May 2025 14:37:42 -0500 Subject: [PATCH 67/77] Rep on damage SP fix --- .../CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs | 2 +- Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs | 9 +++------ Data/Scripts/CoreSystems/Session/SessionEwar.cs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs index f3acec35..f89020c1 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponShoot.cs @@ -360,7 +360,7 @@ private void GiveUpTarget() private void OverHeat() { - if (!Session.I.IsClient && Comp.Data.Repo.Values.Set.Overload > 1) + if (Session.I.IsServer && Comp.Data.Repo.Values.Set.Overload > 1) { var dmg = .02f * Comp.MaxIntegrity; Comp.Slim.DoDamage(dmg, MyDamageType.Environment, true, null, Comp.TopEntity.EntityId); diff --git a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs index 76c650eb..8d885d96 100644 --- a/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs +++ b/Data/Scripts/CoreSystems/Session/SessionDamageMgr.cs @@ -124,7 +124,6 @@ internal void ProcessHits() internal readonly List Clean = new List(); internal void DefferedDestroy() { - var sync = MpActive && (DedicatedServer || IsServer); Clean.Clear(); _destroyedSlims.Clear(); foreach (var d in DeferredDestroy) @@ -146,7 +145,7 @@ internal void DefferedDestroy() { var info = collection[i]; if (!info.Block.IsDestroyed) - info.Block.DoDamage(info.ScaledDamage, info.DamageType, sync, null, info.AttackerId, 0, info.DetonateAmmo); + info.Block.DoDamage(info.ScaledDamage, info.DamageType, IsServer, null, info.AttackerId, 0, info.DetonateAmmo); } collection.Clear(); } @@ -766,7 +765,7 @@ private void DamageGrid(HitEntity hitEnt, ProInfo t) if (!deadBlock || gridBlockCount < 2500) { - block.DoDamage(scaledDamage, damageType, DedicatedServer, null, attackerId); + block.DoDamage(scaledDamage, damageType, IsServer, null, attackerId); var remainingHp = blockHp - scaledDamage; @@ -916,8 +915,6 @@ private void DamageDestObj(HitEntity hitEnt, ProInfo info) var areaDmgGlobal = Settings.Enforcement.AreaDamageModifer; var shieldHeal = info.AmmoDef.DamageScales.Shields.Type == ShieldDef.ShieldType.Heal; - var sync = MpActive && IsServer; - var attackerId = info.Weapon.Comp.CoreEntity.EntityId; var objHp = destObj.Integrity; @@ -964,7 +961,7 @@ private void DamageDestObj(HitEntity hitEnt, ProInfo info) info.ProHits = info.ProHits != null && ProHitPool.Count > 0 ? ProHitPool.Pop() : new List>(); info.ProHits.Add(new MyTuple(hitEnt.Intersection.To, hitEnt.Entity, (float)scaledDamage)); } - destObj.DoDamage(scaledDamage, !info.ShieldBypassed ? MyDamageType.Bullet : MyDamageType.Drill, sync, null, attackerId); + destObj.DoDamage(scaledDamage, !info.ShieldBypassed ? MyDamageType.Bullet : MyDamageType.Drill, IsServer, null, attackerId); } if (info.AmmoDef.Const.Mass > 0) diff --git a/Data/Scripts/CoreSystems/Session/SessionEwar.cs b/Data/Scripts/CoreSystems/Session/SessionEwar.cs index ebe70785..49b0b90f 100644 --- a/Data/Scripts/CoreSystems/Session/SessionEwar.cs +++ b/Data/Scripts/CoreSystems/Session/SessionEwar.cs @@ -269,7 +269,7 @@ private void ComputeEffects(MyCubeGrid grid, AmmoDef ammoDef, double damagePool, { if (scaledDamage < blockHp || gridBlockCount < 1000) { - block.DoDamage((float) scaledDamage, MyDamageType.Explosion, sync, null, attackerId, 0, false); + block.DoDamage((float) scaledDamage, MyDamageType.Explosion, true, null, attackerId, 0, false); } else { From 57aa4f193a197d09bad5fbcb4b1ab59ce70a4e79 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 29 May 2025 16:31:44 -0500 Subject: [PATCH 68/77] Hit options --- .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 13 +++- Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 71 +++++++++++++------ .../Definitions/CoreDefinitions.cs | 2 + .../SerializedConfigs/AmmoConstants.cs | 12 ++-- .../CoreSystems/Projectiles/Projectile.cs | 10 +++ 5 files changed, 82 insertions(+), 26 deletions(-) diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index b824508e..41d14e93 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -59,6 +59,8 @@ public AvShot(Session session) internal bool ForceHitParticle; internal bool HitParticleActive; internal bool ShieldHitParticleActive; + internal bool VoxelHitParticleActive; + internal bool WaterHitParticleActive; internal bool MarkForClose; internal bool ProEnded; internal bool AccelClearance; @@ -130,6 +132,8 @@ internal enum ParticleState None, Custom, Shield, + Water, + Voxel, Dirty, } @@ -388,7 +392,10 @@ internal static void DeferedAvStateUpdates() var lineOnScreen = a.OnScreen > (Screen)2; - if (!a.Active && (a.OnScreen != Screen.None || a.HitSoundInitted || a.TravelSound || aConst.AmmoParticleNoCull || saveHit && (a.Hit.EventType == HitEntity.Type.Shield && aConst.ShieldHitParticleNoCull) || aConst.HitParticleNoCull) || aConst.FieldParticle && aConst.FieldParticleNoCull) { + var anyHitParticleNoCull = saveHit && (aConst.HitParticleNoCull || aConst.ShieldHitParticleNoCull || aConst.VoxelHitParticleNoCull || aConst.WaterHitParticleNoCull); + + if (!a.Active && (a.OnScreen != Screen.None || a.HitSoundInitted || a.TravelSound || aConst.AmmoParticleNoCull || anyHitParticleNoCull || aConst.FieldParticle && aConst.FieldParticleNoCull)) + { a.Active = true; s.Av.AvShots.Add(a); } @@ -972,6 +979,10 @@ internal void HitEffects(bool force = false) if (OnScreen == Screen.Tracer) { if (LastHitShield && ShieldHitParticleActive && AmmoDef.Const.ShieldHitParticle && (AmmoDef.Const.ShieldHitParticleNoCull || distToCameraSqr < 360000)) HitParticle = ParticleState.Shield; + else if (WaterHitParticleActive && Hit.EventType == HitEntity.Type.Water && (AmmoDef.Const.WaterHitParticleNoCull || distToCameraSqr < 360000)) + HitParticle = ParticleState.Water; + else if (VoxelHitParticleActive && Hit.EventType == HitEntity.Type.Voxel && (AmmoDef.Const.VoxelHitParticleNoCull || distToCameraSqr < 360000)) + HitParticle = ParticleState.Voxel; else if (HitParticleActive && AmmoDef.Const.HitParticle && !(LastHitShield && !AmmoDef.AmmoGraphics.Particles.Hit.ApplyToShield) && (AmmoDef.Const.HitParticleNoCull || distToCameraSqr < 360000)) HitParticle = ParticleState.Custom; } diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index f41abe3d..79013d0c 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -11,6 +11,7 @@ using VRageRender; using static VRageRender.MyBillboard; using static CoreSystems.Support.WeaponDefinition.AmmoDef.GraphicDef.LineDef; +using static CoreSystems.Support.WeaponDefinition; namespace CoreSystems.Support { class RunAv @@ -138,38 +139,66 @@ internal void End() else av.TravelEmitter.SetPosition(av.TracerFront); } - if (av.HitParticle == AvShot.ParticleState.Custom || av.HitParticle == AvShot.ParticleState.Shield) + if (!(av.HitParticle == AvShot.ParticleState.Dirty || av.HitParticle == AvShot.ParticleState.None)) { if (av.OnScreen != AvShot.Screen.None) { var pos = Session.I.Tick - av.Hit.HitTick <= 1 && !MyUtils.IsZero(av.Hit.SurfaceHit) ? av.Hit.SurfaceHit : av.TracerFront; - var particle = av.HitParticle == AvShot.ParticleState.Shield ? av.AmmoDef.AmmoGraphics.Particles.ShieldHit : av.AmmoDef.AmmoGraphics.Particles.Hit; + ParticleDef particle; + bool gravPerp = false; MatrixD matrix = MatrixD.CreateTranslation(pos); - if (av.HitParticle == AvShot.ParticleState.Shield) + switch (av.HitParticle) { - if(av.ShieldHitAngle == Vector3D.Zero) + case AvShot.ParticleState.Shield: + particle = av.AmmoDef.AmmoGraphics.Particles.ShieldHit; + if (av.ShieldHitAngle == Vector3D.Zero) + { + var grid = av.Hit.Entity.GetTopMostParent() as IMyCubeGrid; + var lineToShield = pos - grid.PositionComp.WorldAABB.Center; + lineToShield.Normalize(); + matrix = MatrixD.CreateWorld(pos, lineToShield, Vector3D.CalculatePerpendicularVector(lineToShield)); + } + else + matrix = MatrixD.CreateWorld(pos, av.ShieldHitAngle, Vector3D.CalculatePerpendicularVector(av.ShieldHitAngle)); + break; + case AvShot.ParticleState.Water: + particle = av.AmmoDef.AmmoGraphics.Particles.WaterHit; + gravPerp = true; + break; + case AvShot.ParticleState.Voxel: + particle = av.AmmoDef.AmmoGraphics.Particles.VoxelHit; + gravPerp = true; + break; + case AvShot.ParticleState.Custom: + default: + particle = av.AmmoDef.AmmoGraphics.Particles.Hit; + if (particle.Offset == Vector3D.MaxValue) + matrix = MatrixD.CreateWorld(pos, av.VisualDir, Vector3D.CalculatePerpendicularVector(av.VisualDir)); + else if (particle.Offset == Vector3D.MinValue) + gravPerp = true; + break; + } + + if (gravPerp) + { + if (av.Weapon?.Comp?.Ai?.MyPlanet != null) { - var grid = av.Hit.Entity.GetTopMostParent() as IMyCubeGrid; - var lineToShield = pos - grid.PositionComp.WorldAABB.Center; - lineToShield.Normalize(); - matrix = MatrixD.CreateWorld(pos, lineToShield, Vector3D.CalculatePerpendicularVector(lineToShield)); + var planetDir = pos - av.Weapon.Comp.Ai.MyPlanet.PositionComp.WorldAABB.Center; + planetDir.Normalize(); + matrix = MatrixD.CreateWorld(pos, Vector3D.CalculatePerpendicularVector(planetDir), -planetDir); } else - matrix = MatrixD.CreateWorld(pos, av.ShieldHitAngle, Vector3D.CalculatePerpendicularVector(av.ShieldHitAngle)); - } - else if (particle.Offset == Vector3D.MaxValue) - matrix = MatrixD.CreateWorld(pos, av.VisualDir, Vector3D.CalculatePerpendicularVector(av.VisualDir)); - 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); + { + 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.HitParticle == AvShot.ParticleState.Shield ? av.AmmoDef.Const.ShieldHitParticleStr : av.AmmoDef.Const.HitParticleStr, ref matrix, ref pos, uint.MaxValue, out hitEffect)) + if (MyParticlesManager.TryCreateParticleEffect(particle.Name, ref matrix, ref pos, uint.MaxValue, out hitEffect)) { hitEffect.UserScale = particle.Extras.Scale; var tickVelo = av.Hit.HitVelocity / 60; @@ -214,7 +243,7 @@ internal void End() } } - if (av.Hit.EventType == HitEntity.Type.Water) + if (av.Hit.EventType == HitEntity.Type.Water && !av.AmmoDef.Const.WaterHitParticle) { var splashHit = av.Hit.SurfaceHit; var ammoInfo = av.AmmoDef; diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index ac63e859..f338a160 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -824,6 +824,8 @@ public struct AmmoParticleDef [ProtoMember(3)] internal ParticleDef Eject; [ProtoMember(4)] internal ParticleDef WeaponEffect1Override; [ProtoMember(5)] internal ParticleDef ShieldHit; + [ProtoMember(6)] internal ParticleDef VoxelHit; + [ProtoMember(7)] internal ParticleDef WaterHit; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index de9e78a7..3656fe30 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -76,8 +76,6 @@ public enum Texture public readonly Vector4 LinearSegmentColorEnd; public readonly Vector4 LinearTrailColor; public readonly string ModelPath; - public readonly string HitParticleStr; - public readonly string ShieldHitParticleStr; public readonly string DetParticleStr; public readonly string DetSoundStr; public readonly string ShotSoundStr; @@ -138,6 +136,8 @@ public enum Texture public readonly bool AmmoParticle; public readonly bool HitParticle; public readonly bool ShieldHitParticle; + public readonly bool VoxelHitParticle; + public readonly bool WaterHitParticle; public readonly bool CustomDetParticle; public readonly bool FieldParticle; public readonly bool AmmoSkipAccel; @@ -164,6 +164,8 @@ public enum Texture public readonly bool FieldParticleNoCull; public readonly bool HitParticleNoCull; public readonly bool ShieldHitParticleNoCull; + public readonly bool WaterHitParticleNoCull; + public readonly bool VoxelHitParticleNoCull; public readonly bool DrawLine; public readonly bool Ewar; public readonly bool NonAntiSmartEwar; @@ -416,13 +418,15 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon AmmoParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.Ammo.DisableCameraCulling; HitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.Hit.DisableCameraCulling; ShieldHitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.DisableCameraCulling; + VoxelHitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.VoxelHit.DisableCameraCulling; + WaterHitParticleNoCull = ammo.AmmoDef.AmmoGraphics.Particles.WaterHit.DisableCameraCulling; FieldParticleNoCull = ammo.AmmoDef.Ewar.Field.Particle.DisableCameraCulling; AmmoParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Ammo.Name); HitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name); - HitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.Hit.Name; ShieldHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.Name); - ShieldHitParticleStr = ammo.AmmoDef.AmmoGraphics.Particles.ShieldHit.Name; + VoxelHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.VoxelHit.Name); + WaterHitParticle = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.WaterHit.Name); EndOfLifeAv = !ammo.AmmoDef.AreaOfDamage.EndOfLife.NoVisuals && ammo.AmmoDef.AreaOfDamage.EndOfLife.Enable; OverrideWeaponEffect = !string.IsNullOrEmpty(ammo.AmmoDef.AmmoGraphics.Particles.WeaponEffect1Override.Name); diff --git a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs index a3c8a3ac..32f7ef7e 100644 --- a/Data/Scripts/CoreSystems/Projectiles/Projectile.cs +++ b/Data/Scripts/CoreSystems/Projectiles/Projectile.cs @@ -313,6 +313,16 @@ internal void Start() var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.ShieldHit.Extras.HitPlayChance; Info.AvShot.ShieldHitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); } + if (aConst.VoxelHitParticle || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) + { + var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.VoxelHit.Extras.HitPlayChance; + Info.AvShot.VoxelHitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); + } + if (aConst.WaterHitParticle || aConst.EndOfLifeAoe && !ammoDef.AreaOfDamage.EndOfLife.NoVisuals) + { + var hitPlayChance = Info.AmmoDef.AmmoGraphics.Particles.WaterHit.Extras.HitPlayChance; + Info.AvShot.WaterHitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); + } if (aConst.PrimeModel || aConst.TriggerModel) { From 69308a3cfa30896536a1b05a3a50bc17f907fd6a Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 29 May 2025 20:54:46 -0500 Subject: [PATCH 69/77] no more longraycast --- .../CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs | 5 +---- .../CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs index 8f9da23a..590f9580 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTracking.cs @@ -1042,10 +1042,7 @@ private bool RayCheckTest(double rangeToTargetSqr) } } IHitInfo rayHitInfo; - if (distToTargetSqr <= 2500) - Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); - else - Session.I.Physics.CastLongRay(trackingCheckPosition, targetPos, out rayHitInfo, false); //TODO check if this improves voxel detection + Session.I.Physics.CastRay(trackingCheckPosition, targetPos, out rayHitInfo); RayCallBack.NormalShootRayCallBack(rayHitInfo); diff --git a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs index 6ce16c40..261ea475 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Parts/Weapon/WeaponTypes.cs @@ -47,6 +47,7 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) { masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + Weapon.PauseShoot = true; return; } } @@ -62,6 +63,7 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) return; masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckMiss); + Weapon.PauseShoot = true; return; } } @@ -80,6 +82,7 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) { masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckVoxel); + Weapon.PauseShoot = true; return; } @@ -96,6 +99,7 @@ public void NormalShootRayCallBack(IHitInfo hitInfo) { masterWeapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); if (masterWeapon != Weapon) Weapon.Target.Reset(Session.I.Tick, Target.States.RayCheckFriendly); + Weapon.PauseShoot = true; return; } return; From 908cb7c8f6900e59a1a88b833cb041050f51618e Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:59:56 -0500 Subject: [PATCH 70/77] Updates --- .../CoreSystems/Api/CoreSystemsPbApi.cs | 2 + .../Scripts/CoreSystems/AudioVisual/AvShot.cs | 40 +++++++------------ Data/Scripts/CoreSystems/AudioVisual/RunAv.cs | 11 ----- .../Definitions/CoreDefinitions.cs | 1 + .../SerializedConfigs/AmmoConstants.cs | 10 ++++- 5 files changed, 25 insertions(+), 39 deletions(-) diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs index 6e2c06e6..8a414678 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs @@ -8,6 +8,8 @@ namespace CoreSystems.Api { + // WC PB API USERS: Copy the class below into your script + /// /// https://github.com/Ash-LikeSnow/WeaponCore/blob/master/Data/Scripts/CoreSystems/Api/CoreSystemsPbApi.cs /// diff --git a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs index 41d14e93..19765163 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/AvShot.cs @@ -971,12 +971,11 @@ internal void HitEffects(bool force = false) double distToCameraSqr; Vector3D.DistanceSquared(ref Hit.SurfaceHit, ref Session.CameraPos, out distToCameraSqr); - if (Hit.EventType == HitEntity.Type.Water) - { + if (Hit.EventType == HitEntity.Type.Water && !AmmoDef.Const.WaterHitParticle) HitParticleActive = true; - } - if (OnScreen == Screen.Tracer) { + if (OnScreen != Screen.None) + { if (LastHitShield && ShieldHitParticleActive && AmmoDef.Const.ShieldHitParticle && (AmmoDef.Const.ShieldHitParticleNoCull || distToCameraSqr < 360000)) HitParticle = ParticleState.Shield; else if (WaterHitParticleActive && Hit.EventType == HitEntity.Type.Water && (AmmoDef.Const.WaterHitParticleNoCull || distToCameraSqr < 360000)) @@ -987,43 +986,32 @@ internal void HitEffects(bool force = false) HitParticle = ParticleState.Custom; } - var hitSound = AmmoDef.Const.HitSound && HitSoundActive && distToCameraSqr < AmmoDef.Const.HitSoundDistSqr && (!LastHitShield || AmmoDef.AmmoAudio.HitPlayShield); - if (hitSound) { - + if (hitSound) + { MySoundPair pair = null; - var shield = Hit.Entity as IMyUpgradeModule; - var voxel = Hit.Entity as MyVoxelBase; - var player = Hit.Entity as IMyCharacter; - var floating = Hit.Entity as MyFloatingObject; - if (voxel != null && AmmoDef.Const.VoxelSound) { + if (AmmoDef.Const.VoxelSound && Hit.EventType == HitEntity.Type.Voxel) pair = AmmoDef.Const.VoxelSoundPair; - } - else if (player != null && AmmoDef.Const.PlayerSound) { + else if (AmmoDef.Const.PlayerSound && Hit.Entity is IMyCharacter) pair = AmmoDef.Const.PlayerSoundPair; - } - else if (floating != null && AmmoDef.Const.FloatingSound) { + else if (AmmoDef.Const.FloatingSound && Hit.Entity is MyFloatingObject) pair = AmmoDef.Const.FloatingSoundPair; - } - else if (shield != null && AmmoDef.Const.ShieldSound) { + else if (AmmoDef.Const.ShieldSound && LastHitShield) pair = AmmoDef.Const.ShieldSoundPair; - } - else if (AmmoDef.Const.HitSound) { + else if (AmmoDef.Const.WaterSound && Hit.EventType == HitEntity.Type.Water) + pair = AmmoDef.Const.WaterSoundPair; + else if (AmmoDef.Const.HitSound) pair = AmmoDef.Const.HitSoundPair; - } - - if (pair != null) { + if (pair != null) + { var hitEmitter = Session.Av.PersistentEmitters.Count > 0 ? Session.Av.PersistentEmitters.Pop() : new MyEntity3DSoundEmitter(null); - var pos = Session.Tick - Hit.HitTick <= 1 && !MyUtils.IsZero(Hit.SurfaceHit) ? Hit.SurfaceHit : TracerFront; hitEmitter.Entity = Hit.Entity; hitEmitter.SetPosition(pos); hitEmitter.PlaySound(pair); - Session.SoundsToClean.Add(new Session.CleanSound { DelayedReturn = true, Emitter = hitEmitter, Pair = pair, EmitterPool = Session.I.Av.PersistentEmitters, SpawnTick = Session.I.Tick }); - HitSoundInitted = true; } } diff --git a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs index 79013d0c..81691626 100644 --- a/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs +++ b/Data/Scripts/CoreSystems/AudioVisual/RunAv.cs @@ -243,17 +243,6 @@ internal void End() } } - if (av.Hit.EventType == HitEntity.Type.Water && !av.AmmoDef.Const.WaterHitParticle) - { - var splashHit = av.Hit.SurfaceHit; - var ammoInfo = av.AmmoDef; - var radius = ammoInfo.Const.CollisionSize > ammoInfo.Const.LargestHitSize ? (float)ammoInfo.Const.CollisionSize : (float)ammoInfo.Const.LargestHitSize; - if (radius < 3) - radius = 3; - - WaterModAPI.CreateSplash(splashHit, radius, true); - } - if (av.Model != AvShot.ModelState.None) { if (av.AmmoEffect != null && av.AmmoDef.Const.AmmoParticle && av.AmmoDef.Const.PrimeModel) diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index f338a160..e7c034c7 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -1278,6 +1278,7 @@ public struct AmmoAudioDef [ProtoMember(7)] internal string FloatingHitSound; [ProtoMember(8)] internal string ShieldHitSound; [ProtoMember(9)] internal string ShotSound; + [ProtoMember(10)] internal string WaterHitSound; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 3656fe30..60ce8d7c 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -49,6 +49,7 @@ public enum Texture public readonly MySoundPair VoxelSoundPair; public readonly MySoundPair PlayerSoundPair; public readonly MySoundPair FloatingSoundPair; + public readonly MySoundPair WaterSoundPair; public readonly MyAmmoMagazineDefinition MagazineDef; public readonly ApproachConstants[] Approaches; public readonly AmmoDef[] AmmoPattern; @@ -235,6 +236,7 @@ public enum Texture public readonly bool PlayerSound; public readonly bool FloatingSound; public readonly bool ShieldSound; + public readonly bool WaterSound; public readonly bool IsDrone; public readonly bool IsSmart; public readonly bool AccelClearance; @@ -505,7 +507,7 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon Energy(ammo, system, wDef, out EnergyAmmo, out MustCharge, out Reloadable, out EnergyCost, out EnergyMagSize, out ChargSize, out BurstMode, out HasShotReloadDelay, out PowerPerTick); Sound(ammo, system,out HitSound, out HitSoundPair, out AmmoTravelSound, out TravelSoundPair, out ShotSound, out ShotSoundPair, out DetonationSound, out DetSoundPair, out HitSoundDistSqr, out AmmoTravelSoundDistSqr, out AmmoSoundMaxDistSqr, - out ShotSoundDistSqr, out DetonationSoundDistSqr, out ShotSoundStr, out VoxelSound, out VoxelSoundPair, out FloatingSound, out FloatingSoundPair, out PlayerSound, out PlayerSoundPair, out ShieldSound, out ShieldSoundPair); + out ShotSoundDistSqr, out DetonationSoundDistSqr, out ShotSoundStr, out VoxelSound, out VoxelSoundPair, out FloatingSound, out FloatingSoundPair, out PlayerSound, out PlayerSoundPair, out ShieldSound, out ShieldSoundPair, out WaterSound, out WaterSoundPair); MagazineSize = EnergyAmmo ? EnergyMagSize : MagazineDef.Capacity; @@ -1107,7 +1109,8 @@ private void Energy(WeaponSystem.AmmoType ammoPair, WeaponSystem system, WeaponD private void Sound(WeaponSystem.AmmoType ammo, WeaponSystem system, out bool hitSound, out MySoundPair hitSoundPair, out bool ammoTravelSound, out MySoundPair travelSoundPair, out bool shotSound, out MySoundPair shotSoundPair, out bool detSound, out MySoundPair detSoundPair, out float hitSoundDistSqr, out float ammoTravelSoundDistSqr, out float ammoSoundMaxDistSqr, out float shotSoundDistSqr, out float detSoundDistSqr, out string rawShotSoundStr, - out bool voxelSound, out MySoundPair voxelSoundPair, out bool floatingSound, out MySoundPair floatingSoundPair, out bool playerSound, out MySoundPair playerSoundPair, out bool shieldSound, out MySoundPair shieldSoundPair) + out bool voxelSound, out MySoundPair voxelSoundPair, out bool floatingSound, out MySoundPair floatingSoundPair, out bool playerSound, out MySoundPair playerSoundPair, out bool shieldSound, out MySoundPair shieldSoundPair, + out bool waterSound, out MySoundPair waterSoundPair) { var ammoDef = ammo.AmmoDef; var weaponShotSound = !string.IsNullOrEmpty(system.Values.HardPoint.Audio.FiringSound); @@ -1182,6 +1185,9 @@ private void Sound(WeaponSystem.AmmoType ammo, WeaponSystem system, out bool hit shieldSound = !string.IsNullOrEmpty(ammoDef.AmmoAudio.ShieldHitSound); shieldSoundPair = shieldSound ? new MySoundPair(ammoDef.AmmoAudio.ShieldHitSound, false) : hitSound ? new MySoundPair(ammoDef.AmmoAudio.HitSound, false) : null; + + waterSound = !string.IsNullOrEmpty(ammoDef.AmmoAudio.WaterHitSound); + waterSoundPair = waterSound ? new MySoundPair(ammoDef.AmmoAudio.WaterHitSound, false) : null; } private MyEntity PrimeEntityActivator() From 227d0d749da64737104a794f6883c999d7637cf0 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Mon, 2 Jun 2025 23:34:36 -0500 Subject: [PATCH 71/77] Hud mode feedback --- Data/Scripts/CoreSystems/Ui/UiInput.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Data/Scripts/CoreSystems/Ui/UiInput.cs b/Data/Scripts/CoreSystems/Ui/UiInput.cs index 68afb975..b5ca548d 100644 --- a/Data/Scripts/CoreSystems/Ui/UiInput.cs +++ b/Data/Scripts/CoreSystems/Ui/UiInput.cs @@ -299,6 +299,8 @@ internal void UpdateInputState() set.ClientConfig.MinimalHud = !set.ClientConfig.MinimalHud; set.ClientConfig.HideReload = false; } + s.ShowLocalNotify($"WC Top Hud: {(set.ClientConfig.MinimalHud ? "Minimal" : "Full")}", 2000, "Red", true); + s.ShowLocalNotify($"WC Right Info Panel: {(set.ClientConfig.HideReload ? "Off" : "On")}", 2000, "Red"); set.VersionControl.UpdateClientCfgFile(); } else if (!set.ClientConfig.AdvancedMode) From c707649e1a427023693aa348082678f0f56ef996 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:17:54 -0500 Subject: [PATCH 72/77] Updates --- CoreSystems.csproj | 27 ++++-- CoreSystems.ruleset | 51 ---------- CoreSystems.sln | 10 +- .../Controls/CreateCustomActions.cs | 25 +++++ .../Controls/Weapon/WeaponActions.cs | 95 ++++++++++++++++++- .../CoreSystems/Session/SessionControls.cs | 2 + .../CoreSystems/Support/Localization.cs | 10 +- Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs | 14 ++- 8 files changed, 165 insertions(+), 69 deletions(-) delete mode 100644 CoreSystems.ruleset diff --git a/CoreSystems.csproj b/CoreSystems.csproj index 7c7ce43b..32408fad 100644 --- a/CoreSystems.csproj +++ b/CoreSystems.csproj @@ -21,7 +21,7 @@ 4 false 6 - WeaponCore.ruleset + AllRules.ruleset pdbonly @@ -32,7 +32,8 @@ 4 false 7 - WeaponCore.ruleset + + WeaponCore @@ -40,6 +41,24 @@ false + + true + bin\x64\Debug\ + full + x64 + 6 + prompt + AllRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7 + prompt + @@ -118,7 +137,6 @@ PreserveNewest - @@ -681,9 +699,6 @@ False - - - PreserveNewest diff --git a/CoreSystems.ruleset b/CoreSystems.ruleset deleted file mode 100644 index 3e717fad..00000000 --- a/CoreSystems.ruleset +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CoreSystems.sln b/CoreSystems.sln index 8b421450..b636bcb5 100644 --- a/CoreSystems.sln +++ b/CoreSystems.sln @@ -1,20 +1,26 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31112.23 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35806.99 d17.13 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreSystems", "CoreSystems.csproj", "{A02FF223-D75F-45ED-84F4-63ED89AFCCE9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Debug|x64.ActiveCfg = Debug|x64 + {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Debug|x64.Build.0 = Debug|x64 {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Release|Any CPU.ActiveCfg = Release|Any CPU {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Release|Any CPU.Build.0 = Release|Any CPU + {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Release|x64.ActiveCfg = Release|x64 + {A02FF223-D75F-45ED-84F4-63ED89AFCCE9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index 92e9c3b3..e4ac5ace 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -504,6 +504,31 @@ public static void CreateMinSize(Session session) session.CustomActions.Add(action1); } + public static void CreateMaxRange(Session session) + { + var action0 = MyAPIGateway.TerminalControls.CreateAction("MaxRange Increase"); + action0.Icon = @"Textures\GUI\Icons\Actions\Increase.dds"; + action0.Name = new StringBuilder(Localization.GetText("ActionMaxRangeIncrease")); + action0.Action = CustomActions.TerminalActionMaxRangeIncrease; + action0.Writer = CustomActions.MaxRangeWriter; + action0.Enabled = TerminalHelpers.HasTracking; + action0.ValidForGroups = true; + + MyAPIGateway.TerminalControls.AddAction(action0); + session.CustomActions.Add(action0); + + var action1 = MyAPIGateway.TerminalControls.CreateAction("MaxRange Decrease"); + action1.Icon = @"Textures\GUI\Icons\Actions\Decrease.dds"; + action1.Name = new StringBuilder(Localization.GetText("ActionMaxRangeDecrease")); + action1.Action = CustomActions.TerminalActionMaxRangeDecrease; + action1.Writer = CustomActions.MaxRangeWriter; + action1.Enabled = TerminalHelpers.HasTracking; + action1.ValidForGroups = true; + + MyAPIGateway.TerminalControls.AddAction(action1); + session.CustomActions.Add(action1); + } + public static void CreateMovementState(Session session) { var action = MyAPIGateway.TerminalControls.CreateAction("TrackingMode"); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs index 86ed0197..fdf228d2 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/Weapon/WeaponActions.cs @@ -3,6 +3,7 @@ using CoreSystems.Platform; using CoreSystems.Support; using Sandbox.ModAPI; +using VRage.Utils; using VRageMath; using static CoreSystems.Support.CoreComponent.Trigger; @@ -408,6 +409,90 @@ internal static void TerminalActionMinSizeDecrease(IMyTerminalBlock blk) Weapon.WeaponComponent.RequestSetValue(comp, "MinSize", newValue, Session.I.PlayerId); } + internal static void TerminalActionMaxRangeIncrease(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; + + var curRange = comp.Data.Repo.Values.Set.Range; + var maxRange = 0f; + for (int i = 0; i < comp.Collection.Count; i++) + { + var w = comp.Collection[i]; + var curMax = w.GetMaxWeaponRange(); + if (curMax > maxRange) + maxRange = (float)curMax; + } + + var delta = maxRange * 0.1f; + if (curRange == maxRange) + return; + else if (curRange + delta > maxRange) + curRange = maxRange; + else + curRange += delta; + + if (!MyUtils.IsEqual(curRange, comp.Data.Repo.Values.Set.Range)) + { + if (Session.I.IsServer) + { + comp.Data.Repo.Values.Set.Range = curRange; + Weapon.WeaponComponent.SetRange(comp); + if (Session.I.MpActive) + Session.I.SendComp(comp); + } + else + Session.I.SendSetCompFloatRequest(comp, curRange, PacketType.RequestSetRange); + } + } + + internal static void TerminalActionMaxRangeDecrease(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; + + var curRange = comp.Data.Repo.Values.Set.Range; + + var minRange = float.MaxValue; + for (int i = 0; i < comp.Collection.Count; i++) + { + var w = comp.Collection[i]; + var curMin = w.System.WConst.MinTargetDistance; + if (curMin < minRange) + minRange = (float)curMin; + } + + var maxRange = 0f; + for (int i = 0; i < comp.Collection.Count; i++) + { + var w = comp.Collection[i]; + var curMax = w.GetMaxWeaponRange(); + if (curMax > maxRange) + maxRange = (float)curMax; + } + + var delta = maxRange * 0.1f; + if (curRange == minRange) + return; + else if (curRange - delta < minRange) + curRange = minRange; + else + curRange -= delta; + + if (!MyUtils.IsEqual(curRange, comp.Data.Repo.Values.Set.Range)) + { + if (Session.I.IsServer) + { + comp.Data.Repo.Values.Set.Range = curRange; + Weapon.WeaponComponent.SetRange(comp); + if (Session.I.MpActive) + Session.I.SendComp(comp); + } + else + Session.I.SendSetCompFloatRequest(comp, curRange, PacketType.RequestSetRange); + } + } + internal static void TerminalActionCycleAmmo(IMyTerminalBlock block) { var comp = block?.Components?.Get() as Weapon.WeaponComponent; @@ -655,14 +740,20 @@ internal static void MaxSizeWriter(IMyTerminalBlock blk, StringBuilder sb) { var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.MaxSize); + sb.Append($"Mx:{comp.Data.Repo.Values.Set.Overrides.MaxSize}"); } internal static void MinSizeWriter(IMyTerminalBlock blk, StringBuilder sb) { var comp = blk.Components.Get() as Weapon.WeaponComponent; if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; - sb.Append(comp.Data.Repo.Values.Set.Overrides.MinSize); + sb.Append($"Mn:{comp.Data.Repo.Values.Set.Overrides.MinSize}"); + } + internal static void MaxRangeWriter(IMyTerminalBlock blk, StringBuilder sb) + { + var comp = blk.Components.Get() as Weapon.WeaponComponent; + if (comp == null || comp.Platform.State != CorePlatform.PlatformState.Ready) return; + sb.Append($"{comp.Data.Repo.Values.Set.Range}m"); } internal static void ControlStateWriter(IMyTerminalBlock blk, StringBuilder sb) diff --git a/Data/Scripts/CoreSystems/Session/SessionControls.cs b/Data/Scripts/CoreSystems/Session/SessionControls.cs index 8455ff9b..d7c670c2 100644 --- a/Data/Scripts/CoreSystems/Session/SessionControls.cs +++ b/Data/Scripts/CoreSystems/Session/SessionControls.cs @@ -248,6 +248,8 @@ internal static void CreateCustomActionSet(Session session) where T : IMyTerm CreateCustomActions.CreateSelectFriend(session); CreateCustomActions.CreateSelectEnemy(session); + CreateCustomActions.CreateMaxRange(session); + CreateCustomActions.CreateMinSize(session); CreateCustomActions.CreateMaxSize(session); //CreateCustomActions.CreateFriendly(session); diff --git a/Data/Scripts/CoreSystems/Support/Localization.cs b/Data/Scripts/CoreSystems/Support/Localization.cs index e433149a..34174230 100644 --- a/Data/Scripts/CoreSystems/Support/Localization.cs +++ b/Data/Scripts/CoreSystems/Support/Localization.cs @@ -126,10 +126,10 @@ public static class Localization { "ActionUnowned", "Target Unowned On/Off" }, { "ActionFocusTargets", "Focus Fire On/Off" }, { "ActionFocusSubSystem", "Target SubSystems On/Off" }, - { "ActionMaxSizeIncrease", "MaxSize Increase" }, - { "ActionMaxSizeDecrease", "MaxSize Decrease" }, - { "ActionMinSizeIncrease", "MinSize Increase" }, - { "ActionMinSizeDecrease", "MinSize Decrease" }, + { "ActionMaxSizeIncrease", "Max Target Size Increase" }, + { "ActionMaxSizeDecrease", "Max Target Size Decrease" }, + { "ActionMinSizeIncrease", "Min Target Size Increase" }, + { "ActionMinSizeDecrease", "Min Target Size Decrease" }, { "ActionTrackingMode", "Cycle Movement Mode" }, { "ActionWC_CycleAmmo", "Cycle Ammo" }, { "ActionWC_RepelMode", "Repel Mode On/Off" }, @@ -243,6 +243,8 @@ public static class Localization { "WeaponTargNeedSelection", "Manually select target" }, { "WeaponTargTooClose", "Too close" }, { "WeaponInfoCheckAmmoType", "No selected ammo, alternatives in inventory" }, + { "ActionMaxRangeIncrease", "Increase Max Range" }, + { "ActionMaxRangeDecrease", "Decrease Max Range" }, } }, { diff --git a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs index 18fe736a..1330ca70 100644 --- a/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs +++ b/Data/Scripts/CoreSystems/Ui/Hud/HudDraw.cs @@ -228,6 +228,7 @@ private void BackgroundAdd(Vector2D currWeaponDisplayPos, double bgStartPosX) public const string NoSubSystemStr = ": No Subsys"; public const string RotatingStr = ": Rotating"; public const string NotInRangeStr = ": Too Far"; + public const string NoLOSStr = ": No LOS"; public const string InsideMinRangeStr = ": Too Close"; public const string NoTargetSetStr = ": Pick Targ"; public const string FakeOnTarg = ": Aligned"; @@ -255,19 +256,24 @@ private void WeaponsToAdd(bool reset, Vector2D currWeaponDisplayPos, double bgSt weapon.UpdateAndGetFriendlyName(); if (needsTarget) { + var di = comp.MasterAi.DetectionInfo; + var notManual = comp.MasterOverrides.Control != ProtoWeaponOverrides.ControlModes.Manual; + var inRange = weapon.MaxTargetDistanceSqr > 0 && ((di.OtherRangeSqr == double.MaxValue ? false : di.OtherRangeSqr < weapon.MaxTargetDistanceSqr) || (di.PriorityRangeSqr == double.MaxValue ? false : di.PriorityRangeSqr < weapon.MaxTargetDistanceSqr)); if (weapon.OutOfAmmo && !showReloadIcon) displayText += NoAmmoStr; - else if (comp.MasterOverrides.Control != ProtoWeaponOverrides.ControlModes.Manual && (weapon.Target.CurrentState == Target.States.NotSet || weapon.Target.CurrentState == Target.States.Expired)) + else if (notManual && (weapon.Target.CurrentState == Target.States.NotSet || weapon.Target.CurrentState == Target.States.Expired)) displayText += NoTargetSetStr; else if (comp.MasterOverrides.FocusSubSystem && !showReloadIcon && notAnyBlock && weapon.FoundTopMostTarget) displayText += NoSubSystemStr; - else if (weapon.MinTargetDistanceSqr > 0 && (comp.MasterAi.DetectionInfo.OtherRangeSqr < weapon.MinTargetDistanceSqr || comp.MasterAi.DetectionInfo.PriorityRangeSqr < weapon.MinTargetDistanceSqr)) + else if (weapon.MinTargetDistanceSqr > 0 && (di.OtherRangeSqr < weapon.MinTargetDistanceSqr || di.PriorityRangeSqr < weapon.MinTargetDistanceSqr)) displayText += InsideMinRangeStr; - else if (comp.MasterOverrides.Control != ProtoWeaponOverrides.ControlModes.Manual) + else if (!weapon.Target.HasTarget && (weapon.PauseShoot || (notManual && inRange))) + displayText += NoLOSStr; + else if (notManual && !inRange) displayText += NotInRangeStr; } else if (weapon.Comp.HasTurret && !weapon.Target.IsAligned && weapon.Target.ValidEstimate) - displayText += RotatingStr; + displayText += RotatingStr; else if (weapon.Comp.HasTurret && weapon.Target.CurrentState == Target.States.Fake) { if (weapon.Target.IsAligned) From f93d35855283a957c3ee6c27a58e6b70fbcb0306 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:10:29 -0500 Subject: [PATCH 73/77] Ammo sound distance bugfix --- .../Definitions/SerializedConfigs/AmmoConstants.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 60ce8d7c..81e55794 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -1154,19 +1154,19 @@ private void Sound(WeaponSystem.AmmoType ammo, WeaponSystem system, out bool hit if (ob != null) hitSoundDistSqr = ob.MaxDistance * ob.MaxDistance; if (hitSoundDistSqr > ammoSoundMaxDistSqr) ammoSoundMaxDistSqr = hitSoundDistSqr; } - else if (ammoTravelSound && (id == travelSoundStr || id == ammoDef.AmmoAudio.TravelSound)) + if (ammoTravelSound && (id == travelSoundStr || id == ammoDef.AmmoAudio.TravelSound)) { var ob = def.GetObjectBuilder() as MyObjectBuilder_AudioDefinition; if (ob != null) ammoTravelSoundDistSqr = ob.MaxDistance * ob.MaxDistance; if (ammoTravelSoundDistSqr > ammoSoundMaxDistSqr) ammoSoundMaxDistSqr = ammoTravelSoundDistSqr; } - else if (shotSound && (id == shotSoundStr || id == rawShotSoundStr)) + if (shotSound && (id == shotSoundStr || id == rawShotSoundStr)) { var ob = def.GetObjectBuilder() as MyObjectBuilder_AudioDefinition; if (ob != null) shotSoundDistSqr = ob.MaxDistance * ob.MaxDistance; if (shotSoundDistSqr > ammoSoundMaxDistSqr) ammoSoundMaxDistSqr = shotSoundDistSqr; } - else if (detSound && (id == detSoundStr || id == DetSoundStr)) + if (detSound && (id == detSoundStr || id == DetSoundStr)) { var ob = def.GetObjectBuilder() as MyObjectBuilder_AudioDefinition; if (ob != null) detSoundDistSqr = ob.MaxDistance * ob.MaxDistance; From d198690364093ea65b43edcb3f41573965a2db68 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:47:16 -0500 Subject: [PATCH 74/77] lg/sg def options --- Data/Scripts/CoreSystems/Ai/AiTargeting.cs | 8 ++++---- .../Definitions/CoreDefinitions.cs | 2 ++ .../CoreSystems/Definitions/CoreSystems.cs | 5 ++++- .../Controls/CreateCustomActions.cs | 4 ++-- .../EntityComp/Controls/TerminalHelpers.cs | 20 +++++++++++++++---- .../CoreSystems/EntityComp/EntityEvents.cs | 6 ++++++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs index 730fe269..d9ade1aa 100644 --- a/Data/Scripts/CoreSystems/Ai/AiTargeting.cs +++ b/Data/Scripts/CoreSystems/Ai/AiTargeting.cs @@ -253,7 +253,7 @@ private static bool AcquireTopMostEntity(Weapon w, ProtoWeaponOverrides overRide if (info.IsGrid) { - if (!s.TrackGrids || !overRides.Grids || (!overRides.LargeGrid && info.LargeGrid) || (!overRides.SmallGrid && !info.LargeGrid) || !focusTarget && info.FatCount < 2) continue; + if (!s.TrackGrids || !overRides.Grids || ((s.TrackProhibitLG || !overRides.LargeGrid) && info.LargeGrid) || ((s.TrackProhibitSG || !overRides.SmallGrid) && !info.LargeGrid) || !focusTarget && info.FatCount < 2) continue; if (w.System.TargetGridCenter) { @@ -457,7 +457,7 @@ private static bool AcquireObstruction(Weapon w, ProtoWeaponOverrides overRides) if (grid != null) { - if (!overRides.Grids || (!overRides.LargeGrid && info.LargeGrid) || (!overRides.SmallGrid && !info.LargeGrid) || grid.CubeBlocks.Count == 0) continue; + if (!overRides.Grids || ((s.TrackProhibitLG || !overRides.LargeGrid) && info.LargeGrid) || ((s.TrackProhibitSG || !overRides.SmallGrid) && !info.LargeGrid) || grid.CubeBlocks.Count == 0) continue; session.CanShoot++; Vector3D newCenter; @@ -827,7 +827,7 @@ internal static bool ReacquireTarget(Projectile p) if (tInfo.IsGrid) { - if (!s.TrackGrids || !overRides.Grids || !focusTarget && tInfo.FatCount < 2 || !aConst.CheckFutureIntersection && Obstruction(ref tInfo, ref targetPos, p) || (!overRides.LargeGrid && tInfo.LargeGrid) || (!overRides.SmallGrid && !tInfo.LargeGrid)) continue; + if (!s.TrackGrids || !overRides.Grids || !focusTarget && tInfo.FatCount < 2 || !aConst.CheckFutureIntersection && Obstruction(ref tInfo, ref targetPos, p) || ((s.TrackProhibitLG || !overRides.LargeGrid) && tInfo.LargeGrid) || ((s.TrackProhibitSG || !overRides.SmallGrid) && !tInfo.LargeGrid)) continue; if (!AcquireBlock(w, target, tInfo, ref waterSphere, ref info.Random, p, focusTarget)) continue; acquired = true; @@ -1823,7 +1823,7 @@ internal static bool SwitchToDrone(Weapon w) if (info.IsGrid) { - if (!s.TrackGrids || !overRides.Grids || info.FatCount < 2 || (!overRides.LargeGrid && info.LargeGrid) || (!overRides.SmallGrid && !info.LargeGrid)) continue; + if (!s.TrackGrids || !overRides.Grids || info.FatCount < 2 || ((s.TrackProhibitLG || !overRides.LargeGrid) && info.LargeGrid) || ((s.TrackProhibitSG || !overRides.SmallGrid) && !info.LargeGrid)) continue; session.CanShoot++; Vector3D newCenter; if (!w.TurretController) diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index e7c034c7..80ea8e85 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -617,6 +617,8 @@ public struct OtherDef [ProtoMember(9)] internal bool DisableLosCheck; [ProtoMember(10)] internal bool NoVoxelLosCheck; [ProtoMember(11)] internal bool AllowScopeOutsideObb; + [ProtoMember(12)] internal bool ProhibitLGTargeting; + [ProtoMember(13)] internal bool ProhibitSGTargeting; } [ProtoContract] diff --git a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs index f5dd722d..a9f49211 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreSystems.cs @@ -190,6 +190,8 @@ public AmmoType(AmmoDef ammoDef, MyDefinitionId ammoDefinitionId, MyDefinitionId public readonly bool TrackGrids; public readonly bool TrackCharacters; public readonly bool TrackMeteors; + public readonly bool TrackProhibitLG; + public readonly bool TrackProhibitSG; public readonly bool UniqueTargetPerWeapon; public readonly bool TrackNeutrals; public readonly bool DisableLosCheck; @@ -374,7 +376,8 @@ public WeaponSystem(WeaponStructure structure, MyStringHash partNameIdHash, MySt SubSystems(out TargetSubSystems, out OnlySubSystems); ValidTargetSize(out MinTargetRadius, out MaxTargetRadius); Session.CreateAnimationSets(Values.Animations, this, out WeaponAnimationSet, out PartEmissiveSet, out PartLinearMoveSet, out AnimationIdLookup, out PartAnimationLengths, out HeatingSubparts, out ParticleEvents, out EmissiveLookup); - + TrackProhibitLG = Values.HardPoint.Other.ProhibitLGTargeting; + TrackProhibitSG = Values.HardPoint.Other.ProhibitSGTargeting; // CheckForBadAnimations(); ApproximatePeakPower = WConst.IdlePower; diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs index e4ac5ace..40863467 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/CreateCustomActions.cs @@ -377,7 +377,7 @@ public static void CreateLargeGrid(Session session) action.Name = new StringBuilder(Localization.GetText("ActionTargetLargeGrids")); action.Action = CustomActions.TerminalActionToggleLargeGrid; action.Writer = CustomActions.LargeGridWriter; - action.Enabled = TerminalHelpers.HasTracking; + action.Enabled = TerminalHelpers.HasTrackingNoSizeProhibition; action.ValidForGroups = true; MyAPIGateway.TerminalControls.AddAction(action); @@ -391,7 +391,7 @@ public static void CreateSmallGrid(Session session) action.Name = new StringBuilder(Localization.GetText("ActionTargetSmallGrids")); action.Action = CustomActions.TerminalActionToggleSmallGrid; action.Writer = CustomActions.SmallGridWriter; - action.Enabled = TerminalHelpers.HasTracking; + action.Enabled = TerminalHelpers.HasTrackingNoSizeProhibition; action.ValidForGroups = true; MyAPIGateway.TerminalControls.AddAction(action); diff --git a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs index 66f44c37..0c4a2dae 100644 --- a/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs +++ b/Data/Scripts/CoreSystems/EntityComp/Controls/TerminalHelpers.cs @@ -46,8 +46,8 @@ internal static void AddTurretOrTrackingControls(Session session) where T : I AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTrackingNoSizeProhibition); + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTrackingNoSizeProhibition); AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); AddOnOffSwitchNoAction(session, "Supporting PD", Localization.GetText("TerminalSupportingPDTitle"), Localization.GetText("TerminalSupportingPDTooltip"), BlockUi.GetSupportingPD, BlockUi.RequestSetSupportingPD, true, UiDisableSupportingPD); @@ -108,8 +108,8 @@ internal static void AddSearchlightControls(Session session) where T : IMyTer AddOnOffSwitchNoAction(session, "Neutrals", Localization.GetText("TerminalNeutralsTitle"), Localization.GetText("TerminalNeutralsTooltip"), BlockUi.GetNeutrals, BlockUi.RequestSetNeutrals, true, HasTrackingNeutrals); AddOnOffSwitchNoAction(session, "Unowned", Localization.GetText("TerminalUnownedTitle"), Localization.GetText("TerminalUnownedTooltip"), BlockUi.GetUnowned, BlockUi.RequestSetUnowned, true, HasTrackingUnowned); AddOnOffSwitchNoAction(session, "Grids", Localization.GetText("TerminalGridsTitle"), Localization.GetText("TerminalGridsTooltip"), BlockUi.GetGrids, BlockUi.RequestSetGrids, true, TrackGrids); - AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTracking); - AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTracking); + AddOnOffSwitchNoAction(session, "LargeGrid", Localization.GetText("TerminalLGTitle"), Localization.GetText("TerminalLGTooltip"), BlockUi.GetLargeGrid, BlockUi.RequestSetLargeGrid, true, HasTrackingNoSizeProhibition); + AddOnOffSwitchNoAction(session, "SmallGrid", Localization.GetText("TerminalSGTitle"), Localization.GetText("TerminalSGTooltip"), BlockUi.GetSmallGrid, BlockUi.RequestSetSmallGrid, true, HasTrackingNoSizeProhibition); AddOnOffSwitchNoAction(session, "Biologicals", Localization.GetText("TerminalBiologicalsTitle"), Localization.GetText("TerminalBiologicalsTooltip"), BlockUi.GetBiologicals, BlockUi.RequestSetBiologicals, true, TrackBiologicals); AddOnOffSwitchNoAction(session, "Projectiles", Localization.GetText("TerminalProjectilesTitle"), Localization.GetText("TerminalProjectilesTooltip"), BlockUi.GetProjectiles, BlockUi.RequestSetProjectiles, true, TrackProjectiles); AddOnOffSwitchNoAction(session, "Meteors", Localization.GetText("TerminalMeteorsTitle"), Localization.GetText("TerminalMeteorsTooltip"), BlockUi.GetMeteors, BlockUi.RequestSetMeteors, true, TrackMeteors); @@ -410,6 +410,18 @@ internal static bool HasTracking(IMyTerminalBlock block) return (comp.HasTracking || comp.HasGuidance) && !comp.HasAlternateUi; } + internal static bool HasTrackingNoSizeProhibition(IMyTerminalBlock block) + { + var comp = block?.Components?.Get() as Weapon.WeaponComponent; + + var valid = comp != null && comp.Platform.State == CorePlatform.PlatformState.Ready && comp.Data?.Repo != null; + + if (!valid || Session.I.PlayerId != comp.Data.Repo.Values.State.PlayerId && !comp.TakeOwnerShip() || comp.PrimaryWeapon.System.TrackProhibitLG || comp.PrimaryWeapon.System.TrackProhibitSG) + return false; + + return (comp.HasTracking || comp.HasGuidance) && !comp.HasAlternateUi; + } + internal static bool HasTrackingExceptCommSlave(IMyTerminalBlock block) { var comp = block?.Components?.Get() as Weapon.WeaponComponent; diff --git a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs index dfd289cb..69f36974 100644 --- a/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs +++ b/Data/Scripts/CoreSystems/EntityComp/EntityEvents.cs @@ -228,6 +228,12 @@ private void AppendingCustomInfoWeapon(IMyTerminalBlock block, StringBuilder str stringBuilder.Append($"\n{Localization.GetText("WeaponInfoServerModdedLine1")}\n") .Append($"\n{Localization.GetText("WeaponInfoServerModdedLine2")}"); + if (comp.PrimaryWeapon.System.TrackProhibitLG) + stringBuilder.Append($"\nCannot target large grids!"); + + if (comp.PrimaryWeapon.System.TrackProhibitSG) + stringBuilder.Append($"\nCannot target small grids!"); + //Start of new formatting if (IdlePower > 0.01) stringBuilder.Append($"\n{Localization.GetText("WeaponInfoIdlePower")}: {IdlePower:0.00} {Localization.GetText("WeaponInfoMWLabel")}"); From 0fdc0477058d63acc3544da3bf8b276eca69d73e Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:08:26 -0500 Subject: [PATCH 75/77] Combat Block Updates --- Data/Scripts/CoreSystems/Ai/AiConstruct.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs index 383257b5..4ac84830 100644 --- a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs +++ b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs @@ -440,6 +440,19 @@ internal static void CombatBlockUpdates(Ai cAi)//Obligatory comment to annoy com double rangeToTarg = double.MaxValue; var currentAim = aFB.LookAtPosition == null ? Vector3D.Zero : (Vector3D)aFB.LookAtPosition; var hasTarg = aCB.SearchEnemyComponent.FoundEnemy != null; + + if (Session.I.IsServer) + { + if (hasTarg) + { + var targTopmost = aCB.SearchEnemyComponent.FoundEnemy.GetTopMostParent(); + if (checkAi.Construct.Data.Repo.FocusData.Target != targTopmost.EntityId) + checkAi.Construct.Focus.ServerChangeFocus((MyEntity)targTopmost, checkAi, 0, Focus.ChangeMode.Add, true); + } + else if (checkAi.Construct.Data.Repo.FocusData.Target != 0) + checkAi.Construct.Focus.ServerChangeFocus(null, checkAi, 0, Focus.ChangeMode.Release, true); + } + var targSphere = new BoundingSphereD(hasTarg ? aCB.SearchEnemyComponent.FoundEnemy.PositionComp.WorldAABB.Center : Vector3D.Zero, 1); //Does the radius really matter here? use actual enemy or lead position? if (currentAim != Vector3D.Zero && !stopFiring) @@ -452,7 +465,7 @@ internal static void CombatBlockUpdates(Ai cAi)//Obligatory comment to annoy com { foreach (var comp in ais[x].WeaponComps) { - if (comp.HasTurret || comp.HasScanTrackOnly) continue; + if (comp.HasTurret || comp.HasScanTrackOnly || comp.PrimaryWeapon.System.RadioType != WeaponCore.Data.Scripts.CoreSystems.Comms.Radio.RadioTypes.None) continue; if (comp.HasGuidance) { From ffa96fd1a9abfe69962d2d87a911bb46fc78f8dc Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:58:08 -0500 Subject: [PATCH 76/77] Update --- Data/Scripts/CoreSystems/Ai/AiConstruct.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs index 4ac84830..96a78069 100644 --- a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs +++ b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs @@ -465,9 +465,17 @@ internal static void CombatBlockUpdates(Ai cAi)//Obligatory comment to annoy com { foreach (var comp in ais[x].WeaponComps) { - if (comp.HasTurret || comp.HasScanTrackOnly || comp.PrimaryWeapon.System.RadioType != WeaponCore.Data.Scripts.CoreSystems.Comms.Radio.RadioTypes.None) continue; + if (comp.HasTurret || comp.HasScanTrackOnly || comp.PrimaryWeapon.System.RadioType != WeaponCore.Data.Scripts.CoreSystems.Comms.Radio.RadioTypes.Slave) continue; - if (comp.HasGuidance) + if (comp.PrimaryWeapon.System.RadioType == WeaponCore.Data.Scripts.CoreSystems.Comms.Radio.RadioTypes.Slave) + { + var shoot = comp.PrimaryWeapon.Target.HasTarget; + if (shoot && comp.Data.Repo.Values.State.Trigger == CoreComponent.Trigger.Off) + comp.ShootManager.RequestShootSync(0, ShootManager.RequestType.On); + else if ((stopFiring || !shoot) && comp.Data.Repo.Values.State.Trigger == CoreComponent.Trigger.On) + comp.ShootManager.RequestShootSync(0, ShootManager.RequestType.Off); + } + else if (comp.HasGuidance) { bool shoot = hasTarg ? rangeToTarg <= comp.PrimaryWeapon.MaxTargetDistance && MathFuncs.TargetSphereInCone(ref targSphere, ref comp.PrimaryWeapon.AimCone) : false; if (shoot && comp.Data.Repo.Values.State.Trigger == CoreComponent.Trigger.Off) From af5c36ab64441187524a096db1bd50a9090545f2 Mon Sep 17 00:00:00 2001 From: BDCarrillo <90526940+BDCarrillo@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:33:56 -0500 Subject: [PATCH 77/77] Updates --- Data/Scripts/CoreSystems/Ai/AiConstruct.cs | 2 +- Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs | 4 +++- .../Definitions/SerializedConfigs/AmmoConstants.cs | 6 ------ Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs | 5 ----- Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs | 8 -------- 5 files changed, 4 insertions(+), 21 deletions(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs index 96a78069..ef5edeab 100644 --- a/Data/Scripts/CoreSystems/Ai/AiConstruct.cs +++ b/Data/Scripts/CoreSystems/Ai/AiConstruct.cs @@ -441,7 +441,7 @@ internal static void CombatBlockUpdates(Ai cAi)//Obligatory comment to annoy com var currentAim = aFB.LookAtPosition == null ? Vector3D.Zero : (Vector3D)aFB.LookAtPosition; var hasTarg = aCB.SearchEnemyComponent.FoundEnemy != null; - if (Session.I.IsServer) + if (aCB.IsWorking && aFB.IsWorking && Session.I.IsServer) { if (hasTarg) { diff --git a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs index 80ea8e85..e52e8e23 100644 --- a/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs +++ b/Data/Scripts/CoreSystems/Definitions/CoreDefinitions.cs @@ -1314,7 +1314,7 @@ internal enum GuidanceType [ProtoMember(14)] internal uint MaxTrajectoryTime; [ProtoMember(15)] internal ApproachDef[] Approaches; [ProtoMember(16)] internal double TotalAcceleration; - [ProtoMember(17)] internal OnHitDef OnHit; + [ProtoMember(17)] internal OnHitDef OnHit; //Deprecated [ProtoMember(18)] internal float DragPerSecond; [ProtoMember(19)] internal float DragMinSpeed; @@ -1553,6 +1553,7 @@ public struct MinesDef [ProtoContract] public struct OnHitDef { + /* [ProtoMember(1)] internal int Duration; [ProtoMember(2)] internal int ProcInterval; [ProtoMember(3)] internal double ProcAmount; @@ -1561,6 +1562,7 @@ public struct OnHitDef [ProtoMember(6)] internal bool DieOnEnd; [ProtoMember(7)] internal bool StickOnHit; [ProtoMember(8)] internal bool AlignFragtoImpactAngle; + */ } } diff --git a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs index 81e55794..8e631e7a 100644 --- a/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs +++ b/Data/Scripts/CoreSystems/Definitions/SerializedConfigs/AmmoConstants.cs @@ -108,8 +108,6 @@ public enum Texture public readonly int FragGroupDelay; public readonly int DeformDelay; public readonly int OffsetTime; - public readonly uint OnHitProcInterval; - public readonly uint OnHitDuration; public readonly uint FakeVoxelHitTicks; public readonly bool HasApproaches; public readonly bool KeepAliveAfterTargetLoss; @@ -251,7 +249,6 @@ public enum Texture public readonly bool EwarFieldTrigger; public readonly bool ZeroEffortNav; public readonly bool ProjectilesFirst; - public readonly bool OnHit; public readonly bool OverrideWeaponEffect; public readonly bool IgnoreAntiSmarts; public readonly float LargeGridDmgScale; @@ -566,9 +563,6 @@ internal AmmoConstants(WeaponSystem.AmmoType ammo, WeaponDefinition wDef, Weapon ProjectilesFirst = system.ProjectilesFirst; PreComputedMath = new PreComputedMath(ammo, this); - - OnHit = false; - OnHitProcInterval = 0; } internal void Purge() diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index c6d63db7..bba5a838 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -688,11 +688,6 @@ internal bool GenerateHitInfo(Projectile p) info.ProHit.Entity = hitEntity.Entity; info.ProHit.LastHit = hitEntity.HitPos ?? p.Beam.To; - if (aConst.OnHit && Session.I.Tick >= info.ProHit.EndTick) - { - info.ProHit.EndTick = Session.I.Tick + aConst.OnHitDuration; - } - if (p.EnableAv || aConst.VirtualBeams) { Vector3D lastHitVel = Vector3D.Zero; diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs index b661337f..fa553f82 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileTypes.cs @@ -483,14 +483,6 @@ internal class ProHit { internal MyEntity Entity; internal Vector3D LastHit; - internal uint EndTick; - internal int ProcInterval; - internal double ProcAmount; - internal bool ProcOnVoxels; - internal bool FragOnProc; - internal bool DieOnEnd; - internal bool StickOnHit; - internal bool AlignFragtoImpactAngle; } internal class WeaponFrameCache

-inn|9E;4=r@@m!;HvXB_=(G)kBC{h?=sCtz4X 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 literal 0 HcmV?d00001 diff --git a/Data/Audio.sbc b/Data/Audio.sbc new file mode 100644 index 00000000..c2a316d8 --- /dev/null +++ b/Data/Audio.sbc @@ -0,0 +1,43 @@ + + + + + + MyObjectBuilder_AudioDefinition + ArcWepShipGatlingShot + + SHOT + 200 + 0 + HeavyFight + 3 + + + + 0.7 + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d01.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d02.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d03.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d04.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d05.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d06.wav + + + Audio\ARC\WEP\ArcWepShipGatlingShot3d07.wav + + + + + diff --git a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs index 48673dfb..c6d63db7 100644 --- a/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs +++ b/Data/Scripts/CoreSystems/Projectiles/ProjectileHits.cs @@ -936,10 +936,8 @@ internal int GetEntityCompareDist(HitEntity x, HitEntity y, ProInfo info) } } - if (hitEnt.HitPos != null && info.ShieldBypassedHitOther && info.AvShot.HitParticle == AvShot.ParticleState.Dirty) - { + if (hitEnt.HitPos != null && info.ShieldBypassedHitOther && info.AvShot?.HitParticle == AvShot.ParticleState.Dirty) info.AvShot.HitParticle = AvShot.ParticleState.Custom; - } } else if (voxel != null) { From 2c1360cedb884558e4e546ec4de0f2d5ac27fe68 Mon Sep 17 00:00:00 2001 From: Aristeas <94058548+ari-steas@users.noreply.github.com> Date: Tue, 6 May 2025 21:18:13 -0500 Subject: [PATCH 57/77] Create Add/RemoveScanTargetsAction API Hooks Allows mods to set which targets are visible to a grid's AI. --- Data/Scripts/CoreSystems/Ai/AiDatabase.cs | 5 ++++- Data/Scripts/CoreSystems/Api/ApiBackend.cs | 14 ++++++++++++++ .../CoreSystems/Api/CoreSystemsApiBase.cs | 18 ++++++++++++++++++ .../CoreSystems/Session/SessionFields.cs | 1 + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs index 72cc393e..a7db422a 100644 --- a/Data/Scripts/CoreSystems/Ai/AiDatabase.cs +++ b/Data/Scripts/CoreSystems/Ai/AiDatabase.cs @@ -40,7 +40,10 @@ internal void RequestDbUpdate() internal void Scan() { - MyGamePruningStructure.GetAllTopMostEntitiesInSphere(ref ScanVolume, _possibleTargets); + if (Session.I.ScanTargetsAction == null) + MyGamePruningStructure.GetAllTopMostEntitiesInSphere(ref ScanVolume, _possibleTargets); + else + Session.I.ScanTargetsAction.Invoke(GridEntity, ScanVolume, _possibleTargets); NearByEntitiesTmp = _possibleTargets.Count; for (int i = 0; i < NearByEntitiesTmp; i++) diff --git a/Data/Scripts/CoreSystems/Api/ApiBackend.cs b/Data/Scripts/CoreSystems/Api/ApiBackend.cs index 2e222f74..1a00ca87 100644 --- a/Data/Scripts/CoreSystems/Api/ApiBackend.cs +++ b/Data/Scripts/CoreSystems/Api/ApiBackend.cs @@ -141,6 +141,8 @@ internal ApiBackend() ["IsInRange"] = new Func>(IsInRangeLegacy), ["GetConstructEffectiveDpsBase"] = new Func(GetConstructEffectiveDps), ["GetConstructEffectiveDps"] = new Func(GetConstructEffectiveDpsLegacy), + ["AddScanTargetsAction"] = new Action>>(AddScanTargetsAction), + ["RemoveScanTargetsAction"] = new Action>>(RemoveScanTargetsAction), // Phantoms ["GetTargetAssessment"] = new Func>(GetPhantomTargetAssessment), @@ -1519,6 +1521,18 @@ private MyTuple IsInRange(MyEntity entity) } return new MyTuple(); } + + private void AddScanTargetsAction(Action> action) + { + Session.I.ScanTargetsAction += action; + } + + private void RemoveScanTargetsAction(Action> action) + { + Session.I.ScanTargetsAction -= action; + } + + /// /// Phantoms /// diff --git a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs index dca1bc67..36d52f9c 100644 --- a/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs +++ b/Data/Scripts/CoreSystems/Api/CoreSystemsApiBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Sandbox.Game.Entities; using Sandbox.ModAPI; using VRage; using VRage.Collections; @@ -91,6 +92,8 @@ public partial class WcApi private Func> _getMagazineMap; private Func _setMagazine; private Func _forceReload; + private Action>> _addScanTargetsAction; + private Action>> _removeScanTargetsAction; public void SetWeaponTarget(MyEntity weapon, MyEntity target, int weaponId = 0) => _setWeaponTarget?.Invoke(weapon, target, weaponId); @@ -467,6 +470,18 @@ public bool ForceReload(MyEntity weapon, int weaponId) } + ///