private void UpdateDisruptions(Vector2 pingSource, float worldPingRadius, float worldPrevPingRadius) { float worldPingRadiusSqr = worldPingRadius * worldPingRadius; float worldPrevPingRadiusSqr = worldPrevPingRadius * worldPrevPingRadius; disruptedDirections.Clear(); if (Level.Loaded == null) { return; } foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * pingState)) { if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; } float disruptionStrength = levelObject.ActivePrefab.SonarDisruption; Vector2 disruptionPos = new Vector2(levelObject.Position.X, levelObject.Position.Y); float disruptionDist = Vector2.Distance(pingSource, disruptionPos); disruptedDirections.Add(new Pair <Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength)); if (disruptionDist > worldPrevPingRadius && disruptionDist <= worldPingRadius) { for (int i = 0; i < disruptionStrength * Level.GridCellSize * 0.02f; i++) { var blip = new SonarBlip(disruptionPos + Rand.Vector(Rand.Range(0.0f, Level.GridCellSize * 4 * disruptionStrength)), MathHelper.Lerp(1.0f, 1.5f, disruptionStrength), Rand.Range(1.0f, 2.0f + disruptionStrength)); sonarBlips.Add(blip); } } } }
private bool CheckBlipVisibility(SonarBlip blip, Vector2 transducerPos) { Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom; pos.Y = -pos.Y; float posDistSqr = pos.LengthSquared(); if (posDistSqr > DisplayRadius * DisplayRadius) { blip.FadeTimer = 0.0f; return(false); } Vector2 dir = pos / (float)Math.Sqrt(posDistSqr); if (isLastPingDirectional) { if (Vector2.Dot(lastPingDirection, dir) < DirectionalPingDotProduct) { blip.FadeTimer = 0.0f; return(false); } } return(true); }
private void UpdateDisruptions(Vector2 pingSource, float worldPingRadius, float worldPrevPingRadius) { float worldPingRadiusSqr = worldPingRadius * worldPingRadius; float worldPrevPingRadiusSqr = worldPrevPingRadius * worldPrevPingRadius; disruptedDirections.Clear(); float searchRadius = Math.Min(range, worldPingRadius * 2); for (float x = pingSource.X - searchRadius; x < pingSource.X + searchRadius; x += Level.GridCellSize) { for (float y = pingSource.Y - searchRadius; y < pingSource.Y + searchRadius; y += Level.GridCellSize) { Vector2 disruptionPos = new Vector2( MathUtils.RoundTowardsClosest(x, Level.GridCellSize) + Level.GridCellSize / 2, MathUtils.RoundTowardsClosest(y, Level.GridCellSize) + Level.GridCellSize / 2); float disruptionStrength = Level.Loaded.GetSonarDisruptionStrength(disruptionPos); if (disruptionStrength > 0.0f) { float disruptionDist = Vector2.Distance(pingSource, disruptionPos); disruptedDirections.Add(new Pair <Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength)); if (disruptionDist > worldPrevPingRadius && disruptionDist <= worldPingRadius) { for (int i = 0; i < disruptionStrength * Level.GridCellSize * 0.02f; i++) { var blip = new SonarBlip(disruptionPos + Rand.Vector(Rand.Range(0.0f, Level.GridCellSize * 4 * disruptionStrength)), MathHelper.Lerp(1.0f, 1.5f, disruptionStrength), Rand.Range(1.0f, 2.0f + disruptionStrength)); sonarBlips.Add(blip); } } } } } }
private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength) { strength = MathHelper.Clamp(strength, 0.0f, 1.0f); float distort = 1.0f - item.Condition / 100.0f; Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom; pos.Y = -pos.Y; if (Rand.Range(0.5f, 2.0f) < distort) { pos.X = -pos.X; } if (Rand.Range(0.5f, 2.0f) < distort) { pos.Y = -pos.Y; } float posDistSqr = pos.LengthSquared(); if (posDistSqr > DisplayRadius * DisplayRadius) { blip.FadeTimer = 0.0f; return; } if (sonarBlip == null) { GUI.DrawRectangle(spriteBatch, center + pos, Vector2.One * 4, Color.Magenta, true); return; } Vector2 dir = pos / (float)Math.Sqrt(posDistSqr); Vector2 normal = new Vector2(dir.Y, -dir.X); float scale = (strength + 3.0f) * blip.Scale * zoomSqrt; Color color = ToolBox.GradientLerp(strength, blipColorGradient); sonarBlip.Draw(spriteBatch, center + pos, color, sonarBlip.Origin, blip.Rotation ?? MathUtils.VectorToAngle(pos), blip.Size * scale * 0.04f, SpriteEffects.None, 0); pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-scale, scale) * normal; sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale * 0.08f, SpriteEffects.None, 0); }
private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 transducerPos, float pingRadius, float prevPingRadius, float lineStep, float zStep, float range, float pingStrength, bool passive) { lineStep /= zoom; zStep /= zoom; range *= displayScale; float length = (point1 - point2).Length(); Vector2 lineDir = (point2 - point1) / length; for (float x = 0; x < length; x += lineStep * Rand.Range(0.8f, 1.2f)) { Vector2 point = point1 + lineDir * x; //point += cell.Translation; Vector2 pointDiff = point - transducerPos; float pointDist = pointDiff.Length(); float displayPointDist = pointDist * displayScale; if (displayPointDist > DisplayRadius) { continue; } if (displayPointDist < prevPingRadius || displayPointDist > pingRadius) { continue; } bool disrupted = false; foreach (Pair <Vector2, float> disruptDir in disruptedDirections) { float dot = Vector2.Dot(pointDiff / pointDist, disruptDir.First); if (dot > 1.0f - disruptDir.Second) { disrupted = true; break; } } if (disrupted) { continue; } float alpha = pingStrength * Rand.Range(1.5f, 2.0f); for (float z = 0; z < DisplayRadius - displayPointDist; z += zStep) { Vector2 pos = point + Rand.Vector(150.0f / zoom) + Vector2.Normalize(point - item.WorldPosition) * z / displayScale; float fadeTimer = alpha * (1.0f - displayPointDist / range); int minDist = (int)(200 / zoom); sonarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist); var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius)); if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); zStep += 0.5f / zoom; if (z == 0) { alpha = Math.Min(alpha - 0.5f, 1.5f); } else { alpha -= 0.1f; } if (alpha < 0) { break; } } } }
private void Ping(Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, float displayScale, float range, bool passive, float pingStrength = 1.0f) { float prevPingRadiusSqr = prevPingRadius * prevPingRadius; float pingRadiusSqr = pingRadius * pingRadius; //inside a hull -> only show the edges of the hull if (item.CurrentHull != null && DetectSubmarineWalls) { CreateBlipsForLine( new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y), new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y), transducerPos, pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f, passive); CreateBlipsForLine( new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height), new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height), transducerPos, pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f, passive); CreateBlipsForLine( new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y), new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height), transducerPos, pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f, passive); CreateBlipsForLine( new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y), new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height), transducerPos, pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f, passive); return; } foreach (Submarine submarine in Submarine.Loaded) { if (submarine.HullVertices == null) { continue; } if (!DetectSubmarineWalls) { if (UseTransducers) { if (connectedTransducers.Any(t => submarine == t.Transducer.Item.Submarine || submarine.DockedTo.Contains(t.Transducer.Item.Submarine))) { continue; } } else { if (item.Submarine == submarine) { continue; } if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) { continue; } } } for (int i = 0; i < submarine.HullVertices.Count; i++) { Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]); Vector2 end = ConvertUnits.ToDisplayUnits(submarine.HullVertices[(i + 1) % submarine.HullVertices.Count]); if (item.Submarine == submarine) { start += Rand.Vector(500.0f); end += Rand.Vector(500.0f); } CreateBlipsForLine( start + submarine.WorldPosition, end + submarine.WorldPosition, transducerPos, pingRadius, prevPingRadius, 200.0f, 2.0f, range, 1.0f, passive); } } if (Level.Loaded != null && (item.CurrentHull == null || !DetectSubmarineWalls)) { if (Level.Loaded.Size.Y - pingSource.Y < range) { CreateBlipsForLine( new Vector2(pingSource.X - range, Level.Loaded.Size.Y), new Vector2(pingSource.X + range, Level.Loaded.Size.Y), transducerPos, pingRadius, prevPingRadius, 250.0f, 150.0f, range, pingStrength, passive); } List <Voronoi2.VoronoiCell> cells = Level.Loaded.GetCells(pingSource, 7); foreach (Voronoi2.VoronoiCell cell in cells) { foreach (Voronoi2.GraphEdge edge in cell.Edges) { if (!edge.IsSolid) { continue; } float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center); if (cellDot > 0) { continue; } float facingDot = Vector2.Dot( Vector2.Normalize(edge.Point1 - edge.Point2), Vector2.Normalize(cell.Center - pingSource)); CreateBlipsForLine( edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, transducerPos, pingRadius, prevPingRadius, 350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength, passive); } } foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins) { if (!MathUtils.CircleIntersectsRectangle(pingSource, range, ruin.Area)) { continue; } foreach (var ruinShape in ruin.RuinShapes) { foreach (RuinGeneration.Line wall in ruinShape.Walls) { float cellDot = Vector2.Dot( Vector2.Normalize(ruinShape.Center - pingSource), Vector2.Normalize((wall.A + wall.B) / 2.0f - ruinShape.Center)); if (cellDot > 0) { continue; } CreateBlipsForLine( wall.A, wall.B, transducerPos, pingRadius, prevPingRadius, 100.0f, 1000.0f, range, pingStrength, passive); } } } } foreach (Item item in Item.ItemList) { if (item.CurrentHull == null && item.Prefab.SonarSize > 0.0f) { float pointDist = ((item.WorldPosition - pingSource) * displayScale).LengthSquared(); if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) { var blip = new SonarBlip( item.WorldPosition + Rand.Vector(item.Prefab.SonarSize), MathHelper.Clamp(item.Prefab.SonarSize, 0.1f, pingStrength), MathHelper.Clamp(item.Prefab.SonarSize * 0.1f, 0.1f, 10.0f)); if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); } } } foreach (Character c in Character.CharacterList) { if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; } if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; } foreach (Limb limb in c.AnimController.Limbs) { float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared(); if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) { continue; } if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) { var blip = new SonarBlip( limb.WorldPosition + Rand.Vector(limb.Mass / 10.0f), MathHelper.Clamp(limb.Mass, 0.1f, pingStrength), MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f)); if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); } } } }
public override void UpdateHUD(Character character, float deltaTime, Camera cam) { if (unsentChanges) { if (networkUpdateTimer <= 0.0f) { #if CLIENT if (GameMain.Client != null) { item.CreateClientEvent(this); correctionTimer = CorrectionDelay; } #endif if (GameMain.Server != null) { item.CreateServerEvent(this); } networkUpdateTimer = 0.1f; unsentChanges = false; } } networkUpdateTimer -= deltaTime; if (sonarView.Rect.Contains(PlayerInput.MousePosition)) { float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f; if (Math.Abs(scrollSpeed) > 0.0001f) { zoomSlider.BarScroll += PlayerInput.ScrollWheelSpeed / 1000.0f; zoomSlider.OnMoved(zoomSlider, zoomSlider.BarScroll); } } float distort = 1.0f - item.Condition / 100.0f; for (int i = sonarBlips.Count - 1; i >= 0; i--) { sonarBlips[i].FadeTimer -= deltaTime * MathHelper.Lerp(0.5f, 2.0f, distort); sonarBlips[i].Position += sonarBlips[i].Velocity * deltaTime; if (sonarBlips[i].FadeTimer <= 0.0f) { sonarBlips.RemoveAt(i); } } foreach (GUIComponent component in activeControlsContainer.Parent.GetAllChildren()) { if (component is GUITextBlock textBlock) { textBlock.TextColor = IsActive ? textBlock.Style.textColor : textBlock.Style.textColor * 0.5f; } } //sonar view can only get focus when the cursor is inside the circle sonarView.CanBeFocused = Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) < (sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2); if (UseTransducers && connectedTransducers.Count == 0) { return; } Vector2 transducerCenter = UseTransducers ? GetTransducerCenter() : item.WorldPosition; transducerCenter += DisplayOffset; if (Level.Loaded != null) { Dictionary <LevelTrigger, Vector2> levelTriggerFlows = new Dictionary <LevelTrigger, Vector2>(); foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * pingState / zoom)) { //gather all nearby triggers that are causing the water to flow into the dictionary foreach (LevelTrigger trigger in levelObject.Triggers) { Vector2 flow = trigger.GetWaterFlowVelocity(); //ignore ones that are barely doing anything (flow^2 < 1) if (flow.LengthSquared() > 1.0f) { levelTriggerFlows.Add(trigger, flow); } } } foreach (KeyValuePair <LevelTrigger, Vector2> triggerFlow in levelTriggerFlows) { LevelTrigger trigger = triggerFlow.Key; Vector2 flow = triggerFlow.Value; float flowMagnitude = flow.Length(); if (Rand.Range(0.0f, 1.0f) < flowMagnitude / 1000.0f) { float edgeDist = Rand.Range(0.0f, 1.0f); Vector2 blipPos = trigger.WorldPosition + Rand.Vector(trigger.ColliderRadius * edgeDist); Vector2 blipVel = flow; if (trigger.ForceFalloff) { flow *= (1.0f - edgeDist); } //go through other triggers in range and add the flows of the ones that the blip is inside foreach (KeyValuePair <LevelTrigger, Vector2> triggerFlow2 in levelTriggerFlows) { LevelTrigger trigger2 = triggerFlow2.Key; if (trigger2 != trigger && Vector2.DistanceSquared(blipPos, trigger2.WorldPosition) < trigger2.ColliderRadius * trigger2.ColliderRadius) { Vector2 trigger2flow = triggerFlow2.Value; if (trigger2.ForceFalloff) { trigger2flow *= (1.0f - Vector2.Distance(blipPos, trigger2.WorldPosition) / trigger2.ColliderRadius); } blipVel += trigger2flow; } } var flowBlip = new SonarBlip(blipPos, Rand.Range(0.5f, 1.0f), 1.0f) { Velocity = blipVel * Rand.Range(1.0f, 5.0f), Size = new Vector2(MathHelper.Lerp(0.4f, 5f, flowMagnitude / 500.0f), 0.2f), Rotation = (float)Math.Atan2(-blipVel.Y, blipVel.X) }; sonarBlips.Add(flowBlip); } } } if (IsActive) { float pingRadius = DisplayRadius * pingState / zoom; UpdateDisruptions(transducerCenter, pingRadius / displayScale, prevPingRadius / displayScale); Ping(transducerCenter, transducerCenter, pingRadius, prevPingRadius, displayScale, range / zoom, passive: false, pingStrength: 2.0f); prevPingRadius = pingRadius; return; } float passivePingRadius = (float)Math.Sin(Timing.TotalTime * 10); if (passivePingRadius > 0.0f) { disruptedDirections.Clear(); foreach (AITarget t in AITarget.List) { if (t.SoundRange <= 0.0f || !t.Enabled) { continue; } if (Vector2.DistanceSquared(t.WorldPosition, transducerCenter) < t.SoundRange * t.SoundRange) { Ping(t.WorldPosition, transducerCenter, t.SoundRange * passivePingRadius * 0.2f, t.SoundRange * prevPassivePingRadius * 0.2f, displayScale, t.SoundRange, passive: true, pingStrength: 0.5f); sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f)); } } } prevPassivePingRadius = passivePingRadius; }