(d34fb2097) Major refactoring: - Replace custom logic regarding subobjectives with generic logic. - Ensure that all the objectives follow the same logic, reduce duplicate code where possible.
This commit is contained in:
@@ -152,6 +152,32 @@ namespace Barotrauma
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
|
||||||
|
character.MemState.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void ImpactProjSpecific(float impact, Body body)
|
||||||
|
{
|
||||||
|
float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f);
|
||||||
|
|
||||||
|
if (body.UserData is Limb limb && character.Stun <= 0f)
|
||||||
|
{
|
||||||
|
if (impact > 3.0f) { PlayImpactSound(limb); }
|
||||||
|
}
|
||||||
|
else if (body.UserData is Limb || body == Collider.FarseerBody)
|
||||||
|
{
|
||||||
|
if (!character.IsRemotePlayer && impact > ImpactTolerance)
|
||||||
|
{
|
||||||
|
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Character.Controlled == character)
|
||||||
|
{
|
||||||
|
GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (character.MemState.Count < 1) return;
|
if (character.MemState.Count < 1) return;
|
||||||
|
|
||||||
overrideTargetMovement = Vector2.Zero;
|
overrideTargetMovement = Vector2.Zero;
|
||||||
|
|||||||
@@ -74,17 +74,12 @@ namespace Barotrauma
|
|||||||
public CrewManager(XElement element, bool isSinglePlayer)
|
public CrewManager(XElement element, bool isSinglePlayer)
|
||||||
: this(isSinglePlayer)
|
: this(isSinglePlayer)
|
||||||
{
|
{
|
||||||
if (GameMain.Client != null)
|
if (!isSinglePlayer)
|
||||||
{
|
{
|
||||||
//let the server create random conversations in MP
|
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
if (string.IsNullOrEmpty(text)) { return; }
|
||||||
c.AIController is HumanAIController &&
|
|
||||||
!c.IsDead &&
|
|
||||||
c.SpeechImpediment <= 100.0f);
|
|
||||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
|
||||||
}
|
|
||||||
|
|
||||||
var characterInfo = new CharacterInfo(subElement);
|
var characterInfo = new CharacterInfo(subElement);
|
||||||
characterInfos.Add(characterInfo);
|
characterInfos.Add(characterInfo);
|
||||||
@@ -95,6 +90,7 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void InitProjectSpecific()
|
partial void InitProjectSpecific()
|
||||||
@@ -243,24 +239,27 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public IEnumerable<Character> GetCharacters()
|
public IEnumerable<Character> GetCharacters()
|
||||||
{
|
{
|
||||||
if (characterInfos.Contains(characterInfo))
|
if (character?.Inventory == null) return null;
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
characterInfos.Add(characterInfo);
|
var radioItem = character.Inventory.Items.FirstOrDefault(it => it != null && it.GetComponent<WifiComponent>() != null);
|
||||||
|
if (radioItem == null) return null;
|
||||||
|
if (requireEquipped && !character.HasEquippedItem(radioItem)) return null;
|
||||||
|
|
||||||
|
return radioItem.GetComponent<WifiComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<CharacterInfo> GetCharacterInfos()
|
public IEnumerable<CharacterInfo> GetCharacterInfos()
|
||||||
{
|
{
|
||||||
if (character == null)
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace);
|
//let the server create random conversations in MP
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
characters.Remove(character);
|
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||||
if (removeInfo) characterInfos.Remove(character.Info);
|
c.AIController is HumanAIController &&
|
||||||
|
!c.IsDead &&
|
||||||
|
c.SpeechImpediment <= 100.0f);
|
||||||
|
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCharacter(Character character)
|
public void AddCharacter(Character character)
|
||||||
@@ -634,183 +633,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
characterListBox.BarScroll = roundedPos;
|
characterListBox.BarScroll = roundedPos;
|
||||||
}
|
}
|
||||||
var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox")
|
soundIcon.Visible = !muted && !mutedLocally;
|
||||||
{
|
soundIconDisabled.Visible = muted || mutedLocally;
|
||||||
UserData = character,
|
soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally");
|
||||||
Color = frame.Color,
|
|
||||||
SelectedColor = frame.SelectedColor,
|
|
||||||
HoverColor = frame.HoverColor,
|
|
||||||
ToolTip = characterToolTip
|
|
||||||
};
|
|
||||||
|
|
||||||
var soundIcon = new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) },
|
|
||||||
"GUISoundIcon")
|
|
||||||
{
|
|
||||||
UserData = "soundicon",
|
|
||||||
CanBeFocused = false,
|
|
||||||
Visible = true
|
|
||||||
};
|
|
||||||
soundIcon.Color = new Color(soundIcon.Color, 0.0f);
|
|
||||||
new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) },
|
|
||||||
"GUISoundIconDisabled")
|
|
||||||
{
|
|
||||||
UserData = "soundicondisabled",
|
|
||||||
CanBeFocused = true,
|
|
||||||
Visible = false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isSinglePlayer)
|
|
||||||
{
|
|
||||||
characterArea.OnClicked = CharacterClicked;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
characterArea.CanBeFocused = false;
|
|
||||||
characterArea.CanBeSelected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var characterImage = new GUICustomComponent(new RectTransform(new Point(characterArea.Rect.Height), characterArea.RectTransform, Anchor.CenterLeft),
|
|
||||||
onDraw: (sb, component) => character.Info.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
|
|
||||||
{
|
|
||||||
CanBeFocused = false,
|
|
||||||
HoverColor = Color.White,
|
|
||||||
SelectedColor = Color.White,
|
|
||||||
ToolTip = characterToolTip
|
|
||||||
};
|
|
||||||
|
|
||||||
var characterName = new GUITextBlock(new RectTransform(new Point(characterArea.Rect.Width - characterImage.Rect.Width - soundIcon.Rect.Width - 10, characterArea.Rect.Height),
|
|
||||||
characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(soundIcon.Rect.Width + 10, 0) },
|
|
||||||
character.Name, textColor: frame.Color, font: GUI.SmallFont, wrap: true)
|
|
||||||
{
|
|
||||||
Color = frame.Color,
|
|
||||||
HoverColor = Color.Transparent,
|
|
||||||
SelectedColor = Color.Transparent,
|
|
||||||
CanBeFocused = false,
|
|
||||||
ToolTip = characterToolTip,
|
|
||||||
AutoScale = true
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------- order buttons ----------------
|
|
||||||
|
|
||||||
var orderButtonFrame = new GUILayoutGroup(new RectTransform(new Point(100, frame.Rect.Height), frame.RectTransform)
|
|
||||||
{ AbsoluteOffset = new Point(characterInfoWidth + spacing, 0) },
|
|
||||||
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
|
||||||
{
|
|
||||||
AbsoluteSpacing = (int)(10 * GUI.Scale),
|
|
||||||
UserData = "orderbuttons",
|
|
||||||
CanBeFocused = false
|
|
||||||
};
|
|
||||||
|
|
||||||
//listbox for holding the orders inappropriate for this character
|
|
||||||
//(so we can easily toggle their visibility)
|
|
||||||
var wrongOrderList = new GUIListBox(new RectTransform(new Point(50, orderButtonFrame.Rect.Height), orderButtonFrame.RectTransform), isHorizontal: true, style: null)
|
|
||||||
{
|
|
||||||
ScrollBarEnabled = false,
|
|
||||||
ScrollBarVisible = false,
|
|
||||||
Enabled = false,
|
|
||||||
Spacing = spacing,
|
|
||||||
ClampMouseRectToParent = false
|
|
||||||
};
|
|
||||||
wrongOrderList.Content.ClampMouseRectToParent = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < orders.Count; i++)
|
|
||||||
{
|
|
||||||
var order = orders[i];
|
|
||||||
if (order.TargetAllCharacters) continue;
|
|
||||||
|
|
||||||
RectTransform btnParent = (i >= correctOrderCount + neutralOrderCount) ?
|
|
||||||
wrongOrderList.Content.RectTransform :
|
|
||||||
orderButtonFrame.RectTransform;
|
|
||||||
|
|
||||||
var btn = new GUIButton(new RectTransform(new Point(iconSize, iconSize), btnParent, Anchor.CenterLeft),
|
|
||||||
style: null)
|
|
||||||
{
|
|
||||||
UserData = order
|
|
||||||
};
|
|
||||||
|
|
||||||
new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow")
|
|
||||||
{
|
|
||||||
Color = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.8f,
|
|
||||||
HoverColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 1.0f,
|
|
||||||
PressedColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.6f,
|
|
||||||
UserData = "selected",
|
|
||||||
CanBeFocused = false,
|
|
||||||
Visible = false
|
|
||||||
};
|
|
||||||
|
|
||||||
var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite);
|
|
||||||
img.Scale = iconSize / (float)img.SourceRect.Width;
|
|
||||||
img.Color = Color.Lerp(order.Color, frame.Color, 0.5f);
|
|
||||||
img.ToolTip = order.Name;
|
|
||||||
img.HoverColor = Color.Lerp(img.Color, Color.White, 0.5f);
|
|
||||||
|
|
||||||
btn.OnClicked += (GUIButton button, object userData) =>
|
|
||||||
{
|
|
||||||
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false;
|
|
||||||
|
|
||||||
if (btn.GetChildByUserData("selected").Visible)
|
|
||||||
{
|
|
||||||
SetCharacterOrder(character, Order.PrefabList.Find(o => o.AITag == "dismissed"), null, Character.Controlled);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (order.ItemComponentType != null || order.ItemIdentifiers.Length > 0 || order.Options.Length > 1)
|
|
||||||
{
|
|
||||||
CreateOrderTargetFrame(button, character, order);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetCharacterOrder(character, order, null, Character.Controlled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
btn.UserData = order;
|
|
||||||
btn.ToolTip = order.Name;
|
|
||||||
|
|
||||||
//divider between different groups of orders
|
|
||||||
if (i == correctOrderCount - 1 || i == correctOrderCount + neutralOrderCount - 1)
|
|
||||||
{
|
|
||||||
//TODO: divider sprite
|
|
||||||
new GUIFrame(new RectTransform(new Point(8, iconSize), orderButtonFrame.RectTransform), style: "GUIButton");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var toggleWrongOrderBtn = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform),
|
|
||||||
"", style: "UIToggleButton")
|
|
||||||
{
|
|
||||||
UserData = "togglewrongorder",
|
|
||||||
CanBeFocused = false
|
|
||||||
};
|
|
||||||
|
|
||||||
wrongOrderList.RectTransform.NonScaledSize = new Point(
|
|
||||||
wrongOrderList.Content.Children.Sum(c => c.Rect.Width + wrongOrderList.Spacing),
|
|
||||||
wrongOrderList.RectTransform.NonScaledSize.Y);
|
|
||||||
wrongOrderList.RectTransform.SetAsLastChild();
|
|
||||||
|
|
||||||
new GUIFrame(new RectTransform(new Point(
|
|
||||||
wrongOrderList.Rect.Width - toggleWrongOrderBtn.Rect.Width - wrongOrderList.Spacing * 2,
|
|
||||||
wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform),
|
|
||||||
style: null)
|
|
||||||
{
|
|
||||||
CanBeFocused = false
|
|
||||||
};
|
|
||||||
|
|
||||||
//scale to fit the content
|
|
||||||
orderButtonFrame.RectTransform.NonScaledSize = new Point(
|
|
||||||
orderButtonFrame.Children.Sum(c => c.Rect.Width + orderButtonFrame.AbsoluteSpacing),
|
|
||||||
orderButtonFrame.RectTransform.NonScaledSize.Y);
|
|
||||||
|
|
||||||
frame.RectTransform.NonScaledSize = new Point(
|
|
||||||
characterInfoWidth + spacing + (orderButtonFrame.Rect.Width - wrongOrderList.Rect.Width),
|
|
||||||
frame.RectTransform.NonScaledSize.Y);
|
|
||||||
|
|
||||||
characterListBox.RectTransform.NonScaledSize = new Point(
|
|
||||||
characterListBox.Content.Children.Max(c => c.Rect.Width) + wrongOrderList.Rect.Width,
|
|
||||||
characterListBox.RectTransform.NonScaledSize.Y);
|
|
||||||
characterListBox.Content.RectTransform.NonScaledSize = characterListBox.RectTransform.NonScaledSize;
|
|
||||||
characterListBox.UpdateScrollBarSize();
|
|
||||||
return frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
||||||
@@ -954,12 +779,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
|
||||||
c.AIController is HumanAIController &&
|
|
||||||
!c.IsDead &&
|
|
||||||
c.SpeechImpediment <= 100.0f);
|
|
||||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
|
||||||
}
|
|
||||||
|
|
||||||
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
|
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
|
||||||
if (IsSinglePlayer)
|
if (IsSinglePlayer)
|
||||||
@@ -1017,23 +836,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//only one target (or an order with no particular targets), just show options
|
||||||
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
|
else
|
||||||
if (IsSinglePlayer)
|
|
||||||
{
|
{
|
||||||
orderGiver?.Speak(
|
orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas)
|
||||||
order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
|
{ AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) },
|
||||||
}
|
isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
||||||
else if (orderGiver != null)
|
|
||||||
{
|
|
||||||
OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver);
|
|
||||||
if (GameMain.Client != null)
|
|
||||||
{
|
{
|
||||||
GameMain.Client.SendChatMessage(msg);
|
UserData = character,
|
||||||
}
|
Stretch = true
|
||||||
}
|
};
|
||||||
DisplayCharacterOrder(character, order);
|
//line connecting the order button to the option buttons
|
||||||
}
|
//TODO: sprite
|
||||||
|
new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), orderTargetFrame.RectTransform), style: null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create the UI panel that's used to select the target and options for a given order
|
/// Create the UI panel that's used to select the target and options for a given order
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private GUILayoutGroup subPreviewContainer;
|
private GUILayoutGroup subPreviewContainer;
|
||||||
|
|
||||||
|
private GUILayoutGroup subPreviewContainer;
|
||||||
|
|
||||||
private GUIButton loadGameButton;
|
private GUIButton loadGameButton;
|
||||||
|
|
||||||
public Action<Submarine, string, string> StartNewGame;
|
public Action<Submarine, string, string> StartNewGame;
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: go through AIOperate methods where subobjectives are added and ensure that they add the subobjectives correctly -> use TryAddSubObjective method instead?
|
||||||
public void AddSubObjective(AIObjective objective)
|
public void AddSubObjective(AIObjective objective)
|
||||||
{
|
{
|
||||||
if (subObjectives.Any(o => o.IsDuplicate(objective))) { return; }
|
if (subObjectives.Any(o => o.IsDuplicate(objective))) { return; }
|
||||||
@@ -156,10 +157,11 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the objective already is created and added in subobjectives. If not, creates it. Handles objectives that cannot be completed.
|
/// Checks if the objective already is created and added in subobjectives. If not, creates it.
|
||||||
|
/// Handles objectives that cannot be completed. If the objective has been removed form the subobjectives, a null value is assigned to the reference.
|
||||||
/// Returns true if the objective was created.
|
/// Returns true if the objective was created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool TryAddSubObjective<T>(T objective, Func<T> constructor, Action onAbandon = null) where T : AIObjective
|
protected bool TryAddSubObjective<T>(ref T objective, Func<T> constructor, Action onAbandon = null) where T : AIObjective
|
||||||
{
|
{
|
||||||
if (objective != null)
|
if (objective != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,12 +44,17 @@ namespace Barotrauma
|
|||||||
return _weaponComponent;
|
return _weaponComponent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly AIObjectiveFindSafety findSafety;
|
||||||
|
private readonly HashSet<RangedWeapon> rangedWeapons = new HashSet<RangedWeapon>();
|
||||||
|
private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
|
||||||
|
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
|
||||||
|
|
||||||
private AIObjectiveContainItem reloadWeaponObjective;
|
private AIObjectiveContainItem reloadWeaponObjective;
|
||||||
private Hull retreatTarget;
|
|
||||||
private AIObjectiveGoTo retreatObjective;
|
private AIObjectiveGoTo retreatObjective;
|
||||||
private AIObjectiveFindSafety findSafety;
|
|
||||||
private AIObjectiveGoTo followTargetObjective;
|
private AIObjectiveGoTo followTargetObjective;
|
||||||
|
|
||||||
|
private Hull retreatTarget;
|
||||||
private float coolDownTimer;
|
private float coolDownTimer;
|
||||||
|
|
||||||
public enum CombatMode
|
public enum CombatMode
|
||||||
@@ -105,17 +110,13 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Mode = CombatMode.Retreat;
|
Mode = CombatMode.Retreat;
|
||||||
}
|
}
|
||||||
else if (Equip(deltaTime))
|
if (Equip())
|
||||||
{
|
{
|
||||||
if (Reload(deltaTime))
|
if (Reload(deltaTime))
|
||||||
{
|
{
|
||||||
Attack(deltaTime);
|
Attack(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Mode = CombatMode.Retreat;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CombatMode.Retreat:
|
case CombatMode.Retreat:
|
||||||
break;
|
break;
|
||||||
@@ -140,9 +141,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashSet<RangedWeapon> rangedWeapons = new HashSet<RangedWeapon>();
|
|
||||||
private HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
|
|
||||||
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
|
|
||||||
private Item GetWeapon()
|
private Item GetWeapon()
|
||||||
{
|
{
|
||||||
rangedWeapons.Clear();
|
rangedWeapons.Clear();
|
||||||
@@ -150,7 +148,6 @@ namespace Barotrauma
|
|||||||
adHocWeapons.Clear();
|
adHocWeapons.Clear();
|
||||||
Item weapon = null;
|
Item weapon = null;
|
||||||
_weaponComponent = null;
|
_weaponComponent = null;
|
||||||
|
|
||||||
foreach (var item in character.Inventory.Items)
|
foreach (var item in character.Inventory.Items)
|
||||||
{
|
{
|
||||||
if (item == null) { continue; }
|
if (item == null) { continue; }
|
||||||
@@ -220,7 +217,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Equip(float deltaTime)
|
private bool Equip()
|
||||||
{
|
{
|
||||||
if (!character.SelectedItems.Contains(Weapon))
|
if (!character.SelectedItems.Contains(Weapon))
|
||||||
{
|
{
|
||||||
@@ -231,7 +228,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//couldn't equip the item -> escape
|
Mode = CombatMode.Retreat;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,7 +367,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
||||||
}
|
}
|
||||||
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionItemBlocking;
|
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall;
|
||||||
var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
|
var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
|
||||||
if (pickedBody != null)
|
if (pickedBody != null)
|
||||||
{
|
{
|
||||||
@@ -410,7 +407,11 @@ namespace Barotrauma
|
|||||||
return completed;
|
return completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) &&
|
||||||
|
(followTargetObjective == null || followTargetObjective.CanBeCompleted);
|
||||||
|
|
||||||
public override float GetPriority() => (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
public override float GetPriority() => (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
|
|||||||
+35
-53
@@ -9,19 +9,16 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
public override string DebugTag => "contain item";
|
public override string DebugTag => "contain item";
|
||||||
|
|
||||||
public int MinContainedAmount = 1;
|
|
||||||
|
|
||||||
//can either be a tag or an identifier
|
|
||||||
private string[] itemIdentifiers;
|
|
||||||
|
|
||||||
private ItemContainer container;
|
|
||||||
|
|
||||||
private bool isCompleted;
|
|
||||||
|
|
||||||
public string[] ignoredContainerIdentifiers;
|
|
||||||
|
|
||||||
public Func<Item, float> GetItemPriority;
|
public Func<Item, float> GetItemPriority;
|
||||||
|
|
||||||
|
public int MinContainedAmount = 1;
|
||||||
|
public string[] ignoredContainerIdentifiers;
|
||||||
|
|
||||||
|
//can either be a tag or an identifier
|
||||||
|
private readonly string[] itemIdentifiers;
|
||||||
|
private readonly ItemContainer container;
|
||||||
|
|
||||||
|
private bool isCompleted;
|
||||||
private AIObjectiveGetItem getItemObjective;
|
private AIObjectiveGetItem getItemObjective;
|
||||||
private AIObjectiveGoTo goToObjective;
|
private AIObjectiveGoTo goToObjective;
|
||||||
|
|
||||||
@@ -44,28 +41,16 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
if (isCompleted) return true;
|
if (isCompleted) { return true; }
|
||||||
|
|
||||||
int containedItemCount = 0;
|
int containedItemCount = 0;
|
||||||
foreach (Item item in container.Inventory.Items)
|
foreach (Item item in container.Inventory.Items)
|
||||||
{
|
{
|
||||||
if (item != null && itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) containedItemCount++;
|
if (item != null && itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id)))
|
||||||
}
|
|
||||||
|
|
||||||
return containedItemCount >= MinContainedAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanBeCompleted
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (goToObjective != null)
|
|
||||||
{
|
{
|
||||||
return goToObjective.CanBeCompleted;
|
containedItemCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getItemObjective == null || getItemObjective.CanBeCompleted;
|
|
||||||
}
|
}
|
||||||
|
return containedItemCount >= MinContainedAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
@@ -74,69 +59,66 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return AIObjectiveManager.OrderPriority;
|
return AIObjectiveManager.OrderPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
if (isCompleted) return;
|
if (isCompleted) { return; }
|
||||||
|
|
||||||
//get the item that should be contained
|
//get the item that should be contained
|
||||||
Item itemToContain = null;
|
Item itemToContain = null;
|
||||||
foreach (string identifier in itemIdentifiers)
|
foreach (string identifier in itemIdentifiers)
|
||||||
{
|
{
|
||||||
itemToContain = character.Inventory.FindItemByIdentifier(identifier) ?? character.Inventory.FindItemByTag(identifier);
|
itemToContain = character.Inventory.FindItemByIdentifier(identifier) ?? character.Inventory.FindItemByTag(identifier);
|
||||||
if (itemToContain != null && itemToContain.Condition > 0.0f) break;
|
if (itemToContain != null && itemToContain.Condition > 0.0f) { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemToContain == null)
|
if (itemToContain == null)
|
||||||
{
|
{
|
||||||
getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager)
|
TryAddSubObjective(ref getItemObjective, () =>
|
||||||
{
|
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager)
|
||||||
GetItemPriority = GetItemPriority,
|
{
|
||||||
ignoredContainerIdentifiers = ignoredContainerIdentifiers
|
GetItemPriority = GetItemPriority,
|
||||||
};
|
ignoredContainerIdentifiers = ignoredContainerIdentifiers
|
||||||
AddSubObjective(getItemObjective);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.Item.ParentInventory == character.Inventory)
|
if (container.Item.ParentInventory == character.Inventory)
|
||||||
{
|
{
|
||||||
var containedItems = container.Inventory.Items;
|
var containedItems = container.Inventory.Items;
|
||||||
//if there's already something in the mask (empty oxygen tank?), drop it
|
//if there's already something in the mask (empty oxygen tank?), drop it
|
||||||
var existingItem = containedItems.FirstOrDefault(i => i != null);
|
var existingItem = containedItems.FirstOrDefault(i => i != null);
|
||||||
if (existingItem != null) existingItem.Drop(character);
|
if (existingItem != null)
|
||||||
|
{
|
||||||
|
existingItem.Drop(character);
|
||||||
|
}
|
||||||
character.Inventory.RemoveItem(itemToContain);
|
character.Inventory.RemoveItem(itemToContain);
|
||||||
container.Inventory.TryPutItem(itemToContain, null);
|
container.Inventory.TryPutItem(itemToContain, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (container.Item.CurrentHull != character.CurrentHull || (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance && !container.Item.IsInsideTrigger(character.WorldPosition)))
|
if (container.Item.CurrentHull != character.CurrentHull ||
|
||||||
|
(Vector2.DistanceSquared(character.Position, container.Item.Position) > Math.Pow(container.Item.InteractDistance, 2) && !container.Item.IsInsideTrigger(character.WorldPosition)))
|
||||||
{
|
{
|
||||||
goToObjective = new AIObjectiveGoTo(container.Item, character, objectiveManager);
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager));
|
||||||
AddSubObjective(goToObjective);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
container.Combine(itemToContain);
|
container.Combine(itemToContain);
|
||||||
}
|
}
|
||||||
|
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveContainItem objective = otherObjective as AIObjectiveContainItem;
|
if (!(otherObjective is AIObjectiveContainItem objective)) { return false; }
|
||||||
if (objective == null) return false;
|
if (objective.container != container) { return false; }
|
||||||
if (objective.container != container) return false;
|
if (objective.itemIdentifiers.Length != itemIdentifiers.Length) { return false; }
|
||||||
if (objective.itemIdentifiers.Length != itemIdentifiers.Length) return false;
|
|
||||||
|
|
||||||
for (int i = 0; i < itemIdentifiers.Length; i++)
|
for (int i = 0; i < itemIdentifiers.Length; i++)
|
||||||
{
|
{
|
||||||
if (objective.itemIdentifiers[i] != itemIdentifiers[i]) return false;
|
if (objective.itemIdentifiers[i] != itemIdentifiers[i])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-44
@@ -8,18 +8,15 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
public override string DebugTag => "decontain item";
|
public override string DebugTag => "decontain item";
|
||||||
|
|
||||||
//can either be a tag or an identifier
|
|
||||||
private string[] itemIdentifiers;
|
|
||||||
|
|
||||||
private ItemContainer container;
|
|
||||||
|
|
||||||
private bool isCompleted;
|
|
||||||
|
|
||||||
public Func<Item, float> GetItemPriority;
|
public Func<Item, float> GetItemPriority;
|
||||||
|
|
||||||
private AIObjectiveGetItem getItemObjective;
|
//can either be a tag or an identifier
|
||||||
|
private readonly string[] itemIdentifiers;
|
||||||
|
private readonly ItemContainer container;
|
||||||
|
private readonly Item targetItem;
|
||||||
|
|
||||||
private AIObjectiveGoTo goToObjective;
|
private AIObjectiveGoTo goToObjective;
|
||||||
private Item targetItem;
|
private bool isCompleted;
|
||||||
|
|
||||||
public AIObjectiveDecontainItem(Character character, Item targetItem, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
public AIObjectiveDecontainItem(Character character, Item targetItem, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||||
: base(character, objectiveManager, priorityModifier)
|
: base(character, objectiveManager, priorityModifier)
|
||||||
@@ -40,27 +37,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container = container;
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted() => isCompleted;
|
||||||
{
|
|
||||||
return isCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanBeCompleted
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (goToObjective != null)
|
|
||||||
{
|
|
||||||
return goToObjective.CanBeCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getItemObjective == null || getItemObjective.CanBeCompleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
{
|
{
|
||||||
@@ -68,16 +48,13 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return AIObjectiveManager.OrderPriority;
|
return AIObjectiveManager.OrderPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
if (isCompleted) return;
|
if (isCompleted) { return; }
|
||||||
|
|
||||||
Item itemToDecontain = null;
|
Item itemToDecontain = null;
|
||||||
|
|
||||||
//get the item that should be de-contained
|
//get the item that should be de-contained
|
||||||
if (targetItem == null)
|
if (targetItem == null)
|
||||||
{
|
{
|
||||||
@@ -86,7 +63,7 @@ namespace Barotrauma
|
|||||||
foreach (string identifier in itemIdentifiers)
|
foreach (string identifier in itemIdentifiers)
|
||||||
{
|
{
|
||||||
itemToDecontain = container.Inventory.FindItemByIdentifier(identifier) ?? container.Inventory.FindItemByTag(identifier);
|
itemToDecontain = container.Inventory.FindItemByIdentifier(identifier) ?? container.Inventory.FindItemByTag(identifier);
|
||||||
if (itemToDecontain != null) break;
|
if (itemToDecontain != null) { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,38 +71,32 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
itemToDecontain = targetItem;
|
itemToDecontain = targetItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemToDecontain == null || itemToDecontain.Container != container.Item) // Item not found or already de-contained, consider complete
|
if (itemToDecontain == null || itemToDecontain.Container != container.Item) // Item not found or already de-contained, consider complete
|
||||||
{
|
{
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemToDecontain.OwnInventory != character.Inventory && itemToDecontain.ParentInventory != character.Inventory)
|
if (itemToDecontain.OwnInventory != character.Inventory && itemToDecontain.ParentInventory != character.Inventory)
|
||||||
{
|
{
|
||||||
if (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance
|
if (Vector2.DistanceSquared(character.Position, container.Item.Position) > MathUtils.Pow(container.Item.InteractDistance, 2) && !container.Item.IsInsideTrigger(character.WorldPosition))
|
||||||
&& !container.Item.IsInsideTrigger(character.WorldPosition))
|
|
||||||
{
|
{
|
||||||
goToObjective = new AIObjectiveGoTo(container.Item, character, objectiveManager);
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager));
|
||||||
AddSubObjective(goToObjective);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemToDecontain.Drop(character);
|
itemToDecontain.Drop(character);
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveDecontainItem decontainItem = otherObjective as AIObjectiveDecontainItem;
|
if (!(otherObjective is AIObjectiveDecontainItem decontainItem)) { return false; }
|
||||||
if (decontainItem == null) return false;
|
|
||||||
if (decontainItem.itemIdentifiers != null && itemIdentifiers != null)
|
if (decontainItem.itemIdentifiers != null && itemIdentifiers != null)
|
||||||
{
|
{
|
||||||
if (decontainItem.itemIdentifiers.Length != itemIdentifiers.Length) return false;
|
if (decontainItem.itemIdentifiers.Length != itemIdentifiers.Length) { return false; }
|
||||||
for (int i = 0; i < decontainItem.itemIdentifiers.Length; i++)
|
for (int i = 0; i < decontainItem.itemIdentifiers.Length; i++)
|
||||||
{
|
{
|
||||||
if (decontainItem.itemIdentifiers[i] != itemIdentifiers[i]) return false;
|
if (decontainItem.itemIdentifiers[i] != itemIdentifiers[i]) { return false; }
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -133,7 +104,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return decontainItem.targetItem == targetItem;
|
return decontainItem.targetItem == targetItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-85
@@ -13,12 +13,10 @@ namespace Barotrauma
|
|||||||
public override bool ForceRun => true;
|
public override bool ForceRun => true;
|
||||||
public override bool KeepDivingGearOn => true;
|
public override bool KeepDivingGearOn => true;
|
||||||
|
|
||||||
private Hull targetHull;
|
private readonly Hull targetHull;
|
||||||
|
|
||||||
private AIObjectiveGetItem getExtinguisherObjective;
|
private AIObjectiveGetItem getExtinguisherObjective;
|
||||||
|
|
||||||
private AIObjectiveGoTo gotoObjective;
|
private AIObjectiveGoTo gotoObjective;
|
||||||
|
|
||||||
private float useExtinquisherTimer;
|
private float useExtinquisherTimer;
|
||||||
|
|
||||||
public AIObjectiveExtinguishFire(Character character, Hull targetHull, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
public AIObjectiveExtinguishFire(Character character, Hull targetHull, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||||
@@ -40,111 +38,91 @@ namespace Barotrauma
|
|||||||
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1));
|
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted() => targetHull.FireSources.None();
|
||||||
{
|
|
||||||
return targetHull.FireSources.Count == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveExtinguishFire otherExtinguishFire && otherExtinguishFire.targetHull == targetHull;
|
||||||
{
|
|
||||||
var otherExtinguishFire = otherObjective as AIObjectiveExtinguishFire;
|
|
||||||
return otherExtinguishFire != null && otherExtinguishFire.targetHull == targetHull;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanBeCompleted =>
|
|
||||||
(getExtinguisherObjective == null || getExtinguisherObjective.IsCompleted() || getExtinguisherObjective.CanBeCompleted) &&
|
|
||||||
(gotoObjective == null || gotoObjective.IsCompleted() || gotoObjective.CanBeCompleted);
|
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
var extinguisherItem = character.Inventory.FindItemByIdentifier("extinguisher") ?? character.Inventory.FindItemByTag("extinguisher");
|
var extinguisherItem = character.Inventory.FindItemByIdentifier("extinguisher") ?? character.Inventory.FindItemByTag("extinguisher");
|
||||||
if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
|
if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
|
||||||
{
|
{
|
||||||
if (getExtinguisherObjective == null)
|
TryAddSubObjective(ref getExtinguisherObjective, () =>
|
||||||
{
|
{
|
||||||
character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
|
character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
|
||||||
getExtinguisherObjective = new AIObjectiveGetItem(character, "extinguisher", objectiveManager, equip: true);
|
return new AIObjectiveGetItem(character, "extinguisher", objectiveManager, equip: true);
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
getExtinguisherObjective.TryComplete(deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
var extinguisher = extinguisherItem.GetComponent<RepairTool>();
|
|
||||||
if (extinguisher == null)
|
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("AIObjectiveExtinguishFire failed - the item \"" + extinguisherItem + "\" has no RepairTool component but is tagged as an extinguisher");
|
var extinguisher = extinguisherItem.GetComponent<RepairTool>();
|
||||||
return;
|
if (extinguisher == null)
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FireSource fs in targetHull.FireSources)
|
|
||||||
{
|
|
||||||
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
|
||||||
bool move = !inRange;
|
|
||||||
if (inRange || useExtinquisherTimer > 0.0f)
|
|
||||||
{
|
{
|
||||||
useExtinquisherTimer += deltaTime;
|
#if DEBUG
|
||||||
if (useExtinquisherTimer > 2.0f)
|
DebugConsole.ThrowError("AIObjectiveExtinguishFire failed - the item \"" + extinguisherItem + "\" has no RepairTool component but is tagged as an extinguisher");
|
||||||
|
#endif
|
||||||
|
abandon = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (FireSource fs in targetHull.FireSources)
|
||||||
|
{
|
||||||
|
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
||||||
|
bool move = !inRange;
|
||||||
|
if (inRange || useExtinquisherTimer > 0.0f)
|
||||||
{
|
{
|
||||||
useExtinquisherTimer = 0.0f;
|
useExtinquisherTimer += deltaTime;
|
||||||
}
|
if (useExtinquisherTimer > 2.0f)
|
||||||
character.AIController.SteeringManager.Reset();
|
|
||||||
character.CursorPosition = fs.Position;
|
|
||||||
if (extinguisher.Item.RequireAimToUse)
|
|
||||||
{
|
|
||||||
bool isOperatingButtons = false;
|
|
||||||
if (SteeringManager == PathSteering)
|
|
||||||
{
|
{
|
||||||
var door = PathSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
|
useExtinquisherTimer = 0.0f;
|
||||||
if (door != null && !door.IsOpen)
|
}
|
||||||
|
character.AIController.SteeringManager.Reset();
|
||||||
|
character.CursorPosition = fs.Position;
|
||||||
|
if (extinguisher.Item.RequireAimToUse)
|
||||||
|
{
|
||||||
|
bool isOperatingButtons = false;
|
||||||
|
if (SteeringManager == PathSteering)
|
||||||
{
|
{
|
||||||
isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents<Controller>(true).Any();
|
var door = PathSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
|
||||||
|
if (door != null && !door.IsOpen)
|
||||||
|
{
|
||||||
|
isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents<Controller>(true).Any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isOperatingButtons)
|
||||||
|
{
|
||||||
|
character.SetInput(InputType.Aim, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isOperatingButtons)
|
Limb sightLimb = null;
|
||||||
|
if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.RightHand))
|
||||||
{
|
{
|
||||||
character.SetInput(InputType.Aim, false, true);
|
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
||||||
|
}
|
||||||
|
else if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.LeftHand))
|
||||||
|
{
|
||||||
|
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||||
|
}
|
||||||
|
if (!character.CanSeeTarget(fs, sightLimb))
|
||||||
|
{
|
||||||
|
move = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
move = false;
|
||||||
|
extinguisher.Use(deltaTime, character);
|
||||||
|
if (!targetHull.FireSources.Contains(fs))
|
||||||
|
{
|
||||||
|
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Limb sightLimb = null;
|
if (move)
|
||||||
if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.RightHand))
|
|
||||||
{
|
{
|
||||||
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
//go to the first firesource
|
||||||
}
|
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(fs.Position), character, objectiveManager));
|
||||||
else if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.LeftHand))
|
|
||||||
{
|
|
||||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
|
||||||
}
|
|
||||||
if (!character.CanSeeTarget(fs, sightLimb))
|
|
||||||
{
|
|
||||||
move = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
move = false;
|
|
||||||
extinguisher.Use(deltaTime, character);
|
|
||||||
if (!targetHull.FireSources.Contains(fs))
|
|
||||||
{
|
|
||||||
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (move)
|
|
||||||
{
|
|
||||||
//go to the first firesource
|
|
||||||
if (gotoObjective == null || !gotoObjective.CanBeCompleted || gotoObjective.IsCompleted())
|
|
||||||
{
|
|
||||||
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(fs.Position), character, objectiveManager);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gotoObjective.TryComplete(deltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-36
@@ -1,6 +1,7 @@
|
|||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -9,8 +10,10 @@ namespace Barotrauma
|
|||||||
public override string DebugTag => "find diving gear";
|
public override string DebugTag => "find diving gear";
|
||||||
public override bool ForceRun => true;
|
public override bool ForceRun => true;
|
||||||
|
|
||||||
private AIObjective subObjective;
|
private readonly string gearTag;
|
||||||
private string gearTag;
|
|
||||||
|
private AIObjectiveGetItem getDivingGear;
|
||||||
|
private AIObjectiveContainItem getOxygen;
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
@@ -21,14 +24,15 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
var containedItems = character.Inventory.Items[i].ContainedItems;
|
var containedItems = character.Inventory.Items[i].ContainedItems;
|
||||||
if (containedItems == null) { continue; }
|
if (containedItems == null) { continue; }
|
||||||
|
return containedItems.Any(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
|
||||||
var oxygenTank = containedItems.FirstOrDefault(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
|
|
||||||
if (oxygenTank != null) { return true; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override float GetPriority() => MathHelper.Clamp(100 - character.OxygenAvailable, 0, 100);
|
||||||
|
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindDivingGear;
|
||||||
|
|
||||||
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||||
{
|
{
|
||||||
gearTag = needDivingSuit ? "divingsuit" : "diving";
|
gearTag = needDivingSuit ? "divingsuit" : "diving";
|
||||||
@@ -39,45 +43,39 @@ namespace Barotrauma
|
|||||||
var item = character.Inventory.FindItemByTag(gearTag);
|
var item = character.Inventory.FindItemByTag(gearTag);
|
||||||
if (item == null || !character.HasEquippedItem(item))
|
if (item == null || !character.HasEquippedItem(item))
|
||||||
{
|
{
|
||||||
//get a diving mask/suit first
|
TryAddSubObjective(ref getDivingGear, () =>
|
||||||
if (!(subObjective is AIObjectiveGetItem))
|
|
||||||
{
|
{
|
||||||
character.Speak(TextManager.Get("DialogGetDivingGear"), null, 0.0f, "getdivinggear", 30.0f);
|
character.Speak(TextManager.Get("DialogGetDivingGear"), null, 0.0f, "getdivinggear", 30.0f);
|
||||||
subObjective = new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true);
|
return new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (getDivingGear != null) { return; }
|
||||||
|
var containedItems = item.ContainedItems;
|
||||||
|
if (containedItems == null)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError("AIObjectiveFindDivingGear failed - the item \"" + item + "\" has no proper inventory");
|
||||||
|
#endif
|
||||||
|
abandon = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Drop empty tanks
|
||||||
|
foreach (Item containedItem in containedItems)
|
||||||
|
{
|
||||||
|
if (containedItem == null) { continue; }
|
||||||
|
if (containedItem.Condition <= 0.0f)
|
||||||
|
{
|
||||||
|
containedItem.Drop(character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
if (containedItems.None(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f))
|
||||||
{
|
{
|
||||||
var containedItems = item.ContainedItems;
|
TryAddSubObjective(ref getOxygen, () =>
|
||||||
if (containedItems == null) { return; }
|
|
||||||
//check if there's an oxygen tank in the mask/suit
|
|
||||||
foreach (Item containedItem in containedItems)
|
|
||||||
{
|
|
||||||
if (containedItem == null) { continue; }
|
|
||||||
if (containedItem.Condition <= 0.0f)
|
|
||||||
{
|
|
||||||
containedItem.Drop(character);
|
|
||||||
}
|
|
||||||
else if (containedItem.Prefab.Identifier == "oxygentank" || containedItem.HasTag("oxygensource"))
|
|
||||||
{
|
|
||||||
//we've got an oxygen source inside the mask/suit, all good
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
|
|
||||||
{
|
{
|
||||||
character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
|
character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
|
||||||
subObjective = new AIObjectiveContainItem(character, new string[] { "oxygentank", "oxygensource" }, item.GetComponent<ItemContainer>(), objectiveManager);
|
return new AIObjectiveContainItem(character, new string[] { "oxygentank", "oxygensource" }, item.GetComponent<ItemContainer>(), objectiveManager);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
if (subObjective != null)
|
|
||||||
{
|
|
||||||
subObjective.TryComplete(deltaTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted;
|
|
||||||
public override float GetPriority() => MathHelper.Clamp(100 - character.OxygenAvailable, 0, 100);
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindDivingGear;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-137
@@ -18,7 +18,7 @@ namespace Barotrauma
|
|||||||
const float SearchHullInterval = 3.0f;
|
const float SearchHullInterval = 3.0f;
|
||||||
const float clearUnreachableInterval = 30;
|
const float clearUnreachableInterval = 30;
|
||||||
|
|
||||||
public readonly List<Hull> unreachable = new List<Hull>();
|
public readonly HashSet<Hull> unreachable = new HashSet<Hull>();
|
||||||
|
|
||||||
private float currenthullSafety;
|
private float currenthullSafety;
|
||||||
private float unreachableClearTimer;
|
private float unreachableClearTimer;
|
||||||
@@ -32,44 +32,10 @@ namespace Barotrauma
|
|||||||
public override bool IsCompleted() => false;
|
public override bool IsCompleted() => false;
|
||||||
public override bool CanBeCompleted => true;
|
public override bool CanBeCompleted => true;
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindSafety;
|
||||||
|
|
||||||
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
var currentHull = character.AnimController.CurrentHull;
|
|
||||||
if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null)
|
|
||||||
{
|
|
||||||
bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90;
|
|
||||||
bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character);
|
|
||||||
if (!hasEquipment)
|
|
||||||
{
|
|
||||||
divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (divingGearObjective != null)
|
|
||||||
{
|
|
||||||
divingGearObjective.TryComplete(deltaTime);
|
|
||||||
if (divingGearObjective.IsCompleted())
|
|
||||||
{
|
|
||||||
divingGearObjective = null;
|
|
||||||
Priority = 0;
|
|
||||||
}
|
|
||||||
else if (divingGearObjective.CanBeCompleted)
|
|
||||||
{
|
|
||||||
// If diving gear objective is active and can be completed, wait for it to complete.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
divingGearObjective = null;
|
|
||||||
// Reduce the timer so that we get a safe hull target faster.
|
|
||||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
|
||||||
{
|
|
||||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unreachableClearTimer > 0)
|
if (unreachableClearTimer > 0)
|
||||||
{
|
{
|
||||||
unreachableClearTimer -= deltaTime;
|
unreachableClearTimer -= deltaTime;
|
||||||
@@ -79,99 +45,116 @@ namespace Barotrauma
|
|||||||
unreachableClearTimer = clearUnreachableInterval;
|
unreachableClearTimer = clearUnreachableInterval;
|
||||||
unreachable.Clear();
|
unreachable.Clear();
|
||||||
}
|
}
|
||||||
|
if (character.CurrentHull == null)
|
||||||
|
{
|
||||||
|
currenthullSafety = 0;
|
||||||
|
Priority = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold) { Priority = 100; }
|
||||||
|
currenthullSafety = HumanAIController.GetHullSafety(character.CurrentHull);
|
||||||
|
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||||
|
{
|
||||||
|
Priority -= priorityDecrease * deltaTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float dangerFactor = (100 - currenthullSafety) / 100;
|
||||||
|
Priority += dangerFactor * priorityIncrease * deltaTime;
|
||||||
|
}
|
||||||
|
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||||
|
if (divingGearObjective != null && !divingGearObjective.IsCompleted() && divingGearObjective.CanBeCompleted)
|
||||||
|
{
|
||||||
|
Priority = Math.Max(Priority, Math.Min(AIObjectiveManager.OrderPriority + 20, 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Act(float deltaTime)
|
||||||
|
{
|
||||||
|
var currentHull = character.AnimController.CurrentHull;
|
||||||
|
if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null)
|
||||||
|
{
|
||||||
|
bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90;
|
||||||
|
bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character);
|
||||||
|
if (!hasEquipment)
|
||||||
|
{
|
||||||
|
TryAddSubObjective(ref divingGearObjective,
|
||||||
|
() => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
|
||||||
|
onAbandon: () => searchHullTimer = Math.Min(1, searchHullTimer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (divingGearObjective != null) { return; }
|
||||||
|
// Reset the devotion.
|
||||||
|
Priority = 0;
|
||||||
|
if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||||
|
{
|
||||||
|
searchHullTimer = Math.Min(1, searchHullTimer);
|
||||||
|
}
|
||||||
if (searchHullTimer > 0.0f)
|
if (searchHullTimer > 0.0f)
|
||||||
{
|
{
|
||||||
searchHullTimer -= deltaTime;
|
searchHullTimer -= deltaTime;
|
||||||
}
|
}
|
||||||
else if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
else
|
||||||
{
|
{
|
||||||
|
searchHullTimer = SearchHullInterval;
|
||||||
var bestHull = FindBestHull();
|
var bestHull = FindBestHull();
|
||||||
if (bestHull != null && bestHull != currentHull)
|
if (bestHull != null && bestHull != currentHull)
|
||||||
{
|
{
|
||||||
if (goToObjective != null)
|
if (goToObjective?.Target != bestHull)
|
||||||
{
|
{
|
||||||
if (goToObjective.Target != bestHull)
|
goToObjective = null;
|
||||||
|
}
|
||||||
|
TryAddSubObjective(ref goToObjective, () =>
|
||||||
|
new AIObjectiveGoTo(bestHull, character, objectiveManager, getDivingGearIfNeeded: false)
|
||||||
{
|
{
|
||||||
// If we need diving gear, we should already have it, if possible.
|
// If we need diving gear, we should already have it, if possible.
|
||||||
goToObjective = new AIObjectiveGoTo(bestHull, character, objectiveManager, getDivingGearIfNeeded: false)
|
|
||||||
{
|
|
||||||
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
goToObjective = new AIObjectiveGoTo(bestHull, character, objectiveManager, getDivingGearIfNeeded: false)
|
|
||||||
{
|
|
||||||
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
|
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
|
||||||
};
|
}, onAbandon: () => unreachable.Add(goToObjective.Target as Hull));
|
||||||
}
|
|
||||||
}
|
|
||||||
searchHullTimer = SearchHullInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (goToObjective != null)
|
|
||||||
{
|
|
||||||
goToObjective.TryComplete(deltaTime);
|
|
||||||
if (!goToObjective.CanBeCompleted)
|
|
||||||
{
|
|
||||||
if (!unreachable.Contains(goToObjective.Target))
|
|
||||||
{
|
|
||||||
unreachable.Add(goToObjective.Target as Hull);
|
|
||||||
}
|
|
||||||
goToObjective = null;
|
|
||||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
|
||||||
//SteeringManager.SteeringWander();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentHull != null)
|
if (goToObjective != null) { return; }
|
||||||
|
if (currentHull == null) { return; }
|
||||||
|
//goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
|
||||||
|
// -> attempt to manually steer away from hazards
|
||||||
|
Vector2 escapeVel = Vector2.Zero;
|
||||||
|
foreach (FireSource fireSource in currentHull.FireSources)
|
||||||
{
|
{
|
||||||
//goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
|
Vector2 dir = character.Position - fireSource.Position;
|
||||||
// -> attempt to manually steer away from hazards
|
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
|
||||||
Vector2 escapeVel = Vector2.Zero;
|
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||||
foreach (FireSource fireSource in currentHull.FireSources)
|
}
|
||||||
|
foreach (Character enemy in Character.CharacterList)
|
||||||
|
{
|
||||||
|
//don't run from friendly NPCs
|
||||||
|
if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; }
|
||||||
|
//friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
|
||||||
|
if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; }
|
||||||
|
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
|
||||||
|
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
|
||||||
{
|
{
|
||||||
Vector2 dir = character.Position - fireSource.Position;
|
Vector2 dir = character.Position - enemy.Position;
|
||||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
|
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
|
||||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
foreach (Character enemy in Character.CharacterList)
|
if (escapeVel != Vector2.Zero)
|
||||||
|
{
|
||||||
|
//only move if we haven't reached the edge of the room
|
||||||
|
if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) ||
|
||||||
|
(escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50))
|
||||||
{
|
{
|
||||||
//don't run from friendly NPCs
|
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
|
||||||
if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; }
|
|
||||||
//friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
|
|
||||||
if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; }
|
|
||||||
|
|
||||||
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
|
|
||||||
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
|
|
||||||
{
|
|
||||||
Vector2 dir = character.Position - enemy.Position;
|
|
||||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
|
|
||||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escapeVel != Vector2.Zero)
|
|
||||||
{
|
|
||||||
//only move if we haven't reached the edge of the room
|
|
||||||
if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) ||
|
|
||||||
(escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50))
|
|
||||||
{
|
|
||||||
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
|
|
||||||
character.AIController.SteeringManager.Reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
|
||||||
character.AIController.SteeringManager.Reset();
|
character.AIController.SteeringManager.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
character.AIController.SteeringManager.Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null)
|
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null)
|
||||||
@@ -250,36 +233,5 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
return bestHull;
|
return bestHull;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
|
||||||
{
|
|
||||||
return (otherObjective is AIObjectiveFindSafety);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float deltaTime)
|
|
||||||
{
|
|
||||||
if (character.CurrentHull == null)
|
|
||||||
{
|
|
||||||
currenthullSafety = 0;
|
|
||||||
Priority = 100;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold) { Priority = 100; }
|
|
||||||
currenthullSafety = HumanAIController.GetHullSafety(character.CurrentHull);
|
|
||||||
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
|
||||||
{
|
|
||||||
Priority -= priorityDecrease * deltaTime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float dangerFactor = (100 - currenthullSafety) / 100;
|
|
||||||
Priority += dangerFactor * priorityIncrease * deltaTime;
|
|
||||||
}
|
|
||||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
|
||||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted() && divingGearObjective.CanBeCompleted)
|
|
||||||
{
|
|
||||||
Priority = Math.Max(Priority, Math.Min(AIObjectiveManager.OrderPriority + 20, 100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using FarseerPhysics;
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -13,36 +14,31 @@ namespace Barotrauma
|
|||||||
public override bool KeepDivingGearOn => true;
|
public override bool KeepDivingGearOn => true;
|
||||||
public override bool ForceRun => true;
|
public override bool ForceRun => true;
|
||||||
|
|
||||||
private readonly Gap leak;
|
public Gap Leak { get; private set; }
|
||||||
|
|
||||||
private AIObjectiveFindDivingGear findDivingGear;
|
private AIObjectiveFindDivingGear findDivingGear;
|
||||||
|
private AIObjectiveGetItem getWeldingTool;
|
||||||
|
private AIObjectiveContainItem refuelObjective;
|
||||||
private AIObjectiveGoTo gotoObjective;
|
private AIObjectiveGoTo gotoObjective;
|
||||||
private AIObjectiveOperateItem operateObjective;
|
private AIObjectiveOperateItem operateObjective;
|
||||||
|
|
||||||
public Gap Leak
|
|
||||||
{
|
|
||||||
get { return leak; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public AIObjectiveFixLeak(Gap leak, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base (character, objectiveManager, priorityModifier)
|
public AIObjectiveFixLeak(Gap leak, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base (character, objectiveManager, priorityModifier)
|
||||||
{
|
{
|
||||||
this.leak = leak;
|
Leak = leak;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
return leak.Open <= 0.0f || leak.Removed;
|
return Leak.Open <= 0.0f || Leak.Removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanBeCompleted => !abandon && base.CanBeCompleted;
|
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
{
|
{
|
||||||
if (leak.Open == 0.0f) { return 0.0f; }
|
if (Leak.Open == 0.0f) { return 0.0f; }
|
||||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||||
float dist = Math.Abs(character.WorldPosition.X - leak.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - leak.WorldPosition.Y) * 2.0f;
|
float dist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y) * 2.0f;
|
||||||
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 10000, dist));
|
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 10000, dist));
|
||||||
float severity = AIObjectiveFixLeaks.GetLeakSeverity(leak);
|
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak);
|
||||||
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||||
float devotion = Math.Min(Priority, 10) / 100;
|
float devotion = Math.Min(Priority, 10) / 100;
|
||||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + severity * distanceFactor * PriorityModifier, 0, 1));
|
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + severity * distanceFactor * PriorityModifier, 0, 1));
|
||||||
@@ -50,58 +46,64 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveFixLeak fixLeak = otherObjective as AIObjectiveFixLeak;
|
if (!(otherObjective is AIObjectiveFixLeak fixLeak)) { return false; }
|
||||||
if (fixLeak == null) return false;
|
return fixLeak.Leak == Leak;
|
||||||
return fixLeak.leak == leak;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
if (!leak.IsRoomToRoom)
|
if (!Leak.IsRoomToRoom)
|
||||||
{
|
{
|
||||||
if (findDivingGear == null)
|
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, true, objectiveManager));
|
||||||
{
|
if (findDivingGear != null) { return; }
|
||||||
findDivingGear = new AIObjectiveFindDivingGear(character, true, objectiveManager);
|
|
||||||
AddSubObjective(findDivingGear);
|
|
||||||
}
|
|
||||||
else if (!findDivingGear.CanBeCompleted)
|
|
||||||
{
|
|
||||||
abandon = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var weldingTool = character.Inventory.FindItemByTag("weldingtool");
|
var weldingTool = character.Inventory.FindItemByTag("weldingtool");
|
||||||
|
|
||||||
if (weldingTool == null)
|
if (weldingTool == null)
|
||||||
{
|
{
|
||||||
AddSubObjective(new AIObjectiveGetItem(character, "weldingtool", objectiveManager, true));
|
TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingtool", objectiveManager, true));
|
||||||
return;
|
if (getWeldingTool != null) { return; }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var containedItems = weldingTool.ContainedItems;
|
var containedItems = weldingTool.ContainedItems;
|
||||||
if (containedItems == null) return;
|
if (containedItems == null)
|
||||||
|
|
||||||
var fuelTank = containedItems.FirstOrDefault(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f);
|
|
||||||
if (fuelTank == null)
|
|
||||||
{
|
{
|
||||||
AddSubObjective(new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent<ItemContainer>(), objectiveManager));
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no proper inventory");
|
||||||
|
#endif
|
||||||
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Drop empty tanks
|
||||||
|
foreach (Item containedItem in containedItems)
|
||||||
|
{
|
||||||
|
if (containedItem == null) { continue; }
|
||||||
|
if (containedItem.Condition <= 0.0f)
|
||||||
|
{
|
||||||
|
containedItem.Drop(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (containedItems.None(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f))
|
||||||
|
{
|
||||||
|
TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent<ItemContainer>(), objectiveManager));
|
||||||
|
if (refuelObjective != null) { return; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var repairTool = weldingTool.GetComponent<RepairTool>();
|
var repairTool = weldingTool.GetComponent<RepairTool>();
|
||||||
if (repairTool == null) { return; }
|
if (repairTool == null)
|
||||||
|
{
|
||||||
Vector2 gapDiff = leak.WorldPosition - character.WorldPosition;
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
||||||
|
#endif
|
||||||
|
abandon = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
|
||||||
// TODO: use the collider size/reach?
|
// TODO: use the collider size/reach?
|
||||||
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||||
{
|
{
|
||||||
HumanAIController.AnimController.Crouching = true;
|
HumanAIController.AnimController.Crouching = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||||
bool canReach = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
bool canReach = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
||||||
if (canReach)
|
if (canReach)
|
||||||
@@ -115,65 +117,29 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||||
}
|
}
|
||||||
canReach = character.CanSeeTarget(leak, sightLimb);
|
canReach = character.CanSeeTarget(Leak, sightLimb);
|
||||||
}
|
}
|
||||||
else
|
if (!canReach)
|
||||||
{
|
{
|
||||||
if (gotoObjective != null)
|
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach });
|
||||||
{
|
|
||||||
// Check if the objective is already removed -> completed/impossible
|
|
||||||
if (!subObjectives.Contains(gotoObjective))
|
|
||||||
{
|
|
||||||
if (!gotoObjective.CanBeCompleted)
|
|
||||||
{
|
|
||||||
abandon = true;
|
|
||||||
}
|
|
||||||
gotoObjective = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager)
|
|
||||||
{
|
|
||||||
CloseEnough = reach
|
|
||||||
};
|
|
||||||
if (!subObjectives.Contains(gotoObjective))
|
|
||||||
{
|
|
||||||
AddSubObjective(gotoObjective);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (gotoObjective == null || gotoObjective.IsCompleted())
|
if (gotoObjective != null) { return; }
|
||||||
{
|
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
|
||||||
if (operateObjective == null)
|
|
||||||
{
|
|
||||||
operateObjective = new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: leak);
|
|
||||||
AddSubObjective(operateObjective);
|
|
||||||
}
|
|
||||||
else if (!subObjectives.Contains(operateObjective))
|
|
||||||
{
|
|
||||||
operateObjective = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 GetStandPosition()
|
private Vector2 GetStandPosition()
|
||||||
{
|
{
|
||||||
Vector2 standPos = leak.Position;
|
Vector2 standPos = Leak.Position;
|
||||||
var hull = leak.FlowTargetHull;
|
var hull = Leak.FlowTargetHull;
|
||||||
|
if (hull == null) { return standPos; }
|
||||||
if (hull == null) return standPos;
|
if (Leak.IsHorizontal)
|
||||||
|
|
||||||
if (leak.IsHorizontal)
|
|
||||||
{
|
{
|
||||||
standPos += Vector2.UnitX * Math.Sign(hull.Position.X - leak.Position.X) * leak.Rect.Width;
|
standPos += Vector2.UnitX * Math.Sign(hull.Position.X - Leak.Position.X) * Leak.Rect.Width;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
standPos += Vector2.UnitY * Math.Sign(hull.Position.Y - leak.Position.Y) * leak.Rect.Height;
|
standPos += Vector2.UnitY * Math.Sign(hull.Position.Y - Leak.Position.Y) * Leak.Rect.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
return standPos;
|
return standPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
public override string DebugTag => "get item";
|
public override string DebugTag => "get item";
|
||||||
|
|
||||||
|
private readonly bool equip;
|
||||||
|
private readonly HashSet<Item> ignoredItems = new HashSet<Item>();
|
||||||
|
|
||||||
public Func<Item, float> GetItemPriority;
|
public Func<Item, float> GetItemPriority;
|
||||||
|
|
||||||
//can be either tags or identifiers
|
//can be either tags or identifiers
|
||||||
@@ -20,12 +23,6 @@ namespace Barotrauma
|
|||||||
public string[] ignoredContainerIdentifiers;
|
public string[] ignoredContainerIdentifiers;
|
||||||
private AIObjectiveGoTo goToObjective;
|
private AIObjectiveGoTo goToObjective;
|
||||||
private float currItemPriority;
|
private float currItemPriority;
|
||||||
private bool equip;
|
|
||||||
|
|
||||||
private HashSet<Item> ignoredItems = new HashSet<Item>();
|
|
||||||
|
|
||||||
private bool canBeCompleted = true;
|
|
||||||
public override bool CanBeCompleted => canBeCompleted;
|
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
{
|
{
|
||||||
@@ -57,20 +54,15 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckInventory();
|
CheckInventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckInventory()
|
private void CheckInventory()
|
||||||
{
|
{
|
||||||
if (itemIdentifiers == null)
|
if (itemIdentifiers == null) { return; }
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||||
{
|
{
|
||||||
if (character.Inventory.Items[i] == null || character.Inventory.Items[i].Condition <= 0.0f) continue;
|
if (character.Inventory.Items[i] == null || character.Inventory.Items[i].Condition <= 0.0f) { continue; }
|
||||||
if (itemIdentifiers.Any(id => character.Inventory.Items[i].Prefab.Identifier == id || character.Inventory.Items[i].HasTag(id)))
|
if (itemIdentifiers.Any(id => character.Inventory.Items[i].Prefab.Identifier == id || character.Inventory.Items[i].HasTag(id)))
|
||||||
{
|
{
|
||||||
targetItem = character.Inventory.Items[i];
|
targetItem = character.Inventory.Items[i];
|
||||||
@@ -84,7 +76,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (Item containedItem in containedItems)
|
foreach (Item containedItem in containedItems)
|
||||||
{
|
{
|
||||||
if (containedItem == null || containedItem.Condition <= 0.0f) continue;
|
if (containedItem == null || containedItem.Condition <= 0.0f) { continue; }
|
||||||
if (itemIdentifiers.Any(id => containedItem.Prefab.Identifier == id || containedItem.HasTag(id)))
|
if (itemIdentifiers.Any(id => containedItem.Prefab.Identifier == id || containedItem.HasTag(id)))
|
||||||
{
|
{
|
||||||
targetItem = containedItem;
|
targetItem = containedItem;
|
||||||
@@ -103,10 +95,8 @@ namespace Barotrauma
|
|||||||
if (targetItem == null || moveToTarget == null)
|
if (targetItem == null || moveToTarget == null)
|
||||||
{
|
{
|
||||||
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
||||||
//SteeringManager.SteeringWander();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moveToTarget.CurrentHull == character.CurrentHull &&
|
if (moveToTarget.CurrentHull == character.CurrentHull &&
|
||||||
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance, 2))
|
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance, 2))
|
||||||
{
|
{
|
||||||
@@ -116,36 +106,28 @@ namespace Barotrauma
|
|||||||
var pickable = targetItem.GetComponent<Pickable>();
|
var pickable = targetItem.GetComponent<Pickable>();
|
||||||
if (pickable == null)
|
if (pickable == null)
|
||||||
{
|
{
|
||||||
canBeCompleted = false;
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if all the slots required by the item are free
|
//check if all the slots required by the item are free
|
||||||
foreach (InvSlotType slots in pickable.AllowedSlots)
|
foreach (InvSlotType slots in pickable.AllowedSlots)
|
||||||
{
|
{
|
||||||
if (slots.HasFlag(InvSlotType.Any)) continue;
|
if (slots.HasFlag(InvSlotType.Any)) { continue; }
|
||||||
|
|
||||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||||
{
|
{
|
||||||
//slot not needed by the item, continue
|
//slot not needed by the item, continue
|
||||||
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) continue;
|
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; }
|
||||||
|
|
||||||
targetSlot = i;
|
targetSlot = i;
|
||||||
|
|
||||||
//slot free, continue
|
//slot free, continue
|
||||||
if (character.Inventory.Items[i] == null) continue;
|
if (character.Inventory.Items[i] == null) { continue; }
|
||||||
|
|
||||||
//try to move the existing item to LimbSlot.Any and continue if successful
|
//try to move the existing item to LimbSlot.Any and continue if successful
|
||||||
if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any })) continue;
|
if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any })) { continue; }
|
||||||
|
|
||||||
//if everything else fails, simply drop the existing item
|
//if everything else fails, simply drop the existing item
|
||||||
character.Inventory.Items[i].Drop(character);
|
character.Inventory.Items[i].Drop(character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targetItem.TryInteract(character, false, true);
|
targetItem.TryInteract(character, false, true);
|
||||||
|
|
||||||
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
|
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
|
||||||
{
|
{
|
||||||
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
|
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
|
||||||
@@ -161,11 +143,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
//don't attempt to get diving gear to reach the destination if the item we're trying to get is diving gear
|
//don't attempt to get diving gear to reach the destination if the item we're trying to get is diving gear
|
||||||
goToObjective = new AIObjectiveGoTo(moveToTarget, character, objectiveManager, repeat: false, getDivingGearIfNeeded: !gettingDivingGear);
|
goToObjective = new AIObjectiveGoTo(moveToTarget, character, objectiveManager, repeat: false, getDivingGearIfNeeded: !gettingDivingGear);
|
||||||
|
if (!subObjectives.Contains(goToObjective))
|
||||||
|
{
|
||||||
|
AddSubObjective(goToObjective);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (goToObjective != null && !goToObjective.CanBeCompleted)
|
||||||
goToObjective.TryComplete(deltaTime);
|
|
||||||
if (!goToObjective.CanBeCompleted)
|
|
||||||
{
|
{
|
||||||
|
goToObjective = null;
|
||||||
targetItem = null;
|
targetItem = null;
|
||||||
moveToTarget = null;
|
moveToTarget = null;
|
||||||
ignoredItems.Add(targetItem);
|
ignoredItems.Add(targetItem);
|
||||||
@@ -185,24 +170,20 @@ namespace Barotrauma
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item, because neither identifiers nor item is was defined.");
|
DebugConsole.NewMessage($"{character.Name}: Cannot find the item, because neither identifiers nor item is was defined.");
|
||||||
#endif
|
#endif
|
||||||
canBeCompleted = false;
|
abandon = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 1; i++)
|
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 1; i++)
|
||||||
{
|
{
|
||||||
currSearchIndex++;
|
currSearchIndex++;
|
||||||
|
|
||||||
var item = Item.ItemList[currSearchIndex];
|
var item = Item.ItemList[currSearchIndex];
|
||||||
if (ignoredItems.Contains(item)) { continue; }
|
if (ignoredItems.Contains(item)) { continue; }
|
||||||
if (item.Submarine == null) { continue; }
|
if (item.Submarine == null) { continue; }
|
||||||
else if (item.Submarine.TeamID != character.TeamID) { continue; }
|
else if (item.Submarine.TeamID != character.TeamID) { continue; }
|
||||||
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { continue; }
|
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { continue; }
|
||||||
|
|
||||||
if (item.CurrentHull == null || item.Condition <= 0.0f) { continue; }
|
if (item.CurrentHull == null || item.Condition <= 0.0f) { continue; }
|
||||||
if (itemIdentifiers.None(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; }
|
if (itemIdentifiers.None(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; }
|
||||||
|
|
||||||
if (ignoredContainerIdentifiers != null && item.Container != null)
|
if (ignoredContainerIdentifiers != null && item.Container != null)
|
||||||
{
|
{
|
||||||
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
|
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
|
||||||
@@ -217,14 +198,12 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||||
Item rootContainer = item.GetRootContainer();
|
Item rootContainer = item.GetRootContainer();
|
||||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||||
{
|
{
|
||||||
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
float itemPriority = 0.0f;
|
float itemPriority = 0.0f;
|
||||||
if (GetItemPriority != null)
|
if (GetItemPriority != null)
|
||||||
{
|
{
|
||||||
@@ -232,40 +211,35 @@ namespace Barotrauma
|
|||||||
itemPriority = GetItemPriority(item);
|
itemPriority = GetItemPriority(item);
|
||||||
if (itemPriority <= 0.0f) { continue; }
|
if (itemPriority <= 0.0f) { continue; }
|
||||||
}
|
}
|
||||||
|
itemPriority -= Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
|
||||||
itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
|
|
||||||
|
|
||||||
//ignore if the item has a lower priority than the currently selected one
|
//ignore if the item has a lower priority than the currently selected one
|
||||||
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
|
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
|
||||||
|
|
||||||
currItemPriority = itemPriority;
|
currItemPriority = itemPriority;
|
||||||
|
|
||||||
targetItem = item;
|
targetItem = item;
|
||||||
moveToTarget = rootContainer ?? item;
|
moveToTarget = rootContainer ?? item;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//if searched through all the items and a target wasn't found, can't be completed
|
//if searched through all the items and a target wasn't found, can't be completed
|
||||||
if (currSearchIndex >= Item.ItemList.Count - 1 && targetItem == null)
|
if (currSearchIndex >= Item.ItemList.Count - 1 && targetItem == null)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s): {string.Join(", ", itemIdentifiers)}");
|
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s): {string.Join(", ", itemIdentifiers)}");
|
||||||
#endif
|
#endif
|
||||||
canBeCompleted = false;
|
abandon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
||||||
if (getItem == null) return false;
|
if (getItem == null) { return false; }
|
||||||
if (getItem.equip != equip) return false;
|
if (getItem.equip != equip) { return false; }
|
||||||
if (getItem.itemIdentifiers != null && itemIdentifiers != null)
|
if (getItem.itemIdentifiers != null && itemIdentifiers != null)
|
||||||
{
|
{
|
||||||
if (getItem.itemIdentifiers.Length != itemIdentifiers.Length) return false;
|
if (getItem.itemIdentifiers.Length != itemIdentifiers.Length) { return false; }
|
||||||
for (int i = 0; i < getItem.itemIdentifiers.Length; i++)
|
for (int i = 0; i < getItem.itemIdentifiers.Length; i++)
|
||||||
{
|
{
|
||||||
if (getItem.itemIdentifiers[i] != itemIdentifiers[i]) return false;
|
if (getItem.itemIdentifiers[i] != itemIdentifiers[i]) { return false; }
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -273,7 +247,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return getItem.targetItem == targetItem;
|
return getItem.targetItem == targetItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,10 +257,12 @@ namespace Barotrauma
|
|||||||
foreach (string itemName in itemIdentifiers)
|
foreach (string itemName in itemIdentifiers)
|
||||||
{
|
{
|
||||||
var matchingItem = character.Inventory.FindItemByTag(itemName) ?? character.Inventory.FindItemByIdentifier(itemName);
|
var matchingItem = character.Inventory.FindItemByTag(itemName) ?? character.Inventory.FindItemByIdentifier(itemName);
|
||||||
if (matchingItem != null && (!equip || character.HasEquippedItem(matchingItem))) return true;
|
if (matchingItem != null && (!equip || character.HasEquippedItem(matchingItem)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (targetItem != null)
|
else if (targetItem != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,21 +9,14 @@ namespace Barotrauma
|
|||||||
public override string DebugTag => "go to";
|
public override string DebugTag => "go to";
|
||||||
|
|
||||||
private AIObjectiveFindDivingGear findDivingGear;
|
private AIObjectiveFindDivingGear findDivingGear;
|
||||||
|
|
||||||
private Vector2 targetPos;
|
private Vector2 targetPos;
|
||||||
|
|
||||||
private bool repeat;
|
private bool repeat;
|
||||||
private bool cannotReach;
|
|
||||||
|
|
||||||
//how long until the path to the target is declared unreachable
|
//how long until the path to the target is declared unreachable
|
||||||
private float waitUntilPathUnreachable;
|
private float waitUntilPathUnreachable;
|
||||||
|
|
||||||
private bool getDivingGearIfNeeded;
|
private bool getDivingGearIfNeeded;
|
||||||
|
|
||||||
public float CloseEnough { get; set; } = 0.5f;
|
public float CloseEnough { get; set; } = 0.5f;
|
||||||
|
|
||||||
public bool IgnoreIfTargetDead { get; set; }
|
public bool IgnoreIfTargetDead { get; set; }
|
||||||
|
|
||||||
public bool AllowGoingOutside { get; set; }
|
public bool AllowGoingOutside { get; set; }
|
||||||
public bool CheckVisibility { get; set; }
|
public bool CheckVisibility { get; set; }
|
||||||
|
|
||||||
@@ -31,13 +24,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (FollowControlledCharacter && Character.Controlled == null) { return 0.0f; }
|
if (FollowControlledCharacter && Character.Controlled == null) { return 0.0f; }
|
||||||
if (Target != null && Target.Removed) { return 0.0f; }
|
if (Target != null && Target.Removed) { return 0.0f; }
|
||||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) { return 0.0f; }
|
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) { return 0.0f; }
|
||||||
|
|
||||||
if (objectiveManager.CurrentOrder == this)
|
if (objectiveManager.CurrentOrder == this)
|
||||||
{
|
{
|
||||||
return AIObjectiveManager.OrderPriority;
|
return AIObjectiveManager.OrderPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,26 +36,25 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
bool canComplete = !cannotReach && !abandon;
|
if (!abandon)
|
||||||
if (canComplete)
|
|
||||||
{
|
{
|
||||||
if (FollowControlledCharacter && Character.Controlled == null)
|
if (FollowControlledCharacter && Character.Controlled == null)
|
||||||
{
|
{
|
||||||
canComplete = false;
|
abandon = true;
|
||||||
}
|
}
|
||||||
else if (Target != null && Target.Removed)
|
else if (Target != null && Target.Removed)
|
||||||
{
|
{
|
||||||
canComplete = false;
|
abandon = true;
|
||||||
}
|
}
|
||||||
else if (!repeat && waitUntilPathUnreachable < 0)
|
else if (!repeat && waitUntilPathUnreachable < 0)
|
||||||
{
|
{
|
||||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null)
|
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null)
|
||||||
{
|
{
|
||||||
canComplete = !PathSteering.CurrentPath.Unreachable;
|
abandon = PathSteering.CurrentPath.Unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!canComplete)
|
if (abandon)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {(Target != null ? Target.ToString() : TargetPos.ToString())}", Color.Yellow);
|
DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {(Target != null ? Target.ToString() : TargetPos.ToString())}", Color.Yellow);
|
||||||
@@ -75,7 +65,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
character.AIController.SteeringManager.Reset();
|
character.AIController.SteeringManager.Reset();
|
||||||
}
|
}
|
||||||
return canComplete;
|
return base.CanBeCompleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,24 +105,18 @@ namespace Barotrauma
|
|||||||
if (Character.Controlled == null) { return; }
|
if (Character.Controlled == null) { return; }
|
||||||
Target = Character.Controlled;
|
Target = Character.Controlled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Target == character)
|
if (Target == character)
|
||||||
{
|
{
|
||||||
character.AIController.SteeringManager.Reset();
|
character.AIController.SteeringManager.Reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
waitUntilPathUnreachable -= deltaTime;
|
waitUntilPathUnreachable -= deltaTime;
|
||||||
|
|
||||||
if (!character.IsClimbing)
|
if (!character.IsClimbing)
|
||||||
{
|
{
|
||||||
character.SelectedConstruction = null;
|
character.SelectedConstruction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Target != null) { character.AIController.SelectTarget(Target.AiTarget); }
|
if (Target != null) { character.AIController.SelectTarget(Target.AiTarget); }
|
||||||
|
|
||||||
Vector2 currTargetPos = Vector2.Zero;
|
Vector2 currTargetPos = Vector2.Zero;
|
||||||
|
|
||||||
if (Target == null)
|
if (Target == null)
|
||||||
{
|
{
|
||||||
currTargetPos = targetPos;
|
currTargetPos = targetPos;
|
||||||
@@ -147,7 +131,6 @@ namespace Barotrauma
|
|||||||
currTargetPos -= character.Submarine.SimPosition;
|
currTargetPos -= character.Submarine.SimPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sightCheck = true;
|
bool sightCheck = true;
|
||||||
if (CheckVisibility && Target != null)
|
if (CheckVisibility && Target != null)
|
||||||
{
|
{
|
||||||
@@ -165,12 +148,12 @@ namespace Barotrauma
|
|||||||
bool targetIsOutside = (Target != null && Target.Submarine == null) || (insideSteering && PathSteering.CurrentPath.HasOutdoorsNodes);
|
bool targetIsOutside = (Target != null && Target.Submarine == null) || (insideSteering && PathSteering.CurrentPath.HasOutdoorsNodes);
|
||||||
if (isInside && targetIsOutside && !AllowGoingOutside)
|
if (isInside && targetIsOutside && !AllowGoingOutside)
|
||||||
{
|
{
|
||||||
cannotReach = true;
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (insideSteering && !PathSteering.HasAccessToPath(PathSteering.CurrentPath))
|
if (insideSteering && !PathSteering.HasAccessToPath(PathSteering.CurrentPath))
|
||||||
{
|
{
|
||||||
cannotReach = true;
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
character.AIController.SteeringManager.SteeringSeek(currTargetPos);
|
character.AIController.SteeringManager.SteeringSeek(currTargetPos);
|
||||||
@@ -181,15 +164,7 @@ namespace Barotrauma
|
|||||||
Target is Item i && HumanAIController.NeedsDivingGear(i.CurrentHull) ||
|
Target is Item i && HumanAIController.NeedsDivingGear(i.CurrentHull) ||
|
||||||
Target is Character c && HumanAIController.NeedsDivingGear(c.CurrentHull))
|
Target is Character c && HumanAIController.NeedsDivingGear(c.CurrentHull))
|
||||||
{
|
{
|
||||||
if (findDivingGear == null)
|
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, true, objectiveManager));
|
||||||
{
|
|
||||||
findDivingGear = new AIObjectiveFindDivingGear(character, true, objectiveManager);
|
|
||||||
AddSubObjective(findDivingGear);
|
|
||||||
}
|
|
||||||
else if (!findDivingGear.CanBeCompleted)
|
|
||||||
{
|
|
||||||
abandon = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,33 +173,31 @@ namespace Barotrauma
|
|||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
if (repeat) { return false; }
|
if (repeat) { return false; }
|
||||||
|
|
||||||
bool completed = false;
|
bool completed = false;
|
||||||
|
|
||||||
if (Target is Item item)
|
if (Target is Item item)
|
||||||
{
|
{
|
||||||
if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
|
if (item.IsInsideTrigger(character.WorldPosition)) { completed = true; }
|
||||||
}
|
}
|
||||||
else if (Target is Character targetCharacter)
|
else if (Target is Character targetCharacter)
|
||||||
{
|
{
|
||||||
if (character.CanInteractWith(targetCharacter)) completed = true;
|
if (character.CanInteractWith(targetCharacter)) { completed = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
completed = completed || Vector2.DistanceSquared(Target != null ? Target.SimPosition : targetPos, character.SimPosition) < CloseEnough * CloseEnough;
|
completed = completed || Vector2.DistanceSquared(Target != null ? Target.SimPosition : targetPos, character.SimPosition) < CloseEnough * CloseEnough;
|
||||||
|
if (completed) { character.AIController.SteeringManager.Reset(); }
|
||||||
if (completed) character.AIController.SteeringManager.Reset();
|
|
||||||
|
|
||||||
return completed;
|
return completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveGoTo objective = otherObjective as AIObjectiveGoTo;
|
if (!(otherObjective is AIObjectiveGoTo objective)) { return false; }
|
||||||
if (objective == null) return false;
|
if (objective.Target == Target) { return true; }
|
||||||
|
return objective.targetPos == targetPos;
|
||||||
|
}
|
||||||
|
|
||||||
if (objective.Target == Target) return true;
|
private void CalculateCloseEnough()
|
||||||
|
{
|
||||||
return (objective.targetPos == targetPos);
|
float interactionDistance = Target is Item i ? ConvertUnits.ToSimUnits(i.InteractDistance * 0.9f) : 0;
|
||||||
|
CloseEnough = Math.Max(interactionDistance, CloseEnough);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateCloseEnough()
|
private void CalculateCloseEnough()
|
||||||
|
|||||||
@@ -74,6 +74,21 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
if (objectiveManager.CurrentObjective == this)
|
||||||
|
{
|
||||||
|
if (randomTimer > 0)
|
||||||
|
{
|
||||||
|
randomTimer -= deltaTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetRandom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsCompleted() => false;
|
public override bool IsCompleted() => false;
|
||||||
public override bool CanBeCompleted => true;
|
public override bool CanBeCompleted => true;
|
||||||
|
|
||||||
@@ -84,7 +99,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
if (PathSteering == null) return;
|
if (PathSteering == null) { return; }
|
||||||
|
|
||||||
//don't keep dragging others when idling
|
//don't keep dragging others when idling
|
||||||
if (character.SelectedCharacter != null)
|
if (character.SelectedCharacter != null)
|
||||||
@@ -136,7 +151,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
//choose a random available hull
|
//choose a random available hull
|
||||||
var randomHull = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
|
var randomHull = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
|
||||||
|
|
||||||
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
||||||
if (isCurrentHullOK)
|
if (isCurrentHullOK)
|
||||||
{
|
{
|
||||||
@@ -269,7 +283,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void FindTargetHulls()
|
private void FindTargetHulls()
|
||||||
{
|
{
|
||||||
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
|
||||||
targetHulls.Clear();
|
targetHulls.Clear();
|
||||||
hullWeights.Clear();
|
hullWeights.Clear();
|
||||||
foreach (var hull in Hull.hullList)
|
foreach (var hull in Hull.hullList)
|
||||||
|
|||||||
@@ -133,9 +133,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (T target in GetList())
|
foreach (T target in GetList())
|
||||||
{
|
{
|
||||||
|
// The bots always find targets when the objective is an order.
|
||||||
if (objectiveManager.CurrentOrder != this)
|
if (objectiveManager.CurrentOrder != this)
|
||||||
{
|
{
|
||||||
// Battery or pump states cannot be reported and therefore we must ignore them -> the bots always know if they require attention.
|
// Battery or pump states cannot currently be reported (not implemented) and therefore we must ignore them -> the bots always know if they require attention.
|
||||||
bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater;
|
bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater;
|
||||||
if (!ignore && !ReportedTargets.Contains(target)) { continue; }
|
if (!ignore && !ReportedTargets.Contains(target)) { continue; }
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-52
@@ -11,41 +11,21 @@ namespace Barotrauma
|
|||||||
public override string DebugTag => "operate item";
|
public override string DebugTag => "operate item";
|
||||||
|
|
||||||
private ItemComponent component, controller;
|
private ItemComponent component, controller;
|
||||||
|
|
||||||
private Entity operateTarget;
|
private Entity operateTarget;
|
||||||
|
|
||||||
private bool isCompleted;
|
private bool isCompleted;
|
||||||
|
|
||||||
private bool canBeCompleted;
|
|
||||||
|
|
||||||
private bool requireEquip;
|
private bool requireEquip;
|
||||||
|
|
||||||
private bool useController;
|
private bool useController;
|
||||||
|
private AIObjectiveGoTo goToObjective;
|
||||||
|
private AIObjectiveGetItem getItemObjective;
|
||||||
|
|
||||||
private AIObjectiveGoTo gotoObjective;
|
public override bool CanBeCompleted => base.CanBeCompleted && (!useController || controller != null);
|
||||||
|
|
||||||
public override bool CanBeCompleted
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (gotoObjective != null && !gotoObjective.CanBeCompleted) return false;
|
|
||||||
|
|
||||||
if (useController && controller == null) return false;
|
|
||||||
|
|
||||||
return canBeCompleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity OperateTarget
|
|
||||||
{
|
|
||||||
get { return operateTarget; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public Entity OperateTarget => operateTarget;
|
||||||
public ItemComponent Component => component;
|
public ItemComponent Component => component;
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
{
|
{
|
||||||
if (gotoObjective != null && !gotoObjective.CanBeCompleted) { return 0; }
|
if (goToObjective != null && !goToObjective.CanBeCompleted) { return 0; }
|
||||||
if (component.Item.ConditionPercentage <= 0) { return 0; }
|
if (component.Item.ConditionPercentage <= 0) { return 0; }
|
||||||
if (objectiveManager.CurrentOrder == this)
|
if (objectiveManager.CurrentOrder == this)
|
||||||
{
|
{
|
||||||
@@ -65,33 +45,28 @@ namespace Barotrauma
|
|||||||
this.requireEquip = requireEquip;
|
this.requireEquip = requireEquip;
|
||||||
this.operateTarget = operateTarget;
|
this.operateTarget = operateTarget;
|
||||||
this.useController = useController;
|
this.useController = useController;
|
||||||
|
|
||||||
if (useController)
|
if (useController)
|
||||||
{
|
{
|
||||||
var controllers = component.Item.GetConnectedComponents<Controller>();
|
var controllers = component.Item.GetConnectedComponents<Controller>();
|
||||||
if (controllers.Any()) controller = controllers[0];
|
if (controllers.Any()) controller = controllers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
canBeCompleted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
ItemComponent target = useController ? controller : component;
|
ItemComponent target = useController ? controller : component;
|
||||||
|
|
||||||
if (useController && controller == null)
|
if (useController && controller == null)
|
||||||
{
|
{
|
||||||
character.Speak(TextManager.Get("DialogCantFindController").Replace("[item]", component.Item.Name), null, 2.0f, "cantfindcontroller", 30.0f);
|
character.Speak(TextManager.Get("DialogCantFindController").Replace("[item]", component.Item.Name), null, 2.0f, "cantfindcontroller", 30.0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (target.CanBeSelected)
|
if (target.CanBeSelected)
|
||||||
{
|
{
|
||||||
if (Vector2.Distance(character.Position, target.Item.Position) < target.Item.InteractDistance
|
bool inSameRoom = character.CurrentHull == target.Item.CurrentHull;
|
||||||
|| target.Item.IsInsideTrigger(character.WorldPosition))
|
bool withinReach = target.Item.IsInsideTrigger(character.WorldPosition) || Vector2.DistanceSquared(character.Position, target.Item.Position) < MathUtils.Pow(target.Item.InteractDistance, 2);
|
||||||
|
if (inSameRoom && withinReach)
|
||||||
{
|
{
|
||||||
if (character.CurrentHull == target.Item.CurrentHull)
|
if (character.SelectedConstruction != target.Item)
|
||||||
{
|
{
|
||||||
if (character.SelectedConstruction != target.Item)
|
if (character.SelectedConstruction != target.Item)
|
||||||
{
|
{
|
||||||
@@ -103,21 +78,27 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (component.AIOperate(deltaTime, character, this))
|
||||||
|
{
|
||||||
|
isCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
AddSubObjective(gotoObjective = new AIObjectiveGoTo(target.Item, character, objectiveManager));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (component.Item.GetComponent<Pickable>() == null)
|
if (component.Item.GetComponent<Pickable>() == null)
|
||||||
{
|
{
|
||||||
//controller/target can't be selected and the item cannot be picked -> objective can't be completed
|
//controller/target can't be selected and the item cannot be picked -> objective can't be completed
|
||||||
canBeCompleted = false;
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!character.Inventory.Items.Contains(component.Item))
|
else if (!character.Inventory.Items.Contains(component.Item))
|
||||||
{
|
{
|
||||||
AddSubObjective(new AIObjectiveGetItem(character, component.Item, objectiveManager, equip: true));
|
TryAddSubObjective(ref getItemObjective, () => new AIObjectiveGetItem(character, component.Item, objectiveManager, equip: true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -127,18 +108,17 @@ namespace Barotrauma
|
|||||||
var holdable = component.Item.GetComponent<Holdable>();
|
var holdable = component.Item.GetComponent<Holdable>();
|
||||||
if (holdable == null)
|
if (holdable == null)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
DebugConsole.ThrowError("AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component");
|
DebugConsole.ThrowError("AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component");
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < character.Inventory.Capacity; i++)
|
for (int i = 0; i < character.Inventory.Capacity; i++)
|
||||||
{
|
{
|
||||||
if (character.Inventory.SlotTypes[i] == InvSlotType.Any ||
|
if (character.Inventory.SlotTypes[i] == InvSlotType.Any || !holdable.AllowedSlots.Any(s => s.HasFlag(character.Inventory.SlotTypes[i])))
|
||||||
!holdable.AllowedSlots.Any(s => s.HasFlag(character.Inventory.SlotTypes[i])))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//equip slot already taken
|
//equip slot already taken
|
||||||
if (character.Inventory.Items[i] != null)
|
if (character.Inventory.Items[i] != null)
|
||||||
{
|
{
|
||||||
@@ -157,7 +137,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.AIOperate(deltaTime, character, this))
|
if (component.AIOperate(deltaTime, character, this))
|
||||||
{
|
{
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
@@ -166,17 +145,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted() => isCompleted && !IsLoop;
|
||||||
{
|
|
||||||
return isCompleted && !IsLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveOperateItem operateItem = otherObjective as AIObjectiveOperateItem;
|
if (!(otherObjective is AIObjectiveOperateItem operateItem)) { return false; }
|
||||||
if (operateItem == null) return false;
|
return (operateItem.component == component || otherObjective.Option == Option);
|
||||||
|
|
||||||
return (operateItem.component == component ||otherObjective.Option == Option);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-29
@@ -16,8 +16,8 @@ namespace Barotrauma
|
|||||||
public Item Item { get; private set; }
|
public Item Item { get; private set; }
|
||||||
|
|
||||||
private AIObjectiveGoTo goToObjective;
|
private AIObjectiveGoTo goToObjective;
|
||||||
|
|
||||||
private float previousCondition = -1;
|
private float previousCondition = -1;
|
||||||
|
private RepairTool repairTool;
|
||||||
|
|
||||||
public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||||
{
|
{
|
||||||
@@ -42,8 +42,6 @@ namespace Barotrauma
|
|||||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1));
|
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanBeCompleted => !abandon;
|
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
bool isCompleted = Item.IsFullCondition;
|
bool isCompleted = Item.IsFullCondition;
|
||||||
@@ -61,19 +59,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
if (goToObjective != null && !subObjectives.Contains(goToObjective))
|
// Don't allow to repair items in rooms that have a fire or an enemies inside or if outside the sub
|
||||||
|
if (Item.CurrentHull == null || Item.CurrentHull.FireSources.Count > 0 || Character.CharacterList.Any(c => c.CurrentHull == Item.CurrentHull && !HumanAIController.IsFriendly(c)))
|
||||||
{
|
{
|
||||||
if (!goToObjective.IsCompleted() && !goToObjective.CanBeCompleted)
|
abandon = true;
|
||||||
{
|
|
||||||
abandon = true;
|
|
||||||
character?.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f);
|
|
||||||
}
|
|
||||||
goToObjective = null;
|
|
||||||
}
|
|
||||||
if (!abandon)
|
|
||||||
{
|
|
||||||
// Don't allow to repair items in rooms that have a fire or an enemy inside
|
|
||||||
abandon = Item.CurrentHull != null && (Item.CurrentHull.FireSources.Count > 0 || Character.CharacterList.Any(c => c.CurrentHull == Item.CurrentHull && !HumanAIController.IsFriendly(c)));
|
|
||||||
}
|
}
|
||||||
foreach (Repairable repairable in Item.Repairables)
|
foreach (Repairable repairable in Item.Repairables)
|
||||||
{
|
{
|
||||||
@@ -90,6 +79,8 @@ namespace Barotrauma
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Only continue when the get item sub objectives have been completed.
|
||||||
|
if (subObjectives.Any(so => so is AIObjectiveGetItem)) { return; }
|
||||||
if (repairTool == null)
|
if (repairTool == null)
|
||||||
{
|
{
|
||||||
FindRepairTool();
|
FindRepairTool();
|
||||||
@@ -128,24 +119,24 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (goToObjective == null || goToObjective.Target != Item)
|
else
|
||||||
{
|
{
|
||||||
previousCondition = -1;
|
// If cannot reach the item, approach it.
|
||||||
if (goToObjective != null)
|
TryAddSubObjective(ref goToObjective,
|
||||||
{
|
constructor: () =>
|
||||||
subObjectives.Remove(goToObjective);
|
{
|
||||||
}
|
previousCondition = -1;
|
||||||
goToObjective = new AIObjectiveGoTo(Item, character, objectiveManager);
|
var objective = new AIObjectiveGoTo(Item, character, objectiveManager);
|
||||||
if (repairTool != null)
|
if (repairTool != null)
|
||||||
{
|
{
|
||||||
//goToObjective.CloseEnough = (HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range)) * 0.75f;
|
objective.CloseEnough = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||||
goToObjective.CloseEnough = ConvertUnits.ToSimUnits(repairTool.Range);
|
}
|
||||||
}
|
return objective;
|
||||||
AddSubObjective(goToObjective);
|
},
|
||||||
|
onAbandon: () => character.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RepairTool repairTool;
|
|
||||||
private void FindRepairTool()
|
private void FindRepairTool()
|
||||||
{
|
{
|
||||||
foreach (Repairable repairable in Item.Repairables)
|
foreach (Repairable repairable in Item.Repairables)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
|
// TODO: refactor
|
||||||
class AIObjectiveRescue : AIObjective
|
class AIObjectiveRescue : AIObjective
|
||||||
{
|
{
|
||||||
public override string DebugTag => "rescue";
|
public override string DebugTag => "rescue";
|
||||||
@@ -93,6 +94,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove and replace with the priority system
|
||||||
protected override bool ShouldInterruptSubObjective(AIObjective subObjective)
|
protected override bool ShouldInterruptSubObjective(AIObjective subObjective)
|
||||||
{
|
{
|
||||||
if (subObjective is AIObjectiveFindSafety)
|
if (subObjective is AIObjectiveFindSafety)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
// TODO: Ensure that this works well enough. Consider using AIObjectiveLoop class.
|
// TODO: Refactor using AIObjectiveLoop class.
|
||||||
class AIObjectiveRescueAll : AIObjective
|
class AIObjectiveRescueAll : AIObjective
|
||||||
{
|
{
|
||||||
public override string DebugTag => "rescue all";
|
public override string DebugTag => "rescue all";
|
||||||
|
|||||||
@@ -810,6 +810,25 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetItem.Prefab.DeconstructItems.Any())
|
||||||
|
{
|
||||||
|
inputContainer.Inventory.RemoveItem(targetItem);
|
||||||
|
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||||
|
MoveInputQueue();
|
||||||
|
PutItemsToLinkedContainer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (outputContainer.Inventory.Items.All(i => i != null))
|
||||||
|
{
|
||||||
|
targetItem.Drop(dropper: null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (targetItem.Prefab.DeconstructItems.Any())
|
if (targetItem.Prefab.DeconstructItems.Any())
|
||||||
{
|
{
|
||||||
inputContainer.Inventory.RemoveItem(targetItem);
|
inputContainer.Inventory.RemoveItem(targetItem);
|
||||||
|
|||||||
@@ -212,6 +212,33 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2? PosToMaintain
|
||||||
|
{
|
||||||
|
get { return posToMaintain; }
|
||||||
|
set { posToMaintain = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObstacleDebugInfo
|
||||||
|
{
|
||||||
|
public Vector2 Point1;
|
||||||
|
public Vector2 Point2;
|
||||||
|
|
||||||
|
public Vector2? Intersection;
|
||||||
|
|
||||||
|
public float Dot;
|
||||||
|
|
||||||
|
public Vector2 AvoidStrength;
|
||||||
|
|
||||||
|
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength)
|
||||||
|
{
|
||||||
|
Point1 = edge.Point1;
|
||||||
|
Point2 = edge.Point2;
|
||||||
|
Intersection = intersection;
|
||||||
|
Dot = dot;
|
||||||
|
AvoidStrength = avoidStrength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//edge point 1, edge point 2, avoid strength
|
//edge point 1, edge point 2, avoid strength
|
||||||
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
|
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
|
||||||
|
|
||||||
|
|||||||
@@ -1460,6 +1460,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||||
}
|
}
|
||||||
|
if (!broken)
|
||||||
|
{
|
||||||
|
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||||
|
}
|
||||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||||
|
|
||||||
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
|
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
|
||||||
|
|||||||
@@ -626,6 +626,25 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string roomName;
|
||||||
|
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
|
||||||
|
public string RoomName
|
||||||
|
{
|
||||||
|
get { return roomName; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (roomName == value) { return; }
|
||||||
|
roomName = value;
|
||||||
|
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override Rectangle Rect
|
public override Rectangle Rect
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
Reference in New Issue
Block a user