(18eb9f712) Fix bots stealing items if they happen to target them before somebody equips them.

This commit is contained in:
Joonas Rikkonen
2019-05-16 06:19:10 +03:00
parent df89114850
commit e8ee3f9d18
27 changed files with 338 additions and 421 deletions

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.0")]
[assembly: AssemblyFileVersion("0.9.0.0")]
[assembly: AssemblyVersion("0.8.10.0")]
[assembly: AssemblyFileVersion("0.8.10.0")]

View File

@@ -61,14 +61,6 @@ namespace Barotrauma
var path = pathSteering.CurrentPath;
if (path != null)
{
if (path.CurrentNode != null)
{
GUI.DrawLine(spriteBatch, pos,
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.BlueViolet, 0, 3);
GUI.DrawString(spriteBatch, pos + textOffset - new Vector2(0, 20), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
}
for (int i = 1; i < path.Nodes.Count; i++)
{
var previousNode = path.Nodes[i - 1];
@@ -83,6 +75,14 @@ namespace Barotrauma
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
Color.Blue);
}
if (path.CurrentNode != null)
{
GUI.DrawLine(spriteBatch, pos,
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.BlueViolet, 0, 3);
GUI.DrawString(spriteBatch, pos + textOffset - new Vector2(0, 20), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
}
}
}
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);

View File

@@ -121,90 +121,7 @@ namespace Barotrauma
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
character.SelectedConstruction = character.MemState[0].SelectedItem;
}
if (character.MemState[0].Animation == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.CPR;
}
else if (character.AnimController.Anim == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.None;
}
Vector2 newVelocity = Collider.LinearVelocity;
Vector2 newPosition = Collider.SimPosition;
float newRotation = Collider.Rotation;
float newAngularVelocity = Collider.AngularVelocity;
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
Collider.LinearVelocity = newVelocity;
Collider.AngularVelocity = newAngularVelocity;
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
float errorTolerance = character.AllowInput ? 0.01f : 0.2f;
if (distSqrd > errorTolerance)
{
if (distSqrd > 10.0f || !character.AllowInput)
{
Collider.TargetRotation = newRotation;
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
}
else
{
Collider.TargetRotation = newRotation;
Collider.TargetPosition = newPosition;
Collider.MoveToTargetPosition(true);
}
}
//unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually
if (!character.AllowInput)
{
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
float mainLimbErrorTolerance = 0.1f;
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
//don't attempt to correct the position.
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
{
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
}
}
character.MemLocalState.Clear();
}
else
{
//remove states with a timestamp (there may still timestamp-based states
//in the list if the controlled character switches from timestamp-based interpolation to ID-based)
character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
for (int i = 0; i < character.MemLocalState.Count; i++)
{
if (character.Submarine == null)
{
//transform in-sub coordinates to outside coordinates
if (character.MemLocalState[i].Position.Y > lowestSubPos)
{
character.MemLocalState[i].TransformInToOutside();
}
}
else if (currentHull?.Submarine != null)
{
//transform outside coordinates to in-sub coordinates
if (character.MemLocalState[i].Position.Y < lowestSubPos)
{
character.MemLocalState[i].TransformOutToInside(currentHull.Submarine);
}
}
}
character.MemLocalState.Clear();
}

View File

@@ -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

View File

@@ -478,6 +478,8 @@ namespace Barotrauma
private GUILayoutGroup subPreviewContainer;
private GUILayoutGroup subPreviewContainer;
private GUIButton loadGameButton;
public Action<Submarine, string, string> StartNewGame;

View File

@@ -789,10 +789,6 @@ namespace Barotrauma
GUI.Draw(Cam, spriteBatch);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, GameMain.ScissorTestEnable);
GUI.Draw(Cam, spriteBatch);
GUI.Draw(Cam, spriteBatch);
#if DEBUG

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.0")]
[assembly: AssemblyFileVersion("0.9.0.0")]
[assembly: AssemblyVersion("0.8.10.0")]
[assembly: AssemblyFileVersion("0.8.10.0")]

View File

@@ -31,6 +31,41 @@ Game port (used to communicate with clients)
Internal Port: The port you have selected for your server (27015 by default)
Protocol: UDP
Query port (used to communicate with Steam)
Service/Application: barotrauma
External Port: The port you have selected for your server (27016 by default)
Internal Port: The port you have selected for your server (27016 by default)
Protocol: UDP
------------------------------------------------------------------------
------------------------------------------------------------------------
Credits:
------------------------------------------------------------------------
Programming, graphics, sounds, game design - Joonas Rikkonen ("Regalis")
Graphics - James Bear ("Moonsaber99")
Programming - Sebastian Broberg
Programming - Juan Pablo Arce ("juanjp600")
------------------------------------------------------------------------
"Road to Hell"
Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0
http://creativecommons.org/licenses/by/3.0/
------------------------------------------------------------------------
Lidgren.Network networking library - Michael Lidgren
------------------------------------------------------------------------
Sound effects:
metal whining and screeching - ScottFerguson1
metal gate 10 - thencamenow
foil_expanding_multiple - markb
Bug Zapper_Many medium zaps - CGEffex
Query port (used to communicate with Steam)
Service/Application: barotrauma
External Port: The port you have selected for your server (27016 by default)

View File

@@ -493,20 +493,25 @@ namespace Barotrauma
item.ContainedItems.Any(i => i.HasTag(containedTag) && i.ConditionPercentage > conditionPercentage)));
}
/// <summary>
/// Updates the hull safety for all ai characters in the team.
/// </summary>
public static void PropagateHullSafety(Character character, Hull hull)
public static void DoForEachCrewMember(Character character, Action<HumanAIController> action)
{
foreach (var c in Character.CharacterList)
{
if (c.AIController is HumanAIController humanAi && humanAi.IsFriendly(character))
{
humanAi.RefreshHullSafety(hull);
action(humanAi);
}
}
}
/// <summary>
/// Updates the hull safety for all ai characters in the team.
/// </summary>
public static void PropagateHullSafety(Character character, Hull hull)
{
DoForEachCrewMember(character, (humanAi) => humanAi.RefreshHullSafety(hull));
}
private void RefreshHullSafety(Hull hull)
{
if (GetHullSafety(hull) > HULL_SAFETY_THRESHOLD)
@@ -577,21 +582,26 @@ namespace Barotrauma
private static bool AddTargets<T1, T2>(Character caller, T2 target) where T1 : AIObjectiveLoop<T2>
{
bool targetAdded = false;
foreach (var c in Character.CharacterList)
DoForEachCrewMember(caller, humanAI =>
{
if (IsFriendly(caller, c) && c.AIController is HumanAIController humanAI)
var objective = humanAI.ObjectiveManager.GetObjective<T1>();
if (objective != null)
{
var objective = humanAI.ObjectiveManager.GetObjective<T1>();
if (objective == null) { continue; }
if (!targetAdded && objective.AddTarget(target))
{
targetAdded = true;
}
}
}
});
return targetAdded;
}
public static void RemoveTargets<T1, T2>(Character caller, T2 target) where T1 : AIObjectiveLoop<T2>
{
DoForEachCrewMember(caller, humanAI =>
humanAI.ObjectiveManager.GetObjective<T1>()?.ReportedTargets.Remove(target));
}
public float GetHullSafety(Hull hull)
{
if (hull == null) { return 0; }

View File

@@ -71,6 +71,7 @@ namespace Barotrauma
#if DEBUG
DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it is completed.");
#endif
subObjective.OnCompleted();
subObjectives.Remove(subObjective);
}
else if (!subObjective.CanBeCompleted)
@@ -93,9 +94,9 @@ namespace Barotrauma
bool wasCompleted = IsCompleted();
Act(deltaTime);
if (!wasCompleted && IsCompleted())
if (IsCompleted())
{
Completed?.Invoke();
OnCompleted();
}
}
@@ -213,6 +214,15 @@ namespace Barotrauma
//}
}
protected virtual void OnCompleted()
{
if (Completed != null)
{
Completed();
Completed = null;
}
}
public virtual void Reset() { }
protected abstract void Act(float deltaTime);

View File

@@ -65,5 +65,8 @@ namespace Barotrauma
protected override AIObjective ObjectiveConstructor(PowerContainer battery)
=> new AIObjectiveOperateItem(battery, character, objectiveManager, Option, false, priorityModifier: PriorityModifier) { IsLoop = false };
protected override void OnObjectiveCompleted(AIObjective objective, PowerContainer target)
=> HumanAIController.RemoveTargets<AIObjectiveChargeBatteries, PowerContainer>(character, target);
}
}

View File

@@ -1,6 +1,7 @@
using System.Linq;
using System.Collections.Generic;
using Barotrauma.Extensions;
using System;
namespace Barotrauma
{
@@ -20,7 +21,11 @@ namespace Barotrauma
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveExtinguishFires;
protected override IEnumerable<Hull> GetList() => Hull.hullList;
protected override AIObjective ObjectiveConstructor(Hull target) => new AIObjectiveExtinguishFire(character, target, objectiveManager, PriorityModifier);
protected override AIObjective ObjectiveConstructor(Hull target)
=> new AIObjectiveExtinguishFire(character, target, objectiveManager, PriorityModifier);
protected override void OnObjectiveCompleted(AIObjective objective, Hull target)
=> HumanAIController.RemoveTargets<AIObjectiveExtinguishFires, Hull>(character, target);
public static bool IsValidTarget(Hull hull, Character character)
{

View File

@@ -21,14 +21,18 @@ namespace Barotrauma
protected override IEnumerable<Character> GetList() => Character.CharacterList;
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);
protected override float TargetEvaluation()
{
// TODO: sorting criteria
return 100;
}
protected override AIObjective ObjectiveConstructor(Character target)
=> new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
=> HumanAIController.RemoveTargets<AIObjectiveFightIntruders, Character>(character, target);
public static bool IsValidTarget(Character target, Character character)
{
if (target == null || target.IsDead || target.Removed) { return false; }

View File

@@ -773,6 +773,32 @@ namespace Barotrauma
{
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
}
if (subObjectives.Any()) { return; }
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;
}
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 * 1.5f;
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 });
}
}
private Vector2 GetStandPosition()

View File

@@ -27,7 +27,11 @@ namespace Barotrauma
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFixLeaks;
protected override float TargetEvaluation() => Targets.Max(t => GetLeakSeverity(t));
protected override IEnumerable<Gap> GetList() => Gap.GapList;
protected override AIObjective ObjectiveConstructor(Gap gap) => new AIObjectiveFixLeak(gap, character, objectiveManager, PriorityModifier);
protected override AIObjective ObjectiveConstructor(Gap gap)
=> new AIObjectiveFixLeak(gap, character, objectiveManager, PriorityModifier);
protected override void OnObjectiveCompleted(AIObjective objective, Gap target)
=> HumanAIController.RemoveTargets<AIObjectiveFixLeaks, Gap>(character, target);
public static bool IsValidTarget(Gap gap, Character character)
{

View File

@@ -103,37 +103,44 @@ namespace Barotrauma
if (moveToTarget.CurrentHull == character.CurrentHull &&
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance, 2))
{
int targetSlot = -1;
if (equip)
if (IsTakenBySomeone(targetItem))
{
var pickable = targetItem.GetComponent<Pickable>();
if (pickable == null)
abandon = true;
}
else
{
int targetSlot = -1;
if (equip)
{
abandon = true;
return;
}
//check if all the slots required by the item are free
foreach (InvSlotType slots in pickable.AllowedSlots)
{
if (slots.HasFlag(InvSlotType.Any)) { continue; }
for (int i = 0; i < character.Inventory.Items.Length; i++)
var pickable = targetItem.GetComponent<Pickable>();
if (pickable == null)
{
//slot not needed by the item, continue
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; }
targetSlot = i;
//slot free, continue
if (character.Inventory.Items[i] == null) { continue; }
//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 everything else fails, simply drop the existing item
character.Inventory.Items[i].Drop(character);
abandon = true;
return;
}
//check if all the slots required by the item are free
foreach (InvSlotType slots in pickable.AllowedSlots)
{
if (slots.HasFlag(InvSlotType.Any)) { continue; }
for (int i = 0; i < character.Inventory.Items.Length; i++)
{
//slot not needed by the item, continue
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; }
targetSlot = i;
//slot free, continue
if (character.Inventory.Items[i] == null) { continue; }
//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 everything else fails, simply drop the existing item
character.Inventory.Items[i].Drop(character);
}
}
}
}
targetItem.TryInteract(character, false, true);
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
{
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
targetItem.TryInteract(character, false, true);
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
{
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
}
}
}
else
@@ -185,23 +192,7 @@ namespace Barotrauma
{
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
}
// TODO: need to exclude items that already are in the inventory?
//// Don't allow to get items in rooms that have a fire (except fire extinguishers) or an enemy inside (except weapons)
//if (!itemIdentifiers.Contains("extinguisher") && item.CurrentHull.FireSources.Count > 0 ||
// item.GetComponent<MeleeWeapon>() == null && item.GetComponent<RangedWeapon>() == null && Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { continue; }
//if the item is inside a character's inventory, don't steal it unless the character is dead
if (item.ParentInventory is CharacterInventory)
{
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
Item rootContainer = item.GetRootContainer();
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
{
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
}
if (IsTakenBySomeone(item)) { continue; }
float itemPriority = 0.0f;
if (GetItemPriority != null)
{
@@ -209,6 +200,7 @@ namespace Barotrauma
itemPriority = GetItemPriority(item);
if (itemPriority <= 0.0f) { continue; }
}
Item rootContainer = item.GetRootContainer();
itemPriority -= Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
//ignore if the item has a lower priority than the currently selected one
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
@@ -271,10 +263,29 @@ namespace Barotrauma
{
bool isEquipped = !equip || character.HasEquippedItem(item);
if (character.Inventory.Items.Contains(item) && isEquipped) { return true; }
if (!equip)
{
Item rootContainer = item.GetRootContainer();
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
{
return rootContainer.ParentInventory.Owner == character;
}
}
return false;
}
public static bool IsTakenBySomeone(Item item)
{
//if the item is inside a character's inventory, don't steal it unless the character is dead
if (item.ParentInventory is CharacterInventory)
{
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { return true; }
}
//if the item is inside an item, which is inside a character's inventory, don't steal it unless the character is dead
Item rootContainer = item.GetRootContainer();
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
{
return rootContainer.ParentInventory.Owner == character;
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { return true; }
}
return false;
}

View File

@@ -73,21 +73,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 CanBeCompleted => true;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
@@ -87,7 +88,7 @@ namespace Barotrauma
}
}
// the timer is set between 1 and 10 seconds, depending on the priority
// the timer is set between 1 and 10 seconds, depending on the priority modifier and a random +-25%
private float SetTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1);
public override void Reset()
@@ -158,13 +159,18 @@ namespace Barotrauma
if (!Objectives.TryGetValue(target, out AIObjective objective))
{
objective = ObjectiveConstructor(target);
objective.Completed += () => ReportedTargets.Remove(target);
objective.Completed += () => OnObjectiveCompleted(objective, target);
Objectives.Add(target, objective);
AddSubObjective(objective);
if (!subObjectives.Contains(objective))
{
subObjectives.Add(objective);
}
}
}
}
protected abstract void OnObjectiveCompleted(AIObjective objective, T target);
/// <summary>
/// List of all possible items of the specified type. Used for filtering the removed objectives.
/// </summary>

View File

@@ -358,6 +358,10 @@ namespace Barotrauma
{
isCompleted = true;
}
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
}
else
{

View File

@@ -54,7 +54,6 @@ namespace Barotrauma
return pumpList;
}
protected override AIObjective ObjectiveConstructor(Pump pump) => new AIObjectiveOperateItem(pump, character, objectiveManager, Option, false) { IsLoop = false };
protected override float TargetEvaluation()
{
if (Option == "stoppumping")
@@ -66,5 +65,11 @@ namespace Barotrauma
return Targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(-t.FlowPercentage / 100)));
}
}
protected override AIObjective ObjectiveConstructor(Pump pump)
=> new AIObjectiveOperateItem(pump, character, objectiveManager, Option, false) { IsLoop = false };
protected override void OnObjectiveCompleted(AIObjective objective, Pump target)
=> HumanAIController.RemoveTargets<AIObjectivePumpWater, Pump>(character, target);
}
}

View File

@@ -54,7 +54,12 @@ namespace Barotrauma
protected override float TargetEvaluation() => Targets.Max(t => 100 - t.ConditionPercentage);
protected override IEnumerable<Item> GetList() => Item.ItemList;
protected override AIObjective ObjectiveConstructor(Item item) => new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier);
protected override AIObjective ObjectiveConstructor(Item item)
=> new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier);
protected override void OnObjectiveCompleted(AIObjective objective, Item target)
=> HumanAIController.RemoveTargets<AIObjectiveRepairItems, Item>(character, target);
public static bool IsValidTarget(Item item, Character character)
{

View File

@@ -22,12 +22,16 @@ namespace Barotrauma
protected override IEnumerable<Character> GetList() => Character.CharacterList;
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
protected override float TargetEvaluation() => Targets.Max(t => GetVitalityFactor(t)) * 100;
public static float GetVitalityFactor(Character character) => (character.MaxVitality - character.Vitality) / character.MaxVitality;
protected override AIObjective ObjectiveConstructor(Character target)
=> new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
=> HumanAIController.RemoveTargets<AIObjectiveRescueAll, Character>(character, target);
public static bool IsValidTarget(Character target, Character character)
{
if (target == null || target.IsDead || target.Removed) { return false; }

View File

@@ -734,6 +734,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);

View File

@@ -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>();

View File

@@ -1128,6 +1128,10 @@ namespace Barotrauma
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
if (!broken)
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
@@ -1172,6 +1176,27 @@ namespace Barotrauma
}
}
public void UpdateTransform()
{
Submarine prevSub = Submarine;
FindHull();
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
if (Math.Abs(body.LinearVelocity.X) > NetConfig.MaxPhysicsBodyVelocity ||
Math.Abs(body.LinearVelocity.Y) > NetConfig.MaxPhysicsBodyVelocity)
{
body.LinearVelocity = new Vector2(
MathHelper.Clamp(body.LinearVelocity.X, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity),
MathHelper.Clamp(body.LinearVelocity.Y, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity));
}
}
public void UpdateTransform()
{
Submarine prevSub = Submarine;

View File

@@ -607,6 +607,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

View File

@@ -4,47 +4,27 @@ v0.9.0.0
Additions and changes:
- Translations to German, Russian, French, Brazilian Portuguese, Traditional Chinese and Simplified Chinese.
- New logo and main menu art.
- 2 new outposts.
- New docking system.
- Numerous fixes and improvements to the crew AI. The biggest change is that the AI characters can now do
tasks by themselves even when they haven't been assigned an order.
- One new outpost.
- New docking system.
- New logo and main menu art.
- Added a credits tab to the main menu.
- Some new item and structure sprites.
- Some new environment sprites.
- Increased the amount of credits the player starts with in the campaign.
- Balanced location and mission distribution.
- Balanced mission rewards.
- AI characters can be ordered to fight intruders.
- Lock the inventory when aiming with a railgun or a coilgun.
- Swap the order of the starting location & destination in nav terminals when playing in a mirrored sub.
- Option to automatically pause the game when the window loses focus.
Bugfixes:
- Fixed splash screens and tutorial videos crashing the Linux version.
- Fixed damaged item sprites not updating client-side after being repaired.
- Fixed characters occasionally taking impact damage when hitting walls while climbing.
- Fixes crashing when attempting to use symbols such as <, > or | in the save file name.
- UI layout/scale fixes on resolutions larger than 1080p.
- Fixed crashing when loading the same sub twice in the sub editor.
- Fixed submarines occasionally being rendered at an incorrect position when viewing them in the sub editor
after they've been used in-game.
- Improved the syncing of ragdolled characters.
- Fixed characters not receiving impact damage when ragdolled.
- Fixed characters' arms occasionally spinning around when standing still.
- Fixed tutorial level generation parameters being used in some normal campaign levels (leading to extremely
small levels and submarines overlapping with the outposts).
- Fixed characters running more slowly when their torso is in a different hull than the feet (for example
in Humpback's bilge).
- Fixed character's feet getting stuck to platforms when climbing ladders while holding A/D.
- Fixed monsters being able to drag characters through walls.
- Fixed items "vanishing" if they move directly from sub to another without going outside first.
- Fixed content package hash calculation failing if the package is not enabled and contains new monster files.
- Fixed inability to enable content packages if some of the files included in the package are already in
the game folder (which may happen, for example, if enabling a content package fails).
- Fixed AllowRagdollButton settings not being synced with clients, leading to strange ragdolling behavior
client-side if the server has disabled ragdolling.
- Fixed incorrect keybinds being displayed in a couple of tutorial texts.
---------------------------------------------------------------------------------------------------------
v0.8.10.0