Unstable 0.1500.8.0
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -346,7 +347,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#region Escape
|
||||
public abstract void Escape(float deltaTime);
|
||||
public abstract bool Escape(float deltaTime);
|
||||
|
||||
public Gap EscapeTarget { get; private set; }
|
||||
|
||||
@@ -425,17 +426,38 @@ namespace Barotrauma
|
||||
}
|
||||
if (EscapeTarget != null)
|
||||
{
|
||||
SteeringManager.SteeringSeek(EscapeTarget.SimPosition, 10);
|
||||
float sqrDist = Vector2.DistanceSquared(Character.SimPosition, EscapeTarget.SimPosition);
|
||||
if (sqrDist < 0.5f || Character.CurrentHull == null || HasValidPath(requireNonDirty: true, requireUnfinished: false) && pathSteering.CurrentPath.Finished)
|
||||
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
|
||||
float sqrDist = diff.LengthSquared();
|
||||
if (Character.CurrentHull == null || sqrDist < MathUtils.Pow2(50) || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
|
||||
{
|
||||
// Very close to the target, outside, or at the end of the path -> just steer towards it manually without using the path
|
||||
// Very close to the target, outside, or at the end of the path -> try to steer through the gap
|
||||
SteeringManager.Reset();
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(EscapeTarget.WorldPosition - Character.WorldPosition));
|
||||
if (sqrDist < 4)
|
||||
pathSteering?.ResetPath();
|
||||
if (sqrDist < MathUtils.Pow2(50))
|
||||
{
|
||||
return true;
|
||||
// Very close -> just keep steering forward
|
||||
var forward = VectorExtensions.Forward(Character.AnimController.Collider.Rotation + MathHelper.PiOver2);
|
||||
SteeringManager.SteeringManual(deltaTime, forward);
|
||||
}
|
||||
else if (Character.CurrentHull == null)
|
||||
{
|
||||
// Outside -> steer away from the target
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(-diff));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still inside -> steer towards the target
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(diff));
|
||||
}
|
||||
return sqrDist < MathUtils.Pow2(200);
|
||||
}
|
||||
else if (pathSteering != null)
|
||||
{
|
||||
pathSteering.SteeringSeek(EscapeTarget.SimPosition, weight: 1, minGapSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(EscapeTarget.SimPosition, 10);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1903,7 +1903,7 @@ namespace Barotrauma
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 1);
|
||||
if (attacker == null || attacker.AiTarget == null || attacker.Removed || attacker.IsDead) { return; }
|
||||
if (Character.Params.CanInteract)
|
||||
if (Character.Params.CanInteract && attackResult.Damage > 10)
|
||||
{
|
||||
ReleaseDragTargets();
|
||||
}
|
||||
@@ -3607,12 +3607,12 @@ namespace Barotrauma
|
||||
|
||||
public bool CanPassThroughHole(Structure wall, int sectionIndex) => CanPassThroughHole(wall, sectionIndex, requiredHoleCount);
|
||||
|
||||
public override void Escape(float deltaTime)
|
||||
public override bool Escape(float deltaTime)
|
||||
{
|
||||
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else if (SelectedTargetMemory is AITargetMemory targetMemory && SelectedAiTarget?.Entity is Character)
|
||||
{
|
||||
@@ -3653,6 +3653,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
return isSteeringThroughGap;
|
||||
|
||||
void SteerAwayFromTheEnemy()
|
||||
{
|
||||
|
||||
@@ -1364,10 +1364,7 @@ namespace Barotrauma
|
||||
ObjectiveManager.WaitTimer = waitDuration;
|
||||
}
|
||||
|
||||
public override void Escape(float deltaTime)
|
||||
{
|
||||
UpdateEscape(deltaTime, canAttackDoors: false);
|
||||
}
|
||||
public override bool Escape(float deltaTime) => UpdateEscape(deltaTime, canAttackDoors: false);
|
||||
|
||||
private void CheckCrouching(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma
|
||||
{
|
||||
var waypoint = CurrentPath.Nodes[i];
|
||||
float directDistance = Vector2.DistanceSquared(character.WorldPosition, waypoint.WorldPosition);
|
||||
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel) != null)
|
||||
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
|
||||
{
|
||||
pathDistance -= CurrentPath.GetLength(startIndex: i - 1, endIndex: i);
|
||||
continue;
|
||||
|
||||
+16
-7
@@ -9,6 +9,7 @@ namespace Barotrauma
|
||||
public override string Identifier { get; set; } = "return";
|
||||
private AIObjectiveGoTo moveInsideObjective, moveInCaveObjective, moveOutsideObjective;
|
||||
private bool usingEscapeBehavior;
|
||||
private bool isSteeringThroughGap;
|
||||
public Submarine ReturnTarget { get; }
|
||||
|
||||
public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
@@ -57,7 +58,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
bool shouldUseEscapeBehavior = false;
|
||||
if (character.CurrentHull != null)
|
||||
if (character.CurrentHull != null || isSteeringThroughGap)
|
||||
{
|
||||
if (character.Submarine == null || !character.Submarine.IsConnectedTo(ReturnTarget))
|
||||
{
|
||||
@@ -67,8 +68,8 @@ namespace Barotrauma
|
||||
{
|
||||
HumanAIController.ResetEscape();
|
||||
}
|
||||
HumanAIController.Escape(deltaTime);
|
||||
if (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable)
|
||||
isSteeringThroughGap = HumanAIController.Escape(deltaTime);
|
||||
if (!isSteeringThroughGap && (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable))
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -92,7 +93,10 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
RemoveSubObjective(ref moveOutsideObjective);
|
||||
TryAddSubObjective(ref moveInsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveInsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
@@ -110,7 +114,7 @@ namespace Barotrauma
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
else if (moveInCaveObjective == null && moveOutsideObjective == null)
|
||||
else if (!isSteeringThroughGap && moveInCaveObjective == null && moveOutsideObjective == null)
|
||||
{
|
||||
if (HumanAIController.IsInsideCave)
|
||||
{
|
||||
@@ -134,7 +138,8 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref moveInCaveObjective,
|
||||
constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager)
|
||||
{
|
||||
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint
|
||||
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint,
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveInCaveObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
@@ -170,7 +175,10 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref moveInsideObjective);
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
TryAddSubObjective(ref moveOutsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager),
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
@@ -221,6 +229,7 @@ namespace Barotrauma
|
||||
moveInCaveObjective = null;
|
||||
moveOutsideObjective = null;
|
||||
usingEscapeBehavior = false;
|
||||
isSteeringThroughGap = false;
|
||||
HumanAIController.ResetEscape();
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ namespace Barotrauma
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs);
|
||||
if (body != null)
|
||||
{
|
||||
if (body.UserData is Submarine) { return false; }
|
||||
if (body.UserData is Structure s && !s.IsPlatform) { return false; }
|
||||
if (body.UserData is Item && body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall)) { return false; }
|
||||
}
|
||||
|
||||
+35
-15
@@ -684,18 +684,15 @@ namespace Barotrauma
|
||||
|
||||
if (rightHand != null && !rightHand.Disabled)
|
||||
{
|
||||
HandIK(rightHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
-handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
HandIK(rightHand,
|
||||
torso.SimPosition + posAddition + new Vector2(-handPos.X, (Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY),
|
||||
CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
{
|
||||
HandIK(leftHand, torso.SimPosition + posAddition +
|
||||
new Vector2(
|
||||
handPos.X,
|
||||
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
HandIK(leftHand,
|
||||
torso.SimPosition + posAddition + new Vector2(handPos.X, (Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY),
|
||||
CurrentGroundedParams.ArmMoveStrength, CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -705,9 +702,7 @@ namespace Barotrauma
|
||||
Vector2 footPos = colliderPos;
|
||||
if (Crouching)
|
||||
{
|
||||
footPos = new Vector2(
|
||||
Math.Sign(stepSize.X * i) * Dir * 0.4f,
|
||||
colliderPos.Y);
|
||||
footPos = new Vector2(Math.Sign(stepSize.X * i) * Dir * 0.35f, colliderPos.Y);
|
||||
if (Math.Sign(footPos.X) != Math.Sign(Dir))
|
||||
{
|
||||
//lift the foot at the back up a bit
|
||||
@@ -728,9 +723,16 @@ namespace Barotrauma
|
||||
{
|
||||
foot.DebugRefPos = colliderPos;
|
||||
foot.DebugTargetPos = footPos;
|
||||
MoveLimb(foot, footPos, CurrentGroundedParams.FootMoveStrength);
|
||||
FootIK(foot, footPos,
|
||||
CurrentGroundedParams.LegBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
float footMoveForce = CurrentGroundedParams.FootMoveStrength;
|
||||
float legBendTorque = CurrentGroundedParams.LegBendTorque;
|
||||
if (Crouching)
|
||||
{
|
||||
// Keeps the pose
|
||||
legBendTorque = 100;
|
||||
footMoveForce *= 2;
|
||||
}
|
||||
MoveLimb(foot, footPos, footMoveForce);
|
||||
FootIK(foot, footPos, legBendTorque, CurrentGroundedParams.FootTorque, CurrentGroundedParams.FootAngleInRadians);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,6 +762,12 @@ namespace Barotrauma
|
||||
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * CurrentGroundedParams.ArmMoveStrength);
|
||||
}
|
||||
}
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(foreArmType, hand.type);
|
||||
if (wrist != null)
|
||||
{
|
||||
hand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 100f * CurrentGroundedParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1008,6 +1016,12 @@ namespace Barotrauma
|
||||
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
|
||||
}
|
||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(LimbType.RightForearm, LimbType.RightHand);
|
||||
if (wrist != null)
|
||||
{
|
||||
rightHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * rightHand.Mass * 100f * CurrentSwimParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
|
||||
if (leftHand != null && !leftHand.Disabled)
|
||||
@@ -1021,6 +1035,12 @@ namespace Barotrauma
|
||||
speedMultiplier = Math.Min(speedMultiplier, 0.1f);
|
||||
}
|
||||
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
// Try to keep the wrist straight
|
||||
LimbJoint wrist = GetJointBetweenLimbs(LimbType.LeftForearm, LimbType.LeftHand);
|
||||
if (wrist != null)
|
||||
{
|
||||
leftHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * leftHand.Mass * 100f * CurrentSwimParams.HandMoveStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -665,6 +665,7 @@ namespace Barotrauma
|
||||
public float MaxVitality => CharacterHealth.MaxVitality;
|
||||
public float MaxHealth => MaxVitality;
|
||||
public AIState AIState => AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
||||
public bool IsLatched => AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached;
|
||||
|
||||
public float Bloodloss
|
||||
{
|
||||
@@ -2751,7 +2752,8 @@ namespace Barotrauma
|
||||
}
|
||||
else if (this != Controlled)
|
||||
{
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
wasRagdolled = IsRagdolled;
|
||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
}
|
||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
else if (allowRagdoll && (!IsRagdolled || !tooFastToUnragdoll))
|
||||
@@ -2765,13 +2767,18 @@ namespace Barotrauma
|
||||
{
|
||||
wasRagdolled = IsRagdolled;
|
||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.25f; }
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.5f; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasRagdolled && IsRagdolled && selfRagdolled)
|
||||
if (!wasRagdolled && IsRagdolled)
|
||||
{
|
||||
CheckTalents(AbilityEffectType.OnSelfRagdoll);
|
||||
if (selfRagdolled)
|
||||
{
|
||||
CheckTalents(AbilityEffectType.OnSelfRagdoll);
|
||||
}
|
||||
// currently does not work when you are stunned, like it should
|
||||
CheckTalents(AbilityEffectType.OnRagdoll);
|
||||
}
|
||||
|
||||
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
|
||||
@@ -3535,11 +3542,6 @@ namespace Barotrauma
|
||||
|
||||
if (Removed) { return new AttackResult(); }
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
}
|
||||
|
||||
float closestDistance = 0.0f;
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
@@ -3602,7 +3604,11 @@ namespace Barotrauma
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
if (attacker.TeamID == TeamID)
|
||||
{
|
||||
afflictions = afflictions.Where(a => !a.Prefab.IsBuff);
|
||||
if (!afflictions.Any()) { return new AttackResult(); }
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
@@ -4385,14 +4391,14 @@ namespace Barotrauma
|
||||
info.UnlockedTalents.Add(talentPrefab.Identifier);
|
||||
if (characterTalents.Any(t => t.Prefab == talentPrefab)) { return false; }
|
||||
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
#endif
|
||||
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
|
||||
characterTalent.ActivateTalent(addingFirstTime);
|
||||
characterTalents.Add(characterTalent);
|
||||
characterTalent.AddedThisRound = addingFirstTime;
|
||||
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
#endif
|
||||
if (addingFirstTime)
|
||||
{
|
||||
OnTalentGiven(talentPrefab.Identifier);
|
||||
|
||||
@@ -216,6 +216,17 @@ namespace Barotrauma
|
||||
|
||||
public HashSet<string> UnlockedTalents { get; private set; } = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Endocrine boosters can unlock talents outside the user's talent tree. This method is used to cull them from the selection
|
||||
/// </summary>
|
||||
public IEnumerable<string> GetUnlockedTalentsInTree()
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(Job.Prefab.Identifier, out TalentTree talentTree)) { return Enumerable.Empty<string>(); }
|
||||
|
||||
return UnlockedTalents.Where(t => talentTree.TalentIsInTree(t));
|
||||
}
|
||||
|
||||
|
||||
public int AdditionalTalentPoints { get; set; }
|
||||
|
||||
private Sprite _headSprite;
|
||||
@@ -1220,7 +1231,7 @@ namespace Barotrauma
|
||||
var experienceGainMultiplier = new AbilityValue(1f);
|
||||
if (isMissionExperience)
|
||||
{
|
||||
Character.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplier);
|
||||
Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplier);
|
||||
}
|
||||
experienceGainMultiplier.Value += Character.GetStatValue(StatTypes.ExperienceGainMultiplier);
|
||||
|
||||
@@ -1252,7 +1263,7 @@ namespace Barotrauma
|
||||
public int GetAvailableTalentPoints()
|
||||
{
|
||||
// hashset always has at least 1
|
||||
return Math.Max(GetTotalTalentPoints() - UnlockedTalents.Count, 0);
|
||||
return Math.Max(GetTotalTalentPoints() - GetUnlockedTalentsInTree().Count(), 0);
|
||||
}
|
||||
|
||||
public float GetProgressTowardsNextLevel()
|
||||
@@ -1297,6 +1308,8 @@ namespace Barotrauma
|
||||
|
||||
partial void OnExperienceChanged(int prevAmount, int newAmount);
|
||||
|
||||
partial void OnPermanentStatChanged(StatTypes statType);
|
||||
|
||||
public void Rename(string newName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newName)) { return; }
|
||||
@@ -1362,7 +1375,7 @@ namespace Barotrauma
|
||||
Job.Save(charElement);
|
||||
|
||||
XElement savedStatElement = new XElement("savedstatvalues");
|
||||
foreach (var statValuePair in savedStatValues)
|
||||
foreach (var statValuePair in SavedStatValues)
|
||||
{
|
||||
foreach (var savedStat in statValuePair.Value)
|
||||
{
|
||||
@@ -1708,26 +1721,42 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// This could maybe be a LookUp instead?
|
||||
private readonly Dictionary<StatTypes, List<SavedStatValue>> savedStatValues = new Dictionary<StatTypes, List<SavedStatValue>>();
|
||||
public readonly Dictionary<StatTypes, List<SavedStatValue>> SavedStatValues = new Dictionary<StatTypes, List<SavedStatValue>>();
|
||||
|
||||
public void ResetSavedStatValues()
|
||||
public void ClearSavedStatValues()
|
||||
{
|
||||
foreach (var savedStatValue in savedStatValues.SelectMany(s => s.Value))
|
||||
foreach (StatTypes statType in SavedStatValues.Keys)
|
||||
{
|
||||
if (savedStatValue.RemoveOnDeath)
|
||||
{
|
||||
savedStatValue.StatValue = 0f;
|
||||
}
|
||||
OnPermanentStatChanged(statType);
|
||||
}
|
||||
SavedStatValues.Clear();
|
||||
}
|
||||
|
||||
public void ClearSavedStatValues(StatTypes statType)
|
||||
{
|
||||
SavedStatValues.Remove(statType);
|
||||
OnPermanentStatChanged(statType);
|
||||
}
|
||||
|
||||
public void ResetSavedStatValue(string statIdentifier)
|
||||
{
|
||||
savedStatValues.SelectMany(s => s.Value).Where(s => s.StatIdentifier == statIdentifier).ForEach(v => v.StatValue = 0f);
|
||||
foreach (StatTypes statType in SavedStatValues.Keys)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (SavedStatValue savedStatValue in SavedStatValues[statType])
|
||||
{
|
||||
if (savedStatValue.StatIdentifier != statIdentifier) { continue; }
|
||||
if (MathUtils.NearlyEqual(savedStatValue.StatValue, 0.0f)) { continue; }
|
||||
savedStatValue.StatValue = 0.0f;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) { OnPermanentStatChanged(statType); }
|
||||
}
|
||||
}
|
||||
|
||||
public float GetSavedStatValue(StatTypes statType)
|
||||
{
|
||||
if (savedStatValues.TryGetValue(statType, out var statValues))
|
||||
if (SavedStatValues.TryGetValue(statType, out var statValues))
|
||||
{
|
||||
return statValues.Sum(v => v.StatValue);
|
||||
}
|
||||
@@ -1738,7 +1767,7 @@ namespace Barotrauma
|
||||
}
|
||||
public float GetSavedStatValue(StatTypes statType, string statIdentifier)
|
||||
{
|
||||
if (savedStatValues.TryGetValue(statType, out var statValues))
|
||||
if (SavedStatValues.TryGetValue(statType, out var statValues))
|
||||
{
|
||||
return statValues.Where(s => s.StatIdentifier.Equals(statIdentifier, StringComparison.OrdinalIgnoreCase)).Sum(v => v.StatValue);
|
||||
}
|
||||
@@ -1750,19 +1779,24 @@ namespace Barotrauma
|
||||
|
||||
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))
|
||||
if (!SavedStatValues.ContainsKey(statType))
|
||||
{
|
||||
savedStatValues.Add(statType, new List<SavedStatValue>());
|
||||
SavedStatValues.Add(statType, new List<SavedStatValue>());
|
||||
}
|
||||
|
||||
if (savedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
bool changed = false;
|
||||
if (SavedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
{
|
||||
float prevValue = savedStat.StatValue;
|
||||
savedStat.StatValue = setValue ? value : MathHelper.Min(savedStat.StatValue + value, maxValue);
|
||||
changed = !MathUtils.NearlyEqual(savedStat.StatValue, prevValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
savedStatValues[statType].Add(new SavedStatValue(statIdentifier, MathHelper.Min(value, maxValue), removeOnDeath, removeAfterRound));
|
||||
SavedStatValues[statType].Add(new SavedStatValue(statIdentifier, MathHelper.Min(value, maxValue), removeOnDeath, removeAfterRound));
|
||||
changed = true;
|
||||
}
|
||||
if (changed) { OnPermanentStatChanged(statType); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -11,6 +11,7 @@ namespace Barotrauma.Abilities
|
||||
protected readonly List<StatusEffect> statusEffects;
|
||||
|
||||
private readonly bool nearbyCharactersAppliesToSelf;
|
||||
private readonly bool nearbyCharactersAppliesToAllies;
|
||||
private readonly bool applyToSelected;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
@@ -20,6 +21,7 @@ namespace Barotrauma.Abilities
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
|
||||
nearbyCharactersAppliesToSelf = abilityElement.GetAttributeBool("nearbycharactersappliestoself", true);
|
||||
nearbyCharactersAppliesToAllies = abilityElement.GetAttributeBool("nearbycharactersappliestoallies", true);
|
||||
}
|
||||
|
||||
protected void ApplyEffectSpecific(Character targetCharacter)
|
||||
@@ -40,6 +42,10 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
targets.RemoveAll(c => c == Character);
|
||||
}
|
||||
if (!nearbyCharactersAppliesToAllies)
|
||||
{
|
||||
targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character));
|
||||
}
|
||||
statusEffect.SetUser(Character);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
|
||||
}
|
||||
@@ -56,17 +62,20 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (applyToSelected && Character.SelectedCharacter is Character selectedCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(selectedCharacter);
|
||||
}
|
||||
else if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
|
||||
else
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(targetCharacter);
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace Barotrauma.Abilities
|
||||
private readonly bool setValue;
|
||||
|
||||
//private readonly float maximumValue;
|
||||
|
||||
public override bool AllowClientSimulation => true;
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private readonly string statIdentifier;
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
public override bool AllowClientSimulation => true;
|
||||
|
||||
public CharacterAbilityResetPermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
if (closestCharacter.SelectedConstruction == null || !Character.SelectedConstruction.HasTag(tag)) { return; }
|
||||
if (closestCharacter.SelectedConstruction == null || !closestCharacter.SelectedConstruction.HasTag(tag)) { return; }
|
||||
|
||||
if (closestDistance < squaredMaxDistance)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool TalentIsInTree(string talentIdentifier)
|
||||
{
|
||||
return TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))).Any(c => c == talentIdentifier);
|
||||
}
|
||||
|
||||
public static void LoadFromFile(ContentFile file)
|
||||
{
|
||||
DebugConsole.Log("Loading talent tree: " + file.Path);
|
||||
|
||||
@@ -602,7 +602,7 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("giveaffliction", "giveaffliction [affliction name] [affliction strength] [character name] [limb type]: Add an affliction to a character. If the name parameter is omitted, the affliction is added to the controlled character.", (string[] args) =>
|
||||
commands.Add(new Command("giveaffliction", "giveaffliction [affliction name] [affliction strength] [character name] [limb type] [use relative strength]: Add an affliction to a character. If the name parameter is omitted, the affliction is added to the controlled character.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 2) { return; }
|
||||
|
||||
@@ -622,14 +622,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool relativeStrength = false;
|
||||
if (args.Length > 2)
|
||||
if (args.Length > 4)
|
||||
{
|
||||
bool.TryParse(args[2], out relativeStrength);
|
||||
bool.TryParse(args[4], out relativeStrength);
|
||||
}
|
||||
|
||||
Character targetCharacter = (relativeStrength || args.Length <= 2) ? Character.Controlled : FindMatchingCharacter(new string[] { args[2] });
|
||||
|
||||
|
||||
Character targetCharacter = args.Length <= 2 ? Character.Controlled : FindMatchingCharacter(new string[] { args[2] });
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
Limb targetLimb = targetCharacter.AnimController.MainLimb;
|
||||
@@ -1889,9 +1887,9 @@ namespace Barotrauma
|
||||
if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client))
|
||||
{
|
||||
#if DEBUG
|
||||
AddWarning("You're not permitted to use the command \"{matchingCommand.Name}\". Executing the command anyway because this is a debug build.");
|
||||
AddWarning($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\". Executing the command anyway because this is a debug build.");
|
||||
#else
|
||||
ThrowError("You're not permitted to use the command \"" + splitCommand[0].ToLowerInvariant() + "\"!");
|
||||
ThrowError($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\"!");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
OnReduceAffliction,
|
||||
OnAddDamageAffliction,
|
||||
OnSelfRagdoll,
|
||||
OnRagdoll,
|
||||
OnRoundEnd,
|
||||
OnAnyMissionCompleted,
|
||||
OnAllMissionsCompleted,
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Barotrauma
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.prefab))
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.Prefabs))
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
@@ -434,23 +434,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var suitablePrefabs = eventSet.EventPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.prefab.BiomeIdentifier) ||
|
||||
e.prefab.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
bool isPrefabSuitable(EventPrefab p)
|
||||
=> string.IsNullOrEmpty(p.BiomeIdentifier) ||
|
||||
p.BiomeIdentifier.Equals(level.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var suitablePrefabSubsets = eventSet.EventPrefabs
|
||||
.FindAll(p => p.Prefabs.Any(isPrefabSuitable));
|
||||
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
if (suitablePrefabs.Count > 0)
|
||||
if (suitablePrefabSubsets.Count > 0)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(suitablePrefabs);
|
||||
var unusedEvents = suitablePrefabSubsets.ToList();
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => CalculateCommonness(e.prefab, e.commonness) <= 0.0f)) { break; }
|
||||
(EventPrefab eventPrefab, float commonness, float probability) = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e.prefab, e.commonness)).ToList(), rand);
|
||||
if (eventPrefab != null && rand.NextDouble() <= probability)
|
||||
if (unusedEvents.All(e => e.Prefabs.All(p => CalculateCommonness(p, e.Commonness) <= 0.0f))) { break; }
|
||||
EventSet.SubEventPrefab subEventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Prefabs.Max(p => CalculateCommonness(p, e.Commonness))).ToList(), rand);
|
||||
(IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) = subEventPrefab;
|
||||
if (eventPrefabs != null && rand.NextDouble() <= probability)
|
||||
{
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -465,7 +473,7 @@ namespace Barotrauma
|
||||
selectedEvents.Add(eventSet, new List<Event>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
unusedEvents.Remove((eventPrefab, commonness, probability));
|
||||
unusedEvents.Remove(subEventPrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,9 +488,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ((EventPrefab eventPrefab, float commonness, float probability) in suitablePrefabs)
|
||||
foreach ((IEnumerable<EventPrefab> eventPrefabs, float commonness, float probability) in suitablePrefabSubsets)
|
||||
{
|
||||
if (rand.NextDouble() > probability) { continue; }
|
||||
|
||||
var finalPrefabs = eventPrefabs.Where(isPrefabSuitable).ToArray();
|
||||
var finalPrefabCommonnesses = finalPrefabs.Select(p => p.Commonness).ToArray();
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(finalPrefabs, finalPrefabCommonnesses, rand);
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
@@ -866,7 +878,7 @@ namespace Barotrauma
|
||||
|
||||
private float CalculateDistanceTraveled()
|
||||
{
|
||||
if (level == null) { return 0.0f; }
|
||||
if (level == null || pathFinder == null) { return 0.0f; }
|
||||
var refEntity = GetRefEntity();
|
||||
if (refEntity == null) { return 0.0f; }
|
||||
Vector2 target = ConvertUnits.ToSimUnits(level.EndPosition);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma
|
||||
BiomeIdentifier = ConfigElement.GetAttributeString("biome", string.Empty);
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
Probability = Math.Clamp(element.GetAttributeFloat(1.0f, "probability", "spawnprobability"), 0, 1);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", EventType != typeof(ScriptedEvent));
|
||||
|
||||
UnlockPathEvent = element.GetAttributeBool("unlockpathevent", false);
|
||||
UnlockPathTooltip = element.GetAttributeString("unlockpathtooltip", "lockedpathtooltip");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
@@ -48,10 +49,10 @@ namespace Barotrauma
|
||||
List<EventPrefab> eventPrefabs = new List<EventPrefab>(PrefabList);
|
||||
foreach (var eventSet in List)
|
||||
{
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(eventSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
foreach (var childSet in eventSet.ChildSets)
|
||||
{
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.Select(ep => ep.prefab));
|
||||
eventPrefabs.AddRange(childSet.EventPrefabs.SelectMany(ep => ep.Prefabs));
|
||||
}
|
||||
}
|
||||
return eventPrefabs;
|
||||
@@ -98,7 +99,48 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
public readonly List<(EventPrefab prefab, float commonness, float probability)> EventPrefabs;
|
||||
public struct SubEventPrefab
|
||||
{
|
||||
public SubEventPrefab(string debugIdentifier, string[] prefabIdentifiers, float? commonness, float? probability)
|
||||
{
|
||||
EventPrefab tryFindPrefab(string id)
|
||||
{
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{id}\".");
|
||||
}
|
||||
return prefab;
|
||||
}
|
||||
|
||||
this.Prefabs = prefabIdentifiers
|
||||
.Select(tryFindPrefab)
|
||||
.Where(p => p != null)
|
||||
.ToImmutableArray();
|
||||
this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).Max();
|
||||
this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).Max();
|
||||
}
|
||||
|
||||
public SubEventPrefab(EventPrefab prefab, float commonness, float probability)
|
||||
{
|
||||
Prefabs = prefab.ToEnumerable().ToImmutableArray();
|
||||
Commonness = commonness;
|
||||
Probability = probability;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<EventPrefab> Prefabs;
|
||||
public readonly float Commonness;
|
||||
public readonly float Probability;
|
||||
|
||||
public void Deconstruct(out IEnumerable<EventPrefab> prefabs, out float commonness, out float probability)
|
||||
{
|
||||
prefabs = Prefabs;
|
||||
commonness = Commonness;
|
||||
probability = Probability;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly List<SubEventPrefab> EventPrefabs;
|
||||
|
||||
public readonly List<EventSet> ChildSets;
|
||||
|
||||
@@ -112,7 +154,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugIdentifier = element.GetAttributeString("identifier", null) ?? debugIdentifier;
|
||||
Commonness = new Dictionary<string, float>();
|
||||
EventPrefabs = new List<(EventPrefab prefab, float commonness, float probability)>();
|
||||
EventPrefabs = new List<SubEventPrefab>();
|
||||
ChildSets = new List<EventSet>();
|
||||
|
||||
BiomeIdentifier = element.GetAttributeString("biome", string.Empty);
|
||||
@@ -178,23 +220,20 @@ namespace Barotrauma
|
||||
//an element with just an identifier = reference to an event prefab
|
||||
if (!subElement.HasElements && subElement.Attributes().First().Name.ToString().Equals("identifier", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string identifier = subElement.GetAttributeString("identifier", "");
|
||||
var prefab = PrefabList.Find(p => p.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event set \"{debugIdentifier}\" - could not find the event prefab \"{identifier}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
float commonness = subElement.GetAttributeFloat("commonness", prefab.Commonness);
|
||||
float probability = subElement.GetAttributeFloat("probability", prefab.Probability);
|
||||
EventPrefabs.Add((prefab, commonness, probability));
|
||||
}
|
||||
string[] identifiers = subElement.GetAttributeStringArray("identifier", Array.Empty<string>());
|
||||
|
||||
float commonness = subElement.GetAttributeFloat("commonness", -1f);
|
||||
float probability = subElement.GetAttributeFloat("probability", -1f);
|
||||
EventPrefabs.Add(new SubEventPrefab(
|
||||
debugIdentifier,
|
||||
identifiers,
|
||||
commonness>=0f ? commonness : (float?)null,
|
||||
probability>=0f ? probability : (float?)null));
|
||||
}
|
||||
else
|
||||
{
|
||||
var prefab = new EventPrefab(subElement);
|
||||
EventPrefabs.Add((prefab, prefab.Commonness, prefab.Probability));
|
||||
EventPrefabs.Add(new SubEventPrefab(prefab, prefab.Commonness, prefab.Probability));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -346,13 +385,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (thisSet.ChooseRandom)
|
||||
{
|
||||
var unusedEvents = new List<(EventPrefab prefab, float commonness, float probability)>(thisSet.EventPrefabs);
|
||||
var unusedEvents = thisSet.EventPrefabs.ToList();
|
||||
for (int i = 0; i < thisSet.EventCount; i++)
|
||||
{
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.prefab != null)
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.Prefabs.Any(p => p != null))
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
unusedEvents.Remove(eventPrefab);
|
||||
}
|
||||
}
|
||||
@@ -361,7 +400,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var eventPrefab in thisSet.EventPrefabs)
|
||||
{
|
||||
AddEvent(stats, eventPrefab.prefab);
|
||||
AddEvents(stats, eventPrefab.Prefabs);
|
||||
}
|
||||
}
|
||||
foreach (var childSet in thisSet.ChildSets)
|
||||
@@ -370,6 +409,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
static void AddEvents(EventDebugStats stats, IEnumerable<EventPrefab> eventPrefabs)
|
||||
=> eventPrefabs.ForEach(p => AddEvent(stats, p));
|
||||
|
||||
static void AddEvent(EventDebugStats stats, EventPrefab eventPrefab)
|
||||
{
|
||||
if (eventPrefab.EventType == typeof(MonsterEvent))
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
character.Info.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
character.Info?.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#else
|
||||
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
||||
|
||||
@@ -218,12 +218,12 @@ namespace Barotrauma
|
||||
return validContainers;
|
||||
}
|
||||
|
||||
private static readonly float[] qualityCommonnesses = new float[Quality.MaxQuality + 1]
|
||||
private static readonly (int quality, float commonness)[] qualityCommonnesses = new (int quality, float commonness)[Quality.MaxQuality + 1]
|
||||
{
|
||||
0.85f,
|
||||
0.125f,
|
||||
0.0225f,
|
||||
0.0025f,
|
||||
(0, 0.85f),
|
||||
(1, 0.125f),
|
||||
(2, 0.0225f),
|
||||
(3, 0.0025f),
|
||||
};
|
||||
|
||||
private static List<Item> SpawnItem(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer, float difficultyModifier)
|
||||
@@ -243,20 +243,15 @@ namespace Barotrauma
|
||||
containers.Remove(validContainer.Key);
|
||||
break;
|
||||
}
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab)) { break; }
|
||||
|
||||
int quality = 0;
|
||||
float qualityCommmonnessSum = qualityCommonnesses.Sum();
|
||||
float randomNumber = Rand.Range(0f, qualityCommmonnessSum, Rand.RandSync.Server);
|
||||
for (int k = qualityCommonnesses.Length - 1; k >= 0; k--)
|
||||
{
|
||||
if (randomNumber < qualityCommonnesses[k])
|
||||
{
|
||||
quality = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.prefab == itemPrefab);
|
||||
int quality =
|
||||
existingItem?.Quality ??
|
||||
ToolBox.SelectWeightedRandom(
|
||||
qualityCommonnesses.Select(q => q.quality).ToList(),
|
||||
qualityCommonnesses.Select(q => q.commonness).ToList(),
|
||||
Rand.RandSync.Server);
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = validContainer.Key.Item.SpawnedInOutpost,
|
||||
|
||||
+1
-62
@@ -1,6 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -29,65 +27,6 @@ namespace Barotrauma
|
||||
private XElement healthData;
|
||||
public XElement OrderData { get; private set; }
|
||||
|
||||
partial void InitProjSpecific(Client client);
|
||||
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false)
|
||||
{
|
||||
Name = client.Name;
|
||||
InitProjSpecific(client);
|
||||
|
||||
healthData = new XElement("health");
|
||||
client.Character?.CharacterHealth?.Save(healthData);
|
||||
if (giveRespawnPenaltyAffliction)
|
||||
{
|
||||
var respawnPenaltyAffliction = RespawnManager.GetRespawnPenaltyAffliction();
|
||||
healthData.Add(new XElement("Affliction",
|
||||
new XAttribute("identifier", respawnPenaltyAffliction.Identifier),
|
||||
new XAttribute("strength", respawnPenaltyAffliction.Strength.ToString("G", CultureInfo.InvariantCulture))));
|
||||
}
|
||||
if (client.Character?.Inventory != null)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
Character.SaveInventory(client.Character.Inventory, itemData);
|
||||
}
|
||||
OrderData = new XElement("orders");
|
||||
if (client.Character != null)
|
||||
{
|
||||
CharacterInfo.SaveOrderData(client.Character.Info, OrderData);
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterCampaignData(XElement element)
|
||||
{
|
||||
Name = element.GetAttributeString("name", "Unnamed");
|
||||
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
|
||||
string steamID = element.GetAttributeString("steamid", "");
|
||||
if (!string.IsNullOrEmpty(steamID))
|
||||
{
|
||||
ulong.TryParse(steamID, out ulong parsedID);
|
||||
SteamID = parsedID;
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "character":
|
||||
case "characterinfo":
|
||||
CharacterInfo = new CharacterInfo(subElement);
|
||||
break;
|
||||
case "inventory":
|
||||
itemData = subElement;
|
||||
break;
|
||||
case "health":
|
||||
healthData = subElement;
|
||||
break;
|
||||
case "orders":
|
||||
OrderData = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh(Character character)
|
||||
{
|
||||
healthData = new XElement("health");
|
||||
|
||||
@@ -141,10 +141,10 @@ namespace Barotrauma
|
||||
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
|
||||
}
|
||||
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition, int? quality = null)
|
||||
{
|
||||
return
|
||||
base.CanBePutInSlot(itemPrefab, i, condition) &&
|
||||
base.CanBePutInSlot(itemPrefab, i, condition, quality) &&
|
||||
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
string[] allSpecies = SpeciesName.Split(',');
|
||||
string species = allSpecies.GetRandom().Trim();
|
||||
Entity.Spawner.AddToSpawnQueue(species, pos);
|
||||
Entity.Spawner?.AddToSpawnQueue(species, pos);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
@@ -282,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
pos -= sub.Position;
|
||||
}
|
||||
|
||||
Entity.Spawner.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,10 +642,8 @@ namespace Barotrauma.Items.Components
|
||||
item.Drop(character);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
|
||||
}
|
||||
AttachToWall();
|
||||
}
|
||||
|
||||
AttachToWall();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -291,13 +291,16 @@ namespace Barotrauma.Items.Components
|
||||
int index = Inventory.FindIndex(containedItem);
|
||||
if (index >= 0 && index < slotRestrictions.Length)
|
||||
{
|
||||
RelatedItem ri = slotRestrictions[index].ContainableItems?.Find(ci => ci.MatchesItem(containedItem));
|
||||
if (ri != null)
|
||||
if (slotRestrictions[index].ContainableItems != null)
|
||||
{
|
||||
activeContainedItems.RemoveAll(i => i.Item == containedItem);
|
||||
foreach (StatusEffect effect in ri.statusEffects)
|
||||
foreach (var containableItem in slotRestrictions[index].ContainableItems)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, ri.ExcludeBroken));
|
||||
if (!containableItem.MatchesItem(containedItem)) { continue; }
|
||||
foreach (StatusEffect effect in containableItem.statusEffects)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -34,6 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
public OxygenGenerator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//randomize update timer so all oxygen generators don't update at the same time
|
||||
ventUpdateTimer = Rand.Range(0.0f, VentUpdateInterval);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -78,6 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
totalHullVolume = 0.0f;
|
||||
ventList ??= new List<(Vent vent, float hullVolume)>();
|
||||
ventList.Clear();
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
@@ -87,13 +90,14 @@ namespace Barotrauma.Items.Components
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
if (vent?.Item.CurrentHull == null) { continue; }
|
||||
|
||||
totalHullVolume += vent.Item.CurrentHull.Volume;
|
||||
ventList.Add((vent, vent.Item.CurrentHull.Volume));
|
||||
}
|
||||
|
||||
for (int i = 0; i < ventList.Count; i++)
|
||||
{
|
||||
Vent vent = ventList[i].vent;
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 5, ignoreClosedGaps: true))
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: false, searchDepth: 3, ignoreClosedGaps: true))
|
||||
{
|
||||
//another vent in the connected hull -> don't add it to this vent's total hull volume
|
||||
if (ventList.Any(v => v.vent != vent && v.vent.Item.CurrentHull == connectedHull)) { continue; }
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null, int? quality = null)
|
||||
{
|
||||
if (itemPrefab == null) { return false; }
|
||||
if (items.Count > 0)
|
||||
@@ -82,6 +82,11 @@ namespace Barotrauma
|
||||
if (items.Any(it => !it.IsFullCondition)) { return false; }
|
||||
}
|
||||
|
||||
if (quality.HasValue)
|
||||
{
|
||||
if (items[0].Quality != quality.Value) { return false; }
|
||||
}
|
||||
|
||||
if (items[0].Prefab.Identifier != itemPrefab.Identifier ||
|
||||
items.Count + 1 > itemPrefab.MaxStackSize)
|
||||
{
|
||||
@@ -172,6 +177,11 @@ namespace Barotrauma
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
public void RemoveWhere(Func<Item, bool> predicate)
|
||||
{
|
||||
items.RemoveAll(it => predicate(it));
|
||||
}
|
||||
|
||||
public bool Any()
|
||||
{
|
||||
return items.Count > 0;
|
||||
@@ -422,16 +432,16 @@ namespace Barotrauma
|
||||
return slots[i].CanBePut(item, ignoreCondition);
|
||||
}
|
||||
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
|
||||
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null, int? quality = null)
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (CanBePutInSlot(itemPrefab, i, condition)) { return true; }
|
||||
if (CanBePutInSlot(itemPrefab, i, condition, quality)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition = null)
|
||||
public virtual bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition = null, int? quality = null)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
return slots[i].CanBePut(itemPrefab, condition);
|
||||
@@ -503,7 +513,7 @@ namespace Barotrauma
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return
|
||||
return
|
||||
TrySwapping(i, item, user, createNetworkEvent, swapWholeStack: true) ||
|
||||
TrySwapping(i, item, user, createNetworkEvent, swapWholeStack: false);
|
||||
}
|
||||
@@ -776,11 +786,17 @@ namespace Barotrauma
|
||||
{
|
||||
for (int j = 0; j < capacity; j++)
|
||||
{
|
||||
if (slots[j].Contains(item)) { slots[j].RemoveAllItems(); };
|
||||
if (slots[j].Contains(item))
|
||||
{
|
||||
slots[j].RemoveWhere(it => existingItems.Contains(it) || stackedItems.Contains(it));
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < otherInventory.capacity; j++)
|
||||
{
|
||||
if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault())) { otherInventory.slots[j].RemoveAllItems(); }
|
||||
if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault()))
|
||||
{
|
||||
otherInventory.slots[j].RemoveWhere(it => existingItems.Contains(it) || stackedItems.Contains(it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@ namespace Barotrauma
|
||||
return item != null && slots[i].CanBePut(item, ignoreCondition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition, int? quality = null)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return false; }
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition, quality) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
|
||||
@@ -864,6 +864,16 @@ namespace Barotrauma
|
||||
foreach (AbyssIsland island in AbyssIslands)
|
||||
{
|
||||
island.Area = new Rectangle(borders.Width - island.Area.Right, island.Area.Y, island.Area.Width, island.Area.Height);
|
||||
foreach (var cell in island.Cells)
|
||||
{
|
||||
if (!mirroredSites.Contains(cell.Site))
|
||||
{
|
||||
if (cell.Site.Coord.X % GridCellSize < 1.0f &&
|
||||
cell.Site.Coord.X % GridCellSize >= 0.0f) { cell.Site.Coord.X += 1.0f; }
|
||||
cell.Site.Coord.X = borders.Width - cell.Site.Coord.X;
|
||||
mirroredSites.Add(cell.Site);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ruinPositions.Count; i++)
|
||||
@@ -1972,7 +1982,7 @@ namespace Barotrauma
|
||||
ConvertUnits.ToSimUnits(entranceWayPoint.WorldPosition), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) == null;
|
||||
});
|
||||
if (closestWp == null) { continue; }
|
||||
entranceWayPoint.ConnectTo(closestWp);
|
||||
ConnectWaypoints(entranceWayPoint, closestWp, outSideWaypointInterval);
|
||||
}
|
||||
|
||||
//create a waypoint path from the ruin to the closest tunnel
|
||||
|
||||
@@ -1023,7 +1023,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (playSound && damageAmount > 0)
|
||||
{
|
||||
SoundPlayer.PlayDamageSound(attack.StructureSoundType, damageAmount, worldPosition, tags: Tags);
|
||||
string damageSound = Prefab.DamageSound;
|
||||
if (string.IsNullOrWhiteSpace(damageSound))
|
||||
{
|
||||
damageSound = attack.StructureSoundType;
|
||||
}
|
||||
SoundPlayer.PlayDamageSound(damageSound, damageAmount, worldPosition, tags: Tags);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -167,6 +167,9 @@ namespace Barotrauma
|
||||
private set { size = value; }
|
||||
}
|
||||
|
||||
[Serialize("", true)]
|
||||
public string DamageSound { get; private set; }
|
||||
|
||||
public Vector2 ScaledSize => size * Scale;
|
||||
|
||||
protected Vector2 textureScale = Vector2.One;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Barotrauma.Networking
|
||||
UpdateExperience,
|
||||
UpdateTalents,
|
||||
UpdateMoney,
|
||||
UpdatePermanentStats,
|
||||
}
|
||||
|
||||
public readonly Entity Entity;
|
||||
|
||||
@@ -1284,68 +1284,66 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
foreach (int giveExperience in giveExperiences)
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
targetCharacter?.Info?.GiveExperience(giveExperience);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// these effects do not need to be run clientside, as they are replicated from server to clients anyway
|
||||
|
||||
if (giveSkills.Any())
|
||||
{
|
||||
foreach ((string skillIdentifier, float amount) in giveSkills)
|
||||
foreach (int giveExperience in giveExperiences)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
if (skillIdentifier?.ToLowerInvariant() == "randomskill")
|
||||
targetCharacter?.Info?.GiveExperience(giveExperience);
|
||||
}
|
||||
}
|
||||
|
||||
if (giveSkills.Any())
|
||||
{
|
||||
foreach ((string skillIdentifier, float amount) in giveSkills)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
if (skillIdentifier?.ToLowerInvariant() == "randomskill")
|
||||
{
|
||||
// don't let clients simulate random skill gain
|
||||
continue;
|
||||
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount);
|
||||
|
||||
string GetRandomSkill()
|
||||
{
|
||||
return targetCharacter.Info?.Job?.Skills.Select(s => s.Identifier).GetRandom();
|
||||
}
|
||||
}
|
||||
targetCharacter.Info?.IncreaseSkillLevel(GetRandomSkill(), amount);
|
||||
|
||||
string GetRandomSkill()
|
||||
else
|
||||
{
|
||||
return targetCharacter.Info?.Job?.Skills.Select(s => s.Identifier).GetRandom();
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier?.ToLowerInvariant(), amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (giveTalentInfos.Any() && (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient))
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { continue; }
|
||||
// for the sake of technical simplicity, for now do not allow talents to be given if the character could unlock them in their talent tree as well
|
||||
IEnumerable<string> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
IEnumerable<string> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
if (giveTalentInfos.Any())
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { continue; }
|
||||
// for the sake of technical simplicity, for now do not allow talents to be given if the character could unlock them in their talent tree as well
|
||||
IEnumerable<string> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandom(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string talent in viableTalents)
|
||||
IEnumerable<string> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandom(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string talent in viableTalents)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user