Unstable v0.19.3.0
This commit is contained in:
@@ -25,6 +25,12 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Diagonal" gaps are used on sloped walls to allow characters to pass through them either horizontally or vertically.
|
||||
/// Water still flows through them only horizontally or vertically
|
||||
/// </summary>
|
||||
public bool IsDiagonal { get; }
|
||||
|
||||
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
|
||||
private float open;
|
||||
|
||||
@@ -135,12 +141,13 @@ namespace Barotrauma
|
||||
: this(rect, rect.Width < rect.Height, submarine)
|
||||
{ }
|
||||
|
||||
public Gap(Rectangle rect, bool isHorizontal, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
public Gap(Rectangle rect, bool isHorizontal, Submarine submarine, bool isDiagonal = false, ushort id = Entity.NullEntityID)
|
||||
: base(CoreEntityPrefab.GapPrefab, submarine, id)
|
||||
{
|
||||
this.rect = rect;
|
||||
flowForce = Vector2.Zero;
|
||||
IsHorizontal = isHorizontal;
|
||||
IsDiagonal = isDiagonal;
|
||||
open = 1.0f;
|
||||
|
||||
FindHulls();
|
||||
@@ -666,15 +673,15 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Gap gap in gaps)
|
||||
{
|
||||
if (gap.Open == 0.0f || gap.IsRoomToRoom) continue;
|
||||
if (gap.Open == 0.0f || gap.IsRoomToRoom) { continue; }
|
||||
|
||||
if (gap.ConnectedWall != null)
|
||||
{
|
||||
int sectionIndex = gap.ConnectedWall.FindSectionIndex(gap.Position);
|
||||
if (sectionIndex > -1 && !gap.ConnectedWall.SectionBodyDisabled(sectionIndex)) continue;
|
||||
if (sectionIndex > -1 && !gap.ConnectedWall.SectionBodyDisabled(sectionIndex)) { continue; }
|
||||
}
|
||||
|
||||
if (gap.IsHorizontal)
|
||||
if (gap.IsHorizontal || gap.IsDiagonal)
|
||||
{
|
||||
if (worldPos.Y < gap.WorldRect.Y && worldPos.Y > gap.WorldRect.Y - gap.WorldRect.Height &&
|
||||
Math.Abs(gap.WorldRect.Center.X - worldPos.X) < allowedOrthogonalDist)
|
||||
@@ -682,7 +689,7 @@ namespace Barotrauma
|
||||
return gap;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!gap.IsHorizontal || gap.IsDiagonal)
|
||||
{
|
||||
if (worldPos.X > gap.WorldRect.X && worldPos.X < gap.WorldRect.Right &&
|
||||
Math.Abs(gap.WorldRect.Y - gap.WorldRect.Height / 2 - worldPos.Y) < allowedOrthogonalDist)
|
||||
@@ -754,7 +761,7 @@ namespace Barotrauma
|
||||
isHorizontal = horizontalAttribute.Value.ToString() == "true";
|
||||
}
|
||||
|
||||
Gap g = new Gap(rect, isHorizontal, submarine, idRemap.GetOffsetId(element))
|
||||
Gap g = new Gap(rect, isHorizontal, submarine, id: idRemap.GetOffsetId(element))
|
||||
{
|
||||
linkedToID = new List<ushort>(),
|
||||
};
|
||||
|
||||
@@ -759,7 +759,7 @@ namespace Barotrauma
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
msg.WriteRangedSingle(BackgroundSections[i].ColorStrength, 0.0f, 1.0f, 8);
|
||||
msg.Write(BackgroundSections[i].Color.PackedValue);
|
||||
msg.WriteUInt32(BackgroundSections[i].Color.PackedValue);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -994,7 +994,11 @@ namespace Barotrauma
|
||||
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
|
||||
{
|
||||
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
|
||||
item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime);
|
||||
Vector2 force = (gap.LerpedFlowForce / distance) * deltaTime;
|
||||
if (force.LengthSquared() > 0.01f)
|
||||
{
|
||||
item.body.ApplyForce(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1545,7 +1549,7 @@ namespace Barotrauma
|
||||
|
||||
var hull = new Hull(rect, submarine, idRemap.GetOffsetId(element))
|
||||
{
|
||||
WaterVolume = element.GetAttributeFloat("pressure", 0.0f)
|
||||
WaterVolume = element.GetAttributeFloat("water", 0.0f)
|
||||
};
|
||||
hull.linkedToID = new List<ushort>();
|
||||
|
||||
|
||||
@@ -1972,7 +1972,7 @@ namespace Barotrauma
|
||||
|
||||
List<Tunnel> caveBranches = new List<Tunnel>();
|
||||
|
||||
var tunnel = new Tunnel(TunnelType.Cave, SegmentsToNodes(caveSegments), 100, parentTunnel);
|
||||
var tunnel = new Tunnel(TunnelType.Cave, SegmentsToNodes(caveSegments), 150, parentTunnel);
|
||||
Tunnels.Add(tunnel);
|
||||
caveBranches.Add(tunnel);
|
||||
|
||||
@@ -1989,7 +1989,7 @@ namespace Barotrauma
|
||||
bounds: caveArea);
|
||||
if (!branchSegments.Any()) { continue; }
|
||||
|
||||
var branch = new Tunnel(TunnelType.Cave, SegmentsToNodes(branchSegments), 0, parentBranch);
|
||||
var branch = new Tunnel(TunnelType.Cave, SegmentsToNodes(branchSegments), 150, parentBranch);
|
||||
Tunnels.Add(branch);
|
||||
caveBranches.Add(branch);
|
||||
}
|
||||
@@ -2569,34 +2569,38 @@ namespace Barotrauma
|
||||
AbyssResources.Clear();
|
||||
|
||||
var abyssResourcePrefabs = levelResources.Where(r => r.commonnessInfo.AbyssCommonness > 0.0f);
|
||||
int abyssClusterCount = (int)MathHelper.Lerp(GenerationParams.AbyssResourceClustersMin, GenerationParams.AbyssResourceClustersMax, MathUtils.InverseLerp(LevelData.Biome.MinDifficulty, LevelData.Biome.AdjustedMaxDifficulty, Difficulty));
|
||||
for (int i = 0; i < abyssClusterCount; i++)
|
||||
if (abyssResourcePrefabs.Any())
|
||||
{
|
||||
var selectedPrefab = ToolBox.SelectWeightedRandom(
|
||||
abyssResourcePrefabs.Select(r => r.itemPrefab).ToList(),
|
||||
abyssResourcePrefabs.Select(r => r.commonnessInfo.AbyssCommonness).ToList(),
|
||||
Rand.RandSync.ServerAndClient);
|
||||
|
||||
var location = allValidLocations.GetRandom(l =>
|
||||
int abyssClusterCount = (int)MathHelper.Lerp(GenerationParams.AbyssResourceClustersMin, GenerationParams.AbyssResourceClustersMax, MathUtils.InverseLerp(LevelData.Biome.MinDifficulty, LevelData.Biome.AdjustedMaxDifficulty, Difficulty));
|
||||
for (int i = 0; i < abyssClusterCount; i++)
|
||||
{
|
||||
if (l.Cell == null || l.Edge == null) { return false; }
|
||||
if (l.EdgeCenter.Y > AbyssArea.Bottom) { return false; }
|
||||
l.InitializeResources();
|
||||
return l.Resources.Count <= GetMaxResourcesOnEdge(selectedPrefab, l, out _);
|
||||
}, randSync: Rand.RandSync.ServerAndClient);
|
||||
var selectedPrefab = ToolBox.SelectWeightedRandom(
|
||||
abyssResourcePrefabs.Select(r => r.itemPrefab).ToList(),
|
||||
abyssResourcePrefabs.Select(r => r.commonnessInfo.AbyssCommonness).ToList(),
|
||||
Rand.RandSync.ServerAndClient);
|
||||
|
||||
if (location.Cell == null || location.Edge == null) { break; }
|
||||
var location = allValidLocations.GetRandom(l =>
|
||||
{
|
||||
if (l.Cell == null || l.Edge == null) { return false; }
|
||||
if (l.EdgeCenter.Y > AbyssArea.Bottom) { return false; }
|
||||
l.InitializeResources();
|
||||
return l.Resources.Count <= GetMaxResourcesOnEdge(selectedPrefab, l, out _);
|
||||
}, randSync: Rand.RandSync.ServerAndClient);
|
||||
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
|
||||
PlaceResources(selectedPrefab, clusterSize, location, out var placedResources, maxResourceOverlap: 0);
|
||||
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
|
||||
abyssClusterLocation.Resources.AddRange(placedResources);
|
||||
AbyssResources.Add(abyssClusterLocation);
|
||||
if (location.Cell == null || location.Edge == null) { break; }
|
||||
|
||||
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
|
||||
allValidLocations.RemoveAt(locationIndex);
|
||||
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.ServerAndClient);
|
||||
PlaceResources(selectedPrefab, clusterSize, location, out var placedResources, maxResourceOverlap: 0);
|
||||
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
|
||||
abyssClusterLocation.Resources.AddRange(placedResources);
|
||||
AbyssResources.Add(abyssClusterLocation);
|
||||
|
||||
var locationIndex = allValidLocations.FindIndex(l => l.Equals(location));
|
||||
allValidLocations.RemoveAt(locationIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PathPoints.Clear();
|
||||
nextPathPointId = 0;
|
||||
|
||||
@@ -2928,50 +2932,74 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="rotation">Used by clients to set the rotation for the resources</param>
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, out float rotation)
|
||||
public List<Item> GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, out float rotation)
|
||||
{
|
||||
var allValidLocations = GetAllValidClusterLocations();
|
||||
var placedResources = new List<Item>();
|
||||
rotation = 0.0f;
|
||||
|
||||
if (allValidLocations.None()) { return placedResources; } // TODO: WHAT?!
|
||||
|
||||
// Make sure not to pick a spot that already has other level resources
|
||||
for (int i = allValidLocations.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var location = allValidLocations[i];
|
||||
var locationHasResources = PathPoints.Any(p =>
|
||||
p.ClusterLocations.Any(c =>
|
||||
c.Equals(location) &&
|
||||
c.Resources.Any(r => r != null && !r.Removed &&
|
||||
(!(r.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)))));
|
||||
if (locationHasResources)
|
||||
if (HasResources(allValidLocations[i]))
|
||||
{
|
||||
allValidLocations.RemoveAt(i);
|
||||
}
|
||||
|
||||
bool HasResources(ClusterLocation clusterLocation)
|
||||
{
|
||||
foreach (var p in PathPoints)
|
||||
{
|
||||
foreach (var c in p.ClusterLocations)
|
||||
{
|
||||
if (!c.Equals(clusterLocation)) { continue; }
|
||||
foreach (var r in c.Resources)
|
||||
{
|
||||
if (r == null) { continue; }
|
||||
if (r.Removed) { continue; }
|
||||
if (!(r.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)) { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var positionType = PositionType.MainPath;
|
||||
if (PositionsOfInterest.Any(p => p.PositionType == PositionType.Cave))
|
||||
if (PositionsOfInterest.None(p => p.PositionType == positionType))
|
||||
{
|
||||
positionType = PositionType.Cave;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToCave))
|
||||
foreach (var validType in MineralMission.ValidPositionTypes)
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToCave);
|
||||
if (validType != positionType && PositionsOfInterest.Any(p => p.PositionType == validType))
|
||||
{
|
||||
positionType = validType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PositionsOfInterest.Any(p => p.PositionType == PositionType.SidePath))
|
||||
|
||||
try
|
||||
{
|
||||
positionType = PositionType.SidePath;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToSidePath))
|
||||
RemoveInvalidLocations(positionType switch
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToSidePath);
|
||||
}
|
||||
PositionType.MainPath => IsOnMainPath,
|
||||
PositionType.SidePath => IsOnSidePath,
|
||||
PositionType.Cave => IsInCave,
|
||||
PositionType.AbyssCave => IsInAbyssCave,
|
||||
_ => throw new NotImplementedException(),
|
||||
});
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
DebugConsole.ThrowError($"Unexpected PositionType (\"{positionType}\") for mineral mission resources: mineral spawning might not work as expected.");
|
||||
}
|
||||
|
||||
var poi = PositionsOfInterest.GetRandom(p => p.PositionType == positionType, randSync: Rand.RandSync.ServerAndClient);
|
||||
var poiPos = poi.Position.ToVector2();
|
||||
Vector2 poiPos = poi.Position.ToVector2();
|
||||
allValidLocations.Sort((x, y) => Vector2.DistanceSquared(poiPos, x.EdgeCenter)
|
||||
.CompareTo(Vector2.DistanceSquared(poiPos, y.EdgeCenter)));
|
||||
var maxResourceOverlap = 0.4f;
|
||||
float maxResourceOverlap = 0.4f;
|
||||
var selectedLocation = allValidLocations.FirstOrDefault(l =>
|
||||
Vector2.Distance(l.Edge.Point1, l.Edge.Point2) is float edgeLength &&
|
||||
requiredAmount <= (int)Math.Floor(edgeLength / ((1.0f - maxResourceOverlap) * prefab.Size.X)));
|
||||
@@ -2993,9 +3021,18 @@ namespace Barotrauma
|
||||
throw new Exception("Failed to find a suitable level wall edge to place level resources on.");
|
||||
}
|
||||
PlaceResources(prefab, requiredAmount, selectedLocation, out placedResources);
|
||||
var edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
|
||||
Vector2 edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
|
||||
rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
|
||||
return placedResources;
|
||||
|
||||
static bool IsOnMainPath(ClusterLocation location) => location.Edge.NextToMainPath;
|
||||
static bool IsOnSidePath(ClusterLocation location) => location.Edge.NextToSidePath;
|
||||
static bool IsInCave(ClusterLocation location) => location.Edge.NextToCave;
|
||||
bool IsInAbyssCave(ClusterLocation location) => location.EdgeCenter.Y > AbyssArea.Bottom;
|
||||
void RemoveInvalidLocations(Predicate<ClusterLocation> match)
|
||||
{
|
||||
allValidLocations.RemoveAll(match);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ClusterLocation> GetAllValidClusterLocations()
|
||||
@@ -4015,17 +4052,21 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
|
||||
float outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset) > 5000.0f)
|
||||
float? outpostDockingPortOffset = null;
|
||||
if (outpostPort != null)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
|
||||
DebugConsole.NewMessage(warningMsg, Color.Orange);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
|
||||
//don't try to compensate if the port is very far from the outpost's center of mass
|
||||
if (Math.Abs(outpostDockingPortOffset.Value) > 5000.0f)
|
||||
{
|
||||
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset.Value, -5000.0f, 5000.0f);
|
||||
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Info.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
|
||||
DebugConsole.NewMessage(warningMsg, Color.Orange);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Info.Name, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset - outpostDockingPortOffset, verticalMoveDir: 1);
|
||||
Vector2 spawnPos = outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, outpostDockingPortOffset != null ? subDockingPortOffset - outpostDockingPortOffset.Value : 0.0f, verticalMoveDir: 1);
|
||||
if (Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
spawnPos.Y = Math.Min(Size.Y - outpost.Borders.Height * 0.6f, spawnPos.Y + outpost.Borders.Height / 2);
|
||||
|
||||
@@ -357,7 +357,9 @@ namespace Barotrauma
|
||||
float closestDistance = 0.0f;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
{
|
||||
if (port.Item.Submarine != sub || port.IsHorizontal != linkedPort.IsHorizontal) { continue; }
|
||||
if (port.Item.Submarine != sub) { continue; }
|
||||
if (port.IsHorizontal != linkedPort.IsHorizontal) { continue; }
|
||||
if (port.ForceDockingDirection != DockingPort.DirectionType.None && port.ForceDockingDirection == linkedPort.ForceDockingDirection) { continue; }
|
||||
float dist = Vector2.Distance(port.Item.WorldPosition, linkedPort.Item.WorldPosition);
|
||||
if (myPort == null || dist < closestDistance)
|
||||
{
|
||||
@@ -453,22 +455,22 @@ namespace Barotrauma
|
||||
|
||||
saveElement.SetAttributeValue("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition));
|
||||
|
||||
if (linkedTo.Any() || linkedToID.Any())
|
||||
{
|
||||
var linkedPort =
|
||||
linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent<DockingPort>() != null) ??
|
||||
FindEntityByID(linkedToID.First()) as MapEntity;
|
||||
if (linkedPort != null)
|
||||
{
|
||||
saveElement.SetAttributeValue("linkedto", linkedPort.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
saveElement = new XElement("LinkedSubmarine");
|
||||
sub.SaveToXElement(saveElement);
|
||||
}
|
||||
if (linkedTo.Any() || linkedToID.Any())
|
||||
{
|
||||
var linkedPort =
|
||||
linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent<DockingPort>() != null) ??
|
||||
FindEntityByID(linkedToID.First()) as MapEntity;
|
||||
if (linkedPort != null)
|
||||
{
|
||||
saveElement.SetAttributeValue("linkedto", linkedPort.ID);
|
||||
}
|
||||
}
|
||||
|
||||
saveElement.SetAttributeValue("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID);
|
||||
saveElement.SetAttributeValue("originalmyport", originalMyPortID);
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
|
||||
{
|
||||
if (this is Item item && !upgrade.Prefab.UpgradeCategories.Any(category => category.CanBeApplied(item, upgrade.Prefab)))
|
||||
if (!upgrade.Prefab.UpgradeCategories.Any(category => category.CanBeApplied(this, upgrade.Prefab)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -359,16 +359,6 @@ namespace Barotrauma
|
||||
Upgrades.Add(upgrade);
|
||||
}
|
||||
|
||||
// not used anymore
|
||||
#if SERVER
|
||||
// if (createNetworkEvent)
|
||||
// {
|
||||
// if (this is IServerSerializable serializable)
|
||||
// {
|
||||
// GameMain.Server.CreateEntityEvent(serializable, new object[] { NetEntityEvent.Type.Upgrade, upgrade });
|
||||
// }
|
||||
// }
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,17 @@ namespace Barotrauma
|
||||
|
||||
public ImmutableHashSet<Identifier> Tags => Prefab.Tags;
|
||||
|
||||
#if DEBUG
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes)]
|
||||
#else
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
#endif
|
||||
public string SpecialTag
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected Color spriteColor;
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes)]
|
||||
public Color SpriteColor
|
||||
@@ -574,6 +585,11 @@ namespace Barotrauma
|
||||
int xsections = 1, ysections = 1;
|
||||
int width = rect.Width, height = rect.Height;
|
||||
|
||||
WallSection[] prevSections = null;
|
||||
if (Sections != null)
|
||||
{
|
||||
prevSections = Sections.ToArray();
|
||||
}
|
||||
if (!HasBody)
|
||||
{
|
||||
if (FlippedX && IsHorizontal)
|
||||
@@ -657,6 +673,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSections != null && Sections.Length == prevSections.Length)
|
||||
{
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
Sections[i].damage = prevSections[i].damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle GenerateMergedRect(List<WallSection> mergedSections)
|
||||
@@ -829,27 +853,33 @@ namespace Barotrauma
|
||||
|
||||
public WallSection GetSection(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return null;
|
||||
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return null; }
|
||||
return Sections[sectionIndex];
|
||||
|
||||
}
|
||||
|
||||
public bool SectionBodyDisabled(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return false;
|
||||
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return false; }
|
||||
return (Sections[sectionIndex].damage >= MaxHealth);
|
||||
}
|
||||
|
||||
public bool AllSectionBodiesDisabled()
|
||||
{
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
if (Sections[i].damage < MaxHealth) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sections that are leaking have a gap placed on them
|
||||
/// </summary>
|
||||
public bool SectionIsLeaking(int sectionIndex)
|
||||
{
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return false;
|
||||
|
||||
return (Sections[sectionIndex].damage >= MaxHealth * LeakThreshold);
|
||||
if (sectionIndex < 0 || sectionIndex >= Sections.Length) { return false; }
|
||||
return Sections[sectionIndex].damage >= MaxHealth * LeakThreshold;
|
||||
}
|
||||
|
||||
public int SectionLength(int sectionIndex)
|
||||
@@ -1139,21 +1169,22 @@ namespace Barotrauma
|
||||
gapRect.Height += 20;
|
||||
|
||||
bool horizontalGap = !IsHorizontal;
|
||||
bool diagonalGap = false;
|
||||
if (Prefab.BodyRotation != 0.0f)
|
||||
{
|
||||
//rotation within a 90 deg sector (e.g. 100 -> 10, 190 -> 10, -10 -> 80)
|
||||
float sectorizedRotation = MathUtils.WrapAngleTwoPi(BodyRotation) % MathHelper.PiOver2;
|
||||
//diagonal if 30 < angle < 60
|
||||
bool diagonal = sectorizedRotation > MathHelper.Pi / 6 && sectorizedRotation < MathHelper.Pi / 3;
|
||||
diagonalGap = sectorizedRotation > MathHelper.Pi / 6 && sectorizedRotation < MathHelper.Pi / 3;
|
||||
//gaps on the lower half of a diagonal wall are horizontal, ones on the upper half are vertical
|
||||
if (diagonal)
|
||||
if (diagonalGap)
|
||||
{
|
||||
horizontalGap = gapRect.Y - gapRect.Height / 2 < Position.Y;
|
||||
if (FlippedY) { horizontalGap = !horizontalGap; }
|
||||
}
|
||||
}
|
||||
|
||||
Sections[sectionIndex].gap = new Gap(gapRect, horizontalGap, Submarine);
|
||||
Sections[sectionIndex].gap = new Gap(gapRect, horizontalGap, Submarine, isDiagonal: diagonalGap);
|
||||
|
||||
//free the ID, because if we give gaps IDs we have to make sure they always match between the clients and the server and
|
||||
//that clients create them in the correct order along with every other entity created/removed during the round
|
||||
|
||||
@@ -271,20 +271,17 @@ namespace Barotrauma
|
||||
tags.Add("wall".ToIdentifier());
|
||||
}
|
||||
|
||||
if (Description.IsNullOrEmpty())
|
||||
if (!descriptionIdentifier.IsEmpty)
|
||||
{
|
||||
if (!descriptionIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}");
|
||||
}
|
||||
else if (nameIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}");
|
||||
}
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}").Fallback(Description);
|
||||
}
|
||||
else if (nameIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}").Fallback(Description);
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}").Fallback(Description);
|
||||
}
|
||||
|
||||
//backwards compatibility
|
||||
|
||||
@@ -187,7 +187,6 @@ namespace Barotrauma
|
||||
if (structure.Submarine != this || !structure.HasBody || structure.Indestructible) { continue; }
|
||||
realWorldCrushDepth = Math.Min(structure.CrushDepth, realWorldCrushDepth.Value);
|
||||
}
|
||||
realWorldCrushDepth *= Info.GetRealWorldCrushDepthMultiplier();
|
||||
}
|
||||
return realWorldCrushDepth.Value;
|
||||
}
|
||||
@@ -452,10 +451,27 @@ namespace Barotrauma
|
||||
verticalMoveDir = Math.Sign(verticalMoveDir);
|
||||
//do a raycast towards the top/bottom of the level depending on direction
|
||||
Vector2 potentialPos = new Vector2(spawnPos.X, verticalMoveDir > 0 ? Level.Loaded.Size.Y : 0);
|
||||
if (PickBody(ConvertUnits.ToSimUnits(spawnPos), ConvertUnits.ToSimUnits(potentialPos), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
|
||||
//3 raycasts (left, middle and right side of the sub, so we don't accidentally raycast up a passage too narrow for the sub)
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
//if the raycast hit a wall, attempt to place the spawnpos there
|
||||
potentialPos.Y = ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) - 10;
|
||||
Vector2 xOffset = Vector2.UnitX * minWidth / 2 * x;
|
||||
if (PickBody(
|
||||
ConvertUnits.ToSimUnits(spawnPos + xOffset),
|
||||
ConvertUnits.ToSimUnits(potentialPos + xOffset),
|
||||
collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
{
|
||||
int offsetFromWall = 10 * -verticalMoveDir;
|
||||
//if the raycast hit a wall, attempt to place the spawnpos there
|
||||
if (verticalMoveDir > 0)
|
||||
{
|
||||
potentialPos.Y = Math.Min(potentialPos.Y, ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) + offsetFromWall);
|
||||
}
|
||||
else
|
||||
{
|
||||
potentialPos.Y = Math.Max(potentialPos.Y, ConvertUnits.ToDisplayUnits(LastPickedPosition.Y) + offsetFromWall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//step away from the top/bottom of the level, or from whatever wall the raycast hit,
|
||||
|
||||
@@ -505,7 +505,6 @@ namespace Barotrauma
|
||||
if (wall.Submarine != submarine) { continue; }
|
||||
|
||||
float wallCrushDepth = wall.CrushDepth;
|
||||
if (submarine.Info.SubmarineClass == SubmarineClass.DeepDiver) { wallCrushDepth *= 1.2f; }
|
||||
float pastCrushDepth = submarine.RealWorldDepth - wallCrushDepth;
|
||||
if (pastCrushDepth > 0)
|
||||
{
|
||||
@@ -587,9 +586,13 @@ namespace Barotrauma
|
||||
newHull = Hull.FindHull(targetPos, null);
|
||||
}
|
||||
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, ConvertUnits.ToDisplayUnits(points[0]), 200.0f);
|
||||
if (adjacentGap == null) { return true; }
|
||||
//if all the bodies of a wall have been disabled, we don't need to care about gaps (can always pass through)
|
||||
if (!(contact.FixtureA.UserData is Structure wall) || !wall.AllSectionBodiesDisabled())
|
||||
{
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, ConvertUnits.ToDisplayUnits(points[0]), 200.0f);
|
||||
if (adjacentGap == null) { return true; }
|
||||
}
|
||||
|
||||
if (newHull != null)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public enum SubmarineType { Player, Outpost, OutpostModule, Wreck, BeaconStation, EnemySubmarine, Ruin }
|
||||
public enum SubmarineClass { Undefined, Scout, Attack, Transport, DeepDiver }
|
||||
public enum SubmarineClass { Undefined, Scout, Attack, Transport }
|
||||
|
||||
partial class SubmarineInfo : IDisposable
|
||||
{
|
||||
@@ -49,6 +49,12 @@ namespace Barotrauma
|
||||
}
|
||||
public CrewExperienceLevel RecommendedCrewExperience;
|
||||
|
||||
public int Tier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A random int that gets assigned when saving the sub. Used in mp campaign to verify that sub files match
|
||||
/// </summary>
|
||||
@@ -305,6 +311,7 @@ namespace Barotrauma
|
||||
RecommendedCrewExperience = original.RecommendedCrewExperience;
|
||||
RecommendedCrewSizeMin = original.RecommendedCrewSizeMin;
|
||||
RecommendedCrewSizeMax = original.RecommendedCrewSizeMax;
|
||||
Tier = original.Tier;
|
||||
IsManuallyOutfitted = original.IsManuallyOutfitted;
|
||||
Tags = original.Tags;
|
||||
if (original.OutpostModuleInfo != null)
|
||||
@@ -386,6 +393,7 @@ namespace Barotrauma
|
||||
{
|
||||
Enum.TryParse(recommendedCrewExperience.Value, ignoreCase: true, out RecommendedCrewExperience);
|
||||
}
|
||||
Tier = SubmarineElement.GetAttributeInt("tier", GetDefaultTier(Price));
|
||||
|
||||
if (SubmarineElement?.Attribute("type") != null)
|
||||
{
|
||||
@@ -407,7 +415,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (SubmarineElement?.Attribute("class") != null)
|
||||
{
|
||||
if (Enum.TryParse(SubmarineElement.GetAttributeString("class", "Undefined"), out SubmarineClass submarineClass))
|
||||
string classStr = SubmarineElement.GetAttributeString("class", "Undefined");
|
||||
if (classStr == "DeepDiver")
|
||||
{
|
||||
//backwards compatibility
|
||||
SubmarineClass = SubmarineClass.Scout;
|
||||
}
|
||||
else if (Enum.TryParse(classStr, out SubmarineClass submarineClass))
|
||||
{
|
||||
SubmarineClass = submarineClass;
|
||||
}
|
||||
@@ -538,25 +552,9 @@ namespace Barotrauma
|
||||
{
|
||||
realWorldCrushDepth = Level.DefaultRealWorldCrushDepth;
|
||||
}
|
||||
realWorldCrushDepth *= GetRealWorldCrushDepthMultiplier();
|
||||
return realWorldCrushDepth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on <see cref="SubmarineClass"/>
|
||||
/// </summary>
|
||||
public float GetRealWorldCrushDepthMultiplier()
|
||||
{
|
||||
if (SubmarineClass == SubmarineClass.DeepDiver)
|
||||
{
|
||||
return 1.2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//saving/loading ----------------------------------------------------
|
||||
public void SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
|
||||
{
|
||||
@@ -691,7 +689,7 @@ namespace Barotrauma
|
||||
System.IO.Stream stream;
|
||||
try
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(file);
|
||||
stream = SaveUtil.DecompressFileToStream(file);
|
||||
}
|
||||
catch (System.IO.FileNotFoundException e)
|
||||
{
|
||||
@@ -748,5 +746,7 @@ namespace Barotrauma
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static int GetDefaultTier(int price) => price > 20000 ? 3 : price > 10000 ? 2 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user