Unstable 1.8.4.0
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -21,6 +21,8 @@ namespace Barotrauma
|
||||
public float ActualMaxDifficulty => maxDifficulty;
|
||||
public float AdjustedMaxDifficulty => maxDifficulty - 0.1f;
|
||||
|
||||
public readonly float ExperienceFromMissionRewards;
|
||||
|
||||
|
||||
public readonly ImmutableHashSet<int> AllowedZones;
|
||||
|
||||
@@ -50,6 +52,10 @@ namespace Barotrauma
|
||||
AllowedZones = element.GetAttributeIntArray("AllowedZones", new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }).ToImmutableHashSet();
|
||||
MinDifficulty = element.GetAttributeFloat("MinDifficulty", 0);
|
||||
maxDifficulty = element.GetAttributeFloat("MaxDifficulty", 100);
|
||||
float baseExperience = 0.09f;
|
||||
float difficultyRewardMultiplier = 0.25f;
|
||||
float calculateDefaultExperience = baseExperience + MinDifficulty * difficultyRewardMultiplier / 100;
|
||||
ExperienceFromMissionRewards = element.GetAttributeFloat("ExperienceFromMissionRewards", calculateDefaultExperience);
|
||||
|
||||
var submarineAvailabilityOverrides = new HashSet<SubmarineAvailability>();
|
||||
if (element.GetChildElement("submarines") is ContentXElement availabilityElement)
|
||||
|
||||
@@ -217,8 +217,13 @@ namespace Barotrauma
|
||||
/// Makes the cell rounder by subdividing the edges and offsetting them at the middle
|
||||
/// </summary>
|
||||
/// <param name="minEdgeLength">How small the individual subdivided edges can be (smaller values produce rounder shapes, but require more geometry)</param>
|
||||
public static void RoundCell(VoronoiCell cell, float minEdgeLength = 500.0f, float roundingAmount = 0.5f, float irregularity = 0.1f)
|
||||
/// <param name="minThickness">How "thin" irregularity is allowed to make parts of the cell. Very high irregularity values can lead to thin "spikes" or even parts where the "spike's" thickness becomes negative and wall segments intersect each other.</param>
|
||||
public static void RoundCell(VoronoiCell cell, float minEdgeLength = 500.0f, float roundingAmount = 0.5f, float irregularity = 0.1f, float minThickness = 0.0f)
|
||||
{
|
||||
//we need to make sure the vertices of the wall are still ordered counter-clockwise -
|
||||
//if we deform some vertices so much the cell becomes concave, rendering the triangles will break (parts of the inside of the wall will render outside the edges)
|
||||
var compareCCW = new CompareCCW(cell.Center);
|
||||
|
||||
List<GraphEdge> tempEdges = new List<GraphEdge>();
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
@@ -231,8 +236,10 @@ namespace Barotrauma
|
||||
Vector2 edgeDiff = edge.Point2 - edge.Point1;
|
||||
Vector2 edgeDir = Vector2.Normalize(edgeDiff);
|
||||
|
||||
float maxExtrusion = float.PositiveInfinity;
|
||||
const float minPassageWidth = 200.0f;
|
||||
//If the edge is next to an empty cell and there's another solid cell at the other side of the empty one,
|
||||
//don't touch this edge. Otherwise we may end up closing off small passages between cells.
|
||||
//we need to calculate how far we can extrude the edge so it doesn't end up closing off small passages between cells.
|
||||
var adjacentEmptyCell = edge.AdjacentCell(cell);
|
||||
if (adjacentEmptyCell?.CellType == CellType.Solid) { adjacentEmptyCell = null; }
|
||||
if (adjacentEmptyCell != null)
|
||||
@@ -252,8 +259,15 @@ namespace Barotrauma
|
||||
}
|
||||
if (adjacentEdge != null)
|
||||
{
|
||||
tempEdges.Add(edge);
|
||||
continue;
|
||||
maxExtrusion =
|
||||
new[]
|
||||
{
|
||||
Vector2.Distance(edge.Point1, adjacentEdge.Point1),
|
||||
Vector2.Distance(edge.Point1, adjacentEdge.Point2),
|
||||
Vector2.Distance(edge.Point1, adjacentEdge.Point2),
|
||||
Vector2.Distance(edge.Point2, adjacentEdge.Point1),
|
||||
}.Min();
|
||||
maxExtrusion = Math.Max(0, maxExtrusion - minPassageWidth);
|
||||
}
|
||||
}
|
||||
List<Vector2> edgePoints = new List<Vector2>();
|
||||
@@ -274,12 +288,53 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
|
||||
//value that's 0 at edges, 0.5 at center
|
||||
float centerF = 0.5f - Math.Abs(0.5f - (i / (float)pointCount));
|
||||
float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.ServerAndClient);
|
||||
Vector2 extrudedPoint =
|
||||
//make the value "curve" from 0 to 1 at the center, instead of going linearly from 0 to 1
|
||||
centerF = MathF.Sin(centerF * MathHelper.Pi);
|
||||
|
||||
//magic number intended to make old rounding values behave roughly the same with the new formula
|
||||
//previously the extrusion increased linearly towards the center, forming a "spike" like "/\"
|
||||
//now it follows a sine curve, which makes lower values produce rounder results
|
||||
const float RoundingScale = 0.25f;
|
||||
|
||||
//magic number intended to make old variance values behave roughly the same with the new formula
|
||||
//previously a value of 1 would allow a maximum extrusion of 50% of the edge's length at the center,
|
||||
//now we extrude by the variance at any point on the edge (not more in the center)
|
||||
const float RandomVarianceScale = 0.25f;
|
||||
float randomVariance = irregularity * Rand.Range(-0.5f, 0.5f, Rand.RandSync.ServerAndClient);
|
||||
|
||||
float extrusionAmount = edgeLength * ((roundingAmount * RoundingScale * centerF) + randomVariance * RandomVarianceScale);
|
||||
extrusionAmount = Math.Min(extrusionAmount, maxExtrusion);
|
||||
|
||||
Vector2 nonExtrudedPoint =
|
||||
edge.Point1 +
|
||||
edgeDiff * (i / (float)pointCount) +
|
||||
edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;
|
||||
edgeDiff * (i / (float)pointCount);
|
||||
|
||||
Vector2 nextPoint =
|
||||
edge.Point1 +
|
||||
edgeDiff * ((i + 1) / (float)pointCount);
|
||||
|
||||
//"extruding" inwards, need to make sure we don't make the edge poke through the cell from the other side
|
||||
if (extrusionAmount < 0 && minThickness > 0.0f)
|
||||
{
|
||||
foreach (GraphEdge otherEdge in cell.Edges)
|
||||
{
|
||||
if (otherEdge == edge) { continue; }
|
||||
float margin = minThickness * Math.Sign(extrusionAmount);
|
||||
if (MathUtils.GetLineIntersection(
|
||||
nonExtrudedPoint, nonExtrudedPoint + edgeNormal * (extrusionAmount + margin),
|
||||
otherEdge.Point1, otherEdge.Point2, areLinesInfinite: false, out Vector2 intersection))
|
||||
{
|
||||
extrusionAmount = Math.Min(extrusionAmount, Vector2.Distance(edge.Point1, intersection)) - margin;
|
||||
//make sure we don't "overshoot", fix the inwards extrusion by instead extruding too much outwards
|
||||
//(can happen on small cells in caves for example)
|
||||
extrusionAmount = Math.Min(extrusionAmount, edge.Length / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 extrudedPoint = nonExtrudedPoint + edgeNormal * extrusionAmount;
|
||||
|
||||
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
|
||||
bool isInside = false;
|
||||
@@ -306,14 +361,29 @@ namespace Barotrauma
|
||||
}
|
||||
if (isInside) { break; }
|
||||
}
|
||||
if (isInside) { continue; }
|
||||
|
||||
if (!isInside)
|
||||
{
|
||||
edgePoints.Add(extrudedPoint);
|
||||
}
|
||||
//if adding the point would deform the edge so much that the normal of the new edge would point
|
||||
//in the opposite direction from the undeformed edge's normal, don't allow adding the point
|
||||
//(that would lead to the edge being "inside out", the wall texture and objects on the wall pointing inwards)
|
||||
bool isNormalInverted =
|
||||
Vector2.Dot(edgeNormal, GraphEdge.GetNormal(cell, edgePoints.Last(), extrudedPoint)) < 0 ||
|
||||
//check that the edge at the other side of the new point doesn't get inverted either
|
||||
Vector2.Dot(edgeNormal, GraphEdge.GetNormal(cell, extrudedPoint, edge.Point2)) < 0;
|
||||
if (isNormalInverted) { continue; }
|
||||
|
||||
//make sure extruding the point doesn't change the vertex order
|
||||
//(they're assumed to be sorted counter-clockwise, and if they're not, the triangles will generate incorrectly)
|
||||
bool vertexOrderChanged =
|
||||
compareCCW.Compare(edgePoints.Last(), nonExtrudedPoint) != compareCCW.Compare(edgePoints.Last(), extrudedPoint) ||
|
||||
compareCCW.Compare(nonExtrudedPoint, nextPoint) != compareCCW.Compare(extrudedPoint, nextPoint);
|
||||
if (vertexOrderChanged) { continue; }
|
||||
|
||||
edgePoints.Add(extrudedPoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < edgePoints.Count - 1; i++)
|
||||
{
|
||||
tempEdges.Add(new GraphEdge(edgePoints[i], edgePoints[i + 1])
|
||||
@@ -376,19 +446,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 minVert = tempVertices[0];
|
||||
Vector2 maxVert = tempVertices[0];
|
||||
foreach (var vert in tempVertices)
|
||||
{
|
||||
minVert = new Vector2(
|
||||
Math.Min(minVert.X, vert.X),
|
||||
Math.Min(minVert.Y, vert.Y));
|
||||
maxVert = new Vector2(
|
||||
Math.Max(maxVert.X, vert.X),
|
||||
Math.Max(maxVert.Y, vert.Y));
|
||||
}
|
||||
Vector2 center = (minVert + maxVert) / 2;
|
||||
renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, center));
|
||||
renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, cell.Center));
|
||||
|
||||
if (bodyPoints.Count < 2) { continue; }
|
||||
|
||||
@@ -411,7 +469,7 @@ namespace Barotrauma
|
||||
if (cell.CellType == CellType.Empty) { continue; }
|
||||
|
||||
cellBody.UserData = cell;
|
||||
var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(center));
|
||||
var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(cell.Center));
|
||||
|
||||
for (int i = 0; i < triangles.Count; i++)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Barotrauma
|
||||
|
||||
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, Vector2 impulseDirection, float deltaTime, bool playSound = true)
|
||||
{
|
||||
AddDamage(attack.StructureDamage, worldPosition);
|
||||
AddDamage(attack.LevelWallDamage, worldPosition);
|
||||
return new AttackResult(attack.StructureDamage);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,17 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.RuinGeneration;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LevelData
|
||||
{
|
||||
[Flags]
|
||||
public enum LevelType
|
||||
{
|
||||
LocationConnection,
|
||||
Outpost
|
||||
LocationConnection = 1,
|
||||
Outpost = 2
|
||||
}
|
||||
|
||||
public readonly LevelType Type;
|
||||
@@ -47,6 +49,19 @@ namespace Barotrauma
|
||||
|
||||
public SubmarineInfo ForceWreck;
|
||||
|
||||
public RuinGenerationParams ForceRuinGenerationParams;
|
||||
|
||||
public enum ThalamusSpawn
|
||||
{
|
||||
Random,
|
||||
Forced,
|
||||
Disabled
|
||||
}
|
||||
|
||||
public static SubmarineInfo ConsoleForceWreck;
|
||||
public static SubmarineInfo ConsoleForceBeaconStation;
|
||||
public static ThalamusSpawn ForceThalamus = ThalamusSpawn.Random;
|
||||
|
||||
public bool AllowInvalidOutpost;
|
||||
|
||||
public readonly Point Size;
|
||||
@@ -73,10 +88,15 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<EventSet, int> FinishedEvents = new Dictionary<EventSet, int>();
|
||||
|
||||
/// <summary>
|
||||
/// For backwards compatibility (previously "exhausting" one event set exhausted all of them (now we use <see cref="exhaustedEventSets"/> instead).
|
||||
/// </summary>
|
||||
private bool allEventsExhausted;
|
||||
|
||||
/// <summary>
|
||||
/// 'Exhaustible' sets won't appear in the same level until after one world step (~10 min, see Map.ProgressWorld) has passed. <see cref="EventSet.Exhaustible"/>.
|
||||
/// </summary>
|
||||
public bool EventsExhausted { get; set; }
|
||||
private HashSet<Identifier> exhaustedEventSets = new HashSet<Identifier>();
|
||||
|
||||
/// <summary>
|
||||
/// The crush depth of a non-upgraded submarine in in-game coordinates. Note that this can be above the top of the level!
|
||||
@@ -207,7 +227,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
EventsExhausted = element.GetAttributeBool(nameof(EventsExhausted).ToLower(), false);
|
||||
exhaustedEventSets = element.GetAttributeIdentifierArray(nameof(exhaustedEventSets), Array.Empty<Identifier>()).ToHashSet();
|
||||
//backwards compatibility: previously we didn't track which individual event sets have been exhausted
|
||||
allEventsExhausted = element.GetAttributeBool("EventsExhausted", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -216,10 +238,11 @@ namespace Barotrauma
|
||||
public LevelData(LocationConnection locationConnection)
|
||||
{
|
||||
Seed = locationConnection.Locations[0].LevelData.Seed + locationConnection.Locations[1].LevelData.Seed;
|
||||
bool connectionIsBiomeTransition = locationConnection.Locations[0].Biome.Identifier != locationConnection.Locations[1].Biome.Identifier;
|
||||
Biome = locationConnection.Biome;
|
||||
Type = LevelType.LocationConnection;
|
||||
Difficulty = locationConnection.Difficulty;
|
||||
GenerationParams = LevelGenerationParams.GetRandom(Seed, LevelType.LocationConnection, Difficulty, Biome.Identifier);
|
||||
GenerationParams = LevelGenerationParams.GetRandom(Seed, LevelType.LocationConnection, Difficulty, Biome.Identifier, biomeTransition: connectionIsBiomeTransition);
|
||||
|
||||
float sizeFactor = MathUtils.InverseLerp(
|
||||
MapGenerationParams.Instance.SmallLevelConnectionLength,
|
||||
@@ -264,7 +287,7 @@ namespace Barotrauma
|
||||
(int)MathUtils.Round(GenerationParams.Height, Level.GridCellSize));
|
||||
}
|
||||
|
||||
public static LevelData CreateRandom(string seed = "", float? difficulty = null, LevelGenerationParams generationParams = null, bool requireOutpost = false)
|
||||
public static LevelData CreateRandom(string seed = "", float? difficulty = null, LevelGenerationParams generationParams = null, Identifier biomeId = default, bool requireOutpost = false, bool pvpOnly = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(seed))
|
||||
{
|
||||
@@ -273,14 +296,21 @@ namespace Barotrauma
|
||||
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
|
||||
|
||||
LevelType type = generationParams == null ?
|
||||
(requireOutpost ? LevelType.Outpost : LevelType.LocationConnection) :
|
||||
generationParams.Type;
|
||||
LevelType type = generationParams?.Type ??
|
||||
(requireOutpost
|
||||
? LevelType.Outpost
|
||||
: LevelType.LocationConnection);
|
||||
|
||||
float selectedDifficulty = difficulty ?? Rand.Range(30.0f, 80.0f, Rand.RandSync.ServerAndClient);
|
||||
|
||||
if (generationParams == null) { generationParams = LevelGenerationParams.GetRandom(seed, type, selectedDifficulty); }
|
||||
var biome =
|
||||
Biome biome = null;
|
||||
if (!biomeId.IsEmpty && biomeId != "Random")
|
||||
{
|
||||
Biome.Prefabs.TryGet(biomeId, out biome);
|
||||
}
|
||||
generationParams ??= LevelGenerationParams.GetRandom(seed, type, selectedDifficulty, pvpOnly: pvpOnly, biomeId: biomeId);
|
||||
|
||||
biome ??=
|
||||
Biome.Prefabs.FirstOrDefault(b => generationParams?.AllowedBiomeIdentifiers.Contains(b.Identifier) ?? false) ??
|
||||
Biome.Prefabs.GetRandom(Rand.RandSync.ServerAndClient);
|
||||
|
||||
@@ -306,6 +336,32 @@ namespace Barotrauma
|
||||
return levelData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the event set as "exhausted". Exhausted sets won't appear in the same level until after one world step (~10 min, see Map.ProgressWorld) has passed. <see cref="EventSet.Exhaustible"/>.
|
||||
/// </summary>
|
||||
public void ExhaustEventSet(EventSet eventSet)
|
||||
{
|
||||
exhaustedEventSets.Add(eventSet.Identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Has the event set been "exhausted"? Exhausted sets won't appear in the same level until after one world step (~10 min, see Map.ProgressWorld) has passed. <see cref="EventSet.Exhaustible"/>.
|
||||
/// </summary>
|
||||
public bool IsEventSetExhausted(EventSet eventSet)
|
||||
{
|
||||
if (allEventsExhausted) { return true; }
|
||||
return exhaustedEventSets.Contains(eventSet.Identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all "exhausted" event sets, allowing them to appear in the level again.
|
||||
/// </summary>
|
||||
public void ResetExhaustedEventSets()
|
||||
{
|
||||
allEventsExhausted = false;
|
||||
exhaustedEventSets.Clear();
|
||||
}
|
||||
|
||||
public void ReassignGenerationParams(string seed)
|
||||
{
|
||||
GenerationParams = LevelGenerationParams.GetRandom(seed, Type, Difficulty, Biome.Identifier);
|
||||
@@ -341,7 +397,10 @@ namespace Barotrauma
|
||||
new XAttribute("size", XMLExtensions.PointToString(Size)),
|
||||
new XAttribute("generationparams", GenerationParams.Identifier),
|
||||
new XAttribute("initialdepth", InitialDepth),
|
||||
new XAttribute(nameof(EventsExhausted).ToLower(), EventsExhausted));
|
||||
new XAttribute("exhaustedeventsets", allEventsExhausted));
|
||||
|
||||
newElement.Add(
|
||||
new XAttribute(nameof(exhaustedEventSets), string.Join(',', exhaustedEventSets.Select(e => e.Value))));
|
||||
|
||||
if (HasBeaconStation)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly static PrefabCollection<LevelGenerationParams> LevelParams = new PrefabCollection<LevelGenerationParams>();
|
||||
|
||||
public LocalizedString DisplayName { get; private set; }
|
||||
public LocalizedString Description { get; private set; }
|
||||
|
||||
public string Name => Identifier.Value;
|
||||
|
||||
public Identifier OldIdentifier { get; }
|
||||
@@ -68,12 +71,18 @@ namespace Barotrauma
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, "If the given level is only used in PvP modes"), Editable]
|
||||
public bool IsPvPLevel { get; set; }
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes, "If there are multiple level generation parameters available for a level in a given biome, their commonness determines how likely it is for one to get selected."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float Commonness
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, "If the level is a transition from the previous biome to this one."), Editable]
|
||||
public bool TransitionFromPreviousBiome { get; set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, "The difficulty of the level has to be above or equal to this for these parameters to get chosen for the level."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float MinLevelDifficulty
|
||||
@@ -227,7 +236,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f), Serialize(0.5f, IsPropertySaveable.Yes, description: "How much the individual wall cells are rounded. "
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.5f, IsPropertySaveable.Yes, description: "How much the individual wall cells are rounded. "
|
||||
+ "Note that the final shape of the cells is also affected by the CellSubdivisionLength parameter.")]
|
||||
public float CellRoundingAmount
|
||||
{
|
||||
@@ -238,7 +247,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f), Serialize(0.1f, IsPropertySaveable.Yes, description: "How much random variance is applied to the edges of the cells. "
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.1f, IsPropertySaveable.Yes, description: "How much random variance is applied to the edges of the cells. "
|
||||
+ "Note that the final shape of the cells is also affected by the CellSubdivisionLength parameter.")]
|
||||
public float CellIrregularity
|
||||
{
|
||||
@@ -497,6 +506,9 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(0, IsPropertySaveable.Yes, description: "The maximum number of alien ruins in the level."), Editable(MinValueInt = 0, MaxValueInt = 10)]
|
||||
public int MaxRuinCount { get; set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The probability of spawning a ruin in the level. If the level can have multiple ruins, the probability is evaluated separately for each."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2)]
|
||||
public float RuinSpawnProbability { get; set; }
|
||||
|
||||
// TODO: Move the wreck parameters under a separate class?
|
||||
#region Wreck parameters
|
||||
@@ -512,17 +524,20 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(5, IsPropertySaveable.Yes, description: "The maximum number of corpses per wreck."), Editable(MinValueInt = 0, MaxValueInt = 20)]
|
||||
public int MaxCorpseCount { get; set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How likely is it that a character set to be spawned as a corpse spawns as a human husk instead? Percentage from 0 to 1 per character."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float HuskProbability { get; set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How likely is it that a Thalamus inhabits a wreck. Percentage from 0 to 1 per wreck."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How likely is it that a Thalamus inhabits a wreck. Percentage from 0 to 1 per wreck."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float ThalamusProbability { get; set; }
|
||||
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, description: "How likely the water level of a hull inside a wreck is randomly set."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, description: "How likely the water level of a hull inside a wreck is randomly set."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float WreckHullFloodingChance { get; set; }
|
||||
|
||||
[Serialize(0.1f, IsPropertySaveable.Yes, description: "The min water percentage of randomly flooding hulls in wrecks."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
[Serialize(0.1f, IsPropertySaveable.Yes, description: "The min water percentage of randomly flooding hulls in wrecks."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float WreckFloodingHullMinWaterPercentage { get; set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The min water percentage of randomly flooding hulls in wrecks."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The min water percentage of randomly flooding hulls in wrecks."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float WreckFloodingHullMaxWaterPercentage { get; set; }
|
||||
#endregion
|
||||
|
||||
@@ -587,6 +602,13 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(1000.0f, IsPropertySaveable.Yes, description: "How deep inside the walls the wall texture extends to before fading to black."), Editable(minValue: 0.0f, maxValue: 10000.0f)]
|
||||
public float WallTextureExpandInwardsAmount
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Header("Colors")]
|
||||
[Serialize("27,30,36", IsPropertySaveable.Yes), Editable]
|
||||
public Color AmbientLightColor
|
||||
@@ -673,7 +695,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelGenerationParams GetRandom(string seed, LevelData.LevelType type, float difficulty, Identifier biomeId = default)
|
||||
public static LevelGenerationParams GetRandom(string seed, LevelData.LevelType type, float difficulty, Identifier biomeId = default, bool pvpOnly = false, bool biomeTransition = false)
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
|
||||
|
||||
@@ -688,7 +710,41 @@ namespace Barotrauma
|
||||
lp.Type == type &&
|
||||
(lp.AnyBiomeAllowed || lp.AllowedBiomeIdentifiers.Any()) &&
|
||||
!lp.AllowedBiomeIdentifiers.Contains("None".ToIdentifier()));
|
||||
if (biomeId.IsEmpty)
|
||||
|
||||
if (biomeTransition)
|
||||
{
|
||||
var biomeTransitionParams = matchingLevelParams.Where(lp =>
|
||||
lp.TransitionFromPreviousBiome && lp.AllowedBiomeIdentifiers.Contains(biomeId));
|
||||
|
||||
if (biomeTransitionParams.Any())
|
||||
{
|
||||
return ToolBox.SelectWeightedRandom(biomeTransitionParams, p => p.Commonness, Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matchingLevelParams = matchingLevelParams.Where(lp => !lp.TransitionFromPreviousBiome);
|
||||
}
|
||||
|
||||
if (pvpOnly)
|
||||
{
|
||||
var pvpOnlyLevels = matchingLevelParams.Where(static lp => lp.IsPvPLevel);
|
||||
|
||||
if (pvpOnlyLevels.Any())
|
||||
{
|
||||
matchingLevelParams = pvpOnlyLevels;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning("No PvP specific level generation presets found - using all level generation presets instead.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matchingLevelParams = matchingLevelParams.Where(static lp => !lp.IsPvPLevel);
|
||||
}
|
||||
|
||||
if (biomeId.IsEmpty || biomeId == "Random")
|
||||
{
|
||||
//we don't want end levels when generating a completely random level (e.g. in mission mode)
|
||||
matchingLevelParams = matchingLevelParams.Where(lp => lp.AnyBiomeAllowed || !lp.AllowedBiomeIdentifiers.All(b => Biome.Prefabs[b].IsEndBiome));
|
||||
@@ -746,6 +802,20 @@ namespace Barotrauma
|
||||
allowedBiomeIdentifiers.Remove("any".ToIdentifier());
|
||||
AllowedBiomeIdentifiers = allowedBiomeIdentifiers.ToImmutableHashSet();
|
||||
|
||||
DisplayName = TextManager.Get($"levelname.{Identifier}");
|
||||
Description = TextManager.Get($"leveldescription.{Identifier}");
|
||||
|
||||
var nameIdentifier = element.GetAttributeIdentifier("nameidentifier", Identifier.Empty);
|
||||
var descriptionIdentifier = element.GetAttributeIdentifier("descriptionidentifier", Identifier.Empty);
|
||||
if (!nameIdentifier.IsEmpty)
|
||||
{
|
||||
DisplayName = TextManager.Get(nameIdentifier);
|
||||
}
|
||||
if (!descriptionIdentifier.IsEmpty)
|
||||
{
|
||||
Description = TextManager.Get(descriptionIdentifier);
|
||||
}
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
|
||||
+21
-10
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
private List<LevelObject> updateableObjects;
|
||||
private List<LevelObject>[,] objectGrid;
|
||||
|
||||
const float ParallaxStrength = 0.0001f;
|
||||
public const float ParallaxStrength = 0.0001f;
|
||||
|
||||
public float GlobalForceDecreaseTimer
|
||||
{
|
||||
@@ -178,12 +178,21 @@ namespace Barotrauma
|
||||
{
|
||||
float minDistance = level.Size.X * 0.2f;
|
||||
|
||||
bool allowAtStart = prefab.AllowAtStart;
|
||||
bool allowAtEnd = prefab.AllowAtEnd;
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
//in PvP mode, the object must be allowed at both the start and end to be placed at either end
|
||||
//since the 2nd team starts at the end of the level, it'd be unfair to allow e.g. ballast flora to spawn at the end of the level but not the start
|
||||
allowAtEnd = allowAtStart = allowAtEnd && allowAtStart;
|
||||
}
|
||||
|
||||
suitableSpawnPositions.Add(prefab,
|
||||
availableSpawnPositions.Where(sp =>
|
||||
sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
|
||||
sp.Length >= prefab.MinSurfaceWidth &&
|
||||
(prefab.AllowAtStart || !level.IsCloseToStart(sp.GraphEdge.Center, minDistance)) &&
|
||||
(prefab.AllowAtEnd || !level.IsCloseToEnd(sp.GraphEdge.Center, minDistance)) &&
|
||||
(allowAtStart || !level.IsCloseToStart(sp.GraphEdge.Center, minDistance)) &&
|
||||
(allowAtEnd || !level.IsCloseToEnd(sp.GraphEdge.Center, minDistance)) &&
|
||||
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
|
||||
|
||||
spawnPositionWeights.Add(prefab,
|
||||
@@ -407,11 +416,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float minX = spriteCorners.Min(c => c.X) - newObject.Position.Z * ParallaxStrength;
|
||||
float maxX = spriteCorners.Max(c => c.X) + newObject.Position.Z * ParallaxStrength;
|
||||
float minX = spriteCorners.Min(c => c.X) - newObject.Position.Z;
|
||||
float maxX = spriteCorners.Max(c => c.X) + newObject.Position.Z;
|
||||
|
||||
float minY = spriteCorners.Min(c => c.Y) - newObject.Position.Z * ParallaxStrength - level.BottomPos;
|
||||
float maxY = spriteCorners.Max(c => c.Y) + newObject.Position.Z * ParallaxStrength - level.BottomPos;
|
||||
float minY = spriteCorners.Min(c => c.Y) - newObject.Position.Z - level.BottomPos;
|
||||
float maxY = spriteCorners.Max(c => c.Y) + newObject.Position.Z - level.BottomPos;
|
||||
|
||||
if (newObject.Triggers != null)
|
||||
{
|
||||
@@ -446,6 +455,8 @@ namespace Barotrauma
|
||||
#endif
|
||||
objects.Add(newObject);
|
||||
if (newObject.NeedsUpdate) { updateableObjects.Add(newObject); }
|
||||
//add some variance to the Z position to prevent z-fighting
|
||||
//(based on the x and y position of the object, scaled to be visually insignificant)
|
||||
newObject.Position.Z += (minX + minY) % 100.0f * 0.00001f;
|
||||
|
||||
int xStart = (int)Math.Floor(minX / GridSize);
|
||||
@@ -557,7 +568,7 @@ namespace Barotrauma
|
||||
return availableSpawnPositions;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
public void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
GlobalForceDecreaseTimer += deltaTime;
|
||||
if (GlobalForceDecreaseTimer > 1000000.0f)
|
||||
@@ -603,10 +614,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
UpdateProjSpecific(deltaTime);
|
||||
UpdateProjSpecific(deltaTime, cam);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam);
|
||||
|
||||
private void OnObjectTriggered(LevelObject triggeredObject, LevelTrigger trigger, Entity triggerer)
|
||||
{
|
||||
|
||||
+12
-5
@@ -1,7 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -136,6 +135,14 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
[Serialize(3000.0f, IsPropertySaveable.Yes, description: "Objects fade out to the background color of the level the further they are from the camera. This value is the depth at which the object becomes \"maximally\" faded out."), Editable]
|
||||
public float FadeOutDepth
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f),
|
||||
Serialize(0.0f, IsPropertySaveable.Yes, description: "The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are used to determine the probability for an object to spawn at a specific position.")]
|
||||
/// <summary>
|
||||
@@ -339,11 +346,11 @@ namespace Barotrauma
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
//use the maximum width of the sprite as the minimum surface width if no value is given
|
||||
if (element != null && !element.Attributes("minsurfacewidth").Any())
|
||||
//use (a bit less than) the maximum width of the sprite as the minimum surface width if no value is given
|
||||
if (element != null && element.GetAttribute("minsurfacewidth") == null)
|
||||
{
|
||||
if (Sprites.Any()) MinSurfaceWidth = Sprites[0].size.X * MaxSize;
|
||||
if (DeformableSprite != null) MinSurfaceWidth = Math.Max(MinSurfaceWidth, DeformableSprite.Size.X * MaxSize);
|
||||
if (Sprites.Any()) { MinSurfaceWidth = Sprites[0].size.X * MaxSize * 0.8f; }
|
||||
if (DeformableSprite != null) { MinSurfaceWidth = Math.Max(MinSurfaceWidth, DeformableSprite.Size.X * MaxSize * 0.8f); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -55,6 +57,8 @@ namespace Barotrauma
|
||||
private readonly HashSet<Entity> triggerers = new HashSet<Entity>();
|
||||
|
||||
private readonly TriggererType triggeredBy;
|
||||
private readonly Identifier triggerSpeciesOrGroup;
|
||||
private readonly PropertyConditional.LogicalComparison conditionals;
|
||||
|
||||
private readonly float randomTriggerInterval;
|
||||
private readonly float randomTriggerProbability;
|
||||
@@ -255,7 +259,16 @@ namespace Barotrauma
|
||||
string triggeredByStr = element.GetAttributeString("triggeredby", "Character");
|
||||
if (!Enum.TryParse(triggeredByStr, out triggeredBy))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in LevelTrigger config: \"" + triggeredByStr + "\" is not a valid triggerer type.");
|
||||
Identifier speciesOrGroup = triggeredByStr.ToIdentifier();
|
||||
if (CharacterPrefab.Prefabs.Any(p => p.MatchesSpeciesNameOrGroup(speciesOrGroup)))
|
||||
{
|
||||
triggerSpeciesOrGroup = speciesOrGroup;
|
||||
triggeredBy = TriggererType.Character;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in LevelTrigger config: \"" + triggeredByStr + "\" is not a valid triggerer type.");
|
||||
}
|
||||
}
|
||||
if (PhysicsBody != null)
|
||||
{
|
||||
@@ -293,6 +306,8 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
conditionals = PropertyConditional.LoadConditionals(element);
|
||||
|
||||
forceFluctuationTimer = Rand.Range(0.0f, ForceFluctuationInterval);
|
||||
randomTriggerTimer = Rand.Range(0.0f, randomTriggerInterval);
|
||||
@@ -341,7 +356,7 @@ namespace Barotrauma
|
||||
{
|
||||
Entity entity = GetEntity(fixtureB);
|
||||
if (entity == null) { return false; }
|
||||
if (!IsTriggeredByEntity(entity, triggeredBy, mustBeOutside: true)) { return false; }
|
||||
if (!IsTriggeredByEntity(entity, triggeredBy, triggerSpeciesOrGroup: triggerSpeciesOrGroup, conditionals: conditionals, mustBeOutside: true)) { return false; }
|
||||
if (!triggerers.Contains(entity))
|
||||
{
|
||||
if (!IsTriggered)
|
||||
@@ -354,12 +369,22 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsTriggeredByEntity(Entity entity, TriggererType triggeredBy, bool mustBeOutside = false, (bool mustBe, Submarine sub) mustBeOnSpecificSub = default)
|
||||
public static bool IsTriggeredByEntity(
|
||||
Entity entity,
|
||||
TriggererType triggeredBy,
|
||||
Identifier triggerSpeciesOrGroup,
|
||||
PropertyConditional.LogicalComparison conditionals,
|
||||
(bool mustBe, Submarine sub) mustBeOnSpecificSub = default,
|
||||
bool mustBeOutside = false)
|
||||
{
|
||||
if (entity is Character character)
|
||||
{
|
||||
if (mustBeOutside && character.CurrentHull != null) { return false; }
|
||||
if (mustBeOnSpecificSub.mustBe && character.Submarine != mustBeOnSpecificSub.sub) { return false; }
|
||||
if (!triggerSpeciesOrGroup.IsEmpty)
|
||||
{
|
||||
if (character.SpeciesName != triggerSpeciesOrGroup && character.Group != triggerSpeciesOrGroup) { return false; }
|
||||
}
|
||||
if (character.IsHuman)
|
||||
{
|
||||
if (!triggeredBy.HasFlag(TriggererType.Human)) { return false; }
|
||||
@@ -379,6 +404,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (!triggeredBy.HasFlag(TriggererType.Submarine)) { return false; }
|
||||
}
|
||||
if (conditionals != null && entity is ISerializableEntity serializableEntity)
|
||||
{
|
||||
if (!PropertyConditional.CheckConditionals(serializableEntity, conditionals.Conditionals, conditionals.LogicalOperator)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -497,7 +526,7 @@ namespace Barotrauma
|
||||
triggeredTimer = stayTriggeredDelay;
|
||||
if (!wasAlreadyTriggered)
|
||||
{
|
||||
if (!IsTriggeredByEntity(triggerer, triggeredBy, mustBeOutside: true)) { return; }
|
||||
if (!IsTriggeredByEntity(triggerer, triggeredBy, triggerSpeciesOrGroup, conditionals, mustBeOutside: true)) { return; }
|
||||
if (!triggerers.Contains(triggerer))
|
||||
{
|
||||
if (!IsTriggered)
|
||||
@@ -580,6 +609,21 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (PhysicsBody != null)
|
||||
{
|
||||
if (currentForceFluctuation <= 0.0f && statusEffects.None() && attacks.None())
|
||||
{
|
||||
//no force atm, and no status effects or attacks the trigger could apply
|
||||
// -> we can disable the collider and get a minor physics performance improvement
|
||||
PhysicsBody.Enabled = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsBody.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Entity triggerer in triggerers)
|
||||
{
|
||||
if (triggerer.Removed) { continue; }
|
||||
@@ -652,13 +696,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyStatusEffects(List<StatusEffect> statusEffects, Vector2 worldPosition, Entity triggerer, float deltaTime, List<ISerializableEntity> targets)
|
||||
public static void ApplyStatusEffects(List<StatusEffect> statusEffects, Vector2 worldPosition, Entity triggerer, float deltaTime, List<ISerializableEntity> targets, Item targetItem = null)
|
||||
{
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (effect.type == ActionType.OnBroken) { return; }
|
||||
Vector2? position = null;
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This)) { position = worldPosition; }
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
{
|
||||
position = worldPosition;
|
||||
if (targetItem != null)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, triggerer, targetItem.AllPropertyObjects, position);
|
||||
}
|
||||
}
|
||||
if (triggerer is Character character)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, triggerer, character, position);
|
||||
@@ -728,6 +779,8 @@ namespace Barotrauma
|
||||
if (distFactor < 0.0f) return;
|
||||
}
|
||||
|
||||
if (MathUtils.NearlyEqual(currentForceFluctuation, 0.0f)) { return; }
|
||||
|
||||
switch (ForceMode)
|
||||
{
|
||||
case TriggerForceMode.Force:
|
||||
|
||||
Reference in New Issue
Block a user