(965c31410) v0.10.4.0

This commit is contained in:
Joonas Rikkonen
2020-07-30 13:00:09 +03:00
parent eeac247a8e
commit 4978af3d60
539 changed files with 45803 additions and 25359 deletions
@@ -9,16 +9,24 @@ namespace Barotrauma
{
partial class Map
{
private MapGenerationParams generationParams;
private readonly int size;
private readonly MapGenerationParams generationParams;
private Location furthestDiscoveredLocation;
private int Width => generationParams.Width;
private int Height => generationParams.Height;
private List<LocationConnection> connections;
public Action<Location, LocationConnection> OnLocationSelected;
//from -> to
/// <summary>
/// From -> To
/// </summary>
public Action<Location, Location> OnLocationChanged;
public Action<LocationConnection, Mission> OnMissionSelected;
public Location EndLocation { get; private set; }
public Location StartLocation { get; private set; }
public Location CurrentLocation { get; private set; }
public int CurrentLocationIndex
@@ -44,140 +52,176 @@ namespace Barotrauma
public List<Location> Locations { get; private set; }
public Map(string seed)
public List<LocationConnection> Connections { get; private set; }
public Map()
{
generationParams = MapGenerationParams.Instance;
this.Seed = seed;
this.size = generationParams.Size;
Locations = new List<Location>();
Connections = new List<LocationConnection>();
}
connections = new List<LocationConnection>();
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
Generate();
//start from the colony furthest away from the center
float largestDist = 0.0f;
Vector2 center = new Vector2(size, size) / 2;
foreach (Location location in Locations)
/// <summary>
/// Load a previously saved campaign map from XML
/// </summary>
private Map(CampaignMode campaign, XElement element) : this()
{
Seed = element.GetAttributeString("seed", "a");
foreach (XElement subElement in element.Elements())
{
if (location.Type.Identifier != "City") continue;
float dist = Vector2.DistanceSquared(center, location.MapPosition);
if (dist > largestDist)
switch (subElement.Name.ToString().ToLowerInvariant())
{
largestDist = dist;
CurrentLocation = location;
case "location":
int i = subElement.GetAttributeInt("i", 0);
while (Locations.Count <= i)
{
Locations.Add(null);
}
Locations[i] = new Location(subElement);
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
break;
}
}
CurrentLocation.Discovered = true;
System.Diagnostics.Debug.Assert(!Locations.Contains(null));
foreach (LocationConnection connection in connections)
foreach (XElement subElement in element.Elements())
{
connection.Level = Level.CreateRandom(connection);
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "connection":
Point locationIndices = subElement.GetAttributePoint("locations", new Point(0, 1));
var connection = new LocationConnection(Locations[locationIndices.X], Locations[locationIndices.Y])
{
Passed = subElement.GetAttributeBool("passed", false),
Difficulty = subElement.GetAttributeFloat("difficulty", 0.0f)
};
Locations[locationIndices.X].Connections.Add(connection);
Locations[locationIndices.Y].Connections.Add(connection);
connection.LevelData = new LevelData(subElement.Element("Level"));
string biomeId = subElement.GetAttributeString("biome", "");
connection.Biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.Identifier == biomeId) ?? LevelGenerationParams.GetBiomes().First();
Connections.Add(connection);
break;
}
}
int startLocationindex = element.GetAttributeInt("startlocation", -1);
if (startLocationindex > 0 && startLocationindex < Locations.Count)
{
StartLocation = Locations[startLocationindex];
}
else
{
DebugConsole.ThrowError("Error while loading the map (start location index out of bounds).");
}
int endLocationindex = element.GetAttributeInt("endlocation", -1);
if (endLocationindex > 0 && endLocationindex < Locations.Count)
{
EndLocation = Locations[endLocationindex];
}
else
{
DebugConsole.ThrowError("Error while loading the map (end location index out of bounds).");
foreach (Location location in Locations)
{
if (EndLocation == null || location.MapPosition.X > EndLocation.MapPosition.X)
{
EndLocation = location;
}
}
}
InitProjectSpecific();
}
partial void InitProjectSpecific();
public float[,] Noise;
private void GenerateNoiseMap(int octaves, float persistence)
/// <summary>
/// Generate a new campaign map from the seed
/// </summary>
public Map(CampaignMode campaign, string seed) : this()
{
float z = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
Noise = new float[generationParams.NoiseResolution, generationParams.NoiseResolution];
float min = float.MaxValue, max = 0.0f;
for (int x = 0; x < generationParams.NoiseResolution; x++)
Seed = seed;
Rand.SetSyncedSeed(ToolBox.StringToInt(Seed));
Generate();
if (Locations.Count == 0)
{
for (int y = 0; y < generationParams.NoiseResolution; y++)
throw new Exception($"Generating a campaign map failed (no locations created). Width: {Width}, height: {Height}");
}
for (int i = 0; i < Locations.Count; i++)
{
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
}
foreach (Location location in Locations)
{
if (!location.Type.Identifier.Equals("city", StringComparison.OrdinalIgnoreCase) &&
!location.Type.Identifier.Equals("outpost", StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
{
Noise[x, y] = (float)PerlinNoise.OctavePerlin(
(double)x / generationParams.NoiseResolution,
(double)y / generationParams.NoiseResolution,
z, generationParams.NoiseFrequency, octaves, persistence);
min = Math.Min(Noise[x, y], min);
max = Math.Max(Noise[x, y], max);
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
}
}
float radius = generationParams.NoiseResolution / 2;
Vector2 center = Vector2.One * radius;
float range = max - min;
float centerDarkenRadius = radius * generationParams.CenterDarkenRadius;
float edgeDarkenRadius = radius * generationParams.EdgeDarkenRadius;
for (int x = 0; x < generationParams.NoiseResolution; x++)
{
for (int y = 0; y < generationParams.NoiseResolution; y++)
{
//normalize the noise to 0-1 range
Noise[x, y] = (Noise[x, y] - min) / range;
CurrentLocation.CreateStore();
CurrentLocation.Discovered = true;
float dist = Vector2.Distance(center, new Vector2(x, y));
if (dist < centerDarkenRadius)
{
float angle = (float)Math.Atan2(y - center.Y, x - center.X);
float phase = angle * generationParams.CenterDarkenWaveFrequency + Noise[x, y] * generationParams.CenterDarkenWavePhaseNoise;
float currDarkenRadius = centerDarkenRadius * (0.6f + (float)Math.Sin(phase) * 0.4f);
if (dist < currDarkenRadius)
{
float darkenAmount = 1.0f - (dist / currDarkenRadius);
Noise[x, y] = MathHelper.Lerp(Noise[x, y], Noise[x, y] * (1.0f - generationParams.CenterDarkenStrength), darkenAmount);
}
}
if (dist > edgeDarkenRadius)
{
float darkenAmount = Math.Min((dist - edgeDarkenRadius) / (radius - edgeDarkenRadius), 1.0f);
Noise[x, y] = MathHelper.Lerp(Noise[x, y], 1.0f - generationParams.EdgeDarkenStrength, darkenAmount);
}
}
}
InitProjectSpecific();
}
partial void GenerateNoiseMapProjSpecific();
partial void InitProjectSpecific();
#region Generation
private void Generate()
{
connections.Clear();
Connections.Clear();
Locations.Clear();
GenerateNoiseMap(generationParams.NoiseOctaves, generationParams.NoisePersistence);
List<Vector2> sites = new List<Vector2>();
float mapRadius = size / 2;
Vector2 mapCenter = new Vector2(mapRadius, mapRadius);
float locationRadius = mapRadius * generationParams.LocationRadius;
for (float x = mapCenter.X - locationRadius; x < mapCenter.X + locationRadius; x += generationParams.VoronoiSiteInterval)
List<Vector2> voronoiSites = new List<Vector2>();
for (float x = 10.0f; x < Width - 10.0f; x += generationParams.VoronoiSiteInterval.X)
{
for (float y = mapCenter.Y - locationRadius; y < mapCenter.Y + locationRadius; y += generationParams.VoronoiSiteInterval)
for (float y = 10.0f; y < Height - 10.0f; y += generationParams.VoronoiSiteInterval.Y)
{
float noiseVal = Noise[(int)(x / size * generationParams.NoiseResolution), (int)(y / size * generationParams.NoiseResolution)];
if (Rand.Range(generationParams.VoronoiSitePlacementMinVal, 1.0f, Rand.RandSync.Server) <
noiseVal * generationParams.VoronoiSitePlacementProbability)
{
sites.Add(new Vector2(x, y));
}
voronoiSites.Add(new Vector2(
x + generationParams.VoronoiSiteVariance.X * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server),
y + generationParams.VoronoiSiteVariance.Y * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server)));
}
}
Voronoi voronoi = new Voronoi(0.5f);
List<GraphEdge> edges = voronoi.MakeVoronoiGraph(sites, size, size);
float zoneRadius = size / 2 / generationParams.DifficultyZones;
List<GraphEdge> edges = voronoi.MakeVoronoiGraph(voronoiSites, Width, Height);
float zoneWidth = Width / generationParams.DifficultyZones;
sites.Clear();
Vector2 margin = new Vector2(
Math.Min(10, Width * 0.1f),
Math.Min(10, Height * 0.2f));
float startX = margin.X, endX = Width - margin.X;
float startY = margin.Y, endY = Height - margin.Y;
if (!edges.Any())
{
throw new Exception($"Generating a campaign map failed (no edges in the voronoi graph). Width: {Width}, height: {Height}, margin: {margin}");
}
voronoiSites.Clear();
foreach (GraphEdge edge in edges)
{
if (edge.Point1 == edge.Point2) continue;
if (Vector2.DistanceSquared(edge.Point1, mapCenter) >= locationRadius * locationRadius ||
Vector2.DistanceSquared(edge.Point2, mapCenter) >= locationRadius * locationRadius) continue;
if (edge.Point1 == edge.Point2) { continue; }
if (edge.Point1.X < margin.X || edge.Point1.X > Width - margin.X || edge.Point1.Y < startY || edge.Point1.Y > endY)
{
continue;
}
if (edge.Point2.X < margin.X || edge.Point2.X > Width - margin.X || edge.Point2.Y < startY || edge.Point2.Y > endY)
{
continue;
}
Location[] newLocations = new Location[2];
newLocations[0] = Locations.Find(l => l.MapPosition == edge.Point1 || l.MapPosition == edge.Point2);
@@ -193,23 +237,20 @@ namespace Barotrauma
Vector2 position = points[positionIndex];
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) position = points[1 - positionIndex];
int zone = MathHelper.Clamp(generationParams.DifficultyZones - (int)Math.Floor(Vector2.Distance(position, mapCenter) / zoneRadius), 1, generationParams.DifficultyZones);
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.Server));
int zone = MathHelper.Clamp((int)Math.Floor(position.X / zoneWidth) + 1, 1, generationParams.DifficultyZones);
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.Server), requireOutpost: false, Locations);
Locations.Add(newLocations[i]);
}
var newConnection = new LocationConnection(newLocations[0], newLocations[1]);
float centerDist = Vector2.Distance(newConnection.CenterPos, mapCenter);
newConnection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 10.0f, Rand.RandSync.Server), 0, 100);
connections.Add(newConnection);
Connections.Add(newConnection);
}
//remove connections that are too short
float minConnectionDistanceSqr = generationParams.MinConnectionDistance * generationParams.MinConnectionDistance;
for (int i = connections.Count - 1; i >= 0; i--)
for (int i = Connections.Count - 1; i >= 0; i--)
{
LocationConnection connection = connections[i];
LocationConnection connection = Connections[i];
if (Vector2.DistanceSquared(connection.Locations[0].MapPosition, connection.Locations[1].MapPosition) > minConnectionDistanceSqr)
{
@@ -217,17 +258,17 @@ namespace Barotrauma
}
//locations.Remove(connection.Locations[0]);
connections.Remove(connection);
Connections.Remove(connection);
foreach (LocationConnection connection2 in connections)
foreach (LocationConnection connection2 in Connections)
{
if (connection2.Locations[0] == connection.Locations[0]) connection2.Locations[0] = connection.Locations[1];
if (connection2.Locations[1] == connection.Locations[0]) connection2.Locations[1] = connection.Locations[1];
if (connection2.Locations[0] == connection.Locations[0]) { connection2.Locations[0] = connection.Locations[1]; }
if (connection2.Locations[1] == connection.Locations[0]) { connection2.Locations[1] = connection.Locations[1]; }
}
}
HashSet<Location> connectedLocations = new HashSet<Location>();
foreach (LocationConnection connection in connections)
foreach (LocationConnection connection in Connections)
{
connection.Locations[0].Connections.Add(connection);
connection.Locations[1].Connections.Add(connection);
@@ -263,57 +304,182 @@ namespace Barotrauma
}
Locations[i].Connections.Add(connection);
}
Locations[i].Connections.RemoveAll(c => c.OtherLocation(Locations[i]) == Locations[j]);
Locations.RemoveAt(j);
}
}
for (int i = connections.Count - 1; i >= 0; i--)
for (int i = Connections.Count - 1; i >= 0; i--)
{
i = Math.Min(i, connections.Count - 1);
LocationConnection connection = connections[i];
for (int n = Math.Min(i - 1, connections.Count - 1); n >= 0; n--)
i = Math.Min(i, Connections.Count - 1);
LocationConnection connection = Connections[i];
for (int n = Math.Min(i - 1, Connections.Count - 1); n >= 0; n--)
{
if (connection.Locations.Contains(connections[n].Locations[0])
&& connection.Locations.Contains(connections[n].Locations[1]))
if (connection.Locations.Contains(Connections[n].Locations[0])
&& connection.Locations.Contains(Connections[n].Locations[1]))
{
connections.RemoveAt(n);
Connections.RemoveAt(n);
}
}
}
foreach (LocationConnection connection in connections)
foreach (Location location in Locations)
{
float centerDist = Vector2.Distance(connection.CenterPos, mapCenter);
connection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 0, 100);
for (int i = location.Connections.Count - 1; i >= 0; i--)
{
if (!Connections.Contains(location.Connections[i]))
{
location.Connections.RemoveAt(i);
}
}
}
foreach (LocationConnection connection in Connections)
{
connection.Difficulty = MathHelper.Clamp((connection.CenterPos.X / Width * 100) + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 1.2f, 100.0f);
}
AssignBiomes();
CreateEndLocation();
GenerateNoiseMapProjSpecific();
foreach (Location location in Locations)
{
location.LevelData = new LevelData(location);
location.NormalizedDepth = location.MapPosition.X / Width;
}
foreach (LocationConnection connection in Connections)
{
connection.LevelData = new LevelData(connection);
}
}
partial void GenerateLocationConnectionVisuals();
public Biome GetBiome(Vector2 mapPos)
{
return GetBiome(mapPos.X);
}
public Biome GetBiome(float xPos)
{
float zoneWidth = Width / generationParams.DifficultyZones;
int zoneIndex = (int)Math.Floor(xPos / zoneWidth) + 1;
if (zoneIndex < 1)
{
return LevelGenerationParams.GetBiomes().First();
}
else if (zoneIndex >= generationParams.DifficultyZones)
{
return LevelGenerationParams.GetBiomes().Last();
}
return LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.AllowedZones.Contains(zoneIndex));
}
private void AssignBiomes()
{
float locationRadius = size * 0.5f * generationParams.LocationRadius;
var biomes = LevelGenerationParams.GetBiomes();
Vector2 centerPos = new Vector2(size, size) / 2;
float zoneWidth = Width / generationParams.DifficultyZones;
List<Biome> allowedBiomes = new List<Biome>(10);
for (int i = 0; i < generationParams.DifficultyZones; i++)
{
List<Biome> allowedBiomes = biomes.FindAll(b => b.AllowedZones.Contains(generationParams.DifficultyZones - i));
float zoneRadius = locationRadius * ((i + 1.0f) / generationParams.DifficultyZones);
foreach (LocationConnection connection in connections)
{
if (connection.Biome != null) continue;
allowedBiomes.Clear();
allowedBiomes.AddRange(biomes.Where(b => b.AllowedZones.Contains(generationParams.DifficultyZones - i)));
float zoneX = Width - zoneWidth * i;
if (i == generationParams.DifficultyZones - 1 ||
Vector2.Distance(connection.Locations[0].MapPosition, centerPos) < zoneRadius ||
Vector2.Distance(connection.Locations[1].MapPosition, centerPos) < zoneRadius)
foreach (Location location in Locations)
{
if (location.MapPosition.X < zoneX)
{
connection.Biome = allowedBiomes[Rand.Range(0, allowedBiomes.Count, Rand.RandSync.Server)];
location.Biome = allowedBiomes[Rand.Range(0, allowedBiomes.Count, Rand.RandSync.Server)];
}
}
}
foreach (LocationConnection connection in Connections)
{
if (connection.Biome != null) { continue; }
connection.Biome = connection.Locations[0].Biome;
}
System.Diagnostics.Debug.Assert(Locations.All(l => l.Biome != null));
System.Diagnostics.Debug.Assert(Connections.All(c => c.Biome != null));
}
private void CreateEndLocation()
{
float zoneWidth = Width / generationParams.DifficultyZones;
Vector2 endPos = new Vector2(Width - zoneWidth / 2, Height / 2);
float closestDist = float.MaxValue;
EndLocation = Locations.First();
foreach (Location location in Locations)
{
float dist = Vector2.DistanceSquared(endPos, location.MapPosition);
if (location.Biome.IsEndBiome && dist < closestDist)
{
EndLocation = location;
closestDist = dist;
}
}
Location previousToEndLocation = null;
foreach (Location location in Locations)
{
if (!location.Biome.IsEndBiome && (previousToEndLocation == null || location.MapPosition.X > previousToEndLocation.MapPosition.X))
{
previousToEndLocation = location;
}
}
if (EndLocation == null || previousToEndLocation == null) { return; }
//remove all locations from the end biome except the end location
for (int i = Locations.Count - 1; i >= 0; i--)
{
if (Locations[i].Biome.IsEndBiome && Locations[i] != EndLocation)
{
for (int j = Locations[i].Connections.Count - 1; j >= 0; j--)
{
if (j >= Locations[i].Connections.Count) { continue; }
var connection = Locations[i].Connections[j];
var otherLocation = connection.OtherLocation(Locations[i]);
Locations[i].Connections.RemoveAt(j);
otherLocation?.Connections.Remove(connection);
Connections.Remove(connection);
}
Locations.RemoveAt(i);
}
}
//removed all connections from the second-to-last location, need to reconnect it
if (!previousToEndLocation.Connections.Any())
{
Location connectTo = Locations.First();
foreach (Location location in Locations)
{
if (!location.Biome.IsEndBiome && location != previousToEndLocation && location.MapPosition.X > connectTo.MapPosition.X)
{
connectTo = location;
}
}
var newConnection = new LocationConnection(previousToEndLocation, connectTo)
{
Biome = EndLocation.Biome,
Difficulty = 100.0f
};
Connections.Add(newConnection);
previousToEndLocation.Connections.Add(newConnection);
connectTo.Connections.Add(newConnection);
}
var endConnection = new LocationConnection(previousToEndLocation, EndLocation)
{
Biome = EndLocation.Biome,
Difficulty = 100.0f
};
Connections.Add(endConnection);
previousToEndLocation.Connections.Add(endConnection);
EndLocation.Connections.Add(endConnection);
}
private void ExpandBiomes(List<LocationConnection> seeds)
@@ -340,9 +506,22 @@ namespace Barotrauma
}
}
#endregion Generation
public void MoveToNextLocation()
{
if (SelectedConnection == null)
{
DebugConsole.ThrowError("Could not move to the next location (no connection selected).\n"+Environment.StackTrace);
return;
}
if (SelectedLocation == null)
{
DebugConsole.ThrowError("Could not move to the next location (no location selected).\n" + Environment.StackTrace);
return;
}
Location prevLocation = CurrentLocation;
SelectedConnection.Passed = true;
@@ -350,6 +529,7 @@ namespace Barotrauma
CurrentLocation.Discovered = true;
SelectedLocation = null;
CurrentLocation.CreateStore();
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
}
@@ -371,6 +551,16 @@ namespace Barotrauma
CurrentLocation = Locations[index];
CurrentLocation.Discovered = true;
if (prevLocation != CurrentLocation)
{
var connection = CurrentLocation.Connections.Find(c => c.Locations.Contains(prevLocation));
if (connection != null)
{
connection.Passed = true;
}
}
CurrentLocation.CreateStore();
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
}
@@ -392,7 +582,9 @@ namespace Barotrauma
}
SelectedLocation = Locations[index];
SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
SelectedConnection =
Connections.Find(c => c.Locations.Contains(GameMain.GameSession?.Campaign?.CurrentDisplayLocation) && c.Locations.Contains(SelectedLocation)) ??
Connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
}
@@ -407,7 +599,7 @@ namespace Barotrauma
}
SelectedLocation = location;
SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
SelectedConnection = Connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
}
@@ -427,7 +619,7 @@ namespace Barotrauma
if (CurrentLocation.SelectedMission != null &&
CurrentLocation.SelectedMission.Locations[1] != SelectedLocation)
{
SelectLocation(CurrentLocation.SelectedMission.Locations[1]);
CurrentLocation.SelectedMissionIndex = -1;
}
OnMissionSelected?.Invoke(SelectedConnection, CurrentLocation.SelectedMission);
@@ -452,7 +644,12 @@ namespace Barotrauma
{
foreach (Location location in Locations)
{
if (!location.Discovered) continue;
if (!location.Discovered) { continue; }
if (furthestDiscoveredLocation == null || location.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
{
furthestDiscoveredLocation = location;
}
//find which types of locations this one can change to
List<LocationTypeChange> allowedTypeChanges = new List<LocationTypeChange>();
@@ -469,7 +666,7 @@ namespace Barotrauma
break;
}
}
if (disallowedFound) continue;
if (disallowedFound) { continue; }
//check that there's a required adjacent location present
bool requiredFound = false;
@@ -481,7 +678,7 @@ namespace Barotrauma
break;
}
}
if (!requiredFound && typeChange.RequiredAdjacentLocations.Count > 0) continue;
if (!requiredFound && typeChange.RequiredAdjacentLocations.Count > 0) { continue; }
allowedTypeChanges.Add(typeChange);
@@ -514,22 +711,76 @@ namespace Barotrauma
{
location.TypeChangeTimer = 0;
}
location.UpdateStore();
}
}
public int DistanceToClosestLocationWithOutpost(Location startingLocation, out Location endingLocation)
{
if (startingLocation.Type.HasOutpost)
{
endingLocation = startingLocation;
return 0;
}
int iterations = 0;
int distance = 0;
endingLocation = null;
List<Location> testedLocations = new List<Location>();
List<Location> locationsToTest = new List<Location> { startingLocation };
while (endingLocation == null && iterations < 100)
{
List<Location> nextTestingBatch = new List<Location>();
for (int i = 0; i < locationsToTest.Count; i++)
{
Location testLocation = locationsToTest[i];
for (int j = 0; j < testLocation.Connections.Count; j++)
{
Location potentialOutpost = testLocation.Connections[j].OtherLocation(testLocation);
if (potentialOutpost.Type.HasOutpost)
{
distance = iterations + 1;
endingLocation = potentialOutpost;
}
else if (!testedLocations.Contains(potentialOutpost))
{
nextTestingBatch.Add(potentialOutpost);
}
}
testedLocations.Add(testLocation);
}
locationsToTest = nextTestingBatch;
iterations++;
}
return distance;
}
partial void ChangeLocationType(Location location, string prevName, LocationTypeChange change);
partial void ClearAnimQueue();
public static Map LoadNew(XElement element)
/// <summary>
/// Load a previously saved map from an xml element
/// </summary>
public static Map Load(CampaignMode campaign, XElement element)
{
string mapSeed = element.GetAttributeString("seed", "a");
Map map = new Map(mapSeed);
map.Load(element, false);
Map map = new Map(campaign, element);
map.LoadState(element, false);
#if CLIENT
map.DrawOffset = -map.CurrentLocation.MapPosition;
#endif
return map;
}
public void Load(XElement element, bool showNotifications)
/// <summary>
/// Load the state of an existing map from xml (current state of locations, where the crew is now, etc).
/// </summary>
public void LoadState(XElement element, bool showNotifications)
{
ClearAnimQueue();
SetLocation(element.GetAttributeInt("currentlocation", 0));
@@ -545,17 +796,27 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "location":
string locationType = subElement.GetAttributeString("type", "");
Location location = Locations[subElement.GetAttributeInt("i", 0)];
int typeChangeTimer = subElement.GetAttributeInt("changetimer", 0);
int missionsCompleted = subElement.GetAttributeInt("missionscompleted", 0);
location.TypeChangeTimer = subElement.GetAttributeInt("changetimer", 0);
location.Discovered = subElement.GetAttributeBool("discovered", false);
if (location.Discovered)
{
#if CLIENT
RemoveFogOfWar(StartLocation);
#endif
if (furthestDiscoveredLocation == null || location.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
{
furthestDiscoveredLocation = location;
}
}
string locationType = subElement.GetAttributeString("type", "");
string prevLocationName = location.Name;
LocationType prevLocationType = location.Type;
location.Discovered = true;
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(locationType, StringComparison.OrdinalIgnoreCase)));
location.TypeChangeTimer = typeChangeTimer;
location.MissionsCompleted = missionsCompleted;
LocationType newLocationType = LocationType.List.Find(lt => lt.Identifier.Equals(locationType, StringComparison.OrdinalIgnoreCase)) ?? LocationType.List.First();
location.ChangeType(newLocationType);
if (showNotifications && prevLocationType != location.Type)
{
var change = prevLocationType.CanChangeTo.Find(c => c.ChangeToType.Equals(location.Type.Identifier, StringComparison.OrdinalIgnoreCase));
@@ -564,13 +825,25 @@ namespace Barotrauma
ChangeLocationType(location, prevLocationName, change);
}
}
location.LoadMissions(subElement);
break;
case "connection":
int connectionIndex = subElement.GetAttributeInt("i", 0);
connections[connectionIndex].Passed = true;
Connections[connectionIndex].Passed = subElement.GetAttributeBool("passed", false);
break;
}
}
foreach (Location location in Locations)
{
location?.InstantiateLoadedMissions(this);
}
int currentLocationConnection = element.GetAttributeInt("currentlocationconnection", -1);
if (currentLocationConnection >= 0)
{
SelectLocation(Connections[currentLocationConnection].OtherLocation(CurrentLocation));
}
}
public void Save(XElement element)
@@ -579,38 +852,39 @@ namespace Barotrauma
mapElement.Add(new XAttribute("version", GameMain.Version.ToString()));
mapElement.Add(new XAttribute("currentlocation", CurrentLocationIndex));
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
if (campaign.NextLevel != null && campaign.NextLevel.Type == LevelData.LevelType.LocationConnection)
{
mapElement.Add(new XAttribute("currentlocationconnection", Connections.IndexOf(CurrentLocation.Connections.Find(c => c.LevelData == campaign.NextLevel))));
}
else if (Level.Loaded != null && Level.Loaded.Type == LevelData.LevelType.LocationConnection && !CurrentLocation.Type.HasOutpost)
{
mapElement.Add(new XAttribute("currentlocationconnection", Connections.IndexOf(Connections.Find(c => c.LevelData == Level.Loaded.LevelData))));
}
}
mapElement.Add(new XAttribute("selectedlocation", SelectedLocationIndex));
mapElement.Add(new XAttribute("startlocation", Locations.IndexOf(StartLocation)));
mapElement.Add(new XAttribute("endlocation", Locations.IndexOf(EndLocation)));
mapElement.Add(new XAttribute("seed", Seed));
for (int i = 0; i < Locations.Count; i++)
{
var location = Locations[i];
if (!location.Discovered) continue;
var locationElement = new XElement("location", new XAttribute("i", i));
locationElement.Add(new XAttribute("type", location.Type.Identifier));
if (location.TypeChangeTimer > 0)
{
locationElement.Add(new XAttribute("changetimer", location.TypeChangeTimer));
}
location.CheckMissionCompleted();
if (location.MissionsCompleted > 0)
{
locationElement.Add(new XAttribute("missionscompleted", location.MissionsCompleted));
}
mapElement.Add(locationElement);
var locationElement = location.Save(this, mapElement);
locationElement.Add(new XAttribute("i", i));
}
for (int i = 0; i < connections.Count; i++)
for (int i = 0; i < Connections.Count; i++)
{
var connection = connections[i];
if (!connection.Passed) continue;
var connection = Connections[i];
var connectionElement = new XElement("connection",
new XAttribute("i", i),
new XAttribute("passed", connection.Passed));
new XAttribute("passed", connection.Passed),
new XAttribute("difficulty", connection.Difficulty),
new XAttribute("biome", connection.Biome.Identifier),
new XAttribute("locations", Locations.IndexOf(connection.Locations[0]) + "," + Locations.IndexOf(connection.Locations[1])));
connection.LevelData.Save(connectionElement);
mapElement.Add(connectionElement);
}