Unstable 0.1500.7.0 (No edition)

This commit is contained in:
Markus Isberg
2021-10-14 00:42:06 +09:00
parent c8943ef9c4
commit de917c5d74
105 changed files with 871 additions and 443 deletions
@@ -103,6 +103,9 @@ namespace Barotrauma
!pathSteering.CurrentPath.Unreachable &&
(!requireNonDirty || !pathSteering.IsPathDirty);
public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable;
public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished;
protected readonly float colliderWidth;
protected readonly float minGapSize;
protected readonly float colliderLength;
@@ -412,7 +415,7 @@ namespace Barotrauma
}
else if (EscapeTarget != null && EscapeTarget.FlowTargetHull != Character.CurrentHull)
{
if (pathSteering.CurrentPath != null && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
if (IsCurrentPathUnreachable)
{
unreachableGaps.Add(EscapeTarget);
EscapeTarget = null;
@@ -453,7 +453,7 @@ namespace Barotrauma
if (SelectedAiTarget?.Entity != null || EscapeTarget != null)
{
Entity t = SelectedAiTarget?.Entity ?? EscapeTarget;
float referencePos = Vector2.DistanceSquared(Character.WorldPosition, t.WorldPosition) > 100 * 100 && HasValidPath(true) ? PathSteering.CurrentPath.CurrentNode.WorldPosition.X : t.WorldPosition.X;
float referencePos = Vector2.DistanceSquared(Character.WorldPosition, t.WorldPosition) > 100 * 100 && HasValidPath(requireNonDirty: true) ? PathSteering.CurrentPath.CurrentNode.WorldPosition.X : t.WorldPosition.X;
Character.AnimController.TargetDir = Character.WorldPosition.X < referencePos ? Direction.Right : Direction.Left;
}
else
@@ -916,9 +916,7 @@ namespace Barotrauma
{
if (SteeringManager is IndoorsSteeringManager pathSteering)
{
if (patrolTarget == null ||
pathSteering.CurrentPath == null ||
!pathSteering.IsPathDirty && (pathSteering.CurrentPath.Finished || pathSteering.CurrentPath.Unreachable))
if (patrolTarget == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
{
newPatrolTargetTimer = Math.Min(newPatrolTargetTimer, newPatrolTargetIntervalMin);
}
@@ -936,8 +934,7 @@ namespace Barotrauma
else if (targetHulls.Any())
{
patrolTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, minGapSize: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n));
var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, Character.Submarine, minGapSize: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n));
if (path.Unreachable)
{
//can't go to this room, remove it from the list and try another room
@@ -2331,34 +2328,32 @@ namespace Barotrauma
SelectedAiTarget.Entity is Character c && VisibleHulls.Contains(c.CurrentHull))
{
// Steer towards the target if in the same room and swimming
Vector2 dir = Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition);
if (MathUtils.IsValid(dir))
{
SteeringManager.SteeringManual(deltaTime, dir);
}
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition));
}
else
{
// Use path finding
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), weight: 2, minGapWidth: minGapSize);
if (!PathSteering.IsPathDirty && PathSteering.CurrentPath.Unreachable)
{
// Can't reach
State = AIState.Idle;
IgnoreTarget(SelectedAiTarget);
return;
}
}
}
else
{
// Outside
SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 5);
if (Character.AnimController.InWater)
}
if (steeringManager is IndoorsSteeringManager pathSteering)
{
if (!pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
// Can't reach
State = AIState.Idle;
IgnoreTarget(SelectedAiTarget);
}
}
else if (Character.AnimController.InWater)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
}
}
#region Targeting
@@ -2510,6 +2505,11 @@ namespace Barotrauma
else if (targetingFromOutsideToInside)
{
targetingTag = "room";
if (item.Submarine?.Info.IsRuin != null)
{
// Ignore ruin items when the creature is outside.
continue;
}
}
}
else if (targetingTag == "nasonov")
@@ -115,6 +115,11 @@ namespace Barotrauma
IsPathDirty = true;
}
public void SteeringSeekSimple(Vector2 targetSimPos, float weight = 1)
{
steering += base.DoSteeringSeek(targetSimPos, weight);
}
public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
{
steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
@@ -164,7 +169,7 @@ namespace Barotrauma
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
{
bool needsNewPath = currentPath == null || currentPath.Unreachable;
bool needsNewPath = currentPath == null || currentPath.Unreachable || currentPath.Finished;
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
{
Vector2 targetDiff = target - currentTarget;
@@ -194,16 +199,8 @@ namespace Barotrauma
SkipCurrentPathNodes();
currentTarget = target;
Vector2 currentPos = host.SimPosition;
if (character != null && character.Submarine == null)
{
var targetHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(target), null, false);
if (targetHull != null && targetHull.Submarine != null)
{
currentPos -= targetHull.Submarine.SimPosition;
}
}
pathFinder.InsideSubmarine = character.Submarine != null;
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
@@ -218,7 +215,7 @@ namespace Barotrauma
// Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us).
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t);
if (!useNewPath && character.Submarine != null)
if (!useNewPath)
{
// It's possible that the current path was calculated from a start point that is no longer valid.
// Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node.
@@ -322,15 +319,26 @@ namespace Barotrauma
doorsChecked = true;
}
Vector2 pos = host.SimPosition;
if (character != null && CurrentPath.CurrentNode?.Submarine != null)
if (character != null && CurrentPath.CurrentNode != null)
{
if (character.Submarine == null)
var nodeSub = CurrentPath.CurrentNode.Submarine;
if (nodeSub != null)
{
pos -= CurrentPath.CurrentNode.Submarine.SimPosition;
if (character.Submarine == null)
{
// Going inside
pos -= ConvertUnits.ToSimUnits(nodeSub.Position);
}
else if (character.Submarine != nodeSub)
{
// Different subs
pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position);
}
}
else if (character.Submarine != currentPath.CurrentNode.Submarine)
else if (character.Submarine != null)
{
pos -= ConvertUnits.ToSimUnits(currentPath.CurrentNode.Submarine.Position - character.Submarine.Position);
// Going outside
pos += ConvertUnits.ToSimUnits(character.Submarine.Position);
}
}
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
@@ -724,7 +724,10 @@ namespace Barotrauma
}
if (retreatTarget != null && character.CurrentHull != retreatTarget)
{
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true),
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)
{
UsePathingOutside = false
},
onAbandon: () =>
{
if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull))
@@ -783,6 +786,7 @@ namespace Barotrauma
TryAddSubObjective(ref followTargetObjective,
constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true, closeEnough: 50)
{
UsePathingOutside = false,
IgnoreIfTargetDead = true,
DialogueIdentifier = "dialogcannotreachtarget",
TargetName = Enemy.DisplayName,
@@ -334,7 +334,7 @@ namespace Barotrauma
continue;
}
// Don't allow to go outside if not already outside.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition, nodeFilter: node => node.Waypoint.CurrentHull != null);
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition, character.Submarine, nodeFilter: node => node.Waypoint.CurrentHull != null);
if (path.Unreachable)
{
HumanAIController.UnreachableHulls.Add(hull);
@@ -323,7 +323,7 @@ namespace Barotrauma
// This is relatively expensive, so let's do this only when it significantly improves the behavior.
// Only allow one path find call per frame.
hasCalledPathFinder = true;
var path = PathSteering.PathFinder.FindPath(character.SimPosition, item.SimPosition, errorMsgStr: $"AIObjectiveGetItem {character.DisplayName}", nodeFilter: node => node.Waypoint.CurrentHull != null);
var path = PathSteering.PathFinder.FindPath(character.SimPosition, item.SimPosition, character.Submarine, errorMsgStr: $"AIObjectiveGetItem {character.DisplayName}", nodeFilter: node => node.Waypoint.CurrentHull != null);
if (path.Unreachable) { continue; }
}
currItemPriority = itemPriority;
@@ -27,6 +27,7 @@ namespace Barotrauma
public bool followControlledCharacter;
public bool mimic;
public bool SpeakIfFails { get; set; } = true;
public bool UsePathingOutside { get; set; } = true;
public float extraDistanceWhileSwimming;
public float extraDistanceOutsideSub;
@@ -121,13 +122,14 @@ namespace Barotrauma
}
private readonly float avoidLookAheadDistance = 5;
private readonly float pathWaitingTime = 3;
public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0)
: base(character, objectiveManager, priorityModifier)
{
Target = target;
this.repeat = repeat;
waitUntilPathUnreachable = 3.0f;
waitUntilPathUnreachable = pathWaitingTime;
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
if (Target is Item i)
{
@@ -186,7 +188,6 @@ namespace Barotrauma
// Wait
character.AIController.SteeringManager.Reset();
}
waitUntilPathUnreachable -= deltaTime;
if (!character.IsClimbing)
{
character.SelectedConstruction = null;
@@ -222,11 +223,13 @@ namespace Barotrauma
{
Abandon = true;
}
else if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.Unreachable && !PathSteering.IsPathDirty)
else if (HumanAIController.IsCurrentPathUnreachable)
{
waitUntilPathUnreachable -= deltaTime;
SteeringManager.Reset();
if (waitUntilPathUnreachable < 0)
{
waitUntilPathUnreachable = pathWaitingTime;
if (repeat)
{
SpeakCannotReach();
@@ -325,25 +328,29 @@ namespace Barotrauma
}
else
{
SeekGaps(maxGapDistance);
seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
if (TargetGap != null)
bool isRuins = character.Submarine?.Info.IsRuin != null || Target.Submarine?.Info.IsRuin != null;
if (!isRuins || !HumanAIController.HasValidPath(requireNonDirty: true, requireUnfinished: true))
{
// Check that nothing is blocking the way
Vector2 rayStart = character.SimPosition;
Vector2 rayEnd = TargetGap.SimPosition;
if (TargetGap.Submarine != null && character.Submarine == null)
SeekGaps(maxGapDistance);
seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
if (TargetGap != null)
{
rayStart -= TargetGap.Submarine.SimPosition;
}
else if (TargetGap.Submarine == null && character.Submarine != null)
{
rayEnd -= character.Submarine.SimPosition;
}
var closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
if (closestBody != null)
{
TargetGap = null;
// Check that nothing is blocking the way
Vector2 rayStart = character.SimPosition;
Vector2 rayEnd = TargetGap.SimPosition;
if (TargetGap.Submarine != null && character.Submarine == null)
{
rayStart -= TargetGap.Submarine.SimPosition;
}
else if (TargetGap.Submarine == null && character.Submarine != null)
{
rayEnd -= character.Submarine.SimPosition;
}
var closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
if (closestBody != null)
{
TargetGap = null;
}
}
}
}
@@ -454,19 +461,25 @@ namespace Barotrauma
}
else if (!isInside && HumanAIController.UseIndoorSteeringOutside)
{
if (character.Submarine == null && Target.Submarine != null)
{
targetPos += Target.Submarine.SimPosition;
}
nodeFilter = n => n.Waypoint.Tunnel != null;
nodeFilter = n => n.Waypoint.Submarine == null;
}
PathSteering.SteeringSeek(targetPos, weight: 1,
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
endNodeFilter: endNodeFilter,
nodeFilter: nodeFilter,
checkVisiblity: CheckVisibility);
if (!isInside && !UsePathingOutside)
{
PathSteering.SteeringSeekSimple(character.GetRelativeSimPosition(Target), 10);
if (character.AnimController.InWater)
{
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 15);
}
}
else
{
PathSteering.SteeringSeek(targetPos, weight: 1,
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
endNodeFilter: endNodeFilter,
nodeFilter: nodeFilter,
checkVisiblity: CheckVisibility);
}
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
{
if (useScooter)
@@ -251,7 +251,7 @@ namespace Barotrauma
currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
bool isInWrongSub = (character.TeamID == CharacterTeamType.FriendlyNPC && !character.IsEscorted) && character.Submarine.TeamID != character.TeamID;
bool isCurrentHullAllowed = !isInWrongSub && !IsForbidden(character.CurrentHull);
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsgStr: null, nodeFilter: node =>
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, character.Submarine, nodeFilter: node =>
{
if (node.Waypoint.CurrentHull == null) { return false; }
// Check that there is no unsafe hulls on the way to the target
@@ -1,7 +1,6 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -69,7 +68,7 @@ namespace Barotrauma
HumanAIController.ResetEscape();
}
HumanAIController.Escape(deltaTime);
if (HumanAIController.EscapeTarget == null || !HumanAIController.HasValidPath(requireNonDirty: true, requireUnfinished: false))
if (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable)
{
Abandon = true;
}
@@ -92,14 +91,16 @@ namespace Barotrauma
{
RemoveSubObjective(ref moveInCaveObjective);
RemoveSubObjective(ref moveOutsideObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveInsideObjective,
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
onCompleted: () => moveInsideObjective = null);
onCompleted: () => RemoveSubObjective(ref moveInsideObjective),
onAbandon: () => Abandon = true);
}
else
{
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveInsideObjective'");
#endif
}
}
}
@@ -117,8 +118,7 @@ namespace Barotrauma
float closestDistance = float.MaxValue;
foreach (var w in WayPoint.WayPointList)
{
if (w.Tunnel == null) { continue; }
if (w.Tunnel.Type == Level.TunnelType.Cave) { continue; }
if (w.Tunnel != null && w.Tunnel.Type == Level.TunnelType.Cave) { continue; }
if (w.linkedTo.None(l => l is WayPoint linkedWaypoint && linkedWaypoint.Tunnel?.Type == Level.TunnelType.Cave)) { continue; }
float distance = Vector2.DistanceSquared(character.WorldPosition, w.WorldPosition);
if (closestOutsideWaypoint == null || distance < closestDistance)
@@ -131,17 +131,19 @@ namespace Barotrauma
{
RemoveSubObjective(ref moveInsideObjective);
RemoveSubObjective(ref moveOutsideObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveInCaveObjective,
constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager)
{
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint
},
onCompleted: () => moveInCaveObjective = null);
onCompleted: () => RemoveSubObjective(ref moveInCaveObjective),
onAbandon: () => Abandon = true);
}
else
{
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable main or side path node target found for 'moveOutsideObjective'");
#endif
}
}
else
@@ -167,14 +169,16 @@ namespace Barotrauma
{
RemoveSubObjective(ref moveInsideObjective);
RemoveSubObjective(ref moveInCaveObjective);
// TODO: Check 'repeat' and 'onAbandon' parameters
TryAddSubObjective(ref moveOutsideObjective,
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
onCompleted: () => moveOutsideObjective = null);
onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
onAbandon: () => Abandon = true);
}
else
{
#if DEBUG
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'");
#endif
}
}
}
@@ -182,19 +186,11 @@ namespace Barotrauma
{
if (HumanAIController.IsInsideCave)
{
if (moveOutsideObjective != null)
{
RemoveSubObjective(ref moveOutsideObjective);
moveOutsideObjective = null;
}
RemoveSubObjective(ref moveOutsideObjective);
}
else
{
if (moveInCaveObjective != null)
{
RemoveSubObjective(ref moveInCaveObjective);
moveInCaveObjective = null;
}
RemoveSubObjective(ref moveInCaveObjective);
}
}
usingEscapeBehavior = shouldUseEscapeBehavior;
@@ -119,7 +119,8 @@ namespace Barotrauma
public PathFinder(List<WayPoint> wayPoints, bool isCharacter)
{
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => (w.Submarine != null == isCharacter) || (isCharacter && w.Tunnel != null)), removeOrphans: true);
var filtered = isCharacter ? wayPoints : wayPoints.FindAll(w => w.Submarine == null);
nodes = PathNode.GenerateNodes(filtered, removeOrphans: true);
foreach (WayPoint wp in wayPoints)
{
wp.OnLinksChanged += WaypointLinksChanged;
@@ -179,17 +180,37 @@ namespace Barotrauma
foreach (PathNode node in nodes)
{
node.TempPosition = node.Position;
if (hostSub != null)
var wpSub = node.Waypoint.Submarine;
if (hostSub != null && wpSub == null)
{
Vector2 diff = node.Waypoint.Submarine != null ?
hostSub.SimPosition - node.Waypoint.Submarine.SimPosition :
hostSub.SimPosition - node.Waypoint.SimPosition;
node.TempPosition -= diff;
// inside and targeting outside
node.TempPosition -= hostSub.SimPosition;
}
else if (wpSub != null && hostSub != null && wpSub != hostSub)
{
// different subs
node.TempPosition -= hostSub.SimPosition - wpSub.SimPosition;
}
else if (hostSub == null && wpSub != null)
{
// Outside and targeting inside
node.TempPosition += wpSub.SimPosition;
}
float xDiff = Math.Abs(start.X - node.TempPosition.X);
float yDiff = Math.Abs(start.Y - node.TempPosition.Y);
if (yDiff > 1.0f && node.Waypoint.Ladders == null && node.Waypoint.Stairs == null) { yDiff += 10.0f; }
node.TempDistance = xDiff + (InsideSubmarine ? yDiff * 10.0f : yDiff); //higher cost for vertical movement when inside the sub
if (InsideSubmarine)
{
//higher cost for vertical movement when inside the sub
if (yDiff > 1.0f && node.Waypoint.Ladders == null && node.Waypoint.Stairs == null)
{
yDiff += 10.0f;
}
node.TempDistance = xDiff + yDiff * 10.0f;
}
else
{
node.TempDistance = xDiff + yDiff;
}
//much higher cost to waypoints that are outside
if (node.Waypoint.CurrentHull == null && ApplyPenaltyToOutsideNodes) { node.TempDistance *= 10.0f; }
@@ -57,7 +57,10 @@ namespace Barotrauma
public void SteeringManual(float deltaTime, Vector2 velocity)
{
steering += velocity;
if (MathUtils.IsValid(velocity))
{
steering += velocity;
}
}
public void Reset()
@@ -374,7 +374,7 @@ namespace Barotrauma
{
//pull the character's mouth to the target character (again with a fluctuating force)
float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);
}
else
{
@@ -746,7 +746,7 @@ namespace Barotrauma
var arm = GetLimb(armType);
if (arm != null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
{
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f);
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f * CurrentGroundedParams.ArmMoveStrength);
}
//get the elbow to a neutral rotation
@@ -757,7 +757,7 @@ namespace Barotrauma
if (elbow != null)
{
float diff = elbow.JointAngle - (Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit);
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f);
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * CurrentGroundedParams.ArmMoveStrength);
}
}
}
@@ -809,8 +809,8 @@ namespace Barotrauma
{
foreach (Gap gap in currentHull.ConnectedGaps)
{
if (gap.IsHorizontal || gap.Open <= 0.0f) continue;
if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) continue;
if (gap.IsHorizontal || gap.Open <= 0.0f) { continue; }
if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; }
//if the gap is above us and leads outside, there's no surface to limit the movement
if (!gap.IsRoomToRoom && gap.Position.Y > currentHull.Position.Y)
@@ -830,7 +830,7 @@ namespace Barotrauma
}
}
surfaceLimiter = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 0.4f) - surfacePos;
surfaceLimiter = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f) - surfacePos;
surfaceLimiter = Math.Max(1.0f, surfaceLimiter);
if (surfaceLimiter > 50.0f) { return; }
}
@@ -921,7 +921,7 @@ namespace Barotrauma
head.body.ApplyTorque(Dir);
}
movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f;
movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / 50.0f));
}
bool isNotRemote = true;
@@ -1003,7 +1003,10 @@ namespace Barotrauma
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()), 1.0f);
// Limb hand, Vector2 pos, float force = 1.0f
if (character.Inventory != null && character.Inventory.GetItemInLimbSlot(InvSlotType.RightHand) != null)
{
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
}
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
}
@@ -1013,6 +1016,10 @@ namespace Barotrauma
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()), 1.0f);
if (character.Inventory != null && character.Inventory.GetItemInLimbSlot(InvSlotType.LeftHand) != null)
{
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
}
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
}
}
@@ -1154,7 +1161,7 @@ namespace Barotrauma
if (character.SimPosition.Y > ladderSimPos.Y) { climbForce.Y = Math.Min(0.0f, climbForce.Y); }
//apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, WalkParams.HeadTorque);
@@ -1383,7 +1390,7 @@ namespace Barotrauma
target.CharacterHealth.CalculateVitality();
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
{
character.Info?.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive, character.Position + Vector2.UnitY * 150.0f);
character.Info?.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive);
SteamAchievementManager.OnCharacterRevived(target, character);
lastReviveTime = (float)Timing.TotalTime;
#if SERVER
@@ -1348,19 +1348,19 @@ namespace Barotrauma
string errorMsg = null;
if (!MathUtils.IsValid(body.SimPosition) || Math.Abs(body.SimPosition.X) > 1e10f || Math.Abs(body.SimPosition.Y) > 1e10f)
{
errorMsg = GetBodyName() + " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = GetBodyName() + " position invalid (" + body.SimPosition + ", character: " + character.Name + ").";
}
else if (!MathUtils.IsValid(body.LinearVelocity) || Math.Abs(body.LinearVelocity.X) > 1000f || Math.Abs(body.LinearVelocity.Y) > 1000f)
{
errorMsg = GetBodyName() + " velocity invalid (" + body.LinearVelocity + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = GetBodyName() + " velocity invalid (" + body.LinearVelocity + ", character: " + character.Name + ").";
}
else if (!MathUtils.IsValid(body.Rotation))
{
errorMsg = GetBodyName() + " rotation invalid (" + body.Rotation + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = GetBodyName() + " rotation invalid (" + body.Rotation + ", character: " + character.Name + ").";
}
else if (!MathUtils.IsValid(body.AngularVelocity) || Math.Abs(body.AngularVelocity) > 1000f)
{
errorMsg = GetBodyName() + " angular velocity invalid (" + body.AngularVelocity + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = GetBodyName() + " angular velocity invalid (" + body.AngularVelocity + ", character: " + character.Name + ").";
}
if (errorMsg != null)
{
@@ -1469,11 +1469,11 @@ namespace Barotrauma
if (flowForce.LengthSquared() > 0.001f)
{
Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
Collider.ApplyForce(flowForce);
foreach (Limb limb in limbs)
{
if (!limb.InWater) { continue; }
limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
limb.body.ApplyForce(flowForce);
}
}
}
@@ -1506,7 +1506,6 @@ namespace Barotrauma
if (TorsoPosition.HasValue && MathUtils.IsValid(TorsoPosition.Value)) { height = Math.Max(height, TorsoPosition.Value); }
Vector2 rayEnd = rayStart - new Vector2(0.0f, height);
Vector2 onGroundRayEnd = rayStart - Vector2.UnitY * (Collider.height * 0.5f + Collider.radius + ColliderHeightFromFloor * 1.2f);
Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom());
Fixture standOnFloorFixture = null;
@@ -1587,7 +1586,25 @@ namespace Barotrauma
if (closestFraction == 1) //raycast didn't hit anything
{
floorNormal = Vector2.UnitY;
return (currentHull == null) ? -1000.0f : ConvertUnits.ToSimUnits(currentHull.Rect.Y - currentHull.Rect.Height);
if (CurrentHull == null)
{
return -1000.0f;
}
else
{
float hullBottom = currentHull.Rect.Y - currentHull.Rect.Height;
//check if there's a connected hull below
foreach (var gap in currentHull.ConnectedGaps)
{
if (!gap.IsRoomToRoom || gap.Open < 1.0f || gap.ConnectedDoor != null || gap.IsHorizontal) { continue; }
if (WorldPosition.X > gap.WorldRect.X && WorldPosition.X < gap.WorldRect.Right && gap.WorldPosition.Y < WorldPosition.Y)
{
var lowerHull = gap.linkedTo[0] == currentHull ? gap.linkedTo[1] : gap.linkedTo[0];
hullBottom = Math.Min(hullBottom, lowerHull.Rect.Y - lowerHull.Rect.Height);
}
}
return ConvertUnits.ToSimUnits(hullBottom);
}
}
else
{
@@ -1745,8 +1745,12 @@ namespace Barotrauma
}
else if (IsKeyDown(InputType.Attack))
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Controlled != this)
{
if ((currentAttackTarget.DamageTarget as Entity)?.Removed ?? false)
{
currentAttackTarget = default(AttackTargetData);
}
currentAttackTarget.AttackLimb?.UpdateAttack(deltaTime, currentAttackTarget.AttackPos, currentAttackTarget.DamageTarget, out _);
}
else if (IsPlayer)
@@ -3670,16 +3674,14 @@ namespace Barotrauma
{
float attackerSkillLevel = attacker.GetSkillLevel("weapons");
attacker.Info?.IncreaseSkillLevel("weapons",
-healthChange * SkillSettings.Current.SkillIncreasePerHostileDamage / Math.Max(attackerSkillLevel, 1.0f),
attacker.Position + Vector2.UnitY * 100.0f);
-healthChange * SkillSettings.Current.SkillIncreasePerHostileDamage / Math.Max(attackerSkillLevel, 1.0f));
}
}
else if (healthChange > 0.0f)
{
float attackerSkillLevel = attacker.GetSkillLevel("medical");
attacker.Info?.IncreaseSkillLevel("medical",
healthChange * SkillSettings.Current.SkillIncreasePerFriendlyHealed / Math.Max(attackerSkillLevel, 1.0f),
attacker.Position + Vector2.UnitY * 100.0f);
healthChange * SkillSettings.Current.SkillIncreasePerFriendlyHealed / Math.Max(attackerSkillLevel, 1.0f));
}
}
@@ -3927,7 +3929,7 @@ namespace Barotrauma
}
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
public void Revive()
public void Revive(bool removeAllAfflictions = true)
{
if (Removed)
{
@@ -3938,7 +3940,14 @@ namespace Barotrauma
aiTarget?.Remove();
aiTarget = new AITarget(this);
CharacterHealth.RemoveAllAfflictions();
if (removeAllAfflictions)
{
CharacterHealth.RemoveAllAfflictions();
}
else
{
CharacterHealth.RemoveNegativeAfflictions();
}
SetAllDamage(0.0f, 0.0f, 0.0f);
Oxygen = 100.0f;
Bloodloss = 0.0f;
@@ -4284,12 +4293,8 @@ namespace Barotrauma
}
if (Submarine == null && target.Submarine != null)
{
if (AIController == null || !(AIController.SteeringManager is IndoorsSteeringManager))
{
// outside and targeting inside
// doesn't work with inside steering
targetPos += target.Submarine.SimPosition;
}
// outside and targeting inside
targetPos += target.Submarine.SimPosition;
}
else if (Submarine != null && target.Submarine == null)
{
@@ -4376,14 +4381,14 @@ namespace Barotrauma
public bool GiveTalent(TalentPrefab talentPrefab, bool addingFirstTime = true)
{
if (addingFirstTime)
{
if (!info.UnlockedTalents.Add(talentPrefab.Identifier)) { return false; }
}
if (info == null) { return false; }
info.UnlockedTalents.Add(talentPrefab.Identifier);
if (characterTalents.Any(t => t.Prefab == talentPrefab)) { return false; }
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
characterTalent.ActivateTalent(addingFirstTime);
characterTalents.Add(characterTalent);
characterTalent.AddedThisRound = addingFirstTime;
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
@@ -1162,7 +1162,7 @@ namespace Barotrauma
return (int)(salary * Job.Prefab.PriceMultiplier);
}
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 pos, bool gainedFromApprenticeship = false)
public void IncreaseSkillLevel(string skillIdentifier, float increase, bool gainedFromApprenticeship = false)
{
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
@@ -1190,10 +1190,10 @@ namespace Barotrauma
}
}
OnSkillChanged(skillIdentifier, prevLevel, newLevel, pos);
OnSkillChanged(skillIdentifier, prevLevel, newLevel);
}
public void SetSkillLevel(string skillIdentifier, float level, Vector2 pos)
public void SetSkillLevel(string skillIdentifier, float level)
{
if (Job == null) { return; }
@@ -1201,19 +1201,19 @@ namespace Barotrauma
if (skill == null)
{
Job.Skills.Add(new Skill(skillIdentifier, level));
OnSkillChanged(skillIdentifier, 0.0f, level, pos);
OnSkillChanged(skillIdentifier, 0.0f, level);
}
else
{
float prevLevel = skill.Level;
skill.Level = level;
OnSkillChanged(skillIdentifier, prevLevel, skill.Level, pos);
OnSkillChanged(skillIdentifier, prevLevel, skill.Level);
}
}
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos);
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel);
public void GiveExperience(int amount, float popupOffset = 0f, bool isMissionExperience = false)
public void GiveExperience(int amount, bool isMissionExperience = false)
{
int prevAmount = ExperiencePoints;
@@ -1229,7 +1229,7 @@ namespace Barotrauma
if (amount < 0) { return; }
ExperiencePoints += amount;
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * (150.0f + popupOffset));
OnExperienceChanged(prevAmount, ExperiencePoints);
}
public void SetExperience(int newExperience)
@@ -1238,7 +1238,7 @@ namespace Barotrauma
int prevAmount = ExperiencePoints;
ExperiencePoints = newExperience;
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * 150.0f);
OnExperienceChanged(prevAmount, ExperiencePoints);
}
const int BaseExperienceRequired = 50;
@@ -1295,7 +1295,7 @@ namespace Barotrauma
return BaseExperienceRequired + AddedExperienceRequiredPerLevel * level;
}
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos);
partial void OnExperienceChanged(int prevAmount, int newAmount);
public void Rename(string newName)
{
@@ -206,7 +206,11 @@ namespace Barotrauma
public float Stun
{
get { return stunAffliction.Strength; }
set { stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength); }
set
{
if (Character.GodMode) { return; }
stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength);
}
}
public float StunTimer { get; private set; }
@@ -629,6 +633,22 @@ namespace Barotrauma
CalculateVitality();
}
public void RemoveNegativeAfflictions()
{
// also don't remove genetic effects, even if they're negative
foreach (LimbHealth limbHealth in limbHealths)
{
limbHealth.Afflictions.RemoveAll(a => !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
}
afflictions.RemoveAll(a => !irremovableAfflictions.Contains(a) && !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
foreach (Affliction affliction in irremovableAfflictions)
{
affliction.Strength = 0.0f;
}
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true)
{
if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; }
@@ -68,9 +68,20 @@ namespace Barotrauma
}
}
public class PreviewItem
{
public readonly string ItemIdentifier;
public readonly bool ShowPreview;
public PreviewItem(string itemIdentifier, bool showPreview)
{
ItemIdentifier = itemIdentifier;
ShowPreview = showPreview;
}
}
public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>();
public readonly Dictionary<int, List<string>> ItemIdentifiers = new Dictionary<int, List<string>>();
public readonly Dictionary<int, Dictionary<string, bool>> ShowItemPreview = new Dictionary<int, Dictionary<string, bool>>();
public readonly Dictionary<int, List<PreviewItem>> PreviewItems = new Dictionary<int, List<PreviewItem>>();
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
public readonly List<AutonomousObjective> AutonomousObjectives = new List<AutonomousObjective>();
public readonly List<string> AppropriateOrders = new List<string>();
@@ -220,8 +231,7 @@ namespace Barotrauma
{
case "itemset":
ItemSets.Add(variant, subElement);
ItemIdentifiers[variant] = new List<string>();
ShowItemPreview[variant] = new Dictionary<string, bool>();
PreviewItems[variant] = new List<PreviewItem>();
loadItemIdentifiers(subElement, variant);
variant++;
break;
@@ -264,8 +274,7 @@ namespace Barotrauma
}
else
{
ItemIdentifiers[variant].Add(itemIdentifier);
ShowItemPreview[variant][itemIdentifier] = itemElement.GetAttributeBool("showpreview", true);
PreviewItems[variant].Add(new PreviewItem(itemIdentifier, itemElement.GetAttributeBool("showpreview", true)));
}
loadItemIdentifiers(itemElement, variant);
}
@@ -759,6 +759,10 @@ namespace Barotrauma
appliedDamageModifiers.AddRange(tempModifiers);
}
var result = new AttackResult(afflictionsCopy, this, appliedDamageModifiers);
if (result.Afflictions.None())
{
playSound = false;
}
AddDamageProjSpecific(playSound, result);
float bleedingDamage = 0;
@@ -11,12 +11,12 @@ namespace Barotrauma
{
public enum AnimationType
{
NotDefined,
Walk,
Run,
SwimSlow,
SwimFast,
Crouch
NotDefined = 0,
Walk = 1,
Run = 2,
Crouch = 3,
SwimSlow = 4,
SwimFast = 5
}
abstract class GroundedMovementParams : AnimationParams
@@ -597,7 +597,7 @@ namespace Barotrauma
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
public float SpriteOrientation { get; set; }
[Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
[Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
public LimbType InheritLimbDepth { get; set; }
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
@@ -12,9 +12,12 @@ namespace Barotrauma.Abilities
Ranged = 2
};
private readonly bool hittingCountsAsAiming;
private readonly WeaponType weapontype;
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
hittingCountsAsAiming = conditionElement.GetAttributeBool("hittingcountsasaiming", false);
switch (conditionElement.GetAttributeString("weapontype", ""))
{
case "melee":
@@ -28,7 +31,6 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific()
{
bool aimingCorrectItem = false;
if (character.AnimController is HumanoidAnimController animController)
{
foreach (Item item in character.HeldItems)
@@ -36,19 +38,23 @@ namespace Barotrauma.Abilities
switch (weapontype)
{
case WeaponType.Melee:
aimingCorrectItem |= item.GetComponent<MeleeWeapon>() != null && animController.IsAimingMelee;
var meleeWeapon = item.GetComponent<MeleeWeapon>();
if (meleeWeapon != null)
{
if (animController.IsAimingMelee || (meleeWeapon.Hitting && hittingCountsAsAiming)) { return true; }
}
break;
case WeaponType.Ranged:
aimingCorrectItem |= item.GetComponent<RangedWeapon>() != null && animController.IsAiming;
if (animController.IsAiming && item.GetComponent<RangedWeapon>() != null) { return true; }
break;
default:
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee;
if (animController.IsAiming || animController.IsAimingMelee) { return true; }
break;
}
}
}
return aimingCorrectItem;
return false;
}
}
}
@@ -16,7 +16,7 @@ namespace Barotrauma.Abilities
{
if ((abilityObject as IAbilityValue)?.Value is float skillIncrease)
{
Character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, Character.Position + Vector2.UnitY * 175.0f);
Character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease);
}
else
{
@@ -1,21 +0,0 @@
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityGiveMissionCount : CharacterAbility
{
private readonly int amount;
public CharacterAbilityGiveMissionCount(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
amount = abilityElement.GetAttributeInt("amount", 0);
}
public override void InitializeAbility(bool addingFirstTime)
{
if (!addingFirstTime) { return; }
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign)) { return; }
campaign.Settings.AddedMissionCount += amount;
}
}
}
@@ -49,13 +49,12 @@ namespace Barotrauma.Abilities
{
var skill = character.Info?.Job?.Skills?.GetRandom();
if (skill == null) { return; }
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease);
}
else
{
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease);
}
}
}
}
@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
private void ApplyEffectSpecific()
{
Character.Revive();
Character.Revive(removeAllAfflictions: false);
}
protected override void ApplyEffect()
@@ -21,6 +21,7 @@ namespace Barotrauma.Abilities
{
foreach (var talent in talentOption.Talents)
{
if (talent == CharacterTalent.Prefab) { continue; }
Character.GiveTalent(talent);
}
}
@@ -14,7 +14,7 @@ namespace Barotrauma.Abilities
{
if (abilityObject is AbilitySkillGain abilitySkillGain && !abilitySkillGain.GainedFromApprenticeship && abilitySkillGain.Character != Character)
{
Character.Info?.IncreaseSkillLevel(abilitySkillGain.String, 1.0f, Character.Position + Vector2.UnitY * 175.0f, gainedFromApprenticeship: true);
Character.Info?.IncreaseSkillLevel(abilitySkillGain.String, 1.0f, gainedFromApprenticeship: true);
}
}
}
@@ -18,7 +18,7 @@ namespace Barotrauma.Abilities
if (skillIdentifier != lastSkillIdentifier)
{
lastSkillIdentifier = skillIdentifier;
Character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f, Character.Position + Vector2.UnitY * 175.0f);
Character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f);
}
}
}
@@ -13,6 +13,8 @@ namespace Barotrauma
public readonly TalentPrefab Prefab;
public bool AddedThisRound = true;
private readonly Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>> characterAbilityGroupEffectDictionary = new Dictionary<AbilityEffectType, List<CharacterAbilityGroupEffect>>();
private readonly List<CharacterAbilityGroupInterval> characterAbilityGroupIntervals = new List<CharacterAbilityGroupInterval>();
@@ -809,13 +809,13 @@ namespace Barotrauma
{
foreach (Skill skill in character.Info.Job.Skills)
{
character.Info.SetSkillLevel(skill.Identifier, level, character.WorldPosition);
character.Info.SetSkillLevel(skill.Identifier, level);
}
NewMessage($"Set all {character.Name}'s skills to {level}", Color.Green);
}
else
{
character.Info.SetSkillLevel(skillIdentifier, level, character.WorldPosition);
character.Info.SetSkillLevel(skillIdentifier, level);
NewMessage($"Set {character.Name}'s {skillIdentifier} level to {level}", Color.Green);
}
}
@@ -839,7 +839,7 @@ namespace Barotrauma
NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White);
}, isCheat: true));
commands.Add(new Command("givetalent", "give [player] testing [talent]", (string[] args) =>
commands.Add(new Command("givetalent", "givetalent [talent] [player]: give the talent to the specified character. If the character argument is omitted, the talent is given to the controlled character.", (string[] args) =>
{
if (args.Length == 0) { return; }
var character = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled;
@@ -1886,13 +1886,15 @@ namespace Barotrauma
}
return;
}
#if !DEBUG
if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client))
{
#if DEBUG
AddWarning("You're not permitted to use the command \"{matchingCommand.Name}\". Executing the command anyway because this is a debug build.");
#else
ThrowError("You're not permitted to use the command \"" + splitCommand[0].ToLowerInvariant() + "\"!");
return;
}
#endif
}
}
#endif
@@ -119,6 +119,7 @@
MissionMoneyGainMultiplier,
ExperienceGainMultiplier,
MissionExperienceGainMultiplier,
ExtraMissionCount,
ExtraSpecialSalesCount,
ApplyTreatmentsOnSelfFraction,
MaxAttachableCount,
@@ -40,7 +40,7 @@ namespace Barotrauma
var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
foreach (var target in targets)
{
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount, target.Position + Vector2.UnitY * 150.0f);
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount);
}
isFinished = true;
}
@@ -165,12 +165,12 @@ namespace Barotrauma
public override void End()
{
if (AllTargetsEliminated())
if (State == 2)
{
GiveReward();
completed = true;
}
failed = !completed && state > 0;
failed = !completed && State > 0;
}
}
}
@@ -18,7 +18,7 @@ namespace Barotrauma
private readonly int targetsToScan;
private readonly Dictionary<WayPoint, bool> scanTargets = new Dictionary<WayPoint, bool>();
private readonly HashSet<WayPoint> newTargetsScanned = new HashSet<WayPoint>();
private readonly float minTargetDistance, minTargetDistanceSquared;
private readonly float minTargetDistance;
private Ruin TargetRuin { get; set; }
@@ -58,7 +58,6 @@ namespace Barotrauma
itemConfig = prefab.ConfigElement.Element("Items");
targetsToScan = prefab.ConfigElement.GetAttributeInt("targets", 1);
minTargetDistance = prefab.ConfigElement.GetAttributeFloat("mintargetdistance", 0.0f);
minTargetDistanceSquared = minTargetDistance * minTargetDistance;
}
protected override void StartMissionSpecific(Level level)
@@ -86,28 +85,57 @@ namespace Barotrauma
return;
}
var availableWaypoints = TargetRuin.Submarine.GetWaypoints(false);
availableWaypoints.RemoveAll(wp => wp.CurrentHull == null);
if (availableWaypoints.Count < targetsToScan)
var ruinWaypoints = TargetRuin.Submarine.GetWaypoints(false);
ruinWaypoints.RemoveAll(wp => wp.CurrentHull == null);
if (ruinWaypoints.Count < targetsToScan)
{
DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({availableWaypoints.Count} < {targetsToScan})");
DebugConsole.ThrowError($"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({ruinWaypoints.Count} < {targetsToScan})");
return;
}
for (int i = 0; i < targetsToScan; i++)
var availableWaypoints = new List<WayPoint>();
float minTargetDistanceSquared = minTargetDistance * minTargetDistance;
for (int tries = 0; tries < 15; tries++)
{
var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.Server);
scanTargets.Add(selectedWaypoint, false);
availableWaypoints.Remove(selectedWaypoint);
if (i < (targetsToScan - 1))
scanTargets.Clear();
availableWaypoints.Clear();
availableWaypoints.AddRange(ruinWaypoints);
for (int i = 0; i < targetsToScan; i++)
{
availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull);
availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared);
if (availableWaypoints.None())
var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.Server);
scanTargets.Add(selectedWaypoint, false);
availableWaypoints.Remove(selectedWaypoint);
if (i < (targetsToScan - 1))
{
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
break;
availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull);
availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared);
if (availableWaypoints.None())
{
#if DEBUG
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets available on try #{tries + 1} to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
#endif
break;
}
}
}
if (scanTargets.Count >= targetsToScan)
{
#if DEBUG
DebugConsole.NewMessage($"Successfully initialized a Scan mission: targets set on try #{tries + 1}", Color.Green);
#endif
break;
}
if ((tries + 1) % 5 == 0)
{
float reducedMinTargetDistance = (1.0f - (((tries + 1) / 5) * 0.1f)) * minTargetDistance;
minTargetDistanceSquared = reducedMinTargetDistance * reducedMinTargetDistance;
#if DEBUG
DebugConsole.NewMessage($"Reducing minimum distance between Scan mission targets (new min: {reducedMinTargetDistance}) to reach the required target count", Color.Yellow);
#endif
}
}
if (scanTargets.Count < targetsToScan)
{
DebugConsole.ThrowError($"Error initializing a Scan mission: not enough targets (current targets: {scanTargets.Count}, required targets: {targetsToScan})");
}
}
@@ -218,7 +246,7 @@ namespace Barotrauma
public override void End()
{
if (AllTargetsScanned && AllScannersReturned())
if (State == 2 && AllScannersReturned())
{
GiveReward();
completed = true;
@@ -489,6 +489,11 @@ namespace Barotrauma
continue;
}
}
if (orderInfo.Order.TargetEntity == null || (orderInfo.Order.IsIgnoreOrder && ignoreTarget == null))
{
// The order target doesn't exist anymore, just discard the loaded order
continue;
}
if (ignoreTarget != null)
{
ignoreTarget.OrderedToBeIgnored = true;
@@ -51,7 +51,7 @@ namespace Barotrauma
if (reputationChange > 0f)
{
float reputationGainMultiplier = 1f;
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == CharacterTeamType.Team1))
foreach (Character character in GameSession.GetSessionCrewCharacters())
{
reputationGainMultiplier += character.GetStatValue(StatTypes.ReputationGainMultiplier);
}
@@ -17,9 +17,7 @@ namespace Barotrauma
public static CampaignSettings Unsure = Empty;
public bool RadiationEnabled { get; set; }
public int AddedMissionCount { get; set; }
public int TotalMaxMissionCount => MaxMissionCount + AddedMissionCount;
public int TotalMaxMissionCount => MaxMissionCount + GetAddedMissionCount();
private int maxMissionCount;
public int MaxMissionCount
@@ -36,7 +34,6 @@ namespace Barotrauma
{
maxMissionCount = DefaultMaxMissionCount;
RadiationEnabled = inc.ReadBoolean();
AddedMissionCount = inc.ReadInt32();
MaxMissionCount = inc.ReadInt32();
}
@@ -44,7 +41,6 @@ namespace Barotrauma
{
maxMissionCount = DefaultMaxMissionCount;
RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLowerInvariant(), true);
AddedMissionCount = element.GetAttributeInt(nameof(AddedMissionCount).ToLowerInvariant(), 0);
MaxMissionCount = element.GetAttributeInt(nameof(MaxMissionCount).ToLowerInvariant(), DefaultMaxMissionCount);
}
@@ -52,12 +48,21 @@ namespace Barotrauma
{
msg.Write(RadiationEnabled);
msg.Write(MaxMissionCount);
msg.Write(AddedMissionCount);
}
public int GetAddedMissionCount()
{
int count = 0;
foreach (Character character in GameSession.GetSessionCrewCharacters())
{
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
}
return count;
}
public XElement Save()
{
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLowerInvariant(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLowerInvariant(), MaxMissionCount), new XAttribute(nameof(AddedMissionCount).ToLowerInvariant(), AddedMissionCount));
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLowerInvariant(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLowerInvariant(), MaxMissionCount));
}
}
@@ -661,10 +661,10 @@ namespace Barotrauma
public static IEnumerable<Character> GetSessionCrewCharacters()
{
#if SERVER
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null);
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
#else
if (GameMain.GameSession == null) { return Enumerable.Empty<Character>(); }
return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null);
return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null && !c.IsDead);
#endif
}
@@ -49,13 +49,13 @@ namespace Barotrauma.Items.Components
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")]
public Vector2 SpawnAreaOffset { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds")]
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds. Set both to a negative value to disable automatic spawning.")]
public Vector2 SpawnTimerRange { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
public Vector2 SpawnAmountRange { get; set; }
[Editable(MinValueInt = 0), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
[Editable(MinValueInt = int.MinValue, MaxValueInt = int.MaxValue), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
public int MaximumAmount { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
@@ -110,7 +110,12 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember is { IsClient: true }) { return; }
SpawnTimerGoal ??= Rand.Range(Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y), Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y), Rand.RandSync.Unsynced);
float minTime = Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y),
maxTime = Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y);
if (minTime < 0 && maxTime < 0) { return; }
SpawnTimerGoal ??= Rand.Range(minTime, maxTime, Rand.RandSync.Unsynced);
SpawnTimer += deltaTime;
@@ -120,12 +125,12 @@ namespace Barotrauma.Items.Components
SpawnTimerGoal = null;
SpawnTimer = 0;
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
bool isNonZero = signal.value != "0";
bool isClient = GameMain.NetworkMember is { IsClient: true };
switch (connection.Name)
{
@@ -135,6 +140,9 @@ namespace Barotrauma.Items.Components
case "toggle" when isNonZero:
CanSpawn = !CanSpawn;
break;
case "trigger_in" when isNonZero && !isClient:
Spawn();
break;
}
}
@@ -163,6 +171,8 @@ namespace Barotrauma.Items.Components
}
}
if (MaximumAmount < 0) { return true; }
int amount;
if (!string.IsNullOrWhiteSpace(SpeciesName))
@@ -59,6 +59,7 @@ namespace Barotrauma.Items.Components
[Editable, Serialize("3.0, -1.0", false)]
public Vector2 SwingForce { get; set; }
public bool Hitting { get { return hitting; } }
/// <summary>
/// Defines items that boost the weapon functionality, like battery cell for stun batons.
@@ -66,18 +66,18 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in character.AnimController.Limbs)
{
if (limb.WearingItems.Find(w => w.WearableComponent.Item == item) == null) { continue; }
limb.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
limb.body.ApplyForce(propulsion);
}
character.AnimController.Collider.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
character.AnimController.Collider.ApplyForce(propulsion);
if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand))
{
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion);
}
if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
{
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion);
}
#if CLIENT
@@ -200,7 +200,8 @@ namespace Barotrauma.Items.Components
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems)
{
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
float percentageHealth = targetItem.Condition / targetItem.MaxCondition;
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab))
@@ -391,8 +391,7 @@ namespace Barotrauma.Items.Components
user.Info.IncreaseSkillLevel(
skill.Identifier,
addedSkill,
user.Position + Vector2.UnitY * 150.0f);
addedSkill);
}
}
@@ -11,9 +11,12 @@ namespace Barotrauma.Items.Components
private float generatedAmount;
//key = vent, float = total volume of the hull the vent is in and the hulls connected to it
private Dictionary<Vent, float> ventList;
private List<(Vent vent, float hullVolume)> ventList;
private float totalHullVolume;
private float ventUpdateTimer;
const float VentUpdateInterval = 5.0f;
public float CurrFlow
{
@@ -64,7 +67,7 @@ namespace Barotrauma.Items.Components
//20% condition = 4%
CurrFlow *= conditionMult * conditionMult;
UpdateVents(CurrFlow);
UpdateVents(CurrFlow, deltaTime);
}
public override void UpdateBroken(float deltaTime, Camera cam)
@@ -75,7 +78,8 @@ namespace Barotrauma.Items.Components
private void GetVents()
{
ventList = new Dictionary<Vent, float>();
ventList ??= new List<(Vent vent, float hullVolume)>();
ventList.Clear();
foreach (MapEntity entity in item.linkedTo)
{
if (!(entity is Item linkedItem)) { continue; }
@@ -83,30 +87,39 @@ namespace Barotrauma.Items.Components
Vent vent = linkedItem.GetComponent<Vent>();
if (vent?.Item.CurrentHull == null) { continue; }
ventList.Add(vent, 0.0f);
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: true, searchDepth: 10, ignoreClosedGaps: true))
{
ventList.Add((vent, vent.Item.CurrentHull.Volume));
}
for (int i = 0; i < ventList.Count; i++)
{
Vent vent = ventList[i].vent;
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 5, ignoreClosedGaps: true))
{
//another vent in the connected hull -> don't add it to this vent's total hull volume
if (ventList.Any(v => v.vent != vent && v.vent.Item.CurrentHull == connectedHull)) { continue; }
totalHullVolume += connectedHull.Volume;
ventList[vent] += connectedHull.Volume;
ventList[i] = (ventList[i].vent, ventList[i].hullVolume + connectedHull.Volume);
}
}
}
private void UpdateVents(float deltaOxygen)
private void UpdateVents(float deltaOxygen, float deltaTime)
{
if (ventList == null)
if (ventList == null || ventUpdateTimer < 0.0f)
{
GetVents();
ventUpdateTimer = VentUpdateInterval;
}
ventUpdateTimer -= deltaTime;
if (!ventList.Any() || totalHullVolume <= 0.0f) { return; }
foreach (KeyValuePair<Vent, float> v in ventList)
foreach ((Vent vent, float hullVolume) in ventList)
{
if (v.Key?.Item.CurrentHull == null) { continue; }
if (vent.Item.CurrentHull == null) { continue; }
v.Key.OxygenFlow = deltaOxygen * (v.Value / totalHullVolume);
v.Key.IsActive = true;
vent.OxygenFlow = deltaOxygen * (hullVolume / totalHullVolume);
vent.IsActive = true;
}
}
}
@@ -392,8 +392,7 @@ namespace Barotrauma.Items.Components
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
user.Info.IncreaseSkillLevel(
"helm",
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime,
user.Position + Vector2.UnitY * 150.0f);
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime);
}
private void UpdateAutoPilot(float deltaTime)
@@ -340,7 +340,7 @@ namespace Barotrauma.Items.Components
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
}
item.Drop(null);
item.Drop(null, createNetworkEvent: false);
launchPos = item.SimPosition;
@@ -35,7 +35,6 @@ namespace Barotrauma.Items.Components
public RemoteController(Item item, XElement element)
: base(item, element)
{
DrawHudWhenEquipped = false;
}
public override bool Select(Character character)
@@ -440,8 +440,7 @@ namespace Barotrauma.Items.Components
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.Position + Vector2.UnitY * 100.0f);
SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f));
}
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete);
@@ -472,8 +471,7 @@ namespace Barotrauma.Items.Components
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.Position + Vector2.UnitY * 100.0f);
SkillSettings.Current.SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f));
}
deteriorationTimer = 0.0f;
@@ -159,6 +159,8 @@ namespace Barotrauma.Items.Components
ApplyForce(i.body);
}
}
item.SendSignal(IsActive ? "1" : "0", "state_out");
}
private void ApplyForce(PhysicsBody body)
@@ -438,8 +438,7 @@ namespace Barotrauma.Items.Components
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
{
user.Info.IncreaseSkillLevel("weapons",
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f),
user.Position + Vector2.UnitY * 150.0f);
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f));
}
float rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f);
@@ -46,6 +46,7 @@ namespace Barotrauma
public LimbType Limb { get; private set; }
public bool HideLimb { get; private set; }
public bool HideOtherWearables { get; private set; }
public bool CanBeHiddenByOtherWearables { get; private set; }
public List<WearableType> HideWearablesOfType { get; private set; }
public bool InheritLimbDepth { get; private set; }
/// <summary>
@@ -176,6 +177,7 @@ namespace Barotrauma
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
CanBeHiddenByOtherWearables = SourceElement.GetAttributeBool("canbehiddenbyotherwearables", true);
InheritLimbDepth = SourceElement.GetAttributeBool("inheritlimbdepth", true);
var scale = SourceElement.GetAttribute("inheritscale");
if (scale != null)
@@ -1803,7 +1803,7 @@ namespace Barotrauma
Vector2 drag = body.LinearVelocity * volume;
body.ApplyForce((uplift - drag) * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
body.ApplyForce((uplift - drag) * 10.0f);
//apply simple angular drag
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
@@ -314,6 +314,8 @@ namespace Barotrauma
/// </summary>
public bool IsOverride;
public readonly ItemPrefab VariantOf;
public XElement ConfigElement
{
get;
@@ -783,6 +785,7 @@ namespace Barotrauma
}
else
{
VariantOf = basePrefab;
ConfigElement = element = CreateVariantXML(element, basePrefab);
}
}
@@ -36,6 +36,8 @@ namespace Barotrauma
/// </summary>
public bool ExcludeBroken { get; private set; }
private bool allowVariants = true;
public RelationType Type
{
get { return type; }
@@ -82,13 +84,13 @@ namespace Barotrauma
{
if (item == null) { return false; }
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (allowVariants && item.Prefab.VariantOf?.Identifier == id));
}
public bool MatchesItem(ItemPrefab itemPrefab)
{
if (itemPrefab == null) { return false; }
if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; }
return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id));
return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (allowVariants && itemPrefab.VariantOf?.Identifier == id));
}
public RelatedItem(string[] identifiers, string[] excludedIdentifiers)
@@ -168,7 +170,8 @@ namespace Barotrauma
new XAttribute("optional", IsOptional),
new XAttribute("ignoreineditor", IgnoreInEditor),
new XAttribute("excludebroken", ExcludeBroken),
new XAttribute("targetslot", TargetSlot));
new XAttribute("targetslot", TargetSlot),
new XAttribute("allowvariants", allowVariants));
if (excludedIdentifiers.Length > 0)
{
@@ -231,7 +234,8 @@ namespace Barotrauma
RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers)
{
ExcludeBroken = element.GetAttributeBool("excludebroken", true)
ExcludeBroken = element.GetAttributeBool("excludebroken", true),
allowVariants = element.GetAttributeBool("allowvariants", true)
};
string typeStr = element.GetAttributeString("type", "");
if (string.IsNullOrEmpty(typeStr))
@@ -934,7 +934,7 @@ namespace Barotrauma
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
{
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime);
}
}
@@ -1888,7 +1888,10 @@ namespace Barotrauma
{
for (float x = waypointArea.X + outSideWaypointInterval; x < waypointArea.Right - outSideWaypointInterval; x += outSideWaypointInterval)
{
var wayPoint = new WayPoint(new Vector2(x, waypointArea.Y + waypointArea.Height * i), SpawnType.Path, null);
var wayPoint = new WayPoint(new Vector2(x, waypointArea.Y + waypointArea.Height * i), SpawnType.Path, null)
{
Ruin = ruin
};
wayPoints.Add(wayPoint);
if (x == waypointArea.X + outSideWaypointInterval)
{
@@ -1907,7 +1910,10 @@ namespace Barotrauma
WayPoint wayPoint = null;
for (float y = waypointArea.Y; y < waypointArea.Y + waypointArea.Height; y += outSideWaypointInterval)
{
wayPoint = new WayPoint(new Vector2(waypointArea.X + waypointArea.Width * i, y), SpawnType.Path, null);
wayPoint = new WayPoint(new Vector2(waypointArea.X + waypointArea.Width * i, y), SpawnType.Path, null)
{
Ruin = ruin
};
wayPoints.Add(wayPoint);
if (y == waypointArea.Y)
{
@@ -1940,12 +1946,33 @@ namespace Barotrauma
//connect ruin entrances to the outside waypoints
foreach (Gap g in Gap.GapList)
{
if (g.Submarine != ruin.Submarine || g.IsRoomToRoom) { continue; }
if (g.Submarine != ruin.Submarine || g.IsRoomToRoom || g.linkedTo.Count == 0) { continue; }
var gapWaypoint = WayPoint.WayPointList.Find(wp => wp.ConnectedGap == g);
if (gapWaypoint == null) { continue; }
var closestWp = FindClosestWayPoint(gapWaypoint.WorldPosition, wayPoints);
//place another waypoint in front of the entrance
Vector2 entranceDir = Vector2.Zero;
if (g.IsHorizontal)
{
entranceDir = Vector2.UnitX * Math.Sign(g.WorldPosition.X - g.linkedTo[0].WorldPosition.X);
}
else
{
entranceDir = Vector2.UnitY * Math.Sign(g.WorldPosition.Y - g.linkedTo[0].WorldPosition.Y);
}
var entranceWayPoint = new WayPoint(g.WorldPosition + entranceDir * 64.0f, SpawnType.Path, null)
{
Ruin = ruin
};
entranceWayPoint.ConnectTo(gapWaypoint);
var closestWp = FindClosestWayPoint(entranceWayPoint.WorldPosition, wayPoints, (wp) =>
{
return Submarine.PickBody(
ConvertUnits.ToSimUnits(wp.WorldPosition),
ConvertUnits.ToSimUnits(entranceWayPoint.WorldPosition), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) == null;
});
if (closestWp == null) { continue; }
gapWaypoint.ConnectTo(closestWp);
entranceWayPoint.ConnectTo(closestWp);
}
//create a waypoint path from the ruin to the closest tunnel
@@ -666,19 +666,19 @@ namespace Barotrauma
if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * currentForceFluctuation * distFactor, ForceVelocityLimit);
else
body.ApplyForce(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
body.ApplyForce(Force * currentForceFluctuation * distFactor);
break;
case TriggerForceMode.Acceleration:
if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, ForceVelocityLimit);
else
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor);
break;
case TriggerForceMode.Impulse:
if (ForceVelocityLimit < 1000.0f)
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: ForceVelocityLimit);
else
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor);
break;
case TriggerForceMode.LimitVelocity:
float maxVel = ForceVelocityLimit * currentForceFluctuation * distFactor;
@@ -495,7 +495,7 @@ namespace Barotrauma
float GetLevelDifficulty(float areaDifficulty)
{
const float CurveModifier = 1.5f;
const float DifficultyMultiplier = 1.1f;
const float DifficultyMultiplier = 1.14f;
const float BaseDifficulty = -3f;
return (float)(1 - Math.Pow(1 - areaDifficulty, CurveModifier)) * DifficultyMultiplier * 100f + BaseDifficulty;
}
@@ -77,7 +77,7 @@ namespace Barotrauma
locationType = location.GetLocationType();
}
//load the infos of the outpost module files
List<SubmarineInfo> outpostModules = new List<SubmarineInfo>();
foreach (ContentFile outpostModuleFile in outpostModuleFiles)
@@ -85,15 +85,19 @@ namespace Barotrauma
var subInfo = new SubmarineInfo(outpostModuleFile.Path);
if (subInfo.OutpostModuleInfo != null)
{
if (generationParams is RuinGeneration.RuinGenerationParams)
{
if (generationParams is RuinGeneration.RuinGenerationParams)
{
//if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") &&
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key)))
{
continue;
}
}
}
else if (subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin"))
{
continue;
}
outpostModules.Add(subInfo);
}
}
@@ -1177,8 +1177,7 @@ namespace Barotrauma
if (damageDiff < 0.0f)
{
attacker.Info?.IncreaseSkillLevel("mechanical",
-damageDiff * SkillSettings.Current.SkillIncreasePerRepairedStructureDamage / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f),
SectionPosition(sectionIndex));
-damageDiff * SkillSettings.Current.SkillIncreasePerRepairedStructureDamage / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f));
}
}
}
@@ -452,7 +452,7 @@ namespace Barotrauma
public void ApplyForce(Vector2 force)
{
Body.ApplyForce(force, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
Body.ApplyForce(force);
}
public void SetPosition(Vector2 position)
@@ -46,6 +46,7 @@ namespace Barotrauma
public Hull CurrentHull { get; private set; }
public Level.Tunnel Tunnel;
public RuinGeneration.Ruin Ruin;
public SpawnType SpawnType
{
@@ -1097,6 +1098,7 @@ namespace Barotrauma
CurrentHull = null;
ConnectedGap = null;
Tunnel = null;
Ruin = null;
Stairs = null;
Ladders = null;
OnLinksChanged = null;
@@ -238,7 +238,7 @@ namespace Barotrauma.Networking
{
byte retval = NetBitWriter.ReadByte(buf, 1, bitPos);
bitPos++;
return (retval > 0 ? true : false);
return retval > 0;
}
internal static void ReadPadBits(byte[] buf, ref int bitPos)
@@ -672,14 +672,28 @@ namespace Barotrauma.Networking
}
}
buf = new byte[decompressedData.Length];
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length);
try
{
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length);
}
catch (ArgumentException e)
{
throw new ArgumentException($"Failed to copy the incoming compressed buffer. Source buffer length: {decompressedData.Length}, start position: {0}, length: {decompressedData.Length}, destination buffer length: {buf.Length}.", e);
}
lengthBits = decompressedData.Length * 8;
DebugConsole.Log("Decompressing message: " + inLength + " to " + LengthBytes);
}
else
{
buf = new byte[inBuf.Length];
Array.Copy(inBuf, startPos, buf, 0, inLength);
try
{
Array.Copy(inBuf, startPos, buf, 0, inLength);
}
catch (ArgumentException e)
{
throw new ArgumentException($"Failed to copy the incoming uncompressed buffer. Source buffer length: {inBuf.Length}, start position: {startPos}, length: {inLength}, destination buffer length: {buf.Length}.", e);
}
lengthBits = inLength * 8;
}
seekPos = 0;
@@ -641,18 +641,9 @@ namespace Barotrauma
NetConfig.MaxPhysicsBodyAngularVelocity);
}
public void ApplyForce(Vector2 force)
public void ApplyForce(Vector2 force, float maxVelocity = NetConfig.MaxPhysicsBodyVelocity)
{
if (!IsValidValue(force, "force", -1e10f, 1e10f)) return;
FarseerBody.ApplyForce(force);
}
/// <summary>
/// Apply a force to the body without increasing it's velocity above a specific limit.
/// </summary>
public void ApplyForce(Vector2 force, float maxVelocity)
{
if (!IsValidValue(maxVelocity, "max velocity")) return;
if (!IsValidValue(maxVelocity, "max velocity")) { return; }
Vector2 velocityAddition = force / Mass * (float)Timing.Step;
Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition;
@@ -666,20 +657,20 @@ namespace Barotrauma
force = velocityAddition.ClampLength(maxVelAddition) * Mass / (float)Timing.Step;
}
if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) return;
if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) { return; }
FarseerBody.ApplyForce(force);
}
public void ApplyForce(Vector2 force, Vector2 point)
{
if (!IsValidValue(force, "force", -1e10f, 1e10f)) return;
if (!IsValidValue(point, "point")) return;
if (!IsValidValue(force, "force", -1e10f, 1e10f)) { return; }
if (!IsValidValue(point, "point")) { return; }
FarseerBody.ApplyForce(force, point);
}
public void ApplyTorque(float torque)
{
if (!IsValidValue(torque, "torque")) return;
if (!IsValidValue(torque, "torque")) { return; }
FarseerBody.ApplyTorque(torque);
}
@@ -689,8 +680,8 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f);
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f);
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false;
if (!IsValidValue(rotation, "rotation")) return false;
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) { return false; }
if (!IsValidValue(rotation, "rotation")) { return false; }
FarseerBody.SetTransform(simPosition, rotation);
if (setPrevTransform) { SetPrevTransform(simPosition, rotation); }
@@ -703,8 +694,8 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.X) < 1000000.0f);
System.Diagnostics.Debug.Assert(Math.Abs(simPosition.Y) < 1000000.0f);
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false;
if (!IsValidValue(rotation, "rotation")) return false;
if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) { return false; }
if (!IsValidValue(rotation, "rotation")) { return false; }
FarseerBody.SetTransformIgnoreContacts(ref simPosition, rotation);
if (setPrevTransform) { SetPrevTransform(simPosition, rotation); }
@@ -789,7 +780,7 @@ namespace Barotrauma
dragForce = Math.Min(drag, Mass * 500.0f) * -velDir;
}
ApplyForce(dragForce + buoyancy, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
ApplyForce(dragForce + buoyancy);
ApplyTorque(FarseerBody.AngularVelocity * FarseerBody.Mass * -0.08f);
}
@@ -32,7 +32,10 @@ namespace Barotrauma
{ typeof(Rectangle), (str, defVal) => ParseRect(str, true) }
}.ToImmutableDictionary();
public static string ParseContentPathFromUri(this XObject element) => System.IO.Path.GetRelativePath(Environment.CurrentDirectory, element.BaseUri);
public static string ParseContentPathFromUri(this XObject element)
=> !string.IsNullOrWhiteSpace(element.BaseUri)
? System.IO.Path.GetRelativePath(Environment.CurrentDirectory, element.BaseUri.CleanUpPath())
: "";
public static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
{
@@ -1290,7 +1290,7 @@ namespace Barotrauma
Character targetCharacter = CharacterFromTarget(target);
if (targetCharacter != null && !targetCharacter.Removed)
{
targetCharacter?.Info?.GiveExperience(giveExperience, popupOffset: i * 25f);
targetCharacter?.Info?.GiveExperience(giveExperience);
i++;
}
}
@@ -1309,7 +1309,7 @@ namespace Barotrauma
// don't let clients simulate random skill gain
continue;
}
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount, targetCharacter.Position + Vector2.UnitY * (150.0f + i * 25f));
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount);
string GetRandomSkill()
{
@@ -1318,9 +1318,8 @@ namespace Barotrauma
}
else
{
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount, targetCharacter.Position + Vector2.UnitY * (150.0f + i * 25f));
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount);
}
i++;
}
}
}
@@ -633,7 +633,12 @@ namespace Barotrauma
{
if (string.IsNullOrEmpty(path)) { return ""; }
path = path.Replace('\\', '/');
path = path
.Replace('\\', '/');
if (path.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring("file:".Length);
}
while (path.IndexOf("//") >= 0)
{
path = path.Replace("//", "/");