Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Map/WayPoint.cs
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using Microsoft.Xna.Framework.Input;
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 
9 namespace Barotrauma
10 {
11  partial class WayPoint : MapEntity
12  {
13  private static Dictionary<string, Sprite> iconSprites;
14  private const int WaypointSize = 12, SpawnPointSize = 32;
15 
16  public override bool IsVisible(Rectangle worldView)
17  {
19  }
20 
21  public override bool SelectableInEditor
22  {
23  get { return ShouldDrawIcon(); }
24  }
25 
26  public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
27  {
28  if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
29  if (!ShouldDrawIcon()) { return; }
30 
31  Vector2 drawPos = Position;
32  if (Submarine != null) { drawPos += Submarine.DrawPosition; }
33  drawPos.Y = -drawPos.Y;
34 
35  Draw(spriteBatch, drawPos);
36  }
37 
38  public void Draw(SpriteBatch spriteBatch, Vector2 drawPos)
39  {
40  Color clr = CurrentHull == null ? Color.DodgerBlue : GUIStyle.Green;
41  if (spawnType != SpawnType.Path) { clr = Color.Gray; }
42  if (!IsTraversable)
43  {
44  clr = Color.Black;
45  }
46  if (IsHighlighted || IsHighlighted) { clr = Color.Lerp(clr, Color.White, 0.8f); }
47 
48  int iconSize = spawnType == SpawnType.Path ? WaypointSize : SpawnPointSize;
49  if (ConnectedDoor != null || Ladders != null || Stairs != null || SpawnType != SpawnType.Path)
50  {
51  iconSize = (int)(iconSize * 1.5f);
52  }
53 
55  {
56  int glowSize = (int)(iconSize * 1.5f);
57  GUIStyle.UIGlowCircular.Draw(spriteBatch,
58  new Rectangle((int)(drawPos.X - glowSize / 2), (int)(drawPos.Y - glowSize / 2), glowSize, glowSize),
59  Color.White);
60  }
61 
62  Sprite sprite2 = null;
63  //there are no sprites for all possible combinations of SpawnType flags, but in the vanilla game the only possible combination is
64  //SpawnType.Disabled + some other flag, in which case it's fine to just not show the icon.
65  iconSprites.TryGetValue(SpawnType.ToString(), out Sprite sprite);
66  if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
67  {
68  sprite = iconSprites["Path"];
69  }
70  else if (ConnectedDoor != null)
71  {
72  sprite = iconSprites["Door"];
73  if (Ladders != null)
74  {
75  sprite2 = iconSprites["Ladder"];
76  }
77  else if (ConnectedDoor.IsHorizontal)
78  {
79  //connected to a hatch but not ladders, something's probably off here
80  clr = Color.Yellow;
81  }
83  {
84  clr = Color.Red;
85  }
86  }
87  else if (Ladders != null)
88  {
89  sprite = iconSprites["Ladder"];
90  }
91 
92  if (sprite != null)
93  {
94  float spriteScale = iconSize / (float)sprite.SourceRect.Width;
95  sprite.Draw(spriteBatch, drawPos, clr, origin: sprite.size / 2, scale: spriteScale, depth: 0.001f);
96  sprite2?.Draw(spriteBatch, drawPos + sprite.size * spriteScale * 0.5f, clr, origin: sprite2.size / 2, scale: spriteScale, depth: 0.001f);
97  }
98 
99  if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
100  {
101  AssignedJob.Icon.Draw(spriteBatch, drawPos, AssignedJob.UIColor, scale: iconSize / (float)AssignedJob.Icon.SourceRect.Width * 0.8f, depth: 0.0f);
102  }
103 
104  foreach (MapEntity e in linkedTo)
105  {
106  GUI.DrawLine(spriteBatch,
107  drawPos,
108  new Vector2(e.DrawPosition.X, -e.DrawPosition.Y),
109  (IsTraversable ? GUIStyle.Green : Color.Gray) * 0.7f, width: 5, depth: 0.002f);
110  }
111  if (ConnectedGap != null)
112  {
113  GUI.DrawLine(spriteBatch,
114  drawPos,
116  GUIStyle.Green * 0.5f, width: 1);
117  }
118  if (Ladders != null)
119  {
120  GUI.DrawLine(spriteBatch,
121  drawPos,
122  new Vector2(Ladders.Item.DrawPosition.X, -Ladders.Item.DrawPosition.Y),
123  GUIStyle.Green * 0.5f, width: 1);
124  }
125 
126  var color = Color.WhiteSmoke;
127  if (spawnType == SpawnType.Path)
128  {
129  if (linkedTo.Count < 2)
130  {
131  if (linkedTo.Count == 0)
132  {
133  color = Color.Red;
134  }
135  else
136  {
137  if (CurrentHull == null)
138  {
139  color = Ladders == null ? Color.Red : Color.Yellow;
140  }
141  else
142  {
143  color = Color.Yellow;
144  }
145  }
146  }
147  }
148  else if (spawnType == SpawnType.ExitPoint && ExitPointSize != Point.Zero)
149  {
150  GUI.DrawRectangle(spriteBatch, drawPos - ExitPointSize.ToVector2() / 2, ExitPointSize.ToVector2(), Color.Cyan, thickness: 5);
151  }
152 
153  GUIStyle.SmallFont.DrawString(spriteBatch,
154  ID.ToString(),
155  new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
156  color);
157  if (Tunnel?.Type != null)
158  {
159  GUIStyle.SmallFont.DrawString(spriteBatch,
160  Tunnel.Type.ToString(),
161  new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 45),
162  color);
163  }
164  }
165 
166  public override bool IsMouseOn(Vector2 position)
167  {
168  if (!ShouldDrawIcon()) { return false; }
169  float dist = Vector2.DistanceSquared(position, WorldPosition);
170  float radius = (SpawnType == SpawnType.Path ? WaypointSize : SpawnPointSize) * 0.6f;
171  return dist < radius * radius;
172  }
173 
174  private bool ShouldDrawIcon()
175  {
176  if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
177  if (spawnType == SpawnType.Path)
178  {
179  return GameMain.DebugDraw || ShowWayPoints;
180  }
181  else
182  {
183  return GameMain.DebugDraw || ShowSpawnPoints;
184  }
185  }
186 
187  public override void UpdateEditing(Camera cam, float deltaTime)
188  {
189  if (editingHUD == null || editingHUD.UserData != this)
190  {
191  editingHUD = CreateEditingHUD();
192  }
193 
194  if (IsSelected && PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
195  {
196  Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
197 
198  if (PlayerInput.KeyDown(Keys.Space))
199  {
200  foreach (MapEntity e in HighlightedEntities)
201  {
202  if (e is not WayPoint || e == this) { continue; }
203 
204  if (linkedTo.Contains(e))
205  {
206  linkedTo.Remove(e);
207  e.linkedTo.Remove(this);
208  }
209  else
210  {
211  linkedTo.Add(e);
212  e.linkedTo.Add(this);
213  }
214  }
215  }
216  else
217  {
218  FindHull();
219  // Update gaps, ladders, and stairs
220  UpdateLinkedEntity(position, Gap.GapList, gap => ConnectedGap = gap, gap =>
221  {
222  if (ConnectedGap == gap)
223  {
224  ConnectedGap = null;
225  }
226  });
227  UpdateLinkedEntity(position, Item.ItemList, i =>
228  {
229  var ladder = i?.GetComponent<Ladder>();
230  if (ladder != null)
231  {
232  Ladders = ladder;
233  }
234  }, i =>
235  {
236  var ladder = i?.GetComponent<Ladder>();
237  if (ladder != null)
238  {
239  if (Ladders == ladder)
240  {
241  Ladders = null;
242  }
243  }
244  }, inflate: 5);
245  FindStairs();
246  // TODO: Cannot check the rectangle, since the rectangle is not rotated -> Need to use the collider.
247  //var stairList = mapEntityList.Where(me => me is Structure s && s.StairDirection != Direction.None).Select(me => me as Structure);
248  //UpdateLinkedEntity(position, stairList, s =>
249  //{
250  // Stairs = s;
251  //}, s =>
252  //{
253  // if (Stairs == s)
254  // {
255  // Stairs = null;
256  // }
257  //});
258  }
259  }
260  }
261 
262  private void UpdateLinkedEntity<T>(Vector2 worldPos, IEnumerable<T> list, Action<T> match, Action<T> noMatch, int inflate = 0) where T : MapEntity
263  {
264  foreach (var entity in list)
265  {
266  var rect = entity.WorldRect;
267  rect.Inflate(inflate, inflate);
268  if (Submarine.RectContains(rect, worldPos))
269  {
270  match(entity);
271  }
272  else
273  {
274  noMatch(entity);
275  }
276  }
277  }
278 
279  private bool ChangeSpawnType(GUIButton button, object obj)
280  {
281  var prevSpawnType = spawnType;
282  GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
283  var values = (SpawnType[])Enum.GetValues(typeof(SpawnType));
284  int currIndex = values.IndexOf(spawnType);
285  currIndex += (int)button.UserData;
286  int firstIndex = 1;
287  int lastIndex = values.Length - 1;
288  if (currIndex > lastIndex)
289  {
290  currIndex = firstIndex;
291  }
292  if (currIndex < firstIndex)
293  {
294  currIndex = lastIndex;
295  }
296  spawnType = values[currIndex];
297  spawnTypeText.Text = spawnType.ToString();
298  if (spawnType == SpawnType.ExitPoint || prevSpawnType == SpawnType.ExitPoint) { CreateEditingHUD(); }
299  return true;
300  }
301 
302  private GUIComponent CreateEditingHUD()
303  {
304  editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.15f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) })
305  {
306  UserData = this
307  };
308 
309  var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), editingHUD.RectTransform, Anchor.Center))
310  {
311  Stretch = true,
312  AbsoluteSpacing = (int)(GUI.Scale * 5)
313  };
314 
315  if (spawnType == SpawnType.Path)
316  {
317  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("Waypoint"), font: GUIStyle.LargeFont);
318  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("LinkWaypoint"));
319  }
320  else
321  {
322  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("Spawnpoint"), font: GUIStyle.LargeFont);
323 
324  if (!Layer.IsNullOrEmpty())
325  {
326  var layerText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
327  }
328 
329  var spawnTypeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true)
330  {
331  Stretch = true,
332  RelativeSpacing = 0.05f
333  };
334  new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), spawnTypeContainer.RectTransform), TextManager.Get("SpawnType"));
335 
336  var button = new GUIButton(new RectTransform(Vector2.One, spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton")
337  {
338  UserData = -1,
339  OnClicked = ChangeSpawnType
340  };
341  new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), spawnTypeContainer.RectTransform), spawnType.ToString(), textAlignment: Alignment.Center)
342  {
343  UserData = "spawntypetext"
344  };
345  button = new GUIButton(new RectTransform(Vector2.One, spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton")
346  {
347  UserData = 1,
348  OnClicked = ChangeSpawnType
349  };
350 
351  var descText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
352  TextManager.Get("IDCardDescription"), font: GUIStyle.SmallFont)
353  {
354  ToolTip = TextManager.Get("IDCardDescriptionTooltip")
355  };
356  GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), IdCardDesc)
357  {
358  MaxTextLength = 150,
359  ToolTip = TextManager.Get("IDCardDescriptionTooltip")
360  };
361  propertyBox.OnTextChanged += (textBox, text) =>
362  {
363  IdCardDesc = text;
364  return true;
365  };
366  propertyBox.OnEnterPressed += (textBox, text) =>
367  {
368  IdCardDesc = text;
369  textBox.Flash(GUIStyle.Green);
370  return true;
371  };
372  propertyBox.OnDeselected += (textBox, keys) =>
373  {
374  IdCardDesc = textBox.Text;
375  textBox.Flash(GUIStyle.Green);
376  };
377 
378  var idCardTagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
379  TextManager.Get("IDCardTags"), font: GUIStyle.SmallFont)
380  {
381  ToolTip = TextManager.Get("IDCardTagsTooltip")
382  };
383  propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), idCardTagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
384  {
385  MaxTextLength = 60,
386  ToolTip = TextManager.Get("IDCardTagsTooltip")
387  };
388  propertyBox.OnTextChanged += (textBox, text) =>
389  {
390  IdCardTags = text.Split(',');
391  return true;
392  };
393  propertyBox.OnEnterPressed += (textBox, text) =>
394  {
395  textBox.Text = string.Join(",", IdCardTags);
396  textBox.Flash(GUIStyle.Green);
397  return true;
398  };
399  propertyBox.OnDeselected += (textBox, keys) =>
400  {
401  textBox.Text = string.Join(",", IdCardTags);
402  textBox.Flash(GUIStyle.Green);
403  };
404 
405  var jobsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
406  TextManager.Get("SpawnpointJobs"), font: GUIStyle.SmallFont)
407  {
408  ToolTip = TextManager.Get("SpawnpointJobsTooltip")
409  };
410  var jobDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), jobsText.RectTransform, Anchor.CenterRight))
411  {
412  ToolTip = TextManager.Get("SpawnpointJobsTooltip"),
413  OnSelected = (selected, userdata) =>
414  {
415  AssignedJob = userdata as JobPrefab;
416  return true;
417  }
418  };
419  jobDropDown.AddItem(TextManager.Get("Any"), null);
420  foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
421  {
422  jobDropDown.AddItem(jobPrefab.Name, jobPrefab);
423  }
424  jobDropDown.SelectItem(AssignedJob);
425 
426  var tagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
427  TextManager.Get("spawnpointtags"), font: GUIStyle.SmallFont);
428  propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), tagsText.RectTransform, Anchor.CenterRight), string.Join(", ", tags))
429  {
430  MaxTextLength = 60,
431  ToolTip = TextManager.Get("spawnpointtagstooltip")
432  };
433  propertyBox.OnTextChanged += (textBox, text) =>
434  {
435  tags = text.Split(',').ToIdentifiers().ToHashSet();
436  return true;
437  };
438  propertyBox.OnEnterPressed += (textBox, text) =>
439  {
440  textBox.Text = string.Join(",", tags);
441  textBox.Flash(GUIStyle.Green);
442  return true;
443  };
444  propertyBox.OnDeselected += (textBox, keys) =>
445  {
446  textBox.Text = string.Join(",", tags);
447  textBox.Flash(GUIStyle.Green);
448  };
449 
450  if (SpawnType == SpawnType.ExitPoint)
451  {
452  var sizeField = GUI.CreatePointField(ExitPointSize, GUI.IntScale(20), TextManager.Get("dimensions"), paddedFrame.RectTransform);
453  GUINumberInput xField = null, yField = null;
454  foreach (GUIComponent child in sizeField.GetAllChildren())
455  {
456  if (yField == null)
457  {
458  yField = child as GUINumberInput;
459  }
460  else
461  {
462  xField = child as GUINumberInput;
463  if (xField != null) { break; }
464  }
465  }
466  xField.MinValueInt = 0;
467  xField.OnValueChanged = (numberInput) => { ExitPointSize = new Point(numberInput.IntValue, ExitPointSize.Y); };
468  yField.MinValueInt = 0;
469  yField.OnValueChanged = (numberInput) => { ExitPointSize = new Point(ExitPointSize.X, numberInput.IntValue); };
470  }
471  }
472 
473  editingHUD.RectTransform.Resize(new Point(
474  editingHUD.Rect.Width,
475  (int)(paddedFrame.Children.Sum(c => c.Rect.Height + paddedFrame.AbsoluteSpacing) / paddedFrame.RectTransform.RelativeSize.Y)));
476 
477  PositionEditingHUD();
478 
479  return editingHUD;
480  }
481  }
482 }
float? Zoom
Definition: Camera.cs:78
Vector2 ScreenToWorld(Vector2 coords)
Definition: Camera.cs:410
virtual Vector2 WorldPosition
Definition: Entity.cs:49
virtual Vector2 DrawPosition
Definition: Entity.cs:51
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
Definition: Entity.cs:43
static SubEditorScreen SubEditorScreen
Definition: GameMain.cs:68
static bool DebugDraw
Definition: GameMain.cs:29
static readonly List< Item > ItemList
static IEnumerable< MapEntity > HighlightedEntities
static bool KeyDown(InputType inputType)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
static bool IsLayerVisible(MapEntity entity)
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
void Draw(SpriteBatch spriteBatch, Vector2 drawPos)
override bool IsVisible(Rectangle worldView)
override bool IsMouseOn(Vector2 position)
override void UpdateEditing(Camera cam, float deltaTime)
override void Draw(SpriteBatch spriteBatch, bool editing, bool back=true)