(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
@@ -144,6 +145,15 @@ namespace Barotrauma
GetSize(element);
}
public Sprite GetDefaultSprite()
{
return GetSprite(GUIComponent.ComponentState.None);
}
public Sprite GetSprite(GUIComponent.ComponentState state)
{
return Sprites.ContainsKey(state) ? Sprites[state]?.First()?.Sprite : null;
}
public void GetSize(XElement element)
{
Point size = new Point(0, 0);
@@ -0,0 +1,729 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
class CrewManagement
{
private CampaignMode campaign => campaignUI.Campaign;
private readonly CampaignUI campaignUI;
private readonly GUIComponent parentComponent;
private GUIListBox hireableList, pendingList, crewList;
private GUIFrame characterPreviewFrame;
private GUIDropDown sortingDropDown;
private GUITextBlock totalBlock;
private GUIButton validateHiresButton;
private GUIButton clearAllButton;
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
private Point resolutionWhenCreated;
private enum SortingMethod
{
AlphabeticalAsc,
JobAsc,
PriceAsc,
PriceDesc,
SkillAsc,
SkillDesc
}
public CrewManagement(CampaignUI campaignUI, GUIComponent parentComponent)
{
this.campaignUI = campaignUI;
this.parentComponent = parentComponent;
CreateUI();
UpdateLocationView(campaignUI.Campaign.Map.CurrentLocation, true);
campaignUI.Campaign.Map.OnLocationChanged += (prevLocation, newLocation) => UpdateLocationView(newLocation, true, prevLocation);
}
public void RefreshPermissions()
{
RefreshCrewFrames(hireableList);
RefreshCrewFrames(crewList);
RefreshCrewFrames(pendingList);
if (clearAllButton != null) { clearAllButton.Enabled = HasPermission; }
}
private void RefreshCrewFrames(GUIListBox listBox)
{
if (listBox == null) { return; }
listBox.CanBeFocused = HasPermission;
foreach (GUIComponent child in listBox.Content.Children)
{
if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo, true) is GUIButton buyButton)
{
buyButton.Enabled = HasPermission;
}
}
}
private void CreateUI()
{
if (parentComponent.FindChild(c => c.UserData as string == "glow") is GUIComponent glowChild)
{
parentComponent.RemoveChild(glowChild);
}
if (parentComponent.FindChild(c => c.UserData as string == "container") is GUIComponent containerChild)
{
parentComponent.RemoveChild(containerChild);
}
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black * 0.7f)
{
UserData = "glow"
};
new GUIFrame(new RectTransform(new Vector2(0.95f), parentComponent.RectTransform, anchor: Anchor.Center),
style: null)
{
CanBeFocused = false,
UserData = "container"
};
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)
})
{
Stretch = true,
RelativeSpacing = 0.02f
};
// Header ------------------------------------------------
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), availableMainGroup.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.005f
};
var imageWidth = (float)headerGroup.Rect.Height / headerGroup.Rect.Width;
new GUIImage(new RectTransform(new Vector2(imageWidth, 1.0f), headerGroup.RectTransform), "CrewManagementHeaderIcon");
new GUITextBlock(new RectTransform(new Vector2(1.0f - imageWidth, 1.0f), headerGroup.RectTransform), TextManager.Get("campaigncrew.header"), font: GUI.LargeFont)
{
CanBeFocused = false,
ForceUpperCase = true
};
var hireablesGroup = 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), availableMainGroup.RectTransform)).RectTransform))
{
RelativeSpacing = 0.015f,
Stretch = true
};
var sortGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), hireablesGroup.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.015f
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sortGroup.RectTransform), text: TextManager.Get("campaignstore.sortby"));
sortingDropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f), sortGroup.RectTransform), elementCount: 5)
{
OnSelected = (child, userData) =>
{
SortCharacters(hireableList, (SortingMethod)userData);
return true;
}
};
var tag = "sortingmethod.";
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.JobAsc), userData: SortingMethod.JobAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.SkillAsc), userData: SortingMethod.SkillAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.SkillDesc), userData: SortingMethod.SkillDesc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.PriceAsc), userData: SortingMethod.PriceAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.PriceDesc), userData: SortingMethod.PriceDesc);
hireableList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.96f),
hireablesGroup.RectTransform,
anchor: Anchor.Center))
{
Spacing = 1
};
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)
})
{
Stretch = true,
RelativeSpacing = 0.02f
};
var playerBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), pendingAndCrewMainGroup.RectTransform), childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
TextManager.Get("campaignstore.balance"), font: GUI.Font, textAlignment: Alignment.BottomRight)
{
AutoScaleVertical = true,
ForceUpperCase = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () => FormatCurrency(campaign.Money)
};
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)
}).RectTransform));
float height = 0.05f;
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaigncrew.pending"), font: GUI.SubHeadingFont);
pendingList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
};
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))
{
Spacing = 1
};
var group = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), TextManager.Get("campaignstore.total"));
totalBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
TextScale = 1.1f
};
group = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.01f
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ForceUpperCase = true,
OnClicked = (b, o) => ValidatePendingHires(true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ForceUpperCase = true,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
};
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
private void UpdateLocationView(Location location, bool removePending, Location prevLocation = null)
{
if (prevLocation != null && prevLocation == location && GameMain.NetworkMember != null) { return; }
if (characterPreviewFrame != null)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
UpdateHireables(location);
if (pendingList != null)
{
if (removePending)
{
PendingHires?.Clear();
pendingList.Content.ClearChildren();
}
else
{
PendingHires?.ForEach(ci => AddPendingHire(ci));
}
SetTotalHireCost();
}
UpdateCrew();
}
private void UpdateHireables(Location location)
{
if (hireableList != null)
{
hireableList.Content.Children.ToList().ForEach(c => hireableList.RemoveChild(c));
var hireableCharacters = location.GetHireableCharacters();
if (hireableCharacters.None())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), hireableList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
else
{
foreach (CharacterInfo c in hireableCharacters)
{
if (c == null) { continue; }
CreateCharacterFrame(c, hireableList);
}
}
sortingDropDown.SelectItem(SortingMethod.JobAsc);
hireableList.UpdateScrollBarSize();
}
}
public void SetHireables(Location location, List<CharacterInfo> availableHires)
{
HireManager hireManager = location.HireManager;
if (hireManager == null) { return; }
int hireVal = hireManager.AvailableCharacters.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
int newVal = availableHires.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
if (hireVal != newVal)
{
location.HireManager.AvailableCharacters = availableHires;
UpdateHireables(location);
}
}
public void UpdateCrew()
{
crewList.Content.Children.ToList().ForEach(c => crewList.Content.RemoveChild(c));
foreach (CharacterInfo c in GameMain.GameSession.CrewManager.GetCharacterInfos())
{
if (c == null || !((c.Character?.IsBot ?? true) || campaign is SinglePlayerCampaign)) { continue; }
CreateCharacterFrame(c, crewList);
}
SortCharacters(crewList, SortingMethod.JobAsc);
crewList.UpdateScrollBarSize();
}
private void SortCharacters(GUIListBox list, SortingMethod sortingMethod)
{
if (sortingMethod == SortingMethod.AlphabeticalAsc)
{
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name));
}
else if (sortingMethod == SortingMethod.JobAsc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
String.Compare((x.GUIComponent.UserData as Tuple<CharacterInfo, float>)?.Item1.Job.Name, (y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Job.Name, StringComparison.Ordinal));
}
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary));
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
}
else if (sortingMethod == SortingMethod.SkillAsc || sortingMethod == SortingMethod.SkillDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2));
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
}
}
private void CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox)
{
Skill skill = null;
Color? jobColor = null;
if (characterInfo.Job != null)
{
skill = characterInfo.Job?.PrimarySkill ?? characterInfo.Job.Skills.OrderByDescending(s => s.Level).FirstOrDefault();
jobColor = characterInfo.Job.Prefab.UIColor;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, 55), parent: listBox.Content.RectTransform), "ListBoxElement")
{
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? 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)
{
Stretch = true
};
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
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)
{
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)
{
CanBeFocused = false
};
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
float width = 0.6f / 3;
if (characterInfo.Job != 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;
GUIImage skillIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1.0f), skillGroup.RectTransform), skill.Icon)
{
CanBeFocused = false
};
if (jobColor.HasValue) { skillIcon.Color = jobColor.Value; }
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.CenterLeft)
{
CanBeFocused = false
};
}
if (listBox != crewList)
{
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
if (listBox == hireableList)
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
{
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
};
}
else if (listBox == pendingList)
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
{
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
};
}
else if (listBox == crewList && campaign != null)
{
var currentCrew = GameMain.GameSession.CrewManager.GetCharacterInfos();
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
{
UserData = characterInfo,
//can't fire if there's only one character in the crew
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermission,
OnClicked = (btn, obj) =>
{
var confirmDialog = new GUIMessageBox(
TextManager.Get("FireWarningHeader"),
TextManager.GetWithVariable("FireWarningText", "[charactername]", ((CharacterInfo)obj).Name),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
confirmDialog.Buttons[0].UserData = (CharacterInfo)obj;
confirmDialog.Buttons[0].OnClicked = FireCharacter;
confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
confirmDialog.Buttons[1].OnClicked = confirmDialog.Close;
return true;
}
};
}
}
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
{
Pivot pivot = listBox == hireableList ? Pivot.TopLeft : Pivot.TopRight;
Point absoluteOffset = new Point(
pivot == Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5,
characterFrame.Rect.Top);
int frameSize = (int)(GUI.Scale * 300);
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize) < 0)
{
pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight;
absoluteOffset.Y = characterFrame.Rect.Bottom;
}
characterPreviewFrame = new GUIFrame(new RectTransform(new Point(frameSize), parent: campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Parent.RectTransform, pivot: pivot)
{
AbsoluteOffset = absoluteOffset
}, style: "InnerFrame")
{
UserData = characterInfo
};
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), characterPreviewFrame.RectTransform, anchor: Anchor.Center))
{
RelativeSpacing = 0.01f
};
// Character info
GUILayoutGroup infoGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true);
GUILayoutGroup infoLabelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
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);
if (characterInfo.HasGenders)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.Gender.ToString()));
}
if (characterInfo.Job is Job job)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), job.Name);
}
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get("personalitytrait." + trait.Name.Replace(" ", "")));
}
infoLabelGroup.Recalculate();
infoValueGroup.Recalculate();
new GUIImage(new RectTransform(new Vector2(1.0f, 0.05f), mainGroup.RectTransform), "HorizontalLine");
// Skills
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true);
GUILayoutGroup skillNameGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), skillGroup.RectTransform));
GUILayoutGroup skillLevelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1.0f), skillGroup.RectTransform));
List<Skill> characterSkills = characterInfo.Job.Skills;
blockHeight = 1.0f / characterSkills.Count;
foreach (Skill skill in characterSkills)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillLevelGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.Right);
}
}
private bool SelectCharacter(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
{
if (characterPreviewFrame != null && characterPreviewFrame.UserData != characterInfo)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
if (listBox == null || characterFrame == null || characterInfo == null) { return false; }
if (characterPreviewFrame == null)
{
CreateCharacterPreviewFrame(listBox, characterFrame, characterInfo);
}
return true;
}
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
{
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
hireableList.UpdateScrollBarSize();
if (!PendingHires.Contains(characterInfo)) { PendingHires.Add(characterInfo); }
CreateCharacterFrame(characterInfo, pendingList);
SortCharacters(pendingList, SortingMethod.JobAsc);
pendingList.UpdateScrollBarSize();
SetTotalHireCost();
if (createNetworkMessage) { SendCrewState(true); }
return true;
}
private bool RemovePendingHire(CharacterInfo characterInfo, bool setTotalHireCost = true, bool createNetworkMessage = true)
{
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();
if (setTotalHireCost) { SetTotalHireCost(); }
if (createNetworkMessage) { SendCrewState(true); }
return true;
}
private bool RemoveAllPendingHires(bool createNetworkMessage = true)
{
pendingList.Content.Children.ToList().ForEach(c => RemovePendingHire((c.UserData as Tuple<CharacterInfo, float>).Item1, setTotalHireCost: false, createNetworkMessage));
SetTotalHireCost();
return true;
}
private void SetTotalHireCost()
{
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);
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)
{
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.None() || total > campaign.Money) { return false; }
bool atLeastOneHired = false;
foreach (CharacterInfo ci in hires)
{
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
{
break;
}
}
if (atLeastOneHired)
{
UpdateLocationView(campaign.Map.CurrentLocation, true);
SelectCharacter(null, null, null);
var dialog = new GUIMessageBox(
TextManager.Get("newcrewmembers"),
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name),
new string[] { TextManager.Get("Ok") });
dialog.Buttons[0].OnClicked += dialog.Close;
}
if (createNetworkEvent)
{
SendCrewState(true, validateHires: true);
}
return false;
}
private bool FireCharacter(GUIButton button, object selection)
{
if (!(selection is CharacterInfo characterInfo)) { return false; }
campaign.CrewManager.FireCharacter(characterInfo);
SelectCharacter(null, null, null);
UpdateCrew();
SendCrewState(false, firedCharacter: characterInfo);
return false;
}
public void Update()
{
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
{
CreateUI();
UpdateLocationView(campaign.Map.CurrentLocation, false);
}
if ((GUI.MouseOn?.UserData as Tuple<CharacterInfo, float>)?.Item1 is CharacterInfo characterInfo)
{
if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData)
{
GUIComponent component = GUI.MouseOn;
GUIListBox listBox = null;
do
{
if (component.Parent is GUIListBox)
{
listBox = component.Parent as GUIListBox;
break;
}
else if (component.Parent != null)
{
component = component.Parent;
}
else
{
break;
}
} while (listBox == null);
if (listBox != null)
{
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo);
}
}
else
{
// TODO: Reposition the current preview panel if necessary
// Could happen if we scroll a list while hovering?
}
}
else if (characterPreviewFrame != null)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
}
public void SetPendingHires(List<int> characterInfos, Location location)
{
List<CharacterInfo> oldHires = PendingHires.ToList();
foreach (CharacterInfo pendingHire in oldHires)
{
RemovePendingHire(pendingHire, createNetworkMessage: false);
}
PendingHires.Clear();
foreach (int identifier in characterInfos)
{
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier);
if (match != null)
{
PendingHires.Add(match);
AddPendingHire(match, createNetworkMessage: false);
}
else
{
DebugConsole.ThrowError("Received a hire that doesn't exist.");
}
}
}
/// <summary>
/// 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="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)
{
if (campaign is MultiPlayerCampaign)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.CREW);
msg.Write(updatePending);
if (updatePending)
{
msg.Write((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires)
{
msg.Write(pendingHire.GetIdentifier());
}
}
msg.Write(validateHires);
msg.Write(firedCharacter != null);
if (firedCharacter != null)
{
msg.Write(firedCharacter.GetIdentifier());
}
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
}
private string FormatCurrency(int currency) => TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", currency));
}
}
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
@@ -145,6 +146,11 @@ namespace Barotrauma
textBlock.ToolTip = value;
}
}
public bool Pulse { get; set; }
private float pulseTimer;
private float pulseExpand;
private bool flashed;
public GUIButton(RectTransform rectT, string text = "", Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
{
@@ -196,7 +202,14 @@ namespace Barotrauma
protected override void Draw(SpriteBatch spriteBatch)
{
//do nothing
if (Pulse && pulseTimer > 1.0f)
{
Rectangle expandRect = Rect;
float expand = (pulseExpand * 20.0f) * GUI.Scale;
expandRect.Inflate(expand, expand);
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
}
}
protected override void Update(float deltaTime)
@@ -244,13 +257,41 @@ namespace Barotrauma
}
else
{
State = Selected ? ComponentState.Selected : ComponentState.None;
if (!ExternalHighlight)
{
State = Selected ? ComponentState.Selected : ComponentState.None;
}
else
{
State = ComponentState.Hover;
}
}
foreach (GUIComponent child in Children)
{
child.State = State;
}
if (Pulse)
{
pulseTimer += deltaTime;
if (pulseTimer > 1.0f)
{
if (!flashed)
{
flashed = true;
Frame.Flash(Color.White * 0.2f, 0.8f, true);
}
pulseExpand += deltaTime;
if (pulseExpand > 1.0f)
{
pulseTimer = 0.0f;
pulseExpand = 0.0f;
flashed = false;
}
}
}
}
}
}
@@ -20,7 +20,7 @@ namespace Barotrauma
_instance = new GUICanvas();
if (GameMain.Instance != null)
{
GameMain.Instance.OnResolutionChanged += RecalculateSize;
GameMain.Instance.ResolutionChanged += RecalculateSize;
}
_instance.ItemComponentHolder = new GUIFrame(new RectTransform(Vector2.One, _instance, Anchor.Center)).RectTransform;
}
@@ -1,24 +0,0 @@
using Microsoft.Xna.Framework;
namespace Barotrauma
{
public class GUIColorSettings
{
// Inventory
public static Color EquipmentSlotIconColor = new Color(99, 70, 64);
// Health HUD
public static Color BuffColorLow = Color.LightGreen;
public static Color BuffColorMedium = Color.Green;
public static Color BuffColorHigh = Color.DarkGreen;
public static Color DebuffColorLow = Color.DarkSalmon;
public static Color DebuffColorMedium = Color.Red;
public static Color DebuffColorHigh = Color.DarkRed;
public static Color HealthBarColorLow = Color.Red;
public static Color HealthBarColorMedium = Color.Orange;
public static Color HealthBarColorHigh = new Color(78, 114, 88);
}
}
@@ -11,12 +11,16 @@ using System.Net;
namespace Barotrauma
{
public enum SlideDirection { Up, Down, Left, Right }
public abstract class GUIComponent
{
#region Hierarchy
public GUIComponent Parent => RectTransform.Parent?.GUIComponent;
public CursorState HoverCursor = CursorState.Default;
public bool AlwaysOverrideCursor = false;
public delegate bool SecondaryButtonDownHandler(GUIComponent component, object userData);
public SecondaryButtonDownHandler OnSecondaryClicked;
@@ -75,7 +79,7 @@ namespace Barotrauma
public virtual void RemoveChild(GUIComponent child)
{
if (child == null) return;
if (child == null) { return; }
child.RectTransform.Parent = null;
}
@@ -139,6 +143,11 @@ namespace Barotrauma
public bool AutoUpdate { get; set; } = true;
public bool AutoDraw { get; set; } = true;
public int UpdateOrder { get; set; }
public bool Bounce { get; set; }
private float bounceTimer;
private float bounceJump;
private bool bounceDown;
public Action<GUIComponent> OnAddedToGUIUpdateList;
@@ -158,6 +167,8 @@ namespace Barotrauma
protected Color disabledColor;
protected Color pressedColor;
public bool GlowOnSelect { get; set; }
private CoroutineHandle pulsateCoroutine;
protected Color flashColor;
@@ -326,6 +337,11 @@ namespace Barotrauma
{
get { return RectTransform.CountChildren; }
}
/// <summary>
/// Currently only used for the fade effect in GUIListBox, should be set to the same value as Color but only assigned once
/// </summary>
public Color DefaultColor { get; set; }
public virtual Color Color
{
@@ -462,6 +478,36 @@ namespace Barotrauma
OnSecondaryClicked?.Invoke(this, userData);
}
}
if (Bounce)
{
if (bounceTimer > 3.0f || bounceDown)
{
RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 10f));
if (!bounceDown)
{
bounceJump += deltaTime * 4;
if (bounceJump > 0.5f)
{
bounceDown = true;
}
}
else
{
bounceJump -= deltaTime * 4;
if (bounceJump <= 0.0f)
{
bounceJump = 0.0f;
bounceTimer = 0.0f;
bounceDown = false;
}
}
}
else
{
bounceTimer += deltaTime;
}
}
if (flashTimer > 0.0f)
{
@@ -526,12 +572,14 @@ namespace Barotrauma
protected virtual Color GetColor(ComponentState state)
{
if (!Enabled) { return DisabledColor; }
if (ExternalHighlight) { return HoverColor; }
return state switch
{
ComponentState.Hover => HoverColor,
ComponentState.HoverSelected => HoverColor,
ComponentState.Pressed => PressedColor,
ComponentState.Selected => SelectedColor,
ComponentState.Selected when !GlowOnSelect => SelectedColor,
_ => Color,
};
}
@@ -619,6 +667,11 @@ namespace Barotrauma
}
}
if (GlowOnSelect && State == ComponentState.Selected)
{
GUI.UIGlow.Draw(spriteBatch, Rect, SelectedColor);
}
if (flashTimer > 0.0f)
{
//the number of flashes depends on the duration, 1 flash per 1 full second
@@ -690,6 +743,7 @@ namespace Barotrauma
protected virtual void SetAlpha(float a)
{
color = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, a);
hoverColor = new Color(hoverColor.R / 255.0f, hoverColor.G / 255.0f, hoverColor.B / 255.0f, a);;
}
public virtual void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
@@ -707,10 +761,77 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter));
}
private IEnumerable<object> LerpAlpha(float to, float duration, bool removeAfter)
public void FadeIn(float wait, float duration)
{
SetAlpha(0.0f);
CoroutineManager.StartCoroutine(LerpAlpha(1.0f, duration, false, wait));
}
public void SlideIn(float wait, float duration, int amount, SlideDirection direction)
{
RectTransform.ScreenSpaceOffset = direction switch
{
SlideDirection.Up => new Point(0, amount),
SlideDirection.Down => new Point(0, -amount),
SlideDirection.Left => new Point(amount, 0),
SlideDirection.Right => new Point(-amount, 0),
_ => RectTransform.ScreenSpaceOffset
};
CoroutineManager.StartCoroutine(SlideToPosition(duration, wait, Vector2.Zero));
}
public void SlideOut(float duration, int amount, SlideDirection direction)
{
RectTransform.ScreenSpaceOffset = Point.Zero;
Vector2 targetPos = direction switch
{
SlideDirection.Up => new Vector2(0, amount),
SlideDirection.Down => new Vector2(0, -amount),
SlideDirection.Left => new Vector2(amount, 0),
SlideDirection.Right => new Vector2(-amount, 0),
_ => Vector2.Zero
};
CoroutineManager.StartCoroutine(SlideToPosition(duration, 0.0f, targetPos));
}
private IEnumerable<object> SlideToPosition(float duration, float wait, Vector2 target)
{
float t = 0.0f;
float startA = color.A;
var (startX, startY) = RectTransform.ScreenSpaceOffset.ToVector2();
var (endX, endY) = target;
while (t < wait)
{
t += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
t = 0.0f;
while (t < duration)
{
t += CoroutineManager.DeltaTime;
RectTransform.ScreenSpaceOffset = new Point((int)MathHelper.Lerp(startX, endX, t / duration), (int)MathHelper.Lerp(startY, endY, t / duration));
yield return CoroutineStatus.Running;
}
RectTransform.ScreenSpaceOffset = new Point(0, 0);
yield return CoroutineStatus.Success;
}
private IEnumerable<object> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f)
{
State = ComponentState.None;
float t = 0.0f;
float startA = color.A / 255.0f;
while (t < wait)
{
t += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
t = 0.0f;
while (t < duration)
{
@@ -6,6 +6,8 @@ namespace Barotrauma
{
public class GUIFrame : GUIComponent
{
public int OutlineThickness { get; set; }
public GUIFrame(RectTransform rectT, string style = "", Color? color = null) : base(style, rectT)
{
Enabled = true;
@@ -26,7 +28,7 @@ namespace Barotrauma
if (OutlineColor != Color.Transparent)
{
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false);
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false, thickness: OutlineThickness);
}
}
}
@@ -42,17 +42,32 @@ namespace Barotrauma
{
return crop;
}
set
}
public void SetCrop(bool state, bool center = true)
{
crop = state;
if (crop && sprite != null)
{
crop = value;
if (crop)
{
sourceRect.Width = Math.Min(sprite.SourceRect.Width, Rect.Width);
sourceRect.Height = Math.Min(sprite.SourceRect.Height, Rect.Height);
sourceRect.Width = Math.Min(sprite.SourceRect.Width, (int)(Rect.Width / Scale));
sourceRect.Height = Math.Min(sprite.SourceRect.Height, (int)(Rect.Height / Scale));
if (center)
{
sourceRect.X = (sprite.SourceRect.Width - sourceRect.Width) / 2;
sourceRect.Y = (sprite.SourceRect.Height - sourceRect.Height) / 2;
}
origin = sourceRect.Size.ToVector2() / 2;
}
else
{
origin = sprite == null ? Vector2.Zero : sprite.size / 2;
}
}
private Vector2 origin;
public float Scale
{
get;
@@ -72,7 +87,8 @@ namespace Barotrauma
{
if (sprite == value) return;
sprite = value;
sourceRect = sprite.SourceRect;
sourceRect = value == null ? Rectangle.Empty : value.SourceRect;
origin = value == null ? Vector2.Zero : value.size / 2;
if (scaleToFit) RecalculateScale();
}
}
@@ -134,7 +150,8 @@ namespace Barotrauma
{
loadingTextures = true;
loading = true;
TaskPool.Add(LoadTextureAsync(), (Task) =>
TaskPool.Add("LoadTextureAsync",
LoadTextureAsync(), (Task) =>
{
loading = false;
lazyLoaded = true;
@@ -178,7 +195,7 @@ namespace Barotrauma
}
else if (sprite?.Texture != null)
{
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, sprite.size / 2,
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, origin,
Scale, SpriteEffects, 0.0f);
}
@@ -134,7 +134,7 @@ namespace Barotrauma
(RectTransform.Children.Count(c => !c.GUIComponent.IgnoreLayoutGroups) - 1) *
(absoluteSpacing + relativeSpacing * thisSize);
stretchFactor = totalSize <= 0.0f || minSize >= thisSize ?
stretchFactor = totalSize <= 0.0f || minSize >= thisSize || totalSize == minSize ?
1.0f :
(thisSize - minSize) / (totalSize - minSize);
}
@@ -34,7 +34,7 @@ namespace Barotrauma
public GUIScrollBar ScrollBar { get; private set; }
private Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private int totalSize;
private bool childrenNeedsRecalculation;
private bool scrollBarNeedsRecalculation;
@@ -59,6 +59,37 @@ namespace Barotrauma
private bool useGridLayout;
private float targetScroll;
private GUIComponent pendingScroll;
public bool AllowMouseWheelScroll { get; set; } = true;
/// <summary>
/// Scrolls the list smoothly
/// </summary>
public bool SmoothScroll { get; set; }
/// <summary>
/// Whether to only allow scrolling from one element to the next when smooth scrolling is enabled
/// </summary>
public bool ClampScrollToElements { get; set; }
/// <summary>
/// When set to true elements at the bottom of the list are gradually faded
/// </summary>
public bool FadeElements { get; set; }
/// <summary>
/// Adds enough extra padding to the bottom so the end of the scroll will only contain the last element
/// </summary>
public bool PadBottom { get; set; }
/// <summary>
/// When set to true always selects the topmost item on the list
/// </summary>
public bool SelectTop { get; set; }
public bool UseGridLayout
{
get { return useGridLayout; }
@@ -189,10 +220,13 @@ namespace Barotrauma
private Point draggedReferenceOffset;
public GUIComponent DraggedElement => draggedElement;
private bool scheduledScroll = false;
/// <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)
{
HoverCursor = CursorState.Hand;
CanBeFocused = true;
selected = new List<GUIComponent>();
this.useMouseDownToSelect = useMouseDownToSelect;
@@ -363,6 +397,49 @@ namespace Barotrauma
}
}
}
/// <summary>
/// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled.
/// </summary>
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component)
{
GUI.PlayUISound(GUISoundType.Click);
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
targetScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, (children.Count - 0.9f), index)), ScrollBar.MinValue, ScrollBar.MaxValue);
}
public void ScrollToEnd(float duration)
{
CoroutineManager.StartCoroutine(ScrollCoroutine());
IEnumerable<object> ScrollCoroutine()
{
if (BarSize >= 1.0f)
{
yield return CoroutineStatus.Success;
}
float t = 0.0f;
float startScroll = BarScroll * BarSize;
float distanceToTravel = ScrollBar.MaxValue - startScroll;
float progress = startScroll;
float speed = distanceToTravel / duration;
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
{
t += CoroutineManager.DeltaTime;
progress += speed * CoroutineManager.DeltaTime;
BarScroll = progress;
yield return CoroutineStatus.Running;
}
yield return CoroutineStatus.Success;
}
}
private void UpdateChildrenRect()
{
@@ -404,6 +481,32 @@ namespace Barotrauma
}
}
if (SelectTop)
{
foreach (GUIComponent child in Content.Children)
{
child.CanBeFocused = !selected.Contains(child);
if (!child.CanBeFocused)
{
child.State = ComponentState.None;
}
}
}
if (SelectTop && Content.Children.Any() && pendingScroll == null)
{
GUIComponent component = Content.Children.FirstOrDefault(c => (c.Rect.Y - Content.Rect.Y) / (float)c.Rect.Height > -0.1f);
if (component != null && !selected.Contains(component))
{
int index = Content.Children.ToList().IndexOf(component);
if (index >= 0)
{
Select(index, false, false, takeKeyBoardFocus: true);
}
}
}
for (int i = 0; i < Content.CountChildren; i++)
{
var child = Content.RectTransform.GetChild(i)?.GUIComponent;
@@ -418,7 +521,16 @@ namespace Barotrauma
if (mouseDown)
{
Select(i, autoScroll: false);
if (SelectTop)
{
pendingScroll = child;
ScrollToElement(child);
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
else
{
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
}
if (CanDragElements && PlayerInput.LeftButtonDown() && GUI.MouseOn == child)
@@ -538,10 +650,93 @@ namespace Barotrauma
{
UpdateScrollBarSize();
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && PlayerInput.ScrollWheelSpeed != 0)
if (FadeElements)
{
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
foreach (var (component, _) in childVisible)
{
float lerp = 0;
float y = component.Rect.Y;
float contentY = Content.Rect.Y;
float height = component.Rect.Height;
if (y < Content.Rect.Y)
{
float distance = (contentY - y) / height;
lerp = distance;
}
float centerY = Content.Rect.Y + Content.Rect.Height / 2.0f;
if (y > centerY)
{
float distance = (y - centerY) / (centerY - height);
lerp = distance;
}
component.Color = component.HoverColor = ToolBox.GradientLerp(lerp, component.DefaultColor, Color.Transparent);
component.DisabledColor = ToolBox.GradientLerp(lerp, component.Style.DisabledColor, Color.Transparent);
component.HoverColor = ToolBox.GradientLerp(lerp, component.Style.HoverColor, Color.Transparent);
foreach (var child in component.GetAllChildren())
{
Color gradient = ToolBox.GradientLerp(lerp, child.DefaultColor, Color.Transparent);
child.Color = child.HoverColor = gradient;
if (child is GUITextBlock block)
{
block.TextColor = block.HoverTextColor = gradient;
}
}
}
}
if (SmoothScroll)
{
if (targetScroll > -1)
{
float distance = Math.Abs(targetScroll - BarScroll);
float speed = Math.Max(distance * BarSize, 0.1f);
BarScroll = (1.0f - speed) * BarScroll + speed * targetScroll;
if (MathUtils.NearlyEqual(BarScroll, targetScroll) || GUIScrollBar.DraggingBar != null)
{
targetScroll = -1;
pendingScroll = null;
}
}
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
{
float speed = PlayerInput.ScrollWheelSpeed / 500.0f * BarSize;
if (SmoothScroll)
{
if (ClampScrollToElements)
{
bool scrollDown = Math.Clamp(PlayerInput.ScrollWheelSpeed, 0, 1) > 0;
if (scrollDown)
{
SelectPrevious(takeKeyBoardFocus: true);
}
else
{
SelectNext(takeKeyBoardFocus: true);
}
}
else
{
pendingScroll = null;
if (targetScroll < 0) { targetScroll = BarScroll; }
targetScroll -= speed;
targetScroll = Math.Clamp(targetScroll, ScrollBar.MinValue, ScrollBar.MaxValue);
}
}
else
{
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
}
}
ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
if (AutoHideScrollBar)
{
@@ -553,35 +748,47 @@ namespace Barotrauma
}
}
public void SelectNext(bool force = false, bool autoScroll = true)
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
int index = SelectedIndex + 1;
while (index < Content.CountChildren)
{
if (Content.GetChild(index).Visible)
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, autoScroll);
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
}
index++;
}
}
public void SelectPrevious(bool force = false, bool autoScroll = true)
public void SelectPrevious(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
int index = SelectedIndex - 1;
while (index >= 0)
{
if (Content.GetChild(index).Visible)
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, autoScroll);
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
}
index--;
}
}
public void Select(int childIndex, bool force = false, bool autoScroll = true)
public void Select(int childIndex, bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
@@ -646,7 +853,7 @@ namespace Barotrauma
}
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
if (RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
if (takeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
{
Selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
@@ -712,6 +919,14 @@ namespace Barotrauma
totalSize += (ScrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height;
}
totalSize += Content.CountChildren * Spacing;
if (PadBottom)
{
GUIComponent last = Content.Children.LastOrDefault();
if (last != null)
{
totalSize += Rect.Height - last.Rect.Height;
}
}
}
float minScrollBarSize = 20.0f;
@@ -57,7 +57,7 @@ namespace Barotrauma
public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null)
{
coloredText = new ColoredText(text, color, false);
coloredText = new ColoredText(text, color, false, false);
this.lifeTime = lifeTime;
Timer = lifeTime;
@@ -69,7 +69,7 @@ namespace Barotrauma
public GUIMessage(string text, Color color, Vector2 worldPosition, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null)
{
coloredText = new ColoredText(text, color, false);
coloredText = new ColoredText(text, color, false, false);
WorldSpace = true;
pos = worldPosition;
Timer = lifeTime;
@@ -30,6 +30,7 @@ namespace Barotrauma
public GUITextBlock Header { get; private set; }
public GUITextBlock Text { get; private set; }
public string Tag { get; private set; }
public bool Closed { get; private set; }
public GUIImage Icon
{
@@ -47,9 +48,16 @@ namespace Barotrauma
}
}
private bool alwaysVisible;
public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon;
public bool AutoClose;
private readonly bool alwaysVisible;
private float openState;
private float iconState;
private bool iconSwitching;
private bool closing;
private Type type;
@@ -62,7 +70,7 @@ 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)
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)
: 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;
@@ -80,6 +88,15 @@ namespace Barotrauma
}
}
if (backgroundIcon != null)
{
BackgroundIcon = new GUIImage(new RectTransform(backgroundIcon.size.ToPoint(), RectTransform), backgroundIcon)
{
IgnoreLayoutGroups = true,
Color = Color.Transparent
};
}
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, type == Type.InGame ? Anchor.TopCenter : Anchor.Center) { IsFixedSize = false }, style: null);
GUI.Style.Apply(InnerFrame, "", this);
this.type = type;
@@ -145,6 +162,7 @@ namespace Barotrauma
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
alwaysVisible = true;
CanBeFocused = false;
AutoClose = true;
GUI.Style.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
@@ -157,6 +175,10 @@ namespace Barotrauma
{
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
}
else if (iconStyle != string.Empty)
{
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), iconStyle, scaleToFit: true);
}
Content = new GUILayoutGroup(new RectTransform(new Vector2(icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
@@ -182,6 +204,10 @@ namespace Barotrauma
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))
{
Content.ChildAnchor = Anchor.Center;
}
}
if (height == 0)
@@ -226,6 +252,23 @@ namespace Barotrauma
}
}
public void SetBackgroundIcon(Sprite icon)
{
if (icon == null) { return; }
GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
{
IgnoreLayoutGroups = true,
Color = Color.Transparent
};
if (newBackgroundIcon != null)
{
RemoveChild(newBackgroundIcon);
newBackgroundIcon = null;
}
newBackgroundIcon = newIcon;
}
protected override void Update(float deltaTime)
{
if (type == Type.InGame)
@@ -246,10 +289,19 @@ namespace Barotrauma
if (!closing)
{
InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn))
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
@@ -262,13 +314,55 @@ namespace Barotrauma
else
{
openState += deltaTime * 2.0f;
InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
}
}
}
}
@@ -284,6 +378,8 @@ namespace Barotrauma
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
Closed = true;
}
public bool Close(GUIButton button, object obj)
@@ -179,7 +179,7 @@ namespace Barotrauma
private float pressedDelay = 0.5f;
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null) : base(style, rectT)
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null, bool hidePlusMinusButtons = false) : base(style, rectT)
{
LayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
@@ -235,7 +235,7 @@ namespace Barotrauma
return true;
};
if (inputType != NumberType.Int)
if (inputType != NumberType.Int || hidePlusMinusButtons)
{
HidePlusMinusButtons();
}
@@ -121,7 +121,7 @@ namespace Barotrauma
(int)(Rect.Width - style.Padding.X + style.Padding.Z),
(int)(Rect.Height - style.Padding.Y + style.Padding.W));
frame.Visible = showFrame;
slider.Visible = true;
slider.Visible = BarSize > 0.0f;
if (showFrame)
{
@@ -37,6 +37,8 @@ namespace Barotrauma
public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; }
public UISprite ButtonPulse { get; private set; }
public SpriteSheet FocusIndicator { get; private set; }
/// <summary>
@@ -206,6 +208,9 @@ namespace Barotrauma
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
@@ -255,7 +260,8 @@ namespace Barotrauma
DebugConsole.NewMessage("Global font not defined in the current UI style file. The global font is used to render western symbols when using Chinese/Japanese/Korean localization. Using default font instead...", Color.Orange);
}
GameMain.Instance.OnResolutionChanged += () => { RescaleElements(); };
// TODO: Needs to unregister if we ever remove GUIStyles.
GameMain.Instance.ResolutionChanged += RescaleElements;
}
/// <summary>
@@ -271,6 +271,8 @@ namespace Barotrauma
public OnClickDelegate OnClick;
}
public List<ClickableArea> ClickableAreas { get; private set; } = new List<ClickableArea>();
public bool Shadow { get; set; }
/// <summary>
/// This is the new constructor.
@@ -320,10 +322,10 @@ namespace Barotrauma
hasColorHighlight = richTextData != null;
}
public void CalculateHeightFromText(int padding = 0)
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
{
if (wrappedText == null) { return; }
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText).Y + padding));
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText, removeExtraSpacing).Y + padding));
}
public override void ApplyStyle(GUIComponentStyle componentStyle)
@@ -443,8 +445,8 @@ namespace Barotrauma
protected override void SetAlpha(float a)
{
base.SetAlpha(a);
textColor = new Color(textColor.R, textColor.G, textColor.B, a);
// base.SetAlpha(a);
textColor = new Color(TextColor.R / 255.0f, TextColor.G / 255.0f, TextColor.B / 255.0f, a);
}
/// <summary>
@@ -626,12 +628,17 @@ namespace Barotrauma
if (!hasColorHighlight)
{
Font.DrawString(spriteBatch,
Censor ? censoredText : (Wrap ? wrappedText : text),
pos,
currentTextColor * (currentTextColor.A / 255.0f),
0.0f, origin, TextScale,
SpriteEffects.None, textDepth);
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
if (Shadow)
{
Vector2 shadowOffset = new Vector2(GUI.IntScale(2));
Font.DrawString(spriteBatch, textToShow, pos + shadowOffset, Color.Black, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
}
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
}
else
{
@@ -134,6 +134,10 @@ namespace Barotrauma
{
textBlock.OverflowClip = value != null;
maxTextLength = value;
if (Text.Length > MaxTextLength)
{
SetText(textBlock.Text.Substring(0, (int)maxTextLength));
}
}
}
@@ -360,6 +364,7 @@ namespace Barotrauma
}
else
{
CaretIndex = Math.Min(CaretIndex, textDrawn.Length);
textDrawn = Censor ? textBlock.CensoredText : textBlock.Text;
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, CaretIndex));
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin;
@@ -75,6 +75,11 @@ namespace Barotrauma
get; private set;
}
public static Rectangle VotingArea
{
get; private set;
}
public static int Padding
{
get; private set;
@@ -84,7 +89,7 @@ namespace Barotrauma
{
if (GameMain.Instance != null)
{
GameMain.Instance.OnResolutionChanged += CreateAreas;
GameMain.Instance.ResolutionChanged += CreateAreas;
GameMain.Config.OnHUDScaleChanged += CreateAreas;
CreateAreas();
CharacterInfo.Init();
@@ -144,6 +149,13 @@ namespace Barotrauma
int healthWindowY = GameMain.GraphicsHeight / 2 - healthWindowHeight / 2;
HealthWindowAreaLeft = new Rectangle(healthWindowX, healthWindowY, healthWindowWidth, healthWindowHeight);
int votingAreaWidth = (int)(400 * GUI.Scale);
int votingAreaX = GameMain.GraphicsWidth - Padding - votingAreaWidth;
int votingAreaY = Padding + ButtonAreaTop.Height;
// Height is based on text content
VotingArea = new Rectangle(votingAreaX, votingAreaY, votingAreaWidth, 0);
}
public static void Draw(SpriteBatch spriteBatch)
@@ -165,7 +165,7 @@ namespace Barotrauma
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
startOffset: new Point(Rand.Range(0, noiseSprite.SourceRect.Width), Rand.Range(0, noiseSprite.SourceRect.Height)),
startOffset: new Vector2(Rand.Range(0.0f, noiseSprite.SourceRect.Width), Rand.Range(0.0f, noiseSprite.SourceRect.Height)),
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
@@ -185,7 +185,7 @@ namespace Barotrauma
if (LoadState == 100.0f)
{
#if DEBUG
if (GameMain.Config.AutomaticQuickStartEnabled && GameMain.FirstLoad)
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled && GameMain.FirstLoad)
{
loadText = "QUICKSTARTING ...";
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,683 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using System.Linq;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
class SubmarineSelection
{
private const int submarinesPerPage = 4;
private int currentPage = 1;
private int pageCount;
private bool transferService, purchaseService, initialized;
private int deliveryFee;
private string deliveryLocationName;
public GUIFrame GuiFrame;
private GUIFrame pageIndicatorHolder;
private GUICustomComponent selectedSubmarineIndicator;
private GUILayoutGroup submarineHorizontalGroup, submarineControlsGroup;
private GUIButton browseLeftButton, browseRightButton, confirmButton, confirmButtonAlt;
private GUIListBox specsFrame;
private GUIImage[] pageIndicators;
private GUITextBlock descriptionTextBlock;
private int selectionIndicatorThickness;
private GUIImage listBackground;
private List<SubmarineInfo> subsToShow;
private SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null;
private string purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyShorthandText, currencyLongText;
private RectTransform parent;
private Action closeAction;
private Sprite pageIndicator;
public static readonly string[] DeliveryTextVariables = new string[] { "[submarinename1]", "[location1]", "[location2]", "[submarinename2]", "[amount]", "[currencyname]" };
public static readonly string[] SwitchTextVariables = new string[] { "[submarinename1]", "[submarinename2]" };
public static readonly string[] PurchaseAndSwitchTextVariables = new string[] { "[submarinename1]", "[amount]", "[currencyname]", "[submarinename2]" };
public static readonly string[] PurchaseTextVariables = new string[] { "[submarinename]", "[amount]", "[currencyname]" };
private static readonly string[] notEnoughCreditsDeliveryTextVariables = new string[] { "[currencyname]", "[submarinename]", "[location1]", "[location2]" };
private static readonly string[] notEnoughCreditsPurchaseTextVariables = new string[] { "[currencyname]", "[submarinename]" };
private string[] messageBoxOptions;
public const int DeliveryFeePerDistanceTravelled = 1000;
public static bool ContentRefreshRequired = false;
private static readonly Color indicatorColor = new Color(112, 149, 129);
private Point createdForResolution;
private struct SubmarineDisplayContent
{
public GUIFrame background;
public GUIImage submarineImage;
public SubmarineInfo displayedSubmarine;
public GUITextBlock submarineName;
public GUITextBlock submarineClass;
public GUITextBlock submarineFee;
public GUIButton selectSubmarineButton;
public GUITextBlock middleTextBlock;
}
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
{
if (GameMain.GameSession.Campaign == null) return;
transferService = transfer;
purchaseService = !transfer;
this.parent = parent;
this.closeAction = closeAction;
subsToShow = new List<SubmarineInfo>();
if (GameMain.Client == null)
{
messageBoxOptions = new string[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") };
}
else
{
messageBoxOptions = new string[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
}
if (Submarine.MainSub?.Info == null) return;
Initialize();
}
private void Initialize()
{
initialized = true;
if (transferService)
{
deliveryFee = CalculateDeliveryFee();
currentSubText = TextManager.Get("currentsub");
deliveryFeeText = TextManager.Get("deliveryfee");
deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton");
}
else
{
purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase");
priceText = TextManager.Get("price");
}
currencyShorthandText = TextManager.Get("currencyformat");
currencyLongText = TextManager.Get("credit").ToLower();
UpdateSubmarines();
missingPreviewText = TextManager.Get("SubPreviewImageNotFound");
CreateGUI();
}
private int CalculateDeliveryFee()
{
int distanceToOutpost = GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
deliveryLocationName = endLocation.Name;
return DeliveryFeePerDistanceTravelled * distanceToOutpost;
}
private void CreateGUI()
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
GUILayoutGroup content;
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.7f), parent, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.02f) });
selectionIndicatorThickness = HUDLayoutSettings.Padding / 2;
GUIFrame background = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center), color: Color.Black * 0.9f)
{
CanBeFocused = false
};
content = new GUILayoutGroup(new RectTransform(new Point(background.Rect.Width - HUDLayoutSettings.Padding * 4, background.Rect.Height - HUDLayoutSettings.Padding * 4), background.RectTransform, Anchor.Center)) { AbsoluteSpacing = (int)(HUDLayoutSettings.Padding * 1.5f) };
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUI.LargeFont);
header.CalculateHeightFromText(0, true);
GUITextBlock credits = new GUITextBlock(new RectTransform(Vector2.One, header.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight)
{
TextGetter = CampaignUI.GetMoney
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), content.RectTransform), style: "HorizontalLine");
GUILayoutGroup submarineContentGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.4f), content.RectTransform)) { AbsoluteSpacing = HUDLayoutSettings.Padding, Stretch = true };
submarineHorizontalGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), submarineContentGroup.RectTransform)) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding, Stretch = true };
submarineControlsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), submarineContentGroup.RectTransform), true, Anchor.TopCenter);
GUILayoutGroup infoFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.4f), content.RectTransform)) { IsHorizontal = true, Stretch = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform), style: null, new Color(8, 13, 19)) { IgnoreLayoutGroups = true };
listBackground = new GUIImage(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform, Anchor.CenterRight), style: null, true)
{
IgnoreLayoutGroups = true
};
new GUIListBox(new RectTransform(Vector2.One, infoFrame.RectTransform)) { IgnoreLayoutGroups = true, CanBeFocused = false };
specsFrame = new GUIListBox(new RectTransform(new Vector2(0.39f, 1f), infoFrame.RectTransform), style: null) { Spacing = GUI.IntScale(5), Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding, 0, 0) };
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.8f), infoFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, style: "VerticalLine");
GUIListBox descriptionFrame = new GUIListBox(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform), style: null) { Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding / 2f) };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUI.Font, wrap: true) { CanBeFocused = false };
GUILayoutGroup buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), content.RectTransform), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
if (closeAction != null)
{
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
{
OnClicked = (button, userData) =>
{
closeAction();
return true;
}
};
}
if (purchaseService) confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
SetConfirmButtonState(false);
pageIndicatorHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.5f), submarineControlsGroup.RectTransform), style: null);
pageIndicator = GUI.Style.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
UpdatePaging();
for (int i = 0; i < submarineDisplays.Length; i++)
{
SubmarineDisplayContent submarineDisplayElement = new SubmarineDisplayContent();
submarineDisplayElement.background = new GUIFrame(new RectTransform(new Vector2(1f / submarinesPerPage, 1f), submarineHorizontalGroup.RectTransform), style: null, new Color(8, 13, 19));
submarineDisplayElement.submarineImage = new GUIImage(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), null, true);
submarineDisplayElement.middleTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
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);
submarineDisplays[i] = submarineDisplayElement;
}
selectedSubmarineIndicator = new GUICustomComponent(new RectTransform(Point.Zero, submarineHorizontalGroup.RectTransform), onDraw: (sb, component) => DrawSubmarineIndicator(sb, component.Rect)) { IgnoreLayoutGroups = true, CanBeFocused = false };
}
private void UpdatePaging()
{
if (pageIndicatorHolder == null) return;
pageIndicatorHolder.ClearChildren();
if (currentPage > pageCount) currentPage = pageCount;
if (pageCount < 2) return;
browseLeftButton = new GUIButton(new RectTransform(new Vector2(1.15f, 1.15f), pageIndicatorHolder.RectTransform, Anchor.CenterLeft, Pivot.CenterRight) { AbsoluteOffset = new Point(-HUDLayoutSettings.Padding * 3, 0) }, string.Empty, style: "GUIButtonToggleLeft")
{
IgnoreLayoutGroups = true,
OnClicked = (button, userData) =>
{
ChangePage(-1);
return true;
}
};
Point indicatorSize = new Point(GUI.IntScale(pageIndicator.SourceRect.Width * 1.5f), GUI.IntScale(pageIndicator.SourceRect.Height * 1.5f));
pageIndicatorHolder.RectTransform.NonScaledSize = new Point(pageCount * indicatorSize.X + HUDLayoutSettings.Padding * (pageCount - 1), pageIndicatorHolder.RectTransform.NonScaledSize.Y);
int xPos = 0;
int yPos = pageIndicatorHolder.Rect.Height / 2 - indicatorSize.Y / 2;
pageIndicators = new GUIImage[pageCount];
for (int i = 0; i < pageCount; i++)
{
pageIndicators[i] = new GUIImage(new RectTransform(indicatorSize, pageIndicatorHolder.RectTransform) { AbsoluteOffset = new Point(xPos, yPos) }, pageIndicator, null, true);
xPos += indicatorSize.X + HUDLayoutSettings.Padding;
}
for (int i = 0; i < pageIndicators.Length; i++)
{
pageIndicators[i].Color = i == currentPage - 1 ? Color.White : Color.Gray;
}
browseRightButton = new GUIButton(new RectTransform(new Vector2(1.15f, 1.15f), pageIndicatorHolder.RectTransform, Anchor.CenterRight, Pivot.CenterLeft) { AbsoluteOffset = new Point(-HUDLayoutSettings.Padding * 3, 0) }, string.Empty, style: "GUIButtonToggleRight")
{
IgnoreLayoutGroups = true,
OnClicked = (button, userData) =>
{
ChangePage(1);
return true;
}
};
browseLeftButton.Enabled = currentPage > 1;
browseRightButton.Enabled = currentPage < pageCount;
}
private void DrawSubmarineIndicator(SpriteBatch spriteBatch, Rectangle area)
{
if (area == Rectangle.Empty) return;
GUI.DrawRectangle(spriteBatch, area, indicatorColor, thickness: selectionIndicatorThickness);
}
public void Update()
{
if (ContentRefreshRequired)
{
RefreshSubmarineDisplay(true);
}
// Input
if (PlayerInput.KeyHit(Keys.Left))
{
SelectSubmarine(subsToShow.IndexOf(selectedSubmarine), -1);
}
else if (PlayerInput.KeyHit(Keys.Right))
{
SelectSubmarine(subsToShow.IndexOf(selectedSubmarine), 1);
}
}
public void RefreshSubmarineDisplay(bool updateSubs)
{
if (!initialized) Initialize();
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateGUI();
if (updateSubs) UpdateSubmarines();
if (pageIndicators != null)
{
for (int i = 0; i < pageIndicators.Length; i++)
{
pageIndicators[i].Color = i == currentPage - 1 ? Color.White : Color.Gray;
}
}
int submarineIndex = (currentPage - 1) * submarinesPerPage;
for (int i = 0; i < submarineDisplays.Length; i++)
{
SubmarineInfo subToDisplay = GetSubToDisplay(submarineIndex);
if (subToDisplay == null)
{
submarineDisplays[i].submarineImage.Sprite = null;
submarineDisplays[i].submarineName.Text = string.Empty;
submarineDisplays[i].submarineFee.Text = string.Empty;
submarineDisplays[i].submarineClass.Text = string.Empty;
submarineDisplays[i].selectSubmarineButton.Enabled = false;
submarineDisplays[i].selectSubmarineButton.OnClicked = null;
submarineDisplays[i].displayedSubmarine = null;
submarineDisplays[i].middleTextBlock.AutoDraw = false;
}
else
{
submarineDisplays[i].displayedSubmarine = subToDisplay;
Sprite previewImage = GetPreviewImage(subToDisplay);
if (previewImage != null)
{
submarineDisplays[i].submarineImage.Sprite = previewImage;
submarineDisplays[i].middleTextBlock.AutoDraw = false;
}
else
{
submarineDisplays[i].submarineImage.Sprite = null;
submarineDisplays[i].middleTextBlock.Text = missingPreviewText;
submarineDisplays[i].middleTextBlock.AutoDraw = true;
}
submarineDisplays[i].selectSubmarineButton.Enabled = true;
int index = i;
submarineDisplays[i].selectSubmarineButton.OnClicked = (button, userData) =>
{
SelectSubmarine(subToDisplay, submarineDisplays[index].background.Rect);
return true;
};
submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName;
submarineDisplays[i].submarineClass.Text = $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}"))}";
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
{
string amountString = currencyShorthandText.Replace("[credits]", subToDisplay.Price.ToString());
submarineDisplays[i].submarineFee.Text = priceText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
{
if (subToDisplay.Name != CurrentOrPendingSubmarine().Name)
{
if (deliveryFee > 0)
{
string amountString = currencyShorthandText.Replace("[credits]", deliveryFee.ToString());
submarineDisplays[i].submarineFee.Text = deliveryFeeText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
{
submarineDisplays[i].submarineFee.Text = string.Empty;
}
}
else
{
submarineDisplays[i].submarineFee.Text = currentSubText;
}
}
if (transferService && subToDisplay.Name == CurrentOrPendingSubmarine().Name && updateSubs)
{
if (selectedSubmarine == null)
{
CoroutineManager.StartCoroutine(SelectOwnSubmarineWithDelay(subToDisplay, submarineDisplays[i]));
}
else
{
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
}
}
else if (!transferService && selectedSubmarine == null || !transferService && GameMain.GameSession.IsSubmarineOwned(selectedSubmarine) || subToDisplay == selectedSubmarine)
{
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
}
}
submarineIndex++;
}
if (subsToShow.Count == 0)
{
SelectSubmarine(null, Rectangle.Empty);
}
}
private void UpdateSubmarines()
{
subsToShow.Clear();
if (transferService)
{
subsToShow.AddRange(GameMain.GameSession.OwnedSubmarines);
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
string currentSubName = CurrentOrPendingSubmarine().Name;
int currentIndex = subsToShow.FindIndex(s => s.Name == currentSubName);
if (currentIndex != -1)
{
currentPage = (int)Math.Ceiling((currentIndex + 1) / (float)submarinesPerPage);
}
}
else
{
if (GameMain.Client == null)
{
subsToShow.AddRange(SubmarineInfo.SavedSubmarines.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
else
{
subsToShow.AddRange(GameMain.NetLobbyScreen.CampaignSubmarines.Where(s => !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
}
if (transferService) SetConfirmButtonState(selectedSubmarine != null && selectedSubmarine.Name != CurrentOrPendingSubmarine().Name);
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
pageCount = Math.Max(1, (int)Math.Ceiling(subsToShow.Count / (float)submarinesPerPage));
UpdatePaging();
ContentRefreshRequired = false;
}
private SubmarineInfo GetSubToDisplay(int index)
{
if (subsToShow.Count <= index || index < 0) return null;
return subsToShow[index];
}
private Sprite GetPreviewImage(SubmarineInfo info)
{
Sprite preview = info.PreviewImage;
if (preview == null)
{
SubmarineInfo potentialMatch;
if (GameMain.Client == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
else
{
potentialMatch = GameMain.NetLobbyScreen.CampaignSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
preview = potentialMatch?.PreviewImage;
// Try from savedsubmarines with name comparison as a backup
if (preview == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == info.Name);
preview = potentialMatch?.PreviewImage;
}
}
return preview;
}
// Initial submarine selection needs a slight wait to allow the layoutgroups to place content properly
private IEnumerable<object> SelectOwnSubmarineWithDelay(SubmarineInfo info, SubmarineDisplayContent display)
{
yield return new WaitForSeconds(0.05f);
SelectSubmarine(info, display.background.Rect);
}
// Selection based on key input
private void SelectSubmarine(int index, int direction)
{
SubmarineInfo nextSub = GetSubToDisplay(index + direction);
if (nextSub == null) return;
for (int i = 0; i < submarineDisplays.Length; i++)
{
if (submarineDisplays[i].displayedSubmarine == nextSub)
{
SelectSubmarine(nextSub, submarineDisplays[i].background.Rect);
return;
}
}
ChangePage(direction);
for (int i = 0; i < submarineDisplays.Length; i++)
{
if (submarineDisplays[i].displayedSubmarine == nextSub)
{
SelectSubmarine(nextSub, submarineDisplays[i].background.Rect);
return;
}
}
}
private void SelectSubmarine(SubmarineInfo info, Rectangle backgroundRect)
{
#if !DEBUG
if (selectedSubmarine == info) return;
#endif
specsFrame.Content.ClearChildren();
selectedSubmarine = info;
if (info != null)
{
bool owned = GameMain.GameSession.IsSubmarineOwned(info);
if (owned)
{
confirmButton.Text = deliveryFee > 0 ? deliveryText : switchText;
confirmButton.OnClicked = (button, userData) =>
{
ShowTransferPrompt();
return true;
};
}
else
{
confirmButton.Text = purchaseAndSwitchText;
confirmButton.OnClicked = (button, userData) =>
{
ShowBuyPrompt(false);
return true;
};
confirmButtonAlt.Text = purchaseOnlyText;
confirmButtonAlt.OnClicked = (button, userData) =>
{
ShowBuyPrompt(true);
return true;
};
}
SetConfirmButtonState(selectedSubmarine.Name != CurrentOrPendingSubmarine().Name);
selectedSubmarineIndicator.RectTransform.NonScaledSize = backgroundRect.Size;
selectedSubmarineIndicator.RectTransform.AbsoluteOffset = new Point(backgroundRect.Left - submarineHorizontalGroup.Rect.Left, 0);
Sprite previewImage = GetPreviewImage(info);
listBackground.Sprite = previewImage;
listBackground.SetCrop(true);
ScalableFont font = GUI.Font;
info.CreateSpecsWindow(specsFrame, font);
descriptionTextBlock.Text = info.Description;
descriptionTextBlock.CalculateHeightFromText();
}
else
{
listBackground.Sprite = null;
listBackground.SetCrop(false);
descriptionTextBlock.Text = string.Empty;
selectedSubmarineIndicator.RectTransform.NonScaledSize = Point.Zero;
SetConfirmButtonState(false);
}
}
private void SetConfirmButtonState(bool state)
{
if (confirmButtonAlt != null)
{
confirmButtonAlt.Enabled = state;
}
if (confirmButton != null)
{
confirmButton.Enabled = state;
}
}
public static SubmarineInfo CurrentOrPendingSubmarine()
{
if (GameMain.GameSession?.Campaign?.PendingSubmarineSwitch == null)
{
return Submarine.MainSub.Info;
}
else
{
return GameMain.GameSession.Campaign.PendingSubmarineSwitch;
}
}
private void ChangePage(int pageChangeDirection)
{
SelectSubmarine(null, Rectangle.Empty);
if (pageChangeDirection < 0 && currentPage > 1) currentPage--;
if (pageChangeDirection > 0 && currentPage < pageCount) currentPage++;
browseLeftButton.Enabled = currentPage > 1;
browseRightButton.Enabled = currentPage < pageCount;
RefreshSubmarineDisplay(false);
}
private void ShowTransferPrompt()
{
if (GameMain.GameSession.Campaign.Money < deliveryFee && deliveryFee > 0)
{
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext", notEnoughCreditsDeliveryTextVariables,
new string[] { currencyLongText, selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name }));
return;
}
GUIMessageBox msgBox;
if (deliveryFee > 0)
{
msgBox = new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("deliveryrequesttext", DeliveryTextVariables,
new string[6] { selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name, CurrentOrPendingSubmarine().DisplayName, deliveryFee.ToString(), currencyLongText }), messageBoxOptions);
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext", SwitchTextVariables,
new string[2] { CurrentOrPendingSubmarine().DisplayName, selectedSubmarine.DisplayName }), messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.SwitchSub);
}
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
private void ShowBuyPrompt(bool purchaseOnly)
{
if (GameMain.GameSession.Campaign.Money < selectedSubmarine.Price)
{
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext", notEnoughCreditsPurchaseTextVariables,
new string[2] { currencyLongText, selectedSubmarine.DisplayName }));
return;
}
GUIMessageBox msgBox;
if (!purchaseOnly)
{
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext", PurchaseAndSwitchTextVariables,
new string[4] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText, CurrentOrPendingSubmarine().DisplayName }), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseAndSwitchSub);
}
return true;
};
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext", PurchaseTextVariables,
new string[3] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText }), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseSub);
}
return true;
};
}
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
}
}
@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using Barotrauma.Networking;
using System.Globalization;
namespace Barotrauma
{
@@ -13,7 +14,7 @@ namespace Barotrauma
private static bool initialized = false;
private static UISprite spectateIcon, deadIcon, disconnectedIcon;
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor };
@@ -31,7 +32,7 @@ namespace Barotrauma
private List<Character.TeamType> teamIDs;
private const string inLobbyString = "\u2022 \u2022 \u2022";
private static Color ownCharacterBGColor = Color.Gold * 0.7f;
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
private class LinkedGUI
{
@@ -115,10 +116,9 @@ namespace Barotrauma
public void Initialize()
{
spectateIcon = GUI.Style.GetComponentStyle("SpectateIcon").Sprites[GUIComponent.ComponentState.None][0];
deadIcon = GUI.Style.GetComponentStyle("DeadIcon").Sprites[GUIComponent.ComponentState.None][0];
disconnectedIcon = GUI.Style.GetComponentStyle("DisconnectedIcon").Sprites[GUIComponent.ComponentState.None][0];
ownerIcon = GUI.Style.GetComponentStyle("OwnerIcon").Sprites[GUIComponent.ComponentState.None][0].Sprite;
moderatorIcon = GUI.Style.GetComponentStyle("ModeratorIcon").Sprites[GUIComponent.ComponentState.None][0].Sprite;
ownerIcon = GUI.Style.GetComponentStyle("OwnerIcon").GetDefaultSprite();
moderatorIcon = GUI.Style.GetComponentStyle("ModeratorIcon").GetDefaultSprite();
initialized = true;
}
@@ -279,7 +279,7 @@ namespace Barotrauma
teamIDs = crew.Select(c => c.TeamID).Distinct().ToList();
// Show own team first when there's more than one team
if (teamIDs.Count > 1 && GameMain.Client.Character != null)
if (teamIDs.Count > 1 && GameMain.Client?.Character != null)
{
Character.TeamType ownTeam = GameMain.Client.Character.TeamID;
teamIDs = teamIDs.OrderBy(i => i != ownTeam).ThenBy(i => i).ToList();
@@ -408,7 +408,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
{
UserData = character,
Color = (Character.Controlled == character) ? ownCharacterBGColor : Color.Transparent
Color = (Character.Controlled == character) ? OwnCharacterBGColor : Color.Transparent
};
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
@@ -486,7 +486,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
{
UserData = character,
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? ownCharacterBGColor : Color.Transparent
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? OwnCharacterBGColor : Color.Transparent
};
frame.OnSecondaryClicked += (component, data) =>
@@ -631,7 +631,7 @@ namespace Barotrauma
{
if (GameMain.NetworkMember == null || client == null || !client.HasPermissions) return null;
if (!client.AllowKicking) // Owner cannot be kicked
if (client.IsOwner) // Owner cannot be kicked
{
return ownerIcon;
}
@@ -649,7 +649,10 @@ namespace Barotrauma
}
else if (client.Character != null && client.Character.IsDead)
{
client.Character.Info.DrawJobIcon(spriteBatch, area);
if (client.Character.Info != null)
{
client.Character.Info.DrawJobIcon(spriteBatch, area);
}
}
else
{
@@ -780,7 +783,7 @@ namespace Barotrauma
public static void StorePlayerConnectionChangeMessage(ChatMessage message)
{
if (!GameMain.GameSession?.GameMode?.IsRunning ?? true) { return; }
if (!GameMain.GameSession?.IsRunning ?? true) { return; }
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
storedMessages.Add(new Pair<string, PlayerConnectionChangeType>(msg, message.ChangeType));
@@ -847,15 +850,15 @@ namespace Barotrauma
infoFrame.ClearChildren();
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = (int)(0.0245f * missionFrame.Rect.Height);
Location endLocation = GameMain.GameSession.EndLocation;
Sprite portrait = endLocation.Type.GetPortrait(endLocation.PortraitId);
Location location = GameMain.GameSession.EndLocation != null ? GameMain.GameSession.EndLocation : GameMain.GameSession.StartLocation;
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
int contentWidth = hasPortrait ? (int)(missionFrame.Rect.Width * 0.951f) : missionFrame.Rect.Width - padding * 2;
Vector2 locationNameSize = GUI.LargeFont.MeasureString(endLocation.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(endLocation.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, endLocation.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, endLocation.Type.Name, font: GUI.SubHeadingFont);
Vector2 locationNameSize = GUI.LargeFont.MeasureString(location.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(location.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, location.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, location.Type.Name, font: GUI.SubHeadingFont);
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
@@ -881,7 +884,8 @@ namespace Barotrauma
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", mission.Reward.ToString()), 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);
@@ -890,13 +894,15 @@ namespace Barotrauma
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);
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 };
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 };
}
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);
@@ -36,6 +36,12 @@ namespace Barotrauma
get;
private set;
}
public bool MaintainBorderAspectRatio
{
get;
private set;
}
/// <summary>
/// How much the borders of a sliced sprite are allowed to scale
@@ -52,6 +58,7 @@ namespace Barotrauma
{
Sprite = new Sprite(element);
MaintainAspectRatio = element.GetAttributeBool("maintainaspectratio", false);
MaintainBorderAspectRatio = element.GetAttributeBool("maintainborderaspectratio", false);
Tile = element.GetAttributeBool("tile", true);
CrossFadeIn = element.GetAttributeBool("crossfadein", CrossFadeIn);
CrossFadeOut = element.GetAttributeBool("crossfadeout", CrossFadeOut);
@@ -120,13 +127,16 @@ namespace Barotrauma
{
Vector2 pos = new Vector2(rect.X, rect.Y);
float scale = GetSliceBorderScale(rect.Size);
float scale = MaintainBorderAspectRatio ? 1.0f : GetSliceBorderScale(rect.Size);
float aspectScale = MaintainBorderAspectRatio ? Math.Min((float)rect.Width / Sprite.SourceRect.Width, (float)rect.Height / Sprite.SourceRect.Height) : 1.0f;
int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale);
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale);
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale * aspectScale);
for (int x = 0; x < 3; x++)
{
int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale);
int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale * aspectScale);
if (width <= 0) { continue; }
for (int y = 0; y < 3; y++)
{
@@ -147,7 +157,7 @@ namespace Barotrauma
else if (Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), null, color);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color);
}
else
{
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
class VotingInterface
{
public bool VoteRunning = false;
private GUIFrame frame;
private GUITextBlock votingTextBlock, votedTextBlock, voteCounter;
private GUIProgressBar votingTimer;
private GUIButton yesVoteButton, noVoteButton;
private Action onVoteEnd;
private int yesVotes, noVotes, maxVotes;
private Func<int> getYesVotes, getNoVotes, getMaxVotes;
private bool votePassed;
private string votingOnText;
private List<RichTextData> votingOnTextData;
private float votingTime = 100f;
private float timer;
private VoteType currentVoteType;
private Color submarineColor => GUI.Style.Orange;
private Point createdForResolution;
public VotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
{
if (starter == null || info == null) return;
SetSubmarineVotingText(starter, info, type);
this.votingTime = votingTime;
getYesVotes = SubmarineYesVotes;
getNoVotes = SubmarineNoVotes;
getMaxVotes = SubmarineMaxVotes;
onVoteEnd = () => SendSubmarineVoteEndMessage(info, type);
Initialize(starter, type);
}
private void Initialize(Client starter, VoteType type)
{
currentVoteType = type;
CreateVotingGUI();
if (starter.ID == GameMain.Client.ID) SetGUIToVotedState(2);
VoteRunning = true;
}
private void CreateVotingGUI()
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
if (frame != null) frame.Parent.RemoveChild(frame);
frame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.VotingArea, GameMain.Client.InGameHUD.RectTransform), style: "");
int padding = HUDLayoutSettings.Padding * 2;
int spacing = HUDLayoutSettings.Padding;
int yOffset = padding;
int paddedWidth = frame.Rect.Width - padding * 2;
votingTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), votingOnTextData, votingOnText, wrap: true);
votingTextBlock.RectTransform.NonScaledSize = votingTextBlock.RectTransform.MinSize = votingTextBlock.RectTransform.MaxSize = new Point(votingTextBlock.Rect.Width, votingTextBlock.Rect.Height);
votingTextBlock.RectTransform.IsFixedSize = true;
votingTextBlock.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
yOffset += votingTextBlock.Rect.Height + spacing;
voteCounter = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), "(0/0)", GUI.Style.Green, textAlignment: Alignment.Center);
voteCounter.RectTransform.NonScaledSize = voteCounter.RectTransform.MinSize = voteCounter.RectTransform.MaxSize = new Point(voteCounter.Rect.Width, voteCounter.Rect.Height);
voteCounter.RectTransform.IsFixedSize = true;
voteCounter.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
yOffset += voteCounter.Rect.Height + spacing;
votingTimer = new GUIProgressBar(new RectTransform(new Point(paddedWidth, Math.Max(spacing, 8)), frame.RectTransform) { AbsoluteOffset = new Point(padding, yOffset) }, HUDLayoutSettings.Padding);
votingTimer.RectTransform.IsFixedSize = true;
yOffset += votingTimer.Rect.Height + spacing;
int buttonWidth = (int)(paddedWidth * 0.3f);
yesVoteButton = new GUIButton(new RectTransform(new Point(buttonWidth, 0), frame.RectTransform) { AbsoluteOffset = new Point((int)(frame.Rect.Width / 2f - buttonWidth - spacing), yOffset) }, TextManager.Get("yes"))
{
OnClicked = (applyButton, obj) =>
{
SetGUIToVotedState(2);
GameMain.Client.Vote(currentVoteType, 2);
return true;
}
};
noVoteButton = new GUIButton(new RectTransform(new Point(buttonWidth, 0), frame.RectTransform) { AbsoluteOffset = new Point(yesVoteButton.RectTransform.AbsoluteOffset.X + yesVoteButton.Rect.Width + padding, yOffset) }, TextManager.Get("no"))
{
OnClicked = (applyButton, obj) =>
{
SetGUIToVotedState(1);
GameMain.Client.Vote(currentVoteType, 1);
return true;
}
};
votedTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, yesVoteButton.Rect.Height), frame.RectTransform), string.Empty, textAlignment: Alignment.Center);
votedTextBlock.RectTransform.IsFixedSize = true;
votedTextBlock.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
votedTextBlock.Visible = false;
yOffset += yesVoteButton.Rect.Height;
frame.RectTransform.NonScaledSize = new Point(frame.Rect.Width, yOffset + padding);
}
private void SetGUIToVotedState(int vote)
{
yesVoteButton.Visible = noVoteButton.Visible = false;
votedTextBlock.Text = TextManager.Get(vote == 2 ? "yesvoted" : "novoted");
votedTextBlock.Visible = true;
}
public void Update(float deltaTime)
{
if (!VoteRunning) return;
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateVotingGUI();
yesVotes = getYesVotes();
noVotes = getNoVotes();
maxVotes = getMaxVotes();
voteCounter.Text = $"({yesVotes + noVotes}/{maxVotes})";
timer += deltaTime;
votingTimer.BarSize = timer / votingTime;
}
public void EndVote(bool passed, int yesVoteFinal, int noVoteFinal)
{
VoteRunning = false;
votePassed = passed;
yesVotes = yesVoteFinal;
noVotes = noVoteFinal;
onVoteEnd?.Invoke();
}
#region Submarine Voting
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, VoteType type)
{
string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
string submarineRichString = $"‖color:{submarineColor.R},{submarineColor.G},{submarineColor.B}‖{info.DisplayName}‖color:end‖";
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
votingOnText = TextManager.GetWithVariables("submarinepurchaseandswitchvote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
break;
case VoteType.PurchaseSub:
votingOnText = TextManager.GetWithVariables("submarinepurchasevote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
votingOnText = TextManager.GetWithVariables("submarineswitchfeevote", new string[] { "[playername]", "[submarinename]", "[locationname]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower() });
}
else
{
votingOnText = TextManager.GetWithVariables("submarineswitchnofeevote", new string[] { "[playername]", "[submarinename]" }, new string[] { characterRichString, submarineRichString });
}
break;
}
votingOnTextData = RichTextData.GetRichTextData(votingOnText, out votingOnText);
}
private int SubmarineYesVotes()
{
return GameMain.NetworkMember.SubmarineVoteYesCount;
}
private int SubmarineNoVotes()
{
return GameMain.NetworkMember.SubmarineVoteNoCount;
}
private int SubmarineMaxVotes()
{
return GameMain.NetworkMember.SubmarineVoteMax;
}
private void SendSubmarineVoteEndMessage(SubmarineInfo info, VoteType type)
{
GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes.ToString(), noVotes.ToString(), votePassed), ChatMessageType.Server);
}
public static string GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, string yesVoteString, string noVoteString, bool votePassed)
{
string result = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
break;
case VoteType.PurchaseSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchfeevotepassed" : "submarineswitchfeevotefailed", new string[] { "[submarinename]", "[locationname]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
}
else
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed", new string[] { "[submarinename]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, yesVoteString, noVoteString });
}
break;
default:
break;
}
return result;
}
#endregion
public void Remove()
{
if (frame != null)
{
frame.Parent.RemoveChild(frame);
frame = null;
}
}
}
}