1012 lines
49 KiB
C#
1012 lines
49 KiB
C#
using Barotrauma.Extensions;
|
|
using Barotrauma.Networking;
|
|
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
|
|
|
|
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 PlayerBalanceElement? playerBalanceElement;
|
|
|
|
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
|
|
private bool HasPermission => CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageHires);
|
|
|
|
private Point resolutionWhenCreated;
|
|
|
|
private bool needsHireableRefresh;
|
|
|
|
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.RegisterOverwriteExisting(
|
|
"CrewManagement.UpdateLocationView".ToIdentifier(),
|
|
(locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation, true, locationChangeInfo.PrevLocation));
|
|
Reputation.OnAnyReputationValueChanged.RegisterOverwriteExisting(
|
|
"CrewManagement.UpdateLocationView".ToIdentifier(), _ => needsHireableRefresh = true);
|
|
}
|
|
|
|
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)
|
|
{
|
|
CharacterInfo characterInfo = buyButton.UserData as CharacterInfo;
|
|
bool enoughReputationToHire = EnoughReputationToHire(characterInfo);
|
|
buyButton.Enabled = HasPermission && enoughReputationToHire;
|
|
foreach (GUITextBlock text in child.GetAllChildren<GUITextBlock>())
|
|
{
|
|
text.TextColor = new Color(text.TextColor, buyButton.Enabled ? 1.0f : 0.6f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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"
|
|
};
|
|
|
|
int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
|
|
var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
|
|
{
|
|
MaxSize = new Point(panelMaxWidth, 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: GUIStyle.LargeFont)
|
|
{
|
|
CanBeFocused = false,
|
|
ForceUpperCase = ForceUpperCase.Yes
|
|
};
|
|
|
|
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,
|
|
Stretch = true
|
|
};
|
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sortGroup.RectTransform), text: TextManager.Get("campaignstore.sortby"));
|
|
sortingDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 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(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
|
})
|
|
{
|
|
Stretch = true,
|
|
RelativeSpacing = 0.02f
|
|
};
|
|
|
|
playerBalanceElement = CampaignUI.AddBalanceElement(pendingAndCrewMainGroup, new Vector2(1.0f, 0.75f / 14.0f));
|
|
|
|
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(panelMaxWidth, 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: GUIStyle.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: GUIStyle.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: GUIStyle.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(0.4f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
|
|
{
|
|
ClickSound = GUISoundType.ConfirmTransaction,
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
OnClicked = (b, o) => ValidateHires(PendingHires, true)
|
|
};
|
|
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
|
|
{
|
|
ClickSound = GUISoundType.Cart,
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
Enabled = HasPermission,
|
|
OnClicked = (b, o) => RemoveAllPendingHires()
|
|
};
|
|
GUITextBlock.AutoScaleAndNormalize(validateHiresButton.TextBlock, clearAllButton.TextBlock);
|
|
|
|
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) =>
|
|
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
|
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name));
|
|
}
|
|
else if (sortingMethod == SortingMethod.JobAsc)
|
|
{
|
|
SortCharacters(list, SortingMethod.AlphabeticalAsc);
|
|
list.Content.RectTransform.SortChildren((x, y) =>
|
|
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
|
string.Compare(((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Job.Name.Value, ((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Job.Name.Value, StringComparison.Ordinal));
|
|
}
|
|
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
|
|
{
|
|
SortCharacters(list, SortingMethod.AlphabeticalAsc);
|
|
list.Content.RectTransform.SortChildren((x, y) =>
|
|
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
|
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.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) =>
|
|
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
|
((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
|
|
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
|
|
}
|
|
|
|
int? CompareReputationRequirement(GUIComponent c1, GUIComponent c2)
|
|
{
|
|
CharacterInfo info1 = ((InfoSkill)c1.UserData).CharacterInfo;
|
|
CharacterInfo info2 = ((InfoSkill)c2.UserData).CharacterInfo;
|
|
float requirement1 = EnoughReputationToHire(info1) ? 0 : info1.MinReputationToHire.reputation;
|
|
float requirement2 = EnoughReputationToHire(info2) ? 0 : info2.MinReputationToHire.reputation;
|
|
if (MathUtils.NearlyEqual(requirement1, 0.0f) && MathUtils.NearlyEqual(requirement2, 0.0f))
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return requirement1.CompareTo(requirement2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly struct InfoSkill
|
|
{
|
|
public readonly CharacterInfo CharacterInfo;
|
|
public readonly float SkillLevel;
|
|
|
|
public InfoSkill(CharacterInfo characterInfo, float skillLevel)
|
|
{
|
|
CharacterInfo = characterInfo;
|
|
SkillLevel = skillLevel;
|
|
}
|
|
}
|
|
|
|
private void CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox)
|
|
{
|
|
Skill skill = null;
|
|
Color? jobColor = null;
|
|
if (characterInfo.Job != null)
|
|
{
|
|
skill = characterInfo.Job?.PrimarySkill ?? characterInfo.Job.GetSkills().OrderByDescending(s => s.Level).FirstOrDefault();
|
|
jobColor = characterInfo.Job.Prefab.UIColor;
|
|
}
|
|
|
|
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
|
|
{
|
|
UserData = new InfoSkill(characterInfo, skill?.Level ?? 0.0f)
|
|
};
|
|
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
|
{
|
|
Stretch = true
|
|
};
|
|
|
|
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
|
|
var icon = new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
|
|
onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
|
|
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
|
|
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
|
|
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
|
|
textColor: jobColor, textAlignment: Alignment.BottomLeft)
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
|
|
|
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
|
characterInfo.Title ?? characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
if (!characterInfo.MinReputationToHire.factionId.IsEmpty)
|
|
{
|
|
var faction = campaign.Factions.Find(f => f.Prefab.Identifier == characterInfo.MinReputationToHire.factionId);
|
|
if (faction != null)
|
|
{
|
|
jobBlock.TextColor = faction.Prefab.IconColor;
|
|
}
|
|
}
|
|
var fullJobText = jobBlock.Text;
|
|
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
|
if (jobBlock.Text != fullJobText)
|
|
{
|
|
jobBlock.ToolTip = fullJobText;
|
|
jobBlock.CanBeFocused = true;
|
|
}
|
|
float width = 0.6f / 3;
|
|
if (characterInfo.Job != null && skill != null)
|
|
{
|
|
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
|
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
|
|
GUIImage skillIcon = new GUIImage(new RectTransform(Vector2.One, skillGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), skill.Icon, scaleToFit: true)
|
|
{
|
|
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),
|
|
TextManager.FormatCurrency(characterInfo.Salary),
|
|
textAlignment: Alignment.Center)
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
}
|
|
else
|
|
{
|
|
// Just a bit of padding to make list layouts similar
|
|
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
|
}
|
|
|
|
if (listBox == hireableList)
|
|
{
|
|
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
|
{
|
|
ClickSound = GUISoundType.Cart,
|
|
UserData = characterInfo,
|
|
Enabled = CanHire(characterInfo),
|
|
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
|
|
};
|
|
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
|
{
|
|
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
|
{
|
|
if (btn.Enabled)
|
|
{
|
|
btn.ToolTip = TextManager.Get("canthiremorecharacters");
|
|
btn.Enabled = false;
|
|
}
|
|
}
|
|
else if (!btn.Enabled)
|
|
{
|
|
btn.ToolTip = string.Empty;
|
|
btn.Enabled = CanHire(characterInfo);
|
|
}
|
|
};
|
|
}
|
|
else if (listBox == pendingList)
|
|
{
|
|
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
|
|
{
|
|
ClickSound = GUISoundType.Cart,
|
|
UserData = characterInfo,
|
|
Enabled = CanHire(characterInfo),
|
|
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 LocalizedString[] { 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;
|
|
}
|
|
};
|
|
}
|
|
|
|
if (listBox == pendingList || listBox == crewList)
|
|
{
|
|
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
|
|
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
|
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
|
|
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
|
|
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
|
|
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
|
|
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
|
|
{
|
|
Enabled = CanHire(characterInfo),
|
|
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", PlayerInput.PrimaryMouseLabel),
|
|
UserData = characterInfo,
|
|
OnClicked = CreateRenamingComponent
|
|
};
|
|
}
|
|
|
|
bool CanHire(CharacterInfo characterInfo)
|
|
{
|
|
if (!HasPermission) { return false; }
|
|
return EnoughReputationToHire(characterInfo);
|
|
}
|
|
}
|
|
|
|
private bool EnoughReputationToHire(CharacterInfo characterInfo)
|
|
{
|
|
if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
|
|
{
|
|
if (MathF.Round(campaign.GetReputation(characterInfo.MinReputationToHire.factionId)) < characterInfo.MinReputationToHire.reputation)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
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);
|
|
Point frameSize = new Point(GUI.IntScale(300), GUI.IntScale(350));
|
|
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize.Y) < 0)
|
|
{
|
|
pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight;
|
|
absoluteOffset.Y = characterFrame.Rect.Bottom;
|
|
}
|
|
characterPreviewFrame = new GUIFrame(new RectTransform(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,
|
|
Stretch = true
|
|
};
|
|
|
|
// 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"));
|
|
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), "");
|
|
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
|
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
|
|
|
|
if (characterInfo.HasSpecifierTags)
|
|
{
|
|
var menuCategoryVar = characterInfo.Prefab.MenuCategoryVar;
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar));
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.ReplaceVars($"[{menuCategoryVar}]")));
|
|
}
|
|
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), trait.DisplayName);
|
|
}
|
|
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));
|
|
var characterSkills = characterInfo.Job.GetSkills();
|
|
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), font: GUIStyle.SmallFont);
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillLevelGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.Right);
|
|
}
|
|
|
|
if (characterInfo.MinReputationToHire.reputation > 0.0f)
|
|
{
|
|
var repStr = TextManager.GetWithVariables(
|
|
"campaignstore.reputationrequired",
|
|
("[amount]", ((int)characterInfo.MinReputationToHire.reputation).ToString()),
|
|
("[faction]", TextManager.Get("faction." + characterInfo.MinReputationToHire.factionId).Value));
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), mainGroup.RectTransform),
|
|
repStr, textColor: !EnoughReputationToHire(characterInfo) ? GUIStyle.Orange : GUIStyle.Green,
|
|
font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.Center);
|
|
}
|
|
mainGroup.Recalculate();
|
|
characterPreviewFrame.RectTransform.MinSize =
|
|
new Point(0, (int)(mainGroup.Children.Sum(c => c.Rect.Height + mainGroup.Rect.Height * mainGroup.RelativeSpacing) / mainGroup.RectTransform.RelativeSize.Y));
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (PendingHires.Count + campaign.CrewManager.GetCharacters().Count() >= CrewManager.MaxCrewSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == 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 => ((InfoSkill)c.UserData).CharacterInfo == characterInfo));
|
|
pendingList.UpdateScrollBarSize();
|
|
|
|
// Server will reset the names to originals in multiplayer
|
|
if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
|
|
|
|
if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) &&
|
|
hireableList.Content.Children.None(c => c.UserData is InfoSkill userData && userData.CharacterInfo.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
|
|
{
|
|
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(((InfoSkill)c.UserData).CharacterInfo, 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 += ((InfoSkill)c.UserData).CharacterInfo.Salary;
|
|
});
|
|
totalBlock.Text = TextManager.FormatCurrency(total);
|
|
bool enoughMoney = campaign == null || campaign.CanAfford(total);
|
|
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
|
|
validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
|
|
}
|
|
|
|
public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
|
|
{
|
|
if (hires == null || hires.None()) { return false; }
|
|
|
|
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
|
|
hires.ForEach(hireInfo =>
|
|
{
|
|
if (campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
|
{
|
|
nonDuplicateHires.Add(hireInfo);
|
|
}
|
|
});
|
|
|
|
if (nonDuplicateHires.None()) { return false; }
|
|
|
|
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
|
|
|
|
if (!campaign.CanAfford(total)) { return false; }
|
|
|
|
bool atLeastOneHired = false;
|
|
foreach (CharacterInfo ci in nonDuplicateHires)
|
|
{
|
|
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
|
|
{
|
|
atLeastOneHired = true;
|
|
}
|
|
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 LocalizedString[] { TextManager.Get("Ok") });
|
|
dialog.Buttons[0].OnClicked += dialog.Close;
|
|
}
|
|
|
|
if (createNetworkEvent)
|
|
{
|
|
SendCrewState(true, validateHires: true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool CreateRenamingComponent(GUIButton button, object userData)
|
|
{
|
|
if (!HasPermission || userData is not CharacterInfo characterInfo) { return false; }
|
|
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
|
|
style: "OuterGlow", color: Color.Black * 0.7f);
|
|
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
|
|
{
|
|
MaxSize = new Point(400, 300).Multiply(GUI.Scale)
|
|
});
|
|
var layoutGroup = new GUILayoutGroup(new RectTransform((frame.Rect.Size - GUIStyle.ItemFrameMargin).Multiply(new Vector2(0.75f, 1.0f)), frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
|
{
|
|
RelativeSpacing = 0.02f,
|
|
Stretch = true
|
|
};
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
|
var groupElementSize = new Vector2(1.0f, 0.25f);
|
|
var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform))
|
|
{
|
|
MaxTextLength = Client.MaxNameLength
|
|
};
|
|
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm"))
|
|
{
|
|
OnClicked = (button, userData) =>
|
|
{
|
|
if (RenameCharacter(characterInfo, nameBox.Text?.Trim()))
|
|
{
|
|
parentComponent.RemoveChild(outerGlowFrame);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
nameBox.Flash(color: Color.Red);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
};
|
|
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("cancel"))
|
|
{
|
|
OnClicked = (button, userData) =>
|
|
{
|
|
parentComponent.RemoveChild(outerGlowFrame);
|
|
return true;
|
|
}
|
|
};
|
|
layoutGroup.Recalculate();
|
|
return true;
|
|
}
|
|
|
|
public bool RenameCharacter(CharacterInfo characterInfo, string newName)
|
|
{
|
|
if (characterInfo == null || string.IsNullOrEmpty(newName)) { return false; }
|
|
if (newName == characterInfo.Name) { return false; }
|
|
if (GameMain.IsMultiplayer)
|
|
{
|
|
SendCrewState(false, renameCharacter: (characterInfo, newName));
|
|
}
|
|
else
|
|
{
|
|
var crewComponent = crewList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo);
|
|
if (crewComponent != null)
|
|
{
|
|
crewList.Content.RemoveChild(crewComponent);
|
|
campaign.CrewManager.RenameCharacter(characterInfo, newName);
|
|
CreateCharacterFrame(characterInfo, crewList);
|
|
SortCharacters(crewList, SortingMethod.JobAsc);
|
|
}
|
|
else
|
|
{
|
|
var pendingComponent = pendingList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo);
|
|
if (pendingComponent != null)
|
|
{
|
|
pendingList.Content.RemoveChild(pendingComponent);
|
|
campaign.Map.CurrentLocation.HireManager.RenameCharacter(characterInfo, newName);
|
|
CreateCharacterFrame(characterInfo, pendingList);
|
|
SortCharacters(pendingList, SortingMethod.JobAsc);
|
|
SetTotalHireCost();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool FireCharacter(GUIButton button, object selection)
|
|
{
|
|
if (selection is not 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);
|
|
}
|
|
else
|
|
{
|
|
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
|
|
}
|
|
|
|
if (needsHireableRefresh)
|
|
{
|
|
RefreshCrewFrames(hireableList);
|
|
if (sortingDropDown?.SelectedItemData != null)
|
|
{
|
|
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
|
|
}
|
|
needsHireableRefresh = false;
|
|
}
|
|
|
|
(GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
|
|
if (highlightedFrame != null && highlightedInfo != null)
|
|
{
|
|
if (characterPreviewFrame == null || highlightedInfo != 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, highlightedFrame as GUIFrame, highlightedInfo);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
static (GUIComponent GuiComponent, CharacterInfo CharacterInfo) FindHighlightedCharacter(GUIComponent c)
|
|
{
|
|
if (c == null)
|
|
{
|
|
return default;
|
|
}
|
|
if (c.UserData is InfoSkill highlightedData)
|
|
{
|
|
return (c, highlightedData.CharacterInfo);
|
|
}
|
|
if (c.Parent != null)
|
|
{
|
|
if (c.Parent is GUIListBox)
|
|
{
|
|
return default;
|
|
}
|
|
return FindHighlightedCharacter(c.Parent);
|
|
}
|
|
return default;
|
|
}
|
|
}
|
|
|
|
public void SetPendingHires(List<int> characterInfos, Location location)
|
|
{
|
|
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.GetIdentifierUsingOriginalName() == 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="renameCharacter">When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew.</param>
|
|
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
|
|
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
|
|
public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
|
{
|
|
if (campaign is MultiPlayerCampaign)
|
|
{
|
|
IWriteMessage msg = new WriteOnlyMessage();
|
|
msg.WriteByte((byte)ClientPacketHeader.CREW);
|
|
|
|
msg.WriteBoolean(updatePending);
|
|
if (updatePending)
|
|
{
|
|
msg.WriteUInt16((ushort)PendingHires.Count);
|
|
foreach (CharacterInfo pendingHire in PendingHires)
|
|
{
|
|
msg.WriteInt32(pendingHire.GetIdentifierUsingOriginalName());
|
|
}
|
|
}
|
|
|
|
msg.WriteBoolean(validateHires);
|
|
|
|
bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName);
|
|
msg.WriteBoolean(validRenaming);
|
|
if (validRenaming)
|
|
{
|
|
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
|
|
msg.WriteInt32(identifier);
|
|
msg.WriteString(renameCharacter.newName);
|
|
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
|
|
msg.WriteBoolean(existingCrewMember);
|
|
}
|
|
|
|
msg.WriteBoolean(firedCharacter != null);
|
|
if (firedCharacter != null)
|
|
{
|
|
msg.WriteInt32(firedCharacter.GetIdentifier());
|
|
}
|
|
|
|
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
|
}
|
|
}
|
|
}
|
|
}
|