aeafa16...4d3cf73
This commit is contained in:
@@ -22,7 +22,6 @@ namespace Barotrauma
|
||||
|
||||
public const float HULL_SAFETY_THRESHOLD = 50;
|
||||
|
||||
// TODO: update the list when someone gives a report
|
||||
public HashSet<Hull> UnsafeHulls { get; private set; } = new HashSet<Hull>();
|
||||
|
||||
private SteeringManager outsideSteering, insideSteering;
|
||||
@@ -136,10 +135,11 @@ namespace Barotrauma
|
||||
|
||||
Character.AnimController.IgnorePlatforms = ignorePlatforms;
|
||||
|
||||
if (Character.IsClimbing)
|
||||
{
|
||||
Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
|
||||
}
|
||||
// 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;
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace Barotrauma
|
||||
// Try to put the mask in an Any slot, and drop it if that fails
|
||||
if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List<InvSlotType>() { InvSlotType.Any }))
|
||||
{
|
||||
mask.Drop();
|
||||
mask.Drop(Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ namespace Barotrauma
|
||||
if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem))
|
||||
{
|
||||
// TODO: take the item where it was taken from?
|
||||
extinguisherItem.Drop();
|
||||
extinguisherItem.Drop(Character);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,10 @@ namespace Barotrauma
|
||||
PropagateHullSafety(Character, Character.CurrentHull);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportProblems()
|
||||
{
|
||||
if (GameMain.Client != null) return;
|
||||
|
||||
protected void ReportProblems()
|
||||
{
|
||||
@@ -271,10 +275,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportProblems()
|
||||
{
|
||||
if (GameMain.Client != null) return;
|
||||
|
||||
private void UpdateSpeaking()
|
||||
{
|
||||
@@ -293,41 +293,68 @@ namespace Barotrauma
|
||||
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void OnAttacked(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
float totalDamage = attackResult.Damage;
|
||||
if (totalDamage <= 0.0f || attacker == null) return;
|
||||
|
||||
if (attacker.SpeciesName == Character.SpeciesName)
|
||||
float damage = attackResult.Damage;
|
||||
if (damage < 0) { return; }
|
||||
if (attacker == null || attacker.IsDead || attacker.Removed)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
return;
|
||||
}
|
||||
if (IsFriendly(attacker))
|
||||
{
|
||||
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
|
||||
return;
|
||||
}
|
||||
if (attacker.AnimController.Anim == Barotrauma.AnimController.Animation.CPR && attacker.SelectedCharacter == Character)
|
||||
{
|
||||
// Don't attack characters that damage you while doing cpr, because let's assume that they are helping you.
|
||||
// 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.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;
|
||||
return;
|
||||
}
|
||||
float currentVitality = Character.CharacterHealth.Vitality;
|
||||
float dmgPercentage = totalDamage / currentVitality * 100;
|
||||
float dmgPercentage = damage / currentVitality * 100;
|
||||
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.CurrentObjective is AIObjectiveCombat combatObjective)
|
||||
{
|
||||
if (combatObjective.Enemy != attacker)
|
||||
{
|
||||
// Replace the old objective with the new.
|
||||
ObjectiveManager.Objectives.Remove(combatObjective);
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker), Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
}
|
||||
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker), Rand.Range(0.5f, 1, Rand.RandSync.Unsynced), () =>
|
||||
else
|
||||
{
|
||||
//the objective in the manager is not necessarily the same as the one we just instantiated,
|
||||
//because the objective isn't added if there's already an identical objective in the manager
|
||||
var combatObjective = objectiveManager.GetObjective<AIObjectiveCombat>();
|
||||
combatObjective.MaxEnemyDamage = Math.Max(totalDamage, combatObjective.MaxEnemyDamage);
|
||||
});
|
||||
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective)
|
||||
{
|
||||
if (combatObjective.Enemy != attacker)
|
||||
{
|
||||
// Replace the old objective with the new.
|
||||
ObjectiveManager.Objectives.Remove(combatObjective);
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true)
|
||||
@@ -420,7 +447,8 @@ namespace Barotrauma
|
||||
bool ignoreFire = ObjectiveManager.CurrentObjective is AIObjectiveExtinguishFire || ObjectiveManager.CurrentOrder is AIObjectiveExtinguishFires;
|
||||
bool ignoreWater = HasDivingSuit(Character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingGear(Character);
|
||||
return GetHullSafety(hull, Character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies: false);
|
||||
bool ignoreEnemies = ObjectiveManager.CurrentObjective is AIObjectiveCombat || ObjectiveManager.CurrentOrder is AIObjectiveCombat;
|
||||
return GetHullSafety(hull, Character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
||||
}
|
||||
|
||||
public static float GetHullSafety(Hull hull, Character character, bool ignoreWater = false, bool ignoreOxygen = false, bool ignoreFire = false, bool ignoreEnemies = false)
|
||||
@@ -443,5 +471,8 @@ namespace Barotrauma
|
||||
float safety = oxygenFactor * waterFactor * fireFactor * enemyFactor;
|
||||
return MathHelper.Clamp(safety * 100, 0, 100);
|
||||
}
|
||||
|
||||
// TODO: If the aliens are quaranteed to be in another team than the player, we wouldn't need to check the species.
|
||||
public bool IsFriendly(Character other) => other.TeamID == Character.TeamID && other.SpeciesName == Character.SpeciesName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma
|
||||
public virtual bool KeepDivingGearOn => false;
|
||||
|
||||
protected readonly List<AIObjective> subObjectives = new List<AIObjective>();
|
||||
protected float priority;
|
||||
public float Priority { get; set; }
|
||||
protected readonly Character character;
|
||||
protected string option;
|
||||
protected bool abandon;
|
||||
@@ -26,6 +26,7 @@ namespace Barotrauma
|
||||
|
||||
protected HumanAIController HumanAIController => character.AIController as HumanAIController;
|
||||
protected IndoorsSteeringManager PathSteering => HumanAIController.PathSteering;
|
||||
protected SteeringManager SteeringManager => HumanAIController.SteeringManager;
|
||||
|
||||
public string Option
|
||||
{
|
||||
@@ -103,20 +104,20 @@ namespace Barotrauma
|
||||
CurrentSubObjective.SortSubObjectives(objectiveManager);
|
||||
}
|
||||
|
||||
public virtual float GetPriority(AIObjectiveManager objectiveManager) => priority;
|
||||
public virtual float GetPriority(AIObjectiveManager objectiveManager) => Priority;
|
||||
|
||||
public virtual void Update(AIObjectiveManager objectiveManager, float deltaTime)
|
||||
{
|
||||
var subObjective = objectiveManager.CurrentObjective?.CurrentSubObjective;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
priority = AIObjectiveManager.OrderPriority;
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else if (objectiveManager.CurrentObjective == this || subObjective == this)
|
||||
{
|
||||
priority += Devotion * deltaTime;
|
||||
Priority += Devotion * deltaTime;
|
||||
}
|
||||
priority = MathHelper.Clamp(priority, 0, 100);
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
subObjectives.ForEach(so => so.Update(objectiveManager, deltaTime));
|
||||
}
|
||||
|
||||
|
||||
+206
-132
@@ -1,194 +1,268 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCombat : AIObjective
|
||||
{
|
||||
public override string DebugTag => "combat";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
|
||||
const float CoolDown = 10.0f;
|
||||
|
||||
//the largest amount of damage the enemy has inflicted on this character
|
||||
//(may be higher than enemyStrength if the enemy is e.g. a human using items)
|
||||
public float MaxEnemyDamage;
|
||||
|
||||
private Character enemy;
|
||||
|
||||
private AIObjectiveFindSafety escapeObjective;
|
||||
|
||||
public Character Enemy { get; private set; }
|
||||
|
||||
private Item _weapon;
|
||||
private Item Weapon
|
||||
{
|
||||
get { return _weapon; }
|
||||
set
|
||||
{
|
||||
_weapon = value;
|
||||
_weaponComponent = null;
|
||||
reloadWeaponObjective = null;
|
||||
}
|
||||
}
|
||||
private ItemComponent _weaponComponent;
|
||||
private ItemComponent WeaponComponent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_weaponComponent == null)
|
||||
{
|
||||
_weaponComponent =
|
||||
Weapon.GetComponent<RangedWeapon>() as ItemComponent ??
|
||||
Weapon.GetComponent<MeleeWeapon>() as ItemComponent ??
|
||||
Weapon.GetComponent<RepairTool>() as ItemComponent;
|
||||
}
|
||||
return _weaponComponent;
|
||||
}
|
||||
}
|
||||
private AIObjectiveContainItem reloadWeaponObjective;
|
||||
private Hull retreatTarget;
|
||||
private AIObjectiveGoTo retreatObjective;
|
||||
|
||||
private float coolDownTimer;
|
||||
|
||||
public AIObjectiveCombat(Character character, Character enemy) : base(character, "")
|
||||
{
|
||||
this.enemy = enemy;
|
||||
Enemy = enemy;
|
||||
coolDownTimer = CoolDown;
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
|
||||
var weapon = character.Inventory.FindItemByTag("weapon");
|
||||
if (weapon == null)
|
||||
if (Weapon != null && character.Inventory.Items.Contains(_weapon))
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon();
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Escape(deltaTime);
|
||||
}
|
||||
else
|
||||
else if (Equip(deltaTime))
|
||||
{
|
||||
if (!character.SelectedItems.Contains(weapon))
|
||||
if (Reload(deltaTime))
|
||||
{
|
||||
if (character.Inventory.TryPutItem(weapon, 3, true, false, character))
|
||||
{
|
||||
weapon.Equip(character);
|
||||
}
|
||||
else
|
||||
{
|
||||
//couldn't equip the item, escape
|
||||
Escape(deltaTime);
|
||||
return;
|
||||
}
|
||||
Attack(deltaTime);
|
||||
}
|
||||
}
|
||||
if (!abandon)
|
||||
{
|
||||
Move(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the weapon is loaded
|
||||
var weaponComponent =
|
||||
weapon.GetComponent<RangedWeapon>() as ItemComponent ??
|
||||
weapon.GetComponent<MeleeWeapon>() as ItemComponent ??
|
||||
weapon.GetComponent<RepairTool>() as ItemComponent;
|
||||
if (weaponComponent != null && weaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
|
||||
private Item GetWeapon()
|
||||
{
|
||||
_weaponComponent = null;
|
||||
var weapon = character.Inventory.FindItemByTag("weapon");
|
||||
if (weapon == null)
|
||||
{
|
||||
foreach (var item in character.Inventory.Items)
|
||||
{
|
||||
var containedItems = weapon.ContainedItems;
|
||||
foreach (RelatedItem requiredItem in weaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
if (item == null) { continue; }
|
||||
foreach (var component in item.Components)
|
||||
{
|
||||
Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
|
||||
if (containedItem == null)
|
||||
if (component is MeleeWeapon || component is RangedWeapon)
|
||||
{
|
||||
var newReloadWeaponObjective = new AIObjectiveContainItem(character, requiredItem.Identifiers, weapon.GetComponent<ItemContainer>());
|
||||
if (!newReloadWeaponObjective.IsDuplicate(reloadWeaponObjective))
|
||||
return item;
|
||||
}
|
||||
var effects = component.statusEffectLists;
|
||||
if (effects != null)
|
||||
{
|
||||
foreach (var statusEffects in effects.Values)
|
||||
{
|
||||
reloadWeaponObjective = newReloadWeaponObjective;
|
||||
foreach (var statusEffect in statusEffects)
|
||||
{
|
||||
if (statusEffect.Afflictions.Any())
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return weapon;
|
||||
}
|
||||
|
||||
if (reloadWeaponObjective != null)
|
||||
private bool Equip(float deltaTime)
|
||||
{
|
||||
if (!character.SelectedItems.Contains(Weapon))
|
||||
{
|
||||
if (character.Inventory.TryPutItem(Weapon, 3, true, false, character))
|
||||
{
|
||||
if (reloadWeaponObjective.IsCompleted())
|
||||
{
|
||||
reloadWeaponObjective = null;
|
||||
}
|
||||
else if (!reloadWeaponObjective.CanBeCompleted)
|
||||
{
|
||||
Escape(deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
reloadWeaponObjective.TryComplete(deltaTime);
|
||||
}
|
||||
return;
|
||||
Weapon.Equip(character);
|
||||
}
|
||||
|
||||
character.CursorPosition = enemy.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
|
||||
Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position);
|
||||
if (!MathUtils.IsValid(enemyDiff)) enemyDiff = Rand.Vector(1.0f);
|
||||
float weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi);
|
||||
Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle));
|
||||
|
||||
if (Vector2.Dot(enemyDiff, weaponDir) < 0.9f) return;
|
||||
|
||||
List<FarseerPhysics.Dynamics.Body> ignoredBodies = new List<FarseerPhysics.Dynamics.Body>();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
else
|
||||
{
|
||||
ignoredBodies.Add(limb.body.FarseerBody);
|
||||
//couldn't equip the item, escape
|
||||
Escape(deltaTime);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var pickedBody = Submarine.PickBody(character.SimPosition, enemy.SimPosition, ignoredBodies);
|
||||
if (pickedBody != null && !(pickedBody.UserData is Limb)) return;
|
||||
private void Move(float deltaTime)
|
||||
{
|
||||
// Retreat to safety
|
||||
// TODO: aggressive behaviour, chasing?
|
||||
if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted))
|
||||
{
|
||||
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull();
|
||||
}
|
||||
if (retreatTarget != null)
|
||||
{
|
||||
if (retreatObjective == null || retreatObjective.Target != retreatTarget)
|
||||
{
|
||||
retreatObjective = new AIObjectiveGoTo(retreatTarget, character, false, true);
|
||||
}
|
||||
retreatObjective.TryComplete(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
weapon.Use(deltaTime, character);
|
||||
private bool Reload(float deltaTime)
|
||||
{
|
||||
if (WeaponComponent != null && WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
|
||||
{
|
||||
var containedItems = Weapon.ContainedItems;
|
||||
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
{
|
||||
Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
|
||||
if (containedItem == null)
|
||||
{
|
||||
if (reloadWeaponObjective == null)
|
||||
{
|
||||
reloadWeaponObjective = new AIObjectiveContainItem(character, requiredItem.Identifiers, Weapon.GetComponent<ItemContainer>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reloadWeaponObjective != null)
|
||||
{
|
||||
if (reloadWeaponObjective.IsCompleted())
|
||||
{
|
||||
reloadWeaponObjective = null;
|
||||
}
|
||||
else if (!reloadWeaponObjective.CanBeCompleted)
|
||||
{
|
||||
Escape(deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
reloadWeaponObjective.TryComplete(deltaTime);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
|
||||
private void Attack(float deltaTime)
|
||||
{
|
||||
character.CursorPosition = Enemy.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
if (WeaponComponent is MeleeWeapon meleeWeapon)
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range)
|
||||
{
|
||||
Weapon.Use(deltaTime, character);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WeaponComponent is RepairTool repairTool)
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.Position, Enemy.Position) > repairTool.Range * repairTool.Range) { return; }
|
||||
}
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - character.Position) < MathHelper.PiOver4)
|
||||
{
|
||||
if (myBodies == null)
|
||||
{
|
||||
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
||||
}
|
||||
var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies);
|
||||
if (pickedBody != null)
|
||||
{
|
||||
Character target = null;
|
||||
if (pickedBody.UserData is Character c)
|
||||
{
|
||||
target = c;
|
||||
}
|
||||
else if (pickedBody.UserData is Limb limb)
|
||||
{
|
||||
target = limb.character;
|
||||
}
|
||||
if (target != null && target == Enemy)
|
||||
{
|
||||
Weapon.Use(deltaTime, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Escape(float deltaTime)
|
||||
{
|
||||
// TODO: just let the find safety run?
|
||||
if (escapeObjective == null)
|
||||
{
|
||||
escapeObjective = new AIObjectiveFindSafety(character);
|
||||
}
|
||||
|
||||
if (enemy.AnimController.CurrentHull == character.AnimController.CurrentHull)
|
||||
{
|
||||
escapeObjective.OverrideCurrentHullSafety = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
escapeObjective.OverrideCurrentHullSafety = null;
|
||||
}
|
||||
|
||||
escapeObjective.TryComplete(deltaTime);
|
||||
abandon = true;
|
||||
SteeringManager.Reset();
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
return enemy == null || enemy.Removed || enemy.IsDead || coolDownTimer <= 0.0f;
|
||||
}
|
||||
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager)
|
||||
{
|
||||
if (enemy == null || enemy.Removed)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
|
||||
//clamp the strength to the health of this character
|
||||
//(it doesn't make a difference whether the enemy does 200 or 600 damage, it's one hit kill anyway)
|
||||
|
||||
float enemyDanger = Math.Min(Math.Max(CalculateEnemyStrength(), MaxEnemyDamage), character.Health) + enemy.Health / 10.0f;
|
||||
|
||||
if (enemy.AIController is EnemyAIController enemyAI)
|
||||
{
|
||||
if (enemyAI.SelectedAiTarget == character.AiTarget) enemyDanger *= 2.0f;
|
||||
}
|
||||
|
||||
return Math.Max(enemyDanger, AIObjectiveManager.OrderPriority);
|
||||
}
|
||||
public override bool IsCompleted() => Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0.0f;
|
||||
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;
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
AIObjectiveCombat objective = otherObjective as AIObjectiveCombat;
|
||||
if (objective == null) return false;
|
||||
|
||||
return objective.enemy == enemy;
|
||||
if (!(otherObjective is AIObjectiveCombat objective)) return false;
|
||||
return objective.Enemy == Enemy;
|
||||
}
|
||||
|
||||
private float CalculateEnemyStrength()
|
||||
{
|
||||
float enemyStrength = 0;
|
||||
AttackContext currentContext = character.GetAttackContext();
|
||||
foreach (Limb limb in enemy.AnimController.Limbs)
|
||||
{
|
||||
if (limb.attack == null) continue;
|
||||
if (!limb.attack.IsValidContext(currentContext)) { continue; }
|
||||
if (!limb.attack.IsValidTarget(AttackTarget.Character)) { continue; }
|
||||
enemyStrength += limb.attack.GetTotalDamage(false);
|
||||
}
|
||||
return enemyStrength;
|
||||
}
|
||||
//private float CalculateEnemyStrength()
|
||||
//{
|
||||
// float enemyStrength = 0;
|
||||
// AttackContext currentContext = character.GetAttackContext();
|
||||
// foreach (Limb limb in Enemy.AnimController.Limbs)
|
||||
// {
|
||||
// if (limb.attack == null) continue;
|
||||
// if (!limb.attack.IsValidContext(currentContext)) { continue; }
|
||||
// if (!limb.attack.IsValidTarget(AttackTarget.Character)) { continue; }
|
||||
// enemyStrength += limb.attack.GetTotalDamage(false);
|
||||
// }
|
||||
// return enemyStrength;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace Barotrauma
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
float severityFactor = MathHelper.Lerp(0, 1, MathHelper.Clamp(targetHull.FireSources.Sum(fs => fs.Size.X) / targetHull.Size.X, 0, 1));
|
||||
return MathHelper.Clamp(priority * severityFactor * distanceFactor, 0, 100);
|
||||
return MathHelper.Clamp(Priority * severityFactor * distanceFactor, 0, 100);
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ namespace Barotrauma
|
||||
if (containedItem == null) continue;
|
||||
if (containedItem.Condition <= 0.0f)
|
||||
{
|
||||
containedItem.Drop();
|
||||
containedItem.Drop(character);
|
||||
}
|
||||
else if (containedItem.Prefab.Identifier == "oxygentank" || containedItem.HasTag("oxygensource"))
|
||||
{
|
||||
|
||||
+44
-35
@@ -16,18 +16,17 @@ namespace Barotrauma
|
||||
const float priorityIncrease = 25;
|
||||
const float priorityDecrease = 10;
|
||||
const float SearchHullInterval = 3.0f;
|
||||
const float clearUnreachableInterval = 30;
|
||||
|
||||
private List<Hull> unreachable = new List<Hull>();
|
||||
|
||||
private float currenthullSafety;
|
||||
|
||||
private float unreachableClearTimer;
|
||||
private float searchHullTimer;
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjective divingGearObjective;
|
||||
|
||||
public float? OverrideCurrentHullSafety;
|
||||
|
||||
public AIObjectiveFindSafety(Character character) : base(character, "") { }
|
||||
|
||||
public override bool IsCompleted() => false;
|
||||
@@ -52,7 +51,7 @@ namespace Barotrauma
|
||||
if (divingGearObjective.IsCompleted())
|
||||
{
|
||||
divingGearObjective = null;
|
||||
priority = 0;
|
||||
Priority = 0;
|
||||
}
|
||||
else if (divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
@@ -61,6 +60,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (unreachableClearTimer > 0)
|
||||
{
|
||||
unreachableClearTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
unreachableClearTimer = clearUnreachableInterval;
|
||||
unreachable.Clear();
|
||||
}
|
||||
|
||||
if (searchHullTimer > 0.0f)
|
||||
{
|
||||
searchHullTimer -= deltaTime;
|
||||
@@ -147,16 +156,35 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Hull FindBestHull()
|
||||
public Hull FindBestHull()
|
||||
{
|
||||
Hull bestHull = character.CurrentHull;
|
||||
float bestValue = currenthullSafety;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (unreachable.Contains(hull)) { continue; }
|
||||
if (hull.Submarine == null) { continue; }
|
||||
float hullSafety = 0;
|
||||
if (character.Submarine == null)
|
||||
if (character.Submarine != null && SteeringManager == PathSteering)
|
||||
{
|
||||
// Inside or outside near the sub
|
||||
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.
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(hull, true))
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outside
|
||||
if (hull.RoomName?.ToLowerInvariant() == "airlock")
|
||||
@@ -177,7 +205,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Huge preference for closer targets
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, 100000, Vector2.Distance(character.WorldPosition, hull.WorldPosition)));
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
hullSafety *= distanceFactor;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (hull.Submarine.TeamID != character.TeamID && hull.Submarine.TeamID != Character.TeamType.FriendlyNPC)
|
||||
@@ -185,26 +214,6 @@ namespace Barotrauma
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inside
|
||||
// Not connected.
|
||||
if (!character.Submarine.GetConnectedSubs().Contains(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.
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(hull, true))
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
if (hullSafety > bestValue)
|
||||
{
|
||||
bestHull = hull;
|
||||
@@ -224,24 +233,24 @@ namespace Barotrauma
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
currenthullSafety = 0;
|
||||
priority = 5;
|
||||
Priority = 5;
|
||||
return;
|
||||
}
|
||||
if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold) { priority = 100; }
|
||||
currenthullSafety = OverrideCurrentHullSafety ?? HumanAIController.GetHullSafety(character.CurrentHull);
|
||||
if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold) { Priority = 100; }
|
||||
currenthullSafety = HumanAIController.GetHullSafety(character.CurrentHull);
|
||||
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
priority -= priorityDecrease * deltaTime;
|
||||
Priority -= priorityDecrease * deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
float dangerFactor = (100 - currenthullSafety) / 100;
|
||||
priority += dangerFactor * priorityIncrease * deltaTime;
|
||||
Priority += dangerFactor * priorityIncrease * deltaTime;
|
||||
}
|
||||
priority = MathHelper.Clamp(priority, 0, 100);
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted() && divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
priority = Math.Max(priority, AIObjectiveManager.OrderPriority + 10);
|
||||
Priority = Math.Max(Priority, AIObjectiveManager.OrderPriority + 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => "fix leak";
|
||||
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool ForceRun => true;
|
||||
|
||||
private readonly Gap leak;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "fix leaks";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool ForceRun => true;
|
||||
|
||||
public AIObjectiveFixLeaks(Character character) : base (character, "") { }
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Barotrauma
|
||||
if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any })) continue;
|
||||
|
||||
//if everything else fails, simply drop the existing item
|
||||
character.Inventory.Items[i].Drop();
|
||||
character.Inventory.Items[i].Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,17 +45,22 @@ namespace Barotrauma
|
||||
get
|
||||
{
|
||||
bool canComplete = !cannotReach && !abandon;
|
||||
if (FollowControlledCharacter && Character.Controlled == null) { canComplete = false; }
|
||||
else if (Target != null && Target.Removed) { canComplete = false; }
|
||||
else if (repeat || waitUntilPathUnreachable > 0.0f) { canComplete = true; }
|
||||
else if (character.AIController.SteeringManager is IndoorsSteeringManager pathSteering)
|
||||
if (canComplete)
|
||||
{
|
||||
//path doesn't exist (= hasn't been searched for yet), assume for now that the target is reachable TODO: add a timer?
|
||||
if (pathSteering.CurrentPath == null) { canComplete = true; }
|
||||
else if (!AllowGoingOutside && pathSteering.CurrentPath.HasOutdoorsNodes) { canComplete = false; }
|
||||
if (canComplete)
|
||||
if (FollowControlledCharacter && Character.Controlled == null)
|
||||
{
|
||||
canComplete = !pathSteering.CurrentPath.Unreachable;
|
||||
canComplete = false;
|
||||
}
|
||||
else if (Target != null && Target.Removed)
|
||||
{
|
||||
canComplete = false;
|
||||
}
|
||||
else if (!repeat && waitUntilPathUnreachable < 0)
|
||||
{
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null)
|
||||
{
|
||||
canComplete = !PathSteering.CurrentPath.Unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!canComplete)
|
||||
@@ -144,9 +149,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
var indoorSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
|
||||
bool targetIsOutside = (Target != null && Target.Submarine == null) || (indoorSteering != null && indoorSteering.CurrentPath != null && indoorSteering.CurrentPath.HasOutdoorsNodes);
|
||||
if (targetIsOutside && !AllowGoingOutside)
|
||||
bool targetIsOutside = (Target != null && Target.Submarine == null) ||
|
||||
(SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.HasOutdoorsNodes);
|
||||
if (targetIsOutside && character.CurrentHull != null && !AllowGoingOutside)
|
||||
{
|
||||
cannotReach = true;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace Barotrauma
|
||||
public override string DebugTag => "idle";
|
||||
|
||||
const float WallAvoidDistance = 150.0f;
|
||||
private readonly float newTargetIntervalMin = 5;
|
||||
private readonly float newTargetIntervalMax = 15;
|
||||
private readonly float standStillMin = 1;
|
||||
private readonly float standStillMax = 10;
|
||||
private readonly float walkDurationMin = 3;
|
||||
private readonly float walkDurationMax = 10;
|
||||
|
||||
private Hull currentTarget;
|
||||
private float newTargetTimer;
|
||||
@@ -46,7 +52,10 @@ namespace Barotrauma
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
|
||||
if (currentTarget == null && (IsForbidden(character.CurrentHull) || HumanAIController.UnsafeHulls.Contains(character.CurrentHull)))
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
|
||||
if (currentTargetIsInvalid || (currentTarget == null && IsForbidden(character.CurrentHull)))
|
||||
{
|
||||
newTargetTimer = 0;
|
||||
standStillTimer = 0;
|
||||
@@ -70,7 +79,7 @@ namespace Barotrauma
|
||||
PathSteering.SetPath(path);
|
||||
}
|
||||
|
||||
newTargetTimer = currentTarget == null ? 5.0f : 15.0f;
|
||||
newTargetTimer = currentTarget != null && character.AnimController.InWater ? newTargetIntervalMin : Rand.Range(newTargetIntervalMin, newTargetIntervalMax);
|
||||
}
|
||||
|
||||
newTargetTimer -= deltaTime;
|
||||
@@ -79,20 +88,20 @@ namespace Barotrauma
|
||||
// - if reached the end of the path
|
||||
// - if the target is unreachable
|
||||
// - if the path requires going outside
|
||||
if (PathSteering == null || (PathSteering.CurrentPath != null &&
|
||||
if (SteeringManager != PathSteering || (PathSteering.CurrentPath != null &&
|
||||
(PathSteering.CurrentPath.NextNode == null || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
{
|
||||
standStillTimer -= deltaTime;
|
||||
if (standStillTimer > 0.0f)
|
||||
{
|
||||
walkDuration = Rand.Range(1.0f, 5.0f);
|
||||
walkDuration = Rand.Range(walkDurationMin, walkDurationMax);
|
||||
PathSteering.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (standStillTimer < -walkDuration)
|
||||
{
|
||||
standStillTimer = Rand.Range(1.0f, 10.0f);
|
||||
standStillTimer = Rand.Range(standStillMin, standStillMax);
|
||||
}
|
||||
|
||||
//steer away from edges of the hull
|
||||
@@ -128,12 +137,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
character.AIController.SteeringManager.SteeringWander();
|
||||
if (!character.IsClimbing)
|
||||
if (!character.IsClimbing && !character.AnimController.InWater)
|
||||
{
|
||||
//reset vertical steering to prevent dropping down from platforms etc
|
||||
character.AIController.SteeringManager.ResetY();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -150,6 +158,7 @@ namespace Barotrauma
|
||||
{
|
||||
var idCard = character.Inventory.FindItemByIdentifier("idcard");
|
||||
Hull targetHull = null;
|
||||
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
||||
//random chance of navigating back to the room where the character spawned
|
||||
if (Rand.Int(5) == 1 && idCard != null)
|
||||
{
|
||||
@@ -186,9 +195,13 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Check that there is no unsafe or forbidden hulls on the way to the target
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
if (path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; }
|
||||
if (isCurrentHullOK)
|
||||
{
|
||||
// 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.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; }
|
||||
}
|
||||
|
||||
// If we want to do a steering check, we should do it here, before setting the path
|
||||
//if (path.Cost > 1000.0f) { continue; }
|
||||
|
||||
@@ -56,23 +56,6 @@ namespace Barotrauma
|
||||
DelayedObjectives.Add(objective, coroutine);
|
||||
}
|
||||
|
||||
public Dictionary<AIObjective, CoroutineHandle> DelayedObjectives { get; private set; } = new Dictionary<AIObjective, CoroutineHandle>();
|
||||
public void AddObjective(AIObjective objective, float delay, Action callback = null)
|
||||
{
|
||||
if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine))
|
||||
{
|
||||
CoroutineManager.StopCoroutines(coroutine);
|
||||
DelayedObjectives.Remove(objective);
|
||||
}
|
||||
coroutine = CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
DelayedObjectives.Remove(objective);
|
||||
AddObjective(objective);
|
||||
callback?.Invoke();
|
||||
}, delay);
|
||||
DelayedObjectives.Add(objective, coroutine);
|
||||
}
|
||||
|
||||
public T GetObjective<T>() where T : AIObjective
|
||||
{
|
||||
foreach (AIObjective objective in Objectives)
|
||||
@@ -200,27 +183,6 @@ namespace Barotrauma
|
||||
if (order.TargetItemComponent == null) return;
|
||||
CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController);
|
||||
break;
|
||||
case "chargebatteries":
|
||||
currentOrder = new AIObjectiveChargeBatteries(character, option);
|
||||
break;
|
||||
case "rescue":
|
||||
currentOrder = new AIObjectiveRescueAll(character);
|
||||
break;
|
||||
case "repairsystems":
|
||||
currentOrder = new AIObjectiveRepairItems(character) { RequireAdequateSkills = option != "all" };
|
||||
break;
|
||||
case "pumpwater":
|
||||
currentOrder = new AIObjectivePumpWater(character, option);
|
||||
break;
|
||||
case "extinguishfires":
|
||||
currentOrder = new AIObjectiveExtinguishFires(character);
|
||||
break;
|
||||
case "steer":
|
||||
var steering = (order?.TargetEntity as Item)?.GetComponent<Steering>();
|
||||
if (steering != null) steering.PosToMaintain = steering.Item.Submarine?.WorldPosition;
|
||||
if (order.TargetItemComponent == null) return;
|
||||
currentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController);
|
||||
break;
|
||||
default:
|
||||
if (order.TargetItemComponent == null) return;
|
||||
CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController);
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ namespace Barotrauma
|
||||
if (!character.Inventory.Items[i].AllowedSlots.Contains(InvSlotType.Any) ||
|
||||
!character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any }))
|
||||
{
|
||||
character.Inventory.Items[i].Drop();
|
||||
character.Inventory.Items[i].Drop(character);
|
||||
}
|
||||
}
|
||||
if (character.Inventory.TryPutItem(component.Item, i, true, false, character))
|
||||
|
||||
+30
-1
@@ -35,7 +35,7 @@ namespace Barotrauma
|
||||
float damagePriority = MathHelper.Lerp(1, 0, (Item.Condition + 10) / Item.MaxCondition);
|
||||
float successFactor = MathHelper.Lerp(0, 1, Item.Repairables.Average(r => r.DegreeOfSuccess(character)));
|
||||
float isSelected = character.SelectedConstruction == Item ? 50 : 0;
|
||||
float baseLevel = Math.Max(priority + isSelected, 1);
|
||||
float baseLevel = Math.Max(Priority + isSelected, 1);
|
||||
return MathHelper.Clamp(baseLevel * damagePriority * distanceFactor * successFactor, 0, 100);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.CanInteractWith(Item))
|
||||
{
|
||||
OperateRepairTool(deltaTime);
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
if (repairable.CurrentFixer != null && repairable.CurrentFixer != character)
|
||||
@@ -123,5 +124,33 @@ namespace Barotrauma
|
||||
AddSubObjective(goToObjective);
|
||||
}
|
||||
}
|
||||
|
||||
private void OperateRepairTool(float deltaTime)
|
||||
{
|
||||
// Operate repair tool, if required.
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
foreach (var kvp in repairable.requiredItems)
|
||||
{
|
||||
foreach (RelatedItem requiredItem in kvp.Value)
|
||||
{
|
||||
foreach (var item in character.Inventory.Items)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,8 +549,9 @@ namespace Barotrauma
|
||||
limbHealths[i].Afflictions.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
foreach (Affliction affliction in limbHealths[i].Afflictions)
|
||||
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var affliction = limbHealths[i].Afflictions[j];
|
||||
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == i);
|
||||
affliction.Update(this, targetLimb, deltaTime);
|
||||
affliction.DamagePerSecondTimer += deltaTime;
|
||||
|
||||
Reference in New Issue
Block a user