Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Map/MapEntity.cs
3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Graphics;
5 using Microsoft.Xna.Framework.Input;
6 using System;
7 using System.Collections.Generic;
8 using System.Collections.Immutable;
9 using System.Linq;
10 using Barotrauma.Lights;
11 using Microsoft.Xna.Framework;
12 
13 namespace Barotrauma
14 {
15  abstract partial class MapEntity : Entity
16  {
17  protected static Vector2 selectionPos = Vector2.Zero;
18  protected static Vector2 selectionSize = Vector2.Zero;
19 
20  private static Vector2 startMovingPos = Vector2.Zero;
21 
22  private static float keyDelay;
23 
24  public static Vector2 StartMovingPos => startMovingPos;
25  public static Vector2 SelectionPos => selectionPos;
26 
27  public event Action<Rectangle> Resized;
28 
29  public static bool Resizing { get; private set; }
30  private int resizeDirX, resizeDirY;
31  private Rectangle? prevRect;
32 
33  public static bool SelectionChanged;
34 
35  //which entities have been selected for editing
36  public static HashSet<MapEntity> SelectedList { get; private set; } = new HashSet<MapEntity>();
37 
38  public static List<MapEntity> CopiedList = new List<MapEntity>();
39 
40  private static List<MapEntity> highlightedInEditorList = new List<MapEntity>();
41 
42  private static float highlightTimer;
43 
44  private static GUIListBox highlightedListBox;
46  {
47  get { return highlightedListBox; }
48  }
49 
50 
51  protected static GUIComponent editingHUD;
52  public static GUIComponent EditingHUD
53  {
54  get
55  {
56  return editingHUD;
57  }
58  }
59 
60  private static bool disableSelect;
61  public static bool DisableSelect
62  {
63  get { return disableSelect; }
64  set
65  {
66  disableSelect = value;
67  if (disableSelect)
68  {
69  StopSelection();
70  }
71  }
72  }
73 
74  public virtual bool SelectableInEditor => true;
75 
76  public static bool SelectedAny => SelectedList.Count > 0;
77 
78  public bool IsSelected => SelectedList.Contains(this);
79 
80  public bool IsIncludedInSelection { get; set; }
81 
82  public virtual bool IsVisible(Rectangle worldView)
83  {
84  return true;
85  }
86 
91 
92  public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { }
93 
97  public float GetDrawDepth(float baseDepth, Sprite sprite)
98  {
99  float depth = baseDepth
100  //take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
101  + (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.000001f
102  + ID % 100 * 0.0000001f;
103  return Math.Min(depth, 1.0f);
104  }
105 
106  protected Vector2 GetCollapseEffectOffset()
107  {
108  if (Level.Loaded?.Renderer?.CollapseEffectStrength is float collapseEffectStrength and > 0.0f && Submarine is not { Info.Type: SubmarineType.Player })
109  {
110  Vector2 noisePos = new Vector2(
111  (float)PerlinNoise.GetPerlin((float)(Timing.TotalTime + ID) * 0.1f, (float)(Timing.TotalTime + ID) * 0.5f) - 0.5f,
112  (float)PerlinNoise.GetPerlin((float)(Timing.TotalTime + ID) * 0.1f, (float)(Timing.TotalTime + ID) * 0.1f) - 0.5f);
113  Vector2 offsetFromOrigin = Level.Loaded.Renderer.CollapseEffectOrigin - DrawPosition;
114  return offsetFromOrigin * MathF.Pow(collapseEffectStrength, MathHelper.Lerp(1, 4, ID % 1000 / 1000.0f)) + (noisePos * 100.0f * collapseEffectStrength);
115  }
116  return Vector2.Zero;
117  }
118 
122  public static void UpdateSelecting(Camera cam)
123  {
124  if (Resizing)
125  {
126  if (!SelectedAny)
127  {
128  Resizing = false;
129  }
130  return;
131  }
132 
134 
135  if (DisableSelect)
136  {
137  DisableSelect = false;
138  return;
139  }
140 
141  if (startMovingPos == Vector2.Zero
142  && selectionPos == Vector2.Zero
143  && (GUI.MouseOn != null || !PlayerInput.MouseInsideWindow))
144  {
145  if (highlightedListBox == null ||
146  (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn)))
147  {
148  UpdateHighlightedListBox(null, false);
149  return;
150  }
151  }
152 
153  if (MapEntityPrefab.Selected != null)
154  {
155  selectionPos = Vector2.Zero;
156  SelectedList.Clear();
157  return;
158  }
159  if (GUI.KeyboardDispatcher.Subscriber == null)
160  {
161  if (PlayerInput.KeyHit(Keys.Delete))
162  {
163  if (SelectedAny)
164  {
165  SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(SelectedList), true));
166  SelectedList.ForEachMod(static e => { if (!e.Removed) { e.Remove(); } });
167  SelectedList.Clear();
168  }
169  }
170 
171  if (PlayerInput.IsCtrlDown())
172  {
173 #if DEBUG
174  if (PlayerInput.KeyHit(Keys.D))
175  {
176  bool terminate = false;
177  foreach (MapEntity entity in SelectedList)
178  {
179  if (entity is Item item && item.GetComponent<Planter>() is { } planter)
180  {
181  planter.Update(1.0f, cam);
182  for (var i = 0; i < planter.GrowableSeeds.Length; i++)
183  {
184  Growable seed = planter.GrowableSeeds[i];
185  PlantSlot slot = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i] : Planter.NullSlot;
186  if (seed == null) { continue; }
187 
188  seed.CreateDebugHUD(planter, slot);
189  terminate = true;
190  break;
191  }
192  }
193 
194  if (terminate) { break; }
195  }
196  }
197 #endif
198  if (PlayerInput.KeyHit(Keys.C))
199  {
200  Copy(SelectedList.ToList());
201  }
202  else if (PlayerInput.KeyHit(Keys.X))
203  {
204  Cut(SelectedList.ToList());
205  }
206  else if (PlayerInput.KeyHit(Keys.V))
207  {
209  }
210  /*else if (PlayerInput.KeyHit(Keys.G))
211  {
212  if (SelectedList.Any())
213  {
214  if (SelectionGroups.ContainsKey(SelectedList.Last()))
215  {
216  // Ungroup all selected
217  SelectedList.ForEach(e => SelectionGroups.Remove(e));
218  }
219  else
220  {
221  foreach (var entity in SelectedList)
222  {
223  // Remove the old group, if any
224  SelectionGroups.Remove(entity);
225  // Create a group that can be accessed with any member
226  SelectionGroups.Add(entity, SelectedList);
227  }
228  }
229  }
230  }*/
231  }
232  }
233 
234  Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
235  MapEntity highLightedEntity = null;
236  if (startMovingPos == Vector2.Zero)
237  {
238  List<MapEntity> highlightedEntities = new List<MapEntity>();
239  if (highlightedListBox != null && highlightedListBox.IsParentOf(GUI.MouseOn))
240  {
241  highLightedEntity = GUI.MouseOn.UserData as MapEntity;
242  }
243  else
244  {
245  foreach (MapEntity e in MapEntityList)
246  {
247  if (!e.SelectableInEditor) { continue; }
248  if (e.IsMouseOn(position))
249  {
250  int i = 0;
251  while (i < highlightedEntities.Count &&
252  e.Sprite != null &&
253  (highlightedEntities[i].Sprite == null || highlightedEntities[i].SpriteDepth < e.SpriteDepth))
254  {
255  i++;
256  }
257  highlightedEntities.Insert(i, e);
258  if (i == 0) highLightedEntity = e;
259  }
260  }
262  }
263 
264  if (highLightedEntity != null) { highLightedEntity.IsHighlighted = true; }
265  }
266 
267  if (GUI.KeyboardDispatcher.Subscriber == null)
268  {
269  Vector2 nudge = GetNudgeAmount();
270  if (nudge != Vector2.Zero)
271  {
272  foreach (MapEntity entityToNudge in SelectedList) { entityToNudge.Move(nudge); }
273  }
274  }
275  else
276  {
277  keyDelay = 0;
278  }
279 
280  bool isShiftDown = PlayerInput.IsShiftDown();
281 
282  //started moving selected entities
283  if (startMovingPos != Vector2.Zero)
284  {
285  Item targetContainer = GetPotentialContainer(position, SelectedList);
286  if (targetContainer != null) { targetContainer.IsHighlighted = true; }
287 
289  {
290  //mouse released -> move the entities to the new position of the mouse
291 
292  Vector2 moveAmount = position - startMovingPos;
293 
294  if (!isShiftDown)
295  {
296  moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
297  moveAmount.Y = (float)(moveAmount.Y > 0.0f ? Math.Floor(moveAmount.Y / Submarine.GridSize.Y) : Math.Ceiling(moveAmount.Y / Submarine.GridSize.Y)) * Submarine.GridSize.Y;
298  }
299 
300  if (Math.Abs(moveAmount.X) >= Submarine.GridSize.X || Math.Abs(moveAmount.Y) >= Submarine.GridSize.Y || isShiftDown)
301  {
302  if (!isShiftDown) { moveAmount = Submarine.VectorToWorldGrid(moveAmount); }
303 
304  //clone
305  if (PlayerInput.IsCtrlDown())
306  {
307  HashSet<MapEntity> clones = Clone(SelectedList.ToList()).Where(c => c != null).ToHashSet();
308  SelectedList = clones;
309  SelectedList.ForEach(c => c.Move(moveAmount));
310  SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(clones), false));
311  }
312  else // move
313  {
314  var oldRects = SelectedList.Select(e => e.Rect).ToList();
315  List<MapEntity> deposited = new List<MapEntity>();
316  foreach (MapEntity e in SelectedList)
317  {
318  e.Move(moveAmount);
319 
320  if (isShiftDown && e is Item item && targetContainer != null)
321  {
322  if (targetContainer.OwnInventory.TryPutItem(item, Character.Controlled))
323  {
324  SoundPlayer.PlayUISound(GUISoundType.DropItem);
325  deposited.Add(item);
326  }
327  else
328  {
329  SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
330  }
331  }
332  }
333 
334  SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList),SelectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
335  if (deposited.Any() && deposited.Any(entity => entity is Item))
336  {
337  var depositedItems = deposited.Where(entity => entity is Item).Cast<Item>().ToList();
338  SubEditorScreen.StoreCommand(new InventoryPlaceCommand(targetContainer.OwnInventory, depositedItems, false));
339  }
340 
341  deposited.ForEach(entity => { SelectedList.Remove(entity); });
342  }
343  }
344  startMovingPos = Vector2.Zero;
345  }
346 
347  }
348  //started dragging a "selection rectangle"
349  else if (selectionPos != Vector2.Zero)
350  {
351  selectionSize.X = position.X - selectionPos.X;
352  selectionSize.Y = selectionPos.Y - position.Y;
353 
354  foreach (MapEntity entity in MapEntityList)
355  {
356  entity.IsIncludedInSelection = false;
357  }
358 
359  HashSet<MapEntity> newSelection = new HashSet<MapEntity>();// FindSelectedEntities(selectionPos, selectionSize);
360  if (Math.Abs(selectionSize.X) > Submarine.GridSize.X || Math.Abs(selectionSize.Y) > Submarine.GridSize.Y)
361  {
363  }
364  else
365  {
366  if (highLightedEntity != null)
367  {
368  if (SubEditorScreen.IsLayerLinked(highLightedEntity)/*SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group)*/)
369  {
370  ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(highLightedEntity);
371  foreach (MapEntity entity in entitiesInSameLayer.Where(e => !newSelection.Contains(e)))
372  {
373  newSelection.Add(entity);
374  }
375 
376  foreach (MapEntity entity in entitiesInSameLayer)
377  {
378  entity.IsIncludedInSelection = true;
379  }
380  }
381  else
382  {
383  newSelection.Add(highLightedEntity);
384  highLightedEntity.IsIncludedInSelection = true;
385  }
386  }
387  }
388 
390  {
391  if (PlayerInput.IsCtrlDown())
392  {
393  foreach (MapEntity e in newSelection)
394  {
395  if (SelectedList.Contains(e))
396  {
397  RemoveSelection(e);
398  }
399  else
400  {
401  AddSelection(e);
402  }
403  }
404  }
405  else
406  {
407  SelectedList = new HashSet<MapEntity>(newSelection);
408  //selectedList.Clear();
409  //newSelection.ForEach(e => AddSelection(e));
410  foreach (var entity in newSelection)
411  {
412  HandleDoorGapLinks(entity,
413  onGapFound: (door, gap) =>
414  {
415  door.RefreshLinkedGap();
416  if (!SelectedList.Contains(gap))
417  {
418  SelectedList.Add(gap);
419  }
420  },
421  onDoorFound: (door, gap) =>
422  {
423  if (!SelectedList.Contains(door.Item))
424  {
425  SelectedList.Add(door.Item);
426  }
427  });
428  }
429  }
430 
431  //select wire if both items it's connected to are selected
432  var selectedItems = SelectedList.Where(e => e is Item).Cast<Item>().ToList();
433  foreach (Item item in Item.ItemList)
434  {
435  var wire = item.GetComponent<Wire>();
436  if (wire == null) { continue; }
437  Item item0 = wire.Connections[0]?.Item;
438  Item item1 = wire.Connections[1]?.Item;
439 
440  if (item0 == null && item1 != null)
441  {
442  item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
443  }
444  else if (item0 != null && item1 == null)
445  {
446  item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
447  }
448  if (item0 != null && item1 != null && SelectedList.Contains(item0) && SelectedList.Contains(item1))
449  {
450  SelectedList.Add(item);
451  }
452  }
453 
454  selectionPos = Vector2.Zero;
455  selectionSize = Vector2.Zero;
456  foreach (MapEntity entity in MapEntityList)
457  {
458  entity.IsIncludedInSelection = false;
459  }
460  }
461  }
462  //default, not doing anything specific yet
463  else
464  {
466  PlayerInput.KeyUp(Keys.Space) &&
467  PlayerInput.KeyUp(Keys.LeftAlt) &&
468  PlayerInput.KeyUp(Keys.RightAlt) &&
469  (highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn))))
470  {
471  //if clicking a selected entity, start moving it
472  foreach (MapEntity e in SelectedList)
473  {
474  if (e.IsMouseOn(position)) startMovingPos = position;
475  }
476  selectionPos = position;
477 
478  //stop camera movement to prevent accidental dragging or rect selection
480  }
481  }
482  }
483 
484  public static void StopSelection()
485  {
486  startMovingPos = Vector2.Zero;
487  selectionSize = Vector2.Zero;
488  selectionPos = Vector2.Zero;
489  }
490 
491  public static Vector2 GetNudgeAmount(bool doHold = true)
492  {
493  Vector2 nudgeAmount = Vector2.Zero;
494  if (doHold)
495  {
496  int up = PlayerInput.KeyDown(Keys.Up) ? 1 : 0,
497  down = PlayerInput.KeyDown(Keys.Down) ? -1 : 0,
498  left = PlayerInput.KeyDown(Keys.Left) ? -1 : 0,
499  right = PlayerInput.KeyDown(Keys.Right) ? 1 : 0;
500 
501  int xKeysDown = (left + right);
502  int yKeysDown = (up + down);
503 
504  if (xKeysDown != 0 || yKeysDown != 0) { keyDelay += (float) Timing.Step; } else { keyDelay = 0; }
505 
506 
507  if (keyDelay >= 0.5f)
508  {
509  nudgeAmount.Y = yKeysDown;
510  nudgeAmount.X = xKeysDown;
511  }
512  }
513 
514  if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f;
515  if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f;
516  if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f;
517  if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f;
518 
519  return nudgeAmount;
520  }
521 
523  {
524  return ReplacedBy?.GetReplacementOrThis() ?? this;
525  }
526 
527  public static Item GetPotentialContainer(Vector2 position, HashSet<MapEntity> entities = null)
528  {
529  Item targetContainer = null;
530  bool isShiftDown = PlayerInput.IsShiftDown();
531 
532  if (!isShiftDown) { return null; }
533 
534  foreach (MapEntity e in MapEntityList)
535  {
536  if (!e.SelectableInEditor || e is not Item potentialContainer) { continue; }
537 
538  if (e.IsMouseOn(position))
539  {
540  if (entities == null)
541  {
542  if (potentialContainer.OwnInventory != null && potentialContainer.ParentInventory == null && !potentialContainer.OwnInventory.IsFull(takeStacksIntoAccount: true))
543  {
544  targetContainer = potentialContainer;
545  break;
546  }
547  }
548  else
549  {
550  foreach (MapEntity selectedEntity in entities)
551  {
552  if (!(selectedEntity is Item selectedItem)) { continue; }
553  if (potentialContainer.OwnInventory != null && potentialContainer.ParentInventory == null && potentialContainer != selectedItem &&
554  potentialContainer.OwnInventory.CanBePut(selectedItem))
555  {
556  targetContainer = potentialContainer;
557  break;
558  }
559  }
560  }
561  }
562  if (targetContainer != null) { break; }
563  }
564 
565  return targetContainer;
566  }
567 
574  public static void UpdateHighlighting(List<MapEntity> highlightedEntities, bool wiringMode = false)
575  {
576  if (PlayerInput.MouseSpeed.LengthSquared() > 10)
577  {
578  highlightTimer = 0.0f;
579  }
580  else
581  {
582  bool mouseNearHighlightBox = false;
583 
584  if (highlightedListBox != null)
585  {
586  Rectangle expandedRect = highlightedListBox.Rect;
587  expandedRect.Inflate(20, 20);
588  mouseNearHighlightBox = expandedRect.Contains(PlayerInput.MousePosition);
589  if (!mouseNearHighlightBox) highlightedListBox = null;
590  }
591 
592  highlightTimer += (float)Timing.Step;
593  if (highlightTimer > 1.0f)
594  {
595  if (!mouseNearHighlightBox)
596  {
597  UpdateHighlightedListBox(highlightedEntities, wiringMode);
598  highlightTimer = 0.0f;
599  }
600  }
601  }
602  }
603 
604  private static void UpdateHighlightedListBox(List<MapEntity> highlightedEntities, bool wiringMode)
605  {
606  if (highlightedEntities == null || highlightedEntities.Count < 2)
607  {
608  highlightedListBox = null;
609  return;
610  }
611  if (highlightedListBox != null)
612  {
613  if (GUI.MouseOn == highlightedListBox || highlightedListBox.IsParentOf(GUI.MouseOn)) return;
614  if (highlightedEntities.SequenceEqual(highlightedInEditorList)) return;
615  }
616 
617  highlightedInEditorList = highlightedEntities;
618 
619  highlightedListBox = new GUIListBox(new RectTransform(new Point(180, highlightedEntities.Count * 18 + 5), GUI.Canvas)
620  {
621  MaxSize = new Point(int.MaxValue, 256),
622  ScreenSpaceOffset = PlayerInput.MousePosition.ToPoint() + new Point(15)
623  }, style: "GUIToolTip");
624 
625  foreach (MapEntity entity in highlightedEntities)
626  {
627  LocalizedString tooltip = string.Empty;
628 
629  if (wiringMode && entity is Item item)
630  {
631  var wire = item.GetComponent<Wire>();
632  if (wire?.Connections != null)
633  {
634  for (var i = 0; i < wire.Connections.Length; i++)
635  {
636  var conn = wire.Connections[i];
637  if (conn != null)
638  {
639  tooltip += TextManager.GetWithVariables("wirelistformat",
640  ("[item]", conn.Item?.Name),
641  ("[pin]", conn.Name));
642  }
643  if (i != wire.Connections.Length - 1) { tooltip += '\n'; }
644  }
645  }
646  }
647 
648  var textBlock = new GUITextBlock(new RectTransform(new Point(highlightedListBox.Content.Rect.Width, 15), highlightedListBox.Content.RectTransform),
649  ToolBox.LimitString(entity.Name, GUIStyle.SmallFont, 140), font: GUIStyle.SmallFont)
650  {
651  ToolTip = tooltip,
652  UserData = entity
653  };
654  }
655 
656  highlightedListBox.OnSelected = (GUIComponent component, object obj) =>
657  {
658  MapEntity entity = obj as MapEntity;
659 
660  if (PlayerInput.IsCtrlDown() && !wiringMode)
661  {
662  if (SelectedList.Contains(entity))
663  {
664  RemoveSelection(entity);
665  }
666  else
667  {
668  AddSelection(entity);
669  }
670 
671  return true;
672  }
673  SelectEntity(entity);
674 
675  return true;
676  };
677  }
678 
679  public static void AddSelection(MapEntity entity)
680  {
681  if (SelectedList.Contains(entity)) { return; }
682  SelectedList.Add(entity);
683  HandleDoorGapLinks(entity,
684  onGapFound: (door, gap) =>
685  {
686  door.RefreshLinkedGap();
687  if (!SelectedList.Contains(gap))
688  {
689  SelectedList.Add(gap);
690  }
691  },
692  onDoorFound: (door, gap) =>
693  {
694  if (!SelectedList.Contains(door.Item))
695  {
696  SelectedList.Add(door.Item);
697  }
698  });
699  }
700 
701  private static void HandleDoorGapLinks(MapEntity entity, Action<Door, Gap> onGapFound, Action<Door, Gap> onDoorFound)
702  {
703  switch (entity)
704  {
705  case Item i:
706  {
707  var door = i.GetComponent<Door>();
708  var gap = door?.LinkedGap;
709  if (gap != null)
710  {
711  onGapFound(door, gap);
712  }
713 
714  break;
715  }
716  case Gap gap:
717  {
718  var door = gap.ConnectedDoor;
719  if (door != null)
720  {
721  onDoorFound(door, gap);
722  }
723 
724  break;
725  }
726  }
727  }
728 
729  public static void RemoveSelection(MapEntity entity)
730  {
731  SelectedList.Remove(entity);
732  HandleDoorGapLinks(entity,
733  onGapFound: (door, gap) => SelectedList.Remove(gap),
734  onDoorFound: (door, gap) => SelectedList.Remove(door.Item));
735  }
736 
737  static partial void UpdateAllProjSpecific(float deltaTime)
738  {
739  var entitiesToRender = Submarine.VisibleEntities ?? MapEntityList;
740  foreach (MapEntity me in entitiesToRender)
741  {
742  if (me is Item item)
743  {
744  item.UpdateSpriteStates(deltaTime);
745  }
746  else if (me is Structure structure)
747  {
748  structure.UpdateSpriteStates(deltaTime);
749  }
750  }
751  }
752 
756  public static void DrawSelecting(SpriteBatch spriteBatch, Camera cam)
757  {
758  if (Screen.Selected is SubEditorScreen subEditor)
759  {
760  if (subEditor.IsMouseOnEditorGUI()) { return; }
761  }
762  else if (GUI.MouseOn != null)
763  {
764  return;
765  }
766 
767  Vector2 position = PlayerInput.MousePosition;
768  position = cam.ScreenToWorld(position);
769 
770  if (startMovingPos != Vector2.Zero)
771  {
772  Vector2 moveAmount = position - startMovingPos;
773  moveAmount.Y = -moveAmount.Y;
774 
775  bool isShiftDown = PlayerInput.IsShiftDown();
776 
777  if (!isShiftDown)
778  {
779  moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
780  moveAmount.Y = (float)(moveAmount.Y > 0.0f ? Math.Floor(moveAmount.Y / Submarine.GridSize.Y) : Math.Ceiling(moveAmount.Y / Submarine.GridSize.Y)) * Submarine.GridSize.Y;
781  }
782 
783  //started moving the selected entities
784  if (Math.Abs(moveAmount.X) >= Submarine.GridSize.X || Math.Abs(moveAmount.Y) >= Submarine.GridSize.Y || isShiftDown)
785  {
786  foreach (MapEntity e in SelectedList)
787  {
788  SpriteEffects spriteEffects = SpriteEffects.None;
789  float spriteRotation = 0.0f;
790  float rectangleRotation = 0.0f;
791  switch (e)
792  {
793  case Item item:
794  {
795  if (item.FlippedX && item.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
796  if (item.FlippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
797  spriteRotation = MathHelper.ToRadians(item.Rotation);
798  rectangleRotation = spriteRotation;
799  var wire = item.GetComponent<Wire>();
800  if (wire != null && wire.Item.body != null && !wire.Item.body.Enabled)
801  {
802  wire.Draw(spriteBatch, editing: false, new Vector2(moveAmount.X, -moveAmount.Y));
803  continue;
804  }
805  break;
806  }
807  case Structure structure:
808  {
809  if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
810  if (structure.FlippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
811  rectangleRotation = MathHelper.ToRadians(structure.Rotation);
812 
813  spriteRotation = rectangleRotation;
814  bool spriteIsFlippedHorizontally = structure.Sprite.effects.HasFlag(SpriteEffects.FlipHorizontally);
815  bool spriteIsFlippedVertically = structure.Sprite.effects.HasFlag(SpriteEffects.FlipVertically);
816  if (spriteIsFlippedHorizontally != spriteIsFlippedVertically)
817  {
818  spriteRotation = -spriteRotation;
819  }
820 
821  if (structure.FlippedX != structure.FlippedY) { rectangleRotation = -rectangleRotation; }
822  break;
823  }
824  case WayPoint wayPoint:
825  {
826  Vector2 drawPos = e.WorldPosition;
827  drawPos.Y = -drawPos.Y;
828  drawPos += moveAmount;
829  wayPoint.Draw(spriteBatch, drawPos);
830  continue;
831  }
832  case LinkedSubmarine linkedSub:
833  {
834  var ma = moveAmount;
835  ma.Y = -ma.Y;
836  Vector2 lPos = linkedSub.Position;
837  lPos += ma;
838  linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
839  break;
840  }
841  }
842  e.Prefab?.DrawPlacing(spriteBatch,
843  new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteRotation, spriteEffects: spriteEffects);
844  GUI.DrawRectangle(spriteBatch,
845  center: e.WorldRect.Center.ToVector2().FlipY() + moveAmount + new Vector2(0f, e.WorldRect.Height),
846  width: e.WorldRect.Width, height: e.WorldRect.Height,
847  rotation: rectangleRotation, clr: Color.White,
848  depth: 0f, thickness: Math.Max(3.0f / GameScreen.Selected.Cam.Zoom, 2.0f));
849  }
850 
851  //stop dragging the "selection rectangle"
852  selectionPos = Vector2.Zero;
853  }
854  }
855  if (selectionPos != Vector2.Zero)
856  {
857  DrawSelectionRect(spriteBatch, selectionPos, selectionSize, GUIStyle.Blue);
858  }
859  }
860 
861  public static void DrawSelectionRect(SpriteBatch spriteBatch, Vector2 pos, Vector2 size, Color color)
862  {
863  var (sizeX, sizeY) = size;
864  var (posX, posY) = pos;
865 
866  posY = -posY;
867 
868  Vector2[] corners =
869  {
870  new Vector2(posX, posY),
871  new Vector2(posX + sizeX, posY),
872  new Vector2(posX + sizeX, posY + sizeY),
873  new Vector2(posX, posY + sizeY)
874  };
875 
876  float thickness = Math.Max(2f, 2f / Screen.Selected.Cam.Zoom);
877 
878  GUI.DrawFilledRectangle(spriteBatch, corners[0], size, color * 0.1f);
879 
880  Vector2 offset = new Vector2(0f, thickness / 2f);
881 
882  if (sizeY < 0) { offset.Y = -offset.Y; }
883 
884  spriteBatch.DrawLine(corners[0], corners[1], color, thickness);
885  spriteBatch.DrawLine(corners[1] - offset, corners[2] + offset, color, thickness);
886  spriteBatch.DrawLine(corners[2], corners[3], color, thickness);
887  spriteBatch.DrawLine(corners[3] + offset, corners[0] - offset, color, thickness);
888  }
889 
890  protected static void ColorFlipButton(GUIButton btn, bool flip)
891  {
892  var color = flip ? GUIStyle.Green : Color.White;
893  var hsv = ToolBox.RGBToHSV(color);
894 
895  // Boost saturation and reduce value a bit because our default colors are too muted for this button's style
896  var hsvBase = hsv;
897  hsvBase.Y *= 4f;
898  hsvBase.Z *= 0.8f;
899  btn.Color = ToolBoxCore.HSVToRGB(hsvBase.X, hsvBase.Y, hsvBase.Z);
900  btn.SelectedColor = ToolBoxCore.HSVToRGB(hsvBase.X, hsvBase.Y, hsvBase.Z);
901 
902  var hsvHover = hsv;
903  hsvHover.Z *= 1.2f;
904  btn.HoverColor = ToolBoxCore.HSVToRGB(hsvHover.X, hsvHover.Y, hsvHover.Z);
905  }
906 
907  public static List<MapEntity> FilteredSelectedList { get; private set; } = new List<MapEntity>();
908 
909  public static void UpdateEditor(Camera cam, float deltaTime)
910  {
911  if (highlightedListBox != null) { highlightedListBox.UpdateManually(deltaTime); }
912 
913  if (editingHUD != null)
914  {
916  {
917  foreach (GUIComponent component in editingHUD.Children)
918  {
919  var textBox = component as GUITextBox;
920  if (textBox == null) continue;
921  textBox.Deselect();
922  }
923  editingHUD = null;
924  }
925  }
926  FilteredSelectedList.Clear();
927  if (SelectedList.Count == 0) return;
928  foreach (var e in SelectedList)
929  {
930  if (e is Gap gap && gap.ConnectedDoor != null) { continue; }
931  FilteredSelectedList.Add(e);
932  }
933  var first = FilteredSelectedList.FirstOrDefault();
934  if (first != null)
935  {
936  first.UpdateEditing(cam, deltaTime);
937  if (first.ResizeHorizontal || first.ResizeVertical)
938  {
939  first.UpdateResizing(cam);
940  }
941  }
942 
943  if (PlayerInput.IsCtrlDown())
944  {
945  if (PlayerInput.KeyHit(Keys.N))
946  {
947  MapEntity firstSelected = SelectedList.First();
948 
949  float minX = firstSelected.WorldRect.X,
950  maxX = firstSelected.WorldRect.Right;
951 
952  foreach (MapEntity entity in SelectedList)
953  {
954  minX = Math.Min(minX, entity.WorldRect.X);
955  maxX = Math.Max(maxX, entity.WorldRect.Right);
956  }
957 
958  float centerX = (minX + maxX) / 2.0f;
959  foreach (MapEntity me in SelectedList)
960  {
961  me.FlipX(false);
962  me.Move(new Vector2((centerX - me.WorldPosition.X) * 2.0f, 0.0f));
963  }
964  }
965  else if (PlayerInput.KeyHit(Keys.M))
966  {
967  MapEntity firstSelected = SelectedList.First();
968 
969  float minY = firstSelected.WorldRect.Y - firstSelected.WorldRect.Height,
970  maxY = firstSelected.WorldRect.Y;
971 
972  foreach (MapEntity entity in SelectedList)
973  {
974 
975  minY = Math.Min(minY, entity.WorldRect.Y - entity.WorldRect.Height);
976  maxY = Math.Max(maxY, entity.WorldRect.Y);
977  }
978 
979  float centerY = (minY + maxY) / 2.0f;
980  foreach (MapEntity me in SelectedList)
981  {
982  me.FlipY(false);
983  me.Move(new Vector2(0.0f, (centerY - me.WorldPosition.Y) * 2.0f));
984  }
985  }
986  }
987  }
988 
989  public static void ResetEditingHUD()
990  {
991  editingHUD = null;
992  }
993 
994  public static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
995  {
996  if (SelectedList.Count == 1)
997  {
998  MapEntity firstSelected = SelectedList.First();
999  firstSelected.DrawEditing(spriteBatch, cam);
1000  if (firstSelected.ResizeHorizontal || firstSelected.ResizeVertical)
1001  {
1002  firstSelected.DrawResizing(spriteBatch, cam);
1003  }
1004  }
1005  }
1006 
1007  public static void DeselectAll()
1008  {
1009  SelectedList.Clear();
1010  }
1011 
1012  public static void SelectEntity(MapEntity entity)
1013  {
1014  DeselectAll();
1015  AddSelection(entity);
1016  }
1017 
1021  public static void Copy(List<MapEntity> entities)
1022  {
1023  if (entities.Count == 0) { return; }
1024  CopyEntities(entities);
1025  }
1026 
1030  public static void Cut(List<MapEntity> entities)
1031  {
1032  if (entities.Count == 0) { return; }
1033 
1034  CopyEntities(entities);
1035 
1036  SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
1037 
1038  entities.ForEach(e => { if (!e.Removed) { e.Remove(); } });
1039  entities.Clear();
1040  }
1041 
1042  public static void Paste(Vector2 position)
1043  {
1044  if (CopiedList.Count == 0) { return; }
1045 
1046  List<MapEntity> prevEntities = new List<MapEntity>(MapEntityList);
1047  Clone(CopiedList);
1048 
1049  var clones = MapEntityList.Except(prevEntities).ToList();
1050  var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
1051  if (!nonWireClones.Any()) { nonWireClones = clones; }
1052 
1053  Vector2 center = Vector2.Zero;
1054  nonWireClones.ForEach(c => center += c.WorldPosition);
1055  center = Submarine.VectorToWorldGrid(center / nonWireClones.Count());
1056 
1057  Vector2 moveAmount = Submarine.VectorToWorldGrid(position - center);
1058 
1059  SelectedList = new HashSet<MapEntity>(clones);
1060  foreach (MapEntity clone in SelectedList)
1061  {
1062  clone.Move(moveAmount);
1063  clone.Submarine = Submarine.MainSub;
1064  }
1065  foreach (MapEntity clone in SelectedList)
1066  {
1067  (clone as Item)?.GetComponent<ItemContainer>()?.SetContainedItemPositions();
1068  }
1069 
1070  SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false, handleInventoryBehavior: false));
1071  }
1072 
1076  public static List<MapEntity> CopyEntities(List<MapEntity> entities)
1077  {
1078  List<MapEntity> prevEntities = new List<MapEntity>(MapEntityList);
1079 
1080  CopiedList = Clone(entities);
1081 
1082  //find all new entities created during cloning
1083  var newEntities = MapEntityList.Except(prevEntities).ToList();
1084 
1085  //do a "shallow remove" (removes the entities from the game without removing links between them)
1086  // -> items will stay in their containers
1087  newEntities.ForEach(e => e.ShallowRemove());
1088 
1089  return newEntities;
1090  }
1091 
1092  public virtual void AddToGUIUpdateList(int order = 0)
1093  {
1094  if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(order: order); }
1095  }
1096 
1097  public virtual void UpdateEditing(Camera cam, float deltaTime) { }
1098 
1099  protected static void PositionEditingHUD()
1100  {
1101  int maxHeight =
1104  HUDLayoutSettings.InventoryAreaLower.Y - HUDLayoutSettings.CrewArea.Bottom - 10;
1105 
1106 
1107  var listBox = editingHUD.GetChild<GUIListBox>();
1108  if (listBox != null)
1109  {
1110  int padding = 20;
1111  int contentHeight = 0;
1112  foreach (GUIComponent child in listBox.Content.Children)
1113  {
1114  contentHeight += child.Rect.Height + listBox.Spacing;
1115  child.RectTransform.MaxSize = new Point(int.MaxValue, child.Rect.Height);
1116  child.RectTransform.MinSize = new Point(0, child.Rect.Height);
1117  }
1118 
1120  new Point(
1122  MathHelper.Clamp(contentHeight + padding * 2, 50, maxHeight)), resizeChildren: false);
1123  listBox.RectTransform.Resize(new Point(listBox.RectTransform.NonScaledSize.X, editingHUD.RectTransform.NonScaledSize.Y - padding * 2), resizeChildren: false);
1124  }
1127  {
1129  }
1130  else
1131  {
1133  0,
1134  HUDLayoutSettings.HealthBarAfflictionArea.Y - editingHUD.Rect.Height - GUI.IntScale(10));
1135  }
1136  }
1137 
1138  public virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam) { }
1139 
1140  private float RotationRad
1141  => MathHelper.ToRadians(
1142  this switch
1143  {
1144  Structure s => s.Rotation,
1145  Item it => it.Rotation,
1146  _ => 0.0f
1147  });
1148 
1149  private Vector2 GetEditingHandlePos(int x, int y, Camera cam)
1150  {
1151  Vector2 handleDiff = new Vector2(x * (rect.Width * 0.5f), y * (rect.Height * 0.5f));
1152  var rotation = -RotationRad;
1153  handleDiff = MathUtils.RotatePoint(handleDiff, rotation);
1154  if (FlippedX) { handleDiff = handleDiff.FlipX(); }
1155  if (FlippedY) { handleDiff = handleDiff.FlipY(); }
1156  return cam.WorldToScreen(Position + handleDiff);
1157  }
1158 
1159  float ResizeHandleSize => 10 * GUI.Scale;
1160  float ResizeHandleHighlightDistance => 8 * GUI.Scale;
1161 
1162  private void UpdateResizing(Camera cam)
1163  {
1164  IsHighlighted = true;
1165 
1166  int startX = ResizeHorizontal ? -1 : 0;
1167  int startY = ResizeVertical ? -1 : 0;
1168 
1169  for (int x = startX; x < 2; x += 2)
1170  {
1171  for (int y = startY; y < 2; y += 2)
1172  {
1173  Vector2 handlePos = GetEditingHandlePos(x, y, cam);
1174 
1175  bool highlighted = Vector2.DistanceSquared(PlayerInput.MousePosition, handlePos) < ResizeHandleHighlightDistance * ResizeHandleHighlightDistance;
1176 
1177  if (highlighted && PlayerInput.PrimaryMouseButtonDown())
1178  {
1179  selectionPos = Vector2.Zero;
1180  resizeDirX = x;
1181  resizeDirY = y;
1182  Resizing = true;
1183  startMovingPos = Vector2.Zero;
1185  }
1186  }
1187  }
1188 
1189  if (Resizing)
1190  {
1191  if (prevRect == null)
1192  {
1193  prevRect = Rect;
1194  }
1195 
1196  Vector2 placePosition = prevRect.Value.Location.ToVector2();
1197  Vector2 placeSize = prevRect.Value.Size.ToVector2();
1198 
1199  static Vector2 flipThenRotate(Vector2 point, Vector2 center, float angle, bool flipX, bool flipY)
1200  {
1201  if (flipX) { point = (point - center).FlipX() + center; }
1202  if (flipY) { point = (point - center).FlipY() + center; }
1203 
1204  point = MathUtils.RotatePointAroundTarget(point, center, angle);
1205 
1206  return point;
1207  }
1208 
1209  static Vector2 rotateThenFlip(Vector2 point, Vector2 center, float angle, bool flipX, bool flipY)
1210  {
1211  point = MathUtils.RotatePointAroundTarget(point, center, angle);
1212 
1213  if (flipX) { point = (point - center).FlipX() + center; }
1214  if (flipY) { point = (point - center).FlipY() + center; }
1215 
1216  return point;
1217  }
1218 
1219  Vector2 mousePos = cam.ScreenToWorld(PlayerInput.MousePosition);
1220  Vector2 prevPos = placePosition;
1221  Vector2 prevOppositeCorner = prevPos + placeSize.FlipY();
1222  Vector2 prevCenter = placePosition + placeSize.FlipY() * 0.5f;
1223  mousePos = flipThenRotate(mousePos, prevCenter, RotationRad, FlippedX, FlippedY);
1224 
1225  if (!PlayerInput.IsShiftDown())
1226  {
1227  mousePos = Submarine.VectorToWorldGrid(mousePos, Submarine.MainSub, round: true);
1228  }
1229 
1230  if (resizeDirX > 0)
1231  {
1232  mousePos.X = Math.Max(mousePos.X, prevRect.Value.X + Submarine.GridSize.X);
1233  placeSize.X = mousePos.X - placePosition.X;
1234  }
1235  else if (resizeDirX < 0)
1236  {
1237  mousePos.X = Math.Min(mousePos.X, prevRect.Value.Right - Submarine.GridSize.X);
1238 
1239  placeSize.X = MathF.Round((placePosition.X + placeSize.X) - mousePos.X);
1240  placePosition.X = MathF.Round(mousePos.X);
1241  }
1242  if (resizeDirY < 0)
1243  {
1244  mousePos.Y = Math.Min(mousePos.Y, prevRect.Value.Y - Submarine.GridSize.Y);
1245  placeSize.Y = placePosition.Y - mousePos.Y;
1246  }
1247  else if (resizeDirY > 0)
1248  {
1249  mousePos.Y = Math.Max(mousePos.Y, prevRect.Value.Y - prevRect.Value.Height + Submarine.GridSize.Y);
1250 
1251  placeSize.Y = mousePos.Y - (prevRect.Value.Y - prevRect.Value.Height);
1252  placePosition.Y = mousePos.Y;
1253  }
1254 
1255  Vector2 newPos = placePosition;
1256  Vector2 newOppositeCorner = placePosition + placeSize.FlipY();
1257 
1258  Vector2 transformedCornerDiff = rotateThenFlip(newPos-prevPos, Vector2.Zero, -RotationRad, FlippedX, FlippedY);
1259  Vector2 transformedOppositeCornerDiff = rotateThenFlip(newOppositeCorner-prevOppositeCorner, Vector2.Zero, -RotationRad, FlippedX, FlippedY);
1260 
1261  Vector2 newPosTransformed = rotateThenFlip(prevPos, prevCenter, -RotationRad, FlippedX, FlippedY)
1262  + transformedCornerDiff;
1263  Vector2 newOppositeTransformed = rotateThenFlip(prevOppositeCorner, prevCenter, -RotationRad, FlippedX, FlippedY)
1264  + transformedOppositeCornerDiff;
1265  Vector2 newTransformedCenter = (newPosTransformed + newOppositeTransformed) * 0.5f;
1266 
1267  var newDiff = (newOppositeCorner - newPos) * 0.5f;
1268  placePosition = newTransformedCenter - newDiff;
1269 
1270  if ((int)placePosition.X != rect.X || (int)placePosition.Y != rect.Y || (int)placeSize.X != rect.Width || (int)placeSize.Y != rect.Height)
1271  {
1272  Rect = new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y);
1273  }
1274 
1275  if (!PlayerInput.PrimaryMouseButtonHeld())
1276  {
1277  Resizing = false;
1278  Resized?.Invoke(rect);
1279  if (prevRect != null)
1280  {
1281  var newData = new List<Rectangle> { Rect };
1282  var oldData = new List<Rectangle> { prevRect.Value };
1283  SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity> { this }, newData, oldData, true));
1284  }
1285 
1286  if (this is Structure structure)
1287  {
1288  foreach (LightSource light in structure.Lights)
1289  {
1290  light.LightTextureTargetSize = Rect.Size.ToVector2();
1291  light.Position = rect.Location.ToVector2();
1292  }
1293  }
1294  prevRect = null;
1295  }
1296  }
1297  }
1298 
1299  private void DrawResizing(SpriteBatch spriteBatch, Camera cam)
1300  {
1301  IsHighlighted = true;
1302 
1303  int startX = ResizeHorizontal ? -1 : 0;
1304  int startY = ResizeVertical ? -1 : 0;
1305 
1306  for (int x = startX; x < 2; x += 2)
1307  {
1308  for (int y = startY; y < 2; y += 2)
1309  {
1310  Vector2 handlePos = GetEditingHandlePos(x, y, cam);
1311 
1312  bool highlighted = Vector2.DistanceSquared(PlayerInput.MousePosition, handlePos) < ResizeHandleHighlightDistance * ResizeHandleHighlightDistance;
1313  var color = Color.White * (highlighted ? 1.0f : 0.6f);
1314  if (highlighted && !PlayerInput.PrimaryMouseButtonHeld())
1315  {
1316  GUI.MouseCursor = CursorState.Hand;
1317  }
1318  GUI.DrawRectangle(spriteBatch,
1319  handlePos - new Vector2(ResizeHandleSize / 2),
1320  new Vector2(ResizeHandleSize),
1321  color,
1322  true, 0,
1323  (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
1324  }
1325  }
1326  }
1327 
1331  public static HashSet<MapEntity> FindSelectedEntities(Vector2 pos, Vector2 size)
1332  {
1333  HashSet<MapEntity> foundEntities = new HashSet<MapEntity>();
1334 
1335  Rectangle selectionRect = Submarine.AbsRect(pos, size);
1336  Quad2D selectionQuad = Quad2D.FromSubmarineRectangle(selectionRect);
1337 
1338  foreach (MapEntity entity in MapEntityList)
1339  {
1340  if (!entity.SelectableInEditor) { continue; }
1341 
1342  Quad2D entityQuad = entity.GetTransformedQuad();
1343 
1344  if (selectionQuad.Intersects(entityQuad))
1345  {
1346  foundEntities.Add(entity);
1347  entity.IsIncludedInSelection = true;
1348 
1349  if (SubEditorScreen.IsLayerLinked(entity))
1350  {
1351  ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(entity);
1352  foreach (MapEntity layerEntity in entitiesInSameLayer.Where(e => !foundEntities.Contains(e)))
1353  {
1354  foundEntities.Add(layerEntity);
1355  layerEntity.IsIncludedInSelection = true;
1356  }
1357  }
1358  }
1359  }
1360 
1361  return foundEntities;
1362  }
1363  }
1364 }
float? Zoom
Definition: Camera.cs:78
Vector2 ScreenToWorld(Vector2 coords)
Definition: Camera.cs:410
void StopMovement()
Definition: Camera.cs:389
virtual Vector2 WorldPosition
Definition: Entity.cs:49
virtual Vector2 DrawPosition
Definition: Entity.cs:51
Submarine Submarine
Definition: Entity.cs:53
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
Definition: Entity.cs:43
override Color Color
Definition: GUIButton.cs:41
override Color HoverColor
Definition: GUIButton.cs:51
override Color SelectedColor
Definition: GUIButton.cs:61
GUIComponent GetChild(int index)
Definition: GUIComponent.cs:54
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
bool IsParentOf(GUIComponent component, bool recursive=true)
Definition: GUIComponent.cs:75
void UpdateManually(float deltaTime, bool alsoChildren=false, bool recursive=true)
By default, all the gui elements are updated automatically in the same order they appear on the updat...
virtual Rectangle Rect
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
OnSelectedHandler OnSelected
Definition: GUIListBox.cs:17
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
static SubEditorScreen SubEditorScreen
Definition: GameMain.cs:68
static int GraphicsHeight
Definition: GameMain.cs:168
static readonly List< Item > ItemList
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
readonly HashSet< Wire > DisconnectedWires
Wires that have been disconnected from the panel, but not removed completely (visible at the bottom o...
void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth=-1, Color? overrideColor=null)
virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam)
virtual void FlipX(bool relativeToSub)
Flip the entity horizontally
static HashSet< MapEntity > FindSelectedEntities(Vector2 pos, Vector2 size)
Find entities whose rect intersects with the "selection rect"
static Item GetPotentialContainer(Vector2 position, HashSet< MapEntity > entities=null)
static Vector2 GetNudgeAmount(bool doHold=true)
virtual void FlipY(bool relativeToSub)
Flip the entity vertically
static void ColorFlipButton(GUIButton btn, bool flip)
static readonly List< MapEntity > MapEntityList
MapEntity(MapEntityPrefab prefab, Submarine submarine, ushort id)
float GetDrawDepth(float baseDepth, Sprite sprite)
A method that modifies the draw depth to prevent z-fighting between entities with the same sprite dep...
static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
static void UpdateEditor(Camera cam, float deltaTime)
static List< MapEntity > CopyEntities(List< MapEntity > entities)
copies a list of entities to the "clipboard" (copiedList)
static readonly HashSet< MapEntity > highlightedEntities
abstract MapEntity Clone()
static void DrawSelectionRect(SpriteBatch spriteBatch, Vector2 pos, Vector2 size, Color color)
MapEntity ReplacedBy
Used for undo/redo to determine what this item has been replaced with
static void Cut(List< MapEntity > entities)
virtual bool IsVisible(Rectangle worldView)
virtual void UpdateEditing(Camera cam, float deltaTime)
static void UpdateHighlighting(List< MapEntity > highlightedEntities, bool wiringMode=false)
Updates the logic that runs the highlight box when the mouse is sitting still.
static void UpdateSelecting(Camera cam)
Update the selection logic in submarine editor
static void DrawSelecting(SpriteBatch spriteBatch, Camera cam)
Draw the "selection rectangle" and outlines of entities that are being dragged (if any)
virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back=true)
static void Copy(List< MapEntity > entities)
Copy the selected entities to the "clipboard" (copiedList)
static bool KeyDown(InputType inputType)
void SetPosition(Anchor anchor, Pivot? pivot=null)
Point AbsoluteOffset
Absolute in pixels but relative to the anchor point. Calculated away from the anchor point,...
void Resize(Point absoluteSize, bool resizeChildren=true)
Point?? MinSize
Min size in pixels. Does not affect scaling.
Point NonScaledSize
Size before scale multiplications.
Point?? MaxSize
Max size in pixels. Does not affect scaling.
static void StoreCommand(Command command)
static ImmutableHashSet< MapEntity > GetEntitiesInSameLayer(MapEntity entity)
static bool IsLayerLinked(MapEntity entity)
static IEnumerable< MapEntity > VisibleEntities
static Vector2 VectorToWorldGrid(Vector2 position, Submarine sub=null, bool round=false)
static Rectangle AbsRect(Vector2 pos, Vector2 size)
GUISoundType
Definition: GUI.cs:21
CursorState
Definition: GUI.cs:40