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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user