v0.13.0.11
This commit is contained in:
+123
-73
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
private float holdFireTimer;
|
||||
private bool hasAimed;
|
||||
private bool isLethalWeapon;
|
||||
private bool AllowCoolDown => !IsOffensiveOrArrest || Mode != initialMode;
|
||||
|
||||
public Character Enemy { get; private set; }
|
||||
public bool HoldPosition { get; set; }
|
||||
@@ -79,11 +80,18 @@ namespace Barotrauma
|
||||
private float coolDownTimer;
|
||||
private IEnumerable<Body> myBodies;
|
||||
private float aimTimer;
|
||||
private float reloadTimer;
|
||||
private float spreadTimer;
|
||||
|
||||
private bool canSeeTarget;
|
||||
private float visibilityCheckTimer;
|
||||
private readonly float visibilityCheckInterval = 0.2f;
|
||||
|
||||
private float sqrDistance;
|
||||
private readonly float maxDistance = 2000;
|
||||
private readonly float distanceCheckInterval = 0.2f;
|
||||
private float distanceTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Aborts the objective when this condition is true
|
||||
/// </summary>
|
||||
@@ -108,8 +116,12 @@ namespace Barotrauma
|
||||
public CombatMode Mode { get; private set; }
|
||||
|
||||
private bool IsOffensiveOrArrest => initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest;
|
||||
private bool TargetEliminated => Enemy == null || Enemy.Removed || Enemy.IsUnconscious;
|
||||
private bool TargetEliminated => IsEnemyDisabled || Enemy.IsUnconscious;
|
||||
private bool IsEnemyDisabled => Enemy == null || Enemy.Removed || Enemy.IsDead;
|
||||
|
||||
private float AimSpeed => HumanAIController.AimSpeed;
|
||||
private float AimAccuracy => HumanAIController.AimAccuracy;
|
||||
|
||||
private bool EnemyIsClose() => Enemy != null && character.CurrentHull != null && character.CurrentHull == Enemy.CurrentHull || Vector2.DistanceSquared(character.Position, Enemy.Position) < 500;
|
||||
|
||||
public AIObjectiveCombat(Character character, Character enemy, CombatMode mode, AIObjectiveManager objectiveManager, float priorityModifier = 1, float coolDown = 10.0f)
|
||||
@@ -136,6 +148,8 @@ namespace Barotrauma
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
spreadTimer = Rand.Range(-10, 10);
|
||||
HumanAIController.SortTimer = 0;
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
@@ -159,6 +173,10 @@ namespace Barotrauma
|
||||
base.Update(deltaTime);
|
||||
ignoreWeaponTimer -= deltaTime;
|
||||
checkWeaponsTimer -= deltaTime;
|
||||
if (reloadTimer > 0)
|
||||
{
|
||||
reloadTimer -= deltaTime;
|
||||
}
|
||||
if (ignoreWeaponTimer < 0)
|
||||
{
|
||||
ignoredWeapons.Clear();
|
||||
@@ -168,17 +186,25 @@ namespace Barotrauma
|
||||
{
|
||||
findSafety.Priority = 0;
|
||||
}
|
||||
if (!character.IsOnPlayerTeam && !objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>())
|
||||
{
|
||||
distanceTimer -= deltaTime;
|
||||
if (distanceTimer < 0)
|
||||
{
|
||||
distanceTimer = distanceCheckInterval;
|
||||
sqrDistance = Vector2.DistanceSquared(character.WorldPosition, Enemy.WorldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool Check()
|
||||
{
|
||||
if (IsOffensiveOrArrest && Mode != initialMode)
|
||||
if (sqrDistance > maxDistance * maxDistance)
|
||||
{
|
||||
Abandon = true;
|
||||
SteeringManager.Reset();
|
||||
return false;
|
||||
// The target escaped from us.
|
||||
return true;
|
||||
}
|
||||
return IsEnemyDisabled || (!IsOffensiveOrArrest && coolDownTimer <= 0);
|
||||
return IsEnemyDisabled || (AllowCoolDown && coolDownTimer <= 0);
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
@@ -186,10 +212,9 @@ namespace Barotrauma
|
||||
if (abortCondition != null && abortCondition())
|
||||
{
|
||||
Abandon = true;
|
||||
SteeringManager.Reset();
|
||||
return;
|
||||
}
|
||||
if (!IsOffensiveOrArrest)
|
||||
if (AllowCoolDown)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
}
|
||||
@@ -199,7 +224,11 @@ namespace Barotrauma
|
||||
{
|
||||
OperateWeapon(deltaTime);
|
||||
}
|
||||
if (!HoldPosition && seekAmmunitionObjective == null && seekWeaponObjective == null)
|
||||
if (HoldPosition)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
else if (seekAmmunitionObjective == null && seekWeaponObjective == null)
|
||||
{
|
||||
Move(deltaTime);
|
||||
}
|
||||
@@ -431,7 +460,7 @@ namespace Barotrauma
|
||||
priority /= 2;
|
||||
}
|
||||
}
|
||||
if (Enemy.Stun > 1)
|
||||
if (Enemy.IsKnockedDown)
|
||||
{
|
||||
// Enemy is stunned, reduce the priority of stunner weapons.
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
@@ -621,7 +650,7 @@ namespace Barotrauma
|
||||
var slots = Weapon.AllowedSlots.Where(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand));
|
||||
if (character.Inventory.TryPutItem(Weapon, character, slots))
|
||||
{
|
||||
aimTimer = Rand.Range(0.5f, 1f);
|
||||
aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -704,15 +733,12 @@ namespace Barotrauma
|
||||
{
|
||||
IgnoreIfTargetDead = true,
|
||||
DialogueIdentifier = "dialogcannotreachtarget",
|
||||
TargetName = Enemy.DisplayName
|
||||
TargetName = Enemy.DisplayName,
|
||||
AlwaysUseEuclideanDistance = false
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
Abandon = true;
|
||||
SteeringManager.Reset();
|
||||
});
|
||||
onAbandon: () => Abandon = true);
|
||||
if (followTargetObjective == null) { return; }
|
||||
if (Mode == CombatMode.Arrest && Enemy.Stun > 2)
|
||||
if (Mode == CombatMode.Arrest && (Enemy.Stun > 1 || Enemy.IsKnockedDown))
|
||||
{
|
||||
if (HumanAIController.HasItem(character, "handlocker", out _))
|
||||
{
|
||||
@@ -720,8 +746,8 @@ namespace Barotrauma
|
||||
{
|
||||
arrestingRegistered = true;
|
||||
followTargetObjective.Completed += OnArrestTargetReached;
|
||||
followTargetObjective.CloseEnough = 100;
|
||||
}
|
||||
followTargetObjective.CloseEnough = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -737,7 +763,7 @@ namespace Barotrauma
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
if (followTargetObjective != null)
|
||||
if (!arrestingRegistered && followTargetObjective != null)
|
||||
{
|
||||
followTargetObjective.CloseEnough =
|
||||
WeaponComponent is RangedWeapon ? 1000 :
|
||||
@@ -760,7 +786,7 @@ namespace Barotrauma
|
||||
|
||||
private void OnArrestTargetReached()
|
||||
{
|
||||
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && Enemy.Stun > 0 && character.CanInteractWith(Enemy))
|
||||
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
|
||||
{
|
||||
var handCuffs = matchingItems.First();
|
||||
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true))
|
||||
@@ -780,8 +806,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
|
||||
IsCompleted = true;
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -818,25 +844,7 @@ namespace Barotrauma
|
||||
if (WeaponComponent == null) { return false; }
|
||||
if (Weapon.OwnInventory == null) { return true; }
|
||||
// Eject empty ammo
|
||||
if (Weapon.OwnInventory.AllItems.Any(it => it.Condition <= 0.0f))
|
||||
{
|
||||
foreach (Item containedItem in Weapon.OwnInventory.AllItemsMod)
|
||||
{
|
||||
if (containedItem.Condition <= 0)
|
||||
{
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
// If we are outside of main sub, try to put the ammo in the inventory instead dropping it in the sea.
|
||||
if (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
containedItem.Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HumanAIController.UnequipEmptyItems(Weapon);
|
||||
RelatedItem item = null;
|
||||
Item ammunition = null;
|
||||
string[] ammunitionIdentifiers = null;
|
||||
@@ -869,22 +877,13 @@ namespace Barotrauma
|
||||
if (ammunition != null)
|
||||
{
|
||||
var container = Weapon.GetComponent<ItemContainer>();
|
||||
if (container.Item.ParentInventory == character.Inventory)
|
||||
if (!container.Inventory.TryPutItem(ammunition, null))
|
||||
{
|
||||
if (!container.Inventory.CanBePut(ammunition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
character.Inventory.RemoveItem(ammunition);
|
||||
if (!container.Inventory.TryPutItem(ammunition, null))
|
||||
if (ammunition.ParentInventory == character.Inventory)
|
||||
{
|
||||
ammunition.Drop(character);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
container.Combine(ammunition, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -902,6 +901,15 @@ namespace Barotrauma
|
||||
private void Attack(float deltaTime)
|
||||
{
|
||||
character.CursorPosition = Enemy.WorldPosition;
|
||||
if (AimAccuracy < 1)
|
||||
{
|
||||
spreadTimer += deltaTime * Rand.Range(0.01f, 1f);
|
||||
float shake = Rand.Range(0.95f, 1.05f);
|
||||
float offsetAmount = (1 - AimAccuracy) * Rand.Range(300f, 500f);
|
||||
float distanceFactor = MathUtils.InverseLerp(0, 1000 * 1000, sqrDistance);
|
||||
float offset = (float)Math.Sin(spreadTimer * shake) * offsetAmount * distanceFactor;
|
||||
character.CursorPosition += new Vector2(0, offset);
|
||||
}
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
character.CursorPosition -= character.Submarine.Position;
|
||||
@@ -912,7 +920,11 @@ namespace Barotrauma
|
||||
canSeeTarget = character.CanSeeTarget(Enemy);
|
||||
visibilityCheckTimer = visibilityCheckInterval;
|
||||
}
|
||||
if (!canSeeTarget) { return; }
|
||||
if (!canSeeTarget)
|
||||
{
|
||||
aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
|
||||
return;
|
||||
}
|
||||
if (Weapon.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
@@ -928,7 +940,15 @@ namespace Barotrauma
|
||||
aimTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 1) { return; }
|
||||
if (reloadTimer > 0) { return; }
|
||||
if (Mode == CombatMode.Arrest)
|
||||
{
|
||||
// If the target is arrested or if it's stunned and we can't lock the target up, consider the objective done.
|
||||
if (Enemy.IsKnockedDown && !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false) || HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true))
|
||||
{
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
if (holdFireCondition != null && holdFireCondition()) { return; }
|
||||
float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);
|
||||
if (WeaponComponent is MeleeWeapon meleeWeapon)
|
||||
@@ -963,14 +983,12 @@ namespace Barotrauma
|
||||
}
|
||||
if (closeEnough)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Weapon.Use(deltaTime, character);
|
||||
UseWeapon(deltaTime);
|
||||
}
|
||||
else if (!character.IsFacing(Enemy.WorldPosition))
|
||||
{
|
||||
// Don't do the facing check if we are close to the target, because it easily causes the character to get stuck here when it flips around.
|
||||
aimTimer = Rand.Range(1f, 1.5f);
|
||||
aimTimer = Rand.Range(1f, 1.5f) / AimSpeed;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -979,14 +997,15 @@ namespace Barotrauma
|
||||
{
|
||||
if (sqrDist > repairTool.Range * repairTool.Range) { return; }
|
||||
}
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4)
|
||||
float aimFactor = MathHelper.PiOver2 * (1 - AimAccuracy);
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4 + aimFactor)
|
||||
{
|
||||
if (myBodies == null)
|
||||
{
|
||||
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
||||
}
|
||||
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
|
||||
var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
|
||||
var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories, allowInsideFixture: true);
|
||||
if (pickedBody != null)
|
||||
{
|
||||
Character target = null;
|
||||
@@ -1000,31 +1019,62 @@ namespace Barotrauma
|
||||
}
|
||||
if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Weapon.Use(deltaTime, character);
|
||||
float reloadTime = 0;
|
||||
if (WeaponComponent is RangedWeapon rangedWeapon)
|
||||
{
|
||||
reloadTime = rangedWeapon.Reload;
|
||||
}
|
||||
if (WeaponComponent is MeleeWeapon mw)
|
||||
{
|
||||
reloadTime = mw.Reload;
|
||||
}
|
||||
aimTimer = reloadTime * Rand.Range(1f, 1.5f);
|
||||
UseWeapon(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UseWeapon(float deltaTime)
|
||||
{
|
||||
// Never allow to attack characters with deadly weapons while trying to arrest.
|
||||
if (Mode == CombatMode.Arrest && isLethalWeapon) { return; }
|
||||
float reloadTime = 0;
|
||||
if (WeaponComponent is RangedWeapon rangedWeapon)
|
||||
{
|
||||
// If the weapon is just equipped, we can't shoot just yet.
|
||||
if (rangedWeapon.ReloadTimer <= 0)
|
||||
{
|
||||
reloadTime = rangedWeapon.Reload;
|
||||
}
|
||||
}
|
||||
if (WeaponComponent is MeleeWeapon mw)
|
||||
{
|
||||
if (!((HumanoidAnimController)character.AnimController).Crouching)
|
||||
{
|
||||
reloadTime = mw.Reload;
|
||||
}
|
||||
}
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Weapon.Use(deltaTime, character);
|
||||
reloadTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.25f) / AimSpeed);
|
||||
}
|
||||
|
||||
private bool ShouldUnequipWeapon =>
|
||||
Weapon != null &&
|
||||
character.Submarine != null &&
|
||||
character.Submarine.TeamID == character.TeamID &&
|
||||
Character.CharacterList.None(c => c.Submarine == character.Submarine && HumanAIController.IsActive(c) && !HumanAIController.IsFriendly(character, c) && HumanAIController.VisibleHulls.Contains(c.CurrentHull));
|
||||
|
||||
protected override void OnCompleted()
|
||||
{
|
||||
base.OnCompleted();
|
||||
if (Weapon != null)
|
||||
if (ShouldUnequipWeapon)
|
||||
{
|
||||
Unequip();
|
||||
}
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
base.OnAbandon();
|
||||
if (ShouldUnequipWeapon)
|
||||
{
|
||||
Unequip();
|
||||
}
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
Reference in New Issue
Block a user