f8b0295...0671290

This commit is contained in:
Joonas Rikkonen
2019-03-18 22:59:45 +02:00
parent 23687fbf2f
commit 63eb4d64e5
103 changed files with 1378 additions and 4692 deletions
@@ -359,7 +359,7 @@ namespace Barotrauma
if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(Character.CharacterHealth.CrushDepth * 0.75f))
{
//steer straight up if very deep
steeringManager.SteeringManual(deltaTime, Vector2.UnitY * speed);
steeringManager.SteeringManual(deltaTime, Vector2.UnitY);
return;
}
@@ -652,7 +652,7 @@ namespace Barotrauma
{
//wander around randomly and decrease the priority faster if no path is found
if (selectedTargetMemory != null) selectedTargetMemory.Priority -= deltaTime * 10.0f;
steeringManager.SteeringWander(speed);
steeringManager.SteeringWander();
}
else if (indoorsSteering.CurrentPath.Finished)
{
@@ -131,23 +131,20 @@ namespace Barotrauma
ignorePlatforms = true;
}
}
if (Character.IsClimbing && PathSteering.InLadders && PathSteering.IsNextLadderSameAsCurrent)
{
Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
}
}
Character.AnimController.IgnorePlatforms = ignorePlatforms;
// Suspect that this causes issues when trying to exit from the ladders -> could try to check if the next node is ladder?
//if (Character.IsClimbing)
//{
// Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
//}
Vector2 targetMovement = AnimController.TargetMovement;
if (!Character.AnimController.InWater)
{
targetMovement = new Vector2(
Character.AnimController.TargetMovement.X,
MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
}
float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed);
@@ -227,10 +224,6 @@ namespace Barotrauma
PropagateHullSafety(Character, Character.CurrentHull);
}
}
private void ReportProblems()
{
if (GameMain.Client != null) return;
protected void ReportProblems()
{
@@ -252,7 +245,7 @@ namespace Barotrauma
foreach (Character c in Character.CharacterList)
{
if (c.CurrentHull == Character.CurrentHull && !c.IsDead &&
(c.AIController is EnemyAIController || c.TeamID != Character.TeamID))
(c.AIController is EnemyAIController || (c.TeamID != Character.TeamID && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC)))
{
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders");
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
@@ -297,10 +290,13 @@ namespace Barotrauma
public override void OnAttacked(Character attacker, AttackResult attackResult)
{
float damage = attackResult.Damage;
if (damage < 0) { return; }
if (damage <= 0) { return; }
if (attacker == null || attacker.IsDead || attacker.Removed)
{
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
if (objectiveManager.CurrentOrder == null)
{
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
return;
}
if (IsFriendly(attacker))
@@ -314,7 +310,10 @@ namespace Barotrauma
if (!attacker.IsRemotePlayer && Character.Controlled != attacker && attacker.AIController != null && attacker.AIController.Enabled)
{
// Don't react to damage done by friendly ai, because we know that it's accidental
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
if (objectiveManager.CurrentOrder == null)
{
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
return;
}
float currentVitality = Character.CharacterHealth.Vitality;
@@ -322,8 +321,10 @@ namespace Barotrauma
if (dmgPercentage < currentVitality / 10)
{
// Don't react to a minor amount of (accidental) dmg done by friendly characters
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
return;
if (objectiveManager.CurrentOrder == null)
{
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
}
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective)
{
@@ -465,7 +466,9 @@ namespace Barotrauma
// Even the smallest fire reduces the safety by 50%
float fire = hull.FireSources.Count * 0.5f + hull.FireSources.Sum(fs => fs.DamageRange) / hull.Size.X;
float fireFactor = ignoreFire ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(fire, 0, 1));
int enemyCount = Character.CharacterList.Count(e => e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious && (e.AIController is EnemyAIController || e.TeamID != character.TeamID));
int enemyCount = Character.CharacterList.Count(e =>
e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious &&
(e.AIController is EnemyAIController || (e.TeamID != character.TeamID && character.TeamID != Character.TeamType.FriendlyNPC && e.TeamID != Character.TeamType.FriendlyNPC)));
// The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages)
float enemyFactor = ignoreEnemies ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1));
float safety = oxygenFactor * waterFactor * fireFactor * enemyFactor;
@@ -44,8 +44,8 @@ namespace Barotrauma
}
public bool InLadders => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Ladders != null;
private bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null;
private bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders;
public bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null;
public bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders;
public bool InStairs => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Stairs != null;
@@ -213,7 +213,7 @@ namespace Barotrauma
}
else if (character.AnimController.InWater)
{
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < collider.radius * collider.radius)
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2))
{
currentPath.SkipToNextNode();
}
@@ -221,11 +221,14 @@ namespace Barotrauma
else
{
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
Vector2 colliderSize = collider.GetSize();
// Cannot use the head position, because not all characters have head or it can be below the total height of the character
float characterHeight = colliderSize.Y + character.AnimController.ColliderHeightFromFloor;
float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X);
bool isAboveVerticalPosition = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y;
bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + 1.5f;
bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y;
bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight;
if (horizontalDistance < collider.radius * 2 && isAboveVerticalPosition && isNotTooHigh)
if (horizontalDistance < collider.radius * 3 && isAboveFeet && isNotTooHigh)
{
currentPath.SkipToNextNode();
}
@@ -31,6 +31,7 @@ namespace Barotrauma
List<Tuple<string, string, string>> contentPackageFiles = new List<Tuple<string, string, string>>();
foreach (string filePath in filePaths)
{
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
XDocument doc = XMLExtensions.TryLoadXml(filePath);
if (doc == null || doc.Root == null) continue;
string language = doc.Root.GetAttributeString("Language", "English");
@@ -41,6 +42,7 @@ namespace Barotrauma
List<Tuple<string, string, string>> translationFiles = new List<Tuple<string, string, string>>();
foreach (string filePath in Directory.GetFiles(Path.Combine("Content", "NPCConversations")))
{
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
XDocument doc = XMLExtensions.TryLoadXml(filePath);
if (doc == null || doc.Root == null) continue;
string language = doc.Root.GetAttributeString("Language", "English");
@@ -139,6 +139,7 @@ namespace Barotrauma
}
protected virtual bool ShouldInterruptSubObjective(AIObjective subObjective) => false;
public virtual void OnSelected() { }
protected abstract void Act(float deltaTime);
@@ -117,6 +117,17 @@ namespace Barotrauma
return weapon;
}
private void Unequip()
{
if (character.SelectedItems.Contains(Weapon))
{
if (!Weapon.AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(Weapon, character, new List<InvSlotType>() { InvSlotType.Any }))
{
Weapon.Drop(character);
}
}
}
private bool Equip(float deltaTime)
{
if (!character.SelectedItems.Contains(Weapon))
@@ -241,7 +252,19 @@ namespace Barotrauma
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
public override bool IsCompleted() => Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0.0f;
public override bool IsCompleted()
{
bool completed = Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0;
if (completed)
{
if (Weapon != null)
{
Unequip();
}
}
return completed;
}
public override bool CanBeCompleted => !abandon && (reloadWeaponObjective == null || reloadWeaponObjective.CanBeCompleted) && (retreatObjective == null || retreatObjective.CanBeCompleted);
public override float GetPriority(AIObjectiveManager objectiveManager) => Enemy == null || Enemy.Removed || Enemy.IsDead ? 0 : 100;
@@ -18,7 +18,7 @@ namespace Barotrauma
private bool isCompleted;
public bool IgnoreAlreadyContainedItems;
public string[] ignoredContainerIdentifiers;
public Func<Item, float> GetItemPriority;
@@ -95,7 +95,7 @@ namespace Barotrauma
getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers)
{
GetItemPriority = GetItemPriority,
IgnoreContainedItems = IgnoreAlreadyContainedItems
ignoredContainerIdentifiers = ignoredContainerIdentifiers
};
AddSubObjective(getItemObjective);
return;
@@ -78,16 +78,18 @@ namespace Barotrauma
foreach (FireSource fs in targetHull.FireSources)
{
// TODO: check if in the same room?
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
if (!character.IsClimbing && (inRange || useExtinquisherTimer > 0.0f))
if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f))
{
useExtinquisherTimer += deltaTime;
if (useExtinquisherTimer > 2.0f) useExtinquisherTimer = 0.0f;
character.CursorPosition = fs.Position;
character.SetInput(InputType.Aim, false, true);
character.AIController.SteeringManager.Reset();
if (!character.IsClimbing)
{
character.AIController.SteeringManager.Reset();
}
extinguisher.Use(deltaTime, character);
if (!targetHull.FireSources.Contains(fs))
@@ -1,5 +1,5 @@
using Barotrauma.Items.Components;
using System;
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma
@@ -10,31 +10,26 @@ namespace Barotrauma
public override bool ForceRun => true;
private AIObjective subObjective;
private string gearTag;
public override bool IsCompleted()
{
for (int i = 0; i < character.Inventory.Items.Length; i++)
{
if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) continue;
if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) { continue; }
if (character.Inventory.Items[i].HasTag(gearTag))
{
var containedItems = character.Inventory.Items[i].ContainedItems;
if (containedItems == null) continue;
if (containedItems == null) { continue; }
var oxygenTank = containedItems.FirstOrDefault(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
if (oxygenTank != null) return true;
if (oxygenTank != null) { return true; }
}
}
return false;
}
public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted;
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit)
: base(character, "")
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit) : base(character, "")
{
gearTag = needDivingSuit ? "divingsuit" : "diving";
}
@@ -54,12 +49,11 @@ namespace Barotrauma
else
{
var containedItems = item.ContainedItems;
if (containedItems == null) return;
if (containedItems == null) { return; }
//check if there's an oxygen tank in the mask/suit
foreach (Item containedItem in containedItems)
{
if (containedItem == null) continue;
if (containedItem == null) { continue; }
if (containedItem.Condition <= 0.0f)
{
containedItem.Drop(character);
@@ -69,36 +63,21 @@ namespace Barotrauma
//we've got an oxygen source inside the mask/suit, all good
return;
}
}
}
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
{
character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
subObjective = new AIObjectiveContainItem(character, new string[] { "oxygentank", "oxygensource" }, item.GetComponent<ItemContainer>());
}
}
if (subObjective != null)
{
subObjective.TryComplete(deltaTime);
}
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (character.AnimController.CurrentHull == null) return 100.0f;
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 100.0f - character.Oxygen;
}
public override bool IsDuplicate(AIObjective otherObjective)
{
return otherObjective is AIObjectiveFindDivingGear;
}
public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted;
public override float GetPriority(AIObjectiveManager objectiveManager) => MathHelper.Clamp(100 - character.OxygenAvailable, 0, 100);
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindDivingGear;
}
}
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -25,7 +24,7 @@ namespace Barotrauma
private float searchHullTimer;
private AIObjectiveGoTo goToObjective;
private AIObjective divingGearObjective;
private AIObjectiveFindDivingGear divingGearObjective;
public AIObjectiveFindSafety(Character character) : base(character, "") { }
@@ -35,13 +34,12 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
var currentHull = character.AnimController.CurrentHull;
if (HumanAIController.NeedsDivingGear(currentHull))
if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null)
{
bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90;
bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character);
if ((divingGearObjective == null || !divingGearObjective.CanBeCompleted) && !hasEquipment)
if (!hasEquipment)
{
// If the previous objective cannot be completed, create a new and try again.
divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit);
}
}
@@ -55,9 +53,15 @@ namespace Barotrauma
}
else if (divingGearObjective.CanBeCompleted)
{
// If diving gear objective is active, wait for it to complete.
// If diving gear objective is active and can be completed, wait for it to complete.
return;
}
else
{
divingGearObjective = null;
// Reset the timer so that we get a safe hull target.
searchHullTimer = 0;
}
}
if (unreachableClearTimer > 0)
@@ -83,17 +87,18 @@ namespace Barotrauma
{
if (goToObjective.Target != bestHull)
{
goToObjective = new AIObjectiveGoTo(bestHull, character)
// If we need diving gear, we should already have it, if possible.
goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
{
AllowGoingOutside = true
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
};
}
}
else
{
goToObjective = new AIObjectiveGoTo(bestHull, character)
goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
{
AllowGoingOutside = true
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
};
}
}
@@ -110,6 +115,7 @@ namespace Barotrauma
unreachable.Add(goToObjective.Target as Hull);
}
goToObjective = null;
SteeringManager.SteeringWander();
}
}
else if (currentHull != null)
@@ -126,6 +132,11 @@ namespace Barotrauma
foreach (Character enemy in Character.CharacterList)
{
//don't run from friendly NPCs
if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; }
//friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; }
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
{
@@ -170,13 +181,15 @@ namespace Barotrauma
if (unreachable.Contains(hull)) { continue; }
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; }
hullSafety = HumanAIController.GetHullSafety(hull, character);
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f;
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
hullSafety *= distanceFactor;
// Each unsafe node reduces the hull safety value.
// Ignore current hull, because otherwise the would block all paths from the current hull to the target hull.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
if (path.Unreachable) { continue; }
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
hullSafety /= 1 + unsafeNodes;
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
if (!character.Submarine.IsEntityFoundOnThisSub(hull, true))
@@ -31,7 +31,7 @@ namespace Barotrauma
public override bool IsCompleted()
{
return leak.Open <= 0.0f || leak.Removed || pathUnreachable;
return leak.Open <= 0.0f || leak.Removed;
}
public override bool CanBeCompleted => !abandon && base.CanBeCompleted;
@@ -94,16 +94,15 @@ namespace Barotrauma
if (repairTool == null) { return; }
Vector2 gapDiff = leak.WorldPosition - character.WorldPosition;
var humanoidController = character.AnimController as HumanoidAnimController;
// TODO: use the collider size/reach?
if (!character.AnimController.InWater && humanoidController != null && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
{
((HumanoidAnimController)character.AnimController).Crouching = true;
HumanAIController.AnimController.Crouching = true;
}
float armLength = humanoidController != null ? ConvertUnits.ToDisplayUnits(humanoidController.ArmLength) : 100;
bool cannotReach = gapDiff.Length() > armLength + repairTool.Range;
float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range);
bool cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach;
if (cannotReach)
{
if (gotoObjective != null)
@@ -116,7 +115,10 @@ namespace Barotrauma
}
else
{
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character);
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character)
{
CloseEnough = reach * 0.75f
};
if (!subObjectives.Contains(gotoObjective))
{
AddSubObjective(gotoObjective);
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -14,19 +15,15 @@ namespace Barotrauma
//can be either tags or identifiers
private string[] itemIdentifiers;
private Item targetItem, moveToTarget;
private int currSearchIndex;
public bool IgnoreContainedItems;
public string[] ignoredContainerIdentifiers;
private AIObjectiveGoTo goToObjective;
private float currItemPriority;
private bool equip;
private HashSet<Item> ignoredItems = new HashSet<Item>();
private bool canBeCompleted = true;
public override bool CanBeCompleted => canBeCompleted;
@@ -36,25 +33,19 @@ namespace Barotrauma
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false)
: base(character, "")
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) : base(character, "")
{
currSearchIndex = -1;
this.equip = equip;
this.targetItem = targetItem;
}
public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false)
: this(character, new string[] { itemIdentifier }, equip)
{
}
public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false) : this(character, new string[] { itemIdentifier }, equip) { }
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false)
: base(character, "")
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false) : base(character, "")
{
currSearchIndex = -1;
this.equip = equip;
@@ -108,12 +99,12 @@ namespace Barotrauma
FindTargetItem();
if (targetItem == null || moveToTarget == null)
{
// TODO: cannot be completed?
character?.AIController?.SteeringManager?.Reset();
SteeringManager.SteeringWander();
return;
}
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance * 2.0f)
if (moveToTarget.CurrentHull == character.CurrentHull &&
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2))
{
int targetSlot = -1;
if (equip)
@@ -169,9 +160,13 @@ namespace Barotrauma
}
goToObjective.TryComplete(deltaTime);
if (!goToObjective.CanBeCompleted) targetItem = null;
if (!goToObjective.CanBeCompleted)
{
targetItem = null;
moveToTarget = null;
ignoredItems.Add(targetItem);
}
}
}
/// <summary>
@@ -196,22 +191,30 @@ namespace Barotrauma
currSearchIndex++;
var item = Item.ItemList[currSearchIndex];
if (ignoredItems.Contains(item)) { continue; }
if (item.Submarine == null) { continue; }
else if (item.Submarine.TeamID != character.TeamID) { continue; }
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { continue; }
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
if (IgnoreContainedItems && item.Container != null) continue;
if (!itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) continue;
if (item.CurrentHull == null || item.Condition <= 0.0f) { continue; }
if (itemIdentifiers.None(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; }
if (ignoredContainerIdentifiers != null && item.Container != null)
{
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
}
//if the item is inside a character's inventory, don't steal it unless the character is dead
if (item.ParentInventory is CharacterInventory)
{
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) continue;
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
}
//if the item is inside an item, which is inside a character's inventory, don't steal it
Item rootContainer = item.GetRootContainer();
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
{
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) continue;
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
}
float itemPriority = 0.0f;
@@ -219,13 +222,13 @@ namespace Barotrauma
{
//ignore if the item has zero priority
itemPriority = GetItemPriority(item);
if (itemPriority <= 0.0f) continue;
if (itemPriority <= 0.0f) { continue; }
}
itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
//ignore if the item has a lower priority than the currently selected one
if (moveToTarget != null && itemPriority < currItemPriority) continue;
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
currItemPriority = itemPriority;
@@ -186,11 +186,11 @@ namespace Barotrauma
bool completed = false;
float allowedDistance = 0.5f;
float allowedDistance = CloseEnough;
if (Target is Item item)
{
allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), allowedDistance);
allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), CloseEnough);
if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
}
else if (Target is Character targetCharacter)
@@ -200,6 +200,7 @@ namespace Barotrauma
// Check that there is no unsafe or forbidden hulls on the way to the target
// Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
if (path.Unreachable) { continue; }
if (path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; }
}
@@ -30,9 +30,7 @@ namespace Barotrauma
base.Update(objectiveManager, deltaTime);
if (ignoreListTimer > ignoreListClearInterval)
{
ignoreList.Clear();
ignoreListTimer = 0;
UpdateTargets();
Reset();
}
else
{
@@ -67,16 +65,24 @@ namespace Barotrauma
}
}
private void Reset()
{
ignoreList.Clear();
ignoreListTimer = 0;
UpdateTargets();
}
public override void OnSelected()
{
Reset();
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (character.Submarine == null) { return 0; }
if (targets.None()) { return 0; }
float avg = targets.Average(t => Average(t));
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority - MathHelper.Max(0, AIObjectiveManager.OrderPriority - avg);
}
return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority, avg / 100);
return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority + 20, avg / 100);
}
protected void UpdateTargets()
@@ -67,6 +67,7 @@ namespace Barotrauma
private AIObjective GetCurrentObjective()
{
var previousObjective = CurrentObjective;
var firstObjective = Objectives.FirstOrDefault();
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority(this) > firstObjective.GetPriority(this))
{
@@ -76,6 +77,10 @@ namespace Barotrauma
{
CurrentObjective = firstObjective;
}
if (previousObjective != CurrentObjective)
{
CurrentObjective?.OnSelected();
}
return CurrentObjective;
}
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Linq;
using Barotrauma.Extensions;
using FarseerPhysics;
namespace Barotrauma
{
@@ -84,7 +85,14 @@ namespace Barotrauma
}
if (character.CanInteractWith(Item))
{
OperateRepairTool(deltaTime);
if (repairTool == null)
{
FindRepairTool();
}
if (repairTool != null)
{
OperateRepairTool(deltaTime);
}
foreach (Repairable repairable in Item.Repairables)
{
if (repairable.CurrentFixer != null && repairable.CurrentFixer != character)
@@ -121,13 +129,17 @@ namespace Barotrauma
subObjectives.Remove(goToObjective);
}
goToObjective = new AIObjectiveGoTo(Item, character);
if (repairTool != null)
{
goToObjective.CloseEnough = (HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range)) * 0.75f;
}
AddSubObjective(goToObjective);
}
}
private void OperateRepairTool(float deltaTime)
private RepairTool repairTool;
private void FindRepairTool()
{
// Operate repair tool, if required.
foreach (Repairable repairable in Item.Repairables)
{
foreach (var kvp in repairable.requiredItems)
@@ -138,19 +150,29 @@ namespace Barotrauma
{
if (requiredItem.MatchesItem(item))
{
var repairTool = item.GetComponent<RepairTool>();
if (repairTool != null)
{
character.CursorPosition = Item.Position;
character.SetInput(InputType.Aim, false, true);
repairTool.Use(deltaTime, character);
return;
}
repairTool = item.GetComponent<RepairTool>();
}
}
}
}
}
}
private void OperateRepairTool(float deltaTime)
{
character.CursorPosition = Item.Position;
character.SetInput(InputType.Aim, false, true);
Vector2 fromToolToTarget = Item.Position - repairTool.Item.Position;
if (fromToolToTarget.LengthSquared() < MathUtils.Pow(repairTool.Range / 2, 2))
{
// Too close -> steer away
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - Item.SimPosition) / 2);
}
if (character.IsClimbing ||
VectorExtensions.Angle(VectorExtensions.Forward(repairTool.Item.body.TransformedRotation), fromToolToTarget) < MathHelper.PiOver4)
{
repairTool.Use(deltaTime, character);
}
}
}
}
@@ -51,8 +51,14 @@ namespace Barotrauma
{
foreach (Repairable repairable in item.Repairables)
{
if (item.Condition > repairable.ShowRepairUIThreshold) { ignore = true; }
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character)) { ignore = true; }
if (!objectives.ContainsKey(item) && item.Condition > repairable.ShowRepairUIThreshold)
{
ignore = true;
}
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character))
{
ignore = true;
}
if (ignore) { break; }
}
}
@@ -203,7 +203,7 @@ namespace Barotrauma
{
DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed);
return new SteeringPath();
return new SteeringPath(true);
}
closestDist = 0.0f;
@@ -225,8 +225,12 @@ namespace Barotrauma
//if searching for a path inside the sub, make sure the waypoint is visible
if (insideSubmarine)
{
// TODO: for some reason fails to find the path when the sub is flooding. Disabling this check helps fixes it, but we can't disable it
var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition);
if (body != null && body.UserData is Structure) continue;
if (body != null && body.UserData is Structure)
{
continue;
}
}
closestDist = dist;
@@ -237,10 +241,9 @@ namespace Barotrauma
if (endNode == null)
{
DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed);
return new SteeringPath();
return new SteeringPath(true);
}
var path = FindPath(startNode, endNode);
return path;
@@ -266,7 +269,7 @@ namespace Barotrauma
if (startNode == null || endNode == null)
{
DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed);
return new SteeringPath();
return new SteeringPath(true);
}
return FindPath(startNode, endNode);
@@ -290,13 +293,12 @@ namespace Barotrauma
node.G = 0.0f;
node.H = 0.0f;
}
start.state = 1;
while (true)
{
PathNode currNode = null;
float dist = 10000.0f;
float dist = float.MaxValue;
foreach (PathNode node in nodes)
{
if (node.state != 1) continue;
@@ -364,7 +366,7 @@ namespace Barotrauma
if (end.state == 0 || end.Parent == null)
{
//path not found
//DebugConsole.NewMessage("Pathfinding error: path not found", Color.DarkRed);
return new SteeringPath(true);
}
@@ -683,12 +683,6 @@ namespace Barotrauma
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength);
}
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
{
angle -= MathHelper.TwoPi;
}
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
}
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
@@ -1059,6 +1059,8 @@ namespace Barotrauma
}
}
private float prevFootPos;
void UpdateClimbing()
{
if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent<Ladder>() == null)
@@ -1137,15 +1139,34 @@ namespace Barotrauma
handPos.X - Dir * 0.05f,
bottomPos + ColliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y);
MoveLimb(leftFoot,
new Vector2(footPos.X,
(slide ? footPos.Y : MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight) + ladderSimPos.Y),
15.5f, true);
if (slide)
{
MoveLimb(leftFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true);
MoveLimb(rightFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true);
}
else
{
float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight;
float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight;
MoveLimb(leftFoot, new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), 15.5f, true);
MoveLimb(rightFoot,
new Vector2(footPos.X,
(slide ? footPos.Y : MathUtils.Round(footPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
15.5f, true);
float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f);
float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f);
MoveLimb(rightFoot, new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), 15.5f, true);
#if CLIENT
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition);
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition);
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
#endif
prevFootPos = footPos.Y;
}
//apply torque to the legs to make the knees bend
Limb leftLeg = GetLimb(LimbType.LeftLeg);
@@ -1781,14 +1802,24 @@ namespace Barotrauma
float c = Vector2.Distance(pos, shoulderPos);
c = MathHelper.Clamp(c, Math.Abs(upperArmLength - forearmLength), forearmLength + upperArmLength - 0.01f);
float ang2 = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
float armAngle = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
float armAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c);
float handAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c);
float upperArmAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c) * Dir;
float lowerArmAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c) * Dir;
//make sure the arm angle "has the same number of revolutions" as the arm
while (arm.Rotation - armAngle > MathHelper.Pi)
{
armAngle += MathHelper.TwoPi;
}
while (arm.Rotation - armAngle < -MathHelper.Pi)
{
armAngle -= MathHelper.TwoPi;
}
arm?.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f * force * arm.Mass);
forearm?.body.SmoothRotate((ang2 + handAngle * Dir), 20.0f * force * forearm.Mass);
hand?.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force * hand.Mass);
arm?.body.SmoothRotate((armAngle - upperArmAngle), 20.0f * force * arm.Mass, wrapAngle: false);
forearm?.body.SmoothRotate((armAngle + lowerArmAngle), 20.0f * force * forearm.Mass, wrapAngle: false);
hand?.body.SmoothRotate((armAngle + lowerArmAngle), 100.0f * force * hand.Mass, wrapAngle: false);
}
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
@@ -86,7 +86,10 @@ namespace Barotrauma
public bool onGround;
private bool ignorePlatforms;
protected float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale;
/// <summary>
/// In sim units. Joint scale applied.
/// </summary>
public float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale;
public Structure Stairs;
@@ -1324,6 +1327,19 @@ namespace Barotrauma
}
if (errorMsg != null)
{
if (character.IsRemotePlayer)
{
errorMsg += " Ragdoll controlled remotely.";
}
if (SimplePhysicsEnabled)
{
errorMsg += " Simple physics enabled.";
}
if (GameMain.NetworkMember != null)
{
errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
}
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#else
@@ -1343,7 +1359,6 @@ namespace Barotrauma
SetInitialLimbPositions();
return;
}
UpdateProjSpecific(deltaTime);
}
partial void UpdateProjSpecific(float deltaTime);
@@ -120,8 +120,6 @@ namespace Barotrauma
private List<StatusEffect> statusEffects = new List<StatusEffect>();
private List<float> speedMultipliers = new List<float>();
private List<StatusEffect> statusEffects = new List<StatusEffect>();
public Entity ViewTarget
{
get;
@@ -330,7 +328,8 @@ namespace Barotrauma
pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f);
}
}
private float ragdollingLockTimer;
public bool IsRagdolled;
public bool IsForceRagdolled;
public bool dontFollowCursor;
@@ -1877,8 +1876,6 @@ namespace Barotrauma
}
speechImpedimentSet = false;
if (needsAir)
{
bool protectedFromPressure = PressureProtection > 0.0f;
@@ -1935,9 +1932,23 @@ namespace Barotrauma
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
bool allowRagdoll = GameMain.NetworkMember != null ? GameMain.NetworkMember.ServerSettings.AllowRagdollButton : true;
if (IsForceRagdolled)
{
IsRagdolled = IsForceRagdolled;
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f)) //Keep us ragdolled if we were forced or we're too speedy to unragdoll
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
}
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f))
{
if (ragdollingLockTimer > 0.0f)
{
ragdollingLockTimer -= deltaTime;
}
else
{
bool wasRagdolled = IsRagdolled;
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.25f; }
}
}
UpdateSightRange();
UpdateSoundRange();
@@ -19,6 +19,10 @@ namespace Barotrauma
public readonly List<string> AllowedDialogTags;
private float commonness;
public float Commonness
{
get { return commonness; }
}
public NPCPersonalityTrait(XElement element)
{