v1.6.17.0 (Unto the Breach update)

This commit is contained in:
Regalis11
2024-10-22 17:29:04 +03:00
parent e74b3cdb17
commit 6e6c17e100
417 changed files with 17166 additions and 5870 deletions
@@ -43,7 +43,7 @@ namespace Barotrauma
}
}
public void Save(XElement element)
public virtual void Save(XElement element)
{
SerializableProperty.SerializeProperties(this, element);
}
@@ -126,4 +126,46 @@ namespace Barotrauma
WreckContainsThalamus = HasThalamus.No;
}
}
class EnemySubmarineInfo : ExtraSubmarineInfo
{
[Serialize(4000.0f, IsPropertySaveable.Yes), Editable]
public float Reward { get; set; }
[Serialize(50.0f, IsPropertySaveable.Yes), Editable]
public float PreferredDifficulty { get; set; }
private readonly HashSet<Identifier> missionTags = new HashSet<Identifier>();
public HashSet<Identifier> MissionTags => missionTags;
public EnemySubmarineInfo(SubmarineInfo submarineInfo, XElement element) : base(submarineInfo, element)
{
Name = $"{nameof(EnemySubmarineInfo)} ({submarineInfo.Name})";
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
foreach (var missionTag in element.GetAttributeIdentifierArray(nameof(MissionTags), Array.Empty<Identifier>()))
{
missionTags.Add(missionTag);
}
}
public EnemySubmarineInfo(SubmarineInfo submarineInfo) : base(submarineInfo)
{
Name = $"{nameof(EnemySubmarineInfo)} ({submarineInfo.Name})";
}
public EnemySubmarineInfo(EnemySubmarineInfo original) : base(original)
{
foreach (var missionTag in original.missionTags)
{
missionTags.Add(missionTag);
}
}
public override void Save(XElement element)
{
base.Save(element);
element.Add(new XAttribute(nameof(MissionTags), string.Join(',', missionTags)));
}
}
}
@@ -140,24 +140,44 @@ namespace Barotrauma
public ContentPath OutpostFilePath { get; set; }
public class ModuleCount
[Serialize("", IsPropertySaveable.Yes, description: "If set, a fully pre-built outpost with this tag will be used instead of generating the outpost."), Editable]
public Identifier OutpostTag { get; set; }
public class ModuleCount : ISerializableEntity
{
public Identifier Identifier;
public int Count;
public int Order;
public Identifier RequiredFaction;
[Serialize(0, IsPropertySaveable.Yes), Editable]
public int Count { get; set; }
[Serialize(0, IsPropertySaveable.Yes, description: "Can be used to enforce the modules to be placed in a specific order, starting from the docking module (0 = first, 1 = second, etc)."), Editable]
public int Order { get; set; }
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Minimum difficulty of the current level for the module to appear in the outpost."), Editable]
public float MinDifficulty { get; set; }
[Serialize(100.0f, IsPropertySaveable.Yes, description: "Maximum difficulty of the current level for the module to appear in the outpost."), Editable]
public float MaxDifficulty { get; set; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Probability for this type of module to be included in the outpost."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
public float Probability { get; set; }
[Serialize("", IsPropertySaveable.Yes), Editable]
public Identifier RequiredFaction { get; set; }
public string Name => Identifier.Value;
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; set; }
public ModuleCount(ContentXElement element)
{
Identifier = element.GetAttributeIdentifier("flag", element.GetAttributeIdentifier("moduletype", ""));
Count = element.GetAttributeInt("count", 0);
Order = element.GetAttributeInt("order", 0);
RequiredFaction = element.GetAttributeIdentifier("requiredfaction", Identifier.Empty);
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
}
public ModuleCount(Identifier id, int count)
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element: null);
Identifier = id;
Count = count;
RequiredFaction = Identifier.Empty;
@@ -261,6 +281,7 @@ namespace Barotrauma
}
OutpostFilePath = element.GetAttributeContentPath(nameof(OutpostFilePath));
OutpostTag = element.GetAttributeIdentifier(nameof(OutpostTag), Identifier.Empty);
var humanPrefabCollections = new List<NpcCollection>();
foreach (var subElement in element.Elements())
@@ -268,7 +289,23 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "modulecount":
moduleCounts.Add(new ModuleCount(subElement));
var newModuleCount = new ModuleCount(subElement);
if (moduleCounts.None() && newModuleCount.Probability < 1.0f)
{
DebugConsole.AddWarning(
$"Potential error in outpost generation parameters \"{Identifier}\"." +
$" The first module is set to spawn with a probability of {newModuleCount.Probability}%. The first module must always spawn, so the probability will be ignored.",
contentPackage: ContentPackage);
newModuleCount.Probability = 1.0f;
}
else if (newModuleCount.Probability <= 0.0f)
{
DebugConsole.AddWarning(
$"Potential error in outpost generation parameters \"{Identifier}\"." +
$" Probability of the module {newModuleCount.Identifier} is 0% (the module should never spawn, so there's no reason to include it in the generation parameters.",
contentPackage: ContentPackage);
}
moduleCounts.Add(newModuleCount);
break;
case "npcs":
var newCollection = new NpcCollection();
@@ -299,7 +336,7 @@ namespace Barotrauma
return moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag)?.Count ?? 0;
}
public void SetModuleCount(Identifier moduleFlag, int count)
public void SetModuleCount(Identifier moduleFlag, int count, float? probability = null, float? minDifficulty = null, float? maxDifficulty = null)
{
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; }
if (count <= 0)
@@ -311,12 +348,20 @@ namespace Barotrauma
var moduleCount = moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag);
if (moduleCount == null)
{
moduleCounts.Add(new ModuleCount(moduleFlag, count));
}
else
{
moduleCount.Count = count;
moduleCount = new ModuleCount(moduleFlag, count);
if (moduleCount.Probability <= 0.0f)
{
DebugConsole.AddWarning(
$"Potential error in outpost generation parameters \"{Identifier}\"."+
$" Probability of the module {moduleCount.Identifier} is 0 (the module should never spawn, so there's no reason to include it in the generation parameters.",
contentPackage: ContentPackage);
}
moduleCounts.Add(moduleCount);
}
moduleCount.Count = count;
if (probability.HasValue) { moduleCount.Probability = probability.Value; }
if (minDifficulty.HasValue) { moduleCount.MinDifficulty = minDifficulty.Value; }
if (maxDifficulty.HasValue) { moduleCount.MaxDifficulty = maxDifficulty.Value; }
}
}
@@ -330,12 +375,15 @@ namespace Barotrauma
}
}
public IReadOnlyList<HumanPrefab> GetHumanPrefabs(IEnumerable<FactionPrefab> factions, Rand.RandSync randSync)
public IReadOnlyList<HumanPrefab> GetHumanPrefabs(IEnumerable<FactionPrefab> factions, Submarine sub, Rand.RandSync randSync)
{
if (!humanPrefabCollections.Any()) { return Array.Empty<HumanPrefab>(); }
var collection = humanPrefabCollections.GetRandom(randSync);
return collection.GetByFaction(factions).ToImmutableList();
return collection
.GetByFaction(factions)
.Where(humanPrefab => !humanPrefab.RequireSpawnPointTag || WayPoint.WayPointList.Any(wp => wp.Submarine == sub && humanPrefab.GetSpawnPointTags().Any(tag => wp.Tags.Contains(tag))))
.ToImmutableList();
}
public bool CanHaveCampaignInteraction(CampaignMode.InteractionType interactionType)
@@ -66,6 +66,8 @@ namespace Barotrauma
return Generate(generationParams, location.Type, location, onlyEntrance, allowInvalidOutpost);
}
private static SubmarineInfo usedForceOutpostModule;
private static Submarine Generate(OutpostGenerationParams generationParams, LocationType locationType, Location location, bool onlyEntrance = false, bool allowInvalidOutpost = false)
{
var outpostModuleFiles = ContentPackageManager.EnabledPackages.All
@@ -87,6 +89,69 @@ namespace Barotrauma
locationType = location.GetLocationType();
}
Submarine sub = null;
if (generationParams.OutpostTag.IsEmpty)
{
var forceOutpostModule = GameMain.GameSession?.ForceOutpostModule;
sub = GenerateFromModules(generationParams, outpostModuleFiles, sub, locationType, location, onlyEntrance, allowInvalidOutpost);
if (sub != null)
{
return sub;
}
else if (forceOutpostModule != null)
{
//failed to force the module, abort
return null;
}
}
var outpostFiles = ContentPackageManager.EnabledPackages.All
.SelectMany(p => p.GetFiles<OutpostFile>())
.Where(f => !TutorialPrefab.Prefabs.Any(tp => tp.OutpostPath == f.Path))
.OrderBy(f => f.UintIdentifier).ToList();
List<SubmarineInfo> outpostInfos = new List<SubmarineInfo>();
foreach (var outpostFile in outpostFiles)
{
outpostInfos.Add(new SubmarineInfo(outpostFile.Path.Value));
}
if (!generationParams.OutpostTag.IsEmpty)
{
if (outpostInfos.Any(o => o.OutpostTags.Contains(generationParams.OutpostTag)))
{
outpostInfos = outpostInfos.FindAll(o => o.OutpostTags.Contains(generationParams.OutpostTag));
}
else
{
DebugConsole.ThrowError($"Could not find any outposts with the tag {generationParams.OutpostTag}. Choosing a random one instead...");
}
}
if (!outpostInfos.Any())
{
throw new Exception("Failed to generate an outpost. Could not generate an outpost from the available outpost modules and there are no pre-built outposts available.");
}
var prebuiltOutpostInfo = outpostInfos.GetRandom(Rand.RandSync.ServerAndClient);
if (GameMain.NetworkMember?.ServerSettings is { } serverSettings &&
serverSettings.SelectedOutpostName != "Random")
{
var matchingOutpost = outpostInfos.FirstOrDefault(o => o.Name == serverSettings.SelectedOutpostName);
if (matchingOutpost != null)
{
prebuiltOutpostInfo = matchingOutpost;
}
}
prebuiltOutpostInfo.Type = SubmarineType.Outpost;
sub = new Submarine(prebuiltOutpostInfo);
sub.Info.OutpostGenerationParams = generationParams;
location?.RemoveTakenItems();
EnableFactionSpecificEntities(sub, location);
return sub;
}
private static Submarine GenerateFromModules(OutpostGenerationParams generationParams, OutpostModuleFile[] outpostModuleFiles, Submarine sub, LocationType locationType, Location location, bool onlyEntrance = false, bool allowInvalidOutpost = false)
{
//load the infos of the outpost module files
List<SubmarineInfo> outpostModules = new List<SubmarineInfo>();
foreach (var outpostModuleFile in outpostModuleFiles)
@@ -114,7 +179,6 @@ namespace Barotrauma
List<PlacedModule> selectedModules = new List<PlacedModule>();
bool generationFailed = false;
int remainingTries = 5;
Submarine sub = null;
while (remainingTries > -1 && outpostModules.Any())
{
if (sub != null)
@@ -140,10 +204,10 @@ namespace Barotrauma
GameMain.Server.EntityEventManager.Events.RemoveRange(eventCount, GameMain.Server.EntityEventManager.Events.Count - eventCount);
GameMain.Server.EntityEventManager.UniqueEvents.RemoveRange(uniqueEventCount, GameMain.Server.EntityEventManager.UniqueEvents.Count - uniqueEventCount);
#endif
if (remainingTries <= 0)
if (remainingTries <= 0)
{
generationFailed = true;
break;
break;
}
}
@@ -180,9 +244,18 @@ namespace Barotrauma
//the first module is spawned separately, remove it from the list of pending modules
Identifier initialModuleFlag = pendingModuleFlags.FirstOrDefault().IfEmpty("airlock".ToIdentifier());
pendingModuleFlags.Remove(initialModuleFlag);
pendingModuleFlags.Remove(initialModuleFlag);
bool hasForceOutpostWithInitialFlag = GameMain.GameSession?.ForceOutpostModule != null && GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.ModuleFlags.Contains(initialModuleFlag);
var initialModule = hasForceOutpostWithInitialFlag ? GameMain.GameSession.ForceOutpostModule : GetRandomModule(outpostModules, initialModuleFlag, locationType);
if (hasForceOutpostWithInitialFlag)
{
DebugConsole.NewMessage($"Using Force outpost module as initial in Outpost generation: {GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}", Color.Yellow);
usedForceOutpostModule = GameMain.GameSession.ForceOutpostModule;
GameMain.GameSession.ForceOutpostModule = null;
}
var initialModule = GetRandomModule(outpostModules, initialModuleFlag, locationType);
if (initialModule == null)
{
throw new Exception("Failed to generate an outpost (no airlock modules found).");
@@ -202,12 +275,24 @@ namespace Barotrauma
selectedModules.Last().FulfilledModuleTypes.Add(initialModuleFlag);
AppendToModule(
selectedModules.Last(), outpostModules.ToList(), pendingModuleFlags,
selectedModules,
locationType,
selectedModules.Last(), outpostModules.ToList(), pendingModuleFlags,
selectedModules,
locationType,
allowExtendBelowInitialModule: generationParams is RuinGeneration.RuinGenerationParams,
allowDifferentLocationType: remainingTries == 1);
if (GameMain.GameSession?.ForceOutpostModule != null)
{
if (remainingTries > 0)
{
remainingTries--;
continue;
}
DebugConsole.ThrowError($"Could not place force outpost module: {GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}");
GameMain.GameSession.ForceOutpostModule = null;
return null;
}
if (pendingModuleFlags.Any(flag => flag != "none"))
{
if (!allowInvalidOutpost)
@@ -255,34 +340,13 @@ namespace Barotrauma
}
}
EnableFactionSpecificEntities(sub, location);
return sub;
return sub;
}
remainingTries--;
}
#if DEBUG
DebugConsole.ThrowError("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead...");
#else
DebugConsole.NewMessage("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead...");
#endif
var outpostFiles = ContentPackageManager.EnabledPackages.All
.SelectMany(p => p.GetFiles<OutpostFile>())
.Where(f => !TutorialPrefab.Prefabs.Any(tp => tp.OutpostPath == f.Path))
.OrderBy(f => f.UintIdentifier).ToArray();
if (!outpostFiles.Any())
{
throw new Exception("Failed to generate an outpost. Could not generate an outpost from the available outpost modules and there are no pre-built outposts available.");
}
var prebuiltOutpostInfo = new SubmarineInfo(outpostFiles.GetRandom(Rand.RandSync.ServerAndClient).Path.Value)
{
Type = SubmarineType.Outpost
};
sub = new Submarine(prebuiltOutpostInfo);
sub.Info.OutpostGenerationParams = generationParams;
location?.RemoveTakenItems();
EnableFactionSpecificEntities(sub, location);
return sub;
DebugConsole.AddSafeError("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead...");
return null;
List<MapEntity> loadEntities(Submarine sub)
{
@@ -293,13 +357,18 @@ namespace Barotrauma
var selectedModule = selectedModules[i];
sub.Info.GameVersion = selectedModule.Info.GameVersion;
var moduleEntities = MapEntity.LoadAll(sub, selectedModule.Info.SubmarineElement, selectedModule.Info.FilePath, idOffset);
if (usedForceOutpostModule != null && usedForceOutpostModule == selectedModule.Info)
{
sub.ForcedOutpostModuleWayPoints = moduleEntities.OfType<WayPoint>().ToList();
}
MapEntity.InitializeLoadedLinks(moduleEntities);
foreach (MapEntity entity in moduleEntities.ToList())
{
entity.OriginalModuleIndex = i;
if (!(entity is Item item)) { continue; }
if (entity is not Item item) { continue; }
var door = item.GetComponent<Door>();
if (door != null)
{
@@ -319,7 +388,15 @@ namespace Barotrauma
{
hull.SetModuleTags(selectedModule.Info.OutpostModuleInfo.ModuleFlags);
}
if (Screen.Selected is { IsEditor: false })
{
foreach (Identifier layer in selectedModule.Info.LayersHiddenByDefault)
{
Submarine.SetLayerEnabled(layer, enabled: false, entities: moduleEntities);
}
}
if (!hullEntities.Any())
{
selectedModule.HullBounds = new Rectangle(Point.Zero, Submarine.GridSize.ToPoint());
@@ -388,25 +465,25 @@ namespace Barotrauma
while (FindOverlap(subsequentModules, otherModules, out var module1, out var module2) && remainingTries > 0)
{
overlapsFound = true;
if (FindOverlapSolution(subsequentModules, module1, module2, selectedModules, out Dictionary<PlacedModule,Vector2> solution))
if (FindOverlapSolution(subsequentModules, module1, module2, selectedModules, out Dictionary<PlacedModule, Vector2> solution))
{
foreach (KeyValuePair<PlacedModule, Vector2> kvp in solution)
{
kvp.Key.Offset += kvp.Value;
}
}
}
else
{
break;
}
remainingTries--;
}
remainingTries--;
}
}
iteration++;
if (iteration > 10)
if (iteration > 10)
{
generationFailed = true;
break;
break;
}
}
@@ -438,10 +515,10 @@ namespace Barotrauma
//eww
structure.SpriteDepth = MathHelper.Lerp(0.999f, 0.9999f, structure.SpriteDepth);
#if CLIENT
foreach (var light in structure.Lights)
{
light.IsBackground = true;
}
foreach (var light in structure.Lights)
{
light.IsBackground = true;
}
#endif
}
}
@@ -478,21 +555,42 @@ namespace Barotrauma
private static List<Identifier> SelectModules(IEnumerable<SubmarineInfo> modules, Location location, OutpostGenerationParams generationParams)
{
int totalModuleCount = generationParams.TotalModuleCount;
int totalModuleCountExcludingOptional = totalModuleCount - generationParams.ModuleCounts.Count(m => m.Probability < 1.0f);
var pendingModuleFlags = new List<Identifier>();
bool availableModulesFound = true;
Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Identifier;
pendingModuleFlags.Add(initialModuleFlag);
while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound)
while (pendingModuleFlags.Count < totalModuleCountExcludingOptional && availableModulesFound)
{
availableModulesFound = false;
foreach (var moduleCount in generationParams.ModuleCounts)
{
if (!moduleCount.RequiredFaction.IsEmpty &&
location?.Faction?.Prefab.Identifier != moduleCount.RequiredFaction &&
location?.SecondaryFaction?.Prefab.Identifier != moduleCount.RequiredFaction)
float? difficulty = Level.ForcedDifficulty ?? location?.LevelData?.Difficulty;
if (difficulty.HasValue)
{
continue;
if (difficulty.Value < moduleCount.MinDifficulty || difficulty.Value > moduleCount.MaxDifficulty)
{
continue;
}
}
//if this is a module that we're trying to force into the outpost,
//ignore probability and faction requirements
if (GameMain.GameSession?.ForceOutpostModule == null ||
!GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.ModuleFlags.Contains(moduleCount.Identifier))
{
if (moduleCount.Probability < 1.0f &&
Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) > moduleCount.Probability)
{
continue;
}
if (!moduleCount.RequiredFaction.IsEmpty &&
location?.Faction?.Prefab.Identifier != moduleCount.RequiredFaction &&
location?.SecondaryFaction?.Prefab.Identifier != moduleCount.RequiredFaction)
{
continue;
}
}
if (pendingModuleFlags.Count(m => m == moduleCount.Identifier) >= generationParams.GetModuleCount(moduleCount.Identifier))
{
@@ -914,20 +1012,21 @@ namespace Barotrauma
}
modulesWithCorrectFlags = modulesWithCorrectFlags.Where(m => m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition) && m.OutpostModuleInfo.CanAttachToPrevious.HasFlag(gapPosition));
var suitableModules = GetSuitable(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true);
var suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true);
var suitableModulesForAnyOutpost = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false);
if (!suitableModules.Any())
{
//no suitable module found, see if we can find a "generic" module that's not meant for any specific type of outpost
suitableModules = GetSuitable(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false);
suitableModules = suitableModulesForAnyOutpost;
//still not found, see if we can find something that's otherwise suitable but not meant to attach to the previous module
if (!suitableModules.Any())
{
suitableModules = GetSuitable(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true);
suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true);
}
//still not found! Try if we can find a generic module that's not meant to attach to the previous module
if (!suitableModules.Any())
{
suitableModules = GetSuitable(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false);
suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false);
}
}
@@ -945,10 +1044,31 @@ namespace Barotrauma
}
else
{
return ToolBox.SelectWeightedRandom(suitableModules.ToList(), suitableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
var suitableModule = ToolBox.SelectWeightedRandom(suitableModules.ToList(), suitableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
if (GameMain.GameSession?.ForceOutpostModule != null)
{
if (suitableModules.Any(module => module.OutpostModuleInfo.Name == GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name) ||
suitableModulesForAnyOutpost.Any(module => module.OutpostModuleInfo.Name == GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name))
{
var forceOutpostModule = GameMain.GameSession.ForceOutpostModule;
System.Diagnostics.Debug.WriteLine($"Inserting Force outpost module in Outpost generation: {forceOutpostModule.OutpostModuleInfo.Name}");
GameMain.GameSession.ForceOutpostModule = null;
usedForceOutpostModule = forceOutpostModule;
return forceOutpostModule;
}
else if (GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.ModuleFlags.Contains(moduleFlag))
{
// if our force module has the same tag as the selected random one, return nothing
// because we don't want another module of the same type to be hogging the only spot for that type
return null;
}
}
return suitableModule;
}
IEnumerable<SubmarineInfo> GetSuitable(IEnumerable<SubmarineInfo> modules, bool requireAllowAttachToPrevious, bool requireCorrectLocationType, bool disallowNonLocationTypeSpecific)
IEnumerable<SubmarineInfo> GetSuitableModules(IEnumerable<SubmarineInfo> modules, bool requireAllowAttachToPrevious, bool requireCorrectLocationType, bool disallowNonLocationTypeSpecific)
{
IEnumerable<SubmarineInfo> suitable = modules;
if (requireCorrectLocationType)
@@ -1199,7 +1319,14 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {GetOpposingGapPosition(module.ThisGapPosition).ToString().ToLower()} gap of the module \"{module.PreviousModule.Info.Name}\".");
if (thisWayPoint == null)
{
DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {module.ThisGapPosition.ToString().ToLower()} gap of the module \"{module.Info.Name}\".");
}
if (previousWayPoint == null)
{
DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {GetOpposingGapPosition(module.ThisGapPosition).ToString().ToLower()} gap of the module \"{module.PreviousModule.Info.Name}\".");
}
}
gapToRemove.ConnectedDoor?.Item.Remove();
@@ -1215,7 +1342,7 @@ namespace Barotrauma
var suitableHallwayModules = hallwayModules.Where(m =>
m.OutpostModuleInfo.AllowAttachToModules.Any(s => module.Info.OutpostModuleInfo.ModuleFlags.Contains(s)) &&
m.OutpostModuleInfo.AllowAttachToModules.Any(s => module.PreviousModule.Info.OutpostModuleInfo.ModuleFlags.Contains(s)));
if (suitableHallwayModules.Count() == 0)
if (suitableHallwayModules.None())
{
suitableHallwayModules = hallwayModules.Where(m =>
!m.OutpostModuleInfo.AllowAttachToModules.Any() ||
@@ -1359,19 +1486,41 @@ namespace Barotrauma
(endWaypoint, startWaypoint) = (startWaypoint, endWaypoint);
}
if (hallwayLength > 100 && isHorizontal)
//if the hallway is longer than 100 pixels, generate some waypoints inside it
//for vertical hallways this isn't necessarily, it's done as a part of the ladder generation in AlignLadders
const float distanceBetweenWaypoints = 100.0f;
if (hallwayLength > distanceBetweenWaypoints)
{
//if the hallway is longer than 100 pixels, generate some waypoints inside it
//for vertical hallways this isn't necessarily, it's done as a part of the ladder generation in AlignLadders
WayPoint prevWayPoint = startWaypoint;
WayPoint firstWayPoint = null;
for (float x = leftHull.Rect.Right + 50; x < rightHull.Rect.X - 50; x += 100.0f)
if (isHorizontal)
{
var newWayPoint = new WayPoint(new Vector2(x, hullBounds.Y + 110.0f), SpawnType.Path, sub);
firstWayPoint ??= newWayPoint;
prevWayPoint.linkedTo.Add(newWayPoint);
newWayPoint.linkedTo.Add(prevWayPoint);
prevWayPoint = newWayPoint;
for (float x = leftHull.Rect.Right + distanceBetweenWaypoints / 2; x < rightHull.Rect.X - distanceBetweenWaypoints / 2; x += distanceBetweenWaypoints)
{
var newWayPoint = new WayPoint(new Vector2(x, hullBounds.Y + 110.0f), SpawnType.Path, sub);
firstWayPoint ??= newWayPoint;
prevWayPoint.linkedTo.Add(newWayPoint);
newWayPoint.linkedTo.Add(prevWayPoint);
prevWayPoint = newWayPoint;
}
}
else if (startWaypoint.Ladders == null)
{
float bottom = bottomHull.Rect.Y;
float top = topHull.Rect.Y - topHull.Rect.Height;
for (float y = bottom + distanceBetweenWaypoints; y < top - distanceBetweenWaypoints; y += distanceBetweenWaypoints)
{
var newWayPoint = new WayPoint(new Vector2(startWaypoint.Position.X, y), SpawnType.Path, sub);
firstWayPoint ??= newWayPoint;
prevWayPoint.linkedTo.Add(newWayPoint);
newWayPoint.linkedTo.Add(prevWayPoint);
prevWayPoint = newWayPoint;
}
}
else
{
startWaypoint.linkedTo.Add(endWaypoint);
endWaypoint.linkedTo.Add(startWaypoint);
}
if (firstWayPoint != null)
{
@@ -1387,9 +1536,9 @@ namespace Barotrauma
else
{
startWaypoint.linkedTo.Add(endWaypoint);
endWaypoint.linkedTo.Add(startWaypoint);
endWaypoint.linkedTo.Add(startWaypoint);
}
}
}
}
return placedEntities;
}
@@ -1499,12 +1648,16 @@ namespace Barotrauma
static bool ShouldRemoveLinkedEntity(MapEntity e, bool doorInUse, PlacedModule module)
{
if (e is Item it && it.IsLadder)
if (e is Item { IsLadder: true } ladderItem)
{
if (module.UsedGapPositions.HasFlag(OutpostModuleInfo.GapPosition.Top) || module.UsedGapPositions.HasFlag(OutpostModuleInfo.GapPosition.Bottom))
int linkedToLadderCount = Door.DoorList.Count(otherDoor => otherDoor.Item.linkedTo.Contains(ladderItem));
if (linkedToLadderCount > 1)
{
//if there's multiple doors linked to the ladder, never remove it
//(the ladder is presumably not just for moving between two modules in that case, but might e.g. go through the whole module)
return false;
}
return ladderItem.RemoveIfLinkedOutpostDoorInUse == doorInUse;
}
if (e is Structure structure)
@@ -1670,7 +1823,7 @@ namespace Barotrauma
if (location?.Faction != null) { factions.Add(location.Faction.Prefab); }
if (location?.SecondaryFaction != null) { factions.Add(location.SecondaryFaction.Prefab); }
var humanPrefabs = outpost.Info.OutpostGenerationParams.GetHumanPrefabs(factions, Rand.RandSync.ServerAndClient);
var humanPrefabs = outpost.Info.OutpostGenerationParams.GetHumanPrefabs(factions, outpost, Rand.RandSync.ServerAndClient);
foreach (HumanPrefab humanPrefab in humanPrefabs)
{
if (humanPrefab is null) { continue; }