(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,164 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class LevelData
{
public enum LevelType
{
LocationConnection,
Outpost
}
public readonly LevelType Type;
public readonly string Seed;
public float Difficulty;
public readonly Biome Biome;
public readonly LevelGenerationParams GenerationParams;
public OutpostGenerationParams ForceOutpostGenerationParams;
public readonly Point Size;
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
public LevelData(string seed, float difficulty, float sizeFactor, LevelGenerationParams generationParams, Biome biome)
{
Seed = seed ?? throw new ArgumentException("Seed was null");
Biome = biome ?? throw new ArgumentException("Biome was null");
GenerationParams = generationParams ?? throw new ArgumentException("Level generation parameters were null");
Type = GenerationParams.Type;
Difficulty = difficulty;
sizeFactor = MathHelper.Clamp(sizeFactor, 0.0f, 1.0f);
int width = (int)MathHelper.Lerp(generationParams.MinWidth, generationParams.MaxWidth, sizeFactor);
Size = new Point(
(int)MathUtils.Round(width, Level.GridCellSize),
(int)MathUtils.Round(generationParams.Height, Level.GridCellSize));
}
public LevelData(XElement element)
{
Seed = element.GetAttributeString("seed", "");
Difficulty = element.GetAttributeFloat("difficulty", 0.0f);
Size = element.GetAttributePoint("size", new Point(1000));
Enum.TryParse(element.GetAttributeString("type", "LocationConnection"), out Type);
string generationParamsId = element.GetAttributeString("generationparams", "");
GenerationParams = LevelGenerationParams.LevelParams.Find(l => l.Identifier == generationParamsId);
if (GenerationParams == null)
{
DebugConsole.ThrowError($"Error while loading a level. Could not find level generation params with the ID \"{generationParamsId}\".");
GenerationParams = LevelGenerationParams.LevelParams.FirstOrDefault(l => l.Type == Type);
if (GenerationParams == null)
{
GenerationParams = LevelGenerationParams.LevelParams.First();
}
}
string biomeIdentifier = element.GetAttributeString("biome", "");
Biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.Identifier == biomeIdentifier);
if (Biome == null)
{
DebugConsole.ThrowError($"Error in level data: could not find the biome \"{biomeIdentifier}\".");
Biome = LevelGenerationParams.GetBiomes().First();
}
string[] prefabNames = element.GetAttributeStringArray("eventhistory", new string[] { });
EventHistory.AddRange(EventSet.PrefabList.Where(p => prefabNames.Any(n => p.Identifier.Equals(n, StringComparison.InvariantCultureIgnoreCase))));
}
/// <summary>
/// Instantiates level data using the properties of the connection (seed, size, difficulty)
/// </summary>
public LevelData(LocationConnection locationConnection)
{
Seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName;
Biome = locationConnection.Biome;
Type = LevelType.LocationConnection;
GenerationParams = LevelGenerationParams.GetRandom(Seed, LevelType.LocationConnection, Biome);
Difficulty = locationConnection.Difficulty;
float sizeFactor = MathUtils.InverseLerp(
MapGenerationParams.Instance.SmallLevelConnectionLength,
MapGenerationParams.Instance.LargeLevelConnectionLength,
locationConnection.Length);
int width = (int)MathHelper.Lerp(GenerationParams.MinWidth, GenerationParams.MaxWidth, sizeFactor);
Size = new Point(
(int)MathUtils.Round(width, Level.GridCellSize),
(int)MathUtils.Round(GenerationParams.Height, Level.GridCellSize));
}
/// <summary>
/// Instantiates level data using the properties of the location
/// </summary>
public LevelData(Location location)
{
Seed = location.BaseName;
Biome = location.Biome;
Type = LevelType.Outpost;
GenerationParams = LevelGenerationParams.GetRandom(Seed, LevelType.Outpost, Biome);
Difficulty = 0.0f;
var rand = new MTRandom(ToolBox.StringToInt(Seed));
int width = (int)MathHelper.Lerp(GenerationParams.MinWidth, GenerationParams.MaxWidth, (float)rand.NextDouble());
Size = new Point(
(int)MathUtils.Round(width, Level.GridCellSize),
(int)MathUtils.Round(GenerationParams.Height, Level.GridCellSize));
}
public static LevelData CreateRandom(string seed = "", float? difficulty = null, LevelGenerationParams generationParams = null)
{
if (string.IsNullOrEmpty(seed))
{
seed = Rand.Range(0, int.MaxValue, Rand.RandSync.Server).ToString();
}
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
LevelType type = generationParams == null ? LevelData.LevelType.LocationConnection : generationParams.Type;
if (generationParams == null) { generationParams = LevelGenerationParams.GetRandom(seed, type); }
var biome =
LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ??
LevelGenerationParams.GetBiomes().GetRandom(Rand.RandSync.Server);
return new LevelData(
seed,
difficulty ?? Rand.Range(30.0f, 80.0f, Rand.RandSync.Server),
Rand.Range(0.0f, 1.0f, Rand.RandSync.Server),
generationParams,
biome);
}
public void Save(XElement parentElement)
{
var newElement = new XElement("Level",
new XAttribute("seed", Seed),
new XAttribute("biome", Biome.Identifier),
new XAttribute("type", Type.ToString()),
new XAttribute("difficulty", Difficulty.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("size", XMLExtensions.PointToString(Size)),
new XAttribute("generationparams", GenerationParams.Identifier));
if (Type == LevelType.Outpost && EventHistory.Any())
{
newElement.Add(new XAttribute("eventhistory", string.Join(',', EventHistory.Select(p => p.Identifier))));
}
parentElement.Add(newElement);
}
}
}
@@ -13,6 +13,8 @@ namespace Barotrauma
public readonly string DisplayName;
public readonly string Description;
public readonly bool IsEndBiome;
public readonly List<int> AllowedZones = new List<int>();
public Biome(string name, string description)
@@ -40,37 +42,25 @@ namespace Barotrauma
element.GetAttributeString("description", "") ??
TextManager.Get("biomedescription." + Identifier);
string allowedZonesStr = element.GetAttributeString("AllowedZones", "1,2,3,4,5,6,7,8,9");
string[] zoneIndices = allowedZonesStr.Split(',');
for (int i = 0; i < zoneIndices.Length; i++)
{
int zoneIndex = -1;
if (!int.TryParse(zoneIndices[i].Trim(), out zoneIndex))
{
DebugConsole.ThrowError("Error in biome config \"" + Identifier + "\" - \"" + zoneIndices[i] + "\" is not a valid zone index.");
continue;
}
AllowedZones.Add(zoneIndex);
}
IsEndBiome = element.GetAttributeBool("endbiome", false);
AllowedZones.AddRange(element.GetAttributeIntArray("AllowedZones", new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
}
}
class LevelGenerationParams : ISerializableEntity
{
public static List<LevelGenerationParams> LevelParams
{
get { return levelParams; }
}
public static List<LevelGenerationParams> LevelParams { get; private set; }
private static List<LevelGenerationParams> levelParams;
private static List<Biome> biomes;
public string Name
{
get;
private set;
get { return Identifier; }
}
public readonly string Identifier;
private int minWidth, maxWidth, height;
private Point voronoiSiteInterval;
@@ -107,7 +97,7 @@ namespace Barotrauma
private float waterParticleScale;
//which biomes can this type of level appear in
private List<Biome> allowedBiomes = new List<Biome>();
private readonly List<Biome> allowedBiomes = new List<Biome>();
public IEnumerable<Biome> AllowedBiomes
{
@@ -120,6 +110,13 @@ namespace Barotrauma
set;
}
[Serialize(LevelData.LevelType.LocationConnection, true), Editable]
public LevelData.LevelType Type
{
get;
set;
}
[Serialize("27,30,36", true), Editable]
public Color AmbientLightColor
{
@@ -148,6 +145,39 @@ namespace Barotrauma
set;
}
private Vector2 startPosition;
[Serialize("0,0", true, "Start position of the level (relative to the size of the level. 0,0 = top left corner, 1,1 = bottom right corner)"), Editable]
public Vector2 StartPosition
{
get { return startPosition; }
set
{
startPosition = new Vector2(
MathHelper.Clamp(value.X, 0.0f, 1.0f),
MathHelper.Clamp(value.Y, 0.0f, 1.0f));
}
}
private Vector2 endPosition;
[Serialize("1,0", true, "End position of the level (relative to the size of the level. 0,0 = top left corner, 1,1 = bottom right corner)"), Editable]
public Vector2 EndPosition
{
get { return endPosition; }
set
{
endPosition = new Vector2(
MathHelper.Clamp(value.X, 0.0f, 1.0f),
MathHelper.Clamp(value.Y, 0.0f, 1.0f));
}
}
[Serialize(true, true, "Should there be a hole in the wall next to the end outpost (can be used to prevent players from having to backtrack if they approach the outpost from the wrong side of the main path's walls)."), Editable]
public bool CreateHoleNextToEnd
{
get;
set;
}
[Serialize(1000, true, description: "The total number of level objects (vegetation, vents, etc) in the level."), Editable(MinValueInt = 0, MaxValueInt = 100000)]
public int LevelObjectAmount
{
@@ -176,6 +206,13 @@ namespace Barotrauma
set { height = Math.Max(value, 2000); }
}
[Serialize(6500, true), Editable(MinValueInt = 5000, MaxValueInt = 1000000)]
public int MinTunnelRadius
{
get;
set;
}
[Editable, Serialize("3000, 3000", true, description: "How far from each other voronoi sites are placed. " +
"Sites determine shape of the voronoi graph which the level walls are generated from. " +
"(Decreasing this value causes the number of sites, and the complexity of the level, to increase exponentially - be careful when adjusting)")]
@@ -386,31 +423,43 @@ namespace Barotrauma
public Sprite WallEdgeSpriteSpecular { get; private set; }
public Sprite WaterParticles { get; private set; }
public static List<Biome> GetBiomes()
public static IEnumerable<Biome> GetBiomes()
{
return biomes;
}
public static LevelGenerationParams GetRandom(string seed, Biome biome = null)
public static LevelGenerationParams GetRandom(string seed, LevelData.LevelType type, Biome biome = null)
{
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
if (levelParams == null || !levelParams.Any())
if (LevelParams == null || !LevelParams.Any())
{
DebugConsole.ThrowError("Level generation presets not found - using default presets");
return new LevelGenerationParams(null);
}
var matchingLevelParams = LevelParams.FindAll(lp => lp.Type == type && lp.allowedBiomes.Any());
if (biome == null)
{
return levelParams.GetRandom(lp => lp.allowedBiomes.Count > 0, Rand.RandSync.Server);
matchingLevelParams = matchingLevelParams.FindAll(lp => !lp.allowedBiomes.Any(b => b.IsEndBiome));
}
else
{
matchingLevelParams = matchingLevelParams.FindAll(lp => lp.allowedBiomes.Contains(biome));
}
var matchingLevelParams = levelParams.FindAll(lp => lp.allowedBiomes.Contains(biome));
if (matchingLevelParams.Count == 0)
{
DebugConsole.ThrowError("Level generation presets not found for the biome \"" + biome.Identifier + "\"!");
return new LevelGenerationParams(null);
DebugConsole.ThrowError($"Suitable level generation presets not found (biome \"{(biome?.Identifier ?? "null")}\", type: \"{type}\"!");
if (biome != null)
{
//try to find params that at least have a suitable type
matchingLevelParams = LevelParams.FindAll(lp => lp.Type == type);
if (matchingLevelParams.Count == 0)
{
//still not found, give up and choose some params randomly
matchingLevelParams = LevelParams;
}
}
}
return matchingLevelParams[Rand.Range(0, matchingLevelParams.Count, Rand.RandSync.Server)];
@@ -418,11 +467,14 @@ namespace Barotrauma
private LevelGenerationParams(XElement element)
{
Name = element == null ? "default" : element.Name.ToString();
Identifier = element == null ? "default" :
element.GetAttributeString("identifier", null) ?? element.Name.ToString();
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
if (element == null) { return; }
string biomeStr = element.GetAttributeString("biomes", "");
if (string.IsNullOrWhiteSpace(biomeStr))
if (string.IsNullOrWhiteSpace(biomeStr) || biomeStr.Equals("any", StringComparison.OrdinalIgnoreCase))
{
allowedBiomes = new List<Biome>(biomes);
}
@@ -445,7 +497,7 @@ namespace Barotrauma
}
else
{
DebugConsole.NewMessage("Please use biome identifiers instead of names in level generation parameter \"" + Name + "\".", Color.Orange);
DebugConsole.NewMessage("Please use biome identifiers instead of names in level generation parameter \"" + Identifier + "\".", Color.Orange);
}
}
@@ -484,7 +536,7 @@ namespace Barotrauma
public static void LoadPresets()
{
levelParams = new List<LevelGenerationParams>();
LevelParams = new List<LevelGenerationParams>();
biomes = new List<Biome>();
var files = GameMain.Instance.GetFilesOfType(ContentType.LevelGenerationParameters);
@@ -549,7 +601,7 @@ namespace Barotrauma
foreach (XElement levelParamElement in levelParamElements)
{
levelParams.Add(new LevelGenerationParams(levelParamElement));
LevelParams.Add(new LevelGenerationParams(levelParamElement));
}
}
}
@@ -8,7 +8,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
partial class LevelObject
partial class LevelObject : ISpatialEntity
{
public readonly LevelObjectPrefab Prefab;
public Vector3 Position;
@@ -50,6 +50,14 @@ namespace Barotrauma
get { return spriteIndex < 0 || Prefab.SpecularSprites.Count == 0 ? null : Prefab.SpecularSprites[spriteIndex % Prefab.SpecularSprites.Count]; }
}
Vector2 ISpatialEntity.Position => new Vector2(Position.X, Position.Y);
public Vector2 WorldPosition => new Vector2(Position.X, Position.Y);
public Vector2 SimPosition => ConvertUnits.ToSimUnits(WorldPosition);
public Submarine Submarine => null;
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
{
Triggers = new List<LevelTrigger>();
@@ -31,6 +31,8 @@ namespace Barotrauma
public readonly Alignment Alignment;
public readonly float Length;
private readonly float noiseVal;
public SpawnPosition(GraphEdge graphEdge, Vector2 normal, LevelObjectPrefab.SpawnPosType spawnPosType, Alignment alignment)
{
GraphEdge = graphEdge;
@@ -39,16 +41,16 @@ namespace Barotrauma
Alignment = alignment;
Length = Vector2.Distance(graphEdge.Point1, graphEdge.Point2);
noiseVal =
(float)(PerlinNoise.CalculatePerlin(GraphEdge.Point1.X / 10000.0f, GraphEdge.Point1.Y / 10000.0f, 0.5f) +
PerlinNoise.CalculatePerlin(GraphEdge.Point1.X / 20000.0f, GraphEdge.Point1.Y / 20000.0f, 0.5f));
}
public float GetSpawnProbability(LevelObjectPrefab prefab)
{
if (prefab.ClusteringAmount <= 0.0f) return Length;
float noise = (float)(
PerlinNoise.CalculatePerlin(GraphEdge.Point1.X / 10000.0f, GraphEdge.Point1.Y / 10000.0f, prefab.ClusteringGroup) +
PerlinNoise.CalculatePerlin(GraphEdge.Point1.X / 20000.0f, GraphEdge.Point1.Y / 20000.0f, prefab.ClusteringGroup));
if (prefab.ClusteringAmount <= 0.0f) { return Length; }
float noise = (noiseVal + PerlinNoise.GetPerlin(prefab.ClusteringGroup, prefab.ClusteringGroup * 0.3f)) % 1.0f;
return Length * (float)Math.Pow(noise, prefab.ClusteringAmount);
}
}
@@ -90,15 +92,33 @@ namespace Barotrauma
Alignment.Top));
}
availableSpawnPositions.Add(new SpawnPosition(
new GraphEdge(level.StartPosition - Vector2.UnitX, level.StartPosition + Vector2.UnitX),
-Vector2.UnitY, LevelObjectPrefab.SpawnPosType.LevelStart, Alignment.Top));
availableSpawnPositions.Add(new SpawnPosition(
new GraphEdge(level.EndPosition - Vector2.UnitX, level.EndPosition + Vector2.UnitX),
-Vector2.UnitY, LevelObjectPrefab.SpawnPosType.LevelEnd, Alignment.Top));
var availablePrefabs = new List<LevelObjectPrefab>(LevelObjectPrefab.List);
objects = new List<LevelObject>();
Dictionary<LevelObjectPrefab, List<SpawnPosition>> suitableSpawnPositions = new Dictionary<LevelObjectPrefab, List<SpawnPosition>>();
Dictionary<LevelObjectPrefab, List<float>> spawnPositionWeights = new Dictionary<LevelObjectPrefab, List<float>>();
for (int i = 0; i < amount; i++)
{
//get a random prefab and find a place to spawn it
LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams.Name);
SpawnPosition spawnPosition = FindObjectPosition(availableSpawnPositions, level, prefab);
LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams.Identifier, availablePrefabs);
if (prefab == null) { continue; }
if (!suitableSpawnPositions.ContainsKey(prefab))
{
suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp =>
prefab.SpawnPos.HasFlag(sp.SpawnPosType) && (sp.Length >= prefab.MinSurfaceWidth && prefab.Alignment.HasFlag(sp.Alignment) || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelEnd || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelStart)).ToList());
spawnPositionWeights.Add(prefab,
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
}
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) continue;
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
float rotation = 0.0f;
if (prefab.AlignWithSurface && spawnPosition != null)
@@ -124,6 +144,13 @@ namespace Barotrauma
var newObject = new LevelObject(prefab,
new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
AddObject(newObject, level);
if (prefab.MaxCount < amount)
{
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
}
}
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
{
@@ -145,9 +172,7 @@ namespace Barotrauma
AddObject(childObject, level);
}
}
}
}
}
private void AddObject(LevelObject newObject, Level level)
@@ -321,16 +346,6 @@ namespace Barotrauma
return availableSpawnPositions;
}
private SpawnPosition FindObjectPosition(List<SpawnPosition> availableSpawnPositions, Level level, LevelObjectPrefab prefab)
{
if (prefab.SpawnPos == LevelObjectPrefab.SpawnPosType.None) return null;
var suitableSpawnPositions = availableSpawnPositions.Where(sp =>
prefab.SpawnPos.HasFlag(sp.SpawnPosType) && sp.Length >= prefab.MinSurfaceWidth && prefab.Alignment.HasFlag(sp.Alignment)).ToList();
return ToolBox.SelectWeightedRandom(suitableSpawnPositions, suitableSpawnPositions.Select(sp => sp.GetSpawnProbability(prefab)).ToList(), Rand.RandSync.Server);
}
public void Update(float deltaTime)
{
foreach (LevelObject obj in objects)
@@ -383,10 +398,15 @@ namespace Barotrauma
}
private LevelObjectPrefab GetRandomPrefab(string levelType)
{
return GetRandomPrefab(levelType, LevelObjectPrefab.List);
}
private LevelObjectPrefab GetRandomPrefab(string levelType, IList<LevelObjectPrefab> availablePrefabs)
{
return ToolBox.SelectWeightedRandom(
LevelObjectPrefab.List,
LevelObjectPrefab.List.Select(p => p.GetCommonness(levelType)).ToList(), Rand.RandSync.Server);
availablePrefabs,
availablePrefabs.Select(p => p.GetCommonness(levelType)).ToList(), Rand.RandSync.Server);
}
public override void Remove()
@@ -41,7 +41,9 @@ namespace Barotrauma
Wall = 1,
RuinWall = 2,
SeaFloor = 4,
MainPath = 8
MainPath = 8,
LevelStart = 16,
LevelEnd = 32,
}
public List<Sprite> Sprites
@@ -117,6 +119,14 @@ namespace Barotrauma
private set;
}
[Serialize(10000, false, description: "Maximum number of this specific object per level."), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
public int MaxCount
{
get;
private set;
}
[Serialize("0.0,1.0", true), Editable]
public Vector2 DepthRange
{
@@ -152,11 +152,11 @@ namespace Barotrauma.RuinGeneration
{
mainElement = doc.Root.FirstElement();
paramsList.Clear();
DebugConsole.NewMessage($"Overriding all ruin configuration parameters using the file {configFile.Path}.", Color.Yellow);
DebugConsole.NewMessage($"Overriding all ruin generation parameters using the file {configFile.Path}.", Color.Yellow);
}
else if (paramsList.Any())
{
DebugConsole.NewMessage($"Adding additional ruin configuration parameters from file '{configFile.Path}'");
DebugConsole.NewMessage($"Adding additional ruin generation parameters from file '{configFile.Path}'");
}
var newParams = new RuinGenerationParams(mainElement)
{