Unstable 1.2.1.0
This commit is contained in:
@@ -262,7 +262,8 @@ namespace Barotrauma
|
||||
|
||||
if (aiElements.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in file \"" + c.Params.File + "\" - no AI element found.");
|
||||
DebugConsole.ThrowError("Error in file \"" + c.Params.File + "\" - no AI element found.",
|
||||
contentPackage: c.Prefab?.ContentPackage);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
insideSteering = new IndoorsSteeringManager(this, false, false);
|
||||
return;
|
||||
@@ -330,7 +331,8 @@ namespace Barotrauma
|
||||
_aiParams = Character.Params.AI;
|
||||
if (_aiParams == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"No AI Params defined for {Character.SpeciesName}. AI disabled.");
|
||||
DebugConsole.ThrowError($"No AI Params defined for {Character.SpeciesName}. AI disabled.",
|
||||
contentPackage: Character.Prefab.ContentPackage);
|
||||
Enabled = false;
|
||||
_aiParams = new CharacterParams.AIParams(null, Character.Params);
|
||||
}
|
||||
@@ -2503,7 +2505,8 @@ namespace Barotrauma
|
||||
Limb mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
|
||||
if (mouthLimb == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (No head limb defined)");
|
||||
DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (No head limb defined)",
|
||||
contentPackage: Character.Prefab.ContentPackage);
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
@@ -2540,7 +2543,11 @@ namespace Barotrauma
|
||||
item.body.LinearVelocity -= velocity * 0.25f;
|
||||
bool wasBroken = item.Condition <= 0.0f;
|
||||
item.LastEatenTime = (float)Timing.TotalTimeUnpaused;
|
||||
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.02f * Character.Params.EatingSpeed), deltaTime);
|
||||
item.AddDamage(Character,
|
||||
item.WorldPosition,
|
||||
new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.02f * Character.Params.EatingSpeed),
|
||||
impulseDirection: Vector2.Zero,
|
||||
deltaTime);
|
||||
Character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
|
||||
if (item.Condition <= 0.0f)
|
||||
{
|
||||
|
||||
@@ -167,10 +167,6 @@ namespace Barotrauma
|
||||
|
||||
public HumanAIController(Character c) : base(c)
|
||||
{
|
||||
if (!c.IsHuman)
|
||||
{
|
||||
throw new Exception($"Tried to create a human ai controller for a non-human: {c.SpeciesName}!");
|
||||
}
|
||||
insideSteering = new IndoorsSteeringManager(this, true, false);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
@@ -1800,7 +1796,7 @@ namespace Barotrauma
|
||||
if (!TriggerSecurity(otherHumanAI, combatMode))
|
||||
{
|
||||
// Else call the others
|
||||
foreach (Character security in Character.CharacterList.Where(c => c.TeamID == otherCharacter.TeamID).OrderByDescending(c => Vector2.DistanceSquared(character.WorldPosition, c.WorldPosition)))
|
||||
foreach (Character security in Character.CharacterList.Where(c => c.TeamID == otherCharacter.TeamID).OrderBy(c => Vector2.DistanceSquared(character.WorldPosition, c.WorldPosition)))
|
||||
{
|
||||
if (!TriggerSecurity(security.AIController as HumanAIController, combatMode))
|
||||
{
|
||||
@@ -1861,16 +1857,11 @@ namespace Barotrauma
|
||||
}
|
||||
if (!someoneSpoke)
|
||||
{
|
||||
if (!item.StolenDuringRound &&
|
||||
Level.Loaded?.Type == LevelData.LevelType.Outpost &&
|
||||
GameMain.GameSession?.Campaign?.Map?.CurrentLocation != null)
|
||||
if (!item.StolenDuringRound)
|
||||
{
|
||||
var reputationLoss = MathHelper.Clamp(
|
||||
(item.Prefab.GetMinPrice() ?? 0) * Reputation.ReputationLossPerStolenItemPrice,
|
||||
Reputation.MinReputationLossPerStolenItem, Reputation.MaxReputationLossPerStolenItem);
|
||||
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation?.AddReputation(-reputationLoss);
|
||||
ApplyStealingReputationLoss(item);
|
||||
item.StolenDuringRound = true;
|
||||
}
|
||||
item.StolenDuringRound = true;
|
||||
otherCharacter.Speak(TextManager.Get("dialogstealwarning").Value, null, Rand.Range(0.5f, 1.0f), "thief".ToIdentifier(), 10.0f);
|
||||
someoneSpoke = true;
|
||||
#if CLIENT
|
||||
@@ -1881,7 +1872,7 @@ namespace Barotrauma
|
||||
if (!TriggerSecurity(otherHumanAI))
|
||||
{
|
||||
// Else call the others
|
||||
foreach (Character security in Character.CharacterList.Where(c => c.TeamID == otherCharacter.TeamID).OrderByDescending(c => Vector2.DistanceSquared(thief.WorldPosition, c.WorldPosition)))
|
||||
foreach (Character security in Character.CharacterList.Where(c => c.TeamID == otherCharacter.TeamID).OrderBy(c => Vector2.DistanceSquared(thief.WorldPosition, c.WorldPosition)))
|
||||
{
|
||||
if (TriggerSecurity(security.AIController as HumanAIController))
|
||||
{
|
||||
@@ -1902,6 +1893,10 @@ namespace Barotrauma
|
||||
if (humanAI == null) { return false; }
|
||||
if (!humanAI.Character.IsSecurity) { return false; }
|
||||
if (humanAI.ObjectiveManager.IsCurrentObjective<AIObjectiveCombat>()) { return false; }
|
||||
if (humanAI.ObjectiveManager.GetObjective<AIObjectiveFindThieves>() is { } findThieves)
|
||||
{
|
||||
findThieves.InspectEveryone();
|
||||
}
|
||||
humanAI.AddCombatObjective(AIObjectiveCombat.CombatMode.Arrest, thief, delay: GetReactionTime(),
|
||||
abortCondition: obj => thief.Inventory.FindItem(it => it != null && it.StolenDuringRound, true) == null,
|
||||
onAbort: () =>
|
||||
@@ -1919,6 +1914,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyStealingReputationLoss(Item item)
|
||||
{
|
||||
if (Level.Loaded?.Type == LevelData.LevelType.Outpost &&
|
||||
GameMain.GameSession?.Campaign?.Map?.CurrentLocation != null)
|
||||
{
|
||||
var reputationLoss = MathHelper.Clamp(
|
||||
(item.Prefab.GetMinPrice() ?? 0) * Reputation.ReputationLossPerStolenItemPrice,
|
||||
Reputation.MinReputationLossPerStolenItem, Reputation.MaxReputationLossPerStolenItem);
|
||||
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation?.AddReputation(-reputationLoss);
|
||||
}
|
||||
}
|
||||
|
||||
// 0.225 - 0.375
|
||||
private static float GetReactionTime() => reactionTime * Rand.Range(0.75f, 1.25f);
|
||||
|
||||
|
||||
@@ -197,17 +197,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method allows multiple subobjectives of same type. Use with caution.
|
||||
/// </summary>
|
||||
public void AddSubObjectiveInQueue(AIObjective objective)
|
||||
{
|
||||
if (!subObjectives.Contains(objective))
|
||||
{
|
||||
subObjectives.Add(objective);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveSubObjective<T>(ref T objective) where T : AIObjective
|
||||
{
|
||||
if (objective != null)
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCheckStolenItems : AIObjective
|
||||
{
|
||||
public override Identifier Identifier { get; set; } = "check stolen items".ToIdentifier();
|
||||
public override bool AllowOutsideSubmarine => false;
|
||||
public override bool AllowInAnySub => false;
|
||||
|
||||
public float FindStolenItemsProbability = 1.0f;
|
||||
|
||||
enum State
|
||||
{
|
||||
GotoTarget,
|
||||
Inspect,
|
||||
Warn,
|
||||
Done
|
||||
}
|
||||
|
||||
private float inspectDelay;
|
||||
private float warnDelay;
|
||||
|
||||
private State currentState;
|
||||
|
||||
public readonly Character TargetCharacter;
|
||||
|
||||
private AIObjectiveGoTo? goToObjective;
|
||||
|
||||
private readonly List<Item> stolenItems = new List<Item>();
|
||||
|
||||
public AIObjectiveCheckStolenItems(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1) :
|
||||
base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
TargetCharacter = targetCharacter;
|
||||
inspectDelay = 5.0f;
|
||||
warnDelay = 5.0f;
|
||||
}
|
||||
|
||||
public override bool IsLoop
|
||||
{
|
||||
get => false;
|
||||
set => throw new Exception("Trying to set the value for IsLoop from: " + Environment.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific() => false;
|
||||
|
||||
protected override float GetPriority()
|
||||
{
|
||||
if (!Abandon && !IsCompleted && objectiveManager.IsOrder(this))
|
||||
{
|
||||
Priority = objectiveManager.GetOrderPriority(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Priority = AIObjectiveManager.LowestOrderPriority - 1;
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public void ForceComplete()
|
||||
{
|
||||
IsCompleted = true;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case State.GotoTarget:
|
||||
TryAddSubObjective(ref goToObjective,
|
||||
constructor: () =>
|
||||
{
|
||||
return new AIObjectiveGoTo(TargetCharacter, character, objectiveManager, repeat: false)
|
||||
{
|
||||
SpeakIfFails = false
|
||||
};
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
currentState = State.Inspect;
|
||||
stolenItems.Clear();
|
||||
TargetCharacter.Inventory.FindAllItems(it => it.SpawnedInCurrentOutpost && !it.AllowStealing, recursive: true, stolenItems);
|
||||
character.Speak(TextManager.Get("dialogcheckstolenitems").Value);
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
Abandon = true;
|
||||
});
|
||||
break;
|
||||
case State.Inspect:
|
||||
Inspect(deltaTime);
|
||||
break;
|
||||
case State.Warn:
|
||||
Warn(deltaTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Inspect(float deltaTime)
|
||||
{
|
||||
if (inspectDelay > 0.0f)
|
||||
{
|
||||
character.SelectCharacter(TargetCharacter);
|
||||
inspectDelay -= deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stolenItems.Any() &&
|
||||
Rand.Range(0.0f, 1.0f, Rand.RandSync.Unsynced) < FindStolenItemsProbability)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcheckstolenitems.warn").Value);
|
||||
currentState = State.Warn;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcheckstolenitems.nostolenitems").Value);
|
||||
currentState = State.Done;
|
||||
IsCompleted = true;
|
||||
}
|
||||
character.DeselectCharacter();
|
||||
}
|
||||
|
||||
private void Warn(float deltaTime)
|
||||
{
|
||||
if (warnDelay > 0.0f)
|
||||
{
|
||||
warnDelay -= deltaTime;
|
||||
return;
|
||||
}
|
||||
var stolenItemsOnCharacter = stolenItems.Where(it => it.GetRootInventoryOwner() == TargetCharacter);
|
||||
if (stolenItemsOnCharacter.Any())
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcheckstolenitems.arrest").Value);
|
||||
HumanAIController.AddCombatObjective(AIObjectiveCombat.CombatMode.Arrest, TargetCharacter);
|
||||
foreach (var stolenItem in stolenItemsOnCharacter)
|
||||
{
|
||||
HumanAIController.ApplyStealingReputationLoss(stolenItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcheckstolenitems.comply").Value);
|
||||
}
|
||||
foreach (var item in stolenItems)
|
||||
{
|
||||
HumanAIController.ObjectiveManager.AddObjective(new AIObjectiveGetItem(character, item, objectiveManager, equip: false)
|
||||
{
|
||||
BasePriority = 10
|
||||
});
|
||||
}
|
||||
currentState = State.Done;
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
#nullable enable
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFindThieves : AIObjectiveLoop<Character>
|
||||
{
|
||||
public override Identifier Identifier { get; set; } = "find thieves".ToIdentifier();
|
||||
protected override float IgnoreListClearInterval => 30;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
protected override float TargetUpdateTimeMultiplier => 1.0f;
|
||||
|
||||
const float DefaultInspectDistance = 200.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How close the NPC must be to the target to the inspect them? You can use high values to make the NPC
|
||||
/// systematically go through targets no matter where they are, and low values to check targets they happen to come across.
|
||||
/// </summary>
|
||||
public float InspectDistance = DefaultInspectDistance;
|
||||
|
||||
private float? overrideInspectProbability;
|
||||
/// <summary>
|
||||
/// Chance of inspecting a valid target. The NPC won't try to inspect that target again for <see cref="inspectionInterval"/>
|
||||
/// regardless if the target is inspected or not.
|
||||
/// </summary>
|
||||
public float InspectProbability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (overrideInspectProbability.HasValue)
|
||||
{
|
||||
return overrideInspectProbability.Value;
|
||||
}
|
||||
if (GameMain.GameSession?.Campaign is { } campaign)
|
||||
{
|
||||
if (campaign.Map?.CurrentLocation?.Reputation is { } reputation)
|
||||
{
|
||||
return MathHelper.Lerp(
|
||||
campaign.Settings.MaxStolenItemInspectionProbability,
|
||||
campaign.Settings.MinStolenItemInspectionProbability,
|
||||
reputation.NormalizedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When did the character last inspect whether some other character has stolen items on them?
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Character, double> lastInspectionTimes = new Dictionary<Character, double>();
|
||||
|
||||
private readonly float inspectionInterval = 120.0f;
|
||||
|
||||
public AIObjectiveFindThieves(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
protected override bool Filter(Character target)
|
||||
{
|
||||
if (!IsValidTarget(target, character)) { return false; }
|
||||
if (Vector2.DistanceSquared(target.WorldPosition, character.WorldPosition) > InspectDistance * InspectDistance) { return false; }
|
||||
if (lastInspectionTimes.TryGetValue(target, out double lastInspectionTime))
|
||||
{
|
||||
if (Timing.TotalTime < lastInspectionTime + inspectionInterval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
return subObjectives.Any() ? 50 : 0;
|
||||
}
|
||||
|
||||
public void InspectEveryone()
|
||||
{
|
||||
lastInspectionTimes.Clear();
|
||||
overrideInspectProbability = 1.0f;
|
||||
InspectDistance = DefaultInspectDistance * 2;
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
{
|
||||
var checkStolenItemsObjective = new AIObjectiveCheckStolenItems(character, target, objectiveManager);
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Unsynced) >= InspectProbability)
|
||||
{
|
||||
checkStolenItemsObjective.ForceComplete();
|
||||
lastInspectionTimes[target] = Timing.TotalTime;
|
||||
}
|
||||
return checkStolenItemsObjective;
|
||||
}
|
||||
|
||||
private float checkVisibleStolenItemsTimer;
|
||||
private const float CheckVisibleStolenItemsInterval = 5.0f;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
if (checkVisibleStolenItemsTimer > 0.0f)
|
||||
{
|
||||
checkVisibleStolenItemsTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
foreach (var target in Character.CharacterList)
|
||||
{
|
||||
if (!IsValidTarget(target, character)) { continue; }
|
||||
//if we spot someone wearing or holding stolen items, immediately check them (with 100% chance of spotting the stolen items)
|
||||
if (target.Inventory.AllItems.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing && target.HasEquippedItem(it)) &&
|
||||
character.CanSeeTarget(target))
|
||||
{
|
||||
AIObjectiveCheckStolenItems? existingObjective =
|
||||
objectiveManager.GetActiveObjectives<AIObjectiveCheckStolenItems>().FirstOrDefault(o => o.TargetCharacter == target);
|
||||
if (existingObjective == null)
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCheckStolenItems(character, target, objectiveManager));
|
||||
lastInspectionTimes[target] = Timing.TotalTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
checkVisibleStolenItemsTimer = CheckVisibleStolenItemsInterval;
|
||||
}
|
||||
|
||||
private bool IsValidTarget(Character target, Character character)
|
||||
{
|
||||
if (target == null || target.Removed) { return false; }
|
||||
if (target.IsIncapacitated) { return false; }
|
||||
if (target == character) { return false; }
|
||||
if (target.Submarine == null) { return false; }
|
||||
if (character.Submarine == null) { return false; }
|
||||
if (target.CurrentHull == null) { return false; }
|
||||
if (target.Submarine != character.Submarine) { return false; }
|
||||
//only player's crew can steal, ignore other teams
|
||||
if (!target.IsOnPlayerTeam) { return false; }
|
||||
if (target.IsArrested) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
|
||||
{
|
||||
lastInspectionTimes[target] = Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -178,7 +178,7 @@ namespace Barotrauma
|
||||
if (!objectiveManager.IsOrder(this))
|
||||
{
|
||||
// Battery or pump states cannot currently be reported (not implemented) and therefore we must ignore them -> the bots always know if they require attention.
|
||||
bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater;
|
||||
bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater || this is AIObjectiveFindThieves;
|
||||
if (!ignore && !ReportedTargets.Contains(target)) { continue; }
|
||||
}
|
||||
if (!Filter(target)) { continue; }
|
||||
|
||||
+4
@@ -142,6 +142,7 @@ namespace Barotrauma
|
||||
prevIdleObjective.PreferredOutpostModuleTypes.ForEach(t => newIdleObjective.PreferredOutpostModuleTypes.Add(t));
|
||||
}
|
||||
AddObjective(newIdleObjective);
|
||||
|
||||
int objectiveCount = Objectives.Count;
|
||||
foreach (var autonomousObjective in character.Info.Job.Prefab.AutonomousObjectives)
|
||||
{
|
||||
@@ -549,6 +550,9 @@ namespace Barotrauma
|
||||
case "escapehandcuffs":
|
||||
newObjective = new AIObjectiveEscapeHandcuffs(character, this, priorityModifier: priorityModifier);
|
||||
break;
|
||||
case "findthieves":
|
||||
newObjective = new AIObjectiveFindThieves(character, this, priorityModifier: priorityModifier);
|
||||
break;
|
||||
case "prepareforexpedition":
|
||||
newObjective = new AIObjectivePrepare(character, this, order.GetTargetItems(order.Option), order.RequireItems)
|
||||
{
|
||||
|
||||
@@ -441,7 +441,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (NotImplementedException e)
|
||||
{
|
||||
DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}");
|
||||
DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}",
|
||||
contentPackage: ContentPackage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +552,8 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
if (handlePos[i].LengthSquared() > ArmLength)
|
||||
{
|
||||
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)");
|
||||
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)",
|
||||
item.Prefab.ContentPackage);
|
||||
}
|
||||
#endif
|
||||
HandIK(
|
||||
|
||||
+111
-94
@@ -150,7 +150,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly float movementLerp;
|
||||
|
||||
private float cprAnimTimer,cprPump;
|
||||
private float cprAnimTimer, cprPumpTimer;
|
||||
|
||||
private float fallingProneAnimTimer;
|
||||
const float FallingProneAnimDuration = 1.0f;
|
||||
@@ -243,14 +243,17 @@ namespace Barotrauma
|
||||
if (MainLimb == null) { return; }
|
||||
|
||||
levitatingCollider = !IsHanging;
|
||||
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
character.SelectedSecondaryItem?.GetComponent<Ladder>() != null ||
|
||||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||
if (onGround && character.CanMove)
|
||||
{
|
||||
Crouching = false;
|
||||
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
character.SelectedSecondaryItem?.GetComponent<Ladder>() != null ||
|
||||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||
{
|
||||
Crouching = false;
|
||||
}
|
||||
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
||||
}
|
||||
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
||||
|
||||
//stun (= disable the animations) if the ragdoll receives a large enough impact
|
||||
if (strongestImpact > 0.0f)
|
||||
@@ -276,7 +279,7 @@ namespace Barotrauma
|
||||
|
||||
if (!character.CanMove)
|
||||
{
|
||||
if (fallingProneAnimTimer < FallingProneAnimDuration)
|
||||
if (fallingProneAnimTimer < FallingProneAnimDuration && onGround)
|
||||
{
|
||||
fallingProneAnimTimer += deltaTime;
|
||||
UpdateFallingProne(1.0f);
|
||||
@@ -285,7 +288,12 @@ namespace Barotrauma
|
||||
Collider.FarseerBody.FixedRotation = false;
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
Collider.Enabled = false;
|
||||
if (Collider.Enabled)
|
||||
{
|
||||
//deactivating the collider -> make the main limb inherit the collider's velocity because it'll control the movement now
|
||||
MainLimb.body.LinearVelocity = Collider.LinearVelocity;
|
||||
Collider.Enabled = false;
|
||||
}
|
||||
Collider.LinearVelocity = MainLimb.LinearVelocity;
|
||||
Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
|
||||
//reset pull joints to prevent the character from "hanging" mid-air if pull joints had been active when the character was still moving
|
||||
@@ -386,6 +394,12 @@ namespace Barotrauma
|
||||
DragCharacter(character.SelectedCharacter, deltaTime);
|
||||
}
|
||||
|
||||
if (Anim != Animation.CPR)
|
||||
{
|
||||
cprAnimTimer = 0.0f;
|
||||
cprPumpTimer = 0.0f;
|
||||
}
|
||||
|
||||
switch (Anim)
|
||||
{
|
||||
case Animation.Climbing:
|
||||
@@ -648,14 +662,6 @@ namespace Barotrauma
|
||||
|
||||
if (!onGround)
|
||||
{
|
||||
Vector2 move = torso.PullJointWorldAnchorB - torso.SimPosition;
|
||||
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
MoveLimb(limb, limb.SimPosition + move, 15.0f, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1318,14 +1324,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateFallingProne(float strength)
|
||||
void UpdateFallingProne(float strength, bool moveHands = true, bool moveTorso = true, bool moveLegs = true)
|
||||
{
|
||||
if (strength <= 0.0f) { return; }
|
||||
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
|
||||
if (head != null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
|
||||
if (moveHands && head != null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
|
||||
{
|
||||
//if the head is moving, try to protect it with the hands
|
||||
Limb leftHand = GetLimb(LimbType.LeftHand);
|
||||
@@ -1347,7 +1353,7 @@ namespace Barotrauma
|
||||
|
||||
//make the torso tip over
|
||||
//otherwise it tends to just drop straight down, pinning the characters legs in a weird pose
|
||||
if (!InWater)
|
||||
if (moveTorso && !InWater)
|
||||
{
|
||||
//prefer tipping over in the same direction the torso is rotating
|
||||
//or moving
|
||||
@@ -1358,27 +1364,30 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//attempt to make legs stay in a straight line with the torso to prevent the character from doing a split
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (moveLegs)
|
||||
{
|
||||
var thigh = i == 0 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
|
||||
if (thigh == null) { continue; }
|
||||
if (thigh.IsSevered) { continue; }
|
||||
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
|
||||
float diff = torso.Rotation - thigh.Rotation;
|
||||
if (MathUtils.IsValid(diff))
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
|
||||
thigh.body.ApplyTorque(thighTorque * strength);
|
||||
}
|
||||
var thigh = i == 0 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
|
||||
if (thigh == null) { continue; }
|
||||
if (thigh.IsSevered) { continue; }
|
||||
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
|
||||
float diff = torso.Rotation - thigh.Rotation;
|
||||
if (MathUtils.IsValid(diff))
|
||||
{
|
||||
float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
|
||||
thigh.body.ApplyTorque(thighTorque * strength);
|
||||
}
|
||||
|
||||
var leg = i == 0 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
|
||||
if (leg == null || leg.IsSevered) { continue; }
|
||||
float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
|
||||
diff = torso.Rotation - leg.Rotation;
|
||||
if (MathUtils.IsValid(diff))
|
||||
{
|
||||
float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
|
||||
leg.body.ApplyTorque(legTorque * strength);
|
||||
var leg = i == 0 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
|
||||
if (leg == null || leg.IsSevered) { continue; }
|
||||
float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
|
||||
diff = torso.Rotation - leg.Rotation;
|
||||
if (MathUtils.IsValid(diff))
|
||||
{
|
||||
float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
|
||||
leg.body.ApplyTorque(legTorque * strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1398,7 +1407,8 @@ namespace Barotrauma
|
||||
|
||||
Crouching = true;
|
||||
|
||||
Vector2 diff = target.SimPosition - character.SimPosition;
|
||||
Vector2 offset = Vector2.UnitX * -Dir * 0.75f;
|
||||
Vector2 diff = (target.SimPosition + offset) - character.SimPosition;
|
||||
Limb targetHead = target.AnimController.GetLimb(LimbType.Head);
|
||||
Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso);
|
||||
if (targetTorso == null)
|
||||
@@ -1412,7 +1422,23 @@ namespace Barotrauma
|
||||
|
||||
Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition;
|
||||
targetMovement = new Vector2(diff.X, 0.0f);
|
||||
const float CloseEnough = 0.1f;
|
||||
if (Math.Abs(targetMovement.X) < CloseEnough)
|
||||
{
|
||||
targetMovement.X = 0.0f;
|
||||
}
|
||||
|
||||
TargetDir = headDiff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
//if the target's in some weird pose, we may not be able to flip it so it's facing up,
|
||||
//so let's only try it once so we don't end up constantly flipping it
|
||||
if (cprAnimTimer <= 0.0f && target.AnimController.Direction == TargetDir)
|
||||
{
|
||||
target.AnimController.Flip();
|
||||
}
|
||||
(target.AnimController as HumanoidAnimController)?.UpdateFallingProne(strength: 1.0f, moveHands: false, moveTorso: false);
|
||||
|
||||
head.Disabled = true;
|
||||
torso.Disabled = true;
|
||||
|
||||
UpdateStanding();
|
||||
|
||||
@@ -1443,73 +1469,64 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
|
||||
if (cprAnimTimer > 15.0f && targetHead != null && head != null)
|
||||
//Serverside code
|
||||
if (GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
float yPos = (float)Math.Sin(cprAnimTimer) * 0.2f;
|
||||
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.3f + yPos);
|
||||
if (target.Oxygen < -10.0f)
|
||||
{
|
||||
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
||||
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
|
||||
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
|
||||
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
||||
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
||||
}
|
||||
}
|
||||
|
||||
if (targetHead != null && head != null)
|
||||
{
|
||||
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
|
||||
head.PullJointEnabled = true;
|
||||
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.2f));
|
||||
torso.PullJointEnabled = true;
|
||||
|
||||
//Serverside code
|
||||
if (GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
if (target.Oxygen < -10.0f)
|
||||
{
|
||||
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
||||
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
|
||||
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
|
||||
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
||||
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
|
||||
torso.PullJointEnabled = true;
|
||||
|
||||
if (cprPumpTimer >= 1)
|
||||
{
|
||||
if (targetHead != null && head != null)
|
||||
torso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
targetTorso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
cprPumpTimer = 0;
|
||||
|
||||
if (skill < CPRSettings.Active.DamageSkillThreshold)
|
||||
{
|
||||
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
|
||||
head.PullJointEnabled = true;
|
||||
target.LastDamageSource = null;
|
||||
target.DamageLimb(
|
||||
targetTorso.WorldPosition, targetTorso,
|
||||
new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
|
||||
stun: 0.0f,
|
||||
playSound: true,
|
||||
attackImpulse: Vector2.Zero,
|
||||
attacker: null);
|
||||
}
|
||||
|
||||
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
|
||||
torso.PullJointEnabled = true;
|
||||
|
||||
if (cprPump >= 1)
|
||||
//need to CPR for at least a couple of seconds before the target can be revived
|
||||
//(reviving the target when the CPR has barely started looks strange)
|
||||
if (cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
torso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
targetTorso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
cprPump = 0;
|
||||
float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
|
||||
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
|
||||
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
|
||||
reviveChance *= 1f + cprBoost;
|
||||
|
||||
if (skill < CPRSettings.Active.DamageSkillThreshold)
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
|
||||
{
|
||||
target.LastDamageSource = null;
|
||||
target.DamageLimb(
|
||||
targetTorso.WorldPosition, targetTorso,
|
||||
new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
|
||||
0.0f, true, 0.0f, attacker: null);
|
||||
}
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) //Serverside code
|
||||
{
|
||||
float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
|
||||
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
|
||||
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
|
||||
|
||||
reviveChance *= 1f + cprBoost;
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
|
||||
{
|
||||
//increase oxygen and clamp it above zero
|
||||
// -> the character should be revived if there are no major afflictions in addition to lack of oxygen
|
||||
target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
|
||||
}
|
||||
//increase oxygen and clamp it above zero
|
||||
// -> the character should be revived if there are no major afflictions in addition to lack of oxygen
|
||||
target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
|
||||
}
|
||||
}
|
||||
cprPump += deltaTime;
|
||||
}
|
||||
|
||||
cprAnimTimer = (cprAnimTimer + deltaTime) % 17;
|
||||
cprPumpTimer += deltaTime;
|
||||
cprAnimTimer += deltaTime;
|
||||
|
||||
//got the character back into a non-critical state, increase medical skill
|
||||
//BUT only if it has been more than 10 seconds since the character revived someone
|
||||
|
||||
@@ -437,7 +437,18 @@ namespace Barotrauma
|
||||
foreach (var huskAppendage in mainElement.GetChildElements("huskappendage"))
|
||||
{
|
||||
if (!inEditor && huskAppendage.GetAttributeBool("onlyfromafflictions", false)) { continue; }
|
||||
AfflictionHusk.AttachHuskAppendage(character, huskAppendage.GetAttributeIdentifier("affliction", Identifier.Empty), huskAppendage, ragdoll: this);
|
||||
|
||||
Identifier afflictionIdentifier = huskAppendage.GetAttributeIdentifier("affliction", Identifier.Empty);
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab affliction) ||
|
||||
affliction is not AfflictionPrefabHusk matchingAffliction)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!",
|
||||
contentPackage: huskAppendage.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
AfflictionHusk.AttachHuskAppendage(character, matchingAffliction, huskAppendage, ragdoll: this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
{
|
||||
public enum HitDetection
|
||||
{
|
||||
Distance,
|
||||
@@ -391,7 +392,8 @@ namespace Barotrauma
|
||||
element.GetAttribute("burndamage") != null ||
|
||||
element.GetAttribute("bleedingdamage") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Define damage as afflictions instead of using the damage attribute (e.g. <Affliction identifier=\"internaldamage\" strength=\"10\" />).");
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Define damage as afflictions instead of using the damage attribute (e.g. <Affliction identifier=\"internaldamage\" strength=\"10\" />).",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
|
||||
//if level wall damage is not defined, default to the structure damage
|
||||
@@ -414,12 +416,14 @@ namespace Barotrauma
|
||||
AfflictionPrefab afflictionPrefab;
|
||||
if (subElement.GetAttribute("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - define afflictions using identifiers instead of names.");
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - define afflictions using identifiers instead of names.",
|
||||
contentPackage: element.ContentPackage);
|
||||
string afflictionName = subElement.GetAttributeString("name", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Name.Equals(afflictionName, System.StringComparison.OrdinalIgnoreCase));
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.",
|
||||
contentPackage: element.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -428,7 +432,8 @@ namespace Barotrauma
|
||||
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out afflictionPrefab))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.",
|
||||
contentPackage: element.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -441,7 +446,7 @@ namespace Barotrauma
|
||||
}
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public void ReloadAfflictions(XElement element, string parentDebugName)
|
||||
public void ReloadAfflictions(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
Afflictions.Clear();
|
||||
foreach (var subElement in element.GetChildElements("affliction"))
|
||||
@@ -450,13 +455,14 @@ namespace Barotrauma
|
||||
Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", "");
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab afflictionPrefab))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\".");
|
||||
DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
affliction = afflictionPrefab.Instantiate(0.0f);
|
||||
affliction.Deserialize(subElement);
|
||||
//backwards compatibility
|
||||
if (subElement.Attribute("amount") != null && subElement.Attribute("strength") == null)
|
||||
if (subElement.GetAttribute("amount") != null && subElement.GetAttribute("strength") == null)
|
||||
{
|
||||
affliction.Strength = subElement.GetAttributeFloat("amount", 0.0f);
|
||||
}
|
||||
@@ -465,7 +471,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(XElement element)
|
||||
public void Serialize(ContentXElement element)
|
||||
{
|
||||
SerializableProperty.SerializeProperties(this, element, true);
|
||||
foreach (var affliction in Afflictions)
|
||||
@@ -477,7 +483,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void Deserialize(XElement element, string parentDebugName)
|
||||
public void Deserialize(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
ReloadAfflictions(element, parentDebugName);
|
||||
@@ -497,8 +503,9 @@ namespace Barotrauma
|
||||
SetUser(attacker);
|
||||
|
||||
DamageParticles(deltaTime, worldPosition);
|
||||
|
||||
var attackResult = target?.AddDamage(attacker, worldPosition, this, deltaTime, playSound) ?? new AttackResult();
|
||||
|
||||
Vector2 impulseDirection = GetImpulseDirection(target as ISpatialEntity, worldPosition, SourceItem);
|
||||
var attackResult = target?.AddDamage(attacker, worldPosition, this, impulseDirection, deltaTime, playSound) ?? new AttackResult();
|
||||
var conditionalEffectType = attackResult.Damage > 0.0f ? ActionType.OnSuccess : ActionType.OnFailure;
|
||||
var additionalEffectType = ActionType.OnUse;
|
||||
if (targetCharacter != null && targetCharacter.IsDead)
|
||||
@@ -606,7 +613,7 @@ namespace Barotrauma
|
||||
float penetration = Penetration;
|
||||
|
||||
RangedWeapon weapon =
|
||||
SourceItem?.GetComponent<RangedWeapon>() ??
|
||||
SourceItem?.GetComponent<RangedWeapon>() ??
|
||||
SourceItem?.GetComponent<Projectile>()?.Launcher?.GetComponent<RangedWeapon>();
|
||||
float? penetrationValue = weapon?.Penetration;
|
||||
if (penetrationValue.HasValue)
|
||||
@@ -614,7 +621,8 @@ namespace Barotrauma
|
||||
penetration += penetrationValue.Value;
|
||||
}
|
||||
|
||||
var attackResult = targetLimb.character.ApplyAttack(attacker, worldPosition, this, deltaTime, playSound, targetLimb, penetration);
|
||||
Vector2 impulseDirection = GetImpulseDirection(targetLimb, worldPosition, SourceItem);
|
||||
var attackResult = targetLimb.character.ApplyAttack(attacker, worldPosition, this, deltaTime, impulseDirection, playSound, targetLimb, penetration);
|
||||
var conditionalEffectType = attackResult.Damage > 0.0f ? ActionType.OnSuccess : ActionType.OnFailure;
|
||||
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
@@ -666,6 +674,34 @@ namespace Barotrauma
|
||||
return attackResult;
|
||||
}
|
||||
|
||||
private Vector2 GetImpulseDirection(ISpatialEntity target, Vector2 sourceWorldPosition, Item sourceItem)
|
||||
{
|
||||
Vector2 impulseDirection = Vector2.Zero;
|
||||
if (target != null)
|
||||
{
|
||||
impulseDirection = target.WorldPosition - sourceWorldPosition;
|
||||
}
|
||||
|
||||
if (sourceItem?.body != null && sourceItem.body.Enabled && sourceItem.body.LinearVelocity.LengthSquared() > 0.0f)
|
||||
{
|
||||
impulseDirection = sourceItem.body.LinearVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
var projectileComponent = sourceItem?.GetComponent<Projectile>();
|
||||
if (projectileComponent != null)
|
||||
{
|
||||
impulseDirection = new Vector2(MathF.Cos(SourceItem.Rotation), MathF.Sin(SourceItem.Rotation));
|
||||
}
|
||||
}
|
||||
|
||||
if (impulseDirection.LengthSquared() > 0.0001f)
|
||||
{
|
||||
impulseDirection = Vector2.Normalize(impulseDirection);
|
||||
}
|
||||
return impulseDirection;
|
||||
}
|
||||
|
||||
public float AttackTimer { get; private set; }
|
||||
public float CoolDownTimer { get; set; }
|
||||
public float CurrentRandomCoolDown { get; private set; }
|
||||
|
||||
@@ -1098,6 +1098,15 @@ namespace Barotrauma
|
||||
set { CharacterHealth.Unkillable = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the health interface available on this character? Can be used by status effects
|
||||
/// </summary>
|
||||
public bool UseHealthWindow
|
||||
{
|
||||
get { return CharacterHealth.UseHealthWindow; }
|
||||
set { CharacterHealth.UseHealthWindow = value; }
|
||||
}
|
||||
|
||||
public CampaignMode.InteractionType CampaignInteractionType;
|
||||
public Identifier MerchantIdentifier;
|
||||
|
||||
@@ -1223,19 +1232,15 @@ namespace Barotrauma
|
||||
public static Character Create(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||
{
|
||||
Character newCharacter = null;
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName)
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName || hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new EnemyAIController(aiCharacter, seed);
|
||||
|
||||
var ai = (prefab.Identifier == CharacterPrefab.HumanSpeciesName || aiCharacter.Params.UseHumanAI) ?
|
||||
new HumanAIController(aiCharacter) as AIController :
|
||||
new EnemyAIController(aiCharacter, seed);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else if (hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new HumanAIController(aiCharacter);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1282,7 +1287,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (!VariantOf.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError("The variant system does not yet support humans, sorry. It does support other humanoids though!");
|
||||
DebugConsole.ThrowError("The variant system does not yet support humans, sorry. It does support other humanoids though!",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
}
|
||||
if (characterInfo == null)
|
||||
{
|
||||
@@ -1406,7 +1412,8 @@ namespace Barotrauma
|
||||
if (matchingAffliction == null || nonHuskedSpeciesName.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot find a husk infection that matches {speciesName}! Please make sure that the speciesname is added as 'targets' in the husk affliction prefab definition!\n"
|
||||
+ "Note that all the infected speciesnames and files must stick the following pattern: [nonhuskedspeciesname][huskedspeciesname]. E.g. Humanhusk, Crawlerhusk, or Humancustomhusk, or Crawlerzombie. Not \"Customhumanhusk!\" or \"Zombiecrawler\"");
|
||||
+ "Note that all the infected speciesnames and files must stick the following pattern: [nonhuskedspeciesname][huskedspeciesname]. E.g. Humanhusk, Crawlerhusk, or Humancustomhusk, or Crawlerzombie. Not \"Customhumanhusk!\" or \"Zombiecrawler\"",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
// Crashes if we fail to create a ragdoll -> Let's just use some ragdoll so that the user sees the error msg.
|
||||
nonHuskedSpeciesName = IsHumanoid ? CharacterPrefab.HumanSpeciesName : "crawler".ToIdentifier();
|
||||
speciesName = nonHuskedSpeciesName;
|
||||
@@ -2407,6 +2414,11 @@ namespace Barotrauma
|
||||
}
|
||||
else if (body.UserData is Item item)
|
||||
{
|
||||
if (item.GetComponent<Door>() is { HasWindow: true } door)
|
||||
{
|
||||
if (door.IsPositionOnWindow(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition))) { return false; }
|
||||
}
|
||||
|
||||
return item != target;
|
||||
}
|
||||
return true;
|
||||
@@ -2768,9 +2780,17 @@ namespace Barotrauma
|
||||
if (!item.Prefab.InteractThroughWalls && Screen.Selected != GameMain.SubEditorScreen && !insideTrigger)
|
||||
{
|
||||
var body = Submarine.CheckVisibility(SimPosition, itemPosition, ignoreLevel: true);
|
||||
if (body != null && body.UserData as Item != item && (body.UserData as ItemComponent)?.Item != item && Submarine.LastPickedFixture?.UserData as Item != item)
|
||||
{
|
||||
return false;
|
||||
if (body != null)
|
||||
{
|
||||
var otherItem = body.UserData as Item ?? (body.UserData as ItemComponent)?.Item;
|
||||
if (otherItem != item &&
|
||||
(body.UserData as ItemComponent)?.Item != item &&
|
||||
/*allow interacting through open doors (e.g. duct blocks' colliders stay active despite being open)*/
|
||||
otherItem?.GetComponent<Door>() is not { IsOpen: true } &&
|
||||
Submarine.LastPickedFixture?.UserData as Item != item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2797,7 +2817,12 @@ namespace Barotrauma
|
||||
public void DeselectCharacter()
|
||||
{
|
||||
if (SelectedCharacter == null) { return; }
|
||||
SelectedCharacter.AnimController?.ResetPullJoints();
|
||||
if (!SelectedCharacter.AllowInput)
|
||||
{
|
||||
//we cannot reset the pull joints if the target is conscious (moving on its own),
|
||||
//that'd interfere with its animations
|
||||
SelectedCharacter.AnimController?.ResetPullJoints();
|
||||
}
|
||||
SelectedCharacter = null;
|
||||
}
|
||||
|
||||
@@ -3301,10 +3326,7 @@ namespace Barotrauma
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.2f; }
|
||||
}
|
||||
if (IsRagdolled)
|
||||
{
|
||||
SetInput(InputType.Ragdoll, false, true);
|
||||
}
|
||||
SetInput(InputType.Ragdoll, false, IsRagdolled);
|
||||
}
|
||||
if (!wasRagdolled && IsRagdolled)
|
||||
{
|
||||
@@ -3980,15 +4002,15 @@ namespace Barotrauma
|
||||
CharacterHealth.SetAllDamage(damageAmount, bleedingDamageAmount, burnDamageAmount);
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true)
|
||||
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, Vector2 impulseDirection, float deltaTime, bool playSound = true)
|
||||
{
|
||||
return ApplyAttack(attacker, worldPosition, attack, deltaTime, playSound, null);
|
||||
return ApplyAttack(attacker, worldPosition, attack, deltaTime, impulseDirection, playSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the specified attack to this character. If the targetLimb is not specified, the limb closest to worldPosition will receive the damage.
|
||||
/// </summary>
|
||||
public AttackResult ApplyAttack(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false, Limb targetLimb = null, float penetration = 0f)
|
||||
public AttackResult ApplyAttack(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, Vector2 impulseDirection, bool playSound = false, Limb targetLimb = null, float penetration = 0f)
|
||||
{
|
||||
if (Removed)
|
||||
{
|
||||
@@ -4000,7 +4022,16 @@ namespace Barotrauma
|
||||
|
||||
Limb limbHit = targetLimb;
|
||||
|
||||
float attackImpulse = attack.TargetImpulse + attack.TargetForce * attack.ImpactMultiplier * deltaTime;
|
||||
float impulseMagnitude = (attack.TargetImpulse + attack.TargetForce * attack.ImpactMultiplier) * deltaTime;
|
||||
|
||||
Vector2 attackImpulse = Vector2.Zero;
|
||||
if (Math.Abs(impulseMagnitude) > 0.0f)
|
||||
{
|
||||
impulseDirection = impulseDirection.LengthSquared() > 0.0001f ?
|
||||
Vector2.Normalize(impulseDirection) :
|
||||
Vector2.UnitX;
|
||||
attackImpulse = impulseDirection * impulseMagnitude;
|
||||
}
|
||||
|
||||
AbilityAttackData attackData = new AbilityAttackData(attack, this, attacker);
|
||||
IEnumerable<Affliction> attackAfflictions;
|
||||
@@ -4129,12 +4160,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse = 0.0f, Character attacker = null, float damageMultiplier = 1f)
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, Vector2? attackImpulse = null, Character attacker = null, float damageMultiplier = 1f)
|
||||
{
|
||||
return AddDamage(worldPosition, afflictions, stun, playSound, attackImpulse, out _, attacker, damageMultiplier: damageMultiplier);
|
||||
return AddDamage(worldPosition, afflictions, stun, playSound, attackImpulse ?? Vector2.Zero, 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)
|
||||
public AttackResult AddDamage(Vector2 worldPosition, IEnumerable<Affliction> afflictions, float stun, bool playSound, Vector2 attackImpulse, out Limb hitLimb, Character attacker = null, float damageMultiplier = 1)
|
||||
{
|
||||
hitLimb = null;
|
||||
|
||||
@@ -4167,7 +4198,7 @@ namespace Barotrauma
|
||||
CreatureMetrics.RecordKill(target.SpeciesName);
|
||||
}
|
||||
|
||||
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f, bool shouldImplode = false)
|
||||
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, Vector2 attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f, bool shouldImplode = false)
|
||||
{
|
||||
if (Removed) { return new AttackResult(); }
|
||||
|
||||
@@ -4200,18 +4231,17 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Vector2 dir = hitLimb.WorldPosition - worldPosition;
|
||||
if (Math.Abs(attackImpulse) > 0.0f)
|
||||
if (attackImpulse.LengthSquared() > 0.0f)
|
||||
{
|
||||
Vector2 diff = dir;
|
||||
if (diff == Vector2.Zero) { diff = Rand.Vector(1.0f); }
|
||||
Vector2 impulse = Vector2.Normalize(diff) * attackImpulse;
|
||||
Vector2 hitPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff);
|
||||
hitLimb.body.ApplyLinearImpulse(impulse, hitPos, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
|
||||
hitLimb.body.ApplyLinearImpulse(attackImpulse, hitPos, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
|
||||
var mainLimb = hitLimb.character.AnimController.MainLimb;
|
||||
if (hitLimb != mainLimb)
|
||||
{
|
||||
// Always add force to mainlimb
|
||||
mainLimb.body.ApplyLinearImpulse(impulse, hitPos, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
mainLimb.body.ApplyLinearImpulse(attackImpulse, hitPos, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
}
|
||||
bool wasDead = IsDead;
|
||||
@@ -4656,7 +4686,7 @@ namespace Barotrauma
|
||||
}
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
|
||||
|
||||
public void Revive(bool removeAfflictions = true)
|
||||
public void Revive(bool removeAfflictions = true, bool createNetworkEvent = false)
|
||||
{
|
||||
if (Removed)
|
||||
{
|
||||
@@ -4705,7 +4735,11 @@ namespace Barotrauma
|
||||
limb.IsSevered = false;
|
||||
}
|
||||
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
if (createNetworkEvent && GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new CharacterStatusEventData());
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
@@ -4986,7 +5020,9 @@ namespace Barotrauma
|
||||
float maxDistance = 1000f;
|
||||
foreach (var hull in adjacentHulls)
|
||||
{
|
||||
if (hull.ConnectedGaps.Any(g => g.Open > 0.9f && g.linkedTo.Contains(CurrentHull) &&
|
||||
if (hull.ConnectedGaps.Any(g =>
|
||||
(g.Open > 0.9f || g.ConnectedDoor is { HasWindow: true }) &&
|
||||
g.linkedTo.Contains(CurrentHull) &&
|
||||
Vector2.DistanceSquared(g.WorldPosition, WorldPosition) < Math.Pow(maxDistance / 2, 2)))
|
||||
{
|
||||
if (Vector2.DistanceSquared(hull.WorldPosition, WorldPosition) < Math.Pow(maxDistance, 2))
|
||||
@@ -5005,7 +5041,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
if (h.ConnectedGaps.Any(g =>
|
||||
g.Open > 0.9f &&
|
||||
(g.Open > 0.9f || g.ConnectedDoor is { HasWindow: true }) &&
|
||||
Vector2.DistanceSquared(g.WorldPosition, WorldPosition) < Math.Pow(maxDistance / 2, 2) &&
|
||||
CanSeeTarget(g)))
|
||||
{
|
||||
@@ -5027,7 +5063,7 @@ namespace Barotrauma
|
||||
public bool IsEngineer => HasJob("engineer");
|
||||
public bool IsMechanic => HasJob("mechanic");
|
||||
public bool IsMedic => HasJob("medicaldoctor");
|
||||
public bool IsSecurity => HasJob("securityofficer") || HasJob("vipsecurityofficer");
|
||||
public bool IsSecurity => HasJob("securityofficer") || HasJob("vipsecurityofficer") || HasJob("outpostsecurityofficer");
|
||||
public bool IsAssistant => HasJob("assistant");
|
||||
public bool IsWatchman => HasJob("watchman");
|
||||
public bool IsVip => HasJob("prisoner");
|
||||
|
||||
@@ -767,7 +767,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Used for loading the data
|
||||
public CharacterInfo(XElement infoElement, Identifier npcIdentifier = default)
|
||||
public CharacterInfo(ContentXElement infoElement, Identifier npcIdentifier = default)
|
||||
{
|
||||
ID = idCounter;
|
||||
idCounter++;
|
||||
@@ -1311,12 +1311,12 @@ namespace Barotrauma
|
||||
OnExperienceChanged(prevAmount, ExperiencePoints);
|
||||
}
|
||||
|
||||
const int BaseExperienceRequired = -50;
|
||||
const int BaseExperienceRequired = 450;
|
||||
const int AddedExperienceRequiredPerLevel = 500;
|
||||
|
||||
public int GetTotalTalentPoints()
|
||||
{
|
||||
return GetCurrentLevel() + AdditionalTalentPoints - 1;
|
||||
return GetCurrentLevel() + AdditionalTalentPoints;
|
||||
}
|
||||
|
||||
public int GetAvailableTalentPoints()
|
||||
@@ -1342,16 +1342,19 @@ namespace Barotrauma
|
||||
return experienceRequired + ExperienceRequiredPerLevel(level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How much more experience does the character need to reach the specified level?
|
||||
/// </summary>
|
||||
public int GetExperienceRequiredForLevel(int level)
|
||||
{
|
||||
int currentLevel = GetCurrentLevel(out int experienceRequired);
|
||||
int currentLevel = GetCurrentLevel();
|
||||
if (currentLevel >= level) { return 0; }
|
||||
int required = experienceRequired;
|
||||
for (int i = currentLevel + 1; i <= level; i++)
|
||||
int required = 0;
|
||||
for (int i = 0; i < level; i++)
|
||||
{
|
||||
required += ExperienceRequiredPerLevel(i);
|
||||
}
|
||||
return required;
|
||||
return required - ExperiencePoints;
|
||||
}
|
||||
|
||||
public int GetCurrentLevel()
|
||||
@@ -1361,7 +1364,7 @@ namespace Barotrauma
|
||||
|
||||
private int GetCurrentLevel(out int experienceRequired)
|
||||
{
|
||||
int level = 1;
|
||||
int level = 0;
|
||||
experienceRequired = 0;
|
||||
while (experienceRequired + ExperienceRequiredPerLevel(level) <= ExperiencePoints)
|
||||
{
|
||||
|
||||
@@ -95,7 +95,8 @@ namespace Barotrauma
|
||||
name = ParseName(mainElement, file);
|
||||
if (name == Identifier.Empty)
|
||||
{
|
||||
DebugConsole.ThrowError($"No species name defined for: {file.Path}");
|
||||
DebugConsole.ThrowError($"No species name defined for: {file.Path}",
|
||||
contentPackage: file.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
+13
-14
@@ -75,7 +75,8 @@ namespace Barotrauma
|
||||
HuskPrefab = prefab as AfflictionPrefabHusk;
|
||||
if (HuskPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in husk affliction definition: the prefab is of wrong type!");
|
||||
DebugConsole.ThrowError("Error in husk affliction definition: the prefab is of wrong type!",
|
||||
contentPackage: prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +198,7 @@ namespace Barotrauma
|
||||
huskInfection.Add(AfflictionPrefab.InternalDamage.Instantiate(random * 10 * deltaTime / limbCount));
|
||||
character.LastDamageSource = null;
|
||||
float force = applyForce ? random * 0.5f * limb.Mass : 0;
|
||||
character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, force);
|
||||
character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, Rand.Vector(force));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +206,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (huskAppendage == null && character.Params.UseHuskAppendage)
|
||||
{
|
||||
huskAppendage = AttachHuskAppendage(character, Prefab.Identifier);
|
||||
huskAppendage = AttachHuskAppendage(character, Prefab as AfflictionPrefabHusk);
|
||||
}
|
||||
|
||||
if (Prefab is AfflictionPrefabHusk { NeedsAir: false })
|
||||
@@ -285,13 +286,14 @@ namespace Barotrauma
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to turn character \"" + character.Name + "\" into a husk - husk config file not found.");
|
||||
DebugConsole.ThrowError("Failed to turn character \"" + character.Name + "\" into a husk - husk config file not found.",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
XElement parentElement = new XElement("CharacterInfo");
|
||||
XElement infoElement = character.Info?.Save(parentElement);
|
||||
CharacterInfo huskCharacterInfo = infoElement == null ? null : new CharacterInfo(infoElement);
|
||||
CharacterInfo huskCharacterInfo = infoElement == null ? null : new CharacterInfo(new ContentXElement(Prefab.ContentPackage, infoElement));
|
||||
|
||||
if (huskCharacterInfo != null)
|
||||
{
|
||||
@@ -371,31 +373,28 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public static List<Limb> AttachHuskAppendage(Character character, Identifier afflictionIdentifier, ContentXElement appendageDefinition = null, Ragdoll ragdoll = null)
|
||||
public static List<Limb> AttachHuskAppendage(Character character, AfflictionPrefabHusk matchingAffliction, ContentXElement appendageDefinition = null, Ragdoll ragdoll = null)
|
||||
{
|
||||
var appendage = new List<Limb>();
|
||||
if (!(AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier) is AfflictionPrefabHusk matchingAffliction))
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!");
|
||||
return appendage;
|
||||
}
|
||||
Identifier nonhuskedSpeciesName = GetNonHuskedSpeciesName(character.SpeciesName, matchingAffliction);
|
||||
Identifier huskedSpeciesName = GetHuskedSpeciesName(nonhuskedSpeciesName, matchingAffliction);
|
||||
CharacterPrefab huskPrefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName);
|
||||
if (huskPrefab?.ConfigElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find the config file for the husk infected species with the species name '{huskedSpeciesName}'!");
|
||||
DebugConsole.ThrowError($"Failed to find the config file for the husk infected species with the species name '{huskedSpeciesName}'!",
|
||||
contentPackage: matchingAffliction.ContentPackage);
|
||||
return appendage;
|
||||
}
|
||||
var mainElement = huskPrefab.ConfigElement;
|
||||
var element = appendageDefinition;
|
||||
if (element == null)
|
||||
{
|
||||
element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeIdentifier("affliction", Identifier.Empty) == afflictionIdentifier);
|
||||
element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeIdentifier("affliction", Identifier.Empty) == matchingAffliction.Identifier);
|
||||
}
|
||||
if (element == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{huskPrefab.FilePath}': Failed to find a huskappendage that matches the affliction with an identifier '{afflictionIdentifier}'!");
|
||||
DebugConsole.ThrowError($"Error in '{huskPrefab.FilePath}': Failed to find a huskappendage that matches the affliction with an identifier '{matchingAffliction.Identifier}'!",
|
||||
contentPackage: matchingAffliction.ContentPackage);
|
||||
return appendage;
|
||||
}
|
||||
ContentPath pathToAppendage = element.GetAttributeContentPath("path") ?? ContentPath.Empty;
|
||||
|
||||
+18
-9
@@ -170,11 +170,13 @@ namespace Barotrauma
|
||||
|
||||
if (DormantThreshold > ActiveThreshold)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in \"{Identifier}\": {nameof(DormantThreshold)} is greater than {nameof(ActiveThreshold)} ({DormantThreshold} > {ActiveThreshold})");
|
||||
DebugConsole.ThrowError($"Error in \"{Identifier}\": {nameof(DormantThreshold)} is greater than {nameof(ActiveThreshold)} ({DormantThreshold} > {ActiveThreshold})",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (ActiveThreshold > TransitionThreshold)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in \"{Identifier}\": {nameof(ActiveThreshold)} is greater than {nameof(TransitionThreshold)} ({ActiveThreshold} > {TransitionThreshold})");
|
||||
DebugConsole.ThrowError($"Error in \"{Identifier}\": {nameof(ActiveThreshold)} is greater than {nameof(TransitionThreshold)} ({ActiveThreshold} > {TransitionThreshold})",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
|
||||
TransformThresholdOnDeath = element.GetAttributeFloat("transformthresholdondeath", ActiveThreshold);
|
||||
@@ -440,13 +442,15 @@ namespace Barotrauma
|
||||
AbilityFlags flagType = subElement.GetAttributeEnum("flagtype", AbilityFlags.None);
|
||||
if (flagType is AbilityFlags.None)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in affliction \"{parentDebugName}\" - invalid ability flag type \"{subElement.GetAttributeString("flagtype", "")}\".");
|
||||
DebugConsole.ThrowError($"Error in affliction \"{parentDebugName}\" - invalid ability flag type \"{subElement.GetAttributeString("flagtype", "")}\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
AfflictionAbilityFlags |= flagType;
|
||||
break;
|
||||
case "affliction":
|
||||
DebugConsole.AddWarning($"Error in affliction \"{parentDebugName}\" - additional afflictions caused by the affliction should be configured inside status effects.");
|
||||
DebugConsole.AddWarning($"Error in affliction \"{parentDebugName}\" - additional afflictions caused by the affliction should be configured inside status effects.",
|
||||
contentPackage: element.ContentPackage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -537,14 +541,16 @@ namespace Barotrauma
|
||||
}
|
||||
else if (TextTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in affliction \"{affliction.Identifier}\" - no text defined for one of the descriptions.");
|
||||
DebugConsole.ThrowError($"Error in affliction \"{affliction.Identifier}\" - no text defined for one of the descriptions.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
|
||||
MinStrength = element.GetAttributeFloat(nameof(MinStrength), 0.0f);
|
||||
MaxStrength = element.GetAttributeFloat(nameof(MaxStrength), 100.0f);
|
||||
if (MinStrength >= MaxStrength)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in affliction \"{affliction.Identifier}\" - max strength is not larger than min.");
|
||||
DebugConsole.ThrowError($"Error in affliction \"{affliction.Identifier}\" - max strength is not larger than min.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
Target = element.GetAttributeEnum(nameof(Target), TargetType.Any);
|
||||
}
|
||||
@@ -953,7 +959,8 @@ namespace Barotrauma
|
||||
AfflictionOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "statvalue":
|
||||
DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.");
|
||||
DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.",
|
||||
contentPackage: element.ContentPackage);
|
||||
break;
|
||||
case "effect":
|
||||
case "periodiceffect":
|
||||
@@ -962,7 +969,8 @@ namespace Barotrauma
|
||||
descriptions.Add(new Description(subElement, this));
|
||||
break;
|
||||
default:
|
||||
DebugConsole.AddWarning($"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})");
|
||||
DebugConsole.AddWarning($"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})",
|
||||
contentPackage: element.ContentPackage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1046,7 +1054,8 @@ namespace Barotrauma
|
||||
var b = effects[j];
|
||||
if (a.MinStrength < b.MaxStrength && b.MinStrength < a.MaxStrength)
|
||||
{
|
||||
DebugConsole.AddWarning($"Affliction \"{Identifier}\" contains effects with overlapping strength ranges. Only one effect can be active at a time, meaning one of the effects won't work.");
|
||||
DebugConsole.AddWarning($"Affliction \"{Identifier}\" contains effects with overlapping strength ranges. Only one effect can be active at a time, meaning one of the effects won't work.",
|
||||
ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ namespace Barotrauma
|
||||
case "vitalitymultiplier":
|
||||
if (subElement.GetAttribute("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in character health config (" + characterHealth.Character.Name + ") - define vitality multipliers using affliction identifiers or types instead of names.");
|
||||
DebugConsole.ThrowError("Error in character health config (" + characterHealth.Character.Name + ") - define vitality multipliers using affliction identifiers or types instead of names.",
|
||||
contentPackage: element.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
var vitalityMultipliers = subElement.GetAttributeIdentifierArray("identifier", null) ?? subElement.GetAttributeIdentifierArray("identifiers", null);
|
||||
@@ -61,7 +62,8 @@ namespace Barotrauma
|
||||
VitalityMultipliers.Add(vitalityMultiplier, multiplier);
|
||||
if (AfflictionPrefab.Prefabs.None(p => p.Identifier == vitalityMultiplier))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions with the identifier \"{vitalityMultiplier}\". Did you mean to define the afflictions by type instead?");
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions with the identifier \"{vitalityMultiplier}\". Did you mean to define the afflictions by type instead?",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,13 +76,15 @@ namespace Barotrauma
|
||||
VitalityTypeMultipliers.Add(vitalityTypeMultiplier, multiplier);
|
||||
if (AfflictionPrefab.Prefabs.None(p => p.AfflictionType == vitalityTypeMultiplier))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions of the type \"{vitalityTypeMultiplier}\". Did you mean to define the afflictions by identifier instead?");
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions of the type \"{vitalityTypeMultiplier}\". Did you mean to define the afflictions by identifier instead?",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vitalityMultipliers == null && VitalityTypeMultipliers == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in character health config {characterHealth.Character.Name}: affliction identifier(s) or type(s) not defined in the \"VitalityMultiplier\" elements!");
|
||||
DebugConsole.ThrowError($"Error in character health config {characterHealth.Character.Name}: affliction identifier(s) or type(s) not defined in the \"VitalityMultiplier\" elements!",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -148,11 +152,6 @@ namespace Barotrauma
|
||||
return minVitality;
|
||||
}
|
||||
return vitality;
|
||||
|
||||
}
|
||||
private set
|
||||
{
|
||||
vitality = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +253,7 @@ namespace Barotrauma
|
||||
public CharacterHealth(Character character)
|
||||
{
|
||||
this.Character = character;
|
||||
Vitality = 100.0f;
|
||||
vitality = 100.0f;
|
||||
|
||||
DoesBleed = true;
|
||||
UseHealthWindow = false;
|
||||
@@ -271,7 +270,7 @@ namespace Barotrauma
|
||||
this.Character = character;
|
||||
InitIrremovableAfflictions();
|
||||
|
||||
Vitality = UnmodifiedMaxVitality;
|
||||
vitality = UnmodifiedMaxVitality;
|
||||
|
||||
minVitality = character.IsHuman ? -100.0f : 0.0f;
|
||||
|
||||
@@ -971,7 +970,7 @@ namespace Barotrauma
|
||||
|
||||
public void CalculateVitality()
|
||||
{
|
||||
Vitality = MaxVitality;
|
||||
vitality = MaxVitality;
|
||||
IsParalyzed = false;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
@@ -984,7 +983,7 @@ namespace Barotrauma
|
||||
{
|
||||
vitalityDecrease *= GetVitalityMultiplier(affliction, limbHealth);
|
||||
}
|
||||
Vitality -= vitalityDecrease;
|
||||
vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
|
||||
if (affliction.Strength >= affliction.Prefab.MaxStrength &&
|
||||
|
||||
@@ -79,12 +79,13 @@ namespace Barotrauma
|
||||
|
||||
public ref readonly ImmutableArray<Identifier> ParsedAfflictionTypes => ref parsedAfflictionTypes;
|
||||
|
||||
public DamageModifier(XElement element, string parentDebugName, bool checkErrors = true)
|
||||
public DamageModifier(ContentXElement element, string parentDebugName, bool checkErrors = true)
|
||||
{
|
||||
Deserialize(element);
|
||||
if (element.Attribute("afflictionnames") != null)
|
||||
if (element.GetAttribute("afflictionnames") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names.");
|
||||
DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (checkErrors)
|
||||
{
|
||||
@@ -108,12 +109,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
static void createWarningOrError(string msg)
|
||||
void createWarningOrError(string msg)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(msg);
|
||||
DebugConsole.ThrowError(msg, contentPackage: element.ContentPackage);
|
||||
#else
|
||||
DebugConsole.AddWarning(msg);
|
||||
DebugConsole.AddWarning(msg, contentPackage: element.ContentPackage);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ namespace Barotrauma
|
||||
public XElement Element { get; protected set; }
|
||||
|
||||
|
||||
public readonly List<(XElement element, float commonness)> ItemSets = new List<(XElement element, float commonness)>();
|
||||
public readonly List<(XElement element, float commonness)> CustomCharacterInfos = new List<(XElement element, float commonness)>();
|
||||
public readonly List<(ContentXElement element, float commonness)> ItemSets = new List<(ContentXElement element, float commonness)>();
|
||||
public readonly List<(ContentXElement element, float commonness)> CustomCharacterInfos = new List<(ContentXElement element, float commonness)>();
|
||||
|
||||
public readonly Identifier NpcSetIdentifier;
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Barotrauma
|
||||
var spawnItems = ToolBox.SelectWeightedRandom(ItemSets, it => it.commonness, randSync).element;
|
||||
if (spawnItems != null)
|
||||
{
|
||||
foreach (XElement itemElement in spawnItems.GetChildElements("item"))
|
||||
foreach (ContentXElement itemElement in spawnItems.GetChildElements("item"))
|
||||
{
|
||||
int amount = itemElement.GetAttributeInt("amount", 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
@@ -239,14 +239,15 @@ namespace Barotrauma
|
||||
return characterInfo;
|
||||
}
|
||||
|
||||
public static void InitializeItem(Character character, XElement itemElement, Submarine submarine, HumanPrefab humanPrefab, WayPoint spawnPoint = null, Item parentItem = null, bool createNetworkEvents = true)
|
||||
public static void InitializeItem(Character character, ContentXElement itemElement, Submarine submarine, HumanPrefab humanPrefab, WayPoint spawnPoint = null, Item parentItem = null, bool createNetworkEvents = true)
|
||||
{
|
||||
ItemPrefab itemPrefab;
|
||||
string itemIdentifier = itemElement.GetAttributeString("identifier", "");
|
||||
itemPrefab = MapEntityPrefab.FindByIdentifier(itemIdentifier.ToIdentifier()) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to spawn \"" + humanPrefab?.Identifier + "\" with the item \"" + itemIdentifier + "\". Matching item prefab not found.");
|
||||
DebugConsole.ThrowError("Tried to spawn \"" + humanPrefab?.Identifier + "\" with the item \"" + itemIdentifier + "\". Matching item prefab not found.",
|
||||
contentPackage: itemElement?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
Item item = new Item(itemPrefab, character.Position, null);
|
||||
@@ -301,7 +302,7 @@ namespace Barotrauma
|
||||
wifiComponent.TeamID = character.TeamID;
|
||||
}
|
||||
parentItem?.Combine(item, user: null);
|
||||
foreach (XElement childItemElement in itemElement.Elements())
|
||||
foreach (ContentXElement childItemElement in itemElement.Elements())
|
||||
{
|
||||
int amount = childItemElement.GetAttributeInt("amount", 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
|
||||
@@ -47,13 +47,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Job(XElement element)
|
||||
public Job(ContentXElement element)
|
||||
{
|
||||
Identifier identifier = element.GetAttributeIdentifier("identifier", "");
|
||||
JobPrefab p;
|
||||
if (!JobPrefab.Prefabs.ContainsKey(identifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find the job {identifier}. Giving the character a random job.");
|
||||
DebugConsole.ThrowError($"Could not find the job {identifier}. Giving the character a random job.",
|
||||
contentPackage: element.ContentPackage);
|
||||
p = JobPrefab.Random(Rand.RandSync.Unsynced);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Barotrauma
|
||||
Priority = element.GetAttributeFloat("priority", -1f);
|
||||
if (Priority < 0)
|
||||
{
|
||||
DebugConsole.AddWarning($"The 'priority' attribute is missing from the the item repair priorities definition in {element} of {file.Path}.");
|
||||
DebugConsole.AddWarning($"The 'priority' attribute is missing from the the item repair priorities definition in {element} of {file.Path}.",
|
||||
ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -266,7 +266,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"[AnimationParams] Failed to load an animation {a} at {selectedFile} of type {animType} for the character {speciesName}");
|
||||
DebugConsole.ThrowError($"[AnimationParams] Failed to load an animation {a} at {selectedFile} of type {animType} for the character {speciesName}",
|
||||
contentPackage: characterPrefab.ContentPackage);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
+2
-1
@@ -63,7 +63,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (!character.AnimController.CanWalk)
|
||||
{
|
||||
DebugConsole.ThrowError($"{character.SpeciesName} cannot use run animations!");
|
||||
DebugConsole.ThrowError($"{character.SpeciesName} cannot use run animations!",
|
||||
contentPackage: character.Prefab.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature live without water or does it die on dry land?"), Editable]
|
||||
public bool NeedsWater { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Note: non-humans with a human AI aren't fully supported. Enabling this on a non-human character may lead to issues.")]
|
||||
public bool UseHumanAI { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is this creature an artificial creature, like robot or machine that shouldn't be affected by afflictions that affect only organic creatures? Overrides DoesBleed."), Editable]
|
||||
public bool IsMachine { get; set; }
|
||||
|
||||
@@ -556,7 +559,8 @@ namespace Barotrauma
|
||||
DebugConsole.AddWarning($"Character \"{character.SpeciesName}\" has a negative crush depth. "+
|
||||
"Previously the crush depths were defined as display units (e.g. -30000 would correspond to 300 meters below the level), "+
|
||||
"but now they're in meters (e.g. 3000 would correspond to a depth of 3000 meters displayed on the nav terminal). "+
|
||||
$"Changing the crush depth from {CrushDepth} to {newCrushDepth}.");
|
||||
$"Changing the crush depth from {CrushDepth} to {newCrushDepth}.",
|
||||
element.ContentPackage);
|
||||
CrushDepth = newCrushDepth;
|
||||
}
|
||||
}
|
||||
@@ -708,7 +712,8 @@ namespace Barotrauma
|
||||
if (HasTag(tag))
|
||||
{
|
||||
target = null;
|
||||
DebugConsole.AddWarning($"Trying to add multiple targets with the same tag ('{tag}') defined! Only the first will be used!");
|
||||
DebugConsole.AddWarning($"Trying to add multiple targets with the same tag ('{tag}') defined! Only the first will be used!",
|
||||
targetElement.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -84,12 +84,14 @@ namespace Barotrauma
|
||||
doc = XMLExtensions.TryLoadXml(Path);
|
||||
if (doc == null)
|
||||
{
|
||||
DebugConsole.ThrowError("[EditableParams] The document is null! Failed to load the parameters.");
|
||||
DebugConsole.ThrowError("[EditableParams] The document is null! Failed to load the parameters.",
|
||||
contentPackage: file.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
if (MainElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("[EditableParams] The main element is null! Failed to load the parameters.");
|
||||
DebugConsole.ThrowError("[EditableParams] The main element is null! Failed to load the parameters.",
|
||||
contentPackage: file.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
IsLoaded = Deserialize(MainElement);
|
||||
|
||||
+28
-22
@@ -106,7 +106,8 @@ namespace Barotrauma
|
||||
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier == speciesName && (contentPackage == null || p.ContentFile.ContentPackage == contentPackage));
|
||||
if (prefab?.ConfigElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}' (content package {contentPackage?.Name ?? "null"})");
|
||||
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'",
|
||||
contentPackage: contentPackage);
|
||||
return string.Empty;
|
||||
}
|
||||
return GetFolder(prefab.ConfigElement, prefab.ContentFile.Path.Value);
|
||||
@@ -183,7 +184,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (error != null)
|
||||
{
|
||||
DebugConsole.ThrowError(error);
|
||||
DebugConsole.ThrowError(error,
|
||||
contentPackage: prefab?.ContentPackage);
|
||||
}
|
||||
}
|
||||
if (selectedFile == null)
|
||||
@@ -444,7 +446,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (source.MainElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("[RagdollParams] The source XML Element of the given RagdollParams is null!");
|
||||
DebugConsole.ThrowError("[RagdollParams] The source XML Element of the given RagdollParams is null!",
|
||||
contentPackage: source.MainElement?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
Deserialize(source.MainElement, alsoChildren: false);
|
||||
@@ -453,7 +456,8 @@ namespace Barotrauma
|
||||
// TODO: cannot currently undo joint/limb deletion.
|
||||
if (sourceSubParams.Count != subParams.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("[RagdollParams] The count of the sub params differs! Failed to revert to the previous snapshot! Please reset the ragdoll to undo the changes.");
|
||||
DebugConsole.ThrowError("[RagdollParams] The count of the sub params differs! Failed to revert to the previous snapshot! Please reset the ragdoll to undo the changes.",
|
||||
contentPackage: source.MainElement?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < subParams.Count; i++)
|
||||
@@ -461,7 +465,8 @@ namespace Barotrauma
|
||||
var subSubParams = subParams[i].SubParams;
|
||||
if (subSubParams.Count != sourceSubParams[i].SubParams.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("[RagdollParams] The count of the sub sub params differs! Failed to revert to the previous snapshot! Please reset the ragdoll to undo the changes.");
|
||||
DebugConsole.ThrowError("[RagdollParams] The count of the sub sub params differs! Failed to revert to the previous snapshot! Please reset the ragdoll to undo the changes.",
|
||||
contentPackage: source.MainElement?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
subParams[i].Deserialize(sourceSubParams[i].Element, recursive: false);
|
||||
@@ -890,14 +895,14 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
public DecorativeSprite DecorativeSprite { get; private set; }
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public override bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Deserialize(element, recursive);
|
||||
DecorativeSprite.SerializableProperties = SerializableProperty.DeserializeProperties(DecorativeSprite, element ?? Element);
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public override bool Serialize(XElement element = null, bool recursive = true)
|
||||
public override bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Serialize(element, recursive);
|
||||
SerializableProperty.SerializeProperties(DecorativeSprite, element ?? Element);
|
||||
@@ -985,7 +990,8 @@ namespace Barotrauma
|
||||
deformation = new PositionalDeformationParams(deformationElement);
|
||||
break;
|
||||
default:
|
||||
DebugConsole.ThrowError($"SpriteDeformationParams not implemented: '{typeName}'");
|
||||
DebugConsole.ThrowError($"SpriteDeformationParams not implemented: '{typeName}'",
|
||||
contentPackage: element.ContentPackage);
|
||||
break;
|
||||
}
|
||||
if (deformation != null)
|
||||
@@ -1000,14 +1006,14 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
public Dictionary<SpriteDeformationParams, XElement> Deformations { get; private set; }
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public override bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Deserialize(element, recursive);
|
||||
Deformations.ForEach(d => d.Key.SerializableProperties = SerializableProperty.DeserializeProperties(d.Key, d.Value));
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public override bool Serialize(XElement element = null, bool recursive = true)
|
||||
public override bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Serialize(element, recursive);
|
||||
Deformations.ForEach(d => SerializableProperty.SerializeProperties(d.Key, d.Value));
|
||||
@@ -1098,14 +1104,14 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public override bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Deserialize(element, recursive);
|
||||
LightSource.Deserialize(element ?? Element);
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public override bool Serialize(XElement element = null, bool recursive = true)
|
||||
public override bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Serialize(element, recursive);
|
||||
LightSource.Serialize(element ?? Element);
|
||||
@@ -1130,14 +1136,14 @@ namespace Barotrauma
|
||||
Attack = new Attack(element, ragdoll.SpeciesName.Value);
|
||||
}
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public override bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Deserialize(element, recursive);
|
||||
Attack.Deserialize(element ?? Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null");
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public override bool Serialize(XElement element = null, bool recursive = true)
|
||||
public override bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Serialize(element, recursive);
|
||||
Attack.Serialize(element ?? Element);
|
||||
@@ -1182,14 +1188,14 @@ namespace Barotrauma
|
||||
DamageModifier = new DamageModifier(element, ragdoll.SpeciesName.Value);
|
||||
}
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public override bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Deserialize(element, recursive);
|
||||
DamageModifier.Deserialize(element ?? Element);
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public override bool Serialize(XElement element = null, bool recursive = true)
|
||||
public override bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
base.Serialize(element, recursive);
|
||||
DamageModifier.Serialize(element ?? Element);
|
||||
@@ -1218,7 +1224,7 @@ namespace Barotrauma
|
||||
public virtual string Name { get; set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public ContentXElement Element { get; set; }
|
||||
public XElement OriginalElement { get; protected set; }
|
||||
public ContentXElement OriginalElement { get; protected set; }
|
||||
public List<SubParam> SubParams { get; set; } = new List<SubParam>();
|
||||
public RagdollParams Ragdoll { get; private set; }
|
||||
|
||||
@@ -1230,14 +1236,14 @@ namespace Barotrauma
|
||||
public SubParam(ContentXElement element, RagdollParams ragdoll)
|
||||
{
|
||||
Element = element;
|
||||
OriginalElement = new XElement(element);
|
||||
OriginalElement = new ContentXElement(element.ContentPackage, element);
|
||||
Ragdoll = ragdoll;
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
|
||||
public virtual bool Deserialize(XElement element = null, bool recursive = true)
|
||||
public virtual bool Deserialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
element = element ?? Element;
|
||||
element ??= Element;
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
if (recursive)
|
||||
{
|
||||
@@ -1246,9 +1252,9 @@ namespace Barotrauma
|
||||
return SerializableProperties != null;
|
||||
}
|
||||
|
||||
public virtual bool Serialize(XElement element = null, bool recursive = true)
|
||||
public virtual bool Serialize(ContentXElement element = null, bool recursive = true)
|
||||
{
|
||||
element = element ?? Element;
|
||||
element ??= Element;
|
||||
SerializableProperty.SerializeProperties(this, element, true);
|
||||
if (recursive)
|
||||
{
|
||||
|
||||
+3
-2
@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public AbilityCondition(CharacterTalent characterTalent, ContentXElement conditionElement)
|
||||
{
|
||||
this.characterTalent = characterTalent;
|
||||
this.characterTalent = characterTalent ?? throw new ArgumentNullException(nameof(characterTalent));
|
||||
character = characterTalent.Character;
|
||||
invert = conditionElement.GetAttributeBool("invert", false);
|
||||
}
|
||||
@@ -40,7 +40,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (!Enum.TryParse(targetTypeString, true, out TargetType targetType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid target type type \"" + targetTypeString + "\" in CharacterTalent (" + characterTalent.DebugIdentifier + ")");
|
||||
DebugConsole.ThrowError("Invalid target type type \"" + targetTypeString + "\" in CharacterTalent (" + characterTalent.DebugIdentifier + ")",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
targetTypes.Add(targetType);
|
||||
}
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ namespace Barotrauma.Abilities
|
||||
string weaponTypeStr = conditionElement.GetAttributeString("weapontype", "Any");
|
||||
if (!Enum.TryParse(weaponTypeStr, ignoreCase: true, out weapontype))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent.DebugIdentifier}\": \"{weaponTypeStr}\" is not a valid weapon type.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent.DebugIdentifier}\": \"{weaponTypeStr}\" is not a valid weapon type.",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -17,7 +17,7 @@ namespace Barotrauma.Abilities
|
||||
conditionElement.GetAttributeStringArray("targettypes",
|
||||
conditionElement.GetAttributeStringArray("targettype", Array.Empty<string>())));
|
||||
|
||||
foreach (XElement subElement in conditionElement.Elements())
|
||||
foreach (ContentXElement subElement in conditionElement.Elements())
|
||||
{
|
||||
if (subElement.NameAsIdentifier() == "conditional")
|
||||
{
|
||||
@@ -27,7 +27,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (!targetTypes.Any() && !conditionals.Any())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No target types or conditionals defined - the condition will match any character.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No target types or conditionals defined - the condition will match any character.",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -17,13 +17,15 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected void LogAbilityConditionError(AbilityObject abilityObject, Type expectedData)
|
||||
{
|
||||
DebugConsole.ThrowError($"Used data-reliant ability condition when data is incompatible! Expected {expectedData}, but received {abilityObject} in talent {characterTalent.DebugIdentifier}");
|
||||
DebugConsole.ThrowError($"Used data-reliant ability condition when data is incompatible! Expected {expectedData}, but received {abilityObject} in talent {characterTalent.DebugIdentifier}",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
protected abstract bool MatchesConditionSpecific(AbilityObject abilityObject);
|
||||
public override bool MatchesCondition()
|
||||
{
|
||||
DebugConsole.ThrowError($"Used data-reliant ability condition in a state-based ability in talent {characterTalent.DebugIdentifier}! This is not allowed.");
|
||||
DebugConsole.ThrowError($"Used data-reliant ability condition in a state-based ability in talent {characterTalent.DebugIdentifier}! This is not allowed.",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
public override bool MatchesCondition(AbilityObject abilityObject)
|
||||
|
||||
+2
-1
@@ -19,7 +19,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (identifiers.None() && tags.None() && category == MapEntityCategory.None)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No identifiers, tags or category defined.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No identifiers, tags or category defined.",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (!isAffiliated)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in AbilityConditionMission \"{characterTalent.DebugIdentifier}\" - \"{missionTypeString}\" is not a valid mission type.");
|
||||
DebugConsole.ThrowError($"Error in AbilityConditionMission \"{characterTalent.DebugIdentifier}\" - \"{missionTypeString}\" is not a valid mission type.",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
statIdentifier = conditionElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
||||
if (statIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"No stat identifier defined for {this} in talent {characterTalent.DebugIdentifier}!");
|
||||
DebugConsole.ThrowError($"No stat identifier defined for {this} in talent {characterTalent.DebugIdentifier}!",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
}
|
||||
string statTypeName = conditionElement.GetAttributeString("stattype", string.Empty);
|
||||
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, characterTalent.DebugIdentifier);
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ namespace Barotrauma.Abilities
|
||||
tag = conditionElement.GetAttributeIdentifier("tag", Identifier.Empty);
|
||||
if (tag.IsEmpty)
|
||||
{
|
||||
DebugConsole.AddWarning($"Error in talent \"{characterTalent.Prefab.OriginalName}\" - tag not defined in AbilityConditionHasStatusTag.");
|
||||
DebugConsole.AddWarning($"Error in talent \"{characterTalent.Prefab.OriginalName}\" - tag not defined in AbilityConditionHasStatusTag.",
|
||||
characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-8
@@ -26,7 +26,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public CharacterAbility(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement)
|
||||
{
|
||||
CharacterAbilityGroup = characterAbilityGroup;
|
||||
CharacterAbilityGroup = characterAbilityGroup ?? throw new ArgumentNullException(nameof(characterAbilityGroup));
|
||||
CharacterTalent = characterAbilityGroup.CharacterTalent;
|
||||
Character = CharacterTalent.Character;
|
||||
RequiresAlive = abilityElement.GetAttributeBool("requiresalive", true);
|
||||
@@ -59,7 +59,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected virtual void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: Ability {this} does not have an implementation for VerifyState! This ability does not work in interval ability groups.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: Ability {this} does not have an implementation for VerifyState! This ability does not work in interval ability groups.",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
public void ApplyAbilityEffect(AbilityObject abilityObject)
|
||||
@@ -76,17 +77,20 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected virtual void ApplyEffect()
|
||||
{
|
||||
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not have a definition for ApplyEffect in talent {CharacterTalent.DebugIdentifier}");
|
||||
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not have a definition for ApplyEffect in talent {CharacterTalent.DebugIdentifier}",
|
||||
CharacterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
protected virtual void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not take a parameter for ApplyEffect in talent {CharacterTalent.DebugIdentifier}");
|
||||
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not take a parameter for ApplyEffect in talent {CharacterTalent.DebugIdentifier}",
|
||||
CharacterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
protected void LogAbilityObjectMismatch()
|
||||
{
|
||||
DebugConsole.ThrowError($"Incompatible ability! Ability {this} is incompatitible with this type of ability effect type in talent {CharacterTalent.DebugIdentifier}");
|
||||
DebugConsole.ThrowError($"Incompatible ability! Ability {this} is incompatitible with this type of ability effect type in talent {CharacterTalent.DebugIdentifier}",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
// XML
|
||||
@@ -99,13 +103,18 @@ namespace Barotrauma.Abilities
|
||||
abilityType = Type.GetType("Barotrauma.Abilities." + type + "", false, true);
|
||||
if (abilityType == null)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")");
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")", e);
|
||||
if (errorMessages)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")", e,
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -118,7 +127,8 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while creating an instance of a CharacterAbility of the type " + abilityType + ".", e.InnerException);
|
||||
DebugConsole.ThrowError("Error while creating an instance of a CharacterAbility of the type " + abilityType + ".", e.InnerException,
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -30,7 +30,8 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - \"{limbTypeStr}\" is not a valid limb type.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - \"{limbTypeStr}\" is not a valid limb type.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ namespace Barotrauma.Abilities
|
||||
JobPrefab? apprenticeJob = GetApprenticeJob(Character, jobPrefabList);
|
||||
if (apprenticeJob is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityUnlockApprenticeshipTalentTree)}: Could not find apprentice job for character {Character.Name}");
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityUnlockApprenticeshipTalentTree)}: Could not find apprentice job for character {Character.Name}",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@
|
||||
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
||||
if (skillIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: skill identifier not defined.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: skill identifier not defined.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -16,7 +16,8 @@
|
||||
|
||||
if (afflictionId.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveAffliction - affliction identifier not set.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveAffliction - affliction identifier not set.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +28,8 @@
|
||||
var afflictionPrefab = AfflictionPrefab.Prefabs.Find(a => a.Identifier == afflictionId);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in CharacterAbilityGiveAffliction - could not find an affliction with the identifier \"{afflictionId}\".");
|
||||
DebugConsole.ThrowError($"Error in CharacterAbilityGiveAffliction - could not find an affliction with the identifier \"{afflictionId}\".",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
float strength = this.strength;
|
||||
|
||||
+9
-7
@@ -14,23 +14,25 @@ internal sealed class CharacterAbilityGiveExperience : CharacterAbility
|
||||
|
||||
if (amount == 0 && level == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - no exp amount or level defined in {nameof(CharacterAbilityGiveExperience)}.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - no exp amount or level defined in {nameof(CharacterAbilityGiveExperience)}.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
if (amount > 0 && level > 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - {nameof(CharacterAbilityGiveExperience)} defines both an exp amount and a level.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - {nameof(CharacterAbilityGiveExperience)} defines both an exp amount and a level.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyEffectSpecific(Character targetCharacter)
|
||||
{
|
||||
if (amount != 0)
|
||||
{
|
||||
targetCharacter.Info?.GiveExperience(amount);
|
||||
}
|
||||
if (level > 0)
|
||||
{
|
||||
targetCharacter.Info?.GiveExperience(targetCharacter.Info.GetExperienceRequiredForLevel(level));
|
||||
targetCharacter.Info?.GiveExperience(targetCharacter.Info.GetExperienceRequiredForLevel(level) + amount);
|
||||
}
|
||||
else if (amount != 0)
|
||||
{
|
||||
targetCharacter.Info?.GiveExperience(amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@
|
||||
|
||||
if (amount == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveMoney - amount of money set to 0.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveMoney - amount of money set to 0.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -28,7 +28,8 @@
|
||||
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
||||
if (statIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{CharacterTalent.DebugIdentifier}\" - stat identifier not defined.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{CharacterTalent.DebugIdentifier}\" - stat identifier not defined.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
string statTypeName = abilityElement.GetAttributeString("stattype", string.Empty);
|
||||
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, CharacterTalent.DebugIdentifier);
|
||||
|
||||
+4
-2
@@ -13,11 +13,13 @@ namespace Barotrauma.Abilities
|
||||
amount = abilityElement.GetAttributeFloat("amount", 0f);
|
||||
if (factionIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, faction identifier not defined.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, faction identifier not defined.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
if (amount == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of reputation to give is 0.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of reputation to give is 0.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -12,11 +12,13 @@
|
||||
|
||||
if (resistanceId.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.");
|
||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
if (MathUtils.NearlyEqual(multiplier, 1))
|
||||
{
|
||||
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - multiplier set to 1, which will do nothing.");
|
||||
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - multiplier set to 1, which will do nothing.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@
|
||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||
if (amount == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ namespace Barotrauma.Abilities
|
||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||
if (amount == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -16,11 +16,13 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (skillIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill identifier not defined in CharacterAbilityIncreaseSkill.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill identifier not defined in CharacterAbilityIncreaseSkill.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
if (MathUtils.NearlyEqual(skillIncrease, 0))
|
||||
{
|
||||
DebugConsole.AddWarning($"Possible error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill increase set to 0.");
|
||||
DebugConsole.AddWarning($"Possible error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill increase set to 0.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@ namespace Barotrauma.Abilities
|
||||
identifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||
if (identifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, identifier is empty in {nameof(CharacterAbilityMarkAsLooted)}.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, identifier is empty in {nameof(CharacterAbilityMarkAsLooted)}.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -15,11 +15,13 @@
|
||||
|
||||
if (resistanceId.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - resistance identifier not set in {nameof(CharacterAbilityModifyResistance)}.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - resistance identifier not set in {nameof(CharacterAbilityModifyResistance)}.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
if (MathUtils.NearlyEqual(multiplier, 1.0f))
|
||||
{
|
||||
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - resistance set to 1, which will do nothing.");
|
||||
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - resistance set to 1, which will do nothing.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@
|
||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
||||
if (MathUtils.NearlyEqual(addedValue, 0.0f) && MathUtils.NearlyEqual(multiplyValue, 1.0f))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityModifyValue)} - added value is 0 and multiplier is 1, the ability will do nothing.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityModifyValue)} - added value is 0 and multiplier is 1, the ability will do nothing.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -11,7 +11,8 @@
|
||||
amount = abilityElement.GetAttributeInt("amount", 1);
|
||||
if (itemIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - itemIdentifier not defined.");
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - itemIdentifier not defined.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +20,16 @@
|
||||
{
|
||||
if (itemIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot put item in inventory - itemIdentifier not defined.");
|
||||
DebugConsole.ThrowError("Cannot put item in inventory - itemIdentifier not defined.",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPrefab itemPrefab = ItemPrefab.Find(null, itemIdentifier);
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot put item in inventory - item prefab " + itemIdentifier + " not found.");
|
||||
DebugConsole.ThrowError("Cannot put item in inventory - item prefab " + itemIdentifier + " not found.",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < amount; i++)
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (afflictionId.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in {nameof(CharacterAbilityReduceAffliction)} - affliction identifier not set.");
|
||||
DebugConsole.ThrowError($"Error in {nameof(CharacterAbilityReduceAffliction)} - affliction identifier not set.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ namespace Barotrauma.Abilities
|
||||
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
||||
if (statIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityResetPermanentStat)} - statIdentifier is empty.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityResetPermanentStat)} - statIdentifier is empty.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ namespace Barotrauma.Abilities
|
||||
value = abilityElement.GetAttributeInt("value", 0);
|
||||
if (identifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilitySetMetadataInt)} - identifier is empty.");
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilitySetMetadataInt)} - identifier is empty.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -24,7 +24,8 @@ namespace Barotrauma.Abilities
|
||||
JobPrefab? apprentice = CharacterAbilityApplyStatusEffectsToApprenticeship.GetApprenticeJob(Character, JobPrefab.Prefabs.ToImmutableHashSet());
|
||||
if (apprentice is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityUnlockApprenticeshipTalentTree)}: Could not find apprentice job for character {Character.Name}");
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityUnlockApprenticeshipTalentTree)}: Could not find apprentice job for character {Character.Name}",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+31
-13
@@ -31,7 +31,7 @@ namespace Barotrauma.Abilities
|
||||
public CharacterAbilityGroup(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, ContentXElement abilityElementGroup)
|
||||
{
|
||||
AbilityEffectType = abilityEffectType;
|
||||
CharacterTalent = characterTalent;
|
||||
CharacterTalent = characterTalent ?? throw new ArgumentNullException(nameof(characterTalent));
|
||||
Character = CharacterTalent.Character;
|
||||
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
|
||||
foreach (var subElement in abilityElementGroup.Elements())
|
||||
@@ -55,7 +55,8 @@ namespace Barotrauma.Abilities
|
||||
case AbilityEffectType.OnDieToCharacter:
|
||||
if (characterAbilities.Any(a => a.RequiresAlive))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in talent {characterTalent}: an ability group has the type {AbilityEffectType.OnDieToCharacter}, but includes abilities that require the character to be alive, meaning they will never execute.");
|
||||
DebugConsole.AddWarning($"Potential error in talent {characterTalent}: an ability group has the type {AbilityEffectType.OnDieToCharacter}, but includes abilities that require the character to be alive, meaning they will never execute.",
|
||||
characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -90,7 +91,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (newCondition == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"AbilityCondition was not found in talent {CharacterTalent.DebugIdentifier}!");
|
||||
DebugConsole.ThrowError($"AbilityCondition was not found in talent {CharacterTalent.DebugIdentifier}!",
|
||||
contentPackage: conditionElement.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,7 +109,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (characterAbility == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!");
|
||||
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,7 +121,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (characterAbility == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!");
|
||||
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,13 +139,21 @@ namespace Barotrauma.Abilities
|
||||
conditionType = Type.GetType("Barotrauma.Abilities." + type + "", false, true);
|
||||
if (conditionType == null)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")");
|
||||
if (errorMessages)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")", e);
|
||||
if (errorMessages)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")", e,
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -154,13 +166,15 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ".", e.InnerException);
|
||||
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ".", e.InnerException,
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (newCondition == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ", instance was null");
|
||||
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ", instance was null",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -189,7 +203,8 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (newAbility == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Unable to create an ability for {characterTalent.DebugIdentifier}!");
|
||||
DebugConsole.ThrowError($"Unable to create an ability for {characterTalent.DebugIdentifier}!",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -200,7 +215,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (statusEffectElements == null)
|
||||
{
|
||||
DebugConsole.ThrowError("StatusEffect list was not found in talent " + characterTalent.DebugIdentifier);
|
||||
DebugConsole.ThrowError("StatusEffect list was not found in talent " + characterTalent.DebugIdentifier,
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -233,7 +249,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (afflictionElements == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Affliction list was not found in talent " + characterTalent.DebugIdentifier);
|
||||
DebugConsole.ThrowError("Affliction list was not found in talent " + characterTalent.DebugIdentifier,
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -248,7 +265,8 @@ namespace Barotrauma.Abilities
|
||||
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in CharacterTalent (" + characterTalent.DebugIdentifier + ") - Affliction prefab with the identifier \"" + afflictionIdentifier + "\" not found.");
|
||||
DebugConsole.ThrowError("Error in CharacterTalent (" + characterTalent.DebugIdentifier + ") - Affliction prefab with the identifier \"" + afflictionIdentifier + "\" not found.",
|
||||
contentPackage: characterTalent.Prefab.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ namespace Barotrauma
|
||||
|
||||
public CharacterTalent(TalentPrefab talentPrefab, Character character)
|
||||
{
|
||||
Character = character;
|
||||
|
||||
Prefab = talentPrefab;
|
||||
Character = character ?? throw new ArgumentNullException(nameof(character));
|
||||
Prefab = talentPrefab ?? throw new ArgumentNullException(nameof(talentPrefab));
|
||||
var element = talentPrefab.ConfigElement;
|
||||
DebugIdentifier = talentPrefab.OriginalName;
|
||||
|
||||
@@ -46,7 +45,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"No recipe identifier defined for talent {DebugIdentifier}");
|
||||
DebugConsole.ThrowError($"No recipe identifier defined for talent {DebugIdentifier}",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
break;
|
||||
case "addedstoreitem":
|
||||
@@ -56,7 +56,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"No store item identifier defined for talent {DebugIdentifier}");
|
||||
DebugConsole.ThrowError($"No store item identifier defined for talent {DebugIdentifier}",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -146,11 +147,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (!Enum.TryParse(abilityEffectTypeString, true, out AbilityEffectType abilityEffectType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid ability effect type \"" + abilityEffectTypeString + "\" in CharacterTalent (" + characterTalent.DebugIdentifier + ")");
|
||||
DebugConsole.ThrowError("Invalid ability effect type \"" + abilityEffectTypeString + "\" in CharacterTalent (" + characterTalent.DebugIdentifier + ")",
|
||||
contentPackage: characterTalent?.Prefab?.ContentPackage);
|
||||
}
|
||||
if (abilityEffectType == AbilityEffectType.Undefined)
|
||||
{
|
||||
DebugConsole.ThrowError("Ability effect type not defined in CharacterTalent (" + characterTalent.DebugIdentifier + ")");
|
||||
DebugConsole.ThrowError("Ability effect type not defined in CharacterTalent (" + characterTalent.DebugIdentifier + ")",
|
||||
contentPackage: characterTalent?.Prefab?.ContentPackage);
|
||||
}
|
||||
|
||||
return abilityEffectType;
|
||||
|
||||
@@ -84,7 +84,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while loading talent migration for talent \"{Identifier}\".", e);
|
||||
DebugConsole.ThrowError($"Error while loading talent migration for talent \"{Identifier}\".", e,
|
||||
element?.ContentPackage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace Barotrauma
|
||||
|
||||
if (Identifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"No job defined for talent tree in \"{file.Path}\"!");
|
||||
DebugConsole.ThrowError($"No job defined for talent tree in \"{file.Path}\"!",
|
||||
contentPackage: element.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,7 +305,8 @@ namespace Barotrauma
|
||||
|
||||
if (RequiredTalents > MaxChosenTalents)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - MaxChosenTalents is larger than RequiredTalents.");
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - MaxChosenTalents is larger than RequiredTalents.",
|
||||
contentPackage: talentOptionsElement.ContentPackage);
|
||||
}
|
||||
|
||||
HashSet<Identifier> identifiers = new HashSet<Identifier>();
|
||||
@@ -333,11 +335,13 @@ namespace Barotrauma
|
||||
|
||||
if (RequiredTalents > talentIdentifiers.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - completing a stage of the tree requires more talents than there are in the stage.");
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - completing a stage of the tree requires more talents than there are in the stage.",
|
||||
contentPackage: talentOptionsElement.ContentPackage);
|
||||
}
|
||||
if (MaxChosenTalents > talentIdentifiers.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - maximum number of talents to choose is larger than the number of talents.");
|
||||
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - maximum number of talents to choose is larger than the number of talents.",
|
||||
contentPackage: talentOptionsElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user