(ad567dea) v0.9.7.1
This commit is contained in:
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// How long does it take for the ai target to fade out if not kept alive.
|
||||
/// </summary>
|
||||
public float FadeOutTime { get; private set; } = 2;
|
||||
public float FadeOutTime { get; private set; } = 1;
|
||||
|
||||
public bool Static { get; private set; }
|
||||
public bool StaticSound { get; private set; }
|
||||
@@ -92,7 +92,7 @@ namespace Barotrauma
|
||||
public string SonarLabel;
|
||||
public string SonarIconIdentifier;
|
||||
|
||||
public bool Enabled => SoundRange > 0 || SightRange > 0;
|
||||
public bool Enabled = true;
|
||||
|
||||
public float MinSoundRange, MinSightRange;
|
||||
public float MaxSoundRange = 100000, MaxSightRange = 100000;
|
||||
@@ -195,7 +195,7 @@ namespace Barotrauma
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (Enabled && !Static && FadeOutTime > 0)
|
||||
if (!Static && FadeOutTime > 0)
|
||||
{
|
||||
// The aitarget goes silent/invisible if the components don't keep it active
|
||||
if (!StaticSight)
|
||||
|
||||
@@ -1017,63 +1017,58 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttackingLimb != null && AttackingLimb.attack.Retreat)
|
||||
Vector2 offset = Character.SimPosition - steeringLimb.SimPosition;
|
||||
// Offset so that we don't overshoot the movement
|
||||
Vector2 steerPos = attackSimPos + offset;
|
||||
|
||||
if (SteeringManager is IndoorsSteeringManager pathSteering)
|
||||
{
|
||||
UpdateFallBack(attackWorldPos, deltaTime, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 offset = Character.SimPosition - steeringLimb.SimPosition;
|
||||
// Offset so that we don't overshoot the movement
|
||||
Vector2 steerPos = attackSimPos + offset;
|
||||
if (SteeringManager is IndoorsSteeringManager pathSteering)
|
||||
if (pathSteering.CurrentPath != null)
|
||||
{
|
||||
if (pathSteering.CurrentPath != null)
|
||||
// Attack doors
|
||||
if (canAttackSub)
|
||||
{
|
||||
// Attack doors
|
||||
if (canAttackSub)
|
||||
// If the target is in the same hull, there shouldn't be any doors blocking the path
|
||||
if (targetCharacter == null || targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
// If the target is in the same hull, there shouldn't be any doors blocking the path
|
||||
if (targetCharacter == null || targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
|
||||
if (door != null && !door.IsOpen)
|
||||
{
|
||||
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
|
||||
if (door != null && !door.IsOpen)
|
||||
if (door.Item.AiTarget != null && SelectedAiTarget != door.Item.AiTarget)
|
||||
{
|
||||
if (door.Item.AiTarget != null && SelectedAiTarget != door.Item.AiTarget)
|
||||
{
|
||||
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
|
||||
return;
|
||||
}
|
||||
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Steer towards the target if in the same room and swimming
|
||||
if ((Character.AnimController.InWater || pursue) && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(steerPos, 2);
|
||||
// Switch to Idle when cannot reach the target and if cannot damage the walls
|
||||
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Steer towards the target if in the same room and swimming
|
||||
if ((Character.AnimController.InWater || pursue) && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(steerPos, 5);
|
||||
SteeringManager.SteeringSeek(steerPos, 2);
|
||||
// Switch to Idle when cannot reach the target and if cannot damage the walls
|
||||
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(steerPos, 10);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||
SteeringManager.SteeringSeek(steerPos, 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(steerPos, 10);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||
}
|
||||
|
||||
if (canAttack)
|
||||
{
|
||||
if (!UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb))
|
||||
@@ -1277,9 +1272,12 @@ namespace Barotrauma
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
bool canAttack = attacker.Submarine == Character.Submarine && canAttackCharacters || attacker.Submarine != null && canAttackSub;
|
||||
if (Character.Params.AI.AttackWhenProvoked && canAttack)
|
||||
if (Character.Params.AI.AttackWhenProvoked)
|
||||
{
|
||||
ChangeTargetState(attacker, AIState.Attack, 100);
|
||||
if (canAttack)
|
||||
{
|
||||
ChangeTargetState(attacker, AIState.Attack, 100);
|
||||
}
|
||||
}
|
||||
else if (!AIParams.HasTag(attacker.SpeciesName))
|
||||
{
|
||||
@@ -1289,14 +1287,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (!AIParams.HasTag("stronger"))
|
||||
{
|
||||
ChangeTargetState(attacker, canAttack ? AIState.PassiveAggressive : AIState.Escape, 100);
|
||||
ChangeTargetState(attacker, AIState.Escape, 100);
|
||||
}
|
||||
}
|
||||
else if (enemyAI.CombatStrength < CombatStrength)
|
||||
{
|
||||
if (!AIParams.HasTag("weaker"))
|
||||
{
|
||||
ChangeTargetState(attacker, canAttack ? AIState.PassiveAggressive : AIState.Escape, 100);
|
||||
ChangeTargetState(attacker, canAttack ? AIState.Attack : AIState.Escape, 100);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1307,7 +1305,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
ChangeTargetState(attacker, canAttack ? AIState.PassiveAggressive : AIState.Escape, 100);
|
||||
ChangeTargetState(attacker, AIState.Escape, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ namespace Barotrauma
|
||||
|| ObjectiveManager.IsCurrentObjective<AIObjectiveFindSafety>()
|
||||
|| ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn);
|
||||
bool removeDivingSuit = !Character.AnimController.HeadInWater && oxygenLow;
|
||||
AIObjectiveGoTo gotoObjective = ObjectiveManager.GetActiveObjective<AIObjectiveGoTo>();
|
||||
AIObjectiveGoTo gotoObjective = ObjectiveManager.CurrentOrder as AIObjectiveGoTo;
|
||||
if (!removeDivingSuit)
|
||||
{
|
||||
bool targetHasNoSuit = gotoObjective != null && gotoObjective.mimic && !HasDivingSuit(gotoObjective.Target as Character);
|
||||
@@ -536,16 +536,14 @@ namespace Barotrauma
|
||||
Hull targetHull = null;
|
||||
if (Character.CurrentHull != null)
|
||||
{
|
||||
bool isFighting = ObjectiveManager.HasActiveObjective<AIObjectiveCombat>();
|
||||
bool isFleeing = ObjectiveManager.HasActiveObjective<AIObjectiveFindSafety>();
|
||||
foreach (var hull in VisibleHulls)
|
||||
{
|
||||
foreach (Character target in Character.CharacterList)
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (target.CurrentHull != hull || !target.Enabled) { continue; }
|
||||
if (AIObjectiveFightIntruders.IsValidTarget(target, Character))
|
||||
if (c.CurrentHull != hull || !c.Enabled) { continue; }
|
||||
if (AIObjectiveFightIntruders.IsValidTarget(c, Character))
|
||||
{
|
||||
if (AddTargets<AIObjectiveFightIntruders, Character>(Character, target) && newOrder == null)
|
||||
if (AddTargets<AIObjectiveFightIntruders, Character>(Character, c) && newOrder == null)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportintruders");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
@@ -562,48 +560,42 @@ namespace Barotrauma
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
if (!isFighting)
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
foreach (var gap in hull.ConnectedGaps)
|
||||
if (c.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveRescueAll.IsValidTarget(c, Character))
|
||||
{
|
||||
if (AIObjectiveFixLeaks.IsValidTarget(gap, Character))
|
||||
if (AddTargets<AIObjectiveRescueAll, Character>(c, Character) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
|
||||
{
|
||||
if (AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap) && newOrder == null && !gap.IsRoomToRoom)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportbreach");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
var orderPrefab = Order.GetPrefab("requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
if (!isFleeing)
|
||||
}
|
||||
foreach (var gap in hull.ConnectedGaps)
|
||||
{
|
||||
if (AIObjectiveFixLeaks.IsValidTarget(gap, Character))
|
||||
{
|
||||
foreach (Character target in Character.CharacterList)
|
||||
if (AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap) && newOrder == null && !gap.IsRoomToRoom)
|
||||
{
|
||||
if (target.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveRescueAll.IsValidTarget(target, Character))
|
||||
{
|
||||
if (AddTargets<AIObjectiveRescueAll, Character>(Character, target) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
var orderPrefab = Order.GetPrefab("reportbreach");
|
||||
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
|
||||
{
|
||||
if (item.Repairables.All(r => item.ConditionPercentage > r.AIRepairThreshold)) { continue; }
|
||||
if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>())
|
||||
{
|
||||
if (item.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
|
||||
{
|
||||
if (item.Repairables.All(r => item.ConditionPercentage > r.AIRepairThreshold)) { continue; }
|
||||
if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>())
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab("reportbrokendevices");
|
||||
newOrder = new Order(orderPrefab, hull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
var orderPrefab = Order.GetPrefab("reportbrokendevices");
|
||||
newOrder = new Order(orderPrefab, hull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
|
||||
targetHull = hull;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -658,7 +650,7 @@ namespace Barotrauma
|
||||
// Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate).
|
||||
return;
|
||||
}
|
||||
if (!attacker.IsPlayer && attacker.AIController != null && attacker.AIController.Enabled)
|
||||
if (!attacker.IsRemotePlayer && Character.Controlled != attacker && attacker.AIController != null && attacker.AIController.Enabled)
|
||||
{
|
||||
// Don't retaliate on damage done by friendly ai, because we know that it's accidental
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
@@ -672,8 +664,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
float dmgPercentage = MathUtils.Percentage(damage, Character.CharacterHealth.Vitality);
|
||||
if (dmgPercentage < 10)
|
||||
float currentVitality = Character.CharacterHealth.Vitality;
|
||||
float dmgPercentage = damage / currentVitality * 100;
|
||||
if (dmgPercentage < currentVitality / 10)
|
||||
{
|
||||
// Don't retaliate on minor (accidental) dmg done by characters that are in the same team
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
@@ -718,6 +711,7 @@ namespace Barotrauma
|
||||
|
||||
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true)
|
||||
{
|
||||
SetOrderProjSpecific(order, option);
|
||||
CurrentOrderOption = option;
|
||||
CurrentOrder = order;
|
||||
objectiveManager.SetOrder(order, option, orderGiver);
|
||||
@@ -758,6 +752,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void SetOrderProjSpecific(Order order, string option);
|
||||
|
||||
public override void SelectTarget(AITarget target)
|
||||
{
|
||||
SelectedAiTarget = target;
|
||||
@@ -810,11 +806,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public static bool HasDivingMask(Character character, float conditionPercentage = 0) => HasItem(character, "divingmask", "oxygensource", conditionPercentage);
|
||||
|
||||
public static bool HasItem(Character character, string tagOrIdentifier, string containedTag = null, float conditionPercentage = 0)
|
||||
public static bool HasItem(Character character, string identifier, string containedTag, float conditionPercentage = 0)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
if (character.Inventory == null) { return false; }
|
||||
var item = character.Inventory.FindItemByIdentifier(tagOrIdentifier) ?? character.Inventory.FindItemByTag(tagOrIdentifier);
|
||||
var item = character.Inventory.FindItemByIdentifier(identifier) ?? character.Inventory.FindItemByTag(identifier);
|
||||
return item != null &&
|
||||
item.ConditionPercentage > conditionPercentage &&
|
||||
character.HasEquippedItem(item) &&
|
||||
@@ -938,7 +934,7 @@ namespace Barotrauma
|
||||
visibleHulls = VisibleHulls;
|
||||
}
|
||||
// TODO: should we calculate the visible hulls for each hull? -> could be a bit heavy.
|
||||
bool ignoreFire = objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
||||
bool ignoreFire = ObjectiveManager.IsCurrentObjective<AIObjectiveExtinguishFires>() || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
||||
bool ignoreWater = HasDivingSuit(character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingMask(character);
|
||||
bool ignoreEnemies = ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||
@@ -1026,23 +1022,15 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int CountCrew(Character character, Func<HumanAIController, bool> predicate = null, bool onlyActive = true, bool onlyBots = false)
|
||||
public static int CountCrew(Character character, Func<HumanAIController, bool> predicate = null)
|
||||
{
|
||||
if (character == null) { return 0; }
|
||||
int count = 0;
|
||||
foreach (var other in Character.CharacterList)
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (onlyActive && !IsActive(other))
|
||||
if (FilterCrewMember(character, c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (onlyBots && other.IsPlayer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (FilterCrewMember(character, other))
|
||||
{
|
||||
if (predicate == null || predicate(other.AIController as HumanAIController))
|
||||
if (predicate == null || predicate(c.AIController as HumanAIController))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
@@ -1070,7 +1058,7 @@ namespace Barotrauma
|
||||
public void DoForEachCrewMember(Action<HumanAIController> action) => DoForEachCrewMember(Character, action);
|
||||
public bool IsTrueForAnyCrewMember(Func<HumanAIController, bool> predicate) => IsTrueForAnyCrewMember(Character, predicate);
|
||||
public bool IsTrueForAllCrewMembers(Func<HumanAIController, bool> predicate) => IsTrueForAllCrewMembers(Character, predicate);
|
||||
public int CountCrew(Func<HumanAIController, bool> predicate = null, bool onlyActive = true, bool onlyBots = false) => CountCrew(Character, predicate, onlyActive, onlyBots);
|
||||
public int CountCrew(Func<HumanAIController, bool> predicate = null) => CountCrew(Character, predicate);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Returns true if any node in the path is in stairs
|
||||
/// </summary>
|
||||
public bool PathHasStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
|
||||
public bool InStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
|
||||
|
||||
public bool IsNextNodeLadder => GetNextLadder() != null;
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Barotrauma
|
||||
|
||||
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null)
|
||||
{
|
||||
bool needsNewPath = character.Params.PathFinderPriority > 0.5f && (currentPath == null || currentPath.Unreachable || currentPath.Finished || Vector2.DistanceSquared(target, currentTarget) > 1);
|
||||
bool needsNewPath = character.Params.PathFinderPriority > 0.5f && (currentPath == null || currentPath.Unreachable || currentPath.NextNode == null || Vector2.DistanceSquared(target, currentTarget) > 1);
|
||||
//find a new path if one hasn't been found yet or the target is different from the current target
|
||||
if (needsNewPath || findPathTimer < -1.0f)
|
||||
{
|
||||
@@ -308,7 +308,7 @@ namespace Barotrauma
|
||||
currentPath.SkipToNextNode();
|
||||
}
|
||||
}
|
||||
else if (!IsNextLadderSameAsCurrent)
|
||||
else
|
||||
{
|
||||
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
|
||||
Vector2 colliderSize = collider.GetSize();
|
||||
@@ -530,6 +530,7 @@ namespace Barotrauma
|
||||
if (node.Waypoint != null && node.Waypoint.CurrentHull != null)
|
||||
{
|
||||
var hull = node.Waypoint.CurrentHull;
|
||||
|
||||
if (hull.FireSources.Count > 0)
|
||||
{
|
||||
foreach (FireSource fs in hull.FireSources)
|
||||
@@ -537,14 +538,9 @@ namespace Barotrauma
|
||||
penalty += fs.Size.X * 10.0f;
|
||||
}
|
||||
}
|
||||
if (character.NeedsAir && hull.WaterVolume / hull.Rect.Width > 100.0f)
|
||||
{
|
||||
penalty += 500.0f;
|
||||
}
|
||||
if (character.PressureProtection < 10.0f && hull.WaterVolume > hull.Volume)
|
||||
{
|
||||
penalty += 1000.0f;
|
||||
}
|
||||
|
||||
if (character.NeedsAir && hull.WaterVolume / hull.Rect.Width > 100.0f) penalty += 500.0f;
|
||||
if (character.PressureProtection < 10.0f && hull.WaterVolume > hull.Volume) penalty += 1000.0f;
|
||||
}
|
||||
|
||||
return penalty;
|
||||
|
||||
@@ -136,10 +136,9 @@ namespace Barotrauma
|
||||
string allowedJobsStr = element.GetAttributeString("allowedjobs", "");
|
||||
foreach (string allowedJobIdentifier in allowedJobsStr.Split(','))
|
||||
{
|
||||
string key = allowedJobIdentifier.ToLowerInvariant();
|
||||
if (JobPrefab.Prefabs.ContainsKey(key))
|
||||
if (JobPrefab.Prefabs.ContainsKey(allowedJobIdentifier.ToLowerInvariant()))
|
||||
{
|
||||
AllowedJobs.Add(JobPrefab.Prefabs[key]);
|
||||
AllowedJobs.Add(JobPrefab.Prefabs[allowedJobIdentifier.ToLowerInvariant()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,18 +32,6 @@ namespace Barotrauma
|
||||
public virtual bool UnequipItems => false;
|
||||
|
||||
protected readonly List<AIObjective> subObjectives = new List<AIObjective>();
|
||||
private float _cumulatedDevotion;
|
||||
protected float CumulatedDevotion
|
||||
{
|
||||
get { return _cumulatedDevotion; }
|
||||
set { _cumulatedDevotion = MathHelper.Clamp(value, 0, MaxDevotion); }
|
||||
}
|
||||
|
||||
protected virtual float MaxDevotion => 10;
|
||||
|
||||
/// <summary>
|
||||
/// Final priority value after all calculations.
|
||||
/// </summary>
|
||||
public float Priority { get; set; }
|
||||
public float PriorityModifier { get; private set; } = 1;
|
||||
public readonly Character character;
|
||||
@@ -71,7 +59,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual bool IsLoop { get; set; }
|
||||
public IEnumerable<AIObjective> SubObjectives => subObjectives;
|
||||
public AIObjective CurrentSubObjective => subObjectives.FirstOrDefault();
|
||||
|
||||
private readonly List<AIObjective> all = new List<AIObjective>();
|
||||
public IEnumerable<AIObjective> GetSubObjectivesRecursive(bool includingSelf = false)
|
||||
@@ -99,7 +86,7 @@ namespace Barotrauma
|
||||
|
||||
public AIObjective GetActiveObjective()
|
||||
{
|
||||
var subObjective = CurrentSubObjective;
|
||||
var subObjective = SubObjectives.FirstOrDefault();
|
||||
return subObjective == null ? this : subObjective.GetActiveObjective();
|
||||
}
|
||||
|
||||
@@ -170,8 +157,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!AllowSubObjectiveSorting) { return; }
|
||||
if (subObjectives.None()) { return; }
|
||||
subObjectives.ForEach(so => so.GetPriority());
|
||||
subObjectives.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
subObjectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
|
||||
if (ConcurrentObjectives)
|
||||
{
|
||||
subObjectives.ForEach(so => so.SortSubObjectives());
|
||||
@@ -182,23 +168,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this only when the priority needs to be recalculated. Use the cached Priority property when you don't need to recalculate.
|
||||
/// </summary>
|
||||
public virtual float GetPriority()
|
||||
{
|
||||
Priority = CumulatedDevotion * PriorityModifier;
|
||||
return Priority;
|
||||
}
|
||||
|
||||
private void UpdateDevotion(float deltaTime)
|
||||
{
|
||||
var currentObjective = objectiveManager.CurrentObjective;
|
||||
if (currentObjective != null && (currentObjective == this || currentObjective.subObjectives.Any(so => so == this)))
|
||||
{
|
||||
CumulatedDevotion += Devotion * PriorityModifier * deltaTime;
|
||||
}
|
||||
}
|
||||
public virtual float GetPriority() => Priority * PriorityModifier;
|
||||
|
||||
public virtual bool IsDuplicate<T>(T otherObjective) where T : AIObjective => otherObjective.Option == Option;
|
||||
|
||||
@@ -210,7 +180,14 @@ namespace Barotrauma
|
||||
}
|
||||
else if (objectiveManager.WaitTimer <= 0)
|
||||
{
|
||||
UpdateDevotion(deltaTime);
|
||||
if (objectiveManager.CurrentObjective != null)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this || objectiveManager.CurrentObjective.subObjectives.Any(so => so == this))
|
||||
{
|
||||
Priority += Devotion * PriorityModifier * deltaTime;
|
||||
}
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
}
|
||||
subObjectives.ForEach(so => so.Update(deltaTime));
|
||||
}
|
||||
@@ -287,7 +264,6 @@ namespace Barotrauma
|
||||
|
||||
public virtual void OnDeselected()
|
||||
{
|
||||
CumulatedDevotion = 0;
|
||||
Deselected?.Invoke();
|
||||
}
|
||||
|
||||
@@ -306,7 +282,6 @@ namespace Barotrauma
|
||||
isCompleted = false;
|
||||
hasBeenChecked = false;
|
||||
_abandon = false;
|
||||
CumulatedDevotion = 0;
|
||||
}
|
||||
|
||||
protected abstract void Act(float deltaTime);
|
||||
|
||||
+2
-6
@@ -100,11 +100,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
Priority = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
||||
return Priority;
|
||||
}
|
||||
public override float GetPriority() => (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -143,7 +139,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (seekAmmunition == null)
|
||||
{
|
||||
if (Mode != CombatMode.Retreat && TryArm() && Enemy != null && !Enemy.Removed)
|
||||
if (TryArm() && Enemy != null && !Enemy.Removed)
|
||||
{
|
||||
OperateWeapon(deltaTime);
|
||||
}
|
||||
|
||||
+9
@@ -74,6 +74,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage > ConditionLevel;
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
|
||||
+9
@@ -54,6 +54,15 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Check() => IsCompleted;
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
Item itemToDecontain = targetItem ?? sourceContainer.Inventory.FindItem(i => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)), recursive: false);
|
||||
|
||||
+17
-33
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -28,44 +29,32 @@ namespace Barotrauma
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (!objectiveManager.IsCurrentOrder<AIObjectiveExtinguishFires>()
|
||||
&& Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
&& Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetHull == character.CurrentHull)
|
||||
{
|
||||
Priority = 0;
|
||||
distanceFactor = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
|
||||
float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
|
||||
float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1));
|
||||
}
|
||||
|
||||
protected override bool Check() => targetHull.FireSources.None();
|
||||
|
||||
private float sinTime;
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
var extinguisherItem = character.Inventory.FindItemByIdentifier("fireextinguisher") ?? character.Inventory.FindItemByTag("fireextinguisher");
|
||||
var extinguisherItem = character.Inventory.FindItemByIdentifier("extinguisher") ?? character.Inventory.FindItemByTag("extinguisher");
|
||||
if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
|
||||
{
|
||||
TryAddSubObjective(ref getExtinguisherObjective, () =>
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
|
||||
return new AIObjectiveGetItem(character, "fireextinguisher", objectiveManager, equip: true)
|
||||
{
|
||||
// If the item is inside an unsafe hull, decrease the priority
|
||||
GetItemPriority = i => HumanAIController.UnsafeHulls.Contains(i.CurrentHull) ? 0.1f : 1
|
||||
};
|
||||
return new AIObjectiveGetItem(character, "extinguisher", objectiveManager, equip: true);
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -90,12 +79,8 @@ namespace Barotrauma
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
// Aim
|
||||
character.CursorPosition = fs.Position;
|
||||
Vector2 fromCharacterToFireSource = fs.WorldPosition - character.WorldPosition;
|
||||
float dist = fromCharacterToFireSource.Length();
|
||||
character.CursorPosition += VectorExtensions.Forward(extinguisherItem.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
if (extinguisherItem.RequireAimToUse)
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
{
|
||||
bool isOperatingButtons = false;
|
||||
if (SteeringManager == PathSteering)
|
||||
@@ -110,9 +95,8 @@ namespace Barotrauma
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
sinTime += deltaTime * 10;
|
||||
}
|
||||
character.SetInput(extinguisherItem.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
@@ -126,7 +110,7 @@ namespace Barotrauma
|
||||
if (move)
|
||||
{
|
||||
//go to the first firesource
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range / 2)
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire",
|
||||
TargetName = fs.Hull.DisplayName
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "extinguish fires";
|
||||
public override bool ForceRun => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
public AIObjectiveExtinguishFires(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => $"find diving gear ({gearTag})";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
private readonly string gearTag;
|
||||
private readonly string fallbackTag;
|
||||
|
||||
+1
-3
@@ -33,8 +33,6 @@ namespace Barotrauma
|
||||
|
||||
private bool resetPriority;
|
||||
|
||||
public override float GetPriority() => Priority;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (resetPriority)
|
||||
@@ -254,7 +252,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Outside
|
||||
if (hull.RoomName != null && hull.RoomName.Contains("airlock", StringComparison.OrdinalIgnoreCase))
|
||||
if (hull.RoomName != null && hull.RoomName.ToLowerInvariant().Contains("airlock"))
|
||||
{
|
||||
hullSafety = 100;
|
||||
}
|
||||
|
||||
+10
-17
@@ -29,23 +29,16 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (Leak.Removed || Leak.Open <= 0)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y);
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally).
|
||||
// If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby.
|
||||
float distanceFactor = xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, xDist + yDist * 3.0f));
|
||||
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100;
|
||||
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
if (Leak.Removed || Leak.Open <= 0) { return 0; }
|
||||
float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y);
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally).
|
||||
// If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby.
|
||||
float distanceFactor = xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, xDist + yDist * 3.0f));
|
||||
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100;
|
||||
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + severity * distanceFactor * PriorityModifier, 0, 1));
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
|
||||
+4
-3
@@ -11,6 +11,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => "fix leaks";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
public AIObjectiveFixLeaks(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace Barotrauma
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveFixLeaks>(), onlyBots: true);
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveFixLeaks>());
|
||||
int totalLeaks = Targets.Count();
|
||||
if (totalLeaks == 0) { return 0; }
|
||||
int secondaryLeaks = Targets.Count(l => l.IsRoomToRoom);
|
||||
@@ -43,13 +44,13 @@ namespace Barotrauma
|
||||
bool anyFixers = otherFixers > 0;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
float ratio = anyFixers ? totalLeaks / (float)otherFixers : 1;
|
||||
float ratio = anyFixers ? totalLeaks / otherFixers : 1;
|
||||
return Targets.Sum(t => GetLeakSeverity(t)) * ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
float ratio = leaks == 0 ? 1 : anyFixers ? leaks / otherFixers : 1;
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / HumanAIController.CountCrew() > 0.75f))
|
||||
{
|
||||
// Enough fixers
|
||||
return 0;
|
||||
|
||||
+9
@@ -31,6 +31,15 @@ namespace Barotrauma
|
||||
|
||||
public bool AllowToFindDivingGear { get; set; } = true;
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, bool equip = true, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
|
||||
+13
-15
@@ -51,19 +51,14 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (followControlledCharacter && Character.Controlled == null)
|
||||
if (followControlledCharacter && Character.Controlled == null) { return 0.0f; }
|
||||
if (Target is Entity e && e.Removed) { return 0.0f; }
|
||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) { return 0.0f; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = 0;
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
if (Target is Entity e && e.Removed)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
return objectiveManager.CurrentOrder == this ? AIObjectiveManager.OrderPriority : Priority;
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0)
|
||||
@@ -223,7 +218,7 @@ namespace Barotrauma
|
||||
if (n.Waypoint.isObstructed) { return false; }
|
||||
return (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null);
|
||||
}, endNodeFilter, nodeFilter);
|
||||
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
|
||||
if (!isInside && PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(Target.WorldPosition - character.WorldPosition));
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
@@ -270,11 +265,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (character.IsClimbing && SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished)
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath?.CurrentNode?.Ladders != null)
|
||||
{
|
||||
// Still in ladders and the path is not finished -> don't release
|
||||
return false;
|
||||
//don't consider the character to be close enough to the target while climbing ladders,
|
||||
//UNLESS the last node in the path has been reached
|
||||
//otherwise characters can let go of the ladders too soon once they're close enough to the target
|
||||
if (PathSteering.CurrentPath.NextNode != null) { return false; }
|
||||
}
|
||||
|
||||
bool closeEnough = Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough;
|
||||
if (closeEnough)
|
||||
{
|
||||
|
||||
+10
-7
@@ -45,17 +45,20 @@ namespace Barotrauma
|
||||
private float randomUpdateInterval = 5;
|
||||
public float Random { get; private set; }
|
||||
|
||||
public void CalculatePriority()
|
||||
public void SetRandom()
|
||||
{
|
||||
Random = Rand.Range(0.5f, 1.5f);
|
||||
randomTimer = randomUpdateInterval;
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
float max = Math.Min(Math.Min(AIObjectiveManager.RunPriority, AIObjectiveManager.OrderPriority) - 1, 100);
|
||||
float initiative = character.GetSkillLevel("initiative");
|
||||
Priority = MathHelper.Lerp(1, max, MathUtils.InverseLerp(100, 0, initiative * Random));
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public override float GetPriority() => Priority;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this)
|
||||
@@ -66,7 +69,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatePriority();
|
||||
SetRandom();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +182,7 @@ namespace Barotrauma
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
if (SteeringManager != PathSteering || (PathSteering.CurrentPath != null &&
|
||||
(PathSteering.CurrentPath.Finished || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
(PathSteering.CurrentPath.NextNode == null || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
{
|
||||
Wander(deltaTime);
|
||||
return;
|
||||
@@ -261,9 +264,9 @@ namespace Barotrauma
|
||||
public static bool IsForbidden(Hull hull)
|
||||
{
|
||||
if (hull == null) { return true; }
|
||||
string hullName = hull.RoomName;
|
||||
string hullName = hull.RoomName?.ToLowerInvariant();
|
||||
if (hullName == null) { return false; }
|
||||
return hullName.Contains("ballast", StringComparison.OrdinalIgnoreCase) || hullName.Contains("airlock", StringComparison.OrdinalIgnoreCase);
|
||||
return hullName.Contains("ballast") || hullName.Contains("airlock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-39
@@ -45,7 +45,6 @@ namespace Barotrauma
|
||||
public override bool CanBeCompleted => true;
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
public override bool AllowSubObjectiveSorting => true;
|
||||
public virtual bool InverseTargetEvaluation => false;
|
||||
|
||||
public override bool IsLoop { get => true; set => throw new System.Exception("Trying to set the value for IsLoop from: " + System.Environment.StackTrace); }
|
||||
|
||||
@@ -108,46 +107,21 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (character.LockHands || character.Submarine == null || Targets.None())
|
||||
if (character.LockHands) { return 0; }
|
||||
if (character.Submarine == null) { return 0; }
|
||||
if (Targets.None()) { return 0; }
|
||||
// Allow the target value to be more than 100.
|
||||
float targetValue = TargetEvaluation();
|
||||
// If the target value is less than 1% of the max value, let's just treat it as zero.
|
||||
if (targetValue < 1) { return 0; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = 0;
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allow the target value to be more than 100.
|
||||
float targetValue = TargetEvaluation();
|
||||
if (InverseTargetEvaluation)
|
||||
{
|
||||
targetValue = 100 - targetValue;
|
||||
}
|
||||
var currentSubObjective = CurrentSubObjective;
|
||||
if (currentSubObjective != null && currentSubObjective.Priority > targetValue)
|
||||
{
|
||||
// If the priority is higher than the target value, let's just use it.
|
||||
// The priority calculation is more precise, but it takes into account things like distances,
|
||||
// so it's better not to use it if it's lower than the rougher targetValue.
|
||||
targetValue = Priority;
|
||||
}
|
||||
// If the target value is less than 1% of the max value, let's just treat it as zero.
|
||||
if (targetValue < 1)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
float value = MathHelper.Clamp((CumulatedDevotion + (targetValue * PriorityModifier)) / 100, 0, 1);
|
||||
Priority = MathHelper.Lerp(0, max, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
float devotion = MathHelper.Min(10, Priority);
|
||||
float value = MathHelper.Clamp((devotion + targetValue * PriorityModifier) / 100, 0, 1);
|
||||
return MathHelper.Lerp(0, max, value);
|
||||
}
|
||||
|
||||
protected void UpdateTargets()
|
||||
|
||||
+6
-16
@@ -41,15 +41,6 @@ namespace Barotrauma
|
||||
public bool IsActiveObjective<T>() where T : AIObjective => GetActiveObjective() is T;
|
||||
|
||||
public AIObjective GetActiveObjective() => CurrentObjective?.GetActiveObjective();
|
||||
/// <summary>
|
||||
/// Returns the last active objective of the specific type.
|
||||
/// </summary>
|
||||
public T GetActiveObjective<T>() where T : AIObjective => CurrentObjective?.GetSubObjectivesRecursive(includingSelf: true).LastOrDefault(so => so is T) as T;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all active objectives of the specific type. Creates a new collection -> don't use too frequently.
|
||||
/// </summary>
|
||||
public IEnumerable<T> GetActiveObjectives<T>() where T : AIObjective => CurrentObjective?.GetSubObjectivesRecursive(includingSelf: true).Where(so => so is T).Select(so => so as T);
|
||||
|
||||
public bool HasActiveObjective<T>() where T : AIObjective => CurrentObjective is T || CurrentObjective != null && CurrentObjective.GetSubObjectivesRecursive().Any(so => so is T);
|
||||
|
||||
@@ -155,7 +146,7 @@ namespace Barotrauma
|
||||
{
|
||||
var previousObjective = CurrentObjective;
|
||||
var firstObjective = Objectives.FirstOrDefault();
|
||||
if (CurrentOrder != null && firstObjective != null && CurrentOrder.Priority > firstObjective.Priority)
|
||||
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority() > firstObjective.GetPriority())
|
||||
{
|
||||
CurrentObjective = CurrentOrder;
|
||||
}
|
||||
@@ -167,14 +158,14 @@ namespace Barotrauma
|
||||
{
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority();
|
||||
GetObjective<AIObjectiveIdle>().SetRandom();
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
|
||||
public float GetCurrentPriority()
|
||||
{
|
||||
return CurrentObjective == null ? 0.0f : CurrentObjective.Priority;
|
||||
return CurrentObjective == null ? 0.0f : CurrentObjective.GetPriority();
|
||||
}
|
||||
|
||||
public void UpdateObjectives(float deltaTime)
|
||||
@@ -214,8 +205,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Objectives.Any())
|
||||
{
|
||||
Objectives.ForEach(o => o.GetPriority());
|
||||
Objectives.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
Objectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
|
||||
}
|
||||
GetCurrentObjective()?.SortSubObjectives();
|
||||
}
|
||||
@@ -307,7 +297,7 @@ namespace Barotrauma
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && orderGiver.IsPlayer
|
||||
Override = orderGiver != null && (orderGiver == Character.Controlled || orderGiver.IsRemotePlayer)
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -316,7 +306,7 @@ namespace Barotrauma
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && orderGiver.IsPlayer
|
||||
Override = orderGiver != null && (orderGiver == Character.Controlled || orderGiver.IsRemotePlayer)
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
+11
-25
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -32,32 +31,19 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (component.Item.ConditionPercentage <= 0)
|
||||
if (component.Item.ConditionPercentage <= 0) { return 0; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = 0;
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
if (component.Item.CurrentHull == null || component.Item.CurrentHull.FireSources.None() || IsOperatedByAnother(GetTarget()))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier);
|
||||
float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
Priority = MathHelper.Clamp(value, 0, max);
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
if (component.Item.CurrentHull == null) { return 0; }
|
||||
if (component.Item.CurrentHull.FireSources.Count > 0) { return 0; }
|
||||
if (IsOperatedByAnother(GetTarget())) { return 0; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
|
||||
float devotion = MathHelper.Min(10, Priority);
|
||||
float value = devotion + AIObjectiveManager.OrderPriority * PriorityModifier;
|
||||
float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
return MathHelper.Clamp(value, 0, max);
|
||||
}
|
||||
|
||||
public AIObjectiveOperateItem(ItemComponent item, Character character, AIObjectiveManager objectiveManager, string option, bool requireEquip, Entity operateTarget = null, bool useController = false, float priorityModifier = 1)
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => "pump water";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool UnequipItems => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
private IEnumerable<Pump> pumpList;
|
||||
|
||||
|
||||
+14
-21
@@ -30,28 +30,21 @@ namespace Barotrauma
|
||||
{
|
||||
// TODO: priority list?
|
||||
// Ignore items that are being repaired by someone else.
|
||||
if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character))
|
||||
if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character)) { return 0; }
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (Item.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
Priority = 0;
|
||||
distanceFactor = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (Item.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float damagePriority = MathHelper.Lerp(1, 0, Item.Condition / Item.MaxCondition);
|
||||
float successFactor = MathHelper.Lerp(0, 1, Item.Repairables.Average(r => r.DegreeOfSuccess(character)));
|
||||
float isSelected = IsRepairing ? 50 : 0;
|
||||
float devotion = (CumulatedDevotion + isSelected) / 100;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (damagePriority * distanceFactor * successFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
float damagePriority = MathHelper.Lerp(1, 0, Item.Condition / Item.MaxCondition);
|
||||
float successFactor = MathHelper.Lerp(0, 1, Item.Repairables.Average(r => r.DegreeOfSuccess(character)));
|
||||
float isSelected = IsRepairing ? 50 : 0;
|
||||
float devotion = (Math.Min(Priority, 10) + isSelected) / 100;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1));
|
||||
}
|
||||
|
||||
protected override bool Check()
|
||||
@@ -165,7 +158,7 @@ namespace Barotrauma
|
||||
}
|
||||
repairable.StopRepairing(character);
|
||||
}
|
||||
else if (repairable.CurrentFixer != character)
|
||||
else
|
||||
{
|
||||
repairable.StartRepairing(character, Repairable.FixActions.Repair);
|
||||
}
|
||||
|
||||
+4
-3
@@ -81,17 +81,18 @@ namespace Barotrauma
|
||||
// Don't stop fixing until done
|
||||
return 100;
|
||||
}
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRepairItems>(), onlyBots: true);
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRepairItems>());
|
||||
int items = Targets.Count;
|
||||
bool anyFixers = otherFixers > 0;
|
||||
float ratio = anyFixers ? items / (float)otherFixers : 1;
|
||||
float ratio = anyFixers ? items / otherFixers : 1;
|
||||
var result = ratio;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return Targets.Sum(t => 100 - t.ConditionPercentage) * ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / HumanAIController.CountCrew() > 0.75f))
|
||||
{
|
||||
// Enough fixers
|
||||
return 0;
|
||||
|
||||
+19
-50
@@ -14,7 +14,7 @@ namespace Barotrauma
|
||||
|
||||
const float TreatmentDelay = 0.5f;
|
||||
|
||||
const float CloseEnoughToTreat = 100.0f;
|
||||
const float CloseEnoughToTreat = 150.0f;
|
||||
|
||||
private readonly Character targetCharacter;
|
||||
|
||||
@@ -44,15 +44,6 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (targetCharacter.SelectedBy != null && targetCharacter.SelectedBy != character)
|
||||
{
|
||||
var otherCharacter = character.SelectedBy;
|
||||
if (otherCharacter != null)
|
||||
{
|
||||
// Someone else is rescuing/holding the target.
|
||||
Abandon = otherCharacter.IsPlayer || character.GetSkillLevel("medical") < otherCharacter.GetSkillLevel("medical");
|
||||
}
|
||||
}
|
||||
|
||||
if (targetCharacter != character)
|
||||
{
|
||||
@@ -76,11 +67,7 @@ namespace Barotrauma
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -99,11 +86,7 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(safeHull, character, objectiveManager),
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
safeHull = character.CurrentHull;
|
||||
});
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,11 +105,7 @@ namespace Barotrauma
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -148,11 +127,6 @@ namespace Barotrauma
|
||||
private Dictionary<string, float> currentTreatmentSuitabilities = new Dictionary<string, float>();
|
||||
private void GiveTreatment(float deltaTime)
|
||||
{
|
||||
if (!targetCharacter.IsPlayer)
|
||||
{
|
||||
// If the target is a bot, don't let it move
|
||||
targetCharacter.AIController.SteeringManager.Reset();
|
||||
}
|
||||
if (treatmentTimer > 0.0f)
|
||||
{
|
||||
treatmentTimer -= deltaTime;
|
||||
@@ -163,8 +137,9 @@ namespace Barotrauma
|
||||
//find which treatments are the most suitable to treat the character's current condition
|
||||
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false);
|
||||
|
||||
var allAfflictions = GetVitalityReducingAfflictions(targetCharacter).OrderByDescending(a => a.GetVitalityDecrease(targetCharacter.CharacterHealth));
|
||||
//check if we already have a suitable treatment for any of the afflictions
|
||||
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))
|
||||
foreach (Affliction affliction in allAfflictions)
|
||||
{
|
||||
foreach (KeyValuePair<string, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
@@ -225,12 +200,10 @@ namespace Barotrauma
|
||||
onAbandon: () => RemoveSubObjective(ref getItemObjective));
|
||||
}
|
||||
}
|
||||
if (character != targetCharacter)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
}
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
}
|
||||
|
||||
|
||||
private void ApplyTreatment(Affliction affliction, Item item)
|
||||
{
|
||||
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
|
||||
@@ -267,7 +240,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return false;
|
||||
}
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter);
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) > AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager);
|
||||
if (isCompleted && targetCharacter != character)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name),
|
||||
@@ -280,24 +253,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
|
||||
{
|
||||
Priority = 0;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetCharacter.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetCharacter.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float vitalityFactor = 1 - AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) / 100;
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (vitalityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
distanceFactor = 1;
|
||||
}
|
||||
return Priority;
|
||||
float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1));
|
||||
}
|
||||
|
||||
public static IEnumerable<Affliction> GetSortedAfflictions(Character character) => CharacterHealth.SortAfflictionsBySeverity(character.CharacterHealth.GetAllAfflictions());
|
||||
public static IEnumerable<Affliction> GetVitalityReducingAfflictions(Character character) => character.CharacterHealth.GetAllAfflictions(a => a.GetVitalityDecrease(character.CharacterHealth) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-67
@@ -8,11 +8,11 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "rescue all";
|
||||
public override bool ForceRun => true;
|
||||
public override bool InverseTargetEvaluation => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
private const float vitalityThreshold = 80;
|
||||
private const float vitalityThresholdForOrders = 100;
|
||||
public static float GetVitalityThreshold(AIObjectiveManager manager, Character character, Character target)
|
||||
private const float vitalityThresholdForOrders = 95;
|
||||
public static float GetVitalityThreshold(AIObjectiveManager manager)
|
||||
{
|
||||
if (manager == null)
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
return character == target || manager.CurrentOrder is AIObjectiveRescueAll ? vitalityThresholdForOrders : vitalityThreshold;
|
||||
return manager.CurrentOrder is AIObjectiveRescueAll ? vitalityThresholdForOrders : vitalityThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,50 +31,9 @@ namespace Barotrauma
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
int otherRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRescueAll>(), onlyBots: true);
|
||||
int targetCount = Targets.Count;
|
||||
bool anyRescuers = otherRescuers > 0;
|
||||
float ratio = anyRescuers ? targetCount / (float)otherRescuers : 1;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return Targets.Min(t => GetVitalityFactor(t)) / ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
float multiplier = 1;
|
||||
if (anyRescuers)
|
||||
{
|
||||
float mySkill = character.GetSkillLevel("medical");
|
||||
int betterRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.Character.Info.Job.GetSkillLevel("medical") >= mySkill, onlyBots: true);
|
||||
if (targetCount / (float)betterRescuers <= 1)
|
||||
{
|
||||
// Enough rescuers
|
||||
return 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool foundOtherMedics = HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.Info.Job.Prefab.Identifier == "medicaldoctor");
|
||||
if (foundOtherMedics)
|
||||
{
|
||||
if (character.Info.Job.Prefab.Identifier != "medicaldoctor")
|
||||
{
|
||||
// Double the vitality factor -> less likely to take action
|
||||
multiplier = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Targets.Min(t => GetVitalityFactor(t)) / ratio * multiplier;
|
||||
}
|
||||
}
|
||||
protected override float TargetEvaluation() => Targets.Max(t => GetVitalityFactor(t));
|
||||
|
||||
public static float GetVitalityFactor(Character character)
|
||||
{
|
||||
float vitality = character.HealthPercentage - character.Bleeding - character.Bloodloss + Math.Min(character.Oxygen, 0);
|
||||
return Math.Clamp(vitality, 0, 100);
|
||||
}
|
||||
public static float GetVitalityFactor(Character character) => Math.Min(character.HealthPercentage - character.Bleeding - character.Bloodloss - Math.Min(character.Oxygen, 0), 100);
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
=> new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
|
||||
@@ -88,34 +47,16 @@ namespace Barotrauma
|
||||
if (!HumanAIController.IsFriendly(character, target)) { return false; }
|
||||
if (character.AIController is HumanAIController humanAI)
|
||||
{
|
||||
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return false; }
|
||||
if (!humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveRescueAll>())
|
||||
{
|
||||
// Ignore unsafe hulls, unless ordered
|
||||
if (humanAI.UnsafeHulls.Contains(target.CurrentHull))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (GetVitalityFactor(target) > GetVitalityThreshold(humanAI.ObjectiveManager)) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetVitalityFactor(target) >= vitalityThreshold) { return false; }
|
||||
if (GetVitalityFactor(target) > vitalityThreshold) { return false; }
|
||||
}
|
||||
if (target.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (target.Submarine.TeamID != character.Submarine.TeamID) { return false; }
|
||||
if (target.CurrentHull == null) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return false; }
|
||||
if (!target.IsPlayer && HumanAIController.IsActive(target) && target.AIController is HumanAIController targetAI)
|
||||
{
|
||||
// Ignore all concious targets that are currently fighting, fleeing or treating characters
|
||||
if (targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveCombat>() ||
|
||||
targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveFindSafety>() ||
|
||||
targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Don't go into rooms that have enemies
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return false; }
|
||||
return true;
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace Barotrauma
|
||||
}
|
||||
for (int i = 0; i < AppropriateJobs.Length; i++)
|
||||
{
|
||||
if (character.Info.Job.Prefab.Identifier.Equals(AppropriateJobs[i], StringComparison.OrdinalIgnoreCase)) { return true; }
|
||||
if (character.Info.Job.Prefab.Identifier.ToLowerInvariant() == AppropriateJobs[i].ToLowerInvariant()) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -732,20 +732,14 @@ namespace Barotrauma
|
||||
limbJoint.IsSevered = true;
|
||||
limbJoint.Enabled = false;
|
||||
|
||||
Vector2 limbDiff = limbJoint.LimbA.SimPosition - limbJoint.LimbB.SimPosition;
|
||||
if (limbDiff.LengthSquared() < 0.0001f) { limbDiff = Rand.Vector(1.0f); }
|
||||
limbDiff = Vector2.Normalize(limbDiff);
|
||||
float mass = limbJoint.BodyA.Mass + limbJoint.BodyB.Mass;
|
||||
limbJoint.LimbA.body.ApplyLinearImpulse(limbDiff * mass, (limbJoint.LimbA.SimPosition + limbJoint.LimbB.SimPosition) / 2.0f);
|
||||
limbJoint.LimbB.body.ApplyLinearImpulse(-limbDiff * mass, (limbJoint.LimbA.SimPosition + limbJoint.LimbB.SimPosition) / 2.0f);
|
||||
|
||||
List<Limb> connectedLimbs = new List<Limb>();
|
||||
List<LimbJoint> checkedJoints = new List<LimbJoint>();
|
||||
|
||||
GetConnectedLimbs(connectedLimbs, checkedJoints, MainLimb);
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (connectedLimbs.Contains(limb)) { continue; }
|
||||
if (connectedLimbs.Contains(limb)) continue;
|
||||
|
||||
limb.IsSevered = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma
|
||||
[Serialize(AttackTarget.Any, true, description: "Does the attack target only specific targets?"), Editable]
|
||||
public AttackTarget TargetType { get; private set; }
|
||||
|
||||
[Serialize(LimbType.None, true, description: "To which limb is the attack aimed at? If not defined or set to none, the closest limb is used (default)."), Editable]
|
||||
[Serialize(LimbType.None, true, description: "If not defined or set to none, the closest limb is used (default)."), Editable]
|
||||
public LimbType TargetLimbType { get; private set; }
|
||||
|
||||
[Serialize(HitDetection.Distance, true, description: "Collision detection is more accurate, but it only affects targets that are in contact with the limb."), Editable]
|
||||
@@ -87,12 +87,9 @@ namespace Barotrauma
|
||||
[Serialize(AIBehaviorAfterAttack.FallBack, true, description: "The preferred AI behavior after the attack."), Editable]
|
||||
public AIBehaviorAfterAttack AfterAttack { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the AI try to turn around when aiming with this attack?"), Editable]
|
||||
[Serialize(false, true, description: "Should the AI try to reverse when aiming with this attack?"), Editable]
|
||||
public bool Reverse { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the AI try to steer away from the target when aiming with this attack? Best combined with PassiveAggressive behavior."), Editable]
|
||||
public bool Retreat { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target before the AI tries to attack."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
|
||||
public float Range { get; set; }
|
||||
|
||||
@@ -282,7 +279,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - define afflictions using identifiers instead of names.");
|
||||
string afflictionName = subElement.GetAttributeString("name", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Name.Equals(afflictionName, System.StringComparison.OrdinalIgnoreCase));
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Name.ToLowerInvariant() == afflictionName);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
@@ -292,7 +289,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, System.StringComparison.OrdinalIgnoreCase));
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.ToLowerInvariant() == afflictionIdentifier);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
@@ -327,7 +324,7 @@ namespace Barotrauma
|
||||
AfflictionPrefab afflictionPrefab;
|
||||
Affliction affliction;
|
||||
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, System.StringComparison.OrdinalIgnoreCase));
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.ToLowerInvariant() == afflictionIdentifier);
|
||||
if (afflictionPrefab != null)
|
||||
{
|
||||
float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
|
||||
|
||||
@@ -57,9 +57,6 @@ namespace Barotrauma
|
||||
public Hull CurrentHull = null;
|
||||
|
||||
public bool IsRemotePlayer;
|
||||
|
||||
public bool IsPlayer => Controlled == this || IsRemotePlayer;
|
||||
|
||||
public readonly Dictionary<string, SerializableProperty> Properties;
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
@@ -131,40 +128,13 @@ namespace Barotrauma
|
||||
set => Params.Noise = value;
|
||||
}
|
||||
|
||||
public float Visibility
|
||||
{
|
||||
get => Params.Visibility;
|
||||
set => Params.Visibility = value;
|
||||
}
|
||||
|
||||
public bool IsTraitor;
|
||||
public string TraitorCurrentObjective = "";
|
||||
public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private float attackCoolDown;
|
||||
|
||||
public Order CurrentOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return Info?.CurrentOrder;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (Info != null) { Info.CurrentOrder = value; }
|
||||
}
|
||||
}
|
||||
public string CurrentOrderOption
|
||||
{
|
||||
get
|
||||
{
|
||||
return Info?.CurrentOrderOption;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (Info != null) { Info.CurrentOrderOption = value; }
|
||||
}
|
||||
}
|
||||
public Order CurrentOrder { get; private set; }
|
||||
|
||||
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>();
|
||||
private readonly List<float> speedMultipliers = new List<float>();
|
||||
@@ -788,7 +758,7 @@ namespace Barotrauma
|
||||
var matchingAffliction = AfflictionPrefab.List
|
||||
.Where(p => p.AfflictionType == "huskinfection")
|
||||
.Select(p => p as AfflictionPrefabHusk)
|
||||
.FirstOrDefault(p => p.TargetSpecies.Any(t => t.Equals(AfflictionHusk.GetNonHuskedSpeciesName(speciesName, p), StringComparison.OrdinalIgnoreCase)));
|
||||
.FirstOrDefault(p => p.TargetSpecies.Any(t => t.Equals(AfflictionHusk.GetNonHuskedSpeciesName(speciesName, p), StringComparison.InvariantCultureIgnoreCase)));
|
||||
string nonHuskedSpeciesName = string.Empty;
|
||||
if (matchingAffliction == null)
|
||||
{
|
||||
@@ -2042,8 +2012,8 @@ namespace Barotrauma
|
||||
HideFace = false;
|
||||
|
||||
|
||||
UpdateSightRange(deltaTime);
|
||||
UpdateSoundRange(deltaTime);
|
||||
UpdateSightRange();
|
||||
UpdateSoundRange();
|
||||
|
||||
if (IsDead) { return; }
|
||||
|
||||
@@ -2192,8 +2162,6 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam);
|
||||
|
||||
partial void SetOrderProjSpecific(Order order, string orderOption);
|
||||
|
||||
private void UpdateOxygen(float deltaTime)
|
||||
{
|
||||
PressureProtection -= deltaTime * 100.0f;
|
||||
@@ -2296,12 +2264,7 @@ namespace Barotrauma
|
||||
if (itemContainer == null) { return; }
|
||||
foreach (Item inventoryItem in Inventory.Items)
|
||||
{
|
||||
if (inventoryItem == null) { continue; }
|
||||
if (!itemContainer.Inventory.TryPutItem(inventoryItem, user: null))
|
||||
{
|
||||
//if the item couldn't be put inside the despawn container, just drop it
|
||||
inventoryItem.Drop(dropper: this);
|
||||
}
|
||||
itemContainer.Inventory.TryPutItem(inventoryItem, user: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2327,39 +2290,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly float maxAIRange = 10000;
|
||||
private readonly float aiTargetChangeSpeed = 5;
|
||||
|
||||
private void UpdateSightRange(float deltaTime)
|
||||
private void UpdateSightRange()
|
||||
{
|
||||
if (aiTarget == null) { return; }
|
||||
float minRange = Math.Clamp((float)Math.Sqrt(Mass) * Visibility, 250, 1000);
|
||||
float massFactor = (float)Math.Sqrt(Mass / 20);
|
||||
float targetRange = Math.Min(minRange + massFactor * AnimController.Collider.LinearVelocity.Length() * 2 * Visibility, maxAIRange);
|
||||
float newRange = MathHelper.SmoothStep(aiTarget.SightRange, targetRange, deltaTime * aiTargetChangeSpeed);
|
||||
if (!float.IsNaN(newRange))
|
||||
{
|
||||
aiTarget.SightRange = newRange;
|
||||
}
|
||||
float range = (float)Math.Sqrt(Mass) * 250 + AnimController.Collider.LinearVelocity.Length() * 500;
|
||||
aiTarget.SightRange = MathHelper.Clamp(range, 0, 10000);
|
||||
}
|
||||
|
||||
private void UpdateSoundRange(float deltaTime)
|
||||
private void UpdateSoundRange()
|
||||
{
|
||||
if (aiTarget == null) { return; }
|
||||
if (IsDead)
|
||||
{
|
||||
aiTarget.SoundRange = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float massFactor = (float)Math.Sqrt(Mass / 10);
|
||||
float targetRange = Math.Min(massFactor * AnimController.Collider.LinearVelocity.Length() * 2 * Noise, maxAIRange);
|
||||
float newRange = MathHelper.SmoothStep(aiTarget.SoundRange, targetRange, deltaTime * aiTargetChangeSpeed);
|
||||
if (!float.IsNaN(newRange))
|
||||
{
|
||||
aiTarget.SoundRange = newRange;
|
||||
}
|
||||
}
|
||||
float range = ((float)Math.Sqrt(Mass) / 3) * (AnimController.TargetMovement.Length() * 2) * Noise;
|
||||
aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000);
|
||||
}
|
||||
|
||||
public bool CanHearCharacter(Character speaker)
|
||||
@@ -2374,17 +2316,24 @@ namespace Barotrauma
|
||||
|
||||
public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true)
|
||||
{
|
||||
//set the character order only if the character is close enough to hear the message
|
||||
if (orderGiver != null && !CanHearCharacter(orderGiver)) { return; }
|
||||
if (orderGiver != null)
|
||||
{
|
||||
//set the character order only if the character is close enough to hear the message
|
||||
if (!CanHearCharacter(orderGiver)) { return; }
|
||||
}
|
||||
|
||||
if (AIController is HumanAIController humanAI)
|
||||
{
|
||||
humanAI.SetOrder(order, orderOption, orderGiver, speak);
|
||||
}
|
||||
#if CLIENT
|
||||
else
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.DisplayCharacterOrder(this, order, orderOption);
|
||||
}
|
||||
#endif
|
||||
|
||||
SetOrderProjSpecific(order, orderOption);
|
||||
CurrentOrder = order;
|
||||
CurrentOrderOption = orderOption;
|
||||
}
|
||||
|
||||
private readonly List<AIChatMessage> aiChatMessageQueue = new List<AIChatMessage>();
|
||||
@@ -2653,7 +2602,6 @@ namespace Barotrauma
|
||||
mainLimb.body.ApplyLinearImpulse(impulse, hitPos, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
}
|
||||
bool wasDead = IsDead;
|
||||
Vector2 simPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(dir);
|
||||
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound);
|
||||
CharacterHealth.ApplyDamage(hitLimb, attackResult);
|
||||
@@ -2661,10 +2609,6 @@ namespace Barotrauma
|
||||
{
|
||||
OnAttacked?.Invoke(attacker, attackResult);
|
||||
OnAttackedProjSpecific(attacker, attackResult);
|
||||
if (!wasDead)
|
||||
{
|
||||
TryAdjustAttackerSkill(attacker, -attackResult.Damage);
|
||||
}
|
||||
};
|
||||
|
||||
if (attacker != null && attackResult.Damage > 0.0f)
|
||||
@@ -2677,30 +2621,6 @@ namespace Barotrauma
|
||||
|
||||
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult);
|
||||
|
||||
public void TryAdjustAttackerSkill(Character attacker, float healthChange)
|
||||
{
|
||||
if (attacker == null) { return; }
|
||||
|
||||
bool isEnemy = AIController is EnemyAIController || TeamID != attacker.TeamID;
|
||||
if (isEnemy)
|
||||
{
|
||||
if (healthChange < 0.0f)
|
||||
{
|
||||
float attackerSkillLevel = attacker.GetSkillLevel("weapons");
|
||||
attacker.Info?.IncreaseSkillLevel("weapons",
|
||||
-healthChange * SkillSettings.Current.SkillIncreasePerHostileDamage / Math.Max(attackerSkillLevel, 1.0f),
|
||||
attacker.WorldPosition + Vector2.UnitY * 100.0f);
|
||||
}
|
||||
}
|
||||
else if (healthChange > 0.0f)
|
||||
{
|
||||
float attackerSkillLevel = attacker.GetSkillLevel("medical");
|
||||
attacker.Info?.IncreaseSkillLevel("medical",
|
||||
healthChange * SkillSettings.Current.SkillIncreasePerFriendlyHealed / Math.Max(attackerSkillLevel, 1.0f),
|
||||
attacker.WorldPosition + Vector2.UnitY * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStun(float newStun, bool allowStunDecrease = false, bool isNetworkMessage = false)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkMessage) { return; }
|
||||
|
||||
@@ -293,9 +293,6 @@ namespace Barotrauma
|
||||
|
||||
private NPCPersonalityTrait personalityTrait;
|
||||
|
||||
public Order CurrentOrder { get; set;}
|
||||
public string CurrentOrderOption { get; set; }
|
||||
|
||||
//unique ID given to character infos in MP
|
||||
//used by clients to identify which infos are the same to prevent duplicate characters in round summary
|
||||
public ushort ID;
|
||||
@@ -527,11 +524,9 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (XElement subElement in infoElement.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("job", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Job = new Job(subElement);
|
||||
break;
|
||||
}
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "job") continue;
|
||||
Job = new Job(subElement);
|
||||
break;
|
||||
}
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
@@ -666,7 +661,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
|
||||
{
|
||||
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (limbElement.GetAttributeString("type", "").ToLowerInvariant() != "head") { continue; }
|
||||
|
||||
XElement spriteElement = limbElement.Element("sprite");
|
||||
if (spriteElement == null) { continue; }
|
||||
@@ -682,7 +677,7 @@ namespace Barotrauma
|
||||
//go through the files in the directory to find a matching sprite
|
||||
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
||||
{
|
||||
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
if (!file.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -833,11 +828,6 @@ namespace Barotrauma
|
||||
{
|
||||
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
|
||||
|
||||
if (Job.Prefab.Identifier == "assistant")
|
||||
{
|
||||
increase *= SkillSettings.Current.AssistantSkillIncreaseMultiplier;
|
||||
}
|
||||
|
||||
float prevLevel = Job.GetSkillLevel(skillIdentifier);
|
||||
Job.IncreaseSkillLevel(skillIdentifier, increase);
|
||||
|
||||
@@ -965,7 +955,7 @@ namespace Barotrauma
|
||||
foreach (XElement childInvElement in itemElement.Elements())
|
||||
{
|
||||
if (itemContainerIndex >= itemContainers.Count) break;
|
||||
if (!childInvElement.Name.ToString().Equals("inventory", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (childInvElement.Name.ToString().ToLowerInvariant() != "inventory") continue;
|
||||
SpawnInventoryItemsRecursive(itemContainers[itemContainerIndex].Inventory, childInvElement);
|
||||
itemContainerIndex++;
|
||||
}
|
||||
|
||||
+1
-1
@@ -220,7 +220,7 @@ namespace Barotrauma
|
||||
var element = appendageDefinition;
|
||||
if (element == null)
|
||||
{
|
||||
element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeString("affliction", string.Empty).Equals(afflictionIdentifier, System.StringComparison.OrdinalIgnoreCase));
|
||||
element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeString("affliction", string.Empty).Equals(afflictionIdentifier));
|
||||
}
|
||||
if (element == null)
|
||||
{
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace Barotrauma
|
||||
limbHealths.Clear();
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("limb", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "limb") continue;
|
||||
limbHealths.Add(new LimbHealth(subElement, this));
|
||||
}
|
||||
if (limbHealths.Count == 0)
|
||||
@@ -408,10 +408,11 @@ namespace Barotrauma
|
||||
return resistance;
|
||||
}
|
||||
|
||||
private List<Affliction> matchingAfflictions = new List<Affliction>();
|
||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount)
|
||||
{
|
||||
matchingAfflictions.Clear();
|
||||
affliction = affliction.ToLowerInvariant();
|
||||
|
||||
List<Affliction> matchingAfflictions = new List<Affliction>(afflictions);
|
||||
|
||||
if (targetLimb != null)
|
||||
{
|
||||
@@ -425,8 +426,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
matchingAfflictions.RemoveAll(a =>
|
||||
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) &&
|
||||
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase));
|
||||
a.Prefab.Identifier.ToLowerInvariant() != affliction &&
|
||||
a.Prefab.AfflictionType.ToLowerInvariant() != affliction);
|
||||
|
||||
if (matchingAfflictions.Count == 0) return;
|
||||
|
||||
@@ -690,15 +691,13 @@ namespace Barotrauma
|
||||
foreach (Affliction affliction in limbHealth.Afflictions)
|
||||
{
|
||||
float vitalityDecrease = affliction.GetVitalityDecrease(this);
|
||||
string identifier = affliction.Prefab.Identifier.ToLowerInvariant();
|
||||
string type = affliction.Prefab.AfflictionType.ToLowerInvariant();
|
||||
if (limbHealth.VitalityMultipliers.ContainsKey(identifier))
|
||||
if (limbHealth.VitalityMultipliers.ContainsKey(affliction.Prefab.Identifier.ToLowerInvariant()))
|
||||
{
|
||||
vitalityDecrease *= limbHealth.VitalityMultipliers[identifier];
|
||||
vitalityDecrease *= limbHealth.VitalityMultipliers[affliction.Prefab.Identifier.ToLowerInvariant()];
|
||||
}
|
||||
if (limbHealth.VitalityTypeMultipliers.ContainsKey(type))
|
||||
if (limbHealth.VitalityTypeMultipliers.ContainsKey(affliction.Prefab.AfflictionType.ToLowerInvariant()))
|
||||
{
|
||||
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[type];
|
||||
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[affliction.Prefab.AfflictionType.ToLowerInvariant()];
|
||||
}
|
||||
vitalityDecrease *= damageResistanceMultiplier;
|
||||
Vitality -= vitalityDecrease;
|
||||
@@ -792,7 +791,8 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
|
||||
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
|
||||
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
|
||||
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
|
||||
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, float randomization = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
@@ -873,11 +873,5 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific();
|
||||
|
||||
/// <summary>
|
||||
/// Automatically filters out buffs.
|
||||
/// </summary>
|
||||
public static IEnumerable<Affliction> SortAfflictionsBySeverity(IEnumerable<Affliction> afflictions) =>
|
||||
afflictions.Where(a => !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma
|
||||
skills = new Dictionary<string, Skill>();
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("skill", System.StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "skill") { continue; }
|
||||
string skillIdentifier = subElement.GetAttributeString("identifier", "");
|
||||
if (string.IsNullOrEmpty(skillIdentifier)) { continue; }
|
||||
skills.Add(
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (XElement element in mainElement.Elements())
|
||||
{
|
||||
if (element.Name.ToString().Equals("nojob", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (element.Name.ToString().ToLowerInvariant() == "nojob") { continue; }
|
||||
if (element.IsOverride())
|
||||
{
|
||||
var job = new JobPrefab(element.FirstElement(), file.Path)
|
||||
|
||||
+2
-2
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
public static string GetFolder(XDocument doc, string filePath)
|
||||
{
|
||||
var folder = doc.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
folder = Path.Combine(Path.GetDirectoryName(filePath), "Animations");
|
||||
}
|
||||
@@ -198,7 +198,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedFile = filteredFiles.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
selectedFile = filteredFiles.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).ToLowerInvariant() == fileName.ToLowerInvariant());
|
||||
if (selectedFile == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"[AnimationParams] Could not find an animation file that matches the name {fileName} and the animation type {animType}. Using the default animations.");
|
||||
|
||||
@@ -40,9 +40,6 @@ namespace Barotrauma
|
||||
[Serialize(100f, true, description: "How much noise the character makes when moving?"), Editable(minValue: 0f, maxValue: 1000f)]
|
||||
public float Noise { get; set; }
|
||||
|
||||
[Serialize(100f, true, description: "How visible the character is?"), Editable(minValue: 0f, maxValue: 1000f)]
|
||||
public float Visibility { get; set; }
|
||||
|
||||
[Serialize("blood", true), Editable]
|
||||
public string BloodDecal { get; private set; }
|
||||
|
||||
|
||||
@@ -94,7 +94,8 @@ namespace Barotrauma
|
||||
|
||||
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
|
||||
{
|
||||
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier.Equals(speciesName, StringComparison.OrdinalIgnoreCase) && (contentPackage == null || p.ContentPackage == contentPackage));
|
||||
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier.ToLowerInvariant()==speciesName.ToLowerInvariant() &&
|
||||
(contentPackage==null || p.ContentPackage == contentPackage));
|
||||
if (prefab?.XDocument == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}' (content package {contentPackage?.Name ?? "null"})");
|
||||
@@ -106,7 +107,7 @@ namespace Barotrauma
|
||||
public static string GetFolder(XDocument doc, string filePath)
|
||||
{
|
||||
var folder = doc.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
folder = Path.Combine(Path.GetDirectoryName(filePath), "Ragdolls") + Path.DirectorySeparatorChar;
|
||||
}
|
||||
@@ -149,7 +150,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedFile = files.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
selectedFile = files.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).ToLowerInvariant() == fileName.ToLowerInvariant());
|
||||
if (selectedFile == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"[RagdollParams] Could not find a ragdoll file that matches the name {fileName}. Using the default ragdoll.");
|
||||
|
||||
@@ -65,37 +65,6 @@ namespace Barotrauma
|
||||
set { skillIncreasePerFabricatorRequiredSkill = value; }
|
||||
}
|
||||
|
||||
private float skillIncreasePerHostileDamage;
|
||||
[Serialize(0.01f, true)]
|
||||
public float SkillIncreasePerHostileDamage
|
||||
{
|
||||
get { return skillIncreasePerHostileDamage * GetCurrentSkillGainMultiplier(); }
|
||||
set { skillIncreasePerHostileDamage = value; }
|
||||
}
|
||||
|
||||
private float skillIncreasePerSecondWhenOperatingTurret;
|
||||
[Serialize(0.001f, true)]
|
||||
public float SkillIncreasePerSecondWhenOperatingTurret
|
||||
{
|
||||
get { return skillIncreasePerSecondWhenOperatingTurret * GetCurrentSkillGainMultiplier(); }
|
||||
set { skillIncreasePerSecondWhenOperatingTurret = value; }
|
||||
}
|
||||
|
||||
private float skillIncreasePerFriendlyHealed;
|
||||
[Serialize(0.001f, true)]
|
||||
public float SkillIncreasePerFriendlyHealed
|
||||
{
|
||||
get { return skillIncreasePerFriendlyHealed * GetCurrentSkillGainMultiplier(); }
|
||||
set { skillIncreasePerFriendlyHealed = value; }
|
||||
}
|
||||
|
||||
[Serialize(1.1f, true)]
|
||||
public float AssistantSkillIncreaseMultiplier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private SkillSettings(XElement element)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
@@ -460,12 +460,12 @@ namespace Barotrauma
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
|
||||
var rootElement = doc.Root;
|
||||
var element = rootElement.IsOverride() ? rootElement.FirstElement() : rootElement;
|
||||
var ragdollFolder = RagdollParams.GetFolder(doc, file.Path).CleanUpPathCrossPlatform(true);
|
||||
var ragdollFolder = RagdollParams.GetFolder(doc, file.Path);
|
||||
if (Directory.Exists(ragdollFolder))
|
||||
{
|
||||
Directory.GetFiles(ragdollFolder, "*.xml").ForEach(f => filePaths.Add(f));
|
||||
}
|
||||
var animationFolder = AnimationParams.GetFolder(doc, file.Path).CleanUpPathCrossPlatform(true);
|
||||
var animationFolder = AnimationParams.GetFolder(doc, file.Path);
|
||||
if (Directory.Exists(animationFolder))
|
||||
{
|
||||
Directory.GetFiles(animationFolder, "*.xml").ForEach(f => filePaths.Add(f));
|
||||
|
||||
@@ -186,7 +186,6 @@ namespace Barotrauma
|
||||
#endif
|
||||
if (handle.Thread == null)
|
||||
{
|
||||
if (handle.AbortRequested) { return true; }
|
||||
if (handle.Coroutine.Current != null)
|
||||
{
|
||||
WaitForSeconds wfs = handle.Coroutine.Current as WaitForSeconds;
|
||||
|
||||
@@ -531,17 +531,18 @@ namespace Barotrauma
|
||||
|
||||
commands.Add(new Command("findentityids", "findentityids [entityname]", (string[] args) =>
|
||||
{
|
||||
if (args.Length == 0) { return; }
|
||||
if (args.Length == 0) return;
|
||||
args[0] = args[0].ToLowerInvariant();
|
||||
foreach (MapEntity mapEntity in MapEntity.mapEntityList)
|
||||
{
|
||||
if (mapEntity.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase))
|
||||
if (mapEntity.Name.ToLowerInvariant() == args[0])
|
||||
{
|
||||
ThrowError(mapEntity.ID + ": " + mapEntity.Name.ToString());
|
||||
}
|
||||
}
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase) || character.SpeciesName.Equals(args[0], StringComparison.OrdinalIgnoreCase))
|
||||
if (character.Name.ToLowerInvariant() == args[0] || character.SpeciesName.ToLowerInvariant() == args[0])
|
||||
{
|
||||
ThrowError(character.ID + ": " + character.Name.ToString());
|
||||
}
|
||||
@@ -553,8 +554,8 @@ namespace Barotrauma
|
||||
if (args.Length < 2) return;
|
||||
|
||||
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(a =>
|
||||
a.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
|
||||
a.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
a.Name.ToLowerInvariant() == args[0].ToLowerInvariant() ||
|
||||
a.Identifier.ToLowerInvariant() == args[0].ToLowerInvariant());
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
ThrowError("Affliction \"" + args[0] + "\" not found.");
|
||||
@@ -705,7 +706,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Submarine.MainSub == null || Level.Loaded == null) return;
|
||||
|
||||
if (args.Length > 0 && args[0].Equals("start", StringComparison.OrdinalIgnoreCase))
|
||||
if (args.Length > 0 && args[0].ToLowerInvariant() == "start")
|
||||
{
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.StartPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
|
||||
}
|
||||
@@ -1219,17 +1220,15 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
string firstCommand = splitCommand[0].ToLowerInvariant();
|
||||
|
||||
if (!firstCommand.Equals("admin", StringComparison.OrdinalIgnoreCase))
|
||||
if (!splitCommand[0].ToLowerInvariant().Equals("admin"))
|
||||
{
|
||||
NewMessage(command, Color.White, true);
|
||||
}
|
||||
|
||||
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
Command matchingCommand = commands.Find(c => c.names.Contains(firstCommand));
|
||||
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommand[0].ToLowerInvariant()));
|
||||
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
|
||||
@@ -1237,7 +1236,7 @@ namespace Barotrauma
|
||||
NewMessage("Server command: " + command, Color.Cyan);
|
||||
return;
|
||||
}
|
||||
else if (GameMain.Client.HasConsoleCommandPermission(firstCommand))
|
||||
else if (GameMain.Client.HasConsoleCommandPermission(splitCommand[0].ToLowerInvariant()))
|
||||
{
|
||||
if (matchingCommand.RelayToServer)
|
||||
{
|
||||
@@ -1256,7 +1255,7 @@ namespace Barotrauma
|
||||
bool commandFound = false;
|
||||
foreach (Command c in commands)
|
||||
{
|
||||
if (!c.names.Contains(firstCommand)) { continue; }
|
||||
if (!c.names.Contains(splitCommand[0].ToLowerInvariant())) continue;
|
||||
c.Execute(splitCommand.Skip(1).ToArray());
|
||||
commandFound = true;
|
||||
break;
|
||||
@@ -1284,7 +1283,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var matchingCharacters = Character.CharacterList.FindAll(c =>
|
||||
c.Name.Equals(characterName, StringComparison.OrdinalIgnoreCase) &&
|
||||
c.Name.ToLowerInvariant() == characterName &&
|
||||
(!c.IsRemotePlayer || !ignoreRemotePlayers || allowedRemotePlayer?.Character == c));
|
||||
|
||||
if (!matchingCharacters.Any())
|
||||
@@ -1330,7 +1329,7 @@ namespace Barotrauma
|
||||
JobPrefab job = null;
|
||||
if (!JobPrefab.Prefabs.ContainsKey(characterLowerCase))
|
||||
{
|
||||
job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(characterLowerCase, StringComparison.OrdinalIgnoreCase));
|
||||
job = JobPrefab.Prefabs.Find(jp => jp.Name?.ToLowerInvariant() == characterLowerCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1588,7 +1587,12 @@ 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)
|
||||
{
|
||||
commandName = commandName.ToLowerInvariant();
|
||||
return commands.Find(c => c.names.Any(n => n.ToLowerInvariant() == commandName));
|
||||
}
|
||||
|
||||
|
||||
public static void Log(string message)
|
||||
{
|
||||
@@ -1606,24 +1610,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.WriteLine(error);
|
||||
NewMessage(error, Color.Red);
|
||||
#if CLIENT
|
||||
var textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
|
||||
style: "InnerFrame", color: Color.White)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) },
|
||||
error, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
TextColor = Color.Red
|
||||
};
|
||||
textContainer.RectTransform.NonScaledSize = new Point(textContainer.RectTransform.NonScaledSize.X, textBlock.RectTransform.NonScaledSize.Y + 5);
|
||||
textBlock.SetTextPos();
|
||||
|
||||
listBox.UpdateScrollBarSize();
|
||||
listBox.BarScroll = 1.0f;
|
||||
|
||||
if (createMessageBox)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(CreateMessageBox(error));
|
||||
@@ -1632,8 +1620,6 @@ namespace Barotrauma
|
||||
{
|
||||
isOpen = true;
|
||||
}
|
||||
#else
|
||||
NewMessage(error, Color.Red);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -219,40 +219,13 @@ namespace Barotrauma
|
||||
|
||||
private void CreateEvents(ScriptedEventSet eventSet)
|
||||
{
|
||||
int applyCount = 1;
|
||||
if (eventSet.PerRuin)
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
applyCount = Level.Loaded.Ruins.Count();
|
||||
}
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
if (eventSet.EventPrefabs.Count > 0)
|
||||
{
|
||||
if (eventSet.EventPrefabs.Count > 0)
|
||||
{
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(eventSet.EventPrefabs, eventSet.EventPrefabs.Select(e => e.Commonness).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
selectedEvents.Add(eventSet, new List<ScriptedEvent>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
}
|
||||
if (eventSet.ChildSets.Count > 0)
|
||||
{
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
|
||||
if (newEventSet != null) { CreateEvents(newEventSet); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ScriptedEventPrefab eventPrefab in eventSet.EventPrefabs)
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(eventSet.EventPrefabs, eventSet.EventPrefabs.Select(e => e.Commonness).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
@@ -263,11 +236,30 @@ namespace Barotrauma
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
|
||||
foreach (ScriptedEventSet childEventSet in eventSet.ChildSets)
|
||||
}
|
||||
if (eventSet.ChildSets.Count > 0)
|
||||
{
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
|
||||
if (newEventSet != null) { CreateEvents(newEventSet); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ScriptedEventPrefab eventPrefab in eventSet.EventPrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
CreateEvents(childEventSet);
|
||||
selectedEvents.Add(eventSet, new List<ScriptedEvent>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
|
||||
foreach (ScriptedEventSet childEventSet in eventSet.ChildSets)
|
||||
{
|
||||
CreateEvents(childEventSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,14 +296,11 @@ namespace Barotrauma
|
||||
0.0f, 1.0f);
|
||||
|
||||
//don't create new events if within 50 meters of the start/end of the level
|
||||
if (!eventSet.AllowAtStart)
|
||||
if (distanceTraveled <= 0.0f ||
|
||||
distFromStart * Physics.DisplayToRealWorldRatio < 50.0f ||
|
||||
distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f)
|
||||
{
|
||||
if (distanceTraveled <= 0.0f ||
|
||||
distFromStart * Physics.DisplayToRealWorldRatio < 50.0f ||
|
||||
distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
|
||||
|
||||
@@ -44,11 +44,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override int TeamCount
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
public CombatMission(MissionPrefab prefab, Location[] locations)
|
||||
: base(prefab, locations)
|
||||
{
|
||||
@@ -118,7 +113,7 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (wifiComponent.Item.Submarine == subs[i] || subs[i].ConnectedDockingPorts.ContainsKey(wifiComponent.Item.Submarine))
|
||||
if (wifiComponent.Item.Submarine == subs[i] || subs[i].DockedTo.Contains(wifiComponent.Item.Submarine))
|
||||
{
|
||||
wifiComponent.TeamID = subs[i].TeamID;
|
||||
}
|
||||
|
||||
@@ -74,11 +74,6 @@ namespace Barotrauma
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual int TeamCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get { return Enumerable.Empty<Vector2>(); }
|
||||
@@ -141,7 +136,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
allowedMissions.AddRange(MissionPrefab.List.Where(m => ((int)(missionType & m.Type)) != 0));
|
||||
allowedMissions.AddRange(MissionPrefab.List.Where(m => ((int)(missionType & m.type)) != 0));
|
||||
}
|
||||
|
||||
allowedMissions.RemoveAll(m => isSinglePlayer ? m.MultiplayerOnly : m.SingleplayerOnly);
|
||||
@@ -173,9 +168,10 @@ namespace Barotrauma
|
||||
|
||||
public virtual void Update(float deltaTime) { }
|
||||
|
||||
public virtual void AssignTeamIDs(List<Networking.Client> clients)
|
||||
public virtual bool AssignTeamIDs(List<Networking.Client> clients)
|
||||
{
|
||||
clients.ForEach(c => c.TeamID = Character.TeamType.Team1);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void ShowMessage(int missionState)
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly ConstructorInfo constructor;
|
||||
|
||||
public readonly MissionType Type;
|
||||
public readonly MissionType type;
|
||||
|
||||
public readonly bool MultiplayerOnly, SingleplayerOnly;
|
||||
|
||||
@@ -154,18 +154,18 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
string missionTypeName = element.GetAttributeString("type", "");
|
||||
if (!Enum.TryParse(missionTypeName, out Type))
|
||||
if (!Enum.TryParse(missionTypeName, out type))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in mission prefab \"" + Name + "\" - \"" + missionTypeName + "\" is not a valid mission type.");
|
||||
return;
|
||||
}
|
||||
if (Type == MissionType.None)
|
||||
if (type == MissionType.None)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in mission prefab \"" + Name + "\" - mission type cannot be none.");
|
||||
return;
|
||||
}
|
||||
|
||||
constructor = missionClasses[Type].GetConstructor(new[] { typeof(MissionPrefab), typeof(Location[]) });
|
||||
constructor = missionClasses[type].GetConstructor(new[] { typeof(MissionPrefab), typeof(Location[]) });
|
||||
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
@@ -176,11 +176,11 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Pair<string, string> allowedLocationType in AllowedLocationTypes)
|
||||
{
|
||||
if (allowedLocationType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
if (allowedLocationType.First.ToLowerInvariant() == "any" ||
|
||||
allowedLocationType.First.ToLowerInvariant() == from.Type.Identifier.ToLowerInvariant())
|
||||
{
|
||||
if (allowedLocationType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
if (allowedLocationType.Second.ToLowerInvariant() == "any" ||
|
||||
allowedLocationType.Second.ToLowerInvariant() == to.Type.Identifier.ToLowerInvariant())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ namespace Barotrauma
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public Mission Instantiate(Location[] locations)
|
||||
{
|
||||
return constructor?.Invoke(new object[] { this, locations }) as Mission;
|
||||
|
||||
@@ -9,38 +9,42 @@ namespace Barotrauma
|
||||
{
|
||||
class MonsterEvent : ScriptedEvent
|
||||
{
|
||||
private readonly string speciesName;
|
||||
private readonly int minAmount, maxAmount;
|
||||
private string speciesName;
|
||||
|
||||
private int minAmount, maxAmount;
|
||||
|
||||
private List<Character> monsters;
|
||||
|
||||
private readonly bool spawnDeep;
|
||||
private bool spawnDeep;
|
||||
|
||||
private Vector2? spawnPos;
|
||||
|
||||
private readonly bool disallowed;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
private bool disallowed;
|
||||
|
||||
private Level.PositionType spawnPosType;
|
||||
|
||||
private bool spawnPending;
|
||||
|
||||
private string characterFileName;
|
||||
|
||||
public override Vector2 DebugDrawPos
|
||||
{
|
||||
get { return spawnPos ?? Vector2.Zero; }
|
||||
get { return spawnPos.HasValue ? spawnPos.Value : Vector2.Zero; }
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (maxAmount <= 1)
|
||||
{
|
||||
return "MonsterEvent (" + speciesName + ")";
|
||||
return "MonsterEvent (" + characterFileName + ")";
|
||||
}
|
||||
else if (minAmount < maxAmount)
|
||||
{
|
||||
return "MonsterEvent (" + speciesName + " x" + minAmount + "-" + maxAmount + ")";
|
||||
return "MonsterEvent (" + characterFileName + " x" + minAmount + "-" + maxAmount + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "MonsterEvent (" + speciesName + " x" + maxAmount + ")";
|
||||
return "MonsterEvent (" + characterFileName + " x" + maxAmount + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +76,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
spawnDeep = prefab.ConfigElement.GetAttributeBool("spawndeep", false);
|
||||
characterFileName = Path.GetFileName(Path.GetDirectoryName(speciesName)).ToLower();
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
@@ -240,37 +245,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (submarine.IsOutpost) { continue; }
|
||||
float minDist = GetMinDistanceToSub(submarine);
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { return; }
|
||||
}
|
||||
|
||||
//if spawning in a ruin/cave, wait for someone to be close to it to spawning
|
||||
//unnecessary monsters in places the players might never visit during the round
|
||||
if (spawnPosType == Level.PositionType.Ruin ||
|
||||
spawnPosType == Level.PositionType.Cave)
|
||||
{
|
||||
bool someoneNearby = false;
|
||||
float minDist = Items.Components.Sonar.DefaultSonarRange * 0.8f;
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (submarine.IsOutpost) { continue; }
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist)
|
||||
{
|
||||
someoneNearby = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c == Character.Controlled || c.IsRemotePlayer)
|
||||
{
|
||||
if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist)
|
||||
{
|
||||
someoneNearby = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!someoneNearby) { return; }
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) return;
|
||||
}
|
||||
|
||||
spawnPending = false;
|
||||
@@ -305,7 +280,7 @@ namespace Barotrauma
|
||||
|
||||
Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);
|
||||
#if CLIENT
|
||||
if (Character.Controlled != null) { targetEntity = Character.Controlled; }
|
||||
if (Character.Controlled != null) targetEntity = (Entity)Character.Controlled;
|
||||
#endif
|
||||
|
||||
bool monstersDead = true;
|
||||
@@ -322,7 +297,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (monstersDead) { Finished(); }
|
||||
if (monstersDead) Finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,6 @@ namespace Barotrauma
|
||||
//the events in this set are delayed if the current EventManager intensity is not between these values
|
||||
public readonly float MinIntensity, MaxIntensity;
|
||||
|
||||
public readonly bool AllowAtStart;
|
||||
|
||||
public readonly bool PerRuin;
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
public readonly List<ScriptedEventPrefab> EventPrefabs;
|
||||
@@ -58,9 +54,6 @@ namespace Barotrauma
|
||||
MinDistanceTraveled = element.GetAttributeFloat("mindistancetraveled", 0.0f);
|
||||
MinMissionTime = element.GetAttributeFloat("minmissiontime", 0.0f);
|
||||
|
||||
AllowAtStart = element.GetAttributeBool("allowatstart", false);
|
||||
PerRuin = element.GetAttributeBool("perruin", false);
|
||||
|
||||
Commonness[""] = 1.0f;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -70,7 +63,7 @@ namespace Barotrauma
|
||||
Commonness[""] = subElement.GetAttributeFloat("commonness", 0.0f);
|
||||
foreach (XElement overrideElement in subElement.Elements())
|
||||
{
|
||||
if (overrideElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase))
|
||||
if (overrideElement.Name.ToString().ToLowerInvariant() == "override")
|
||||
{
|
||||
string levelType = overrideElement.GetAttributeString("leveltype", "");
|
||||
if (!Commonness.ContainsKey(levelType))
|
||||
@@ -123,7 +116,7 @@ namespace Barotrauma
|
||||
int i = 0;
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
{
|
||||
if (!element.Name.ToString().Equals("eventset", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (element.Name.ToString().ToLowerInvariant() != "eventset") { continue; }
|
||||
List.Add(new ScriptedEventSet(element, i.ToString()));
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -7,23 +7,24 @@ namespace Barotrauma.Extensions
|
||||
public static class IEnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Randomizes the collection (using OrderBy) and returns it.
|
||||
/// Randomizes the collection and returns it.
|
||||
/// </summary>
|
||||
public static IOrderedEnumerable<T> Randomize<T>(this IEnumerable<T> source, Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
public static IOrderedEnumerable<T> Randomize<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return source.OrderBy(i => Rand.Value(randSync));
|
||||
return source.OrderBy(i => Rand.Value());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomizes the list in place without creating a new collection, using a Fisher-Yates-based algorithm.
|
||||
/// Randomizes the list in place.
|
||||
/// </summary>
|
||||
public static void Shuffle<T>(this IList<T> list, Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
public static void RandomizeList<T>(this List<T> list)
|
||||
{
|
||||
//Fisher-Yates shuffle
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = Rand.Int(n + 1, randSync);
|
||||
int k = Rand.Int(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
@@ -89,11 +90,6 @@ namespace Barotrauma.Extensions
|
||||
return source.Count(predicate) > 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ToEnumerable<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
|
||||
// source: https://stackoverflow.com/questions/19237868/get-all-children-to-one-list-recursive-c-sharp
|
||||
public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Barotrauma
|
||||
|
||||
spawnedItems.Clear();
|
||||
var validContainers = new Dictionary<ItemContainer, PreferredContainer>();
|
||||
prefabsWithContainer.Shuffle();
|
||||
prefabsWithContainer.RandomizeList();
|
||||
// Spawn items that have an ItemContainer component first so we can fill them up with items if needed (oxygen tanks inside the spawned diving masks, etc)
|
||||
for (int i = 0; i < prefabsWithContainer.Count; i++)
|
||||
{
|
||||
@@ -82,7 +82,7 @@ namespace Barotrauma
|
||||
// Another pass for items with containers because also they can spawn inside other items (like smg magazine)
|
||||
prefabsWithContainer.ForEach(i => SpawnItems(i));
|
||||
// Spawn items that don't have containers last
|
||||
prefabsWithoutContainer.Shuffle();
|
||||
prefabsWithoutContainer.RandomizeList();
|
||||
prefabsWithoutContainer.ForEach(i => SpawnItems(i));
|
||||
|
||||
if (OutputDebugInfo)
|
||||
|
||||
@@ -150,14 +150,14 @@ namespace Barotrauma
|
||||
SaveUtil.LoadGame(SavePath);
|
||||
}
|
||||
|
||||
public void StartRound(string levelSeed, float? difficulty = null)
|
||||
public void StartRound(string levelSeed, float? difficulty = null, bool loadSecondSub = false)
|
||||
{
|
||||
Level randomLevel = Level.CreateRandom(levelSeed, difficulty);
|
||||
|
||||
StartRound(randomLevel, true);
|
||||
StartRound(randomLevel, true, loadSecondSub);
|
||||
}
|
||||
|
||||
public void StartRound(Level level, bool reloadSub = true, bool mirrorLevel = false)
|
||||
public void StartRound(Level level, bool reloadSub = true, bool loadSecondSub = false, bool mirrorLevel = false)
|
||||
{
|
||||
//make sure no status effects have been carried on from the next round
|
||||
//(they should be stopped in EndRound, this is a safeguard against cases where the round is ended ungracefully)
|
||||
@@ -177,7 +177,7 @@ namespace Barotrauma
|
||||
|
||||
if (reloadSub || Submarine.MainSub != Submarine) { Submarine.Load(true); }
|
||||
Submarine.MainSub = Submarine;
|
||||
if (GameMode.Mission != null && GameMode.Mission.TeamCount > 1)
|
||||
if (loadSecondSub)
|
||||
{
|
||||
if (Submarine.MainSubs[1] == null)
|
||||
{
|
||||
@@ -231,7 +231,7 @@ namespace Barotrauma
|
||||
if (port.Item.WorldPosition.Y < Submarine.WorldPosition.Y) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(port.Item.WorldPosition, level.StartOutpost.WorldPosition);
|
||||
if (myPort == null || dist < closestDistance || (port.MainDockingPort && !myPort.MainDockingPort))
|
||||
if (myPort == null || dist < closestDistance)
|
||||
{
|
||||
myPort = port;
|
||||
closestDistance = dist;
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace Barotrauma
|
||||
{
|
||||
voiceChatVolume = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
#if CLIENT
|
||||
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume, 0);
|
||||
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume * 30.0f, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,6 @@ namespace Barotrauma
|
||||
|
||||
#if DEBUG
|
||||
public bool AutomaticQuickStartEnabled { get; set; }
|
||||
public bool TextManagerDebugModeEnabled { get; set; }
|
||||
#endif
|
||||
|
||||
private FileSystemWatcher modsFolderWatcher;
|
||||
@@ -1203,7 +1202,6 @@ namespace Barotrauma
|
||||
new XAttribute("tutorialskipwarning", ShowTutorialSkipWarning)
|
||||
#if DEBUG
|
||||
, new XAttribute("automaticquickstartenabled", AutomaticQuickStartEnabled)
|
||||
, new XAttribute("textmanagerdebugmodeenabled", TextManagerDebugModeEnabled)
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -1389,7 +1387,6 @@ namespace Barotrauma
|
||||
ShowTutorialSkipWarning = doc.Root.GetAttributeBool("tutorialskipwarning", true);
|
||||
#if DEBUG
|
||||
AutomaticQuickStartEnabled = doc.Root.GetAttributeBool("automaticquickstartenabled", AutomaticQuickStartEnabled);
|
||||
TextManagerDebugModeEnabled = doc.Root.GetAttributeBool("textmanagerdebugmodeenabled", TextManagerDebugModeEnabled);
|
||||
#endif
|
||||
XElement gameplayElement = doc.Root.Element("gameplay");
|
||||
jobPreferences = new List<Pair<string, int>>();
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "item") continue;
|
||||
|
||||
string itemIdentifier = subElement.GetAttributeString("identifier", "");
|
||||
ItemPrefab itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||
|
||||
@@ -58,13 +58,6 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
|
||||
public bool MainDockingPort
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DockingPort DockingTarget { get; private set; }
|
||||
|
||||
public bool Docked
|
||||
@@ -180,8 +173,8 @@ namespace Barotrauma.Items.Components
|
||||
if (!item.linkedTo.Contains(target.item)) item.linkedTo.Add(target.item);
|
||||
if (!target.item.linkedTo.Contains(item)) target.item.linkedTo.Add(item);
|
||||
|
||||
if (!target.item.Submarine.DockedTo.Contains(item.Submarine)) target.item.Submarine.ConnectedDockingPorts.Add(item.Submarine, target);
|
||||
if (!item.Submarine.DockedTo.Contains(target.item.Submarine)) item.Submarine.ConnectedDockingPorts.Add(target.item.Submarine, this);
|
||||
if (!target.item.Submarine.DockedTo.Contains(item.Submarine)) target.item.Submarine.DockedTo.Add(item.Submarine);
|
||||
if (!item.Submarine.DockedTo.Contains(target.item.Submarine)) item.Submarine.DockedTo.Add(target.item.Submarine);
|
||||
|
||||
DockingTarget = target;
|
||||
DockingTarget.DockingTarget = this;
|
||||
@@ -710,8 +703,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnSecondaryUse, 1.0f);
|
||||
|
||||
DockingTarget.item.Submarine.ConnectedDockingPorts.Remove(item.Submarine);
|
||||
item.Submarine.ConnectedDockingPorts.Remove(DockingTarget.item.Submarine);
|
||||
DockingTarget.item.Submarine.DockedTo.Remove(item.Submarine);
|
||||
item.Submarine.DockedTo.Remove(DockingTarget.item.Submarine);
|
||||
|
||||
if (door != null && DockingTarget.door != null)
|
||||
{
|
||||
|
||||
@@ -287,21 +287,24 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (isBroken) { return true; }
|
||||
bool hasRequiredItems = HasRequiredItems(character, false);
|
||||
if (HasAccess(character))
|
||||
if (!isBroken)
|
||||
{
|
||||
float originalPickingTime = PickingTime;
|
||||
PickingTime = 0;
|
||||
ToggleState(ActionType.OnUse, character);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
bool hasRequiredItems = HasRequiredItems(character, false);
|
||||
if (HasAccess(character))
|
||||
{
|
||||
float originalPickingTime = PickingTime;
|
||||
PickingTime = 0;
|
||||
ToggleState(ActionType.OnUse, character);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
#if CLIENT
|
||||
else if (hasRequiredItems && character != null && character == Character.Controlled)
|
||||
{
|
||||
GUI.AddMessage(accessDeniedTxt, GUI.Style.Red);
|
||||
}
|
||||
else if (hasRequiredItems && character != null && character == Character.Controlled)
|
||||
{
|
||||
GUI.AddMessage(accessDeniedTxt, GUI.Style.Red);
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "attack") { continue; }
|
||||
attack = new Attack(subElement, item.Name + ", MeleeWeapon");
|
||||
}
|
||||
item.IsShootable = true;
|
||||
|
||||
+44
-57
@@ -29,13 +29,6 @@ namespace Barotrauma.Items.Components
|
||||
set { reload = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How projectiles the weapon launches when fired once.")]
|
||||
public int ProjectileCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "Random spread applied to the firing angle of the projectiles when used by a character with sufficient skills to use the weapon (in degrees).")]
|
||||
public float Spread
|
||||
{
|
||||
@@ -117,61 +110,55 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ProjectileCount; i++)
|
||||
Projectile projectile = FindProjectile(triggerOnUseOnContainers: true);
|
||||
if (projectile == null) { return true; }
|
||||
|
||||
float spread = GetSpread(character);
|
||||
float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
|
||||
rotation += spread * Rand.Range(-0.5f, 0.5f);
|
||||
|
||||
projectile.User = character;
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
projectile.IgnoredBodies = new List<Body>(limbBodies);
|
||||
|
||||
Vector2 projectilePos = item.SimPosition;
|
||||
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
{
|
||||
Projectile projectile = FindProjectile(triggerOnUseOnContainers: true);
|
||||
if (projectile == null) { return true; }
|
||||
|
||||
float spread = GetSpread(character);
|
||||
float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
|
||||
rotation += spread * Rand.Range(-0.5f, 0.5f);
|
||||
|
||||
projectile.User = character;
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
projectile.IgnoredBodies = new List<Body>(limbBodies);
|
||||
|
||||
Vector2 projectilePos = item.SimPosition;
|
||||
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
{
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = barrelPos;
|
||||
}
|
||||
else if ((sourcePos - barrelPos).LengthSquared() > 0.0001f)
|
||||
{
|
||||
//spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle
|
||||
projectilePos = sourcePos - Vector2.Normalize(barrelPos - projectilePos) * Math.Max(projectile.Item.body.GetMaxExtent(), 0.1f);
|
||||
}
|
||||
|
||||
projectile.Item.body.ResetDynamics();
|
||||
projectile.Item.SetTransform(projectilePos, rotation);
|
||||
|
||||
projectile.Use(deltaTime);
|
||||
if (projectile.Item.Removed) { continue; }
|
||||
projectile.User = character;
|
||||
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
|
||||
|
||||
//set the rotation of the projectile again because dropping the projectile resets the rotation
|
||||
projectile.Item.SetTransform(projectilePos,
|
||||
rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians));
|
||||
|
||||
item.RemoveContained(projectile.Item);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
//recoil
|
||||
item.body.ApplyLinearImpulse(
|
||||
new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f,
|
||||
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = barrelPos;
|
||||
}
|
||||
else if ((sourcePos - barrelPos).LengthSquared() > 0.0001f)
|
||||
{
|
||||
//spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle
|
||||
projectilePos = sourcePos - Vector2.Normalize(barrelPos - projectilePos) * Math.Max(projectile.Item.body.GetMaxExtent(), 0.1f);
|
||||
}
|
||||
|
||||
projectile.Item.body.ResetDynamics();
|
||||
projectile.Item.SetTransform(projectilePos, rotation);
|
||||
|
||||
projectile.Use(deltaTime);
|
||||
if (projectile.Item.Removed) { return true; }
|
||||
projectile.User = character;
|
||||
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
|
||||
|
||||
//set the rotation of the projectile again because dropping the projectile resets the rotation
|
||||
projectile.Item.SetTransform(projectilePos,
|
||||
rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians));
|
||||
|
||||
//recoil
|
||||
item.body.ApplyLinearImpulse(
|
||||
new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f,
|
||||
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
|
||||
LaunchProjSpecific();
|
||||
|
||||
item.RemoveContained(projectile.Item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
sinTime += deltaTime * 5;
|
||||
bool isAiming = false;
|
||||
var holdable = item.GetComponent<Holdable>();
|
||||
if (holdable != null)
|
||||
{
|
||||
isAiming = holdable.ControlPose;
|
||||
}
|
||||
sinTime = isAiming ? sinTime + deltaTime * 5 : 0;
|
||||
}
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
|
||||
@@ -741,7 +741,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
}
|
||||
ParseMsg();
|
||||
@@ -870,7 +870,6 @@ namespace Barotrauma.Items.Components
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "requireditem":
|
||||
case "requireditems":
|
||||
RelatedItem newRequiredItem = RelatedItem.Load(subElement, returnEmptyRequirements, item.Name);
|
||||
if (newRequiredItem == null) continue;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("fabricableitem", StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "fabricableitem")
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item " + item.Name + "! Fabrication recipes should be defined in the craftable item's xml, not in the fabricator.");
|
||||
break;
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Barotrauma.Items.Components
|
||||
if (GameMain.Client != null) { return false; }
|
||||
#endif
|
||||
|
||||
if (objective.Option.Equals("stoppumping", StringComparison.OrdinalIgnoreCase))
|
||||
if (objective.Option.ToLowerInvariant() == "stoppumping")
|
||||
{
|
||||
#if SERVER
|
||||
if (FlowPercentage > 0.0f)
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -652,20 +651,6 @@ namespace Barotrauma.Items.Components
|
||||
unsentChanges = true;
|
||||
}
|
||||
break;
|
||||
case "set_fissionrate":
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
{
|
||||
FissionRate = newFissionRate;
|
||||
unsentChanges = true;
|
||||
}
|
||||
break;
|
||||
case "set_turbineoutput":
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
{
|
||||
TurbineOutput = newTurbineOutput;
|
||||
unsentChanges = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (HasBeenTuned) { return true; }
|
||||
|
||||
if (string.IsNullOrEmpty(objective.Option) || objective.Option.Equals("charge", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.IsNullOrEmpty(objective.Option) || objective.Option.ToLowerInvariant() == "charge")
|
||||
{
|
||||
if (Math.Abs(rechargeSpeed - maxRechargeSpeed * aiRechargeTargetRatio) > 0.05f)
|
||||
{
|
||||
|
||||
@@ -53,9 +53,7 @@ namespace Barotrauma.Items.Components
|
||||
private PrismaticJoint stickJoint;
|
||||
private Body stickTarget;
|
||||
|
||||
private readonly Attack attack;
|
||||
|
||||
private Vector2 launchPos;
|
||||
private Attack attack;
|
||||
|
||||
public List<Body> IgnoredBodies;
|
||||
|
||||
@@ -159,7 +157,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "attack") continue;
|
||||
attack = new Attack(subElement, item.Name + ", Projectile");
|
||||
}
|
||||
}
|
||||
@@ -222,8 +220,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.Drop(null);
|
||||
|
||||
launchPos = item.SimPosition;
|
||||
|
||||
item.body.Enabled = true;
|
||||
item.body.ApplyLinearImpulse(impulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
|
||||
@@ -419,9 +415,7 @@ namespace Barotrauma.Items.Components
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) - dir,
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) + dir,
|
||||
collisionCategory: Physics.CollisionWall);
|
||||
if (wallBody?.FixtureList?.First() != null && wallBody.UserData is Structure structure &&
|
||||
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
|
||||
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
|
||||
if (wallBody?.FixtureList?.First() != null && wallBody.UserData is Structure structure)
|
||||
{
|
||||
target = wallBody.FixtureList.First();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@@ -112,7 +111,7 @@ namespace Barotrauma.Items.Components
|
||||
element.GetAttributeString("name", "");
|
||||
|
||||
//backwards compatibility
|
||||
var showRepairUIAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase));
|
||||
var showRepairUIAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().ToLowerInvariant() == "showrepairuithreshold");
|
||||
if (showRepairUIAttribute != null)
|
||||
{
|
||||
float repairThreshold;
|
||||
@@ -131,26 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the character manages to succesfully repair the item
|
||||
/// </summary>
|
||||
public bool CheckCharacterSuccess(Character character)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
|
||||
// Only check for success when repairing electrical devices
|
||||
if (requiredSkills.None(s => s != null && s.Identifier.Equals("electrical", StringComparison.OrdinalIgnoreCase))) { return true; }
|
||||
|
||||
//unpowered items can be repaired without a risk of electrical shock
|
||||
if (item.GetComponent<Powered>() is Powered powered && powered.Voltage < 0.1f) { return true; }
|
||||
|
||||
if (Rand.Range(0.0f, 0.5f) < DegreeOfSuccess(character)) { return true; }
|
||||
|
||||
item.ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public bool StartRepairing(Character character, FixActions action)
|
||||
{
|
||||
if (character == null || character.IsDead || action == FixActions.None)
|
||||
@@ -163,15 +143,8 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (CurrentFixer != character || currentFixerAction != action)
|
||||
{
|
||||
if (!CheckCharacterSuccess(character))
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, character.ID });
|
||||
return false;
|
||||
}
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#else
|
||||
if (GameMain.Client == null && (CurrentFixer != character || currentFixerAction != action) && !CheckCharacterSuccess(character)) { return false; }
|
||||
#endif
|
||||
CurrentFixer = character;
|
||||
CurrentFixerAction = action;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (XElement subElement in item.Prefab.ConfigElement.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("connectionpanel", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "connectionpanel") { continue; }
|
||||
|
||||
foreach (XElement connectionElement in subElement.Elements())
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("statuseffect", System.StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "statuseffect")
|
||||
{
|
||||
StatusEffects.Add(StatusEffect.Load(subElement, parentDebugName: "custom interface element (label " + Label + ")"));
|
||||
}
|
||||
|
||||
@@ -272,12 +272,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void UpdateAITarget(AITarget target)
|
||||
{
|
||||
target.Enabled = IsActive;
|
||||
if (!IsActive) { return; }
|
||||
if (target.MaxSightRange <= 0)
|
||||
{
|
||||
target.MaxSightRange = Range * 5;
|
||||
}
|
||||
target.SightRange = Math.Max(target.SightRange, target.MaxSightRange * lightBrightness);
|
||||
target.SightRange = target.MaxSightRange * lightBrightness;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness);
|
||||
|
||||
@@ -39,14 +39,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Serialize(false, false, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.")]
|
||||
public bool AllowCrossTeamCommunication
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "If enabled, any signals received from another chat-linked wifi component are displayed " +
|
||||
"as chat messages in the chatbox of the player holding the item.")]
|
||||
public bool LinkToChat
|
||||
@@ -92,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (sender == null || sender.channel != channel) { return false; }
|
||||
|
||||
if (sender.TeamID != TeamID && !AllowCrossTeamCommunication)
|
||||
if (sender.TeamID != TeamID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,13 +72,6 @@ namespace Barotrauma.Items.Components
|
||||
set { reloadTime = value; }
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How projectiles the weapon launches when fired once.")]
|
||||
public int ProjectileCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize("0.0,0.0", true, description: "The range at which the barrel can rotate. TODO")]
|
||||
public Vector2 RotationLimits
|
||||
{
|
||||
@@ -220,13 +213,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
this.cam = cam;
|
||||
|
||||
if (reload > 0.0f) { reload -= deltaTime; }
|
||||
if (reload > 0.0f) reload -= deltaTime;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (minRotation == maxRotation) { return; }
|
||||
if (minRotation == maxRotation) return;
|
||||
|
||||
float targetMidDiff = MathHelper.WrapAngle(targetRotation - (minRotation + maxRotation) / 2.0f);
|
||||
|
||||
@@ -237,19 +230,12 @@ namespace Barotrauma.Items.Components
|
||||
targetRotation = (targetMidDiff < 0.0f) ? minRotation : maxRotation;
|
||||
}
|
||||
|
||||
float degreeOfSuccess = user == null ? 0.5f : DegreeOfSuccess(user);
|
||||
if (degreeOfSuccess < 0.5f) { degreeOfSuccess *= degreeOfSuccess; } //the ease of aiming drops quickly with insufficient skill levels
|
||||
float degreeOfSuccess = user == null ? 0.5f : DegreeOfSuccess(user);
|
||||
if (degreeOfSuccess < 0.5f) degreeOfSuccess *= degreeOfSuccess; //the ease of aiming drops quickly with insufficient skill levels
|
||||
float springStiffness = MathHelper.Lerp(SpringStiffnessLowSkill, SpringStiffnessHighSkill, degreeOfSuccess);
|
||||
float springDamping = MathHelper.Lerp(SpringDampingLowSkill, SpringDampingHighSkill, degreeOfSuccess);
|
||||
float rotationSpeed = MathHelper.Lerp(RotationSpeedLowSkill, RotationSpeedHighSkill, degreeOfSuccess);
|
||||
|
||||
if (user?.Info != null)
|
||||
{
|
||||
user.Info.IncreaseSkillLevel("weapons",
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f),
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
|
||||
angularVelocity +=
|
||||
(MathHelper.WrapAngle(targetRotation - rotation) * springStiffness - angularVelocity * springDamping) * deltaTime;
|
||||
angularVelocity = MathHelper.Clamp(angularVelocity, -rotationSpeed, rotationSpeed);
|
||||
@@ -279,16 +265,18 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (!characterUsable && character != null) { return false; }
|
||||
if (!characterUsable && character != null) return false;
|
||||
return TryLaunch(deltaTime, character);
|
||||
}
|
||||
|
||||
private bool TryLaunch(float deltaTime, Character character = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null) return false;
|
||||
#endif
|
||||
|
||||
if (reload > 0.0f) return false;
|
||||
|
||||
if (reload > 0.0f) { return false; }
|
||||
|
||||
if (GetAvailableBatteryPower() < powerConsumption)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -300,77 +288,72 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Projectile launchedProjectile = null;
|
||||
for (int i = 0; i < ProjectileCount; i++)
|
||||
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
//use linked projectile containers in case they have to react to the turret being launched somehow
|
||||
//(play a sound, spawn more projectiles)
|
||||
if (!(e is Item linkedItem)) continue;
|
||||
ItemContainer projectileContainer = linkedItem.GetComponent<ItemContainer>();
|
||||
if (projectileContainer != null)
|
||||
{
|
||||
//use linked projectile containers in case they have to react to the turret being launched somehow
|
||||
//(play a sound, spawn more projectiles)
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
ItemContainer projectileContainer = linkedItem.GetComponent<ItemContainer>();
|
||||
if (projectileContainer != null)
|
||||
linkedItem.Use(deltaTime, null);
|
||||
var repairable = linkedItem.GetComponent<Repairable>();
|
||||
if (repairable != null)
|
||||
{
|
||||
linkedItem.Use(deltaTime, null);
|
||||
var repairable = linkedItem.GetComponent<Repairable>();
|
||||
if (repairable != null)
|
||||
{
|
||||
repairable.LastActiveTime = (float)Timing.TotalTime + 1.0f;
|
||||
}
|
||||
repairable.LastActiveTime = (float)Timing.TotalTime + 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
var projectiles = GetLoadedProjectiles(true);
|
||||
if (projectiles.Count == 0)
|
||||
{
|
||||
//coilguns spawns ammo in the ammo boxes with the OnUse statuseffect when the turret is launched,
|
||||
//causing a one frame delay before the gun can be launched (or more in multiplayer where there may be a longer delay)
|
||||
// -> attempt to launch the gun multiple times before showing the "no ammo" flash
|
||||
failedLaunchAttempts++;
|
||||
#if CLIENT
|
||||
if (!flashNoAmmo && character != null && character == Character.Controlled && failedLaunchAttempts > 20)
|
||||
{
|
||||
flashNoAmmo = true;
|
||||
failedLaunchAttempts = 0;
|
||||
GUI.PlayUISound(GUISoundType.PickItemFail);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
failedLaunchAttempts = 0;
|
||||
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float neededPower = powerConsumption;
|
||||
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
float takePower = neededPower / batteries.Count;
|
||||
takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
neededPower -= takePower;
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
launchedProjectile = projectiles[0];
|
||||
Launch(projectiles[0].Item, character);
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (character != null && launchedProjectile != null)
|
||||
var projectiles = GetLoadedProjectiles(true);
|
||||
if (projectiles.Count == 0)
|
||||
{
|
||||
string msg = character.LogName + " launched " + item.Name + " (projectile: " + launchedProjectile.Item.Name;
|
||||
var containedItems = launchedProjectile.Item.ContainedItems;
|
||||
//coilguns spawns ammo in the ammo boxes with the OnUse statuseffect when the turret is launched,
|
||||
//causing a one frame delay before the gun can be launched (or more in multiplayer where there may be a longer delay)
|
||||
// -> attempt to launch the gun multiple times before showing the "no ammo" flash
|
||||
failedLaunchAttempts++;
|
||||
#if CLIENT
|
||||
if (!flashNoAmmo && character != null && character == Character.Controlled && failedLaunchAttempts > 20)
|
||||
{
|
||||
flashNoAmmo = true;
|
||||
failedLaunchAttempts = 0;
|
||||
GUI.PlayUISound(GUISoundType.PickItemFail);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
failedLaunchAttempts = 0;
|
||||
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float neededPower = powerConsumption;
|
||||
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
float takePower = neededPower / batteries.Count;
|
||||
takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
neededPower -= takePower;
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Launch(projectiles[0].Item, character);
|
||||
|
||||
#if SERVER
|
||||
if (character != null)
|
||||
{
|
||||
string msg = character.LogName + " launched " + item.Name + " (projectile: " + projectiles[0].Item.Name;
|
||||
var containedItems = projectiles[0].Item.ContainedItems;
|
||||
if (containedItems == null || !containedItems.Any())
|
||||
{
|
||||
msg += ")";
|
||||
@@ -564,7 +547,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase))
|
||||
if (objective.Option.ToLowerInvariant() == "fireatwill")
|
||||
{
|
||||
character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f);
|
||||
character.SetInput(InputType.Shoot, true, true);
|
||||
|
||||
@@ -211,7 +211,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
public bool AutoEquipWhenFull { get; private set; }
|
||||
public bool DisplayContainedStatus { get; private set; }
|
||||
|
||||
public readonly int Variants;
|
||||
|
||||
@@ -265,7 +264,6 @@ namespace Barotrauma.Items.Components
|
||||
limbType = new LimbType[spriteCount];
|
||||
limb = new Limb[spriteCount];
|
||||
AutoEquipWhenFull = element.GetAttributeBool("autoequipwhenfull", true);
|
||||
DisplayContainedStatus = element.GetAttributeBool("displaycontainedstatus", false);
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -286,7 +284,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (XElement lightElement in subElement.Elements())
|
||||
{
|
||||
if (!lightElement.Name.ToString().Equals("lightcomponent", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (lightElement.Name.ToString().ToLowerInvariant() != "lightcomponent") continue;
|
||||
wearableSprites[i].LightComponent = new LightComponent(item, lightElement)
|
||||
{
|
||||
Parent = this
|
||||
|
||||
@@ -1839,30 +1839,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public float GetContainedItemConditionPercentage()
|
||||
{
|
||||
var containedItems = ContainedItems;
|
||||
|
||||
if (containedItems != null)
|
||||
{
|
||||
float condition = 0f;
|
||||
float maxCondition = 0f;
|
||||
|
||||
foreach (Item item in containedItems)
|
||||
{
|
||||
condition += item.condition;
|
||||
maxCondition += item.MaxCondition;
|
||||
}
|
||||
|
||||
if (maxCondition > 0.0f)
|
||||
{
|
||||
return condition / maxCondition;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Use(float deltaTime, Character character = null, Limb targetLimb = null)
|
||||
{
|
||||
if (RequireAimToUse && (character == null || !character.IsKeyDown(InputType.Aim)))
|
||||
|
||||
@@ -810,7 +810,7 @@ namespace Barotrauma
|
||||
|
||||
public PriceInfo GetPrice(Location location)
|
||||
{
|
||||
if (prices == null || !prices.ContainsKey(location.Type.Identifier.ToLowerInvariant())) { return null; }
|
||||
if (prices == null || !prices.ContainsKey(location.Type.Identifier.ToLowerInvariant())) return null;
|
||||
return prices[location.Type.Identifier.ToLowerInvariant()];
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma
|
||||
public void Save(XElement element)
|
||||
{
|
||||
element.Add(
|
||||
new XAttribute("items", JoinedIdentifiers),
|
||||
new XAttribute("identifiers", JoinedIdentifiers),
|
||||
new XAttribute("type", type.ToString()),
|
||||
new XAttribute("optional", IsOptional),
|
||||
new XAttribute("ignoreineditor", IgnoreInEditor));
|
||||
@@ -219,10 +219,7 @@ namespace Barotrauma
|
||||
string typeStr = element.GetAttributeString("type", "");
|
||||
if (string.IsNullOrEmpty(typeStr))
|
||||
{
|
||||
if (element.Name.ToString().Equals("containable", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
typeStr = "Contained";
|
||||
}
|
||||
if (element.Name.ToString().ToLowerInvariant() == "containable") typeStr = "Contained";
|
||||
}
|
||||
if (!Enum.TryParse(typeStr, true, out ri.type))
|
||||
{
|
||||
@@ -249,7 +246,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "statuseffect") continue;
|
||||
ri.statusEffects.Add(StatusEffect.Load(subElement, parentDebugName));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma
|
||||
partial class FireSource : ISpatialEntity
|
||||
{
|
||||
const float OxygenConsumption = 50.0f;
|
||||
const float GrowSpeed = 20.0f;
|
||||
const float GrowSpeed = 5.0f;
|
||||
|
||||
protected Hull hull;
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ namespace Barotrauma
|
||||
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 5.0f * sizeModifier, Math.Min(hull2.WaterVolume, hull2.Volume));
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - (hull1.WaterVolume));
|
||||
delta = Math.Min(delta, hull1.Volume + Hull.MaxCompress - (hull1.WaterVolume));
|
||||
hull1.WaterVolume += delta;
|
||||
hull2.WaterVolume -= delta;
|
||||
if (hull1.WaterVolume > hull1.Volume)
|
||||
@@ -399,7 +399,7 @@ namespace Barotrauma
|
||||
delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 5.0f * sizeModifier, Math.Min(hull1.WaterVolume, hull1.Volume));
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - (hull2.WaterVolume));
|
||||
delta = Math.Min(delta, hull2.Volume + Hull.MaxCompress - (hull2.WaterVolume));
|
||||
hull1.WaterVolume -= delta;
|
||||
hull2.WaterVolume += delta;
|
||||
if (hull2.WaterVolume > hull2.Volume)
|
||||
@@ -414,14 +414,14 @@ namespace Barotrauma
|
||||
{
|
||||
float avg = (hull1.Surface + hull2.Surface) / 2.0f;
|
||||
|
||||
if (hull1.WaterVolume < hull1.Volume / Hull.MaxCompress &&
|
||||
if (hull1.WaterVolume < hull1.Volume - Hull.MaxCompress &&
|
||||
hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1] < rect.Y)
|
||||
{
|
||||
hull1.WaveVel[hull1.WaveY.Length - 1] = (avg - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f;
|
||||
hull1.WaveVel[hull1.WaveY.Length - 2] = hull1.WaveVel[hull1.WaveY.Length - 1];
|
||||
}
|
||||
|
||||
if (hull2.WaterVolume < hull2.Volume / Hull.MaxCompress &&
|
||||
if (hull2.WaterVolume < hull2.Volume - Hull.MaxCompress &&
|
||||
hull2.Surface + hull2.WaveY[0] < rect.Y)
|
||||
{
|
||||
hull2.WaveVel[0] = (avg - (hull2.Surface + hull2.WaveY[0])) * 0.1f;
|
||||
@@ -436,12 +436,12 @@ namespace Barotrauma
|
||||
//lower room is full of water
|
||||
if (hull2.Pressure + subOffset.Y > hull1.Pressure && hull2.WaterVolume > 0.0f)
|
||||
{
|
||||
float delta = Math.Min(hull2.WaterVolume - hull2.Volume + (hull2.Volume * Hull.MaxCompress), deltaTime * 8000.0f * sizeModifier);
|
||||
float delta = Math.Min(hull2.WaterVolume - hull2.Volume + Hull.MaxCompress, deltaTime * 8000.0f * sizeModifier);
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
if (hull1.WaterVolume + delta > hull1.Volume * Hull.MaxCompress)
|
||||
if (hull1.WaterVolume + delta > hull1.Volume + Hull.MaxCompress)
|
||||
{
|
||||
delta -= (hull1.WaterVolume + delta) - (hull1.Volume * Hull.MaxCompress);
|
||||
delta -= (hull1.WaterVolume + delta) - (hull1.Volume + Hull.MaxCompress);
|
||||
}
|
||||
|
||||
delta = Math.Max(delta, 0.0f);
|
||||
@@ -469,9 +469,9 @@ namespace Barotrauma
|
||||
float delta = Math.Min(hull1.WaterVolume, deltaTime * 25000f * sizeModifier);
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
if (hull2.WaterVolume + delta > hull2.Volume * Hull.MaxCompress)
|
||||
if (hull2.WaterVolume + delta > hull2.Volume + Hull.MaxCompress)
|
||||
{
|
||||
delta -= (hull2.WaterVolume + delta) - (hull2.Volume * Hull.MaxCompress);
|
||||
delta -= (hull2.WaterVolume + delta) - (hull2.Volume + Hull.MaxCompress);
|
||||
}
|
||||
hull1.WaterVolume -= delta;
|
||||
hull2.WaterVolume += delta;
|
||||
@@ -489,7 +489,7 @@ namespace Barotrauma
|
||||
|
||||
if (open > 0.0f)
|
||||
{
|
||||
if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress && hull2.WaterVolume > hull2.Volume / Hull.MaxCompress)
|
||||
if (hull1.WaterVolume > hull1.Volume - Hull.MaxCompress && hull2.WaterVolume > hull2.Volume - Hull.MaxCompress)
|
||||
{
|
||||
float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f;
|
||||
hull1.LethalPressure = avgLethality;
|
||||
@@ -515,10 +515,10 @@ namespace Barotrauma
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size * open * open;
|
||||
|
||||
float delta = hull1.Volume * Hull.MaxCompress * sizeModifier * deltaTime;
|
||||
float delta = Hull.MaxCompress * sizeModifier * deltaTime;
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
|
||||
delta = Math.Min(delta, hull1.Volume + Hull.MaxCompress - hull1.WaterVolume);
|
||||
hull1.WaterVolume += delta;
|
||||
|
||||
if (hull1.WaterVolume > hull1.Volume) hull1.Pressure += 0.5f;
|
||||
@@ -541,7 +541,7 @@ namespace Barotrauma
|
||||
higherSurface = hull1.Surface;
|
||||
lowerSurface = rect.Y;
|
||||
|
||||
if (hull1.WaterVolume < hull1.Volume / Hull.MaxCompress &&
|
||||
if (hull1.WaterVolume < hull1.Volume - Hull.MaxCompress &&
|
||||
hull1.Surface < rect.Y)
|
||||
{
|
||||
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
|
||||
@@ -576,7 +576,7 @@ namespace Barotrauma
|
||||
{
|
||||
flowForce = new Vector2(0.0f, delta);
|
||||
}
|
||||
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
|
||||
if (hull1.WaterVolume >= hull1.Volume - Hull.MaxCompress)
|
||||
{
|
||||
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,8 @@ namespace Barotrauma
|
||||
public static float WaveSpread = 0.05f;
|
||||
public static float WaveDampening = 0.05f;
|
||||
|
||||
//how much excess water the room can contain, relative to the volume of the room.
|
||||
//needed to make it possible for pressure to "push" water up through U-shaped hull configurations
|
||||
public const float MaxCompress = 1.05f;
|
||||
//how much excess water the room can contain (= more than the volume of the room)
|
||||
public const float MaxCompress = 10000f;
|
||||
|
||||
public readonly Dictionary<string, SerializableProperty> properties;
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
@@ -155,7 +154,7 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
waterVolume = MathHelper.Clamp(value, 0.0f, Volume * MaxCompress);
|
||||
waterVolume = MathHelper.Clamp(value, 0.0f, Volume + MaxCompress);
|
||||
if (waterVolume < Volume) Pressure = rect.Y - rect.Height + waterVolume / rect.Width;
|
||||
if (waterVolume > 0.0f) update = true;
|
||||
}
|
||||
@@ -322,7 +321,6 @@ namespace Barotrauma
|
||||
CeilingHeight = ConvertUnits.ToDisplayUnits(upperPickedPos.Y - lowerPickedPos.Y);
|
||||
}
|
||||
}
|
||||
Pressure = rect.Y - rect.Height + waterVolume / rect.Width;
|
||||
}
|
||||
|
||||
public void AddToGrid(Submarine submarine)
|
||||
@@ -875,7 +873,7 @@ namespace Barotrauma
|
||||
|
||||
var hull = new Hull(MapEntityPrefab.Find(null, "hull"), rect, submarine)
|
||||
{
|
||||
WaterVolume = element.GetAttributeFloat("pressure", 0.0f),
|
||||
waterVolume = element.GetAttributeFloat("pressure", 0.0f),
|
||||
ID = (ushort)int.Parse(element.Attribute("ID").Value)
|
||||
};
|
||||
|
||||
|
||||
@@ -310,7 +310,6 @@ namespace Barotrauma
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders();
|
||||
dockedSubBorders.Inflate(dockedSubBorders.Size.ToVector2() * 0.05f);
|
||||
minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height));
|
||||
minWidth = Math.Min(minWidth, maxWidth);
|
||||
}
|
||||
@@ -1623,7 +1622,7 @@ namespace Barotrauma
|
||||
EndOutpost = outpost;
|
||||
if (GameMain.GameSession?.EndLocation != null) { outpost.Name = GameMain.GameSession.EndLocation.Name; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsModeStartOutpostCompatible()
|
||||
|
||||
@@ -415,10 +415,10 @@ namespace Barotrauma
|
||||
string biomeName = biomeNames[i].Trim().ToLowerInvariant();
|
||||
if (biomeName == "none") { continue; }
|
||||
|
||||
Biome matchingBiome = biomes.Find(b => b.Identifier.Equals(biomeName, StringComparison.OrdinalIgnoreCase));
|
||||
Biome matchingBiome = biomes.Find(b => b.Identifier.ToLowerInvariant() == biomeName);
|
||||
if (matchingBiome == null)
|
||||
{
|
||||
matchingBiome = biomes.Find(b => b.DisplayName.Equals(biomeName, StringComparison.OrdinalIgnoreCase));
|
||||
matchingBiome = biomes.Find(b => b.DisplayName.ToLowerInvariant() == biomeName);
|
||||
if (matchingBiome == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in level generation parameters: biome \"" + biomeName + "\" not found.");
|
||||
@@ -487,7 +487,7 @@ namespace Barotrauma
|
||||
mainElement = doc.Root.FirstElement();
|
||||
biomeElements.Clear();
|
||||
levelParamElements.Clear();
|
||||
DebugConsole.NewMessage($"Overriding the level generation parameters and biomes with '{file.Path}'", Color.Yellow);
|
||||
DebugConsole.NewMessage($"Overriding the level generation parameters with '{file.Path}'", Color.Yellow);
|
||||
}
|
||||
else if (biomeElements.Any() || levelParamElements.Any())
|
||||
{
|
||||
@@ -497,22 +497,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (XElement element in mainElement.Elements())
|
||||
{
|
||||
if (element.IsOverride())
|
||||
{
|
||||
if (element.FirstElement().Name.ToString().Equals("biomes", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
biomeElements.Clear();
|
||||
biomeElements.AddRange(element.FirstElement().Elements());
|
||||
DebugConsole.NewMessage($"Overriding biomes with '{file.Path}'", Color.Yellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
levelParamElements.Clear();
|
||||
DebugConsole.NewMessage($"Overriding the level generation parameters with '{file.Path}'", Color.Yellow);
|
||||
levelParamElements.AddRange(element.Elements());
|
||||
}
|
||||
}
|
||||
else if (element.Name.ToString().Equals("biomes", StringComparison.OrdinalIgnoreCase))
|
||||
if (element.Name.ToString().ToLowerInvariant() == "biomes")
|
||||
{
|
||||
biomeElements.AddRange(element.Elements());
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace Barotrauma.RuinGeneration
|
||||
{
|
||||
foreach (XElement subElement in element2.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("chooseone", StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "chooseone")
|
||||
{
|
||||
groupIndex++;
|
||||
LoadEntities(subElement, ref groupIndex);
|
||||
@@ -390,7 +390,7 @@ namespace Barotrauma.RuinGeneration
|
||||
SourceEntityIdentifier = element.GetAttributeString("sourceentity", "");
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("wire", StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "wire")
|
||||
{
|
||||
WireConnection = new Pair<string, string>(
|
||||
subElement.GetAttributeString("from", ""),
|
||||
|
||||
@@ -721,10 +721,8 @@ namespace Barotrauma.RuinGeneration
|
||||
|
||||
//doors create their own gaps, don't create an additional one if there's a door at this
|
||||
bool doorFound = false;
|
||||
foreach (Item item in Item.ItemList)
|
||||
foreach (Door door in doors)
|
||||
{
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door == null) { continue; }
|
||||
if (Math.Abs(door.Item.WorldPosition.X - gapRect.Value.Center.X) < 5 &&
|
||||
Math.Abs(door.Item.WorldPosition.Y - gapRect.Value.Center.Y) < 5)
|
||||
{
|
||||
|
||||
@@ -284,7 +284,7 @@ namespace Barotrauma
|
||||
foreach (LocationConnection connection in connections)
|
||||
{
|
||||
float centerDist = Vector2.Distance(connection.CenterPos, mapCenter);
|
||||
connection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 0, 100);
|
||||
connection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 10.0f, Rand.RandSync.Server), 0, 100);
|
||||
}
|
||||
|
||||
AssignBiomes();
|
||||
@@ -463,7 +463,7 @@ namespace Barotrauma
|
||||
bool disallowedFound = false;
|
||||
foreach (string disallowedLocationName in typeChange.DisallowedAdjacentLocations)
|
||||
{
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Identifier.Equals(disallowedLocationName, StringComparison.OrdinalIgnoreCase)))
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Identifier.ToLowerInvariant() == disallowedLocationName.ToLowerInvariant()))
|
||||
{
|
||||
disallowedFound = true;
|
||||
break;
|
||||
@@ -475,7 +475,7 @@ namespace Barotrauma
|
||||
bool requiredFound = false;
|
||||
foreach (string requiredLocationName in typeChange.RequiredAdjacentLocations)
|
||||
{
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Identifier.Equals(requiredLocationName, StringComparison.OrdinalIgnoreCase)))
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Identifier.ToLowerInvariant() == requiredLocationName.ToLowerInvariant()))
|
||||
{
|
||||
requiredFound = true;
|
||||
break;
|
||||
@@ -499,7 +499,7 @@ namespace Barotrauma
|
||||
if (selectedTypeChange != null)
|
||||
{
|
||||
string prevName = location.Name;
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(selectedTypeChange.ChangeToType, StringComparison.OrdinalIgnoreCase)));
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.ToLowerInvariant() == selectedTypeChange.ChangeToType.ToLowerInvariant()));
|
||||
ChangeLocationType(location, prevName, selectedTypeChange);
|
||||
location.TypeChangeTimer = -1;
|
||||
break;
|
||||
@@ -553,12 +553,13 @@ namespace Barotrauma
|
||||
string prevLocationName = location.Name;
|
||||
LocationType prevLocationType = location.Type;
|
||||
location.Discovered = true;
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(locationType, StringComparison.OrdinalIgnoreCase)));
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.ToLowerInvariant() == locationType.ToLowerInvariant()));
|
||||
location.TypeChangeTimer = typeChangeTimer;
|
||||
location.MissionsCompleted = missionsCompleted;
|
||||
if (showNotifications && prevLocationType != location.Type)
|
||||
{
|
||||
var change = prevLocationType.CanChangeTo.Find(c => c.ChangeToType.Equals(location.Type.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
var change = prevLocationType.CanChangeTo.Find(c =>
|
||||
c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant());
|
||||
if (change != null)
|
||||
{
|
||||
ChangeLocationType(location, prevLocationName, change);
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace Barotrauma
|
||||
|
||||
protected string originalName;
|
||||
protected string identifier;
|
||||
protected ContentPackage contentPackage;
|
||||
|
||||
public Sprite sprite;
|
||||
|
||||
@@ -242,10 +243,7 @@ namespace Barotrauma
|
||||
/// <param name="identifier">The identifier of the item (if null, the identifier is ignored and the search is done only based on the name)</param>
|
||||
public static MapEntityPrefab Find(string name, string identifier = null, bool showErrorMessages = true)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
name = name.ToLowerInvariant();
|
||||
}
|
||||
if (name != null) name = name.ToLowerInvariant();
|
||||
foreach (MapEntityPrefab prefab in List)
|
||||
{
|
||||
if (identifier != null)
|
||||
@@ -256,17 +254,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) { return prefab; }
|
||||
if (string.IsNullOrEmpty(name)) return prefab;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
if (prefab.Name.Equals(name, StringComparison.OrdinalIgnoreCase) ||
|
||||
prefab.originalName.Equals(name, StringComparison.OrdinalIgnoreCase) ||
|
||||
(prefab.Aliases != null && prefab.Aliases.Any(a => a.Equals(name, StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
return prefab;
|
||||
}
|
||||
if (prefab.Name.ToLowerInvariant() == name || prefab.originalName.ToLowerInvariant() == name || (prefab.Aliases != null && prefab.Aliases.Any(a => a.ToLowerInvariant() == name))) return prefab;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,9 +282,27 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Check if the name or any of the aliases of this prefab match the given name.
|
||||
/// </summary>
|
||||
public bool NameMatches(string name, StringComparison comparisonType) => originalName.Equals(name, comparisonType) || (Aliases != null && Aliases.Any(a => a.Equals(name, comparisonType)));
|
||||
public bool NameMatches(string name, bool caseSensitive = false)
|
||||
{
|
||||
if (caseSensitive)
|
||||
{
|
||||
return this.originalName == name || (Aliases != null && Aliases.Any(a => a == name));
|
||||
}
|
||||
else
|
||||
{
|
||||
name = name.ToLowerInvariant();
|
||||
return this.originalName.ToLowerInvariant() == name || (Aliases != null && Aliases.Any(a => a.ToLowerInvariant() == name));
|
||||
}
|
||||
}
|
||||
|
||||
public bool NameMatches(IEnumerable<string> allowedNames, StringComparison comparisonType) => allowedNames.Any(n => NameMatches(n, comparisonType));
|
||||
public bool NameMatches(IEnumerable<string> allowedNames, bool caseSensitive = false)
|
||||
{
|
||||
foreach (string name in allowedNames)
|
||||
{
|
||||
if (NameMatches(name, caseSensitive)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsLinkAllowed(MapEntityPrefab target)
|
||||
{
|
||||
|
||||
@@ -260,15 +260,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (subElement.GetAttributeBool("fliphorizontal", false))
|
||||
if (subElement.GetAttributeBool("fliphorizontal", false))
|
||||
sp.sprite.effects = SpriteEffects.FlipHorizontally;
|
||||
if (subElement.GetAttributeBool("flipvertical", false))
|
||||
if (subElement.GetAttributeBool("flipvertical", false))
|
||||
sp.sprite.effects = SpriteEffects.FlipVertically;
|
||||
#endif
|
||||
|
||||
|
||||
sp.canSpriteFlipX = subElement.GetAttributeBool("canflipx", true);
|
||||
sp.canSpriteFlipY = subElement.GetAttributeBool("canflipy", true);
|
||||
|
||||
|
||||
if (subElement.Attribute("name") == null && !string.IsNullOrWhiteSpace(sp.Name))
|
||||
{
|
||||
sp.sprite.Name = sp.Name;
|
||||
@@ -286,10 +286,12 @@ namespace Barotrauma
|
||||
sp.BackgroundSprite.RelativeOrigin = subElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
#if CLIENT
|
||||
if (subElement.GetAttributeBool("fliphorizontal", false)) { sp.BackgroundSprite.effects = SpriteEffects.FlipHorizontally; }
|
||||
if (subElement.GetAttributeBool("flipvertical", false)) { sp.BackgroundSprite.effects = SpriteEffects.FlipVertically; }
|
||||
sp.BackgroundSpriteColor = subElement.GetAttributeColor("color", Color.White);
|
||||
if (subElement.GetAttributeBool("fliphorizontal", false))
|
||||
sp.BackgroundSprite.effects = SpriteEffects.FlipHorizontally;
|
||||
if (subElement.GetAttributeBool("flipvertical", false))
|
||||
sp.BackgroundSprite.effects = SpriteEffects.FlipVertically;
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +77,7 @@ namespace Barotrauma
|
||||
|
||||
private SubmarineBody subBody;
|
||||
|
||||
public readonly Dictionary<Submarine, DockingPort> ConnectedDockingPorts;
|
||||
public IEnumerable<Submarine> DockedTo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ConnectedDockingPorts == null) { yield break; }
|
||||
foreach (Submarine sub in ConnectedDockingPorts.Keys)
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
}
|
||||
}
|
||||
public readonly List<Submarine> DockedTo;
|
||||
|
||||
private static Vector2 lastPickedPosition;
|
||||
private static float lastPickedFraction;
|
||||
@@ -453,7 +442,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
ConnectedDockingPorts = new Dictionary<Submarine, DockingPort>();
|
||||
DockedTo = new List<Submarine>();
|
||||
|
||||
FreeID();
|
||||
}
|
||||
@@ -511,7 +500,7 @@ namespace Barotrauma
|
||||
LeftBehindSubDockingPortOccupied = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("linkedsubmarine")) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "linkedsubmarine") { continue; }
|
||||
if (subElement.Attribute("location") == null) { continue; }
|
||||
|
||||
subsLeftBehind = true;
|
||||
@@ -572,32 +561,27 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Returns a rect that contains the borders of this sub and all subs docked to it
|
||||
/// </summary>
|
||||
public Rectangle GetDockedBorders(List<Submarine> checkd=null)
|
||||
public Rectangle GetDockedBorders()
|
||||
{
|
||||
if (checkd == null) { checkd = new List<Submarine>(); }
|
||||
checkd.Add(this);
|
||||
|
||||
Rectangle dockedBorders = Borders;
|
||||
dockedBorders.Y -= dockedBorders.Height;
|
||||
|
||||
var connectedSubs = DockedTo.Where(s => !checkd.Contains(s) && !s.IsOutpost).ToList();
|
||||
var connectedSubs = GetConnectedSubs();
|
||||
|
||||
foreach (Submarine dockedSub in connectedSubs)
|
||||
{
|
||||
//use docking ports instead of world position to determine
|
||||
//borders, as world position will not necessarily match where
|
||||
//the subs are supposed to go
|
||||
Vector2? expectedLocation = CalculateDockOffset(this, dockedSub);
|
||||
if (expectedLocation == null) { continue; }
|
||||
if (dockedSub == this) continue;
|
||||
|
||||
Rectangle dockedSubBorders = dockedSub.GetDockedBorders(checkd);
|
||||
dockedSubBorders.Location += MathUtils.ToPoint(expectedLocation.Value);
|
||||
Vector2 diff = dockedSub.Submarine == this ? dockedSub.WorldPosition : dockedSub.WorldPosition - WorldPosition;
|
||||
|
||||
Rectangle dockedSubBorders = dockedSub.Borders;
|
||||
dockedSubBorders.Y -= dockedSubBorders.Height;
|
||||
dockedSubBorders.Location += MathUtils.ToPoint(diff);
|
||||
|
||||
dockedBorders.Y = -dockedBorders.Y;
|
||||
dockedSubBorders.Y = -dockedSubBorders.Y;
|
||||
dockedBorders = Rectangle.Union(dockedBorders, dockedSubBorders);
|
||||
dockedBorders.Y = -dockedBorders.Y;
|
||||
}
|
||||
|
||||
dockedBorders.Y += dockedBorders.Height;
|
||||
return dockedBorders;
|
||||
}
|
||||
|
||||
@@ -1147,37 +1131,25 @@ namespace Barotrauma
|
||||
prevPosition = position;
|
||||
}
|
||||
|
||||
public void SetPosition(Vector2 position, List<Submarine> checkd=null)
|
||||
public void SetPosition(Vector2 position)
|
||||
{
|
||||
if (!MathUtils.IsValid(position)) return;
|
||||
|
||||
if (checkd == null) { checkd = new List<Submarine>(); }
|
||||
if (checkd.Contains(this)) { return; }
|
||||
|
||||
checkd.Add(this);
|
||||
|
||||
|
||||
subBody.SetPosition(position);
|
||||
|
||||
foreach (Submarine dockedSub in DockedTo)
|
||||
foreach (Submarine sub in loaded)
|
||||
{
|
||||
Vector2? expectedLocation = CalculateDockOffset(this, dockedSub);
|
||||
if (expectedLocation == null) { continue; }
|
||||
if (sub != this && sub.Submarine == this)
|
||||
{
|
||||
sub.SetPosition(position + sub.WorldPosition);
|
||||
sub.Submarine = null;
|
||||
}
|
||||
|
||||
dockedSub.SetPosition(position + expectedLocation.Value, checkd);
|
||||
}
|
||||
//Level.Loaded.SetPosition(-position);
|
||||
//prevPosition = position;
|
||||
}
|
||||
|
||||
public static Vector2? CalculateDockOffset(Submarine sub, Submarine dockedSub)
|
||||
{
|
||||
Item myPort = sub.ConnectedDockingPorts.ContainsKey(dockedSub) ? sub.ConnectedDockingPorts[dockedSub].Item : null;
|
||||
if (myPort == null) { return null; }
|
||||
Item theirPort = dockedSub.ConnectedDockingPorts.ContainsKey(sub) ? dockedSub.ConnectedDockingPorts[sub].Item : null;
|
||||
if (theirPort == null) { return null; }
|
||||
return (myPort.Position - sub.HiddenSubPosition) - (theirPort.Position - dockedSub.HiddenSubPosition);
|
||||
}
|
||||
|
||||
public void Translate(Vector2 amount)
|
||||
{
|
||||
if (amount == Vector2.Zero || !MathUtils.IsValid(amount)) return;
|
||||
@@ -1264,17 +1236,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (Path.GetFullPath(savedSubmarines[i].filePath) == fullPath)
|
||||
{
|
||||
if (savedSubmarines[i] == MainSub)
|
||||
{
|
||||
savedSubmarines.Remove(savedSubmarines[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
savedSubmarines[i].Dispose();
|
||||
}
|
||||
savedSubmarines[i].Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var sub = new Submarine(filePath);
|
||||
@@ -1592,7 +1556,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Hull hull in matchingHulls)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.Contains("roomname.", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.ToLowerInvariant().Contains("roomname."))
|
||||
{
|
||||
hull.RoomName = hull.CreateRoomName();
|
||||
}
|
||||
@@ -1769,7 +1733,7 @@ namespace Barotrauma
|
||||
if (MainSub == this) MainSub = null;
|
||||
if (MainSubs[1] == this) MainSubs[1] = null;
|
||||
|
||||
ConnectedDockingPorts?.Clear();
|
||||
DockedTo?.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -121,15 +121,9 @@ namespace Barotrauma
|
||||
idCardTags = new string[0];
|
||||
|
||||
#if CLIENT
|
||||
if (iconSprites == null)
|
||||
if (iconTexture == null)
|
||||
{
|
||||
iconSprites = new Dictionary<SpawnType, Sprite>()
|
||||
{
|
||||
{ SpawnType.Path, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,0,128,128)) },
|
||||
{ SpawnType.Human, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,0,128,128)) },
|
||||
{ SpawnType.Enemy, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256,0,128,128)) },
|
||||
{ SpawnType.Cargo, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384,0,128,128)) }
|
||||
};
|
||||
iconTexture = Sprite.LoadTexture("Content/Map/waypointIcons.png");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -154,12 +148,21 @@ namespace Barotrauma
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static bool GenerateSubWaypoints(Submarine submarine)
|
||||
public override bool IsMouseOn(Vector2 position)
|
||||
{
|
||||
#if CLIENT
|
||||
if (IsHidden()) return false;
|
||||
#endif
|
||||
|
||||
return base.IsMouseOn(position);
|
||||
}
|
||||
|
||||
public static void GenerateSubWaypoints(Submarine submarine)
|
||||
{
|
||||
if (!Hull.hullList.Any())
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't generate waypoints: no hulls found.");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
List<WayPoint> existingWaypoints = WayPointList.FindAll(wp => wp.spawnType == SpawnType.Path);
|
||||
@@ -462,8 +465,6 @@ namespace Barotrauma
|
||||
{
|
||||
door.Body.Enabled = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null)
|
||||
@@ -653,7 +654,7 @@ namespace Barotrauma
|
||||
{
|
||||
w.assignedJob =
|
||||
JobPrefab.Get(jobIdentifier) ??
|
||||
JobPrefab.Prefabs.Find(jp => jp.Name.Equals(jobIdentifier, StringComparison.OrdinalIgnoreCase));
|
||||
JobPrefab.Prefabs.Find(jp => jp.Name.ToLowerInvariant() == jobIdentifier);
|
||||
}
|
||||
|
||||
w.ladderId = (ushort)element.GetAttributeInt("ladders", 0);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("command", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "command") continue;
|
||||
string commandName = subElement.GetAttributeString("name", "");
|
||||
|
||||
DebugConsole.Command command = DebugConsole.FindCommand(commandName);
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace Barotrauma.Networking
|
||||
RESPONSE_STARTGAME, //tell the server whether you're ready to start
|
||||
SERVER_COMMAND, //tell the server to end a round or kick/ban someone (special permissions required)
|
||||
|
||||
REQUEST_STARTGAMEFINALIZE, //tell the server you're ready to finalize round initialization
|
||||
|
||||
ERROR //tell the server that an error occurred
|
||||
}
|
||||
enum ClientNetObject
|
||||
@@ -62,7 +60,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
QUERY_STARTGAME, //ask the clients whether they're ready to start
|
||||
STARTGAME, //start a new round
|
||||
STARTGAMEFINALIZE, //finalize round initialization
|
||||
ENDGAME,
|
||||
|
||||
TRAITOR_MESSAGE,
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
return (a == null) == (b == null);
|
||||
}
|
||||
return a.ToString().Equals(b.ToString(), StringComparison.OrdinalIgnoreCase);
|
||||
return a.ToString().Equals(b.ToString(), StringComparison.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +759,7 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(600.0f, true)]
|
||||
[Serialize(120.0f, true)]
|
||||
public float KickAFKTime
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -70,15 +70,14 @@ namespace Barotrauma.Steam
|
||||
return Steamworks.SteamClient.Name;
|
||||
}
|
||||
|
||||
public static bool OverlayCustomURL(string url)
|
||||
public static void OverlayCustomURL(string url)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
Steamworks.SteamFriends.OpenWebOverlay(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool UnlockAchievement(string achievementName)
|
||||
|
||||
@@ -679,7 +679,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "upgrade") { continue; }
|
||||
var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0"));
|
||||
if (savedVersion >= upgradeVersion) { continue; }
|
||||
|
||||
|
||||
@@ -570,7 +570,7 @@ namespace Barotrauma
|
||||
{
|
||||
bool hexFailed = true;
|
||||
stringColor = stringColor.Trim();
|
||||
if (stringColor.Length > 0 && stringColor[0] == '#')
|
||||
if (stringColor[0]=='#')
|
||||
{
|
||||
stringColor = stringColor.Substring(1);
|
||||
|
||||
|
||||
@@ -329,10 +329,10 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (XElement subElement in SourceElement.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "override")
|
||||
{
|
||||
string language = subElement.GetAttributeString("language", "");
|
||||
if (TextManager.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase))
|
||||
if (TextManager.Language.ToLower() == language.ToLower())
|
||||
{
|
||||
return subElement;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class DelayedListElement
|
||||
{
|
||||
public readonly DelayedEffect Parent;
|
||||
public readonly Entity Entity;
|
||||
public readonly Vector2? WorldPosition;
|
||||
public readonly List<ISerializableEntity> Targets;
|
||||
public DelayedEffect Parent;
|
||||
public Entity Entity;
|
||||
public Vector2? WorldPosition;
|
||||
public List<ISerializableEntity> Targets;
|
||||
public float StartTimer;
|
||||
|
||||
public DelayedListElement(DelayedEffect parentEffect, Entity parentEntity, IEnumerable<ISerializableEntity> targets, float delay, Vector2? worldPosition)
|
||||
{
|
||||
Parent = parentEffect;
|
||||
Entity = parentEntity;
|
||||
Targets = new List<ISerializableEntity>(targets);
|
||||
StartTimer = delay;
|
||||
WorldPosition = worldPosition;
|
||||
}
|
||||
}
|
||||
class DelayedEffect : StatusEffect
|
||||
{
|
||||
@@ -37,18 +27,28 @@ namespace Barotrauma
|
||||
|
||||
public override void Apply(ActionType type, float deltaTime, Entity entity, ISerializableEntity target, Vector2? worldPosition = null)
|
||||
{
|
||||
if (this.type != type || !HasRequiredItems(entity)) { return; }
|
||||
if (!Stackable && DelayList.Any(d => d.Parent == this && d.Targets.FirstOrDefault() == target)) { return; }
|
||||
if (targetIdentifiers != null && !IsValidTarget(target)) { return; }
|
||||
if (!HasRequiredConditions(target.ToEnumerable())) { return; }
|
||||
if (this.type != type || !HasRequiredItems(entity)) return;
|
||||
if (!Stackable && DelayList.Any(d => d.Parent == this && d.Targets.FirstOrDefault() == target)) return;
|
||||
|
||||
if (targetIdentifiers != null && !IsValidTarget(target)) return;
|
||||
if (!HasRequiredConditions(new List<ISerializableEntity>() { target })) return;
|
||||
|
||||
DelayList.Add(new DelayedListElement(this, entity, target.ToEnumerable(), delay, worldPosition));
|
||||
DelayedListElement element = new DelayedListElement
|
||||
{
|
||||
Parent = this,
|
||||
StartTimer = delay,
|
||||
Entity = entity,
|
||||
WorldPosition = worldPosition,
|
||||
Targets = new List<ISerializableEntity>() { target }
|
||||
};
|
||||
|
||||
DelayList.Add(element);
|
||||
}
|
||||
|
||||
public override void Apply(ActionType type, float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Vector2? worldPosition = null)
|
||||
{
|
||||
if (this.type != type || !HasRequiredItems(entity)) { return; }
|
||||
if (!Stackable && DelayList.Any(d => d.Parent == this && d.Targets.SequenceEqual(targets))) { return; }
|
||||
if (this.type != type || !HasRequiredItems(entity)) return;
|
||||
if (!Stackable && DelayList.Any(d => d.Parent == this && d.Targets.SequenceEqual(targets))) return;
|
||||
|
||||
currentTargets.Clear();
|
||||
foreach (ISerializableEntity target in targets)
|
||||
@@ -61,9 +61,18 @@ namespace Barotrauma
|
||||
currentTargets.Add(target);
|
||||
}
|
||||
|
||||
if (!HasRequiredConditions(currentTargets)) { return; }
|
||||
if (!HasRequiredConditions(currentTargets)) return;
|
||||
|
||||
DelayList.Add(new DelayedListElement(this, entity, currentTargets, delay, worldPosition));
|
||||
DelayedListElement element = new DelayedListElement
|
||||
{
|
||||
Parent = this,
|
||||
StartTimer = delay,
|
||||
Entity = entity,
|
||||
WorldPosition = worldPosition,
|
||||
Targets = currentTargets
|
||||
};
|
||||
|
||||
DelayList.Add(element);
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
@@ -79,7 +88,7 @@ namespace Barotrauma
|
||||
|
||||
element.StartTimer -= deltaTime;
|
||||
|
||||
if (element.StartTimer > 0.0f) { continue; }
|
||||
if (element.StartTimer > 0.0f) continue;
|
||||
|
||||
element.Parent.Apply(1.0f, element.Entity, element.Targets, element.WorldPosition);
|
||||
DelayList.Remove(element);
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace Barotrauma
|
||||
// Only used by attacks
|
||||
public readonly bool TargetSelf;
|
||||
|
||||
// Only used by conditionals targeting an item (makes the conditional check the item/character whose inventory this item is inside)
|
||||
public readonly bool TargetContainer;
|
||||
private readonly string[] afflictionNames = new string[] { "internaldamage", "bleeding", "burn", "oxygenlow", "bloodloss", "pressure", "stun", "husk", "afflictionhusk", "huskinfection" };
|
||||
|
||||
private readonly int cancelStatusEffect;
|
||||
|
||||
@@ -63,7 +62,6 @@ namespace Barotrauma
|
||||
{
|
||||
case "targetitemcomponent":
|
||||
case "targetself":
|
||||
case "targetcontainer":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
@@ -134,7 +132,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
TargetItemComponentName = attribute.Parent.GetAttributeString("targetitemcomponent", "");
|
||||
TargetContainer = attribute.Parent.GetAttributeBool("targetcontainer", false);
|
||||
TargetSelf = attribute.Parent.GetAttributeBool("targetself", false);
|
||||
|
||||
foreach (XElement subElement in attribute.Parent.Elements())
|
||||
@@ -153,9 +150,13 @@ namespace Barotrauma
|
||||
|
||||
if (!Enum.TryParse(AttributeName, true, out Type))
|
||||
{
|
||||
if (AfflictionPrefab.Prefabs.Any(p => p.Identifier.Equals(AttributeName, StringComparison.OrdinalIgnoreCase)))
|
||||
if (afflictionNames.Any(n => n == AttributeName))
|
||||
{
|
||||
Type = ConditionType.Affliction;
|
||||
if (AttributeName == "husk" || AttributeName == "huskaffliction")
|
||||
{
|
||||
AttributeName = "huskinfection";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -172,7 +173,8 @@ namespace Barotrauma
|
||||
|
||||
public bool Matches(ISerializableEntity target)
|
||||
{
|
||||
string valStr = AttributeValue.ToString();
|
||||
string valStr = AttributeValue.ToString();
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ConditionType.PropertyValue:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,27 +9,11 @@ namespace Barotrauma
|
||||
{
|
||||
class DurationListElement
|
||||
{
|
||||
public readonly StatusEffect Parent;
|
||||
public readonly Entity Entity;
|
||||
public readonly List<ISerializableEntity> Targets;
|
||||
public Character User { get; private set; }
|
||||
|
||||
public StatusEffect Parent;
|
||||
public Entity Entity;
|
||||
public List<ISerializableEntity> Targets;
|
||||
public float Timer;
|
||||
|
||||
public DurationListElement(StatusEffect parentEffect, Entity parentEntity, IEnumerable<ISerializableEntity> targets, float duration, Character user)
|
||||
{
|
||||
Parent = parentEffect;
|
||||
Entity = parentEntity;
|
||||
Targets = new List<ISerializableEntity>(targets);
|
||||
Timer = duration;
|
||||
User = user;
|
||||
}
|
||||
|
||||
public void Reset(float duration, Character newUser)
|
||||
{
|
||||
Timer = duration;
|
||||
User = newUser;
|
||||
}
|
||||
public Character User;
|
||||
}
|
||||
|
||||
partial class StatusEffect
|
||||
@@ -71,7 +54,7 @@ namespace Barotrauma
|
||||
//backwards compatibility
|
||||
DebugConsole.ThrowError("Error in StatusEffect config (" + element.ToString() + ") - use item identifier instead of the name.");
|
||||
string itemPrefabName = element.GetAttributeString("name", "");
|
||||
ItemPrefab = ItemPrefab.Prefabs.Find(m => m.NameMatches(itemPrefabName, StringComparison.OrdinalIgnoreCase) || m.Tags.Contains(itemPrefabName));
|
||||
ItemPrefab = ItemPrefab.Prefabs.Find(m => m.NameMatches(itemPrefabName) || m.Tags.Contains(itemPrefabName));
|
||||
if (ItemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect \""+ parentDebugName + "\" - item prefab \"" + itemPrefabName + "\" not found.");
|
||||
@@ -374,7 +357,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - define afflictions using identifiers instead of names.");
|
||||
string afflictionName = subElement.GetAttributeString("name", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Name.Equals(afflictionName, StringComparison.OrdinalIgnoreCase));
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Name.ToLowerInvariant() == afflictionName);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
@@ -384,7 +367,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase));
|
||||
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.ToLowerInvariant() == afflictionIdentifier);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - Affliction prefab with the identifier \"" + afflictionIdentifier + "\" not found.");
|
||||
@@ -493,68 +476,41 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasRequiredConditions(IEnumerable<ISerializableEntity> targets)
|
||||
{
|
||||
return HasRequiredConditions(targets, targetingContainer: false);
|
||||
}
|
||||
|
||||
private bool HasRequiredConditions(IEnumerable<ISerializableEntity> targets, bool targetingContainer)
|
||||
public virtual bool HasRequiredConditions(List<ISerializableEntity> targets)
|
||||
{
|
||||
if (!propertyConditionals.Any()) { return true; }
|
||||
if (requiredItems.Any() && requiredItems.All(ri => ri.MatchOnEmpty) && !targets.Any()) { return true; }
|
||||
if (requiredItems.Any() && requiredItems.All(ri => ri.MatchOnEmpty) && targets.Count == 0) { return true; }
|
||||
switch (conditionalComparison)
|
||||
{
|
||||
case PropertyConditional.Comparison.Or:
|
||||
foreach (PropertyConditional pc in propertyConditionals)
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
if (pc.TargetContainer && !targetingContainer)
|
||||
foreach (PropertyConditional pc in propertyConditionals)
|
||||
{
|
||||
var target = targets.FirstOrDefault(t => t is Item || t is ItemComponent);
|
||||
var targetItem = target as Item ?? (target as ItemComponent)?.Item;
|
||||
if (targetItem?.ParentInventory == null) { continue; }
|
||||
if (targetItem.ParentInventory.Owner is Item container && HasRequiredConditions(container.AllPropertyObjects, targetingContainer: true)) { return true; }
|
||||
if (targetItem.ParentInventory.Owner is Character character && HasRequiredConditions(character.ToEnumerable(), targetingContainer: true)) { return true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ISerializableEntity target in targets)
|
||||
if (!string.IsNullOrEmpty(pc.TargetItemComponentName))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pc.TargetItemComponentName))
|
||||
if (!(target is ItemComponent ic) || ic.Name != pc.TargetItemComponentName)
|
||||
{
|
||||
if (!(target is ItemComponent ic) || ic.Name != pc.TargetItemComponentName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (pc.Matches(target)) { return true; }
|
||||
}
|
||||
if (pc.Matches(target)) { return true; }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case PropertyConditional.Comparison.And:
|
||||
foreach (PropertyConditional pc in propertyConditionals)
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
if (pc.TargetContainer && !targetingContainer)
|
||||
foreach (PropertyConditional pc in propertyConditionals)
|
||||
{
|
||||
var target = targets.FirstOrDefault(t => t is Item || t is ItemComponent);
|
||||
var targetItem = target as Item ?? (target as ItemComponent)?.Item;
|
||||
if (targetItem?.ParentInventory == null) { return false; }
|
||||
if (targetItem.ParentInventory.Owner is Item container && !HasRequiredConditions(container.AllPropertyObjects, targetingContainer: true)) { return false; }
|
||||
if (targetItem.ParentInventory.Owner is Character character && !HasRequiredConditions(character.ToEnumerable(), targetingContainer: true)) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ISerializableEntity target in targets)
|
||||
if (!string.IsNullOrEmpty(pc.TargetItemComponentName))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pc.TargetItemComponentName))
|
||||
if (!(target is ItemComponent ic) || ic.Name != pc.TargetItemComponentName)
|
||||
{
|
||||
if (!(target is ItemComponent ic) || ic.Name != pc.TargetItemComponentName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!pc.Matches(target)) { return false; }
|
||||
}
|
||||
if (!pc.Matches(target)) { return false; }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -604,26 +560,33 @@ namespace Barotrauma
|
||||
|
||||
public virtual void Apply(ActionType type, float deltaTime, Entity entity, ISerializableEntity target, Vector2? worldPosition = null)
|
||||
{
|
||||
if (this.type != type || !HasRequiredItems(entity)) { return; }
|
||||
if (this.type != type || !HasRequiredItems(entity)) return;
|
||||
|
||||
if (targetIdentifiers != null && !IsValidTarget(target)) { return; }
|
||||
if (targetIdentifiers != null && !IsValidTarget(target)) return;
|
||||
|
||||
if (duration > 0.0f && !Stackable)
|
||||
{
|
||||
//ignore if not stackable and there's already an identical statuseffect
|
||||
DurationListElement existingEffect = DurationList.Find(d => d.Parent == this && d.Targets.FirstOrDefault() == target);
|
||||
existingEffect?.Reset(Math.Max(existingEffect.Timer, duration), user);
|
||||
return;
|
||||
if (existingEffect != null)
|
||||
{
|
||||
existingEffect.Timer = Math.Max(existingEffect.Timer, duration);
|
||||
existingEffect.User = user;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasRequiredConditions(target.ToEnumerable())) { return; }
|
||||
Apply(deltaTime, entity, target.ToEnumerable(), worldPosition);
|
||||
List<ISerializableEntity> targets = new List<ISerializableEntity> { target };
|
||||
|
||||
if (!HasRequiredConditions(targets)) return;
|
||||
|
||||
Apply(deltaTime, entity, targets, worldPosition);
|
||||
}
|
||||
|
||||
protected readonly List<ISerializableEntity> currentTargets = new List<ISerializableEntity>();
|
||||
public virtual void Apply(ActionType type, float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Vector2? worldPosition = null)
|
||||
{
|
||||
if (this.type != type) { return; }
|
||||
if (this.type != type) return;
|
||||
|
||||
currentTargets.Clear();
|
||||
foreach (ISerializableEntity target in targets)
|
||||
@@ -638,20 +601,24 @@ namespace Barotrauma
|
||||
|
||||
if (targetIdentifiers != null && currentTargets.Count == 0) { return; }
|
||||
|
||||
if (!HasRequiredItems(entity) || !HasRequiredConditions(currentTargets)) { return; }
|
||||
if (!HasRequiredItems(entity) || !HasRequiredConditions(currentTargets)) return;
|
||||
|
||||
if (duration > 0.0f && !Stackable)
|
||||
{
|
||||
//ignore if not stackable and there's already an identical statuseffect
|
||||
DurationListElement existingEffect = DurationList.Find(d => d.Parent == this && d.Targets.SequenceEqual(currentTargets));
|
||||
existingEffect?.Reset(Math.Max(existingEffect.Timer, duration), user);
|
||||
return;
|
||||
if (existingEffect != null)
|
||||
{
|
||||
existingEffect.Timer = Math.Max(existingEffect.Timer, duration);
|
||||
existingEffect.User = user;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Apply(deltaTime, entity, currentTargets, worldPosition);
|
||||
}
|
||||
|
||||
protected void Apply(float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Vector2? worldPosition = null)
|
||||
protected void Apply(float deltaTime, Entity entity, List<ISerializableEntity> targets, Vector2? worldPosition = null)
|
||||
{
|
||||
if (lifeTime > 0)
|
||||
{
|
||||
@@ -718,7 +685,16 @@ namespace Barotrauma
|
||||
|
||||
if (duration > 0.0f)
|
||||
{
|
||||
DurationList.Add(new DurationListElement(this, entity, targets, duration, user));
|
||||
DurationListElement element = new DurationListElement
|
||||
{
|
||||
Parent = this,
|
||||
Timer = duration,
|
||||
Entity = entity,
|
||||
Targets = targets,
|
||||
User = user
|
||||
};
|
||||
|
||||
DurationList.Add(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -756,7 +732,7 @@ namespace Barotrauma
|
||||
character.LastDamageSource = entity;
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
limb.character.DamageLimb(position, limb, multipliedAffliction.ToEnumerable(), stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
|
||||
limb.character.DamageLimb(position, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
|
||||
limb.character.TrySeverLimbJoints(limb, SeverLimbsProbability);
|
||||
//only apply non-limb-specific afflictions to the first limb
|
||||
if (!affliction.Prefab.LimbSpecific) { break; }
|
||||
@@ -765,7 +741,7 @@ namespace Barotrauma
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
if (limb.character.Removed || limb.Removed) { continue; }
|
||||
limb.character.DamageLimb(position, limb, multipliedAffliction.ToEnumerable(), stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
|
||||
limb.character.DamageLimb(position, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
|
||||
limb.character.TrySeverLimbJoints(limb, SeverLimbsProbability);
|
||||
}
|
||||
}
|
||||
@@ -788,7 +764,6 @@ namespace Barotrauma
|
||||
{
|
||||
float prevVitality = targetCharacter.Vitality;
|
||||
targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAmount);
|
||||
targetCharacter.TryAdjustAttackerSkill(user, targetCharacter.Vitality - prevVitality);
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, user, prevVitality - targetCharacter.Vitality);
|
||||
#endif
|
||||
@@ -878,7 +853,7 @@ namespace Barotrauma
|
||||
ApplyProjSpecific(deltaTime, entity, targets, hull, position);
|
||||
}
|
||||
|
||||
partial void ApplyProjSpecific(float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Hull currentHull, Vector2 worldPosition);
|
||||
partial void ApplyProjSpecific(float deltaTime, Entity entity, List<ISerializableEntity> targets, Hull currentHull, Vector2 worldPosition);
|
||||
|
||||
private void ApplyToProperty(ISerializableEntity target, SerializableProperty property, object value, float deltaTime)
|
||||
{
|
||||
@@ -959,12 +934,12 @@ namespace Barotrauma
|
||||
if (target is Character character)
|
||||
{
|
||||
if (character.Removed) { continue; }
|
||||
character.AddDamage(character.WorldPosition, multipliedAffliction.ToEnumerable(), stun: 0.0f, playSound: false, attacker: element.User);
|
||||
character.AddDamage(character.WorldPosition, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attacker: element.User);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
if (limb.character.Removed || limb.Removed) { continue; }
|
||||
limb.character.DamageLimb(limb.WorldPosition, limb, multipliedAffliction.ToEnumerable(), stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: element.User);
|
||||
limb.character.DamageLimb(limb.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: element.User);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,7 +960,6 @@ namespace Barotrauma
|
||||
{
|
||||
float prevVitality = targetCharacter.Vitality;
|
||||
targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAffliction.Second * deltaTime);
|
||||
targetCharacter.TryAdjustAttackerSkill(element.User, targetCharacter.Vitality - prevVitality);
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, element.User, prevVitality - targetCharacter.Vitality);
|
||||
#endif
|
||||
|
||||
@@ -216,13 +216,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (GameMain.Config != null && GameMain.Config.TextManagerDebugModeEnabled)
|
||||
{
|
||||
return textTag;
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
{
|
||||
doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc != null) { break; }
|
||||
if (filePath.Equals("content/texts/englishvanilla.xml", StringComparison.OrdinalIgnoreCase))
|
||||
if (filePath.ToLowerInvariant() == "content/texts/englishvanilla.xml")
|
||||
{
|
||||
//try fixing legacy EnglishVanilla path
|
||||
string newPath = "Content/Texts/English/EnglishVanilla.xml";
|
||||
|
||||
@@ -118,10 +118,6 @@ namespace Barotrauma
|
||||
|
||||
for (int i = 0; i < subDirs.Length; i++)
|
||||
{
|
||||
if (i == subDirs.Length - 1 && string.IsNullOrEmpty(subDirs[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
string enumPath = string.IsNullOrEmpty(filename) ? "./" : filename;
|
||||
List<string> filePaths = Directory.GetFileSystemEntries(enumPath).Select(s => Path.GetFileName(s)).ToList();
|
||||
if (filePaths.Any(s => s.Equals(subDirs[i], StringComparison.Ordinal)))
|
||||
@@ -151,7 +147,7 @@ namespace Barotrauma
|
||||
|
||||
public static string RemoveInvalidFileNameChars(string fileName)
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars().Concat(new char[] {':', ';'});
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
foreach (char invalidChar in invalidChars)
|
||||
{
|
||||
fileName = fileName.Replace(invalidChar.ToString(), "");
|
||||
|
||||
Reference in New Issue
Block a user