(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:
Joonas Rikkonen
2019-04-05 16:20:11 +03:00
parent 501a114000
commit 25768d8845
11 changed files with 214 additions and 214 deletions
@@ -66,121 +66,6 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
Point scrollButtonSize = new Point((int)(200 * GUI.Scale), (int)(30 * GUI.Scale));
crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), "", Color.Transparent)
{
CanBeFocused = false
};
toggleCrewButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)
{ AbsoluteOffset = HUDLayoutSettings.CrewArea.Location },
"", style: "UIToggleButton");
toggleCrewButton.OnClicked += (GUIButton btn, object userdata) =>
{
toggleCrewAreaOpen = !toggleCrewAreaOpen;
foreach (GUIComponent child in btn.Children)
{
child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
}
return true;
};
characterListBox = new GUIListBox(new RectTransform(new Point(100, (int)(crewArea.Rect.Height - scrollButtonSize.Y * 1.6f)), crewArea.RectTransform, Anchor.CenterLeft), false, Color.Transparent, null)
{
//Spacing = (int)(3 * GUI.Scale),
ScrollBarEnabled = false,
ScrollBarVisible = false,
CanBeFocused = false
};
scrollButtonUp = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.TopLeft, Pivot.TopLeft), "", Alignment.Center, "GUIButtonVerticalArrow")
{
Visible = false,
UserData = -1,
OnClicked = ScrollCharacterList
};
scrollButtonDown = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), "", Alignment.Center, "GUIButtonVerticalArrow")
{
Visible = false,
UserData = 1,
OnClicked = ScrollCharacterList
};
scrollButtonDown.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipVertically);
if (isSinglePlayer)
{
chatBox = new ChatBox(guiFrame, isSinglePlayer: true)
{
OnEnterMessage = (textbox, text) =>
{
if (Character.Controlled?.Info == null)
{
textbox.Deselect();
textbox.Text = "";
return true;
}
textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
if (!string.IsNullOrWhiteSpace(text))
{
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
AddSinglePlayerChatMessage(
Character.Controlled.Info.Name,
msg,
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
Character.Controlled);
var headset = GetHeadset(Character.Controlled, true);
if (headset != null && headset.CanTransmit())
{
headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sendToChat: false);
}
}
textbox.Deselect();
textbox.Text = "";
return true;
}
};
chatBox.InputBox.OnTextChanged += chatBox.TypingChatMessage;
}
var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null);
reportButtonFrame = new GUILayoutGroup(new RectTransform(
new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform))
{
AbsoluteSpacing = (int)(5 * GUI.Scale),
UserData = "reportbuttons",
CanBeFocused = false
};
//report buttons
foreach (Order order in reports)
{
if (!order.TargetAllCharacters || order.SymbolSprite == null) continue;
var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null)
{
OnClicked = (GUIButton button, object userData) =>
{
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false;
SetCharacterOrder(null, order, null, Character.Controlled);
HumanAIController.PropagateHullSafety(Character.Controlled, Character.Controlled.CurrentHull);
return true;
},
UserData = order,
ToolTip = order.Name
};
new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow")
{
Color = Color.Red * 0.8f,
HoverColor = Color.Red * 1.0f,
PressedColor = Color.Red * 0.6f,
UserData = "highlighted",
CanBeFocused = false,
Visible = false
};
var characterInfo = new CharacterInfo(subElement); var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo); characterInfos.Add(characterInfo);
foreach (XElement invElement in subElement.Elements()) foreach (XElement invElement in subElement.Elements())
@@ -234,6 +234,85 @@ namespace Barotrauma.Items.Components
private set; private set;
} }
private bool useAlternativeLayout;
public bool UseAlternativeLayout
{
get { return useAlternativeLayout; }
set
{
if (AlternativeLayout != null)
{
if (value == useAlternativeLayout) { return; }
useAlternativeLayout = value;
if (useAlternativeLayout)
{
AlternativeLayout?.ApplyTo(GuiFrame.RectTransform);
}
else
{
DefaultLayout?.ApplyTo(GuiFrame.RectTransform);
}
}
}
public void ApplyTo(RectTransform target)
{
if (RelativeOffset.HasValue)
{
target.RelativeOffset = RelativeOffset.Value;
}
else if (AbsoluteOffset.HasValue)
{
target.AbsoluteOffset = AbsoluteOffset.Value;
}
if (RelativeSize.HasValue)
{
target.RelativeSize = RelativeSize.Value;
}
else if (AbsoluteSize.HasValue)
{
target.NonScaledSize = AbsoluteSize.Value;
}
if (Anchor.HasValue)
{
target.Anchor = Anchor.Value;
}
if (Pivot.HasValue)
{
target.Pivot = Pivot.Value;
}
else
{
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
}
target.RecalculateChildren(true, true);
}
}
public GUIFrame GuiFrame { get; protected set; }
[Serialize(false, false)]
public bool AllowUIOverlap
{
get;
set;
}
private ItemComponent linkToUIComponent;
[Serialize("", false)]
public string LinkUIToComponent
{
get;
set;
}
[Serialize(0, false)]
public int HudPriority
{
get;
private set;
}
private bool shouldMuffleLooping; private bool shouldMuffleLooping;
private float lastMuffleCheckTime; private float lastMuffleCheckTime;
private ItemSound loopingSound; private ItemSound loopingSound;
@@ -144,15 +144,10 @@ namespace Barotrauma
private void CreateUI() private void CreateUI()
{ {
TopPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), GUI.Canvas) { MinSize = new Point(0, 35) }, "GUIFrameTop"); TopPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), GUI.Canvas) { MinSize = new Point(0, 35) }, "GUIFrameTop");
GUIFrame paddedTopPanel = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.55f), TopPanel.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, -0.1f) },
GUIFrame paddedTopPanel = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.55f), TopPanel.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, -0.1f) }, style: null); style: null);
var button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft), TextManager.Get("Back")) var button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft), TextManager.Get("OpenSubButton"))
{
OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu
};
button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.07f, 0.0f) }, TextManager.Get("OpenSubButton"))
{ {
OnClicked = (GUIButton btn, object data) => OnClicked = (GUIButton btn, object data) =>
{ {
@@ -163,7 +158,7 @@ namespace Barotrauma
} }
}; };
button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.14f, 0.0f) }, TextManager.Get("SaveSubButton")) button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.08f, 0.0f) }, TextManager.Get("SaveSubButton"))
{ {
OnClicked = (GUIButton btn, object data) => OnClicked = (GUIButton btn, object data) =>
{ {
@@ -174,13 +169,13 @@ namespace Barotrauma
} }
}; };
var nameLabel = new GUITextBlock(new RectTransform(new Vector2(0.1f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.21f, 0.0f) }, var nameLabel = new GUITextBlock(new RectTransform(new Vector2(0.1f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.15f, 0.0f) },
"", font: GUI.LargeFont, textAlignment: Alignment.CenterLeft) "", font: GUI.LargeFont, textAlignment: Alignment.CenterLeft)
{ {
TextGetter = GetSubName TextGetter = GetSubName
}; };
linkedSubBox = new GUIDropDown(new RectTransform(new Vector2(0.15f, 0.9f), paddedTopPanel.RectTransform) { RelativeOffset = new Vector2(0.385f, 0.0f) }, linkedSubBox = new GUIDropDown(new RectTransform(new Vector2(0.15f, 0.9f), paddedTopPanel.RectTransform) { RelativeOffset = new Vector2(0.4f, 0.0f) },
TextManager.Get("AddSubButton"), elementCount: 20) TextManager.Get("AddSubButton"), elementCount: 20)
{ {
ToolTip = TextManager.Get("AddSubToolTip") ToolTip = TextManager.Get("AddSubToolTip")
@@ -284,7 +279,7 @@ namespace Barotrauma
var paddedTab = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), EntityMenu.RectTransform, Anchor.Center), style: null); var paddedTab = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), EntityMenu.RectTransform, Anchor.Center), style: null);
var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedTab.RectTransform) { AbsoluteOffset = new Point(0, 10) }, isHorizontal: true) var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedTab.RectTransform), isHorizontal: true)
{ {
Color = secondaryColor, Color = secondaryColor,
Stretch = true, Stretch = true,
@@ -299,7 +294,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { ClearFilter(); entityFilterBox.Flash(Color.White); return true; } OnClicked = (btn, userdata) => { ClearFilter(); entityFilterBox.Flash(Color.White); return true; }
}; };
var entityListHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.85f), paddedTab.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.06f) }); var entityListHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.85f), paddedTab.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) });
var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.1f), entityListHolder.RectTransform, Anchor.TopRight, Pivot.BottomRight), var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.1f), entityListHolder.RectTransform, Anchor.TopRight, Pivot.BottomRight),
isHorizontal: true) isHorizontal: true)
@@ -861,8 +856,6 @@ namespace Barotrauma
GUI.AddMessage(TextManager.Get("SubSavedNotification").Replace("[filepath]", Submarine.MainSub.FilePath), Color.Green); GUI.AddMessage(TextManager.Get("SubSavedNotification").Replace("[filepath]", Submarine.MainSub.FilePath), Color.Green);
Submarine.RefreshSavedSub(savePath);
linkedSubBox.ClearChildren(); linkedSubBox.ClearChildren();
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (Submarine sub in Submarine.SavedSubmarines)
{ {
@@ -2224,19 +2217,19 @@ namespace Barotrauma
Sprite backgroundSprite = LevelGenerationParams.LevelParams.Find(l => l.BackgroundTopSprite != null).BackgroundTopSprite; Sprite backgroundSprite = LevelGenerationParams.LevelParams.Find(l => l.BackgroundTopSprite != null).BackgroundTopSprite;
using (RenderTarget2D rt = new RenderTarget2D( Sprite backgroundSprite = LevelGenerationParams.LevelParams.Find(l => l.BackgroundTopSprite != null).BackgroundTopSprite;
GameMain.Instance.GraphicsDevice, if (backgroundSprite != null)
width, height, false, SurfaceFormat.Color, DepthFormat.None))
using (SpriteBatch spriteBatch = new SpriteBatch(GameMain.Instance.GraphicsDevice))
{ {
GameMain.Instance.GraphicsDevice.SetRenderTarget(rt); spriteBatch.Begin();
backgroundSprite.Draw(spriteBatch, Vector2.Zero, new Color(0.025f, 0.075f, 0.131f, 1.0f));
if (backgroundSprite != null) spriteBatch.End();
{ }
spriteBatch.Begin();
backgroundSprite.Draw(spriteBatch, Vector2.Zero, new Color(0.025f, 0.075f, 0.131f, 1.0f)); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, transform);
spriteBatch.End(); Submarine.Draw(spriteBatch, false);
} Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, transform); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, transform);
Submarine.Draw(spriteBatch, false); Submarine.Draw(spriteBatch, false);
@@ -293,13 +293,9 @@ namespace Barotrauma
if (damage <= 0) { return; } if (damage <= 0) { return; }
if (attacker == null || attacker.IsDead || attacker.Removed) if (attacker == null || attacker.IsDead || attacker.Removed)
{ {
if (objectiveManager.CurrentOrder == null) AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
{
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
return;
} }
if (IsFriendly(attacker)) else if (IsFriendly(attacker))
{ {
if (attacker.AnimController.Anim == Barotrauma.AnimController.Animation.CPR && attacker.SelectedCharacter == Character) 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) 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 // Don't retaliate on damage done by friendly ai, because we know that it's accidental
if (objectiveManager.CurrentOrder == null) AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
{
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));
}
} }
else 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 else
{
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive);
}
void AddCombatObjective(AIObjectiveCombat.CombatMode mode, float delay = 0)
{ {
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective) 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. // Replace the old objective with the new.
ObjectiveManager.Objectives.Remove(combatObjective); ObjectiveManager.Objectives.Remove(combatObjective);
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker)); objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode));
} }
} }
else 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; 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; Enemy = enemy;
coolDownTimer = CoolDown; coolDownTimer = CoolDown;
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0; HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
Mode = mode;
if (Enemy == null)
{
Mode = CombatMode.Retreat;
}
} }
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
coolDownTimer -= deltaTime; coolDownTimer -= deltaTime;
if (Weapon != null && character.Inventory.Items.Contains(_weapon)) if (abandon) { return; }
switch (Mode)
{ {
Weapon = null; case CombatMode.Defensive:
} if (Weapon != null && character.Inventory.Items.Contains(_weapon))
if (Weapon == null) {
{ Weapon = null;
Weapon = GetWeapon(); }
} if (Weapon == null)
if (Weapon == null) {
{ Weapon = GetWeapon();
Escape(deltaTime); }
} if (Weapon == null)
else if (Equip(deltaTime)) {
{ Mode = CombatMode.Retreat;
if (Reload(deltaTime)) }
{ else if (Equip(deltaTime))
Attack(deltaTime); {
} if (Reload(deltaTime))
} {
if (!abandon) Attack(deltaTime);
{ }
Move(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 else
{ {
//couldn't equip the item, escape //couldn't equip the item, escape
Escape(deltaTime); //Abandon(deltaTime);
return false; return false;
} }
} }
return true; 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)) 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) if (retreatTarget != null)
{ {
@@ -190,7 +212,7 @@ namespace Barotrauma
} }
else if (!reloadWeaponObjective.CanBeCompleted) else if (!reloadWeaponObjective.CanBeCompleted)
{ {
Escape(deltaTime); Mode = CombatMode.Retreat;
} }
else else
{ {
@@ -249,16 +271,16 @@ namespace Barotrauma
} }
} }
private void Escape(float deltaTime) private void Abandon(float deltaTime)
{ {
abandon = true; abandon = true;
SteeringManager.Reset(); SteeringManager.Reset();
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100; //HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
} }
public override bool IsCompleted() 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 (completed)
{ {
if (Weapon != null) if (Weapon != null)
@@ -270,7 +292,7 @@ namespace Barotrauma
} }
public override bool CanBeCompleted => !abandon && (reloadWeaponObjective == null || reloadWeaponObjective.CanBeCompleted) && (retreatObjective == null || retreatObjective.CanBeCompleted); 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) public override bool IsDuplicate(AIObjective otherObjective)
{ {
@@ -86,7 +86,10 @@ namespace Barotrauma
character.AIController.SteeringManager.Reset(); character.AIController.SteeringManager.Reset();
character.CursorPosition = fs.Position; character.CursorPosition = fs.Position;
character.SetInput(InputType.Aim, false, true); if (extinguisher.Item.RequireAimToUse)
{
character.SetInput(InputType.Aim, false, true);
}
extinguisher.Use(deltaTime, character); extinguisher.Use(deltaTime, character);
if (!targetHull.FireSources.Contains(fs)) if (!targetHull.FireSources.Contains(fs))
@@ -168,13 +168,14 @@ namespace Barotrauma
} }
} }
public Hull FindBestHull() public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null)
{ {
Hull bestHull = character.CurrentHull; Hull bestHull = null;
float bestValue = currenthullSafety; float bestValue = 0;
foreach (Hull hull in Hull.hullList) foreach (Hull hull in Hull.hullList)
{ {
if (hull.Submarine == null) { continue; } if (hull.Submarine == null) { continue; }
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
float hullSafety = 0; float hullSafety = 0;
if (character.Submarine != null && SteeringManager == PathSteering) if (character.Submarine != null && SteeringManager == PathSteering)
{ {
@@ -64,6 +64,7 @@ namespace Barotrauma.Items.Components
attack = new Attack(subElement, item.Name + ", MeleeWeapon"); attack = new Attack(subElement, item.Name + ", MeleeWeapon");
} }
item.IsShootable = true; item.IsShootable = true;
// TODO: should define this in xml if we have melee weapons that don't require aim to use
item.RequireAimToUse = true; item.RequireAimToUse = true;
} }
@@ -58,6 +58,7 @@ namespace Barotrauma.Items.Components
: base(item, element) : base(item, element)
{ {
item.IsShootable = true; item.IsShootable = true;
// TODO: should define this in xml if we have ranged weapons that don't require aim to use
item.RequireAimToUse = true; item.RequireAimToUse = true;
} }
@@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components
} }
} }
item.IsShootable = true; item.IsShootable = true;
// TODO: should define this in xml if we have repair tools that don't require aim to use
item.RequireAimToUse = true; item.RequireAimToUse = true;
InitProjSpecific(element); InitProjSpecific(element);
} }
@@ -533,6 +533,25 @@ namespace Barotrauma
{ {
maxX = Math.Min(maxX, ruin.Area.X - 100.0f); 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) if (minX < 0.0f && maxX > Level.Loaded.Size.X)