From 8cefb98c6c69a586b08a787a19820862f3d1210f Mon Sep 17 00:00:00 2001 From: Gauge Date: Wed, 10 Jul 2024 19:39:30 -0700 Subject: [PATCH 01/21] corrected the touching surfaces calculation mistake. Added a debug option for just block coloring. --- .../Data/Scripts/Thermodynamics/Settings.cs | 3 +- .../Scripts/Thermodynamics/ThermalCell.cs | 2 +- .../Data/Scripts/Thermodynamics/Tools.cs | 36 +++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs index 635cce5..00c7638 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs @@ -15,7 +15,8 @@ public class Settings { public const string Filename = "ThermodynamicsConfig.cfg"; public const string Name = "Thermodynamics"; - public const bool Debug = false; + public const bool Debug = true; + public const bool DebugBlockColors = false; public static Settings Instance; diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs index f0da1ce..1a07ca1 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs @@ -417,7 +417,7 @@ internal void Update() Block.DoDamage((Temperature - Definition.CriticalTemperature) * Definition.CriticalTemperatureScaler, MyStringHash.GetOrCompute("thermal"), false); } - if (Settings.Debug && MyAPIGateway.Session.IsServer) + if (Settings.DebugBlockColors && MyAPIGateway.Session.IsServer) { Vector3 color = Tools.GetTemperatureColor(Temperature); if (Block.ColorMaskHSV != color) diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs index 37308c7..deef90b 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs @@ -96,16 +96,40 @@ public static Vector3 GetTemperatureColor(float temp, float max = 2000, float lo /// public static int FindTouchingSurfaceArea(Vector3I minA, Vector3I maxA, Vector3I minB, Vector3I maxB) { - int deltaX = Math.Min(maxA.X, maxB.X) - Math.Max(minA.X, minB.X); - int deltaY = Math.Min(maxA.Y, maxB.Y) - Math.Max(minA.Y, minB.Y); - int deltaZ = Math.Min(maxA.Z, maxB.Z) - Math.Max(minA.Z, minB.Z); + // Check if they touch on the X face + if (minA.X == maxB.X || maxA.X == minB.X) + { + int overlapY = Math.Min(maxA.Y, maxB.Y) - Math.Max(minA.Y, minB.Y); + int overlapZ = Math.Min(maxA.Z, maxB.Z) - Math.Max(minA.Z, minB.Z); + if (overlapY > 0 && overlapZ > 0) + { + return overlapY * overlapZ; + } + } + + // Check if they touch on the Y face + if (minA.Y == maxB.Y || maxA.Y == minB.Y) + { + int overlapX = Math.Min(maxA.X, maxB.X) - Math.Max(minA.X, minB.X); + int overlapZ = Math.Min(maxA.Z, maxB.Z) - Math.Max(minA.Z, minB.Z); + if (overlapX > 0 && overlapZ > 0) + { + return overlapX * overlapZ; + } + } - if (deltaX <= 0 || deltaY <= 0 || deltaZ <= 0) + // Check if they touch on the Z face + if (minA.Z == maxB.Z || maxA.Z == minB.Z) { - return 0; + int overlapX = Math.Min(maxA.X, maxB.X) - Math.Max(minA.X, minB.X); + int overlapY = Math.Min(maxA.Y, maxB.Y) - Math.Max(minA.Y, minB.Y); + if (overlapX > 0 && overlapY > 0) + { + return overlapX * overlapY; + } } - return deltaX * deltaY * deltaZ; + return 0; } } } From f6a09fb86e36390684c42fdf15f772fa25eebf5e Mon Sep 17 00:00:00 2001 From: Gauge Date: Wed, 10 Jul 2024 22:38:35 -0700 Subject: [PATCH 02/21] reverted some changes to fix exposed surface issues. --- .../Scripts/Thermodynamics/PlanetManager.cs | 46 ----------- .../Data/Scripts/Thermodynamics/Session.cs | 58 ++++++++------ .../Scripts/Thermodynamics/ThermalCell.cs | 4 +- .../Scripts/Thermodynamics/ThermalGrid.cs | 4 +- .../Thermodynamics/ThermalGridMapper.cs | 77 +++++++++++++++++-- .../Data/Scripts/Thermodynamics/Tools.cs | 37 ++++++++- 6 files changed, 144 insertions(+), 82 deletions(-) diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/PlanetManager.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/PlanetManager.cs index 4a87bd8..c39fc0e 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/PlanetManager.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/PlanetManager.cs @@ -129,51 +129,5 @@ public static Planet GetClosestPlanet(Vector3D position) return current; } - - public static bool IsSolarOccluded(Vector3D observer, Vector3 solarDirection, MyPlanet planet) - { - Vector3D local = observer - planet.PositionComp.WorldMatrixRef.Translation; - double distance = local.Length(); - Vector3D localNorm = local / distance; - - double dot = Vector3.Dot(localNorm, solarDirection); - return dot < GetLargestOcclusionDotProduct(GetVisualSize(distance, planet.AverageRadius)); - } - - - /// - /// a number between 0 and 1 representing the side object based on distance - /// - /// the local vector between the observer and the target - /// the size of the target - //public static double GetVisualSize(Vector3D observer, double radius) - //{ - // return 2 * Math.Atan(radius / (2 * observer.Length())); - //} - - /// - /// a number between 0 and 1 representing the side object based on distance - /// - /// the distance between the observer and the target - /// the size of the target - public static double GetVisualSize(double distance, double radius) - { - return 2 * Math.Atan(radius / (2 * distance)); - } - - /// - /// an equation made by plotting the edge most angle of the occluded sun - /// takes in the current visual size of the planet and produces a number between 0 and -1 - /// if the dot product of the planet and sun directions is less than this number it is occluded - /// - /// - /// - public static double GetLargestOcclusionDotProduct(double visualSize) - { - return -1 + (0.85 * visualSize * visualSize * visualSize); - } - } - - } diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs index 71f737e..076dcdf 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs @@ -56,12 +56,39 @@ public Color GetTemperatureColor(float temp) return new Color(red, 0, blue, 255); } - public override void Simulate() + public void DrawBillboard(ThermalCell c, MatrixD cameraMatrix) + { + Vector3D blockPosition; + + c.Block.ComputeWorldCenter(out blockPosition); + + Color color = ColorExtensions.HSVtoColor(Tools.GetTemperatureColor(c.Temperature)); + + Vector3D drawPosition = MyAPIGateway.Session.Camera.WorldToScreen(ref blockPosition); + float distance = 0.00001f; + + double blockDistance = (cameraMatrix.Translation - blockPosition).Length(); + float scaler = (float)Tools.GetVisualSize(blockDistance, c.Grid.Grid.GridSizeHalf); + + + MyTransparentGeometry.AddBillboardOriented( + MyStringId.GetOrCompute("Square"), // Texture or material name for the billboard + color, // Color of the billboard + cameraMatrix.Translation + (cameraMatrix.Forward * distance) + (cameraMatrix.Up * drawPosition.Y*distance) + (cameraMatrix.Left * drawPosition.X*distance), // Position of the billboard + cameraMatrix.Left, // Left direction of the billboard + cameraMatrix.Up, // Up direction of the billboard + 1f * scaler*distance, // Width of the billboard + 1f * scaler*distance // Height of the billboard + ); + } + + public override void Draw() { if (Settings.Debug && !MyAPIGateway.Utilities.IsDedicated) { //MyAPIGateway.Utilities.ShowNotification($"[Grid] Frequency: {Settings.Instance.Frequency}", 1, "White"); MatrixD matrix = MyAPIGateway.Session.Camera.WorldMatrix; + Vector3D start = matrix.Translation; Vector3D end = start + (matrix.Forward * 15); @@ -82,30 +109,13 @@ public override void Simulate() if (c == null) return; - Vector3D blockPosition; - Matrix blockRotation; - - block.ComputeWorldCenter(out blockPosition); - block.Orientation.GetMatrix(out blockRotation); - - MatrixD gridRotationMatrix = block.CubeGrid.WorldMatrix; - gridRotationMatrix.Translation = Vector3D.Zero; - blockRotation *= gridRotationMatrix; - MatrixD blockWorldMatrix = MatrixD.CreateWorld(blockPosition, blockRotation.Forward, blockRotation.Up); - - float unit = block.CubeGrid.GridSize * 0.5f; - Vector3 halfExtents = new Vector3((float)unit, (float)unit, (float)unit); - BoundingBoxD box = new BoundingBoxD(-halfExtents, halfExtents); - - //GetTemperatureColor(c.Temperature); - - Color color = ColorExtensions.HSVtoColor(Tools.GetTemperatureColor(c.Temperature)); - - - MySimpleObjectDraw.DrawTransparentBox(ref blockWorldMatrix, ref box, ref color, MySimpleObjectRasterizer.Solid, 1, 0.01f, null, null, true, -1, BlendTypeEnum.AdditiveTop, 1000f); - - //MyAPIGateway.Utilities.ShowNotification($"[Grid] {tGrid.Entity.EntityId} Count: {tGrid.Thermals.Count}", 1, "White"); + DrawBillboard(c, matrix); + for (int i = 0; i < c.Neighbors.Count; i++) + { + ThermalCell n = c.Neighbors[i]; + DrawBillboard(n, matrix); + } MyAPIGateway.Utilities.ShowNotification($"[Env] " + diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs index 1a07ca1..d397a80 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs @@ -49,11 +49,11 @@ public class ThermalCell public IMySlimBlock Block; public ThermalCellDefinition Definition; - private List Neighbors = new List(); + public List Neighbors = new List(); private List TouchingSerfacesByNeighbor = new List(); public int ExposedSurfaces = 0; - private List ExposedSurfaceDirections = new List(); + public List ExposedSurfaceDirections = new List(); diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs index f673d01..0aff6d9 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Text; using System.Transactions; @@ -371,6 +372,7 @@ public override void UpdateBeforeSimulation() { if (!ThermalCellUpdateComplete) { + MyLog.Default.Info($"[Thermals] updating serfaces {ExteriorNodes.Count}"); cell.UpdateSurfaces(ref ExteriorNodes, ref neighbors); } @@ -449,7 +451,7 @@ private void PrepareNextSimulationStep() Vector3D planetDirection = planetLocal / distance; double dot = Vector3D.Dot(planetDirection, FrameSolarDirection); - double occlusionDot = PlanetManager.GetLargestOcclusionDotProduct(PlanetManager.GetVisualSize(distance, myPlanet.AverageRadius)); + double occlusionDot = Tools.GetLargestOcclusionDotProduct(Tools.GetVisualSize(distance, myPlanet.AverageRadius)); if (dot < occlusionDot) { diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs index dfa4634..76179fd 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs @@ -47,6 +47,16 @@ private void MapBlocks(IMySlimBlock block, bool isNew = false, IMySlimBlock remo Vector3I min = block.Min; Vector3I max = block.Max + 1; + if (isNew) + { + if (Vector3I.Min(Grid.Min - 1, this.min) != this.min || + Vector3I.Max(Grid.Max + 1, this.max) != this.max) + ResetMapper(); + } + + HashSet processedNeighbours = new HashSet(); + processedNeighbours.Add(removed); + MyCubeBlockDefinition def = block.BlockDefinition as MyCubeBlockDefinition; Matrix blockMatrix; block.Orientation.GetMatrix(out blockMatrix); @@ -55,30 +65,81 @@ private void MapBlocks(IMySlimBlock block, bool isNew = false, IMySlimBlock remo bool isAirTight = def?.IsAirTight == true; if (isAirTight) { - state = 63; // All sides airtight + state = 63; } - // Iterate over the block's volume for (int x = min.X; x < max.X; x++) { for (int y = min.Y; y < max.Y; y++) { for (int z = min.Z; z < max.Z; z++) { + Vector3I node = new Vector3I(x, y, z); - // Update state based on neighbors - UpdateNodeState(ref state, node, block, def, blockMatrix); + for (int i = 0; i < neighbors.Length; i++) + { + Vector3I n = node + neighbors[i]; + Vector3 direction1 = node - n; + Vector3 direction2 = n - node; + + // neighbour is the same block + if (n.X >= min.X && n.Y >= min.Y && n.Z >= min.Z && + n.X < max.X && n.Y < max.Y && n.Z < max.Z) + { + + if (IsAirtight(ref block, ref def, ref node, ref direction2, ref blockMatrix)) + { + state |= 1 << i; + } - // Safely add or update nodes in the dictionary - if (BlockNodes.ContainsKey(node)) + if (IsAirtight(ref block, ref def, ref n, ref direction1, ref blockMatrix)) + { + state |= 1 << i + 6; + } + } + else + { + IMySlimBlock nblock = Grid.GetCubeBlock(n); + if (nblock == null) + { + continue; + } + + // update all neighbor blocks if this block was just added to the grid + if (isNew && !processedNeighbours.Contains(nblock)) + { + MapBlocks(nblock); + processedNeighbours.Add(nblock); + } + + MyCubeBlockDefinition ndef = nblock.BlockDefinition as MyCubeBlockDefinition; + Matrix nMatrix; + nblock.Orientation.GetMatrix(out nMatrix); + nMatrix.TransposeRotationInPlace(); + + if (IsAirtight(ref block, ref def, ref node, ref direction2, ref blockMatrix)) + { + state |= 1 << i; + } + + if (ndef?.IsAirTight == true || + IsAirtight(ref nblock, ref ndef, ref n, ref direction1, ref nMatrix)) + { + state |= 1 << i + 6; + } + } + } + + if (isNew) { - BlockNodes[node] = state; // Update the existing node + BlockNodes.Add(node, state); } else { - BlockNodes.Add(node, state); // Add new node + BlockNodes[node] = state; } + } } } diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs index deef90b..c270f5f 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Tools.cs @@ -1,6 +1,9 @@ -using System; +using Sandbox.Game.Entities; +using Sandbox.ModAPI; +using System; using System.Collections.Generic; using System.Text; +using VRage.ModAPI; using VRageMath; namespace Thermodynamics @@ -131,5 +134,37 @@ public static int FindTouchingSurfaceArea(Vector3I minA, Vector3I maxA, Vector3I return 0; } + + public static bool IsSolarOccluded(Vector3D observer, Vector3 solarDirection, MyPlanet planet) + { + Vector3D local = observer - planet.PositionComp.WorldMatrixRef.Translation; + double distance = local.Length(); + Vector3D localNorm = local / distance; + + double dot = Vector3.Dot(localNorm, solarDirection); + return dot < GetLargestOcclusionDotProduct(GetVisualSize(distance, planet.AverageRadius)); + } + + /// + /// a number between 0 and 1 representing the side object based on distance + /// + /// the distance between the observer and the target + /// the size of the target + public static double GetVisualSize(double distance, double radius) + { + return 2 * Math.Atan(radius / (2 * distance)); + } + + /// + /// an equation made by plotting the edge most angle of the occluded sun + /// takes in the current visual size of the planet and produces a number between 0 and -1 + /// if the dot product of the planet and sun directions is less than this number it is occluded + /// + /// + /// + public static double GetLargestOcclusionDotProduct(double visualSize) + { + return -1 + (0.85 * visualSize * visualSize * visualSize); + } } } From aa7c507b0fcbd1bf59790bbd0fb780fc2cbcad52 Mon Sep 17 00:00:00 2001 From: Gauge Date: Sat, 13 Jul 2024 12:00:42 -0700 Subject: [PATCH 03/21] Optimization pass on rope force and attach to zipline --- .../Data/Scripts/hook/WeaponControlLayer.cs | 157 +++++++++--------- 1 file changed, 77 insertions(+), 80 deletions(-) diff --git a/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs b/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs index d32d44f..58ba390 100644 --- a/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs +++ b/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs @@ -8,9 +8,7 @@ using SENetworkAPI; using System; using System.Collections.Generic; -using System.Drawing; -using System.Security.Policy; -using System.Security.Principal; +using System.Reflection; using System.Text; using VRage.Game; using VRage.Game.Components; @@ -66,6 +64,7 @@ public override void Init(MyObjectBuilder_EntityBase objectBuilder) if (MyAPIGateway.Session.IsServer) { Settings.Init(); + settings = new NetSync(this, TransferType.ServerToClient, Settings.Instance, true, false); } else @@ -299,30 +298,24 @@ private void UpdateGrappleLength() public void ApplyForce() { - try + if (Turret.CubeGrid.Physics == null || ConnectedEntity == null || ConnectedEntity.Physics == null || Entity.MarkedForClose || ConnectedEntity.MarkedForClose) { - if (Turret.CubeGrid.Physics == null || ConnectedEntity == null || ConnectedEntity.Physics == null || Entity.MarkedForClose || ConnectedEntity.MarkedForClose) - { - return; - } + return; + } - Vector3D turretPostion = Turret.PositionComp.WorldMatrixRef.Translation; - Vector3D entityPostion = Vector3D.Transform(LocalGrapplePosition.Value, ConnectedEntity.WorldMatrix); - Vector3D direction = turretPostion - entityPostion; - double currentLength = direction.Length(); - direction.Normalize(); + Vector3D turretPostion = Turret.PositionComp.WorldMatrixRef.Translation; + Vector3D entityPostion = Vector3D.Transform(LocalGrapplePosition.Value, ConnectedEntity.WorldMatrix); + Vector3D direction = turretPostion - entityPostion; + double currentLength = direction.Length(); + direction = direction / currentLength; - double force = settings.Value.RopeForce * Math.Max(0, currentLength - GrappleLength.Value) - settings.Value.RopeDamping * (Turret.CubeGrid.Physics.LinearVelocity + ConnectedEntity.Physics.LinearVelocity).Length(); + double force = settings.Value.RopeForce * (currentLength - GrappleLength.Value) - (settings.Value.RopeDamping * (Turret.CubeGrid.Physics.LinearVelocity + ConnectedEntity.Physics.LinearVelocity).Length()); + Vector3D forceVector = direction * force; - if (force > 0 && turretPostion != Vector3D.Zero && entityPostion != Vector3D.Zero) - { - Turret.CubeGrid.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_FORCE, -1 * direction * force, turretPostion, null, null, true); - ConnectedEntity.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_FORCE, direction * force, entityPostion, null, null, true); - } - } - catch (Exception e) + if (force > 0) { - Tools.Debug(e.ToString()); + Turret.CubeGrid.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_FORCE, -1 * forceVector, turretPostion, null, null, true); + ConnectedEntity.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_FORCE, forceVector, entityPostion, null, null, true); } } @@ -348,7 +341,7 @@ private void UpdateZiplineInteract() if (MyAPIGateway.Session.Player == null || !(MyAPIGateway.Session.Player.Controller.ControlledEntity is IMyCharacter)) return; ZiplineEntity e; - if ((e = GetZiplineEntity(MyAPIGateway.Session.Player.IdentityId)) != null) + if ((e = GetZiplineEntity(MyAPIGateway.Session.Player.IdentityId)) != null) { if (interactKeyPressed) { @@ -357,7 +350,6 @@ private void UpdateZiplineInteract() return; } - IMyCamera cam = MyAPIGateway.Session.Camera; if (cam == null) return; @@ -372,9 +364,6 @@ private void UpdateZiplineInteract() MatrixD worldMatrix = Turret.WorldMatrix; BoundingBoxD bounds = new BoundingBoxD(Vector3D.Min(a, b), Vector3D.Max(a, b)); - VRageMath.Color color = VRageMath.Color.Green; - MySimpleObjectDraw.DrawTransparentBox(ref worldMatrix, ref bounds, ref color, MySimpleObjectRasterizer.Wireframe, 1, 0.01f); - double? result; bounds.Intersects(ref camRay, out result); if (result.HasValue && result.Value < 3.5f) @@ -408,16 +397,16 @@ private void AttachToZipline(ZiplineEntity ziplineData) IMyCharacter character = ziplineData.player.Character; Vector3D[] points = GetLinePoints(); - double nearIndex = 0; - double nearValue = double.MaxValue; - Vector3D anchorSegment = Vector3D.Zero; + Vector3D centerSegment = Vector3D.Zero; Vector3D nextSegment = Vector3D.Zero; Vector3D previousSegment = Vector3D.Zero; - for (int j = 0; j < points.Length; j++) + + int nearIndex = 0; + double nearValue = double.MaxValue; + for (int i = 0; i < points.Length; i++) { - int index = j; - if (!ziplineData.direction) - index = points.Length - 1 - j; + // iterates from end to start if the character is facing twards the turret + int index = (ziplineData.direction) ? i : points.Length - 1 - i; Vector3D point = points[index]; @@ -426,71 +415,79 @@ private void AttachToZipline(ZiplineEntity ziplineData) { nearIndex = index; nearValue = distance; + centerSegment = point; + } + } - anchorSegment = point; - - int j2 = j + 1; - if (!ziplineData.direction) - j2 = points.Length - 1 - (j + 1); - - if (j2 >= 0 && j2 < points.Length) - { - nextSegment = points[j2]; - } - - int j3 = j - 1; - if (!ziplineData.direction) - j3 = points.Length - 1 - (j - 1); + // Correctly assign next and previous segments based on nearIndex + if (nearIndex + 1 < points.Length) + { + nextSegment = points[nearIndex + 1]; + } + else + { + nextSegment = centerSegment; + } - if (j3 >= 0 && j3 < points.Length) - { - previousSegment = points[j3]; - } - } + if (nearIndex - 1 >= 0) + { + previousSegment = points[nearIndex - 1]; + } + else + { + previousSegment = centerSegment; } - //Tools.Debug($"find the distance and direction of attachment force"); - Vector3D characterPosition = character.WorldMatrix.Translation + character.WorldMatrix.Up; + // If direction is false, swap next and previous segments + if (!ziplineData.direction) + { + Vector3D temp = nextSegment; + nextSegment = previousSegment; + previousSegment = temp; + } - Vector3D anchorToNext = nextSegment - anchorSegment; - double anchorToNextMag = anchorToNext.Length(); + Vector3D pulley; + Vector3D anchor; + Vector3D anchorNorm; + double sinTheta; - Vector3D previousToAnchor = anchorSegment - previousSegment; - double previousToAnchorMag = previousToAnchor.Length(); + Vector3D characterPosition = character.WorldMatrix.Translation + character.WorldMatrix.Up; + Vector3D centerToNext = nextSegment - centerSegment; + Vector3D playerToCenter = centerSegment - characterPosition; + double playerToCenterMag = playerToCenter.Length(); - Vector3D playerToAnchor = anchorSegment - characterPosition; - double playerToAnchorMag = playerToAnchor.Length(); + // check if we are infront or behind the center point. + double directionDot = Vector3D.Dot(playerToCenter, centerToNext); - // check if we are moving towards or away from the current point. - double directionDot = Vector3D.Dot(playerToAnchor, anchorToNext); - Vector3D pulley; if (directionDot < 0) { - double sinTheta = Tools.GetSinAngle(anchorToNext, -playerToAnchor); - double playerPulleyMag = sinTheta * playerToAnchorMag; - double pulleyToAnchorMag = Math.Sqrt(playerToAnchorMag * playerToAnchorMag - playerPulleyMag * playerPulleyMag); - Vector3D anchorToNextNorm = Vector3D.Normalize(anchorToNext); - - pulley = anchorSegment + anchorToNextNorm * pulleyToAnchorMag; - + // infront uses the next segment point as the anchor + anchor = centerToNext; + anchorNorm = Vector3D.Normalize(anchor); + sinTheta = Tools.GetSinAngle(anchor, -playerToCenter); } - else + else { - double sinTheta = Tools.GetSinAngle(previousToAnchor, playerToAnchor); - double playerPulleyMag = sinTheta * playerToAnchorMag; - double pulleyToAnchorMag = Math.Sqrt(playerToAnchorMag * playerToAnchorMag - playerPulleyMag * playerPulleyMag); - Vector3D previousToAnchorNorm = -Vector3D.Normalize(previousToAnchor); - - pulley = anchorSegment + previousToAnchorNorm * pulleyToAnchorMag; + // behind uses the center segment point as the anchor + anchor = centerSegment - previousSegment; + anchorNorm = -Vector3D.Normalize(anchor); + sinTheta = Tools.GetSinAngle(anchor, playerToCenter); } + double playerPulleyMag = sinTheta * playerToCenterMag; + double pulleyToAnchorMag = Math.Sqrt(playerToCenterMag * playerToCenterMag - playerPulleyMag * playerPulleyMag); + pulley = centerSegment + anchorNorm * pulleyToAnchorMag; + Tools.Debug($"Got pulley location {pulley}"); ziplineData.pulley = pulley; ziplineData.lastPulley = pulley; } + + + private void UpdateZiplineForces() { Vector3D[] points = GetLinePoints(); @@ -770,9 +767,9 @@ public Vector3D[] GetLinePoints() Vector3D sagDirection = GetSagDirection(); return ComputeCurvePoints(gunPosition, position, sagDirection, GrappleLength.Value, settings.Value.RopeSegments); } - catch + catch { - return new Vector3D[0]; + return new Vector3D[0]; } } From 71bd4195bd61fa630ffcd8b86fd81da598608ec9 Mon Sep 17 00:00:00 2001 From: Gauge Date: Sat, 13 Jul 2024 12:17:32 -0700 Subject: [PATCH 04/21] small optimizations to zipline tether force --- .../Data/Scripts/hook/WeaponControlLayer.cs | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs b/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs index 58ba390..78f062e 100644 --- a/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs +++ b/GrappleHook/Data/Scripts/hook/WeaponControlLayer.cs @@ -8,6 +8,7 @@ using SENetworkAPI; using System; using System.Collections.Generic; +using System.Data.SqlTypes; using System.Reflection; using System.Text; using VRage.Game; @@ -494,7 +495,6 @@ private void UpdateZiplineForces() for (int i = 0; i < ZiplinePlayers.Value.Count; i++) { ZiplineEntity zipEntity = ZiplinePlayers.Value[i]; - ZiplineEntity.Populate(ref zipEntity); if (zipEntity.player == null || !(zipEntity.player.Controller.ControlledEntity is IMyCharacter)) return; @@ -510,13 +510,12 @@ private void UpdateZiplineForces() } Vector3D characterPosition = character.WorldMatrix.Translation + character.WorldMatrix.Up * 1.7f; - Vector3D ropeForceDirection = zipEntity.pulley - characterPosition; - Vector3D ropeForceDirectionNorm = ropeForceDirection.Normalized(); - - double force = settings.Value.ZiplineTetherForce * Math.Max(0, ropeForceDirection.Length() - settings.Value.ZiplineTetherLength); - - Vector3D forceVector = ropeForceDirectionNorm * force - settings.Value.ZiplineDamping * character.Physics.LinearVelocity; + Vector3D tetherVector = zipEntity.pulley - characterPosition; + double tetherLength = tetherVector.Length(); + Vector3D tetherDirectionNorm = tetherVector / tetherLength; + double force = settings.Value.ZiplineTetherForce * (tetherLength - settings.Value.ZiplineTetherLength); + Vector3D forceVector = tetherDirectionNorm * force - settings.Value.ZiplineDamping * character.Physics.LinearVelocity; if (force > 0) { character.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_FORCE, forceVector, characterPosition, null, null, true); @@ -532,25 +531,23 @@ private void UpdateZiplineForces() double a = 0; if (deltaVector != Vector3D.Zero) { - a = Vector3D.Dot(-ropeForceDirectionNorm, deltaVector.Normalized()) * g; + a = Vector3D.Dot(-tetherDirectionNorm, deltaVector.Normalized()) * g; } double min = settings.Value.ZiplinePulleyMinSpeed * 0.01666667f; double velocityCalc = v0 * v0 + 2d * a * xDelta * xDelta; - // ADD BACK IN 'a' when you fix it double velocitySquared = Math.Max(velocityCalc, min * min); double velocityToApply = Math.Sqrt(velocitySquared); - //Tools.Debug($"V0: {v0}, a:{a}, xd:{xDelta} => Velocity: {velocityCalc}, min: {min*min}"); - double distanceRemaining = velocityToApply; zipEntity.lastPulley = zipEntity.pulley; zipEntity.lastPulleyVelocity = zipEntity.pulleyVelocity; - zipEntity.pulleyVelocity = velocityToApply; + + double distanceRemaining = velocityToApply; for (int j = 0; j < points.Length - 1; j++) { if (distanceRemaining == 0) break; @@ -580,8 +577,6 @@ private void UpdateZiplineForces() Vector3D endNorm = endDirection.Normalized(); - - if ((Vector3D.Dot(startNorm, segmentNorm) <= 0 || Math.Abs((zipEntity.pulley - startDirection).Length()) < 0.01f) && Vector3D.Dot(endNorm, segmentNorm) >= 0) { double length = endDirection.Length(); @@ -691,7 +686,7 @@ private void Draw() texture = MyStringId.GetOrCompute("cable"); } - Vector3D sagDirection = GetSagDirection(); + Vector3D sagDirection = GetSegmentDirection(); Vector3D gunPosition = Turret.PositionComp.WorldMatrixRef.Translation; Vector3D position; @@ -743,7 +738,7 @@ private void Draw() } } - private Vector3D GetSagDirection() + private Vector3D GetSegmentDirection() { ExternalForceData planetForces = WorldPlanets.GetExternalForces(Turret.PositionComp.WorldMatrixRef.Translation); Vector3D sagDirection = planetForces.Gravity; @@ -764,7 +759,7 @@ public Vector3D[] GetLinePoints() Vector3D gunPosition = Turret.WorldMatrix.Translation; Vector3D position = Vector3D.Transform(LocalGrapplePosition.Value, ConnectedEntity.WorldMatrix); - Vector3D sagDirection = GetSagDirection(); + Vector3D sagDirection = GetSegmentDirection(); return ComputeCurvePoints(gunPosition, position, sagDirection, GrappleLength.Value, settings.Value.RopeSegments); } catch @@ -793,7 +788,7 @@ public Vector3D[] ComputeCurvePoints(Vector3D start, Vector3D end, Vector3D sagD Vector3D newPt = start * v + end * u; if (sagAmount > 0) { - newPt += sagDirection * ComputeRopeSag(u) * sagAmount; + newPt += sagDirection * ComputeRopeSegment(u) * sagAmount; } result[i] = newPt; } @@ -801,7 +796,7 @@ public Vector3D[] ComputeCurvePoints(Vector3D start, Vector3D end, Vector3D sagD return result; } - public double ComputeRopeSag(double x) + public double ComputeRopeSegment(double x) { return -4 * x * x + 4 * x; } From 0098d16e488aa2cdd6f3b96f23450d2a532c9b5c Mon Sep 17 00:00:00 2001 From: Gauge Date: Sat, 13 Jul 2024 22:15:54 -0700 Subject: [PATCH 05/21] updated the heat indicator. --- .../Data/Scripts/Thermodynamics/Session.cs | 100 +++++++++++------- .../Data/Scripts/Thermodynamics/Settings.cs | 6 +- .../Scripts/Thermodynamics/ThermalCell.cs | 2 - ThermalDynamics/Data/TransparentMaterials.sbc | 18 ++++ .../Particles/GaugeThermalTexture.dds | Bin 0 -> 16512 bytes 5 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 ThermalDynamics/Data/TransparentMaterials.sbc create mode 100644 ThermalDynamics/Textures/Particles/GaugeThermalTexture.dds diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs index 076dcdf..786b230 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs @@ -7,6 +7,7 @@ using Sandbox.ModAPI; using System; using System.Collections.Generic; +using System.Net; using System.Text; using VRage.Game; using VRage.Game.Components; @@ -56,34 +57,10 @@ public Color GetTemperatureColor(float temp) return new Color(red, 0, blue, 255); } - public void DrawBillboard(ThermalCell c, MatrixD cameraMatrix) - { - Vector3D blockPosition; - - c.Block.ComputeWorldCenter(out blockPosition); - - Color color = ColorExtensions.HSVtoColor(Tools.GetTemperatureColor(c.Temperature)); - - Vector3D drawPosition = MyAPIGateway.Session.Camera.WorldToScreen(ref blockPosition); - float distance = 0.00001f; - double blockDistance = (cameraMatrix.Translation - blockPosition).Length(); - float scaler = (float)Tools.GetVisualSize(blockDistance, c.Grid.Grid.GridSizeHalf); - - - MyTransparentGeometry.AddBillboardOriented( - MyStringId.GetOrCompute("Square"), // Texture or material name for the billboard - color, // Color of the billboard - cameraMatrix.Translation + (cameraMatrix.Forward * distance) + (cameraMatrix.Up * drawPosition.Y*distance) + (cameraMatrix.Left * drawPosition.X*distance), // Position of the billboard - cameraMatrix.Left, // Left direction of the billboard - cameraMatrix.Up, // Up direction of the billboard - 1f * scaler*distance, // Width of the billboard - 1f * scaler*distance // Height of the billboard - ); - } - public override void Draw() - { + public override void Simulate() + { if (Settings.Debug && !MyAPIGateway.Utilities.IsDedicated) { //MyAPIGateway.Utilities.ShowNotification($"[Grid] Frequency: {Settings.Instance.Frequency}", 1, "White"); @@ -109,15 +86,6 @@ public override void Draw() if (c == null) return; - - DrawBillboard(c, matrix); - for (int i = 0; i < c.Neighbors.Count; i++) - { - ThermalCell n = c.Neighbors[i]; - DrawBillboard(n, matrix); - } - - MyAPIGateway.Utilities.ShowNotification($"[Env] " + $"sim: {Settings.Instance.SimulationSpeed.ToString("n2")} " + $"freq: {Settings.Instance.Frequency.ToString("n2")} " + @@ -164,5 +132,65 @@ public override void Draw() //MyAPIGateway.Utilities.ShowNotification($"[External] {tGrid.Mapper.Blocks.Count} EComplete: {tGrid.Mapper.ExternalRoomUpdateComplete} BComplete: {tGrid.ThermalCellUpdateComplete}", 1, "White"); } } - } + + public override void Draw() + { + if (Settings.Debug && !MyAPIGateway.Utilities.IsDedicated) + { + //MyAPIGateway.Utilities.ShowNotification($"[Grid] Frequency: {Settings.Instance.Frequency}", 1, "White"); + MatrixD matrix = MyAPIGateway.Session.Camera.WorldMatrix; + + Vector3D start = matrix.Translation; + Vector3D end = start + (matrix.Forward * 15); + + IHitInfo hit; + MyAPIGateway.Physics.CastRay(start, end, out hit); + MyCubeGrid grid = hit?.HitEntity as MyCubeGrid; + if (grid == null) return; + + Vector3I position = grid.WorldToGridInteger(hit.Position + (matrix.Forward * 0.01f)); + + ThermalGrid g = grid.GameLogic.GetAs(); + + IMySlimBlock block = grid.GetCubeBlock(position); + + ThermalCell c = g.Get(block.Position); + + if (c == null) return; + + MyAPIGateway.Utilities.ShowNotification($"Made it to draw", 1); + + DrawBillboard(c, matrix); + for (int i = 0; i < c.Neighbors.Count; i++) + { + ThermalCell n = c.Neighbors[i]; + DrawBillboard(n, matrix); + } + } + } + + public void DrawBillboard(ThermalCell c, MatrixD cameraMatrix) + { + Vector3D position; + c.Block.ComputeWorldCenter(out position); + + float averageBlockLength = Vector3I.DistanceManhattan(c.Block.Max + 1, c.Block.Min) * 0.33f; + + Color color = ColorExtensions.HSVtoColor(Tools.GetTemperatureColor(c.Temperature)); + + float distance = 0.01f; + position = cameraMatrix.Translation + (position - cameraMatrix.Translation) * distance; + float scaler = 1.2f * c.Grid.Grid.GridSizeHalf * averageBlockLength * distance; + + MyTransparentGeometry.AddBillboardOriented( + MyStringId.GetOrCompute("GaugeThermalTexture"), // Texture or material name for the billboard + color, // Color of the billboard + position, + cameraMatrix.Left, // Left direction of the billboard + cameraMatrix.Up, // Up direction of the billboard + scaler, // Width of the billboard + scaler // Height of the billboard + ); + } + } } diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs index 00c7638..29a934b 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Settings.cs @@ -16,7 +16,7 @@ public class Settings public const string Filename = "ThermodynamicsConfig.cfg"; public const string Name = "Thermodynamics"; public const bool Debug = true; - public const bool DebugBlockColors = false; + public const bool DebugBlockColors = true; public static Settings Instance; @@ -75,8 +75,8 @@ public static Settings GetDefaults() Version = 1, EnableSolarHeat = true, EnableDamage = true, - Frequency = 60, - SimulationSpeed = 1, + Frequency = 15, + SimulationSpeed = 4, SolarEnergy = 1000f, EnvironmentalRaycastDistance = 5000f, VaccumeRadiationStrength = 0.05f, diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs index d397a80..08144ae 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs @@ -55,8 +55,6 @@ public class ThermalCell public int ExposedSurfaces = 0; public List ExposedSurfaceDirections = new List(); - - public ThermalCell(ThermalGrid g, IMySlimBlock b) { Grid = g; diff --git a/ThermalDynamics/Data/TransparentMaterials.sbc b/ThermalDynamics/Data/TransparentMaterials.sbc new file mode 100644 index 0000000..2085fba --- /dev/null +++ b/ThermalDynamics/Data/TransparentMaterials.sbc @@ -0,0 +1,18 @@ + + + + + + TransparentMaterialDefinition + GaugeThermalTexture + + false + 1 + false + false + 0.1 + Textures\Particles\GaugeThermalTexture.dds + false + + + \ No newline at end of file diff --git a/ThermalDynamics/Textures/Particles/GaugeThermalTexture.dds b/ThermalDynamics/Textures/Particles/GaugeThermalTexture.dds new file mode 100644 index 0000000000000000000000000000000000000000..48e49ab5b1f338805cf861d074e0e3e9a60b414a GIT binary patch literal 16512 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 Date: Sun, 14 Jul 2024 11:22:59 -0700 Subject: [PATCH 06/21] Fixed null reference exception --- ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs | 6 +++--- ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs index 786b230..05a96b5 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs @@ -148,18 +148,18 @@ public override void Draw() MyCubeGrid grid = hit?.HitEntity as MyCubeGrid; if (grid == null) return; - Vector3I position = grid.WorldToGridInteger(hit.Position + (matrix.Forward * 0.01f)); + Vector3I position = grid.WorldToGridInteger(hit.Position + (matrix.Forward * 0.005f)); ThermalGrid g = grid.GameLogic.GetAs(); IMySlimBlock block = grid.GetCubeBlock(position); + if (block == null) return; + ThermalCell c = g.Get(block.Position); if (c == null) return; - MyAPIGateway.Utilities.ShowNotification($"Made it to draw", 1); - DrawBillboard(c, matrix); for (int i = 0; i < c.Neighbors.Count; i++) { diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs index 0aff6d9..9326240 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs @@ -372,7 +372,7 @@ public override void UpdateBeforeSimulation() { if (!ThermalCellUpdateComplete) { - MyLog.Default.Info($"[Thermals] updating serfaces {ExteriorNodes.Count}"); + //MyLog.Default.Info($"[Thermals] updating serfaces {ExteriorNodes.Count}"); cell.UpdateSurfaces(ref ExteriorNodes, ref neighbors); } From fd8227afbcfdf4ff028426f85bf9e53890f0849f Mon Sep 17 00:00:00 2001 From: Gauge Date: Sun, 14 Jul 2024 18:45:59 -0700 Subject: [PATCH 07/21] trying to fixed shrapnel damage crashing --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 4e18e8c..d17268b 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -10,8 +10,6 @@ namespace Shrapnel [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation)] public class Core : MySessionComponentBase { - public const float ReductionMult = 1.0f; - private Queue queue = new Queue(); public override void Init(MyObjectBuilder_SessionComponent sessionComponent) @@ -24,16 +22,19 @@ public void ProcessDamage(object target, ref MyDamageInformation info) IMySlimBlock slim = target as IMySlimBlock; if (slim == null) return; - if (info.Type != MyDamageType.Bullet) + if (info.Type == MyDamageType.Bullet) { if (slim.Integrity >= info.Amount) return; float overkill = info.Amount - slim.Integrity; info.Amount = slim.Integrity; + List n = new List(); + slim.GetNeighbours(n); + queue.Enqueue(new ShrapnelData() { - Neighbours = slim.Neighbours, + Neighbours = n, OverKill = overkill }); } @@ -57,7 +58,7 @@ public override void UpdateBeforeSimulation() { tasks++; ShrapnelData data = queue.Dequeue(); - int count = data.Neighbours.Count; + float count = 1f / data.Neighbours.Count; foreach (IMySlimBlock neighbour in data.Neighbours) { if (neighbour == null) continue; @@ -68,11 +69,7 @@ public override void UpdateBeforeSimulation() generalMult = ((MyCubeBlockDefinition)neighbour.BlockDefinition).GeneralDamageMultiplier; } - // total over kill damage devided by the number of neighbours - // that times the reduction multiplier 0 to 1 - // that times the blocks general reduction multiplier 0 - 1 - float damage = ((data.OverKill / (float)count) * ReductionMult * generalMult); - neighbour.DoDamage(damage, MyDamageType.Bullet, true); + neighbour.DoDamage(data.OverKill * count * generalMult, MyDamageType.Bullet, true); } } } From acc49f81f662ee813a3b8e25d402e6115c684a12 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:10:25 -0700 Subject: [PATCH 08/21] trying to fix damage spread issue --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index d17268b..d6df114 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -58,12 +58,12 @@ public override void UpdateBeforeSimulation() { tasks++; ShrapnelData data = queue.Dequeue(); - float count = 1f / data.Neighbours.Count; + float count = 1f / (float)data.Neighbours.Count; foreach (IMySlimBlock neighbour in data.Neighbours) { if (neighbour == null) continue; // get block resistance - float generalMult = 1; + float generalMult = 1f; if (neighbour.BlockDefinition is MyCubeBlockDefinition) { generalMult = ((MyCubeBlockDefinition)neighbour.BlockDefinition).GeneralDamageMultiplier; From 5014e9aea503ae075790716b830edc5be4460f5e Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:14:06 -0700 Subject: [PATCH 09/21] added additional types --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index d6df114..570bee5 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -22,7 +22,7 @@ public void ProcessDamage(object target, ref MyDamageInformation info) IMySlimBlock slim = target as IMySlimBlock; if (slim == null) return; - if (info.Type == MyDamageType.Bullet) + if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { if (slim.Integrity >= info.Amount) return; From 12dd4a2b9c26311bce442d7d7d8fbc0e87998861 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:26:02 -0700 Subject: [PATCH 10/21] fixed logic error --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 570bee5..446aacf 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -4,6 +4,7 @@ using VRage.Game; using VRage.Game.Components; using VRage.Game.ModAPI; +using VRage.Utils; namespace Shrapnel { @@ -24,7 +25,7 @@ public void ProcessDamage(object target, ref MyDamageInformation info) if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { - if (slim.Integrity >= info.Amount) return; + if (slim.Integrity <= info.Amount) return; float overkill = info.Amount - slim.Integrity; info.Amount = slim.Integrity; @@ -59,6 +60,8 @@ public override void UpdateBeforeSimulation() tasks++; ShrapnelData data = queue.Dequeue(); float count = 1f / (float)data.Neighbours.Count; + + //MyLog.Default.Info($"queue: {queue.Count} - overkill: {data.OverKill}, spread: {data.OverKill * count}"); foreach (IMySlimBlock neighbour in data.Neighbours) { if (neighbour == null) continue; From 55a168b950347ac043b6a6f6e78ee881c7fc16d1 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:34:20 -0700 Subject: [PATCH 11/21] test --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 446aacf..3529ed2 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -25,7 +25,7 @@ public void ProcessDamage(object target, ref MyDamageInformation info) if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { - if (slim.Integrity <= info.Amount) return; + //if (slim.Integrity >= info.Amount) return; float overkill = info.Amount - slim.Integrity; info.Amount = slim.Integrity; @@ -61,7 +61,7 @@ public override void UpdateBeforeSimulation() ShrapnelData data = queue.Dequeue(); float count = 1f / (float)data.Neighbours.Count; - //MyLog.Default.Info($"queue: {queue.Count} - overkill: {data.OverKill}, spread: {data.OverKill * count}"); + MyLog.Default.Info($"queue: {queue.Count} - overkill: {data.OverKill}, spread: {data.OverKill * count}"); foreach (IMySlimBlock neighbour in data.Neighbours) { if (neighbour == null) continue; From 862da4545251e14b9a7a0b2019a6f1248d0e53a2 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:39:56 -0700 Subject: [PATCH 12/21] more testing --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 3529ed2..9b0688f 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -1,6 +1,7 @@ using Sandbox.Definitions; using Sandbox.ModAPI; using System.Collections.Generic; +using System.IO; using VRage.Game; using VRage.Game.Components; using VRage.Game.ModAPI; @@ -15,7 +16,7 @@ public class Core : MySessionComponentBase public override void Init(MyObjectBuilder_SessionComponent sessionComponent) { - MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(9, ProcessDamage); + MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(7, ProcessDamage); } public void ProcessDamage(object target, ref MyDamageInformation info) @@ -25,9 +26,10 @@ public void ProcessDamage(object target, ref MyDamageInformation info) if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { - //if (slim.Integrity >= info.Amount) return; + if (slim.Integrity >= info.Amount) return; float overkill = info.Amount - slim.Integrity; + MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}, overkill: {overkill}"); info.Amount = slim.Integrity; List n = new List(); From e062c6eb8d5659f0c985de39b20a3f69c679eae3 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:44:43 -0700 Subject: [PATCH 13/21] removed log spam --- SuiteCombatBalancer/Data/Scripts/hacking/Core.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/SuiteCombatBalancer/Data/Scripts/hacking/Core.cs b/SuiteCombatBalancer/Data/Scripts/hacking/Core.cs index 70541f1..7cca873 100644 --- a/SuiteCombatBalancer/Data/Scripts/hacking/Core.cs +++ b/SuiteCombatBalancer/Data/Scripts/hacking/Core.cs @@ -25,8 +25,6 @@ public override void Init(MyObjectBuilder_SessionComponent sessionComponent) private void Handler(object target, ref MyDamageInformation info) { - MyLog.Default.Info(target.ToString()); - IMyEntity ent = MyAPIGateway.Entities.GetEntityById(info.AttackerId); if (ent == null) return; // End: Must be a player character From 1e9c70d6c620c698f0e17c46e692a29f98b8da35 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:47:22 -0700 Subject: [PATCH 14/21] test test testing --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 9b0688f..d72ab70 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -26,10 +26,11 @@ public void ProcessDamage(object target, ref MyDamageInformation info) if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { + MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}"); if (slim.Integrity >= info.Amount) return; float overkill = info.Amount - slim.Integrity; - MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}, overkill: {overkill}"); + //MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}, overkill: {overkill}"); info.Amount = slim.Integrity; List n = new List(); From d6c9c29d0b17095e3c45f248a1b3b7d8b249664f Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 21:58:06 -0700 Subject: [PATCH 15/21] testinj --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index d72ab70..2b5c87e 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -16,7 +16,7 @@ public class Core : MySessionComponentBase public override void Init(MyObjectBuilder_SessionComponent sessionComponent) { - MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(7, ProcessDamage); + MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(500, ProcessDamage); } public void ProcessDamage(object target, ref MyDamageInformation info) From 1c2580416b565cb3ca5ee50ddd374df3b0033a10 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 22:08:13 -0700 Subject: [PATCH 16/21] fixed it. --- .../Data/Scripts/shrapnel/Core.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 2b5c87e..5fe3c8d 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -24,13 +24,19 @@ public void ProcessDamage(object target, ref MyDamageInformation info) IMySlimBlock slim = target as IMySlimBlock; if (slim == null) return; + float generalMult = 1f; + if (slim.BlockDefinition is MyCubeBlockDefinition) + { + generalMult = ((MyCubeBlockDefinition)slim.BlockDefinition).GeneralDamageMultiplier; + } + + float amount = generalMult * info.Amount; + if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { - MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}"); - if (slim.Integrity >= info.Amount) return; + if (slim.Integrity >= amount) return; - float overkill = info.Amount - slim.Integrity; - //MyLog.Default.Info($"Ammount: {info.Amount}, integrity: {slim.Integrity}, overkill: {overkill}"); + float overkill = amount - slim.Integrity; info.Amount = slim.Integrity; List n = new List(); @@ -46,7 +52,7 @@ public void ProcessDamage(object target, ref MyDamageInformation info) { queue.Enqueue(new ShrapnelData() { - OverKill = info.Amount, + OverKill = amount, Neighbours = new List() { slim }, }); info.Amount = 0; @@ -68,14 +74,14 @@ public override void UpdateBeforeSimulation() foreach (IMySlimBlock neighbour in data.Neighbours) { if (neighbour == null) continue; - // get block resistance - float generalMult = 1f; - if (neighbour.BlockDefinition is MyCubeBlockDefinition) - { - generalMult = ((MyCubeBlockDefinition)neighbour.BlockDefinition).GeneralDamageMultiplier; - } + //// get block resistance + //float generalMult = 1f; + //if (neighbour.BlockDefinition is MyCubeBlockDefinition) + //{ + // generalMult = ((MyCubeBlockDefinition)neighbour.BlockDefinition).GeneralDamageMultiplier; + //} - neighbour.DoDamage(data.OverKill * count * generalMult, MyDamageType.Bullet, true); + neighbour.DoDamage(data.OverKill * count, MyDamageType.Bullet, true); } } } From 99f55f2704355096cd651d45f94d95c85d11a7e0 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 22:17:21 -0700 Subject: [PATCH 17/21] crap... more testing. --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 5fe3c8d..e00ba32 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -32,6 +32,8 @@ public void ProcessDamage(object target, ref MyDamageInformation info) float amount = generalMult * info.Amount; + MyLog.Default.Info($"amount: {amount}, integrity: {slim.Integrity}, overkill: {amount - slim.Integrity}"); + if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { if (slim.Integrity >= amount) return; From a0d5b7141d3fa69658bc6f7373043d58ea53e276 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 22:22:37 -0700 Subject: [PATCH 18/21] test --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index e00ba32..0ad8556 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -32,7 +32,7 @@ public void ProcessDamage(object target, ref MyDamageInformation info) float amount = generalMult * info.Amount; - MyLog.Default.Info($"amount: {amount}, integrity: {slim.Integrity}, overkill: {amount - slim.Integrity}"); + MyLog.Default.Info($"generalMult:{generalMult}, amount1: {info.Amount}, amount2: {amount}, integrity: {slim.Integrity}, overkill: {amount - slim.Integrity}"); if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) { From 1aa11f36f6619cb057e2ba62646a9a72ba880b9e Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 22:29:37 -0700 Subject: [PATCH 19/21] test --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 0ad8556..4d6612f 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -16,7 +16,7 @@ public class Core : MySessionComponentBase public override void Init(MyObjectBuilder_SessionComponent sessionComponent) { - MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(500, ProcessDamage); + MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(0, ProcessDamage); } public void ProcessDamage(object target, ref MyDamageInformation info) From 83d1ae2b5fbd0afc7b6fd307a7baf8af5eb19d09 Mon Sep 17 00:00:00 2001 From: Gauge Date: Tue, 23 Jul 2024 22:48:40 -0700 Subject: [PATCH 20/21] test --- ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs index 4d6612f..011fba8 100644 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs @@ -30,6 +30,8 @@ public void ProcessDamage(object target, ref MyDamageInformation info) generalMult = ((MyCubeBlockDefinition)slim.BlockDefinition).GeneralDamageMultiplier; } + MyLog.Default.Info($"generalMult:{generalMult}, amount1: {info.Amount}"); + float amount = generalMult * info.Amount; MyLog.Default.Info($"generalMult:{generalMult}, amount1: {info.Amount}, amount2: {amount}, integrity: {slim.Integrity}, overkill: {amount - slim.Integrity}"); From 7169c43aacbd2d70bb0fb80bc6975978d9d013dc Mon Sep 17 00:00:00 2001 From: Gauge Date: Wed, 24 Jul 2024 19:47:46 -0700 Subject: [PATCH 21/21] remove shrapnel damage --- .../Data/Scripts/shrapnel/Core.cs | 99 ------------------ ShrapnelDamageV2/metadata.mod | 4 - ShrapnelDamageV2/modinfo.sbmi | 11 -- ShrapnelDamageV2/thumb.jpg | Bin 53712 -> 0 bytes 4 files changed, 114 deletions(-) delete mode 100644 ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs delete mode 100644 ShrapnelDamageV2/metadata.mod delete mode 100644 ShrapnelDamageV2/modinfo.sbmi delete mode 100644 ShrapnelDamageV2/thumb.jpg diff --git a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs b/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs deleted file mode 100644 index 011fba8..0000000 --- a/ShrapnelDamageV2/Data/Scripts/shrapnel/Core.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Sandbox.Definitions; -using Sandbox.ModAPI; -using System.Collections.Generic; -using System.IO; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.Utils; - -namespace Shrapnel -{ - [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation)] - public class Core : MySessionComponentBase - { - private Queue queue = new Queue(); - - public override void Init(MyObjectBuilder_SessionComponent sessionComponent) - { - MyAPIGateway.Session.DamageSystem.RegisterBeforeDamageHandler(0, ProcessDamage); - } - - public void ProcessDamage(object target, ref MyDamageInformation info) - { - IMySlimBlock slim = target as IMySlimBlock; - if (slim == null) return; - - float generalMult = 1f; - if (slim.BlockDefinition is MyCubeBlockDefinition) - { - generalMult = ((MyCubeBlockDefinition)slim.BlockDefinition).GeneralDamageMultiplier; - } - - MyLog.Default.Info($"generalMult:{generalMult}, amount1: {info.Amount}"); - - float amount = generalMult * info.Amount; - - MyLog.Default.Info($"generalMult:{generalMult}, amount1: {info.Amount}, amount2: {amount}, integrity: {slim.Integrity}, overkill: {amount - slim.Integrity}"); - - if (info.Type == MyDamageType.Weapon || info.Type == MyDamageType.Bullet || info.Type == MyDamageType.Rocket) - { - if (slim.Integrity >= amount) return; - - float overkill = amount - slim.Integrity; - info.Amount = slim.Integrity; - - List n = new List(); - slim.GetNeighbours(n); - - queue.Enqueue(new ShrapnelData() - { - Neighbours = n, - OverKill = overkill - }); - } - else if (info.Type == MyDamageType.Explosion && !(slim.FatBlock is IMyWarhead)) - { - queue.Enqueue(new ShrapnelData() - { - OverKill = amount, - Neighbours = new List() { slim }, - }); - info.Amount = 0; - - } - - } - - public override void UpdateBeforeSimulation() - { - int tasks = 0; - while (queue.Count > 0 && tasks < 200) - { - tasks++; - ShrapnelData data = queue.Dequeue(); - float count = 1f / (float)data.Neighbours.Count; - - MyLog.Default.Info($"queue: {queue.Count} - overkill: {data.OverKill}, spread: {data.OverKill * count}"); - foreach (IMySlimBlock neighbour in data.Neighbours) - { - if (neighbour == null) continue; - //// get block resistance - //float generalMult = 1f; - //if (neighbour.BlockDefinition is MyCubeBlockDefinition) - //{ - // generalMult = ((MyCubeBlockDefinition)neighbour.BlockDefinition).GeneralDamageMultiplier; - //} - - neighbour.DoDamage(data.OverKill * count, MyDamageType.Bullet, true); - } - } - } - } - - internal class ShrapnelData - { - public float OverKill { get; set; } - public List Neighbours { get; set; } - } -} diff --git a/ShrapnelDamageV2/metadata.mod b/ShrapnelDamageV2/metadata.mod deleted file mode 100644 index 0a020fd..0000000 --- a/ShrapnelDamageV2/metadata.mod +++ /dev/null @@ -1,4 +0,0 @@ - - - 1.0 - \ No newline at end of file diff --git a/ShrapnelDamageV2/modinfo.sbmi b/ShrapnelDamageV2/modinfo.sbmi deleted file mode 100644 index ae8451f..0000000 --- a/ShrapnelDamageV2/modinfo.sbmi +++ /dev/null @@ -1,11 +0,0 @@ - - - 76561198079985653 - 0 - - - 3278152123 - Steam - - - \ No newline at end of file diff --git a/ShrapnelDamageV2/thumb.jpg b/ShrapnelDamageV2/thumb.jpg deleted file mode 100644 index 33aebfb1971cbfa3c0a09d2b490696885b001340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53712 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