(43c3787ae) Refactor AIObjectiveRescue and remove ShouldInterruptSubObjective property. Not yet tested.
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,12 +74,17 @@ namespace Barotrauma
|
|||||||
public CrewManager(XElement element, bool isSinglePlayer)
|
public CrewManager(XElement element, bool isSinglePlayer)
|
||||||
: this(isSinglePlayer)
|
: this(isSinglePlayer)
|
||||||
{
|
{
|
||||||
if (!isSinglePlayer)
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
|
//let the server create random conversations in MP
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrEmpty(text)) { return; }
|
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||||
|
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);
|
||||||
@@ -90,7 +95,6 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void InitProjectSpecific()
|
partial void InitProjectSpecific()
|
||||||
@@ -239,27 +243,24 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public IEnumerable<Character> GetCharacters()
|
public IEnumerable<Character> GetCharacters()
|
||||||
{
|
{
|
||||||
if (character?.Inventory == null) return null;
|
if (characterInfos.Contains(characterInfo))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var radioItem = character.Inventory.Items.FirstOrDefault(it => it != null && it.GetComponent<WifiComponent>() != null);
|
characterInfos.Add(characterInfo);
|
||||||
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 (GameMain.Client != null)
|
if (character == null)
|
||||||
{
|
{
|
||||||
//let the server create random conversations in MP
|
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
characters.Remove(character);
|
||||||
c.AIController is HumanAIController &&
|
if (removeInfo) characterInfos.Remove(character.Info);
|
||||||
!c.IsDead &&
|
|
||||||
c.SpeechImpediment <= 100.0f);
|
|
||||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCharacter(Character character)
|
public void AddCharacter(Character character)
|
||||||
@@ -633,9 +634,183 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
characterListBox.BarScroll = roundedPos;
|
characterListBox.BarScroll = roundedPos;
|
||||||
}
|
}
|
||||||
soundIcon.Visible = !muted && !mutedLocally;
|
var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox")
|
||||||
soundIconDisabled.Visible = muted || mutedLocally;
|
{
|
||||||
soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally");
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
||||||
@@ -779,6 +954,12 @@ 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)
|
||||||
@@ -836,19 +1017,23 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//only one target (or an order with no particular targets), just show options
|
|
||||||
else
|
character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
|
||||||
|
if (IsSinglePlayer)
|
||||||
{
|
{
|
||||||
orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas)
|
orderGiver?.Speak(
|
||||||
{ AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) },
|
order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
|
||||||
isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
}
|
||||||
|
else if (orderGiver != null)
|
||||||
|
{
|
||||||
|
OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver);
|
||||||
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
UserData = character,
|
GameMain.Client.SendChatMessage(msg);
|
||||||
Stretch = true
|
}
|
||||||
};
|
}
|
||||||
//line connecting the order button to the option buttons
|
DisplayCharacterOrder(character, order);
|
||||||
//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
|
||||||
|
|||||||
@@ -204,6 +204,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;
|
||||||
|
|||||||
@@ -77,13 +77,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it cannot be completed.");
|
DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it cannot be completed.");
|
||||||
#endif
|
|
||||||
subObjectives.Remove(subObjective);
|
|
||||||
}
|
|
||||||
else if (subObjective.ShouldInterruptSubObjective(subObjective))
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it is interrupted.");
|
|
||||||
#endif
|
#endif
|
||||||
subObjectives.Remove(subObjective);
|
subObjectives.Remove(subObjective);
|
||||||
}
|
}
|
||||||
@@ -199,9 +192,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
protected virtual bool ShouldInterruptSubObjective(AIObjective subObjective) => false;
|
|
||||||
|
|
||||||
public virtual void OnSelected()
|
public virtual void OnSelected()
|
||||||
{
|
{
|
||||||
// Should we reset steering here?
|
// Should we reset steering here?
|
||||||
|
|||||||
@@ -125,6 +125,31 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
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
|
#endif
|
||||||
abandon = true;
|
abandon = true;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -74,21 +74,6 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
}
|
}
|
||||||
|
if (component.AIOperate(deltaTime, character, this))
|
||||||
|
{
|
||||||
|
isCompleted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ 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";
|
||||||
@@ -21,21 +20,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private float treatmentTimer;
|
private float treatmentTimer;
|
||||||
|
|
||||||
public override bool CanBeCompleted
|
public override bool CanBeCompleted => base.CanBeCompleted && AIObjectiveRescueAll.IsValidTarget(targetCharacter, character);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (targetCharacter.Removed ||
|
|
||||||
targetCharacter.IsDead ||
|
|
||||||
targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.VitalityThreshold)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (goToObjective != null && !goToObjective.CanBeCompleted) { return false; }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
public AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||||
: base(character, objectiveManager, priorityModifier)
|
: base(character, objectiveManager, priorityModifier)
|
||||||
@@ -52,14 +37,19 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
//target in water -> move to a dry place first
|
// Target is not in a safe place -> Move to a safe place first
|
||||||
if (targetCharacter.AnimController.InWater)
|
if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||||
{
|
{
|
||||||
if (character.SelectedCharacter != targetCharacter)
|
if (character.SelectedCharacter != targetCharacter)
|
||||||
{
|
{
|
||||||
|
// Go to the target and select it
|
||||||
if (!character.CanInteractWith(targetCharacter))
|
if (!character.CanInteractWith(targetCharacter))
|
||||||
{
|
{
|
||||||
AddSubObjective(goToObjective = new AIObjectiveGoTo(targetCharacter, character, objectiveManager));
|
if (goToObjective != null && goToObjective.Target != targetCharacter)
|
||||||
|
{
|
||||||
|
goToObjective = null;
|
||||||
|
}
|
||||||
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -68,15 +58,28 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddSubObjective(new AIObjectiveFindSafety(character, objectiveManager));
|
// Drag the character into safety
|
||||||
|
if (goToObjective != null && goToObjective.Target == targetCharacter)
|
||||||
|
{
|
||||||
|
goToObjective = null;
|
||||||
|
}
|
||||||
|
var findSafety = objectiveManager.GetObjective<AIObjectiveFindSafety>();
|
||||||
|
if (findSafety == null)
|
||||||
|
{
|
||||||
|
// Ensure that we have the find safety objective (should always be the case)
|
||||||
|
objectiveManager.AddObjective(new AIObjectiveFindSafety(character, objectiveManager));
|
||||||
|
}
|
||||||
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(findSafety.FindBestHull(), character, objectiveManager));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//target not in water -> we can start applying treatment
|
if (subObjectives.Any()) { return; }
|
||||||
|
|
||||||
|
// We can start applying treatment
|
||||||
if (!character.CanInteractWith(targetCharacter))
|
if (!character.CanInteractWith(targetCharacter))
|
||||||
{
|
{
|
||||||
AddSubObjective(goToObjective = new AIObjectiveGoTo(targetCharacter, character, objectiveManager));
|
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -87,32 +90,12 @@ namespace Barotrauma
|
|||||||
null, 1.0f,
|
null, 1.0f,
|
||||||
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
|
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
character.SelectCharacter(targetCharacter);
|
character.SelectCharacter(targetCharacter);
|
||||||
GiveTreatment(deltaTime);
|
GiveTreatment(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove and replace with the priority system
|
// TODO: consider optimizing a bit
|
||||||
protected override bool ShouldInterruptSubObjective(AIObjective subObjective)
|
|
||||||
{
|
|
||||||
if (subObjective is AIObjectiveFindSafety)
|
|
||||||
{
|
|
||||||
if (character.SelectedCharacter != targetCharacter) return true;
|
|
||||||
if (character.AnimController.InWater || targetCharacter.AnimController.InWater) return false;
|
|
||||||
|
|
||||||
foreach (Limb limb in targetCharacter.AnimController.Limbs)
|
|
||||||
{
|
|
||||||
if (!Submarine.RectContains(targetCharacter.CurrentHull.WorldRect, limb.WorldPosition)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !character.AnimController.InWater && !targetCharacter.AnimController.InWater &&
|
|
||||||
HumanAIController.GetHullSafety(character.CurrentHull, character) > HumanAIController.HULL_SAFETY_THRESHOLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GiveTreatment(float deltaTime)
|
private void GiveTreatment(float deltaTime)
|
||||||
{
|
{
|
||||||
if (treatmentTimer > 0.0f)
|
if (treatmentTimer > 0.0f)
|
||||||
@@ -129,7 +112,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return Math.Sign(a2.GetVitalityDecrease(targetCharacter.CharacterHealth) - a1.GetVitalityDecrease(targetCharacter.CharacterHealth));
|
return Math.Sign(a2.GetVitalityDecrease(targetCharacter.CharacterHealth) - a1.GetVitalityDecrease(targetCharacter.CharacterHealth));
|
||||||
});
|
});
|
||||||
|
|
||||||
//check if we already have a suitable treatment for any of the afflictions
|
//check if we already have a suitable treatment for any of the afflictions
|
||||||
foreach (Affliction affliction in allAfflictions)
|
foreach (Affliction affliction in allAfflictions)
|
||||||
{
|
{
|
||||||
@@ -144,7 +126,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//didn't have any suitable treatments available, try to find some medical items
|
//didn't have any suitable treatments available, try to find some medical items
|
||||||
HashSet<string> suitableItemIdentifiers = new HashSet<string>();
|
HashSet<string> suitableItemIdentifiers = new HashSet<string>();
|
||||||
foreach (Affliction affliction in allAfflictions)
|
foreach (Affliction affliction in allAfflictions)
|
||||||
@@ -167,9 +148,8 @@ namespace Barotrauma
|
|||||||
itemNameList.Add(itemPrefab.Name);
|
itemNameList.Add(itemPrefab.Name);
|
||||||
}
|
}
|
||||||
//only list the first 4 items
|
//only list the first 4 items
|
||||||
if (itemNameList.Count >= 4) break;
|
if (itemNameList.Count >= 4) { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemNameList.Count > 0)
|
if (itemNameList.Count > 0)
|
||||||
{
|
{
|
||||||
string itemListStr = "";
|
string itemListStr = "";
|
||||||
@@ -186,30 +166,29 @@ namespace Barotrauma
|
|||||||
.Replace("[treatmentlist]", itemListStr),
|
.Replace("[treatmentlist]", itemListStr),
|
||||||
null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f);
|
null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
character.DeselectCharacter();
|
character.DeselectCharacter();
|
||||||
AddSubObjective(new AIObjectiveGetItem(character, suitableItemIdentifiers.ToArray(), objectiveManager, equip: true));
|
AddSubObjective(new AIObjectiveGetItem(character, suitableItemIdentifiers.ToArray(), objectiveManager, equip: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyTreatment(Affliction affliction, Item item)
|
private void ApplyTreatment(Affliction affliction, Item item)
|
||||||
{
|
{
|
||||||
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
|
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
|
||||||
|
|
||||||
bool remove = false;
|
bool remove = false;
|
||||||
foreach (ItemComponent ic in item.Components)
|
foreach (ItemComponent ic in item.Components)
|
||||||
{
|
{
|
||||||
if (!ic.HasRequiredContainedItems(addMessage: false)) continue;
|
if (!ic.HasRequiredContainedItems(addMessage: false)) { continue; }
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
ic.PlaySound(ActionType.OnUse, character.WorldPosition, character);
|
ic.PlaySound(ActionType.OnUse, character.WorldPosition, character);
|
||||||
#endif
|
#endif
|
||||||
ic.WasUsed = true;
|
ic.WasUsed = true;
|
||||||
ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb);
|
ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb);
|
||||||
if (ic.DeleteOnUse) remove = true;
|
if (ic.DeleteOnUse)
|
||||||
|
{
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remove)
|
if (remove)
|
||||||
{
|
{
|
||||||
Entity.Spawner?.AddToRemoveQueue(item);
|
Entity.Spawner?.AddToRemoveQueue(item);
|
||||||
@@ -218,29 +197,26 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
bool isCompleted =
|
bool isCompleted = targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.VitalityThreshold;
|
||||||
targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.VitalityThreshold;
|
|
||||||
|
|
||||||
if (isCompleted)
|
if (isCompleted)
|
||||||
{
|
{
|
||||||
character?.Speak(TextManager.Get("DialogTargetHealed").Replace("[targetname]", targetCharacter.Name),
|
character?.Speak(TextManager.Get("DialogTargetHealed").Replace("[targetname]", targetCharacter.Name),
|
||||||
null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f);
|
null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f);
|
||||||
}
|
}
|
||||||
|
return isCompleted || targetCharacter.Removed || targetCharacter.IsDead;
|
||||||
return isCompleted || targetCharacter.IsDead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override float GetPriority()
|
public override float GetPriority()
|
||||||
{
|
{
|
||||||
// TODO: review
|
if (targetCharacter.CurrentHull == null || targetCharacter.IsDead) { return 0; ; }
|
||||||
if (targetCharacter.AnimController.CurrentHull == null || targetCharacter.IsDead) { return 0.0f; }
|
// Don't go into rooms that have enemies
|
||||||
|
if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(c))) { return 0; }
|
||||||
Vector2 diff = targetCharacter.WorldPosition - character.WorldPosition;
|
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||||
float distance = Math.Abs(diff.X) + Math.Abs(diff.Y);
|
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
|
||||||
|
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist));
|
||||||
float vitalityFactor = (targetCharacter.MaxVitality - targetCharacter.Vitality) / targetCharacter.MaxVitality;
|
float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter);
|
||||||
|
float devotion = Math.Max(Priority, 10) / 100;
|
||||||
return 1000.0f * vitalityFactor / distance;
|
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -791,6 +791,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,33 +212,6 @@ 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>();
|
||||||
|
|
||||||
|
|||||||
@@ -1568,6 +1568,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; }
|
||||||
|
|||||||
@@ -512,6 +512,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