(63921aa0d) Implement GetVisibleHulls method.

This commit is contained in:
Joonas Rikkonen
2019-05-16 06:49:11 +03:00
parent 97b6888274
commit af7fcb823d
37 changed files with 386 additions and 548 deletions
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.1")] [assembly: AssemblyVersion("0.9.0.0")]
[assembly: AssemblyFileVersion("0.9.0.1")] [assembly: AssemblyFileVersion("0.9.0.0")]
@@ -108,6 +108,61 @@ namespace Barotrauma
} }
} }
//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.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 //unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually // -> we need to correct it manually
if (!character.AllowInput) if (!character.AllowInput)
@@ -151,32 +206,34 @@ namespace Barotrauma
} }
} }
character.MemLocalState.Clear();
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
character.MemState.Clear();
} }
} else
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); } //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)
else if (body.UserData is Limb || body == Collider.FarseerBody) character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
{
if (!character.IsRemotePlayer && impact > ImpactTolerance) for (int i = 0; i < character.MemLocalState.Count; i++)
{ {
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider); 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);
}
}
} }
}
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;
@@ -74,17 +74,12 @@ namespace Barotrauma
public CrewManager(XElement element, bool isSinglePlayer) public CrewManager(XElement element, bool isSinglePlayer)
: this(isSinglePlayer) : this(isSinglePlayer)
{ {
if (GameMain.Client != null) if (!isSinglePlayer)
{ {
//let the server create random conversations in MP DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
return; return;
} }
List<Character> availableSpeakers = Character.CharacterList.FindAll(c => if (string.IsNullOrEmpty(text)) { return; }
c.AIController is HumanAIController &&
!c.IsDead &&
c.SpeechImpediment <= 100.0f);
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
}
var characterInfo = new CharacterInfo(subElement); var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo); characterInfos.Add(characterInfo);
@@ -95,6 +90,7 @@ namespace Barotrauma
break; break;
} }
} }
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
} }
partial void InitProjectSpecific() partial void InitProjectSpecific()
@@ -243,24 +239,27 @@ namespace Barotrauma
public IEnumerable<Character> GetCharacters() public IEnumerable<Character> GetCharacters()
{ {
if (characterInfos.Contains(characterInfo)) if (character?.Inventory == null) return null;
{
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
return;
}
characterInfos.Add(characterInfo); var radioItem = character.Inventory.Items.FirstOrDefault(it => it != null && it.GetComponent<WifiComponent>() != null);
if (radioItem == null) return null;
if (requireEquipped && !character.HasEquippedItem(radioItem)) return null;
return radioItem.GetComponent<WifiComponent>();
} }
public IEnumerable<CharacterInfo> GetCharacterInfos() public IEnumerable<CharacterInfo> GetCharacterInfos()
{ {
if (character == null) if (GameMain.Client != null)
{ {
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace); //let the server create random conversations in MP
return; return;
} }
characters.Remove(character); List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
if (removeInfo) characterInfos.Remove(character.Info); c.AIController is HumanAIController &&
!c.IsDead &&
c.SpeechImpediment <= 100.0f);
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
} }
public void AddCharacter(Character character) public void AddCharacter(Character character)
@@ -634,183 +633,9 @@ namespace Barotrauma
{ {
characterListBox.BarScroll = roundedPos; characterListBox.BarScroll = roundedPos;
} }
var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox") soundIcon.Visible = !muted && !mutedLocally;
{ soundIconDisabled.Visible = muted || mutedLocally;
UserData = character, soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally");
Color = frame.Color,
SelectedColor = frame.SelectedColor,
HoverColor = frame.HoverColor,
ToolTip = characterToolTip
};
var soundIcon = new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) },
"GUISoundIcon")
{
UserData = "soundicon",
CanBeFocused = false,
Visible = true
};
soundIcon.Color = new Color(soundIcon.Color, 0.0f);
new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) },
"GUISoundIconDisabled")
{
UserData = "soundicondisabled",
CanBeFocused = true,
Visible = false
};
if (isSinglePlayer)
{
characterArea.OnClicked = CharacterClicked;
}
else
{
characterArea.CanBeFocused = false;
characterArea.CanBeSelected = false;
}
var characterImage = new GUICustomComponent(new RectTransform(new Point(characterArea.Rect.Height), characterArea.RectTransform, Anchor.CenterLeft),
onDraw: (sb, component) => character.Info.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
{
CanBeFocused = false,
HoverColor = Color.White,
SelectedColor = Color.White,
ToolTip = characterToolTip
};
var characterName = new GUITextBlock(new RectTransform(new Point(characterArea.Rect.Width - characterImage.Rect.Width - soundIcon.Rect.Width - 10, characterArea.Rect.Height),
characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(soundIcon.Rect.Width + 10, 0) },
character.Name, textColor: frame.Color, font: GUI.SmallFont, wrap: true)
{
Color = frame.Color,
HoverColor = Color.Transparent,
SelectedColor = Color.Transparent,
CanBeFocused = false,
ToolTip = characterToolTip,
AutoScale = true
};
//---------------- order buttons ----------------
var orderButtonFrame = new GUILayoutGroup(new RectTransform(new Point(100, frame.Rect.Height), frame.RectTransform)
{ AbsoluteOffset = new Point(characterInfoWidth + spacing, 0) },
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
AbsoluteSpacing = (int)(10 * GUI.Scale),
UserData = "orderbuttons",
CanBeFocused = false
};
//listbox for holding the orders inappropriate for this character
//(so we can easily toggle their visibility)
var wrongOrderList = new GUIListBox(new RectTransform(new Point(50, orderButtonFrame.Rect.Height), orderButtonFrame.RectTransform), isHorizontal: true, style: null)
{
ScrollBarEnabled = false,
ScrollBarVisible = false,
Enabled = false,
Spacing = spacing,
ClampMouseRectToParent = false
};
wrongOrderList.Content.ClampMouseRectToParent = false;
for (int i = 0; i < orders.Count; i++)
{
var order = orders[i];
if (order.TargetAllCharacters) continue;
RectTransform btnParent = (i >= correctOrderCount + neutralOrderCount) ?
wrongOrderList.Content.RectTransform :
orderButtonFrame.RectTransform;
var btn = new GUIButton(new RectTransform(new Point(iconSize, iconSize), btnParent, Anchor.CenterLeft),
style: null)
{
UserData = order
};
new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow")
{
Color = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.8f,
HoverColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 1.0f,
PressedColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.6f,
UserData = "selected",
CanBeFocused = false,
Visible = false
};
var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite);
img.Scale = iconSize / (float)img.SourceRect.Width;
img.Color = Color.Lerp(order.Color, frame.Color, 0.5f);
img.ToolTip = order.Name;
img.HoverColor = Color.Lerp(img.Color, Color.White, 0.5f);
btn.OnClicked += (GUIButton button, object userData) =>
{
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false;
if (btn.GetChildByUserData("selected").Visible)
{
SetCharacterOrder(character, Order.PrefabList.Find(o => o.AITag == "dismissed"), null, Character.Controlled);
}
else
{
if (order.ItemComponentType != null || order.ItemIdentifiers.Length > 0 || order.Options.Length > 1)
{
CreateOrderTargetFrame(button, character, order);
}
else
{
SetCharacterOrder(character, order, null, Character.Controlled);
}
}
return true;
};
btn.UserData = order;
btn.ToolTip = order.Name;
//divider between different groups of orders
if (i == correctOrderCount - 1 || i == correctOrderCount + neutralOrderCount - 1)
{
//TODO: divider sprite
new GUIFrame(new RectTransform(new Point(8, iconSize), orderButtonFrame.RectTransform), style: "GUIButton");
}
}
var toggleWrongOrderBtn = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform),
"", style: "UIToggleButton")
{
UserData = "togglewrongorder",
CanBeFocused = false
};
wrongOrderList.RectTransform.NonScaledSize = new Point(
wrongOrderList.Content.Children.Sum(c => c.Rect.Width + wrongOrderList.Spacing),
wrongOrderList.RectTransform.NonScaledSize.Y);
wrongOrderList.RectTransform.SetAsLastChild();
new GUIFrame(new RectTransform(new Point(
wrongOrderList.Rect.Width - toggleWrongOrderBtn.Rect.Width - wrongOrderList.Spacing * 2,
wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform),
style: null)
{
CanBeFocused = false
};
//scale to fit the content
orderButtonFrame.RectTransform.NonScaledSize = new Point(
orderButtonFrame.Children.Sum(c => c.Rect.Width + orderButtonFrame.AbsoluteSpacing),
orderButtonFrame.RectTransform.NonScaledSize.Y);
frame.RectTransform.NonScaledSize = new Point(
characterInfoWidth + spacing + (orderButtonFrame.Rect.Width - wrongOrderList.Rect.Width),
frame.RectTransform.NonScaledSize.Y);
characterListBox.RectTransform.NonScaledSize = new Point(
characterListBox.Content.Children.Max(c => c.Rect.Width) + wrongOrderList.Rect.Width,
characterListBox.RectTransform.NonScaledSize.Y);
characterListBox.Content.RectTransform.NonScaledSize = characterListBox.RectTransform.NonScaledSize;
characterListBox.UpdateScrollBarSize();
return frame;
} }
private IEnumerable<object> KillCharacterAnim(GUIComponent component) private IEnumerable<object> KillCharacterAnim(GUIComponent component)
@@ -954,12 +779,6 @@ namespace Barotrauma
} }
return; return;
} }
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
c.AIController is HumanAIController &&
!c.IsDead &&
c.SpeechImpediment <= 100.0f);
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
}
character.SetOrder(order, option, orderGiver, speak: orderGiver != character); character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
if (IsSinglePlayer) if (IsSinglePlayer)
@@ -1017,23 +836,19 @@ namespace Barotrauma
} }
} }
} }
//only one target (or an order with no particular targets), just show options
character.SetOrder(order, option, orderGiver, speak: orderGiver != character); else
if (IsSinglePlayer)
{ {
orderGiver?.Speak( orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas)
order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null); { AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) },
} isHorizontal: true, childAnchor: Anchor.BottomLeft)
else if (orderGiver != null)
{
OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver);
if (GameMain.Client != null)
{ {
GameMain.Client.SendChatMessage(msg); UserData = character,
} Stretch = true
} };
DisplayCharacterOrder(character, order); //line connecting the order button to the option buttons
} //TODO: sprite
new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), orderTargetFrame.RectTransform), style: null);
/// <summary> /// <summary>
/// Create the UI panel that's used to select the target and options for a given order /// Create the UI panel that's used to select the target and options for a given order
@@ -53,9 +53,9 @@ namespace Barotrauma.Tutorials
doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>(); doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>();
doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>(); doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>();
var patientHull1 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "waitingroom").CurrentHull; var patientHull1 = Hull.hullList.Find(h => h.RoomName == "Waiting room" && h.Submarine == doctor.Submarine);
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull; var patientHull2 = Hull.hullList.Find(h => h.RoomName == "Airlock" && h.Submarine == doctor.Submarine);
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull; medBay = Hull.hullList.Find(h => h.RoomName == "Med bay" && h.Submarine == doctor.Submarine);
var assistantInfo = new CharacterInfo(Character.HumanConfigFile, "", JobPrefab.List.Find(jp => jp.Identifier == "assistant")); var assistantInfo = new CharacterInfo(Character.HumanConfigFile, "", JobPrefab.List.Find(jp => jp.Identifier == "assistant"));
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1"); patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
@@ -36,7 +36,6 @@ namespace Barotrauma.Items.Components
{ {
UserData = ciElement UserData = ciElement
}; };
tickBox.TextBlock.AutoScale = true;
tickBox.OnSelected += (tBox) => tickBox.OnSelected += (tBox) =>
{ {
if (GameMain.Client == null) if (GameMain.Client == null)
@@ -58,7 +57,6 @@ namespace Barotrauma.Items.Components
{ {
UserData = ciElement UserData = ciElement
}; };
btn.TextBlock.AutoScale = true;
btn.OnClicked += (_, userdata) => btn.OnClicked += (_, userdata) =>
{ {
if (GameMain.Client == null) if (GameMain.Client == null)
@@ -1114,10 +1114,8 @@ namespace Barotrauma
//---------------------------------------- //----------------------------------------
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ? var itemPrefab = MapEntityPrefab.Find(itemName, itemIdentifier) as ItemPrefab;
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab : if (itemPrefab == null) return null;
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab;
if (itemPrefab == null) { return null; }
Inventory inventory = null; Inventory inventory = null;
@@ -686,6 +686,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;
@@ -9,6 +9,7 @@ namespace Barotrauma
private GUIListBox listBox; private GUIListBox listBox;
private readonly float scrollSpeed; private readonly float scrollSpeed;
public CreditsPlayer(RectTransform rectT, string configFile) : base(null, rectT) public CreditsPlayer(RectTransform rectT, string configFile) : base(null, rectT)
{ {
var doc = XMLExtensions.TryLoadXml(configFile); var doc = XMLExtensions.TryLoadXml(configFile);
@@ -113,11 +114,6 @@ namespace Barotrauma
} }
} }
public void Restart()
{
listBox.BarScroll = 0.0f;
}
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
{ {
listBox.BarScroll += scrollSpeed / listBox.TotalSize * deltaTime; listBox.BarScroll += scrollSpeed / listBox.TotalSize * deltaTime;
@@ -470,7 +470,6 @@ namespace Barotrauma
break; break;
case Tab.Credits: case Tab.Credits:
titleText.Visible = false; titleText.Visible = false;
creditsPlayer.Restart();
break; break;
} }
} }
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.1")] [assembly: AssemblyVersion("0.9.0.0")]
[assembly: AssemblyFileVersion("0.9.0.1")] [assembly: AssemblyFileVersion("0.9.0.0")]
@@ -169,7 +169,15 @@ namespace Barotrauma
bool highPressure = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0; bool highPressure = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0;
bool shouldKeepTheGearOn = !ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>(); bool shouldKeepTheGearOn = !ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>();
bool removeDivingSuit = (oxygenLow && !highPressure) || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs); // Don't allow to drop the diving suit in water or while climbing or if the current path has stairs
bool removeDivingSuit =
(oxygenLow && !highPressure) ||
(!shouldKeepTheGearOn &&
Character.CurrentHull.WaterPercentage < 1 &&
!Character.IsClimbing &&
steeringManager == insideSteering &&
!PathSteering.InStairs);
if (removeDivingSuit) if (removeDivingSuit)
{ {
var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit"); var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit");
@@ -495,8 +503,10 @@ namespace Barotrauma
public static void DoForEachCrewMember(Character character, Action<HumanAIController> action) public static void DoForEachCrewMember(Character character, Action<HumanAIController> action)
{ {
if (character == null) { return; }
foreach (var c in Character.CharacterList) foreach (var c in Character.CharacterList)
{ {
if (c == null || c.IsDead || c.Removed) { continue; }
if (c.AIController is HumanAIController humanAi && humanAi.IsFriendly(character)) if (c.AIController is HumanAIController humanAi && humanAi.IsFriendly(character))
{ {
action(humanAi); action(humanAi);
@@ -53,13 +53,9 @@ namespace Barotrauma
(currentPath.NextNode != null && currentPath.NextNode.Ladders != null)); (currentPath.NextNode != null && currentPath.NextNode.Ladders != null));
/// <summary> /// <summary>
/// Returns true if the previous, current or the next node is in stairs. /// Returns true if any node in the path is in stairs
/// </summary> /// </summary>
public bool InStairs => public bool InStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
currentPath != null && (
currentPath.PrevNode != null && currentPath.PrevNode.Stairs != null ||
currentPath.CurrentNode != null && currentPath.CurrentNode.Stairs != null ||
currentPath.NextNode != null && currentPath.NextNode.Stairs != null);
public bool IsNextNodeLadder public bool IsNextNodeLadder
{ {
@@ -304,7 +300,9 @@ namespace Barotrauma
float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X); float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X);
bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y; bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y;
bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight; bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight;
if (currentPath.CurrentNode != null && currentPath.CurrentNode.Stairs != null) float margin = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 10, 0, 1));
float targetDistance = collider.radius * margin;
if (horizontalDistance < targetDistance && isAboveFeet && isNotTooHigh)
{ {
float multiplierX = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 10, 0, 1)); float multiplierX = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 10, 0, 1));
float multiplierY = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.Y) / 10, 0, 1)); float multiplierY = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.Y) / 10, 0, 1));
@@ -1141,6 +1141,32 @@ 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 * 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 });
}
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 #endif
abandon = true; abandon = true;
return; return;
@@ -73,6 +73,21 @@ namespace Barotrauma
} }
} }
public override void Update(float deltaTime)
{
if (objectiveManager.CurrentObjective == this)
{
if (randomTimer > 0)
{
randomTimer -= deltaTime;
}
else
{
SetRandom();
}
}
}
public override bool IsCompleted() => false; public override bool IsCompleted() => false;
public override bool CanBeCompleted => true; public override bool CanBeCompleted => true;
@@ -773,6 +773,10 @@ namespace Barotrauma
{ {
isCompleted = true; isCompleted = true;
} }
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
} }
else else
{ {
@@ -97,12 +97,11 @@ namespace Barotrauma
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}"; public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}";
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/"; public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) => public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml";
$"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml";
public static string GetFolder(string speciesName, ContentPackage contentPackage = null) public static string GetFolder(string speciesName)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty); var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
{ {
folder = GetDefaultFolder(speciesName); folder = GetDefaultFolder(speciesName);
@@ -64,7 +64,7 @@ namespace Barotrauma
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll"; public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Ragdolls/"; public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Ragdolls/";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml"; public static string GetDefaultFile(string speciesName) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName)}.xml";
private static readonly object[] dummyParams = new object[] private static readonly object[] dummyParams = new object[]
{ {
@@ -79,9 +79,9 @@ namespace Barotrauma
new XAttribute("sourcerect", $"0, 0, 1, 1"))) new XAttribute("sourcerect", $"0, 0, 1, 1")))
}; };
public static string GetFolder(string speciesName, ContentPackage contentPackage = null) public static string GetFolder(string speciesName)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty); var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
{ {
//DebugConsole.NewMessage("[RagollParams] Using the default folder."); //DebugConsole.NewMessage("[RagollParams] Using the default folder.");
@@ -853,26 +853,15 @@ namespace Barotrauma
{ {
if (characterConfigFiles == null) if (characterConfigFiles == null)
{ {
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character); characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true);
} }
return characterConfigFiles; return characterConfigFiles;
} }
} }
public static string GetConfigFile(string speciesName, ContentPackage contentPackage = null) public static string GetConfigFile(string speciesName)
{ {
string configFile = null; string configFile = CharacterConfigFiles.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml");
if (contentPackage == null)
{
configFile = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true)
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml");
}
else
{
configFile = contentPackage.GetFilesOfType(ContentType.Character)?
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml");
}
if (configFile == null) if (configFile == null)
{ {
DebugConsole.ThrowError($"Couldn't find a config file for {speciesName} from the selected content packages!"); DebugConsole.ThrowError($"Couldn't find a config file for {speciesName} from the selected content packages!");
@@ -1147,33 +1136,7 @@ namespace Barotrauma
if (!(this is AICharacter) || Controlled == this || IsRemotePlayer) if (!(this is AICharacter) || Controlled == this || IsRemotePlayer)
{ {
SmoothedCursorPosition = cursorPosition; Vector2 targetMovement = GetTargetMovement();
}
else
{
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
}
if (!(this is AICharacter) || Controlled == this || IsRemotePlayer)
{
SmoothedCursorPosition = cursorPosition;
}
else
{
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
}
if (!(this is AICharacter) || Controlled == this || IsRemotePlayer)
{
if (speedMultipliers.Count == 0) return 1f;
AnimController.TargetMovement = targetMovement; AnimController.TargetMovement = targetMovement;
AnimController.IgnorePlatforms = AnimController.TargetMovement.Y < -0.1f; AnimController.IgnorePlatforms = AnimController.TargetMovement.Y < -0.1f;
@@ -2681,5 +2644,57 @@ namespace Barotrauma
} }
public AttackContext GetAttackContext() => AnimController.CurrentAnimationParams.IsGroundedAnimation ? AttackContext.Ground : AttackContext.Water; public AttackContext GetAttackContext() => AnimController.CurrentAnimationParams.IsGroundedAnimation ? AttackContext.Ground : AttackContext.Water;
private List<Hull> visibleHulls = new List<Hull>();
private HashSet<Hull> tempList = new HashSet<Hull>();
/// <summary>
/// Returns hulls that are visible to the player, including the current hull.
/// Can be heavy if used every frame.
/// </summary>
public List<Hull> GetVisibleHulls()
{
visibleHulls.Clear();
tempList.Clear();
if (CurrentHull != null)
{
visibleHulls.Add(CurrentHull);
var adjacentHulls = CurrentHull.GetConnectedHulls(true, 1);
float maxDistance = 1000f;
foreach (var hull in adjacentHulls)
{
if (hull.ConnectedGaps.Any(g => g.Open > 0.9f && g.linkedTo.Contains(CurrentHull) &&
Vector2.DistanceSquared(g.WorldPosition, WorldPosition) < Math.Pow(maxDistance / 2, 2)))
{
if (Vector2.DistanceSquared(hull.WorldPosition, WorldPosition) < Math.Pow(maxDistance, 2))
{
visibleHulls.Add(hull);
}
}
}
visibleHulls.AddRange(CurrentHull.GetLinkedEntities<Hull>(tempList, filter: h =>
{
// Ignore adjacent hulls because they were already handled above
if (adjacentHulls.Contains(h))
{
return false;
}
else
{
if (h.ConnectedGaps.Any(g =>
g.Open > 0.9f &&
Vector2.DistanceSquared(g.WorldPosition, WorldPosition) < Math.Pow(maxDistance / 2, 2) &&
CanSeeTarget(g)))
{
return Vector2.DistanceSquared(h.WorldPosition, WorldPosition) < Math.Pow(maxDistance, 2);
}
else
{
return false;
}
}
}));
}
return visibleHulls;
}
} }
} }
@@ -362,11 +362,10 @@ namespace Barotrauma
case ContentType.Character: case ContentType.Character:
XDocument doc = XMLExtensions.TryLoadXml(file.Path); XDocument doc = XMLExtensions.TryLoadXml(file.Path);
string speciesName = doc.Root.GetAttributeString("name", ""); string speciesName = doc.Root.GetAttributeString("name", "");
//TODO: check non-default paths if defined filePaths.Add(RagdollParams.GetDefaultFile(speciesName));
filePaths.Add(RagdollParams.GetDefaultFile(speciesName, this));
foreach (AnimationType animationType in Enum.GetValues(typeof(AnimationType))) foreach (AnimationType animationType in Enum.GetValues(typeof(AnimationType)))
{ {
filePaths.Add(AnimationParams.GetDefaultFile(speciesName, animationType, this)); filePaths.Add(AnimationParams.GetDefaultFile(speciesName, animationType));
} }
break; break;
} }
@@ -492,11 +491,6 @@ namespace Barotrauma
public ContentFile(string path, ContentType type, Workshop.Item workShopItem = null) public ContentFile(string path, ContentType type, Workshop.Item workShopItem = null)
{ {
Path = path; Path = path;
#if OSX
Path = Path.Replace("\\", "/");
#endif
Type = type; Type = type;
WorkShopItem = workShopItem; WorkShopItem = workShopItem;
} }
@@ -582,6 +582,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);
@@ -144,17 +144,6 @@ namespace Barotrauma.Items.Components
} }
} }
public Vector2 SteeringInput
{
get { return steeringInput; }
set
{
if (!MathUtils.IsValid(value)) return;
steeringInput.X = MathHelper.Clamp(value.X, -100.0f, 100.0f);
steeringInput.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f);
}
}
public SteeringPath SteeringPath public SteeringPath SteeringPath
{ {
if (!CanBeSelected) return false; if (!CanBeSelected) return false;
@@ -175,6 +164,12 @@ namespace Barotrauma.Items.Components
set { posToMaintain = value; } set { posToMaintain = value; }
} }
public Vector2? PosToMaintain
{
get { return posToMaintain; }
set { posToMaintain = value; }
}
struct ObstacleDebugInfo struct ObstacleDebugInfo
{ {
public Vector2 Point1; public Vector2 Point1;
@@ -1139,70 +1139,6 @@ namespace Barotrauma
} }
} }
public void UpdateTransform()
{
Submarine prevSub = Submarine;
FindHull();
if (Submarine == null && prevSub != null)
{
body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation);
}
else if (Submarine != null && prevSub == null)
{
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
}
else if (Submarine != null && prevSub != null && Submarine != prevSub)
{
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
}
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;
FindHull();
if (Submarine == null && prevSub != null)
{
body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation);
}
else if (Submarine != null && prevSub == null)
{
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
}
else if (Submarine != null && prevSub != null && Submarine != prevSub)
{
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
}
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() public void UpdateTransform()
{ {
Submarine prevSub = Submarine; Submarine prevSub = Submarine;
@@ -1591,7 +1527,11 @@ namespace Barotrauma
if (!picked) return false; if (!picked) return false;
if (picker != null) if (picker.SelectedConstruction == this)
{
if (picker.IsKeyHit(InputType.Select) || forceSelectKey) picker.SelectedConstruction = null;
}
else if (selected)
{ {
if (picker.SelectedConstruction == this) if (picker.SelectedConstruction == this)
{ {
@@ -2006,7 +1946,6 @@ namespace Barotrauma
return Load(element, submarine, createNetworkEvent: false); return Load(element, submarine, createNetworkEvent: false);
} }
/// <summary> /// <summary>
/// Instantiate a new item and load its data from the XML element. /// Instantiate a new item and load its data from the XML element.
/// </summary> /// </summary>
@@ -2019,10 +1958,30 @@ namespace Barotrauma
string name = element.Attribute("name").Value; string name = element.Attribute("name").Value;
string identifier = element.GetAttributeString("identifier", ""); string identifier = element.GetAttributeString("identifier", "");
ItemPrefab prefab = ItemPrefab.Find(name, identifier); ItemPrefab prefab;
if (string.IsNullOrEmpty(identifier))
{
//legacy support:
//1. attempt to find a prefab with an empty identifier and a matching name
prefab = MapEntityPrefab.Find(name, "", showErrorMessages: false) as ItemPrefab;
//2. not found, attempt to find a prefab with a matching name
if (prefab == null) prefab = MapEntityPrefab.Find(name) as ItemPrefab;
}
else
{
prefab = MapEntityPrefab.Find(null, identifier, showErrorMessages: false) as ItemPrefab;
//not found, see if we can find a prefab with a matching alias
if (prefab == null)
{
string lowerCaseName = name.ToLowerInvariant();
prefab = MapEntityPrefab.List.Find(me => me.Aliases != null && me.Aliases.Contains(lowerCaseName)) as ItemPrefab;
}
}
if (prefab == null) if (prefab == null)
{ {
DebugConsole.ThrowError("Error loading item - item prefab \"" + name + "\" (identifier \"" + identifier + "\") not found.");
return null; return null;
} }
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Xml.Linq; using System.Xml.Linq;
using System.Linq; using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -440,7 +441,6 @@ namespace Barotrauma
configFile = filePath; configFile = filePath;
ConfigElement = element; ConfigElement = element;
string nonTranslatedName = element.GetAttributeString("name", "");
identifier = element.GetAttributeString("identifier", ""); identifier = element.GetAttributeString("identifier", "");
//nameidentifier can be used to make multiple items use the same names and descriptions //nameidentifier can be used to make multiple items use the same names and descriptions
@@ -448,21 +448,20 @@ namespace Barotrauma
if (string.IsNullOrEmpty(nameIdentifier)) if (string.IsNullOrEmpty(nameIdentifier))
{ {
name = TextManager.Get("EntityName." + identifier, true) ?? nonTranslatedName; name = TextManager.Get("EntityName." + identifier, true) ?? element.GetAttributeString("name", "");
} }
else else
{ {
name = TextManager.Get("EntityName." + nameIdentifier, true) ?? nonTranslatedName; name = TextManager.Get("EntityName." + nameIdentifier, true) ?? element.GetAttributeString("name", "");
} }
if (name == "") { DebugConsole.ThrowError("Unnamed item in " + filePath + "!"); } if (name == "") { DebugConsole.ThrowError("Unnamed item in " + filePath + "!"); }
DebugConsole.Log(" " + name); DebugConsole.Log(" " + name);
Aliases = new HashSet<string> Aliases =
(element.GetAttributeStringArray("aliases", null, convertToLowerInvariant: true) ?? element.GetAttributeStringArray("aliases", null, convertToLowerInvariant: true) ??
element.GetAttributeStringArray("Aliases", new string[0], convertToLowerInvariant: true)); element.GetAttributeStringArray("Aliases", new string[0], convertToLowerInvariant: true);
Aliases.Add(nonTranslatedName.ToLowerInvariant());
if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category)) if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category))
{ {
@@ -482,7 +481,7 @@ namespace Barotrauma
} }
Tags = new HashSet<string>(element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true)); Tags = new HashSet<string>(element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true));
if (!Tags.Any()) if (Tags.None())
{ {
Tags = new HashSet<string>(element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true)); Tags = new HashSet<string>(element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true));
} }
@@ -720,42 +719,7 @@ namespace Barotrauma
return prices[location.Type.Identifier.ToLowerInvariant()]; return prices[location.Type.Identifier.ToLowerInvariant()];
} }
public static ItemPrefab Find(string name, string identifier)
{
ItemPrefab prefab;
if (string.IsNullOrEmpty(identifier))
{
//legacy support:
//1. attempt to find a prefab with an empty identifier and a matching name
prefab = Find(name, "", showErrorMessages: false) as ItemPrefab;
//2. not found, attempt to find a prefab with a matching name
if (prefab == null) prefab = Find(name) as ItemPrefab;
//not found, see if we can find a prefab with a matching alias
if (prefab == null)
{
string lowerCaseName = name.ToLowerInvariant();
prefab = List.Find(me => me.Aliases != null && me.Aliases.Contains(lowerCaseName)) as ItemPrefab;
}
}
else
{
prefab = Find(null, identifier, showErrorMessages: false) as ItemPrefab;
//not found, see if we can find a prefab with a matching alias
if (prefab == null)
{
string lowerCaseName = name.ToLowerInvariant();
prefab = List.Find(me => me.Aliases != null && me.Aliases.Contains(lowerCaseName)) as ItemPrefab;
}
}
if (prefab == null)
{
DebugConsole.ThrowError("Error loading item - item prefab \"" + name + "\" (identifier \"" + identifier + "\") not found.");
}
return prefab;
}
public IEnumerable<PriceInfo> GetPrices() public IEnumerable<PriceInfo> GetPrices()
{ {
return prices?.Values; return prices?.Values;
+36 -77
View File
@@ -78,11 +78,30 @@ namespace Barotrauma
private float[] leftDelta; private float[] leftDelta;
private float[] rightDelta; private float[] rightDelta;
public List<Gap> ConnectedGaps;
public readonly List<Gap> ConnectedGaps = new List<Gap>(); public readonly List<Gap> ConnectedGaps = new List<Gap>();
public readonly List<Gap> ConnectedGaps = new List<Gap>();
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 string DisplayName
{
get;
private set;
}
private string roomName; private string roomName;
[Editable, Serialize("", true, translationTextTag: "RoomName.")] [Editable, Serialize("", true, translationTextTag: "RoomName.")]
public string RoomName public string RoomName
@@ -838,9 +857,6 @@ namespace Barotrauma
OxygenPercentage = 100.0f; OxygenPercentage = 100.0f;
FireSources = new List<FireSource>(); FireSources = new List<FireSource>();
linkedTo = new System.Collections.ObjectModel.ObservableCollection<MapEntity>();
properties = SerializableProperty.GetProperties(this); properties = SerializableProperty.GetProperties(this);
@@ -1205,93 +1221,36 @@ namespace Barotrauma
FireSources.Remove(fire); FireSources.Remove(fire);
} }
public IEnumerable<Hull> GetConnectedHulls(int? searchDepth) private HashSet<Hull> adjacentHulls = new HashSet<Hull>();
public IEnumerable<Hull> GetConnectedHulls(bool includingThis, int? searchDepth = null)
{ {
return GetAdjacentHulls(new HashSet<Hull>(), 0, searchDepth); adjacentHulls.Clear();
int startStep = 0;
return GetAdjacentHulls(includingThis, adjacentHulls, ref startStep, searchDepth);
} }
private HashSet<Hull> GetAdjacentHulls(HashSet<Hull> connectedHulls, int steps, int? searchDepth) private HashSet<Hull> GetAdjacentHulls(bool includingThis, HashSet<Hull> connectedHulls, ref int step, int? searchDepth)
{ {
if (distance >= maxDistance) return float.MaxValue; if (includingThis)
if (this == target)
{ {
return distance + Vector2.Distance(startPos, endPos); connectedHulls.Add(this);
}
if (step > searchDepth.Value)
{
return connectedHulls;
} }
connectedHulls.Add(this);
foreach (Gap g in ConnectedGaps) foreach (Gap g in ConnectedGaps)
{ {
if (g.ConnectedDoor != null)
{
//gap blocked if the door is not open or the predicted state is not open
if (!g.ConnectedDoor.IsOpen || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
{
if (g.ConnectedDoor.OpenState < 0.1f) continue;
}
}
else if (g.Open <= 0.0f)
{
continue;
}
for (int i = 0; i < 2 && i < g.linkedTo.Count; i++) for (int i = 0; i < 2 && i < g.linkedTo.Count; i++)
{ {
if (g.linkedTo[i] is Hull hull && !connectedHulls.Contains(hull)) if (g.linkedTo[i] is Hull hull && !connectedHulls.Contains(hull))
{ {
float dist = hull.GetApproximateHullDistance(g.Position, endPos, connectedHulls, target, distance + Vector2.Distance(startPos, g.Position), maxDistance); step++;
if (dist < float.MaxValue) return dist; hull.GetAdjacentHulls(true, connectedHulls, ref step, searchDepth);
} }
} }
} }
return connectedHulls;
return float.MaxValue;
}
/// <summary>
/// Approximate distance from this hull to the target hull, moving through open gaps without passing through walls.
/// Uses a greedy algo and may not use the most optimal path. Returns float.MaxValue if no path is found.
/// </summary>
public float GetApproximateDistance(Vector2 startPos, Vector2 endPos, Hull targetHull, float maxDistance)
{
return GetApproximateHullDistance(startPos, endPos, new HashSet<Hull>(), targetHull, 0.0f, maxDistance);
}
private float GetApproximateHullDistance(Vector2 startPos, Vector2 endPos, HashSet<Hull> connectedHulls, Hull target, float distance, float maxDistance)
{
if (distance >= maxDistance) return float.MaxValue;
if (this == target)
{
return distance + Vector2.Distance(startPos, endPos);
}
connectedHulls.Add(this);
foreach (Gap g in ConnectedGaps)
{
if (g.ConnectedDoor != null)
{
//gap blocked if the door is not open or the predicted state is not open
if (!g.ConnectedDoor.IsOpen || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
{
if (g.ConnectedDoor.OpenState < 0.1f) continue;
}
}
else if (g.Open <= 0.0f)
{
continue;
}
for (int i = 0; i < 2 && i < g.linkedTo.Count; i++)
{
if (g.linkedTo[i] is Hull hull && !connectedHulls.Contains(hull))
{
hull.GetAdjacentHulls(connectedHulls, steps++, searchDepth);
}
}
}
return float.MaxValue;
} }
/// <summary> /// <summary>
@@ -20,12 +20,12 @@ namespace Barotrauma
protected List<ushort> linkedToID; protected List<ushort> linkedToID;
//observable collection because some entities may need to be notified when the collection is modified //observable collection because some entities may need to be notified when the collection is modified
public ObservableCollection<MapEntity> linkedTo; public readonly ObservableCollection<MapEntity> linkedTo = new ObservableCollection<MapEntity>();
private bool flippedX, flippedY; private bool flippedX, flippedY;
public bool FlippedX { get { return flippedX; } } public bool FlippedX { get { return flippedX; } }
public bool FlippedY { get { return flippedY; } } public bool FlippedY { get { return flippedY; } }
public bool ShouldBeSaved = true; public bool ShouldBeSaved = true;
//the position and dimensions of the entity //the position and dimensions of the entity
@@ -41,7 +41,7 @@ namespace Barotrauma
get { return isHighlighted || ExternalHighlight; } get { return isHighlighted || ExternalHighlight; }
set { isHighlighted = value; } set { isHighlighted = value; }
} }
public virtual Rectangle Rect public virtual Rectangle Rect
{ {
get { return rect; } get { return rect; }
@@ -161,7 +161,7 @@ namespace Barotrauma
// Quick undo/redo for size and movement only. TODO: Remove if we do a more general implementation. // Quick undo/redo for size and movement only. TODO: Remove if we do a more general implementation.
private Memento<Rectangle> rectMemento; private Memento<Rectangle> rectMemento;
public MapEntity(MapEntityPrefab prefab, Submarine submarine) : base(submarine) public MapEntity(MapEntityPrefab prefab, Submarine submarine) : base(submarine)
{ {
this.prefab = prefab; this.prefab = prefab;
Scale = prefab != null ? prefab.Scale : 1; Scale = prefab != null ? prefab.Scale : 1;
@@ -289,7 +289,7 @@ namespace Barotrauma
mapEntityList.Insert(i, this); mapEntityList.Insert(i, this);
} }
/// <summary> /// <summary>
/// Remove the entity from the entity list without removing links to other entities /// Remove the entity from the entity list without removing links to other entities
/// </summary> /// </summary>
@@ -409,7 +409,7 @@ namespace Barotrauma
try try
{ {
MethodInfo loadMethod = t.GetMethod("Load", new [] { typeof(XElement), typeof(Submarine) }); MethodInfo loadMethod = t.GetMethod("Load", new[] { typeof(XElement), typeof(Submarine) });
if (loadMethod == null) if (loadMethod == null)
{ {
DebugConsole.ThrowError("Could not find the method \"Load\" in " + t + "."); DebugConsole.ThrowError("Could not find the method \"Load\" in " + t + ".");
@@ -499,6 +499,38 @@ namespace Barotrauma
if (linkedTo.Contains(e)) linkedTo.Remove(e); if (linkedTo.Contains(e)) linkedTo.Remove(e);
} }
/// <summary>
/// Gets all linked entities of specific type.
/// </summary>
public HashSet<T> GetLinkedEntities<T>(HashSet<T> list = null, int? maxDepth = null, Func<T, bool> filter = null) where T : MapEntity
{
list = list ?? new HashSet<T>();
int startDepth = 0;
GetLinkedEntitiesRecursive<T>(this, list, ref startDepth, maxDepth, filter);
return list;
}
/// <summary>
/// Gets all linked entities of specific type.
/// </summary>
private static void GetLinkedEntitiesRecursive<T>(MapEntity mapEntity, HashSet<T> linkedTargets, ref int depth, int? maxDepth = null, Func<T, bool> filter = null)
where T : MapEntity
{
if (depth > maxDepth) { return; }
foreach (var linkedEntity in mapEntity.linkedTo)
{
if (linkedEntity is T linkedTarget)
{
if (!linkedTargets.Contains(linkedTarget) && (filter == null || filter(linkedTarget)))
{
linkedTargets.Add(linkedTarget);
depth++;
GetLinkedEntitiesRecursive(linkedEntity, linkedTargets, ref depth, maxDepth, filter);
}
}
}
}
#region Serialized properties #region Serialized properties
// We could use NaN or nullables, but in this case the first is not preferable, because it needs to be checked every time the value is used. // We could use NaN or nullables, but in this case the first is not preferable, because it needs to be checked every time the value is used.
// Nullable on the other requires boxing that we don't want to do too often, since it generates garbage. // Nullable on the other requires boxing that we don't want to do too often, since it generates garbage.
@@ -115,7 +115,6 @@ namespace Barotrauma
Linkable = true Linkable = true
}; };
ep.AllowedLinks.Add("hull"); ep.AllowedLinks.Add("hull");
ep.Aliases = new HashSet<string> { "hull" };
List.Add(ep); List.Add(ep);
ep = new MapEntityPrefab ep = new MapEntityPrefab
@@ -225,6 +225,12 @@ namespace Barotrauma
string nonTranslatedName = element.GetAttributeString("name", null) ?? element.Name.ToString(); string nonTranslatedName = element.GetAttributeString("name", null) ?? element.Name.ToString();
sp.Aliases.Add(nonTranslatedName.ToLowerInvariant()); sp.Aliases.Add(nonTranslatedName.ToLowerInvariant());
SerializableProperty.DeserializeProperties(sp, element);
if (sp.Body)
{
sp.Tags.Add("wall");
}
SerializableProperty.DeserializeProperties(sp, element); SerializableProperty.DeserializeProperties(sp, element);
if (sp.Body) if (sp.Body)
{ {
@@ -131,12 +131,7 @@ namespace Barotrauma.Networking
float dist = Vector2.Distance(listener.WorldPosition, sender.WorldPosition); float dist = Vector2.Distance(listener.WorldPosition, sender.WorldPosition);
if (dist > range) { return 1.0f; } if (dist > range) { return 1.0f; }
Hull listenerHull = listener == null ? null : Hull.FindHull(listener.WorldPosition); if (Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null) dist = (dist + 100f) * obstructionmult;
Hull sourceHull = sender == null ? null : Hull.FindHull(sender.WorldPosition);
if (sourceHull != listenerHull)
{
if (Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null) dist = (dist + 100f) * obstructionmult;
}
if (dist > range) { return 1.0f; } if (dist > range) { return 1.0f; }
return dist / range; return dist / range;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-16
View File
@@ -1,19 +1,3 @@
---------------------------------------------------------------------------------------------------------
v0.9.0.1
---------------------------------------------------------------------------------------------------------
- Added a bunch of missing translations.
- Fixed Steam Workshop on Mac.
- Fixed docking interface button not working in the multiplayer.
- Fixed medical doctor tutorial crashing on languages other than English.
- Fixed a bug that caused AI characters to occasionally get stuck next to stairways.
- Fixed animation and ragdoll file paths getting messed up if there are multiple content packages
installed that include monsters with the same name.
- Fixed crashing when a StatusEffect causes an item to be used on a target.
- Fixed a gap between Remora and the drone.
- Added more supplies to Remora.
- Fixed watchman's dialogue getting muffled.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.9.0.0 v0.9.0.0
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------