Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs
3 using FarseerPhysics;
4 using Microsoft.Xna.Framework;
5 using Microsoft.Xna.Framework.Graphics;
6 using System;
7 using System.Collections.Generic;
8 using System.Linq;
9 
11 {
12  partial class Steering : Powered, IServerSerializable, IClientSerializable
13  {
14  private GUIButton steeringModeSwitch;
15  private GUITickBox autopilotIndicator, manualPilotIndicator;
16 
17  enum Destination
18  {
20  LevelEnd,
21  LevelStart
22  };
23 
24  private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox;
25 
26  private GUIComponent statusContainer, dockingContainer;
27 
28  public GUIComponent ControlContainer { get; private set; }
29 
30  private bool dockingNetworkMessagePending;
31 
32  private GUIButton dockingButton;
33  private LocalizedString dockText, undockText;
34 
35  private GUIComponent steerArea;
36 
37  private GUITextBlock pressureWarningText, iceSpireWarningText;
38 
39  private GUITextBlock tipContainer;
40 
41  private LocalizedString noPowerTip, autoPilotMaintainPosTip, autoPilotLevelStartTip, autoPilotLevelEndTip;
42 
43  private Sprite maintainPosIndicator, maintainPosOriginIndicator;
44  private Sprite steeringIndicator;
45 
46  private List<DockingPort> connectedPorts = new List<DockingPort>();
47  private float checkConnectedPortsTimer;
48  private const float CheckConnectedPortsInterval = 1.0f;
49 
50  public DockingPort ActiveDockingSource, DockingTarget;
51 
52  private Vector2 keyboardInput = Vector2.Zero;
53  private float inputCumulation;
54 
55  private bool? swapDestinationOrder;
56 
57  private GUIMessageBox enterOutpostPrompt, exitOutpostPrompt;
58 
59  private bool levelStartSelected;
60  public bool LevelStartSelected
61  {
62  get { return levelStartTickBox.Selected; }
63  set { levelStartTickBox.Selected = value; }
64  }
65 
66  private bool levelEndSelected;
67  public bool LevelEndSelected
68  {
69  get { return levelEndTickBox.Selected; }
70  set { levelEndTickBox.Selected = value; }
71  }
72 
73  private bool maintainPos;
74  public bool MaintainPos
75  {
76  get { return maintainPosTickBox.Selected; }
77  set { maintainPosTickBox.Selected = value; }
78  }
79 
80  private float steerRadius;
81  public float? SteerRadius
82  {
83  get
84  {
85  return steerRadius;
86  }
87  set
88  {
89  steerRadius = value ?? (steerArea.Rect.Width / 2);
90  }
91  }
92 
93  private bool disableControls;
97  public bool DisableControls
98  {
99  get { return disableControls; }
100  set
101  {
102  if (disableControls == value) { return; }
103  disableControls = value;
104  UpdateGUIElements();
105  }
106  }
107 
108  public override bool RecreateGUIOnResolutionChange => true;
109 
110 
111  partial void InitProjSpecific(ContentXElement element)
112  {
113  foreach (var subElement in element.Elements())
114  {
115  switch (subElement.Name.ToString().ToLowerInvariant())
116  {
117  case "steeringindicator":
118  steeringIndicator = new Sprite(subElement);
119  break;
120  case "maintainposindicator":
121  maintainPosIndicator = new Sprite(subElement);
122  break;
123  case "maintainposoriginindicator":
124  maintainPosOriginIndicator = new Sprite(subElement);
125  break;
126  }
127  }
128  CreateGUI();
129  }
130 
131  protected override void CreateGUI()
132  {
133  ControlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterRight), "ItemUI");
134  var paddedControlContainer = new GUIFrame(new RectTransform(ControlContainer.Rect.Size - GUIStyle.ItemFrameMargin, ControlContainer.RectTransform, Anchor.Center)
135  {
136  AbsoluteOffset = GUIStyle.ItemFrameOffset
137  }, style: null);
138 
139  var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null);
140  steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical")
141  {
142  UserData = UIHighlightAction.ElementId.SteeringModeSwitch,
143  Selected = autoPilot,
144  Enabled = true,
145  ClickSound = GUISoundType.UISwitch,
146  OnClicked = (button, data) =>
147  {
148  button.Selected = !button.Selected;
149  AutoPilot = button.Selected;
150  if (GameMain.Client != null)
151  {
152  unsentChanges = true;
153  user = Character.Controlled;
154  }
155  return true;
156  }
157  };
158  var steeringModeRightSide = new GUIFrame(new RectTransform(new Vector2(1.0f - steeringModeSwitch.RectTransform.RelativeSize.X, 0.8f), steeringModeArea.RectTransform, Anchor.CenterLeft)
159  {
160  RelativeOffset = new Vector2(steeringModeSwitch.RectTransform.RelativeSize.X, 0)
161  }, style: null);
162  manualPilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.TopLeft),
163  TextManager.Get("SteeringManual"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
164  {
165  Selected = !autoPilot,
166  Enabled = false
167  };
168  autopilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.BottomLeft),
169  TextManager.Get("SteeringAutoPilot"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
170  {
171  Selected = autoPilot,
172  Enabled = false
173  };
174  manualPilotIndicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal);
175  autopilotIndicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal);
176  GUITextBlock.AutoScaleAndNormalize(manualPilotIndicator.TextBlock, autopilotIndicator.TextBlock);
177 
178  var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.62f), paddedControlContainer.RectTransform, Anchor.BottomCenter), "OutlineFrame");
179  var paddedAutoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.92f, 0.88f), autoPilotControls.RectTransform, Anchor.Center), style: null);
180 
181  int textLimit = (int)(paddedAutoPilotControls.Rect.Width * 0.75f);
182  maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
183  ToolBox.LimitString(TextManager.Get("SteeringMaintainPos"), GUIStyle.SmallFont, textLimit), font: GUIStyle.SmallFont, style: "GUIRadioButton")
184  {
185  UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
186  Enabled = autoPilot,
187  Selected = maintainPos,
188  OnSelected = tickBox =>
189  {
190  if (maintainPos != tickBox.Selected)
191  {
192  unsentChanges = true;
193  user = Character.Controlled;
194  maintainPos = tickBox.Selected;
195  if (maintainPos)
196  {
197  if (controlledSub == null)
198  {
199  posToMaintain = null;
200  }
201  else
202  {
203  posToMaintain = controlledSub.WorldPosition;
204  }
205  }
206  else if (!LevelEndSelected && !LevelStartSelected)
207  {
208  AutoPilot = false;
209  }
210  if (!maintainPos)
211  {
212  posToMaintain = null;
213  }
214  }
215  return true;
216  }
217  };
218  levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
219  GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.DisplayName, GUIStyle.SmallFont, textLimit),
220  font: GUIStyle.SmallFont, style: "GUIRadioButton")
221  {
222  Enabled = autoPilot,
223  Selected = levelStartSelected,
224  OnSelected = tickBox =>
225  {
226  if (levelStartSelected != tickBox.Selected)
227  {
228  unsentChanges = true;
229  user = Character.Controlled;
230  levelStartSelected = tickBox.Selected;
231  levelEndSelected = !levelStartSelected;
232  if (levelStartSelected)
233  {
234  UpdatePath();
235  }
236  else if (!MaintainPos && !LevelEndSelected)
237  {
238  AutoPilot = false;
239  }
240  }
241  return true;
242  }
243  };
244 
245  levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.BottomCenter),
246  (GameMain.GameSession?.EndLocation == null || Level.IsLoadedOutpost) ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.DisplayName, GUIStyle.SmallFont, textLimit),
247  font: GUIStyle.SmallFont, style: "GUIRadioButton")
248  {
249  Enabled = autoPilot,
250  Selected = levelEndSelected,
251  Visible = GameMain.GameSession?.EndLocation != null,
252  OnSelected = tickBox =>
253  {
254  if (levelEndSelected != tickBox.Selected)
255  {
256  unsentChanges = true;
257  user = Character.Controlled;
258  levelEndSelected = tickBox.Selected;
259  levelStartSelected = !levelEndSelected;
260  if (levelEndSelected)
261  {
262  UpdatePath();
263  }
264  else if (!MaintainPos && !LevelStartSelected)
265  {
266  AutoPilot = false;
267  }
268  }
269  return true;
270  }
271  };
272  maintainPosTickBox.RectTransform.IsFixedSize = levelStartTickBox.RectTransform.IsFixedSize = levelEndTickBox.RectTransform.IsFixedSize = false;
273  maintainPosTickBox.RectTransform.MaxSize = levelStartTickBox.RectTransform.MaxSize = levelEndTickBox.RectTransform.MaxSize =
274  new Point(int.MaxValue, paddedAutoPilotControls.Rect.Height / 3);
275  maintainPosTickBox.RectTransform.MinSize = levelStartTickBox.RectTransform.MinSize = levelEndTickBox.RectTransform.MinSize =
276  Point.Zero;
277 
278  GUITextBlock.AutoScaleAndNormalize(scaleHorizontal: false, scaleVertical: true, maintainPosTickBox.TextBlock, levelStartTickBox.TextBlock, levelEndTickBox.TextBlock);
279 
280  GUIRadioButtonGroup destinations = new GUIRadioButtonGroup();
281  destinations.AddRadioButton((int)Destination.MaintainPos, maintainPosTickBox);
282  destinations.AddRadioButton((int)Destination.LevelStart, levelStartTickBox);
283  destinations.AddRadioButton((int)Destination.LevelEnd, levelEndTickBox);
284  destinations.Selected = (int)(maintainPos ? Destination.MaintainPos :
285  levelStartSelected ? Destination.LevelStart : Destination.LevelEnd);
286 
287  // Status ->
288  statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight)
289  {
290  RelativeOffset = Sonar.controlBoxOffset
291  }, "ItemUI");
292  var paddedStatusContainer = new GUIFrame(new RectTransform(statusContainer.Rect.Size - GUIStyle.ItemFrameMargin, statusContainer.RectTransform, Anchor.Center, isFixedSize: false)
293  {
294  AbsoluteOffset = GUIStyle.ItemFrameOffset
295  }, style: null);
296 
297  var elements = GUI.CreateElements(3, new Vector2(1f, 0.333f), paddedStatusContainer.RectTransform, rt => new GUIFrame(rt, style: null), Anchor.TopCenter, relativeSpacing: 0.01f);
298  List<GUIComponent> leftElements = new List<GUIComponent>(), centerElements = new List<GUIComponent>(), rightElements = new List<GUIComponent>();
299  for (int i = 0; i < elements.Count; i++)
300  {
301  var e = elements[i];
302  var group = new GUILayoutGroup(new RectTransform(Vector2.One, e.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
303  {
304  RelativeSpacing = 0.01f,
305  Stretch = true
306  };
307  var left = new GUIFrame(new RectTransform(new Vector2(0.45f, 1), group.RectTransform), style: null);
308  var center = new GUIFrame(new RectTransform(new Vector2(0.15f, 1), group.RectTransform), style: null);
309  var right = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.8f), group.RectTransform), style: null);
310  leftElements.Add(left);
311  centerElements.Add(center);
312  rightElements.Add(right);
313  LocalizedString leftText = string.Empty, centerText = string.Empty;
314  GUITextBlock.TextGetterHandler rightTextGetter = null;
315  switch (i)
316  {
317  case 0:
318  leftText = TextManager.Get("DescentVelocity");
319  centerText = TextManager.Get("KilometersPerHour");
320  rightTextGetter = () =>
321  {
322  Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
323  var realWorldVel = ConvertUnits.ToDisplayUnits(vel.Y * Physics.DisplayToRealWorldRatio) * 3.6f;
324  return (-realWorldVel).ToString("0.0");
325  };
326  break;
327  case 1:
328  leftText = TextManager.Get("Velocity");
329  centerText = TextManager.Get("KilometersPerHour");
330  rightTextGetter = () =>
331  {
332  Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
333  var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
334  if (controlledSub != null && controlledSub.FlippedX) { realWorldVel *= -1; }
335  return realWorldVel.ToString("0.0");
336  };
337  break;
338  case 2:
339  leftText = TextManager.Get("Depth");
340  centerText = TextManager.Get("Meter");
341  rightTextGetter = () =>
342  {
343  if (Level.Loaded is { IsEndBiome: true })
344  {
345  return Timing.TotalTime % 5.0f < 0.5f ? Rand.Range(-9000, 9000).ToString() : "ERROR";
346  }
347  float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
348  return ((int)realWorldDepth).ToString();
349  };
350  break;
351  }
352  new GUITextBlock(new RectTransform(Vector2.One, left.RectTransform), leftText, font: GUIStyle.SubHeadingFont, wrap: leftText.Contains(" "), textAlignment: Alignment.CenterRight);
353  new GUITextBlock(new RectTransform(Vector2.One, center.RectTransform), centerText, font: GUIStyle.Font, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
354  var digitalFrame = new GUIFrame(new RectTransform(Vector2.One, right.RectTransform), style: "DigitalFrameDark");
355  new GUITextBlock(new RectTransform(Vector2.One * 0.85f, digitalFrame.RectTransform, Anchor.Center), "12345", GUIStyle.TextColorDark, GUIStyle.DigitalFont, Alignment.CenterRight)
356  {
357  TextGetter = rightTextGetter
358  };
359  }
360  GUITextBlock.AutoScaleAndNormalize(leftElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
361  GUITextBlock.AutoScaleAndNormalize(centerElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
362  GUITextBlock.AutoScaleAndNormalize(rightElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
363 
364  //docking interface ----------------------------------------------------
365  float dockingButtonSize = 1.1f;
366  float elementScale = 0.6f;
367  dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
368  {
369  RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, -0.05f)
370  }, style: null);
371 
372  dockText = TextManager.Get("label.navterminaldock", "captain.dock");
373  undockText = TextManager.Get("label.navterminalundock", "captain.undock");
374  dockingButton = new GUIButton(new RectTransform(new Vector2(elementScale), dockingContainer.RectTransform, Anchor.Center), dockText, style: "PowerButton")
375  {
376  OnClicked = (btn, userdata) =>
377  {
378  if (GameMain.GameSession?.Missions.Any(m => !m.AllowUndocking) ?? false)
379  {
380  new GUIMessageBox("", TextManager.Get("undockingdisabledbymission"));
381  return false;
382  }
383  else if (GameMain.GameSession?.Campaign is CampaignMode campaign)
384  {
385  if (Level.IsLoadedOutpost &&
386  DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
387  {
388  // Undocking from an outpost
389  if (!ObjectiveManager.AllActiveObjectivesCompleted())
390  {
391  exitOutpostPrompt = new GUIMessageBox("",
392  TextManager.GetWithVariable("CampaignExitTutorialOutpostPrompt", "[locationname]", campaign.Map.CurrentLocation.DisplayName),
393  new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
394  exitOutpostPrompt.Buttons[0].OnClicked += (_, _) =>
395  {
396  exitOutpostPrompt.Close();
397  return OpenMap(campaign);
398  };
399  exitOutpostPrompt.Buttons[1].OnClicked += exitOutpostPrompt.Close;
400  return false;
401  }
402  return OpenMap(campaign);
403  }
404  else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
405  !ActiveDockingSource.Docked && DockingTarget?.Item?.Submarine == Level.Loaded.StartOutpost && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
406  {
407  // Docking to an outpost
408  var subsToLeaveBehind = CampaignMode.GetSubsToLeaveBehind(Item.Submarine);
409  if (subsToLeaveBehind.Any())
410  {
411  enterOutpostPrompt = new GUIMessageBox(
412  TextManager.GetWithVariable("enterlocation", "[locationname]", DockingTarget.Item.Submarine.Info.Name),
413  TextManager.Get(subsToLeaveBehind.Count == 1 ? "LeaveSubBehind" : "LeaveSubsBehind"),
414  new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
415  }
416  else
417  {
418  enterOutpostPrompt = new GUIMessageBox("",
419  TextManager.GetWithVariable("campaignenteroutpostprompt", "[locationname]", DockingTarget.Item.Submarine.Info.Name),
420  new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
421  }
422  enterOutpostPrompt.Buttons[0].OnClicked += (btn, userdata) =>
423  {
424  SendDockingSignal();
425  enterOutpostPrompt.Close();
426  return true;
427  };
428  enterOutpostPrompt.Buttons[1].OnClicked += enterOutpostPrompt.Close;
429  return false;
430  }
431  }
432  SendDockingSignal();
433 
434  return true;
435  }
436  };
437 
438  bool OpenMap(CampaignMode campaign)
439  {
440  campaign.ShowCampaignUI = true;
441  campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
442  return false;
443  }
444 
445  void SendDockingSignal()
446  {
447  if (GameMain.Client == null)
448  {
449  item.SendSignal(new Signal("1", sender: Character.Controlled), "toggle_docking");
450  }
451  else
452  {
453  dockingNetworkMessagePending = true;
454  item.CreateClientEvent(this);
455  }
456  }
457 
458  dockingButton.Font = GUIStyle.SubHeadingFont;
459  dockingButton.TextBlock.RectTransform.MaxSize = new Point((int)(dockingButton.Rect.Width * 0.7f), int.MaxValue);
460  dockingButton.TextBlock.AutoScaleHorizontal = true;
461 
462  var style = GUIStyle.GetComponentStyle("DockingButtonUp");
463  Sprite buttonSprite = style.Sprites.FirstOrDefault().Value.FirstOrDefault()?.Sprite;
464  Point buttonSize = buttonSprite != null ? buttonSprite.size.ToPoint() : new Point(149, 52);
465  Point horizontalButtonSize = buttonSize.Multiply(elementScale * GUI.Scale * dockingButtonSize);
466  Point verticalButtonSize = horizontalButtonSize.Flip();
467  var leftButton = new GUIButton(new RectTransform(verticalButtonSize, dockingContainer.RectTransform, Anchor.CenterLeft), "", style: "DockingButtonLeft")
468  {
469  OnClicked = NudgeButtonClicked,
470  UserData = -Vector2.UnitX
471  };
472  var rightButton = new GUIButton(new RectTransform(verticalButtonSize, dockingContainer.RectTransform, Anchor.CenterRight), "", style: "DockingButtonRight")
473  {
474  OnClicked = NudgeButtonClicked,
475  UserData = Vector2.UnitX
476  };
477  var upButton = new GUIButton(new RectTransform(horizontalButtonSize, dockingContainer.RectTransform, Anchor.TopCenter), "", style: "DockingButtonUp")
478  {
479  OnClicked = NudgeButtonClicked,
480  UserData = Vector2.UnitY
481  };
482  var downButton = new GUIButton(new RectTransform(horizontalButtonSize, dockingContainer.RectTransform, Anchor.BottomCenter), "", style: "DockingButtonDown")
483  {
484  OnClicked = NudgeButtonClicked,
485  UserData = -Vector2.UnitY
486  };
487 
488  // Sonar area
489  steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest),
490  (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
491  steerRadius = steerArea.Rect.Width / 2;
492 
493  iceSpireWarningText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
494  TextManager.Get("NavTerminalIceSpireWarning"), GUIStyle.Red, GUIStyle.SubHeadingFont, Alignment.Center, color: Color.Black * 0.8f, wrap: true)
495  {
496  Visible = false
497  };
498  pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
499  TextManager.Get("SteeringDepthWarning"), GUIStyle.Red, GUIStyle.SubHeadingFont, Alignment.Center, color: Color.Black * 0.8f)
500  {
501  Visible = false
502  };
503  // Tooltip/helper text
504  tipContainer = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), steerArea.RectTransform, Anchor.BottomCenter, Pivot.TopCenter)
505  , "", font: GUIStyle.Font, wrap: true, style: "GUIToolTip", textAlignment: Alignment.Center)
506  {
507  AutoScaleHorizontal = true
508  };
509  noPowerTip = TextManager.Get("SteeringNoPowerTip");
510  autoPilotMaintainPosTip = TextManager.Get("SteeringAutoPilotMaintainPosTip");
511  autoPilotLevelStartTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
512  GameMain.GameSession?.StartLocation == null ? "Start" : GameMain.GameSession.StartLocation.DisplayName);
513  autoPilotLevelEndTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
514  GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.DisplayName);
515  }
516 
517  protected override void OnResolutionChanged()
518  {
519  UpdateGUIElements();
520  }
521 
525  public void AttachToSonarHUD(GUICustomComponent sonarView)
526  {
527  steerArea.Visible = false;
528  sonarView.OnDraw += (spriteBatch, guiCustomComponent) =>
529  {
530  DrawHUD(spriteBatch, guiCustomComponent.Rect);
531  steerArea.DrawChildren(spriteBatch, recursive: true);
532  };
533  }
534 
538  private static Vector2 MapSquareToCircle(Vector2 steeringVector)
539  {
540  float xSqr = steeringVector.X * steeringVector.X;
541  float ySqr = steeringVector.Y * steeringVector.Y;
542  float length = MathF.Sqrt(ySqr + xSqr);
543  if (MathUtils.NearlyEqual(length, 0.0f)) { return Vector2.Zero; }
544 
545  //FG-Squircular mapping formula from https://arxiv.org/ftp/arxiv/papers/1509/1509.06344.pdf
546  float x = steeringVector.X * MathF.Sqrt(xSqr + ySqr - xSqr * ySqr) / length;
547  float y = steeringVector.Y * MathF.Sqrt(xSqr + ySqr - xSqr * ySqr) / length;
548  return new Vector2(x, y);
549  }
550 
551  public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect)
552  {
553  int width = rect.Width, height = rect.Height;
554  int x = rect.X;
555  int y = rect.Y;
556 
557  if (Voltage < MinVoltage) { return; }
558 
559  Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
560  Vector2 steeringOrigin = steerArea.Rect.Center.ToVector2();
561 
562  if (!AutoPilot)
563  {
564  //map input from rectangle to circle
565  Vector2 steeringInputPos = MapSquareToCircle(steeringInput / 100f) * 100.0f;
566  steeringInputPos.Y = -steeringInputPos.Y;
567  steeringInputPos += steeringOrigin;
568 
569  if (steeringIndicator != null)
570  {
571  Vector2 dir = steeringInputPos - steeringOrigin;
572  float angle = (float)Math.Atan2(dir.Y, dir.X);
573  steeringIndicator.Draw(spriteBatch, steeringOrigin, Color.White, origin: steeringIndicator.Origin, rotate: angle,
574  scale: new Vector2(dir.Length() / steeringIndicator.size.X, 1.0f));
575  }
576  else
577  {
578  GUI.DrawLine(spriteBatch, steeringOrigin, steeringInputPos, Color.LightGray);
579  GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 5, (int)steeringInputPos.Y - 5, 10, 10), Color.White);
580  }
581 
582  if (velRect.Contains(PlayerInput.MousePosition))
583  {
584  GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 4, (int)steeringInputPos.Y - 4, 8, 8), GUIStyle.Red, thickness: 2);
585  }
586  }
587  else if (posToMaintain.HasValue && !LevelStartSelected && !LevelEndSelected)
588  {
589  Sonar sonar = item.GetComponent<Sonar>();
590  if (sonar != null && controlledSub != null)
591  {
592  Vector2 displayPosToMaintain = ((posToMaintain.Value - controlledSub.WorldPosition)) * sonar.DisplayScale;
593 
594  displayPosToMaintain.Y = -displayPosToMaintain.Y;
595  displayPosToMaintain = displayPosToMaintain.ClampLength(velRect.Width / 2);
596  displayPosToMaintain = steerArea.Rect.Center.ToVector2() + displayPosToMaintain;
597 
598  Color crosshairColor = GUIStyle.Orange * (0.5f + ((float)Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 4.0f);
599  if (maintainPosIndicator != null)
600  {
601  maintainPosIndicator.Draw(spriteBatch, displayPosToMaintain, crosshairColor, scale: 0.5f * sonar.Zoom);
602  }
603  else
604  {
605  float crossHairSize = 8.0f;
606  GUI.DrawLine(spriteBatch, displayPosToMaintain + Vector2.UnitY * crossHairSize, displayPosToMaintain - Vector2.UnitY * crossHairSize, crosshairColor, width: 3);
607  GUI.DrawLine(spriteBatch, displayPosToMaintain + Vector2.UnitX * crossHairSize, displayPosToMaintain - Vector2.UnitX * crossHairSize, crosshairColor, width: 3);
608  }
609 
610  if (maintainPosOriginIndicator != null)
611  {
612  maintainPosOriginIndicator.Draw(spriteBatch, steeringOrigin, GUIStyle.Orange, scale: 0.5f * sonar.Zoom);
613  }
614  else
615  {
616  GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringOrigin.X - 5, (int)steeringOrigin.Y - 5, 10, 10), GUIStyle.Orange);
617  }
618  }
619  }
620 
621  //map velocity from rectangle to circle
622  Vector2 steeringPos = MapSquareToCircle(targetVelocity / 100f) * 90.0f;
623  steeringPos.Y = -steeringPos.Y;
624  steeringPos += steeringOrigin;
625 
626  if (steeringIndicator != null)
627  {
628  Vector2 dir = steeringPos - steeringOrigin;
629  float angle = (float)Math.Atan2(dir.Y, dir.X);
630  steeringIndicator.Draw(spriteBatch, steeringOrigin, Color.Gray, origin: steeringIndicator.Origin, rotate: angle,
631  scale: new Vector2(dir.Length() / steeringIndicator.size.X, 0.7f));
632  }
633  else
634  {
635  GUI.DrawLine(spriteBatch,
636  steeringOrigin,
637  steeringPos,
638  Color.CadetBlue, 0, 2);
639  }
640  }
641 
642  public void DebugDrawHUD(SpriteBatch spriteBatch, Vector2 transducerCenter, float displayScale, float displayRadius, Vector2 center)
643  {
644  if (SteeringPath == null) return;
645 
646  Vector2 prevPos = Vector2.Zero;
647  foreach (WayPoint wp in SteeringPath.Nodes)
648  {
649  Vector2 pos = (wp.Position - transducerCenter) * displayScale;
650  if (pos.Length() > displayRadius) continue;
651 
652  pos.Y = -pos.Y;
653  pos += center;
654 
655  GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 3 / 2, (int)pos.Y - 3, 6, 6), (SteeringPath.CurrentNode == wp) ? Color.LightGreen : GUIStyle.Green, false);
656 
657  if (prevPos != Vector2.Zero)
658  {
659  GUI.DrawLine(spriteBatch, pos, prevPos, GUIStyle.Green);
660  }
661 
662  prevPos = pos;
663  }
664 
665  foreach (ObstacleDebugInfo obstacle in debugDrawObstacles)
666  {
667  Vector2 pos1 = (obstacle.Point1 - transducerCenter) * displayScale;
668  pos1.Y = -pos1.Y;
669  pos1 += center;
670  Vector2 pos2 = (obstacle.Point2 - transducerCenter) * displayScale;
671  pos2.Y = -pos2.Y;
672  pos2 += center;
673 
674  GUI.DrawLine(spriteBatch,
675  pos1,
676  pos2,
677  GUIStyle.Red * 0.6f, width: 3);
678 
679  if (obstacle.Intersection.HasValue)
680  {
681  Vector2 intersectionPos = (obstacle.Intersection.Value - transducerCenter) * displayScale;
682  intersectionPos.Y = -intersectionPos.Y;
683  intersectionPos += center;
684  GUI.DrawRectangle(spriteBatch, intersectionPos - Vector2.One * 2, Vector2.One * 4, GUIStyle.Red);
685  }
686 
687  Vector2 obstacleCenter = (pos1 + pos2) / 2;
688  if (obstacle.AvoidStrength.LengthSquared() > 0.01f)
689  {
690  GUI.DrawLine(spriteBatch,
691  obstacleCenter,
692  obstacleCenter + new Vector2(obstacle.AvoidStrength.X, -obstacle.AvoidStrength.Y) * 100,
693  Color.Lerp(GUIStyle.Green, GUIStyle.Orange, obstacle.Dot), width: 2);
694  }
695  }
696  }
697 
698  public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
699  {
700  if (swapDestinationOrder == null)
701  {
702  swapDestinationOrder = item.Submarine != null && item.Submarine.FlippedX;
703  if (swapDestinationOrder.Value)
704  {
705  levelStartTickBox.RectTransform.SetAsLastChild();
706  }
707  }
708 
709  if (steerArea.Rect.Contains(PlayerInput.MousePosition))
710  {
711  if (!PlayerInput.KeyDown(InputType.Deselect) && !PlayerInput.KeyHit(InputType.Deselect))
712  {
713  Character.DisableControls = true;
714  }
715  }
716 
717  if (DisableControls)
718  {
719  dockingModeEnabled = false;
720  }
721 
722  dockingContainer.Visible = DockingModeEnabled;
723  statusContainer.Visible = !DockingModeEnabled;
724  if (!DockingModeEnabled)
725  {
726  enterOutpostPrompt?.Close();
727  }
728 
729  if (DockingModeEnabled && ActiveDockingSource != null)
730  {
731  if (Math.Abs(ActiveDockingSource.Item.WorldPosition.X - DockingTarget.Item.WorldPosition.X) < ActiveDockingSource.DistanceTolerance.X &&
732  Math.Abs(ActiveDockingSource.Item.WorldPosition.Y - DockingTarget.Item.WorldPosition.Y) < ActiveDockingSource.DistanceTolerance.Y)
733  {
734  dockingButton.Text = dockText;
735  if (dockingButton.FlashTimer <= 0.0f)
736  {
737  dockingButton.Flash(GUIStyle.Blue, 0.5f, useCircularFlash: true);
738  dockingButton.Pulsate(Vector2.One, Vector2.One * 1.2f, dockingButton.FlashTimer);
739  }
740  }
741  else
742  {
743  enterOutpostPrompt?.Close();
744  }
745  }
746  else if (connectedPorts.Any(d => d.Docked))
747  {
748  dockingButton.Text = undockText;
749  dockingContainer.Visible = true;
750  statusContainer.Visible = false;
751  if (dockingButton.FlashTimer <= 0.0f)
752  {
753  dockingButton.Flash(GUIStyle.Orange, useCircularFlash: true);
754  dockingButton.Pulsate(Vector2.One, Vector2.One * 1.2f, dockingButton.FlashTimer);
755  }
756  }
757  else
758  {
759  dockingButton.Text = dockText;
760  }
761 
762  if (Voltage < MinVoltage)
763  {
764  tipContainer.Visible = true;
765  tipContainer.Text = noPowerTip;
766  return;
767  }
768 
769  tipContainer.Visible = AutoPilot;
770  if (AutoPilot)
771  {
772  if (maintainPos)
773  {
774  tipContainer.Text = autoPilotMaintainPosTip;
775  }
776  else if (LevelStartSelected)
777  {
778  tipContainer.Text = autoPilotLevelStartTip;
779  }
780  else if (LevelEndSelected)
781  {
782  tipContainer.Text = autoPilotLevelEndTip;
783  }
784 
785  if (DockingModeEnabled && DockingTarget != null)
786  {
787  posToMaintain += ConvertUnits.ToDisplayUnits(DockingTarget.Item.Submarine.Velocity) * deltaTime;
788  }
789  }
790 
791  pressureWarningText.Visible = item.Submarine != null && Timing.TotalTime % 1.0f < 0.8f;
792  if (Level.Loaded != null && pressureWarningText.Visible &&
793  item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - PressureWarningThreshold &&
794  item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - PressureWarningThreshold)
795  {
796  pressureWarningText.Visible = true;
797  pressureWarningText.Text =
798  item.Submarine.AtDamageDepth ?
799  TextManager.Get("SteeringDepthWarning") :
800  TextManager.GetWithVariable("SteeringDepthWarningLow", "[crushdepth]", ((int)item.Submarine.RealWorldCrushDepth).ToString());
801  }
802  else
803  {
804  pressureWarningText.Visible = false;
805  }
806 
807  iceSpireWarningText.Visible = item.Submarine != null && !pressureWarningText.Visible && showIceSpireWarning && Timing.TotalTime % 1.0f < 0.8f;
808 
809  if (!disableControls && Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
810  {
812  (!GameMain.GameSession?.Campaign?.ShowCampaignUI ?? true) && !GUIMessageBox.MessageBoxes.Any(msgBox => msgBox is GUIMessageBox { MessageBoxType: GUIMessageBox.Type.Default }))
813  {
814  Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2();
815  inputPos.Y = -inputPos.Y;
816  if (AutoPilot && !LevelStartSelected && !LevelEndSelected)
817  {
818  posToMaintain = controlledSub != null ?
819  controlledSub.WorldPosition + inputPos / sonar.DisplayRadius * sonar.Range / sonar.Zoom :
820  item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
821  }
822  else
823  {
824  SteeringInput = inputPos;
825  }
826  unsentChanges = true;
827  user = Character.Controlled;
828  }
829  }
830  if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
831  {
832  steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
833  Vector2 input = Vector2.Zero;
834  if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
835  if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
836  if (PlayerInput.KeyDown(InputType.Up)) { input += Vector2.UnitY; }
837  if (PlayerInput.KeyDown(InputType.Down)) { input -= Vector2.UnitY; }
838  if (PlayerInput.KeyDown(InputType.Run))
839  {
840  SteeringInput += input * deltaTime * 200;
841  inputCumulation = 0;
842  keyboardInput = Vector2.Zero;
843  unsentChanges = true;
844  }
845  else
846  {
847  float step = deltaTime * 5;
848  if (input.Length() > 0)
849  {
850  inputCumulation += step;
851  }
852  else
853  {
854  inputCumulation -= step;
855  }
856  float maxCumulation = 1;
857  inputCumulation = MathHelper.Clamp(inputCumulation, 0, maxCumulation);
858  float length = MathHelper.Lerp(0, 0.2f, MathUtils.InverseLerp(0, maxCumulation, inputCumulation));
859  var normalizedInput = Vector2.Normalize(input);
860  if (MathUtils.IsValid(normalizedInput))
861  {
862  keyboardInput += normalizedInput * length;
863  }
864  if (keyboardInput.LengthSquared() > 0.01f)
865  {
866  SteeringInput += keyboardInput;
867  unsentChanges = true;
868  user = Character.Controlled;
869  keyboardInput *= MathHelper.Clamp(1 - step, 0, 1);
870  }
871  }
872  }
873  else
874  {
875  inputCumulation = 0;
876  keyboardInput = Vector2.Zero;
877  }
878 
879  if (!UseAutoDocking || DisableControls) { return; }
880 
881  if (checkConnectedPortsTimer <= 0.0f)
882  {
883  Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
884  if (dockingConnection != null)
885  {
886  connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true, allowTraversingBackwards: false);
887  }
888  checkConnectedPortsTimer = CheckConnectedPortsInterval;
889  }
890  else
891  {
892  checkConnectedPortsTimer -= deltaTime;
893  }
894 
895  DockingModeEnabled = false;
896 
897  if (connectedPorts.None()) { return; }
898 
899  float closestDist = DockingAssistThreshold * DockingAssistThreshold;
900  foreach (DockingPort sourcePort in connectedPorts)
901  {
902  if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; }
903  if (sourcePort.Item.Submarine != controlledSub) { continue; }
904 
905  int sourceDir = sourcePort.GetDir();
906 
907  foreach (DockingPort targetPort in DockingPort.List)
908  {
909  if (targetPort.Docked || targetPort.Item.Submarine == null) { continue; }
910  if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; }
911  if (targetPort.Item.Submarine.DockedTo?.Contains(sourcePort.Item.Submarine) ?? false) { continue; }
912  if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
913  if (sourceDir == targetPort.GetDir()) { continue; }
914 
915  float dist = Vector2.DistanceSquared(sourcePort.Item.WorldPosition, targetPort.Item.WorldPosition);
916  if (dist < closestDist)
917  {
918  closestDist = dist;
919  DockingModeEnabled = true;
920  ActiveDockingSource = sourcePort;
921  DockingTarget = targetPort;
922  }
923  }
924  }
925  }
926 
927  private bool NudgeButtonClicked(GUIButton btn, object userdata)
928  {
929  if (!MaintainPos || !AutoPilot)
930  {
931  AutoPilot = true;
932  posToMaintain = item.Submarine.WorldPosition;
933  }
934  MaintainPos = true;
935  if (userdata is Vector2 nudgeAmount)
936  {
937  if (item.GetComponent<Sonar>() is Sonar sonar)
938  {
939  nudgeAmount *= 500.0f / sonar.Zoom;
940  }
941  PosToMaintain += nudgeAmount;
942  }
943  return true;
944  }
945 
946  protected override void RemoveComponentSpecific()
947  {
948  base.RemoveComponentSpecific();
949  maintainPosIndicator?.Remove();
950  maintainPosOriginIndicator?.Remove();
951  steeringIndicator?.Remove();
952  enterOutpostPrompt?.Close();
953  exitOutpostPrompt?.Close();
954  pathFinder = null;
955  }
956 
957  public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
958  {
959  msg.WriteBoolean(AutoPilot);
960  msg.WriteBoolean(dockingNetworkMessagePending);
961  dockingNetworkMessagePending = false;
962 
963  if (!AutoPilot)
964  {
965  //no need to write steering info if autopilot is controlling
966  msg.WriteSingle(steeringInput.X);
967  msg.WriteSingle(steeringInput.Y);
968  }
969  else
970  {
971  msg.WriteBoolean(posToMaintain != null);
972  if (posToMaintain != null)
973  {
974  msg.WriteSingle(((Vector2)posToMaintain).X);
975  msg.WriteSingle(((Vector2)posToMaintain).Y);
976  }
977  else
978  {
979  msg.WriteBoolean(LevelStartSelected);
980  }
981  }
982  }
983 
984  public void ClientEventRead(IReadMessage msg, float sendingTime)
985  {
986  int msgStartPos = msg.BitPosition;
987 
988  bool autoPilot = msg.ReadBoolean();
989  bool dockingButtonClicked = msg.ReadBoolean();
990  ushort userID = msg.ReadUInt16();
991 
992  Vector2 newSteeringInput = steeringInput;
993  Vector2 newTargetVelocity = targetVelocity;
994  float newSteeringAdjustSpeed = steeringAdjustSpeed;
995  Vector2? newPosToMaintain = null;
996  bool headingToStart = false;
997 
998  if (dockingButtonClicked)
999  {
1000  item.SendSignal(new Signal("1", sender: Entity.FindEntityByID(userID) as Character), "toggle_docking");
1001  }
1002 
1003  if (autoPilot)
1004  {
1005  if (msg.ReadBoolean())
1006  {
1007  newPosToMaintain = new Vector2(
1008  msg.ReadSingle(),
1009  msg.ReadSingle());
1010  }
1011  else
1012  {
1013  headingToStart = msg.ReadBoolean();
1014  }
1015  }
1016  else
1017  {
1018  newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
1019  newTargetVelocity = new Vector2(msg.ReadSingle(), msg.ReadSingle());
1020  newSteeringAdjustSpeed = msg.ReadSingle();
1021  }
1022 
1023  if (correctionTimer > 0.0f)
1024  {
1025  int msgLength = (int)(msg.BitPosition - msgStartPos);
1026  msg.BitPosition = msgStartPos;
1027  StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime);
1028  return;
1029  }
1030 
1031  AutoPilot = autoPilot;
1032 
1033  if (!AutoPilot)
1034  {
1035  SteeringInput = newSteeringInput;
1036  TargetVelocity = newTargetVelocity;
1037  steeringAdjustSpeed = newSteeringAdjustSpeed;
1038  }
1039  else
1040  {
1041  MaintainPos = newPosToMaintain != null;
1042  posToMaintain = newPosToMaintain;
1043 
1044  if (posToMaintain == null)
1045  {
1046  LevelStartSelected = headingToStart;
1047  LevelEndSelected = !headingToStart;
1048  UpdatePath();
1049  }
1050  else
1051  {
1052  LevelStartSelected = false;
1053  LevelEndSelected = false;
1054  }
1055  }
1056  }
1057 
1058  private void UpdateGUIElements()
1059  {
1060  steeringModeSwitch.Selected = AutoPilot;
1061  autopilotIndicator.Selected = AutoPilot;
1062  manualPilotIndicator.Selected = !AutoPilot;
1063  if (DisableControls)
1064  {
1065  steeringModeSwitch.Enabled = false;
1066  maintainPosTickBox.Enabled = false;
1067  levelEndTickBox.Enabled = false;
1068  levelStartTickBox.Enabled = false;
1069  }
1070  else
1071  {
1072  steeringModeSwitch.Enabled = true;
1073  maintainPosTickBox.Enabled = AutoPilot;
1074  levelEndTickBox.Enabled = AutoPilot;
1075  levelStartTickBox.Enabled = AutoPilot;
1076  }
1077  }
1078  }
1079 }
float GetSkillLevel(string skillIdentifier)
Responsible for keeping track of the characters in the player crew, saving and loading their orders,...
Submarine Submarine
Definition: Entity.cs:53
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
Definition: Entity.cs:204
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectInflate=null)
Definition: GUIButton.cs:211
override GUIFont? Font
Definition: GUIButton.cs:125
GUITextBlock TextBlock
Definition: GUIButton.cs:11
LocalizedString Text
Definition: GUIButton.cs:138
override float FlashTimer
Definition: GUIButton.cs:120
void Pulsate(Vector2 startScale, Vector2 endScale, float duration)
virtual void DrawChildren(SpriteBatch spriteBatch, bool recursive)
Draws all the children manually.
virtual Rectangle Rect
RectTransform RectTransform
GUIComponent that can be used to render custom content on the UI
Action< SpriteBatch, GUICustomComponent > OnDraw
static readonly List< GUIComponent > MessageBoxes
List< GUIButton > Buttons
void AddRadioButton(int key, GUITickBox radioButton)
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...
delegate LocalizedString TextGetterHandler()
static GameSession?? GameSession
Definition: GameMain.cs:88
static GameClient Client
Definition: GameMain.cs:188
void SendSignal(string signal, string connectionName)
float DisplayScale
Current scale of the display, taking zoom into account. In other words, the scaling factor of world c...
override void CreateGUI()
Overload this method and implement. The method is automatically called when the resolution changes.
bool DisableControls
Can be used by status effects to disable all the UI controls
void DebugDrawHUD(SpriteBatch spriteBatch, Vector2 transducerCenter, float displayScale, float displayRadius, Vector2 center)
void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData=null)
void AttachToSonarHUD(GUICustomComponent sonarView)
Makes the sonar view CustomComponent render the steering HUD, preventing it from being drawn behing t...
override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
float RealWorldCrushDepth
The crush depth of a non-upgraded submarine in "real world units" (meters from the surface of Europa)...
static bool IsLoadedOutpost
Is there a loaded level set and is it an outpost?
override string ToString()
LocalizedString DisplayName
Definition: Location.cs:58
static bool KeyDown(InputType inputType)
Point?? MaxSize
Max size in pixels. Does not affect scaling.
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
List< WayPoint > Nodes
float? RealWorldDepth
How deep down the sub is from the surface of Europa in meters (affected by level type,...
Highlights an UI element of some kind. Generally used in tutorials.
Interface for entities that the clients can send events to the server
Interface for entities that the server can send events to the clients
GUISoundType
Definition: GUI.cs:21