Unstable 0.1400.0.0
This commit is contained in:
@@ -189,6 +189,10 @@ namespace Barotrauma
|
||||
if (roomName == value) { return; }
|
||||
roomName = value;
|
||||
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
|
||||
if (!IsWetRoom && ForceAsWetRoom)
|
||||
{
|
||||
IsWetRoom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +333,42 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool ForceAsWetRoom =>
|
||||
roomName != null && (
|
||||
roomName.Contains("ballast", StringComparison.OrdinalIgnoreCase) ||
|
||||
roomName.Contains("bilge", StringComparison.OrdinalIgnoreCase) ||
|
||||
roomName.Contains("airlock", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private bool isWetRoom;
|
||||
[Editable, Serialize(false, true, description: "It's normal for this hull to be filled with water. If the room name contains 'ballast', 'bilge', or 'airlock', you can't disable this setting.")]
|
||||
public bool IsWetRoom
|
||||
{
|
||||
get { return isWetRoom; }
|
||||
set
|
||||
{
|
||||
isWetRoom = value;
|
||||
if (ForceAsWetRoom)
|
||||
{
|
||||
isWetRoom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool avoidStaying;
|
||||
[Editable, Serialize(false, true, description: "Bots avoid staying here, but they are still allowed to access the room when needed and go through it. Forced true for wet rooms.")]
|
||||
public bool AvoidStaying
|
||||
{
|
||||
get { return avoidStaying || IsWetRoom; }
|
||||
set
|
||||
{
|
||||
avoidStaying = value;
|
||||
if (IsWetRoom)
|
||||
{
|
||||
avoidStaying = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float WaterPercentage => MathUtils.Percentage(WaterVolume, Volume);
|
||||
|
||||
public float OxygenPercentage
|
||||
|
||||
@@ -325,9 +325,11 @@ namespace Barotrauma
|
||||
get { return LevelData.Seed; }
|
||||
}
|
||||
|
||||
|
||||
public static float? ForcedDifficulty;
|
||||
public float Difficulty
|
||||
{
|
||||
get { return LevelData.Difficulty; }
|
||||
get { return ForcedDifficulty ?? LevelData.Difficulty; }
|
||||
}
|
||||
|
||||
public LevelData.LevelType Type
|
||||
@@ -2710,14 +2712,21 @@ namespace Barotrauma
|
||||
return position;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
|
||||
public bool TryGetInterestingPositionAwayFromPoint(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Vector2 awayPoint, float minDistFromPoint, Func<InterestingPosition, bool> filter = null)
|
||||
{
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, filter);
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, awayPoint, minDistFromPoint, filter);
|
||||
position = pos.ToVector2();
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Func<InterestingPosition, bool> filter = null)
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
|
||||
{
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, Vector2.Zero, minDistFromPoint: 0, filter);
|
||||
position = pos.ToVector2();
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Vector2 awayPoint, float minDistFromPoint = 0f, Func<InterestingPosition, bool> filter = null)
|
||||
{
|
||||
if (!PositionsOfInterest.Any())
|
||||
{
|
||||
@@ -2755,6 +2764,11 @@ namespace Barotrauma
|
||||
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), sub.WorldPosition) < minDistFromSubs * minDistFromSubs);
|
||||
}
|
||||
}
|
||||
if (minDistFromPoint > 0.0f)
|
||||
{
|
||||
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), awayPoint) < minDistFromPoint * minDistFromPoint);
|
||||
}
|
||||
|
||||
if (!farEnoughPositions.Any())
|
||||
{
|
||||
string errorMsg = "Could not find a position of interest far enough from the submarines. (PositionType: " + positionType + ", minDistFromSubs: " + minDistFromSubs + ")\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
@@ -2826,7 +2840,7 @@ namespace Barotrauma
|
||||
if (index < 0 || index >= bottomPositions.Count - 1) { return new Vector2(xPosition, BottomPos); }
|
||||
|
||||
float t = (xPosition - bottomPositions[index].X) / (bottomPositions[index + 1].X - bottomPositions[index].X);
|
||||
Debug.Assert(t < 1.0f);
|
||||
Debug.Assert(t <= 1.0f);
|
||||
t = MathHelper.Clamp(t, 0.0f, 1.0f);
|
||||
|
||||
float yPos = MathHelper.Lerp(bottomPositions[index].Y, bottomPositions[index + 1].Y, t);
|
||||
@@ -3094,12 +3108,11 @@ namespace Barotrauma
|
||||
sub.SetPosition(spawnPoint);
|
||||
wreckPositions.Add(sub, positions);
|
||||
blockedRects.Add(sub, rects);
|
||||
|
||||
return sub;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds.ToString()} (ms).", Color.Red);
|
||||
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds} (ms).", Color.Red);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3306,18 +3319,19 @@ namespace Barotrauma
|
||||
}
|
||||
wreckFiles.Shuffle(Rand.RandSync.Server);
|
||||
|
||||
int wreckCount = Math.Min(Loaded.GenerationParams.WreckCount, wreckFiles.Count);
|
||||
int minWreckCount = Math.Min(Loaded.GenerationParams.MinWreckCount, wreckFiles.Count);
|
||||
int maxWreckCount = Math.Min(Loaded.GenerationParams.MaxWreckCount, wreckFiles.Count);
|
||||
int wreckCount = Rand.Range(minWreckCount, maxWreckCount, Rand.RandSync.Server);
|
||||
Wrecks = new List<Submarine>(wreckCount);
|
||||
for (int i = 0; i < wreckCount; i++)
|
||||
{
|
||||
ContentFile contentFile = wreckFiles[i];
|
||||
if (contentFile == null) { continue; }
|
||||
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
|
||||
// For storing the translations. Used only for debugging.
|
||||
SpawnSubOnPath(wreckName, contentFile, SubmarineType.Wreck);
|
||||
}
|
||||
totalSW.Stop();
|
||||
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds.ToString()} (ms)");
|
||||
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds} (ms)");
|
||||
}
|
||||
|
||||
private bool HasStartOutpost()
|
||||
@@ -3365,11 +3379,8 @@ namespace Barotrauma
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (Submarine.MainSubs.Length > 1 && Submarine.MainSubs[0] != null && Submarine.MainSubs[1] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GameMain.GameSession.GameMode is PvPMode) { continue; }
|
||||
|
||||
bool isStart = (i == 0) == !Mirrored;
|
||||
if (isStart)
|
||||
{
|
||||
|
||||
@@ -496,8 +496,11 @@ namespace Barotrauma
|
||||
[Serialize(1, true, description: "The number of alien ruins in the level."), Editable(MinValueInt = 0, MaxValueInt = 10)]
|
||||
public int RuinCount { get; set; }
|
||||
|
||||
[Serialize(1, true, description: "The minimum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
|
||||
public int MinWreckCount { get; set; }
|
||||
|
||||
[Serialize(1, true, description: "The maximum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
|
||||
public int WreckCount { get; set; }
|
||||
public int MaxWreckCount { get; set; }
|
||||
|
||||
// TODO: Move the wreck parameters under a separate class?
|
||||
#region Wreck parameters
|
||||
|
||||
+27
-1
@@ -21,6 +21,12 @@ namespace Barotrauma
|
||||
private List<LevelObject> updateableObjects;
|
||||
private List<LevelObject>[,] objectGrid;
|
||||
|
||||
public float GlobalForceDecreaseTimer
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public LevelObjectManager() : base(null, Entity.NullEntityID)
|
||||
{
|
||||
}
|
||||
@@ -133,10 +139,24 @@ namespace Barotrauma
|
||||
suitableSpawnPositions.Add(prefab,
|
||||
availableSpawnPositions.Where(sp =>
|
||||
sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
|
||||
sp.Length >= prefab.MinSurfaceWidth &&
|
||||
sp.Length >= prefab.MinSurfaceWidth &&
|
||||
(prefab.AllowAtStart || !closeToStart(sp.GraphEdge.Center)) &&
|
||||
(prefab.AllowAtEnd || !closeToEnd(sp.GraphEdge.Center)) &&
|
||||
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
|
||||
|
||||
spawnPositionWeights.Add(prefab,
|
||||
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
|
||||
|
||||
bool closeToStart(Vector2 position)
|
||||
{
|
||||
float minDist = level.Size.X * 0.2f;
|
||||
return MathUtils.LineSegmentToPointDistanceSquared(level.StartPosition.ToPoint(), level.StartExitPosition.ToPoint(), position.ToPoint()) < minDist * minDist;
|
||||
}
|
||||
bool closeToEnd(Vector2 position)
|
||||
{
|
||||
float minDist = level.Size.X * 0.2f;
|
||||
return MathUtils.LineSegmentToPointDistanceSquared(level.EndPosition.ToPoint(), level.EndExitPosition.ToPoint(), position.ToPoint()) < minDist * minDist;
|
||||
}
|
||||
}
|
||||
|
||||
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
|
||||
@@ -484,6 +504,12 @@ namespace Barotrauma
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
GlobalForceDecreaseTimer += deltaTime;
|
||||
if (GlobalForceDecreaseTimer > 1000000.0f)
|
||||
{
|
||||
GlobalForceDecreaseTimer = 0.0f;
|
||||
}
|
||||
|
||||
foreach (LevelObject obj in updateableObjects)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
|
||||
@@ -176,6 +176,20 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the object be placed near the start of the level.")]
|
||||
public bool AllowAtStart
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the object be placed near the end of the level.")]
|
||||
public bool AllowAtEnd
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Minimum length of a graph edge the object can spawn on."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
/// <summary>
|
||||
/// Minimum length of a graph edge the object can spawn on.
|
||||
|
||||
@@ -140,6 +140,11 @@ namespace Barotrauma
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public float GlobalForceDecreaseInterval
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private readonly TriggerForceMode forceMode;
|
||||
public TriggerForceMode ForceMode
|
||||
@@ -234,6 +239,7 @@ namespace Barotrauma
|
||||
ForceFluctuationInterval = element.GetAttributeFloat("forcefluctuationinterval", 0.01f);
|
||||
ForceFluctuationStrength = Math.Max(element.GetAttributeFloat("forcefluctuationstrength", 0.0f), 0.0f);
|
||||
ForceFalloff = element.GetAttributeBool("forcefalloff", true);
|
||||
GlobalForceDecreaseInterval = element.GetAttributeFloat("globalforcedecreaseinterval", 0.0f);
|
||||
|
||||
ForceVelocityLimit = ConvertUnits.ToSimUnits(element.GetAttributeFloat("forcevelocitylimit", float.MaxValue));
|
||||
string forceModeStr = element.GetAttributeString("forcemode", "Force");
|
||||
@@ -434,6 +440,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
|
||||
@@ -457,7 +465,13 @@ namespace Barotrauma
|
||||
|
||||
if (!UseNetworkSyncing || isNotClient)
|
||||
{
|
||||
if (ForceFluctuationStrength > 0.0f)
|
||||
if (GlobalForceDecreaseInterval > 0.0f && Level.Loaded?.LevelObjectManager != null &&
|
||||
Level.Loaded.LevelObjectManager.GlobalForceDecreaseTimer % (GlobalForceDecreaseInterval * 2) < GlobalForceDecreaseInterval)
|
||||
{
|
||||
NeedsNetworkSyncing |= currentForceFluctuation > 0.0f;
|
||||
currentForceFluctuation = 0.0f;
|
||||
}
|
||||
else if (ForceFluctuationStrength > 0.0f)
|
||||
{
|
||||
//no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers
|
||||
if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any())
|
||||
@@ -533,8 +547,8 @@ namespace Barotrauma
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
var targets = new List<ISerializableEntity>();
|
||||
effect.GetNearbyTargets(worldPosition, targets);
|
||||
targets.Clear();
|
||||
targets.AddRange(effect.GetNearbyTargets(worldPosition, targets));
|
||||
effect.Apply(effect.type, deltaTime, triggerer, targets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,8 +533,24 @@ namespace Barotrauma
|
||||
//prefer connections that haven't been passed through, and connections with fewer available missions
|
||||
connection = ToolBox.SelectWeightedRandom(
|
||||
suitableConnections.ToList(),
|
||||
suitableConnections.Select(c => (c.Passed ? 1.0f : 5.0f) / Math.Max(availableMissions.Count(m => m.Locations.Contains(c.OtherLocation(this))), 1.0f)).ToList(),
|
||||
Rand.RandSync.Unsynced);
|
||||
suitableConnections.Select(c => GetConnectionWeight(this, c)).ToList(),
|
||||
Rand.RandSync.Unsynced);
|
||||
|
||||
static float GetConnectionWeight(Location location, LocationConnection c)
|
||||
{
|
||||
float weight = c.Passed ? 1.0f : 5.0f;
|
||||
Location destination = c.OtherLocation(location);
|
||||
if (destination != null)
|
||||
{
|
||||
if (destination.MapPosition.X > location.MapPosition.X) { weight *= 2.0f; }
|
||||
int missionCount = location.availableMissions.Count(m => m.Locations.Contains(destination));
|
||||
if (missionCount > 0)
|
||||
{
|
||||
weight /= missionCount * 2;
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
return InstantiateMission(prefab, connection);
|
||||
}
|
||||
@@ -542,14 +558,14 @@ namespace Barotrauma
|
||||
private Mission InstantiateMission(MissionPrefab prefab, LocationConnection connection)
|
||||
{
|
||||
Location destination = connection.OtherLocation(this);
|
||||
var mission = prefab.Instantiate(new Location[] { this, destination });
|
||||
var mission = prefab.Instantiate(new Location[] { this, destination }, Submarine.MainSub);
|
||||
mission.AdjustLevelData(connection.LevelData);
|
||||
return mission;
|
||||
}
|
||||
|
||||
private Mission InstantiateMission(MissionPrefab prefab)
|
||||
{
|
||||
var mission = prefab.Instantiate(new Location[] { this, this });
|
||||
var mission = prefab.Instantiate(new Location[] { this, this }, Submarine.MainSub);
|
||||
mission.AdjustLevelData(LevelData);
|
||||
return mission;
|
||||
}
|
||||
@@ -570,7 +586,7 @@ namespace Barotrauma
|
||||
{
|
||||
destination = Connections.First().OtherLocation(this);
|
||||
}
|
||||
var mission = loadedMission.MissionPrefab.Instantiate(new Location[] { this, destination });
|
||||
var mission = loadedMission.MissionPrefab.Instantiate(new Location[] { this, destination }, Submarine.MainSub);
|
||||
availableMissions.Add(mission);
|
||||
if (loadedMission.SelectedMission) { SelectedMission = mission; }
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ namespace Barotrauma
|
||||
|
||||
if (location.Type.HasOutpost && !wasCritical && location.IsCriticallyRadiated())
|
||||
{
|
||||
location.ClearMissions();
|
||||
amountOfOutposts--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace Barotrauma
|
||||
public void ResolveLinks(IdRemap childRemap)
|
||||
{
|
||||
if (unresolvedLinkedToID == null) { return; }
|
||||
for (int i=0;i<unresolvedLinkedToID.Count;i++)
|
||||
for (int i = 0; i < unresolvedLinkedToID.Count; i++)
|
||||
{
|
||||
int srcId = unresolvedLinkedToID[i];
|
||||
int targetId = childRemap.GetOffsetId(srcId);
|
||||
@@ -649,7 +649,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
object newEntity = loadMethod.Invoke(t, new object[] { element, submarine, idRemap });
|
||||
if (newEntity != null) entities.Add((MapEntity)newEntity);
|
||||
if (newEntity != null)
|
||||
{
|
||||
entities.Add((MapEntity)newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
|
||||
@@ -1238,6 +1238,26 @@ namespace Barotrauma
|
||||
return list.FindAll(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
|
||||
}
|
||||
|
||||
public List<(ItemContainer container, int freeSlots)> GetCargoContainers()
|
||||
{
|
||||
List<(ItemContainer container, int freeSlots)> containers = new List<(ItemContainer container, int freeSlots)>();
|
||||
var connectedSubs = GetConnectedSubs();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
if (!item.HasTag("cargocontainer")) { continue; }
|
||||
var itemContainer = item.GetComponent<ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
int emptySlots = 0;
|
||||
for (int i = 0; i < itemContainer.Inventory.Capacity; i++)
|
||||
{
|
||||
if (itemContainer.Inventory.GetItemAt(i) == null) { emptySlots++; }
|
||||
}
|
||||
containers.Add((itemContainer, emptySlots));
|
||||
}
|
||||
return containers;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetEntities<T>(bool includingConnectedSubs, IEnumerable<T> list) where T : MapEntity
|
||||
{
|
||||
return list.Where(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
|
||||
@@ -1527,6 +1547,8 @@ namespace Barotrauma
|
||||
|
||||
Rectangle dimensions = CalculateDimensions();
|
||||
element.Add(new XAttribute("dimensions", XMLExtensions.Vector2ToString(dimensions.Size.ToVector2())));
|
||||
var cargoContainers = GetCargoContainers();
|
||||
element.Add(new XAttribute("cargocapacity", cargoContainers.Sum(c => c.freeSlots)));
|
||||
element.Add(new XAttribute("recommendedcrewsizemin", Info.RecommendedCrewSizeMin));
|
||||
element.Add(new XAttribute("recommendedcrewsizemax", Info.RecommendedCrewSizeMax));
|
||||
element.Add(new XAttribute("recommendedcrewexperience", Info.RecommendedCrewExperience ?? ""));
|
||||
@@ -1537,6 +1559,40 @@ namespace Barotrauma
|
||||
Info.OutpostModuleInfo?.Save(element);
|
||||
}
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.PendingItemSwap?.SwappableItem?.ConnectedItemsToSwap == null) { continue; }
|
||||
foreach (var (requiredTag, swapTo) in item.PendingItemSwap.SwappableItem.ConnectedItemsToSwap)
|
||||
{
|
||||
List<Item> itemsToSwap = new List<Item>();
|
||||
itemsToSwap.AddRange(item.linkedTo.Where(lt => (lt as Item)?.HasTag(requiredTag) ?? false).Cast<Item>());
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null)
|
||||
{
|
||||
foreach (Connection c in connectionPanel.Connections)
|
||||
{
|
||||
foreach (var connectedComponent in item.GetConnectedComponentsRecursive<ItemComponent>(c))
|
||||
{
|
||||
if (!itemsToSwap.Contains(connectedComponent.Item) && connectedComponent.Item.HasTag(requiredTag))
|
||||
{
|
||||
itemsToSwap.Add(connectedComponent.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemPrefab itemPrefab = ItemPrefab.Find("", swapTo);
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to swap an item connected to \"{item.Name}\" into \"{swapTo}\".");
|
||||
continue;
|
||||
}
|
||||
foreach (Item itemToSwap in itemsToSwap)
|
||||
{
|
||||
if (itemPrefab != itemToSwap.Prefab) { itemToSwap.PendingItemSwap = itemPrefab; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MapEntity e in MapEntity.mapEntityList.OrderBy(e => e.ID))
|
||||
{
|
||||
if (!e.ShouldBeSaved) { continue; }
|
||||
|
||||
@@ -174,6 +174,11 @@ namespace Barotrauma
|
||||
float simWidth = ConvertUnits.ToSimUnits(width);
|
||||
float simHeight = ConvertUnits.ToSimUnits(height);
|
||||
|
||||
if (sub.FlippedX)
|
||||
{
|
||||
simPos.X = -simPos.X;
|
||||
}
|
||||
|
||||
if (width > 0.0f && height > 0.0f)
|
||||
{
|
||||
item.StaticFixtures.Add(farseerBody.CreateRectangle(simWidth, simHeight, 5.0f, simPos));
|
||||
@@ -513,7 +518,7 @@ namespace Barotrauma
|
||||
{
|
||||
return CheckCharacterCollision(contact, character);
|
||||
}
|
||||
else if (f2.UserData is Items.Components.DockingPort)
|
||||
else if (f1.UserData is Items.Components.DockingPort || f2.UserData is Items.Components.DockingPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,12 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public int CargoCapacity
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string FilePath
|
||||
{
|
||||
get;
|
||||
@@ -261,6 +267,7 @@ namespace Barotrauma
|
||||
SubmarineClass = original.SubmarineClass;
|
||||
hash = !string.IsNullOrEmpty(original.FilePath) ? original.MD5Hash : null;
|
||||
Dimensions = original.Dimensions;
|
||||
CargoCapacity = original.CargoCapacity;
|
||||
FilePath = original.FilePath;
|
||||
RequiredContentPackages = new HashSet<string>(original.RequiredContentPackages);
|
||||
IsFileCorrupted = original.IsFileCorrupted;
|
||||
@@ -323,6 +330,7 @@ namespace Barotrauma
|
||||
Tags = tags;
|
||||
}
|
||||
Dimensions = SubmarineElement.GetAttributeVector2("dimensions", Vector2.Zero);
|
||||
CargoCapacity = SubmarineElement.GetAttributeInt("cargocapacity", -1);
|
||||
RecommendedCrewSizeMin = SubmarineElement.GetAttributeInt("recommendedcrewsizemin", 0);
|
||||
RecommendedCrewSizeMax = SubmarineElement.GetAttributeInt("recommendedcrewsizemax", 0);
|
||||
RecommendedCrewExperience = SubmarineElement.GetAttributeString("recommendedcrewexperience", "Unknown");
|
||||
@@ -511,10 +519,14 @@ namespace Barotrauma
|
||||
//saving/loading ----------------------------------------------------
|
||||
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
|
||||
{
|
||||
var newElement = new XElement(SubmarineElement.Name,
|
||||
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
|
||||
var newElement = new XElement(
|
||||
SubmarineElement.Name,
|
||||
SubmarineElement.Attributes()
|
||||
.Where(a =>
|
||||
!string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
|
||||
SubmarineElement.Elements());
|
||||
|
||||
if (Type == SubmarineType.OutpostModule)
|
||||
{
|
||||
OutpostModuleInfo.Save(newElement);
|
||||
@@ -574,7 +586,7 @@ namespace Barotrauma
|
||||
var contentPackageSubs = ContentPackage.GetFilesOfType(
|
||||
GameMain.Config.AllEnabledPackages,
|
||||
ContentType.Submarine, ContentType.Outpost, ContentType.OutpostModule,
|
||||
ContentType.Wreck, ContentType.BeaconStation);
|
||||
ContentType.Wreck, ContentType.BeaconStation, ContentType.EnemySubmarine);
|
||||
|
||||
for (int i = savedSubmarines.Count - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -192,17 +192,37 @@ namespace Barotrauma
|
||||
float minDist = 100.0f;
|
||||
float heightFromFloor = 110.0f;
|
||||
float hullMinHeight = 100;
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
// Ignore hulls that a human couldn't fit in.
|
||||
// Doesn't take multi-hull rooms into account, but it's probably best to leave them to be setup manually.
|
||||
if (hull.Rect.Height < hullMinHeight) { continue; }
|
||||
// Don't create waypoints if there's no floor.
|
||||
Vector2 floorPos = new Vector2(hull.SimPosition.X, ConvertUnits.ToSimUnits(hull.Rect.Y - hull.RectHeight - 50));
|
||||
Body floor = Submarine.PickBody(hull.SimPosition, floorPos, collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform, customPredicate: f => !(f.Body.UserData is Submarine));
|
||||
// Do five raycasts to check if there's a floor. Don't create waypoints unless we can find a floor.
|
||||
Body floor = null;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
float horizontalOffset = 0;
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
horizontalOffset = hull.RectWidth * 0.2f;
|
||||
break;
|
||||
case 2:
|
||||
horizontalOffset = hull.RectWidth * 0.4f;
|
||||
break;
|
||||
case 3:
|
||||
horizontalOffset = -hull.RectWidth * 0.2f;
|
||||
break;
|
||||
case 4:
|
||||
horizontalOffset = -hull.RectWidth * 0.4f;
|
||||
break;
|
||||
}
|
||||
horizontalOffset = ConvertUnits.ToSimUnits(horizontalOffset);
|
||||
Vector2 floorPos = new Vector2(hull.SimPosition.X + horizontalOffset, ConvertUnits.ToSimUnits(hull.Rect.Y - hull.RectHeight - 50));
|
||||
floor = Submarine.PickBody(new Vector2(hull.SimPosition.X + horizontalOffset, hull.SimPosition.Y), floorPos, collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform, customPredicate: f => !(f.Body.UserData is Submarine));
|
||||
if (floor != null) { break; }
|
||||
}
|
||||
if (floor == null) { continue; }
|
||||
// Make sure that the waypoints don't go higher than the halfway of the room.
|
||||
float waypointHeight = hull.Rect.Height > heightFromFloor * 2 ? heightFromFloor : hull.Rect.Height / 2;
|
||||
if (hull.Rect.Width < diffFromHullEdge * 3.0f)
|
||||
{
|
||||
@@ -223,12 +243,42 @@ namespace Barotrauma
|
||||
new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Platforms
|
||||
foreach (Structure platform in Structure.WallList)
|
||||
{
|
||||
if (!platform.IsPlatform) { continue; }
|
||||
float waypointHeight = heightFromFloor;
|
||||
WayPoint prevWaypoint = null;
|
||||
for (float x = platform.Rect.X + diffFromHullEdge; x <= platform.Rect.Right - diffFromHullEdge; x += minDist)
|
||||
{
|
||||
WayPoint wayPoint = new WayPoint(new Vector2(x, platform.Rect.Y + waypointHeight), SpawnType.Path, submarine);
|
||||
if (prevWaypoint != null)
|
||||
{
|
||||
wayPoint.ConnectTo(prevWaypoint);
|
||||
}
|
||||
// If the waypoint is close to hull waypoints, remove it.
|
||||
if (wayPoint != null)
|
||||
{
|
||||
for (int dir = -1; dir <= 1; dir += 2)
|
||||
{
|
||||
if (wayPoint.FindClosest(dir, horizontalSearch: true, tolerance: new Vector2(minDist, heightFromFloor), ignored: prevWaypoint.ToEnumerable()) != null)
|
||||
{
|
||||
wayPoint.Remove();
|
||||
wayPoint = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prevWaypoint = wayPoint;
|
||||
}
|
||||
}
|
||||
|
||||
float outSideWaypointInterval = 100.0f;
|
||||
if (submarine.Info.Type != SubmarineType.OutpostModule)
|
||||
{
|
||||
List<WayPoint> outsideWaypoints = new List<WayPoint>();
|
||||
List<(WayPoint, int)> outsideWaypoints = new List<(WayPoint, int)>();
|
||||
|
||||
Rectangle borders = Hull.GetBorders();
|
||||
int originalWidth = borders.Width;
|
||||
@@ -260,7 +310,7 @@ namespace Barotrauma
|
||||
new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
|
||||
SpawnType.Path, submarine);
|
||||
|
||||
outsideWaypoints.Add(wayPoint);
|
||||
outsideWaypoints.Add((wayPoint, i));
|
||||
|
||||
if (x == borders.X + outSideWaypointInterval)
|
||||
{
|
||||
@@ -284,7 +334,7 @@ namespace Barotrauma
|
||||
new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
|
||||
SpawnType.Path, submarine);
|
||||
|
||||
outsideWaypoints.Add(wayPoint);
|
||||
outsideWaypoints.Add((wayPoint, i));
|
||||
|
||||
if (y == borders.Y - borders.Height)
|
||||
{
|
||||
@@ -302,8 +352,9 @@ namespace Barotrauma
|
||||
Vector2 center = ConvertUnits.ToSimUnits(submarine.HiddenSubPosition);
|
||||
float halfHeight = ConvertUnits.ToSimUnits(borders.Height / 2);
|
||||
// Try to move the waypoints so that they are near the walls, roughly following the shape of the sub.
|
||||
foreach (WayPoint wp in outsideWaypoints)
|
||||
foreach (var wayPoint in outsideWaypoints)
|
||||
{
|
||||
WayPoint wp = wayPoint.Item1;
|
||||
float xDiff = center.X - wp.SimPosition.X;
|
||||
Vector2 targetPos = new Vector2(center.X - xDiff * 0.5f, center.Y);
|
||||
Body wall = Submarine.PickBody(wp.SimPosition, targetPos, collisionCategory: Physics.CollisionWall, customPredicate: f => !(f.Body.UserData is Submarine));
|
||||
@@ -331,8 +382,9 @@ namespace Barotrauma
|
||||
var removals = new List<WayPoint>();
|
||||
WayPoint previous = null;
|
||||
float tooClose = outSideWaypointInterval / 2;
|
||||
foreach (WayPoint wp in outsideWaypoints)
|
||||
foreach (var wayPoint in outsideWaypoints)
|
||||
{
|
||||
WayPoint wp = wayPoint.Item1;
|
||||
if (wp.CurrentHull != null ||
|
||||
Submarine.PickBody(wp.SimPosition, wp.SimPosition + Vector2.Normalize(center - wp.SimPosition) * 0.1f, collisionCategory: Physics.CollisionWall | Physics.CollisionItem, customPredicate: f => !(f.Body.UserData is Submarine), allowInsideFixture: true) != null)
|
||||
{
|
||||
@@ -341,8 +393,9 @@ namespace Barotrauma
|
||||
previous = wp;
|
||||
continue;
|
||||
}
|
||||
foreach (WayPoint otherWp in outsideWaypoints)
|
||||
foreach (var otherWayPoint in outsideWaypoints)
|
||||
{
|
||||
WayPoint otherWp = otherWayPoint.Item1;
|
||||
if (otherWp == wp) { continue; }
|
||||
if (removals.Contains(otherWp)) { continue; }
|
||||
float sqrDist = Vector2.DistanceSquared(wp.Position, otherWp.Position);
|
||||
@@ -356,13 +409,12 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (WayPoint wp in removals)
|
||||
{
|
||||
outsideWaypoints.Remove(wp);
|
||||
outsideWaypoints.RemoveAll(w => w.Item1 == wp);
|
||||
wp.Remove();
|
||||
}
|
||||
// Connect loose ends (TODO: this sometimes fails, creating the connection to a wrong node)
|
||||
for (int i = 0; i < outsideWaypoints.Count; i++)
|
||||
{
|
||||
WayPoint current = outsideWaypoints[i];
|
||||
WayPoint current = outsideWaypoints[i].Item1;
|
||||
if (current.linkedTo.Count > 1) { continue; }
|
||||
WayPoint next = null;
|
||||
int maxConnections = 2;
|
||||
@@ -371,19 +423,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (current.linkedTo.Count >= maxConnections) { break; }
|
||||
tooFar /= current.linkedTo.Count;
|
||||
// First try to find a loose end
|
||||
next = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp != next && wp.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.linkedTo.Count < 2);
|
||||
// Then accept any connection that not connected to the existing connection
|
||||
next ??= current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp != next && wp.linkedTo.None(e => current.linkedTo.Contains(e)));
|
||||
next = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp.Item1 != next && wp.Item1.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.Item1.linkedTo.Count < 2 && wp.Item2 < i);
|
||||
if (next != null)
|
||||
{
|
||||
current.ConnectTo(next);
|
||||
}
|
||||
}
|
||||
if (current.linkedTo.Count == 1)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't automatically link waypoint {current.ID}. You should do it manually.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +577,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 70), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
|
||||
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 100), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
|
||||
}
|
||||
if (closest == null) { continue; }
|
||||
ladderPoint.ConnectTo(closest);
|
||||
@@ -601,13 +646,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && !w.linkedTo.Any());
|
||||
|
||||
var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && w.linkedTo.None());
|
||||
foreach (WayPoint wp in orphans)
|
||||
{
|
||||
wp.Remove();
|
||||
}
|
||||
|
||||
foreach (WayPoint wp in WayPointList)
|
||||
{
|
||||
if (wp.CurrentHull == null && wp.Ladders == null && wp.linkedTo.Count < 2)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't automatically link the waypoint {wp.ID} outside of the submarine. You should do it manually. The waypoint ID is shown in red color.");
|
||||
}
|
||||
}
|
||||
|
||||
//re-disable the bodies of the doors that are supposed to be open
|
||||
foreach (Door door in openDoors)
|
||||
{
|
||||
@@ -617,18 +669,20 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private WayPoint FindClosestOutside(IEnumerable<WayPoint> waypointList, float tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<WayPoint, bool> filter = null)
|
||||
private WayPoint FindClosestOutside(IEnumerable<(WayPoint, int)> waypointList, float tolerance, Body ignoredBody = null, IEnumerable<WayPoint> ignored = null, Func<(WayPoint, int), bool> filter = null)
|
||||
{
|
||||
float closestDist = 0;
|
||||
WayPoint closest = null;
|
||||
foreach (WayPoint wp in waypointList)
|
||||
foreach (var wayPoint in waypointList)
|
||||
{
|
||||
WayPoint wp = wayPoint.Item1;
|
||||
if (wp.SpawnType != SpawnType.Path || wp == this) { continue; }
|
||||
// Ignore if already linked
|
||||
if (linkedTo.Contains(wp)) { continue; }
|
||||
if (ignored != null && ignored.Contains(wp)) { continue; }
|
||||
if (filter != null && !filter(wp)) { continue; }
|
||||
if (filter != null && !filter(wayPoint)) { continue; }
|
||||
float sqrDist = Vector2.DistanceSquared(Position, wp.Position);
|
||||
if (sqrDist > tolerance * tolerance) { continue; }
|
||||
if (closest == null || sqrDist < closestDist)
|
||||
{
|
||||
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
|
||||
|
||||
Reference in New Issue
Block a user