Merge remote-tracking branch 'origin/master' into dev

# Conflicts:
#	Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
#	Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs
#	Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs
#	Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs
#	Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/Door.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs
#	Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs
#	Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs
#	Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs
#	Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs
#	Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs
#	Barotrauma/BarotraumaClient/ClientSource/Networking/Voip/VoipClient.cs
#	Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen/ServerListScreen.cs
#	Barotrauma/BarotraumaClient/LinuxClient.csproj
#	Barotrauma/BarotraumaClient/MacClient.csproj
#	Barotrauma/BarotraumaClient/WindowsClient.csproj
#	Barotrauma/BarotraumaServer/LinuxServer.csproj
#	Barotrauma/BarotraumaServer/MacServer.csproj
#	Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs
#	Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs
#	Barotrauma/BarotraumaServer/ServerSource/Items/Inventory.cs
#	Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs
#	Barotrauma/BarotraumaServer/ServerSource/Utils/DoSProtection.cs
#	Barotrauma/BarotraumaServer/WindowsServer.csproj
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveIdle.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/ShipCommand/ShipIssueWorkerOperateWeapons.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs
#	Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/Enums.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCFollowAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/LevelResource.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs
#	Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs
#	Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs
#	Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs
#	Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs
#	Barotrauma/BarotraumaShared/changelog.txt
This commit is contained in:
Regalis11
2023-06-15 16:55:39 +03:00
238 changed files with 5395 additions and 3045 deletions
@@ -9,15 +9,17 @@ namespace Barotrauma
{
private LightSource lightSource;
partial void UpdateProjSpecific(float growModifier)
private float particleTimer;
partial void UpdateProjSpecific(float growModifier, float deltaTime)
{
if (this is DummyFireSource)
{
EmitParticles(size, WorldPosition, hull, growModifier, null);
EmitParticles(size, WorldPosition, deltaTime, hull, growModifier, null);
}
else
{
EmitParticles(size, WorldPosition, hull, growModifier, OnChangeHull);
EmitParticles(size, WorldPosition, deltaTime, hull, growModifier, OnChangeHull);
}
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
@@ -25,23 +27,29 @@ namespace Barotrauma
if (Vector2.DistanceSquared(lightSource.Position, position) > 5.0f) { lightSource.Position = position + Vector2.UnitY * 30.0f; }
}
public static void EmitParticles(Vector2 size, Vector2 worldPosition, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)
public void EmitParticles(Vector2 size, Vector2 worldPosition, float deltaTime, Hull hull, float growModifier, Particle.OnChangeHullHandler onChangeHull = null)
{
float particleCount = Rand.Range(0.0f, size.X / 50.0f);
var particlePrefab = ParticleManager.FindPrefab("flame");
if (particlePrefab == null) { return; }
for (int i = 0; i < particleCount; i++)
float particlesPerSecond = MathHelper.Clamp(size.X / 2.0f, 10.0f, 200.0f);
float particleInterval = 1.0f / particlesPerSecond;
particleTimer += deltaTime;
while (particleTimer > particleInterval)
{
particleTimer -= particleInterval;
Vector2 particlePos = new Vector2(
worldPosition.X + Rand.Range(0.0f, size.X),
Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f));
worldPosition.Y - size.Y + particlePrefab.CollisionRadius);
Vector2 particleVel = new Vector2(
particlePos.X - (worldPosition.X + size.X / 2.0f),
Math.Max((float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier, 0.0f));
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
var particle = GameMain.ParticleManager.CreateParticle("flame",
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab,
particlePos, particleVel, 0.0f, hull);
if (particle == null) { continue; }
@@ -54,7 +62,7 @@ namespace Barotrauma
if (Rand.Int(5) == 1)
{
var smokeParticle = GameMain.ParticleManager.CreateParticle("smoke",
particlePos, new Vector2(particleVel.X, particleVel.Y * 0.1f), 0.0f, hull);
particlePos, new Vector2(particleVel.X, particleVel.Y * 0.1f), 0.0f, hull);
if (smokeParticle != null)
{
@@ -27,7 +27,7 @@ namespace Barotrauma
{
foreach (var edge in cell.Edges)
{
if (MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
if (MathUtils.GetLineSegmentIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
{
intersectionFound = true;
particlePos = intersection;
@@ -93,13 +93,16 @@ namespace Barotrauma.Lights
private readonly int thickness;
public bool IsExteriorWall;
public VertexPositionColor[] ShadowVertices { get; private set; }
public VertexPositionTexture[] PenumbraVertices { get; private set; }
public int ShadowVertexCount { get; private set; }
public int PenumbraVertexCount { get; private set; }
/// <summary>
/// Overrides the maximum distance a LOS vertex can be moved to make it align with a nearby LOS segment
/// </summary>
public float? MaxMergeLosVerticesDist;
private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
public MapEntity ParentEntity { get; private set; }
@@ -130,7 +133,7 @@ namespace Barotrauma.Lights
public Rectangle BoundingBox { get; private set; }
public ConvexHull(Rectangle rect, bool? isHorizontal, MapEntity parent)
public ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
{
shadowEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
{
@@ -150,15 +153,15 @@ namespace Barotrauma.Lights
BoundingBox = rect;
this.isHorizontal = isHorizontal ?? BoundingBox.Width > BoundingBox.Height;
this.isHorizontal = isHorizontal;
if (ParentEntity is Structure structure)
{
System.Diagnostics.Debug.Assert(!structure.Removed);
Debug.Assert(!structure.Removed);
isHorizontal = structure.IsHorizontal;
}
else if (ParentEntity is Item item)
{
System.Diagnostics.Debug.Assert(!item.Removed);
Debug.Assert(!item.Removed);
var door = item.GetComponent<Door>();
if (door != null) { isHorizontal = door.IsHorizontal; }
}
@@ -205,44 +208,97 @@ namespace Barotrauma.Lights
{
if (ch == this) { return; }
//hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces)
float mergeDist = MathHelper.Clamp(ch.thickness * 0.55f, 16, 512);
mergeDist = Math.Min(mergeDist, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) / 2);
//merge dist in the direction parallel to the segment
//(e.g. how far up/down we can stretch a vertical segment)
float mergeDistParallel = MathHelper.Clamp(ch.thickness * 0.65f, 16, 512);
if (MaxMergeLosVerticesDist.HasValue)
{
mergeDistParallel = Math.Max(mergeDistParallel, MaxMergeLosVerticesDist.Value);
}
else
{
Rectangle inflatedAABB = ch.BoundingBox;
inflatedAABB.Inflate(2, 2);
//if this los segment isn't touching the other's bounding box,
//don't extend the segment by more than 50% of it's length
if (!inflatedAABB.Contains(losVertices[0].Pos) &&
!inflatedAABB.Contains(losVertices[1].Pos))
{
mergeDistParallel = Math.Min(mergeDistParallel, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) * 0.5f);
}
}
//merge dist in the direction perpendicular to the segment
//(e.g. how far right/left we can stretch a vertical segment)
//do not allow more than ~half of the thickness, because that'd make the segment go outside the convex hull
float mergeDistPerpendicular = Math.Min(mergeDistParallel, thickness * 0.35f);
float mergeDistSqr = mergeDist * mergeDist;
Vector2 center = (losVertices[0].Pos + losVertices[1].Pos) / 2;
bool changed = false;
for (int i = 0; i < losVertices.Length; i++)
{
//find the closest point on the other convex hull segment
Vector2 segmentDir = Vector2.Normalize(losVertices[i].Pos - center);
//check if the closest point on the other convex hull segment is close enough, disregarding any offsets
//otherwise we might end up moving the vertex too much if we stretch it to an already-offset segment
if (!isCloseEnough(
MathUtils.GetClosestPointOnLineSegment(ch.losVertices[0].Pos, ch.losVertices[1].Pos, losVertices[i].Pos),
losVertices[i].Pos))
{
continue;
}
//check the offset position of the segment next
Vector2 closest = MathUtils.GetClosestPointOnLineSegment(
ch.losVertices[0].Pos + ch.losOffsets[0],
ch.losVertices[1].Pos + ch.losOffsets[1],
losVertices[i].Pos);
if (Vector2.DistanceSquared(closest, losVertices[i].Pos) > mergeDistSqr) { continue; }
if (!isCloseEnough(closest, losVertices[i].Pos)) { continue; }
//find where the segments would intersect if they had infinite length
// if it's close to the closest point, let's use that instead to keep
// the direction of the segment unchanged (i.e. vertical segment stays vertical)
if (MathUtils.GetLineIntersection(
ch.losVertices[0].Pos + ch.losOffsets[0],
ch.losVertices[1].Pos + ch.losOffsets[1],
losVertices[0].Pos,
losVertices[1].Pos,
out Vector2 intersection))
ch.losVertices[0].Pos + ch.losOffsets[0], ch.losVertices[1].Pos + ch.losOffsets[1],
losVertices[0].Pos, losVertices[1].Pos,
areLinesInfinite: true, out Vector2 intersection) &&
//the intersection needs to be outwards from the vertex we're checking
Vector2.Dot(segmentDir, intersection - losVertices[i].Pos) > 0 &&
//the intersection needs to be close enough to the default position of the vertex and the closest point
//(we don't want to merge the segments somewhere close to infinity!)
(Vector2.DistanceSquared(intersection, losVertices[i].Pos) < mergeDistParallel * mergeDistParallel ||
Vector2.DistanceSquared(intersection, closest) < 16.0f * 16.0f))
{
if (Vector2.DistanceSquared(intersection, losVertices[i].Pos) < mergeDistSqr ||
Vector2.DistanceSquared(intersection, closest) < 16.0f * 16.0f)
{
closest = intersection;
}
closest = intersection;
}
//don't move the vertices of the segment too close to each other
if (Vector2.DistanceSquared(losVertices[1 - i].Pos + losOffsets[1 - i], closest) < mergeDistPerpendicular * mergeDistPerpendicular)
{
continue;
}
losOffsets[i] = closest - losVertices[i].Pos;
overlappingHulls.Add(ch);
ch.overlappingHulls.Add(this);
changed = true;
bool isCloseEnough(Vector2 closest, Vector2 vertex)
{
float dist = Vector2.Distance(closest, vertex);
if (dist < 0.001f) { return true; }
if (dist > mergeDistParallel) { return false; }
Vector2 closestDir = (closest - vertex) / dist;
float dot = Math.Abs(Vector2.Dot(segmentDir, closestDir));
float distAlongAxis = dist * dot;
if (distAlongAxis > mergeDistParallel) { return false; }
float distPerpendicular = dist * (1.0f - dot);
if (distPerpendicular > mergeDistPerpendicular) { return false; }
return true;
}
}
if (changed && refreshOtherOverlappingHulls)
{
@@ -253,6 +309,13 @@ namespace Barotrauma.Lights
}
}
public bool LosIntersects(Vector2 pos1, Vector2 pos2)
{
return MathUtils.LineSegmentsIntersect(
losVertices[0].Pos + losOffsets[0], losVertices[1].Pos + losOffsets[1],
pos1, pos2);
}
public void Rotate(Vector2 origin, float amount)
{
Matrix rotationMatrix =
@@ -347,6 +410,7 @@ namespace Barotrauma.Lights
for (int i = 0; i < 2; i++)
{
losVertices[i] = new SegmentPoint(losPoints[i], this);
losOffsets[i] = Vector2.Zero;
}
overlappingHulls.Clear();
@@ -612,8 +676,11 @@ namespace Barotrauma.Lights
vertexPos0 += ParentEntity.Submarine.DrawPosition;
vertexPos1 += ParentEntity.Submarine.DrawPosition;
}
Vector2 viewTargetPos = LightManager.ViewTarget.WorldPosition;
float alpha = IsSegmentFacing(vertexPos0, vertexPos1, viewTargetPos) ? 1.0f : 0.5f;
float alpha = 1.0f;
if (LightManager.ViewTarget != null)
{
alpha = IsSegmentFacing(vertexPos0, vertexPos1, LightManager.ViewTarget.WorldPosition) ? 1.0f : 0.5f;
}
vertexPos0.Y = -vertexPos0.Y;
vertexPos1.Y = -vertexPos1.Y;
GUI.DrawLine(spriteBatch, vertexPos0, vertexPos1, color * alpha, width: width);
@@ -189,7 +189,7 @@ namespace Barotrauma.Lights
}
}
private class RayCastTask
private sealed class RayCastTask
{
public LightSource LightSource;
public Vector2 DrawPos;
@@ -298,7 +298,8 @@ namespace Barotrauma.Lights
range *
((Character.Controlled?.Submarine != null && light.ParentSub == Character.Controlled?.Submarine) ? 2.0f : 1.0f) *
(light.CastShadows ? 10.0f : 1.0f) *
(light.LightSourceParams.OverrideLightSpriteAlpha ?? (light.Color.A / 255.0f));
(light.LightSourceParams.OverrideLightSpriteAlpha ?? (light.Color.A / 255.0f)) *
light.PriorityMultiplier;
}
//find the lights with an active light volume
@@ -477,6 +478,17 @@ namespace Barotrauma.Lights
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
}
if (ConnectionPanel.ShouldDebugDrawWiring)
{
foreach (MapEntity e in (Submarine.VisibleEntities ?? MapEntity.mapEntityList))
{
if (e is Item item && item.GetComponent<Wire>() is Wire wire)
{
wire.DebugDraw(spriteBatch, alpha: 0.4f);
}
}
}
lightEffect.World = transform;
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
@@ -686,19 +698,42 @@ namespace Barotrauma.Lights
if (LosEnabled && LosMode != LosMode.None && ViewTarget != null)
{
Vector2 pos = ViewTarget.DrawPosition;
if (ViewTarget is Character character &&
bool centeredOnHead = false;
if (ViewTarget is Character character &&
character.AnimController?.GetLimb(LimbType.Head) is Limb head &&
!head.IsSevered && !head.Removed)
{
pos = head.body.DrawPosition;
centeredOnHead = true;
}
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
Matrix shadowTransform = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
var convexHulls = ConvexHull.GetHullsInRange(ViewTarget.Position, cam.WorldView.Width * 0.75f, ViewTarget.Submarine);
//make sure the head isn't peeking through any LOS segments, and if it is,
//center the LOS on the character's collider instead
if (centeredOnHead)
{
foreach (var ch in convexHulls)
{
Vector2 currentViewPos = pos;
Vector2 defaultViewPos = ViewTarget.DrawPosition;
if (ch.ParentEntity?.Submarine != null)
{
defaultViewPos -= ch.ParentEntity.Submarine.DrawPosition;
currentViewPos -= ch.ParentEntity.Submarine.DrawPosition;
}
//check if a line from the character's collider to the head intersects with the los segment (= head poking through it)
if (ch.LosIntersects(defaultViewPos, currentViewPos))
{
pos = ViewTarget.DrawPosition;
}
}
}
if (convexHulls != null)
{
List<VertexPositionColor> shadowVerts = new List<VertexPositionColor>();
@@ -706,7 +741,6 @@ namespace Barotrauma.Lights
foreach (ConvexHull convexHull in convexHulls)
{
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
if (LosMode == LosMode.BlockOutsideView && !convexHull.IsExteriorWall) { continue; };
Vector2 relativeLightPos = pos;
if (convexHull.ParentEntity?.Submarine != null) { relativeLightPos -= convexHull.ParentEntity.Submarine.Position; }
@@ -744,13 +778,14 @@ namespace Barotrauma.Lights
public void DebugDrawLos(SpriteBatch spriteBatch, Camera cam)
{
if (ViewTarget == null) { return; }
Vector2 pos = ViewTarget?.Position ?? cam.Position;
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform);
var convexHulls = ConvexHull.GetHullsInRange(ViewTarget.Position, cam.WorldView.Width * 0.75f, ViewTarget?.Submarine);
var convexHulls = ConvexHull.GetHullsInRange(pos, cam.WorldView.Width * 0.75f, ViewTarget?.Submarine);
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
foreach (ConvexHull convexHull in convexHulls)
{
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
if (convexHull.ParentEntity is Structure { CastShadow: false }) { continue; }
convexHull.DebugDraw(spriteBatch);
}
spriteBatch.End();
@@ -53,10 +53,6 @@ namespace Barotrauma.Lights
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
public float Rotation { get; set; }
[Serialize(false, IsPropertySaveable.Yes, "Directional lights only shine in \"one direction\", meaning no shadows are cast behind them."+
" Note that this does not affect how the light texture is drawn: if you want something like a conical spotlight, you should use an appropriate texture for that.")]
public bool Directional { get; set; }
public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)));
private float flicker;
@@ -233,6 +229,7 @@ namespace Barotrauma.Lights
//do we need to recalculate the vertices of the light volume
private bool needsRecalculation;
private bool needsRecalculationWhenUpToDate;
public bool NeedsRecalculation
{
get { return needsRecalculation; }
@@ -246,9 +243,15 @@ namespace Barotrauma.Lights
}
}
needsRecalculation = value;
if (needsRecalculation && state != LightVertexState.UpToDate)
{
//if we're currently recalculating light vertices, mark that we need to recalculate them again after it's done
needsRecalculationWhenUpToDate = true;
}
}
}
//when were the vertices of the light volume last calculated
public float LastRecalculationTime { get; private set; }
@@ -309,8 +312,6 @@ namespace Barotrauma.Lights
if (Math.Abs(value - rotation) < 0.001f) { return; }
rotation = value;
dir = new Vector2(MathF.Cos(rotation), -MathF.Sin(rotation));
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
{
return;
@@ -321,8 +322,6 @@ namespace Barotrauma.Lights
}
}
private Vector2 dir = Vector2.UnitX;
private Vector2 _spriteScale = Vector2.One;
public Vector2 SpriteScale
@@ -396,6 +395,8 @@ namespace Barotrauma.Lights
public float Priority;
public float PriorityMultiplier = 1.0f;
private Vector2 lightTextureTargetSize;
public Vector2 LightTextureTargetSize
@@ -444,7 +445,7 @@ namespace Barotrauma.Lights
public bool Enabled = true;
private readonly ISerializableEntity conditionalTarget;
private readonly PropertyConditional.LogicalOperatorType logicalOperator;
private readonly PropertyConditional.Comparison comparison;
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
public LightSource(ContentXElement element, ISerializableEntity conditionalTarget = null)
@@ -452,8 +453,11 @@ namespace Barotrauma.Lights
{
lightSourceParams = new LightSourceParams(element);
CastShadows = element.GetAttributeBool("castshadows", true);
logicalOperator = element.GetAttributeEnum(nameof(logicalOperator),
element.GetAttributeEnum("comparison", logicalOperator));
string comparison = element.GetAttributeString("comparison", null);
if (comparison != null)
{
Enum.TryParse(comparison, ignoreCase: true, out this.comparison);
}
if (lightSourceParams.DeformableLightSpriteElement != null)
{
@@ -466,7 +470,13 @@ namespace Barotrauma.Lights
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "conditional":
conditionals.AddRange(PropertyConditional.FromXElement(subElement));
foreach (XAttribute attribute in subElement.Attributes())
{
if (PropertyConditional.IsValid(attribute))
{
conditionals.Add(new PropertyConditional(attribute));
}
}
break;
}
}
@@ -529,32 +539,11 @@ namespace Barotrauma.Lights
var fullChList = ConvexHull.HullLists.FirstOrDefault(chList => chList.Submarine == sub);
if (fullChList == null) { return; }
//used to check whether the lightsource hits the target hull if the light is directional
Vector2 ray = new Vector2(dir.X, -dir.Y) * TextureRange;
Vector2 normal = new Vector2(-ray.Y, ray.X);
chList.List.Clear();
foreach (var convexHull in fullChList.List)
{
if (!convexHull.Enabled) { continue; }
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, convexHull.BoundingBox)) { continue; }
if (lightSourceParams.Directional && false)
{
Rectangle bounds = convexHull.BoundingBox;
//invert because GetLineRectangleIntersection uses the messed up rects that start from top-left
bounds.Y -= bounds.Height;
//the ray can't hit if
// center is in the opposite direction from the ray (cheapest check first)
if (Vector2.Dot(ray, convexHull.BoundingBox.Center.ToVector2() - lightPos) <= 0 &&
/*ray doesn't hit the convex hull*/
!MathUtils.GetLineRectangleIntersection(lightPos, lightPos + ray, bounds, out _) &&
/*normal vectors of the ray don't hit the convex hull */
!MathUtils.GetLineRectangleIntersection(lightPos + normal, lightPos - normal, bounds, out _))
{
continue;
}
}
chList.List.Add(convexHull);
}
chList.IsHidden.RemoveWhere(ch => !chList.List.Contains(ch));
@@ -704,7 +693,7 @@ namespace Barotrauma.Lights
lock (mutex)
{
hull.RefreshWorldPositions();
hull.GetVisibleSegments(drawPos, visibleSegments);
hull.GetVisibleSegments(drawPos, visibleSegments);
foreach (var visibleSegment in visibleSegments)
{
if (visibleSegment.ConvexHull?.ParentEntity?.Submarine != null)
@@ -713,7 +702,6 @@ namespace Barotrauma.Lights
}
}
}
}
}
foreach (ConvexHull hull in chList.List)
@@ -812,7 +800,7 @@ namespace Barotrauma.Lights
}
else
{
intersects = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b, out intersection);
intersects = MathUtils.GetLineSegmentIntersection(p1a, p1b, p2a, p2b, out intersection);
}
if (intersects)
@@ -1006,7 +994,7 @@ namespace Barotrauma.Lights
}
else
{
intersects = MathUtils.GetLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection);
intersects = MathUtils.GetLineSegmentIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, out intersection);
}
if (intersects)
@@ -1041,9 +1029,11 @@ namespace Barotrauma.Lights
Vector2 drawPos = calculatedDrawPos;
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
Vector2 uvOffset = Vector2.Zero;
Vector2 overrideTextureDims = Vector2.One;
Vector2 dir = this.dir;
if (OverrideLightTexture != null)
{
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
@@ -1052,7 +1042,8 @@ namespace Barotrauma.Lights
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
dir = -dir;
cosAngle = -cosAngle;
sinAngle = -sinAngle;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
@@ -1125,8 +1116,8 @@ namespace Barotrauma.Lights
//calculate texture coordinates based on the light's rotation
Vector2 originDiff = diff;
diff.X = originDiff.X * dir.X - originDiff.Y * dir.Y;
diff.Y = originDiff.X * dir.Y + originDiff.Y * dir.X;
diff.X = originDiff.X * cosAngle - originDiff.Y * sinAngle;
diff.Y = originDiff.X * sinAngle + originDiff.Y * cosAngle;
diff *= (overrideTextureDims / OverrideLightTexture.size);// / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
diff += uvOffset;
}
@@ -1231,6 +1222,9 @@ namespace Barotrauma.Lights
}
drawPos.Y = -drawPos.Y;
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
float bounds = TextureRange;
if (OverrideLightTexture != null)
@@ -1242,8 +1236,8 @@ namespace Barotrauma.Lights
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
origin *= TextureRange;
drawPos.X += origin.X * dir.Y + origin.Y * dir.X;
drawPos.Y += origin.X * dir.X + origin.Y * dir.Y;
drawPos.X += origin.X * sinAngle + origin.Y * cosAngle;
drawPos.Y += origin.X * cosAngle + origin.Y * sinAngle;
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
@@ -1349,7 +1343,7 @@ namespace Barotrauma.Lights
{
if (conditionals.None()) { return; }
if (conditionalTarget == null) { return; }
if (logicalOperator == PropertyConditional.LogicalOperatorType.And)
if (comparison == PropertyConditional.Comparison.And)
{
Enabled = conditionals.All(c => c.Matches(conditionalTarget));
}
@@ -1405,7 +1399,9 @@ namespace Barotrauma.Lights
CalculateLightVertices(verts);
LastRecalculationTime = (float)Timing.TotalTime;
NeedsRecalculation = false;
NeedsRecalculation = needsRecalculationWhenUpToDate;
needsRecalculationWhenUpToDate = false;
state = LightVertexState.UpToDate;
}
}
@@ -56,12 +56,12 @@ namespace Barotrauma
}
private static readonly List<RoundSound> roundSounds = new List<RoundSound>();
private static readonly Dictionary<string, RoundSound> roundSoundByPath = new Dictionary<string, RoundSound>();
public static RoundSound? Load(ContentXElement element, bool stream = false)
{
if (GameMain.SoundManager?.Disabled ?? true) { return null; }
var filename = element.GetAttributeContentPath("file") ?? element.GetAttributeContentPath("sound");
if (filename is null)
{
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
@@ -70,7 +70,11 @@ namespace Barotrauma
return null;
}
Sound? existingSound = roundSounds.Find(s => s.Filename == filename?.FullPath && s.Stream == stream && s.Sound is { Disposed: false })?.Sound;
Sound? existingSound = null;
if (roundSoundByPath.TryGetValue(filename.FullPath, out RoundSound? rs) && rs.Sound is { Disposed: false })
{
existingSound = rs.Sound;
}
if (existingSound is null)
{
@@ -99,7 +103,10 @@ namespace Barotrauma
}
RoundSound newSound = new RoundSound(element, existingSound);
if (filename is not null && !newSound.Stream)
{
roundSoundByPath.TryAdd(filename.FullPath, newSound);
}
roundSounds.Add(newSound);
return newSound;
}
@@ -124,24 +131,14 @@ namespace Barotrauma
roundSound.Sound = existingSound;
}
private static void Remove(RoundSound roundSound)
{
#warning TODO: what is going on here????
roundSound.Sound?.Dispose();
if (roundSounds.Contains(roundSound)) { roundSounds.Remove(roundSound); }
foreach (RoundSound otherSound in roundSounds)
{
if (otherSound.Sound == roundSound.Sound) { otherSound.Sound = null; }
}
}
public static void RemoveAllRoundSounds()
{
for (int i = roundSounds.Count - 1; i >= 0; i--)
foreach (var roundSound in roundSounds)
{
Remove(roundSounds[i]);
roundSound.Sound?.Dispose();
}
roundSounds.Clear();
roundSoundByPath.Clear();
}
}
}
@@ -58,11 +58,8 @@ namespace Barotrauma
convexHulls ??= new List<ConvexHull>();
var h = new ConvexHull(
new Rectangle((position - size / 2).ToPoint(), size.ToPoint()),
IsHorizontal,
this)
{
IsExteriorWall = IsExteriorWall
};
IsHorizontal,
this);
if (Math.Abs(rotation) > 0.001f)
{
h.Rotate(position, rotation);
@@ -501,7 +498,7 @@ namespace Barotrauma
private bool ConditionalMatches(PropertyConditional conditional)
{
if (!string.IsNullOrEmpty(conditional.TargetItemComponent))
if (!string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
return false;
}
@@ -116,11 +116,11 @@ namespace Barotrauma
foreach (MapEntity e in entitiesToRender)
{
if (!e.DrawOverWater) continue;
if (!e.DrawOverWater) { continue; }
if (predicate != null)
{
if (!predicate(e)) continue;
if (!predicate(e)) { continue; }
}
e.Draw(spriteBatch, editing, false);
@@ -1,6 +1,7 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -18,7 +19,7 @@ namespace Barotrauma
return;
}
List<Submarine> subsToMove = submarine.GetConnectedSubs();
var subsToMove = submarine.GetConnectedSubs();
foreach (Submarine dockedSub in subsToMove)
{
if (dockedSub == submarine) { continue; }
@@ -51,7 +52,6 @@ namespace Barotrauma
sub.PhysicsBody.SetTransformIgnoreContacts(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
}
}
if (closestSub != null && subsToMove.Contains(closestSub))
{
GameMain.GameScreen.Cam.Position += moveAmount;
@@ -67,6 +67,10 @@ namespace Barotrauma
else if (ConnectedDoor != null)
{
sprite = iconSprites["Door"];
if (ConnectedDoor.IsHorizontal && Ladders == null)
{
clr = Color.Yellow;
}
}
else if (Ladders != null)
{