Build 0.18.4.0
This commit is contained in:
@@ -10,12 +10,11 @@ using System.Net;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
#warning TODO: MapEntityPrefab should be constrained further to not include item assemblies, as assemblies are effectively not entities at all
|
||||
partial class ItemAssemblyPrefab : MapEntityPrefab
|
||||
{
|
||||
public static readonly PrefabCollection<ItemAssemblyPrefab> Prefabs = new PrefabCollection<ItemAssemblyPrefab>();
|
||||
|
||||
public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies");
|
||||
|
||||
private readonly XElement configElement;
|
||||
|
||||
public readonly ImmutableArray<(Identifier Identifier, Rectangle Rect)> DisplayEntities;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma
|
||||
Cave = 0x4,
|
||||
Ruin = 0x8,
|
||||
Wreck = 0x10,
|
||||
BeaconStation = 0x20, // Not used anywhere
|
||||
BeaconStation = 0x20,
|
||||
Abyss = 0x40,
|
||||
AbyssCave = 0x80
|
||||
}
|
||||
@@ -395,6 +395,13 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public static bool IsLoadedOutpost => Loaded?.Type == LevelData.LevelType.Outpost;
|
||||
|
||||
/// <summary>
|
||||
/// Is there a loaded level set, and is it a friendly outpost (FriendlyNPC or Team1)
|
||||
/// </summary>
|
||||
public static bool IsLoadedFriendlyOutpost =>
|
||||
loaded?.Type == LevelData.LevelType.Outpost &&
|
||||
(loaded?.StartLocation?.Type?.OutpostTeam == CharacterTeamType.FriendlyNPC || loaded?.StartLocation?.Type?.OutpostTeam == CharacterTeamType.Team1);
|
||||
|
||||
public LevelGenerationParams GenerationParams
|
||||
{
|
||||
get { return LevelData.GenerationParams; }
|
||||
@@ -421,7 +428,7 @@ namespace Barotrauma
|
||||
borders = new Rectangle(Point.Zero, levelData.Size);
|
||||
}
|
||||
|
||||
public static Level Generate(LevelData levelData, bool mirror, SubmarineInfo startOutpost = null, SubmarineInfo endOutpost = null)
|
||||
public static Level Generate(LevelData levelData, bool mirror, Location startLocation, Location endLocation, SubmarineInfo startOutpost = null, SubmarineInfo endOutpost = null)
|
||||
{
|
||||
Debug.Assert(levelData.Biome != null);
|
||||
if (levelData.Biome == null) { throw new ArgumentException("Biome was null"); }
|
||||
@@ -433,11 +440,11 @@ namespace Barotrauma
|
||||
preSelectedStartOutpost = startOutpost,
|
||||
preSelectedEndOutpost = endOutpost
|
||||
};
|
||||
level.Generate(mirror);
|
||||
level.Generate(mirror, startLocation, endLocation);
|
||||
return level;
|
||||
}
|
||||
|
||||
private void Generate(bool mirror)
|
||||
private void Generate(bool mirror, Location startLocation, Location endLocation)
|
||||
{
|
||||
Loaded?.Remove();
|
||||
Loaded = this;
|
||||
@@ -454,8 +461,8 @@ namespace Barotrauma
|
||||
|
||||
if (LevelData.ForceOutpostGenerationParams == null)
|
||||
{
|
||||
StartLocation = GameMain.GameSession?.StartLocation;
|
||||
EndLocation = GameMain.GameSession?.EndLocation;
|
||||
StartLocation = startLocation;
|
||||
EndLocation = endLocation;
|
||||
}
|
||||
|
||||
GenerateEqualityCheckValue(LevelGenStage.GenStart);
|
||||
@@ -509,7 +516,7 @@ namespace Barotrauma
|
||||
Rectangle pathBorders = borders;
|
||||
pathBorders.Inflate(
|
||||
-Math.Min(Math.Min(minMainPathWidth * 2, MaxSubmarineWidth), borders.Width / 5),
|
||||
-Math.Min(minMainPathWidth, borders.Height / 5));
|
||||
-Math.Min(minMainPathWidth * 2, borders.Height / 5));
|
||||
|
||||
if (pathBorders.Width <= 0) { throw new InvalidOperationException($"The width of the level's path area is invalid ({pathBorders.Width})"); }
|
||||
if (pathBorders.Height <= 0) { throw new InvalidOperationException($"The height of the level's path area is invalid ({pathBorders.Height})"); }
|
||||
@@ -1713,7 +1720,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (abyssHeight > 30000)
|
||||
{
|
||||
//if the bottom of the abyss area is below crush depth, try to move it up to keep (most) of the abyss content above crush depth
|
||||
//but only if start of the abyss is above crush depth (no point in doing this if all of it is below crush depth)
|
||||
@@ -3527,6 +3534,8 @@ namespace Barotrauma
|
||||
}
|
||||
else if (type == SubmarineType.BeaconStation)
|
||||
{
|
||||
PositionsOfInterest.Add(new InterestingPosition(spawnPoint.ToPoint(), PositionType.BeaconStation, submarine: sub));
|
||||
|
||||
sub.ShowSonarMarker = false;
|
||||
sub.DockedTo.ForEach(s => s.ShowSonarMarker = false);
|
||||
sub.PhysicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
@@ -3940,7 +3949,7 @@ namespace Barotrauma
|
||||
//the submarine port has to be at the top of the sub
|
||||
if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; }
|
||||
float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X);
|
||||
if (dist < closestDistance)
|
||||
if (dist < closestDistance || subPort.MainDockingPort)
|
||||
{
|
||||
subPort = port;
|
||||
closestDistance = dist;
|
||||
@@ -4023,6 +4032,26 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("No BeaconStation files found in the selected content packages!");
|
||||
return;
|
||||
}
|
||||
|
||||
var beaconInfos = SubmarineInfo.SavedSubmarines.Where(i => i.IsBeacon);
|
||||
for (int i = beaconStationFiles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var beaconStationFile = beaconStationFiles[i];
|
||||
var matchingInfo = beaconInfos.SingleOrDefault(info => info.FilePath == beaconStationFile.Path.Value);
|
||||
Debug.Assert(matchingInfo != null);
|
||||
if (matchingInfo?.BeaconStationInfo is BeaconStationInfo beaconInfo)
|
||||
{
|
||||
if (LevelData.Difficulty < beaconInfo.MinLevelDifficulty || LevelData.Difficulty > beaconInfo.MaxLevelDifficulty)
|
||||
{
|
||||
beaconStationFiles.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (beaconStationFiles.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"No BeaconStation files found for the level difficulty {LevelData.Difficulty}!");
|
||||
return;
|
||||
}
|
||||
var contentFile = beaconStationFiles.GetRandom(Rand.RandSync.ServerAndClient);
|
||||
string beaconStationName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path.Value);
|
||||
|
||||
@@ -4078,24 +4107,22 @@ namespace Barotrauma
|
||||
{
|
||||
if (!(GameMain.NetworkMember?.IsClient ?? false))
|
||||
{
|
||||
//empty the reactor
|
||||
if (reactorContainer != null)
|
||||
bool allowDisconnectedWires = true;
|
||||
bool allowDamagedWalls = true;
|
||||
if (BeaconStation.Info?.BeaconStationInfo is BeaconStationInfo info)
|
||||
{
|
||||
foreach (Item item in reactorContainer.Inventory.AllItems)
|
||||
{
|
||||
if (item.NonInteractable) { continue; }
|
||||
Spawner.AddItemToRemoveQueue(item);
|
||||
}
|
||||
allowDisconnectedWires = info.AllowDisconnectedWires;
|
||||
allowDamagedWalls = info.AllowDamagedWalls;
|
||||
}
|
||||
|
||||
//remove wires
|
||||
float removeWireMinDifficulty = 20.0f;
|
||||
float removeWireProbability = MathUtils.InverseLerp(removeWireMinDifficulty, 100.0f, LevelData.Difficulty) * 0.5f;
|
||||
if (removeWireProbability > 0.0f)
|
||||
if (removeWireProbability > 0.0f && allowDisconnectedWires)
|
||||
{
|
||||
foreach (Item item in beaconItems.Where(it => it.GetComponent<Wire>() != null).ToList())
|
||||
{
|
||||
if (item.NonInteractable) { continue; }
|
||||
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
||||
Wire wire = item.GetComponent<Wire>();
|
||||
if (wire.Locked) { continue; }
|
||||
if (wire.Connections[0] != null && (wire.Connections[0].Item.NonInteractable || wire.Connections[0].Item.GetComponent<ConnectionPanel>().Locked))
|
||||
@@ -4115,8 +4142,8 @@ namespace Barotrauma
|
||||
connection.ConnectionPanel.DisconnectedWires.Add(wire);
|
||||
wire.RemoveConnection(connection.Item);
|
||||
#if SERVER
|
||||
connection.ConnectionPanel.Item.CreateServerEvent(connection.ConnectionPanel);
|
||||
wire.CreateNetworkEvent();
|
||||
connection.ConnectionPanel.Item.CreateServerEvent(connection.ConnectionPanel);
|
||||
wire.CreateNetworkEvent();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -4124,23 +4151,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//break powered items
|
||||
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered) && it.Components.Any(c => c is Repairable)))
|
||||
if (allowDamagedWalls)
|
||||
{
|
||||
if (item.NonInteractable) { continue; }
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.5f)
|
||||
//break powered items
|
||||
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered) && it.Components.Any(c => c is Repairable)))
|
||||
{
|
||||
item.Condition *= Rand.Range(0.6f, 0.8f, Rand.RandSync.Unsynced);
|
||||
if (item.NonInteractable || item.InvulnerableToDamage) { continue; }
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.5f)
|
||||
{
|
||||
item.Condition *= Rand.Range(0.6f, 0.8f, Rand.RandSync.Unsynced);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//poke holes in the walls
|
||||
foreach (Structure structure in Structure.WallList.Where(s => s.Submarine == BeaconStation))
|
||||
{
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.25f)
|
||||
//poke holes in the walls
|
||||
foreach (Structure structure in Structure.WallList.Where(s => s.Submarine == BeaconStation))
|
||||
{
|
||||
int sectionIndex = Rand.Range(0, structure.SectionCount - 1, Rand.RandSync.Unsynced);
|
||||
structure.AddDamage(sectionIndex, Rand.Range(structure.MaxHealth * 0.2f, structure.MaxHealth, Rand.RandSync.Unsynced));
|
||||
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.25f)
|
||||
{
|
||||
int sectionIndex = Rand.Range(0, structure.SectionCount - 1, Rand.RandSync.Unsynced);
|
||||
structure.AddDamage(sectionIndex, Rand.Range(structure.MaxHealth * 0.2f, structure.MaxHealth, Rand.RandSync.Unsynced));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly string Seed;
|
||||
|
||||
public float Difficulty;
|
||||
public readonly float Difficulty;
|
||||
|
||||
public readonly Biome Biome;
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ namespace Barotrauma
|
||||
if (sub != null)
|
||||
{
|
||||
bool leaveBehind = false;
|
||||
if (!sub.DockedTo.Contains(Submarine.MainSub))
|
||||
if (sub.Submarine != null && !sub.DockedTo.Contains(sub.Submarine))
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(Submarine.MainSub.AtEndExit || Submarine.MainSub.AtStartExit);
|
||||
if (Submarine.MainSub.AtEndExit)
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Load a previously saved campaign map from XML
|
||||
/// </summary>
|
||||
private Map(CampaignMode campaign, XElement element, CampaignSettings settings) : this(settings)
|
||||
private Map(CampaignMode campaign, XElement element) : this(campaign.Settings)
|
||||
{
|
||||
Seed = element.GetAttributeString("seed", "a");
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(Seed));
|
||||
@@ -104,7 +104,7 @@ namespace Barotrauma
|
||||
case "radiation":
|
||||
Radiation = new Radiation(this, generationParams.RadiationParams, subElement)
|
||||
{
|
||||
Enabled = settings.RadiationEnabled
|
||||
Enabled = campaign.Settings.RadiationEnabled
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -208,12 +208,12 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Generate a new campaign map from the seed
|
||||
/// </summary>
|
||||
public Map(CampaignMode campaign, string seed, CampaignSettings settings) : this(settings)
|
||||
public Map(CampaignMode campaign, string seed) : this(campaign.Settings)
|
||||
{
|
||||
Seed = seed;
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(Seed));
|
||||
|
||||
Generate();
|
||||
Generate(campaign.Settings);
|
||||
|
||||
if (Locations.Count == 0)
|
||||
{
|
||||
@@ -228,10 +228,7 @@ namespace Barotrauma
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (location.Type.Identifier != "outpost") { continue; }
|
||||
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
|
||||
{
|
||||
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
|
||||
}
|
||||
SetStartLocation(location);
|
||||
}
|
||||
//if no outpost was found (using a mod that replaces the outpost location type?), find any type of outpost
|
||||
if (CurrentLocation == null)
|
||||
@@ -239,25 +236,36 @@ namespace Barotrauma
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (!location.Type.HasOutpost) { continue; }
|
||||
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
|
||||
{
|
||||
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
|
||||
}
|
||||
SetStartLocation(location);
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
|
||||
if (StartLocation?.LevelData != null)
|
||||
|
||||
void SetStartLocation(Location location)
|
||||
{
|
||||
StartLocation.LevelData.Difficulty = 0;
|
||||
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
|
||||
{
|
||||
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
//ensure all paths from the starting location have 0 difficulty to make the 1st campaign round very easy
|
||||
foreach (var locationConnection in StartLocation.Connections)
|
||||
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
|
||||
|
||||
int loops = campaign.CampaignMetadata.GetInt("campaign.endings".ToIdentifier(), 0);
|
||||
if (loops == 0 && (campaign.Settings.Difficulty == GameDifficulty.Easy || campaign.Settings.Difficulty == GameDifficulty.Medium))
|
||||
{
|
||||
if (locationConnection.Difficulty > 0.0f)
|
||||
if (StartLocation != null)
|
||||
{
|
||||
locationConnection.Difficulty = 0.0f;
|
||||
locationConnection.LevelData = new LevelData(locationConnection);
|
||||
StartLocation.LevelData = new LevelData(StartLocation, 0);
|
||||
}
|
||||
|
||||
//ensure all paths from the starting location have 0 difficulty to make the 1st campaign round very easy
|
||||
foreach (var locationConnection in StartLocation.Connections)
|
||||
{
|
||||
if (locationConnection.Difficulty > 0.0f)
|
||||
{
|
||||
locationConnection.Difficulty = 0.0f;
|
||||
locationConnection.LevelData = new LevelData(locationConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +284,7 @@ namespace Barotrauma
|
||||
|
||||
#region Generation
|
||||
|
||||
private void Generate()
|
||||
private void Generate(CampaignSettings settings)
|
||||
{
|
||||
Connections.Clear();
|
||||
Locations.Clear();
|
||||
@@ -294,7 +302,6 @@ namespace Barotrauma
|
||||
|
||||
Voronoi voronoi = new Voronoi(0.5f);
|
||||
List<GraphEdge> edges = voronoi.MakeVoronoiGraph(voronoiSites, Width, Height);
|
||||
float zoneWidth = Width / generationParams.DifficultyZones;
|
||||
|
||||
Vector2 margin = new Vector2(
|
||||
Math.Min(10, Width * 0.1f),
|
||||
@@ -310,6 +317,7 @@ namespace Barotrauma
|
||||
|
||||
voronoiSites.Clear();
|
||||
Dictionary<int, List<Location>> locationsPerZone = new Dictionary<int, List<Location>>();
|
||||
bool possibleStartOutpostCreated = false;
|
||||
foreach (GraphEdge edge in edges)
|
||||
{
|
||||
if (edge.Point1 == edge.Point2) { continue; }
|
||||
@@ -344,12 +352,26 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
LocationType forceLocationType = null;
|
||||
foreach (LocationType locationType in LocationType.Prefabs.OrderBy(lt => lt.Identifier))
|
||||
if (!possibleStartOutpostCreated)
|
||||
{
|
||||
if (locationType.MinCountPerZone.TryGetValue(zone, out int minCount) && locationsPerZone[zone].Count(l => l.Type == locationType) < minCount)
|
||||
float zoneWidth = Width / generationParams.DifficultyZones;
|
||||
float threshold = zoneWidth * 0.1f;
|
||||
if (position.X < threshold)
|
||||
{
|
||||
forceLocationType = locationType;
|
||||
break;
|
||||
LocationType.Prefabs.TryGet("outpost", out forceLocationType);
|
||||
possibleStartOutpostCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (forceLocationType == null)
|
||||
{
|
||||
foreach (LocationType locationType in LocationType.Prefabs.OrderBy(lt => lt.Identifier))
|
||||
{
|
||||
if (locationType.MinCountPerZone.TryGetValue(zone, out int minCount) && locationsPerZone[zone].Count(l => l.Type == locationType) < minCount)
|
||||
{
|
||||
forceLocationType = locationType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,9 +477,7 @@ namespace Barotrauma
|
||||
if (zone1 == zone2) { continue; }
|
||||
if (zone1 > zone2)
|
||||
{
|
||||
int temp = zone2;
|
||||
zone2 = zone1;
|
||||
zone1 = temp;
|
||||
(zone1, zone2) = (zone2, zone1);
|
||||
}
|
||||
|
||||
if (generationParams.GateCount[zone1] == 0) { continue; }
|
||||
@@ -527,32 +547,43 @@ namespace Barotrauma
|
||||
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
float difficulty = connection.CenterPos.X / Width * 100;
|
||||
float minDifficulty = 0;
|
||||
float maxDifficulty = 100;
|
||||
var biome = connection.Biome;
|
||||
if (biome != null)
|
||||
if (connection.Locations.Any(l => l.IsGateBetweenBiomes))
|
||||
{
|
||||
minDifficulty = connection.Biome.MinDifficulty;
|
||||
maxDifficulty = connection.Biome.MaxDifficulty;
|
||||
if (connection.Locked)
|
||||
{
|
||||
connection.Difficulty = maxDifficulty;
|
||||
}
|
||||
connection.Difficulty = connection.Locations.Min(l => l.Biome.MaxDifficulty);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.Difficulty = CalculateDifficulty(connection.CenterPos.X, connection.Biome);
|
||||
}
|
||||
connection.Difficulty = MathHelper.Clamp(difficulty, minDifficulty, maxDifficulty);
|
||||
}
|
||||
|
||||
CreateEndLocation();
|
||||
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
location.LevelData = new LevelData(location, MathHelper.Clamp(location.MapPosition.X / Width * 100, 0.0f, 100.0f));
|
||||
location.LevelData = new LevelData(location, CalculateDifficulty(location.MapPosition.X, location.Biome));
|
||||
}
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
connection.LevelData = new LevelData(connection);
|
||||
}
|
||||
|
||||
float CalculateDifficulty(float mapPosition, Biome biome)
|
||||
{
|
||||
float settingsFactor = settings.LevelDifficultyMultiplier;
|
||||
float minDifficulty = 0;
|
||||
float maxDifficulty = 100;
|
||||
float difficulty = mapPosition / Width * 100;
|
||||
System.Diagnostics.Debug.Assert(biome != null);
|
||||
if (biome != null)
|
||||
{
|
||||
minDifficulty = biome.MinDifficulty;
|
||||
maxDifficulty = biome.MaxDifficulty;
|
||||
float diff = 1 - settingsFactor;
|
||||
difficulty *= 1 - (1f / biome.AllowedZones.Max() * diff);
|
||||
}
|
||||
return MathHelper.Clamp(difficulty, minDifficulty, maxDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
partial void GenerateLocationConnectionVisuals();
|
||||
@@ -633,6 +664,11 @@ namespace Barotrauma
|
||||
|
||||
if (EndLocation == null || previousToEndLocation == null) { return; }
|
||||
|
||||
if (LocationType.Prefabs.TryGet("none", out LocationType locationType))
|
||||
{
|
||||
previousToEndLocation.ChangeType(locationType);
|
||||
}
|
||||
|
||||
//remove all locations from the end biome except the end location
|
||||
for (int i = Locations.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -652,7 +688,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//removed all connections from the second-to-last location, need to reconnect it
|
||||
if (!previousToEndLocation.Connections.Any())
|
||||
if (previousToEndLocation.Connections.None())
|
||||
{
|
||||
Location connectTo = Locations.First();
|
||||
foreach (Location location in Locations)
|
||||
@@ -759,6 +795,7 @@ namespace Barotrauma
|
||||
CurrentLocation = Locations[index];
|
||||
CurrentLocation.Discover();
|
||||
|
||||
CurrentLocation.CreateStores();
|
||||
if (prevLocation != CurrentLocation)
|
||||
{
|
||||
var connection = CurrentLocation.Connections.Find(c => c.Locations.Contains(prevLocation));
|
||||
@@ -766,10 +803,8 @@ namespace Barotrauma
|
||||
{
|
||||
connection.Passed = true;
|
||||
}
|
||||
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
|
||||
}
|
||||
|
||||
CurrentLocation.CreateStores();
|
||||
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
|
||||
}
|
||||
|
||||
public void SelectLocation(int index)
|
||||
@@ -789,6 +824,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
Location prevSelected = SelectedLocation;
|
||||
SelectedLocation = Locations[index];
|
||||
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation();
|
||||
SelectedConnection =
|
||||
@@ -798,7 +834,10 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("A locked connection was selected - this should not be possible.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
if (prevSelected != SelectedLocation)
|
||||
{
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectLocation(Location location)
|
||||
@@ -811,13 +850,17 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
Location prevSelected = SelectedLocation;
|
||||
SelectedLocation = location;
|
||||
SelectedConnection = Connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
|
||||
if (SelectedConnection?.Locked ?? false)
|
||||
{
|
||||
DebugConsole.ThrowError("A locked connection was selected - this should not be possible.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
if (prevSelected != SelectedLocation)
|
||||
{
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectMission(IEnumerable<int> missionIndices)
|
||||
@@ -830,23 +873,24 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentLocation.SetSelectedMissionIndices(missionIndices);
|
||||
|
||||
foreach (Mission selectedMission in CurrentLocation.SelectedMissions.ToList())
|
||||
if (!missionIndices.SequenceEqual(GetSelectedMissionIndices()))
|
||||
{
|
||||
if (selectedMission.Locations[0] != CurrentLocation ||
|
||||
selectedMission.Locations[1] != CurrentLocation)
|
||||
CurrentLocation.SetSelectedMissionIndices(missionIndices);
|
||||
foreach (Mission selectedMission in CurrentLocation.SelectedMissions.ToList())
|
||||
{
|
||||
if (SelectedConnection == null) { return; }
|
||||
//the destination must be the same as the destination of the mission
|
||||
if (selectedMission.Locations[1] != SelectedLocation)
|
||||
if (selectedMission.Locations[0] != CurrentLocation ||
|
||||
selectedMission.Locations[1] != CurrentLocation)
|
||||
{
|
||||
CurrentLocation.DeselectMission(selectedMission);
|
||||
if (SelectedConnection == null) { return; }
|
||||
//the destination must be the same as the destination of the mission
|
||||
if (selectedMission.Locations[1] != SelectedLocation)
|
||||
{
|
||||
CurrentLocation.DeselectMission(selectedMission);
|
||||
}
|
||||
}
|
||||
}
|
||||
OnMissionsSelected?.Invoke(SelectedConnection, CurrentLocation.SelectedMissions);
|
||||
}
|
||||
|
||||
OnMissionsSelected?.Invoke(SelectedConnection, CurrentLocation.SelectedMissions);
|
||||
}
|
||||
|
||||
public void SelectRandomLocation(bool preferUndiscovered)
|
||||
@@ -1070,9 +1114,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Load a previously saved map from an xml element
|
||||
/// </summary>
|
||||
public static Map Load(CampaignMode campaign, XElement element, CampaignSettings settings)
|
||||
public static Map Load(CampaignMode campaign, XElement element)
|
||||
{
|
||||
Map map = new Map(campaign, element, settings);
|
||||
Map map = new Map(campaign, element);
|
||||
map.LoadState(element, false);
|
||||
#if CLIENT
|
||||
map.DrawOffset = -map.CurrentLocation.MapPosition;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class BeaconStationInfo : ISerializableEntity
|
||||
{
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool AllowDamagedWalls { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool AllowDisconnectedWires { get; set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable]
|
||||
public float MinLevelDifficulty { get; set; }
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes), Editable]
|
||||
public float MaxLevelDifficulty { get; set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
public BeaconStationInfo(SubmarineInfo submarineInfo, XElement element)
|
||||
{
|
||||
Name = $"BeaconStationInfo ({submarineInfo.Name})";
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
|
||||
public BeaconStationInfo(SubmarineInfo submarineInfo)
|
||||
{
|
||||
Name = $"BeaconStationInfo ({submarineInfo.Name})";
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this);
|
||||
}
|
||||
|
||||
public BeaconStationInfo(BeaconStationInfo original)
|
||||
{
|
||||
Name = original.Name;
|
||||
SerializableProperties = new Dictionary<Identifier, SerializableProperty>();
|
||||
foreach (KeyValuePair<Identifier, SerializableProperty> kvp in original.SerializableProperties)
|
||||
{
|
||||
SerializableProperties.Add(kvp.Key, kvp.Value);
|
||||
if (SerializableProperty.GetSupportedTypeName(kvp.Value.PropertyType) != null)
|
||||
{
|
||||
kvp.Value.TrySetValue(this, kvp.Value.GetValue(original));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(XElement element)
|
||||
{
|
||||
SerializableProperty.SerializeProperties(this, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -830,43 +830,44 @@ namespace Barotrauma
|
||||
|
||||
private static SubmarineInfo GetRandomModule(OutpostModuleInfo prevModule, IEnumerable<SubmarineInfo> modules, Identifier moduleFlag, OutpostModuleInfo.GapPosition gapPosition, LocationType locationType, bool allowDifferentLocationType)
|
||||
{
|
||||
IEnumerable<SubmarineInfo> availableModules = null;
|
||||
IEnumerable<SubmarineInfo> modulesWithCorrectFlags = null;
|
||||
if (moduleFlag.IsEmpty || moduleFlag.Equals("none"))
|
||||
{
|
||||
availableModules = modules
|
||||
modulesWithCorrectFlags = modules
|
||||
.Where(m => !m.OutpostModuleInfo.ModuleFlags.Any() || (m.OutpostModuleInfo.ModuleFlags.Count() == 1 && m.OutpostModuleInfo.ModuleFlags.Contains("none".ToIdentifier())));
|
||||
}
|
||||
else
|
||||
{
|
||||
availableModules = modules
|
||||
modulesWithCorrectFlags = modules
|
||||
.Where(m => m.OutpostModuleInfo.ModuleFlags.Contains(moduleFlag));
|
||||
}
|
||||
modulesWithCorrectFlags = modulesWithCorrectFlags.Where(m => m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition) && m.OutpostModuleInfo.CanAttachToPrevious.HasFlag(gapPosition));
|
||||
|
||||
availableModules = availableModules.Where(m => m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition) && m.OutpostModuleInfo.CanAttachToPrevious.HasFlag(gapPosition));
|
||||
|
||||
if (prevModule != null)
|
||||
var suitableModules = GetSuitable(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true);
|
||||
if (!suitableModules.Any())
|
||||
{
|
||||
availableModules = availableModules.Where(m => CanAttachTo(m.OutpostModuleInfo, prevModule));// && CanAttachTo(prevModule, m.OutpostModuleInfo));
|
||||
//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);
|
||||
//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);
|
||||
}
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
if (availableModules.Count() == 0) { return null; }
|
||||
|
||||
//try to search for modules made specifically for this location type first
|
||||
var modulesSuitableForLocationType =
|
||||
availableModules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier));
|
||||
|
||||
//if not found, search for modules suitable for any location type
|
||||
if (allowDifferentLocationType && !modulesSuitableForLocationType.Any())
|
||||
{
|
||||
modulesSuitableForLocationType = availableModules.Where(m => !m.OutpostModuleInfo.AllowedLocationTypes.Any());
|
||||
}
|
||||
|
||||
if (!modulesSuitableForLocationType.Any())
|
||||
if (!suitableModules.Any())
|
||||
{
|
||||
if (allowDifferentLocationType)
|
||||
{
|
||||
if (modulesWithCorrectFlags.Any())
|
||||
|
||||
DebugConsole.NewMessage($"Could not find a suitable module for the location type {locationType}. Module flag: {moduleFlag}.", Color.Orange);
|
||||
return ToolBox.SelectWeightedRandom(availableModules.ToList(), availableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
|
||||
return ToolBox.SelectWeightedRandom(modulesWithCorrectFlags.ToList(), modulesWithCorrectFlags.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -875,7 +876,28 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
return ToolBox.SelectWeightedRandom(modulesSuitableForLocationType.ToList(), modulesSuitableForLocationType.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
|
||||
return ToolBox.SelectWeightedRandom(suitableModules.ToList(), suitableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
IEnumerable<SubmarineInfo> GetSuitable(IEnumerable<SubmarineInfo> modules, bool requireAllowAttachToPrevious, bool requireCorrectLocationType, bool disallowNonLocationTypeSpecific)
|
||||
{
|
||||
IEnumerable<SubmarineInfo> suitable = modules;
|
||||
if (requireCorrectLocationType)
|
||||
{
|
||||
if (disallowNonLocationTypeSpecific)
|
||||
{
|
||||
suitable = modules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
suitable = modules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier) || !m.OutpostModuleInfo.AllowedLocationTypes.Any());
|
||||
}
|
||||
}
|
||||
if (requireAllowAttachToPrevious && prevModule != null)
|
||||
{
|
||||
suitable = suitable.Where(m => CanAttachTo(m.OutpostModuleInfo, prevModule));
|
||||
}
|
||||
return suitable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,10 +1612,6 @@ namespace Barotrauma
|
||||
{
|
||||
npc.CharacterHealth.Unkillable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
npc.AddStaticHealthMultiplier(humanPrefab.HealthMultiplier);
|
||||
}
|
||||
humanPrefab.GiveItems(npc, outpost, Rand.RandSync.ServerAndClient);
|
||||
foreach (Item item in npc.Inventory.FindAllItems(it => it != null, recursive: true))
|
||||
{
|
||||
|
||||
@@ -1483,8 +1483,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (item.Submarine != this) continue;
|
||||
if (item.ParentInventory != null || item.body != null) continue;
|
||||
var lightComponent = item.GetComponent<Items.Components.LightComponent>();
|
||||
if (lightComponent != null) lightComponent.LightColor = new Color(lightComponent.LightColor, lightComponent.LightColor.A / 255.0f * 0.5f);
|
||||
foreach (var light in item.GetComponents<LightComponent>())
|
||||
{
|
||||
light.LightColor = new Color(light.LightColor, light.LightColor.A / 255.0f * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
GenerateOutdoorNodes();
|
||||
@@ -1555,7 +1557,7 @@ namespace Barotrauma
|
||||
element.Add(new XAttribute("cargocapacity", cargoCapacity));
|
||||
element.Add(new XAttribute("recommendedcrewsizemin", Info.RecommendedCrewSizeMin));
|
||||
element.Add(new XAttribute("recommendedcrewsizemax", Info.RecommendedCrewSizeMax));
|
||||
element.Add(new XAttribute("recommendedcrewexperience", Info.RecommendedCrewExperience ?? ""));
|
||||
element.Add(new XAttribute("recommendedcrewexperience", Info.RecommendedCrewExperience.ToString()));
|
||||
element.Add(new XAttribute("requiredcontentpackages", string.Join(", ", Info.RequiredContentPackages)));
|
||||
|
||||
if (Info.Type == SubmarineType.OutpostModule)
|
||||
@@ -1632,6 +1634,7 @@ namespace Barotrauma
|
||||
Type = Info.Type,
|
||||
FilePath = filePath,
|
||||
OutpostModuleInfo = Info.OutpostModuleInfo != null ? new OutpostModuleInfo(Info.OutpostModuleInfo) : null,
|
||||
BeaconStationInfo = Info.BeaconStationInfo != null ? new BeaconStationInfo(Info.BeaconStationInfo) : null,
|
||||
Name = Path.GetFileNameWithoutExtension(filePath)
|
||||
};
|
||||
#if CLIENT
|
||||
|
||||
@@ -39,7 +39,15 @@ namespace Barotrauma
|
||||
public SubmarineTag Tags { get; private set; }
|
||||
|
||||
public int RecommendedCrewSizeMin = 1, RecommendedCrewSizeMax = 2;
|
||||
public string RecommendedCrewExperience;
|
||||
|
||||
public enum CrewExperienceLevel
|
||||
{
|
||||
Unknown,
|
||||
CrewExperienceLow,
|
||||
CrewExperienceMid,
|
||||
CrewExperienceHigh
|
||||
}
|
||||
public CrewExperienceLevel RecommendedCrewExperience;
|
||||
|
||||
/// <summary>
|
||||
/// A random int that gets assigned when saving the sub. Used in mp campaign to verify that sub files match
|
||||
@@ -89,6 +97,7 @@ namespace Barotrauma
|
||||
public SubmarineClass SubmarineClass;
|
||||
|
||||
public OutpostModuleInfo OutpostModuleInfo { get; set; }
|
||||
public BeaconStationInfo BeaconStationInfo { get; set; }
|
||||
|
||||
public bool IsOutpost => Type == SubmarineType.Outpost || Type == SubmarineType.OutpostModule;
|
||||
|
||||
@@ -280,6 +289,10 @@ namespace Barotrauma
|
||||
{
|
||||
OutpostModuleInfo = new OutpostModuleInfo(original.OutpostModuleInfo);
|
||||
}
|
||||
if (original.BeaconStationInfo != null)
|
||||
{
|
||||
BeaconStationInfo = new BeaconStationInfo(original.BeaconStationInfo);
|
||||
}
|
||||
#if CLIENT
|
||||
PreviewImage = original.PreviewImage != null ? new Sprite(original.PreviewImage) : null;
|
||||
#endif
|
||||
@@ -330,7 +343,24 @@ namespace Barotrauma
|
||||
CargoCapacity = SubmarineElement.GetAttributeInt("cargocapacity", -1);
|
||||
RecommendedCrewSizeMin = SubmarineElement.GetAttributeInt("recommendedcrewsizemin", 0);
|
||||
RecommendedCrewSizeMax = SubmarineElement.GetAttributeInt("recommendedcrewsizemax", 0);
|
||||
RecommendedCrewExperience = SubmarineElement.GetAttributeString("recommendedcrewexperience", "Unknown");
|
||||
var recommendedCrewExperience = SubmarineElement.GetAttributeIdentifier("recommendedcrewexperience", CrewExperienceLevel.Unknown.ToIdentifier());
|
||||
// Backwards compatibility
|
||||
if (recommendedCrewExperience == "Beginner")
|
||||
{
|
||||
RecommendedCrewExperience = CrewExperienceLevel.CrewExperienceLow;
|
||||
}
|
||||
else if (recommendedCrewExperience == "Intermediate")
|
||||
{
|
||||
RecommendedCrewExperience = CrewExperienceLevel.CrewExperienceMid;
|
||||
}
|
||||
else if (recommendedCrewExperience == "Experienced")
|
||||
{
|
||||
RecommendedCrewExperience = CrewExperienceLevel.CrewExperienceHigh;
|
||||
}
|
||||
else
|
||||
{
|
||||
Enum.TryParse(recommendedCrewExperience.Value, ignoreCase: true, out RecommendedCrewExperience);
|
||||
}
|
||||
|
||||
if (SubmarineElement?.Attribute("type") != null)
|
||||
{
|
||||
@@ -341,6 +371,10 @@ namespace Barotrauma
|
||||
{
|
||||
OutpostModuleInfo = new OutpostModuleInfo(this, SubmarineElement);
|
||||
}
|
||||
else if (Type == SubmarineType.BeaconStation)
|
||||
{
|
||||
BeaconStationInfo = new BeaconStationInfo(this, SubmarineElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,20 +393,6 @@ namespace Barotrauma
|
||||
SubmarineClass = SubmarineClass.Undefined;
|
||||
}
|
||||
|
||||
//backwards compatibility (use text tags instead of the actual text)
|
||||
if (RecommendedCrewExperience == "Beginner")
|
||||
{
|
||||
RecommendedCrewExperience = "CrewExperienceLow";
|
||||
}
|
||||
else if (RecommendedCrewExperience == "Intermediate")
|
||||
{
|
||||
RecommendedCrewExperience = "CrewExperienceMid";
|
||||
}
|
||||
else if (RecommendedCrewExperience == "Experienced")
|
||||
{
|
||||
RecommendedCrewExperience = "CrewExperienceHigh";
|
||||
}
|
||||
|
||||
RequiredContentPackages.Clear();
|
||||
string[] contentPackageNames = SubmarineElement.GetAttributeStringArray("requiredcontentpackages", Array.Empty<string>());
|
||||
foreach (string contentPackageName in contentPackageNames)
|
||||
@@ -528,6 +548,11 @@ namespace Barotrauma
|
||||
OutpostModuleInfo.Save(newElement);
|
||||
OutpostModuleInfo = new OutpostModuleInfo(this, newElement);
|
||||
}
|
||||
else if (Type == SubmarineType.BeaconStation)
|
||||
{
|
||||
BeaconStationInfo.Save(newElement);
|
||||
BeaconStationInfo = new BeaconStationInfo(this, newElement);
|
||||
}
|
||||
XDocument doc = new XDocument(newElement);
|
||||
|
||||
doc.Root.Add(new XAttribute("name", Name));
|
||||
@@ -590,6 +615,7 @@ namespace Barotrauma
|
||||
List<string> filePaths = new List<string>();
|
||||
foreach (BaseSubFile subFile in contentPackageSubs)
|
||||
{
|
||||
if (!File.Exists(subFile.Path.Value)) { continue; }
|
||||
if (!filePaths.Any(fp => fp == subFile.Path))
|
||||
{
|
||||
filePaths.Add(subFile.Path.Value);
|
||||
|
||||
Reference in New Issue
Block a user