(4b54fb4bf) Refactor AIObjectiveCombat and the reactions to the damage. Fixes bots not reacting to any damage done with repair tools. Now they should flee (but not retaliate).
This commit is contained in:
@@ -293,13 +293,9 @@ namespace Barotrauma
|
||||
if (damage <= 0) { return; }
|
||||
if (attacker == null || attacker.IsDead || attacker.Removed)
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
return;
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
if (IsFriendly(attacker))
|
||||
else if (IsFriendly(attacker))
|
||||
{
|
||||
if (attacker.AnimController.Anim == Barotrauma.AnimController.Animation.CPR && attacker.SelectedCharacter == Character)
|
||||
{
|
||||
@@ -309,51 +305,50 @@ 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
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
return;
|
||||
}
|
||||
float currentVitality = Character.CharacterHealth.Vitality;
|
||||
float dmgPercentage = damage / currentVitality * 100;
|
||||
if (dmgPercentage < currentVitality / 10)
|
||||
{
|
||||
// Don't react to a minor amount of (accidental) dmg done by friendly characters
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
// Don't retaliate on damage done by friendly ai, because we know that it's accidental
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker), Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
float currentVitality = Character.CharacterHealth.Vitality;
|
||||
float dmgPercentage = damage / currentVitality * 100;
|
||||
if (dmgPercentage < currentVitality / 10)
|
||||
{
|
||||
// Don't retaliate on minor (accidental) dmg done by friendly characters
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive);
|
||||
}
|
||||
|
||||
void AddCombatObjective(AIObjectiveCombat.CombatMode mode, float delay = 0)
|
||||
{
|
||||
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective)
|
||||
{
|
||||
if (combatObjective.Enemy != attacker)
|
||||
if (combatObjective.Enemy != attacker || (combatObjective.Enemy == null && attacker == null))
|
||||
{
|
||||
// Replace the old objective with the new.
|
||||
ObjectiveManager.Objectives.Remove(combatObjective);
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker));
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker));
|
||||
if (delay > 0)
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode), delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,38 +47,62 @@ namespace Barotrauma
|
||||
|
||||
private float coolDownTimer;
|
||||
|
||||
public AIObjectiveCombat(Character character, Character enemy) : base(character, "")
|
||||
public enum CombatMode
|
||||
{
|
||||
Defensive,
|
||||
Offensive, // Not implemented
|
||||
Retreat
|
||||
}
|
||||
|
||||
public CombatMode Mode { get; private set; }
|
||||
|
||||
public AIObjectiveCombat(Character character, Character enemy, CombatMode mode) : base(character, "")
|
||||
{
|
||||
Enemy = enemy;
|
||||
coolDownTimer = CoolDown;
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
|
||||
Mode = mode;
|
||||
if (Enemy == null)
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
if (Weapon != null && character.Inventory.Items.Contains(_weapon))
|
||||
if (abandon) { return; }
|
||||
switch (Mode)
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon();
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Escape(deltaTime);
|
||||
}
|
||||
else if (Equip(deltaTime))
|
||||
{
|
||||
if (Reload(deltaTime))
|
||||
{
|
||||
Attack(deltaTime);
|
||||
}
|
||||
}
|
||||
if (!abandon)
|
||||
{
|
||||
Move(deltaTime);
|
||||
case CombatMode.Defensive:
|
||||
if (Weapon != null && character.Inventory.Items.Contains(_weapon))
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon();
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
else if (Equip(deltaTime))
|
||||
{
|
||||
if (Reload(deltaTime))
|
||||
{
|
||||
Attack(deltaTime);
|
||||
}
|
||||
}
|
||||
// When defensive, try to retreat to safety. TODO: in offsensive mode, engage the target
|
||||
Retreat(deltaTime);
|
||||
break;
|
||||
case CombatMode.Retreat:
|
||||
Retreat(deltaTime);
|
||||
break;
|
||||
case CombatMode.Offensive:
|
||||
default:
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,20 +164,18 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
//couldn't equip the item, escape
|
||||
Escape(deltaTime);
|
||||
//Abandon(deltaTime);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Move(float deltaTime)
|
||||
private void Retreat(float deltaTime)
|
||||
{
|
||||
// Retreat to safety
|
||||
// TODO: aggressive behaviour, chasing?
|
||||
if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted))
|
||||
{
|
||||
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull();
|
||||
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(new List<Hull>() { character.CurrentHull });
|
||||
}
|
||||
if (retreatTarget != null)
|
||||
{
|
||||
@@ -190,7 +212,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (!reloadWeaponObjective.CanBeCompleted)
|
||||
{
|
||||
Escape(deltaTime);
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -249,16 +271,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void Escape(float deltaTime)
|
||||
private void Abandon(float deltaTime)
|
||||
{
|
||||
abandon = true;
|
||||
SteeringManager.Reset();
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
//HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
bool completed = Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0;
|
||||
bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || coolDownTimer <= 0;
|
||||
if (completed)
|
||||
{
|
||||
if (Weapon != null)
|
||||
@@ -270,7 +292,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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 float GetPriority(AIObjectiveManager objectiveManager) => (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : 100;
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
|
||||
+4
-1
@@ -86,7 +86,10 @@ namespace Barotrauma
|
||||
|
||||
character.AIController.SteeringManager.Reset();
|
||||
character.CursorPosition = fs.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
extinguisher.Use(deltaTime, character);
|
||||
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
|
||||
+4
-3
@@ -168,13 +168,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Hull FindBestHull()
|
||||
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null)
|
||||
{
|
||||
Hull bestHull = character.CurrentHull;
|
||||
float bestValue = currenthullSafety;
|
||||
Hull bestHull = null;
|
||||
float bestValue = 0;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine == null) { continue; }
|
||||
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
||||
float hullSafety = 0;
|
||||
if (character.Submarine != null && SteeringManager == PathSteering)
|
||||
{
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Barotrauma.Items.Components
|
||||
attack = new Attack(subElement, item.Name + ", MeleeWeapon");
|
||||
}
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have melee weapons that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have ranged weapons that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have repair tools that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -533,6 +533,25 @@ namespace Barotrauma
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
{
|
||||
//no walls found at either side, just use the initial spawnpos and hope for the best
|
||||
}
|
||||
else if (minX < 0)
|
||||
{
|
||||
//no wall found at the left side, spawn to the left from the right-side wall
|
||||
spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset;
|
||||
}
|
||||
else if (maxX > Level.Loaded.Size.X)
|
||||
{
|
||||
//no wall found at right side, spawn to the right from the left-side wall
|
||||
spawnPos.X = minX + minWidth + 100.0f + subDockingPortOffset;
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
|
||||
Reference in New Issue
Block a user