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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -50,7 +50,8 @@ namespace Barotrauma
|
||||
if (identifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"No identifier defined for the affliction '{elementName}' in file '{Path}'");
|
||||
$"No identifier defined for the affliction '{elementName}' in file '{Path}'",
|
||||
contentPackage: element?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,12 +61,13 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.NewMessage(
|
||||
$"Overriding an affliction or a buff with the identifier '{identifier}' using the file '{Path}'",
|
||||
Color.Yellow);
|
||||
Color.MediumPurple);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Duplicate affliction: '{identifier}' defined in {elementName} of '{Path}'");
|
||||
$"Duplicate affliction: '{identifier}' defined in {elementName} of '{Path}'",
|
||||
contentPackage: element?.ContentPackage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -17,12 +17,12 @@ namespace Barotrauma
|
||||
XDocument doc = XMLExtensions.TryLoadXml(Path);
|
||||
if (doc == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Loading character file failed: {Path}");
|
||||
DebugConsole.ThrowError($"Loading character file failed: {Path}", contentPackage: ContentPackage);
|
||||
return;
|
||||
}
|
||||
if (CharacterPrefab.Prefabs.AllPrefabs.Any(kvp => kvp.Value.Any(cf => cf?.ContentFile == this)))
|
||||
{
|
||||
DebugConsole.ThrowError($"Duplicate path: {Path}");
|
||||
DebugConsole.ThrowError($"Duplicate path: {Path}", contentPackage: ContentPackage);
|
||||
return;
|
||||
}
|
||||
var mainElement = doc.Root.FromPackage(ContentPackage);
|
||||
@@ -69,7 +69,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to preload a ragdoll file for the character \"{characterPrefab.Name}\"", e);
|
||||
DebugConsole.ThrowError($"Failed to preload a ragdoll file for the character \"{characterPrefab.Name}\"", e,
|
||||
contentPackage: characterPrefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -76,7 +76,8 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.AddWarning(
|
||||
$"The content type \"TraitorMission\" in content package \"{package.Name}\" is no longer supported." +
|
||||
$" Traitor missions should be implemented using the scripted event system and the content type TraitorEvents.");
|
||||
$" Traitor missions should be implemented using the scripted event system and the content type TraitorEvents.",
|
||||
package);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"GenericPrefabFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}");
|
||||
DebugConsole.ThrowError($"GenericPrefabFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}", contentPackage: ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -2,6 +2,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
[NotSyncedInMultiplayer]
|
||||
sealed class NPCConversationsFile : ContentFile
|
||||
{
|
||||
public NPCConversationsFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { }
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"OrdersFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}");
|
||||
DebugConsole.ThrowError($"OrdersFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}",
|
||||
contentPackage: parentElement?.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -65,7 +65,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"RandomEventsFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}");
|
||||
DebugConsole.ThrowError($"RandomEventsFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}",
|
||||
contentPackage: parentElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
[NotSyncedInMultiplayer]
|
||||
public sealed class TextFile : ContentFile
|
||||
{
|
||||
public TextFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { }
|
||||
@@ -37,6 +38,13 @@ namespace Barotrauma
|
||||
if (newHashSet.Count != 0) { TextManager.TextPacks.TryAdd(kvp.Key, newHashSet); }
|
||||
}
|
||||
TextManager.IncrementLanguageVersion();
|
||||
if (!TextManager.TextPacks.ContainsKey(GameSettings.CurrentConfig.Language))
|
||||
{
|
||||
DebugConsole.AddWarning($"The language {GameSettings.CurrentConfig.Language} is no longer available. Switching to {TextManager.DefaultLanguage}...");
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.Language = TextManager.DefaultLanguage;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Sort()
|
||||
|
||||
+3
-2
@@ -283,7 +283,7 @@ namespace Barotrauma
|
||||
catch (Exception e)
|
||||
{
|
||||
var innermost = e.GetInnermost();
|
||||
DebugConsole.LogError($"Failed to load \"{filesToLoad[i].Path}\": {innermost.Message}\n{innermost.StackTrace}");
|
||||
DebugConsole.LogError($"Failed to load \"{filesToLoad[i].Path}\": {innermost.Message}\n{innermost.StackTrace}", contentPackage: this);
|
||||
exception = e;
|
||||
}
|
||||
if (exception != null)
|
||||
@@ -391,7 +391,8 @@ namespace Barotrauma
|
||||
|
||||
DebugConsole.AddWarning(
|
||||
$"The following errors occurred while loading the content package \"{Name}\". The package might not work correctly.\n" +
|
||||
string.Join('\n', FatalLoadErrors.Select(errorToStr)));
|
||||
string.Join('\n', FatalLoadErrors.Select(errorToStr)),
|
||||
this);
|
||||
|
||||
static string errorToStr(LoadError error)
|
||||
=> error.ToString();
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma
|
||||
public Identifier[] GetAttributeIdentifierArray(string key, Identifier[] def, bool trim = true) => Element.GetAttributeIdentifierArray(key, def, trim);
|
||||
[return: NotNullIfNotNull("def")]
|
||||
public ImmutableHashSet<Identifier> GetAttributeIdentifierImmutableHashSet(string key, ImmutableHashSet<Identifier>? def, bool trim = true) => Element.GetAttributeIdentifierImmutableHashSet(key, def, trim);
|
||||
|
||||
[return: NotNullIfNotNull(parameterName: "def")]
|
||||
public string? GetAttributeString(string key, string? def) => Element.GetAttributeString(key, def);
|
||||
public string GetAttributeStringUnrestricted(string key, string def) => Element.GetAttributeStringUnrestricted(key, def);
|
||||
public string[]? GetAttributeStringArray(string key, string[]? def, bool convertToLowerInvariant = false) => Element.GetAttributeStringArray(key, def, convertToLowerInvariant);
|
||||
@@ -90,6 +90,7 @@ namespace Barotrauma
|
||||
public Color? GetAttributeColor(string key) => Element.GetAttributeColor(key);
|
||||
public Color[]? GetAttributeColorArray(string key, Color[]? def) => Element.GetAttributeColorArray(key, def);
|
||||
public Rectangle GetAttributeRect(string key, in Rectangle def) => Element.GetAttributeRect(key, def);
|
||||
public Version GetAttributeVersion(string key, Version def) => Element.GetAttributeVersion(key, def);
|
||||
public T GetAttributeEnum<T>(string key, in T def) where T : struct, Enum => Element.GetAttributeEnum(key, def);
|
||||
public (T1, T2) GetAttributeTuple<T1, T2>(string key, in (T1, T2) def) => Element.GetAttributeTuple(key, def);
|
||||
public (T1, T2)[] GetAttributeTupleArray<T1, T2>(string key, in (T1, T2)[] def) => Element.GetAttributeTupleArray(key, def);
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Barotrauma.IO;
|
||||
@@ -40,8 +41,8 @@ namespace Barotrauma
|
||||
{
|
||||
public partial class Command
|
||||
{
|
||||
public readonly string[] names;
|
||||
public readonly string help;
|
||||
public readonly ImmutableArray<Identifier> Names;
|
||||
public readonly string Help;
|
||||
|
||||
public Action<string[]> OnExecute;
|
||||
|
||||
@@ -57,8 +58,8 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public Command(string name, string help, Action<string[]> onExecute, Func<string[][]> getValidArgs = null, bool isCheat = false)
|
||||
{
|
||||
names = name.Split('|');
|
||||
this.help = help;
|
||||
Names = name.Split('|').ToIdentifiers().ToImmutableArray();
|
||||
this.Help = help;
|
||||
|
||||
this.OnExecute = onExecute;
|
||||
|
||||
@@ -76,7 +77,8 @@ namespace Barotrauma
|
||||
#endif
|
||||
if (!allowCheats && !CheatsEnabled && IsCheat)
|
||||
{
|
||||
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
||||
NewMessage(
|
||||
$"You need to enable cheats using the command \"enablecheats\" before you can use the command \"{Names.First()}\".", Color.Red);
|
||||
#if USE_STEAM
|
||||
NewMessage("Enabling cheats will disable Steam achievements during this play session.", Color.Red);
|
||||
#endif
|
||||
@@ -88,7 +90,7 @@ namespace Barotrauma
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return names[0].GetHashCode();
|
||||
return Names.First().GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +166,7 @@ namespace Barotrauma
|
||||
|
||||
private static void AssignOnExecute(string names, Action<string[]> onExecute)
|
||||
{
|
||||
var matchingCommand = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
|
||||
var matchingCommand = commands.Find(c => c.Names.Intersect(names.Split('|').ToIdentifiers()).Any());
|
||||
if (matchingCommand == null)
|
||||
{
|
||||
throw new Exception("AssignOnExecute failed. Command matching the name(s) \"" + names + "\" not found.");
|
||||
@@ -187,13 +189,13 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Command c in commands)
|
||||
{
|
||||
if (string.IsNullOrEmpty(c.help)) continue;
|
||||
if (string.IsNullOrEmpty(c.Help)) continue;
|
||||
ShowHelpMessage(c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var matchingCommand = commands.Find(c => c.names.Any(name => name == args[0]));
|
||||
var matchingCommand = commands.Find(c => c.Names.Any(name => name == args[0]));
|
||||
if (matchingCommand == null)
|
||||
{
|
||||
NewMessage("Command " + args[0] + " not found.", Color.Red);
|
||||
@@ -208,7 +210,7 @@ namespace Barotrauma
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
commands.SelectMany(c => c.names).ToArray(),
|
||||
commands.SelectMany(c => c.Names).Select(n => n.Value).ToArray(),
|
||||
Array.Empty<string>()
|
||||
};
|
||||
}));
|
||||
@@ -403,7 +405,7 @@ namespace Barotrauma
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
|
||||
commands.Select(c => c.names[0]).Union(new string[]{ "All" }).ToArray()
|
||||
commands.Select(c => c.Names.First().Value).Union(new []{ "All" }).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -415,7 +417,7 @@ namespace Barotrauma
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
|
||||
commands.Select(c => c.names[0]).Union(new string[]{ "All" }).ToArray()
|
||||
commands.Select(c => c.Names.First().Value).Union(new []{ "All" }).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1133,7 +1135,7 @@ namespace Barotrauma
|
||||
}
|
||||
},null));
|
||||
|
||||
commands.Add(new Command("teleportsub", "teleportsub [start/end/cursor]: Teleport the submarine to the position of the cursor, or the start or end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) =>
|
||||
commands.Add(new Command("teleportsub", "teleportsub [start/end/endoutpost/cursor]: Teleport the submarine to the position of the cursor, or the start or end of the level. The 'endoutpost' argument also automatically docks the sub with the outpost at the end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) =>
|
||||
{
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
|
||||
@@ -1159,7 +1161,7 @@ namespace Barotrauma
|
||||
}
|
||||
Submarine.MainSub.SetPosition(pos);
|
||||
}
|
||||
else
|
||||
else if (args[0].Equals("end", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Level.Loaded == null)
|
||||
{
|
||||
@@ -1172,13 +1174,29 @@ namespace Barotrauma
|
||||
pos -= Vector2.UnitY * (Submarine.MainSub.Borders.Height + Level.Loaded.EndOutpost.Borders.Height) / 2;
|
||||
}
|
||||
Submarine.MainSub.SetPosition(pos);
|
||||
}
|
||||
else if (args[0].Equals("endoutpost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.EndExitPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
|
||||
|
||||
var submarineDockingPort = DockingPort.List.FirstOrDefault(d => d.Item.Submarine == Submarine.MainSub);
|
||||
if (Level.Loaded?.EndOutpost == null)
|
||||
{
|
||||
NewMessage("Can't teleport the sub to the end outpost (no outpost at the end of the level).", Color.Red);
|
||||
return;
|
||||
}
|
||||
var outpostDockingPort = DockingPort.List.FirstOrDefault(d => d.Item.Submarine == Level.Loaded.EndOutpost);
|
||||
if (submarineDockingPort != null && outpostDockingPort != null)
|
||||
{
|
||||
submarineDockingPort.Dock(outpostDockingPort);
|
||||
}
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
new string[] { "start", "end", "cursor" }
|
||||
new string[] { "start", "end", "endoutpost", "cursor" }
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
@@ -1959,7 +1977,7 @@ namespace Barotrauma
|
||||
|
||||
InitProjectSpecific();
|
||||
|
||||
commands.Sort((c1, c2) => c1.names[0].CompareTo(c2.names[0]));
|
||||
commands.Sort((c1, c2) => c1.Names.First().CompareTo(c2.Names.First()));
|
||||
}
|
||||
|
||||
public static string AutoComplete(string command, int increment = 1)
|
||||
@@ -1970,14 +1988,14 @@ namespace Barotrauma
|
||||
//if an argument is given or the last character is a space, attempt to autocomplete the argument
|
||||
if (args.Length > 0 || (splitCommand.Length > 0 && command.Last() == ' '))
|
||||
{
|
||||
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommand[0]));
|
||||
if (matchingCommand == null || matchingCommand.GetValidArgs == null) return command;
|
||||
Command matchingCommand = commands.Find(c => c.Names.Contains(splitCommand[0].ToIdentifier()));
|
||||
if (matchingCommand?.GetValidArgs == null) { return command; }
|
||||
|
||||
int autoCompletedArgIndex = args.Length > 0 && command.Last() != ' ' ? args.Length - 1 : args.Length;
|
||||
|
||||
//get all valid arguments for the given command
|
||||
string[][] allArgs = matchingCommand.GetValidArgs();
|
||||
if (allArgs == null || allArgs.GetLength(0) < autoCompletedArgIndex + 1) return command;
|
||||
if (allArgs == null || allArgs.GetLength(0) < autoCompletedArgIndex + 1) { return command; }
|
||||
|
||||
if (string.IsNullOrEmpty(currentAutoCompletedCommand))
|
||||
{
|
||||
@@ -1989,7 +2007,7 @@ namespace Barotrauma
|
||||
currentAutoCompletedCommand.Trim().Length <= arg.Length &&
|
||||
arg.Substring(0, currentAutoCompletedCommand.Trim().Length).ToLower() == currentAutoCompletedCommand.Trim().ToLower()).ToArray();
|
||||
|
||||
if (validArgs.Length == 0) return command;
|
||||
if (validArgs.Length == 0) { return command; }
|
||||
|
||||
currentAutoCompletedIndex = MathUtils.PositiveModulo(currentAutoCompletedIndex + increment, validArgs.Length);
|
||||
string autoCompletedArg = validArgs[currentAutoCompletedIndex];
|
||||
@@ -2010,13 +2028,13 @@ namespace Barotrauma
|
||||
currentAutoCompletedCommand = command;
|
||||
}
|
||||
|
||||
List<string> matchingCommands = new List<string>();
|
||||
List<Identifier> matchingCommands = new List<Identifier>();
|
||||
foreach (Command c in commands)
|
||||
{
|
||||
foreach (string name in c.names)
|
||||
foreach (var name in c.Names)
|
||||
{
|
||||
if (currentAutoCompletedCommand.Length > name.Length) continue;
|
||||
if (currentAutoCompletedCommand == name.Substring(0, currentAutoCompletedCommand.Length))
|
||||
if (currentAutoCompletedCommand.Length > name.Value.Length) { continue; }
|
||||
if (name.StartsWith(currentAutoCompletedCommand))
|
||||
{
|
||||
matchingCommands.Add(name);
|
||||
}
|
||||
@@ -2026,7 +2044,7 @@ namespace Barotrauma
|
||||
if (matchingCommands.Count == 0) return command;
|
||||
|
||||
currentAutoCompletedIndex = MathUtils.PositiveModulo(currentAutoCompletedIndex + increment, matchingCommands.Count);
|
||||
return matchingCommands[currentAutoCompletedIndex];
|
||||
return matchingCommands[currentAutoCompletedIndex].Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2064,9 +2082,9 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
string firstCommand = splitCommand[0].ToLowerInvariant();
|
||||
Identifier firstCommand = splitCommand[0].ToIdentifier();
|
||||
|
||||
if (!firstCommand.Equals("admin", StringComparison.OrdinalIgnoreCase))
|
||||
if (firstCommand != "admin")
|
||||
{
|
||||
NewCommand(command);
|
||||
}
|
||||
@@ -2074,7 +2092,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
Command matchingCommand = commands.Find(c => c.names.Contains(firstCommand));
|
||||
Command matchingCommand = commands.Find(c => c.Names.Contains(firstCommand));
|
||||
if (matchingCommand == null)
|
||||
{
|
||||
//if the command is not defined client-side, we'll relay it anyway because it may be a custom command at the server's side
|
||||
@@ -2095,12 +2113,12 @@ namespace Barotrauma
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!IsCommandPermitted(splitCommand[0].ToLowerInvariant(), GameMain.Client))
|
||||
if (!IsCommandPermitted(firstCommand, GameMain.Client))
|
||||
{
|
||||
#if DEBUG
|
||||
AddWarning($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\". Executing the command anyway because this is a debug build.");
|
||||
AddWarning($"You're not permitted to use the command \"{firstCommand}\". Executing the command anyway because this is a debug build.");
|
||||
#else
|
||||
ThrowError($"You're not permitted to use the command \"{splitCommand[0].ToLowerInvariant()}\"!");
|
||||
ThrowError($"You're not permitted to use the command \"{firstCommand}\"!");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
@@ -2110,7 +2128,7 @@ namespace Barotrauma
|
||||
bool commandFound = false;
|
||||
foreach (Command c in commands)
|
||||
{
|
||||
if (!c.names.Contains(firstCommand)) { continue; }
|
||||
if (!c.Names.Contains(firstCommand)) { continue; }
|
||||
c.Execute(splitCommand.Skip(1).ToArray());
|
||||
commandFound = true;
|
||||
break;
|
||||
@@ -2397,8 +2415,13 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogError(string msg, Color? color = null)
|
||||
public static void LogError(string msg, Color? color = null, ContentPackage contentPackage = null)
|
||||
{
|
||||
if (contentPackage != null)
|
||||
{
|
||||
string colorStr = XMLExtensions.ToStringHex(Color.MediumPurple);
|
||||
msg = $"‖color:{colorStr}‖[{contentPackage.Name}]‖color:end‖ {msg}";
|
||||
}
|
||||
color ??= Color.Red;
|
||||
NewMessage(msg, color.Value, isCommand: false, isError: true);
|
||||
}
|
||||
@@ -2515,7 +2538,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Command FindCommand(string commandName) => commands.Find(c => c.names.Any(n => n.Equals(commandName, StringComparison.OrdinalIgnoreCase)));
|
||||
public static Command FindCommand(string commandName) => commands.Find(c => c.Names.Contains(commandName.ToIdentifier()));
|
||||
|
||||
public static void Log(LocalizedString message) => Log(message?.Value);
|
||||
|
||||
@@ -2532,8 +2555,13 @@ namespace Barotrauma
|
||||
ThrowError(error.Value, e, createMessageBox, appendStackTrace);
|
||||
}
|
||||
|
||||
public static void ThrowError(string error, Exception e = null, bool createMessageBox = false, bool appendStackTrace = false)
|
||||
public static void ThrowError(string error, Exception e = null, ContentPackage contentPackage = null, bool createMessageBox = false, bool appendStackTrace = false)
|
||||
{
|
||||
if (contentPackage != null)
|
||||
{
|
||||
string color = XMLExtensions.ToStringHex(Color.MediumPurple);
|
||||
error = $"‖color:{color}‖[{contentPackage.Name}]‖color:end‖ {error}";
|
||||
}
|
||||
if (e != null)
|
||||
{
|
||||
error += " {" + e.Message + "}\n";
|
||||
@@ -2547,7 +2575,7 @@ namespace Barotrauma
|
||||
error += "\n\nInner exception: " + innermost.Message + "\n";
|
||||
if (innermost.StackTrace != null)
|
||||
{
|
||||
error += innermost.StackTrace.CleanupStackTrace(); ;
|
||||
error += innermost.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2580,10 +2608,16 @@ namespace Barotrauma
|
||||
errorMsg);
|
||||
}
|
||||
|
||||
public static void AddWarning(string warning)
|
||||
public static void AddWarning(string warning, ContentPackage contentPackage = null)
|
||||
{
|
||||
warning = $"WARNING: {warning}";
|
||||
if (contentPackage != null)
|
||||
{
|
||||
string color = XMLExtensions.ToStringHex(Color.MediumPurple);
|
||||
warning = $"‖color:{color}‖[{contentPackage.Name}]‖color:end‖ {warning}";
|
||||
}
|
||||
System.Diagnostics.Debug.WriteLine(warning);
|
||||
NewMessage($"WARNING: {warning}", Color.Yellow);
|
||||
NewMessage(warning, Color.Yellow);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (prefab.ConfigElement.GetAttribute("itemname") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in ArtifactEvent - use item identifier instead of the name of the item.");
|
||||
DebugConsole.ThrowError("Error in ArtifactEvent - use item identifier instead of the name of the item.",
|
||||
contentPackage: prefab?.ContentPackage);
|
||||
string itemName = prefab.ConfigElement.GetAttributeString("itemname", "");
|
||||
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
@@ -44,11 +45,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
string itemIdentifier = prefab.ConfigElement.GetAttributeString("itemidentifier", "");
|
||||
itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||
Identifier itemIdentifier = prefab.ConfigElement.GetAttributeIdentifier("itemidentifier", Identifier.Empty);
|
||||
itemPrefab = MapEntityPrefab.FindByIdentifier(itemIdentifier) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in ArtifactEvent - couldn't find an item prefab with the identifier " + itemIdentifier);
|
||||
DebugConsole.ThrowError("Error in ArtifactEvent - couldn't find an item prefab with the identifier " + itemIdentifier,
|
||||
contentPackage: prefab?.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma
|
||||
|
||||
public Event(EventPrefab prefab)
|
||||
{
|
||||
this.prefab = prefab;
|
||||
this.prefab = prefab ?? throw new ArgumentNullException(nameof(prefab));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<ContentFile> GetFilesToPreload()
|
||||
|
||||
+6
-3
@@ -14,12 +14,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no target tag! This will cause the check to automatically succeed.",
|
||||
contentPackage: parentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
Conditional = PropertyConditional.FromXElement(element, IsNotTargetTagAttribute).FirstOrDefault();
|
||||
if (Conditional == null)
|
||||
{
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckConditionalAction with no valid PropertyConditional! This will cause the check to automatically succeed.",
|
||||
contentPackage: parentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
static bool IsNotTargetTagAttribute(XAttribute attribute) => attribute.NameAsIdentifier() != "targettag";
|
||||
@@ -46,7 +48,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
DebugConsole.LogError($"{nameof(CheckConditionalAction)} error: {GetEventName()} uses a {nameof(CheckConditionalAction)} but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.");
|
||||
DebugConsole.LogError($"{nameof(CheckConditionalAction)} error: {GetEventName()} uses a {nameof(CheckConditionalAction)} but no valid target was found for tag \"{TargetTag}\"! This will cause the check to automatically succeed.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
if (target == null || Conditional == null)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Barotrauma
|
||||
Condition = element.GetAttributeString("value", string.Empty)!;
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentEvent.Prefab.Identifier}\". CheckDataAction with no condition set ({element}).");
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentEvent.Prefab.Identifier}\". CheckDataAction with no condition set ({element}).",
|
||||
contentPackage: element?.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +43,8 @@ namespace Barotrauma
|
||||
Condition = element.GetAttributeString("value", string.Empty)!;
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).");
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentDebugString}\". CheckDataAction with no condition set ({element}).",
|
||||
contentPackage: element?.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +61,8 @@ namespace Barotrauma
|
||||
(Operator, string value) = PropertyConditional.ExtractComparisonOperatorFromConditionString(Condition);
|
||||
if (Operator == PropertyConditional.ComparisonOperatorType.None)
|
||||
{
|
||||
DebugConsole.ThrowError($"{Condition} is invalid, it should start with an operator followed by a boolean or a floating point value.");
|
||||
DebugConsole.ThrowError($"{Condition} is invalid, it should start with an operator followed by a boolean or a floating point value.",
|
||||
contentPackage: ParentEvent?.Prefab?.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ namespace Barotrauma
|
||||
ItemIdentifiers.None() &&
|
||||
TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.");
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
checkPercentage = element.GetAttribute(nameof(RequiredConditionalMatchPercentage)) is not null;
|
||||
if (checkPercentage && conditionals.None())
|
||||
@@ -86,7 +87,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (Amount != 1 && checkPercentage)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Cannot define both '{Amount}' and '{RequiredConditionalMatchPercentage}' in {nameof(CheckItemAction)}.");
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Cannot define both '{Amount}' and '{RequiredConditionalMatchPercentage}' in {nameof(CheckItemAction)}.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Barotrauma
|
||||
var targetCharacters = ParentEvent.GetTargets(TargetTag);
|
||||
if (targetCharacters.None())
|
||||
{
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target characters were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.");
|
||||
DebugConsole.LogError($"CheckConditionalAction error: {GetEventName()} uses a CheckOrderAction but no valid target characters were found for tag \"{TargetTag}\"! This will cause the check to automatically fail.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
foreach (var t in targetCharacters)
|
||||
|
||||
+4
-4
@@ -1,7 +1,5 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -31,7 +29,8 @@ namespace Barotrauma
|
||||
}
|
||||
default:
|
||||
{
|
||||
DebugConsole.ThrowError("CheckReputationAction requires a \"TargetType\" but none were specified.");
|
||||
DebugConsole.ThrowError("CheckReputationAction requires a \"TargetType\" but none were specified.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +40,8 @@ namespace Barotrauma
|
||||
|
||||
protected override bool GetBool(CampaignMode campaignMode)
|
||||
{
|
||||
DebugConsole.ThrowError("Boolean comparison cannot be applied to reputations.");
|
||||
DebugConsole.ThrowError("Boolean comparison cannot be applied to reputations.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,13 +87,13 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
void Error(string errorMsg)
|
||||
{
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg, contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
#else
|
||||
|
||||
void Error(string errorMsg)
|
||||
{
|
||||
DebugConsole.LogError(errorMsg);
|
||||
DebugConsole.LogError(errorMsg, contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot use the action {nameof(CheckTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.");
|
||||
DebugConsole.ThrowError($"Cannot use the action {nameof(CheckTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -16,7 +16,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (parentEvent is not TraitorEvent)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\" - {nameof(CheckTraitorVoteAction)} can only be used in traitor events.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\" - {nameof(CheckTraitorVoteAction)} can only be used in traitor events.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\"" +
|
||||
$" - unrecognized child element \"Replace\".");
|
||||
$" - unrecognized child element \"Replace\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,14 +61,16 @@ namespace Barotrauma
|
||||
}
|
||||
if (MinAmount > MaxAmount && MaxAmount > -1)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {MinAmount} is larger than {MaxAmount} in {nameof(CountTargetsAction)}.");
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {MinAmount} is larger than {MaxAmount} in {nameof(CountTargetsAction)}.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MinPercentageRelativeToTarget < 0.0f && MaxPercentageRelativeToTarget < 0.0f)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Comparing to another target, but neither {nameof(MinPercentageRelativeToTarget)} or {nameof(MaxPercentageRelativeToTarget)} is set.");
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Comparing to another target, but neither {nameof(MinPercentageRelativeToTarget)} or {nameof(MaxPercentageRelativeToTarget)} is set.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (e.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event prefab \"{scriptedEvent.Prefab.Identifier}\". Status effect configured as a sub action (text: \"{Text}\"). Please configure status effects as child elements of a StatusEffectAction.");
|
||||
DebugConsole.ThrowError($"Error in event prefab \"{scriptedEvent.Prefab.Identifier}\". Status effect configured as a sub action (text: \"{Text}\"). Please configure status effects as child elements of a StatusEffectAction.",
|
||||
contentPackage: elem.ContentPackage);
|
||||
continue;
|
||||
}
|
||||
var action = Instantiate(scriptedEvent, e);
|
||||
@@ -102,7 +103,7 @@ namespace Barotrauma
|
||||
|
||||
public EventAction(ScriptedEvent parentEvent, ContentXElement element)
|
||||
{
|
||||
ParentEvent = parentEvent;
|
||||
ParentEvent = parentEvent ?? throw new ArgumentNullException(nameof(parentEvent));
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
|
||||
@@ -147,7 +148,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find an {nameof(EventAction)} class of the type \"{element.Name}\".");
|
||||
DebugConsole.ThrowError($"Could not find an {nameof(EventAction)} class of the type \"{element.Name}\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -162,7 +164,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
|
||||
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString(),
|
||||
contentPackage: element.ContentPackage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (Id == Identifier.Empty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no id.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no id.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
//append the target tag so logs targeted to different players don't interfere with each other even if they use the same Id
|
||||
Id = (Id.ToString() + TargetTag).ToIdentifier();
|
||||
@@ -42,7 +43,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (Text.IsNullOrEmpty())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no text set ({element}).");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\". {nameof(EventLogAction)} with no text set ({element}).",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+4
-2
@@ -49,13 +49,15 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\""+
|
||||
$" - {nameof(TextTag)} will do nothing unless the action triggers a message box or a video.");
|
||||
$" - {nameof(TextTag)} will do nothing unless the action triggers a message box or a video.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (element.GetChildElement("Replace") != null)
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\"" +
|
||||
$" - unrecognized child element \"Replace\".");
|
||||
$" - unrecognized child element \"Replace\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveExpAction)} without a target tag (the action needs to know whose skill to check).");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveExpAction)} without a target tag (the action needs to know whose skill to check).",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveSkillExpAction)} without a target tag (the action needs to know whose skill to check).");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": {nameof(GiveSkillExpAction)} without a target tag (the action needs to know whose skill to check).",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,11 +37,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (MissionIdentifier.IsEmpty && MissionTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (!MissionIdentifier.IsEmpty && !MissionTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
LocationTypes = element.GetAttributeIdentifierArray("locationtype", Array.Empty<Identifier>()).ToImmutableArray();
|
||||
random = new MTRandom(parentEvent.RandomSeed);
|
||||
@@ -103,11 +105,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (!MissionIdentifier.IsEmpty)
|
||||
{
|
||||
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier,
|
||||
invokingContentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else if (!MissionTag.IsEmpty)
|
||||
{
|
||||
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag, random);
|
||||
unlockedMission = unlockLocation.UnlockMissionByTag(MissionTag, random,
|
||||
invokingContentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
if (campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
@@ -139,7 +143,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" (LocationType: {string.Join(", ", LocationTypes)}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})");
|
||||
DebugConsole.AddWarning($"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" (LocationType: {string.Join(", ", LocationTypes)}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})",
|
||||
ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace Barotrauma
|
||||
State = element.GetAttributeInt("value", State);
|
||||
if (MissionIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -43,7 +43,8 @@ namespace Barotrauma
|
||||
var faction = campaign.Factions.Find(f => f.Prefab.Identifier == Faction);
|
||||
if (faction == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{Faction}\".");
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{Faction}\".",
|
||||
contentPackage: ParentEvent?.Prefab?.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -55,7 +56,8 @@ namespace Barotrauma
|
||||
var secondaryFaction = campaign.Factions.Find(f => f.Prefab.Identifier == SecondaryFaction);
|
||||
if (secondaryFaction == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{SecondaryFaction}\".");
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a faction with the identifier \"{SecondaryFaction}\".",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -67,7 +69,8 @@ namespace Barotrauma
|
||||
var locationType = LocationType.Prefabs.Find(lt => lt.Identifier == Type);
|
||||
if (locationType == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a location type with the identifier \"{Type}\".");
|
||||
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a location type with the identifier \"{Type}\".",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else if (!location.LocationTypeChangesBlocked)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Barotrauma
|
||||
var enums = Enum.GetValues(typeof(CharacterTeamType)).Cast<CharacterTeamType>();
|
||||
if (!enums.Contains(TeamID))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in {nameof(NPCChangeTeamAction)} in the event {ParentEvent.Prefab.Identifier}. \"{TeamID}\" is not a valid Team ID. Valid values are {string.Join(',', Enum.GetNames(typeof(CharacterTeamType)))}.");
|
||||
DebugConsole.ThrowError($"Error in {nameof(NPCChangeTeamAction)} in the event {ParentEvent.Prefab.Identifier}. \"{TeamID}\" is not a valid Team ID. Valid values are {string.Join(',', Enum.GetNames(typeof(CharacterTeamType)))}.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (Chance >= 1.0f)
|
||||
{
|
||||
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 1.0 (100%) or more, the action will always succeed.");
|
||||
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 1.0 (100%) or more, the action will always succeed.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
else if (Chance <= 0.0f)
|
||||
{
|
||||
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 0 or less, the action will never succeed.");
|
||||
DebugConsole.ThrowError($"Incorrectly configured RNG Action in event \"{parentEvent.Prefab.Identifier}\". Probability is 0 or less, the action will never succeed.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Faction with the identifier \"{Identifier}\" was not found.");
|
||||
DebugConsole.ThrowError($"Faction with the identifier \"{Identifier}\" was not found.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -66,7 +67,8 @@ namespace Barotrauma
|
||||
}
|
||||
default:
|
||||
{
|
||||
DebugConsole.ThrowError("ReputationAction requires a \"TargetType\" but none were specified.");
|
||||
DebugConsole.ThrowError("ReputationAction requires a \"TargetType\" but none were specified.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot use the action {nameof(SetTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.");
|
||||
DebugConsole.ThrowError($"Cannot use the action {nameof(SetTraitorEventStateAction)} in the event \"{parentEvent.Prefab.Identifier}\" because it's not a traitor event.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetTag.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": SkillCheckAction without a target tag (the action needs to know whose skill to check).");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": SkillCheckAction without a target tag (the action needs to know whose skill to check).",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in even \"{(parentEvent.Prefab?.Identifier.ToString() ?? "unknown")}\". " +
|
||||
$"The attribute \"submarinetype\" is not valid in {nameof(SpawnAction)}. Did you mean {nameof(SpawnLocation)}?");
|
||||
$"The attribute \"submarinetype\" is not valid in {nameof(SpawnAction)}. Did you mean {nameof(SpawnLocation)}?",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +234,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (MapEntityPrefab.FindByIdentifier(ItemIdentifier) is not ItemPrefab itemPrefab)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)");
|
||||
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -256,7 +258,8 @@ namespace Barotrauma
|
||||
|
||||
if (spawnInventory == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.");
|
||||
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,19 @@ namespace Barotrauma
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
private bool targetNotFound = false;
|
||||
/// <summary>
|
||||
/// If the action tags some entities directly (not trying to find targets on the fly),
|
||||
/// we may be able to determine that targets can not be found even if we'd recheck
|
||||
/// </summary>
|
||||
private bool cantFindTargets = false;
|
||||
|
||||
/// <summary>
|
||||
/// If the TagAction adds a target predicate (a criteria that keeps finding targets on the fly),
|
||||
/// we must keep checking if targets have been found to determine if the action can continue or not
|
||||
/// </summary>
|
||||
private bool mustRecheckTargets = false;
|
||||
|
||||
private bool taggingDone = false;
|
||||
|
||||
public TagAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
@@ -198,6 +210,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(tag, predicate);
|
||||
mustRecheckTargets = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +218,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
if (ChoosePercentage > 0.0f)
|
||||
@@ -230,7 +243,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +263,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
ParentEvent.AddTarget(tag, entities.GetRandomUnsynced());
|
||||
@@ -260,26 +273,30 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished || targetNotFound) { return; }
|
||||
if (isFinished || cantFindTargets) { return; }
|
||||
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
|
||||
targetNotFound = false;
|
||||
foreach (string entry in criteriaSplit)
|
||||
if (!taggingDone)
|
||||
{
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
cantFindTargets = false;
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
foreach (string entry in criteriaSplit)
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage,
|
||||
contentPackage: ParentEvent.Prefab?.ContentPackage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
}
|
||||
}
|
||||
taggingDone = true;
|
||||
}
|
||||
|
||||
if (ContinueIfNoTargetsFound)
|
||||
@@ -288,7 +305,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !targetNotFound;
|
||||
if (mustRecheckTargets)
|
||||
{
|
||||
isFinished = ParentEvent.GetTargets(Tag).Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !cantFindTargets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
var eventPrefab = EventSet.GetEventPrefab(Identifier);
|
||||
if (eventPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.");
|
||||
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user