Unstable 0.1400.7.0 (Coronavirus edition)

This commit is contained in:
Markus Isberg
2021-07-23 21:01:17 +03:00
parent ee5308b39f
commit 47707c824a
133 changed files with 1310 additions and 533 deletions

View File

@@ -479,7 +479,7 @@ namespace Barotrauma
{
CalculateLimbDepths();
var controller = character.SelectedConstruction?.GetComponent<Controller>();
if (controller != null && controller.ControlCharacterPose && controller.User == character)
if (controller != null && controller.ControlCharacterPose && controller.User == character && controller.UserInCorrectPosition)
{
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
{

View File

@@ -174,7 +174,7 @@ namespace Barotrauma
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
{
if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null)
if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null && Screen.Selected != GameMain.SubEditorScreen)
{
bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null;
if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked())
@@ -391,7 +391,7 @@ namespace Barotrauma
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, npc.CurrentHull == character.CurrentHull ? 500.0f : 100.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
}
foreach (Item item in Item.ItemList)
@@ -405,17 +405,17 @@ namespace Barotrauma
}
if (character.SelectedConstruction != null &&
(character.CanInteractWith(Character.Controlled.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen))
(character.CanInteractWith(character.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen))
{
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
character.SelectedConstruction.DrawHUD(spriteBatch, cam, character);
}
if (Character.Controlled.Inventory != null)
if (character.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.AllItems)
foreach (Item item in character.Inventory.AllItems)
{
if (Character.Controlled.HasEquippedItem(item))
if (character.HasEquippedItem(item))
{
item.DrawHUD(spriteBatch, cam, Character.Controlled);
item.DrawHUD(spriteBatch, cam, character);
}
}
}

View File

@@ -641,6 +641,8 @@ namespace Barotrauma
commands.Add(new Command("wikiimage_sub", "Save an image of the main submarine with a transparent background.", (string[] args) =>
{
if (Submarine.MainSub == null) { return; }
MapEntity.SelectedList.Clear();
MapEntity.mapEntityList.ForEach(me => me.IsHighlighted = false);
WikiImage.Create(Submarine.MainSub);
}));

View File

@@ -741,7 +741,6 @@ namespace Barotrauma
{
float diff = isHorizontal ? scrollToElement.Rect.X - Content.Rect.X : scrollToElement.Rect.Y - Content.Rect.Y;
float speed = MathHelper.Clamp(Math.Abs(diff) * 0.1f, 5.0f, 100.0f);
System.Diagnostics.Debug.WriteLine(speed);
if (Math.Abs(diff) < speed || GUIScrollBar.DraggingBar != null)
{
speed = Math.Abs(diff);

View File

@@ -259,7 +259,11 @@ namespace Barotrauma
public StrikethroughSettings Strikethrough = null;
public readonly List<RichTextData> RichTextData = null;
public List<RichTextData> RichTextData
{
get;
private set;
}
public bool HasColorHighlight => RichTextData != null;
@@ -334,6 +338,12 @@ namespace Barotrauma
if (wrappedText == null) { return; }
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText, removeExtraSpacing).Y + padding));
}
public void SetRichText(string richText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(richText, out string sanitizedText);
Text = sanitizedText;
}
public override void ApplyStyle(GUIComponentStyle componentStyle)
{
@@ -485,7 +495,7 @@ namespace Barotrauma
for (int j = 0; j <= line.Length; j++)
{
Vector2 lineTextSize = Font.MeasureString(line.Substring(0, j)) * textScale;
Vector2 indexPos = new Vector2(lineTextSize.X + Padding.X, totalTextHeight + Padding.Y - halfHeight);
Vector2 indexPos = new Vector2(lineTextSize.X, totalTextHeight - halfHeight) + TextPos - Origin * textScale;
//DebugConsole.NewMessage($"index: {index}, pos: {indexPos}", Color.AliceBlue);
positions.Add(new Tuple<Vector2, int>(indexPos, index + j));
}
@@ -498,7 +508,7 @@ namespace Barotrauma
for (int i = 0; i <= Text.Length; i++)
{
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, i)) * textScale;
Vector2 indexPos = new Vector2(textSize.X + Padding.X, textSize.Y + Padding.Y - halfHeight) + TextPos - Origin * textScale;
Vector2 indexPos = new Vector2(textSize.X, textSize.Y - halfHeight) + TextPos - Origin * textScale;
//DebugConsole.NewMessage($"index: {i}, pos: {indexPos}", Color.WhiteSmoke);
positions.Add(new Tuple<Vector2, int>(indexPos, i));
}

View File

@@ -1131,7 +1131,9 @@ namespace Barotrauma
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name);
int sortResult = itemX.ItemPrefab.Name != itemY.ItemPrefab.Name ?
itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name) :
itemX.ItemPrefab.Identifier.CompareTo(itemY.ItemPrefab.Identifier);
if (sortingMethod == SortingMethod.AlphabeticalDesc) { sortResult *= -1; }
return sortResult;
}

View File

@@ -824,25 +824,34 @@ namespace Barotrauma
currentUpgradeCategory = category;
var entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true) && !i.HiddenInGame && i.AllowSwapping && i.Prefab.SwappableItem != null && category.ItemTags.Any(t => i.HasTag(t))).ToList();
int slotIndex = 0;
foreach (Item item in entitiesOnSub)
{
slotIndex++;
CreateSwappableItemSlideDown(parent, slotIndex, item, submarine);
CreateSwappableItemSlideDown(parent, item, entitiesOnSub, submarine);
}
}
private void CreateSwappableItemSlideDown(GUIListBox parent, int slotIndex, Item item, Submarine submarine)
private void CreateSwappableItemSlideDown(GUIListBox parent, Item item, List<Item> swappableEntities, Submarine submarine)
{
if (Campaign == null || submarine == null) { return; }
IEnumerable<ItemPrefab> availableReplacements = MapEntityPrefab.List.Where(p =>
p is ItemPrefab itemPrefab &&
itemPrefab.SwappableItem != null &&
itemPrefab.SwappableItem.CanBeBought &&
itemPrefab.SwappableItem.CanBeBought &&
itemPrefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase)).Cast<ItemPrefab>();
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item) ?? new List<Item>() { item };
//create the swap entry only for one of the items (the one with the smallest ID)
if (linkedItems.Min(it => it.ID) < item.ID) { return; }
var currentOrPending = item.PendingItemSwap ?? item.Prefab;
string name = currentOrPending.Name;
string quantityText = "";
if (linkedItems.Count > 1)
{
quantityText = " " + TextManager.GetWithVariable("campaignstore.quantity", "[amount]", (linkedItems.Count).ToString());
name += quantityText;
}
bool isOpen = false;
GUIButton toggleButton = new GUIButton(rectT(1f, 0.1f, parent.Content), text: string.Empty, style: "SlideDown")
@@ -850,10 +859,21 @@ namespace Barotrauma
UserData = item
};
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true);
new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: TextManager.GetWithVariable("weaponslot", "[number]", slotIndex.ToString()), font: GUI.SubHeadingFont);
string slotText = "";
if (linkedItems.Count > 1)
{
slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString())));
}
else
{
slotText = TextManager.GetWithVariable("weaponslot", "[number]", (swappableEntities.IndexOf(item) + 1).ToString());
}
new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUI.SubHeadingFont);
GUILayoutGroup group = new GUILayoutGroup(rectT(0.7f, 1f, buttonLayout), isHorizontal: true) { Stretch = true };
string title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", currentOrPending.Name) : item.Name;
string title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : (linkedItems.Count > 1 ? item.Name + quantityText : item.Name);
GUITextBlock text = new GUITextBlock(rectT(0.7f, 1f, group), text: title, font: GUI.SubHeadingFont, textAlignment: Alignment.Right, parseRichText: true)
{
TextColor = GUI.Style.Orange
@@ -876,7 +896,7 @@ namespace Barotrauma
if (isUninstallPending) { canUninstall = false; }
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite,
TextManager.GetWithVariable(item.PendingItemSwap != null ? "upgrades.pendingitem" : "upgrades.installeditem", "[itemname]", currentOrPending.Name),
TextManager.GetWithVariable(item.PendingItemSwap != null ? "upgrades.pendingitem" : "upgrades.installeditem", "[itemname]", name),
currentOrPending.Description,
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton"));
@@ -915,7 +935,7 @@ namespace Barotrauma
bool isPurchased = item.AvailableSwaps.Contains(replacement);
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation);
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count;
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement,
@@ -931,7 +951,7 @@ namespace Barotrauma
{
string promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
new[] { "[itemtoinstall]", "[amount]" },
new[] { replacement.Name, replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation).ToString() });
new[] { replacement.Name, (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString() });
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -966,6 +986,7 @@ namespace Barotrauma
toggleButton.OnClicked = delegate
{
if (Campaign == null) { return false; }
isOpen = !isOpen;
toggleButton.Selected = !toggleButton.Selected;
foreach (GUIFrame frame in frames)
@@ -974,9 +995,10 @@ namespace Barotrauma
}
if (toggleButton.Selected)
{
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item);
foreach (var itemPreview in itemPreviews)
{
itemPreview.Value.OutlineColor = itemPreview.Value.Color = itemPreview.Key == item ? GUI.Style.Orange : previewWhite;
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUI.Style.Orange : previewWhite;
}
foreach (GUIComponent otherComponent in toggleButton.Parent.Children)
{
@@ -1145,9 +1167,9 @@ namespace Barotrauma
private void CreateItemTooltip(MapEntity entity)
{
int slotIndex = -1;
if (currentStoreLayout?.SelectedData is CategoryData categoryData)
if (entity is Item swappableItem && swappableItem.Prefab.SwappableItem != null)
{
var entitiesOnSub = Submarine.MainSub.GetItems(true).Where(i => i.Prefab.SwappableItem != null && Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && categoryData.Category.ItemTags.Any(t => i.HasTag(t))).ToList();
var entitiesOnSub = Submarine.MainSub.GetItems(true).Where(i => i.Prefab.SwappableItem != null && Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && i.Prefab.SwappableItem.SwapIdentifier == swappableItem.Prefab.SwappableItem?.SwapIdentifier).ToList();
slotIndex = entitiesOnSub.IndexOf(entity) + 1;
}
@@ -1231,6 +1253,8 @@ namespace Barotrauma
private void UpdateSubmarinePreview(float deltaTime, GUICustomComponent parent)
{
if (Campaign == null) { return; }
if (!parent.Children.Any() || Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine || GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
{
GameMain.GameSession?.SubmarineInfo?.CheckSubsLeftBehind();
@@ -1279,7 +1303,8 @@ namespace Barotrauma
{
if (selectedUpgradeCategoryLayout != null)
{
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData as Item == HoveredItem, recursive: true) is GUIButton itemElement)
var linkedItems = HoveredItem is Item ? Campaign.UpgradeManager.GetLinkedItemsToSwap((Item)HoveredItem) : new List<Item>();
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData as Item == HoveredItem || linkedItems.Contains(c.UserData as Item), recursive: true) is GUIButton itemElement)
{
if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }
(itemElement.Parent?.Parent?.Parent as GUIListBox)?.ScrollToElement(itemElement);

View File

@@ -115,6 +115,7 @@ namespace Barotrauma
if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.PrimaryMouseButtonHeld()))
{
Hovered?.Invoke();
System.Diagnostics.Debug.WriteLine("hovered");
if (RequireMouseOn || PlayerInput.PrimaryMouseButtonDown())
{
if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None())
@@ -126,6 +127,7 @@ namespace Barotrauma
}
else if (selectedWidgets.Contains(this))
{
System.Diagnostics.Debug.WriteLine("selectedWidgets.Contains(this) -> remove");
selectedWidgets.Remove(this);
Deselected?.Invoke();
}

View File

@@ -140,8 +140,7 @@ namespace Barotrauma
msg,
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
Character.Controlled);
var headset = GetHeadset(Character.Controlled, true);
if (headset != null && headset.CanTransmit())
if (ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent headset))
{
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
headset.TransmitSignal(s, sentFromChat: true);
@@ -610,17 +609,6 @@ namespace Barotrauma
ChatBox.AddMessage(message);
}
private WifiComponent GetHeadset(Character character, bool requireEquipped)
{
if (character?.Inventory == null) { return null; }
var radioItem = character.Inventory.AllItems.FirstOrDefault(it => it.GetComponent<WifiComponent>() != null);
if (radioItem == null) { return null; }
if (requireEquipped && !character.HasEquippedItem(radioItem)) { return null; }
return radioItem.GetComponent<WifiComponent>();
}
partial void CreateRandomConversation()
{
if (GameMain.Client != null)
@@ -1429,7 +1417,7 @@ namespace Barotrauma
}
}
if (closestNode != null && closestNode == selectedNode)
if (closestNode != null && closestNode.CanBeFocused && closestNode == selectedNode)
{
timeSelected += deltaTime;
if (timeSelected >= selectionTime)
@@ -2485,6 +2473,7 @@ namespace Barotrauma
foreach (Order p in Order.PrefabList)
{
targetComponent = null;
if (p.UseController && itemContext.Components.None(c => c is Controller)) { continue; }
if ((p.TargetItems.Length > 0 && (p.TargetItems.Contains(itemContext.Prefab.Identifier) || itemContext.HasTag(p.TargetItems))) ||
p.TryGetTargetItemComponent(itemContext, out targetComponent))
{
@@ -3444,10 +3433,8 @@ namespace Barotrauma
bool canIssueOrders = false;
if (Character.Controlled?.CurrentHull?.Submarine != null && Character.Controlled.SpeechImpediment < 100.0f)
{
WifiComponent radio = GetHeadset(Character.Controlled, true);
canIssueOrders =
radio != null &&
radio.CanTransmit() &&
ChatMessage.CanUseRadio(Character.Controlled) &&
Character.Controlled?.CurrentHull?.Submarine?.TeamID == Character.Controlled.TeamID &&
!Character.Controlled.CurrentHull.Submarine.Info.IsWreck;
}

View File

@@ -224,12 +224,7 @@ namespace Barotrauma
{
endRoundButton.ToolTip = buttonText;
}
if (Character.Controlled?.ViewTarget is Item item)
{
Turret turret = item.GetComponent<Turret>();
endRoundButton.RectTransform.ScreenSpaceOffset = turret == null ? Point.Zero : new Point(0, (int)(turret.UIElementHeight * 1.25f));
}
else if (Character.Controlled?.CharacterHealth?.SuicideButton?.Visible ?? false)
if (Character.Controlled?.CharacterHealth?.SuicideButton?.Visible ?? false)
{
endRoundButton.RectTransform.ScreenSpaceOffset = new Point(0, Character.Controlled.CharacterHealth.SuicideButton.Rect.Height);
}

View File

@@ -552,7 +552,7 @@ namespace Barotrauma
if (Vector2.DistanceSquared(Character.Controlled.WorldPosition, gap.ConnectedDoor.Item.WorldPosition) > 400 * 400) { continue; }
if (!gap.IsRoomToRoom)
{
if (!(Character.Controlled.GetEquippedItem("deepdiving") is Item)) { continue; }
if (!(Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item)) { continue; }
if (Character.Controlled.IsProtectedFromPressure()) { continue; }
if (DisplayHint("divingsuitwarning", extendTextTag: false)) { return; }
continue;

View File

@@ -167,6 +167,13 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(0, false)]
public int HudLayer
{
get;
private set;
}
private bool useAlternativeLayout;
public bool UseAlternativeLayout
{
@@ -443,6 +450,8 @@ namespace Barotrauma.Items.Components
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
public virtual void UpdateEditing(float deltaTime) { }
public virtual void CreateEditingHUD(SerializableEntityEditor editor)
{
}

View File

@@ -44,20 +44,6 @@ namespace Barotrauma.Items.Components
private set;
}
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
public Vector2 ItemPos { get; set; }
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
public Vector2 ItemInterval { get; set; }
[Serialize(100, false, description: "How many items are placed in a row before starting a new row.")]
public int ItemsPerRow { get; set; }
/// <summary>
/// Depth at which the contained sprites are drawn. If not set, the original depth of the item sprites is used.
/// </summary>
@@ -291,7 +277,6 @@ namespace Barotrauma.Items.Components
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.DrawPosition;
}

View File

@@ -21,6 +21,10 @@ namespace Barotrauma.Items.Components
private float[] charWidths;
private float prevScale;
private Rectangle prevRect;
private StringBuilder sb;
private Vector4 padding;
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
@@ -49,7 +53,8 @@ namespace Barotrauma.Items.Components
}
text = value;
SetDisplayText(value);
SetDisplayText(value);
UpdateScrollingText();
}
}
@@ -205,9 +210,16 @@ namespace Barotrauma.Items.Components
if (!needsScrolling) { return; }
scrollAmount -= deltaTime * ScrollSpeed;
UpdateScrollingText();
}
private void UpdateScrollingText()
{
if (!scrollable || !needsScrolling) { return; }
float currLength = 0;
StringBuilder sb = new StringBuilder();
sb ??= new StringBuilder();
sb.Clear();
float textAreaWidth = textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z;
for (int i = scrollIndex; i < scrollingText.Length; i++)
{
@@ -246,10 +258,7 @@ namespace Barotrauma.Items.Components
prevScale = item.Scale;
prevRect = item.Rect;
}
private float prevScale;
private Rectangle prevRect;
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (editing)
@@ -267,15 +276,6 @@ namespace Barotrauma.Items.Components
item.DrawPosition.X - item.Rect.Width / 2.0f,
-(item.DrawPosition.Y + item.Rect.Height / 2.0f));
Rectangle worldRect = item.WorldRect;
if (worldRect.X > Screen.Selected.Cam.WorldView.Right ||
worldRect.Right < Screen.Selected.Cam.WorldView.X ||
worldRect.Y < Screen.Selected.Cam.WorldView.Y - Screen.Selected.Cam.WorldView.Height ||
worldRect.Y - worldRect.Height > Screen.Selected.Cam.WorldView.Y)
{
return;
}
textBlock.TextDepth = item.SpriteDepth - 0.0001f;
textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + (editing ? Vector2.Zero : new Vector2(scrollAmount + scrollPadding, 0.0f));
textBlock.DrawManually(spriteBatch);

View File

@@ -561,7 +561,8 @@ namespace Barotrauma.Items.Components
private bool StartButtonClicked(GUIButton button, object obj)
{
if (selectedItem == null) { return false; }
if (fabricatedItem == null && !outputContainer.Inventory.CanBePut(selectedItem.TargetItem))
if (fabricatedItem == null &&
!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition))
{
outputSlot.Flash(GUI.Style.Red);
return false;

View File

@@ -23,6 +23,10 @@ namespace Barotrauma.Items.Components
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Character character)
{
if (DraggingConnected?.Item.Removed ?? false)
{
DraggingConnected = null;
}
Rectangle panelRect = panel.GuiFrame.Rect;
int x = panelRect.X, y = panelRect.Y;
int width = panelRect.Width, height = panelRect.Height;

View File

@@ -304,6 +304,21 @@ namespace Barotrauma.Items.Components
}
}
public override void UpdateEditing(float deltaTime)
{
if (Screen.Selected == GameMain.SubEditorScreen && item.IsSelected)
{
if (widgets.ContainsKey("maxrotation"))
{
widgets["maxrotation"].Update(deltaTime);
}
if (widgets.ContainsKey("minrotation"))
{
widgets["minrotation"].Update(deltaTime);
}
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (crosshairSprite != null)
@@ -469,7 +484,6 @@ namespace Barotrauma.Items.Components
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
widget.Update(deltaTime);
};
});

View File

@@ -1104,6 +1104,8 @@ namespace Barotrauma
public static void UpdateDragging()
{
DraggingItems.RemoveAll(it => !Character.Controlled.CanInteractWith(it));
if (DraggingItems.Any() && PlayerInput.PrimaryMouseButtonReleased())
{
Character.Controlled.ClearInputs();

View File

@@ -22,6 +22,8 @@ namespace Barotrauma
private readonly List<ItemComponent> activeHUDs = new List<ItemComponent>();
private readonly List<SerializableEntityEditor> activeEditors = new List<SerializableEntityEditor>();
public GUIComponentStyle IconStyle { get; private set; } = null;
partial void AssignCampaignInteractionTypeProjSpecific(CampaignMode.InteractionType interactionType)
{
@@ -40,7 +42,6 @@ namespace Barotrauma
public float LastImpactSoundTime;
public const float ImpactSoundInterval = 0.2f;
private bool editingHUDRefreshPending;
private float editingHUDRefreshTimer;
private ContainedItemSprite activeContainedSprite;
@@ -581,14 +582,18 @@ namespace Barotrauma
}
}
public override void UpdateEditing(Camera cam)
public override void UpdateEditing(Camera cam, float deltaTime)
{
if (editingHUD == null || editingHUD.UserData as Item != this ||
(editingHUDRefreshPending && editingHUDRefreshTimer <= 0.0f))
if (editingHUD == null || editingHUD.UserData as Item != this)
{
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
editingHUDRefreshTimer = 1.0f;
}
if (editingHUDRefreshTimer <= 0.0f)
{
activeEditors.ForEach(e => e?.RefreshValues());
editingHUDRefreshTimer = 1.0f;
}
if (Screen.Selected != GameMain.SubEditorScreen) { return; }
@@ -606,6 +611,11 @@ namespace Barotrauma
if (Character.Controlled == null) { activeHUDs.Clear(); }
foreach (ItemComponent ic in components)
{
ic.UpdateEditing(deltaTime);
}
if (!Linkable) { return; }
if (!PlayerInput.KeyDown(Keys.Space)) { return; }
@@ -632,7 +642,7 @@ namespace Barotrauma
public GUIComponent CreateEditingHUD(bool inGame = false)
{
editingHUDRefreshPending = false;
activeEditors.Clear();
int heightScaled = (int)(20 * GUI.Scale);
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
@@ -643,6 +653,7 @@ namespace Barotrauma
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont) { UserData = this };
activeEditors.Add(itemEditor);
itemEditor.Children.First().Color = Color.Black * 0.7f;
if (!inGame)
{
@@ -782,6 +793,7 @@ namespace Barotrauma
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame, titleFont: GUI.SubHeadingFont) { UserData = ic };
componentEditor.Children.First().Color = Color.Black * 0.7f;
activeEditors.Add(componentEditor);
if (inGame)
{
@@ -976,7 +988,7 @@ namespace Barotrauma
Screen.Selected == GameMain.SubEditorScreen)
{
GUIComponent prevEditingHUD = editingHUD;
UpdateEditing(cam);
UpdateEditing(cam, deltaTime);
editingHUDCreated = editingHUD != null && editingHUD != prevEditingHUD;
}
@@ -996,7 +1008,7 @@ namespace Barotrauma
{
if (prefab.IsLinkAllowed(entity.prefab) && entity is Item i)
{
if (!i.DisplaySideBySideWhenLinked) continue;
if (!i.DisplaySideBySideWhenLinked) { continue; }
activeComponents.AddRange(i.components);
}
}
@@ -1030,6 +1042,8 @@ namespace Barotrauma
}
}
activeHUDs.Sort((h1, h2) => { return h2.HudLayer.CompareTo(h1.HudLayer); });
//active HUDs have changed, need to reposition
if (!prevActiveHUDs.SequenceEqual(activeHUDs) || editingHUDCreated)
{
@@ -1132,8 +1146,6 @@ namespace Barotrauma
}
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
bool noComponentText = true;
if (CampaignInteractionType != CampaignMode.InteractionType.None)
{
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameMain.Config.KeyBindText(InputType.Use)), Color.Cyan, false, false));
@@ -1159,7 +1171,6 @@ namespace Barotrauma
}
}
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
noComponentText = false;
}
}
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
@@ -1295,7 +1306,6 @@ namespace Barotrauma
break;
case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, false);
editingHUDRefreshPending = true;
break;
case NetEntityEvent.Type.Upgrade:
string identifier = msg.ReadString();

View File

@@ -101,7 +101,7 @@ namespace Barotrauma
return editingHUD;
}
public override void UpdateEditing(Camera cam)
public override void UpdateEditing(Camera cam, float deltaTime)
{
if (editingHUD == null || editingHUD.UserData as Hull != this)
{

View File

@@ -23,6 +23,14 @@ namespace Barotrauma
public LevelWallVertexBuffer(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{
if (wallVertices.Length == 0)
{
throw new ArgumentException("Failed to instantiate a LevelWallVertexBuffer (no wall vertices).");
}
if (wallVertices.Length == 0)
{
throw new ArgumentException("Failed to instantiate a LevelWallVertexBuffer (no wall edge vertices).");
}
this.wallVertices = LevelRenderer.GetColoredVertices(wallVertices, color);
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallVertices.Length, BufferUsage.WriteOnly);
WallBuffer.SetData(this.wallVertices);

View File

@@ -617,14 +617,10 @@ namespace Barotrauma.Lights
private List<Vector2> FindRaycastHits()
{
if (!CastShadows)
{
return null;
}
if (Range < 1.0f || Color.A < 1) { return null; }
if (!CastShadows || Range < 1.0f || Color.A < 1) { return null; }
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
var hulls = new List<ConvexHull>();
foreach (ConvexHullList chList in hullsInRange)
@@ -826,13 +822,13 @@ namespace Barotrauma.Lights
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
Pair<int,Vector2> intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
Pair<int,Vector2> intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
if (intersection1.First < 0) return null;
if (intersection2.First < 0) return null;
Segment seg1 = visibleSegments[intersection1.First];
Segment seg2 = visibleSegments[intersection2.First];
if (intersection1.index < 0) return null;
if (intersection2.index < 0) return null;
Segment seg1 = visibleSegments[intersection1.index];
Segment seg2 = visibleSegments[intersection2.index];
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
@@ -849,12 +845,12 @@ namespace Barotrauma.Lights
hullList.IsHidden.Remove(seg2.ConvexHull);
}
}
else if (intersection1.First != intersection2.First)
else if (intersection1.index != intersection2.index)
{
//the raycasts landed on different segments
//we definitely want to generate new geometry here
output.Add(isPoint1 ? p.WorldPos : intersection1.Second);
output.Add(isPoint2 ? p.WorldPos : intersection2.Second);
output.Add(isPoint1 ? p.WorldPos : intersection1.pos);
output.Add(isPoint2 ? p.WorldPos : intersection2.pos);
foreach (ConvexHullList hullList in hullsInRange)
{
@@ -884,7 +880,7 @@ namespace Barotrauma.Lights
return output;
}
private Pair<int, Vector2> RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
private (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
{
Vector2? closestIntersection = null;
int segment = -1;
@@ -943,8 +939,7 @@ namespace Barotrauma.Lights
}
}
Pair<int, Vector2> retVal = new Pair<int, Vector2>(segment, closestIntersection == null ? rayEnd : (Vector2)closestIntersection);
return retVal;
return (segment, closestIntersection == null ? rayEnd : (Vector2)closestIntersection);
}
@@ -1281,11 +1276,6 @@ namespace Barotrauma.Lights
{
if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
if (CastShadows)
{
CheckHullsInRange();
}
//if the light doesn't cast shadows, we can simply render the texture without having to calculate the light volume
if (!CastShadows)
{
@@ -1298,16 +1288,27 @@ namespace Barotrauma.Lights
float scale = Range / (currentTexture.Width / 2.0f);
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation, center, scale, SpriteEffects.None, 1);
return;
}
CheckHullsInRange();
if (NeedsRecalculation)
{
var verts = FindRaycastHits();
if (verts == null)
{
#if DEBUG
DebugConsole.ThrowError($"Failed to generate vertices for a light source. Range: {Range}, color: {Color}, brightness: {CurrentBrightness}, parent: {ParentBody?.UserData ?? "Unknown"}");
#endif
Enabled = false;
return;
}
CalculateLightVertices(verts);
lastRecalculationTime = (float)Timing.TotalTime;

View File

@@ -55,23 +55,23 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, pos + Vector2.UnitX * 50.0f, pos - Vector2.UnitX * 50.0f, color * alpha, 0.0f, 5);
}
public override void UpdateEditing(Camera cam)
public override void UpdateEditing(Camera cam, float deltaTime)
{
if (editingHUD == null || editingHUD.UserData as LinkedSubmarine != this)
{
editingHUD = CreateEditingHUD();
}
editingHUD.UpdateManually((float)Timing.Step);
editingHUD.UpdateManually(deltaTime);
if (!PlayerInput.PrimaryMouseButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return;
if (!PlayerInput.PrimaryMouseButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity entity in mapEntityList)
{
if (entity == this || !entity.IsHighlighted || !(entity is Item) || !entity.IsMouseOn(position)) continue;
if (((Item)entity).GetComponent<DockingPort>() == null) continue;
if (entity == this || !entity.IsHighlighted || !(entity is Item) || !entity.IsMouseOn(position)) { continue; }
if (((Item)entity).GetComponent<DockingPort>() == null) { continue; }
if (linkedTo.Contains(entity))
{
linkedTo.Remove(entity);

View File

@@ -838,9 +838,9 @@ namespace Barotrauma
public static List<MapEntity> FilteredSelectedList { get; private set; } = new List<MapEntity>();
public static void UpdateEditor(Camera cam)
public static void UpdateEditor(Camera cam, float deltaTime)
{
if (highlightedListBox != null) highlightedListBox.UpdateManually((float)Timing.Step);
if (highlightedListBox != null) { highlightedListBox.UpdateManually(deltaTime); }
if (editingHUD != null)
{
@@ -865,7 +865,7 @@ namespace Barotrauma
var first = FilteredSelectedList.FirstOrDefault();
if (first != null)
{
first.UpdateEditing(cam);
first.UpdateEditing(cam, deltaTime);
if (first.ResizeHorizontal || first.ResizeVertical)
{
first.UpdateResizing(cam);
@@ -1017,7 +1017,7 @@ namespace Barotrauma
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList();
}
public virtual void UpdateEditing(Camera cam) { }
public virtual void UpdateEditing(Camera cam, float deltaTime) { }
protected static void PositionEditingHUD()
{

View File

@@ -83,7 +83,7 @@ namespace Barotrauma
convexHulls.Add(h);
}
public override void UpdateEditing(Camera cam)
public override void UpdateEditing(Camera cam, float deltaTime)
{
if (editingHUD == null || editingHUD.UserData as Structure != this)
{

View File

@@ -53,8 +53,8 @@ namespace Barotrauma
}
}
private Dictionary<string,HullCollection> hullCollections;
private List<Door> doors;
private readonly Dictionary<string, HullCollection> hullCollections;
private readonly List<Door> doors;
private static SubmarinePreview instance = null;
@@ -104,7 +104,7 @@ namespace Barotrauma
(spriteBatch, component) => {
camera.UpdateTransform(interpolate: true, updateListener: false);
Rectangle drawRect = new Rectangle(component.Rect.X + 1, component.Rect.Y + 1, component.Rect.Width - 2, component.Rect.Height - 2);
RenderSubmarine(spriteBatch, drawRect);
RenderSubmarine(spriteBatch, drawRect, component);
},
(deltaTime, component) => {
bool isMouseOnComponent = GUI.MouseOn == component;
@@ -121,6 +121,10 @@ namespace Barotrauma
{
specsContainer.Visible = GUI.IsMouseOn(titleText);
}
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
Dispose();
}
});
var topContainer = new GUIFrame(new RectTransform(new Vector2(1f, 0.07f), innerPadded.RectTransform, Anchor.TopLeft), style: null)
@@ -225,6 +229,7 @@ namespace Barotrauma
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
{
if (subElement.GetAttributeBool("hiddeningame", false)) { continue; }
switch (subElement.Name.LocalName.ToLowerInvariant())
{
case "item":
@@ -240,8 +245,7 @@ namespace Barotrauma
string identifier = subElement.GetAttributeString("roomname", "").ToLowerInvariant();
if (!string.IsNullOrEmpty(identifier))
{
HullCollection hullCollection = null;
if (!hullCollections.TryGetValue(identifier, out hullCollection))
if (!hullCollections.TryGetValue(identifier, out HullCollection hullCollection))
{
hullCollection = new HullCollection(identifier);
hullCollections.Add(identifier, hullCollection);
@@ -476,12 +480,14 @@ namespace Barotrauma
}
}
var transformedBarrelPos = MathUtils.RotatePointAroundTarget(
subElement.GetAttributeVector2("barrelpos", Vector2.Zero) * scale,
new Vector2(rect.Width / 2, rect.Height / 2),
Vector2 barrelPos = subElement.GetAttributeVector2("barrelpos", Vector2.Zero);
Vector2 relativeBarrelPos = barrelPos * prefab.Scale - new Vector2(rect.Width / 2, rect.Height / 2);
var transformedBarrelPos = MathUtils.RotatePoint(
relativeBarrelPos,
MathHelper.ToRadians(rotation));
Vector2 drawPos = new Vector2(rect.X + transformedBarrelPos.X, rect.Y - transformedBarrelPos.Y);
float relativeScale = scale / prefab.Scale;
Vector2 drawPos = new Vector2(rect.X + rect.Width * relativeScale / 2 + transformedBarrelPos.X * relativeScale, rect.Y - rect.Height * relativeScale / 2 - transformedBarrelPos.Y * relativeScale);
drawPos.Y = -drawPos.Y;
railSprite?.Draw(spriteRecorder,
@@ -491,7 +497,7 @@ namespace Barotrauma
SpriteEffects.None, depth + (railSprite.Depth - prefab.sprite.Depth));
barrelSprite?.Draw(spriteRecorder,
drawPos - new Vector2((float)Math.Cos(MathHelper.ToRadians(rotation)), (float)Math.Sin(MathHelper.ToRadians(rotation))) * scale,
drawPos,
color,
rotation + MathHelper.PiOver2, scale,
SpriteEffects.None, depth + (barrelSprite.Depth - prefab.sprite.Depth));
@@ -564,7 +570,7 @@ namespace Barotrauma
}
}
private void RenderSubmarine(SpriteBatch spriteBatch, Rectangle scissorRectangle)
private void RenderSubmarine(SpriteBatch spriteBatch, Rectangle scissorRectangle, GUIComponent component)
{
if (spriteRecorder == null) { return; }
@@ -605,11 +611,13 @@ namespace Barotrauma
foreach (var hullCollection in hullCollections.Values)
{
bool mouseOver = false;
foreach (var rect in hullCollection.Rects)
if (GUI.MouseOn == null || GUI.MouseOn == component)
{
mouseOver = rect.Contains(mousePos);
if (mouseOver) { break; }
foreach (var rect in hullCollection.Rects)
{
mouseOver = rect.Contains(mousePos);
if (mouseOver) { break; }
}
}
foreach (var rect in hullCollection.Rects)

View File

@@ -149,7 +149,7 @@ namespace Barotrauma
}
}
public override void UpdateEditing(Camera cam)
public override void UpdateEditing(Camera cam, float deltaTime)
{
if (editingHUD == null || editingHUD.UserData != this)
{

View File

@@ -318,9 +318,10 @@ namespace Barotrauma
if (MapGenerationParams.Instance.RadiationParams != null)
{
bool prevRadiationToggleEnabled = EnableRadiationToggle?.Selected ?? true;
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), CampaignSettingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
{
Selected = true,
Selected = prevRadiationToggleEnabled,
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
};
}
@@ -340,7 +341,9 @@ namespace Barotrauma
return true;
}
};
MaxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), CampaignSettings.DefaultMaxMissionCount.ToString(), textAlignment: Alignment.Center, style: "GUITextBox");
string prevMaxMissionCountText = MaxMissionCountText?.Text ?? CampaignSettings.DefaultMaxMissionCount.ToString();
MaxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), prevMaxMissionCountText, textAlignment: Alignment.Center, style: "GUITextBox");
maxMissionCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.15f, 0.8f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleRight")
{
OnClicked = (button, obj) =>
@@ -806,11 +809,12 @@ namespace Barotrauma
UserData = "savefileframe"
};
new GUITextBlock(new RectTransform(new Vector2(1, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter)
var titleText = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, 0.05f)
},
Path.GetFileNameWithoutExtension(fileName), font: GUI.LargeFont, textAlignment: Alignment.Center);
titleText.Text = ToolBox.LimitString(titleText.Text, titleText.Font, titleText.Rect.Width);
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.5f), saveFileFrame.RectTransform, Anchor.Center)
{

View File

@@ -22,6 +22,7 @@ namespace Barotrauma
private GUIListBox missionList;
private readonly List<GUITickBox> missionTickBoxes = new List<GUITickBox>();
private readonly List<GUITextBlock> missionRewardTexts = new List<GUITextBlock>();
private bool hasMaxMissions;
@@ -368,6 +369,7 @@ namespace Barotrauma
public void SelectLocation(Location location, LocationConnection connection)
{
missionTickBoxes.Clear();
missionRewardTexts.Clear();
locationInfoPanel.ClearChildren();
//don't select the map panel if we're looking at some other tab
if (selectedTab == CampaignMode.InteractionType.Map)
@@ -524,6 +526,12 @@ namespace Barotrauma
Campaign.Map.CurrentLocation.DeselectMission(mission);
}
foreach (GUITextBlock rewardText in missionRewardTexts)
{
Mission otherMission = rewardText.UserData as Mission;
rewardText.SetRichText(otherMission.GetMissionRewardText(Submarine.MainSub));
}
UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
@@ -567,7 +575,11 @@ namespace Barotrauma
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(Submarine.MainSub), wrap: true, parseRichText: true);
var rewardText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(Submarine.MainSub), wrap: true, parseRichText: true)
{
UserData = mission
};
missionRewardTexts.Add(rewardText);
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText, wrap: true, parseRichText: true);

View File

@@ -119,6 +119,7 @@ namespace Barotrauma
addValueDropdown.AddItem(nameof(LimbType), typeof(LimbType));
addValueDropdown.AddItem(nameof(ReputationAction.ReputationType), typeof(ReputationAction.ReputationType));
addValueDropdown.AddItem(nameof(SpawnAction.SpawnLocationType), typeof(SpawnAction.SpawnLocationType));
addValueDropdown.AddItem(nameof(CharacterTeamType), typeof(CharacterTeamType));
loadButton.OnClicked += (button, o) => Load(loadDropdown.SelectedData as EventPrefab);
addActionButton.OnClicked += (button, o) => AddAction(addActionDropdown.SelectedData as Type);

View File

@@ -1089,6 +1089,7 @@ namespace Barotrauma
itemContentPackage = ContentPackage.CreatePackage(sub.Name, Path.Combine(destinationFolder, SteamManager.MetadataFileName), corePackage: false);
SteamManager.CreateWorkshopItemStaging(itemContentPackage, out itemEditor);
bool fileMoved = false;
string submarineDir = Path.GetDirectoryName(sub.FilePath);
if (submarineDir != Path.GetDirectoryName(destinationFolder))
{
@@ -1097,14 +1098,18 @@ namespace Barotrauma
{
File.Move(sub.FilePath, destinationPath);
}
fileMoved = true;
sub.FilePath = destinationPath;
}
itemContentPackage.AddFile(sub.FilePath, ContentType.Submarine);
itemContentPackage.Name = sub.Name;
itemContentPackage.Save(itemContentPackage.Path);
//ContentPackage.List.Add(itemContentPackage);
//GameMain.Config.SelectContentPackage(itemContentPackage);
if (fileMoved)
{
GameMain.Config.EnableRegularPackage(itemContentPackage);
}
itemEditor = itemEditor?.WithTitle(sub.Name).WithTag("Submarine").WithDescription(sub.Description);

View File

@@ -4696,7 +4696,7 @@ namespace Barotrauma
CloseItem();
}
}
MapEntity.UpdateEditor(cam);
MapEntity.UpdateEditor(cam, (float)deltaTime);
}
entityMenuOpenState = entityMenuOpen && !WiringMode ?

View File

@@ -10,10 +10,10 @@ namespace Barotrauma
{
class SerializableEntityEditor : GUIComponent
{
private int elementHeight;
private GUILayoutGroup layoutGroup;
private float inputFieldWidth = 0.5f;
private float largeInputFieldWidth = 0.8f;
private readonly int elementHeight;
private readonly GUILayoutGroup layoutGroup;
private readonly float inputFieldWidth = 0.5f;
private readonly float largeInputFieldWidth = 0.8f;
#if DEBUG
public static List<string> MissingLocalizations = new List<string>();
#endif
@@ -23,6 +23,8 @@ namespace Barotrauma
public static DateTime NextCommandPush;
public static Tuple<SerializableProperty, PropertyCommand> CommandBuffer;
private Action refresh;
public int ContentHeight
{
get
@@ -312,6 +314,11 @@ namespace Barotrauma
Recalculate();
}
public void RefreshValues()
{
refresh?.Invoke();
}
public void Recalculate() => RectTransform.Resize(new Point(RectTransform.NonScaledSize.X, ContentHeight));
public GUIComponent CreateNewField(SerializableProperty property, ISerializableEntity entity)
@@ -459,6 +466,10 @@ namespace Barotrauma
return true;
}
};
refresh += () =>
{
propertyTickBox.Selected = (bool)property.GetValue(entity);
};
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { propertyTickBox }); }
return propertyTickBox;
}
@@ -480,7 +491,7 @@ namespace Barotrauma
ToolTip = toolTip,
Font = GUI.SmallFont
};
field = numberInput as GUIComponent;
field = numberInput;
}
else
{
@@ -499,7 +510,11 @@ namespace Barotrauma
TrySendNetworkUpdate(entity, property);
}
};
field = numberInput as GUIComponent;
refresh += () =>
{
if (!numberInput.TextBox.Selected) { numberInput.IntValue = (int)property.GetValue(entity); }
};
field = numberInput;
}
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { field }); }
return frame;
@@ -538,6 +553,10 @@ namespace Barotrauma
TrySendNetworkUpdate(entity, property);
}
};
refresh += () =>
{
if (!numberInput.TextBox.Selected) { numberInput.FloatValue = (float)property.GetValue(entity); }
};
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { numberInput }); }
return frame;
}
@@ -567,6 +586,10 @@ namespace Barotrauma
}
return true;
};
refresh += () =>
{
if (!enumDropDown.Dropped) { enumDropDown.SelectItem(property.GetValue(entity)); }
};
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { enumDropDown }); }
return frame;
}
@@ -635,6 +658,10 @@ namespace Barotrauma
propertyBox.OnDeselected += (textBox, keys) => OnApply(textBox);
propertyBox.OnEnterPressed += (box, text) => OnApply(box);
refresh += () =>
{
if (!propertyBox.Selected) { propertyBox.Text = (string)property.GetValue(entity); }
};
bool OnApply(GUITextBox textBox)
{
@@ -730,6 +757,15 @@ namespace Barotrauma
};
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Point value = (Point)property.GetValue(entity);
((GUINumberInput)fields[0]).IntValue = value.X;
((GUINumberInput)fields[1]).IntValue = value.Y;
}
};
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
@@ -791,6 +827,15 @@ namespace Barotrauma
};
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Vector2 value = (Vector2)property.GetValue(entity);
((GUINumberInput)fields[0]).FloatValue = value.X;
((GUINumberInput)fields[1]).FloatValue = value.Y;
}
};
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
@@ -857,6 +902,16 @@ namespace Barotrauma
};
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Vector3 value = (Vector3)property.GetValue(entity);
((GUINumberInput)fields[0]).FloatValue = value.X;
((GUINumberInput)fields[1]).FloatValue = value.Y;
((GUINumberInput)fields[2]).FloatValue = value.Z;
}
};
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
@@ -927,6 +982,17 @@ namespace Barotrauma
};
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Vector4 value = (Vector4)property.GetValue(entity);
((GUINumberInput)fields[0]).FloatValue = value.X;
((GUINumberInput)fields[1]).FloatValue = value.Y;
((GUINumberInput)fields[2]).FloatValue = value.Z;
((GUINumberInput)fields[3]).FloatValue = value.W;
}
};
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
@@ -993,13 +1059,13 @@ namespace Barotrauma
{
Color newVal = (Color)property.GetValue(entity);
if (comp == 0)
newVal.R = (byte)(numInput.IntValue);
newVal.R = (byte)numInput.IntValue;
else if (comp == 1)
newVal.G = (byte)(numInput.IntValue);
newVal.G = (byte)numInput.IntValue;
else if (comp == 2)
newVal.B = (byte)(numInput.IntValue);
newVal.B = (byte)numInput.IntValue;
else
newVal.A = (byte)(numInput.IntValue);
newVal.A = (byte)numInput.IntValue;
if (SetPropertyValue(property, entity, newVal))
{
@@ -1010,6 +1076,17 @@ namespace Barotrauma
colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = (Color)property.GetValue(entity);
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Color value = (Color)property.GetValue(entity);
((GUINumberInput)fields[0]).IntValue = value.R;
((GUINumberInput)fields[1]).IntValue = value.G;
((GUINumberInput)fields[2]).IntValue = value.B;
((GUINumberInput)fields[3]).IntValue = value.A;
}
};
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
@@ -1072,6 +1149,17 @@ namespace Barotrauma
};
fields[i] = numberInput;
}
refresh += () =>
{
if (!fields.Any(f => ((GUINumberInput)f).TextBox.Selected))
{
Rectangle value = (Rectangle)property.GetValue(entity);
((GUINumberInput)fields[0]).IntValue = value.X;
((GUINumberInput)fields[1]).IntValue = value.Y;
((GUINumberInput)fields[2]).IntValue = value.Width;
((GUINumberInput)fields[3]).IntValue = value.Height;
}
};
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
return frame;
}

View File

@@ -9,14 +9,14 @@ namespace Barotrauma.Sounds
{
public class SoundBuffers : IDisposable
{
private static HashSet<uint> bufferPool = new HashSet<uint>();
private static readonly HashSet<uint> bufferPool = new HashSet<uint>();
#if OSX
public const int MaxBuffers = 400; //TODO: check that this value works for macOS
#else
public const int MaxBuffers = 32000;
#endif
public static int BuffersGenerated { get; private set; } = 0;
private Sound sound;
private readonly Sound sound;
public uint AlBuffer { get; private set; } = 0;
public uint AlMuffledBuffer { get; private set; } = 0;
@@ -24,55 +24,73 @@ namespace Barotrauma.Sounds
public SoundBuffers(Sound sound) { this.sound = sound; }
public void Dispose()
{
if (AlBuffer != 0) { bufferPool.Add(AlBuffer); }
if (AlMuffledBuffer != 0) { bufferPool.Add(AlMuffledBuffer); }
if (AlBuffer != 0)
{
lock (bufferPool)
{
bufferPool.Add(AlBuffer);
}
}
if (AlMuffledBuffer != 0)
{
lock (bufferPool)
{
bufferPool.Add(AlMuffledBuffer);
}
}
AlBuffer = 0;
AlMuffledBuffer = 0;
}
public static void ClearPool()
{
bufferPool.ForEach(b => Al.DeleteBuffer(b));
bufferPool.Clear();
lock (bufferPool)
{
bufferPool.ForEach(b => Al.DeleteBuffer(b));
bufferPool.Clear();
}
BuffersGenerated = 0;
}
public bool RequestAlBuffers()
{
if (AlBuffer != 0) { return false; }
int alError = 0;
while (bufferPool.Count < 2 && BuffersGenerated < MaxBuffers)
int alError;
lock (bufferPool)
{
Al.GenBuffer(out uint newBuffer);
alError = Al.GetError();
if (alError != Al.NoError)
while (bufferPool.Count < 2 && BuffersGenerated < MaxBuffers)
{
DebugConsole.AddWarning($"Error when generating sound buffer: {Al.GetErrorString(alError)}. {BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
BuffersGenerated = MaxBuffers;
}
else if (!Al.IsBuffer(newBuffer))
{
DebugConsole.AddWarning($"Error when generating sound buffer: result is not a valid buffer. {BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
BuffersGenerated = MaxBuffers;
}
else
{
bufferPool.Add(newBuffer);
BuffersGenerated++;
if (BuffersGenerated >= MaxBuffers)
Al.GenBuffer(out uint newBuffer);
alError = Al.GetError();
if (alError != Al.NoError)
{
DebugConsole.AddWarning($"{BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
DebugConsole.AddWarning($"Error when generating sound buffer: {Al.GetErrorString(alError)}. {BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
BuffersGenerated = MaxBuffers;
}
else if (!Al.IsBuffer(newBuffer))
{
DebugConsole.AddWarning($"Error when generating sound buffer: result is not a valid buffer. {BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
BuffersGenerated = MaxBuffers;
}
else
{
bufferPool.Add(newBuffer);
BuffersGenerated++;
if (BuffersGenerated >= MaxBuffers)
{
DebugConsole.AddWarning($"{BuffersGenerated} buffer(s) were generated. No more sound buffers will be generated.");
}
}
}
}
if (bufferPool.Count >= 2)
{
AlBuffer = bufferPool.First();
bufferPool.Remove(AlBuffer);
AlMuffledBuffer = bufferPool.First();
bufferPool.Remove(AlMuffledBuffer);
return true;
if (bufferPool.Count >= 2)
{
AlBuffer = bufferPool.First();
bufferPool.Remove(AlBuffer);
AlMuffledBuffer = bufferPool.First();
bufferPool.Remove(AlMuffledBuffer);
return true;
}
}
//can't generate any more OpenAL buffers! we'll have to steal a buffer from someone...
@@ -112,7 +130,6 @@ namespace Barotrauma.Sounds
throw new Exception(sound.Filename + " has an invalid muffled buffer!");
}
return true;
}

View File

@@ -167,10 +167,10 @@ namespace Barotrauma.Sounds
public CategoryModifier(int gainMultiplierIndex, float gain, bool muffle)
{
Muffle = muffle;
GainMultipliers = new float[gainMultiplierIndex+1];
for (int i=0;i<GainMultipliers.Length;i++)
GainMultipliers = new float[gainMultiplierIndex + 1];
for (int i = 0; i < GainMultipliers.Length; i++)
{
if (i==gainMultiplierIndex)
if (i == gainMultiplierIndex)
{
GainMultipliers[i] = gain;
}
@@ -183,11 +183,11 @@ namespace Barotrauma.Sounds
public void SetGainMultiplier(int index, float gain)
{
if (GainMultipliers.Length < index+1)
if (GainMultipliers.Length < index + 1)
{
int oldLength = GainMultipliers.Length;
Array.Resize(ref GainMultipliers, index + 1);
for (int i=oldLength;i<GainMultipliers.Length;i++)
for (int i = oldLength; i < GainMultipliers.Length; i++)
{
GainMultipliers[i] = 1.0f;
}

View File

@@ -149,7 +149,7 @@ namespace Barotrauma
{
OverrideMusicType = null;
var soundFiles = GameMain.Instance.GetFilesOfType(ContentType.Sounds);
var soundFiles = GameMain.Instance.GetFilesOfType(ContentType.Sounds).ToList();
List<XElement> soundElements = new List<XElement>();
foreach (ContentFile soundFile in soundFiles)

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -94,7 +94,7 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
@@ -104,6 +104,16 @@
<ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.MacOS.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.MacOS.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,6 +6,11 @@ namespace Barotrauma
{
public bool HasSpawned;
public bool HasItemData
{
get { return itemData != null; }
}
partial void InitProjSpecific(Client client)
{
ClientEndPoint = client.Connection.EndPointString;

View File

@@ -172,11 +172,25 @@ namespace Barotrauma
//refresh the character data of clients who are still in the server
foreach (Client c in GameMain.Server.ConnectedClients)
{
if (c.HasSpawned && c.CharacterInfo != null && c.CharacterInfo.CauseOfDeath != null && c.CharacterInfo.CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
{
//the client has opted to spawn this round with Reaper's Tax
if (c.WaitForNextRoundRespawn.HasValue && !c.WaitForNextRoundRespawn.Value)
{
c.CharacterInfo.StartItemsGiven = false;
characterData.RemoveAll(cd => cd.MatchesClient(c));
characterData.Add(new CharacterCampaignData(c, giveRespawnPenaltyAffliction: true));
continue;
}
}
if (c.Character?.Info == null) { continue; }
if (c.Character.IsDead && c.Character.CauseOfDeath?.Type != CauseOfDeathType.Disconnected) { continue; }
if (c.Character.IsDead && c.Character.CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
{
continue;
}
c.CharacterInfo = c.Character.Info;
characterData.RemoveAll(cd => cd.MatchesClient(c));
characterData.Add(new CharacterCampaignData(c));
characterData.Add(new CharacterCampaignData(c));
}
//refresh the character data of clients who aren't in the server anymore

View File

@@ -70,18 +70,20 @@ namespace Barotrauma
{
Item droppedItem = item;
Entity prevOwner = Owner;
Inventory previousInventory = droppedItem.ParentInventory;
droppedItem.Drop(null);
droppedItem.PreviousParentInventory = previousInventory;
var previousInventory = prevOwner switch
var previousCharacterInventory = prevOwner switch
{
Item itemInventory => (itemInventory.FindParentInventory(inventory => inventory is CharacterInventory) as CharacterInventory),
Item itemInventory => itemInventory.FindParentInventory(inventory => inventory is CharacterInventory) as CharacterInventory,
Character character => character.Inventory,
_ => null
};
if (previousInventory != null && previousInventory != c.Character?.Inventory)
if (previousCharacterInventory != null && previousCharacterInventory != c.Character?.Inventory)
{
GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(previousInventory, c, droppedItem);
GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(previousCharacterInventory, c, droppedItem);
}
if (droppedItem.body != null && prevOwner != null)
@@ -109,7 +111,8 @@ namespace Barotrauma
var holdable = item.GetComponent<Holdable>();
if (holdable != null && !holdable.CanBeDeattached()) { continue; }
if (!prevItems.Contains(item) && !item.CanClientAccess(c))
if (!prevItems.Contains(item) && !item.CanClientAccess(c) &&
(c.Character == null || item.PreviousParentInventory == null || !c.Character.CanAccessInventory(item.PreviousParentInventory)))
{
#if DEBUG || UNSTABLE
DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {(item.ParentInventory?.Owner.ToString() ?? "null")}). No access.", Color.Yellow);

View File

@@ -10,6 +10,8 @@ namespace Barotrauma
{
private CoroutineHandle logPropertyChangeCoroutine;
public Inventory PreviousParentInventory;
public override Sprite Sprite
{
get { return prefab?.sprite; }

View File

@@ -36,21 +36,21 @@ namespace Barotrauma.MapCreatures.Behavior
msg.Write(Offset.X);
msg.Write(Offset.Y);
}
public void ServerWriteBranchGrowth(IWriteMessage msg, BallastFloraBranch branch, int parentId = -1)
{
var (x, y) = branch.Position;
msg.Write(parentId);
msg.Write((int)branch.ID);
msg.WriteRangedInteger((byte) branch.Type, 0b0000, 0b1111);
msg.WriteRangedInteger((byte) branch.Sides, 0b0000, 0b1111);
msg.WriteRangedInteger((byte)branch.Type, 0b0000, 0b1111);
msg.WriteRangedInteger((byte)branch.Sides, 0b0000, 0b1111);
msg.WriteRangedInteger(branch.FlowerConfig.Serialize(), 0, 0xFFF);
msg.WriteRangedInteger(branch.LeafConfig.Serialize(), 0, 0xFFF);
msg.Write((ushort) branch.MaxHealth);
msg.Write((int) (x / VineTile.Size));
msg.Write((int) (y / VineTile.Size));
msg.Write((ushort)branch.MaxHealth);
msg.Write((int)(x / VineTile.Size));
msg.Write((int)(y / VineTile.Size));
}
public void ServerWriteBranchDamage(IWriteMessage msg, BallastFloraBranch branch, float damage)
{
msg.Write((int)branch.ID);

View File

@@ -2342,7 +2342,15 @@ namespace Barotrauma.Networking
}
else
{
characterData.SpawnInventoryItems(spawnedCharacter, spawnedCharacter.Inventory);
if (!characterData.HasItemData && !characterData.CharacterInfo.StartItemsGiven)
{
//clients who've chosen to spawn with the respawn penalty can have CharacterData without inventory data
spawnedCharacter.GiveJobItems(mainSubWaypoints[i]);
}
else
{
characterData.SpawnInventoryItems(spawnedCharacter, spawnedCharacter.Inventory);
}
characterData.ApplyHealthData(spawnedCharacter);
characterData.ApplyOrderData(spawnedCharacter);
spawnedCharacter.GiveIdCardTags(mainSubWaypoints[i]);
@@ -3058,13 +3066,7 @@ namespace Barotrauma.Networking
case ChatMessageType.Radio:
case ChatMessageType.Order:
if (senderCharacter == null) { return; }
//return if senderCharacter doesn't have a working radio
var radio = senderCharacter.Inventory?.AllItems.FirstOrDefault(i => i.GetComponent<WifiComponent>() != null);
if (radio == null || !senderCharacter.HasEquippedItem(radio)) { return; }
senderRadio = radio.GetComponent<WifiComponent>();
if (!senderRadio.CanTransmit()) { return; }
if (!ChatMessage.CanUseRadio(senderCharacter, out senderRadio)) { return; }
break;
case ChatMessageType.Dead:
//character still alive and capable of speaking -> dead chat not allowed

View File

@@ -411,11 +411,7 @@ namespace Barotrauma.Networking
var characterData = campaign?.GetClientCharacterData(clients[i]);
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned)
{
var respawnPenaltyAffliction = AfflictionPrefab.List.FirstOrDefault(a => a.AfflictionType.Equals("respawnpenalty", StringComparison.OrdinalIgnoreCase));
if (respawnPenaltyAffliction != null)
{
character.CharacterHealth.ApplyAffliction(targetLimb: null, respawnPenaltyAffliction.Instantiate(10.0f));
}
GiveRespawnPenaltyAffliction(character);
}
if (characterData == null || characterData.HasSpawned)

View File

@@ -78,6 +78,7 @@ namespace Barotrauma
List<Item> suitableItems = new List<Item>();
foreach (Item item in Item.ItemList)
{
if (item.HiddenInGame || item.NonInteractable || item.NonPlayerTeamInteractable) { continue; }
if (item.Submarine == null || traitors.All(traitor => item.Submarine.TeamID != traitor.Character.TeamID))
{
continue;

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.14.6.0</Version>
<Version>0.1400.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -300,9 +300,9 @@ namespace Barotrauma
if (containedItem == null) { continue; }
if (predicate == null || predicate(containedItem))
{
if (character.Submarine != Submarine.MainSub && avoidDroppingInSea)
if (avoidDroppingInSea && !character.IsInFriendlySub)
{
// If we are outside of main sub, try to put the item in the inventory instead dropping it in the sea.
// If we are not inside a friendly sub (= same team), try to put the item in the inventory instead dropping it.
if (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot))
{
continue;

View File

@@ -2595,7 +2595,7 @@ namespace Barotrauma
{
if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub)
{
if (!(SelectedAiTarget.Entity is Structure wall))
if (!(SelectedAiTarget?.Entity is Structure wall))
{
wall = wallTarget?.Structure;
}

View File

@@ -475,14 +475,16 @@ namespace Barotrauma
bool needsGear = NeedsDivingGear(Character.CurrentHull, out _);
if (!needsGear || oxygenLow)
{
bool shouldKeepTheGearOn =
bool isCurrentObjectiveFindSafety = ObjectiveManager.IsCurrentObjective<AIObjectiveFindSafety>();
bool shouldKeepTheGearOn =
isCurrentObjectiveFindSafety ||
Character.AnimController.InWater ||
Character.AnimController.HeadInWater ||
Character.CurrentHull == null ||
(Character.Submarine?.TeamID != Character.TeamID && !Character.IsEscorted) || // these instances should maybe be combined to a method
ObjectiveManager.IsCurrentObjective<AIObjectiveFindSafety>() ||
Character.Submarine == null ||
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn);
if (oxygenLow && Character.CurrentHull.Oxygen > 0)
if (oxygenLow && Character.CurrentHull.Oxygen > 0 && (!isCurrentObjectiveFindSafety || Character.OxygenAvailable < 1))
{
shouldKeepTheGearOn = false;
}
@@ -1348,13 +1350,15 @@ namespace Barotrauma
/// <summary>
/// Check whether the character has a diving suit in usable condition plus some oxygen.
/// </summary>
public static bool HasDivingSuit(Character character, float conditionPercentage = 0) => HasItem(character, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out _, AIObjectiveFindDivingGear.OXYGEN_SOURCE, conditionPercentage, requireEquipped: true,
predicate: (Item item) => { return character.HasEquippedItem(item, InvSlotType.OuterClothes); });
public static bool HasDivingSuit(Character character, float conditionPercentage = 0)
=> HasItem(character, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out _, AIObjectiveFindDivingGear.OXYGEN_SOURCE, conditionPercentage, requireEquipped: true,
predicate: (Item item) => character.HasEquippedItem(item, InvSlotType.OuterClothes));
/// <summary>
/// Check whether the character has a diving mask in usable condition plus some oxygen.
/// </summary>
public static bool HasDivingMask(Character character, float conditionPercentage = 0) => HasItem(character, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out _, AIObjectiveFindDivingGear.OXYGEN_SOURCE, conditionPercentage, requireEquipped: true);
public static bool HasDivingMask(Character character, float conditionPercentage = 0)
=> HasItem(character, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out _, AIObjectiveFindDivingGear.OXYGEN_SOURCE, conditionPercentage, requireEquipped: true);
private static List<Item> matchingItems = new List<Item>();
@@ -2048,7 +2052,7 @@ namespace Barotrauma
{
var repairItemsObjective = operatingAI.ObjectiveManager.GetObjective<AIObjectiveRepairItems>();
if (repairItemsObjective == null) { continue; }
if (repairItemsObjective.SubObjectives.None(o => o is AIObjectiveRepairItem repairObjective && repairObjective.Item == target))
if (!(repairItemsObjective.SubObjectives.FirstOrDefault(o => o is AIObjectiveRepairItem) is AIObjectiveRepairItem activeObjective) || activeObjective.Item != target)
{
// Not targeting the same item.
continue;

View File

@@ -133,7 +133,7 @@ namespace Barotrauma
}
else
{
if (ItemToContain.ParentInventory == character.Inventory && character.Submarine == Submarine.MainSub)
if (ItemToContain.ParentInventory == character.Inventory && character.IsInFriendlySub)
{
ItemToContain.Drop(character);
}

View File

@@ -53,7 +53,7 @@ namespace Barotrauma
if (HumanAIController.NeedsDivingGear(character.CurrentHull, out bool needsSuit) &&
(needsSuit ?
!HumanAIController.HasDivingSuit(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN) :
!HumanAIController.HasDivingMask(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN)))
!HumanAIController.HasDivingGear(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN)))
{
Priority = 100;
}

View File

@@ -108,6 +108,7 @@ namespace Barotrauma
void ReportWeldingFuelTankCount()
{
if (character.Submarine != Submarine.MainSub) { return; }
int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("weldingfuel") && i.Condition > 1);
if (remainingOxygenTanks == 0)
{
@@ -131,7 +132,7 @@ namespace Barotrauma
Abandon = true;
return;
}
Vector2 toLeak = Leak.WorldPosition - character.WorldPosition;
Vector2 toLeak = Leak.WorldPosition - character.AnimController.AimSourceWorldPos;
// TODO: use the collider size/reach?
if (!character.AnimController.InWater && Math.Abs(toLeak.X) < 100 && toLeak.Y < 0.0f && toLeak.Y > -150)
{
@@ -157,6 +158,7 @@ namespace Barotrauma
{
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(Leak, character, objectiveManager)
{
UseDistanceRelativeToAimSourcePos = true,
CloseEnough = reach,
DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak" : null,
TargetName = Leak.FlowTargetHull?.DisplayName,
@@ -165,7 +167,7 @@ namespace Barotrauma
onAbandon: () =>
{
if (CheckObjectiveSpecific()) { IsCompleted = true; }
else if ((Leak.WorldPosition - character.WorldPosition).LengthSquared() > MathUtils.Pow(reach * 2, 2))
else if ((Leak.WorldPosition - character.AnimController.AimSourceWorldPos).LengthSquared() > MathUtils.Pow(reach * 2, 2))
{
// Too far
Abandon = true;

View File

@@ -66,6 +66,11 @@ namespace Barotrauma
public bool AlwaysUseEuclideanDistance { get; set; } = true;
/// <summary>
/// If true, the distance to the destination is calculated from the character's AimSourcePos (= shoulder) instead of the collider's position
/// </summary>
public bool UseDistanceRelativeToAimSourcePos { get; set; } = false;
public override bool AbandonWhenCannotCompleteSubjectives => !repeat;
public override bool AllowOutsideSubmarine => AllowGoingOutside;
@@ -589,7 +594,9 @@ namespace Barotrauma
float xDiff = Math.Abs(Target.WorldPosition.X - character.WorldPosition.X);
return xDiff <= CloseEnough;
}
return Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough;
Vector2 sourcePos = UseDistanceRelativeToAimSourcePos ? character.AnimController.AimSourceWorldPos : character.WorldPosition;
return Vector2.DistanceSquared(Target.WorldPosition, sourcePos) < CloseEnough * CloseEnough;
}
}

View File

@@ -215,7 +215,9 @@ namespace Barotrauma
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
{
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
endNodeFilter = node => node.Waypoint.Ladders == null
endNodeFilter = node => node.Waypoint.Ladders == null,
// Allow repairing hatches and airlock doors.
AllowGoingOutside = HumanAIController.ObjectiveManager.IsCurrentOrder<AIObjectiveRepairItems>() && Item.GetComponent<Door>() != null
};
if (repairTool != null)
{

View File

@@ -82,6 +82,7 @@ namespace Barotrauma
public static bool ViableForRepair(Item item, Character character, HumanAIController humanAIController)
{
if (!IsValidTarget(item, character)) { return false; }
if (item.CurrentHull == null) { return true; }
if (item.CurrentHull.FireSources.Count > 0) { return false; }
// Don't repair items in rooms that have enemies inside.
if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !humanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
@@ -150,7 +151,6 @@ namespace Barotrauma
if (item.IgnoreByAI(character)) { return false; }
if (!item.IsInteractable(character)) { return false; }
if (item.IsFullCondition) { return false; }
if (item.CurrentHull == null) { return false; }
if (item.Submarine == null || character.Submarine == null) { return false; }
//player crew ignores items in outposts
if (character.IsOnPlayerTeam && item.Submarine.Info.IsOutpost) { return false; }

View File

@@ -234,8 +234,6 @@ namespace Barotrauma
new string[2] { targetCharacter.Name, targetCharacter.CurrentHull.DisplayName }, new bool[2] { false, true }),
null, 1.0f, "foundwoundedtarget" + targetCharacter.Name, 60.0f);
}
character.SelectCharacter(targetCharacter);
}
GiveTreatment(deltaTime);
}
@@ -268,6 +266,8 @@ namespace Barotrauma
}
treatmentTimer = TreatmentDelay;
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
//find which treatments are the most suitable to treat the character's current condition
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false);
@@ -282,6 +282,7 @@ namespace Barotrauma
{
Item matchingItem = character.Inventory.FindItemByIdentifier(treatmentSuitability.Key, true);
if (matchingItem == null) { continue; }
character.SelectCharacter(targetCharacter);
ApplyTreatment(affliction, matchingItem);
//wait a bit longer after applying a treatment to wait for potential side-effects to manifest
treatmentTimer = TreatmentDelay * 4;
@@ -292,7 +293,6 @@ namespace Barotrauma
// Find treatments outside of own inventory only if inside the own sub.
if (character.Submarine != null && character.Submarine.TeamID == character.TeamID)
{
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
//didn't have any suitable treatments available, try to find some medical items
if (currentTreatmentSuitabilities.Any(s => s.Value > cprSuitability))
{
@@ -329,7 +329,6 @@ namespace Barotrauma
new string[2] { targetCharacter.Name, itemListStr }, new bool[2] { false, true }),
null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f);
}
character.DeselectCharacter();
RemoveSubObjective(ref getItemObjective);
TryAddSubObjective(ref getItemObjective,
constructor: () => new AIObjectiveGetItem(character, suitableItemIdentifiers.ToArray(), objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
@@ -345,9 +344,24 @@ namespace Barotrauma
}
}
}
else if (!targetCharacter.IsUnconscious)
{
//no suitable treatments found, not inside our own sub (= can't search for more treatments), the target isn't unconscious (= can't give CPR)
// -> abandon
Abandon = true;
return;
}
if (character != targetCharacter)
{
character.AnimController.Anim = AnimController.Animation.CPR;
if (cprSuitability > 0.0f)
{
character.SelectCharacter(targetCharacter);
character.AnimController.Anim = AnimController.Animation.CPR;
}
else
{
character.DeselectCharacter();
}
}
}

View File

@@ -63,7 +63,7 @@ namespace Barotrauma
allItems = Wreck.GetItems(false);
thalamusItems = allItems.FindAll(i => IsThalamus(i.prefab));
hulls.AddRange(Wreck.GetHulls(false));
var potentialBrainHulls = new Dictionary<Hull, float>();
var potentialBrainHulls = new List<(Hull hull, float weight)>();
brain = new Item(brainPrefab, Vector2.Zero, Wreck);
thalamusItems.Add(brain);
Point minSize = brain.Rect.Size.Multiply(brain.Scale);
@@ -100,10 +100,10 @@ namespace Barotrauma
}
if (weight > 0)
{
potentialBrainHulls.TryAdd(hull, weight);
potentialBrainHulls.Add((hull, weight));
}
}
Hull brainHull = ToolBox.SelectWeightedRandom(potentialBrainHulls.Keys.ToList(), potentialBrainHulls.Values.ToList(), Rand.RandSync.Server);
Hull brainHull = ToolBox.SelectWeightedRandom(potentialBrainHulls.Select(pbh => pbh.hull).ToList(), potentialBrainHulls.Select(pbh => pbh.weight).ToList(), Rand.RandSync.Server);
var thalamusStructurePrefabs = StructurePrefab.Prefabs.Where(p => IsThalamus(p));
if (brainHull == null)
{
@@ -187,8 +187,11 @@ namespace Barotrauma
if (!spawnOrgans.Contains(item))
{
spawnOrgans.Add(item);
// Try to flood the hull so that the spawner won't die.
item.CurrentHull.WaterVolume = item.CurrentHull.Volume;
if (item.CurrentHull != null)
{
// Try to flood the hull so that the spawner won't die.
item.CurrentHull.WaterVolume = item.CurrentHull.Volume;
}
}
}
}

View File

@@ -108,6 +108,16 @@ namespace Barotrauma
public enum Animation { None, Climbing, UsingConstruction, Struggle, CPR };
public Animation Anim;
public Vector2 AimSourceWorldPos
{
get
{
Vector2 sourcePos = character.AnimController.AimSourcePos;
if (character.Submarine != null) { sourcePos += character.Submarine.Position; }
return sourcePos;
}
}
public Vector2 AimSourcePos => ConvertUnits.ToDisplayUnits(AimSourceSimPos);
public virtual Vector2 AimSourceSimPos => Collider.SimPosition;

View File

@@ -1463,7 +1463,7 @@ namespace Barotrauma
target.CharacterHealth.CalculateVitality();
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
{
character.Info.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive, character.Position + Vector2.UnitY * 150.0f);
character.Info?.IncreaseSkillLevel("medical", SkillSettings.Current.SkillIncreasePerCprRevive, character.Position + Vector2.UnitY * 150.0f);
SteamAchievementManager.OnCharacterRevived(target, character);
lastReviveTime = (float)Timing.TotalTime;
#if SERVER

View File

@@ -576,7 +576,7 @@ namespace Barotrauma
get { return pressureProtection; }
set
{
pressureProtection = Math.Max(value, 0.0f);
pressureProtection = Math.Max(value, pressureProtection);
pressureProtectionLastSet = Timing.TotalTime;
}
}
@@ -882,6 +882,8 @@ namespace Barotrauma
}
}
public bool IsInFriendlySub => Submarine != null && Submarine.TeamID == TeamID;
public delegate void OnDeathHandler(Character character, CauseOfDeath causeOfDeath);
public OnDeathHandler OnDeath;
@@ -2065,10 +2067,9 @@ namespace Barotrauma
if (!CanInteract || inventory.Locked) { return false; }
//the inventory belongs to some other character
if (inventory.Owner is Character && inventory.Owner != this)
if (inventory.Owner is Character character && inventory.Owner != this)
{
var owner = (Character)inventory.Owner;
var owner = character;
//can only be accessed if the character is incapacitated and has been selected
return SelectedCharacter == owner && owner.CanInventoryBeAccessed;
}
@@ -2361,7 +2362,7 @@ namespace Barotrauma
#if CLIENT
if (isLocalPlayer)
{
if (!IsMouseOnUI)
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this))
{
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
{
@@ -2832,7 +2833,7 @@ namespace Barotrauma
{
if (Timing.TotalTime > pressureProtectionLastSet + 0.1)
{
PressureProtection = 0.0f;
pressureProtection = 0.0f;
}
}
if (NeedsWater)
@@ -2994,10 +2995,7 @@ namespace Barotrauma
{
despawnTimer = GameMain.Config.CorpseDespawnDelay;
UpdateDespawn(1.0f, ignoreThresholds: true);
if (createNetworkEvents)
{
Spawner.Update();
}
Spawner.Update(createNetworkEvents);
}
public static void RemoveByPrefab(CharacterPrefab prefab)
@@ -3526,6 +3524,7 @@ namespace Barotrauma
}
bool wasDead = IsDead;
Vector2 simPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(dir);
float prevVitality = CharacterHealth.Vitality;
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier, penetration: penetration);
CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking);
if (attacker != this)
@@ -3534,7 +3533,7 @@ namespace Barotrauma
OnAttackedProjSpecific(attacker, attackResult, stun);
if (!wasDead)
{
TryAdjustAttackerSkill(attacker, -attackResult.Damage);
TryAdjustAttackerSkill(attacker, CharacterHealth.Vitality - prevVitality);
if (IsDead)
{
attacker?.RecordKill(this);
@@ -4001,7 +4000,7 @@ namespace Barotrauma
}
else
{
canBePutInOriginalInventory = inventory.CanBePut(newItem, slotIndices[0]);
canBePutInOriginalInventory = inventory.CanBePut(newItem, slotIndices[0], ignoreCondition: true);
}
if (canBePutInOriginalInventory)

View File

@@ -97,7 +97,7 @@ namespace Barotrauma
State = InfectionState.Final;
ActivateHusk();
ApplyDamage(deltaTime, applyForce: true);
character.SetStun(1);
character.SetStun(5);
}
}

View File

@@ -18,10 +18,10 @@
OnFire, InWater, NotInWater,
OnImpact,
OnEating,
OnDeath = OnBroken,
OnDamaged,
OnSevered,
OnProduceSpawned,
OnOpen, OnClose,
OnDeath = OnBroken,
}
}

View File

@@ -10,8 +10,8 @@ namespace Barotrauma
[Serialize("", true)]
public string NPCTag { get; set; }
[Serialize(0, true)]
public int TeamTag { get; set; }
[Serialize(CharacterTeamType.None, true)]
public CharacterTeamType TeamTag { get; set; }
[Serialize(false, true)]
public bool AddToCrew { get; set; }
@@ -29,11 +29,10 @@ namespace Barotrauma
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
foreach (var npc in affectedNpcs)
{
CharacterTeamType newTeam = (CharacterTeamType)TeamTag;
// characters will still remain on friendlyNPC team for rest of the tick
npc.SetOriginalTeam(newTeam);
npc.SetOriginalTeam(TeamTag);
if (AddToCrew && (newTeam == CharacterTeamType.Team1 || newTeam == CharacterTeamType.Team2))
if (AddToCrew && (TeamTag == CharacterTeamType.Team1 || TeamTag == CharacterTeamType.Team2))
{
npc.Info.StartItemsGiven = true;
@@ -44,11 +43,11 @@ namespace Barotrauma
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
if (wifiComponent != null)
{
wifiComponent.TeamID = newTeam;
wifiComponent.TeamID = TeamTag;
}
}
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, newTeam, npc.Inventory.AllItems.Select(it => it.ID).ToArray() });
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, TeamTag, npc.Inventory.AllItems.Select(it => it.ID).ToArray() });
#endif
}
}

View File

@@ -124,13 +124,24 @@ namespace Barotrauma
}
MTRandom rand = new MTRandom(seed);
var initialEventSet = SelectRandomEvents(EventSet.List, rand);
EventSet initialEventSet = SelectRandomEvents(EventSet.List, rand);
EventSet additiveSet = null;
if (initialEventSet != null && initialEventSet.Additive)
{
additiveSet = initialEventSet;
initialEventSet = SelectRandomEvents(EventSet.List.FindAll(e => !e.Additive), rand);
}
if (initialEventSet != null)
{
pendingEventSets.Add(initialEventSet);
CreateEvents(initialEventSet, rand);
}
if (additiveSet != null)
{
pendingEventSets.Add(additiveSet);
CreateEvents(additiveSet, rand);
}
if (level?.LevelData?.Type == LevelData.LevelType.Outpost)
{
//if the outpost is connected to a locked connection, create an event to unlock it

View File

@@ -94,6 +94,8 @@ namespace Barotrauma
public readonly bool TriggerEventCooldown;
public readonly bool Additive;
public readonly Dictionary<string, float> Commonness;
public readonly List<(EventPrefab prefab, float commonness, float probability)> EventPrefabs;
@@ -117,6 +119,8 @@ namespace Barotrauma
MinLevelDifficulty = element.GetAttributeFloat("minleveldifficulty", 0);
MaxLevelDifficulty = Math.Max(element.GetAttributeFloat("maxleveldifficulty", 100), MinLevelDifficulty);
Additive = element.GetAttributeBool("additive", false);
string levelTypeStr = element.GetAttributeString("leveltype", "LocationConnection");
if (!Enum.TryParse(levelTypeStr, true, out LevelType))
{

View File

@@ -24,6 +24,8 @@ namespace Barotrauma
private Submarine sub;
private readonly List<CargoMission> previouslySelectedMissions = new List<CargoMission>();
public override string Description
{
get
@@ -42,7 +44,13 @@ namespace Barotrauma
{
this.sub = sub;
itemConfig = prefab.ConfigElement.Element("Items");
requiredDeliveryAmount = Math.Min(prefab.ConfigElement.GetAttributeFloat("requireddeliveryamount", 0.98f), 1.0f);
requiredDeliveryAmount = Math.Min(prefab.ConfigElement.GetAttributeFloat("requireddeliveryamount", 0.98f), 1.0f);
//this can get called between rounds when the client receives a campaign save
//don't attempt to determine cargo if the sub hasn't been fully loaded
if (sub == null || sub.Loading || sub.Removed || Submarine.Unloading)
{
return;
}
DetermineCargo();
}
@@ -58,6 +66,30 @@ namespace Barotrauma
List<(ItemContainer container, int freeSlots)> containers = sub.GetCargoContainers();
containers.Sort((c1, c2) => { return c2.container.Capacity.CompareTo(c1.container.Capacity); });
previouslySelectedMissions.Clear();
if (GameMain.GameSession?.StartLocation?.SelectedMissions != null)
{
bool isPriorMission = true;
foreach (Mission mission in GameMain.GameSession.StartLocation.SelectedMissions)
{
if (!(mission is CargoMission otherMission)) { continue; }
if (mission == this) { isPriorMission = false; }
previouslySelectedMissions.Add(otherMission);
if (!isPriorMission) { continue; }
foreach (var (element, container) in otherMission.itemsToSpawn)
{
for (int i = 0; i < containers.Count; i++)
{
if (containers[i].container == container)
{
containers[i] = (containers[i].container, containers[i].freeSlots - 1);
break;
}
}
}
}
}
maxItemCount = 0;
foreach (XElement subElement in itemConfig.Elements())
{
@@ -87,9 +119,9 @@ namespace Barotrauma
}
calculatedReward = 0;
foreach (var itemToSpawn in itemsToSpawn)
foreach (var (element, container) in itemsToSpawn)
{
int price = itemToSpawn.element.GetAttributeInt("reward", Prefab.Reward / itemsToSpawn.Count);
int price = element.GetAttributeInt("reward", Prefab.Reward / itemsToSpawn.Count);
if (rewardPerCrate.HasValue)
{
if (price != rewardPerCrate.Value) { rewardPerCrate = -1; }
@@ -108,7 +140,28 @@ namespace Barotrauma
public override int GetReward(Submarine sub)
{
if (sub != this.sub)
bool missionsChanged = false;
if (GameMain.GameSession?.StartLocation?.SelectedMissions != null)
{
List<Mission> currentMissions = GameMain.GameSession.StartLocation.SelectedMissions.Where(m => m is CargoMission).ToList();
if (currentMissions.Count != previouslySelectedMissions.Count)
{
missionsChanged = true;
}
else
{
for (int i = 0; i < previouslySelectedMissions.Count; i++)
{
if (previouslySelectedMissions[i] != currentMissions[i])
{
missionsChanged = true;
break;
}
}
}
}
if (sub != this.sub || missionsChanged)
{
this.sub = sub;
DetermineCargo();

View File

@@ -183,7 +183,7 @@ namespace Barotrauma
}
}
public virtual void SetDifficulty(float difficulty) { }
public virtual void SetLevel(LevelData level) { }
public static Mission LoadRandom(Location[] locations, string seed, bool requireCorrectLocationType, MissionType missionType, bool isSinglePlayer = false)
{
@@ -423,13 +423,16 @@ namespace Barotrauma
protected Character CreateHuman(HumanPrefab humanPrefab, List<Character> characters, Dictionary<Character, List<Item>> characterItems, Submarine submarine, CharacterTeamType teamType, ISpatialEntity positionToStayIn = null, Rand.RandSync humanPrefabRandSync = Rand.RandSync.Server, bool giveTags = true)
{
if (positionToStayIn == null)
{
positionToStayIn = WayPoint.GetRandom(SpawnType.Human, null, submarine);
}
var characterInfo = humanPrefab.GetCharacterInfo(Rand.RandSync.Server) ?? new CharacterInfo(CharacterPrefab.HumanSpeciesName, npcIdentifier: humanPrefab.Identifier, jobPrefab: humanPrefab.GetJobPrefab(humanPrefabRandSync), randSync: humanPrefabRandSync);
characterInfo.TeamID = teamType;
if (positionToStayIn == null)
{
positionToStayIn =
WayPoint.GetRandom(SpawnType.Human, characterInfo.Job?.Prefab, submarine) ??
WayPoint.GetRandom(SpawnType.Human, null, submarine);
}
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, positionToStayIn.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
spawnedCharacter.Prefab = humanPrefab;
humanPrefab.InitializeCharacter(spawnedCharacter, positionToStayIn);

View File

@@ -107,7 +107,7 @@ namespace Barotrauma
//ruin/cave/wreck items are allowed to spawn close to the sub
float minDistance = spawnPositionType == Level.PositionType.Ruin || spawnPositionType == Level.PositionType.Cave || spawnPositionType == Level.PositionType.Wreck ?
0.0f : Level.Loaded.Size.X * 0.3f;
nestPosition = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
Level.Loaded.TryGetInterestingPosition(true, spawnPositionType, 0.0f, out Vector2 nestPosition);
List<GraphEdge> spawnEdges = new List<GraphEdge>();
if (spawnPositionType == Level.PositionType.Cave)
{

View File

@@ -28,6 +28,8 @@ namespace Barotrauma
private float pirateSightingUpdateTimer;
private Vector2? lastSighting;
private LevelData levelData;
public override int TeamCount => 2;
private bool outsideOfSonarRange;
@@ -83,21 +85,24 @@ namespace Barotrauma
characterTypeConfig = prefab.ConfigElement.Element("CharacterTypes");
addedMissionDifficultyPerPlayer = prefab.ConfigElement.GetAttributeFloat("addedmissiondifficultyperplayer", 0);
// for campaign missions, set difficulty at construction
// for campaign missions, set level at construction
LevelData levelData = locations[0].Connections.Where(c => c.Locations.Contains(locations[1])).FirstOrDefault()?.LevelData ?? locations[0]?.LevelData;
SetDifficulty(levelData?.Difficulty ?? Level.Loaded?.Difficulty ?? 0f);
if (levelData != null)
{
SetLevel(levelData);
}
}
public override void SetDifficulty(float difficulty)
public override void SetLevel(LevelData level)
{
if (missionDifficulty > 0f)
if (levelData != null)
{
// difficulty already set
//level already set
return;
}
missionDifficulty = difficulty;
levelData = level;
missionDifficulty = level?.Difficulty ?? 0;
XElement submarineConfig = GetRandomDifficultyModifiedElement(submarineTypeConfig, missionDifficulty, ShipRandomnessModifier);
@@ -123,23 +128,24 @@ namespace Barotrauma
submarineInfo = new SubmarineInfo(contentFile.Path);
}
private float GetDifficultyModifiedValue(float preferredDifficulty, float levelDifficulty, float randomnessModifier)
private float GetDifficultyModifiedValue(float preferredDifficulty, float levelDifficulty, float randomnessModifier, Random rand)
{
return Math.Abs(levelDifficulty - preferredDifficulty + (Rand.Range(-randomnessModifier, randomnessModifier, Rand.RandSync.Server)));
return Math.Abs(levelDifficulty - preferredDifficulty + MathHelper.Lerp(-randomnessModifier, randomnessModifier, (float)rand.NextDouble()));
}
private int GetDifficultyModifiedAmount(int minAmount, int maxAmount, float levelDifficulty)
private int GetDifficultyModifiedAmount(int minAmount, int maxAmount, float levelDifficulty, Random rand)
{
return Math.Max((int)Math.Round(minAmount + (maxAmount - minAmount) * ((levelDifficulty + Rand.Range(-RandomnessModifier, RandomnessModifier, Rand.RandSync.Server)) / MaxDifficulty)), minAmount);
return Math.Max((int)Math.Round(minAmount + (maxAmount - minAmount) * (levelDifficulty + MathHelper.Lerp(-RandomnessModifier, RandomnessModifier, (float)rand.NextDouble())) / MaxDifficulty), minAmount);
}
private XElement GetRandomDifficultyModifiedElement(XElement parentElement, float levelDifficulty, float randomnessModifier)
{
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
// look for the element that is closest to our difficulty, with some randomness
XElement bestElement = null;
float bestValue = float.MaxValue;
foreach (XElement element in parentElement.Elements())
{
float applicabilityValue = GetDifficultyModifiedValue(element.GetAttributeFloat(0f, "preferreddifficulty"), levelDifficulty, randomnessModifier);
float applicabilityValue = GetDifficultyModifiedValue(element.GetAttributeFloat(0f, "preferreddifficulty"), levelDifficulty, randomnessModifier, rand);
if (applicabilityValue < bestValue)
{
bestElement = element;
@@ -154,11 +160,11 @@ namespace Barotrauma
Vector2 patrolPos = enemySub.WorldPosition;
Point subSize = enemySub.GetDockedBorders().Size;
if (!Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath | Level.PositionType.SidePath, Level.Loaded.Size.X * 0.3f, out preferredSpawnPos))
if (!Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out preferredSpawnPos))
{
DebugConsole.ThrowError("Could not spawn pirate submarine in an interesting location! " + this);
}
if (!Level.Loaded.TryGetInterestingPositionAwayFromPoint(true, Level.PositionType.MainPath | Level.PositionType.SidePath, Level.Loaded.Size.X * 0.3f, out patrolPos, preferredSpawnPos, minDistFromPoint: 10000f))
if (!Level.Loaded.TryGetInterestingPositionAwayFromPoint(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out patrolPos, preferredSpawnPos, minDistFromPoint: 10000f))
{
DebugConsole.ThrowError("Could not give pirate submarine an interesting location to patrol to! " + this);
}
@@ -182,7 +188,7 @@ namespace Barotrauma
}
}
private void InitPirateShip(Vector2 spawnPos)
private void InitPirateShip()
{
enemySub.NeutralizeBallast();
if (enemySub.GetItems(alsoFromConnectedSubs: false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
@@ -193,6 +199,7 @@ namespace Barotrauma
enemySub.TeamID = CharacterTeamType.None;
//make the enemy sub withstand atleast the same depth as the player sub
enemySub.RealWorldCrushDepth = Math.Max(enemySub.RealWorldCrushDepth, Submarine.MainSub.RealWorldCrushDepth);
enemySub.ImmuneToBallastFlora = true;
}
private void InitPirates()
@@ -214,12 +221,14 @@ namespace Barotrauma
float enemyCreationDifficulty = missionDifficulty + playerCount * addedMissionDifficultyPerPlayer;
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
bool commanderAssigned = false;
foreach (XElement element in characterConfig.Elements())
{
// it is possible to get more than the "max" amount of characters if the modified difficulty is high enough; this is intentional
// if necessary, another "hard max" value could be used to clamp the value for performance/gameplay concerns
int amountCreated = GetDifficultyModifiedAmount(element.GetAttributeInt("minamount", 0), element.GetAttributeInt("maxamount", 0), enemyCreationDifficulty);
int amountCreated = GetDifficultyModifiedAmount(element.GetAttributeInt("minamount", 0), element.GetAttributeInt("maxamount", 0), enemyCreationDifficulty, rand);
for (int i = 0; i < amountCreated; i++)
{
XElement characterType = characterTypeConfig.Elements().Where(e => e.GetAttributeString("typeidentifier", string.Empty) == element.GetAttributeString("typeidentifier", string.Empty)).FirstOrDefault();
@@ -307,7 +316,7 @@ namespace Barotrauma
#endif
if (!IsClient)
{
InitPirateShip(spawnPos);
InitPirateShip();
}
enemySub.SetPosition(spawnPos);

View File

@@ -651,6 +651,7 @@ namespace Barotrauma
location.ClearMissions();
location.Discovered = false;
location.LevelData?.EventHistory?.Clear();
location.UnlockInitialMissions();
}
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
Map.SelectLocation(-1);
@@ -700,7 +701,7 @@ namespace Barotrauma
if (npc == null || interactor == null) { yield return CoroutineStatus.Failure; }
HumanAIController humanAI = npc.AIController as HumanAIController;
if (humanAI == null) { yield return CoroutineStatus.Failure; }
if (humanAI == null) { yield return CoroutineStatus.Success; }
var waitOrder = Order.PrefabList.Find(o => o.Identifier.Equals("wait", StringComparison.OrdinalIgnoreCase));
humanAI.SetForcedOrder(waitOrder, string.Empty, null);
@@ -719,8 +720,10 @@ namespace Barotrauma
#if CLIENT
ShowCampaignUI = false;
#endif
humanAI.ClearForcedOrder();
if (!npc.Removed)
{
humanAI.ClearForcedOrder();
}
yield return CoroutineStatus.Success;
}
@@ -729,13 +732,16 @@ namespace Barotrauma
public void AssignNPCMenuInteraction(Character character, InteractionType interactionType)
{
character.CampaignInteractionType = interactionType;
if (interactionType == InteractionType.None)
character.CharacterHealth.UseHealthWindow =
interactionType == InteractionType.None ||
interactionType == InteractionType.Examine ||
interactionType == InteractionType.Talk;
if (interactionType == InteractionType.None)
{
character.SetCustomInteract(null, null);
return;
return;
}
character.CharacterHealth.UseHealthWindow = false;
//character.CanInventoryBeAccessed = false;
character.SetCustomInteract(
NPCInteract,
#if CLIENT

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using System.Globalization;
using System.Xml.Linq;
namespace Barotrauma
@@ -29,20 +30,30 @@ namespace Barotrauma
public XElement OrderData { get; private set; }
partial void InitProjSpecific(Client client);
public CharacterCampaignData(Client client)
public CharacterCampaignData(Client client, bool giveRespawnPenaltyAffliction = false)
{
Name = client.Name;
InitProjSpecific(client);
healthData = new XElement("health");
client.Character.CharacterHealth.Save(healthData);
if (client.Character.Inventory != null)
client.Character?.CharacterHealth?.Save(healthData);
if (giveRespawnPenaltyAffliction)
{
var respawnPenaltyAffliction = RespawnManager.GetRespawnPenaltyAffliction();
healthData.Add(new XElement("Affliction",
new XAttribute("identifier", respawnPenaltyAffliction.Identifier),
new XAttribute("strength", respawnPenaltyAffliction.Strength.ToString("G", CultureInfo.InvariantCulture))));
}
if (client.Character?.Inventory != null)
{
itemData = new XElement("inventory");
Character.SaveInventory(client.Character.Inventory, itemData);
}
OrderData = new XElement("orders");
CharacterInfo.SaveOrderData(client.Character.Info, OrderData);
if (client.Character != null)
{
CharacterInfo.SaveOrderData(client.Character.Info, OrderData);
}
}
public CharacterCampaignData(XElement element)

View File

@@ -368,8 +368,8 @@ namespace Barotrauma
foreach (Mission mission in GameMode.Missions)
{
// setting difficulty for missions that may involve difficulty-related submarine creation
mission.SetDifficulty(levelData?.Difficulty ?? 0f);
// setting level for missions that may involve difficulty-related submarine creation
mission.SetLevel(levelData);
}
if (Submarine.MainSubs[1] == null)

View File

@@ -296,10 +296,12 @@ namespace Barotrauma
return;
}
var linkedItems = GetLinkedItemsToSwap(itemToRemove);
int price = 0;
if (!itemToRemove.AvailableSwaps.Contains(itemToInstall))
{
price = itemToInstall.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation);
price = itemToInstall.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count;
}
if (force)
@@ -309,7 +311,7 @@ namespace Barotrauma
if (Campaign.Money >= price)
{
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
PurchasedItemSwaps.RemoveAll(p => linkedItems.Contains(p.ItemToRemove));
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
// only make the NPC speak if more than 5 minutes have passed since the last purchased service
@@ -322,23 +324,27 @@ namespace Barotrauma
Campaign.Money -= price;
itemToRemove.AvailableSwaps.Add(itemToRemove.Prefab);
if (itemToInstall != null && !itemToRemove.AvailableSwaps.Contains(itemToInstall))
foreach (Item itemToSwap in linkedItems)
{
itemToRemove.PurchasedNewSwap = true;
itemToRemove.AvailableSwaps.Add(itemToInstall);
itemToSwap.AvailableSwaps.Add(itemToSwap.Prefab);
if (itemToInstall != null && !itemToSwap.AvailableSwaps.Contains(itemToInstall))
{
itemToSwap.PurchasedNewSwap = true;
itemToSwap.AvailableSwaps.Add(itemToInstall);
}
if (itemToSwap.Prefab != itemToInstall && itemToInstall != null)
{
itemToSwap.PendingItemSwap = itemToInstall;
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToSwap, itemToInstall));
DebugLog($"CLIENT: Swapped item \"{itemToSwap.Name}\" with \"{itemToInstall.Name}\".", Color.Orange);
}
else
{
DebugLog($"CLIENT: Cancelled swapping the item \"{itemToSwap.Name}\" with \"{(itemToSwap.PendingItemSwap?.Name ?? null)}\".", Color.Orange);
}
}
if (itemToRemove.Prefab != itemToInstall && itemToInstall != null)
{
itemToRemove.PendingItemSwap = itemToInstall;
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
DebugLog($"CLIENT: Swapped item \"{itemToRemove.Name}\" with \"{itemToInstall.Name}\".", Color.Orange);
}
else
{
DebugLog($"CLIENT: Cancelled swapping the item \"{itemToRemove.Name}\" with \"{(itemToRemove.PendingItemSwap?.Name ?? null)}\".", Color.Orange);
}
OnUpgradesChanged?.Invoke();
}
else
@@ -382,30 +388,55 @@ namespace Barotrauma
}
}
if (itemToRemove.PendingItemSwap == null)
var linkedItems = GetLinkedItemsToSwap(itemToRemove);
foreach (Item itemToCancel in linkedItems)
{
var replacement = MapEntityPrefab.Find("", swappableItem.ReplacementOnUninstall) as ItemPrefab;
if (replacement == null)
if (itemToCancel.PendingItemSwap == null)
{
DebugConsole.ThrowError($"Failed to uninstall item \"{itemToRemove.Name}\". Could not find the replacement item \"{swappableItem.ReplacementOnUninstall}\".");
return;
var replacement = MapEntityPrefab.Find("", swappableItem.ReplacementOnUninstall) as ItemPrefab;
if (replacement == null)
{
DebugConsole.ThrowError($"Failed to uninstall item \"{itemToCancel.Name}\". Could not find the replacement item \"{swappableItem.ReplacementOnUninstall}\".");
return;
}
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToCancel);
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToCancel, replacement));
DebugLog($"Uninstalled item item \"{itemToCancel.Name}\".", Color.Orange);
itemToCancel.PendingItemSwap = replacement;
}
else
{
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToCancel);
DebugLog($"Cancelled swapping the item \"{itemToCancel.Name}\" with \"{itemToCancel.PendingItemSwap.Name}\".", Color.Orange);
itemToCancel.PendingItemSwap = null;
}
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, replacement));
DebugLog($"Uninstalled item item \"{itemToRemove.Name}\".", Color.Orange);
itemToRemove.PendingItemSwap = replacement;
}
else
{
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
DebugLog($"Cancelled swapping the item \"{itemToRemove.Name}\" with \"{itemToRemove.PendingItemSwap.Name}\".", Color.Orange);
itemToRemove.PendingItemSwap = null;
}
#if CLIENT
OnUpgradesChanged?.Invoke();
#endif
}
public List<Item> GetLinkedItemsToSwap(Item item)
{
List<Item> linkedItems = new List<Item>() { item };
foreach (MapEntity linkedEntity in item.linkedTo)
{
foreach (MapEntity secondLinkedEntity in linkedEntity.linkedTo)
{
if (!(secondLinkedEntity is Item linkedItem) || linkedItem == item) { continue; }
if (linkedItem.AllowSwapping &&
linkedItem.Prefab.SwappableItem != null && linkedItem.Prefab.SwappableItem.CanBeBought &&
linkedItem.Prefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase))
{
linkedItems.Add(linkedItem);
}
}
}
return linkedItems;
}
/// <summary>
/// Applies all our pending upgrades to the submarine.
/// </summary>
@@ -565,7 +596,7 @@ namespace Barotrauma
/// <param name="submarine"></param>
/// <param name="level"></param>
/// <returns>New level that was applied, -1 if no upgrades were applied.</returns>
private static int BuyUpgrade(UpgradePrefab prefab, UpgradeCategory category, Submarine submarine, int level = 1)
private static int BuyUpgrade(UpgradePrefab prefab, UpgradeCategory category, Submarine submarine, int level = 1, Submarine parentSub = null)
{
int? newLevel = null;
if (category.IsWallUpgrade)
@@ -604,6 +635,7 @@ namespace Barotrauma
foreach (Submarine loadedSub in Submarine.Loaded.Where(sub => sub != submarine))
{
if (loadedSub == parentSub) { continue; }
XElement? root = loadedSub.Info?.SubmarineElement;
if (root == null) { continue; }
@@ -615,7 +647,7 @@ namespace Barotrauma
ushort dockingPortID = (ushort) root.GetAttributeInt("originallinkedto", 0);
if (dockingPortID > 0 && submarine.GetItems(true).Any(item => item.ID == dockingPortID))
{
BuyUpgrade(prefab, category, loadedSub, level);
BuyUpgrade(prefab, category, loadedSub, level, submarine);
}
}
}

View File

@@ -132,17 +132,17 @@ namespace Barotrauma
return false;
}
public override bool CanBePut(Item item, int i)
public override bool CanBePut(Item item, int i, bool ignoreCondition = false)
{
return
base.CanBePut(item, i) && item.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) &&
base.CanBePut(item, i, ignoreCondition) && item.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) &&
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
}
public override bool CanBePut(ItemPrefab itemPrefab, int i)
public override bool CanBePut(ItemPrefab itemPrefab, int i, float? condition)
{
return
base.CanBePut(itemPrefab, i) &&
base.CanBePut(itemPrefab, i, condition) &&
(SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1);
}
@@ -214,7 +214,7 @@ namespace Barotrauma
}
}
if (allowedSlots != null && !allowedSlots.Contains(InvSlotType.Any))
if (allowedSlots != null && allowedSlots.Any() && !allowedSlots.Contains(InvSlotType.Any))
{
bool allSlotsTaken = true;
foreach (var allowedSlot in allowedSlots)

View File

@@ -167,10 +167,9 @@ namespace Barotrauma.Items.Components
{
foreach (DockingPort port in list)
{
if (port == this || port.item.Submarine == item.Submarine) continue;
if (Math.Abs(port.item.WorldPosition.X - item.WorldPosition.X) > DistanceTolerance.X) continue;
if (Math.Abs(port.item.WorldPosition.Y - item.WorldPosition.Y) > DistanceTolerance.Y) continue;
if (port == this || port.item.Submarine == item.Submarine || port.IsHorizontal != IsHorizontal) { continue; }
if (Math.Abs(port.item.WorldPosition.X - item.WorldPosition.X) > DistanceTolerance.X) { continue; }
if (Math.Abs(port.item.WorldPosition.Y - item.WorldPosition.Y) > DistanceTolerance.Y) { continue; }
return port;
}

View File

@@ -82,13 +82,13 @@ namespace Barotrauma.Items.Components
get { return nodes; }
}
private readonly List<Pair<Character,Node>> charactersInRange = new List<Pair<Character, Node>>();
private readonly List<(Character character, Node node)> charactersInRange = new List<(Character character, Node node)>();
private bool charging;
private float timer;
private Attack attack;
private readonly Attack attack;
public ElectricalDischarger(Item item, XElement element) :
base(item, element)
@@ -114,8 +114,8 @@ namespace Barotrauma.Items.Components
{
//already active, do nothing
if (IsActive) { return false; }
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
if (character != null && !CharacterUsable) { return false; }
CurrPowerConsumption = powerConsumption;
charging = true;
@@ -182,9 +182,9 @@ namespace Barotrauma.Items.Components
FindNodes(item.WorldPosition, Range);
if (attack != null)
{
foreach (Pair<Character, Node> characterInRange in charactersInRange)
foreach ((Character character, Node node) in charactersInRange)
{
characterInRange.First.ApplyAttack(null, characterInRange.Second.WorldPosition, attack, 1.0f);
character.ApplyAttack(null, node.WorldPosition, attack, 1.0f);
}
}
DischargeProjSpecific();
@@ -315,7 +315,6 @@ namespace Barotrauma.Items.Components
if (closestIndex == -1 || closestDist > currentRange)
{
int originalParentNodeIndex = parentNodeIndex;
//nothing in range, create some arcs to random directions
for (int i = 0; i < Rand.Int(4); i++)
{
@@ -455,7 +454,7 @@ namespace Barotrauma.Items.Components
AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
nodes.Add(new Node(targetPos, parentNodeIndex));
entitiesInRange.RemoveAt(closestIndex);
charactersInRange.Add(new Pair<Character, Node>(character, nodes[parentNodeIndex]));
charactersInRange.Add((character, nodes[parentNodeIndex]));
FindNodes(entitiesInRange, targetPos, nodes.Count - 1, currentRange);
}
}

View File

@@ -678,7 +678,7 @@ namespace Barotrauma.Items.Components
Reset();
previousGap = leak;
}
Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition;
Vector2 fromCharacterToLeak = leak.WorldPosition - character.AnimController.AimSourceWorldPos;
float dist = fromCharacterToLeak.Length();
float reach = AIObjectiveFixLeak.CalculateReach(this, character);
@@ -692,10 +692,10 @@ namespace Barotrauma.Items.Components
if (!character.AnimController.InWater)
{
// TODO: use the collider size?
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController humanAnim &&
Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
{
((HumanoidAnimController)character.AnimController).Crouching = true;
humanAnim.Crouching = true;
}
}
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.inWater))

View File

@@ -12,6 +12,9 @@ namespace Barotrauma.Items.Components
private bool midAir;
//continuous collision detection is used while the item is moving faster than this
const float ContinuousCollisionThreshold = 5.0f;
public Character CurrentThrower
{
get;
@@ -61,6 +64,13 @@ namespace Barotrauma.Items.Components
if (!item.body.Enabled) { return; }
if (midAir)
{
if (item.body.FarseerBody.IsBullet)
{
if (item.body.LinearVelocity.LengthSquared() < ContinuousCollisionThreshold * ContinuousCollisionThreshold)
{
item.body.FarseerBody.IsBullet = false;
}
}
if (item.body.LinearVelocity.LengthSquared() < 0.01f)
{
CurrentThrower = null;
@@ -156,6 +166,7 @@ namespace Barotrauma.Items.Components
//disable platform collisions until the item comes back to rest again
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
item.body.FarseerBody.IsBullet = true;
midAir = true;
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
@@ -165,6 +176,7 @@ namespace Barotrauma.Items.Components
item.body.AngularVelocity = rightHand.body.AngularVelocity;
throwPos = 0;
throwDone = true;
IsActive = true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{

View File

@@ -42,7 +42,7 @@ namespace Barotrauma.Items.Components
protected bool canBeCombined;
protected bool removeOnCombined;
public bool WasUsed;
public bool WasUsed, WasSecondaryUsed;
public readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using FarseerPhysics;
namespace Barotrauma.Items.Components
{
@@ -60,7 +61,21 @@ namespace Barotrauma.Items.Components
Drawable = !hideItems;
}
}
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
public Vector2 ItemPos { get; set; }
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
public Vector2 ItemInterval { get; set; }
[Serialize(100, false, description: "How many items are placed in a row before starting a new row.")]
public int ItemsPerRow { get; set; }
[Serialize(true, false, description: "Should the inventory of this item be visible when the item is selected.")]
public bool DrawInventory
{
@@ -118,6 +133,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false, description: "Should the items configured using SpawnWithId spawn if this item is broken.")]
public bool SpawnWithIdWhenBroken
{
get;
set;
}
[Serialize(false, false)]
public bool RemoveContainedItemsOnDeconstruct { get; set; }
@@ -335,20 +357,66 @@ namespace Barotrauma.Items.Components
public void SetContainedItemPositions()
{
Vector2 simPos = item.SimPosition;
Vector2 displayPos = item.Position;
Vector2 transformedItemPos = ItemPos * item.Scale;
Vector2 transformedItemInterval = ItemInterval * item.Scale;
Vector2 transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
Vector2 transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y);
if (item.body == null)
{
if (item.FlippedX)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemPos.X += item.Rect.Width;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
if (item.FlippedY)
{
transformedItemPos.Y = -transformedItemPos.Y;
transformedItemPos.Y -= item.Rect.Height;
transformedItemInterval.Y = -transformedItemInterval.Y;
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
}
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
if (Math.Abs(item.Rotation) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
}
}
else
{
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
if (item.body.Dir == -1.0f)
{
transformedItemPos.X = -transformedItemPos.X;
transformedItemInterval.X = -transformedItemInterval.X;
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
}
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.Position;
}
float currentRotation = itemRotation;
if (item.body != null)
{
currentRotation += item.body.Rotation;
}
int i = 0;
Vector2 currentItemPos = transformedItemPos;
foreach (Item contained in Inventory.AllItems)
{
if (contained.body != null)
{
try
{
Vector2 simPos = ConvertUnits.ToSimUnits(currentItemPos);
contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, currentRotation);
contained.body.SetPrevTransform(contained.body.SimPosition, contained.body.Rotation);
contained.body.UpdateDrawPosition();
@@ -365,14 +433,29 @@ namespace Barotrauma.Items.Components
contained.Rect =
new Rectangle(
(int)(displayPos.X - contained.Rect.Width / 2.0f),
(int)(displayPos.Y + contained.Rect.Height / 2.0f),
(int)(currentItemPos.X - contained.Rect.Width / 2.0f),
(int)(currentItemPos.Y + contained.Rect.Height / 2.0f),
contained.Rect.Width, contained.Rect.Height);
contained.Submarine = item.Submarine;
contained.CurrentHull = item.CurrentHull;
contained.SetContainedItemPositions();
i++;
if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
{
//interval set on both axes -> use a grid layout
currentItemPos += transformedItemIntervalHorizontal;
if (i % ItemsPerRow == 0)
{
currentItemPos = transformedItemPos;
currentItemPos += transformedItemIntervalVertical * (i / ItemsPerRow);
}
}
else
{
currentItemPos += transformedItemInterval;
}
}
}
@@ -410,7 +493,7 @@ namespace Barotrauma.Items.Components
private void SpawnAlwaysContainedItems()
{
if (SpawnWithId.Length > 0)
if (SpawnWithId.Length > 0 && (item.Condition > 0.0f || SpawnWithIdWhenBroken))
{
string[] splitIds = SpawnWithId.Split(',');
foreach (string id in splitIds)

View File

@@ -102,6 +102,12 @@ namespace Barotrauma.Items.Components
get { return limbPositions.Count > 0; }
}
public bool UserInCorrectPosition
{
get;
private set;
}
public bool AllowAiming
{
get;
@@ -149,6 +155,7 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
this.cam = cam;
UserInCorrectPosition = false;
if (IsToggle)
{
@@ -189,6 +196,7 @@ namespace Barotrauma.Items.Components
else
{
user.AnimController.TargetMovement = Vector2.Zero;
UserInCorrectPosition = true;
}
}
else
@@ -197,7 +205,7 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
{
if (Math.Abs(diff.X) > 20.0f)
{
{
//wait for the character to walk to the correct position
return;
}
@@ -218,7 +226,8 @@ namespace Barotrauma.Items.Components
return;
}
}
user.AnimController.TargetMovement = Vector2.Zero;
user.AnimController.TargetMovement = Vector2.Zero;
UserInCorrectPosition = true;
}
}
@@ -365,7 +374,7 @@ namespace Barotrauma.Items.Components
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
{
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f) { continue; }
if (item.LastSentSignalRecipients[i].Item.Condition <= 0.0f || item.LastSentSignalRecipients[i].IsPower) { continue; }
if (item.LastSentSignalRecipients[i].Item.Prefab.FocusOnSelected)
{
return item.LastSentSignalRecipients[i].Item;

View File

@@ -170,7 +170,7 @@ namespace Barotrauma.Items.Components
private void StartFabricating(FabricationRecipe selectedItem, Character user, bool addToServerLog = true)
{
if (selectedItem == null) { return; }
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem)) { return; }
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition)) { return; }
#if CLIENT
itemList.Enabled = false;
@@ -308,7 +308,7 @@ namespace Barotrauma.Items.Components
}
Character tempUser = user;
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem);
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition);
for (int i = 0; i < fabricatedItem.Amount; i++)
{
if (i < amountFittingContainer)
@@ -334,7 +334,7 @@ namespace Barotrauma.Items.Components
}
}
if (user != null && !user.Removed)
if (user?.Info != null && !user.Removed)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
{

View File

@@ -50,7 +50,7 @@ namespace Barotrauma.Items.Components
set { maxFlow = value; }
}
[Editable, Serialize(true, false, alwaysUseInstanceValues: true)]
[Editable, Serialize(true, true, alwaysUseInstanceValues: true)]
public bool IsOn
{
get { return IsActive; }

View File

@@ -234,7 +234,10 @@ namespace Barotrauma.Items.Components
//use a smoothed "correct output" instead of the actual correct output based on the load
//so the player doesn't have to keep adjusting the rate impossibly fast when the load fluctuates heavily
correctTurbineOutput += MathHelper.Clamp((load / MaxPowerOutput * 100.0f) - correctTurbineOutput, -10.0f, 10.0f) * deltaTime;
if (!MathUtils.NearlyEqual(MaxPowerOutput, 0.0f))
{
correctTurbineOutput += MathHelper.Clamp((load / MaxPowerOutput * 100.0f) - correctTurbineOutput, -10.0f, 10.0f) * deltaTime;
}
//calculate tolerances of the meters based on the skills of the user
//more skilled characters have larger "sweet spots", making it easier to keep the power output at a suitable level
@@ -320,7 +323,7 @@ namespace Barotrauma.Items.Components
//reset the fission rate, turbine output and
//temperature to optimal levels to prevent fires
//at the start of the round
correctTurbineOutput = currentLoad / MaxPowerOutput * 100.0f;
correctTurbineOutput = MathUtils.NearlyEqual(MaxPowerOutput, 0.0f) ? 0.0f : currentLoad / MaxPowerOutput * 100.0f;
tolerance = MathHelper.Lerp(2.5f, 10.0f, degreeOfSuccess);
optimalTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);
tolerance = MathHelper.Lerp(5.0f, 20.0f, degreeOfSuccess);

View File

@@ -795,6 +795,7 @@ namespace Barotrauma.Items.Components
steeringInput = XMLExtensions.ParseVector2(signal.value, errorMessages: false);
steeringInput.X = MathHelper.Clamp(steeringInput.X, -100.0f, 100.0f);
steeringInput.Y = MathHelper.Clamp(-steeringInput.Y, -100.0f, 100.0f);
TargetVelocity = steeringInput;
}
else
{

View File

@@ -74,8 +74,6 @@ namespace Barotrauma.Items.Components
[Serialize(100f, true, "How much fertilizer can the planter hold.")]
public float FertilizerCapacity { get; set; }
public string LastAction { get; set; } = "";
public Growable?[] GrowableSeeds = new Growable?[0];
private readonly List<RelatedItem> SuitableFertilizer = new List<RelatedItem>();
@@ -119,7 +117,7 @@ namespace Barotrauma.Items.Components
GrowableSeeds = new Growable[container.Capacity];
}
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
public override bool HasRequiredItems(Character character, bool addMessage, string? msg = null)
{
if (container?.Inventory == null) { return false; }
@@ -137,9 +135,16 @@ namespace Barotrauma.Items.Components
return true;
}
Msg = MsgHarvest;
if (GrowableSeeds.Any(s => s != null))
{
Msg = MsgHarvest;
ParseMsg();
return true;
}
Msg = string.Empty;
ParseMsg();
return true;
return false;
}
public override bool Pick(Character character)
@@ -163,9 +168,16 @@ namespace Barotrauma.Items.Components
switch (plantItem.Type)
{
case PlantItemType.Seed:
LastAction = "PlantSeed";
ApplyStatusEffects(ActionType.OnPicked, 1.0f, character);
return container.Inventory.TryPutItem(plantItem.Item, character, new List<InvSlotType> { InvSlotType.Any });
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
return container.Inventory.TryPutItem(plantItem.Item, character);
}
else
{
//let the server handle moving the item
return false;
}
case PlantItemType.Fertilizer when plantItem.Item != null:
float canAdd = FertilizerCapacity - Fertilizer;
float maxAvailable = plantItem.Item.Condition;
@@ -175,7 +187,6 @@ namespace Barotrauma.Items.Components
#if CLIENT
character.UpdateHUDProgressBar(this, Item.DrawPosition, Fertilizer / FertilizerCapacity, Color.SaddleBrown, Color.SaddleBrown, "entityname.fertilizer");
#endif
LastAction = "ApplyFertilizer";
ApplyStatusEffects(ActionType.OnPicked, 1.0f, character);
return false;
}
@@ -203,7 +214,6 @@ namespace Barotrauma.Items.Components
container?.Inventory.RemoveItem(seed.Item);
Entity.Spawner?.AddToRemoveQueue(seed.Item);
GrowableSeeds[i] = null;
LastAction = "Harvest";
ApplyStatusEffects(ActionType.OnPicked, 1.0f, character);
return true;
}

View File

@@ -133,6 +133,12 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (item.Connections == null)
{
IsActive = false;
return;
}
isRunning = true;
float chargeRatio = charge / capacity;
float gridPower = 0.0f;
@@ -173,7 +179,13 @@ namespace Barotrauma.Items.Components
}
else
{
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f);
float missingCharge = capacity - charge;
float targetRechargeSpeed = rechargeSpeed;
if (missingCharge < 1.0f)
{
targetRechargeSpeed *= missingCharge;
}
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, targetRechargeSpeed, 0.05f);
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f;
}

View File

@@ -408,21 +408,25 @@ namespace Barotrauma.Items.Components
}
}
bool hitSomething = false;
int hitCount = 0;
Vector2 lastHitPos = item.WorldPosition;
hits = hits.OrderBy(h => h.Fraction).ToList();
foreach (HitscanResult h in hits)
for (int i = 0; i < hits.Count; i++)
{
var h = hits[i];
item.SetTransform(h.Point, rotation);
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
{
LaunchProjSpecific(rayStartWorld, item.WorldPosition);
hitSomething = true;
break;
hitCount++;
if (hitCount >= MaxTargetsToHit || i == hits.Count - 1)
{
LaunchProjSpecific(rayStartWorld, item.WorldPosition);
break;
}
}
}
//the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething)
//the raycast didn't hit anything (or didn't hit enough targets to stop the projectile) -> the projectile flew somewhere outside the level and is permanently lost
if (hitCount < MaxTargetsToHit)
{
item.body.SetTransformIgnoreContacts(item.body.SimPosition, rotation);
LaunchProjSpecific(rayStartWorld, rayEndWorld);
@@ -467,7 +471,7 @@ namespace Barotrauma.Items.Components
}
if (fixture.Body.UserData is VineTile) { return true; }
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return true; }
if (fixture.Body.UserData as string == "ruinroom") { return true; }
if (fixture.Body.UserData as string == "ruinroom" || fixture.Body.UserData is Hull || fixture.UserData is Hull) { return true; }
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
if (submarine != null)
@@ -505,7 +509,7 @@ namespace Barotrauma.Items.Components
if (fixture.Body.UserData is VineTile) { return -1; }
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return -1; }
if (fixture.Body?.UserData as string == "ruinroom") { return -1; }
if (fixture.Body.UserData as string == "ruinroom" || fixture.Body?.UserData is Hull || fixture.UserData is Hull) { return -1; }
//ignore everything else than characters, sub walls and level walls
if (!fixture.CollisionCategories.HasFlag(Physics.CollisionCharacter) &&
@@ -640,7 +644,7 @@ namespace Barotrauma.Items.Components
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) - dir,
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) + dir,
collisionCategory: Physics.CollisionWall);
if (wallBody?.FixtureList?.First() != null && wallBody.UserData is Structure &&
if (wallBody?.FixtureList?.First() != null && (wallBody.UserData is Structure || wallBody.UserData is Item) &&
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
{
@@ -738,7 +742,15 @@ namespace Barotrauma.Items.Components
}
else if (target.Body.UserData is IDamageable damageable)
{
if (Attack != null) { attackResult = Attack.DoDamage(User ?? Attacker, damageable, item.WorldPosition, 1.0f); }
if (Attack != null)
{
Vector2 pos = item.WorldPosition;
if (item.Submarine == null && damageable is Structure structure && structure.Submarine != null && Vector2.DistanceSquared(item.WorldPosition, structure.WorldPosition) > 10000.0f * 10000.0f)
{
item.Submarine = structure.Submarine;
}
attackResult = Attack.DoDamage(User ?? Attacker, damageable, pos, 1.0f);
}
}
else if (target.Body.UserData is VoronoiCell voronoiCell && voronoiCell.IsDestructible && Attack != null && Math.Abs(Attack.LevelWallDamage) > 0.0f)
{

View File

@@ -139,7 +139,7 @@ namespace Barotrauma.Items.Components
//backwards compatibility
var repairThresholdAttribute =
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)) ??
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("airepairth44reshold", StringComparison.OrdinalIgnoreCase));
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("airepairthreshold", StringComparison.OrdinalIgnoreCase));
if (repairThresholdAttribute != null)
{
if (float.TryParse(repairThresholdAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out float repairThreshold))
@@ -349,7 +349,7 @@ namespace Barotrauma.Items.Components
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.Position + Vector2.UnitY * 100.0f);
}
@@ -379,7 +379,7 @@ namespace Barotrauma.Items.Components
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
CurrentFixer.Info?.IncreaseSkillLevel(skill.Identifier,
SkillSettings.Current.SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.Position + Vector2.UnitY * 100.0f);
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
@@ -23,7 +24,7 @@ namespace Barotrauma.Items.Components
private int signalQueueSize;
private int delayTicks;
private Queue<DelayedSignal> signalQueue;
private readonly Queue<DelayedSignal> signalQueue;
private DelayedSignal prevQueuedSignal;
@@ -37,7 +38,7 @@ namespace Barotrauma.Items.Components
if (value == delay) { return; }
delay = value;
delayTicks = (int)(delay / Timing.Step);
signalQueueSize = delayTicks * 2;
signalQueueSize = Math.Max(delayTicks, 1) * 2;
}
}
@@ -74,7 +75,14 @@ namespace Barotrauma.Items.Components
var signalOut = signalQueue.Peek();
signalOut.SendDuration -= 1;
item.SendSignal(new Signal(signalOut.Signal.value, strength: signalOut.Signal.strength), "signal_out");
if (signalOut.SendDuration <= 0) { signalQueue.Dequeue(); } else { break; }
if (signalOut.SendDuration <= 0)
{
signalQueue.Dequeue();
}
else
{
break;
}
}
}

View File

@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
return retVal;
}
public static bool operator==(Signal a, Signal b) =>
public static bool operator ==(Signal a, Signal b) =>
a.value == b.value &&
a.stepsTaken == b.stepsTaken &&
a.sender == b.sender &&
@@ -35,6 +35,6 @@ namespace Barotrauma.Items.Components
MathUtils.NearlyEqual(a.power, b.power) &&
MathUtils.NearlyEqual(a.strength, b.strength);
public static bool operator!=(Signal a, Signal b) => !(a == b);
public static bool operator !=(Signal a, Signal b) => !(a == b);
}
}

View File

@@ -77,11 +77,10 @@ namespace Barotrauma.Items.Components
//item in water -> we definitely want to send the True output
isInWater = true;
}
else if (item.CurrentHull != null)
else if (item.CurrentHull != null && item.CurrentHull.WaterPercentage > 0.0f)
{
//item in not water -> check if there's water anywhere within the rect of the item
if (item.CurrentHull.Surface > item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height + 1 &&
item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
//(center of the) item in not water -> check if the water surface is below the bottom of the item's rect
if (item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
{
isInWater = true;
}

View File

@@ -22,7 +22,9 @@ namespace Barotrauma.Items.Components
private string prevSignal;
private int[] channelMemory = new int[ChannelMemorySize];
private readonly int[] channelMemory = new int[ChannelMemorySize];
private Connection signalOutConnection;
[Serialize(CharacterTeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.", alwaysUseInstanceValues: true)]
public CharacterTeamType TeamID { get; set; }
@@ -93,6 +95,10 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
if (item.Connections != null)
{
signalOutConnection = item.Connections.Find(c => c.Name == "signal_out");
}
if (channelMemory.All(m => m == 0))
{
for (int i = 0; i < channelMemory.Length; i++)
@@ -155,6 +161,10 @@ namespace Barotrauma.Items.Components
public void TransmitSignal(Signal signal, bool sentFromChat)
{
if (sentFromChat)
{
item.LastSentSignalRecipients.Clear();
}
var senderComponent = signal.source?.GetComponent<WifiComponent>();
if (senderComponent != null && !CanReceive(senderComponent)) { return; }
@@ -168,10 +178,14 @@ namespace Barotrauma.Items.Components
//signal strength diminishes by distance
float sentSignalStrength = signal.strength *
MathHelper.Clamp(1.0f - (Vector2.Distance(item.WorldPosition, wifiComp.item.WorldPosition) / wifiComp.range), 0.0f, 1.0f);
Signal s = new Signal(signal.value, signal.stepsTaken, sender: signal.sender, source: signal.source,
Signal s = new Signal(signal.value, ++signal.stepsTaken, sender: signal.sender, source: signal.source,
power: 0.0f, strength: sentSignalStrength);
wifiComp.item.SendSignal(s, "signal_out");
if (wifiComp.signalOutConnection != null)
{
wifiComp.item.SendSignal(s, wifiComp.signalOutConnection);
}
if (signal.source != null)
{
foreach (Connection receiver in wifiComp.item.LastSentSignalRecipients)

View File

@@ -844,10 +844,11 @@ namespace Barotrauma.Items.Components
ClearConnections();
base.RemoveComponentSpecific();
#if CLIENT
if (DraggingWire == this) { draggingWire = null; }
overrideSprite?.Remove();
overrideSprite = null;
wireSprite = null;
#endif
}
}
}
}

View File

@@ -562,6 +562,7 @@ namespace Barotrauma.Items.Components
//use linked projectile containers in case they have to react to the turret being launched somehow
//(play a sound, spawn more projectiles)
if (!(e is Item linkedItem)) { continue; }
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
if (linkedItem.Condition <= 0.0f)
{
loaderBroken = true;
@@ -971,6 +972,7 @@ namespace Barotrauma.Items.Components
foreach (MapEntity e in item.linkedTo)
{
if (!item.IsInteractable(character)) { continue; }
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
if (e is Item projectileContainer)
{
var container = projectileContainer.GetComponent<ItemContainer>();
@@ -1323,6 +1325,7 @@ namespace Barotrauma.Items.Components
CheckProjectileContainer(item, projectiles, out bool _);
foreach (MapEntity e in item.linkedTo)
{
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
if (e is Item projectileContainer)
{
CheckProjectileContainer(projectileContainer, projectiles, out bool stopSearching);

View File

@@ -28,22 +28,25 @@ namespace Barotrauma
get { return items.Count; }
}
public bool CanBePut(Item item)
public bool CanBePut(Item item, bool ignoreCondition = false)
{
if (item == null) { return false; }
if (items.Count > 0)
{
if (item.IsFullCondition)
if (!ignoreCondition)
{
if (items.Any(it => !it.IsFullCondition)) { return false; }
}
else if (MathUtils.NearlyEqual(item.Condition, 0.0f))
{
if (items.Any(it => !MathUtils.NearlyEqual(it.Condition, 0.0f))) { return false; }
}
else
{
return false;
if (item.IsFullCondition)
{
if (items.Any(it => !it.IsFullCondition)) { return false; }
}
else if (MathUtils.NearlyEqual(item.Condition, 0.0f))
{
if (items.Any(it => !MathUtils.NearlyEqual(it.Condition, 0.0f))) { return false; }
}
else
{
return false;
}
}
if (items[0].Prefab.Identifier != item.Prefab.Identifier ||
items.Count + 1 > item.Prefab.MaxStackSize)
@@ -54,12 +57,31 @@ namespace Barotrauma
return true;
}
public bool CanBePut(ItemPrefab itemPrefab)
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
{
if (itemPrefab == null) { return false; }
if (items.Count > 0)
{
if (items.Any(it => !it.IsFullCondition)) { return false; }
if (condition.HasValue)
{
if (MathUtils.NearlyEqual(condition.Value, 0.0f))
{
if (items.Any(it => it.Condition > 0.0f)) { return false; }
}
else if (MathUtils.NearlyEqual(condition.Value, itemPrefab.Health))
{
if (items.Any(it => !it.IsFullCondition)) { return false; }
}
else
{
return false;
}
}
else
{
if (items.Any(it => !it.IsFullCondition)) { return false; }
}
if (items[0].Prefab.Identifier != itemPrefab.Identifier ||
items.Count + 1 > itemPrefab.MaxStackSize)
{
@@ -70,13 +92,31 @@ namespace Barotrauma
}
/// <param name="maxStackSize">Defaults to <see cref="ItemPrefab.MaxStackSize"/> if null</param>
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null)
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null, float? condition = null)
{
if (itemPrefab == null) { return 0; }
maxStackSize ??= itemPrefab.MaxStackSize;
if (items.Count > 0)
{
if (items.Any(it => !it.IsFullCondition)) { return 0; }
if (condition.HasValue)
{
if (MathUtils.NearlyEqual(condition.Value, 0.0f))
{
if (items.Any(it => it.Condition > 0.0f)) { return 0; }
}
else if (MathUtils.NearlyEqual(condition.Value, itemPrefab.Health))
{
if (items.Any(it => !it.IsFullCondition)) { return 0; }
}
else
{
return 0;
}
}
else
{
if (items.Any(it => !it.IsFullCondition)) { return 0; }
}
if (items[0].Prefab.Identifier != itemPrefab.Identifier) { return 0; }
return maxStackSize.Value - items.Count;
}
@@ -373,42 +413,42 @@ namespace Barotrauma
/// <summary>
/// Can the item be put in the specified slot.
/// </summary>
public virtual bool CanBePut(Item item, int i)
public virtual bool CanBePut(Item item, int i, bool ignoreCondition = false)
{
if (ItemOwnsSelf(item)) { return false; }
if (i < 0 || i >= slots.Length) { return false; }
return slots[i].CanBePut(item);
return slots[i].CanBePut(item, ignoreCondition);
}
public bool CanBePut(ItemPrefab itemPrefab)
public bool CanBePut(ItemPrefab itemPrefab, float? condition = null)
{
for (int i = 0; i < capacity; i++)
{
if (CanBePut(itemPrefab, i)) { return true; }
if (CanBePut(itemPrefab, i, condition)) { return true; }
}
return false;
}
public virtual bool CanBePut(ItemPrefab itemPrefab, int i)
public virtual bool CanBePut(ItemPrefab itemPrefab, int i, float? condition = null)
{
if (i < 0 || i >= slots.Length) { return false; }
return slots[i].CanBePut(itemPrefab);
return slots[i].CanBePut(itemPrefab, condition);
}
public int HowManyCanBePut(ItemPrefab itemPrefab)
public int HowManyCanBePut(ItemPrefab itemPrefab, float? condition = null)
{
int count = 0;
for (int i = 0; i < capacity; i++)
{
count += HowManyCanBePut(itemPrefab, i);
count += HowManyCanBePut(itemPrefab, i, condition);
}
return count;
}
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i)
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
{
if (i < 0 || i >= slots.Length) { return 0; }
return slots[i].HowManyCanBePut(itemPrefab);
return slots[i].HowManyCanBePut(itemPrefab, condition: condition);
}
/// <summary>

Some files were not shown because too many files have changed in this diff Show More