3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
18 private GUIListBox hireableList, pendingList, crewList;
19 private GUIFrame characterPreviewFrame;
25 private PlayerBalanceElement? playerBalanceElement;
37 private Point resolutionWhenCreated;
39 private bool needsHireableRefresh;
41 private enum SortingMethod
53 this.campaignUI = campaignUI;
54 this.parentComponent = parentComponent;
60 "CrewManagement.UpdateLocationView".ToIdentifier(),
61 (locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation,
true, locationChangeInfo.PrevLocation));
63 "CrewManagement.UpdateLocationView".ToIdentifier(), _ => needsHireableRefresh =
true);
68 RefreshCrewFrames(hireableList);
69 RefreshCrewFrames(crewList);
70 RefreshCrewFrames(pendingList);
71 if (clearAllButton !=
null) { clearAllButton.
Enabled = HasPermissionToHire; }
74 private void RefreshCrewFrames(
GUIListBox listBox)
76 if (listBox ==
null) {
return; }
80 if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo,
true) is GUIButton buyButton)
82 CharacterInfo characterInfo = buyButton.UserData as CharacterInfo;
83 bool enougMoneyToHire = !HiringNewCharacter || campaign.
CanAfford(HireManager.GetSalaryFor(characterInfo));
84 buyButton.Enabled = HasPermissionToHire && EnoughReputationToHire(characterInfo) && enougMoneyToHire;
85 foreach (GUITextBlock text
in child.GetAllChildren<GUITextBlock>())
87 text.TextColor =
new Color(text.TextColor, buyButton.Enabled ? 1.0f : 0.6f);
93 private void CreateUI()
95 if (parentComponent.FindChild(c => c.UserData as
string ==
"glow") is GUIComponent glowChild)
97 parentComponent.RemoveChild(glowChild);
99 if (parentComponent.FindChild(c => c.UserData as
string ==
"container") is GUIComponent containerChild)
101 parentComponent.RemoveChild(containerChild);
104 new GUIFrame(
new RectTransform(
new Vector2(1.25f, 1.25f), parentComponent.RectTransform,
Anchor.Center),
105 style:
"OuterGlow", color: Color.Black * 0.7f)
109 new GUIFrame(
new RectTransform(
new Vector2(0.95f), parentComponent.RectTransform, anchor:
Anchor.Center),
112 CanBeFocused =
false,
113 UserData =
"container"
116 int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
117 var availableMainGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
119 MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
123 RelativeSpacing = 0.02f
127 var headerGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.75f / 14.0f), availableMainGroup.RectTransform), isHorizontal:
true)
129 RelativeSpacing = 0.005f
131 var imageWidth = (float)headerGroup.Rect.Height / headerGroup.Rect.Width;
132 new GUIImage(
new RectTransform(
new Vector2(imageWidth, 1.0f), headerGroup.RectTransform),
"CrewManagementHeaderIcon");
133 new GUITextBlock(
new RectTransform(
new Vector2(1.0f - imageWidth, 1.0f), headerGroup.RectTransform), TextManager.Get(
"campaigncrew.header"), font: GUIStyle.LargeFont)
135 CanBeFocused =
false,
139 var hireablesGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.95f), anchor:
Anchor.Center,
140 parent:
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 13.25f / 14.0f), availableMainGroup.RectTransform)).RectTransform))
142 RelativeSpacing = 0.015f,
146 var sortGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.04f), hireablesGroup.RectTransform), isHorizontal:
true)
148 RelativeSpacing = 0.015f,
151 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1.0f), sortGroup.RectTransform), text: TextManager.Get(
"campaignstore.sortby"));
152 sortingDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1.0f), sortGroup.RectTransform), elementCount: 5)
154 OnSelected = (child, userData) =>
156 SortCharacters(hireableList, (SortingMethod)userData);
160 var tag =
"sortingmethod.";
161 sortingDropDown.
AddItem(TextManager.Get(tag + SortingMethod.JobAsc), userData: SortingMethod.JobAsc);
162 sortingDropDown.
AddItem(TextManager.Get(tag + SortingMethod.SkillAsc), userData: SortingMethod.SkillAsc);
163 sortingDropDown.
AddItem(TextManager.Get(tag + SortingMethod.SkillDesc), userData: SortingMethod.SkillDesc);
164 sortingDropDown.
AddItem(TextManager.Get(tag + SortingMethod.PriceAsc), userData: SortingMethod.PriceAsc);
165 sortingDropDown.
AddItem(TextManager.Get(tag + SortingMethod.PriceDesc), userData: SortingMethod.PriceDesc);
167 hireableList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.96f),
168 hireablesGroup.RectTransform,
174 var pendingAndCrewMainGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor:
Anchor.TopRight)
176 MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
180 RelativeSpacing = 0.02f
183 playerBalanceElement = CampaignUI.AddBalanceElement(pendingAndCrewMainGroup,
new Vector2(1.0f, 0.75f / 14.0f));
185 pendingAndCrewGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.95f), anchor:
Anchor.Center,
186 parent:
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
188 MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
191 float height = 0.05f;
192 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, height), pendingAndCrewGroup.
RectTransform), TextManager.Get(
"campaigncrew.pending"), font: GUIStyle.SubHeadingFont);
193 pendingList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 8 * height), pendingAndCrewGroup.
RectTransform))
198 var crewHeader =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, height), pendingAndCrewGroup.
RectTransform), TextManager.Get(
"campaignmenucrew"), font: GUIStyle.SubHeadingFont);
200 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1.0f), crewHeader.RectTransform,
Anchor.CenterRight),
string.Empty, textAlignment: Alignment.CenterRight)
205 return $
"{crewSize}/{CrewManager.MaxCrewSize}";
208 crewList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 8 * height), pendingAndCrewGroup.
RectTransform))
213 var group =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, height), pendingAndCrewGroup.
RectTransform), isHorizontal:
true);
214 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1.0f), group.RectTransform), TextManager.Get(
"campaignstore.total"));
215 totalBlock =
new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1.0f), group.RectTransform),
"", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right)
219 group =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, height), pendingAndCrewGroup.
RectTransform), isHorizontal:
true, childAnchor:
Anchor.TopRight)
221 RelativeSpacing = 0.01f
223 validateHiresButton =
new GUIButton(
new RectTransform(
new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get(
"campaigncrew.validate"))
227 OnClicked = (b, o) =>
ValidateHires(PendingHires, createNetworkEvent:
true)
229 clearAllButton =
new GUIButton(
new RectTransform(
new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get(
"campaignstore.clearall"))
233 Enabled = HasPermissionToHire,
234 OnClicked = (b, o) => RemoveAllPendingHires()
236 GUITextBlock.AutoScaleAndNormalize(validateHiresButton.
TextBlock, clearAllButton.
TextBlock);
238 resolutionWhenCreated =
new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
241 private void UpdateLocationView(Location location,
bool removePending, Location prevLocation =
null)
243 if (prevLocation !=
null && prevLocation == location && GameMain.NetworkMember !=
null) {
return; }
245 if (characterPreviewFrame !=
null)
248 characterPreviewFrame =
null;
251 if (pendingList !=
null)
255 PendingHires?.Clear();
260 PendingHires?.ForEach(ci => AddPendingHire(ci));
274 if (hireableList ==
null) {
return; }
277 if (hireableCharacters.None())
279 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.2f), hireableList.
Content.
RectTransform), TextManager.Get(
"HireUnavailable"), textAlignment: Alignment.Center)
286 foreach (CharacterInfo c
in hireableCharacters)
288 if (c ==
null || PendingHires.Contains(c)) {
continue; }
292 sortingDropDown.SelectItem(SortingMethod.JobAsc);
299 if (hireManager ==
null) {
return; }
300 int hireVal = hireManager.
AvailableCharacters.Aggregate(0, (curr, hire) => curr + hire.ID);
301 int newVal = availableHires.Aggregate(0, (curr, hire) => curr + hire.ID);
302 if (hireVal != newVal)
317 SortCharacters(crewList, SortingMethod.JobAsc);
321 private void SortCharacters(
GUIListBox list, SortingMethod sortingMethod)
323 if (sortingMethod == SortingMethod.AlphabeticalAsc)
326 CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
327 ((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name));
329 else if (sortingMethod == SortingMethod.JobAsc)
331 SortCharacters(list, SortingMethod.AlphabeticalAsc);
333 CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
334 string.Compare(((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Job.Name.Value, ((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Job.Name.Value, StringComparison.Ordinal));
336 else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
338 SortCharacters(list, SortingMethod.AlphabeticalAsc);
340 CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
341 ((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Salary));
344 else if (sortingMethod == SortingMethod.SkillAsc || sortingMethod == SortingMethod.SkillDesc)
346 SortCharacters(list, SortingMethod.AlphabeticalAsc);
348 CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
349 ((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
353 int? CompareReputationRequirement(GUIComponent c1, GUIComponent c2)
355 CharacterInfo info1 = ((InfoSkill)c1.UserData).CharacterInfo;
356 CharacterInfo info2 = ((InfoSkill)c2.UserData).CharacterInfo;
357 float requirement1 = EnoughReputationToHire(info1) ? 0 : info1.MinReputationToHire.reputation;
358 float requirement2 = EnoughReputationToHire(info2) ? 0 : info2.MinReputationToHire.reputation;
359 if (MathUtils.NearlyEqual(requirement1, 0.0f) && MathUtils.NearlyEqual(requirement2, 0.0f))
365 return requirement1.CompareTo(requirement2);
370 private readonly
struct InfoSkill
372 public readonly CharacterInfo CharacterInfo;
373 public readonly
float SkillLevel;
375 public InfoSkill(CharacterInfo characterInfo,
float skillLevel)
377 CharacterInfo = characterInfo;
378 SkillLevel = skillLevel;
385 Color? jobColor =
null;
386 if (characterInfo.
Job !=
null)
394 UserData =
new InfoSkill(characterInfo, skill?.
Level ?? 0.0f)
401 float portraitWidth = (0.8f * mainGroup.
Rect.Height) / mainGroup.
Rect.Width;
403 onDraw: (sb, component) => characterInfo.
DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
412 textColor: jobColor, textAlignment: Alignment.BottomLeft)
416 nameBlock.
Text = ToolBox.LimitString(nameBlock.
Text, nameBlock.
Font, nameBlock.
Rect.Width);
419 characterInfo.
Title ?? characterInfo.
Job.
Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
423 if (!characterInfo.MinReputationToHire.factionId.IsEmpty)
425 var faction = campaign.
Factions.Find(f => f.Prefab.Identifier == characterInfo.MinReputationToHire.factionId);
428 jobBlock.
TextColor = faction.Prefab.IconColor;
431 var fullJobText = jobBlock.
Text;
432 jobBlock.
Text = ToolBox.LimitString(fullJobText, jobBlock.
Font, jobBlock.
Rect.Width);
433 if (jobBlock.
Text != fullJobText)
435 jobBlock.
ToolTip = fullJobText;
438 float width = 0.6f / 3;
439 if (characterInfo.
Job !=
null && skill !=
null)
442 float iconWidth = (float)skillGroup.
Rect.Height / skillGroup.
Rect.Width;
447 if (jobColor.HasValue) { skillIcon.
Color = jobColor.Value; }
456 if (listBox != crewList)
460 textAlignment: Alignment.Center)
472 if (listBox == hireableList)
476 ToolTip = TextManager.Get(
"hirebutton"),
478 UserData = characterInfo,
479 Enabled = CanHire(characterInfo) && !HiringNewCharacter,
482 hireButton.OnAddedToGUIUpdateList += (
GUIComponent btn) =>
484 if (HiringNewCharacter)
492 btn.ToolTip = TextManager.Get(
"canthiremorecharacters");
496 else if (!btn.Enabled)
498 btn.ToolTip =
string.Empty;
499 btn.Enabled = CanHire(characterInfo);
503 if (HiringNewCharacter)
508 ToolTip = canHire ? TextManager.Get(
"hireandtakecontrol") : TextManager.Get(
"hireandtakecontroldisabled"),
510 UserData = characterInfo,
512 OnClicked = (b, o) =>
518 Client client = gameClient.ConnectedClients.FirstOrDefault(c => c.SessionId == gameClient.SessionId);
523 gameClient.SendTakeOverBotRequest(characterInfo);
524 needsHireableRefresh =
true;
529 takeoverButton.OnAddedToGUIUpdateList += (
GUIComponent btn) =>
532 btn.ToolTip = TextManager.Get(canHireCurrently ?
"hireandtakecontrol" :
"hireandtakecontroldisabled");
534 btn.Enabled = canHireCurrently;
538 else if (listBox == pendingList)
543 UserData = characterInfo,
544 Enabled = CanHire(characterInfo),
548 else if (listBox == crewList && campaign !=
null)
553 UserData = characterInfo,
555 Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermissionToHire,
556 OnClicked = (btn, obj) =>
559 TextManager.Get(
"FireWarningHeader"),
560 TextManager.GetWithVariable(
"FireWarningText",
"[charactername]", ((
CharacterInfo)obj).Name),
561 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
563 confirmDialog.Buttons[0].OnClicked = FireCharacter;
564 confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
565 confirmDialog.Buttons[1].OnClicked = confirmDialog.Close;
571 if (listBox == pendingList || listBox == crewList)
574 nameBlock.
Text = ToolBox.LimitString(nameBlock.
Text, nameBlock.
Font, nameBlock.
Rect.Width);
576 Point size =
new Point((
int)(0.7f * nameBlock.
Rect.Height));
578 size =
new Point(3 * mainGroup.
AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.
Rect.Width, mainGroup.
Rect.Height);
581 Enabled = CanHire(characterInfo),
583 UserData = characterInfo,
584 OnClicked = CreateRenamingComponent
590 if (!HasPermissionToHire) {
return false; }
591 return EnoughReputationToHire(thisCharacterInfo);
597 private bool EnoughReputationToHire(
CharacterInfo characterInfo)
599 if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
601 if (MathF.Round(campaign.
GetReputation(characterInfo.MinReputationToHire.factionId)) < characterInfo.MinReputationToHire.reputation)
609 private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
611 Pivot pivot = listBox == hireableList ?
Pivot.TopLeft :
Pivot.TopRight;
612 Point absoluteOffset =
new Point(
613 pivot ==
Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5,
614 characterFrame.Rect.Top);
615 Point frameSize =
new Point(GUI.IntScale(300), GUI.IntScale(350));
616 if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize.Y) < 0)
618 pivot = listBox == hireableList ?
Pivot.BottomLeft :
Pivot.BottomRight;
619 absoluteOffset.Y = characterFrame.Rect.Bottom;
621 characterPreviewFrame =
new GUIFrame(
new RectTransform(frameSize, parent: campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Parent.RectTransform, pivot: pivot)
623 AbsoluteOffset = absoluteOffset
624 }, style:
"InnerFrame")
626 UserData = characterInfo
628 GUILayoutGroup mainGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f), characterPreviewFrame.
RectTransform, anchor:
Anchor.Center))
630 RelativeSpacing = 0.01f,
635 GUILayoutGroup infoGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal:
true);
636 GUILayoutGroup infoLabelGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.4f, 1.0f), infoGroup.RectTransform)) { Stretch =
true };
637 GUILayoutGroup infoValueGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch =
true };
638 float blockHeight = 1.0f / 4;
639 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(
"name"));
640 GUITextBlock nameBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform),
"");
641 string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
642 nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
644 if (characterInfo.HasSpecifierTags)
646 var menuCategoryVar = characterInfo.Prefab.MenuCategoryVar;
647 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar));
648 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.ReplaceVars($
"[{menuCategoryVar}]")));
650 if (characterInfo.Job is Job job)
652 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(
"tabmenu.job"));
653 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), job.Name);
655 if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
657 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(
"PersonalityTrait"));
658 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), trait.DisplayName);
660 infoLabelGroup.Recalculate();
661 infoValueGroup.Recalculate();
663 new GUIImage(
new RectTransform(
new Vector2(1.0f, 0.05f), mainGroup.RectTransform),
"HorizontalLine");
666 GUILayoutGroup skillGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal:
true);
667 GUILayoutGroup skillNameGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 1.0f), skillGroup.RectTransform));
668 GUILayoutGroup skillLevelGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.2f, 1.0f), skillGroup.RectTransform));
669 var characterSkills = characterInfo.Job.GetSkills();
670 blockHeight = 1.0f / characterSkills.Count();
671 foreach (Skill skill
in characterSkills)
673 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get(
"SkillName." + skill.Identifier), font: GUIStyle.SmallFont);
674 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, blockHeight), skillLevelGroup.RectTransform), ((
int)skill.Level).ToString(), textAlignment: Alignment.Right);
677 if (characterInfo.MinReputationToHire.reputation > 0.0f)
679 var repStr = TextManager.GetWithVariables(
680 "campaignstore.reputationrequired",
681 (
"[amount]", ((
int)characterInfo.MinReputationToHire.reputation).ToString()),
682 (
"[faction]", TextManager.Get(
"faction." + characterInfo.MinReputationToHire.factionId).Value));
683 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), mainGroup.RectTransform),
684 repStr, textColor: !EnoughReputationToHire(characterInfo) ? GUIStyle.Orange : GUIStyle.Green,
685 font: GUIStyle.SmallFont, wrap:
true, textAlignment: Alignment.Center);
687 mainGroup.Recalculate();
689 new Point(0, (
int)(mainGroup.Children.Sum(c => c.Rect.Height + mainGroup.Rect.Height * mainGroup.RelativeSpacing) / mainGroup.RectTransform.RelativeSize.Y));
692 private bool SelectCharacter(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
694 if (characterPreviewFrame !=
null && characterPreviewFrame.
UserData != characterInfo)
697 characterPreviewFrame =
null;
700 if (listBox ==
null || characterFrame ==
null || characterInfo ==
null) {
return false; }
702 if (characterPreviewFrame ==
null)
704 CreateCharacterPreviewFrame(listBox, characterFrame, characterInfo);
710 private bool AddPendingHire(CharacterInfo characterInfo,
bool createNetworkMessage =
true)
719 if (!PendingHires.Contains(characterInfo)) { PendingHires.Add(characterInfo); }
721 SortCharacters(pendingList, SortingMethod.JobAsc);
728 private bool RemovePendingHire(CharacterInfo characterInfo,
bool setTotalHireCost =
true,
bool createNetworkMessage =
true)
730 if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
735 if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
738 hireableList.
Content.
Children.None(c => c.UserData is InfoSkill userData && userData.
CharacterInfo.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
741 SortCharacters(hireableList, (SortingMethod)sortingDropDown.
SelectedItemData);
745 if (setTotalHireCost) { SetTotalHireCost(); }
750 private bool RemoveAllPendingHires(
bool createNetworkMessage =
true)
752 pendingList.
Content.
Children.ToList().ForEach(c => RemovePendingHire(((InfoSkill)c.UserData).CharacterInfo, setTotalHireCost:
false, createNetworkMessage));
757 private void SetTotalHireCost()
759 if (pendingList ==
null || totalBlock ==
null || validateHiresButton ==
null) {
return; }
760 var infos = pendingList.
Content.
Children.Select(
static c => ((InfoSkill)c.UserData).CharacterInfo).ToArray();
761 int total = HireManager.GetSalaryFor(infos);
762 totalBlock.
Text = TextManager.FormatCurrency(total);
763 bool enoughMoney = campaign ==
null || campaign.
CanAfford(total);
764 totalBlock.
TextColor = enoughMoney ? Color.White : Color.Red;
768 public bool ValidateHires(List<CharacterInfo> hires,
bool takeMoney =
true,
bool createNetworkEvent =
false,
bool createNotification =
true)
770 if (hires ==
null || hires.None()) {
return false; }
772 List<CharacterInfo> nonDuplicateHires =
new List<CharacterInfo>();
773 hires.ForEach(hireInfo =>
775 if (campaign.
CrewManager.
GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
777 nonDuplicateHires.Add(hireInfo);
781 if (nonDuplicateHires.None()) {
return false; }
786 if (!campaign.
CanAfford(total)) {
return false; }
789 bool atLeastOneHired =
false;
794 atLeastOneHired =
true;
805 SelectCharacter(
null,
null,
null);
806 if (createNotification)
809 TextManager.Get(
"newcrewmembers"),
810 TextManager.GetWithVariable(
"crewhiredmessage",
"[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
812 dialog.Buttons[0].OnClicked += dialog.Close;
816 if (createNetworkEvent)
824 private bool CreateRenamingComponent(
GUIButton button,
object userData)
826 if (!HasPermissionToHire || userData is not
CharacterInfo characterInfo) {
return false; }
827 var outerGlowFrame =
new GUIFrame(
new RectTransform(
new Vector2(1.25f, 1.25f), parentComponent.RectTransform,
Anchor.Center),
828 style:
"OuterGlow", color: Color.Black * 0.7f);
829 var frame =
new GUIFrame(
new RectTransform(
new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor:
Anchor.Center)
831 MaxSize = new Point(400, 300).Multiply(GUI.Scale)
833 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)
835 RelativeSpacing = 0.02f,
838 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get(
"campaigncrew.givenickname"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap:
true);
839 var groupElementSize =
new Vector2(1.0f, 0.25f);
840 var nameBox =
new GUITextBox(
new RectTransform(groupElementSize, layoutGroup.RectTransform))
844 new GUIButton(
new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get(
"confirm"))
846 OnClicked = (button, userData) =>
850 parentComponent.RemoveChild(outerGlowFrame);
855 nameBox.Flash(color: Color.Red);
861 new GUIButton(
new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get(
"cancel"))
863 OnClicked = (button, userData) =>
865 parentComponent.RemoveChild(outerGlowFrame);
869 layoutGroup.Recalculate();
875 if (characterInfo ==
null ||
string.IsNullOrEmpty(newName)) {
return false; }
876 if (newName == characterInfo.
Name) {
return false; }
879 SendCrewState(
false, renameCharacter: (characterInfo, newName));
884 if (crewComponent !=
null)
889 SortCharacters(crewList, SortingMethod.JobAsc);
894 if (pendingComponent !=
null)
899 SortCharacters(pendingList, SortingMethod.JobAsc);
911 private bool FireCharacter(
GUIButton button,
object selection)
913 if (selection is not
CharacterInfo characterInfo) {
return false; }
916 SelectCharacter(
null,
null,
null);
936 pendingAndCrewGroup.
Visible = !HiringNewCharacter;
938 if (needsHireableRefresh)
940 RefreshCrewFrames(hireableList);
941 if (sortingDropDown?.SelectedItemData !=
null)
943 SortCharacters(hireableList, (SortingMethod)sortingDropDown.
SelectedItemData);
945 needsHireableRefresh =
false;
949 if (highlightedFrame !=
null && highlightedInfo !=
null)
951 if (characterPreviewFrame ==
null || highlightedInfo != characterPreviewFrame.
UserData)
962 else if (component.
Parent !=
null)
964 component = component.
Parent;
970 }
while (listBox ==
null);
974 SelectCharacter(listBox, highlightedFrame as
GUIFrame, highlightedInfo);
983 else if (characterPreviewFrame !=
null)
986 characterPreviewFrame =
null;
995 if (c.
UserData is InfoSkill highlightedData)
997 return (c, highlightedData.CharacterInfo);
1005 return FindHighlightedCharacter(c.
Parent);
1013 List<CharacterInfo> oldHires = PendingHires.ToList();
1016 RemovePendingHire(pendingHire, createNetworkMessage:
false);
1018 PendingHires.Clear();
1019 foreach (UInt16 identifier
in characterInfos)
1024 AddPendingHire(match, createNetworkMessage:
false);
1025 System.Diagnostics.Debug.Assert(PendingHires.Contains(match));
1029 DebugConsole.ThrowError(
"Received a hire that doesn't exist.");
1060 bool validRenaming = renameCharacter.info !=
null && !
string.IsNullOrEmpty(renameCharacter.newName);
1071 if (firedCharacter !=
null)
virtual bool TryPurchase(Client client, int price)
bool CanAfford(int cost, Client client=null)
float GetReputation(Identifier factionIdentifier)
bool??????? ShowCampaignUI
IReadOnlyList< Faction > Factions
static bool AllowedToManageCampaign(ClientPermissions permissions)
There is a server-side implementation of the method in MultiPlayerCampaign
bool TryHireCharacter(Location location, CharacterInfo characterInfo, bool takeMoney=true, Client client=null)
static ? PlayerBalanceElement UpdateBalanceElement(PlayerBalanceElement? playerBalanceElement)
Stores information about the Character that is needed between rounds in the menu etc....
CharacterInfo(Identifier speciesName, string name="", string originalName="", Either< Job, JobPrefab > jobOrJobPrefab=null, int variant=0, Rand.RandSync randSync=Rand.RandSync.Unsynced, Identifier npcIdentifier=default)
Character Character
Note: Can be null.
void DrawIcon(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 targetAreaSize)
ushort ID
Unique ID given to character infos in MP. Non-persistent. Used by clients to identify which infos are...
void SetHireables(Location location, List< CharacterInfo > availableHires)
void RefreshPermissions()
bool ValidateHires(List< CharacterInfo > hires, bool takeMoney=true, bool createNetworkEvent=false, bool createNotification=true)
bool RenameCharacter(CharacterInfo characterInfo, string newName)
GUIComponent CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox, bool hideSalary=false)
void SetPendingHires(List< UInt16 > characterInfos, Location location)
CrewManagement(CampaignUI campaignUI, GUIComponent parentComponent)
void SendCrewState(bool updatePending,(CharacterInfo info, string newName) renameCharacter=default, CharacterInfo firedCharacter=null, bool validateHires=false)
Notify the server of crew changes
IEnumerable< CharacterInfo > GetCharacterInfos()
void FireCharacter(CharacterInfo characterInfo)
void RenameCharacter(CharacterInfo characterInfo, string newName)
virtual void RemoveChild(GUIComponent child)
virtual void ClearChildren()
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
virtual RichString ToolTip
RectTransform RectTransform
IEnumerable< GUIComponent > Children
GUIComponent that can be used to render custom content on the UI
GUIComponent AddItem(LocalizedString text, object userData=null, LocalizedString toolTip=null, Color? color=null, Color? textColor=null)
override void RemoveChild(GUIComponent child)
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
void UpdateScrollBarSize()
static GameSession?? GameSession
static int GraphicsHeight
static bool IsMultiplayer
static NetworkMember NetworkMember
static int GetSalaryFor(IReadOnlyCollection< CharacterInfo > hires)
List< CharacterInfo > PendingHires
void RenameCharacter(CharacterInfo characterInfo, string newName)
List< CharacterInfo > AvailableCharacters
IEnumerable< Skill > GetSkills()
IEnumerable< CharacterInfo > GetHireableCharacters()
readonly NamedEvent< LocationChangeInfo > OnLocationChanged
From -> To
CharacterInfo CharacterInfo
static readonly NamedEvent< Reputation > OnAnyReputationValueChanged
void WriteString(string val)
void WriteBoolean(bool val)
void WriteUInt16(UInt16 val)