Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs
2025-03-12 12:56:27 +00:00

851 lines
36 KiB
C#

using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
namespace Barotrauma
{
partial class Submarine : Entity, IServerPositionSync
{
//drawing ----------------------------------------------------
private static readonly HashSet<Submarine> visibleSubs = new HashSet<Submarine>();
private static double prevCullTime;
private static Rectangle prevCullArea;
/// <summary>
/// Interval at which we force culled entites to be updated, regardless if the camera has moved
/// </summary>
private const float CullInterval = 0.25f;
/// <summary>
/// Margin applied around the view area when culling entities (i.e. entities that are this far outside the view are still considered visible)
/// </summary>
private const int CullMargin = 50;
/// <summary>
/// Update entity culling when any corner of the view has moved more than this
/// </summary>
private const int CullMoveThreshold = 50;
public static void CullEntities(Camera cam)
{
Rectangle camView = cam.WorldView;
camView = new Rectangle(camView.X - CullMargin, camView.Y + CullMargin, camView.Width + CullMargin * 2, camView.Height + CullMargin * 2);
if (Level.Loaded?.Renderer?.CollapseEffectStrength is > 0.0f)
{
//force everything to be visible when the collapse effect (which moves everything to a single point) is active
camView = Rectangle.Union(AbsRect(camView.Location.ToVector2(), camView.Size.ToVector2()), new Rectangle(Point.Zero, Level.Loaded.Size));
camView.Y += camView.Height;
}
if (Math.Abs(camView.X - prevCullArea.X) < CullMoveThreshold &&
Math.Abs(camView.Y - prevCullArea.Y) < CullMoveThreshold &&
Math.Abs(camView.Right - prevCullArea.Right) < CullMoveThreshold &&
Math.Abs(camView.Bottom - prevCullArea.Bottom) < CullMoveThreshold &&
prevCullTime > Timing.TotalTime - CullInterval)
{
return;
}
visibleSubs.Clear();
foreach (Submarine sub in Loaded)
{
if (Level.Loaded != null && sub.WorldPosition.Y < Level.MaxEntityDepth) { continue; }
Rectangle worldBorders = new Rectangle(
sub.VisibleBorders.X + (int)sub.WorldPosition.X,
sub.VisibleBorders.Y + (int)sub.WorldPosition.Y,
sub.VisibleBorders.Width,
sub.VisibleBorders.Height);
if (RectsOverlap(worldBorders, camView))
{
visibleSubs.Add(sub);
}
}
if (visibleEntities == null)
{
visibleEntities = new List<MapEntity>(MapEntity.MapEntityList.Count);
}
else
{
visibleEntities.Clear();
}
foreach (MapEntity entity in MapEntity.MapEntityList)
{
if (entity == null || entity.Removed) { continue; }
if (entity.Submarine != null)
{
if (!visibleSubs.Contains(entity.Submarine)) { continue; }
}
if (entity.IsVisible(camView)) { visibleEntities.Add(entity); }
}
prevCullArea = camView;
prevCullTime = Timing.TotalTime;
}
public static void ForceVisibilityRecheck()
{
prevCullTime = 0;
}
public static void ForceRemoveFromVisibleEntities(MapEntity entity)
{
visibleEntities?.Remove(entity);
}
public static void Draw(SpriteBatch spriteBatch, bool editing = false)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.MapEntityList;
foreach (MapEntity e in entitiesToRender)
{
e.Draw(spriteBatch, editing);
}
}
public static void DrawFront(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.MapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (!e.DrawOverWater) { continue; }
if (predicate != null)
{
if (!predicate(e)) { continue; }
}
e.Draw(spriteBatch, editing, false);
}
if (GameMain.DebugDraw)
{
foreach (Submarine sub in Loaded)
{
Rectangle worldBorders = sub.Borders;
worldBorders.Location += (sub.DrawPosition + sub.HiddenSubPosition).ToPoint();
worldBorders.Y = -worldBorders.Y;
GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5);
if (sub.SubBody == null || sub.subBody.PositionBuffer.Count < 2) continue;
Vector2 prevPos = ConvertUnits.ToDisplayUnits(sub.subBody.PositionBuffer[0].Position);
prevPos.Y = -prevPos.Y;
for (int i = 1; i < sub.subBody.PositionBuffer.Count; i++)
{
Vector2 currPos = ConvertUnits.ToDisplayUnits(sub.subBody.PositionBuffer[i].Position);
currPos.Y = -currPos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 10, (int)currPos.Y - 10, 20, 20), Color.Blue * 0.6f, true, 0.01f);
GUI.DrawLine(spriteBatch, prevPos, currPos, Color.Cyan * 0.5f, 0, 5);
prevPos = currPos;
}
}
}
}
public static float DamageEffectCutoff;
public static void DrawDamageable(SpriteBatch spriteBatch, Effect damageEffect, bool editing = false, Predicate<MapEntity> predicate = null)
{
if (!editing && visibleEntities != null)
{
foreach (MapEntity e in visibleEntities)
{
if (e is Structure structure && structure.DrawDamageEffect)
{
if (predicate != null)
{
if (!predicate(structure)) { continue; }
}
structure.DrawDamage(spriteBatch, damageEffect, editing);
}
}
}
else
{
foreach (Structure structure in Structure.WallList)
{
if (structure.DrawDamageEffect)
{
if (predicate != null)
{
if (!predicate(structure)) { continue; }
}
structure.DrawDamage(spriteBatch, damageEffect, editing);
}
}
}
}
public static void DrawPaintedColors(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.MapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (e is Hull hull)
{
if (hull.SupportsPaintedColors)
{
if (predicate != null)
{
if (!predicate(e)) { continue; }
}
hull.DrawSectionColors(spriteBatch);
}
}
}
}
public static void DrawBack(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.MapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (!e.DrawBelowWater) continue;
if (predicate != null)
{
if (!predicate(e)) continue;
}
e.Draw(spriteBatch, editing, true);
}
}
public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f)
{
Vector2 topLeft = roundedGridCenter - Vector2.One * GridSize * gridCells / 2;
Vector2 bottomRight = roundedGridCenter + Vector2.One * GridSize * gridCells / 2;
for (int i = 0; i < gridCells; i++)
{
float distFromGridX = (MathUtils.RoundTowardsClosest(gridCenter.X, GridSize.X) - gridCenter.X) / GridSize.X;
float distFromGridY = (MathUtils.RoundTowardsClosest(gridCenter.Y, GridSize.Y) - gridCenter.Y) / GridSize.Y;
float normalizedDistX = Math.Abs(i + distFromGridX - gridCells / 2) / (gridCells / 2);
float normalizedDistY = Math.Abs(i - distFromGridY - gridCells / 2) / (gridCells / 2);
float expandX = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistX);
GUI.DrawLine(spriteBatch,
new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y),
new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y),
Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3);
GUI.DrawLine(spriteBatch,
new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY),
new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY),
Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3);
}
}
// TODO remove
[Obsolete("Use MiniMap.CreateMiniMap()")]
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null, bool ignoreOutpost = false)
{
Rectangle worldBorders = GetDockedBorders();
worldBorders.Location += WorldPosition.ToPoint();
//create a container that has the same "aspect ratio" as the sub
float aspectRatio = worldBorders.Width / (float)worldBorders.Height;
float parentAspectRatio = parent.Rect.Width / (float)parent.Rect.Height;
float scale = 0.9f;
GUIFrame hullContainer = new GUIFrame(new RectTransform(
(parentAspectRatio > aspectRatio ? new Vector2(aspectRatio / parentAspectRatio, 1.0f) : new Vector2(1.0f, parentAspectRatio / aspectRatio)) * scale,
parent.RectTransform, Anchor.Center),
style: null)
{
UserData = "hullcontainer"
};
var connectedSubs = GetConnectedSubs();
HashSet<Hull> hullList = Hull.HullList.Where(hull => hull.Submarine == this || connectedSubs.Contains(hull.Submarine)).Where(hull => !ignoreOutpost || IsEntityFoundOnThisSub(hull, true)).ToHashSet();
Dictionary<Hull, HashSet<Hull>> combinedHulls = new Dictionary<Hull, HashSet<Hull>>();
foreach (Hull hull in hullList)
{
if (combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull))) { continue; }
List<Hull> linkedHulls = new List<Hull>();
MiniMap.GetLinkedHulls(hull, linkedHulls);
linkedHulls.Remove(hull);
foreach (Hull linkedHull in linkedHulls)
{
if (!combinedHulls.ContainsKey(hull))
{
combinedHulls.Add(hull, new HashSet<Hull>());
}
combinedHulls[hull].Add(linkedHull);
}
}
foreach (Hull hull in hullList)
{
Vector2 relativeHullPos = new Vector2(
(hull.WorldRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - hull.WorldRect.Y) / (float)worldBorders.Height);
Vector2 relativeHullSize = new Vector2(hull.Rect.Width / (float)worldBorders.Width, hull.Rect.Height / (float)worldBorders.Height);
bool hideHull = combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull));
if (hideHull) { continue; }
Color color = Color.DarkCyan * 0.8f;
var hullFrame = new GUIFrame(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, style: "MiniMapRoom", color: color)
{
UserData = hull
};
new GUIFrame(new RectTransform(Vector2.One, hullFrame.RectTransform), style: "ScanLines", color: color);
}
foreach (var (mainHull, linkedHulls) in combinedHulls)
{
MiniMapHullData data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
Vector2 relativeHullPos = new Vector2(
(data.Bounds.X - worldBorders.X) / worldBorders.Width,
(worldBorders.Y - data.Bounds.Y) / worldBorders.Height);
Vector2 relativeHullSize = new Vector2(data.Bounds.Width / worldBorders.Width, data.Bounds.Height / worldBorders.Height);
Color color = Color.DarkCyan * 0.8f;
float highestY = 0f,
highestX = 0f;
foreach (var (r, _) in data.RectDatas)
{
float y = r.Y - -r.Height,
x = r.X;
if (y > highestY) { highestY = y; }
if (x > highestX) { highestX = x; }
}
HashSet<GUIFrame> frames = new HashSet<GUIFrame>();
foreach (var (snappredRect, hull) in data.RectDatas)
{
RectangleF rect = snappredRect;
rect.Height = -rect.Height;
rect.Y -= rect.Height;
var (parentW, parentH) = hullContainer.Rect.Size.ToVector2();
Vector2 size = new Vector2(rect.Width / parentW, rect.Height / parentH);
// TODO this won't be required if we some day switch RectTransform to use RectangleF
Vector2 pos = new Vector2(rect.X / parentW, rect.Y / parentH);
GUIFrame hullFrame = new GUIFrame(new RectTransform(size, hullContainer.RectTransform) { RelativeOffset = pos }, style: "ScanLinesSeamless", color: color)
{
UserData = hull,
UVOffset = new Vector2(highestX - rect.X, highestY - rect.Y)
};
frames.Add(hullFrame);
}
new GUICustomComponent(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, (spriteBatch, component) =>
{
foreach (List<Vector2> list in data.Polygon)
{
spriteBatch.DrawPolygonInner(hullContainer.Rect.Location.ToVector2(), list, component.Color, 2f);
}
}, (deltaTime, component) =>
{
if (component.Parent.Rect.Size != data.ParentSize)
{
data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
}
})
{
UserData = frames,
Color = color,
CanBeFocused = false
};
}
if (pointsOfInterest != null)
{
foreach (Entity entity in pointsOfInterest)
{
Vector2 relativePos = new Vector2(
(entity.WorldPosition.X - worldBorders.X) / worldBorders.Width,
(worldBorders.Y - entity.WorldPosition.Y) / worldBorders.Height);
new GUIFrame(new RectTransform(new Point(1, 1), hullContainer.RectTransform) { RelativeOffset = relativePos }, style: null)
{
CanBeFocused = false,
UserData = entity
};
}
}
}
public static MiniMapHullData ConstructLinkedHulls(Hull mainHull, HashSet<Hull> linkedHulls, GUIComponent parent, Rectangle worldBorders)
{
Rectangle parentRect = parent.Rect;
Dictionary<Hull, Rectangle> rects = new Dictionary<Hull, Rectangle>();
Rectangle worldRect = mainHull.WorldRect;
worldRect.Y = -worldRect.Y;
rects.Add(mainHull, worldRect);
foreach (Hull hull in linkedHulls)
{
Rectangle rect = hull.WorldRect;
rect.Y = -rect.Y;
worldRect = Rectangle.Union(worldRect, rect);
rects.Add(hull, rect);
}
worldRect.Y = -worldRect.Y;
List<RectangleF> normalizedRects = new List<RectangleF>();
List<Hull> hullRefs = new List<Hull>();
foreach (var (hull, rect) in rects)
{
Rectangle wRect = rect;
wRect.Y = -wRect.Y;
var (posX, posY) = new Vector2(
(wRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - wRect.Y) / (float)worldBorders.Height);
var (scaleX, scaleY) = new Vector2(wRect.Width / (float)worldBorders.Width, wRect.Height / (float)worldBorders.Height);
RectangleF newRect = new RectangleF(posX * parentRect.Width, posY * parentRect.Height, scaleX * parentRect.Width, scaleY * parentRect.Height);
normalizedRects.Add(newRect);
hullRefs.Add(hull);
}
ImmutableArray<RectangleF> snappedRectangles = ToolBox.SnapRectangles(normalizedRects, treshold: 1);
List<List<Vector2>> polygon = ToolBox.CombineRectanglesIntoShape(snappedRectangles);
List<List<Vector2>> scaledPolygon = new List<List<Vector2>>();
foreach (List<Vector2> list in polygon)
{
var (polySizeX, polySizeY) = ToolBox.GetPolygonBoundingBoxSize(list);
float sizeX = polySizeX - 1f,
sizeY = polySizeY - 1f;
scaledPolygon.Add(ToolBox.ScalePolygon(list, new Vector2(sizeX / polySizeX, sizeY / polySizeY)));
}
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
public void CheckForErrors()
{
List<string> errorMsgs = new List<string>();
List<SubEditorScreen.WarningType> warnings = new List<SubEditorScreen.WarningType>();
if (!Hull.HullList.Any())
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
{
errorMsgs.Add(TextManager.Get("NoHullsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoHulls);
}
}
if (Info.Type != SubmarineType.OutpostModule ||
(Info.OutpostModuleInfo?.ModuleFlags.Any(f => f != "hallwayvertical" && f != "hallwayhorizontal") ?? true))
{
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
{
errorMsgs.Add(TextManager.Get("NoWaypointsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoWaypoints);
}
}
}
if (Hull.HullList.Any(h => h.WaterVolume > 0.0f))
{
errorMsgs.Add(TextManager.Get("WaterInHullsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.WaterInHulls);
Hull.ShowHulls = true;
}
if (Info.IsWreck)
{
Point vanillaBrainSize = new Point(204, 204);
if (WreckAI.GetPotentialBrainRooms(this, WreckAIConfig.GetRandom(), minSize: vanillaBrainSize).None())
{
errorMsgs.Add(TextManager.Get("NoSuitableBrainRoomsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoSuitableBrainRooms);
}
}
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NotEnoughContainers))
{
HashSet<ContainerTagPrefab> missingContainerTags = new();
foreach (var prefab in ContainerTagPrefab.Prefabs)
{
if (!prefab.IsRecommendedForSub(this) || !prefab.WarnIfLess) { continue; }
int count = Item.ItemList.Count(i => i.HasTag(prefab.Identifier));
if (count < prefab.RecommendedAmount)
{
missingContainerTags.Add(prefab);
}
}
if (missingContainerTags.Any())
{
StringBuilder sb = new();
int count = 0;
foreach (var tag in missingContainerTags)
{
sb.AppendLine($"- {tag.Name}");
count++;
if (missingContainerTags.Count > count && count >= 3)
{
var moreIndicator = TextManager.GetWithVariable(
"upgradeuitooltip.moreindicator",
"[amount]",
(missingContainerTags.Count - count).ToString()).Value;
sb.AppendLine(moreIndicator);
break;
}
}
errorMsgs.Add(TextManager.GetWithVariable(
"ContainerTagUI.CountWarning",
"[tags]",
sb.ToString()).Value);
warnings.Add(SubEditorScreen.WarningType.NotEnoughContainers);
}
}
if (Info.Type == SubmarineType.Player)
{
foreach (Item item in Item.ItemList)
{
if (item.GetComponent<Vent>() == null) { continue; }
if (!item.linkedTo.Any())
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.DisconnectedVents))
{
errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.DisconnectedVents);
}
break;
}
}
foreach (Item item in Item.ItemList)
{
if (item.GetComponent<OxygenGenerator>() is not OxygenGenerator oxygenGenerator) { continue; }
Dictionary<Hull, float> hullOxygenFlow = new Dictionary<Hull, float>();
foreach (var linkedTo in item.linkedTo)
{
if (linkedTo is not Item linkedItem || linkedItem.GetComponent<Vent>() is not Vent vent) { continue; }
if (vent.Item.CurrentHull == null)
{
vent.Item.FindHull();
if (vent.Item.CurrentHull == null) { continue; }
}
float oxygenFlow = oxygenGenerator.GetVentOxygenFlow(vent);
if (!hullOxygenFlow.ContainsKey(vent.Item.CurrentHull))
{
hullOxygenFlow[vent.Item.CurrentHull] = oxygenFlow;
}
else
{
hullOxygenFlow[vent.Item.CurrentHull] += oxygenFlow;
}
}
foreach ((Hull hull, float oxygenFlow) in hullOxygenFlow)
{
if (oxygenFlow < Hull.OxygenConsumptionSpeed)
{
errorMsgs.Add(TextManager.GetWithVariable("LowOxygenOutputWarning", "[roomname]",
hull.DisplayName).Value);
warnings.Add(SubEditorScreen.WarningType.LowOxygenOutputWarning);
}
}
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoHumanSpawnpoints))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoHumanSpawnpoints);
}
}
if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoCargoSpawnpoints))
{
errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoCargoSpawnpoints);
}
}
if (Item.ItemList.None(it => it.GetComponent<Pump>() != null && it.HasTag(Tags.Ballast)))
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoBallastTag))
{
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoBallastTag);
}
}
if (Item.ItemList.None(it => it.HasTag(Tags.HiddenItemContainer)))
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoHiddenContainers))
{
errorMsgs.Add(TextManager.Get("NoHiddenContainersWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NoHiddenContainers);
}
}
if (Info.Dimensions.X * Physics.DisplayToRealWorldRatio > 80 ||
Info.Dimensions.Y * Physics.DisplayToRealWorldRatio > 32)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.TooLargeForEndGame))
{
errorMsgs.Add(TextManager.Get("TooLargeForEndGameWarning").Value);
warnings.Add(SubEditorScreen.WarningType.TooLargeForEndGame);
}
}
}
else if (Info.Type == SubmarineType.OutpostModule)
{
foreach (Item item in Item.ItemList)
{
var junctionBox = item.GetComponent<PowerTransfer>();
if (junctionBox == null) { continue; }
int doorLinks =
item.linkedTo.Count(lt => lt is Gap || (lt is Item it2 && it2.GetComponent<Door>() != null)) +
Item.ItemList.Count(it2 => it2.linkedTo.Contains(item) && !item.linkedTo.Contains(it2));
for (int i = 0; i < item.Connections.Count; i++)
{
int wireCount = item.Connections[i].Wires.Count;
if (doorLinks + wireCount > item.Connections[i].MaxWires)
{
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
("[doorcount]", doorLinks.ToString()),
("[freeconnectioncount]", (item.Connections[i].MaxWires - wireCount).ToString())).Value);
warnings.Add(SubEditorScreen.WarningType.InsufficientFreeConnectionsWarning);
break;
}
}
}
}
if (Gap.GapList.Any(g => g.linkedTo.Count == 0))
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NonLinkedGaps))
{
errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning").Value);
warnings.Add(SubEditorScreen.WarningType.NonLinkedGaps);
}
}
float entityCountWarningThreshold = 0.75f;
if (Item.ItemList.Count > SubEditorScreen.MaxItems * entityCountWarningThreshold)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.ItemCount))
{
errorMsgs.Add(TextManager.Get("subeditor.itemcountwarning").Value);
warnings.Add(SubEditorScreen.WarningType.ItemCount);
}
}
if ((MapEntity.MapEntityList.Count - Item.ItemList.Count - Hull.HullList.Count - WayPoint.WayPointList.Count - Gap.GapList.Count) > SubEditorScreen.MaxStructures * entityCountWarningThreshold)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.StructureCount))
{
errorMsgs.Add(TextManager.Get("subeditor.structurecountwarning").Value);
warnings.Add(SubEditorScreen.WarningType.StructureCount);
}
}
if (Structure.WallList.Count > SubEditorScreen.MaxStructures * entityCountWarningThreshold)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.WallCount))
{
errorMsgs.Add(TextManager.Get("subeditor.wallcountwarning").Value);
warnings.Add(SubEditorScreen.WarningType.WallCount);
}
}
if (GetLightCount() > SubEditorScreen.MaxLights * entityCountWarningThreshold)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.LightCount))
{
errorMsgs.Add(TextManager.Get("subeditor.lightcountwarning").Value);
warnings.Add(SubEditorScreen.WarningType.LightCount);
}
}
if (GetShadowCastingLightCount() > SubEditorScreen.MaxShadowCastingLights * entityCountWarningThreshold)
{
if (!IsWarningSuppressed(SubEditorScreen.WarningType.ShadowCastingLightCount))
{
errorMsgs.Add(TextManager.Get("subeditor.shadowcastinglightswarning").Value);
warnings.Add(SubEditorScreen.WarningType.ShadowCastingLightCount);
}
}
if (errorMsgs.Any())
{
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("Warning"), string.Empty, new Vector2(0.25f, 0.0f), minSize: new Point(GUI.IntScale(650), GUI.IntScale(650)));
if (warnings.Any())
{
var textListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), msgBox.Content.RectTransform));
var text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textListBox.Content.RectTransform), string.Join("\n\n", errorMsgs), wrap: true)
{
CanBeFocused = false
};
text.RectTransform.MinSize = new Point(0, (int)text.TextSize.Y);
Point size = msgBox.RectTransform.NonScaledSize;
GUITickBox suppress = new GUITickBox(new RectTransform(new Vector2(1f, 0.33f), msgBox.Content.RectTransform), TextManager.Get("editor.suppresswarnings"));
msgBox.RectTransform.NonScaledSize = new Point(size.X, size.Y + suppress.RectTransform.NonScaledSize.Y);
msgBox.Buttons[0].OnClicked += (button, obj) =>
{
if (suppress.Selected)
{
foreach (SubEditorScreen.WarningType warning in warnings.Where(warning => !SubEditorScreen.SuppressedWarnings.Contains(warning)))
{
SubEditorScreen.SuppressedWarnings.Add(warning);
}
}
return true;
};
}
}
foreach (MapEntity e in MapEntity.MapEntityList)
{
if (Vector2.Distance(e.Position, HiddenSubPosition) > 20000)
{
//move disabled items (wires, items inside containers) inside the sub
if (e is Item item && item.body != null && !item.body.Enabled)
{
item.SetTransform(ConvertUnits.ToSimUnits(HiddenSubPosition), 0.0f);
}
}
}
foreach (MapEntity e in MapEntity.MapEntityList)
{
if (Vector2.Distance(e.Position, HiddenSubPosition) > 20000)
{
var msgBox = new GUIMessageBox(
TextManager.Get("Warning"),
TextManager.Get("FarAwayEntitiesWarning"),
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked += (btn, obj) =>
{
GameMain.SubEditorScreen.Cam.Position = e.WorldPosition;
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
break;
}
}
bool IsWarningSuppressed(SubEditorScreen.WarningType type)
{
return SubEditorScreen.SuppressedWarnings.Contains(type);
}
}
public static int GetLightCount()
{
int disabledItemLightCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.ParentInventory == null) { continue; }
disabledItemLightCount += item.GetComponents<Items.Components.LightComponent>().Count();
}
return GameMain.LightManager.Lights.Count() - disabledItemLightCount;
}
public static int GetShadowCastingLightCount()
{
int disabledItemLightCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.ParentInventory == null) { continue; }
disabledItemLightCount += item.GetComponents<Items.Components.LightComponent>().Count();
}
return GameMain.LightManager.Lights.Count(l => l.CastShadows && !l.IsBackground) - disabledItemLightCount;
}
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub, Vector2? mousePos = null, bool round = false)
{
Vector2 position = mousePos ?? PlayerInput.MousePosition;
position = cam.ScreenToWorld(position);
Vector2 worldGridPos = VectorToWorldGrid(position, sub, round);
return worldGridPos;
}
public void ClientReadPosition(IReadMessage msg, float sendingTime)
{
var posInfo = PhysicsBody.ClientRead(msg, sendingTime, parentDebugName: Info.Name);
msg.ReadPadBits();
if (posInfo != null)
{
int index = 0;
while (index < subBody.PositionBuffer.Count && sendingTime > subBody.PositionBuffer[index].Timestamp)
{
index++;
}
subBody.PositionBuffer.Insert(index, posInfo);
}
}
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
Identifier layerIdentifier = msg.ReadIdentifier();
bool enabled = msg.ReadBoolean();
SetLayerEnabled(layerIdentifier, enabled);
}
}
}