Unstable 0.1500.4.0 (Shrek edition)
This commit is contained in:
+1
-1
@@ -464,7 +464,7 @@ namespace Barotrauma
|
||||
};
|
||||
break;
|
||||
case "return":
|
||||
newObjective = new AIObjectiveReturn(character, this, priorityModifier: priorityModifier);
|
||||
newObjective = new AIObjectiveReturn(character, orderGiver, this, priorityModifier: priorityModifier);
|
||||
newObjective.Abandoned += () => DismissSelf(order, option);
|
||||
newObjective.Completed += () => DismissSelf(order, option);
|
||||
break;
|
||||
|
||||
+1
-1
@@ -408,7 +408,7 @@ namespace Barotrauma
|
||||
}
|
||||
bool isCompleted =
|
||||
AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) ||
|
||||
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength < a.Prefab.TreatmentThreshold);
|
||||
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold);
|
||||
|
||||
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ namespace Barotrauma
|
||||
if (character.AIController is HumanAIController humanAI)
|
||||
{
|
||||
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) ||
|
||||
target.CharacterHealth.GetAllAfflictions().All(a => a.Strength < a.Prefab.TreatmentThreshold))
|
||||
target.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+5
-3
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
private bool usingEscapeBehavior;
|
||||
public Submarine ReturnTarget { get; }
|
||||
|
||||
public AIObjectiveReturn(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
ReturnTarget = GetReturnTarget(Submarine.MainSubs) ?? GetReturnTarget(Submarine.Loaded);
|
||||
if (ReturnTarget == null)
|
||||
@@ -23,10 +23,12 @@ namespace Barotrauma
|
||||
|
||||
Submarine GetReturnTarget(IEnumerable<Submarine> subs)
|
||||
{
|
||||
var requiredTeamID = orderGiver?.TeamID ?? character?.TeamID;
|
||||
Submarine returnTarget = null;
|
||||
foreach (var sub in subs)
|
||||
{
|
||||
if (sub?.TeamID != character.TeamID) { continue; }
|
||||
if (sub == null) { continue; }
|
||||
if (sub.TeamID != requiredTeamID) { continue; }
|
||||
returnTarget = sub;
|
||||
break;
|
||||
}
|
||||
@@ -229,7 +231,7 @@ namespace Barotrauma
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
base.OnAbandon();
|
||||
SteeringManager.Reset();
|
||||
SteeringManager?.Reset();
|
||||
if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective)
|
||||
{
|
||||
string msg = TextManager.Get("dialogcannotreturn", returnNull: true);
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace Barotrauma
|
||||
public readonly Vector2 Position;
|
||||
public readonly int WayPointID;
|
||||
|
||||
public bool blocked;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"PathNode {WayPointID}";
|
||||
@@ -81,6 +79,31 @@ namespace Barotrauma
|
||||
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
private bool? blocked;
|
||||
public bool IsBlocked()
|
||||
{
|
||||
if (blocked.HasValue) { return blocked.Value; }
|
||||
|
||||
blocked = false;
|
||||
|
||||
if (Waypoint.Submarine != null) { return blocked.Value; }
|
||||
if (Waypoint.Tunnel?.Type != Level.TunnelType.Cave) { return blocked.Value; }
|
||||
foreach (var w in Level.Loaded.ExtraWalls)
|
||||
{
|
||||
if (!(w is DestructibleLevelWall d)) { return blocked.Value; }
|
||||
if (d.Destroyed) { return blocked.Value; }
|
||||
if (!d.IsPointInside(Waypoint.Position)) { return blocked.Value; }
|
||||
blocked = true;
|
||||
break;
|
||||
}
|
||||
return blocked.Value;
|
||||
}
|
||||
|
||||
public void ResetBlocked()
|
||||
{
|
||||
blocked = null;
|
||||
}
|
||||
}
|
||||
|
||||
class PathFinder
|
||||
@@ -146,7 +169,10 @@ namespace Barotrauma
|
||||
|
||||
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
||||
{
|
||||
UpdateBlockedNodes();
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
node.ResetBlocked();
|
||||
}
|
||||
|
||||
//sort nodes roughly according to distance
|
||||
sortedNodes.Clear();
|
||||
@@ -202,11 +228,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (startNode == null || node.TempDistance < startNode.TempDistance)
|
||||
{
|
||||
if (node.blocked) { continue; }
|
||||
if (nodeFilter != null && !nodeFilter(node)) { continue; }
|
||||
if (startNodeFilter != null && !startNodeFilter(node)) { continue; }
|
||||
// Always check the visibility for the start node
|
||||
if (!IsWaypointVisible(node, start)) { continue; }
|
||||
if (node.IsBlocked()) { continue; }
|
||||
startNode = node;
|
||||
}
|
||||
}
|
||||
@@ -251,11 +277,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (endNode == null || node.TempDistance < endNode.TempDistance)
|
||||
{
|
||||
if (node.blocked) { continue; }
|
||||
if (nodeFilter != null && !nodeFilter(node)) { continue; }
|
||||
if (endNodeFilter != null && !endNodeFilter(node)) { continue; }
|
||||
// Only check the visibility for the end node when allowed (fix leaks)
|
||||
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { continue; }
|
||||
if (node.IsBlocked()) { continue; }
|
||||
endNode = node;
|
||||
}
|
||||
}
|
||||
@@ -326,15 +352,13 @@ namespace Barotrauma
|
||||
float dist = float.MaxValue;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
if (node.state != 1) { continue; }
|
||||
if (node.state != 1 || node.F > dist) { continue; }
|
||||
if (isCharacter && node.Waypoint.isObstructed) { continue; }
|
||||
if (node.blocked) { continue; }
|
||||
if (filter != null && !filter(node)) { continue; }
|
||||
if (node.F < dist)
|
||||
{
|
||||
dist = node.F;
|
||||
currNode = node;
|
||||
}
|
||||
if (node.IsBlocked()) { continue; }
|
||||
|
||||
dist = node.F;
|
||||
currNode = node;
|
||||
}
|
||||
|
||||
if (currNode == null || currNode == end) { break; }
|
||||
@@ -436,25 +460,6 @@ namespace Barotrauma
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private void UpdateBlockedNodes()
|
||||
{
|
||||
if (!isCharacter) { return; }
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
n.blocked = false;
|
||||
if (n.Waypoint.Submarine != null) { continue; }
|
||||
if (n.Waypoint.Tunnel?.Type != Level.TunnelType.Cave) { continue; }
|
||||
foreach (var w in Level.Loaded.ExtraWalls)
|
||||
{
|
||||
if (!(w is DestructibleLevelWall d)) { continue; }
|
||||
if (d.Destroyed) { continue; }
|
||||
if (!d.IsPointInside(n.Waypoint.Position)) { continue; }
|
||||
n.blocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this is HumanoidAnimController humanAnimController && humanAnimController.Crouching)
|
||||
{
|
||||
return humanAnimController.HumanCrouchParams;
|
||||
}
|
||||
return IsMovingFast ? RunParams : WalkParams;
|
||||
}
|
||||
}
|
||||
@@ -96,7 +100,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (CanWalk)
|
||||
{
|
||||
return new List<AnimationParams> { WalkParams, RunParams, SwimSlowParams, SwimFastParams };
|
||||
var anims = new List<AnimationParams> { WalkParams, RunParams, SwimSlowParams, SwimFastParams };
|
||||
if (this is HumanoidAnimController humanAnimController)
|
||||
{
|
||||
anims.Add(humanAnimController.HumanCrouchParams);
|
||||
}
|
||||
return anims;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -154,7 +163,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual void UpdateUseItem(bool allowMovement, Vector2 handWorldPos) { }
|
||||
|
||||
public float GetSpeed(AnimationType type)
|
||||
public virtual float GetSpeed(AnimationType type)
|
||||
{
|
||||
GroundedMovementParams movementParams;
|
||||
switch (type)
|
||||
@@ -207,7 +216,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
animType = AnimationType.Walk;
|
||||
if (this is HumanoidAnimController humanAnimController && humanAnimController.Crouching)
|
||||
{
|
||||
animType = AnimationType.Crouch;
|
||||
}
|
||||
else
|
||||
{
|
||||
animType = AnimationType.Walk;
|
||||
}
|
||||
}
|
||||
}
|
||||
return GetSpeed(animType);
|
||||
@@ -221,6 +237,12 @@ namespace Barotrauma
|
||||
return WalkParams;
|
||||
case AnimationType.Run:
|
||||
return RunParams;
|
||||
case AnimationType.Crouch:
|
||||
if (this is HumanoidAnimController humanAnimController)
|
||||
{
|
||||
return humanAnimController.HumanCrouchParams;
|
||||
}
|
||||
throw new NotImplementedException(type.ToString());
|
||||
case AnimationType.SwimSlow:
|
||||
return SwimSlowParams;
|
||||
case AnimationType.SwimFast:
|
||||
|
||||
@@ -97,9 +97,9 @@ namespace Barotrauma
|
||||
public new FishSwimParams CurrentSwimParams => base.CurrentSwimParams as FishSwimParams;
|
||||
|
||||
public float? TailAngle => GetValidOrNull(CurrentAnimationParams, CurrentFishAnimation?.TailAngleInRadians);
|
||||
public float FootTorque => CurrentFishAnimation.FootTorque;
|
||||
public float HeadTorque => CurrentFishAnimation.HeadTorque;
|
||||
public float TorsoTorque => CurrentFishAnimation.TorsoTorque;
|
||||
public float FootTorque => CurrentAnimationParams.FootTorque;
|
||||
public float HeadTorque => CurrentAnimationParams.HeadTorque;
|
||||
public float TorsoTorque => CurrentAnimationParams.TorsoTorque;
|
||||
public float TailTorque => CurrentFishAnimation.TailTorque;
|
||||
public float HeadMoveForce => CurrentGroundedParams.HeadMoveForce;
|
||||
public float TorsoMoveForce => CurrentGroundedParams.TorsoMoveForce;
|
||||
|
||||
+95
-87
@@ -2,7 +2,6 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
@@ -73,6 +72,20 @@ namespace Barotrauma
|
||||
set { _humanRunParams = value; }
|
||||
}
|
||||
|
||||
private HumanCrouchParams _humanCrouchParams;
|
||||
public HumanCrouchParams HumanCrouchParams
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_humanCrouchParams == null)
|
||||
{
|
||||
_humanCrouchParams = HumanCrouchParams.GetDefaultAnimParams(character);
|
||||
}
|
||||
return _humanCrouchParams;
|
||||
}
|
||||
set { _humanCrouchParams = value; }
|
||||
}
|
||||
|
||||
private HumanSwimSlowParams _humanSwimSlowParams;
|
||||
public HumanSwimSlowParams HumanSwimSlowParams
|
||||
{
|
||||
@@ -102,8 +115,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public new HumanGroundedParams CurrentGroundedParams => base.CurrentGroundedParams as HumanGroundedParams;
|
||||
|
||||
public new HumanSwimParams CurrentSwimParams => base.CurrentSwimParams as HumanSwimParams;
|
||||
|
||||
public IHumanAnimation CurrentHumanAnimParams => CurrentAnimationParams as IHumanAnimation;
|
||||
|
||||
public override GroundedMovementParams WalkParams
|
||||
{
|
||||
get { return HumanWalkParams; }
|
||||
@@ -169,42 +185,9 @@ namespace Barotrauma
|
||||
private float swimmingStateLockTimer;
|
||||
|
||||
private float useItemTimer;
|
||||
|
||||
public override float? TorsoPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Crouching && !swimming ? CurrentGroundedParams.CrouchingTorsoPos * RagdollParams.JointScale : base.TorsoPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override float? HeadPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Crouching && !swimming ? CurrentGroundedParams.CrouchingHeadPos * RagdollParams.JointScale : base.HeadPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override float? TorsoAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return Crouching && !swimming ? MathHelper.ToRadians(CurrentGroundedParams.CrouchingTorsoAngle) : base.TorsoAngle;
|
||||
}
|
||||
}
|
||||
|
||||
public override float? HeadAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return Crouching && !swimming ? MathHelper.ToRadians(CurrentGroundedParams.CrouchingHeadAngle) : base.HeadAngle;
|
||||
}
|
||||
}
|
||||
|
||||
public float HeadLeanAmount => CurrentGroundedParams.HeadLeanAmount;
|
||||
public float TorsoLeanAmount => CurrentGroundedParams.TorsoLeanAmount;
|
||||
public Vector2 FootMoveOffset => (Crouching ? CurrentGroundedParams.CrouchingFootMoveOffset : CurrentGroundedParams.FootMoveOffset) * RagdollParams.JointScale;
|
||||
public Vector2 FootMoveOffset => CurrentGroundedParams.FootMoveOffset * RagdollParams.JointScale;
|
||||
public float LegBendTorque => CurrentGroundedParams.LegBendTorque * RagdollParams.JointScale;
|
||||
public Vector2 HandMoveOffset => CurrentGroundedParams.HandMoveOffset * RagdollParams.JointScale;
|
||||
|
||||
@@ -298,7 +281,7 @@ namespace Barotrauma
|
||||
LimbType lowerLegType = LimbType.RightLeg;
|
||||
LimbType footType = LimbType.RightFoot;
|
||||
|
||||
var waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLegType);
|
||||
var waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLegType) ?? GetJointBetweenLimbs(LimbType.Torso, upperLegType);
|
||||
Vector2 localAnchorWaist = Vector2.Zero;
|
||||
Vector2 localAnchorKnee = Vector2.Zero;
|
||||
if (waistJoint != null)
|
||||
@@ -336,7 +319,7 @@ namespace Barotrauma
|
||||
levitatingCollider = true;
|
||||
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
||||
if (character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false ||
|
||||
(ForceSelectAnimationType != AnimationType.Walk && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||
{
|
||||
Crouching = false;
|
||||
ColliderIndex = 0;
|
||||
@@ -439,9 +422,8 @@ namespace Barotrauma
|
||||
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
|
||||
|
||||
if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
|
||||
|
||||
HandIK(rightHand, midPos);
|
||||
HandIK(leftHand, midPos);
|
||||
HandIK(rightHand, midPos, CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
||||
HandIK(leftHand, midPos, CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
||||
}
|
||||
else if (character.AnimController.AnimationTestPose)
|
||||
{
|
||||
@@ -638,7 +620,7 @@ namespace Barotrauma
|
||||
Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y);
|
||||
}
|
||||
|
||||
getUpForce = getUpForce * Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
|
||||
getUpForce *= Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
|
||||
|
||||
torso.PullJointEnabled = true;
|
||||
head.PullJointEnabled = true;
|
||||
@@ -710,9 +692,12 @@ namespace Barotrauma
|
||||
float torsoAngle = TorsoAngle.Value;
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
|
||||
torsoAngle -= herpesStrength / 150.0f;
|
||||
torso.body.SmoothRotate(torsoAngle * Dir, 50.0f);
|
||||
torso.body.SmoothRotate(torsoAngle * Dir, CurrentGroundedParams.TorsoTorque);
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
head.body.SmoothRotate(HeadAngle.Value * Dir, CurrentGroundedParams.HeadTorque);
|
||||
}
|
||||
if (HeadAngle.HasValue) head.body.SmoothRotate(HeadAngle.Value * Dir, 50.0f);
|
||||
|
||||
if (!onGround)
|
||||
{
|
||||
@@ -743,12 +728,23 @@ namespace Barotrauma
|
||||
Vector2 footPos = stepSize * -i;
|
||||
footPos += new Vector2(Math.Sign(movement.X) * FootMoveOffset.X, FootMoveOffset.Y);
|
||||
|
||||
if (footPos.Y < 0.0f) footPos.Y = -0.15f;
|
||||
if (footPos.Y < 0.0f) { footPos.Y = -0.15f; }
|
||||
|
||||
//make the character limp if the feet are damaged
|
||||
float footAfflictionStrength = character.CharacterHealth.GetAfflictionStrength("damage", foot, true);
|
||||
footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
|
||||
|
||||
if (CurrentGroundedParams.FootLiftHorizontalFactor > 0)
|
||||
{
|
||||
// Calculate the foot y dynamically based on the foot position relative to the waist,
|
||||
// so that the foot aims higher when it's behind the waist and lower when it's in the front.
|
||||
float xDiff = (foot.SimPosition.X - waistPos.X + FootMoveOffset.X) * Dir;
|
||||
float min = MathUtils.InverseLerp(1, 0, CurrentGroundedParams.FootLiftHorizontalFactor);
|
||||
float max = 1 + MathUtils.InverseLerp(0, 1, CurrentGroundedParams.FootLiftHorizontalFactor);
|
||||
float xFactor = MathHelper.Lerp(min, max, MathUtils.InverseLerp(RagdollParams.JointScale, -RagdollParams.JointScale, xDiff));
|
||||
footPos.Y *= xFactor;
|
||||
}
|
||||
|
||||
if (onSlope && Stairs == null)
|
||||
{
|
||||
footPos.Y *= 2.0f;
|
||||
@@ -770,7 +766,7 @@ namespace Barotrauma
|
||||
foot.DebugTargetPos = colliderPos + footPos;
|
||||
MoveLimb(foot, colliderPos + footPos, CurrentGroundedParams.FootMoveStrength);
|
||||
FootIK(foot, colliderPos + footPos,
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootRotateStrength, CurrentGroundedParams.FootAngleInRadians);
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,7 +785,7 @@ namespace Barotrauma
|
||||
HandIK(rightHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
-handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.HandMoveStrength);
|
||||
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
@@ -797,16 +793,14 @@ namespace Barotrauma
|
||||
HandIK(leftHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.HandMoveStrength);
|
||||
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = -1; i < 2; i += 2)
|
||||
{
|
||||
Vector2 footPos = colliderPos;
|
||||
|
||||
if (Crouching)
|
||||
{
|
||||
footPos = new Vector2(
|
||||
@@ -817,27 +811,24 @@ namespace Barotrauma
|
||||
//lift the foot at the back up a bit
|
||||
footPos.Y += 0.15f;
|
||||
}
|
||||
footPos.X += torso.SimPosition.X;
|
||||
footPos.X += colliderPos.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
footPos = new Vector2(colliderPos.X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f);
|
||||
}
|
||||
|
||||
if (Stairs == null)
|
||||
{
|
||||
footPos.Y = Math.Max(Math.Min(FloorY, footPos.Y + 0.5f), footPos.Y);
|
||||
}
|
||||
|
||||
var foot = i == -1 ? rightFoot : leftFoot;
|
||||
|
||||
if (foot != null && !foot.Disabled)
|
||||
{
|
||||
foot.DebugRefPos = colliderPos;
|
||||
foot.DebugTargetPos = footPos;
|
||||
MoveLimb(foot, footPos, CurrentGroundedParams.FootMoveStrength);
|
||||
FootIK(foot, footPos,
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootRotateStrength, CurrentGroundedParams.FootAngleInRadians);
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,7 +961,7 @@ namespace Barotrauma
|
||||
if (!aiming)
|
||||
{
|
||||
float newRotation = MathUtils.VectorToAngle(TargetMovement) - MathHelper.PiOver2;
|
||||
Collider.SmoothRotate(newRotation, 5.0f * character.SpeedMultiplier);
|
||||
Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -981,7 +972,7 @@ namespace Barotrauma
|
||||
Vector2 diff = (mousePos - torso.SimPosition) * Dir;
|
||||
TargetMovement = new Vector2(0.0f, -0.1f);
|
||||
float newRotation = MathUtils.VectorToAngle(diff);
|
||||
Collider.SmoothRotate(newRotation, 5.0f * character.SpeedMultiplier);
|
||||
Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,19 +982,19 @@ namespace Barotrauma
|
||||
|
||||
if (TorsoAngle.HasValue)
|
||||
{
|
||||
torso.body.SmoothRotate(Collider.Rotation + TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque);
|
||||
torso.body.SmoothRotate(Collider.Rotation + TorsoAngle.Value * Dir, CurrentSwimParams.TorsoTorque);
|
||||
}
|
||||
else
|
||||
{
|
||||
torso.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.SteerTorque);
|
||||
torso.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.TorsoTorque);
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
head.body.SmoothRotate(Collider.Rotation + HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
|
||||
head.body.SmoothRotate(Collider.Rotation + HeadAngle.Value * Dir, CurrentSwimParams.HeadTorque);
|
||||
}
|
||||
else
|
||||
{
|
||||
head.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.SteerTorque);
|
||||
head.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.HeadTorque);
|
||||
}
|
||||
|
||||
//dont try to move upwards if head is already out of water
|
||||
@@ -1044,25 +1035,25 @@ namespace Barotrauma
|
||||
float legMoveMultiplier = 1.0f;
|
||||
if (movement.LengthSquared() < 0.001f)
|
||||
{
|
||||
//TODO: expose these?
|
||||
// Swimming in place (TODO: expose?)
|
||||
legMoveMultiplier = 0.3f;
|
||||
legCyclePos += 0.4f;
|
||||
handCyclePos += 0.1f;
|
||||
}
|
||||
|
||||
var waist = GetLimb(LimbType.Waist);
|
||||
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
||||
footPos = waist == null ? Vector2.Zero : waist.SimPosition - new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * (upperLegLength + lowerLegLength);
|
||||
Vector2 transformedFootPos = new Vector2((float)Math.Sin(legCyclePos / CurrentSwimParams.LegCycleLength) * CurrentSwimParams.LegMoveAmount * legMoveMultiplier, 0.0f);
|
||||
transformedFootPos = Vector2.Transform(transformedFootPos, Matrix.CreateRotationZ(Collider.Rotation));
|
||||
|
||||
float torque = CurrentSwimParams.FootRotateStrength * character.SpeedMultiplier * (1.2f - character.GetLegPenalty());
|
||||
float legTorque = CurrentSwimParams.LegTorque * character.SpeedMultiplier * (1.2f - character.GetLegPenalty());
|
||||
if (rightFoot != null && !rightFoot.Disabled)
|
||||
{
|
||||
FootIK(rightFoot, footPos - transformedFootPos, torque, torque, CurrentSwimParams.FootAngleInRadians);
|
||||
FootIK(rightFoot, footPos - transformedFootPos, legTorque, CurrentSwimParams.FootTorque, CurrentSwimParams.FootAngleInRadians);
|
||||
}
|
||||
if (leftFoot != null && !leftFoot.Disabled)
|
||||
{
|
||||
FootIK(leftFoot, footPos + transformedFootPos, torque, torque, CurrentSwimParams.FootAngleInRadians);
|
||||
FootIK(leftFoot, footPos + transformedFootPos, legTorque, CurrentSwimParams.FootTorque, CurrentSwimParams.FootAngleInRadians);
|
||||
}
|
||||
|
||||
handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
|
||||
@@ -1071,7 +1062,7 @@ namespace Barotrauma
|
||||
// -> hands just float around
|
||||
if ((!headInWater && TargetMovement.X == 0.0f && TargetMovement.Y > 0) || TargetMovement.LengthSquared() < 0.001f)
|
||||
{
|
||||
handPos += MathUtils.RotatePoint(Vector2.UnitX * Dir * 0.6f, torso.Rotation);
|
||||
handPos += MathUtils.RotatePoint(Vector2.UnitX * Dir * 0.2f, torso.Rotation);
|
||||
|
||||
float wobbleAmount = 0.1f;
|
||||
|
||||
@@ -1079,14 +1070,14 @@ namespace Barotrauma
|
||||
{
|
||||
MoveLimb(rightHand, new Vector2(
|
||||
handPos.X + (float)Math.Sin(handCyclePos / 1.5f) * wobbleAmount,
|
||||
handPos.Y + (float)Math.Sin(handCyclePos / 3.5f) * wobbleAmount - 0.25f), 1.5f);
|
||||
handPos.Y + (float)Math.Sin(handCyclePos / 3.5f) * wobbleAmount - 0.25f), CurrentSwimParams.ArmMoveStrength);
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
{
|
||||
MoveLimb(leftHand, new Vector2(
|
||||
handPos.X + (float)Math.Sin(handCyclePos / 2.0f) * wobbleAmount,
|
||||
handPos.Y + (float)Math.Sin(handCyclePos / 3.0f) * wobbleAmount - 0.25f), 1.5f);
|
||||
handPos.Y + (float)Math.Sin(handCyclePos / 3.0f) * wobbleAmount - 0.25f), CurrentSwimParams.ArmMoveStrength);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1107,8 +1098,8 @@ namespace Barotrauma
|
||||
Vector2 rightHandPos = new Vector2(-handPosX, -handPosY) + handMoveOffset;
|
||||
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
|
||||
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
|
||||
|
||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()));
|
||||
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetRightHandPenalty());
|
||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
@@ -1116,8 +1107,8 @@ namespace Barotrauma
|
||||
Vector2 leftHandPos = new Vector2(handPosX, handPosY) + handMoveOffset;
|
||||
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
|
||||
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
|
||||
|
||||
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()));
|
||||
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty());
|
||||
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1173,15 +1164,16 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float bottomPos = Collider.SimPosition.Y - ColliderHeightFromFloor - Collider.radius - Collider.height / 2.0f;
|
||||
float headPos = HeadPosition ?? 0;
|
||||
float torsoPos = TorsoPosition ?? 0;
|
||||
MoveLimb(head, new Vector2(ladderSimPos.X - 0.2f * Dir, bottomPos + headPos), 10.5f);
|
||||
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.35f * Dir, bottomPos + torsoPos), 10.5f);
|
||||
|
||||
MoveLimb(head, new Vector2(ladderSimPos.X - 0.35f * Dir, bottomPos + WalkParams.HeadPosition), 10.5f);
|
||||
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.35f * Dir, bottomPos + WalkParams.TorsoPosition), 10.5f);
|
||||
|
||||
Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.1f * Dir, Collider.SimPosition.Y), 10.5f);
|
||||
Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.1f * Dir, Collider.SimPosition.Y), 10.5f);
|
||||
|
||||
Vector2 handPos = new Vector2(
|
||||
ladderSimPos.X,
|
||||
bottomPos + WalkParams.TorsoPosition + movement.Y * 0.1f - ladderSimPos.Y);
|
||||
bottomPos + torsoPos + movement.Y * 0.1f - ladderSimPos.Y);
|
||||
|
||||
//prevent the hands from going above the top of the ladders
|
||||
handPos.Y = Math.Min(-0.5f, handPos.Y);
|
||||
@@ -1258,7 +1250,8 @@ namespace Barotrauma
|
||||
|
||||
//apply forces to the collider to move the Character up/down
|
||||
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
head.body.SmoothRotate(0.0f);
|
||||
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
|
||||
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, WalkParams.HeadTorque);
|
||||
|
||||
if (!character.SelectedConstruction.Prefab.Triggers.Any())
|
||||
{
|
||||
@@ -1881,7 +1874,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (!character.Inventory.IsInLimbSlot(item, i == 0 ? InvSlotType.RightHand : InvSlotType.LeftHand)) { continue; }
|
||||
HandIK(i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i]);
|
||||
HandIK(i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i], CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1906,7 +1899,7 @@ namespace Barotrauma
|
||||
return (lowFreqNoise * 1.0f + highFreqNoise * 0.1f) * wobbleStrength;
|
||||
}
|
||||
|
||||
private void HandIK(Limb hand, Vector2 pos, float force = 1.0f)
|
||||
private void HandIK(Limb hand, Vector2 pos, float armTorque = 1.0f, float handTorque = 1.0f)
|
||||
{
|
||||
Vector2 shoulderPos;
|
||||
|
||||
@@ -1948,9 +1941,11 @@ namespace Barotrauma
|
||||
armAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
arm?.body.SmoothRotate((armAngle - upperArmAngle), 20.0f * force * arm.Mass, wrapAngle: false);
|
||||
forearm?.body.SmoothRotate((armAngle + lowerArmAngle), 20.0f * force * forearm.Mass, wrapAngle: false);
|
||||
hand?.body.SmoothRotate((armAngle + lowerArmAngle), 100.0f * force * hand.Mass, wrapAngle: false);
|
||||
arm?.body.SmoothRotate(armAngle - upperArmAngle, 100.0f * armTorque * arm.Mass, wrapAngle: false);
|
||||
float forearmAngle = armAngle + lowerArmAngle;
|
||||
forearm?.body.SmoothRotate(forearmAngle, 100.0f * handTorque * forearm.Mass, wrapAngle: false);
|
||||
float handAngle = forearm != null ? armAngle : forearmAngle;
|
||||
hand?.body.SmoothRotate(handAngle, 100.0f * handTorque * hand.Mass, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
|
||||
@@ -1976,12 +1971,12 @@ namespace Barotrauma
|
||||
upperLeg = GetLimb(LimbType.RightThigh);
|
||||
lowerLeg = GetLimb(LimbType.RightLeg);
|
||||
}
|
||||
var torso = GetLimb(LimbType.Torso);
|
||||
var waist = GetJointBetweenLimbs(LimbType.Waist, upperLeg.type);
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
LimbJoint waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLeg.type) ?? GetJointBetweenLimbs(LimbType.Torso, upperLeg.type);
|
||||
Vector2 waistPos = Vector2.Zero;
|
||||
if (waist != null)
|
||||
if (waistJoint != null)
|
||||
{
|
||||
waistPos = waist.LimbA == upperLeg ? waist.WorldAnchorA : waist.WorldAnchorB;
|
||||
waistPos = waistJoint.LimbA == upperLeg ? waistJoint.WorldAnchorA : waistJoint.WorldAnchorB;
|
||||
}
|
||||
|
||||
//distance from waist joint to the target position
|
||||
@@ -2137,5 +2132,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetSpeed(AnimationType type)
|
||||
{
|
||||
if (type == AnimationType.Crouch)
|
||||
{
|
||||
if (!CanWalk)
|
||||
{
|
||||
DebugConsole.ThrowError($"{character.SpeciesName} cannot crouch!");
|
||||
return 0;
|
||||
}
|
||||
return IsMovingBackwards ? HumanCrouchParams.MovementSpeed * HumanCrouchParams.BackwardsMovementMultiplier : HumanCrouchParams.MovementSpeed;
|
||||
}
|
||||
return base.GetSpeed(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,8 +309,6 @@ namespace Barotrauma
|
||||
|
||||
public string TraitorCurrentObjective = "";
|
||||
public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase);
|
||||
public bool IsMale => Info != null && Info.HasGenders && Info.Gender == Gender.Male;
|
||||
public bool IsFemale => Info != null && Info.HasGenders && Info.Gender == Gender.Female;
|
||||
|
||||
private float attackCoolDown;
|
||||
|
||||
@@ -1665,9 +1663,9 @@ namespace Barotrauma
|
||||
AnimController.IgnorePlatforms = AnimController.TargetMovement.Y < -0.1f;
|
||||
}
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
if (AnimController is HumanoidAnimController humanAnimController)
|
||||
{
|
||||
((HumanoidAnimController)AnimController).Crouching = IsKeyDown(InputType.Crouch);
|
||||
humanAnimController.Crouching = humanAnimController.ForceSelectAnimationType == AnimationType.Crouch || IsKeyDown(InputType.Crouch);
|
||||
}
|
||||
|
||||
if (!aiControlled &&
|
||||
@@ -2760,9 +2758,9 @@ namespace Barotrauma
|
||||
//ragdoll button
|
||||
if (IsRagdolled || !CanMove)
|
||||
{
|
||||
if (AnimController is HumanoidAnimController)
|
||||
if (AnimController is HumanoidAnimController humanAnimController)
|
||||
{
|
||||
((HumanoidAnimController)AnimController).Crouching = false;
|
||||
humanAnimController.Crouching = false;
|
||||
}
|
||||
AnimController.ResetPullJoints();
|
||||
SelectedConstruction = null;
|
||||
@@ -3505,9 +3503,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse = 0.0f, Character attacker = null)
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse = 0.0f, Character attacker = null, float damageMultiplier = 1f)
|
||||
{
|
||||
return AddDamage(worldPosition, afflictions, stun, playSound, attackImpulse, out _, attacker);
|
||||
return AddDamage(worldPosition, afflictions, stun, playSound, attackImpulse, out _, attacker, damageMultiplier: damageMultiplier);
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, out Limb hitLimb, Character attacker = null, float damageMultiplier = 1)
|
||||
@@ -4351,7 +4349,7 @@ namespace Barotrauma
|
||||
return GiveTalent(talentPrefab, addingFirstTime);
|
||||
}
|
||||
|
||||
private bool GiveTalent(TalentPrefab talentPrefab, bool addingFirstTime = true)
|
||||
public bool GiveTalent(TalentPrefab talentPrefab, bool addingFirstTime = true)
|
||||
{
|
||||
if (addingFirstTime)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -14,7 +14,6 @@ namespace Barotrauma
|
||||
public enum Gender { None, Male, Female };
|
||||
public enum Race { None, White, Black, Brown, Asian };
|
||||
|
||||
// TODO: Generating the HeadInfo could be simplified.
|
||||
partial class CharacterInfo
|
||||
{
|
||||
public class HeadInfo
|
||||
@@ -25,15 +24,7 @@ namespace Barotrauma
|
||||
get { return _headSpriteId; }
|
||||
set
|
||||
{
|
||||
_headSpriteId = value;
|
||||
if (_headSpriteId < (int)headSpriteRange.X)
|
||||
{
|
||||
_headSpriteId = (int)headSpriteRange.Y;
|
||||
}
|
||||
if (_headSpriteId > (int)headSpriteRange.Y)
|
||||
{
|
||||
_headSpriteId = (int)headSpriteRange.X;
|
||||
}
|
||||
_headSpriteId = Math.Max(Math.Clamp(value, (int)headSpriteRange.X, (int)headSpriteRange.Y), 1);
|
||||
GetSpriteSheetIndex();
|
||||
}
|
||||
}
|
||||
@@ -42,6 +33,10 @@ namespace Barotrauma
|
||||
public Gender gender;
|
||||
public Race race;
|
||||
|
||||
public Color HairColor;
|
||||
public Color FacialHairColor;
|
||||
public Color SkinColor;
|
||||
|
||||
public int HairIndex { get; set; } = -1;
|
||||
public int BeardIndex { get; set; } = -1;
|
||||
public int MoustacheIndex { get; set; } = -1;
|
||||
@@ -74,11 +69,11 @@ namespace Barotrauma
|
||||
FaceAttachmentIndex = -1;
|
||||
}
|
||||
|
||||
private void GetSpriteSheetIndex()
|
||||
public void GetSpriteSheetIndex()
|
||||
{
|
||||
if (heads != null && heads.Any())
|
||||
{
|
||||
var matchingHead = heads.Keys.FirstOrDefault(h => h.Gender == gender && h.Race == race && h.ID == _headSpriteId);
|
||||
var matchingHead = heads.Keys.FirstOrDefault(h => h.ID == HeadSpriteId && IsMatchingGender(h.Gender, gender) && IsMatchingRace(h.Race, race));
|
||||
if (matchingHead != null)
|
||||
{
|
||||
if (heads.TryGetValue(matchingHead, out Vector2 index))
|
||||
@@ -99,14 +94,13 @@ namespace Barotrauma
|
||||
if (head != value && value != null)
|
||||
{
|
||||
head = value;
|
||||
if (head.race == Race.None)
|
||||
if (!IsValidRace(head.race))
|
||||
{
|
||||
head.race = GetRandomRace(Rand.RandSync.Unsynced);
|
||||
}
|
||||
CalculateHeadSpriteRange();
|
||||
Head.HeadSpriteId = value.HeadSpriteId;
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
RefreshHeadSprites();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +151,9 @@ namespace Barotrauma
|
||||
|
||||
public bool HasNickname => Name != OriginalName;
|
||||
public string OriginalName { get; private set; }
|
||||
|
||||
public string Name;
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
@@ -387,29 +383,31 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
Head.HeadSpriteId = value;
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
ResetHeadAttachments();
|
||||
RefreshHeadSprites();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool HasGenders;
|
||||
public readonly bool HasRaces;
|
||||
|
||||
public Gender Gender
|
||||
{
|
||||
get { return Head.gender; }
|
||||
set
|
||||
{
|
||||
if (Head.gender == value) return;
|
||||
Gender previousValue = Head.gender;
|
||||
Head.gender = value;
|
||||
if (Head.gender == Gender.None)
|
||||
if (!IsValidGender(Head.gender))
|
||||
{
|
||||
Head.gender = Gender.Male;
|
||||
Head.gender = GetDefaultGender();
|
||||
}
|
||||
if (Head.gender != previousValue)
|
||||
{
|
||||
CalculateHeadSpriteRange();
|
||||
ResetHeadAttachments();
|
||||
RefreshHeadSprites();
|
||||
}
|
||||
CalculateHeadSpriteRange();
|
||||
ResetHeadAttachments();
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,28 +416,82 @@ namespace Barotrauma
|
||||
get { return Head.race; }
|
||||
set
|
||||
{
|
||||
if (Head.race == value) { return; }
|
||||
Race previousValue = Head.race;
|
||||
Head.race = value;
|
||||
if (Head.race == Race.None)
|
||||
if (!IsValidRace(Head.race))
|
||||
{
|
||||
Head.race = Race.White;
|
||||
Head.race = GetDefaultRace();
|
||||
}
|
||||
if (Head.race != previousValue)
|
||||
{
|
||||
CalculateHeadSpriteRange();
|
||||
ResetHeadAttachments();
|
||||
RefreshHeadSprites();
|
||||
}
|
||||
CalculateHeadSpriteRange();
|
||||
ResetHeadAttachments();
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int HairIndex { get => Head.HairIndex; set => Head.HairIndex = value; }
|
||||
public int BeardIndex { get => Head.BeardIndex; set => Head.BeardIndex = value; }
|
||||
public int MoustacheIndex { get => Head.MoustacheIndex; set => Head.MoustacheIndex = value; }
|
||||
public int FaceAttachmentIndex { get => Head.FaceAttachmentIndex; set => Head.FaceAttachmentIndex = value; }
|
||||
private bool IsValidRace(Race race) => HasRaces ? race != Race.None : race == Race.None;
|
||||
|
||||
public XElement HairElement { get => Head.HairElement; set => Head.HairElement = value; }
|
||||
public XElement BeardElement { get => Head.BeardElement; set => Head.BeardElement = value; }
|
||||
public XElement MoustacheElement { get => Head.MoustacheElement; set => Head.MoustacheElement = value; }
|
||||
public XElement FaceAttachment { get => Head.FaceAttachment; set => Head.FaceAttachment = value; }
|
||||
private bool IsValidGender(Gender gender) => HasGenders ? gender != Gender.None : gender == Gender.None;
|
||||
|
||||
private Gender GetDefaultGender() => HasGenders ? Gender.Male : Gender.None;
|
||||
|
||||
private Race GetDefaultRace() => HasRaces ? Race.White : Race.None;
|
||||
|
||||
public int HairIndex
|
||||
{
|
||||
get => Head.HairIndex;
|
||||
set => Head.HairIndex = value;
|
||||
}
|
||||
|
||||
public int BeardIndex
|
||||
{
|
||||
get => Head.BeardIndex;
|
||||
set => Head.BeardIndex = value;
|
||||
}
|
||||
|
||||
public int MoustacheIndex
|
||||
{
|
||||
get => Head.MoustacheIndex;
|
||||
set => Head.MoustacheIndex = value;
|
||||
}
|
||||
|
||||
public int FaceAttachmentIndex
|
||||
{
|
||||
get => Head.FaceAttachmentIndex;
|
||||
set => Head.FaceAttachmentIndex = value;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<Color> HairColors;
|
||||
public readonly ImmutableArray<Color> FacialHairColors;
|
||||
public readonly ImmutableArray<Color> SkinColors;
|
||||
|
||||
public Color HairColor
|
||||
{
|
||||
get => Head.HairColor;
|
||||
set => Head.HairColor = value;
|
||||
}
|
||||
|
||||
public Color FacialHairColor
|
||||
{
|
||||
get => Head.FacialHairColor;
|
||||
set => Head.FacialHairColor = value;
|
||||
}
|
||||
|
||||
public Color SkinColor
|
||||
{
|
||||
get => Head.SkinColor;
|
||||
set => Head.SkinColor = value;
|
||||
}
|
||||
|
||||
public XElement HairElement => Head.HairElement;
|
||||
|
||||
public XElement BeardElement => Head.BeardElement;
|
||||
|
||||
public XElement MoustacheElement => Head.MoustacheElement;
|
||||
|
||||
public XElement FaceAttachment => Head.FaceAttachment;
|
||||
|
||||
private RagdollParams ragdoll;
|
||||
public RagdollParams Ragdoll
|
||||
@@ -480,16 +532,18 @@ namespace Barotrauma
|
||||
if (doc == null) { return; }
|
||||
CharacterConfigElement = doc.Root.IsOverride() ? doc.Root.FirstElement() : doc.Root;
|
||||
// TODO: support for variants
|
||||
head = new HeadInfo();
|
||||
Head = new HeadInfo();
|
||||
HasGenders = CharacterConfigElement.GetAttributeBool("genders", false);
|
||||
if (HasGenders)
|
||||
{
|
||||
Head.gender = GetRandomGender(randSync);
|
||||
}
|
||||
Head.gender = GetRandomGender(randSync);
|
||||
HasRaces = CharacterConfigElement.GetAttributeBool("races", false);
|
||||
Head.race = GetRandomRace(randSync);
|
||||
CalculateHeadSpriteRange();
|
||||
Head.HeadSpriteId = GetRandomHeadID(randSync);
|
||||
HeadSpriteId = GetRandomHeadID(randSync);
|
||||
Job = (jobPrefab == null) ? Job.Random(Rand.RandSync.Unsynced) : new Job(jobPrefab, variant);
|
||||
HairColors = CharacterConfigElement.GetAttributeColorArray("haircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeColorArray("facialhaircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeColorArray("skincolors", new Color[] { new Color(255, 215, 200, 255) }).ToImmutableArray();
|
||||
SetColors();
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
@@ -502,23 +556,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
name = "";
|
||||
if (CharacterConfigElement.Element("name") != null)
|
||||
{
|
||||
string firstNamePath = CharacterConfigElement.Element("name").GetAttributeString("firstname", "");
|
||||
if (firstNamePath != "")
|
||||
{
|
||||
firstNamePath = firstNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
Name = ToolBox.GetRandomLine(firstNamePath, randSync);
|
||||
}
|
||||
|
||||
string lastNamePath = CharacterConfigElement.Element("name").GetAttributeString("lastname", "");
|
||||
if (lastNamePath != "")
|
||||
{
|
||||
lastNamePath = lastNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
if (Name != "") Name += " ";
|
||||
Name += ToolBox.GetRandomLine(lastNamePath, randSync);
|
||||
}
|
||||
}
|
||||
Name = GetRandomName(randSync);
|
||||
}
|
||||
OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : Name;
|
||||
personalityTrait = NPCPersonalityTrait.GetRandom(name + HeadSpriteId);
|
||||
@@ -530,6 +568,53 @@ namespace Barotrauma
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
|
||||
public string GetRandomName(Rand.RandSync randSync)
|
||||
{
|
||||
string name = "";
|
||||
if (CharacterConfigElement.Element("name") != null)
|
||||
{
|
||||
string firstNamePath = CharacterConfigElement.Element("name").GetAttributeString("firstname", "");
|
||||
if (firstNamePath != "")
|
||||
{
|
||||
firstNamePath = firstNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
name = ToolBox.GetRandomLine(firstNamePath, randSync);
|
||||
}
|
||||
|
||||
string lastNamePath = CharacterConfigElement.Element("name").GetAttributeString("lastname", "");
|
||||
if (lastNamePath != "")
|
||||
{
|
||||
lastNamePath = lastNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
if (name != "") { name += " "; }
|
||||
name += ToolBox.GetRandomLine(lastNamePath, randSync);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private void SetColors()
|
||||
{
|
||||
HairColor = HairColors.GetRandom();
|
||||
FacialHairColor = FacialHairColors.GetRandom();
|
||||
SkinColor = SkinColors.GetRandom();
|
||||
}
|
||||
|
||||
private void CheckColors()
|
||||
{
|
||||
if (HairColor == Color.Black)
|
||||
{
|
||||
HairColor = HairColors.GetRandom();
|
||||
}
|
||||
if (FacialHairColor == Color.Black)
|
||||
{
|
||||
FacialHairColor = FacialHairColors.GetRandom();
|
||||
}
|
||||
if (SkinColor == Color.Black)
|
||||
{
|
||||
SkinColor = SkinColors.GetRandom();
|
||||
}
|
||||
}
|
||||
|
||||
// Used for loading the data
|
||||
public CharacterInfo(XElement infoElement)
|
||||
{
|
||||
@@ -542,7 +627,7 @@ namespace Barotrauma
|
||||
ExperiencePoints = infoElement.GetAttributeInt("experiencepoints", 0);
|
||||
UnlockedTalents = new HashSet<string>(infoElement.GetAttributeStringArray("unlockedtalents", new string[0], convertToLowerInvariant: true));
|
||||
AdditionalTalentPoints = infoElement.GetAttributeInt("additionaltalentpoints", 0);
|
||||
Enum.TryParse(infoElement.GetAttributeString("race", "White"), true, out Race race);
|
||||
Enum.TryParse(infoElement.GetAttributeString("race", "None"), true, out Race race);
|
||||
Enum.TryParse(infoElement.GetAttributeString("gender", "None"), true, out Gender gender);
|
||||
_speciesName = infoElement.GetAttributeString("speciesname", null);
|
||||
XDocument doc = null;
|
||||
@@ -560,14 +645,19 @@ namespace Barotrauma
|
||||
// TODO: support for variants
|
||||
CharacterConfigElement = doc.Root.IsOverride() ? doc.Root.FirstElement() : doc.Root;
|
||||
HasGenders = CharacterConfigElement.GetAttributeBool("genders", false);
|
||||
if (HasGenders && gender == Gender.None)
|
||||
HasRaces = CharacterConfigElement.GetAttributeBool("hasraces", false);
|
||||
if (!IsValidGender(gender))
|
||||
{
|
||||
gender = GetRandomGender(Rand.RandSync.Unsynced);
|
||||
}
|
||||
else if (!HasGenders)
|
||||
if (!IsValidRace(race))
|
||||
{
|
||||
gender = Gender.None;
|
||||
race = GetRandomRace(Rand.RandSync.Unsynced);
|
||||
}
|
||||
HairColors = CharacterConfigElement.GetAttributeColorArray("haircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeColorArray("facialhaircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeColorArray("skincolors", new Color[] { new Color(255, 215, 200, 255) }).ToImmutableArray();
|
||||
|
||||
RecreateHead(
|
||||
infoElement.GetAttributeInt("headspriteid", 1),
|
||||
race,
|
||||
@@ -577,6 +667,11 @@ namespace Barotrauma
|
||||
infoElement.GetAttributeInt("moustacheindex", -1),
|
||||
infoElement.GetAttributeInt("faceattachmentindex", -1));
|
||||
|
||||
SkinColor = infoElement.GetAttributeColor("skincolor", Color.White);
|
||||
HairColor = infoElement.GetAttributeColor("haircolor", Color.White);
|
||||
FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.White);
|
||||
CheckColors();
|
||||
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
{
|
||||
if (CharacterConfigElement.Element("name") != null)
|
||||
@@ -652,8 +747,25 @@ namespace Barotrauma
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
|
||||
public Gender GetRandomGender(Rand.RandSync randSync) => (Rand.Range(0.0f, 1.0f, randSync) < CharacterConfigElement.GetAttributeFloat("femaleratio", 0.5f)) ? Gender.Female : Gender.Male;
|
||||
public Race GetRandomRace(Rand.RandSync randSync) => new Race[] { Race.White, Race.Black, Race.Asian }.GetRandom(randSync);
|
||||
public Gender GetRandomGender(Rand.RandSync randSync)
|
||||
{
|
||||
if (HasGenders)
|
||||
{
|
||||
return (Rand.Range(0.0f, 1.0f, randSync) < CharacterConfigElement.GetAttributeFloat("femaleratio", 0.5f)) ? Gender.Female : Gender.Male;
|
||||
}
|
||||
return Gender.None;
|
||||
}
|
||||
|
||||
public Race GetRandomRace(Rand.RandSync randSync)
|
||||
{
|
||||
if (HasRaces)
|
||||
{
|
||||
return new Race[] { Race.White, Race.Black, Race.Asian }.GetRandom(randSync);
|
||||
}
|
||||
return Race.None;
|
||||
}
|
||||
|
||||
|
||||
public int GetRandomHeadID(Rand.RandSync randSync) => Head.headSpriteRange != Vector2.Zero ? Rand.Range((int)Head.headSpriteRange.X, (int)Head.headSpriteRange.Y + 1, randSync) : 0;
|
||||
|
||||
private List<XElement> hairs;
|
||||
@@ -720,10 +832,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (elements == null) { return elements; }
|
||||
return elements.Where(w =>
|
||||
Enum.TryParse(w.GetAttributeString("gender", "None"), true, out Gender g) && g == gender &&
|
||||
Enum.TryParse(w.GetAttributeString("race", "None"), true, out Race r) && r == race);
|
||||
IsMatchingGender(Enum.Parse<Gender>(w.GetAttributeString("gender", "None"), ignoreCase: true), gender) &&
|
||||
IsMatchingRace(Enum.Parse<Race>(w.GetAttributeString("race", "None"), ignoreCase: true), race));
|
||||
}
|
||||
|
||||
public static bool IsMatchingGender(Gender gender, Gender myGender) => gender == Gender.None || gender == myGender;
|
||||
public static bool IsMatchingRace(Race race, Race myRace) => race == Race.None || race == myRace;
|
||||
|
||||
private void LoadHeadPresets()
|
||||
{
|
||||
if (CharacterConfigElement == null) { return; }
|
||||
@@ -752,9 +867,16 @@ namespace Barotrauma
|
||||
// If there are any head presets defined, use them.
|
||||
if (heads.Any())
|
||||
{
|
||||
var ids = heads.Keys.Where(h => h.Race == Race && h.Gender == Gender).Select(w => w.ID);
|
||||
var ids = heads.Keys.Where(h => IsMatchingRace(Race, h.Race) && IsMatchingGender(Gender, h.Gender)).Select(w => w.ID);
|
||||
ids = ids.OrderBy(id => id);
|
||||
Head.headSpriteRange = new Vector2(ids.First(), ids.Last());
|
||||
if (ids.Any())
|
||||
{
|
||||
Head.headSpriteRange = new Vector2(ids.First(), ids.Last());
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"[CharacterInfo] Couldn't find a head definition that matches {Race} and {Gender}!");
|
||||
}
|
||||
}
|
||||
// Else we calculate the range from the wearables.
|
||||
if (Head.headSpriteRange == Vector2.Zero)
|
||||
@@ -787,26 +909,72 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void RecreateHead(HeadInfo headInfo)
|
||||
{
|
||||
RecreateHead(
|
||||
headInfo.HeadSpriteId,
|
||||
headInfo.race,
|
||||
headInfo.gender,
|
||||
headInfo.HairIndex,
|
||||
headInfo.BeardIndex,
|
||||
headInfo.MoustacheIndex,
|
||||
headInfo.FaceAttachmentIndex);
|
||||
|
||||
SkinColor = headInfo.SkinColor;
|
||||
HairColor = headInfo.HairColor;
|
||||
FacialHairColor = headInfo.FacialHairColor;
|
||||
CheckColors();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recreates the head info and checks that everything is valid.
|
||||
/// </summary>
|
||||
public void RecreateHead(int headID, Race race, Gender gender, int hairIndex, int beardIndex, int moustacheIndex, int faceAttachmentIndex)
|
||||
{
|
||||
if (HasGenders && gender == Gender.None)
|
||||
if (!IsValidGender(gender))
|
||||
{
|
||||
gender = GetRandomGender(Rand.RandSync.Unsynced);
|
||||
}
|
||||
else if (!HasGenders)
|
||||
if (!IsValidRace(race))
|
||||
{
|
||||
gender = Gender.None;
|
||||
race = GetRandomRace(Rand.RandSync.Unsynced);
|
||||
}
|
||||
if (heads == null)
|
||||
{
|
||||
LoadHeadPresets();
|
||||
}
|
||||
head = new HeadInfo(headID, gender, race, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
Color skin = Color.Black;
|
||||
Color hair = Color.Black;
|
||||
Color facialHair = Color.Black;
|
||||
if (head != null)
|
||||
{
|
||||
skin = head.SkinColor;
|
||||
hair = head.HairColor;
|
||||
facialHair = head.FacialHairColor;
|
||||
}
|
||||
head = new HeadInfo(headID, gender, race, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex)
|
||||
{
|
||||
SkinColor = skin,
|
||||
HairColor = hair,
|
||||
FacialHairColor = facialHair
|
||||
};
|
||||
CalculateHeadSpriteRange();
|
||||
ReloadHeadAttachments();
|
||||
RefreshHead();
|
||||
}
|
||||
|
||||
public void LoadHeadSprite()
|
||||
/// <summary>
|
||||
/// Reloads the head sprite and the attachment sprites.
|
||||
/// </summary>
|
||||
public void RefreshHead()
|
||||
{
|
||||
ReloadHeadAttachments();
|
||||
RefreshHeadSprites();
|
||||
}
|
||||
|
||||
partial void LoadHeadSpriteProjectSpecific(XElement limbElement);
|
||||
|
||||
private void LoadHeadSprite()
|
||||
{
|
||||
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
|
||||
{
|
||||
@@ -816,6 +984,7 @@ namespace Barotrauma
|
||||
if (spriteElement == null) { continue; }
|
||||
|
||||
string spritePath = spriteElement.Attribute("texture").Value;
|
||||
if (string.IsNullOrEmpty(spritePath)) { continue; }
|
||||
|
||||
spritePath = spritePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
spritePath = spritePath.Replace("[RACE]", Head.race.ToString().ToLowerInvariant());
|
||||
@@ -823,6 +992,8 @@ namespace Barotrauma
|
||||
|
||||
string fileName = Path.GetFileNameWithoutExtension(spritePath);
|
||||
|
||||
if (string.IsNullOrEmpty(fileName)) { continue; }
|
||||
|
||||
//go through the files in the directory to find a matching sprite
|
||||
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
||||
{
|
||||
@@ -847,13 +1018,12 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
|
||||
LoadHeadSpriteProjectSpecific(limbElement);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads only the elements according to the indices, not the sprites.
|
||||
/// </summary>
|
||||
public void LoadHeadAttachments()
|
||||
{
|
||||
if (Wearables != null)
|
||||
@@ -1138,7 +1308,7 @@ namespace Barotrauma
|
||||
new XAttribute("name", Name),
|
||||
new XAttribute("originalname", OriginalName),
|
||||
new XAttribute("speciesname", SpeciesName),
|
||||
new XAttribute("gender", Head.gender == Gender.Male ? "male" : "female"),
|
||||
new XAttribute("gender", Head.gender.ToString()),
|
||||
new XAttribute("race", Head.race.ToString()),
|
||||
new XAttribute("salary", Salary),
|
||||
new XAttribute("experiencepoints", ExperiencePoints),
|
||||
@@ -1149,6 +1319,9 @@ namespace Barotrauma
|
||||
new XAttribute("beardindex", BeardIndex),
|
||||
new XAttribute("moustacheindex", MoustacheIndex),
|
||||
new XAttribute("faceattachmentindex", FaceAttachmentIndex),
|
||||
new XAttribute("skincolor", XMLExtensions.ColorToString(SkinColor)),
|
||||
new XAttribute("haircolor", XMLExtensions.ColorToString(HairColor)),
|
||||
new XAttribute("facialhaircolor", XMLExtensions.ColorToString(FacialHairColor)),
|
||||
new XAttribute("startitemsgiven", StartItemsGiven),
|
||||
new XAttribute("ragdoll", ragdollFileName),
|
||||
new XAttribute("personality", personalityTrait == null ? "" : personalityTrait.Name));
|
||||
@@ -1462,13 +1635,19 @@ namespace Barotrauma
|
||||
if (healthData != null) { character?.CharacterHealth.Load(healthData); }
|
||||
}
|
||||
|
||||
public void ReloadHeadAttachments()
|
||||
/// <summary>
|
||||
/// Reloads the attachment xml elements according to the indices. Doesn't reload the sprites.
|
||||
/// </summary>
|
||||
private void ReloadHeadAttachments()
|
||||
{
|
||||
ResetLoadedAttachments();
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
|
||||
public void ResetHeadAttachments()
|
||||
/// <summary>
|
||||
/// Loads only the elements according to the indices, not the sprites.
|
||||
/// </summary>
|
||||
private void ResetHeadAttachments()
|
||||
{
|
||||
ResetAttachmentIndices();
|
||||
ResetLoadedAttachments();
|
||||
@@ -1500,6 +1679,12 @@ namespace Barotrauma
|
||||
AttachmentSprites = null;
|
||||
}
|
||||
|
||||
private void RefreshHeadSprites()
|
||||
{
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
}
|
||||
|
||||
// This could maybe be a LookUp instead?
|
||||
private readonly Dictionary<StatTypes, List<SavedStatValue>> savedStatValues = new Dictionary<StatTypes, List<SavedStatValue>>();
|
||||
|
||||
@@ -1541,7 +1726,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue)
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue, bool setValue = false)
|
||||
{
|
||||
if (!savedStatValues.ContainsKey(statType))
|
||||
{
|
||||
@@ -1550,7 +1735,7 @@ namespace Barotrauma
|
||||
|
||||
if (savedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
{
|
||||
savedStat.StatValue = MathHelper.Min(savedStat.StatValue + value, maxValue);
|
||||
savedStat.StatValue = setValue ? value : MathHelper.Min(savedStat.StatValue + value, maxValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+14
@@ -628,6 +628,11 @@ namespace Barotrauma
|
||||
Description = TextManager.Get("AfflictionDescription." + translationId, true) ?? element.GetAttributeString("description", "");
|
||||
IsBuff = element.GetAttributeBool("isbuff", false);
|
||||
|
||||
if (element.Attribute("nameidentifier") != null)
|
||||
{
|
||||
Name = TextManager.Get(element.GetAttributeString("nameidentifier", string.Empty), returnNull: true) ?? Name;
|
||||
}
|
||||
|
||||
LimbSpecific = element.GetAttributeBool("limbspecific", false);
|
||||
if (!LimbSpecific)
|
||||
{
|
||||
@@ -669,6 +674,15 @@ namespace Barotrauma
|
||||
case "afflictionoverlay":
|
||||
AfflictionOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "statvalue":
|
||||
DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.");
|
||||
break;
|
||||
case "effect":
|
||||
case "periodiceffect":
|
||||
break;
|
||||
default:
|
||||
DebugConsole.AddWarning($"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -979,7 +979,7 @@ namespace Barotrauma
|
||||
float minSuitability = -10, maxSuitability = 10;
|
||||
foreach (Affliction affliction in getAfflictions(limb))
|
||||
{
|
||||
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (affliction.Strength <= affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (ignoreHiddenAfflictions && affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
@@ -1088,7 +1088,7 @@ namespace Barotrauma
|
||||
/// Automatically filters out buffs.
|
||||
/// </summary>
|
||||
public static IEnumerable<Affliction> SortAfflictionsBySeverity(IEnumerable<Affliction> afflictions, bool excludeBuffs = true) =>
|
||||
afflictions.Where(a => !excludeBuffs || !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength);
|
||||
afflictions.Where(a => !excludeBuffs || !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength / a.Prefab.MaxStrength);
|
||||
|
||||
public void Save(XElement healthElement)
|
||||
{
|
||||
|
||||
@@ -275,7 +275,8 @@ namespace Barotrauma
|
||||
|
||||
Skills.Sort((x,y) => y.LevelRange.X.CompareTo(x.LevelRange.X));
|
||||
|
||||
ClothingElement = element.GetChildElement("PortraitClothing");
|
||||
// Disabled on purpose, TODO: remove all references?
|
||||
//ClothingElement = element.GetChildElement("PortraitClothing");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -744,7 +744,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (attacker != null)
|
||||
{
|
||||
var abilityAffliction = new AbilityAffliction(newAffliction);
|
||||
var abilityAffliction = new AbilityAfflictionCharacter(newAffliction, character);
|
||||
attacker.CheckTalents(AbilityEffectType.OnAddDamageAffliction, abilityAffliction);
|
||||
}
|
||||
if (applyAffliction)
|
||||
|
||||
+17
-1
@@ -14,6 +14,7 @@ namespace Barotrauma
|
||||
NotDefined,
|
||||
Walk,
|
||||
Run,
|
||||
Crouch,
|
||||
SwimSlow,
|
||||
SwimFast
|
||||
}
|
||||
@@ -56,12 +57,15 @@ namespace Barotrauma
|
||||
{
|
||||
[Serialize(25.0f, true, description: "Turning speed (or rather a force applied on the main collider to make it turn). Note that you can set a limb-specific steering forces too (additional)."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float SteerTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to move the legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float LegTorque { get; set; }
|
||||
}
|
||||
|
||||
abstract class AnimationParams : EditableParams, IMemorizable<AnimationParams>
|
||||
{
|
||||
public string SpeciesName { get; private set; }
|
||||
public bool IsGroundedAnimation => AnimationType == AnimationType.Walk || AnimationType == AnimationType.Run;
|
||||
public bool IsGroundedAnimation => AnimationType == AnimationType.Walk || AnimationType == AnimationType.Run || AnimationType == AnimationType.Crouch;
|
||||
public bool IsSwimAnimation => AnimationType == AnimationType.SwimSlow || AnimationType == AnimationType.SwimFast;
|
||||
|
||||
protected static Dictionary<string, Dictionary<string, AnimationParams>> allAnimations = new Dictionary<string, Dictionary<string, AnimationParams>>();
|
||||
@@ -110,8 +114,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TorsoAngleInRadians { get; private set; } = float.NaN;
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the head to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float HeadTorque { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float TorsoTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float FootTorque { get; set; }
|
||||
|
||||
[Serialize(AnimationType.NotDefined, true), Editable]
|
||||
public virtual AnimationType AnimationType { get; protected set; }
|
||||
|
||||
@@ -402,6 +416,8 @@ namespace Barotrauma
|
||||
return typeof(HumanWalkParams);
|
||||
case AnimationType.Run:
|
||||
return typeof(HumanRunParams);
|
||||
case AnimationType.Crouch:
|
||||
return typeof(HumanCrouchParams);
|
||||
case AnimationType.SwimSlow:
|
||||
return typeof(HumanSwimSlowParams);
|
||||
case AnimationType.SwimFast:
|
||||
|
||||
-20
@@ -87,18 +87,9 @@ namespace Barotrauma
|
||||
[Serialize(8.0f, true, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootMoveForce { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the head to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float HeadTorque { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float TorsoTorque { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float TailTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float FootTorque { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "Optional torque that's constantly applied to legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float LegTorque { get; set; }
|
||||
|
||||
@@ -173,20 +164,12 @@ namespace Barotrauma
|
||||
[Editable, Serialize(true, true, description: "Should the character face towards the direction it's heading.")]
|
||||
public bool RotateTowardsMovement { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)]
|
||||
public float TorsoTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the head to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)]
|
||||
public float HeadTorque { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)]
|
||||
public float TailTorque { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Multiplier applied based on the angle difference between the tail and the main limb. Increasing the value prevents snake-like characters from getting tangled on themselves. Default = 1 (no boost)"), Editable(MinValueFloat = 1, MaxValueFloat = 100)]
|
||||
public float TailTorqueMultiplier { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float FootTorque { get; set; }
|
||||
|
||||
[Serialize(null, true), Editable]
|
||||
public string FootAngles
|
||||
@@ -224,10 +207,7 @@ namespace Barotrauma
|
||||
Dictionary<int, float> FootAnglesInRadians { get; set; }
|
||||
float TailAngle { get; set; }
|
||||
float TailAngleInRadians { get; }
|
||||
float HeadTorque { get; set; }
|
||||
float TorsoTorque { get; set; }
|
||||
float TailTorque { get; set; }
|
||||
float FootTorque { get; set; }
|
||||
bool Flip { get; set; }
|
||||
float FlipCooldown { get; set; }
|
||||
float FlipDelay { get; set; }
|
||||
|
||||
+45
-37
@@ -24,6 +24,17 @@ namespace Barotrauma
|
||||
public override void StoreSnapshot() => StoreSnapshot<HumanRunParams>();
|
||||
}
|
||||
|
||||
class HumanCrouchParams : HumanGroundedParams
|
||||
{
|
||||
public static HumanCrouchParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<HumanCrouchParams>(character, AnimationType.Crouch);
|
||||
public static HumanCrouchParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
return GetAnimParams<HumanCrouchParams>(character.SpeciesName, AnimationType.Crouch, fileName);
|
||||
}
|
||||
|
||||
public override void StoreSnapshot() => StoreSnapshot<HumanCrouchParams>();
|
||||
}
|
||||
|
||||
class HumanSwimFastParams: HumanSwimParams
|
||||
{
|
||||
public static HumanSwimFastParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<HumanSwimFastParams>(character, AnimationType.SwimFast);
|
||||
@@ -58,9 +69,6 @@ namespace Barotrauma
|
||||
[Serialize("0.5, 0.1", true), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveAmount { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
|
||||
[Serialize(5.0f, true), Editable]
|
||||
public float HandCycleSpeed { get; set; }
|
||||
|
||||
@@ -81,36 +89,23 @@ namespace Barotrauma
|
||||
}
|
||||
public float FootAngleInRadians { get; private set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootRotateStrength { get; set; }
|
||||
[Serialize(1f, true, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 20, DecimalCount = 2)]
|
||||
public float ArmMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ArmIKStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandIKStrength { get; set; }
|
||||
}
|
||||
|
||||
abstract class HumanGroundedParams : GroundedMovementParams, IHumanAnimation
|
||||
{
|
||||
[Serialize(0.3f, true, description: "How much force is used to force the character upright."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float GetUpForce { get; set; }
|
||||
|
||||
// -- TODO: use a separate clip for crawling -> replace these when implemented.
|
||||
|
||||
[Serialize(0.65f, true, description: "Height of the torso when crouching."), Editable(MinValueFloat = 0, MaxValueFloat = 5, DecimalCount = 2)]
|
||||
public float CrouchingTorsoPos { get; set; }
|
||||
|
||||
[Serialize(0.65f, true, description: "Height of the head when crouching."), Editable(MinValueFloat = 0, MaxValueFloat = 5, DecimalCount = 2)]
|
||||
public float CrouchingHeadPos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees
|
||||
/// </summary>
|
||||
[Serialize(-10f, true, description: "Angle of the torso when crouching."), Editable(MinValueFloat = -360, MaxValueFloat = 360)]
|
||||
public float CrouchingTorsoAngle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees
|
||||
/// </summary>
|
||||
[Serialize(-10f, true, description: "Angle of the head when crouching."), Editable(MinValueFloat = -360, MaxValueFloat = 360)]
|
||||
public float CrouchingHeadAngle { get; set; }
|
||||
|
||||
// --
|
||||
|
||||
[Serialize(0.25f, true, description: "How much the character's head leans forwards when moving."), Editable(DecimalCount = 2)]
|
||||
public float HeadLeanAmount { get; set; }
|
||||
@@ -121,6 +116,9 @@ namespace Barotrauma
|
||||
[Serialize(15.0f, true, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootMoveStrength { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "How much the horizontal difference of waist and the foot positions has an effect to lifting the foot."), Editable(DecimalCount = 2, ValueStep = 0.1f, MinValueFloat = 0f, MaxValueFloat = 1f)]
|
||||
public float FootLiftHorizontalFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
@@ -135,15 +133,9 @@ namespace Barotrauma
|
||||
}
|
||||
public float FootAngleInRadians { get; private set; }
|
||||
|
||||
[Serialize(20.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootRotateStrength { get; set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true, description: "Added to the calculated foot positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their feet one unit behind them."), Editable(DecimalCount = 2)]
|
||||
public Vector2 FootMoveOffset { get; set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true, description: "Added to the calculated foot positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their feet one unit behind them."), Editable(DecimalCount = 2)]
|
||||
public Vector2 CrouchingFootMoveOffset { get; set; }
|
||||
|
||||
[Serialize(10.0f, true, description: "How much torque is used to bend the characters legs when taking a step."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float LegBendTorque { get; set; }
|
||||
|
||||
@@ -153,17 +145,33 @@ namespace Barotrauma
|
||||
[Serialize("-0.15, 0.0", true, description: "Added to the calculated hand positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their hands one unit behind them."), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveOffset { get; set; }
|
||||
|
||||
[Serialize(0.7f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 2, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
|
||||
[Serialize(-1.0f, true, description: "The position of the hands is clamped below this (relative to the position of the character's torso)."), Editable(DecimalCount = 2)]
|
||||
public float HandClampY { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ArmMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ArmIKStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandIKStrength { get; set; }
|
||||
}
|
||||
|
||||
public interface IHumanAnimation
|
||||
{
|
||||
float FootAngle { get; set; }
|
||||
float FootAngleInRadians { get; }
|
||||
float FootRotateStrength { get; set; }
|
||||
|
||||
float ArmMoveStrength { get; set; }
|
||||
|
||||
float HandMoveStrength { get; set; }
|
||||
|
||||
float ArmIKStrength { get; set; }
|
||||
|
||||
float HandIKStrength { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace Barotrauma
|
||||
[Serialize("", true, description: "Default path for the limb sprite textures. Used only if the limb specific path for the limb is not defined"), Editable]
|
||||
public string Texture { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
||||
public Color Color { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "The orientation of the sprites as drawn on the sprite sheet. Can be overridden by setting a value for Limb's 'Sprite Orientation'. Used mainly for animations and widgets."), Editable(-360, 360)]
|
||||
public float SpritesheetOrientation { get; set; }
|
||||
|
||||
@@ -556,7 +559,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override string GenerateName() => $"Limb {ID}";
|
||||
public override string GenerateName() => Type != LimbType.None ? $"{Type} ({ID})" : $"Limb {ID}";
|
||||
|
||||
public SpriteParams GetSprite() => deformSpriteParams ?? normalSpriteParams;
|
||||
|
||||
@@ -574,7 +577,7 @@ namespace Barotrauma
|
||||
[Serialize("", true), Editable]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable]
|
||||
[Serialize(1f, true), Editable(DecimalCount = 2)]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Does the limb flip when the character flips?"), Editable()]
|
||||
@@ -889,6 +892,9 @@ namespace Barotrauma
|
||||
[Serialize("", true), Editable()]
|
||||
public string Texture { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable()]
|
||||
public bool IgnoreTint { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
||||
public Color Color { get; set; }
|
||||
|
||||
|
||||
+3
@@ -33,6 +33,7 @@ namespace Barotrauma.Abilities
|
||||
NotSelf = 3,
|
||||
Alive = 4,
|
||||
Monster = 5,
|
||||
InFriendlySubmarine = 6,
|
||||
};
|
||||
|
||||
protected List<TargetType> ParseTargetTypes(string[] targetTypeStrings)
|
||||
@@ -80,6 +81,8 @@ namespace Barotrauma.Abilities
|
||||
return !targetCharacter.IsDead;
|
||||
case TargetType.Monster:
|
||||
return !targetCharacter.IsHuman;
|
||||
case TargetType.InFriendlySubmarine:
|
||||
return targetCharacter.Submarine != null && targetCharacter.Submarine.TeamID == character.TeamID;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionAffliction : AbilityConditionData
|
||||
{
|
||||
private readonly string[] afflictions;
|
||||
public AbilityConditionAffliction(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
afflictions = conditionElement.GetAttributeStringArray("afflictions", new string[0], convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityAffliction)?.Affliction is Affliction affliction)
|
||||
{
|
||||
return afflictions.Any(a => a == affliction.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityObject, typeof(IAbilityAttackResult));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionLocation : AbilityConditionData
|
||||
{
|
||||
private readonly bool? hasOutpost;
|
||||
private readonly string[] locationIdentifiers;
|
||||
|
||||
public AbilityConditionLocation(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
if (conditionElement.Attribute("hasoutpost") != null)
|
||||
{
|
||||
hasOutpost = conditionElement.GetAttributeBool("hasoutpost", false);
|
||||
}
|
||||
locationIdentifiers = conditionElement.GetAttributeStringArray("locationtype", new string[0]);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
||||
{
|
||||
if (abilityObject is IAbilityLocation abilityLocation)
|
||||
{
|
||||
if (locationIdentifiers.Any())
|
||||
{
|
||||
if (!locationIdentifiers.Contains(abilityLocation.Location.Type.Identifier)) { return false; }
|
||||
}
|
||||
if (hasOutpost.HasValue)
|
||||
{
|
||||
if (hasOutpost.Value != abilityLocation.Location.HasOutpost()) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityObject, typeof(IAbilityItemPrefab));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -20,6 +20,11 @@
|
||||
public Mission Mission { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityLocation
|
||||
{
|
||||
public Location Location { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityCharacter
|
||||
{
|
||||
public Character Character { get; set; }
|
||||
|
||||
+33
-1
@@ -43,6 +43,17 @@ namespace Barotrauma.Abilities
|
||||
public Affliction Affliction { get; set; }
|
||||
}
|
||||
|
||||
class AbilityAfflictionCharacter : AbilityObject, IAbilityAffliction, IAbilityCharacter
|
||||
{
|
||||
public AbilityAfflictionCharacter(Affliction affliction, Character character)
|
||||
{
|
||||
Affliction = affliction;
|
||||
Character = character;
|
||||
}
|
||||
public Character Character { get; set; }
|
||||
public Affliction Affliction { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueItem : AbilityObject, IAbilityValue, IAbilityItemPrefab
|
||||
{
|
||||
public AbilityValueItem(float value, ItemPrefab itemPrefab)
|
||||
@@ -54,6 +65,17 @@ namespace Barotrauma.Abilities
|
||||
public ItemPrefab ItemPrefab { get; set; }
|
||||
}
|
||||
|
||||
class AbilityItemPrefabItem : AbilityObject, IAbilityItem, IAbilityItemPrefab
|
||||
{
|
||||
public AbilityItemPrefabItem(Item item, ItemPrefab itemPrefab)
|
||||
{
|
||||
Item = item;
|
||||
ItemPrefab = itemPrefab;
|
||||
}
|
||||
public Item Item { get; set; }
|
||||
public ItemPrefab ItemPrefab { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueString : AbilityObject, IAbilityValue, IAbilityString
|
||||
{
|
||||
public AbilityValueString(float value, string abilityString)
|
||||
@@ -65,7 +87,7 @@ namespace Barotrauma.Abilities
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueStringCharacter : AbilityObject, IAbilityValue, IAbilityString
|
||||
class AbilityValueStringCharacter : AbilityObject, IAbilityValue, IAbilityString, IAbilityCharacter
|
||||
{
|
||||
public AbilityValueStringCharacter(float value, string abilityString, Character character)
|
||||
{
|
||||
@@ -111,6 +133,16 @@ namespace Barotrauma.Abilities
|
||||
public Mission Mission { get; set; }
|
||||
}
|
||||
|
||||
class AbilityLocation : AbilityObject, IAbilityLocation
|
||||
{
|
||||
public AbilityLocation(Location location)
|
||||
{
|
||||
Location = location;
|
||||
}
|
||||
|
||||
public Location Location { get; set; }
|
||||
}
|
||||
|
||||
// this is an exception class that should only be passed in this form, so classes that use it should cast into it directly
|
||||
class AbilityAttackData : AbilityObject, IAbilityCharacter
|
||||
{
|
||||
|
||||
+7
-2
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -9,10 +8,12 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityApplyStatusEffectsToAllies : CharacterAbilityApplyStatusEffects
|
||||
{
|
||||
private readonly bool allowSelf;
|
||||
private readonly float maxDistance = float.MaxValue;
|
||||
|
||||
public CharacterAbilityApplyStatusEffectsToAllies(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
allowSelf = abilityElement.GetAttributeBool("allowself", true);
|
||||
maxDistance = abilityElement.GetAttributeFloat("maxdistance", float.MaxValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +23,10 @@ namespace Barotrauma.Abilities
|
||||
|
||||
foreach (Character character in chosenCharacters)
|
||||
{
|
||||
if (maxDistance < float.MaxValue)
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, Character.WorldPosition) > maxDistance * maxDistance) { continue; }
|
||||
}
|
||||
ApplyEffectSpecific(character);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
@@ -8,7 +7,7 @@ namespace Barotrauma.Abilities
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
private readonly int amount;
|
||||
private StatTypes scalingStatType;
|
||||
private readonly StatTypes scalingStatType;
|
||||
|
||||
public CharacterAbilityGiveMoney(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
+4
-2
@@ -13,6 +13,7 @@ namespace Barotrauma.Abilities
|
||||
private readonly bool removeOnDeath;
|
||||
private readonly bool removeAfterRound;
|
||||
private readonly bool giveOnAddingFirstTime;
|
||||
private readonly bool setValue;
|
||||
|
||||
//private readonly float maximumValue;
|
||||
|
||||
@@ -28,6 +29,7 @@ namespace Barotrauma.Abilities
|
||||
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
|
||||
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
|
||||
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
|
||||
setValue = abilityElement.GetAttributeBool("setvalue", false);
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
@@ -52,11 +54,11 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (targetAllies)
|
||||
{
|
||||
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue));
|
||||
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue, setValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue);
|
||||
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue, setValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public CharacterAbilityGiveResistance(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
resistanceId = abilityElement.GetAttributeString("resistanceid", "");
|
||||
resistanceId = abilityElement.GetAttributeString("resistanceid", abilityElement.GetAttributeString("resistance", string.Empty));
|
||||
multiplier = abilityElement.GetAttributeFloat("multiplier", 1f);
|
||||
|
||||
if (string.IsNullOrEmpty(resistanceId))
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyStatToLevel : CharacterAbility
|
||||
{
|
||||
private readonly StatTypes statType;
|
||||
private readonly float statPerLevel;
|
||||
private readonly int maxLevel;
|
||||
private float lastValue = 0f;
|
||||
|
||||
public CharacterAbilityModifyStatToLevel(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
statPerLevel = abilityElement.GetAttributeFloat("statperlevel", 0f);
|
||||
maxLevel = abilityElement.GetAttributeInt("maxlevel", int.MaxValue);
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
Character.ChangeStat(statType, -lastValue);
|
||||
if (conditionsMatched)
|
||||
{
|
||||
int level = MathHelper.Min(Character?.Info.GetCurrentLevel() ?? 0, maxLevel);
|
||||
lastValue = statPerLevel * level;
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastValue = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -10,19 +10,24 @@ namespace Barotrauma.Abilities
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
private readonly List<Item> openedContainers = new List<Item>();
|
||||
private readonly float randomChance;
|
||||
private readonly bool oncePerContainer;
|
||||
|
||||
public CharacterAbilitySpawnItemsToContainer(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
randomChance = abilityElement.GetAttributeFloat("randomchance", 1f);
|
||||
oncePerContainer = abilityElement.GetAttributeBool("oncepercontainer", false);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityItem)?.Item is Item item)
|
||||
{
|
||||
if (openedContainers.Contains(item)) { return; }
|
||||
openedContainers.Add(item);
|
||||
if (oncePerContainer)
|
||||
{
|
||||
if (openedContainers.Contains(item)) { return; }
|
||||
openedContainers.Add(item);
|
||||
}
|
||||
if (randomChance < Rand.Range(0f, 1f, Rand.RandSync.Unsynced)) { return; }
|
||||
|
||||
foreach (var statusEffect in statusEffects)
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityUnlockTree : CharacterAbility
|
||||
{
|
||||
public CharacterAbilityUnlockTree(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
{
|
||||
if (!addingFirstTime) { return; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(Character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
var subTree = talentTree.TalentSubTrees.Find(t => t.TalentOptionStages.Any(ts => ts.Talents.Contains(CharacterTalent.Prefab)));
|
||||
if (subTree != null)
|
||||
{
|
||||
foreach (var talentOption in subTree.TalentOptionStages)
|
||||
{
|
||||
foreach (var talent in talentOption.Talents)
|
||||
{
|
||||
Character.GiveTalent(talent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -4,14 +4,14 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityEnigmaMachine : CharacterAbility
|
||||
class CharacterAbilityAtmosMachine : CharacterAbility
|
||||
{
|
||||
private readonly float addedValue;
|
||||
private readonly float multiplyValue;
|
||||
private readonly string[] tags;
|
||||
private readonly int maxMultiplyCount;
|
||||
|
||||
public CharacterAbilityEnigmaMachine(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
public CharacterAbilityAtmosMachine(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityStonewall : CharacterAbility
|
||||
{
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
private readonly List<StatusEffect> statusEffectsReset;
|
||||
private readonly int maxEnemyCount;
|
||||
private readonly float squaredDistance;
|
||||
|
||||
public CharacterAbilityStonewall(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
statusEffectsReset = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffectsreset"));
|
||||
maxEnemyCount = abilityElement.GetAttributeInt("maxenemycount", 0);
|
||||
squaredDistance = MathF.Pow(abilityElement.GetAttributeFloat("distance", 0), 2);
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
int numberOfEnemiesInRange = Character.CharacterList.Count(c => !HumanAIController.IsFriendly(Character, c) && !c.IsDead && Vector2.DistanceSquared(Character.WorldPosition, c.WorldPosition) < squaredDistance);
|
||||
|
||||
foreach (var statusEffect in statusEffectsReset)
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, 1f, Character, Character);
|
||||
}
|
||||
|
||||
if (conditionsMatched && numberOfEnemiesInRange > 0)
|
||||
{
|
||||
foreach (var statusEffect in statusEffects)
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, Math.Min(numberOfEnemiesInRange, maxEnemyCount), Character, Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -23,6 +23,7 @@ namespace Barotrauma.Abilities
|
||||
characterAbility.ApplyAbilityEffect(abilityObject);
|
||||
}
|
||||
}
|
||||
timesTriggered++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -39,6 +39,10 @@ namespace Barotrauma.Abilities
|
||||
characterAbility.UpdateCharacterAbility(conditionsMatched, TimeSinceLastUpdate);
|
||||
}
|
||||
}
|
||||
if (conditionsMatched)
|
||||
{
|
||||
timesTriggered++;
|
||||
}
|
||||
TimeSinceLastUpdate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
|
||||
if (string.IsNullOrEmpty(jobIdentifier))
|
||||
{
|
||||
DebugConsole.ThrowError("No job defined for talent tree!");
|
||||
DebugConsole.ThrowError($"No job defined for talent tree in \"{filePath}\"!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,10 @@
|
||||
OnAllyGainMissionExperience,
|
||||
OnGainMissionExperience,
|
||||
OnGainMissionMoney,
|
||||
OnLocationDiscovered,
|
||||
OnItemDeconstructed,
|
||||
OnItemDeconstructedMaterial,
|
||||
OnItemDeconstructedRetainProbability,
|
||||
OnItemDeconstructedInventory,
|
||||
OnStopTinkering,
|
||||
OnItemPicked,
|
||||
AfterSubmarineAttacked,
|
||||
@@ -96,7 +97,6 @@
|
||||
// Utility
|
||||
RepairSpeed,
|
||||
DeconstructorSpeedMultiplier,
|
||||
TinkeringDuration,
|
||||
RepairToolStructureRepairMultiplier,
|
||||
RepairToolStructureDamageMultiplier,
|
||||
RepairToolDeattachTimeMultiplier,
|
||||
@@ -105,6 +105,10 @@
|
||||
GeneticMaterialRefineBonus,
|
||||
GeneticMaterialTaintedProbabilityReductionOnCombine,
|
||||
SkillGainSpeed,
|
||||
// Tinker
|
||||
TinkeringDuration,
|
||||
TinkeringStrength,
|
||||
TinkeringDamage,
|
||||
// Misc
|
||||
ReputationGainMultiplier,
|
||||
MissionMoneyGainMultiplier,
|
||||
@@ -114,6 +118,7 @@
|
||||
Coauthor,
|
||||
WarriorPoetMissionRuns,
|
||||
WarriorPoetEnemiesKilled,
|
||||
QuickfixRepairCount,
|
||||
}
|
||||
|
||||
public enum AbilityFlags
|
||||
|
||||
@@ -868,6 +868,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (level == null) { return 0.0f; }
|
||||
var refEntity = GetRefEntity();
|
||||
if (refEntity == null) { return 0.0f; }
|
||||
Vector2 target = ConvertUnits.ToSimUnits(level.EndPosition);
|
||||
var steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(refEntity.WorldPosition), target);
|
||||
if (steeringPath.Unreachable || float.IsPositiveInfinity(totalPathLength))
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace Barotrauma
|
||||
{
|
||||
partial class MineralMission : Mission
|
||||
{
|
||||
private Dictionary<string, Pair<int, float>> ResourceClusters { get; } = new Dictionary<string, Pair<int, float>>();
|
||||
private Dictionary<string, List<Item>> SpawnedResources { get; } = new Dictionary<string, List<Item>>();
|
||||
private Dictionary<string, Item[]> RelevantLevelResources { get; } = new Dictionary<string, Item[]>();
|
||||
private List<Tuple<string, Vector2>> MissionClusterPositions { get; } = new List<Tuple<string, Vector2>>();
|
||||
private readonly Dictionary<string, (int amount, float rotation)> resourceClusters = new Dictionary<string, (int amount, float rotation)>();
|
||||
private readonly Dictionary<string, List<Item>> spawnedResources = new Dictionary<string, List<Item>>();
|
||||
private readonly Dictionary<string, Item[]> relevantLevelResources = new Dictionary<string, Item[]>();
|
||||
private readonly List<Tuple<string, Vector2>> missionClusterPositions = new List<Tuple<string, Vector2>>();
|
||||
|
||||
private readonly HashSet<Level.Cave> caves = new HashSet<Level.Cave>();
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return MissionClusterPositions
|
||||
.Where(p => SpawnedResources.ContainsKey(p.Item1) && AnyAreUncollected(SpawnedResources[p.Item1]))
|
||||
return missionClusterPositions
|
||||
.Where(p => spawnedResources.ContainsKey(p.Item1) && AnyAreUncollected(spawnedResources[p.Item1]))
|
||||
.Select(p => p.Item2);
|
||||
}
|
||||
}
|
||||
@@ -33,53 +33,53 @@ namespace Barotrauma
|
||||
{
|
||||
var identifier = c.GetAttributeString("identifier", null);
|
||||
if (string.IsNullOrWhiteSpace(identifier)) { continue; }
|
||||
if (ResourceClusters.ContainsKey(identifier))
|
||||
if (resourceClusters.ContainsKey(identifier))
|
||||
{
|
||||
ResourceClusters[identifier].First++;
|
||||
resourceClusters[identifier] = (resourceClusters[identifier].amount + 1, resourceClusters[identifier].rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourceClusters.Add(identifier, new Pair<int, float>(1, 0.0f));
|
||||
resourceClusters.Add(identifier, (1, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (SpawnedResources.Any())
|
||||
if (spawnedResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({SpawnedResources.Count})");
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({spawnedResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Spawned resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
SpawnedResources.Clear();
|
||||
spawnedResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (RelevantLevelResources.Any())
|
||||
if (relevantLevelResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({RelevantLevelResources.Count})");
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({relevantLevelResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Relevant level resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
RelevantLevelResources.Clear();
|
||||
relevantLevelResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (MissionClusterPositions.Any())
|
||||
if (missionClusterPositions.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({MissionClusterPositions.Count})");
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({missionClusterPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Mission cluster positions list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
MissionClusterPositions.Clear();
|
||||
missionClusterPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
caves.Clear();
|
||||
|
||||
if (IsClient) { return; }
|
||||
foreach (var kvp in ResourceClusters)
|
||||
foreach (var kvp in resourceClusters)
|
||||
{
|
||||
var prefab = ItemPrefab.Find(null, kvp.Key);
|
||||
if (prefab == null)
|
||||
@@ -88,15 +88,14 @@ namespace Barotrauma
|
||||
"couldn't find an item prefab with the identifier " + kvp.Key);
|
||||
continue;
|
||||
}
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.First, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.First)
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.amount, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.amount)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.First + " of " + prefab.Name);
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.amount + " of " + prefab.Name);
|
||||
}
|
||||
if (spawnedResources.None()) { continue; }
|
||||
SpawnedResources.Add(kvp.Key, spawnedResources);
|
||||
kvp.Value.Second = rotation;
|
||||
this.spawnedResources.Add(kvp.Key, spawnedResources);
|
||||
|
||||
foreach (Level.Cave cave in Level.Loaded.Caves)
|
||||
{
|
||||
@@ -142,7 +141,7 @@ namespace Barotrauma
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var kvp in SpawnedResources)
|
||||
foreach (var kvp in spawnedResources)
|
||||
{
|
||||
foreach (var i in kvp.Value)
|
||||
{
|
||||
@@ -152,33 +151,33 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnedResources.Clear();
|
||||
RelevantLevelResources.Clear();
|
||||
MissionClusterPositions.Clear();
|
||||
spawnedResources.Clear();
|
||||
relevantLevelResources.Clear();
|
||||
missionClusterPositions.Clear();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
|
||||
private void FindRelevantLevelResources()
|
||||
{
|
||||
RelevantLevelResources.Clear();
|
||||
foreach (var identifier in ResourceClusters.Keys)
|
||||
relevantLevelResources.Clear();
|
||||
foreach (var identifier in resourceClusters.Keys)
|
||||
{
|
||||
var items = Item.ItemList.Where(i => i.Prefab.Identifier == identifier &&
|
||||
i.Submarine == null && i.ParentInventory == null &&
|
||||
(!(i.GetComponent<Holdable>() is Holdable h) || (h.Attachable && h.Attached)))
|
||||
.ToArray();
|
||||
RelevantLevelResources.Add(identifier, items);
|
||||
relevantLevelResources.Add(identifier, items);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnoughHaveBeenCollected()
|
||||
{
|
||||
foreach (var kvp in ResourceClusters)
|
||||
foreach (var kvp in resourceClusters)
|
||||
{
|
||||
if (RelevantLevelResources.TryGetValue(kvp.Key, out var availableResources))
|
||||
if (relevantLevelResources.TryGetValue(kvp.Key, out var availableResources))
|
||||
{
|
||||
var collected = availableResources.Count(r => HasBeenCollected(r));
|
||||
var needed = kvp.Value.First;
|
||||
var needed = kvp.Value.amount;
|
||||
if (collected < needed) { return false; }
|
||||
}
|
||||
else
|
||||
@@ -210,8 +209,8 @@ namespace Barotrauma
|
||||
|
||||
private void CalculateMissionClusterPositions()
|
||||
{
|
||||
MissionClusterPositions.Clear();
|
||||
foreach (var kvp in SpawnedResources)
|
||||
missionClusterPositions.Clear();
|
||||
foreach (var kvp in spawnedResources)
|
||||
{
|
||||
if (kvp.Value.None()) { continue; }
|
||||
var pos = Vector2.Zero;
|
||||
@@ -222,7 +221,7 @@ namespace Barotrauma
|
||||
itemCount++;
|
||||
}
|
||||
pos /= itemCount;
|
||||
MissionClusterPositions.Add(new Tuple<string, Vector2>(kvp.Key, pos));
|
||||
missionClusterPositions.Add(new Tuple<string, Vector2>(kvp.Key, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,11 @@ namespace Barotrauma
|
||||
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(patrolPos), ConvertUnits.ToSimUnits(preferredSpawnPos));
|
||||
if (!path.Unreachable)
|
||||
{
|
||||
preferredSpawnPos = path.Nodes[Rand.Range(0, path.Nodes.Count - 1)].WorldPosition; // spawn the sub in a random point in the path if possible
|
||||
var validNodes = path.Nodes.FindAll(n => !Level.Loaded.ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(n.WorldPosition))));
|
||||
if (validNodes.Any())
|
||||
{
|
||||
preferredSpawnPos = validNodes.GetRandom().WorldPosition; // spawn the sub in a random point in the path if possible
|
||||
}
|
||||
}
|
||||
|
||||
int graceDistance = 500; // the sub still spawns awkwardly close to walls, so this helps. could also be given as a parameter instead
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Extensions
|
||||
{
|
||||
public static class ColorExtensions
|
||||
{
|
||||
public static Color Multiply(this Color color, float value, bool onlyAlpha = false)
|
||||
{
|
||||
return onlyAlpha ?
|
||||
new Color(color.R, color.G, color.B, (byte)(color.A * value)) :
|
||||
new Color((byte)(color.R * value), (byte)(color.G * value), (byte)(color.B * value), (byte)(color.A * value));
|
||||
}
|
||||
|
||||
public static Color Multiply(this Color thisColor, Color color)
|
||||
{
|
||||
return new Color((byte)(thisColor.R * color.R / 255f), (byte)(thisColor.G * color.G / 255f), (byte)(thisColor.B * color.B / 255f), (byte)(thisColor.A * color.A / 255f));
|
||||
}
|
||||
|
||||
public static Color Opaque(this Color color)
|
||||
{
|
||||
return new Color(color.R, color.G, color.B, (byte)255);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,16 +659,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (Location location in Map.Locations)
|
||||
{
|
||||
if (location.Type != location.OriginalType)
|
||||
{
|
||||
location.ChangeType(location.OriginalType);
|
||||
location.PendingLocationTypeChange = null;
|
||||
}
|
||||
location.CreateStore(force: true);
|
||||
location.ClearMissions();
|
||||
location.Discovered = false;
|
||||
location.LevelData?.EventHistory?.Clear();
|
||||
location.UnlockInitialMissions();
|
||||
location.Reset();
|
||||
}
|
||||
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
|
||||
Map.SelectLocation(-1);
|
||||
|
||||
@@ -452,9 +452,11 @@ namespace Barotrauma
|
||||
StatusEffect.StopAll();
|
||||
|
||||
#if CLIENT
|
||||
#if !DEBUG
|
||||
GameMain.LightManager.LosEnabled = GameMain.Client == null || GameMain.Client.CharacterInfo != null;
|
||||
#endif
|
||||
if (GameMain.LightManager.LosEnabled) { GameMain.LightManager.LosAlpha = 1f; }
|
||||
if (GameMain.Client == null) GameMain.LightManager.LosMode = GameMain.Config.LosMode;
|
||||
if (GameMain.Client == null) { GameMain.LightManager.LosMode = GameMain.Config.LosMode; }
|
||||
#endif
|
||||
LevelData = level?.LevelData;
|
||||
Level = level;
|
||||
@@ -661,6 +663,7 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null);
|
||||
#else
|
||||
if (GameMain.GameSession == null) { return Enumerable.Empty<Character>(); }
|
||||
return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -143,14 +143,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public int CharacterHeadIndex { get; set; }
|
||||
public int CharacterHairIndex { get; set; }
|
||||
public int CharacterBeardIndex { get; set; }
|
||||
public int CharacterMoustacheIndex { get; set; }
|
||||
public int CharacterFaceAttachmentIndex { get; set; }
|
||||
|
||||
public Gender CharacterGender { get; set; }
|
||||
public Race CharacterRace { get; set; }
|
||||
internal CharacterInfo.HeadInfo PlayerCharacterCustomization { get; set; }
|
||||
|
||||
private float aimAssistAmount;
|
||||
public float AimAssistAmount
|
||||
@@ -855,171 +848,6 @@ namespace Barotrauma
|
||||
UnsavedSettings = false;
|
||||
}
|
||||
|
||||
private void SaveNewDefaultConfig()
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
|
||||
if (doc.Root == null)
|
||||
{
|
||||
doc.Add(new XElement("config"));
|
||||
}
|
||||
|
||||
doc.Root.Add(
|
||||
new XAttribute("language", TextManager.Language),
|
||||
new XAttribute("masterserverurl", MasterServerUrl),
|
||||
new XAttribute("remotecontenturl", RemoteContentUrl),
|
||||
new XAttribute("autocheckupdates", AutoCheckUpdates),
|
||||
new XAttribute("musicvolume", musicVolume),
|
||||
new XAttribute("soundvolume", soundVolume),
|
||||
new XAttribute("microphonevolume", microphoneVolume),
|
||||
new XAttribute("voicechatvolume", voiceChatVolume),
|
||||
new XAttribute("voicechatcutoffprevention", VoiceChatCutoffPrevention),
|
||||
new XAttribute("verboselogging", VerboseLogging),
|
||||
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
|
||||
new XAttribute("submarineautosave", EnableSubmarineAutoSave),
|
||||
new XAttribute("maxautosaves", MaximumAutoSaves),
|
||||
new XAttribute("autosaveintervalseconds", AutoSaveIntervalSeconds),
|
||||
new XAttribute("subeditorbackground", XMLExtensions.ColorToString(SubEditorBackgroundColor)),
|
||||
new XAttribute("subeditorundobuffer", SubEditorMaxUndoBuffer),
|
||||
new XAttribute("enablesplashscreen", EnableSplashScreen),
|
||||
new XAttribute("usesteammatchmaking", UseSteamMatchmaking),
|
||||
new XAttribute("quickstartsub", QuickStartSubmarineName),
|
||||
new XAttribute("requiresteamauthentication", RequireSteamAuthentication),
|
||||
new XAttribute("aimassistamount", aimAssistAmount),
|
||||
new XAttribute("tutorialskipwarning", ShowTutorialSkipWarning));
|
||||
|
||||
if (!ShowUserStatisticsPrompt)
|
||||
{
|
||||
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
|
||||
}
|
||||
|
||||
XElement gMode = doc.Root.Element("graphicsmode");
|
||||
if (gMode == null)
|
||||
{
|
||||
gMode = new XElement("graphicsmode");
|
||||
doc.Root.Add(gMode);
|
||||
}
|
||||
if (GraphicsWidth == 0 || GraphicsHeight == 0)
|
||||
{
|
||||
gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
gMode.ReplaceAttributes(
|
||||
new XAttribute("width", GraphicsWidth),
|
||||
new XAttribute("height", GraphicsHeight),
|
||||
new XAttribute("vsync", VSyncEnabled),
|
||||
new XAttribute("framelimit", Timing.FrameLimit),
|
||||
new XAttribute("displaymode", windowMode));
|
||||
}
|
||||
|
||||
XElement gSettings = doc.Root.Element("graphicssettings");
|
||||
if (gSettings == null)
|
||||
{
|
||||
gSettings = new XElement("graphicssettings");
|
||||
doc.Root.Add(gSettings);
|
||||
}
|
||||
|
||||
gSettings.ReplaceAttributes(
|
||||
new XAttribute("particlelimit", ParticleLimit),
|
||||
new XAttribute("lightmapscale", LightMapScale),
|
||||
new XAttribute("chromaticaberration", ChromaticAberrationEnabled),
|
||||
new XAttribute("losmode", LosMode),
|
||||
new XAttribute("hudscale", HUDScale),
|
||||
new XAttribute("inventoryscale", InventoryScale));
|
||||
|
||||
foreach (ContentPackage contentPackage in ContentPackage.CorePackages)
|
||||
{
|
||||
if (contentPackage.Path.Contains(VanillaContentPackagePath))
|
||||
{
|
||||
doc.Root.Add(new XElement("contentpackages", new XElement("core", new XAttribute("name", contentPackage.Name))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
var keyMappingElement = new XElement("keymapping");
|
||||
doc.Root.Add(keyMappingElement);
|
||||
for (int i = 0; i < keyMapping.Length; i++)
|
||||
{
|
||||
KeyOrMouse bind = keyMapping[i];
|
||||
if (bind.MouseButton == MouseButton.None)
|
||||
{
|
||||
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), bind.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), bind.MouseButton));
|
||||
}
|
||||
}
|
||||
|
||||
var inventoryKeyMappingElement = new XElement("inventorykeymapping");
|
||||
doc.Root.Add(inventoryKeyMappingElement);
|
||||
for (int i = 0; i < inventoryKeyMapping.Length; i++)
|
||||
{
|
||||
KeyOrMouse bind = inventoryKeyMapping[i];
|
||||
if (bind.MouseButton == MouseButton.None)
|
||||
{
|
||||
inventoryKeyMappingElement.Add(new XAttribute($"slot{i}", bind.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
inventoryKeyMappingElement.Add(new XAttribute($"slot{i}", bind.MouseButton));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
var gameplay = new XElement("gameplay");
|
||||
var jobPreferences = new XElement("jobpreferences");
|
||||
foreach (Pair<string, int> job in JobPreferences)
|
||||
{
|
||||
XElement jobElement = new XElement("job");
|
||||
jobElement.Add(new XAttribute("identifier", job.First));
|
||||
jobElement.Add(new XAttribute("variant", job.Second));
|
||||
jobPreferences.Add(jobElement);
|
||||
}
|
||||
gameplay.Add(jobPreferences);
|
||||
|
||||
var teamPreference = new XElement("teampreference");
|
||||
teamPreference.Add(new XAttribute("team", TeamPreference.ToString()));
|
||||
gameplay.Add(teamPreference);
|
||||
|
||||
doc.Root.Add(gameplay);
|
||||
|
||||
var playerElement = new XElement("player",
|
||||
new XAttribute("name", playerName ?? ""),
|
||||
new XAttribute("headindex", CharacterHeadIndex),
|
||||
new XAttribute("gender", CharacterGender),
|
||||
new XAttribute("race", CharacterRace),
|
||||
new XAttribute("hairindex", CharacterHairIndex),
|
||||
new XAttribute("beardindex", CharacterBeardIndex),
|
||||
new XAttribute("moustacheindex", CharacterMoustacheIndex),
|
||||
new XAttribute("faceattachmentindex", CharacterFaceAttachmentIndex));
|
||||
doc.Root.Add(playerElement);
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
OmitXmlDeclaration = true,
|
||||
NewLineOnAttributes = true
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using (var writer = XmlWriter.Create(SavePath, settings))
|
||||
{
|
||||
doc.WriteTo(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving game settings failed.", e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
#region Load PlayerConfig
|
||||
public void LoadPlayerConfig()
|
||||
{
|
||||
@@ -1307,15 +1135,20 @@ namespace Barotrauma
|
||||
gameplay.Add(jobPreferences);
|
||||
doc.Root.Add(gameplay);
|
||||
|
||||
var playerElement = new XElement("player",
|
||||
new XAttribute("name", playerName ?? ""),
|
||||
new XAttribute("headindex", CharacterHeadIndex),
|
||||
new XAttribute("gender", CharacterGender),
|
||||
new XAttribute("race", CharacterRace),
|
||||
new XAttribute("hairindex", CharacterHairIndex),
|
||||
new XAttribute("beardindex", CharacterBeardIndex),
|
||||
new XAttribute("moustacheindex", CharacterMoustacheIndex),
|
||||
new XAttribute("faceattachmentindex", CharacterFaceAttachmentIndex));
|
||||
var playerElement = new XElement("player", new XAttribute("name", playerName ?? ""));
|
||||
if (PlayerCharacterCustomization != null)
|
||||
{
|
||||
playerElement.SetAttributeValue("headindex", PlayerCharacterCustomization.HeadSpriteId);
|
||||
playerElement.SetAttributeValue("gender", PlayerCharacterCustomization.gender);
|
||||
playerElement.SetAttributeValue("race", PlayerCharacterCustomization.race);
|
||||
playerElement.SetAttributeValue("hairindex", PlayerCharacterCustomization.HairIndex);
|
||||
playerElement.SetAttributeValue("beardindex", PlayerCharacterCustomization.BeardIndex);
|
||||
playerElement.SetAttributeValue("moustacheindex", PlayerCharacterCustomization.MoustacheIndex);
|
||||
playerElement.SetAttributeValue("faceattachmentindex", PlayerCharacterCustomization.FaceAttachmentIndex);
|
||||
playerElement.SetAttributeValue("skincolor", XMLExtensions.ColorToString(PlayerCharacterCustomization.SkinColor));
|
||||
playerElement.SetAttributeValue("haircolor", XMLExtensions.ColorToString(PlayerCharacterCustomization.HairColor));
|
||||
playerElement.SetAttributeValue("facialhaircolor", XMLExtensions.ColorToString(PlayerCharacterCustomization.FacialHairColor));
|
||||
}
|
||||
doc.Root.Add(playerElement);
|
||||
|
||||
#if CLIENT
|
||||
@@ -1434,23 +1267,22 @@ namespace Barotrauma
|
||||
if (playerElement != null)
|
||||
{
|
||||
playerName = playerElement.GetAttributeString("name", playerName);
|
||||
CharacterHeadIndex = playerElement.GetAttributeInt("headindex", CharacterHeadIndex);
|
||||
if (Enum.TryParse(playerElement.GetAttributeString("gender", "none"), true, out Gender g))
|
||||
int head = playerElement.GetAttributeInt("headindex", -1);
|
||||
Enum.TryParse(playerElement.GetAttributeString("gender", "none"), true, out Gender gender);
|
||||
Enum.TryParse(playerElement.GetAttributeString("race", "white"), true, out Race race);
|
||||
int hair = playerElement.GetAttributeInt("hairindex", -1);
|
||||
int beard = playerElement.GetAttributeInt("beardindex", -1);
|
||||
int moustache = playerElement.GetAttributeInt("moustacheindex", -1);
|
||||
int faceAttachment = playerElement.GetAttributeInt("faceattachmentindex", -1);
|
||||
Color skinColor = playerElement.GetAttributeColor("skincolor", Color.Black);
|
||||
Color hairColor = playerElement.GetAttributeColor("haircolor", Color.Black);
|
||||
Color facialHairColor = playerElement.GetAttributeColor("facialhaircolor", Color.Black);
|
||||
PlayerCharacterCustomization = new CharacterInfo.HeadInfo(head, gender, race, hair, beard, moustache, faceAttachment)
|
||||
{
|
||||
CharacterGender = g;
|
||||
}
|
||||
if (Enum.TryParse(playerElement.GetAttributeString("race", "white"), true, out Race r))
|
||||
{
|
||||
CharacterRace = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterRace = Race.White;
|
||||
}
|
||||
CharacterHairIndex = playerElement.GetAttributeInt("hairindex", CharacterHairIndex);
|
||||
CharacterBeardIndex = playerElement.GetAttributeInt("beardindex", CharacterBeardIndex);
|
||||
CharacterMoustacheIndex = playerElement.GetAttributeInt("moustacheindex", CharacterMoustacheIndex);
|
||||
CharacterFaceAttachmentIndex = playerElement.GetAttributeInt("faceattachmentindex", CharacterFaceAttachmentIndex);
|
||||
SkinColor = skinColor,
|
||||
HairColor = hairColor,
|
||||
FacialHairColor = facialHairColor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1656,13 +1488,7 @@ namespace Barotrauma
|
||||
UseSteamMatchmaking = true;
|
||||
RequireSteamAuthentication = true;
|
||||
QuickStartSubmarineName = string.Empty;
|
||||
CharacterHeadIndex = 1;
|
||||
CharacterHairIndex = -1;
|
||||
CharacterBeardIndex = -1;
|
||||
CharacterMoustacheIndex = -1;
|
||||
CharacterFaceAttachmentIndex = -1;
|
||||
CharacterGender = Gender.None;
|
||||
CharacterRace = Race.White;
|
||||
PlayerCharacterCustomization = null;
|
||||
aimAssistAmount = 0.5f;
|
||||
EnableMouseLook = true;
|
||||
EnableRadialDistortion = true;
|
||||
|
||||
@@ -15,14 +15,14 @@ namespace Barotrauma.Items.Components
|
||||
private Character targetCharacter;
|
||||
private AfflictionPrefab selectedEffect, selectedTaintedEffect;
|
||||
|
||||
[Serialize("", false)]
|
||||
[Serialize("", true)]
|
||||
public string Effect
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("geneticmaterialdebuff", false)]
|
||||
[Serialize("geneticmaterialdebuff", true)]
|
||||
public string TaintedEffect
|
||||
{
|
||||
get;
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private bool tainted;
|
||||
[Serialize(false, false)]
|
||||
[Serialize(false, true)]
|
||||
public bool Tainted
|
||||
{
|
||||
get { return tainted; }
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//only for saving the selected tainted effect
|
||||
[Serialize("", false)]
|
||||
[Serialize("", true)]
|
||||
public string SelectedTaintedEffect
|
||||
{
|
||||
get { return selectedTaintedEffect?.Identifier ?? string.Empty; }
|
||||
@@ -100,6 +100,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float selectedTaintedEffectStrength = item.ConditionPercentage / 100.0f * selectedTaintedEffect.MaxStrength;
|
||||
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
|
||||
var existingAffliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
|
||||
if (existingAffliction != null)
|
||||
{
|
||||
existingAffliction.Strength = selectedTaintedEffectStrength;
|
||||
}
|
||||
targetCharacter = character;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
@@ -111,6 +116,11 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnWearing, 1.0f);
|
||||
float selectedEffectStrength = item.ConditionPercentage / 100.0f * selectedEffect.MaxStrength;
|
||||
character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
|
||||
var existingAffliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
|
||||
if (existingAffliction != null)
|
||||
{
|
||||
existingAffliction.Strength = selectedEffectStrength;
|
||||
}
|
||||
targetCharacter = character;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
@@ -197,5 +207,21 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string TryCreateName(ItemPrefab prefab, XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals(nameof(GeneticMaterial), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string nameId = subElement.GetAttributeString("nameidentifier", "");
|
||||
if (!string.IsNullOrEmpty(nameId))
|
||||
{
|
||||
return prefab.Name.Replace("[type]", TextManager.Get(nameId, returnNull: true) ?? nameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return prefab.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,6 +603,11 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector2 GetAttachPosition(Character user, bool useWorldCoordinates = false)
|
||||
{
|
||||
if (user == null) { return useWorldCoordinates ? item.WorldPosition : item.Position; }
|
||||
|
||||
@@ -61,8 +61,10 @@ namespace Barotrauma.Items.Components
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Attack = new Attack(subElement, item.Name + ", MeleeWeapon", item);
|
||||
Attack.DamageRange = item.body == null ? 10.0f : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent());
|
||||
Attack = new Attack(subElement, item.Name + ", MeleeWeapon", item)
|
||||
{
|
||||
DamageRange = item.body == null ? 10.0f : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent())
|
||||
};
|
||||
}
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have melee weapons that don't require aim to use
|
||||
@@ -266,16 +268,10 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
Character targetCharacter = null;
|
||||
Limb targetLimb = null;
|
||||
Structure targetStructure = null;
|
||||
Item targetItem = null;
|
||||
|
||||
if (f2.Body.UserData is Limb)
|
||||
if (f2.Body.UserData is Limb targetLimb)
|
||||
{
|
||||
targetLimb = (Limb)f2.Body.UserData;
|
||||
if (targetLimb.IsSevered || targetLimb.character == null || targetLimb.character == User) { return false; }
|
||||
targetCharacter = targetLimb.character;
|
||||
var targetCharacter = targetLimb.character;
|
||||
if (targetCharacter == picker) { return false; }
|
||||
if (AllowHitMultiple)
|
||||
{
|
||||
@@ -287,9 +283,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
hitTargets.Add(targetCharacter);
|
||||
}
|
||||
else if (f2.Body.UserData is Character)
|
||||
else if (f2.Body.UserData is Character targetCharacter)
|
||||
{
|
||||
targetCharacter = (Character)f2.Body.UserData;
|
||||
if (targetCharacter == picker || targetCharacter == User) { return false; }
|
||||
targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways
|
||||
if (AllowHitMultiple)
|
||||
@@ -302,9 +297,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
hitTargets.Add(targetCharacter);
|
||||
}
|
||||
else if (f2.Body.UserData is Structure)
|
||||
else if (f2.Body.UserData is Structure targetStructure)
|
||||
{
|
||||
targetStructure = (Structure)f2.Body.UserData;
|
||||
if (AllowHitMultiple)
|
||||
{
|
||||
if (hitTargets.Contains(targetStructure)) { return true; }
|
||||
@@ -315,9 +309,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
hitTargets.Add(targetStructure);
|
||||
}
|
||||
else if (f2.Body.UserData is Item)
|
||||
else if (f2.Body.UserData is Item targetItem)
|
||||
{
|
||||
targetItem = (Item)f2.Body.UserData;
|
||||
if (AllowHitMultiple)
|
||||
{
|
||||
if (hitTargets.Contains(targetItem)) { return true; }
|
||||
@@ -350,13 +343,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Limb targetLimb = target.UserData as Limb;
|
||||
Character targetCharacter = targetLimb?.character ?? target.UserData as Character;
|
||||
Structure targetStructure = target.UserData as Structure;
|
||||
Item targetItem = target.UserData as Item;
|
||||
|
||||
if (Attack != null)
|
||||
{
|
||||
Attack.SetUser(User);
|
||||
Attack.DamageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
|
||||
Attack.DamageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.AttackMultiplier);
|
||||
|
||||
if (targetLimb != null)
|
||||
{
|
||||
@@ -370,12 +361,12 @@ namespace Barotrauma.Items.Components
|
||||
targetCharacter.LastDamageSource = item;
|
||||
Attack.DoDamage(User, targetCharacter, item.WorldPosition, 1.0f);
|
||||
}
|
||||
else if (targetStructure != null)
|
||||
else if (target.UserData is Structure targetStructure)
|
||||
{
|
||||
if (targetStructure.Removed) { return; }
|
||||
Attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f);
|
||||
}
|
||||
else if (targetItem != null && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
|
||||
else if (target.UserData is Item targetItem && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
|
||||
{
|
||||
if (targetItem.Removed) { return; }
|
||||
Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
|
||||
@@ -196,7 +196,8 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
float rotation = (Item.body.Dir == 1.0f) ? Item.body.Rotation : Item.body.Rotation - MathHelper.Pi;
|
||||
float spread = GetSpread(character) * Rand.Range(-0.5f, 0.5f);
|
||||
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: limbBodies.ToList(), createNetworkEvent: false);
|
||||
float damageMultiplier = 1f + item.GetQualityModifier(Quality.StatType.AttackMultiplier);
|
||||
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: limbBodies.ToList(), createNetworkEvent: false, damageMultiplier);
|
||||
projectile.Item.GetComponent<Rope>()?.Attach(Item, projectile.Item);
|
||||
if (i == 0)
|
||||
{
|
||||
|
||||
@@ -619,7 +619,7 @@ namespace Barotrauma.Items.Components
|
||||
levelResource.requiredItems.Any() &&
|
||||
levelResource.HasRequiredItems(user, addMessage: false))
|
||||
{
|
||||
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier);
|
||||
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * (1f + item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier));
|
||||
levelResource.DeattachTimer += addedDetachTime;
|
||||
#if CLIENT
|
||||
Character.Controlled?.UpdateHUDProgressBar(
|
||||
|
||||
+17
-14
@@ -19,6 +19,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float userDeconstructorSpeedMultiplier = 1.0f;
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
public ItemContainer InputContainer
|
||||
@@ -89,12 +91,20 @@ namespace Barotrauma.Items.Components
|
||||
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
|
||||
progressTimer += deltaTime * Math.Min(Voltage, 1.0f);
|
||||
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
{
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
}
|
||||
// doesn't quite work properly, remaining time changes if tinkering stops
|
||||
float deconstructionSpeedModifier = userDeconstructorSpeedMultiplier * (1f + tinkeringStrength * TinkeringSpeedIncrease);
|
||||
|
||||
if (DeconstructItemsSimultaneously)
|
||||
{
|
||||
float deconstructTime = 0.0f;
|
||||
foreach (Item targetItem in inputContainer.Inventory.AllItems)
|
||||
{
|
||||
deconstructTime += targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * userDeconstructorSpeedMultiplier);
|
||||
deconstructTime += targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier);
|
||||
}
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
@@ -126,7 +136,7 @@ namespace Barotrauma.Items.Components
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.FindAll(it =>
|
||||
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / DeconstructionSpeed : 1.0f;
|
||||
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
if (progressTimer > deconstructTime)
|
||||
@@ -234,9 +244,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (user != null && !user.Removed)
|
||||
{
|
||||
var itemsCreated = new AbilityValueItem(1f, targetItem.Prefab);
|
||||
var itemsCreated = new AbilityValueItem(amount, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedMaterial, itemsCreated);
|
||||
amount = (int)itemsCreated.Value;
|
||||
|
||||
// used to spawn items directly into the deconstructor
|
||||
var itemContainer = new AbilityItemPrefabItem(item, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemContainer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
@@ -256,17 +270,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (user != null && !user.Removed)
|
||||
{
|
||||
var deconstructItemRetainProbability = new AbilityValueItem(0f, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedRetainProbability, deconstructItemRetainProbability);
|
||||
|
||||
if (deconstructItemRetainProbability.Value > Rand.Range(0f, 1f, Rand.RandSync.Unsynced))
|
||||
{
|
||||
allowRemove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.AllowDeconstruct && allowRemove)
|
||||
{
|
||||
//drop all items that are inside the deconstructed item
|
||||
|
||||
@@ -73,6 +73,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private const float TinkeringForceIncrease = 1.5f;
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -128,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
currForce *= maxForce * forceMultiplier;
|
||||
if (item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering)
|
||||
{
|
||||
currForce *= 2.5f;
|
||||
currForce *= 1f + repairable.TinkeringStrength * TinkeringForceIncrease;
|
||||
}
|
||||
|
||||
//less effective when in a bad condition
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(1.0f, true)]
|
||||
public float SkillRequirementMultiplier { get; set; }
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
private enum FabricatorState
|
||||
{
|
||||
Active = 1,
|
||||
@@ -279,7 +281,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f);
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
{
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
}
|
||||
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
|
||||
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(Voltage, 1.0f);
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
@@ -329,12 +338,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
|
||||
|
||||
float floatQuality = 0.0f;
|
||||
foreach (string tag in fabricatedItem.TargetItem.Tags)
|
||||
{
|
||||
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
|
||||
}
|
||||
quality = (int)floatQuality;
|
||||
quality = GetFabricatedItemQuality(fabricatedItem, user);
|
||||
}
|
||||
|
||||
var tempUser = user;
|
||||
@@ -404,6 +408,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private int GetFabricatedItemQuality(FabricationRecipe fabricatedItem, Character user)
|
||||
{
|
||||
if (user == null) { return 0; }
|
||||
if (fabricatedItem.TargetItem.ConfigElement.GetChildElement("Quality") == null) { return 0; }
|
||||
int quality = 0;
|
||||
float floatQuality = 0.0f;
|
||||
foreach (string tag in fabricatedItem.TargetItem.Tags)
|
||||
{
|
||||
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
|
||||
}
|
||||
quality = (int)floatQuality;
|
||||
|
||||
const int MaxCraftingSkill = 100;
|
||||
|
||||
quality += fabricatedItem.RequiredSkills.All(s => user.GetSkillLevel(s.Identifier) >= MaxCraftingSkill) ? 1 : 0;
|
||||
quality += FabricationDegreeOfSuccess(user, fabricatedItem.RequiredSkills) >= 0.5f ? 1 : 0;
|
||||
return quality;
|
||||
}
|
||||
|
||||
partial void UpdateRequiredTimeProjSpecific();
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, Dictionary<string, List<Item>> availableIngredients, Character character)
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace Barotrauma.Items.Components
|
||||
public bool HasPower => IsActive && Voltage >= MinVoltage;
|
||||
public bool IsAutoControlled => pumpSpeedLockTimer > 0.0f || isActiveLockTimer > 0.0f;
|
||||
|
||||
private const float TinkeringSpeedIncrease = 1.5f;
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -108,7 +110,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering)
|
||||
{
|
||||
currFlow *= 2.5f;
|
||||
currFlow *= 1f + repairable.TinkeringStrength * TinkeringSpeedIncrease;
|
||||
}
|
||||
|
||||
//less effective when in a bad condition
|
||||
|
||||
@@ -370,7 +370,7 @@ namespace Barotrauma.Items.Components
|
||||
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.X * Physics.DisplayToRealWorldRatio) * 3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_x");
|
||||
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.Y * Physics.DisplayToRealWorldRatio) * -3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_y");
|
||||
|
||||
item.SendSignal(new Signal(sub.WorldPosition.X.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
|
||||
item.SendSignal(new Signal((sub.WorldPosition.X * Physics.DisplayToRealWorldRatio).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
|
||||
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_y");
|
||||
}
|
||||
|
||||
|
||||
@@ -227,10 +227,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation)
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f)
|
||||
{
|
||||
Item.body.ResetDynamics();
|
||||
Item.SetTransform(simPosition, rotation);
|
||||
Attack.DamageMultiplier = damageMultiplier;
|
||||
// Set user for hitscan projectiles to work properly.
|
||||
User = user;
|
||||
// Need to set null for non-characterusable items.
|
||||
@@ -243,7 +244,7 @@ namespace Barotrauma.Items.Components
|
||||
Item.SetTransform(simPosition, rotation + (Item.body.Dir * LaunchRotationRadians));
|
||||
}
|
||||
|
||||
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent)
|
||||
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f)
|
||||
{
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
@@ -264,7 +265,7 @@ namespace Barotrauma.Items.Components
|
||||
projectilePos = newPos;
|
||||
}
|
||||
}
|
||||
Launch(user, projectilePos, rotation);
|
||||
Launch(user, projectilePos, rotation, damageMultiplier);
|
||||
if (createNetworkEvent && !Item.Removed && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
#if SERVER
|
||||
|
||||
@@ -114,6 +114,9 @@ namespace Barotrauma.Items.Components
|
||||
private Item currentRepairItem;
|
||||
|
||||
private float tinkeringDuration;
|
||||
private float tinkeringStrength;
|
||||
|
||||
public float TinkeringStrength => tinkeringStrength;
|
||||
|
||||
public enum FixActions : int
|
||||
{
|
||||
@@ -240,6 +243,8 @@ namespace Barotrauma.Items.Components
|
||||
CurrentFixerAction = action;
|
||||
if (action == FixActions.Tinker)
|
||||
{
|
||||
tinkeringStrength = 1f + CurrentFixer.GetStatValue(StatTypes.TinkeringStrength);
|
||||
|
||||
if (character.HasAbilityFlag(AbilityFlags.CanTinkerFabricatorsAndDeconstructors) && item.GetComponent<Deconstructor>() != null || item.GetComponent<Fabricator>() != null)
|
||||
{
|
||||
// fabricators and deconstructors can be tinkered indefinitely (more or less)
|
||||
@@ -370,6 +375,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
tinkeringDuration -= deltaTime;
|
||||
// not great to interject it here, should be less reliant on returning
|
||||
|
||||
float conditionDecrease = deltaTime * (CurrentFixer.GetStatValue(StatTypes.TinkeringDamage) / item.MaxCondition) * 100f;
|
||||
item.Condition -= conditionDecrease;
|
||||
|
||||
if (!CanTinker(CurrentFixer) || tinkeringDuration <= 0f)
|
||||
{
|
||||
StopRepairing(CurrentFixer);
|
||||
@@ -476,8 +485,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!character.HasAbilityFlag(AbilityFlags.CanTinker)) { return false; }
|
||||
if (item.GetComponent<Engine>() != null) { return true; }
|
||||
if (item.GetComponent<Turret>() != null) { return true; }
|
||||
if (item.GetComponent<Pump>() != null) { return true; }
|
||||
if (item.HasTag("turretammosource")) { return true; }
|
||||
if (!character.HasAbilityFlag(AbilityFlags.CanTinkerFabricatorsAndDeconstructors)) { return false; }
|
||||
if (item.GetComponent<Fabricator>() != null) { return true; }
|
||||
if (item.GetComponent<Deconstructor>() != null) { return true; }
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
public RegExFindComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
nonContinuousOutputSent = true;
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ namespace Barotrauma.Items.Components
|
||||
private Character currentTarget;
|
||||
const float aiFindTargetInterval = 5.0f;
|
||||
|
||||
private const float TinkeringPowerCostReduction = 1.25f;
|
||||
private const float TinkeringPowerCostReduction = 0.2f;
|
||||
private const float TinkeringDamageIncrease = 0.2f;
|
||||
private const float TinkeringReloadDecrease = 0.2f;
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
@@ -560,7 +562,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Projectile launchedProjectile = null;
|
||||
bool loaderBroken = false;
|
||||
bool isTinkering = false;
|
||||
float tinkeringStrength = 0f;
|
||||
|
||||
for (int i = 0; i < ProjectileCount; i++)
|
||||
{
|
||||
var projectiles = GetLoadedProjectiles();
|
||||
@@ -624,9 +627,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (linkedItem.GetComponent<Repairable>() is Repairable repairable && linkedItem.HasTag("turretammosource"))
|
||||
if (linkedItem.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && linkedItem.HasTag("turretammosource"))
|
||||
{
|
||||
isTinkering = repairable.IsTinkering;
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,10 +639,8 @@ namespace Barotrauma.Items.Components
|
||||
float neededPower = GetPowerRequiredToShoot();
|
||||
// tinkering is currently not factored into the common method as it is checked only when shooting
|
||||
// but this is a minor issue that causes mostly cosmetic woes. might still be worth refactoring later
|
||||
if (isTinkering)
|
||||
{
|
||||
neededPower /= TinkeringPowerCostReduction;
|
||||
}
|
||||
neededPower /= 1f + (tinkeringStrength * TinkeringPowerCostReduction);
|
||||
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
@@ -673,12 +674,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Projectile projectile in projectiles)
|
||||
{
|
||||
Launch(projectile.Item, character, isTinkering: isTinkering);
|
||||
Launch(projectile.Item, character, tinkeringStrength: tinkeringStrength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(null, character, isTinkering: isTinkering);
|
||||
Launch(null, character, tinkeringStrength: tinkeringStrength);
|
||||
}
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
@@ -712,13 +713,10 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null, bool isTinkering = false)
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null, float tinkeringStrength = 0f)
|
||||
{
|
||||
reload = reloadTime;
|
||||
if (isTinkering)
|
||||
{
|
||||
reload /= 1.25f;
|
||||
}
|
||||
reload /= 1f + (tinkeringStrength * TinkeringReloadDecrease);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
@@ -747,10 +745,8 @@ namespace Barotrauma.Items.Components
|
||||
if (projectileComponent != null)
|
||||
{
|
||||
projectileComponent.Attacker = projectileComponent.User = user;
|
||||
if (isTinkering)
|
||||
{
|
||||
projectileComponent.Attack.DamageMultiplier = 1.25f;
|
||||
}
|
||||
projectileComponent.Attack.DamageMultiplier = 1f + (TinkeringDamageIncrease * tinkeringStrength);
|
||||
|
||||
projectileComponent.Use();
|
||||
projectile.GetComponent<Rope>()?.Attach(item, projectile);
|
||||
projectileComponent.User = user;
|
||||
|
||||
@@ -5,7 +5,6 @@ using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
@@ -49,7 +48,13 @@ namespace Barotrauma
|
||||
public bool HideOtherWearables { get; private set; }
|
||||
public List<WearableType> HideWearablesOfType { get; private set; }
|
||||
public bool InheritLimbDepth { get; private set; }
|
||||
public bool InheritTextureScale { get; private set; }
|
||||
/// <summary>
|
||||
/// Does the wearable inherit all the scalings of the wearer? Also the wearable's own scale is used!
|
||||
/// </summary>
|
||||
public bool InheritScale { get; private set; }
|
||||
public bool IgnoreRagdollScale { get; private set; }
|
||||
public bool IgnoreLimbScale { get; private set; }
|
||||
public bool IgnoreTextureScale { get; private set; }
|
||||
public bool InheritOrigin { get; private set; }
|
||||
public bool InheritSourceRect { get; private set; }
|
||||
|
||||
@@ -113,10 +118,9 @@ namespace Barotrauma
|
||||
case WearableType.Husk:
|
||||
case WearableType.Herpes:
|
||||
Limb = LimbType.Head;
|
||||
HideLimb = type == WearableType.Husk || type == WearableType.Herpes;
|
||||
HideOtherWearables = false;
|
||||
InheritLimbDepth = true;
|
||||
InheritTextureScale = true;
|
||||
InheritScale = true;
|
||||
InheritOrigin = true;
|
||||
InheritSourceRect = true;
|
||||
break;
|
||||
@@ -173,7 +177,19 @@ namespace Barotrauma
|
||||
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
|
||||
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
|
||||
InheritLimbDepth = SourceElement.GetAttributeBool("inheritlimbdepth", true);
|
||||
InheritTextureScale = SourceElement.GetAttributeBool("inherittexturescale", false);
|
||||
var scale = SourceElement.GetAttribute("inheritscale");
|
||||
if (scale != null)
|
||||
{
|
||||
InheritScale = scale.GetAttributeBool(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
InheritScale = SourceElement.GetAttributeBool("inherittexturescale", false);
|
||||
}
|
||||
IgnoreLimbScale = SourceElement.GetAttributeBool("ignorelimbscale", false);
|
||||
IgnoreTextureScale = SourceElement.GetAttributeBool("ignoretexturescale", false);
|
||||
IgnoreRagdollScale = SourceElement.GetAttributeBool("ignoreragdollscale", false);
|
||||
SourceElement.GetAttributeBool("inherittexturescale", false);
|
||||
InheritOrigin = SourceElement.GetAttributeBool("inheritorigin", false);
|
||||
InheritSourceRect = SourceElement.GetAttributeBool("inheritsourcerect", false);
|
||||
DepthLimb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("depthlimb", "None"), true);
|
||||
|
||||
@@ -842,6 +842,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
name = GeneticMaterial.TryCreateName(this, element);
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
DebugConsole.ThrowError($"Unnamed item ({identifier}) in {filePath}!");
|
||||
|
||||
@@ -167,7 +167,8 @@ namespace Barotrauma
|
||||
new XAttribute("type", type.ToString()),
|
||||
new XAttribute("optional", IsOptional),
|
||||
new XAttribute("ignoreineditor", IgnoreInEditor),
|
||||
new XAttribute("excludebroken", ExcludeBroken));
|
||||
new XAttribute("excludebroken", ExcludeBroken),
|
||||
new XAttribute("targetslot", TargetSlot));
|
||||
|
||||
if (excludedIdentifiers.Length > 0)
|
||||
{
|
||||
|
||||
@@ -340,7 +340,7 @@ namespace Barotrauma
|
||||
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
|
||||
Vector2 dir = worldPosition - limb.WorldPosition;
|
||||
Vector2 hitPos = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
|
||||
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
|
||||
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker, damageMultiplier: attack.DamageMultiplier);
|
||||
damages.Add(limb, attackResult.Damage);
|
||||
|
||||
if (attack.StatusEffects != null && attack.StatusEffects.Any())
|
||||
|
||||
@@ -2563,10 +2563,18 @@ namespace Barotrauma
|
||||
if (PositionsOfInterest.Any(p => p.PositionType == PositionType.Cave))
|
||||
{
|
||||
positionType = PositionType.Cave;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToCave))
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToCave);
|
||||
}
|
||||
}
|
||||
else if (PositionsOfInterest.Any(p => p.PositionType == PositionType.SidePath))
|
||||
{
|
||||
positionType = PositionType.SidePath;
|
||||
if (allValidLocations.Any(l => l.Edge.NextToSidePath))
|
||||
{
|
||||
allValidLocations.RemoveAll(l => !l.Edge.NextToSidePath);
|
||||
}
|
||||
}
|
||||
|
||||
var poi = PositionsOfInterest.GetRandom(p => p.PositionType == positionType, randSync: Rand.RandSync.Server);
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Barotrauma
|
||||
|
||||
private LocationType addInitialMissionsForType;
|
||||
|
||||
public bool Discovered;
|
||||
public bool Discovered { get; private set; }
|
||||
|
||||
public readonly Dictionary<LocationTypeChange.Requirement, int> ProximityTimer = new Dictionary<LocationTypeChange.Requirement, int>();
|
||||
public (LocationTypeChange typeChange, int delay, MissionPrefab parentMission)? PendingLocationTypeChange;
|
||||
@@ -868,6 +868,8 @@ namespace Barotrauma
|
||||
// Adjust by random price modifier
|
||||
price = ((100 + StorePriceModifier) / 100.0f) * price;
|
||||
|
||||
price *= priceInfo.BuyingPriceMultiplier;
|
||||
|
||||
// Adjust by daily special status
|
||||
if (considerDailySpecials && DailySpecials.Contains(item))
|
||||
{
|
||||
@@ -1111,6 +1113,30 @@ namespace Barotrauma
|
||||
return nextStatus;
|
||||
}
|
||||
|
||||
public void Discover(bool checkTalents = true)
|
||||
{
|
||||
if (Discovered) { return; }
|
||||
Discovered = true;
|
||||
if (checkTalents)
|
||||
{
|
||||
GameSession.GetSessionCrewCharacters().ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new Abilities.AbilityLocation(this)));
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (Type != OriginalType)
|
||||
{
|
||||
ChangeType(OriginalType);
|
||||
PendingLocationTypeChange = null;
|
||||
}
|
||||
CreateStore(force: true);
|
||||
ClearMissions();
|
||||
LevelData?.EventHistory?.Clear();
|
||||
UnlockInitialMissions();
|
||||
Discovered = false;
|
||||
}
|
||||
|
||||
public XElement Save(Map map, XElement parentElement)
|
||||
{
|
||||
var locationElement = new XElement("location",
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Barotrauma
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
|
||||
|
||||
CurrentLocation.Discovered = true;
|
||||
CurrentLocation.Discover(true);
|
||||
CurrentLocation.CreateStore();
|
||||
|
||||
InitProjectSpecific();
|
||||
@@ -671,7 +671,7 @@ namespace Barotrauma
|
||||
SelectedConnection.Passed = true;
|
||||
|
||||
CurrentLocation = SelectedLocation;
|
||||
CurrentLocation.Discovered = true;
|
||||
CurrentLocation.Discover();
|
||||
SelectedLocation = null;
|
||||
|
||||
CurrentLocation.CreateStore();
|
||||
@@ -702,7 +702,7 @@ namespace Barotrauma
|
||||
|
||||
Location prevLocation = CurrentLocation;
|
||||
CurrentLocation = Locations[index];
|
||||
CurrentLocation.Discovered = true;
|
||||
CurrentLocation.Discover();
|
||||
|
||||
if (prevLocation != CurrentLocation)
|
||||
{
|
||||
@@ -1055,7 +1055,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
location.LoadLocationTypeChange(subElement);
|
||||
location.Discovered = subElement.GetAttributeBool("discovered", false);
|
||||
if (subElement.GetAttributeBool("discovered", false))
|
||||
{
|
||||
location.Discover(checkTalents: false);
|
||||
}
|
||||
if (location.Discovered)
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace Barotrauma
|
||||
/// The item isn't available in stores unless the level's difficulty is above this value
|
||||
/// </summary>
|
||||
public readonly int MinLevelDifficulty;
|
||||
/// <summary>
|
||||
/// The cost of item when sold by the store. Higher modifier means the item costs more to buy from the store.
|
||||
/// </summary>
|
||||
public readonly float BuyingPriceMultiplier = 1f;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Support for the old style of determining item prices
|
||||
@@ -34,6 +39,7 @@ namespace Barotrauma
|
||||
{
|
||||
Price = element.GetAttributeInt("buyprice", 0);
|
||||
MinLevelDifficulty = element.GetAttributeInt("minleveldifficulty", 0);
|
||||
BuyingPriceMultiplier = element.GetAttributeFloat("buyingpricemultiplier", 1f);
|
||||
CanBeBought = true;
|
||||
var minAmount = GetMinAmount(element);
|
||||
MinAvailableAmount = Math.Min(minAmount, CargoManager.MaxQuantity);
|
||||
@@ -42,11 +48,12 @@ namespace Barotrauma
|
||||
MaxAvailableAmount = Math.Max(maxAmount, MinAvailableAmount);
|
||||
}
|
||||
|
||||
public PriceInfo(int price, bool canBeBought, int minAmount = 0, int maxAmount = 0, bool canBeSpecial = true, int minLevelDifficulty = 0)
|
||||
public PriceInfo(int price, bool canBeBought, int minAmount = 0, int maxAmount = 0, bool canBeSpecial = true, int minLevelDifficulty = 0, float buyingPriceMultiplier = 1f)
|
||||
{
|
||||
Price = price;
|
||||
CanBeBought = canBeBought;
|
||||
MinAvailableAmount = Math.Min(minAmount, CargoManager.MaxQuantity);
|
||||
BuyingPriceMultiplier = buyingPriceMultiplier;
|
||||
maxAmount = Math.Min(maxAmount, CargoManager.MaxQuantity);
|
||||
MaxAvailableAmount = Math.Max(maxAmount, minAmount);
|
||||
MinLevelDifficulty = minLevelDifficulty;
|
||||
@@ -62,6 +69,7 @@ namespace Barotrauma
|
||||
var maxAmount = GetMaxAmount(element);
|
||||
var minLevelDifficulty = element.GetAttributeInt("minleveldifficulty", 0);
|
||||
var canBeSpecial = element.GetAttributeBool("canbespecial", true);
|
||||
var buyingPriceMultiplier = element.GetAttributeFloat("buyingpricemultiplier", 1f);
|
||||
var priceInfos = new List<Tuple<string, PriceInfo>>();
|
||||
|
||||
foreach (XElement childElement in element.GetChildElements("price"))
|
||||
@@ -73,7 +81,7 @@ namespace Barotrauma
|
||||
minAmount: sold ? GetMinAmount(childElement, minAmount) : 0,
|
||||
maxAmount: sold ? GetMaxAmount(childElement, maxAmount) : 0,
|
||||
canBeSpecial,
|
||||
childElement.GetAttributeInt("minleveldifficulty", minLevelDifficulty))));
|
||||
childElement.GetAttributeInt("minleveldifficulty", minLevelDifficulty), childElement.GetAttributeFloat("buyingpricemultiplier", buyingPriceMultiplier))));
|
||||
}
|
||||
|
||||
var canBeBoughtAtOtherLocations = soldByDefault && element.GetAttributeBool("soldeverywhere", true);
|
||||
@@ -81,7 +89,7 @@ namespace Barotrauma
|
||||
minAmount: canBeBoughtAtOtherLocations ? minAmount : 0,
|
||||
maxAmount: canBeBoughtAtOtherLocations ? maxAmount : 0,
|
||||
canBeSpecial,
|
||||
minLevelDifficulty);
|
||||
minLevelDifficulty, buyingPriceMultiplier);
|
||||
|
||||
return priceInfos;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Barotrauma.Networking
|
||||
Double ReadDouble();
|
||||
UInt32 ReadVariableUInt32();
|
||||
String ReadString();
|
||||
Microsoft.Xna.Framework.Color ReadColorR8G8B8();
|
||||
Microsoft.Xna.Framework.Color ReadColorR8G8B8A8();
|
||||
int ReadRangedInteger(int min, int max);
|
||||
Single ReadRangedSingle(Single min, Single max, int bitCount);
|
||||
byte[] ReadBytes(int numberOfBytes);
|
||||
|
||||
+2
@@ -15,6 +15,8 @@ namespace Barotrauma.Networking
|
||||
void Write(UInt64 val);
|
||||
void Write(Single val);
|
||||
void Write(Double val);
|
||||
void WriteColorR8G8B8(Microsoft.Xna.Framework.Color val);
|
||||
void WriteColorR8G8B8A8(Microsoft.Xna.Framework.Color val);
|
||||
void WriteVariableUInt32(UInt32 val);
|
||||
void Write(string val);
|
||||
void WriteRangedInteger(int val, int min, int max);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Barotrauma.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -138,6 +139,26 @@ namespace Barotrauma.Networking
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
WriteBytes(ref buf, ref bitPos, bytes, 0, 8);
|
||||
}
|
||||
|
||||
internal static void WriteColorR8G8B8(ref byte[] buf, ref int bitPos, Microsoft.Xna.Framework.Color val)
|
||||
{
|
||||
EnsureBufferSize(ref buf, bitPos + 24);
|
||||
|
||||
Write(ref buf, ref bitPos, val.R);
|
||||
Write(ref buf, ref bitPos, val.G);
|
||||
Write(ref buf, ref bitPos, val.B);
|
||||
}
|
||||
|
||||
internal static void WriteColorR8G8B8A8(ref byte[] buf, ref int bitPos, Microsoft.Xna.Framework.Color val)
|
||||
{
|
||||
EnsureBufferSize(ref buf, bitPos + 32);
|
||||
|
||||
Write(ref buf, ref bitPos, val.R);
|
||||
Write(ref buf, ref bitPos, val.G);
|
||||
Write(ref buf, ref bitPos, val.B);
|
||||
Write(ref buf, ref bitPos, val.A);
|
||||
}
|
||||
|
||||
internal static void Write(ref byte[] buf, ref int bitPos, string val)
|
||||
{
|
||||
if (string.IsNullOrEmpty(val))
|
||||
@@ -299,6 +320,23 @@ namespace Barotrauma.Networking
|
||||
return BitConverter.ToDouble(bytes, 0);
|
||||
}
|
||||
|
||||
internal static Microsoft.Xna.Framework.Color ReadColorR8G8B8(byte[] buf, ref int bitPos)
|
||||
{
|
||||
byte r = ReadByte(buf, ref bitPos);
|
||||
byte g = ReadByte(buf, ref bitPos);
|
||||
byte b = ReadByte(buf, ref bitPos);
|
||||
return new Color(r, g, b, (byte)255);
|
||||
}
|
||||
|
||||
internal static Microsoft.Xna.Framework.Color ReadColorR8G8B8A8(byte[] buf, ref int bitPos)
|
||||
{
|
||||
byte r = ReadByte(buf, ref bitPos);
|
||||
byte g = ReadByte(buf, ref bitPos);
|
||||
byte b = ReadByte(buf, ref bitPos);
|
||||
byte a = ReadByte(buf, ref bitPos);
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
internal static UInt32 ReadVariableUInt32(byte[] buf, ref int bitPos)
|
||||
{
|
||||
int bitLength = buf.Length * 8;
|
||||
@@ -482,6 +520,16 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteColorR8G8B8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteColorR8G8B8A8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteVariableUInt32(UInt32 val)
|
||||
{
|
||||
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
|
||||
@@ -702,6 +750,17 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadString(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8A8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
|
||||
public int ReadRangedInteger(int min, int max)
|
||||
{
|
||||
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
|
||||
@@ -845,6 +904,16 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteColorR8G8B8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteColorR8G8B8A8(Color val)
|
||||
{
|
||||
MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void WriteVariableUInt32(UInt32 val)
|
||||
{
|
||||
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
|
||||
@@ -936,6 +1005,16 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadString(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8A8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public int ReadRangedInteger(int min, int max)
|
||||
{
|
||||
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma
|
||||
return GameMain.NetworkMember?.ServerSettings?.AllowLinkingWifiToChat ?? true;
|
||||
case ConditionType.IsSwappableItem:
|
||||
{
|
||||
return entity is Item item && item.Prefab.SwappableItem != null;
|
||||
return entity is Item item && item.Prefab.SwappableItem != null && Screen.Selected == GameMain.SubEditorScreen;
|
||||
}
|
||||
case ConditionType.AllowRotating:
|
||||
{
|
||||
|
||||
@@ -236,7 +236,7 @@ namespace Barotrauma
|
||||
{
|
||||
lock (list)
|
||||
{
|
||||
list.RemoveAll(wRef => !wRef.TryGetTarget(out Sprite s) || s==this);
|
||||
list.RemoveAll(wRef => !wRef.TryGetTarget(out Sprite s) || s == this);
|
||||
}
|
||||
DisposeTexture();
|
||||
}
|
||||
|
||||
@@ -457,7 +457,14 @@ namespace Barotrauma
|
||||
</Description>
|
||||
*/
|
||||
|
||||
string extraDescriptionLine = Get(descriptionElement.GetAttributeString("tag", string.Empty));
|
||||
if (descriptionElement.GetAttributeBool("linebreak", false))
|
||||
{
|
||||
Description += "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
string descriptionTag = descriptionElement.GetAttributeString("tag", string.Empty);
|
||||
string extraDescriptionLine = Get(descriptionTag);
|
||||
if (string.IsNullOrEmpty(extraDescriptionLine)) { return; }
|
||||
foreach (XElement replaceElement in descriptionElement.Elements())
|
||||
{
|
||||
@@ -468,12 +475,23 @@ namespace Barotrauma
|
||||
string replacementValue = string.Empty;
|
||||
for (int i = 0; i < replacementValues.Length; i++)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!int.TryParse(replacementValues[i], out int _) && !float.TryParse(replacementValues[i], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out float __) && !ContainsTag(replacementValues[i]))
|
||||
{
|
||||
DebugConsole.AddWarning($"Couldn't find the tag \"{replacementValues[i]}\" in text files for description \"{descriptionTag}\". Is the tag correct?");
|
||||
}
|
||||
#endif
|
||||
replacementValue += Get(replacementValues[i], returnNull: true) ?? replacementValues[i];
|
||||
if (i < replacementValues.Length - 1)
|
||||
{
|
||||
replacementValue += ", ";
|
||||
}
|
||||
}
|
||||
if (replaceElement.Attribute("color") != null)
|
||||
{
|
||||
string colorStr = replaceElement.GetAttributeString("color", "255,255,255,255");
|
||||
replacementValue = $"‖color:{colorStr}‖{replacementValue}‖color:end‖";
|
||||
}
|
||||
extraDescriptionLine = extraDescriptionLine.Replace(tag, replacementValue);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Description)) { Description += "\n"; }
|
||||
|
||||
@@ -23,17 +23,19 @@ namespace Barotrauma.IO
|
||||
{
|
||||
path = System.IO.Path.GetFullPath(path).CleanUpPath();
|
||||
|
||||
string extension = System.IO.Path.GetExtension(path).Replace(" ", "");
|
||||
if (unwritableExtensions.Any(e => e.Equals(extension, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!isDirectory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!path.StartsWith(System.IO.Path.GetFullPath("Mods/").CleanUpPath(), StringComparison.OrdinalIgnoreCase)
|
||||
&& (extension.Equals(".dll", StringComparison.OrdinalIgnoreCase)
|
||||
|| extension.Equals(".exe", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
string extension = System.IO.Path.GetExtension(path).Replace(" ", "");
|
||||
if (unwritableExtensions.Any(e => e.Equals(extension, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!path.StartsWith(System.IO.Path.GetFullPath("Mods/").CleanUpPath(), StringComparison.OrdinalIgnoreCase)
|
||||
&& (extension.Equals(".dll", StringComparison.OrdinalIgnoreCase)
|
||||
|| extension.Equals(".exe", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string unwritableDir in unwritableDirs)
|
||||
@@ -251,6 +253,7 @@ namespace Barotrauma.IO
|
||||
if (!Validation.CanWrite(path, true))
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot create directory \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
|
||||
Validation.CanWrite(path, true);
|
||||
return null;
|
||||
}
|
||||
return System.IO.Directory.CreateDirectory(path);
|
||||
|
||||
Reference in New Issue
Block a user