v0.13.0.11

This commit is contained in:
Joonas Rikkonen
2021-04-22 17:33:08 +03:00
parent 0697d7fc64
commit 8bb31f2893
391 changed files with 17271 additions and 5949 deletions
@@ -321,6 +321,7 @@ namespace Barotrauma
float prevSize = chatBox.BarSize;
string displayedText = message.TranslatedText;
string senderName = "";
Color senderColor = Color.White;
if (!string.IsNullOrWhiteSpace(message.SenderName))
@@ -377,13 +378,29 @@ namespace Barotrauma
}
var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, parseRichText: true)
{
UserData = message.SenderName,
CanBeFocused = true
CanBeFocused = false
};
msgText.CalculateHeightFromText();
if (msgText.RichTextData != null)
{
foreach (var data in msgText.RichTextData)
{
var clickableArea = new GUITextBlock.ClickableArea()
{
Data = data
};
if (GameMain.NetLobbyScreen != null && GameMain.NetworkMember != null)
{
clickableArea.OnClick = GameMain.NetLobbyScreen.SelectPlayer;
}
msgText.ClickableAreas.Add(clickableArea);
}
}
if (message is OrderChatMessage orderChatMsg &&
Character.Controlled != null &&
@@ -444,7 +461,7 @@ namespace Barotrauma
senderText.RectTransform.MinSize = new Point(0, senderText.Rect.Height);
}
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true)
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true, parseRichText: true)
{
CanBeFocused = false
};
@@ -523,6 +540,7 @@ namespace Barotrauma
if (ToggleButton != null)
{
ToggleButton.Selected = ToggleOpen;
ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height);
}
@@ -566,6 +584,7 @@ namespace Barotrauma
if (ToggleOpen)
{
GUIFrame.CanBeFocused = true;
openState += deltaTime * 5.0f;
//delete all popup messages when the chatbox is open
foreach (var popupMsg in popupMessages)
@@ -576,6 +595,7 @@ namespace Barotrauma
}
else
{
GUIFrame.CanBeFocused = false;
openState -= deltaTime * 5.0f;
int yOffset = 0;
@@ -1,10 +1,10 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -91,9 +91,10 @@ namespace Barotrauma
UserData = "container"
};
int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
})
{
Stretch = true,
@@ -149,7 +150,7 @@ namespace Barotrauma
var pendingAndCrewMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor: Anchor.TopRight)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
})
{
Stretch = true,
@@ -177,7 +178,7 @@ namespace Barotrauma
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
}).RectTransform));
float height = 0.05f;
@@ -188,7 +189,7 @@ namespace Barotrauma
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont);
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, (8)* height), pendingAndCrewGroup.RectTransform))
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
};
@@ -207,7 +208,7 @@ namespace Barotrauma
{
ClickSound = GUISoundType.HireRepairClick,
ForceUpperCase = true,
OnClicked = (b, o) => ValidatePendingHires(true)
OnClicked = (b, o) => ValidateHires(PendingHires, true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
@@ -335,9 +336,9 @@ namespace Barotrauma
jobColor = characterInfo.Job.Prefab.UIColor;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, 55), parent: listBox.Content.RectTransform), "ListBoxElement")
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
{
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f)
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill?.Level ?? 0.0f)
};
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
@@ -345,19 +346,22 @@ namespace Barotrauma
};
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
var icon = new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
{
CanBeFocused = false
};
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform));
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft)
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
textColor: jobColor, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false
};
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
{
@@ -366,7 +370,7 @@ namespace Barotrauma
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
float width = 0.6f / 3;
if (characterInfo.Job != null)
if (characterInfo.Job != null && skill != null)
{
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
@@ -383,11 +387,18 @@ namespace Barotrauma
if (listBox != crewList)
{
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center)
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
FormatCurrency(characterInfo.Salary),
textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
else
{
// Just a bit of padding to make list layouts similar
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
}
if (listBox == hireableList)
{
@@ -446,6 +457,23 @@ namespace Barotrauma
}
};
}
if (listBox == pendingList || listBox == crewList)
{
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
{
Enabled = HasPermission,
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", TextManager.Get($"input.{(PlayerInput.MouseButtonsSwapped() ? "rightmouse" : "leftmouse")}")),
UserData = characterInfo,
OnClicked = CreateRenamingComponent
};
}
}
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
@@ -478,7 +506,10 @@ namespace Barotrauma
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
float blockHeight = 1.0f / 4;
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), characterInfo.Name);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), "");
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
if (characterInfo.HasGenders)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
@@ -553,9 +584,18 @@ namespace Barotrauma
if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
pendingList.UpdateScrollBarSize();
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
hireableList.UpdateScrollBarSize();
// Server will reset the names to originals in multiplayer
if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) &&
hireableList.Content.Children.None(c => c.UserData is Tuple<CharacterInfo, float> userData && userData.Item1.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
{
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
hireableList.UpdateScrollBarSize();
}
if (setTotalHireCost) { SetTotalHireCost(); }
if (createNetworkMessage) { SendCrewState(true); }
return true;
@@ -572,36 +612,41 @@ namespace Barotrauma
{
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
int total = 0;
pendingList.Content.Children.ForEach(c => total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary);
pendingList.Content.Children.ForEach(c =>
{
total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary;
});
totalBlock.Text = FormatCurrency(total);
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
}
public bool ValidatePendingHires(bool createNetworkEvent = false)
public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
{
List<CharacterInfo> hires = new List<CharacterInfo>();
int total = 0;
foreach (GUIComponent c in pendingList.Content.Children.ToList())
{
if (c.UserData is Tuple<CharacterInfo, float> info)
{
hires.Add(info.Item1);
total += info.Item1.Salary;
}
}
if (hires == null || hires.None()) { return false; }
if (hires.None() || total > campaign.Money) { return false; }
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
hires.ForEach(hireInfo =>
{
if(campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
{
nonDuplicateHires.Add(hireInfo);
}
});
if (nonDuplicateHires.None()) { return false; }
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
if (total > campaign.Money) { return false; }
bool atLeastOneHired = false;
foreach (CharacterInfo ci in hires)
foreach (CharacterInfo ci in nonDuplicateHires)
{
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
{
atLeastOneHired = true;
PendingHires.Remove(ci);
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == ci));
}
else
{
@@ -628,6 +673,93 @@ namespace Barotrauma
return false;
}
private bool CreateRenamingComponent(GUIButton button, object userData)
{
if (!HasPermission || !(userData is CharacterInfo characterInfo)) { return false; }
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black * 0.7f);
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
{
MaxSize = new Point(400, 300).Multiply(GUI.Scale)
});
var layoutGroup = new GUILayoutGroup(new RectTransform((frame.Rect.Size - GUIStyle.ItemFrameMargin).Multiply(new Vector2(0.75f, 1.0f)), frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.02f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
var groupElementSize = new Vector2(1.0f, 0.25f);
var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform))
{
MaxTextLength = Client.MaxNameLength
};
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm"))
{
OnClicked = (button, userData) =>
{
if (RenameCharacter(characterInfo, nameBox.Text?.Trim()))
{
parentComponent.RemoveChild(outerGlowFrame);
return true;
}
else
{
nameBox.Flash(color: Color.Red);
return false;
}
}
};
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("cancel"))
{
OnClicked = (button, userData) =>
{
parentComponent.RemoveChild(outerGlowFrame);
return true;
}
};
layoutGroup.Recalculate();
return true;
}
public bool RenameCharacter(CharacterInfo characterInfo, string newName)
{
if (characterInfo == null || string.IsNullOrEmpty(newName)) { return false; }
if (newName == characterInfo.Name) { return false; }
if (GameMain.IsMultiplayer)
{
SendCrewState(false, renameCharacter: (characterInfo, newName));
}
else
{
var crewComponent = crewList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
if (crewComponent != null)
{
crewList.Content.RemoveChild(crewComponent);
campaign.CrewManager.RenameCharacter(characterInfo, newName);
CreateCharacterFrame(characterInfo, crewList);
SortCharacters(crewList, SortingMethod.JobAsc);
}
else
{
var pendingComponent = pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
if (pendingComponent != null)
{
pendingList.Content.RemoveChild(pendingComponent);
campaign.Map.CurrentLocation.HireManager.RenameCharacter(characterInfo, newName);
CreateCharacterFrame(characterInfo, pendingList);
SortCharacters(pendingList, SortingMethod.JobAsc);
SetTotalHireCost();
}
else
{
return false;
}
}
}
return true;
}
private bool FireCharacter(GUIButton button, object selection)
{
if (!(selection is CharacterInfo characterInfo)) { return false; }
@@ -648,9 +780,10 @@ namespace Barotrauma
UpdateLocationView(campaign.Map.CurrentLocation, false);
}
if ((GUI.MouseOn?.UserData as Tuple<CharacterInfo, float>)?.Item1 is CharacterInfo characterInfo)
(GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
if (highlightedFrame != null && highlightedInfo != null)
{
if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData)
if (characterPreviewFrame == null || highlightedInfo != characterPreviewFrame.UserData)
{
GUIComponent component = GUI.MouseOn;
GUIListBox listBox = null;
@@ -673,7 +806,7 @@ namespace Barotrauma
if (listBox != null)
{
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo);
SelectCharacter(listBox, highlightedFrame as GUIFrame, highlightedInfo);
}
}
else
@@ -687,6 +820,27 @@ namespace Barotrauma
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
static (GUIComponent, CharacterInfo) FindHighlightedCharacter(GUIComponent c)
{
if (c == null)
{
return default;
}
if (c.UserData is Tuple<CharacterInfo, float> highlightedData)
{
return (c, highlightedData.Item1);
}
if (c.Parent != null)
{
if (c.Parent is GUIListBox)
{
return default;
}
return FindHighlightedCharacter(c.Parent);
}
return default;
}
}
public void SetPendingHires(List<int> characterInfos, Location location)
@@ -699,7 +853,7 @@ namespace Barotrauma
PendingHires.Clear();
foreach (int identifier in characterInfos)
{
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier);
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier);
if (match != null)
{
PendingHires.Add(match);
@@ -716,9 +870,10 @@ namespace Barotrauma
/// Notify the server of crew changes
/// </summary>
/// <param name="updatePending">When set to true will tell the server to update the pending hires</param>
/// <param name="renameCharacter">When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew.</param>
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false)
public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
{
if (campaign is MultiPlayerCampaign)
{
@@ -731,12 +886,23 @@ namespace Barotrauma
msg.Write((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires)
{
msg.Write(pendingHire.GetIdentifier());
msg.Write(pendingHire.GetIdentifierUsingOriginalName());
}
}
msg.Write(validateHires);
bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName);
msg.Write(validRenaming);
if (validRenaming)
{
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
msg.Write(identifier);
msg.Write(renameCharacter.newName);
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
msg.Write(existingCrewMember);
}
msg.Write(firedCharacter != null);
if (firedCharacter != null)
{
@@ -218,6 +218,21 @@ namespace Barotrauma
public static bool DisableHUD, DisableUpperHUD, DisableItemHighlights, DisableCharacterNames;
private static bool isSavingIndicatorEnabled;
private static Color savingIndicatorColor = Color.Transparent;
private static bool IsSavingIndicatorVisible => savingIndicatorColor.A > 0;
private static float savingIndicatorSpriteIndex;
private static float savingIndicatorColorLerpAmount;
private static SavingIndicatorState savingIndicatorState = SavingIndicatorState.None;
private static float? timeUntilSavingIndicatorDisabled;
private enum SavingIndicatorState
{
None,
FadingIn,
FadingOut
}
public static void Init(GameWindow window, IEnumerable<ContentPackage> selectedContentPackages, GraphicsDevice graphicsDevice)
{
GraphicsDevice = graphicsDevice;
@@ -345,7 +360,11 @@ namespace Barotrauma
}
#endif
if (DisableHUD) { return; }
if (DisableHUD)
{
DrawSavingIndicator(spriteBatch);
return;
}
if (GameMain.ShowFPS || GameMain.DebugDraw)
{
@@ -539,60 +558,42 @@ namespace Barotrauma
}
}
IEnumerable<string> strings;
if (MouseOn != null)
{
RectTransform mouseOnRect = MouseOn.RectTransform;
bool isAbsoluteOffsetInUse = mouseOnRect.AbsoluteOffset != Point.Zero || mouseOnRect.RelativeOffset == Vector2.Zero;
string selectedString = $"Selected UI Element: {MouseOn.GetType().Name} ({ MouseOn.Style?.Element.Name.LocalName ?? "no style" }, {MouseOn.Rect}";
string offsetString = $"Relative Offset: {mouseOnRect.RelativeOffset} | Absolute Offset: {(isAbsoluteOffsetInUse ? mouseOnRect.AbsoluteOffset : mouseOnRect.ParentRect.MultiplySize(mouseOnRect.RelativeOffset))}{(isAbsoluteOffsetInUse ? "" : " (Calculated from RelativeOffset)")}";
string anchorPivotString = $"Anchor: {mouseOnRect.Anchor} | Pivot: {mouseOnRect.Pivot}";
Vector2 selectedStringSize = SmallFont.MeasureString(selectedString);
Vector2 offsetStringSize = SmallFont.MeasureString(offsetString);
Vector2 anchorPivotStringSize = SmallFont.MeasureString(anchorPivotString);
int padding = IntScale(10);
int yPos = padding;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)selectedStringSize.X - padding, yPos), selectedString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)selectedStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)offsetStringSize.X - padding, yPos), offsetString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)offsetStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)anchorPivotStringSize.X - padding, yPos), anchorPivotString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)anchorPivotStringSize.Y + padding / 2;
strings = new string[]
{
$"Selected UI Element: {MouseOn.GetType().Name} ({ MouseOn.Style?.Element.Name.LocalName ?? "no style" }, {MouseOn.Rect}",
$"Relative Offset: {mouseOnRect.RelativeOffset} | Absolute Offset: {(isAbsoluteOffsetInUse ? mouseOnRect.AbsoluteOffset : mouseOnRect.ParentRect.MultiplySize(mouseOnRect.RelativeOffset))}{(isAbsoluteOffsetInUse ? "" : " (Calculated from RelativeOffset)")}",
$"Anchor: {mouseOnRect.Anchor} | Pivot: {mouseOnRect.Pivot}"
};
}
else
{
string guiScaleString = $"GUI.Scale: {Scale}";
string guixScaleString = $"GUI.xScale: {xScale}";
string guiyScaleString = $"GUI.yScale: {yScale}";
string relativeHorizontalAspectRatioString = $"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}";
string relativeVerticalAspectRatioString = $"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}";
Vector2 guiScaleStringSize = SmallFont.MeasureString(guiScaleString);
Vector2 guixScaleStringSize = SmallFont.MeasureString(guixScaleString);
Vector2 guiyScaleStringSize = SmallFont.MeasureString(guiyScaleString);
Vector2 relativeHorizontalAspectRatioStringSize = SmallFont.MeasureString(relativeHorizontalAspectRatioString);
Vector2 relativeVerticalAspectRatioStringSize = SmallFont.MeasureString(relativeVerticalAspectRatioString);
strings = new string[]
{
$"GUI.Scale: {Scale}",
$"GUI.xScale: {xScale}",
$"GUI.yScale: {yScale}",
$"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}",
$"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}",
};
}
int padding = IntScale(10);
int yPos = padding;
strings = strings.Concat(new string[] { $"Cam.Zoom: {Screen.Selected.Cam?.Zoom ?? 0f}" });
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiScaleStringSize.X - padding, yPos), guiScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guiScaleStringSize.Y + padding / 2;
int padding = IntScale(10);
int yPos = padding;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guixScaleStringSize.X - padding, yPos), guixScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guixScaleStringSize.Y + padding / 2;
foreach (string str in strings)
{
Vector2 stringSize = SmallFont.MeasureString(str);
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiyScaleStringSize.X - padding, yPos), guiyScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guiyScaleStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeHorizontalAspectRatioStringSize.X - padding, yPos), relativeHorizontalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)relativeHorizontalAspectRatioStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeVerticalAspectRatioStringSize.X - padding, yPos), relativeVerticalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)relativeVerticalAspectRatioStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)stringSize.X - padding, yPos), str, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)stringSize.Y + padding / 2;
}
}
@@ -645,6 +646,8 @@ namespace Barotrauma
}
}
DrawSavingIndicator(spriteBatch);
if (GameMain.WindowActive && !HideCursor)
{
spriteBatch.End();
@@ -855,6 +858,7 @@ namespace Barotrauma
lock (mutex)
{
GUIMessageBox.AddActiveToGUIUpdateList();
GUIContextMenu.AddActiveToGUIUpdateList();
if (pauseMenuOpen)
{
@@ -921,6 +925,7 @@ namespace Barotrauma
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn)
{
MouseOn = c;
var sakdjfnsjkd = c.MouseRect;
}
break;
}
@@ -1043,7 +1048,7 @@ namespace Barotrauma
}
}
if (parent != null)
if (parent != null && parent.CanBeFocused)
{
if (!parent.Rect.Equals(monitorRect)) { return parent.HoverCursor; }
}
@@ -1222,6 +1227,7 @@ namespace Barotrauma
Debug.Assert(updateList.Count == updateListSet.Count);
updateList.ForEach(c => c.UpdateAuto(deltaTime));
UpdateMessages(deltaTime);
UpdateSavingIndicator(deltaTime);
}
}
@@ -1266,6 +1272,58 @@ namespace Barotrauma
}
private static void UpdateSavingIndicator(float deltaTime)
{
lock (mutex)
{
if (timeUntilSavingIndicatorDisabled.HasValue)
{
timeUntilSavingIndicatorDisabled -= deltaTime;
if (timeUntilSavingIndicatorDisabled <= 0.0f)
{
isSavingIndicatorEnabled = false;
timeUntilSavingIndicatorDisabled = null;
}
}
if (isSavingIndicatorEnabled)
{
if (savingIndicatorColor == Color.Transparent)
{
savingIndicatorState = SavingIndicatorState.FadingIn;
savingIndicatorColorLerpAmount = 0.0f;
}
else if (savingIndicatorColor == Color.White)
{
savingIndicatorState = SavingIndicatorState.None;
}
}
else
{
if (savingIndicatorColor == Color.White)
{
savingIndicatorState = SavingIndicatorState.FadingOut;
savingIndicatorColorLerpAmount = 0.0f;
}
else if (savingIndicatorColor == Color.Transparent)
{
savingIndicatorState = SavingIndicatorState.None;
}
}
if (savingIndicatorState != SavingIndicatorState.None)
{
bool isFadingIn = savingIndicatorState == SavingIndicatorState.FadingIn;
Color lerpStartColor = isFadingIn ? Color.Transparent : Color.White;
Color lerpTargetColor = isFadingIn ? Color.White : Color.Transparent;
savingIndicatorColorLerpAmount += (isFadingIn ? 2.0f : 0.5f) * deltaTime;
savingIndicatorColor = Color.Lerp(lerpStartColor, lerpTargetColor, savingIndicatorColorLerpAmount);
}
if (IsSavingIndicatorVisible)
{
savingIndicatorSpriteIndex = (savingIndicatorSpriteIndex + 15.0f * deltaTime) % (Style.SavingIndicator.FrameCount + 1);
}
}
}
#region Element drawing
private static List<float> usedIndicatorAngles = new List<float>();
@@ -1278,7 +1336,7 @@ namespace Barotrauma
Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length();
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
if (overrideAlpha.HasValue || dist > hideDist)
{
@@ -1336,9 +1394,9 @@ namespace Barotrauma
}
}
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
{
DrawLine(sb, t, start, end, clr, depth, width);
DrawLine(sb, t, start, end, clr, depth, (int)width);
}
public static void DrawLine(SpriteBatch sb, Sprite sprite, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
@@ -1405,7 +1463,7 @@ namespace Barotrauma
font.DrawStringWithColors(sb, text, pos, color, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, depth, richTextData);
}
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
{
if (size.X < 0)
{
@@ -1420,7 +1478,7 @@ namespace Barotrauma
DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness);
}
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
{
if (isFilled)
{
@@ -1428,15 +1486,15 @@ namespace Barotrauma
}
else
{
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + rect.Width - thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
sb.Draw(t, new Vector2(rect.X, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height), SpriteEffects.None, depth);
sb.Draw(t, new Vector2(rect.X + thickness, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
sb.Draw(t, new Vector2(rect.X + thickness, rect.Bottom - thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
sb.Draw(t, new Vector2(rect.Right - thickness, rect.Y + thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height - thickness * 2f), SpriteEffects.None, depth);
}
}
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, int thickness = 1)
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1)
{
Matrix rotate = Matrix.CreateRotationZ(rotation);
@@ -1453,7 +1511,7 @@ namespace Barotrauma
DrawLine(sb, bottomLeft, topLeft, clr, depth, thickness);
}
public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, int thickness = 1)
public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, float thickness = 1)
{
if (corners.Length != 4)
{
@@ -1590,6 +1648,14 @@ namespace Barotrauma
ShapeExtensions.DrawPoint(spriteBatch, pos, color, dotSize);
}
}
private static void DrawSavingIndicator(SpriteBatch spriteBatch)
{
if (!IsSavingIndicatorVisible) { return; }
var sheet = Style.SavingIndicator;
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));
}
#endregion
#region Element creation
@@ -2246,7 +2312,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) =>
{
if (!GameMain.Client.HasPermission(ClientPermissions.ManageRound)) { return false; }
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition))
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
{
var msgBox = new GUIMessageBox("",
TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd"),
@@ -2343,6 +2409,21 @@ namespace Barotrauma
float aspectRatio = HorizontalAspectRatio;
return aspectRatio > 1.3f && aspectRatio < 1.4f;
}
public static void SetSavingIndicatorState(bool enabled)
{
if (enabled)
{
timeUntilSavingIndicatorDisabled = null;
}
isSavingIndicatorEnabled = enabled;
}
public static void DisableSavingIndicatorDelayed(float delay = 3.0f)
{
if (!isSavingIndicatorEnabled) { return; }
timeUntilSavingIndicatorDisabled = delay;
}
#endregion
}
}
@@ -77,6 +77,12 @@ namespace Barotrauma
return RectTransform.IsParentOf(component.RectTransform, recursive);
}
public bool IsChildOf(GUIComponent component, bool recursive = true)
{
if (component == null) { return false; }
return RectTransform.IsChildOf(component.RectTransform, recursive);
}
public virtual void RemoveChild(GUIComponent child)
{
if (child == null) { return; }
@@ -705,6 +711,29 @@ namespace Barotrauma
if (!Visible) return;
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
}
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Vector2 pos, List<RichTextData> richTextData = null)
{
if (Tutorials.Tutorial.ContentRunning) { return; }
int width = (int)(400 * GUI.Scale);
int height = (int)(18 * GUI.Scale);
Point padding = new Point((int)(10 * GUI.Scale));
if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip)
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
}
toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint();
toolTipBlock.SetTextPos();
toolTipBlock.DrawManually(spriteBatch);
}
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List<RichTextData> richTextData = null)
{
@@ -0,0 +1,292 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
struct ContextMenuOption
{
public string Label;
public Action OnSelected;
public ContextMenuOption[]? SubOptions;
public bool IsEnabled;
public string Tooltip;
// Creates a regular context menu
public ContextMenuOption(string label, bool isEnabled, Action onSelected)
{
Label = TextManager.Get(label, returnNull: true) ?? label;
OnSelected = onSelected;
IsEnabled = isEnabled;
SubOptions = null;
Tooltip = string.Empty;
}
// Creates a option with a sub context menu
public ContextMenuOption(string label, bool isEnabled, params ContextMenuOption[] options): this(label, isEnabled, () => { })
{
SubOptions = options;
}
}
internal class GUIContextMenu : GUIComponent
{
public static GUIContextMenu? CurrentContextMenu;
private readonly Dictionary<ContextMenuOption, GUITextBlock> Options = new Dictionary<ContextMenuOption, GUITextBlock>();
private GUIContextMenu? SubMenu;
public readonly GUITextBlock? HeaderLabel;
public GUITextBlock? ParentOption;
/// <summary>
/// Creates a context menu. This constructor does not make the context menu active.
/// Use <see cref="CreateContextMenu(Barotrauma.ContextMenuOption[])"/> to make right click context menus.
/// </summary>
/// <param name="position">Position at which to create the context menu</param>
/// <param name="header">Header text</param>
/// <param name="style">Background style</param>
/// <param name="options">list of context menu options</param>
public GUIContextMenu(Vector2? position, string header, string style, params ContextMenuOption[] options) : base(style, new RectTransform(Point.Zero, GUI.Canvas))
{
Vector2 pos = position ?? PlayerInput.MousePosition;
ScalableFont headerFont = GUI.SubHeadingFont;
ScalableFont font = GUI.SmallFont; // font the context menu options use
Vector4 padding = new Vector4(4), headerPadding = new Vector4(8);
int horizontalPadding = (int) (padding.X + padding.Z), verticalPadding = (int) (padding.Y + padding.W);
bool hasHeader = !string.IsNullOrWhiteSpace(header);
//----------------------------------------------------------------------------------
// Estimate the size of the context menu
//----------------------------------------------------------------------------------
Dictionary<ContextMenuOption, Vector2> optionsAndSizes = new Dictionary<ContextMenuOption, Vector2>();
// estimate how big the context menu needs to be
Point estimatedSize = new Point(horizontalPadding, verticalPadding);
if (hasHeader)
{
InflateSize(ref estimatedSize, header, headerFont);
}
foreach (ContextMenuOption option in options)
{
Vector2 optionSize = InflateSize(ref estimatedSize, option.Label, font);
optionsAndSizes.Add(option, optionSize);
}
// it's better to overestimate the size since it's going to be cropped anyways
estimatedSize = estimatedSize.Multiply(1.2f);
RectTransform.NonScaledSize = estimatedSize;
RectTransform.AbsoluteOffset = pos.ToPoint();
//----------------------------------------------------------------------------------
// Construct the GUI elements
//----------------------------------------------------------------------------------
GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center));
if (hasHeader)
{
HeaderLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), background.RectTransform), header, font: headerFont) { Padding = headerPadding };
}
GUIListBox optionList = new GUIListBox(new RectTransform(new Vector2(1f, hasHeader ? 0.8f : 1f), background.RectTransform), style: null)
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding
};
foreach (var (option, size) in optionsAndSizes)
{
GUITextBlock optionElement = new GUITextBlock(new RectTransform(size.ToPoint(), optionList.Content.RectTransform), option.Label, font: font)
{
UserData = option,
Enabled = option.IsEnabled
};
Options.Add(option, optionElement);
if (!string.IsNullOrWhiteSpace(option.Tooltip) && optionElement.Enabled)
{
optionElement.ToolTip = option.Tooltip;
}
if (!option.IsEnabled)
{
optionElement.TextColor *= 0.5f;
}
}
//----------------------------------------------------------------------------------
// Positioning and cropping the context menu
//----------------------------------------------------------------------------------
List<GUIComponent> children = optionList.Content.Children.ToList();
// Resize all children to the size of their text
foreach (GUITextBlock block in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
{
block.RectTransform.NonScaledSize = new Point((int) (block.TextSize.X + (block.Padding.X + block.Padding.Z)), (int) (18 * GUI.Scale));
}
int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
// if the header is bigger than any of the options then overwrite
if (HeaderLabel != null)
{
RectTransform headerTransform = HeaderLabel.RectTransform;
headerTransform.MinSize = new Point((int) (HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
if (largestWidth < headerTransform.MinSize.X)
{
largestWidth = headerTransform.MinSize.X;
}
}
// resize all children to the size of the longest element
foreach (GUIComponent c in children)
{
c.RectTransform.MinSize = new Point(largestWidth, c.Rect.Height);
}
// the cropped size of the option list
Point newSize = new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding);
// resize the menu itself taking into account the option menus relative Y size
RectTransform.NonScaledSize = new Point(newSize.X, (int) (newSize.Y / optionList.RectTransform.RelativeSize.Y));
optionList.RectTransform.NonScaledSize = newSize;
// move the context menu if it would go outside of screen
if (RectTransform.Rect.Bottom > GameMain.GraphicsHeight)
{
Rectangle rect = RectTransform.Rect;
RectTransform.AbsoluteOffset = new Point(rect.X, rect.Y - rect.Height);
}
if (RectTransform.Rect.Right > GameMain.GraphicsWidth)
{
Rectangle rect = RectTransform.Rect;
RectTransform.AbsoluteOffset = new Point(rect.X - rect.Width, rect.Y);
}
background.Recalculate();
optionList.OnSelected = OnSelected;
}
public static GUIContextMenu CreateContextMenu(params ContextMenuOption[] options) => CreateContextMenu(PlayerInput.MousePosition, string.Empty, null, options);
public static GUIContextMenu CreateContextMenu(Vector2? pos, string header, Color? headerColor, params ContextMenuOption[] options)
{
GUIContextMenu menu = new GUIContextMenu(pos,header, "GUIToolTip", options);
if (headerColor != null)
{
menu.HeaderLabel?.OverrideTextColor(headerColor.Value);
}
CurrentContextMenu = menu;
return menu;
}
private bool OnSelected(GUIComponent _, object data)
{
if (data is ContextMenuOption option && option.IsEnabled)
{
CurrentContextMenu = null;
option.OnSelected();
return true;
}
return false;
}
/// <summary>
/// Inflates a point by the size of the text
/// </summary>
/// <param name="size">Pint to resize</param>
/// <param name="label">String whose size to inflate by</param>
/// <param name="font">What font to use</param>
/// <returns>The size of the text</returns>
private Vector2 InflateSize(ref Point size, string label, ScalableFont font)
{
Vector2 textSize = font.MeasureString(label);
size.X = Math.Max((int) Math.Ceiling(textSize.X), size.X);
size.Y += (int) Math.Ceiling(textSize.Y);
return textSize;
}
protected override void Update(float deltaTime)
{
base.Update(deltaTime);
// keep the parent highlighted
if (ParentOption != null)
{
ParentOption.State = ComponentState.Hover;
}
if (SubMenu != null && !SubMenu.IsMouseOver())
{
SubMenu = null;
return;
}
foreach (var (option, textBlock) in Options)
{
// Create a new sub context menu if hovering over an option with sub options
if (GUI.MouseOn != textBlock) { continue; }
if (option.IsEnabled && option.SubOptions is { } subOptions && subOptions.Any())
{
Vector2 subMenuPos = new Vector2(textBlock.MouseRect.Right + 4, textBlock.MouseRect.Y);
SubMenu = new GUIContextMenu(subMenuPos, "", "GUIToolTip", subOptions)
{
ParentOption = textBlock
};
}
}
}
/// <summary>
/// Checks if the mouse cursor is over this context menu or any of its sub menus
/// </summary>
/// <returns></returns>
private bool IsMouseOver()
{
Rectangle expandedRect = Rect;
expandedRect.Inflate(20, 20);
bool isMouseOn = expandedRect.Contains(PlayerInput.MousePosition);
if (ParentOption != null)
{
isMouseOn |= GUI.MouseOn == ParentOption;
}
// Recursively check sub context menus
if (!isMouseOn && SubMenu != null)
{
isMouseOn = SubMenu.IsMouseOver();
}
return isMouseOn;
}
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
base.AddToGUIUpdateList(ignoreChildren, order);
SubMenu?.AddToGUIUpdateList();
}
public static void AddActiveToGUIUpdateList()
{
if (CurrentContextMenu != null && !CurrentContextMenu.IsMouseOver())
{
CurrentContextMenu = null;
}
CurrentContextMenu?.AddToGUIUpdateList();
}
}
}
@@ -194,6 +194,8 @@ namespace Barotrauma
public bool ScrollBarEnabled { get; set; } = true;
public bool KeepSpaceForScrollBar { get; set; }
public bool CanTakeKeyBoardFocus { get; set; } = true;
public bool ScrollBarVisible
{
get
@@ -214,7 +216,22 @@ namespace Barotrauma
public bool AutoHideScrollBar { get; set; } = true;
private bool IsScrollBarOnDefaultSide { get; set; }
public bool CanDragElements { get; set; } = false;
public bool CanDragElements
{
get
{
return canDragElements;
}
set
{
if (value == false && canDragElements && draggedElement != null)
{
draggedElement = null;
}
canDragElements = value;
}
}
private bool canDragElements = false;
private GUIComponent draggedElement;
private Rectangle draggedReferenceRectangle;
private Point draggedReferenceOffset;
@@ -223,9 +240,12 @@ namespace Barotrauma
private bool scheduledScroll = false;
private readonly bool isHorizontal;
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{
this.isHorizontal = isHorizontal;
HoverCursor = CursorState.Hand;
CanBeFocused = true;
selected = new List<GUIComponent>();
@@ -454,22 +474,42 @@ namespace Barotrauma
}
else
{
draggedElement.RectTransform.AbsoluteOffset = draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
draggedElement.RectTransform.AbsoluteOffset = isHorizontal ?
draggedReferenceOffset + new Point((int)PlayerInput.MousePosition.X - draggedReferenceRectangle.Center.X, 0) :
draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform);
int currIndex = index;
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
if (isHorizontal)
{
currIndex--;
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
while (currIndex > 0 && PlayerInput.MousePosition.X < draggedReferenceRectangle.Left)
{
currIndex--;
draggedReferenceRectangle.X -= draggedReferenceRectangle.Width;
draggedReferenceOffset.X -= draggedReferenceRectangle.Width;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.X > draggedReferenceRectangle.Right)
{
currIndex++;
draggedReferenceRectangle.X += draggedReferenceRectangle.Width;
draggedReferenceOffset.X += draggedReferenceRectangle.Width;
}
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
else
{
currIndex++;
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
{
currIndex--;
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
{
currIndex++;
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
}
}
if (currIndex != index)
@@ -853,7 +893,7 @@ namespace Barotrauma
}
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
if (takeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
if (takeKeyBoardFocus && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
{
Selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
@@ -943,9 +983,10 @@ namespace Barotrauma
public override void RemoveChild(GUIComponent child)
{
if (child == null) { return; }
if (child == null) { return; }
child.RectTransform.Parent = null;
if (selected.Contains(child)) selected.Remove(child);
if (selected.Contains(child)) { selected.Remove(child); }
if (draggedElement == child) { draggedElement = null; }
UpdateScrollBarSize();
}
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -22,11 +23,11 @@ namespace Barotrauma
{
Default,
InGame,
Vote
Vote,
Hint
}
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
//public GUIFrame BackgroundFrame { get; private set; }
public GUILayoutGroup Content { get; private set; }
public GUIFrame InnerFrame { get; private set; }
public GUITextBlock Header { get; private set; }
@@ -58,8 +59,6 @@ namespace Barotrauma
public bool AutoClose;
private readonly bool alwaysVisible;
private float openState;
private float iconState;
private bool iconSwitching;
@@ -77,10 +76,17 @@ namespace Barotrauma
this.Buttons[0].OnClicked = Close;
}
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null)
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, bool parseRichText = false)
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
{
int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0;
int width = (int)(DefaultWidth * type switch
{
Type.Default => 1.0f,
Type.Hint => 1.25f,
_ => 1.5f
});
int height = 0;
if (relativeSize.HasValue)
{
width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X);
@@ -107,6 +113,7 @@ namespace Barotrauma
Anchor anchor = type switch
{
Type.InGame => Anchor.TopCenter,
Type.Hint => Anchor.TopRight,
Type.Vote => Anchor.TopRight,
_ => Anchor.Center
};
@@ -127,13 +134,13 @@ namespace Barotrauma
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this);
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
@@ -180,7 +187,6 @@ namespace Barotrauma
else if (type == Type.InGame)
{
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
alwaysVisible = true;
CanBeFocused = false;
AutoClose = true;
GUI.Style.Apply(InnerFrame, "", this);
@@ -235,13 +241,13 @@ namespace Barotrauma
};
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this);
Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
@@ -266,30 +272,184 @@ namespace Barotrauma
}
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
}
else if (type == Type.Hint)
{
CanBeFocused = false;
GUI.Style.Apply(InnerFrame, "", this);
Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = absoluteSpacing.Y,
Stretch = true
};
var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
int iconMaxHeight = 0;
if (icon != null)
{
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
iconMaxHeight = (int)Icon.Sprite.size.Y;
}
else
{
bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle);
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform),
iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true);
if (!iconStyleDefined)
{
Icon.Color = Color.Orange;
}
iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40);
}
iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight);
Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight);
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform))
{
AbsoluteSpacing = absoluteSpacing.Y,
};
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null);
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.dontshowagain"))
{
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
UserData = "dontshowagain"
};
//var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform),
// TextManager.Get("hintmessagebox.disablehints"))
//{
// ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
// UserData = "disablehints"
//};
Buttons = new List<GUIButton>(1)
{
new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight),
text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall")
{
OnClicked = Close
}
};
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
GUI.Style.Apply(Text, "", this);
Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
if (string.IsNullOrWhiteSpace(headerText))
{
Header.RectTransform.Parent = null;
Content.ChildAnchor = Anchor.Center;
}
}
if (height == 0)
{
height = absoluteSpacing.Y;
int upperContainerHeight = absoluteSpacing.Y;
if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; }
if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; }
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
height += upperContainerHeight;
height += absoluteSpacing.Y;
height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight);
height += absoluteSpacing.Y;
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
verticalLayoutGroup.Recalculate();
topHorizontalLayoutGroup.Recalculate();
Content.Recalculate();
tickBoxLayoutGroup.Recalculate();
}
InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height);
Point GetVerticalLayoutGroupSize()
{
return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2);
}
}
MessageBoxes.Add(this);
}
/// <summary>
/// Use to create a message box of Hint type
/// </summary>
public GUIMessageBox(string hintIdentifier, string text, Sprite icon) : this("", text, new string[0], textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
{
if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
{
dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain;
dontShowAgainTickBox.UserData = hintIdentifier;
}
if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox)
{
disableHintsTickBox.OnSelected = HintManager.OnDisableHints;
disableHintsTickBox.UserData = hintIdentifier;
}
}
private static Type[] messageBoxTypes;
public static void AddActiveToGUIUpdateList()
{
for (int i = 0; i < MessageBoxes.Count; i++)
messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type));
foreach (var type in messageBoxTypes)
{
if (MessageBoxes[i] is GUIMessageBox alwaysVisibleMsgBox && alwaysVisibleMsgBox.alwaysVisible)
// Don't display hints when HUD is disabled
if (type == Type.Hint && GUI.DisableHUD) { continue; }
for (int i = 0; i < MessageBoxes.Count; i++)
{
alwaysVisibleMsgBox.AddToGUIUpdateList();
break;
}
}
for (int i = MessageBoxes.Count - 1; i >= 0; i--)
{
if (MessageBoxes[i].UserData as string == "verificationprompt" ||
MessageBoxes[i].UserData as string == "bugreporter")
{
continue;
}
if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible)
{
MessageBoxes[i].AddToGUIUpdateList();
if (MessageBoxes[i] == null) { continue; }
if (!(MessageBoxes[i] is GUIMessageBox messageBox))
{
if (type == Type.Default)
{
// Message box not of type GUIMessageBox is likely the round summary
MessageBoxes[i].AddToGUIUpdateList();
break;
}
continue;
}
if (messageBox.type != type) { continue; }
// These are handled separately in GUI.HandlePersistingElements()
if (MessageBoxes[i].UserData as string == "verificationprompt") { continue; }
if (MessageBoxes[i].UserData as string == "bugreporter") { continue; }
messageBox.AddToGUIUpdateList();
break;
}
}
@@ -337,11 +497,21 @@ namespace Barotrauma
}
}
if (type == Type.InGame)
if (type == Type.InGame || type == Type.Hint)
{
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
Vector2 initialPos, defaultPos, endPos;
if (type == Type.InGame)
{
initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
}
else
{
initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height);
defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.ButtonAreaTop.Height + GUI.IntScale(64));
endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y);
}
if (!closing)
{
@@ -428,7 +598,7 @@ namespace Barotrauma
public void Close()
{
if (type == Type.InGame)
if (type == Type.InGame || type == Type.Hint)
{
closing = true;
}
@@ -34,6 +34,11 @@ namespace Barotrauma
public readonly Sprite[] CursorSprite = new Sprite[7];
public UISprite RadiationSprite { get; private set; }
public SpriteSheet RadiationAnimSpriteSheet { get; private set; }
public SpriteSheet SavingIndicator { get; private set; }
public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; }
@@ -77,6 +82,12 @@ namespace Barotrauma
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
public Color ColorReputationVeryLow { get; private set; } = Color.Red;
public Color ColorReputationLow { get; private set; } = Color.Orange;
public Color ColorReputationNeutral { get; private set; } = Color.White * 0.8f;
public Color ColorReputationHigh { get; private set; } = Color.LightBlue;
public Color ColorReputationVeryHigh { get; private set; } = Color.Blue;
// Inventory
public Color EquipmentSlotIconColor { get; private set; } = new Color(99, 70, 64);
@@ -167,6 +178,21 @@ namespace Barotrauma
case "textcolor":
TextColor = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationverylow":
ColorReputationVeryLow = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationlow":
ColorReputationLow = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationneutral":
ColorReputationNeutral = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationhigh":
ColorReputationHigh = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationveryhigh":
ColorReputationVeryHigh = subElement.GetAttributeColor("color", TextColor);
break;
case "equipmentsloticoncolor":
EquipmentSlotIconColor = subElement.GetAttributeColor("color", EquipmentSlotIconColor);
break;
@@ -209,6 +235,12 @@ namespace Barotrauma
case "uiglow":
UIGlow = new UISprite(subElement);
break;
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
case "radiationanimspritesheet":
RadiationAnimSpriteSheet = new SpriteSheet(subElement);
break;
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
@@ -218,6 +250,9 @@ namespace Barotrauma
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
case "savingindicator":
SavingIndicator = new SpriteSheet(subElement);
break;
case "font":
Font = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[Font] = subElement.GetAttributeBool("forceuppercase", false);
@@ -259,9 +259,11 @@ namespace Barotrauma
public StrikethroughSettings Strikethrough = null;
private readonly List<RichTextData> richTextData = null;
public readonly List<RichTextData> RichTextData = null;
private readonly bool hasColorHighlight = false;
public bool HasColorHighlight => RichTextData != null;
public bool OverrideRichTextDataAlpha = true;
public struct ClickableArea
{
@@ -279,7 +281,8 @@ namespace Barotrauma
/// If the rectT height is set 0, the height is calculated from the text.
/// </summary>
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
bool playerInput = false, bool parseRichText = false)
: base(style, rectT)
{
if (color.HasValue)
@@ -289,7 +292,12 @@ namespace Barotrauma
if (textColor.HasValue)
{
OverrideTextColor(textColor.Value);
}
}
if (parseRichText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
//use the default CJK font as a fallback
@@ -318,8 +326,7 @@ namespace Barotrauma
public GUITextBlock(RectTransform rectT, List<RichTextData> richTextData, string text, Color? textColor = null, ScalableFont font = null, Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
: this(rectT, text, textColor, font, textAlignment, wrap, style, color, playerInput)
{
this.richTextData = richTextData;
hasColorHighlight = richTextData != null;
this.RichTextData = richTextData;
}
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
@@ -568,8 +575,9 @@ namespace Barotrauma
{
base.Update(deltaTime);
if (ClickableAreas.Any() && (GUI.MouseOn?.IsParentOf(this) ?? true) && Rect.Contains(PlayerInput.MousePosition))
if (ClickableAreas.Any() && (GUI.MouseOn?.IsParentOf(this) ?? true))
{
if (!Rect.Contains(PlayerInput.MousePosition)) { return; }
int index = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
foreach (ClickableArea clickableArea in ClickableAreas)
{
@@ -627,7 +635,7 @@ namespace Barotrauma
currentTextColor = selectedTextColor;
}
if (!hasColorHighlight)
if (!HasColorHighlight)
{
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
@@ -639,12 +647,15 @@ namespace Barotrauma
}
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
}
else
{
if (OverrideRichTextDataAlpha)
{
RichTextData.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
}
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text), pos,
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, richTextData);
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
}
if (Strikethrough != null)
@@ -626,6 +626,9 @@ namespace Barotrauma
{
if (Text == null) Text = "";
// Prevent alt gr from triggering any of these as that combination is often needed for special characters
if (PlayerInput.IsAltDown()) return;
switch (command)
{
case '\b' when !Readonly: //backspace
@@ -667,7 +670,10 @@ namespace Barotrauma
}
break;
case (char)0x1: // ctrl-a
SelectAll();
if (PlayerInput.IsCtrlDown())
{
SelectAll();
}
break;
case (char)0x1A when !Readonly && !SubEditorScreen.IsSubEditor(): // ctrl-z
text = memento.Undo();
@@ -128,7 +128,7 @@ namespace Barotrauma
int messageAreaWidth = GameMain.GraphicsWidth / 3;
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height);
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom + ButtonAreaTop.Height, messageAreaWidth, ButtonAreaTop.Height);
bool isFourByThree = GUI.IsFourByThree();
int chatBoxWidth = !isFourByThree ? (int)(475 * GUI.Scale) : (int)(375 * GUI.Scale);
@@ -139,7 +139,9 @@ namespace Barotrauma
int objectiveAnchorOffsetY = (int)(150 * GUI.Scale);
ObjectiveAnchor = new Rectangle(Padding, ChatBoxArea.Y - objectiveAnchorOffsetY, objectiveAnchorWidth, 0);
CrewArea = new Rectangle(Padding, Padding, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding * 2);
int crewAreaY = ButtonAreaTop.Bottom + Padding;
int crewAreaHeight = ObjectiveAnchor.Top - Padding - crewAreaY;
CrewArea = new Rectangle(Padding, crewAreaY, (int)Math.Max(400 * GUI.Scale, 220), crewAreaHeight);
InventoryAreaLower = new Rectangle(ChatBoxArea.Right + Padding * 7, inventoryTopY, GameMain.GraphicsWidth - Padding * 9 - ChatBoxArea.Width, GameMain.GraphicsHeight - inventoryTopY);
@@ -70,6 +70,14 @@ namespace Barotrauma
}
private string selectedTip;
private List<RichTextData> selectedTipRichTextData;
private bool selectedTipRichTextUnparsed;
private void SetSelectedTip(string tip)
{
selectedTip = tip;
selectedTipRichTextData = null;
selectedTipRichTextUnparsed = true;
}
private readonly object loadMutex = new object();
private float? loadState;
@@ -115,7 +123,7 @@ namespace Barotrauma
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true;
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
@@ -215,14 +223,34 @@ namespace Barotrauma
if (GUI.Font != null && selectedTip != null)
{
if (selectedTipRichTextUnparsed)
{
selectedTipRichTextData = RichTextData.GetRichTextData(selectedTip, out selectedTip);
selectedTipRichTextUnparsed = false;
}
string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font);
string[] lines = wrappedTip.Split('\n');
float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
for (int i = 0; i < lines.Length; i++)
if (selectedTipRichTextData != null)
{
GUI.Font.DrawString(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
int rtdOffset = 0;
for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawStringWithColors(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White,
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTipRichTextData, rtdOffset);
rtdOffset += lines[i].Length;
}
}
else
{
for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawString(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
}
}
}
@@ -302,7 +330,7 @@ namespace Barotrauma
{
GameMain.Config.Language = language;
//reload tip in the selected language
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
GameMain.Config.SetDefaultBindings(legacy: false);
GameMain.Config.CheckBindings(useDefaults: true);
WaitForLanguageSelection = false;
@@ -364,7 +392,7 @@ namespace Barotrauma
{
drawn = false;
LoadState = null;
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn)
@@ -94,7 +94,7 @@ namespace Barotrauma
}
}
private static Point maxPoint = new Point(int.MaxValue, int.MaxValue);
public readonly static Point MaxPoint = new Point(int.MaxValue, int.MaxValue);
private Point? maxSize;
/// <summary>
@@ -103,7 +103,7 @@ namespace Barotrauma
/// </summary>
public Point MaxSize
{
get { return maxSize ?? maxPoint; }
get { return maxSize ?? MaxPoint; }
set
{
if (maxSize == value) { return; }
@@ -640,6 +640,12 @@ namespace Barotrauma
return children.Contains(rectT) || (recursive && children.Any(c => c.IsParentOf(rectT)));
}
public bool IsChildOf(RectTransform rectT, bool recursive = true)
{
if (Parent == null) { return false; }
return Parent == rectT || (recursive && Parent.IsChildOf(rectT));
}
public void ClearChildren()
{
children.ForEachMod(c => c.Parent = null);
@@ -24,7 +24,6 @@ namespace Barotrauma
private int buyTotal, sellTotal;
private GUITextBlock merchantBalanceBlock;
private GUILayoutGroup valueChangeGroup;
private GUITextBlock currentSellValueBlock, newSellValueBlock;
private GUIImage sellValueChangeArrow;
private GUIDropDown sortingDropDown;
@@ -158,7 +157,7 @@ namespace Barotrauma
};
// Store header ------------------------------------------------
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform), isHorizontal: true)
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.95f / 14.0f), storeContent.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.005f
};
@@ -209,7 +208,7 @@ namespace Barotrauma
// Item sell value ------------------------------------------------
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{
CanBeFocused = false,
CanBeFocused = true,
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
@@ -220,9 +219,9 @@ namespace Barotrauma
ForceUpperCase = true
};
valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
var valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
CanBeFocused = true,
CanBeFocused = false,
RelativeSpacing = 0.02f
};
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
@@ -247,7 +246,7 @@ namespace Barotrauma
{
string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ?
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
valueChangeGroup.ToolTip = TextManager.Get(tooltipTag);
sellValueContainer.ToolTip = TextManager.Get(tooltipTag);
currentSellValueBlock.TextColor = newStatus.Color;
sellValueChangeArrow.Color = newStatus.Color;
sellValueChangeArrow.Visible = true;
@@ -256,7 +255,7 @@ namespace Barotrauma
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
}
valueChangeGroup.ToolTip = null;
sellValueContainer.ToolTip = TextManager.Get("campaignstore.sellvaluetooltip");
currentSellValueBlock.TextColor = CurrentLocation.BalanceColor;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
@@ -264,7 +263,7 @@ namespace Barotrauma
}
else
{
valueChangeGroup.ToolTip = null;
sellValueContainer.ToolTip = null;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return null;
@@ -293,7 +292,7 @@ namespace Barotrauma
newSellValueBlock.Padding = newPadding;
// Store mode buttons ------------------------------------------------
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), storeContent.RectTransform), style: null);
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f / 14.0f), storeContent.RectTransform), style: null);
var modeButtonContainer = new GUILayoutGroup(new RectTransform(Vector2.One, modeButtonFrame.RectTransform), isHorizontal: true);
var tabs = Enum.GetValues(typeof(StoreTab));
@@ -676,6 +675,7 @@ namespace Barotrauma
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
SetQuantityLabelText(StoreTab.Buy, itemFrame);
SetOwnedLabelText(itemFrame);
SetPriceGetters(itemFrame, true);
}
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
existingItemFrames.Add(itemFrame);
@@ -750,6 +750,7 @@ namespace Barotrauma
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.Sell, itemFrame);
SetOwnedLabelText(itemFrame);
SetPriceGetters(itemFrame, false);
}
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
if (itemQuantity < 1 && !isRequestedGood)
@@ -772,6 +773,37 @@ namespace Barotrauma
shoppingCrateSellList.BarScroll = prevShoppingCrateScroll;
}
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
{
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
{
if (buying)
{
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, considerDailySpecials: false) ?? 0);
}
else
{
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, considerRequestedGoods: false) ?? 0);
}
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
{
if (buying)
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab) ?? 0);
}
else
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab) ?? 0);
}
}
}
public void RefreshItemsToSell()
{
itemsToSell.Clear();
@@ -1188,14 +1220,6 @@ namespace Barotrauma
};
priceBlock.Color *= (forceDisable ? 0.5f : 1.0f);
priceBlock.CalculateHeightFromText();
if (isSellingRelatedList)
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
}
else
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
}
if (locationHasDealOnItem)
{
var undiscounterPriceBlock = new GUITextBlock(
@@ -1209,17 +1233,8 @@ namespace Barotrauma
TextColor = priceBlock.TextColor,
UserData = "undiscountedprice"
};
if (isSellingRelatedList)
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo, considerRequestedGoods: false) ?? 0);
}
else
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo, considerDailySpecials: false) ?? 0);
}
}
SetPriceGetters(frame, !isSellingRelatedList);
if (isParentOnLeftSideOfInterface)
{
@@ -1268,7 +1283,8 @@ namespace Barotrauma
// Add items on the sub(s)
Submarine.MainSub?.GetItems(true)
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)))
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
ItemAndAllContainersInteractable(i))
.ForEach(i => AddToOwnedItems(i.Prefab));
// Add items in character inventories
@@ -1286,6 +1302,16 @@ namespace Barotrauma
ownedItemsUpdateTimer = 0.0f;
static bool ItemAndAllContainersInteractable(Item item)
{
do
{
if (!item.IsPlayerTeamInteractable) { return false; }
item = item.Container;
} while (item != null);
return true;
}
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
{
if (OwnedItems.ContainsKey(itemPrefab))
@@ -60,6 +60,7 @@ namespace Barotrauma
public GUITextBlock submarineFee;
public GUIButton selectSubmarineButton;
public GUITextBlock middleTextBlock;
public GUIButton previewButton;
}
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
@@ -191,6 +192,12 @@ namespace Barotrauma
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUI.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
submarineDisplayElement.selectSubmarineButton = new GUIButton(new RectTransform(Vector2.One, submarineDisplayElement.background.RectTransform), style: null);
submarineDisplayElement.previewButton = new GUIButton(new RectTransform(Vector2.One * 0.12f, submarineDisplayElement.background.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point((int)(0.03f * background.Rect.Height)) }, style: "ExpandButton")
{
Color = Color.White,
HoverColor = Color.White,
PressedColor = Color.White
};
submarineDisplays[i] = submarineDisplayElement;
}
@@ -299,6 +306,7 @@ namespace Barotrauma
submarineDisplays[i].selectSubmarineButton.OnClicked = null;
submarineDisplays[i].displayedSubmarine = null;
submarineDisplays[i].middleTextBlock.AutoDraw = false;
submarineDisplays[i].previewButton.Visible = false;
}
else
{
@@ -369,6 +377,13 @@ namespace Barotrauma
{
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
}
submarineDisplays[i].previewButton.Visible = true;
submarineDisplays[i].previewButton.OnClicked = (btn, obj) =>
{
SubmarinePreview.Create(subToDisplay);
return false;
};
}
submarineIndex++;
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using Barotrauma.Networking;
using System.Globalization;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -17,7 +18,7 @@ namespace Barotrauma
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor };
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine };
private static InfoFrameTab selectedTab;
private GUIFrame infoFrame, contentFrame;
@@ -40,11 +41,11 @@ namespace Barotrauma
private const ushort mediumPingThreshold = 200;
private ushort currentPing;
private Client client;
private Character character;
private bool hasCharacter;
private GUITextBlock textBlock;
private GUIFrame frame;
private readonly Client client;
private readonly Character character;
private readonly bool hasCharacter;
private readonly GUITextBlock textBlock;
private readonly GUIFrame frame;
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
{
@@ -180,60 +181,79 @@ namespace Barotrauma
infoFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, infoFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
//this used to be a switch expression but i changed it because it killed enc :(
Vector2 contentFrameSize;
switch (selectedTab)
{
case InfoFrameTab.Crew:
case InfoFrameTab.Mission:
case InfoFrameTab.Traitor:
default:
contentFrame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.667f), infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { /*MinSize = new Point(width, height),*/ RelativeOffset = new Vector2(0.025f, 0.12f) });
break;
case InfoFrameTab.MyCharacter:
contentFrame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.5f), infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { /*MinSize = new Point(width, height),*/ RelativeOffset = new Vector2(0.025f, 0.12f) });
contentFrameSize = new Vector2(0.45f, 0.5f);
break;
default:
contentFrameSize = new Vector2(0.45f, 0.667f);
break;
}
contentFrame = new GUIFrame(new RectTransform(contentFrameSize, infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.12f) });
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(17.5f)) }, style: null);
var buttonArea = new GUILayoutGroup(new RectTransform(new Point(innerFrame.Rect.Width, GUI.IntScale(25f)), innerFrame.RectTransform) { AbsoluteOffset = new Point(2, 0) }, isHorizontal: true)
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(25f)) }, isHorizontal: true)
{
RelativeSpacing = 0.01f
};
infoFrameHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.926f), innerFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter), style: null);
var crewButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Crew"), style: "GUITabButton")
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(0.07f, 1f), parent: horizontalLayoutGroup.RectTransform), isHorizontal: false)
{
UserData = InfoFrameTab.Crew,
OnClicked = SelectInfoFrameTab
AbsoluteSpacing = GUI.IntScale(5f)
};
tabButtons.Add(crewButton);
var missionButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Mission"), style: "GUITabButton")
var innerLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.92f, 1f), horizontalLayoutGroup.RectTransform))
{
UserData = InfoFrameTab.Mission,
OnClicked = SelectInfoFrameTab
RelativeSpacing = 0.01f,
Stretch = true
};
tabButtons.Add(missionButton);
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
if (isTraitor && GameMain.Client.TraitorMission != null)
float absoluteSpacing = innerLayoutGroup.RelativeSpacing * innerLayoutGroup.Rect.Height;
int multiplier = GameMain.GameSession?.GameMode is CampaignMode ? 2 : 1;
int infoFrameHolderHeight = Math.Min((int)(0.97f * innerLayoutGroup.Rect.Height), (int)(innerLayoutGroup.Rect.Height - multiplier * (GUI.IntScale(15f) + absoluteSpacing)));
infoFrameHolder = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: null);
GUIButton createTabButton(InfoFrameTab tab, string textTag)
{
var traitorButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("tabmenu.traitor"), style: "GUITabButton")
var newButton = new GUIButton(new RectTransform(Vector2.One, buttonArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: $"InfoFrameTabButton.{tab}")
{
UserData = InfoFrameTab.Traitor,
UserData = tab,
ToolTip = TextManager.Get(textTag),
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(traitorButton);
tabButtons.Add(newButton);
return newButton;
}
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
var missionButton = createTabButton(InfoFrameTab.Mission, "mission");
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{
var reputationButton = createTabButton(InfoFrameTab.Reputation, "reputation");
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), "", textAlignment: Alignment.Right, parseRichText: true)
{
TextGetter = () => TextManager.GetWithVariable("campaignmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", campaignMode.Money))
};
}
else
{
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
if (isTraitor && GameMain.Client.TraitorMission != null)
{
var traitorButton = createTabButton(InfoFrameTab.Traitor, "tabmenu.traitor");
}
}
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
if (GameMain.NetworkMember != null)
{
var myCharacterButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("tabmenu.character"), style: "GUITabButton")
{
UserData = InfoFrameTab.MyCharacter,
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(myCharacterButton);
var myCharacterButton = createTabButton(InfoFrameTab.MyCharacter, "tabmenu.character");
}
}
@@ -252,6 +272,14 @@ namespace Barotrauma
case InfoFrameTab.Mission:
CreateMissionInfo(infoFrameHolder);
break;
case InfoFrameTab.Reputation:
if (GameMain.GameSession.RoundSummary != null && GameMain.GameSession.GameMode is CampaignMode campaignMode)
{
infoFrameHolder.ClearChildren();
GUIFrame reputationFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrameHolder.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
GameMain.GameSession.RoundSummary.CreateReputationInfoPanel(reputationFrame, campaignMode);
}
break;
case InfoFrameTab.Traitor:
TraitorMissionPrefab traitorMission = GameMain.Client.TraitorMission;
Character traitor = GameMain.Client.Character;
@@ -262,6 +290,9 @@ namespace Barotrauma
if (GameMain.NetworkMember == null) { return false; }
GameMain.NetLobbyScreen.CreatePlayerFrame(infoFrameHolder);
break;
case InfoFrameTab.Submarine:
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
break;
}
return true;
@@ -356,6 +387,21 @@ namespace Barotrauma
crewFrame.RectTransform.AbsoluteOffset = new Point(0, (int)(headerFrames[0].Rect.Height * headerFrames.Length) - (teamIDs.Count > 1 ? GUI.IntScale(10f) : 0));
float totalRelativeHeight = 0.0f;
if (teamIDs.Count > 1) { totalRelativeHeight += teamIDs.Count * nameHeight; }
headerFrames.ForEach(f => totalRelativeHeight += f.RectTransform.RelativeSize.Y);
crewListArray.ForEach(f => totalRelativeHeight += f.RectTransform.RelativeSize.Y);
if (totalRelativeHeight > 1.0f)
{
float heightOverflow = totalRelativeHeight - 1.0f;
float heightToReduce = heightOverflow / crewListArray.Length;
crewListArray.ForEach(l =>
{
l.RectTransform.Resize(l.RectTransform.RelativeSize - new Vector2(0.0f, heightToReduce));
l.UpdateDimensions();
});
}
if (GameMain.IsMultiplayer)
{
CreateMultiPlayerList(false);
@@ -676,7 +722,7 @@ namespace Barotrauma
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
if (existingPreview != null) infoFrameHolder.RemoveChild(existingPreview);
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.061f, 0) })
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.145f, 0) })
{
UserData = "SelectedCharacter"
};
@@ -831,12 +877,24 @@ namespace Barotrauma
if (logList != null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont)
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont, parseRichText: true)
{
TextColor = textColor,
CanBeFocused = false,
UserData = line
}.CalculateHeightFromText();
};
textBlock.CalculateHeightFromText();
if (textBlock.HasColorHighlight)
{
foreach (var data in textBlock.RichTextData)
{
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{
Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer
});
}
}
}
}
@@ -857,54 +915,109 @@ namespace Barotrauma
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
GUIFrame missionDescriptionHolder;
GUIListBox missionList;
if (hasPortrait)
{
GUIFrame missionImageHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), missionImageHolder.RectTransform), portrait, scaleToFit: true);
missionImageHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, missionImageHolder.RectTransform.AbsoluteOffset.Y + missionImageHolder.Rect.Height + padding) }, style: null);
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true);
portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) });
}
else
{
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) }, style: null);
}
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
}
missionList.ContentBackground.Color = Color.Transparent;
missionList.Spacing = GUI.IntScale(15);
Mission mission = GameMain.GameSession?.Mission;
if (mission != null)
if (GameMain.GameSession?.Missions != null)
{
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft);
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y));
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
if (mission.Prefab.Icon != null)
foreach (Mission mission in GameMain.GameSession.Missions)
{
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft)
{
AbsoluteSpacing = GUI.IntScale(5)
};
string descriptionText = mission.Description;
foreach (string missionMessage in mission.ShownMessages)
{
descriptionText += "\n\n" + missionMessage;
}
string rewardText = mission.GetMissionRewardText();
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor };
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
var missionRewardRichTextData = RichTextData.GetRichTextData(rewardText, out string missionRewardString);
var missionReputationRichTextData = RichTextData.GetRichTextData(reputationText, out string missionReputationString);
var missionDescriptionRichTextData = RichTextData.GetRichTextData(descriptionText, out string missionDescriptionString);
missionNameString = ToolBox.WrapText(missionNameString, missionTextGroup.Rect.Width, GUI.LargeFont);
missionRewardString = ToolBox.WrapText(missionRewardString, missionTextGroup.Rect.Width, GUI.Font);
missionReputationString = ToolBox.WrapText(missionReputationString, missionTextGroup.Rect.Width, GUI.Font);
missionDescriptionString = ToolBox.WrapText(missionDescriptionString, missionTextGroup.Rect.Width, GUI.Font);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
Vector2 missionReputationSize = GUI.Font.MeasureString(missionReputationString);
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
bool displayDifficulty = mission.Difficulty.HasValue;
if (displayDifficulty) { ySize += missionRewardSize.Y; }
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)ySize);
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
if (mission.Prefab.Icon != null)
{
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
Color = mission.Prefab.IconColor,
HoverColor = mission.Prefab.IconColor,
SelectedColor = mission.Prefab.IconColor,
CanBeFocused = false
};
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameRichTextData, missionNameString, font: GUI.LargeFont);
GUILayoutGroup difficultyIndicatorGroup = null;
if (displayDifficulty)
{
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, (int)missionRewardSize.Y), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
AbsoluteSpacing = 1
};
var difficultyColor = mission.GetDifficultyColor();
for (int i = 0; i < mission.Difficulty.Value; i++)
{
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
{
CanBeFocused = false,
Color = difficultyColor
};
}
}
var rewardTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardRichTextData, missionRewardString);
if (difficultyIndicatorGroup != null)
{
difficultyIndicatorGroup.RectTransform.Resize(new Point((int)(difficultyIndicatorGroup.Rect.Width - rewardTextBlock.Padding.X - rewardTextBlock.Padding.Z), difficultyIndicatorGroup.Rect.Height));
difficultyIndicatorGroup.RectTransform.AbsoluteOffset = new Point((int)rewardTextBlock.Padding.X, 0);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionReputationRichTextData, missionReputationString);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionRichTextData, missionDescriptionString);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
}
else
{
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionList.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), TextManager.Get("NoMission"), font: GUI.LargeFont);
}
}
@@ -938,5 +1051,93 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
}
private void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
{
GUIFrame subInfoFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
GUIFrame paddedFrame = new GUIFrame(new RectTransform(Vector2.One * 0.97f, subInfoFrame.RectTransform, Anchor.Center), style: null);
var previewButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.43f), paddedFrame.RectTransform), style: null)
{
OnClicked = (btn, obj) => { SubmarinePreview.Create(sub.Info); return false; },
};
var previewImage = sub.Info.PreviewImage ?? SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name.Equals(sub.Info.Name, StringComparison.OrdinalIgnoreCase))?.PreviewImage;
if (previewImage == null)
{
new GUITextBlock(new RectTransform(Vector2.One, previewButton.RectTransform), TextManager.Get("SubPreviewImageNotFound"));
}
else
{
var submarinePreviewBackground = new GUIFrame(new RectTransform(Vector2.One, previewButton.RectTransform), style: null)
{
Color = Color.Black,
HoverColor = Color.Black,
SelectedColor = Color.Black,
PressedColor = Color.Black,
CanBeFocused = false,
};
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), previewImage, scaleToFit: true) { CanBeFocused = false };
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black) { CanBeFocused = false };
}
new GUIFrame(new RectTransform(Vector2.One * 0.12f, previewButton.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight)
{
AbsoluteOffset = new Point((int)(0.03f * previewButton.Rect.Height))
},
"ExpandButton", Color.White)
{
Color = Color.White,
HoverColor = Color.White,
PressedColor = Color.White
};
var subInfoTextLayout = new GUILayoutGroup(new RectTransform(Vector2.One, paddedFrame.RectTransform));
string className = !sub.Info.HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{sub.Info.SubmarineClass}") : TextManager.Get("shuttle");
int nameHeight = (int)GUI.LargeFont.MeasureString(sub.Info.DisplayName, true).Y;
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
var submarineNameText = new GUITextBlock(new RectTransform(new Point(subInfoTextLayout.Rect.Width, nameHeight + HUDLayoutSettings.Padding / 2), subInfoTextLayout.RectTransform), sub.Info.DisplayName, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { CanBeFocused = false };
submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
var submarineClassText = new GUITextBlock(new RectTransform(new Point(subInfoTextLayout.Rect.Width, classHeight), subInfoTextLayout.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.09f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0f, 0.43f) }, isHorizontal: true) { Stretch = true };
GUIImage headerIcon = new GUIImage(new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "SubmarineIcon");
new GUITextBlock(new RectTransform(Vector2.One, headerLayout.RectTransform), TextManager.Get("uicategory.upgrades"), font: GUI.LargeFont);
var upgradeRootLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.48f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), isHorizontal: true);
var upgradeCategoryPanel = UpgradeStore.CreateUpgradeCategoryList(new RectTransform(new Vector2(0.4f, 1f), upgradeRootLayout.RectTransform));
upgradeCategoryPanel.HideChildrenOutsideFrame = true;
UpgradeStore.UpdateCategoryList(upgradeCategoryPanel, campaign, sub, UpgradeStore.GetApplicableCategories(sub).ToArray());
GUIComponent[] toRemove = upgradeCategoryPanel.Content.FindChildren(c => !c.Enabled).ToArray();
toRemove.ForEach(c => upgradeCategoryPanel.RemoveChild(c));
var upgradePanel = new GUIListBox(new RectTransform(new Vector2(0.6f, 1f), upgradeRootLayout.RectTransform));
upgradeCategoryPanel.OnSelected = (component, userData) =>
{
upgradePanel.ClearChildren();
if (userData is UpgradeStore.CategoryData categoryData && Submarine.MainSub != null)
{
foreach (UpgradePrefab prefab in categoryData.Prefabs)
{
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false);
UpgradeStore.UpdateUpgradeEntry(frame, prefab, categoryData.Category, campaign);
}
}
return true;
};
}
else
{
var specsListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.57f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft));
sub.Info.CreateSpecsWindow(specsListBox, GUI.Font, includeTitle: false, includeClass: false, includeDescription: true);
}
}
}
}
@@ -15,7 +15,7 @@ namespace Barotrauma
internal class UpgradeStore
{
private readonly struct CategoryData
public readonly struct CategoryData
{
public readonly UpgradeCategory Category;
public readonly List<UpgradePrefab> Prefabs;
@@ -125,7 +125,7 @@ namespace Barotrauma
{
if (component.UserData is CategoryData data)
{
UpdateUpgradeEntry(component, data.SinglePrefab, data.Category);
UpdateUpgradeEntry(component, data.SinglePrefab, data.Category, Campaign);
}
}
}
@@ -133,29 +133,35 @@ namespace Barotrauma
// update the small indicator icons on the list
if (currentStoreLayout?.Parent != null)
{
foreach (GUIComponent component in currentStoreLayout.Content.Children)
{
if (!(component.UserData is CategoryData data)) { continue; }
if (component.FindChild("indicators", true) is { } indicators)
{
UpdateCategoryIndicators(indicators, component, data.Prefabs, data.Category);
}
}
UpdateCategoryList(currentStoreLayout, Campaign, drawnSubmarine, applicableCategories);
}
}
// reset the order first
foreach (UpgradeCategory category in UpgradeCategory.Categories)
//TODO: move this somewhere else
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
{
foreach (GUIComponent component in categoryList.Content.Children)
{
if (!(component.UserData is CategoryData data)) { continue; }
if (component.FindChild("indicators", true) is { } indicators)
{
GUIComponent component = currentStoreLayout.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
component?.SetAsLastChild();
UpdateCategoryIndicators(indicators, component, data.Prefabs, data.Category, campaign, drawnSubmarine, applicableCategories);
}
}
// send the disabled components to the bottom
List<GUIComponent> lastChilds = currentStoreLayout.Content.Children.Where(component => !component.Enabled).ToList();
// reset the order first
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
GUIComponent component = categoryList.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
component?.SetAsLastChild();
}
foreach (var lastChild in lastChilds)
{
lastChild.SetAsLastChild();
}
// send the disabled components to the bottom
List<GUIComponent> lastChilds = categoryList.Content.Children.Where(component => !component.Enabled).ToList();
foreach (var lastChild in lastChilds)
{
lastChild.SetAsLastChild();
}
}
@@ -511,9 +517,10 @@ namespace Barotrauma
}
}
private void CreateUpgradeTab()
//TODO: put this somewhere else
public static GUIListBox CreateUpgradeCategoryList(RectTransform rectTransform)
{
currentStoreLayout = new GUIListBox(rectT(1.0f, 1.5f, storeLayout), style: null)
var upgradeCategoryList = new GUIListBox(rectTransform, style: null)
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
@@ -548,7 +555,7 @@ namespace Barotrauma
foreach (var (category, prefabs) in upgrades)
{
var frameChild = new GUIFrame(rectT(1, 0.15f, currentStoreLayout.Content), style: "UpgradeUIFrame")
var frameChild = new GUIFrame(rectT(1, 0.15f, upgradeCategoryList.Content), style: "UpgradeUIFrame")
{
UserData = new CategoryData(category, prefabs),
GlowOnSelect = true
@@ -565,9 +572,9 @@ namespace Barotrauma
* |-----------------------------|--------------------------|
*/
GUILayoutGroup contentLayout = new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild, Anchor.Center));
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUI.SubHeadingFont) { CanBeFocused = false };
GUILayoutGroup indicatorLayout = new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout, Anchor.BottomRight), isHorizontal: true, childAnchor: Anchor.TopRight) { UserData = "indicators", IgnoreLayoutGroups = true, RelativeSpacing = 0.01f };
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUI.SubHeadingFont) { CanBeFocused = false };
GUILayoutGroup indicatorLayout = new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout, Anchor.BottomRight), isHorizontal: true, childAnchor: Anchor.TopRight) { UserData = "indicators", IgnoreLayoutGroups = true, RelativeSpacing = 0.01f };
foreach (var prefab in prefabs)
{
GUIImage upgradeIndicator = new GUIImage(rectT(0.1f, 1f, indicatorLayout), style: "UpgradeIndicator", scaleToFit: true) { UserData = prefab, CanBeFocused = false };
@@ -582,6 +589,13 @@ namespace Barotrauma
indicatorLayout.Recalculate();
}
return upgradeCategoryList;
}
private void CreateUpgradeTab()
{
currentStoreLayout = CreateUpgradeCategoryList(rectT(1.0f, 1.5f, storeLayout));
selectedUpgradeCategoryLayout = new GUIFrame(rectT(GUI.IsFourByThree() ? 0.3f : 0.25f, 1, mainStoreLayout), style: null) { CanBeFocused = false };
RefreshUpgradeList();
@@ -637,7 +651,7 @@ namespace Barotrauma
}
}
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
{
/* UPGRADE PREFAB ENTRY
* |------------------------------------------------------------------|
@@ -648,8 +662,8 @@ namespace Barotrauma
* | | progress bar | x / y | |
* |------------------------------------------------------------------|
*/
GUIFrame prefabFrame = new GUIFrame(rectT(1f, 0.25f, parent), style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = new CategoryData(category, prefab) };
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f,0.95f, prefabFrame, Anchor.Center), isHorizontal: true);
GUIFrame prefabFrame = new GUIFrame(rectTransform, style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = new CategoryData(category, prefab) };
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: true) { Stretch = true };
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout), prefab.Sprite, scaleToFit: true) { CanBeFocused = false };
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
@@ -659,9 +673,13 @@ namespace Barotrauma
GUILayoutGroup progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUI.Style.Orange);
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUI.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), FormatCurrency(prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation)), textAlignment: Alignment.Center) { Padding = Vector4.Zero };
GUILayoutGroup buyButtonLayout = null;
if (addBuyButton)
{
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), FormatCurrency(prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation)), textAlignment: Alignment.Center) { Padding = Vector4.Zero };
var buyButton = new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "UpgradeBuyButton") { Enabled = false };
}
description.CalculateHeightFromText();
// cut the description if it overflows and add a tooltip to it
@@ -677,14 +695,33 @@ namespace Barotrauma
}
// Recalculate everything to prevent jumping
if (parent is GUILayoutGroup group) { group.Recalculate(); }
if (rectTransform.Parent.GUIComponent is GUILayoutGroup group) { group.Recalculate(); }
descriptionLayout.Recalculate();
prefabLayout.Recalculate();
imageLayout.Recalculate();
textLayout.Recalculate();
progressLayout.Recalculate();
buyButtonLayout.Recalculate();
buyButtonLayout?.Recalculate();
return prefabFrame;
}
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
{
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
GUILayoutGroup[] childLayouts = prefabLayout.GetAllChildren<GUILayoutGroup>().ToArray();
var imageLayout = childLayouts[0];
var icon = imageLayout.GetChild<GUIImage>();
var textLayout = childLayouts[1];
var name = textLayout.GetChild<GUITextBlock>();
GUILayoutGroup[] textChildLayouts = textLayout.GetAllChildren<GUILayoutGroup>().ToArray();
var descriptionLayout = textChildLayouts[0];
var description = descriptionLayout.GetChild<GUITextBlock>();
var progressLayout = textChildLayouts[1];
var buyButtonLayout = childLayouts[2];
var buyButton = buyButtonLayout.GetChild<GUIButton>();
if (!HasPermission || itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab)))
{
@@ -713,7 +750,7 @@ namespace Barotrauma
return true;
};
UpdateUpgradeEntry(prefabFrame, prefab, category);
UpdateUpgradeEntry(prefabFrame, prefab, category, Campaign);
}
private void CreateItemTooltip(MapEntity entity)
@@ -778,6 +815,18 @@ namespace Barotrauma
static string CreateListEntry(string name, int level) => TextManager.GetWithVariables("upgradeuitooltip.upgradelistelement", new[] { "[upgradename]", "[level]" }, new[] { name, $"{level}" });
}
public static IEnumerable<UpgradeCategory> GetApplicableCategories(Submarine drawnSubmarine)
{
Item[] entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToArray();
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
{
yield return category;
}
}
}
private void UpdateSubmarinePreview(float deltaTime, GUICustomComponent parent)
{
if (!parent.Children.Any() || Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine || GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
@@ -789,16 +838,8 @@ namespace Barotrauma
CreateSubmarinePreview(drawnSubmarine, parent);
CreateHullBorderVerticies(drawnSubmarine, parent);
List<Item> entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToList();
applicableCategories.Clear();
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
{
applicableCategories.Add(category);
}
}
applicableCategories.AddRange(GetApplicableCategories(drawnSubmarine));
}
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
@@ -1002,9 +1043,9 @@ namespace Barotrauma
}
}
private void UpdateUpgradeEntry(GUIComponent prefabFrame, UpgradePrefab prefab, UpgradeCategory category)
public static void UpdateUpgradeEntry(GUIComponent prefabFrame, UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign)
{
int currentLevel = Campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
int currentLevel = campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
string progressText = TextManager.GetWithVariables("upgrades.progressformat", new[] { "[level]", "[maxlevel]" }, new[] { currentLevel.ToString(), prefab.MaxLevel.ToString() });
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
@@ -1023,7 +1064,7 @@ namespace Barotrauma
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
{
GUITextBlock priceLabel = buttonParent.GetChild<GUITextBlock>();
int price = prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation);
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
if (priceLabel != null && !WaitForServerUpdate)
{
@@ -1038,7 +1079,7 @@ namespace Barotrauma
if (button != null)
{
button.Enabled = currentLevel < prefab.MaxLevel;
if (WaitForServerUpdate || !HasPermission || price > Campaign.Money)
if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || price > campaign.Money)
{
button.Enabled = false;
}
@@ -1046,7 +1087,14 @@ namespace Barotrauma
}
}
private void UpdateCategoryIndicators(GUIComponent indicators, GUIComponent parent, List<UpgradePrefab> prefabs, UpgradeCategory category)
private static void UpdateCategoryIndicators(
GUIComponent indicators,
GUIComponent parent,
List<UpgradePrefab> prefabs,
UpgradeCategory category,
CampaignMode campaign,
Submarine drawnSubmarine,
IEnumerable<UpgradeCategory> applicableCategories)
{
// Disables the parent and only re-enables if the submarine contains valid items
if (!category.IsWallUpgrade && drawnSubmarine != null)
@@ -1078,13 +1126,13 @@ namespace Barotrauma
GUIComponentStyle dimStyle = styles["upgradeindicatordim"];
GUIComponentStyle offStyle = styles["upgradeindicatoroff"];
if (Campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
{
// we check this to avoid flickering from re-applying the same style
if (image.Style == onStyle) { continue; }
image.ApplyStyle(onStyle);
}
else if (Campaign.UpgradeManager.GetUpgradeLevel(prefab, category) > 0)
else if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) > 0)
{
if (image.Style == dimStyle) { continue; }
image.ApplyStyle(dimStyle);