Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Map/SubmarineInfo.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Globalization;
4 using System.Linq;
5 
6 namespace Barotrauma
7 {
8  partial class SubmarineInfo : IDisposable
9  {
11 
12  partial void InitProjectSpecific()
13  {
14  string previewImageData = SubmarineElement.GetAttributeString("previewimage", "");
15  if (!string.IsNullOrEmpty(previewImageData))
16  {
17  try
18  {
19  using (System.IO.MemoryStream mem = new System.IO.MemoryStream(Convert.FromBase64String(previewImageData)))
20  {
21  var texture = TextureLoader.FromStream(mem, path: FilePath, compress: false);
22  if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
23  PreviewImage = new Sprite(texture, sourceRectangle: null, newOffset: null, path: FilePath);
24  }
25  }
26  catch (Exception e)
27  {
28  DebugConsole.ThrowError("Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.", e);
29  GameAnalyticsManager.AddErrorEventOnce("Submarine..ctor:PreviewImageLoadingFailed", GameAnalyticsManager.ErrorSeverity.Error,
30  "Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.");
31  PreviewImage = null;
32  }
33  }
34  }
35 
36  public void CreatePreviewWindow(GUIComponent parent)
37  {
38  var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null);
39 
40  var previewButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), content.RectTransform), style: null)
41  {
42  CanBeFocused = SubmarineElement != null,
43  OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; },
44  };
45 
46  var previewImage = PreviewImage ?? savedSubmarines.Find(s => s.Name.Equals(Name, StringComparison.OrdinalIgnoreCase))?.PreviewImage;
47  if (previewImage == null)
48  {
49  new GUITextBlock(new RectTransform(Vector2.One, previewButton.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
50  }
51  else
52  {
53  var submarinePreviewBackground = new GUIFrame(new RectTransform(Vector2.One, previewButton.RectTransform), style: null)
54  {
55  Color = Color.Black,
56  HoverColor = Color.Black,
57  SelectedColor = Color.Black,
58  PressedColor = Color.Black,
59  CanBeFocused = false,
60  };
61  new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), previewImage, scaleToFit: true) { CanBeFocused = false };
62  new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black) { CanBeFocused = false };
63  }
64 
65  if (SubmarineElement != null)
66  {
67  new GUIFrame(new RectTransform(Vector2.One * 0.12f, previewButton.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight)
68  {
69  AbsoluteOffset = new Point((int)(0.03f * previewButton.Rect.Height))
70  },
71  "ExpandButton", Color.White)
72  {
73  Color = Color.White,
74  HoverColor = Color.White,
75  PressedColor = Color.White
76  };
77  }
78 
79  var descriptionBox = new GUIListBox(new RectTransform(new Vector2(1, 0.5f), content.RectTransform, Anchor.BottomCenter))
80  {
81  UserData = "descriptionbox",
82  ScrollBarVisible = true,
83  Spacing = 5,
84  CurrentSelectMode = GUIListBox.SelectMode.None
85  };
86 
87  GUIFont font = parent.Rect.Width < 350 ? GUIStyle.SmallFont : GUIStyle.Font;
88 
89  CreateSpecsWindow(descriptionBox, font, includeDescription: true);
90  }
91 
92  public void CreateSpecsWindow(GUIListBox parent, GUIFont font,
93  bool includeTitle = true,
94  bool includeClass = true,
95  bool includeDescription = false,
96  bool includeCrushDepth = false)
97  {
98  float leftPanelWidth = 0.6f;
99  float rightPanelWidth = 0.4f / leftPanelWidth;
100  LocalizedString className = !HasTag(SubmarineTag.Shuttle) ?
101  TextManager.GetWithVariables("submarine.classandtier",
102  ("[class]", TextManager.Get($"submarineclass.{SubmarineClass}")),
103  ("[tier]", TextManager.Get($"submarinetier.{Tier}"))) :
104  TextManager.Get("shuttle");
105 
106  int classHeight = (int)GUIStyle.SubHeadingFont.MeasureString(className).Y;
107  int leftPanelWidthInt = (int)(parent.Rect.Width * leftPanelWidth);
108 
109  GUITextBlock submarineNameText = null;
110  GUITextBlock submarineClassText = null;
111  if (includeTitle)
112  {
113  int nameHeight = (int)GUIStyle.LargeFont.MeasureString(DisplayName, true).Y;
114  submarineNameText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, nameHeight + HUDLayoutSettings.Padding / 2), parent.Content.RectTransform), DisplayName, textAlignment: Alignment.CenterLeft, font: GUIStyle.LargeFont)
115  {
116  CanBeFocused = false
117  };
118  submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
119  }
120  if (includeClass)
121  {
122  submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont)
123  {
124  ToolTip = TextManager.Get("submarinetierandclass.description")+"\n\n"+ TextManager.Get($"submarineclass.{SubmarineClass}.description")
125  };
126  submarineClassText.HoverColor = Color.Transparent;
127  submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
128  }
129 
130  if (Price > 0)
131  {
132  var priceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
133  TextManager.Get("subeditor.price"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
134  { CanBeFocused = false };
135  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), priceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
136  TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Price)), textAlignment: Alignment.TopLeft, font: font, wrap: true)
137  { CanBeFocused = false };
138  }
139 
140  Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
141  if (realWorldDimensions != Vector2.Zero)
142  {
143  LocalizedString dimensionsStr = TextManager.GetWithVariables("DimensionsFormat", ("[width]", ((int)realWorldDimensions.X).ToString()), ("[height]", ((int)realWorldDimensions.Y).ToString()));
144  var dimensionsText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
145  TextManager.Get("Dimensions"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
146  { CanBeFocused = false };
147  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), dimensionsText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
148  dimensionsStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
149  { CanBeFocused = false };
150  dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
151  }
152 
153  var cargoCapacityStr = CargoCapacity < 0 ? TextManager.Get("unknown") : TextManager.GetWithVariable("cargocapacityformat", "[cratecount]", CargoCapacity.ToString());
154  var cargoCapacityText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
155  TextManager.Get("cargocapacity"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
156  { CanBeFocused = false };
157  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), cargoCapacityText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
158  cargoCapacityStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
159  { CanBeFocused = false };
160  cargoCapacityText.RectTransform.MinSize = new Point(0, cargoCapacityText.Children.First().Rect.Height);
161 
162  if (includeCrushDepth)
163  {
164  var crushDepthText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
165  TextManager.Get("CrushDepth"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
166  {
167  CanBeFocused = false
168  };
169  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crushDepthText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
170  TextManager.GetWithVariable("meterformat", "[meters]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetSubCrushDepth())),
171  textAlignment: Alignment.TopLeft, font: font, wrap: true)
172  {
173  CanBeFocused = false
174  };
175  crushDepthText.RectTransform.MinSize = new Point(0, crushDepthText.Children.First().Rect.Height);
176  }
177 
178  if (RecommendedCrewSizeMax > 0)
179  {
180  var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
181  TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
182  { CanBeFocused = false };
183  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewSizeText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
184  RecommendedCrewSizeMin + " - " + RecommendedCrewSizeMax, textAlignment: Alignment.TopLeft, font: font, wrap: true)
185  { CanBeFocused = false };
186  crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
187  }
188 
190  {
191  var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
192  TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
193  { CanBeFocused = false };
194  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
195  TextManager.Get(RecommendedCrewExperience.ToIdentifier()), textAlignment: Alignment.TopLeft, font: font, wrap: true)
196  { CanBeFocused = false };
197  crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
198  }
199 
200  if (RequiredContentPackages.Any())
201  {
202  var contentPackagesText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
203  TextManager.Get("RequiredContentPackages"), textAlignment: Alignment.TopLeft, font: font)
204  { CanBeFocused = false };
205  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), contentPackagesText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
206  string.Join(", ", RequiredContentPackages), textAlignment: Alignment.TopLeft, font: font, wrap: true)
207  { CanBeFocused = false };
208  contentPackagesText.RectTransform.MinSize = new Point(0, contentPackagesText.Children.First().Rect.Height);
209  }
210 
211  // show what game version the submarine was created on
212  if (!IsVanillaSubmarine() && GameVersion != null)
213  {
214  var versionText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
215  TextManager.Get("serverlistversion"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
216  { CanBeFocused = false };
217  new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), versionText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
218  GameVersion.ToString(), textAlignment: Alignment.TopLeft, font: font, wrap: true)
219  { CanBeFocused = false };
220 
221  versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
222  }
223 
224  if (submarineNameText != null)
225  {
226  submarineNameText.AutoScaleHorizontal = true;
227  }
228 
229  GUITextBlock descBlock = null;
230  if (includeDescription)
231  {
232  //space
233  new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), parent.Content.RectTransform), style: null);
234 
235  if (!Description.IsNullOrEmpty())
236  {
237  var wsItemDesc = new GUITextBlock(new RectTransform(new Vector2(1, 0), parent.Content.RectTransform),
238  TextManager.Get("SaveSubDialogDescription", "WorkshopItemDescription"), font: GUIStyle.Font, wrap: true)
239  { CanBeFocused = false, ForceUpperCase = ForceUpperCase.Yes };
240 
241  descBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), parent.Content.RectTransform), Description, font: font, wrap: true)
242  {
243  CanBeFocused = false
244  };
245  }
246  }
247  GUITextBlock.AutoScaleAndNormalize(parent.Content.GetAllChildren<GUITextBlock>().Where(c => c != submarineNameText && c != descBlock));
248  parent.ForceLayoutRecalculation();
249  }
250 
251  public readonly record struct PendingSubInfo(SubmarineInfo PendingSub = null, bool StructuresDefineRealWorldCrushDepth = false, float RealWorldCrushDepth = Level.DefaultRealWorldCrushDepth);
252 
253  private float GetSubCrushDepth()
254  {
255  var pendingSubInfo = new PendingSubInfo();
256  return GetSubCrushDepth(this, ref pendingSubInfo);
257  }
258 
259  public static float GetSubCrushDepth(SubmarineInfo subInfo, ref PendingSubInfo pendingSubInfo)
260  {
261  float subCrushDepth = Level.DefaultRealWorldCrushDepth;
262  if (Submarine.MainSub != null && Submarine.MainSub.Info == subInfo)
263  {
264  subCrushDepth = Submarine.MainSub.RealWorldCrushDepth;
265  }
266  else if (subInfo != null)
267  {
268  if (pendingSubInfo.PendingSub != subInfo)
269  {
270  // Store the real world crush depth for the pending sub so that we don't have to calculate it again every time
271  pendingSubInfo = new PendingSubInfo(subInfo, subInfo.IsCrushDepthDefinedInStructures(out float realWorldCrushDepth), realWorldCrushDepth);
272  }
273  subCrushDepth = pendingSubInfo.RealWorldCrushDepth;
274  }
275  if (GameMain.GameSession?.Campaign?.UpgradeManager != null && UpgradePrefab.Find("increasewallhealth".ToIdentifier()) is UpgradePrefab hullUpgradePrefab)
276  {
277  int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First(), info: subInfo);
278  // If there is a sub switch pending, unless its structures have crush depth defined in their elements,
279  // calculate the value based on the default crush depth and pending upgrade level
280  int currentLevel = 0;
281  if (pendingSubInfo.PendingSub is null || pendingSubInfo.StructuresDefineRealWorldCrushDepth)
282  {
283  currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevelForSub(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First(), subInfo);
284  }
285  if (pendingLevel > currentLevel)
286  {
287  string updateValueStr = hullUpgradePrefab.SourceElement?.GetChildElement("Structure")?.GetAttributeString("crushdepth", null);
288  if (!string.IsNullOrEmpty(updateValueStr))
289  {
290  if (currentLevel > 0)
291  {
292  // If the current level is greater than 0, reset the crush depth value back to the base value before calculating the upgrade
293  int upgradePercentage = UpgradePrefab.ParsePercentage(updateValueStr, Identifier.Empty, suppressWarnings: true);
294  subCrushDepth /= (1f + (upgradePercentage / 100f * currentLevel));
295  }
296  subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel, updateValueStr);
297  }
298  }
299  }
300  return subCrushDepth;
301  }
302  }
303 }
virtual Rectangle Rect
IEnumerable< GUIComponent > GetAllChildren()
Returns all child elements in the hierarchy.
Definition: GUIComponent.cs:49
virtual Color HoverColor
RectTransform RectTransform
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
override void ForceLayoutRecalculation()
Definition: GUIListBox.cs:874
bool AutoScaleHorizontal
When enabled, the text is automatically scaled down to fit the textblock horizontally.
static void AutoScaleAndNormalize(params GUITextBlock[] textBlocks)
Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text with...
static GameSession?? GameSession
Definition: GameMain.cs:88
Point?? MinSize
Min size in pixels. Does not affect scaling.
readonly record struct PendingSubInfo(SubmarineInfo PendingSub=null, bool StructuresDefineRealWorldCrushDepth=false, float RealWorldCrushDepth=Level.DefaultRealWorldCrushDepth)
void CreateSpecsWindow(GUIListBox parent, GUIFont font, bool includeTitle=true, bool includeClass=true, bool includeDescription=false, bool includeCrushDepth=false)
static float GetSubCrushDepth(SubmarineInfo subInfo, ref PendingSubInfo pendingSubInfo)
static IEnumerable< SubmarineInfo > SavedSubmarines
static void Create(SubmarineInfo submarineInfo)
int GetUpgradeLevel(UpgradePrefab prefab, UpgradeCategory category, SubmarineInfo? info=null)
Gets the progress that is shown on the store interface. Includes values stored in the metadata and Pe...
int GetRealUpgradeLevelForSub(UpgradePrefab prefab, UpgradeCategory category, SubmarineInfo info)
Gets the level of the upgrade that is stored in the metadata. Takes into account the limits of the pr...