Client LuaCsForBarotrauma
RoundSummary.cs
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using System;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Globalization;
8 using System.Linq;
9 
10 namespace Barotrauma
11 {
13  {
14  private float crewListAnimDelay = 0.25f;
15  private float missionIconAnimDelay;
16 
17  private const float JobColumnWidthPercentage = 0.1f;
18  private const float CharacterColumnWidthPercentage = 0.4f;
19  private const float StatusColumnWidthPercentage = 0.12f;
20  private const float KillColumnWidthPercentage = 0.1f;
21  private const float DeathColumnWidthPercentage = 0.1f;
22 
23  private int jobColumnWidth, characterColumnWidth, statusColumnWidth, killColumnWidth, deathColumnWidth;
24 
25  private readonly List<Mission> selectedMissions;
26  private readonly Location startLocation, endLocation;
27 
28  private readonly GameMode gameMode;
29 
30  private readonly Dictionary<Identifier, float> initialFactionReputations = new Dictionary<Identifier, float>();
31 
32  public GUILayoutGroup ButtonArea { get; private set; }
33 
34  public GUIButton ContinueButton { get; private set; }
35 
36  public GUIComponent Frame { get; private set; }
37 
38  public RoundSummary(GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
39  {
40  this.gameMode = gameMode;
41  this.selectedMissions = selectedMissions.ToList();
42  this.startLocation = startLocation;
43  this.endLocation = endLocation;
44  if (gameMode is CampaignMode campaignMode)
45  {
46  foreach (Faction faction in campaignMode.Factions)
47  {
48  initialFactionReputations.Add(faction.Prefab.Identifier, faction.Reputation.Value);
49  }
50  }
51  }
52 
53  public GUIFrame CreateSummaryFrame(GameSession gameSession, string endMessage, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None, TraitorManager.TraitorResults? traitorResults = null)
54  {
55  bool singleplayer = GameMain.NetworkMember == null;
56  bool gameOver =
57  gameSession.GameMode.IsSinglePlayer ?
58  gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated) :
59  gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated || c.IsBot);
60 
61  if (!singleplayer)
62  {
63  SoundPlayer.OverrideMusicType = (gameOver ? "crewdead" : "endround").ToIdentifier();
64  SoundPlayer.OverrideMusicDuration = 18.0f;
65  }
66 
67  GUIFrame background = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker")
68  {
69  UserData = this
70  };
71 
72  List<GUIComponent> rightPanels = new List<GUIComponent>();
73 
74  int minWidth = 400, minHeight = 350;
75  int padding = GUI.IntScale(25.0f);
76 
77  //crew panel -------------------------------------------------------------------------------
78 
79  GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.4f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
80  GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame");
81 
82  var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center))
83  {
84  Stretch = true
85  };
86 
87  var crewHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent.RectTransform),
88  TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
89  crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f));
90 
91  var crewList = CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2), traitorResults);
92  if (traitorResults != null && traitorResults.Value.VotedAsTraitorClientSessionId > 0)
93  {
94  var traitorInfoPanel = CreateTraitorInfoPanel(crewList.Content, traitorResults.Value, crewListAnimDelay);
95  traitorInfoPanel.RectTransform.SetAsFirstChild();
96  var spacing = new GUIFrame(new RectTransform(new Point(0, GUI.IntScale(20)), crewList.Content.RectTransform), style: null);
97  spacing.RectTransform.RepositionChildInHierarchy(1);
98  }
99 
100  //another crew frame for the 2nd team in combat missions
101  if (gameSession.Missions.Any(m => m is CombatMission))
102  {
103  crewHeader.Text = CombatMission.GetTeamName(CharacterTeamType.Team1);
104  GUIFrame crewFrame2 = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
105  rightPanels.Add(crewFrame2);
106  GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame");
107  var crewContent2 = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner2.RectTransform, Anchor.Center))
108  {
109  Stretch = true
110  };
111  var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform),
112  CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
113  crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
114  CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2), traitorResults);
115 
117  {
118  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader.RectTransform),
119  TextManager.Get(CombatMission.Winner == CharacterTeamType.Team1 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
120  textColor: CombatMission.Winner == CharacterTeamType.Team1 ? GUIStyle.Green : GUIStyle.Red);
121  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader2.RectTransform),
122  TextManager.Get(CombatMission.Winner == CharacterTeamType.Team2 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
123  textColor: CombatMission.Winner == CharacterTeamType.Team2 ? GUIStyle.Green : GUIStyle.Red);
124  }
125  }
126 
127  //header -------------------------------------------------------------------------------
128 
129  LocalizedString headerText = GetHeaderText(gameOver, transitionType);
130  GUITextBlock headerTextBlock = null;
131  if (!headerText.IsNullOrEmpty())
132  {
133  headerTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), crewFrame.RectTransform, Anchor.TopLeft, Pivot.BottomLeft),
134  headerText, textAlignment: Alignment.BottomLeft, font: GUIStyle.LargeFont, wrap: true);
135  }
136 
137  //reputation panel -------------------------------------------------------------------------------
138 
139  var campaignMode = gameMode as CampaignMode;
140  if (campaignMode != null)
141  {
142  GUIFrame reputationframe = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: crewFrame.RectTransform.MinSize));
143  rightPanels.Add(reputationframe);
144  GUIFrame reputationframeInner = new GUIFrame(new RectTransform(new Point(reputationframe.Rect.Width - padding * 2, reputationframe.Rect.Height - padding * 2), reputationframe.RectTransform, Anchor.Center), style: "InnerFrame");
145 
146  var reputationContent = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), reputationframeInner.RectTransform, Anchor.Center))
147  {
148  Stretch = true
149  };
150 
151  var reputationHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), reputationContent.RectTransform),
152  TextManager.Get("reputation"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
153  reputationHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(reputationHeader.Rect.Height * 2.0f));
154 
155  CreateReputationInfoPanel(reputationContent, campaignMode);
156  }
157 
158  //mission panel -------------------------------------------------------------------------------
159 
160  GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.4f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
161  GUILayoutGroup missionFrameContent = new GUILayoutGroup(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center))
162  {
163  Stretch = true,
164  RelativeSpacing = 0.03f
165  };
166  GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), missionFrameContent.RectTransform, Anchor.Center), style: "InnerFrame");
167 
168  var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.93f), missionframeInner.RectTransform, Anchor.Center))
169  {
170  Stretch = true
171  };
172 
173  List<Mission> missionsToDisplay = new List<Mission>(selectedMissions.Where(m => m.Prefab.ShowInMenus));
174  if (startLocation != null)
175  {
176  foreach (Mission mission in startLocation.SelectedMissions)
177  {
178  if (missionsToDisplay.Contains(mission)) { continue; }
179  if (!mission.Prefab.ShowInMenus) { continue; }
180  if (mission.Locations[0] == mission.Locations[1] ||
181  mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
182  {
183  missionsToDisplay.Add(mission);
184  }
185  }
186  }
187 
188  GUIListBox missionList = new GUIListBox(new RectTransform(Vector2.One, missionContent.RectTransform, Anchor.Center))
189  {
190  Spacing = GUI.IntScale(15)
191  };
192  missionList.ContentBackground.Color = Color.Transparent;
193 
194  ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionFrameContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight)
195  {
196  RelativeSpacing = 0.025f
197  };
198 
199  missionFrameContent.Recalculate();
200  missionContent.Recalculate();
201 
202  if (!string.IsNullOrWhiteSpace(endMessage))
203  {
204  var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionList.Content.RectTransform),
205  TextManager.GetServerMessage(endMessage), wrap: true)
206  {
207  CanBeFocused = false
208  };
209  }
210 
211  float animDelay = missionIconAnimDelay;
212  foreach (Mission mission in missionsToDisplay)
213  {
214  var textContent = new List<LocalizedString>();
215 
216  if (selectedMissions.Contains(mission))
217  {
218  textContent.Add(mission.Completed ? mission.SuccessMessage : mission.FailureMessage);
219 
220  var repText = mission.GetReputationRewardText();
221  if (!repText.IsNullOrEmpty()) { textContent.Add(repText); }
222 
223  int totalReward = mission.GetFinalReward(Submarine.MainSub);
224  if (totalReward > 0)
225  {
226  textContent.Add(mission.GetMissionRewardText(Submarine.MainSub));
227  if (GameMain.IsMultiplayer && Character.Controlled is { } controlled && mission.Completed)
228  {
229  var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(totalReward));
230  if (share > 0)
231  {
232  string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
233  RichString yourShareString = TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}"));
234  textContent.Add(yourShareString);
235  }
236  }
237  }
238  }
239  else
240  {
241  var repText = mission.GetReputationRewardText();
242  if (!repText.IsNullOrEmpty()) { textContent.Add(repText); }
243  textContent.Add(mission.GetMissionRewardText(Submarine.MainSub));
244  textContent.Add(mission.Description);
245  textContent.AddRange(mission.ShownMessages);
246  }
247 
249  missionList.Content,
250  mission.Name,
251  textContent,
252  mission.Difficulty ?? 0,
253  mission.Prefab.Icon, mission.Prefab.IconColor,
254  out GUIImage missionIcon);
255 
256  if (selectedMissions.Contains(mission))
257  {
258  UpdateMissionStateIcon(mission is CombatMission combatMission ? CombatMission.IsInWinningTeam(GameMain.Client?.Character) : mission.Completed, missionIcon, animDelay);
259  animDelay += 0.25f;
260  }
261  }
262 
263  if (!missionsToDisplay.Any())
264  {
265  var missionContentHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), missionList.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
266  {
267  RelativeSpacing = 0.025f,
268  Stretch = true,
269  CanBeFocused = true
270  };
271  GUIImage missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
272  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContentHorizontal.RectTransform),
273  TextManager.Get("nomission"), font: GUIStyle.LargeFont);
274  }
275 
276  gameSession?.EventManager?.EventLog?.CreateEventLogUI(missionList.Content, traitorResults);
277 
278  AddSeparators(missionList.Content);
279 
280  ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close"));
283 
284  missionFrameContent.Recalculate();
285 
286  // set layout -------------------------------------------------------------------
287 
288  int panelSpacing = GUI.IntScale(20);
289  int totalHeight = crewFrame.Rect.Height + panelSpacing + missionframe.Rect.Height;
290  int totalWidth = crewFrame.Rect.Width;
291 
292  crewFrame.RectTransform.AbsoluteOffset = new Point(0, (GameMain.GraphicsHeight - totalHeight) / 2);
293  missionframe.RectTransform.AbsoluteOffset = new Point(0, crewFrame.Rect.Bottom + panelSpacing);
294 
295  if (rightPanels.Any())
296  {
297  totalWidth = crewFrame.Rect.Width * 2 + panelSpacing;
298  if (headerTextBlock != null)
299  {
300  headerTextBlock.RectTransform.MinSize = new Point(totalWidth, 0);
301  }
302  crewFrame.RectTransform.AbsoluteOffset = new Point(-(crewFrame.Rect.Width + panelSpacing) / 2, crewFrame.RectTransform.AbsoluteOffset.Y);
303  foreach (var rightPanel in rightPanels)
304  {
305  rightPanel.RectTransform.AbsoluteOffset = new Point((rightPanel.Rect.Width + panelSpacing) / 2, crewFrame.RectTransform.AbsoluteOffset.Y);
306  }
307  }
308 
309  Frame = background;
310  return background;
311  }
312 
313  public void CreateReputationInfoPanel(GUIComponent parent, CampaignMode campaignMode)
314  {
315  GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform));
316  reputationList.ContentBackground.Color = Color.Transparent;
317 
318  foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
319  {
320  float initialReputation = faction.Reputation.Value;
321  if (!initialFactionReputations.TryGetValue(faction.Prefab.Identifier, out initialReputation))
322  {
323  DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
324  }
325  var factionFrame = CreateReputationElement(
326  reputationList.Content,
327  faction.Prefab.Name,
328  faction.Reputation, initialReputation,
329  faction.Prefab.ShortDescription, faction.Prefab.Description,
330  faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor);
331  CreatePathUnlockElement(factionFrame, faction, null);
332  }
333 
334  float maxDescriptionHeight = 0.0f;
335  foreach (GUIComponent child in reputationList.Content.Children)
336  {
337  var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
338  float descriptionHeight = descriptionElement.TextSize.Y * 1.1f;
339  if (child.FindChild("unlockinfo") is GUIComponent unlockInfoComponent)
340  {
341  descriptionHeight += 1.25f * unlockInfoComponent.Rect.Height;
342  }
343  maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionHeight);
344  }
345  foreach (GUIComponent child in reputationList.Content.Children)
346  {
347  var headerElement = child.FindChild("header", recursive: true) as GUITextBlock;
348  var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
349  descriptionElement.RectTransform.NonScaledSize = new Point(descriptionElement.Rect.Width, (int)maxDescriptionHeight);
350  descriptionElement.RectTransform.IsFixedSize = true;
351  child.RectTransform.NonScaledSize = new Point(child.Rect.Width, headerElement.Rect.Height + descriptionElement.RectTransform.Parent.Children.Sum(c => c.Rect.Height + ((GUILayoutGroup)descriptionElement.Parent).AbsoluteSpacing));
352  }
353 
354  void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location)
355  {
356  if (GameMain.GameSession?.Campaign?.Map == null) { return; }
357 
358  IEnumerable<LocationConnection> connectionsBetweenBiomes =
359  GameMain.GameSession.Campaign.Map.Connections.Where(c => c.Locations[0].Biome != c.Locations[1].Biome);
360 
361  foreach (LocationConnection connection in connectionsBetweenBiomes)
362  {
363  if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
364 
365  //don't show the "reputation required to unlock" text if another connection between the biomes has already been unlocked
366  if (connectionsBetweenBiomes.Where(c => !c.Locked).Any(c =>
367  (c.Locations[0].Biome == connection.Locations[0].Biome && c.Locations[1].Biome == connection.Locations[1].Biome) ||
368  (c.Locations[1].Biome == connection.Locations[0].Biome && c.Locations[0].Biome == connection.Locations[1].Biome)))
369  {
370  continue;
371  }
372 
373  var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
374  var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
375 
376  if (unlockEvent == null) { continue; }
377  if (unlockEvent.Faction.IsEmpty)
378  {
379  if (location == null || gateLocation != location) { continue; }
380  }
381  else
382  {
383  if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
384  }
385 
386  if (unlockEvent != null)
387  {
388  Reputation unlockReputation = gateLocation.Reputation;
389  Faction unlockFaction = null;
390  if (!unlockEvent.Faction.IsEmpty)
391  {
392  unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
393  unlockReputation = unlockFaction?.Reputation;
394  }
395  float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
396  RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
397  "lockedpathreputationrequirement",
398  ("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
399  ("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
400  var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
401  unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
402  unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
403  unlockInfoPanel.UserData = "unlockinfo";
404  if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
405  {
406  unlockInfoPanel.Font = GUIStyle.SmallFont;
407  }
408  }
409  }
410  }
411  }
412 
413  private static GUIComponent CreateTraitorInfoPanel(GUIComponent parent, TraitorManager.TraitorResults traitorResults, float iconAnimDelay)
414  {
415  var traitorCharacter = traitorResults.GetTraitorClient()?.Character;
416 
417  string resultTag =
418  traitorResults.VotedCorrectTraitor ?
419  traitorResults.ObjectiveSuccessful ? "traitor.blameresult.correct.objectivesuccessful" : "traitor.blameresult.correct.objectivefailed" :
420  "traitor.blameresult.failure";
421 
422  var textContent = new List<LocalizedString>()
423  {
424  TextManager.GetWithVariable("traitor.blameresult", "[name]", traitorCharacter?.Name ?? "unknown"),
425  TextManager.Get(resultTag)
426  };
427 
428  if (traitorResults.MoneyPenalty > 0)
429  {
430  textContent.Add(
431  TextManager.GetWithVariable(
432  "traitor.blameresult.failure.penalty",
433  "[money]",
434  TextManager.FormatCurrency(traitorResults.MoneyPenalty, includeCurrencySymbol: false)));
435  }
436 
437  var icon = GUIStyle.GetComponentStyle("TraitorMissionIcon")?.GetDefaultSprite();
438 
439  var content = CreateMissionEntry(
440  parent,
441  string.Empty,
442  textContent,
443  difficultyIconCount: 0,
444  icon, GUIStyle.Red,
445  out GUIImage missionIcon);
446  UpdateMissionStateIcon(traitorResults.VotedCorrectTraitor, missionIcon, iconAnimDelay);
447  return content;
448  }
449 
450  public static GUIComponent CreateMissionEntry(GUIComponent parent, LocalizedString header, List<LocalizedString> textContent, int difficultyIconCount,
451  Sprite icon, Color iconColor, out GUIImage missionIcon)
452  {
453  int spacing = GUI.IntScale(5);
454 
455  int defaultLineHeight = (int)GUIStyle.Font.MeasureChar('T').Y;
456 
457  //make the icon big enough for header + some lines of text
458  int iconSize = (int)(GUIStyle.SubHeadingFont.MeasureChar('T').Y + defaultLineHeight * 6);
459 
460  GUILayoutGroup content = new GUILayoutGroup(new RectTransform(new Point(parent.Rect.Width, iconSize), parent.RectTransform), isHorizontal: true)
461  {
462  Stretch = true,
463  AbsoluteSpacing = spacing,
464  CanBeFocused = true
465  };
466  if (icon != null)
467  {
468  missionIcon = new GUIImage(new RectTransform(new Point(iconSize), content.RectTransform), icon, null, true)
469  {
470  Color = iconColor,
471  HoverColor = iconColor,
472  SelectedColor = iconColor,
473  CanBeFocused = false
474  };
475  missionIcon.RectTransform.IsFixedSize = true;
476  }
477  else
478  {
479  missionIcon = null;
480  }
481  GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), content.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.TopLeft)
482  {
483  AbsoluteSpacing = spacing
484  };
485  content.Recalculate();
486 
487  RichString missionNameString = RichString.Rich(header);
488  List<RichString> contentStrings = new List<RichString>(textContent.Select(t => RichString.Rich(t)));
489 
490  if (!header.IsNullOrEmpty())
491  {
492  var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform),
493  missionNameString, font: GUIStyle.SubHeadingFont, wrap: true);
494  nameText.RectTransform.MinSize = new Point(0, (int)nameText.TextSize.Y);
495  }
496 
497  GUILayoutGroup difficultyIndicatorGroup = null;
498  if (difficultyIconCount > 0)
499  {
500  difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, defaultLineHeight), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
501  {
502  AbsoluteSpacing = 1
503  };
504  difficultyIndicatorGroup.RectTransform.MinSize = new Point(0, defaultLineHeight);
505  var difficultyColor = Mission.GetDifficultyColor(difficultyIconCount);
506  for (int i = 0; i < difficultyIconCount; i++)
507  {
508  new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
509  {
510  CanBeFocused = false,
511  Color = difficultyColor
512  };
513  }
514  }
515 
516  GUITextBlock firstContentText = null;
517  foreach (var contentString in contentStrings)
518  {
519  var text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), contentString, wrap: true);
520  text.RectTransform.MinSize = new Point(0, (int)text.TextSize.Y);
521  firstContentText ??= text;
522  }
523  if (difficultyIndicatorGroup != null && firstContentText != null)
524  {
525  //make the icons align with the text content
526  difficultyIndicatorGroup.RectTransform.AbsoluteOffset = new Point((int)firstContentText.Padding.X, 0);
527  }
528  missionTextGroup.RectTransform.MinSize =
529  new Point(0, missionTextGroup.Children.Sum(c => c.Rect.Height + missionTextGroup.AbsoluteSpacing) - missionTextGroup.AbsoluteSpacing);
530  missionTextGroup.Recalculate();
531  content.RectTransform.MinSize = new Point(0, Math.Max(missionTextGroup.Rect.Height, iconSize));
532 
533  return content;
534  }
535 
536  public static void AddSeparators(GUIComponent container)
537  {
538  var children = container.Children.ToList();
539  if (children.Count < 2) { return; }
540 
541  var lastChild = children.Last();
542  foreach (var child in children)
543  {
544  if (child != lastChild)
545  {
546  var separator = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), container.RectTransform), style: "HorizontalLine");
547  separator.RectTransform.RepositionChildInHierarchy(container.GetChildIndex(child) + 1);
548  }
549  }
550  }
551 
552  public static void UpdateMissionStateIcon(bool success, GUIImage missionIcon, float delay = 0.5f)
553  {
554  if (missionIcon == null) { return; }
555  string style = success ? "MissionCompletedIcon" : "MissionFailedIcon";
556  GUIImage stateIcon = missionIcon.GetChild<GUIImage>();
557  if (string.IsNullOrEmpty(style))
558  {
559  if (stateIcon != null)
560  {
561  stateIcon.Visible = false;
562  }
563  }
564  else
565  {
566  bool wasVisible = stateIcon is { Visible: true };
567  stateIcon ??= new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform, Anchor.Center), style, scaleToFit: true);
568  stateIcon.Visible = true;
569  if (!wasVisible)
570  {
571  stateIcon.FadeIn(delay, 0.15f);
572  stateIcon.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.0f + delay);
573  }
574  }
575  }
576 
577 
578  private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
579  {
580  LocalizedString locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.DisplayName : startLocation?.DisplayName;
581 
582  string textTag;
583  if (gameMode is PvPMode)
584  {
585  textTag = "RoundSummaryRoundHasEnded";
586  }
587  else if (gameOver)
588  {
589  textTag = "RoundSummaryGameOver";
590  }
591  else
592  {
593  switch (transitionType)
594  {
595  case CampaignMode.TransitionType.LeaveLocation:
596  locationName = startLocation?.DisplayName;
597  textTag = "RoundSummaryLeaving";
598  break;
599  case CampaignMode.TransitionType.ProgressToNextLocation:
600  locationName = endLocation?.DisplayName;
601  textTag = "RoundSummaryProgress";
602  break;
603  case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
604  locationName = endLocation?.DisplayName;
605  textTag = "RoundSummaryProgressToEmptyLocation";
606  break;
607  case CampaignMode.TransitionType.ReturnToPreviousLocation:
608  locationName = startLocation?.DisplayName;
609  textTag = "RoundSummaryReturn";
610  break;
611  case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
612  locationName = startLocation?.DisplayName;
613  textTag = "RoundSummaryReturnToEmptyLocation";
614  break;
615  default:
616  if (Submarine.MainSub == null)
617  {
618  textTag = "RoundSummaryRoundHasEnded";
619  }
620  else
621  {
622  textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
623  }
624  break;
625  }
626  }
627 
628  if (startLocation?.Biome != null && startLocation.Biome.IsEndBiome)
629  {
630  locationName ??= startLocation.DisplayName;
631  }
632 
633  if (textTag == null) { return ""; }
634 
635  if (locationName == null)
636  {
637  DebugConsole.ThrowError($"Error while creating round summary: could not determine destination location. Start location: {startLocation?.DisplayName ?? "null"}, end location: {endLocation?.DisplayName ?? "null"}");
638  locationName = "[UNKNOWN]";
639  }
640 
641  LocalizedString subName = string.Empty;
642  SubmarineInfo currentOrPending = SubmarineSelection.CurrentOrPendingSubmarine();
643  if (currentOrPending != null)
644  {
645  subName = currentOrPending.DisplayName;
646  }
647 
648  return TextManager.GetWithVariables(textTag, ("[sub]", subName), ("[location]", locationName));
649  }
650 
651  private GUIListBox CreateCrewList(GUIComponent parent, IEnumerable<CharacterInfo> characterInfos, TraitorManager.TraitorResults? traitorResults)
652  {
653  var headerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform, Anchor.TopCenter, minSize: new Point(0, (int)(30 * GUI.Scale))) { }, isHorizontal: true)
654  {
655  AbsoluteSpacing = 2,
656  Stretch = true
657  };
658  GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(JobColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
659  GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(CharacterColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
660 
661  if (gameMode is PvPMode)
662  {
663  var killButton = new GUIButton(new RectTransform(new Vector2(KillColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("killcount"), style: "GUIButtonSmallFreeScale");
664  killColumnWidth = killButton.Rect.Width;
665  var deathButton = new GUIButton(new RectTransform(new Vector2(DeathColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("deathcount"), style: "GUIButtonSmallFreeScale");
666  deathColumnWidth = deathButton.Rect.Width;
667  }
668 
669  GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(StatusColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
670 
671  foreach (var btn in headerFrame.GetAllChildren<GUIButton>())
672  {
673  btn.TextBlock.Font = GUIStyle.HotkeyFont;
674  btn.ForceUpperCase = ForceUpperCase.Yes;
675  btn.CanBeFocused = false;
676  }
677 
678  jobColumnWidth = jobButton.Rect.Width;
679  characterColumnWidth = characterButton.Rect.Width;
680  statusColumnWidth = statusButton.Rect.Width;
681 
682  GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
683  {
684  Padding = new Vector4(4, 10, 0, 0) * GUI.Scale,
685  AutoHideScrollBar = false
686  };
687  crewList.ContentBackground.Color = Color.Transparent;
688 
689  headerFrame.RectTransform.RelativeSize -= new Vector2(crewList.ScrollBar.RectTransform.RelativeSize.X, 0.0f);
690 
691  killCounts.Clear();
692  if (GameMain.NetworkMember != null)
693  {
694  foreach (CharacterInfo characterInfo in characterInfos)
695  {
696  if (characterInfo == null) { continue; }
697  Character character = characterInfo.Character;
698  Client ownerClient = GameMain.NetworkMember.ConnectedClients.FirstOrDefault(c => c.Character == character);
699  int killCount = 0, deathCount = 0;
700  foreach (var mission in selectedMissions)
701  {
702  if (mission is not CombatMission combatMission) { continue; }
703  killCount += ownerClient == null ? combatMission.GetBotKillCount(characterInfo) : combatMission.GetClientKillCount(ownerClient);
704  deathCount += ownerClient == null ? combatMission.GetBotDeathCount(characterInfo) : combatMission.GetClientDeathCount(ownerClient);
705  }
706  killCounts[characterInfo] = killCount;
707  deathCounts[characterInfo] = deathCount;
708  }
709  }
710 
711  float delay = crewListAnimDelay;
712  foreach (CharacterInfo characterInfo in characterInfos.OrderByDescending(ci => killCounts.GetValueOrDefault(ci)))
713  {
714  if (characterInfo == null) { continue; }
715  CreateCharacterElement(characterInfo, crewList, traitorResults, delay);
716  delay += crewListAnimDelay;
717  }
718  missionIconAnimDelay = delay;
719 
720  return crewList;
721  }
722 
723  private readonly Dictionary<CharacterInfo, int> killCounts = new();
724  private readonly Dictionary<CharacterInfo, int> deathCounts = new();
725 
726 
727  private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox, TraitorManager.TraitorResults? traitorResults, float animDelay)
728  {
729  GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(45)), listBox.Content.RectTransform), style: "ListBoxElement")
730  {
731  CanBeFocused = false,
732  UserData = characterInfo,
733  Color = (Character.Controlled?.Info == characterInfo) ? TabMenu.OwnCharacterBGColor : Color.Transparent
734  };
735 
736  var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
737  {
738  AbsoluteSpacing = 2,
739  Stretch = true
740  };
741 
742  new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => characterInfo.DrawJobIcon(sb, component.Rect))
743  {
744  ToolTip = characterInfo.Job.Name ?? "",
745  HoverColor = Color.White,
746  SelectedColor = Color.White
747  };
748 
749  GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
750  ToolBox.LimitString(characterInfo.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
751 
752  LocalizedString statusText = TextManager.Get("StatusOK");
753  Color statusColor = GUIStyle.Green;
754 
755  Character character = characterInfo.Character;
756  if (character == null || character.IsDead)
757  {
758  if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
759  {
760  statusText = TextManager.Get("CampaignCrew.NewHire");
761  statusColor = GUIStyle.Blue;
762  }
763  else if (characterInfo.CauseOfDeath == null)
764  {
765  statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
766  statusColor = Color.DarkRed;
767  }
768  else if (characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction && characterInfo.CauseOfDeath.Affliction == null)
769  {
770  string errorMsg = "Character \"[name]\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified).";
771  DebugConsole.ThrowError(errorMsg.Replace("[name]", characterInfo.Name));
772  GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", characterInfo.SpeciesName.Value));
773  statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
774  statusColor = GUIStyle.Red;
775  }
776  else
777  {
778  statusText = characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction ?
779  characterInfo.CauseOfDeath.Affliction.CauseOfDeathDescription :
780  TextManager.Get("CauseOfDeathDescription." + characterInfo.CauseOfDeath.Type.ToString());
781  statusColor = Color.DarkRed;
782  }
783  }
784  else
785  {
786  if (character.IsUnconscious)
787  {
788  statusText = TextManager.Get("Unconscious");
789  statusColor = Color.DarkOrange;
790  }
791  else if (character.Vitality / character.MaxVitality < 0.8f)
792  {
793  statusText = TextManager.Get("Injured");
794  statusColor = Color.DarkOrange;
795  }
796  }
797 
798  if (gameMode is PvPMode pvpMode)
799  {
800  new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
801  killCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
802  new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
803  deathCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
804  }
805 
806  GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
807  ToolBox.LimitString(statusText.Value, GUIStyle.Font, statusColumnWidth), textAlignment: Alignment.Center, textColor: statusColor, font: GUIStyle.SmallFont)
808  {
809  ToolTip = statusText.Value
810  };
811 
812  frame.FadeIn(animDelay, 0.15f);
813  foreach (var child in frame.GetAllChildren())
814  {
815  child.FadeIn(animDelay, 0.15f);
816  }
817 
818  if (traitorResults.HasValue && GameMain.NetworkMember != null)
819  {
820  var clientVotedAsTraitor = traitorResults.Value.GetTraitorClient();
821  bool isTraitor = clientVotedAsTraitor != null && clientVotedAsTraitor.Character == character;
822  if (isTraitor)
823  {
824  var img = new GUIImage(new RectTransform(new Point(paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.CenterRight), style: "TraitorVoteButton")
825  {
826  IgnoreLayoutGroups = true,
827  ToolTip = TextManager.GetWithVariable("traitor.blameresult", "[name]", characterInfo.Name)
828  };
829  img.FadeIn(1.0f + animDelay, 0.15f);
830  img.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.5f + animDelay);
831  }
832  }
833  }
834 
835  private static GUIFrame CreateReputationElement(GUIComponent parent,
836  LocalizedString name, Reputation reputation, float initialReputation,
837  LocalizedString shortDescription, LocalizedString fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
838  {
839  var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), style: null);
840 
841  if (backgroundPortrait != null)
842  {
843  new GUICustomComponent(new RectTransform(Vector2.One, factionFrame.RectTransform), onDraw: (sb, customComponent) =>
844  {
845  backgroundPortrait.Draw(sb, customComponent.Rect.Center.ToVector2(), customComponent.Color, backgroundPortrait.size / 2, scale: customComponent.Rect.Width / backgroundPortrait.size.X);
846  })
847  {
848  HideElementsOutsideFrame = true,
849  IgnoreLayoutGroups = true,
850  Color = iconColor * 0.2f
851  };
852  }
853 
854  var factionInfoHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), factionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterRight, isHorizontal: true)
855  {
856  AbsoluteSpacing = GUI.IntScale(5),
857  Stretch = true
858  };
859 
860  var factionIcon = new GUIImage(new RectTransform(Vector2.One * 0.7f, factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
861  {
862  Color = iconColor
863  };
864  var factionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, factionInfoHorizontal.RectTransform))
865  {
866  AbsoluteSpacing = GUI.IntScale(10),
867  Stretch = true
868  };
869 
870  factionInfoHorizontal.Recalculate();
871 
872  var header = new GUITextBlock(new RectTransform(new Point(factionTextContent.Rect.Width, GUI.IntScale(40)), factionTextContent.RectTransform),
873  name, font: GUIStyle.SubHeadingFont)
874  {
875  Padding = Vector4.Zero,
876  UserData = "header"
877  };
878  header.RectTransform.IsFixedSize = true;
879 
880  var sliderHolder = new GUILayoutGroup(new RectTransform(new Point((int)(factionTextContent.Rect.Width * 0.8f), GUI.IntScale(20.0f)), factionTextContent.RectTransform),
881  childAnchor: Anchor.CenterLeft, isHorizontal: true)
882  {
883  RelativeSpacing = 0.05f,
884  Stretch = true
885  };
886  sliderHolder.RectTransform.IsFixedSize = true;
887  factionTextContent.Recalculate();
888 
889  new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
890  onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, reputation.NormalizedValue, reputation.MinReputation, reputation.MaxReputation));
891 
892  var reputationText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
893  string.Empty, textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
894  SetReputationText(reputationText);
895  reputation?.OnReputationValueChanged.RegisterOverwriteExisting("RefreshRoundSummary".ToIdentifier(), _ =>
896  {
897  SetReputationText(reputationText);
898  });
899 
900  void SetReputationText(GUITextBlock textBlock)
901  {
902  LocalizedString reputationText = Reputation.GetFormattedReputationText(reputation.NormalizedValue, reputation.Value, addColorTags: true);
903  int reputationChange = (int)Math.Round(reputation.Value - initialReputation);
904  if (Math.Abs(reputationChange) > 0)
905  {
906  string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
907  string colorStr = XMLExtensions.ToStringHex(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red);
908  textBlock.Text = RichString.Rich($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)");
909  }
910  else
911  {
912  textBlock.Text = RichString.Rich(reputationText);
913  }
914  }
915 
916  //spacing
917  new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), factionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(5)) }, style: null);
918 
919  var factionDescription = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.6f), factionTextContent.RectTransform),
920  shortDescription, font: GUIStyle.SmallFont, wrap: true)
921  {
922  UserData = "description",
923  Padding = Vector4.Zero
924  };
925  if (shortDescription != fullDescription && !fullDescription.IsNullOrEmpty())
926  {
927  factionDescription.ToolTip = fullDescription;
928  }
929 
930  //spacing
931  new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), factionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(5)) }, style: null);
932 
933  factionInfoHorizontal.Recalculate();
934  factionTextContent.Recalculate();
935 
936  return factionFrame;
937  }
938 
939  public static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation, float minReputation, float maxReputation)
940  {
941  int segmentWidth = rect.Width / 5;
942  rect.Width = segmentWidth * 5;
943  for (int i = 0; i < 5; i++)
944  {
945  GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), Reputation.GetReputationColor(i / 5.0f), isFilled: true);
946  GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), GUIStyle.ColorInventoryBackground, isFilled: false);
947  }
948  GUI.DrawRectangle(sb, rect, GUIStyle.ColorInventoryBackground, isFilled: false);
949 
950  GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.ColorInventoryBackground, scale: GUI.Scale, spriteEffect: SpriteEffects.FlipVertically);
951  GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.TextColorNormal, scale: GUI.Scale * 0.8f, spriteEffect: SpriteEffects.FlipVertically);
952 
953  GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), ((int)minReputation).ToString(), GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
954  string maxRepText = ((int)maxReputation).ToString();
955  Vector2 textSize = GUIStyle.SmallFont.MeasureString(maxRepText);
956  GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), maxRepText, GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
957  }
958  }
959 }
Biome(ContentXElement element, LevelGenerationParametersFile file)
Definition: Biome.cs:35
static LocalizedString GetTeamName(CharacterTeamType teamID)
IEnumerable< CharacterInfo > GetCharacterInfos()
Note: this only returns AI characters' infos in multiplayer. The infos are used to manage hiring/firi...
static EventPrefab GetUnlockPathEvent(Identifier biomeIdentifier, Faction faction)
Definition: EventPrefab.cs:150
FactionPrefab Prefab
Definition: Factions.cs:18
Reputation Reputation
Definition: Factions.cs:17
GUIComponent GetChild(int index)
Definition: GUIComponent.cs:54
void Pulsate(Vector2 startScale, Vector2 endScale, float duration)
virtual GUIFont Font
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
Definition: GUIComponent.cs:95
void FadeIn(float wait, float duration, bool alsoChildren=false)
virtual Rectangle Rect
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
Vector2 MeasureChar(char c)
Definition: GUIPrefab.cs:289
GUIFrame ContentBackground
A frame drawn behind the content of the listbox
Definition: GUIListBox.cs:37
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:42
static GameSession?? GameSession
Definition: GameMain.cs:88
static int GraphicsHeight
Definition: GameMain.cs:168
static bool IsMultiplayer
Definition: GameMain.cs:35
static NetworkMember NetworkMember
Definition: GameMain.cs:190
static GameClient Client
Definition: GameMain.cs:188
static ImmutableHashSet< Character > GetSessionCrewCharacters(CharacterType type)
Returns a list of crew characters currently in the game with a given filter.
bool IsGateBetweenBiomes
Definition: Location.cs:515
List< LocationConnection > Connections
virtual RichString GetMissionRewardText(Submarine sub)
Returns the full reward text of the mission (e.g. "Reward: 2,000 mk" or "Reward: 500 mk x 2 (out of m...
int GetFinalReward(Submarine sub)
Get the final reward, taking talent bonuses into account if the mission has concluded and the talents...
Vector2 RelativeSize
Relative to the parent rect.
Point?? MinSize
Min size in pixels. Does not affect scaling.
Point NonScaledSize
Size before scale multiplications.
bool IsFixedSize
If false, the element will resize if the parent is resized (with the children). If true,...
float??????? Value
Definition: Reputation.cs:44
Reputation(CampaignMetadata metadata, Location location, Identifier identifier, int minReputation, int maxReputation, int initialReputation)
Definition: Reputation.cs:128
static Color GetReputationColor(float normalizedValue)
Definition: Reputation.cs:179
LocalizedString GetFormattedReputationText(bool addColorTags=false)
Definition: Reputation.cs:199
static RichString Rich(LocalizedString str, Func< string, string >? postProcess=null)
Definition: RichString.cs:67
GUIFrame CreateSummaryFrame(GameSession gameSession, string endMessage, CampaignMode.TransitionType transitionType=CampaignMode.TransitionType.None, TraitorManager.TraitorResults? traitorResults=null)
Definition: RoundSummary.cs:53
void CreateReputationInfoPanel(GUIComponent parent, CampaignMode campaignMode)
static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation, float minReputation, float maxReputation)
static void AddSeparators(GUIComponent container)
static void UpdateMissionStateIcon(bool success, GUIImage missionIcon, float delay=0.5f)
GUILayoutGroup ButtonArea
Definition: RoundSummary.cs:32
static GUIComponent CreateMissionEntry(GUIComponent parent, LocalizedString header, List< LocalizedString > textContent, int difficultyIconCount, Sprite icon, Color iconColor, out GUIImage missionIcon)
RoundSummary(GameMode gameMode, IEnumerable< Mission > selectedMissions, Location startLocation, Location endLocation)
Definition: RoundSummary.cs:38
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
CharacterType
Definition: Enums.cs:711
@ Character
Characters only