(89fea1963) Refactoring and fixing: - Remove relatively expensive HasAccessToPath calls. - Fix finding diving gear subobjective of goto main objective not checking if we need the gear. - Move the abandon checks from CanBeCompleted property under Act method and use the inherited property. Fixes incorrect results when the goto objective is not the active objective. - Improve the support for concurrent sub objectives.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -47,7 +48,7 @@ namespace Barotrauma
|
||||
if (currentObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
var subObjective = currentObjective.CurrentSubObjective;
|
||||
var subObjective = currentObjective.SubObjectives.FirstOrDefault();
|
||||
if (subObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
|
||||
@@ -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;
|
||||
|
||||
overrideTargetMovement = Vector2.Zero;
|
||||
|
||||
@@ -74,17 +74,12 @@ namespace Barotrauma
|
||||
public CrewManager(XElement element, bool 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;
|
||||
}
|
||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||
c.AIController is HumanAIController &&
|
||||
!c.IsDead &&
|
||||
c.SpeechImpediment <= 100.0f);
|
||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
||||
}
|
||||
if (string.IsNullOrEmpty(text)) { return; }
|
||||
|
||||
var characterInfo = new CharacterInfo(subElement);
|
||||
characterInfos.Add(characterInfo);
|
||||
@@ -95,6 +90,7 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
@@ -243,24 +239,27 @@ namespace Barotrauma
|
||||
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
{
|
||||
if (characterInfos.Contains(characterInfo))
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
if (character?.Inventory == null) return null;
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
characters.Remove(character);
|
||||
if (removeInfo) characterInfos.Remove(character.Info);
|
||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||
c.AIController is HumanAIController &&
|
||||
!c.IsDead &&
|
||||
c.SpeechImpediment <= 100.0f);
|
||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
||||
}
|
||||
|
||||
public void AddCharacter(Character character)
|
||||
@@ -634,183 +633,9 @@ namespace Barotrauma
|
||||
{
|
||||
characterListBox.BarScroll = roundedPos;
|
||||
}
|
||||
var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox")
|
||||
{
|
||||
UserData = character,
|
||||
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;
|
||||
soundIcon.Visible = !muted && !mutedLocally;
|
||||
soundIconDisabled.Visible = muted || mutedLocally;
|
||||
soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally");
|
||||
}
|
||||
|
||||
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
||||
@@ -954,12 +779,6 @@ namespace Barotrauma
|
||||
}
|
||||
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);
|
||||
if (IsSinglePlayer)
|
||||
@@ -1017,23 +836,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
|
||||
if (IsSinglePlayer)
|
||||
//only one target (or an order with no particular targets), just show options
|
||||
else
|
||||
{
|
||||
orderGiver?.Speak(
|
||||
order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
|
||||
}
|
||||
else if (orderGiver != null)
|
||||
{
|
||||
OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver);
|
||||
if (GameMain.Client != null)
|
||||
orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas)
|
||||
{ AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) },
|
||||
isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
||||
{
|
||||
GameMain.Client.SendChatMessage(msg);
|
||||
}
|
||||
}
|
||||
DisplayCharacterOrder(character, order);
|
||||
}
|
||||
UserData = character,
|
||||
Stretch = true
|
||||
};
|
||||
//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>
|
||||
/// Create the UI panel that's used to select the target and options for a given order
|
||||
|
||||
@@ -186,6 +186,8 @@ namespace Barotrauma
|
||||
|
||||
private GUILayoutGroup subPreviewContainer;
|
||||
|
||||
private GUILayoutGroup subPreviewContainer;
|
||||
|
||||
private GUIButton loadGameButton;
|
||||
|
||||
public Action<Submarine, string, string> StartNewGame;
|
||||
|
||||
@@ -432,7 +432,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Check whether the character has a diving mask in usable condition plus some oxygen.
|
||||
/// </summary>
|
||||
public static bool HasDivingGear(Character character) => HasItem(character, "diving", "oxygensource");
|
||||
public static bool HasDivingMask(Character character) => HasItem(character, "diving", "oxygensource");
|
||||
|
||||
public static bool HasItem(Character character, string tag, string containedTag, float conditionPercentage = 0)
|
||||
{
|
||||
@@ -528,7 +528,7 @@ namespace Barotrauma
|
||||
if (hull == null) { return 0; }
|
||||
bool ignoreFire = ObjectiveManager.IsCurrentObjective<AIObjectiveExtinguishFires>() || ObjectiveManager.IsCurrentObjective<AIObjectiveExtinguishFire>();
|
||||
bool ignoreWater = HasDivingSuit(Character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingGear(Character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingMask(Character);
|
||||
bool ignoreEnemies = ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||
return GetHullSafety(hull, Character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual bool IsLoop { get; set; }
|
||||
public IEnumerable<AIObjective> SubObjectives => subObjectives;
|
||||
public AIObjective CurrentSubObjective { get; private set; }
|
||||
|
||||
public event Action Completed;
|
||||
|
||||
@@ -90,11 +89,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (!subObjectives.Contains(CurrentSubObjective))
|
||||
{
|
||||
CurrentSubObjective = null;
|
||||
}
|
||||
|
||||
foreach (AIObjective objective in subObjectives)
|
||||
{
|
||||
objective.TryComplete(deltaTime);
|
||||
@@ -123,8 +117,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (subObjectives.None()) { return; }
|
||||
subObjectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
|
||||
CurrentSubObjective = SubObjectives.First();
|
||||
CurrentSubObjective.SortSubObjectives();
|
||||
if (ConcurrentObjectives)
|
||||
{
|
||||
subObjectives.ForEach(so => SortSubObjectives());
|
||||
}
|
||||
else
|
||||
{
|
||||
subObjectives.First().SortSubObjectives();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float GetPriority() => Priority * PriorityModifier;
|
||||
@@ -137,10 +137,12 @@ namespace Barotrauma
|
||||
}
|
||||
else if (objectiveManager.WaitTimer <= 0)
|
||||
{
|
||||
var subObjective = objectiveManager.CurrentObjective?.CurrentSubObjective;
|
||||
if ((objectiveManager.CurrentObjective == this || subObjective == this))
|
||||
if (objectiveManager.CurrentObjective != null)
|
||||
{
|
||||
Priority += Devotion * PriorityModifier * deltaTime;
|
||||
if (objectiveManager.CurrentObjective == this || objectiveManager.CurrentObjective.subObjectives.Any(so => so == this))
|
||||
{
|
||||
Priority += Devotion * PriorityModifier * deltaTime;
|
||||
}
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
subObjectives.ForEach(so => so.Update(deltaTime));
|
||||
@@ -202,7 +204,11 @@ namespace Barotrauma
|
||||
|
||||
public virtual void OnSelected()
|
||||
{
|
||||
//SteeringManager.Reset();
|
||||
// Should we reset steering here?
|
||||
//if (!ConcurrentObjectives)
|
||||
//{
|
||||
// SteeringManager.Reset();
|
||||
//}
|
||||
}
|
||||
|
||||
public virtual void Reset() { }
|
||||
|
||||
-1
@@ -27,7 +27,6 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (gotoObjective != null && !gotoObjective.CanBeCompleted) { return 0; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c))) { return 0; }
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y) * 2.0f;
|
||||
|
||||
+44
-2
@@ -80,7 +80,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (needsDivingGear)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingGear(character);
|
||||
needsEquipment = !HumanAIController.HasDivingMask(character);
|
||||
}
|
||||
if (needsEquipment)
|
||||
{
|
||||
@@ -601,6 +601,49 @@ namespace Barotrauma
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
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)
|
||||
{
|
||||
Vector2 dir = character.Position - fireSource.Position;
|
||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
|
||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||
}
|
||||
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 - 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
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,7 +677,6 @@ namespace Barotrauma
|
||||
unreachable.Add(hull);
|
||||
continue;
|
||||
}
|
||||
if (!PathSteering.HasAccessToPath(path)) { continue; }
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
|
||||
@@ -346,6 +346,31 @@ namespace Barotrauma
|
||||
{
|
||||
#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?
|
||||
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||
{
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
||||
if (canOperate)
|
||||
{
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
|
||||
}
|
||||
var repairTool = weldingTool.GetComponent<RepairTool>();
|
||||
if (repairTool == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
||||
#endif
|
||||
abandon = true;
|
||||
return;
|
||||
|
||||
@@ -9,9 +9,6 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "go to";
|
||||
|
||||
const float checkAccessInterval = 1;
|
||||
private float accessCheckTimer;
|
||||
|
||||
private AIObjectiveFindDivingGear findDivingGear;
|
||||
private Vector2 targetPos;
|
||||
private bool repeat;
|
||||
@@ -36,43 +33,6 @@ namespace Barotrauma
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!abandon)
|
||||
{
|
||||
if (FollowControlledCharacter && Character.Controlled == null)
|
||||
{
|
||||
abandon = true;
|
||||
}
|
||||
else if (Target != null && Target.Removed)
|
||||
{
|
||||
abandon = true;
|
||||
}
|
||||
else if (!repeat && waitUntilPathUnreachable < 0)
|
||||
{
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null)
|
||||
{
|
||||
abandon = PathSteering.CurrentPath.Unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (abandon)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {(Target != null ? Target.ToString() : TargetPos.ToString())}", Color.Yellow);
|
||||
#endif
|
||||
if (objectiveManager.CurrentOrder != null)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogCannotReach"), identifier: "cannotreach", minDurationBetweenSimilar: 10.0f);
|
||||
}
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
return base.CanBeCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public Entity Target { get; private set; }
|
||||
|
||||
public Vector2 TargetPos => Target != null ? Target.SimPosition : targetPos;
|
||||
@@ -106,12 +66,17 @@ namespace Barotrauma
|
||||
{
|
||||
if (FollowControlledCharacter)
|
||||
{
|
||||
if (Character.Controlled == null) { return; }
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
Target = Character.Controlled;
|
||||
}
|
||||
if (Target == character)
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
waitUntilPathUnreachable -= deltaTime;
|
||||
@@ -119,7 +84,17 @@ namespace Barotrauma
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
if (Target != null) { character.AIController.SelectTarget(Target.AiTarget); }
|
||||
if (Target != null)
|
||||
{
|
||||
if (Target.Removed)
|
||||
{
|
||||
abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SelectTarget(Target.AiTarget);
|
||||
}
|
||||
}
|
||||
Vector2 currTargetPos = Vector2.Zero;
|
||||
if (Target == null)
|
||||
{
|
||||
@@ -153,30 +128,46 @@ namespace Barotrauma
|
||||
if (isInside && targetIsOutside && !AllowGoingOutside)
|
||||
{
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
if (accessCheckTimer <= 0)
|
||||
else if (!repeat && waitUntilPathUnreachable < 0)
|
||||
{
|
||||
accessCheckTimer = checkAccessInterval;
|
||||
if (insideSteering && !PathSteering.HasAccessToPath(PathSteering.CurrentPath))
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null)
|
||||
{
|
||||
abandon = true;
|
||||
return;
|
||||
abandon = PathSteering.CurrentPath.Unreachable;
|
||||
}
|
||||
}
|
||||
if (abandon)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {(Target != null ? Target.ToString() : TargetPos.ToString())}", Color.Yellow);
|
||||
#endif
|
||||
if (objectiveManager.CurrentOrder != null)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogCannotReach"), identifier: "cannotreach", minDurationBetweenSimilar: 10.0f);
|
||||
}
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
accessCheckTimer -= deltaTime;
|
||||
}
|
||||
character.AIController.SteeringManager.SteeringSeek(currTargetPos);
|
||||
if (getDivingGearIfNeeded)
|
||||
{
|
||||
if (targetIsOutside ||
|
||||
Target is Hull h && HumanAIController.NeedsDivingGear(h) ||
|
||||
Target is Item i && HumanAIController.NeedsDivingGear(i.CurrentHull) ||
|
||||
Target is Character c && HumanAIController.NeedsDivingGear(c.CurrentHull))
|
||||
character.AIController.SteeringManager.SteeringSeek(currTargetPos);
|
||||
if (getDivingGearIfNeeded)
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, true, objectiveManager));
|
||||
var targetHull = Target is Hull h ? h : Target is Item i ? i.CurrentHull : Target is Character c ? c.CurrentHull : character.CurrentHull;
|
||||
bool needsDivingGear = HumanAIController.NeedsDivingGear(targetHull);
|
||||
bool needsDivingSuit = needsDivingGear && (targetIsOutside || targetHull.WaterPercentage > 90);
|
||||
bool needsEquipment = false;
|
||||
if (needsDivingSuit)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingSuit(character);
|
||||
}
|
||||
else if (needsDivingGear)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingMask(character);
|
||||
}
|
||||
if (needsEquipment)
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 CanBeCompleted => true;
|
||||
|
||||
@@ -142,10 +157,9 @@ namespace Barotrauma
|
||||
// Check that there is no unsafe or forbidden hulls on the way to the target
|
||||
// Only do this when the current hull is ok, because otherwise would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition);
|
||||
if (path.Unreachable ||
|
||||
path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) ||
|
||||
IsForbidden(n.CurrentHull)) ||
|
||||
!PathSteering.HasAccessToPath(path))
|
||||
if (path.Unreachable ||
|
||||
path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) ||
|
||||
IsForbidden(n.CurrentHull)))
|
||||
{
|
||||
//can't go to this room, remove it from the list and try another room next frame
|
||||
int index = targetHulls.IndexOf(randomHull);
|
||||
|
||||
+4
-1
@@ -25,7 +25,6 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (goToObjective != null && !goToObjective.CanBeCompleted) { return 0; }
|
||||
if (component.Item.ConditionPercentage <= 0) { return 0; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
@@ -140,6 +139,10 @@ namespace Barotrauma
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
if (component.AIOperate(deltaTime, character, this))
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -620,6 +620,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())
|
||||
{
|
||||
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
|
||||
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
|
||||
|
||||
|
||||
@@ -1532,6 +1532,10 @@ namespace Barotrauma
|
||||
{
|
||||
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);
|
||||
|
||||
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
|
||||
|
||||
@@ -341,6 +341,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
|
||||
{
|
||||
get
|
||||
|
||||
Reference in New Issue
Block a user