v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
@@ -9,7 +9,10 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -216,7 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
for (int i = 0; i < Prefab.BrokenSprites.Length;i++)
|
||||
{
|
||||
if (Prefab.BrokenSprites[i].FadeIn) { continue; }
|
||||
@@ -340,9 +343,28 @@ namespace Barotrauma
|
||||
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
|
||||
if (renderTransparent) { color *= 0.15f; }
|
||||
|
||||
if (Character.Controlled != null && Character.DebugDrawInteract)
|
||||
{
|
||||
color = Color.Red;
|
||||
foreach (var ic in components)
|
||||
{
|
||||
var interactionType = GetComponentInteractionVisibility(Character.Controlled, ic);
|
||||
if (interactionType == InteractionVisibility.MissingRequirement)
|
||||
{
|
||||
color = Color.Orange;
|
||||
}
|
||||
else if (interactionType == InteractionVisibility.Visible)
|
||||
{
|
||||
color = Color.LightGreen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BrokenItemSprite fadeInBrokenSprite = null;
|
||||
float fadeInBrokenSpriteAlpha = 0.0f;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
Vector2 drawOffset = GetCollapseEffectOffset();
|
||||
drawOffset.Y = -drawOffset.Y;
|
||||
|
||||
@@ -849,27 +871,6 @@ namespace Barotrauma
|
||||
itemEditor.Children.First().Color = Color.Black * 0.7f;
|
||||
if (!inGame)
|
||||
{
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagsField = itemEditor.Fields["Tags".ToIdentifier()].First().Parent;
|
||||
|
||||
//find all the items that can be put inside the container and add their PreferredContainer identifiers/tags to the available tags
|
||||
ImmutableHashSet<Identifier> availableTags = ItemPrefab.Prefabs
|
||||
.Where(ip => itemContainer.CanBeContained(ip))
|
||||
.SelectMany(ip => ip.PreferredContainers.SelectMany(pc => pc.Primary.Union(pc.Secondary)))
|
||||
//remove identifiers from the available container tags
|
||||
//(otherwise the list will include many irrelevant options,
|
||||
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
|
||||
.Where(t => !ItemPrefab.Prefabs.ContainsKey(t))
|
||||
.ToImmutableHashSet();
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
|
||||
{
|
||||
OnClicked = (bt, userData) => { CreateTagPicker(tagsField.GetChild<GUITextBox>(), availableTags); return true; }
|
||||
};
|
||||
}
|
||||
|
||||
if (Linkable)
|
||||
{
|
||||
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("HoldToLink"), font: GUIStyle.SmallFont);
|
||||
@@ -881,6 +882,33 @@ namespace Barotrauma
|
||||
linkText.TextColor = GUIStyle.Orange;
|
||||
itemsText.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagBox = itemEditor.Fields["Tags".ToIdentifier()].First() as GUITextBox;
|
||||
var tagsField = tagBox?.Parent;
|
||||
|
||||
var containerTagLayout = new GUILayoutGroup(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), isHorizontal: true);
|
||||
var containerTagButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1), containerTagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
|
||||
new GUIButton(new RectTransform(new Vector2(0.95f, 1), containerTagButtonLayout.RectTransform), text: TextManager.Get("containertaguibutton"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (_, _) => { CreateContainerTagPicker(tagBox); return true; },
|
||||
TextBlock = { AutoScaleHorizontal = true }
|
||||
};
|
||||
var containerTagText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1), containerTagLayout.RectTransform), TextManager.Get("containertaguibuttondescription"), font: GUIStyle.SmallFont)
|
||||
{
|
||||
TextColor = GUIStyle.Orange
|
||||
};
|
||||
var limitedString = ToolBox.LimitString(containerTagText.Text, containerTagText.Font, itemEditor.Rect.Width - containerTagButtonLayout.Rect.Width);
|
||||
if (limitedString != containerTagText.Text)
|
||||
{
|
||||
containerTagText.ToolTip = containerTagText.Text;
|
||||
containerTagText.Text = limitedString;
|
||||
}
|
||||
itemEditor.AddCustomContent(containerTagLayout, 3);
|
||||
}
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
|
||||
{
|
||||
@@ -972,6 +1000,12 @@ namespace Barotrauma
|
||||
};
|
||||
itemEditor.AddCustomContent(tickBox, 1);
|
||||
}
|
||||
|
||||
if (!Layer.IsNullOrEmpty())
|
||||
{
|
||||
var layerText = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)) { MinSize = new Point(0, heightScaled) }, TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
|
||||
itemEditor.AddCustomContent(layerText, 1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
@@ -987,7 +1021,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ic.requiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
if (ic.RequiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
@@ -1004,7 +1038,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<RelatedItem> requiredItems = new List<RelatedItem>();
|
||||
foreach (var kvp in ic.requiredItems)
|
||||
foreach (var kvp in ic.RequiredItems)
|
||||
{
|
||||
foreach (RelatedItem relatedItem in kvp.Value)
|
||||
{
|
||||
@@ -1089,34 +1123,389 @@ namespace Barotrauma
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CreateTagPicker(GUITextBox textBox, IEnumerable<Identifier> availableTags)
|
||||
public void CreateContainerTagPicker([MaybeNull] GUITextBox tagTextBox)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", "", new LocalizedString[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
|
||||
var msgBox = new GUIMessageBox(string.Empty, string.Empty, new[] { TextManager.Get("Ok") }, new Vector2(0.35f, 0.6f), new Point(400, 400));
|
||||
msgBox.Buttons[0].OnClicked = msgBox.Close;
|
||||
|
||||
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
|
||||
var infoIcon = new GUIImage(new RectTransform(new Vector2(0.066f), msgBox.InnerFrame.RectTransform)
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
OnSelected = (component, userData) =>
|
||||
RelativeOffset = new Vector2(0.015f)
|
||||
}, style: "GUIButtonInfo")
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.tutorial"),
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
var layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.85f), msgBox.Content.RectTransform));
|
||||
|
||||
var list = new GUIListBox(new RectTransform(new Vector2(1f, 1f), layout.RectTransform));
|
||||
|
||||
const float NameSize = 0.4f;
|
||||
const float ItemSize = 0.5f;
|
||||
const float CountSize = 0.1f;
|
||||
|
||||
var headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(NameSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.tag"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(ItemSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.items"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(CountSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.count"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
var itemsByTag =
|
||||
ContainerTagPrefab.Prefabs
|
||||
.ToImmutableDictionary(
|
||||
ct => ct,
|
||||
ct => ct.GetItemsAndSpawnProbabilities());
|
||||
|
||||
// Group the prefabs by category and turn them into a dictionary where the key is the category and value is the list of identifiers of the prefabs.
|
||||
// LINQ GroupBy returns GroupedEnumerable where the enumerable is the list of prefabs and key is what we grouped by.
|
||||
var tagCategories = ContainerTagPrefab.Prefabs
|
||||
.GroupBy(ct => ct.Category)
|
||||
.ToImmutableDictionary(
|
||||
g => g.Key,
|
||||
g => g.Select(ct => ct.Identifier).ToImmutableArray());
|
||||
|
||||
foreach (var (category, categoryTags) in tagCategories)
|
||||
{
|
||||
var categoryButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), style: "GUIButtonSmallFreeScale");
|
||||
categoryButton.Color *= 0.66f;
|
||||
var categoryLayout = new GUILayoutGroup(new RectTransform(Vector2.One, categoryButton.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
var categoryText = new GUITextBlock(new RectTransform(Vector2.One, categoryLayout.RectTransform), TextManager.Get($"tagcategory.{category}"), font: GUIStyle.SubHeadingFont);
|
||||
var arrowImage = new GUIImage(new RectTransform(new Vector2(1f, 0.5f), categoryLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonVerticalArrowFreeScale");
|
||||
var arrowPadding = new GUIFrame(new RectTransform(new Vector2(0.025f, 1f), categoryLayout.RectTransform), style: null);
|
||||
|
||||
bool hasHiddenCategories = false;
|
||||
foreach (var categoryTag in categoryTags.OrderBy(t => t.Value))
|
||||
{
|
||||
if (!(userData is Identifier)) { return true; }
|
||||
AddTag((Identifier)userData);
|
||||
textBox.Text = Tags;
|
||||
msgBox.Close();
|
||||
var found = itemsByTag.FirstOrNull(kvp => kvp.Key.Identifier == categoryTag);
|
||||
if (found is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find tag with identifier {categoryTag} in itemsByTag");
|
||||
continue;
|
||||
}
|
||||
|
||||
var (tag, prefabsAndProbabilities) = found.Value;
|
||||
|
||||
bool isCorrectSubType = tag.IsRecommendedForSub(Submarine);
|
||||
|
||||
var tagLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), list.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = category,
|
||||
Visible = isCorrectSubType
|
||||
};
|
||||
|
||||
if (!isCorrectSubType)
|
||||
{
|
||||
hasHiddenCategories = true;
|
||||
}
|
||||
|
||||
var checkBoxLayout = new GUILayoutGroup(new RectTransform(new Vector2(NameSize, 1f), tagLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
var enabledCheckBox = new GUITickBox(new RectTransform(Vector2.One, checkBoxLayout.RectTransform, Anchor.Center), tag.Name, font: GUIStyle.SmallFont)
|
||||
{
|
||||
Selected = tags.Contains(tag.Identifier),
|
||||
ToolTip = tag.Description
|
||||
};
|
||||
|
||||
var tickBoxText = enabledCheckBox.TextBlock;
|
||||
tickBoxText.Text = ToolBox.LimitString(tickBoxText.Text, tickBoxText.Font, tickBoxText.Rect.Width);
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(ItemSize, 1f), tagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var itemLayoutScissor = new GUIScissorComponent(new RectTransform(new Vector2(0.8f, 1f), itemLayout.RectTransform)) { CanBeFocused = false };
|
||||
var itemLayoutButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1), itemLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
var itemLayoutButton = new GUIButton(new RectTransform(new Vector2(0.8f), itemLayoutButtonLayout.RectTransform), text: "...", style: "GUICharacterInfoButton")
|
||||
{
|
||||
UserData = tag,
|
||||
ToolTip = TextManager.Get("containertagui.viewprobabilities")
|
||||
};
|
||||
|
||||
itemLayoutButtonLayout.Recalculate();
|
||||
|
||||
float scroll = 0f;
|
||||
float localScroll = 0f;
|
||||
int lastSkippedItems = 0;
|
||||
int skippedItems = 0;
|
||||
var itemLayoutDraw = new GUICustomComponent(new RectTransform(new Vector2(1f, 0.9f), itemLayoutScissor.Content.RectTransform, Anchor.CenterLeft), onDraw: (spriteBatch, component) =>
|
||||
{
|
||||
component.ToolTip = string.Empty;
|
||||
|
||||
const float padding = 8f;
|
||||
float offset = 0f;
|
||||
float size = component.Rect.Height;
|
||||
int start = (int)Math.Floor(scroll);
|
||||
int amountToDraw = (int)Math.Ceiling(component.Rect.Width / size) + 1; // +1 just to be on the safe side
|
||||
bool shouldIncrementOnSkip = true;
|
||||
float toDrawWidth = prefabsAndProbabilities.Length * (size + padding);
|
||||
|
||||
// if the width is less than the component width we need to limit how many items we draw or it looks weird
|
||||
if (toDrawWidth < component.Rect.Width)
|
||||
{
|
||||
shouldIncrementOnSkip = false;
|
||||
amountToDraw = prefabsAndProbabilities.Length;
|
||||
}
|
||||
|
||||
for (int i = start; i < start + amountToDraw; i++)
|
||||
{
|
||||
var (ip, probability, _) = prefabsAndProbabilities[i % prefabsAndProbabilities.Length];
|
||||
var sprite = ip.InventoryIcon ?? ip.Sprite;
|
||||
|
||||
if (sprite is null)
|
||||
{
|
||||
// I don't think this should happen but just in case
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
amountToDraw++;
|
||||
skippedItems++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldHideItemPrefab(ip, probability))
|
||||
{
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
skippedItems++;
|
||||
amountToDraw++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float partialScroll = localScroll * (size + padding);
|
||||
var drawRect = new RectangleF(itemLayoutScissor.Rect.X + offset - partialScroll, component.Rect.Y, size, size);
|
||||
|
||||
var isMouseOver = drawRect.Contains(PlayerInput.MousePosition);
|
||||
if (isMouseOver)
|
||||
{
|
||||
component.ToolTip = ip.CreateTooltipText();
|
||||
}
|
||||
|
||||
var slotSprite = Inventory.SlotSpriteSmall;
|
||||
slotSprite?.Draw(spriteBatch, drawRect.Location, Color.White, origin: Vector2.Zero, rotate: 0f, scale: size / slotSprite.size.X * Inventory.SlotSpriteSmallScale);
|
||||
|
||||
float iconScale = Math.Min(drawRect.Width / sprite.size.X, drawRect.Height / sprite.size.Y) * 0.9f;
|
||||
|
||||
Color drawColor = ip.InventoryIconColor;
|
||||
|
||||
sprite.Draw(spriteBatch, drawRect.Center, drawColor, origin: sprite.Origin, scale: iconScale);
|
||||
offset += size + padding;
|
||||
}
|
||||
|
||||
// we need to compensate for the skipped items so that the scroll doesn't jump around
|
||||
if (skippedItems < lastSkippedItems)
|
||||
{
|
||||
scroll += lastSkippedItems - skippedItems;
|
||||
}
|
||||
|
||||
lastSkippedItems = skippedItems;
|
||||
skippedItems = 0;
|
||||
}, onUpdate: (deltaTime, component) =>
|
||||
{
|
||||
if (GUI.MouseOn != component && MathUtils.NearlyEqual(localScroll, 0, deltaTime * 2))
|
||||
{
|
||||
localScroll = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float totalWidth = prefabsAndProbabilities.Length * (component.Rect.Height + 8f);
|
||||
if (totalWidth < component.Rect.Width) { return; }
|
||||
scroll += deltaTime;
|
||||
localScroll = scroll % 1f;
|
||||
})
|
||||
{
|
||||
HoverCursor = CursorState.Default,
|
||||
AlwaysOverrideCursor = true
|
||||
};
|
||||
|
||||
var tooltip = TextManager.Get(tag.WarnIfLess ? "ContainerTagUI.RecommendedAmount" : "ContainerTagUI.SuggestedAmount");
|
||||
|
||||
var countBlock = new GUITextBlock(new RectTransform(new Vector2(CountSize, 1f), tagLayout.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
ToolTip = tooltip
|
||||
};
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
|
||||
enabledCheckBox.OnSelected += tickBox =>
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
AddTag(tag.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveTag(tag.Identifier);
|
||||
}
|
||||
|
||||
if (tagTextBox is not null)
|
||||
{
|
||||
tagTextBox.Text = string.Join(',', tags.Where(t => !Prefab.Tags.Contains(t)));
|
||||
}
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
return true;
|
||||
};
|
||||
|
||||
itemLayoutButton.OnClicked = (button, _) =>
|
||||
{
|
||||
CreateContainerTagItemListPopup(tag, button.Rect.Center, layout, prefabsAndProbabilities);
|
||||
return true;
|
||||
};
|
||||
|
||||
void UpdateCountBlock(GUITextBlock textBlock, ContainerTagPrefab containerTag)
|
||||
{
|
||||
if (textBlock is null) { return; }
|
||||
|
||||
var tagCount = Submarine.GetItems(alsoFromConnectedSubs: true).Count(i => i.HasTag(containerTag.Identifier));
|
||||
textBlock.Text = $"{tagCount} ({containerTag.RecommendedAmount})";
|
||||
|
||||
if (!isCorrectSubType || !containerTag.WarnIfLess || containerTag.RecommendedAmount <= 0) { return; }
|
||||
|
||||
if (tagCount < containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
textBlock.Text += "*";
|
||||
textBlock.ToolTip = RichString.Rich($"{tooltip}\n\n‖color:gui.red‖{TextManager.Get("ContainerTagUI.RecommendedAmountWarning")}‖color:end‖");
|
||||
}
|
||||
else if (tagCount >= containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Green;
|
||||
textBlock.ToolTip = tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrowImage.SpriteEffects = hasHiddenCategories ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
categoryButton.OnClicked = (_, _) =>
|
||||
{
|
||||
arrowImage.SpriteEffects ^= SpriteEffects.FlipVertically;
|
||||
|
||||
foreach (var child in list.Content.Children)
|
||||
{
|
||||
if (child.UserData is Identifier id && id == category)
|
||||
{
|
||||
child.Visible = !child.Visible;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateContainerTagItemListPopup(ContainerTagPrefab tag, Point location, GUIComponent popupParent, ImmutableArray<ContainerTagPrefab.ItemAndProbability> prefabAndProbabilities)
|
||||
{
|
||||
const string TooltipUserData = "tooltip";
|
||||
const string ProbabilityUserData = "probability";
|
||||
|
||||
if (popupParent.GetChildByUserData(TooltipUserData) is { } existingTooltip)
|
||||
{
|
||||
popupParent.RemoveChild(existingTooltip);
|
||||
}
|
||||
|
||||
var tooltip = new GUIFrame(new RectTransform(new Point(popupParent.Rect.Height), popupParent.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = location - popupParent.Rect.Location
|
||||
})
|
||||
{
|
||||
UserData = TooltipUserData,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
if (tooltip.Rect.Bottom > GameMain.GraphicsHeight)
|
||||
{
|
||||
int diffY = tooltip.Rect.Bottom - GameMain.GraphicsHeight;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(0, diffY);
|
||||
}
|
||||
|
||||
if (tooltip.Rect.Right > GameMain.GraphicsWidth)
|
||||
{
|
||||
int diffX = tooltip.Rect.Right - GameMain.GraphicsWidth;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(diffX, 0);
|
||||
}
|
||||
|
||||
var tooltipLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(tooltip.RectTransform, 0.9f), tooltip.RectTransform, Anchor.Center));
|
||||
|
||||
var tooltipHeader = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), tag.Name, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
|
||||
var tooltipList = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), tooltipLayout.RectTransform));
|
||||
|
||||
var tooltipHeaderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(0.66f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.item"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(0.33f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.probability"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
foreach (var itemAndProbability in prefabAndProbabilities.OrderByDescending(p => p.Probability))
|
||||
{
|
||||
var (ip, probability, campaignOnlyProbability) = itemAndProbability;
|
||||
if (ShouldHideItemPrefab(ip, probability)) { continue; }
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = itemAndProbability
|
||||
};
|
||||
|
||||
var itemNameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.66f, 1f), itemLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var itemIcon = new GUIImage(new RectTransform(Vector2.One, itemNameLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), ip.InventoryIcon ?? ip.Sprite, scaleToFit: true)
|
||||
{
|
||||
Color = ip.InventoryIconColor
|
||||
};
|
||||
|
||||
var itemName = new GUITextBlock(new RectTransform(Vector2.One, itemNameLayout.RectTransform), ip.Name);
|
||||
itemName.Text = ToolBox.LimitString(ip.Name, itemName.Font, itemName.Rect.Width);
|
||||
|
||||
var toolTipContainer = new GUIFrame(new RectTransform(Vector2.One, itemNameLayout.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = ip.CreateTooltipText()
|
||||
};
|
||||
|
||||
var probabilityText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1f), itemLayout.RectTransform), ProbabilityToPercentage(campaignOnlyProbability), textAlignment: Alignment.Right)
|
||||
{
|
||||
UserData = ProbabilityUserData
|
||||
};
|
||||
if (MathUtils.NearlyEqual(campaignOnlyProbability, 0f)) { probabilityText.TextColor = GUIStyle.Red; }
|
||||
}
|
||||
|
||||
var campaignCheckbox = new GUITickBox(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), label: TextManager.Get("containertagui.campaignonly"))
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.campaignonlytooltip"),
|
||||
Selected = true,
|
||||
OnSelected = box =>
|
||||
{
|
||||
foreach (var child in tooltipList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ContainerTagPrefab.ItemAndProbability data) { continue; }
|
||||
|
||||
if (child.GetChildByUserData(ProbabilityUserData) is not GUITextBlock text) { continue; }
|
||||
|
||||
float probability = box.Selected
|
||||
? data.CampaignProbability
|
||||
: data.Probability;
|
||||
text.Text = ProbabilityToPercentage(probability);
|
||||
|
||||
text.TextColor = MathUtils.NearlyEqual(probability, 0f)
|
||||
? GUIStyle.Red
|
||||
: GUIStyle.TextColorNormal;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var availableTag in availableTags.ToList().OrderBy(t => t))
|
||||
var tooltipClose = new GUIButton(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), TextManager.Get("Close"))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
|
||||
ToolBox.LimitString(availableTag.Value, GUIStyle.Font, textList.Content.Rect.Width))
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
UserData = availableTag
|
||||
};
|
||||
}
|
||||
popupParent.RemoveChild(tooltip);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static LocalizedString ProbabilityToPercentage(float probability)
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", MathF.Round((probability * 100f), 1).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
}
|
||||
|
||||
private static bool ShouldHideItemPrefab(ItemPrefab ip, float probability)
|
||||
=> ip.HideInMenus && MathUtils.NearlyEqual(probability, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Reposition currently active item interfaces to make sure they don't overlap with each other
|
||||
/// </summary>
|
||||
@@ -1213,10 +1602,21 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
activeHUDs.Clear();
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic)
|
||||
{
|
||||
if (!ic.ShouldDrawHUD(character)) { return false; }
|
||||
if (character.HasEquippedItem(this))
|
||||
{
|
||||
return ic.DrawHudWhenEquipped;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false);
|
||||
}
|
||||
}
|
||||
//the HUD of the component with the highest priority will be drawn
|
||||
//if all components have a priority of 0, all of them are drawn
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic) => ic.ShouldDrawHUD(character) && (ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false) || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped));
|
||||
foreach (ItemComponent ic in activeComponents)
|
||||
{
|
||||
if (ic.HudPriority > 0 && DrawHud(ic) && (maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
|
||||
@@ -1317,7 +1717,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<ColoredText> texts = new List<ColoredText>();
|
||||
readonly List<ColoredText> texts = new();
|
||||
public List<ColoredText> GetHUDTexts(Character character, bool recreateHudTexts = true)
|
||||
{
|
||||
// Always create the texts if they have not yet been created
|
||||
@@ -1348,42 +1748,91 @@ namespace Barotrauma
|
||||
nameText += $" x{DroppedStack.Count()}";
|
||||
}
|
||||
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, false, false));
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, isCommand: false, isError: false));
|
||||
|
||||
if (CampaignMode.BlocksInteraction(CampaignInteractionType))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
foreach (ItemComponent itemComponent in components)
|
||||
{
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
if (ic is Repairable r)
|
||||
{
|
||||
if (r.IsBelowRepairThreshold) { color = Color.Cyan; }
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
||||
var interactionVisibility = GetComponentInteractionVisibility(character, itemComponent);
|
||||
if (interactionVisibility == InteractionVisibility.None) { continue; }
|
||||
if (itemComponent.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
|
||||
Color color = interactionVisibility == InteractionVisibility.MissingRequirement ? Color.Gray : Color.Cyan;
|
||||
texts.Add(new ColoredText(itemComponent.DisplayMsg.Value, color, isCommand: false, isError: false));
|
||||
}
|
||||
}
|
||||
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.Get("itemmsg.morreoptionsavailable").Value, Color.LightGray * 0.7f, isCommand: false, isError: false));
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
private enum InteractionVisibility
|
||||
{
|
||||
None,
|
||||
MissingRequirement,
|
||||
Visible
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine, for UI display purposes, the type of interaction visibility for an item component.
|
||||
///
|
||||
/// Example:
|
||||
/// Visible -> Display cyan "click to interact" type text on item hover.
|
||||
/// MissingRequirement -> Display gray "need tool" type text on item hover.
|
||||
/// None -> Hide from item hover texts.
|
||||
/// </summary>
|
||||
/// <param name="character">Character, for tool requirement purposes.</param>
|
||||
/// <param name="itemComponent">The item component to inspect.</param>
|
||||
/// <returns>The interaction visibility state for this component.</returns>
|
||||
private static InteractionVisibility GetComponentInteractionVisibility(Character character, ItemComponent itemComponent)
|
||||
{
|
||||
if (!itemComponent.CanBePicked && !itemComponent.CanBeSelected) { return InteractionVisibility.None; }
|
||||
if (itemComponent is Holdable holdable && !holdable.CanBeDeattached()) { return InteractionVisibility.None; }
|
||||
if (itemComponent is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { return InteractionVisibility.None; }
|
||||
|
||||
InteractionVisibility interactionVisibility = InteractionVisibility.MissingRequirement;
|
||||
if (itemComponent.HasRequiredItems(character, addMessage: false))
|
||||
{
|
||||
if (itemComponent is Repairable repairable)
|
||||
{
|
||||
if (repairable.IsBelowRepairThreshold)
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
return interactionVisibility;
|
||||
}
|
||||
|
||||
public bool HasVisibleInteraction(Character character)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (GetComponentInteractionVisibility(character, component) == InteractionVisibility.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForceHUDLayoutUpdate(bool ignoreLocking = false)
|
||||
{
|
||||
foreach (ItemComponent ic in activeHUDs)
|
||||
|
||||
Reference in New Issue
Block a user