5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Graphics;
7 using Microsoft.Xna.Framework.Input;
9 using System.Collections.Generic;
10 using System.Collections.Immutable;
12 using System.Threading;
13 using System.Xml.Linq;
32 private readonly record
struct LayerData(bool IsVisible =
true,
bool IsGrouped =
false);
50 InsufficientFreeConnectionsWarning,
55 ShadowCastingLightCount,
57 LowOxygenOutputWarning,
64 private readonly Point defaultPreviewImageSize =
new Point(640, 368);
66 private readonly
Camera cam;
67 private Vector2 camTargetFocus = Vector2.Zero;
71 private readonly HashSet<ulong> publishedWorkshopItemIds =
new HashSet<ulong>();
73 private Point screenResolution;
75 private bool lightingEnabled;
77 private bool wasSelectedBefore;
81 private readonly List<GUITickBox> showEntitiesTickBoxes =
new List<GUITickBox>();
82 private readonly Dictionary<string, bool> hiddenSubCategories =
new Dictionary<string, bool>();
88 private bool entityMenuOpen =
true;
89 private float entityMenuOpenState = 1.0f;
90 private string lastFilter;
93 private GUIListBox categorizedEntityList, allEntityList;
98 private GUITickBox defaultModeTickBox, wiringModeTickBox;
104 private GUIButton selectedCategoryButton;
106 private readonly List<GUIButton> entityCategoryButtons =
new List<GUIButton>();
114 const int PreviouslyUsedCount = 10;
115 private GUIFrame previouslyUsedPanel;
121 private List<GUIButton> layerSpecificButtons =
new List<GUIButton>();
124 private GUIFrame undoBufferDisclaimer;
130 private static int MaxAutoSaves => GameSettings.CurrentConfig.MaxAutoSaves;
132 public static readonly
object ItemAddMutex =
new object(), ItemRemoveMutex =
new object();
138 private static object bulkItemBufferinUse;
142 get => bulkItemBufferinUse;
145 if (value != bulkItemBufferinUse && bulkItemBufferinUse !=
null)
147 CommitBulkItemBuffer();
150 bulkItemBufferinUse = value;
153 public static List<AddOrDeleteCommand>
BulkItemBuffer =
new List<AddOrDeleteCommand>();
173 private Item OpenedItem;
178 private Vector2 oldItemPosition;
184 public static readonly List<Command>
Commands =
new List<Command>();
185 private static int commandIndex;
189 private Option<DateTime> editorSelectedTime;
194 private const int submarineNameLimit = 30;
197 private const int submarineDescriptionLimit = 500;
198 private GUITextBlock submarineDescriptionCharacterCount;
202 private Vector2 MeasurePositionStart = Vector2.Zero;
205 private bool lockMode;
207 private static bool isAutoSaving;
216 private static readonly
string autoSavePath = Path.Combine(
"Submarines",
".AutoSaves");
217 private static readonly
string autoSaveInfoPath = Path.Combine(autoSavePath,
"autosaves.xml");
219 private static string GetSubDescription()
221 if (MainSub?.Info !=
null)
223 LocalizedString localizedDescription = TextManager.Get($
"submarine.description.{MainSub.Info.Name ?? ""}");
224 if (!localizedDescription.IsNullOrEmpty()) {
return localizedDescription.
Value; }
232 return $
"{TextManager.Get("TotalHullVolume
")}:\n{Hull.HullList.Sum(h => h.Volume)}";
235 private static LocalizedString GetSelectedHullVolume()
237 float buoyancyVol = 0.0f;
238 float selectedVol = 0.0f;
239 float neutralPercentage = SubmarineBody.NeutralBallastPercentage;
240 Hull.HullList.ForEach(h =>
242 buoyancyVol += h.Volume;
245 selectedVol += h.Volume;
248 buoyancyVol *= neutralPercentage;
249 string retVal = $
"{TextManager.Get("SelectedHullVolume
")}:\n{selectedVol}";
250 if (selectedVol > 0.0f && buoyancyVol > 0.0f)
252 if (buoyancyVol / selectedVol < 1.0f)
254 retVal += $
" ({TextManager.GetWithVariable("OptimalBallastLevel
", "[value]
", (buoyancyVol / selectedVol).ToString("0.0000
"))})";
258 retVal += $
" ({TextManager.Get("InsufficientBallast
")})";
266 private static readonly Dictionary<string, LayerData> Layers =
new Dictionary<string, LayerData>();
281 private void CreateUI()
283 TopPanel =
new GUIFrame(
new RectTransform(
new Vector2(GUI.Canvas.RelativeSize.X, 0.01f), GUI.Canvas) { MinSize = new Point(0, 35) },
"GUIFrameTop");
286 isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
288 RelativeSpacing = 0.005f
291 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"GUIButtonToggleLeft")
293 ToolTip = TextManager.Get(
"back"),
294 OnClicked = (b, d) =>
296 var msgBox =
new GUIMessageBox(
"", TextManager.Get(
"PauseMenuQuitVerificationEditor"),
new[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") })
298 UserData =
"verificationprompt"
300 msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
302 GUIMessageBox.CloseAll();
303 GameMain.MainMenuScreen.Select();
306 msgBox.Buttons[0].OnClicked += msgBox.Close;
307 msgBox.Buttons[1].OnClicked = (_, userdata) =>
316 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.9f), paddedTopPanel.
RectTransform), style:
"VerticalLine");
318 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"OpenButton")
320 ToolTip = TextManager.Get(
"OpenSubButton"),
321 OnClicked = (btn, data) =>
330 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.9f), paddedTopPanel.
RectTransform), style:
"VerticalLine");
332 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"SaveButton")
334 ToolTip = RichString.Rich(TextManager.Get(
"SaveSubButton") +
"‖color:125,125,125‖\nCtrl + S‖color:end‖"),
335 OnClicked = (btn, data) =>
338 if (ContentPackageManager.EnabledPackages.All.Any(cp => cp != ContentPackageManager.VanillaCorePackage && cp.Files.Any(f => f is not BaseSubFile)))
340 var msgBox =
new GUIMessageBox(
"DEBUG-ONLY WARNING",
"You currently have some mods enabled. Are you sure you want to save the submarine? If the mods override any vanilla content, saving the submarine may cause unintended changes.",
341 new LocalizedString[] {
"Yes, I know what I'm doing",
"Cancel" });
342 msgBox.Buttons[0].OnClicked = (btn, data) =>
349 msgBox.Buttons[1].OnClicked += msgBox.Close;
359 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.9f), paddedTopPanel.
RectTransform), style:
"VerticalLine");
361 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"TestButton")
363 ToolTip = TextManager.Get(
"TestSubButton"),
364 OnClicked = TestSubmarine
367 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.9f), paddedTopPanel.
RectTransform), style:
"VerticalLine");
369 visibilityButton =
new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"SetupVisibilityButton")
371 ToolTip = TextManager.Get(
"SubEditorVisibilityButton") +
'\n' + TextManager.Get(
"SubEditorVisibilityToolTip"),
372 OnClicked = (btn, userData) =>
374 previouslyUsedPanel.
Visible =
false;
375 undoBufferPanel.
Visible =
false;
383 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"EditorLayerButton")
385 ToolTip = TextManager.Get(
"editor.layer.button") +
'\n' + TextManager.Get(
"editor.layer.tooltip"),
386 OnClicked = (btn, userData) =>
388 previouslyUsedPanel.
Visible =
false;
390 undoBufferPanel.
Visible =
false;
397 var previouslyUsedButton =
new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"RecentlyUsedButton")
399 ToolTip = TextManager.Get(
"PreviouslyUsedLabel"),
400 OnClicked = (btn, userData) =>
403 undoBufferPanel.
Visible =
false;
411 var undoBufferButton =
new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"UndoHistoryButton")
413 ToolTip = TextManager.Get(
"Editor.UndoHistoryButton"),
414 OnClicked = (btn, userData) =>
417 previouslyUsedPanel.
Visible =
false;
425 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.9f), paddedTopPanel.
RectTransform), style:
"VerticalLine");
427 subNameLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 0.9f), paddedTopPanel.
RectTransform,
Anchor.CenterLeft),
428 TextManager.Get(
"unspecifiedsubfilename"), font: GUIStyle.LargeFont, textAlignment: Alignment.CenterLeft);
430 linkedSubBox =
new GUIDropDown(
new RectTransform(
new Vector2(0.15f, 0.9f), paddedTopPanel.
RectTransform),
431 TextManager.Get(
"AddSubButton"), elementCount: 20)
433 ToolTip = TextManager.Get(
"AddSubToolTip")
436 List<(
string Name, SubmarineInfo
Sub)> subs =
new List<(
string Name, SubmarineInfo Sub)>();
438 foreach (SubmarineInfo sub
in SubmarineInfo.SavedSubmarines)
441 subs.Add((sub.Name, sub));
444 foreach (var (name, sub) in subs.OrderBy(tuple => tuple.Name))
446 linkedSubBox.
AddItem(name, sub);
450 linkedSubBox.
OnDropped += (component, obj) =>
452 MapEntity.SelectedList.Clear();
456 var spacing =
new GUIFrame(
new RectTransform(
new Vector2(0.02f, 1.0f), paddedTopPanel.
RectTransform), style:
null);
457 new GUIFrame(
new RectTransform(
new Vector2(0.1f, 0.9f), spacing.RectTransform,
Anchor.Center), style:
"VerticalLine");
459 defaultModeTickBox =
new GUITickBox(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"EditSubButton")
461 ToolTip = RichString.Rich(TextManager.Get(
"SubEditorEditingMode") +
"‖color:125,125,125‖\nCtrl + 1‖color:end‖"),
475 wiringModeTickBox =
new GUITickBox(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"WiringModeButton")
477 ToolTip = RichString.Rich(TextManager.Get(
"WiringModeButton") +
'\n' + TextManager.Get(
"WiringModeToolTip") +
"‖color:125,125,125‖\nCtrl + 2‖color:end‖"),
490 spacing =
new GUIFrame(
new RectTransform(
new Vector2(0.02f, 1.0f), paddedTopPanel.
RectTransform), style:
null);
491 new GUIFrame(
new RectTransform(
new Vector2(0.1f, 0.9f), spacing.RectTransform,
Anchor.Center), style:
"VerticalLine");
493 new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.9f), paddedTopPanel.
RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"GenerateWaypointsButton")
495 ToolTip = TextManager.Get(
"GenerateWaypointsButton") +
'\n' + TextManager.Get(
"GenerateWaypointsToolTip"),
496 OnClicked = (btn, userdata) =>
498 if (WayPoint.WayPointList.Any())
500 var generateWaypointsVerification =
new GUIMessageBox(
"", TextManager.Get(
"generatewaypointsverification"),
new[] { TextManager.Get(
"ok"), TextManager.Get(
"cancel") });
501 generateWaypointsVerification.Buttons[0].OnClicked = delegate
503 if (GenerateWaypoints())
505 GUI.AddMessage(TextManager.Get(
"waypointsgeneratedsuccesfully"), GUIStyle.Green);
507 WayPoint.ShowWayPoints =
true;
508 var matchingTickBox = showEntitiesTickBoxes?.Find(tb => tb.UserData as
string ==
"waypoint");
509 if (matchingTickBox !=
null)
511 matchingTickBox.Selected =
true;
513 generateWaypointsVerification.Close();
516 generateWaypointsVerification.Buttons[1].OnClicked = generateWaypointsVerification.Close;
520 if (GenerateWaypoints())
522 GUI.AddMessage(TextManager.Get(
"waypointsgeneratedsuccesfully"), GUIStyle.Green);
524 WayPoint.ShowWayPoints =
true;
531 spacing =
new GUIFrame(
new RectTransform(
new Vector2(0.02f, 1.0f), paddedTopPanel.
RectTransform), style:
null);
533 var selectedLayerText =
new GUITextBlock(
new RectTransform(
new Vector2(0.15f, 1.0f), paddedTopPanel.
RectTransform),
534 string.Empty, textAlignment: Alignment.Center);
535 selectedLayerText.TextGetter = () =>
537 string selectedLayer = layerList.
SelectedData as string;
538 if (selectedLayer != prevSelectedLayer)
540 prevSelectedLayer = selectedLayer;
541 return selectedLayer.IsNullOrEmpty() ?
string.Empty : TextManager.GetWithVariable(
"editor.layer.editinglayer",
"[layer]", selectedLayer);
543 return selectedLayerText.Text;
551 previouslyUsedPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.1f, 0.2f), GUI.Canvas) { MinSize = new Point(200, 200) })
555 previouslyUsedList =
new GUIListBox(
new RectTransform(
new Vector2(0.9f, 0.9f), previouslyUsedPanel.
RectTransform,
Anchor.Center))
557 PlaySoundOnSelect =
true,
558 ScrollBarVisible =
true,
559 OnSelected = SelectPrefab
564 layerPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.25f, 0.4f), GUI.Canvas, minSize:
new Point(300, 320)))
569 GUILayoutGroup layerGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f), layerPanel.
RectTransform, anchor:
Anchor.Center));
571 layerList =
new GUIListBox(
new RectTransform(
new Vector2(1f, 0.8f), layerGroup.RectTransform))
573 ScrollBarVisible =
true,
574 AutoHideScrollBar =
false,
575 OnSelected = (component, userdata) =>
581 layerSpecificButtons.ForEach(btn => btn.Enabled =
false);
587 layerSpecificButtons.ForEach(btn => btn.Enabled =
true);
593 GUILayoutGroup layerButtonGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1f, 0.2f), layerGroup.RectTransform));
594 GUILayoutGroup layerButtonTopGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1f, 0.5f), layerButtonGroup.RectTransform), isHorizontal:
true);
595 GUILayoutGroup layerButtonBottomGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1f, 0.5f), layerButtonGroup.RectTransform), isHorizontal:
true);
597 GUIButton layerAddButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1f), layerButtonTopGroup.RectTransform), text: TextManager.Get(
"editor.layer.newlayer"), style:
"GUIButtonFreeScale")
599 OnClicked = (button, o) =>
601 CreateNewLayer(
null, MapEntity.SelectedList.ToList());
606 GUIButton layerDeleteButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1f), layerButtonTopGroup.RectTransform), text: TextManager.Get(
"editor.layer.deletelayer"), style:
"GUIButtonFreeScale")
609 OnClicked = (button, o) =>
613 RenameLayer(layer,
null);
618 layerSpecificButtons.Add(layerDeleteButton);
620 GUIButton layerRenameButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), layerButtonBottomGroup.RectTransform), text: TextManager.Get(
"editor.layer.renamelayer"), style:
"GUIButtonFreeScale")
623 OnClicked = (button, o) =>
627 GUI.PromptTextInput(TextManager.Get(
"editor.layer.renamelayer"), layer, newName =>
629 RenameLayer(layer, newName);
635 layerSpecificButtons.Add(layerRenameButton);
637 GUIButton selectLayerButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), layerButtonBottomGroup.RectTransform), text: TextManager.Get(
"editor.layer.selectlayer"), style:
"GUIButtonFreeScale")
640 OnClicked = (button, o) =>
644 foreach (MapEntity entity
in MapEntity.MapEntityList.Where(me => !me.Removed && me.Layer == layer))
646 if (entity.IsSelected) {
continue; }
647 MapEntity.SelectedList.Add(entity);
653 layerSpecificButtons.Add(selectLayerButton);
655 GUITextBlock.AutoScaleAndNormalize(layerAddButton.TextBlock, layerDeleteButton.TextBlock, layerRenameButton.TextBlock, selectLayerButton.TextBlock);
657 Vector2 subPanelSize =
new Vector2(0.925f, 0.9f);
659 undoBufferPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.15f, 0.2f), GUI.Canvas) { MinSize = new Point(200, 200) })
664 undoBufferList =
new GUIListBox(
new RectTransform(subPanelSize, undoBufferPanel.
RectTransform,
Anchor.Center))
666 PlaySoundOnSelect =
true,
667 ScrollBarVisible =
true,
668 OnSelected = (_, userData) =>
671 if (userData is Command command)
680 int diff = index- commandIndex;
681 int amount = Math.Abs(diff);
696 undoBufferDisclaimer =
new GUIFrame(
new RectTransform(subPanelSize, undoBufferPanel.
RectTransform,
Anchor.Center), style:
null)
701 new GUITextBlock(
new RectTransform(Vector2.One, undoBufferDisclaimer.
RectTransform,
Anchor.Center), text: TextManager.Get(
"editor.undounavailable"), textAlignment: Alignment.Center, wrap:
true, font: GUIStyle.SubHeadingFont)
703 TextColor = GUIStyle.Orange
710 showEntitiesPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.15f, 0.5f), GUI.Canvas)
712 MinSize = new Point(190, 0)
723 var tickBox =
new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowLighting"))
725 UserData =
"lighting",
727 OnSelected = (GUITickBox obj) =>
733 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowWalls"))
737 OnSelected = (GUITickBox obj) => {
Structure.ShowWalls = obj.Selected;
return true; }
739 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowStructures"))
741 UserData =
"structure",
743 OnSelected = (GUITickBox obj) => {
Structure.ShowStructures = obj.Selected;
return true; }
745 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowItems"))
749 OnSelected = (GUITickBox obj) => {
Item.ShowItems = obj.Selected;
return true; }
751 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowWires"))
755 OnSelected = (GUITickBox obj) => {
Item.ShowWires = obj.Selected;
return true; }
757 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowWaypoints"))
759 UserData =
"waypoint",
761 OnSelected = (GUITickBox obj) => { WayPoint.ShowWayPoints = obj.
Selected;
return true; }
763 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowSpawnpoints"))
765 UserData =
"spawnpoint",
766 Selected = WayPoint.ShowSpawnPoints,
767 OnSelected = (GUITickBox obj) => { WayPoint.ShowSpawnPoints = obj.
Selected;
return true; }
769 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowLinks"))
773 OnSelected = (GUITickBox obj) => {
Item.ShowLinks = obj.Selected;
return true; }
775 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowHulls"))
779 OnSelected = (GUITickBox obj) => { Hull.ShowHulls = obj.
Selected;
return true; }
781 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"ShowGaps"))
785 OnSelected = (GUITickBox obj) => { Gap.ShowGaps = obj.
Selected;
return true; },
787 showEntitiesTickBoxes.AddRange(paddedShowEntitiesPanel.Children.Select(c => c as GUITickBox));
789 var subcategoryHeader =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), paddedShowEntitiesPanel.RectTransform), TextManager.Get(
"subcategories"), font: GUIStyle.SubHeadingFont);
790 subcategoryHeader.RectTransform.MinSize =
new Point(0, (
int)(subcategoryHeader.Rect.Height * 1.5f));
792 var subcategoryList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedShowEntitiesPanel.RectTransform) { MinSize = new Point(0, showEntitiesPanel.Rect.Height / 3) });
793 List<string> availableSubcategories =
new List<string>();
794 foreach (var prefab
in MapEntityPrefab.List)
796 if (!
string.IsNullOrEmpty(prefab.Subcategory) && !availableSubcategories.Contains(prefab.Subcategory))
798 availableSubcategories.Add(prefab.Subcategory);
801 foreach (
string subcategory
in availableSubcategories)
803 var tb =
new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.15f), subcategoryList.Content.RectTransform),
804 TextManager.Get(
"subcategory." + subcategory).Fallback(subcategory), font: GUIStyle.SmallFont)
806 UserData = subcategory,
808 OnSelected = (GUITickBox obj) => { hiddenSubCategories[(string)obj.UserData] = !obj.Selected;
return true; },
810 tb.TextBlock.Wrap =
true;
813 GUITextBlock.AutoScaleAndNormalize(subcategoryList.Content.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
814 foreach (GUIComponent child
in subcategoryList.Content.Children)
816 if (child is GUITickBox tb && tb.TextBlock.TextSize.X > tb.TextBlock.Rect.Width * 1.25f)
818 tb.ToolTip = tb.Text;
819 tb.Text = ToolBox.LimitString(tb.Text.Value, tb.Font, (
int)(tb.TextBlock.Rect.Width * 1.25f));
825 (
int)Math.Max(
showEntitiesPanel.
RectTransform.
NonScaledSize.X, paddedShowEntitiesPanel.RectTransform.Children.Max(c => (
int)((c.GUIComponent as GUITickBox)?.TextBlock.TextSize.X ?? 0)) / paddedShowEntitiesPanel.RectTransform.RelativeSize.X),
826 (
int)(paddedShowEntitiesPanel.RectTransform.Children.Sum(c => c.MinSize.Y) / paddedShowEntitiesPanel.RectTransform.RelativeSize.Y));
827 GUITextBlock.AutoScaleAndNormalize(paddedShowEntitiesPanel.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
831 float longestTextWidth = GUIStyle.SmallFont.MeasureString(TextManager.Get(
"SubEditorShadowCastingLights")).X;
832 entityCountPanel =
new GUIFrame(
new RectTransform(
new Vector2(0.08f, 0.5f), GUI.Canvas)
834 MinSize = new Point(Math.Max(170, (int)(longestTextWidth * 1.5f)), 0),
835 AbsoluteOffset = new Point(0, TopPanel.Rect.Height)
838 GUILayoutGroup paddedEntityCountPanel =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.95f), entityCountPanel.
RectTransform,
Anchor.Center))
841 AbsoluteSpacing = (int)(GUI.Scale * 4)
844 var itemCountText =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get(
"Items"),
845 textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont);
846 var itemCount =
new GUITextBlock(
new RectTransform(
new Vector2(0.33f, 1.0f), itemCountText.RectTransform,
Anchor.TopRight,
Pivot.TopLeft),
"", textAlignment: Alignment.CenterRight);
847 itemCount.TextGetter = () =>
849 int count =
Item.ItemList.Count;
850 if (dummyCharacter?.Inventory !=
null)
854 itemCount.TextColor = count >
MaxItems ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, count / (
float)
MaxItems);
855 return count.ToString();
858 var structureCountText =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get(
"Structures"),
859 textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont);
860 var structureCount =
new GUITextBlock(
new RectTransform(
new Vector2(0.33f, 1.0f), structureCountText.RectTransform,
Anchor.TopRight,
Pivot.TopLeft),
"", textAlignment: Alignment.CenterRight);
861 structureCount.TextGetter = () =>
863 int count = MapEntity.MapEntityList.Count -
Item.ItemList.Count - Hull.HullList.Count - WayPoint.WayPointList.Count - Gap.GapList.Count;
864 structureCount.TextColor = count >
MaxStructures ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, count / (
float)
MaxStructures);
865 return count.ToString();
868 var wallCountText =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get(
"Walls"),
869 textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont);
870 var wallCount =
new GUITextBlock(
new RectTransform(
new Vector2(0.33f, 1.0f), wallCountText.RectTransform,
Anchor.TopRight,
Pivot.TopLeft),
"", textAlignment: Alignment.CenterRight);
871 wallCount.TextGetter = () =>
874 return Structure.WallList.Count.ToString();
877 var lightCountLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get(
"SubEditorLights"),
878 textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont);
879 var lightCountText =
new GUITextBlock(
new RectTransform(
new Vector2(0.33f, 1.0f), lightCountLabel.RectTransform,
Anchor.TopRight,
Pivot.TopLeft),
"", textAlignment: Alignment.CenterRight);
880 lightCountText.TextGetter = () =>
883 foreach (Item item
in Item.ItemList)
885 if (item.ParentInventory !=
null) {
continue; }
888 lightCountText.TextColor = lightCount >
MaxLights ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, lightCount / (
float)
MaxLights);
889 return lightCount.ToString() +
"/" +
MaxLights;
891 var shadowCastingLightCountLabel =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get(
"SubEditorShadowCastingLights"),
892 textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont, wrap:
true);
893 var shadowCastingLightCountText =
new GUITextBlock(
new RectTransform(
new Vector2(0.33f, 1.0f), shadowCastingLightCountLabel.RectTransform,
Anchor.TopRight,
Pivot.TopLeft),
"", textAlignment: Alignment.CenterRight);
894 shadowCastingLightCountText.TextGetter = () =>
897 foreach (Item item
in Item.ItemList)
899 if (item.ParentInventory !=
null) {
continue; }
900 lightCount += item.GetComponents<
LightComponent>().Count(l => l.CastShadows && !l.DrawBehindSubs);
907 (
int)(paddedEntityCountPanel.RectTransform.Children.Max(c => (
int)((GUITextBlock) c.GUIComponent).TextSize.X / 0.75f) / paddedEntityCountPanel.RectTransform.RelativeSize.X),
908 (
int)(paddedEntityCountPanel.RectTransform.Children.Sum(c => (
int)(c.NonScaledSize.Y * 1.5f) + paddedEntityCountPanel.AbsoluteSpacing) / paddedEntityCountPanel.RectTransform.RelativeSize.Y));
913 hullVolumeFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.15f, 2.0f),
TopPanel.
RectTransform,
Anchor.BottomLeft,
Pivot.TopLeft, minSize:
new Point(300, 85)) { AbsoluteOffset =
new Point(entityCountPanel.
Rect.Width, 0) },
"GUIToolTip")
917 GUITextBlock totalHullVolume =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.5f), hullVolumeFrame.
RectTransform),
"", font: GUIStyle.SmallFont)
919 TextGetter = GetTotalHullVolume
921 GUITextBlock selectedHullVolume =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.5f), hullVolumeFrame.
RectTransform) { RelativeOffset = new Vector2(0.0f, 0.5f) },
"", font: GUIStyle.SmallFont)
923 TextGetter = GetSelectedHullVolume
927 { MinSize = new Point((int)(250 * GUI.Scale), (int)(80 * GUI.Scale)), AbsoluteOffset = new Point((int)(10 * GUI.Scale), -entityCountPanel.Rect.Height - (int)(10 * GUI.Scale)) },
"InnerFrame")
931 var saveAssemblyButton =
new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.8f), saveAssemblyFrame.
RectTransform,
Anchor.Center), TextManager.Get(
"SaveItemAssembly"));
932 saveAssemblyButton.TextBlock.AutoScaleHorizontal =
true;
933 saveAssemblyButton.OnClicked += (btn, userdata) =>
935 CreateSaveAssemblyScreen();
938 saveAssemblyFrame.
RectTransform.
MinSize =
new Point(saveAssemblyFrame.
Rect.Width, (
int)(saveAssemblyButton.Rect.Height / saveAssemblyButton.RectTransform.RelativeSize.Y));
941 { MinSize = new Point((int)(250 * GUI.Scale), (int)(80 * GUI.Scale)), AbsoluteOffset = new Point((int)(10 * GUI.Scale), -saveAssemblyFrame.Rect.Height - entityCountPanel.Rect.Height - (int)(10 * GUI.Scale)) },
"InnerFrame")
945 var saveStampButton =
new GUIButton(
new RectTransform(
new Vector2(0.9f, 0.8f), snapToGridFrame.
RectTransform,
Anchor.Center), TextManager.Get(
"subeditor.snaptogrid",
"spriteeditor.snaptogrid"));
946 saveStampButton.TextBlock.AutoScaleHorizontal =
true;
947 saveStampButton.OnClicked += (btn, userdata) =>
952 snapToGridFrame.
RectTransform.
MinSize =
new Point(snapToGridFrame.
Rect.Width, (
int)(saveStampButton.Rect.Height / saveStampButton.RectTransform.RelativeSize.Y));
957 EntityMenu =
new GUIFrame(
new RectTransform(
new Point(GameMain.GraphicsWidth, (
int)(359 * GUI.Scale)), GUI.Canvas,
Anchor.BottomRight));
959 toggleEntityMenuButton =
new GUIButton(
new RectTransform(
new Vector2(0.15f, 0.08f),
EntityMenu.
RectTransform,
Anchor.TopCenter,
Pivot.BottomCenter) { MinSize = new Point(0, 15) },
960 style:
"UIToggleButtonVertical")
962 OnClicked = (btn, userdata) =>
964 entityMenuOpen = !entityMenuOpen;
966 foreach (GUIComponent child
in btn.Children)
968 child.SpriteEffects = entityMenuOpen ? SpriteEffects.None : SpriteEffects.FlipVertically;
976 RelativeSpacing = 0.04f,
980 var entityMenuTop =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.13f), paddedTab.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
985 selectedCategoryButton =
new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), entityMenuTop.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"CategoryButton.All")
989 selectedCategoryText =
new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1.0f), entityMenuTop.RectTransform), TextManager.Get(
"MapEntityCategory.All"), font: GUIStyle.LargeFont);
991 var filterText =
new GUITextBlock(
new RectTransform(
new Vector2(0.1f, 1.0f), entityMenuTop.RectTransform), TextManager.Get(
"serverlog.filter"), font: GUIStyle.SubHeadingFont);
992 filterText.RectTransform.MaxSize =
new Point((
int)(filterText.TextSize.X * 1.5f),
int.MaxValue);
993 entityFilterBox =
new GUITextBox(
new RectTransform(
new Vector2(0.17f, 1.0f), entityMenuTop.RectTransform), font: GUIStyle.Font, createClearButton:
true);
996 if (text == lastFilter) {
return true; }
998 FilterEntities(text);
1003 new GUIFrame(
new RectTransform(
new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style:
null);
1005 entityCategoryButtons.Clear();
1006 entityCategoryButtons.Add(
1007 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), entityMenuTop.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
"", style:
"CategoryButton.All")
1009 OnClicked = (btn, userdata) =>
1011 OpenEntityMenu(null);
1019 entityCategoryButtons.Add(
new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), entityMenuTop.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
1020 "", style:
"CategoryButton." + category.ToString())
1022 UserData = category,
1023 ToolTip = TextManager.Get(
"MapEntityCategory." + category.ToString()),
1024 OnClicked = (btn, userdata) =>
1026 MapEntityCategory newCategory = (MapEntityCategory)userdata;
1027 OpenEntityMenu(newCategory);
1032 entityCategoryButtons.ForEach(b => b.RectTransform.MaxSize =
new Point(b.Rect.Height));
1034 new GUIFrame(
new RectTransform(
new Vector2(0.8f, 0.01f), paddedTab.RectTransform), style:
"HorizontalLine");
1036 var entityListContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.9f), paddedTab.RectTransform), style:
null);
1037 categorizedEntityList =
new GUIListBox(
new RectTransform(Vector2.One, entityListContainer.RectTransform), useMouseDownToSelect:
true);
1038 allEntityList =
new GUIListBox(
new RectTransform(Vector2.One, entityListContainer.RectTransform), useMouseDownToSelect:
true)
1040 OnSelected = SelectPrefab,
1041 UseGridLayout =
true,
1042 CheckSelected = MapEntityPrefab.GetSelected,
1044 PlaySoundOnSelect =
true,
1047 paddedTab.Recalculate();
1049 screenResolution =
new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
1052 private bool TestSubmarine(GUIButton button,
object obj)
1054 List<LocalizedString> errorMsgs =
new List<LocalizedString>();
1056 if (!Hull.HullList.Any())
1058 errorMsgs.Add(TextManager.Get(
"NoHullsWarning"));
1061 if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType ==
SpawnType.Human))
1063 errorMsgs.Add(TextManager.Get(
"NoHumanSpawnpointWarning"));
1066 if (errorMsgs.Any())
1068 new GUIMessageBox(TextManager.Get(
"Error"), LocalizedString.Join(
"\n\n", errorMsgs),
new Vector2(0.25f, 0.0f),
new Point(400, 200));
1074 backedUpSubInfo =
new SubmarineInfo(MainSub);
1076 GameSession gameSession =
new GameSession(backedUpSubInfo, Option.None, CampaignDataPath.Empty, GameModePreset.TestMode, CampaignSettings.Empty,
null);
1081 gameSession.ForceOutpostModule =
new SubmarineInfo(MainSub);
1084 GameMain.GameScreen.Select();
1086 gameSession.StartRound(
null,
false);
1088 foreach ((
string layerName, LayerData layerData) in Layers)
1090 Identifier identifier = layerName.ToIdentifier();
1091 bool enabled = layerData.IsVisible;
1092 MainSub.SetLayerEnabled(identifier, enabled);
1095 if (gameSession.GameMode is TestGameMode testGameMode)
1097 testGameMode.OnRoundEnd = () =>
1100 GameMain.SubEditorScreen.Select();
1109 backedUpSubInfo =
null;
1112 private void UpdateEntityList()
1117 int maxTextWidth = (int)(GUIStyle.SubHeadingFont.MeasureString(TextManager.Get(
"mapentitycategory.misc")).X + GUI.IntScale(50));
1118 Dictionary<string, List<MapEntityPrefab>> entityLists =
new Dictionary<string, List<MapEntityPrefab>>();
1119 Dictionary<string, MapEntityCategory> categoryKeys =
new Dictionary<string, MapEntityCategory>();
1124 LocalizedString categoryName = TextManager.Get(
"MapEntityCategory." + category);
1125 maxTextWidth = (int)Math.Max(maxTextWidth, GUIStyle.SubHeadingFont.MeasureString(categoryName.Replace(
" ",
"\n")).X + GUI.IntScale(50));
1126 foreach (MapEntityPrefab ep
in MapEntityPrefab.List)
1128 if (!ep.Category.HasFlag(category)) {
continue; }
1130 if (!entityLists.ContainsKey(category + ep.Subcategory))
1132 entityLists[category + ep.Subcategory] =
new List<MapEntityPrefab>();
1134 entityLists[category + ep.Subcategory].Add(ep);
1135 categoryKeys[category + ep.Subcategory] = category;
1136 LocalizedString subcategoryName = TextManager.Get(
"subcategory." + ep.Subcategory).Fallback(ep.Subcategory);
1137 if (subcategoryName !=
null)
1139 maxTextWidth = (int)Math.Max(maxTextWidth, GUIStyle.SubHeadingFont.MeasureString(subcategoryName.Replace(
" ",
"\n")).X + GUI.IntScale(50));
1145 int entitiesPerRow = (int)Math.Ceiling(categorizedEntityList.
Content.
Rect.Width / Math.Max(125 * GUI.Scale, 60));
1146 foreach (
string categoryKey
in entityLists.Keys)
1148 var categoryFrame =
new GUIFrame(
new RectTransform(Vector2.One, categorizedEntityList.
Content.
RectTransform), style:
null)
1150 ClampMouseRectToParent =
true,
1151 UserData = categoryKeys[categoryKey]
1154 new GUIFrame(
new RectTransform(Vector2.One, categoryFrame.RectTransform), style:
"HorizontalLine");
1156 LocalizedString categoryName = TextManager.Get(
"MapEntityCategory." + entityLists[categoryKey].First().Category);
1157 LocalizedString subCategoryName = entityLists[categoryKey].First().Subcategory;
1158 if (subCategoryName.IsNullOrEmpty())
1160 new GUITextBlock(
new RectTransform(
new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform,
Anchor.TopLeft),
1161 categoryName, textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont, wrap:
true)
1163 Padding =
new Vector4(GUI.IntScale(10))
1169 subCategoryName = subCategoryName.IsNullOrEmpty() ?
1170 TextManager.Get(
"mapentitycategory.misc") :
1171 (TextManager.Get($
"subcategory.{subCategoryName}").Fallback(subCategoryName));
1172 var categoryTitle =
new GUITextBlock(
new RectTransform(
new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform,
Anchor.TopLeft),
1173 categoryName, textAlignment: Alignment.TopLeft, font: GUIStyle.Font, wrap:
true)
1175 Padding =
new Vector4(GUI.IntScale(10))
1177 new GUITextBlock(
new RectTransform(
new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform,
Anchor.TopLeft) { AbsoluteOffset = new Point(0, (int)(categoryTitle.TextSize.Y + GUI.IntScale(10))) },
1178 subCategoryName, textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont, wrap:
true)
1180 Padding =
new Vector4(GUI.IntScale(10))
1184 var entityListInner =
new GUIListBox(
new RectTransform(
new Point(categoryFrame.Rect.Width - maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform,
Anchor.CenterRight),
1186 useMouseDownToSelect:
true)
1188 ScrollBarVisible =
false,
1189 AutoHideScrollBar =
false,
1190 OnSelected = SelectPrefab,
1191 UseGridLayout =
true,
1192 CheckSelected = MapEntityPrefab.GetSelected,
1193 ClampMouseRectToParent =
true,
1194 PlaySoundOnSelect =
true,
1196 entityListInner.ContentBackground.ClampMouseRectToParent =
true;
1197 entityListInner.Content.ClampMouseRectToParent =
true;
1199 foreach (MapEntityPrefab ep
in entityLists[categoryKey])
1202 if ((ep.HideInMenus || ep.HideInEditors) && !GameMain.DebugDraw) {
continue; }
1204 CreateEntityElement(ep, entitiesPerRow, entityListInner.Content);
1207 entityListInner.UpdateScrollBarSize();
1208 int contentHeight = (int)(entityListInner.TotalSize + entityListInner.Padding.Y + entityListInner.Padding.W);
1209 categoryFrame.RectTransform.NonScaledSize =
new Point(categoryFrame.Rect.Width, contentHeight);
1210 categoryFrame.RectTransform.MinSize =
new Point(0, contentHeight);
1211 entityListInner.RectTransform.NonScaledSize =
new Point(entityListInner.Rect.Width, contentHeight);
1212 entityListInner.RectTransform.MinSize =
new Point(0, contentHeight);
1214 entityListInner.Content.RectTransform.SortChildren((i1, i2) =>
1215 string.Compare(((MapEntityPrefab)i1.GUIComponent.UserData)?.Name.Value, (i2.GUIComponent.UserData as MapEntityPrefab)?.Name.Value, StringComparison.Ordinal));
1218 foreach (MapEntityPrefab ep
in MapEntityPrefab.List)
1221 if ((ep.HideInMenus || ep.HideInEditors) && !GameMain.DebugDraw) {
continue; }
1223 CreateEntityElement(ep, entitiesPerRow, allEntityList.
Content);
1226 string.Compare(((MapEntityPrefab)i1.GUIComponent.UserData)?.Name.Value, (i2.GUIComponent.UserData as MapEntityPrefab)?.Name.Value, StringComparison.Ordinal));
1230 private void CreateEntityElement(MapEntityPrefab ep,
int entitiesPerRow, GUIComponent parent)
1234 float relWidth = 1.0f / entitiesPerRow;
1235 GUIFrame frame =
new GUIFrame(
new RectTransform(
1236 new Vector2(relWidth, relWidth * ((
float)parent.Rect.Width / parent.Rect.Height)),
1237 parent.RectTransform)
1238 { MinSize = new Point(0, 50) },
1239 style:
"GUITextBox")
1242 ClampMouseRectToParent =
true
1244 frame.RectTransform.MinSize =
new Point(0, frame.Rect.Width);
1245 frame.RectTransform.MaxSize =
new Point(
int.MaxValue, frame.Rect.Width);
1247 LocalizedString name = legacy ? TextManager.GetWithVariable(
"legacyitemformat",
"[name]", ep.Name) : ep.Name;
1248 frame.ToolTip = ep.CreateTooltipText();
1252 frame.Color = Color.Magenta;
1255 if (ep.HideInMenus || ep.HideInEditors)
1257 frame.Color = Color.Red;
1258 name =
"[HIDDEN] " + name;
1260 frame.ToolTip = RichString.Rich(frame.ToolTip);
1262 GUILayoutGroup paddedFrame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 0.8f), frame.RectTransform,
Anchor.Center), childAnchor:
Anchor.TopCenter)
1265 RelativeSpacing = 0.03f,
1266 CanBeFocused =
false
1269 Sprite icon = ep.Sprite;
1270 Color iconColor = Color.White;
1271 if (ep is ItemPrefab itemPrefab)
1273 if (itemPrefab.InventoryIcon !=
null)
1275 icon = itemPrefab.InventoryIcon;
1276 iconColor = itemPrefab.InventoryIconColor;
1280 iconColor = itemPrefab.SpriteColor;
1283 GUIImage img =
null;
1284 if (ep.Sprite !=
null)
1286 img =
new GUIImage(
new RectTransform(
new Vector2(1.0f, 0.8f),
1287 paddedFrame.RectTransform,
Anchor.TopCenter), icon)
1289 CanBeFocused =
false,
1290 LoadAsynchronously =
true,
1291 SpriteEffects = icon.effects,
1292 Color = legacy ? iconColor * 0.6f : iconColor
1296 if (ep is ItemAssemblyPrefab itemAssemblyPrefab)
1298 new GUICustomComponent(
new RectTransform(
new Vector2(1.0f, 0.75f),
1299 paddedFrame.RectTransform,
Anchor.TopCenter), onDraw: (sb, customComponent) =>
1301 if (GUIImage.LoadingTextures) { return; }
1302 itemAssemblyPrefab.DrawIcon(sb, customComponent);
1305 HideElementsOutsideFrame =
true,
1306 ToolTip = frame.ToolTip.SanitizedString
1310 GUITextBlock textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), paddedFrame.RectTransform,
Anchor.BottomCenter),
1311 text: name, textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
1313 CanBeFocused =
false
1315 if (legacy) { textBlock.TextColor *= 0.6f; }
1316 if (name.IsNullOrEmpty())
1318 DebugConsole.AddWarning($
"Entity \"{ep.Identifier.Value}\" has no name!",
1319 contentPackage: ep.ContentPackage);
1320 textBlock.Text = frame.ToolTip = ep.Identifier.Value;
1321 textBlock.TextColor = GUIStyle.Red;
1323 textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
1326 && ep.ContentPackage?.Files.Length == 1
1327 && ContentPackageManager.LocalPackages.Contains(ep.ContentPackage))
1329 var deleteButton =
new GUIButton(
new RectTransform(
new Vector2(1.0f, 0.2f), paddedFrame.RectTransform,
Anchor.BottomCenter) { MinSize = new Point(0, 20) },
1330 TextManager.Get(
"Delete"), style:
"GUIButtonSmall")
1333 OnClicked = (btn, userData) =>
1335 ItemAssemblyPrefab assemblyPrefab = (ItemAssemblyPrefab)userData;
1336 if (assemblyPrefab !=
null)
1338 var msgBox =
new GUIMessageBox(
1339 TextManager.Get(
"DeleteDialogLabel"),
1340 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", assemblyPrefab.Name),
1341 new[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
1342 msgBox.Buttons[0].OnClicked += (deleteBtn, userData2) =>
1346 assemblyPrefab.Delete();
1351 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"DeleteFileError",
"[file]", assemblyPrefab.Name), e);
1355 msgBox.Buttons[0].OnClicked += msgBox.Close;
1356 msgBox.Buttons[1].OnClicked += msgBox.Close;
1363 paddedFrame.Recalculate();
1366 img.Scale = Math.Min(Math.Min(img.Rect.Width / img.Sprite.size.X, img.Rect.Height / img.Sprite.size.Y), 1.5f);
1367 img.RectTransform.NonScaledSize =
new Point((
int)(img.Sprite.size.X * img.Scale), img.Rect.Height);
1373 Select(enableAutoSave:
true);
1378 public void Select(
bool enableAutoSave =
true)
1383 $
"DeterminePublishedItemIds",
1384 SteamManager.Workshop.GetPublishedItems(),
1387 if (!t.TryGetResult(out ISet<Steamworks.Ugc.Item> items)) { return; }
1389 publishedWorkshopItemIds.Clear();
1390 publishedWorkshopItemIds.UnionWith(items.Select(it => it.Id.Value));
1393 GUI.PreventPauseMenuToggle =
false;
1394 if (!Directory.Exists(autoSavePath))
1396 if (Directory.CreateDirectory(autoSavePath, catchUnauthorizedAccessExceptions:
true) is { Exists: true } directoryInfo)
1398 directoryInfo.Attributes = System.IO.FileAttributes.Directory | System.IO.FileAttributes.Hidden;
1402 DebugConsole.ThrowError(
"Failed to create auto save directory!");
1406 if (!File.Exists(autoSaveInfoPath))
1410 AutoSaveInfo =
new XDocument(
new XElement(
"AutoSaves"));
1411 AutoSaveInfo.SaveSafe(autoSaveInfoPath, throwExceptions:
true);
1415 DebugConsole.ThrowError(
"Saving auto save info to \"" + autoSaveInfoPath +
"\" failed!", e);
1420 AutoSaveInfo = XMLExtensions.TryLoadXml(autoSaveInfoPath);
1423 GameMain.LightManager.AmbientLight =
1424 Level.Loaded?.GenerationParams?.AmbientLightColor ??
1425 new Color(3, 3, 3, 3);
1427 isAutoSaving =
false;
1429 if (!wasSelectedBefore)
1431 OpenEntityMenu(
null);
1432 wasSelectedBefore =
true;
1436 OpenEntityMenu(selectedCategory);
1439 if (backedUpSubInfo !=
null)
1444 string name = (MainSub ==
null) ? TextManager.Get(
"unspecifiedsubfilename").Value : MainSub.Info.Name;
1445 if (backedUpSubInfo !=
null) { name = backedUpSubInfo.
Name; }
1446 subNameLabel.
Text = ToolBox.LimitString(name, subNameLabel.
Font, subNameLabel.
Rect.Width);
1448 editorSelectedTime = Option<DateTime>.Some(DateTime.Now);
1450 GUI.ForceMouseOn(
null);
1451 SetMode(Mode.Default);
1453 if (backedUpSubInfo !=
null)
1455 MainSub =
new Submarine(backedUpSubInfo);
1460 backedUpSubInfo =
null;
1462 else if (MainSub ==
null)
1464 var subInfo =
new SubmarineInfo();
1465 MainSub =
new Submarine(subInfo, showErrorMessages:
false);
1466 ReconstructLayers();
1469 MainSub.UpdateTransform(interpolate:
false);
1470 cam.Position = MainSub.Position + MainSub.HiddenSubPosition;
1475 string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
1478 List<(
string Name, SubmarineInfo
Sub)> subs =
new List<(
string Name, SubmarineInfo Sub)>();
1480 foreach (SubmarineInfo sub
in SubmarineInfo.SavedSubmarines)
1483 if (Path.GetDirectoryName(Path.GetFullPath(sub.FilePath)) == downloadFolder) {
continue; }
1484 subs.Add((sub.Name, sub));
1487 foreach (var (subName, sub) in subs.OrderBy(tuple => tuple.Name))
1489 linkedSubBox.
AddItem(subName, sub);
1492 cam.UpdateTransform();
1494 CreateDummyCharacter();
1496 if (GameSettings.CurrentConfig.EnableSubmarineAutoSave && enableAutoSave)
1498 CoroutineManager.StartCoroutine(AutoSaveCoroutine(),
"SubEditorAutoSave");
1501 ImageManager.OnEditorSelected();
1504 ReconstructLayers();
1516 DebugConsole.ThrowError($
"Could not drag and drop the file. File \"{filePath}\" is corrupted!");
1521 LocalizedString body = TextManager.GetWithVariable(
"SubEditor.LoadConfirmBody",
"[submarine]", info.
Name);
1522 GUI.AskForConfirmation(TextManager.Get(
"Load"), body, onConfirm: () => LoadSub(info), onDeny: () => info.
Dispose());
1526 string text = File.ReadAllText(filePath);
1528 Vector2 mousePos = Mouse.GetState().Position.ToVector2();
1529 PasteAssembly(text, cam.ScreenToWorld(mousePos));
1535 if (saveFrame ==
null) {
break; }
1537 Texture2D texture =
Sprite.LoadTexture(filePath, compress:
false);
1539 if (MainSub !=
null)
1541 MainSub.Info.PreviewImage = previewImage.
Sprite;
1547 DebugConsole.ThrowError($
"Could not drag and drop the file. \"{extension}\" is not a valid file extension! (expected .xml, .sub, .png or .jpg)");
1557 private static IEnumerable<CoroutineStatus> AutoSaveCoroutine()
1559 DateTime target = DateTime.Now.AddSeconds(GameSettings.CurrentConfig.AutoSaveIntervalSeconds);
1560 DateTime tempTarget = DateTime.Now;
1562 bool wasPaused =
false;
1569 tempTarget = DateTime.Now;
1573 if (!GameMain.Instance.Paused && wasPaused)
1576 target = target.AddSeconds((DateTime.Now - tempTarget).TotalSeconds);
1578 yield
return CoroutineStatus.Running;
1581 if (Selected is SubEditorScreen)
1584 CoroutineManager.StartCoroutine(AutoSaveCoroutine(),
"SubEditorAutoSave");
1586 yield
return CoroutineStatus.Success;
1594 autoSaveLabel =
null;
1596 if (editorSelectedTime.TryUnwrap(out DateTime selectedTime))
1598 TimeSpan timeInEditor = DateTime.Now - selectedTime;
1601 if (timeInEditor.TotalSeconds > Timing.TotalTime * 1.5)
1603 DebugConsole.ThrowErrorAndLogToGA(
1604 "SubEditorScreen.DeselectEditorSpecific:InvalidTimeInEditor",
1605 $
"Error in sub editor screen. Calculated time in editor {timeInEditor} was larger than the time the game has run ({Timing.TotalTime} s).");
1609 AchievementManager.IncrementStat(AchievementStat.HoursInEditor, (
float)timeInEditor.TotalHours);
1610 editorSelectedTime = Option<DateTime>.None();
1614 GUI.ForceMouseOn(
null);
1616 if (ImageManager.EditorMode) { GameSettings.SaveCurrentConfig(); }
1627 DebugConsole.DeactivateCheats();
1630 SetMode(
Mode.Default);
1632 SoundPlayer.OverrideMusicType = Identifier.Empty;
1636 if (CoroutineManager.IsCoroutineRunning(
"SubEditorAutoSave"))
1638 CoroutineManager.StopCoroutines(
"SubEditorAutoSave");
1641 if (dummyCharacter !=
null)
1644 dummyCharacter =
null;
1650 if (component is
GUIMessageBox { Closed:
false, UserData:
"colorpicker" } msgBox)
1654 colorPicker.Dispose();
1664 private void CreateDummyCharacter()
1666 if (dummyCharacter !=
null) { RemoveDummyCharacter(); }
1668 dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero,
"",
id: Entity.DummyID, hasAi:
false);
1669 dummyCharacter.
Info.
Name =
"Galldren";
1674 if (CharacterInventory.PersonalSlots.HasFlag(dummyCharacter.
Inventory.
SlotTypes[i])) {
continue; }
1683 GameMain.World.ProcessChanges();
1691 private static void AutoSave()
1693 if (MapEntity.MapEntityList.Any() && GameSettings.CurrentConfig.EnableSubmarineAutoSave && !isAutoSaving)
1695 if (MainSub !=
null)
1697 isAutoSaving =
true;
1698 if (!Directory.Exists(autoSavePath)) {
return; }
1700 XDocument doc =
new XDocument(
new XElement(
"Submarine"));
1701 MainSub.SaveToXElement(doc.Root);
1702 Thread saveThread =
new Thread(start =>
1707 TimeSpan time = DateTime.UtcNow - DateTime.MinValue;
1708 string filePath = Path.Combine(autoSavePath, $
"AutoSave_{(ulong)time.TotalMilliseconds}.sub");
1709 SaveUtil.CompressStringToFile(filePath, doc.ToString());
1711 CrossThread.RequestExecutionOnMainThread(() =>
1713 if (AutoSaveInfo?.Root == null || MainSub?.Info == null) { return; }
1715 int saveCount = AutoSaveInfo.Root.Elements().Count();
1716 while (AutoSaveInfo.Root.Elements().Count() > MaxAutoSaves)
1718 XElement min = AutoSaveInfo.Root.Elements().OrderBy(element => element.GetAttributeUInt64(
"time", 0)).FirstOrDefault();
1719 #warning TODO: revise
1720 string path = min.GetAttributeStringUnrestricted(
"file",
"");
1721 if (string.IsNullOrWhiteSpace(path)) { continue; }
1723 if (IO.File.Exists(path)) { IO.File.Delete(path); }
1727 XElement newElement =
new XElement(
"AutoSave",
1728 new XAttribute(
"file", filePath),
1729 new XAttribute(
"name", MainSub.Info.Name),
1730 new XAttribute(
"time", (ulong)time.TotalSeconds));
1731 AutoSaveInfo.Root.Add(newElement);
1735 IO.SafeXML.SaveSafe(AutoSaveInfo, autoSaveInfoPath);
1739 DebugConsole.ThrowError(
"Saving auto save info to \"" + autoSaveInfoPath +
"\" failed!", e);
1744 CrossThread.RequestExecutionOnMainThread(DisplayAutoSavePrompt);
1748 CrossThread.RequestExecutionOnMainThread(() => DebugConsole.ThrowError(
"Auto saving submarine failed!", e));
1750 isAutoSaving =
false;
1751 }) { Name =
"Auto Save Thread" };
1757 private static void DisplayAutoSavePrompt()
1759 if (Selected != GameMain.SubEditorScreen) {
return; }
1762 LocalizedString label = TextManager.Get(
"AutoSaved");
1763 autoSaveLabel =
new GUILayoutGroup(
new RectTransform(
new Point(GUI.IntScale(150), GUI.IntScale(32)), GameMain.SubEditorScreen.EntityMenu.RectTransform,
Anchor.TopRight)
1765 ScreenSpaceOffset = new Point(-GUI.IntScale(16), -GUI.IntScale(48))
1766 }, isHorizontal:
true)
1768 CanBeFocused =
false
1771 GUIImage checkmark =
new GUIImage(
new RectTransform(
new Vector2(0.25f, 1f), autoSaveLabel.
RectTransform), style:
"MissionCompletedIcon", scaleToFit:
true);
1772 GUITextBlock labelComponent =
new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 1f), autoSaveLabel.
RectTransform), label, font: GUIStyle.SubHeadingFont, color: GUIStyle.Green)
1774 Padding = Vector4.Zero,
1775 AutoScaleHorizontal =
true,
1776 AutoScaleVertical =
true
1779 labelComponent.FadeOut(0.5f,
true, 1f);
1780 checkmark.FadeOut(0.5f,
true, 1f);
1781 autoSaveLabel?.
FadeOut(0.5f,
true, 1f);
1784 private bool SaveSub(ContentPackage packageToSaveTo)
1786 void handleExceptions(Action action)
1794 DebugConsole.ThrowError($
"An error occurred while trying to save {nameBox.Text}", e, createMessageBox:
true);
1798 if (
string.IsNullOrWhiteSpace(nameBox.
Text))
1800 GUI.AddMessage(TextManager.Get(
"SubNameMissingWarning"), GUIStyle.Red);
1806 if (packageToSaveTo ==
null)
1808 var subFiles = ContentPackageManager.EnabledPackages.All
1809 .SelectMany(p => p.GetFiles<SubmarineFile>());
1811 var nameConflictFile = subFiles.FirstOrDefault(file => Path.GetFileNameWithoutExtension(file.Path.Value).Equals(nameBox.
Text, StringComparison.InvariantCultureIgnoreCase));
1813 if (nameConflictFile !=
null)
1815 new GUIMessageBox(TextManager.Get(
"error"), TextManager.GetWithVariable(
"subeditor.duplicatefilenameerror",
"[packagename]", nameConflictFile.ContentPackage.Name));
1823 MainSub.Info.OutpostModuleInfo !=
null)
1825 MainSub.Info.PreviewImage =
null;
1830 var msgBox =
new GUIMessageBox(TextManager.Get(
"warning"), TextManager.Get(
"undefinedsubmarineclasswarning"),
new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1832 msgBox.Buttons[0].OnClicked = (bt, userdata) =>
1834 handleExceptions(() => SaveSubToFile(nameBox.
Text, packageToSaveTo));
1839 msgBox.Buttons[1].OnClicked = (bt, userdata) =>
1847 bool result =
false;
1848 handleExceptions(() => result = SaveSubToFile(nameBox.
Text, packageToSaveTo));
1853 private void ReloadModifiedPackage(ContentPackage p)
1855 if (p is
null) {
return; }
1856 p.ReloadSubsAndItemAssemblies();
1857 if (p.Files.Length == 0)
1859 Directory.Delete(p.Dir, recursive:
true);
1860 ContentPackageManager.LocalPackages.Refresh();
1861 ContentPackageManager.EnabledPackages.DisableRemovedMods();
1878 private bool SaveSubToFile(
string name,
ContentPackage packageToSaveTo)
1880 Type subFileType = DetermineSubFileType(MainSub?.Info.Type ??
SubmarineType.Player);
1882 static string getExistingFilePath(
ContentPackage package,
string fileName)
1885 if (package.
Files.Any(f => f.Path == MainSub.Info.FilePath && Path.GetFileName(f.Path.Value) == fileName))
1887 return MainSub.Info.FilePath;
1892 if (!GameMain.DebugDraw)
1894 if (
Submarine.GetLightCount() > MaxLights)
1896 new GUIMessageBox(TextManager.Get(
"error"), TextManager.GetWithVariable(
"subeditor.lightcounterror",
"[max]", MaxLights.ToString()));
1900 if (
Submarine.GetShadowCastingLightCount() > MaxShadowCastingLights)
1902 new GUIMessageBox(TextManager.Get(
"error"), TextManager.GetWithVariable(
"subeditor.shadowcastinglightcounterror",
"[max]", MaxShadowCastingLights.ToString()));
1907 if (
string.IsNullOrWhiteSpace(name))
1909 GUI.AddMessage(TextManager.Get(
"SubNameMissingWarning"), GUIStyle.Red);
1913 foreach (var illegalChar
in Path.GetInvalidFileNameCharsCrossPlatform())
1915 if (!name.Contains(illegalChar)) {
continue; }
1916 GUI.AddMessage(TextManager.GetWithVariable(
"SubNameIllegalCharsWarning",
"[illegalchar]", illegalChar.ToString()), GUIStyle.Red);
1922 string newLocalModDir = $
"{ContentPackage.LocalModsDir}/{name}";
1924 string savePath = $
"{name}.sub";
1925 string prevSavePath =
null;
1926 if (packageToSaveTo !=
null)
1928 var modProject =
new ModProject(packageToSaveTo);
1929 var fileListPath = packageToSaveTo.
Path;
1930 if (packageToSaveTo == ContentPackageManager.VanillaCorePackage)
1933 throw new InvalidOperationException(
"Cannot save to Vanilla package");
1936 getExistingFilePath(packageToSaveTo, savePath) ??
1937 string.Format((MainSub?.Info.Type ??
SubmarineType.Player)
switch
1939 SubmarineType.Player =>
"Content/Submarines/{0}",
1940 SubmarineType.Outpost =>
"Content/Map/Outposts/{0}",
1941 SubmarineType.Ruin =>
"Content/Submarines/{0}",
1942 SubmarineType.Wreck =>
"Content/Map/Wrecks/{0}",
1943 SubmarineType.BeaconStation =>
"Content/Map/BeaconStations/{0}",
1944 SubmarineType.EnemySubmarine =>
"Content/Map/EnemySubmarines/{0}",
1945 SubmarineType.OutpostModule => MainSub.Info.FilePath.Contains(
"RuinModules") ?
"Content/Map/RuinModules/{0}" :
"Content/Map/Outposts/{0}",
1946 _ => throw new InvalidOperationException()
1948 modProject.ModVersion =
"";
1952 string existingFilePath = getExistingFilePath(packageToSaveTo, savePath);
1954 if (existingFilePath !=
null)
1956 savePath = existingFilePath;
1961 savePath = Path.Combine(packageToSaveTo.
Dir, savePath);
1962 if (File.Exists(savePath))
1964 var verification =
new GUIMessageBox(TextManager.Get(
"warning"), TextManager.Get(
"subeditor.duplicatesubinpackage"),
1965 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1966 verification.Buttons[0].OnClicked = (_, _) =>
1968 addSubAndSave(modProject, savePath, fileListPath);
1969 verification.Close();
1972 verification.Buttons[1].OnClicked = verification.Close;
1977 addSubAndSave(modProject, savePath, fileListPath);
1981 savePath = Path.Combine(newLocalModDir, savePath);
1982 if (File.Exists(savePath))
1984 new GUIMessageBox(TextManager.Get(
"warning"), TextManager.GetWithVariable(
"subeditor.packagealreadyexists",
"[name]", name));
1989 ModProject modProject =
new ModProject { Name = name };
1990 addSubAndSave(modProject, savePath, Path.Combine(Path.GetDirectoryName(savePath), ContentPackage.FileListFileName));
1994 void addSubAndSave(ModProject modProject,
string filePath,
string packagePath)
1996 filePath = filePath.CleanUpPath();
1997 packagePath = packagePath.CleanUpPath();
1998 string packageDir = Path.GetDirectoryName(packagePath).CleanUpPathCrossPlatform(correctFilenameCase:
false);
1999 if (filePath.StartsWith(packageDir))
2001 filePath = $
"{ContentPath.ModDirStr}/{filePath[packageDir.Length..]}";
2003 if (!modProject.Files.Any(f => f.Type == subFileType && f.Path == filePath))
2006 var matchingFile = modProject.Files.FirstOrDefault(f => f.Type == subFileType && filePath.CleanUpPath().Equals(f.Path.CleanUpPath(), StringComparison.OrdinalIgnoreCase));
2007 if (matchingFile !=
null)
2009 File.Delete(matchingFile.Path.Replace(ContentPath.ModDirStr, packageDir, StringComparison.OrdinalIgnoreCase));
2010 modProject.RemoveFile(matchingFile);
2012 var newFile = ModProject.File.FromPath(filePath, subFileType);
2013 modProject.AddFile(newFile);
2016 using var _ = Validation.SkipInDebugBuilds();
2017 modProject.DiscardHashAndInstallTime();
2018 modProject.Save(packagePath);
2020 savePath = savePath.CleanUpPathCrossPlatform(correctFilenameCase:
false);
2021 if (MainSub !=
null)
2024 if (previewImage?.Sprite?.Texture !=
null && !previewImage.
Sprite.
Texture.IsDisposed && MainSub.Info.Type !=
SubmarineType.OutpostModule)
2026 bool savePreviewImage =
true;
2027 using System.IO.MemoryStream imgStream =
new System.IO.MemoryStream();
2034 DebugConsole.ThrowError($
"Saving the preview image of the submarine \"{MainSub.Info.Name}\" failed.", e);
2035 savePreviewImage =
false;
2037 MainSub.TrySaveAs(savePath, savePreviewImage ? imgStream :
null);
2041 MainSub.TrySaveAs(savePath);
2045 MainSub.CheckForErrors();
2047 GUI.AddMessage(TextManager.GetWithVariable(
"SubSavedNotification",
"[filepath]", savePath), GUIStyle.Green);
2049 if (savePath.StartsWith(newLocalModDir))
2051 ContentPackageManager.LocalPackages.Refresh();
2052 var newPackage = ContentPackageManager.LocalPackages.FirstOrDefault(p => p.Path.StartsWith(newLocalModDir));
2053 if (newPackage is RegularPackage regular)
2055 ContentPackageManager.EnabledPackages.EnableRegular(regular);
2056 GameSettings.SaveCurrentConfig();
2059 if (packageToSaveTo !=
null) { ReloadModifiedPackage(packageToSaveTo); }
2060 SubmarineInfo.RefreshSavedSub(savePath);
2061 if (prevSavePath !=
null && prevSavePath != savePath) { SubmarineInfo.RefreshSavedSub(prevSavePath); }
2062 MainSub.Info.PreviewImage = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.FilePath == savePath)?.PreviewImage;
2064 string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
2066 foreach (SubmarineInfo sub
in SubmarineInfo.SavedSubmarines)
2069 if (Path.GetDirectoryName(Path.GetFullPath(sub.FilePath)) == downloadFolder) {
continue; }
2070 linkedSubBox.
AddItem(sub.Name, sub);
2072 subNameLabel.
Text = ToolBox.LimitString(MainSub.Info.Name, subNameLabel.
Font, subNameLabel.
Rect.Width);
2079 private void CreateSaveScreen(
bool quickSave =
false)
2081 if (saveFrame !=
null) {
return; }
2086 SetMode(Mode.Default);
2089 saveFrame =
new GUIFrame(
new RectTransform(Vector2.One, GUI.Canvas,
Anchor.Center), style:
"GUIBackgroundBlocker");
2091 var innerFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.6f, 0.7f), saveFrame.
RectTransform,
Anchor.Center) { MinSize = new Point(750, 500) });
2092 var paddedSaveFrame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.9f), innerFrame.RectTransform,
Anchor.Center)) { Stretch =
true, RelativeSpacing = 0.02f };
2094 var columnArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.9f), paddedSaveFrame.RectTransform), isHorizontal:
true) { RelativeSpacing = 0.02f, Stretch =
true };
2095 var leftColumn =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.55f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.01f, Stretch =
true };
2096 var rightColumn =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.42f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.02f, Stretch =
true };
2100 var nameHeaderGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.03f), leftColumn.RectTransform),
true);
2101 var saveSubLabel =
new GUITextBlock(
new RectTransform(
new Vector2(.5f, 1f), nameHeaderGroup.RectTransform),
2102 TextManager.Get(
"SaveSubDialogName"), font: GUIStyle.SubHeadingFont);
2104 submarineNameCharacterCount =
new GUITextBlock(
new RectTransform(
new Vector2(.5f, 1f), nameHeaderGroup.RectTransform),
string.Empty, textAlignment: Alignment.TopRight);
2106 nameBox =
new GUITextBox(
new RectTransform(
new Vector2(1.0f, 0.05f), leftColumn.RectTransform))
2108 OnEnterPressed = ChangeSubName
2112 if (text.Length > submarineNameLimit)
2114 nameBox.
Text = text.Substring(0, submarineNameLimit);
2115 nameBox.
Flash(GUIStyle.Red);
2119 submarineNameCharacterCount.
Text = text.
Length +
" / " + submarineNameLimit;
2123 nameBox.
Text = MainSub?.Info.Name ??
"";
2125 submarineNameCharacterCount.
Text = nameBox.
Text.Length +
" / " + submarineNameLimit;
2127 var descriptionHeaderGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.03f), leftColumn.RectTransform), isHorizontal:
true);
2129 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), descriptionHeaderGroup.RectTransform), TextManager.Get(
"SaveSubDialogDescription"), font: GUIStyle.SubHeadingFont);
2130 submarineDescriptionCharacterCount =
new GUITextBlock(
new RectTransform(
new Vector2(.5f, 1f), descriptionHeaderGroup.RectTransform),
string.Empty, textAlignment: Alignment.TopRight);
2132 var descriptionContainer =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.25f), leftColumn.RectTransform));
2133 descriptionBox =
new GUITextBox(
new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform,
Anchor.Center),
2134 font: GUIStyle.SmallFont, style:
"GUITextBoxNoBorder", wrap:
true, textAlignment: Alignment.TopLeft)
2136 Padding =
new Vector4(10 * GUI.Scale)
2141 if (text.Length > submarineDescriptionLimit)
2143 descriptionBox.
Text = text.Substring(0, submarineDescriptionLimit);
2144 descriptionBox.
Flash(GUIStyle.Red);
2148 Vector2 textSize = textBox.Font.MeasureString(descriptionBox.
WrappedText);
2149 textBox.RectTransform.NonScaledSize =
new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(descriptionContainer.Content.Rect.Height, (
int)textSize.Y + 10));
2150 descriptionContainer.UpdateScrollBarSize();
2151 descriptionContainer.BarScroll = 1.0f;
2152 ChangeSubDescription(textBox, text);
2156 descriptionBox.
Text = GetSubDescription();
2158 var subTypeContainer =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.01f), leftColumn.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
2163 new GUITextBlock(
new RectTransform(
new Vector2(0.4f, 1f), subTypeContainer.RectTransform), TextManager.Get(
"submarinetype"));
2164 var subTypeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.6f, 1f), subTypeContainer.RectTransform));
2165 subTypeContainer.RectTransform.MinSize =
new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
2166 foreach (SubmarineType subType
in Enum.GetValues(typeof(SubmarineType)))
2169 string textTag =
"SubmarineType." + subType;
2170 if (subType ==
SubmarineType.EnemySubmarine && !TextManager.ContainsTag(textTag))
2172 textTag =
"MissionType.Pirate";
2174 subTypeDropdown.AddItem(TextManager.Get(textTag), subType);
2179 var layerVisibilityGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.01f), leftColumn.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2180 var visibleLayers = Layers.Where(l => !MainSub.Info.LayersHiddenByDefault.Contains(l.Key.ToIdentifier()));
2181 LocalizedString visibleLayersString = LocalizedString.Join(
", ", visibleLayers.Select(l => TextManager.Capitalize(l.Key)) ?? ((LocalizedString)
"None").ToEnumerable());
2182 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), layerVisibilityGroup.RectTransform), TextManager.Get(
"editor.layer.visiblebydefault"), textAlignment: Alignment.CenterLeft);
2183 var layerVisibilityDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), layerVisibilityGroup.RectTransform), text: visibleLayersString, selectMultiple:
true);
2184 foreach (var layer
in Layers)
2186 string layerName = layer.Key;
2187 layerVisibilityDropDown.AddItem(TextManager.Capitalize(layerName), layerName);
2188 if (visibleLayers.Contains(layer))
2190 layerVisibilityDropDown.SelectItem(layerName);
2193 layerVisibilityDropDown.AfterSelected += (button, _) =>
2195 MainSub.Info.LayersHiddenByDefault.Clear();
2196 foreach (var layer
in Layers)
2198 if (!layerVisibilityDropDown.SelectedDataMultiple.Contains(layer.Key))
2200 MainSub.Info.LayersHiddenByDefault.Add(layer.Key.ToIdentifier());
2204 layerVisibilityDropDown.Text = ToolBox.LimitString(layerVisibilityDropDown.Text.Value, layerVisibilityDropDown.Font, layerVisibilityDropDown.Rect.Width);
2207 layerVisibilityGroup.RectTransform.MinSize = layerVisibilityDropDown.RectTransform.MinSize =
new Point(0, layerVisibilityDropDown.RectTransform.Children.Max(c => c.MinSize.Y));
2212 var subTypeDependentSettingFrame =
new GUIFrame(
new RectTransform((1.0f, 0.6f), leftColumn.RectTransform), style:
"InnerFrame");
2214 var outpostModuleSettingsContainer =
new GUILayoutGroup(
new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
2216 CanBeFocused =
true,
2223 var outpostModuleGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2225 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform), TextManager.Get(
"outpostmoduletype"), textAlignment: Alignment.CenterLeft);
2226 HashSet<Identifier> availableFlags =
new HashSet<Identifier>();
2227 foreach (Identifier flag
in OutpostGenerationParams.OutpostParams.SelectMany(p => p.ModuleCounts.Select(m => m.Identifier))) { availableFlags.Add(flag); }
2228 foreach (Identifier flag
in RuinGeneration.RuinGenerationParams.RuinParams.SelectMany(p => p.ModuleCounts.Select(m => m.Identifier))) { availableFlags.Add(flag); }
2229 foreach (var sub
in SubmarineInfo.SavedSubmarines)
2231 if (sub.OutpostModuleInfo ==
null) {
continue; }
2232 foreach (Identifier flag
in sub.OutpostModuleInfo.ModuleFlags)
2234 if (flag ==
"none") {
continue; }
2235 availableFlags.Add(flag);
2238 if (MainSub?.Info?.OutpostModuleInfo is { } moduleInfo)
2240 foreach (var moduleType
in moduleInfo.ModuleFlags)
2242 availableFlags.Add(moduleType);
2245 var moduleTypeDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform),
2246 text: LocalizedString.Join(
", ", MainSub?.Info?.OutpostModuleInfo?.ModuleFlags.Select(s => TextManager.Capitalize(s.Value)) ?? ((LocalizedString)
"None").ToEnumerable()), selectMultiple:
true);
2247 foreach (Identifier flag
in availableFlags.OrderBy(f => f.Value, StringComparer.InvariantCultureIgnoreCase))
2249 moduleTypeDropDown.AddItem(TextManager.Capitalize(flag.Value), flag);
2250 if (MainSub?.Info?.OutpostModuleInfo ==
null) {
continue; }
2251 if (MainSub.Info.OutpostModuleInfo.ModuleFlags.Contains(flag))
2253 moduleTypeDropDown.SelectItem(flag);
2256 moduleTypeDropDown.AfterSelected += (_, __) =>
2258 if (MainSub?.Info?.OutpostModuleInfo ==
null) {
return false; }
2259 MainSub.Info.OutpostModuleInfo.SetFlags(moduleTypeDropDown.SelectedDataMultiple.Cast<Identifier>());
2260 moduleTypeDropDown.Text = ToolBox.LimitString(
2261 MainSub.Info.OutpostModuleInfo.ModuleFlags.Any(f => f !=
"none") ? moduleTypeDropDown.Text :
"None",
2262 moduleTypeDropDown.Font, moduleTypeDropDown.Rect.Width);
2265 outpostModuleGroup.RectTransform.MinSize =
new Point(0, outpostModuleGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2267 var addTypeGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2268 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), addTypeGroup.RectTransform), TextManager.Get(
"leveleditor.addmoduletype"), textAlignment: Alignment.CenterLeft);
2269 var textBox =
new GUITextBox(
new RectTransform(
new Vector2(0.4f, 1f), addTypeGroup.RectTransform));
2271 new GUIButton(
new RectTransform(
new Vector2(0.1f, 0.9f), addTypeGroup.RectTransform), text:
"+", style:
"GUIButtonSmallFreeScale")
2273 OnClicked = (btn, _) =>
2275 if (textBox.Text.IsNullOrEmpty())
2280 if (MainSub?.Info?.OutpostModuleInfo is { } moduleInfo)
2282 moduleInfo.SetFlags(moduleInfo.ModuleFlags.Append(textBox.Text.ToIdentifier()).ToList());
2290 addTypeGroup.RectTransform.MinSize =
new Point(0, addTypeGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2294 var allowAttachGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2296 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), allowAttachGroup.RectTransform), TextManager.Get(
"outpostmoduleallowattachto"), textAlignment: Alignment.CenterLeft);
2298 var allowAttachDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), allowAttachGroup.RectTransform),
2299 text: LocalizedString.Join(
", ", MainSub?.Info?.OutpostModuleInfo?.AllowAttachToModules.Select(s => TextManager.Capitalize(s.Value)) ?? ((LocalizedString)
"Any").ToEnumerable()), selectMultiple:
true);
2300 allowAttachDropDown.AddItem(TextManager.Capitalize(
"any"),
"any".ToIdentifier());
2301 if (MainSub.Info.OutpostModuleInfo ==
null ||
2302 !MainSub.Info.OutpostModuleInfo.AllowAttachToModules.Any() ||
2303 MainSub.Info.OutpostModuleInfo.AllowAttachToModules.All(s => s ==
"any"))
2305 allowAttachDropDown.SelectItem(
"any".ToIdentifier());
2307 foreach (Identifier flag
in availableFlags.OrderBy(f => f.Value, StringComparer.InvariantCultureIgnoreCase))
2309 if (flag ==
"any" || flag ==
"none") {
continue; }
2310 allowAttachDropDown.AddItem(TextManager.Capitalize(flag.Value), flag);
2311 if (MainSub?.Info?.OutpostModuleInfo ==
null) {
continue; }
2312 if (MainSub.Info.OutpostModuleInfo.AllowAttachToModules.Contains(flag))
2314 allowAttachDropDown.SelectItem(flag);
2317 allowAttachDropDown.AfterSelected += (_, __) =>
2319 if (MainSub?.Info?.OutpostModuleInfo ==
null) {
return false; }
2320 MainSub.Info.OutpostModuleInfo.SetAllowAttachTo(allowAttachDropDown.SelectedDataMultiple.Cast<Identifier>());
2321 allowAttachDropDown.Text = ToolBox.LimitString(
2322 MainSub.Info.OutpostModuleInfo.ModuleFlags.Any(f => f !=
"none") ? allowAttachDropDown.Text.Value :
"None",
2323 allowAttachDropDown.Font, allowAttachDropDown.Rect.Width);
2326 allowAttachGroup.RectTransform.MinSize =
new Point(0, allowAttachGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2330 var locationTypeGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2332 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), locationTypeGroup.RectTransform), TextManager.Get(
"outpostmoduleallowedlocationtypes"), textAlignment: Alignment.CenterLeft);
2333 HashSet<Identifier> availableLocationTypes =
new HashSet<Identifier>();
2334 foreach (LocationType locationType
in LocationType.Prefabs) { availableLocationTypes.Add(locationType.Identifier); }
2336 var locationTypeDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), locationTypeGroup.RectTransform),
2337 text: LocalizedString.Join(
", ", MainSub?.Info?.OutpostModuleInfo?.AllowedLocationTypes.Select(lt => TextManager.Capitalize(lt.Value)) ?? ((LocalizedString)
"any").ToEnumerable()), selectMultiple:
true);
2338 locationTypeDropDown.AddItem(TextManager.Capitalize(
"any"),
"any".ToIdentifier());
2339 foreach (Identifier locationType
in availableLocationTypes.OrderBy(f => f.Value, StringComparer.InvariantCultureIgnoreCase))
2341 locationTypeDropDown.AddItem(TextManager.Capitalize(locationType.Value), locationType);
2342 if (MainSub?.Info?.OutpostModuleInfo ==
null) {
continue; }
2343 if (MainSub.Info.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType))
2345 locationTypeDropDown.SelectItem(locationType);
2348 if (!MainSub.Info?.OutpostModuleInfo?.AllowedLocationTypes?.Any() ??
true) { locationTypeDropDown.SelectItem(
"any".ToIdentifier()); }
2350 locationTypeDropDown.AfterSelected += (_, __) =>
2352 MainSub?.Info?.OutpostModuleInfo?.SetAllowedLocationTypes(locationTypeDropDown.SelectedDataMultiple.Cast<Identifier>());
2353 locationTypeDropDown.Text = ToolBox.LimitString(locationTypeDropDown.Text.Value, locationTypeDropDown.Font, locationTypeDropDown.Rect.Width);
2356 locationTypeGroup.RectTransform.MinSize =
new Point(0, locationTypeGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2360 var gapPositionGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2361 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), gapPositionGroup.RectTransform), TextManager.Get(
"outpostmodulegappositions"), textAlignment: Alignment.CenterLeft);
2362 var gapPositionDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), gapPositionGroup.RectTransform),
2363 text:
"", selectMultiple:
true);
2365 var outpostModuleInfo = MainSub.Info?.OutpostModuleInfo;
2366 if (outpostModuleInfo !=
null)
2368 if (outpostModuleInfo.GapPositions == OutpostModuleInfo.GapPosition.None)
2370 outpostModuleInfo.DetermineGapPositions(MainSub);
2372 foreach (OutpostModuleInfo.GapPosition gapPos in Enum.GetValues(typeof(OutpostModuleInfo.GapPosition)))
2374 if (gapPos == OutpostModuleInfo.GapPosition.None) {
continue; }
2375 gapPositionDropDown.AddItem(TextManager.Capitalize(gapPos.ToString()), gapPos);
2376 if (outpostModuleInfo.GapPositions.HasFlag(gapPos))
2378 gapPositionDropDown.SelectItem(gapPos);
2383 gapPositionDropDown.AfterSelected += (_, __) =>
2385 if (MainSub.Info?.OutpostModuleInfo ==
null) {
return false; }
2386 MainSub.Info.OutpostModuleInfo.GapPositions = OutpostModuleInfo.GapPosition.None;
2387 if (gapPositionDropDown.SelectedDataMultiple.Any())
2389 List<LocalizedString> gapPosTexts =
new List<LocalizedString>();
2390 foreach (OutpostModuleInfo.GapPosition gapPos in gapPositionDropDown.SelectedDataMultiple)
2392 MainSub.Info.OutpostModuleInfo.GapPositions |= gapPos;
2393 gapPosTexts.Add(TextManager.Capitalize(gapPos.ToString()));
2395 gapPositionDropDown.Text = ToolBox.LimitString(
string.Join(
", ", gapPosTexts), gapPositionDropDown.Font, gapPositionDropDown.Rect.Width);
2399 gapPositionDropDown.Text = ToolBox.LimitString(
"None", gapPositionDropDown.Font, gapPositionDropDown.Rect.Width);
2403 gapPositionGroup.RectTransform.MinSize =
new Point(0, gapPositionGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2405 var canAttachToPrevGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
2406 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 1f), canAttachToPrevGroup.RectTransform), TextManager.Get(
"canattachtoprevious"), textAlignment: Alignment.CenterLeft)
2408 ToolTip = TextManager.Get(
"canattachtoprevious.tooltip")
2410 var canAttachToPrevDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.5f, 1f), canAttachToPrevGroup.RectTransform),
2411 text:
"", selectMultiple:
true);
2412 if (outpostModuleInfo !=
null)
2414 foreach (OutpostModuleInfo.GapPosition gapPos in Enum.GetValues(typeof(OutpostModuleInfo.GapPosition)))
2416 if (gapPos == OutpostModuleInfo.GapPosition.None) {
continue; }
2417 canAttachToPrevDropDown.AddItem(TextManager.Capitalize(gapPos.ToString()), gapPos);
2418 if (outpostModuleInfo.CanAttachToPrevious.HasFlag(gapPos))
2420 canAttachToPrevDropDown.SelectItem(gapPos);
2425 canAttachToPrevDropDown.AfterSelected += (_, __) =>
2427 if (
Submarine.MainSub.Info?.OutpostModuleInfo ==
null) {
return false; }
2428 Submarine.MainSub.Info.OutpostModuleInfo.CanAttachToPrevious = OutpostModuleInfo.GapPosition.None;
2429 if (canAttachToPrevDropDown.SelectedDataMultiple.Any())
2431 List<string> gapPosTexts =
new List<string>();
2432 foreach (OutpostModuleInfo.GapPosition gapPos in canAttachToPrevDropDown.SelectedDataMultiple)
2434 Submarine.MainSub.Info.OutpostModuleInfo.CanAttachToPrevious |= gapPos;
2435 gapPosTexts.Add(TextManager.Capitalize(gapPos.ToString()).Value);
2437 canAttachToPrevDropDown.Text = ToolBox.LimitString(
string.Join(
", ", gapPosTexts), canAttachToPrevDropDown.Font, canAttachToPrevDropDown.Rect.Width);
2441 canAttachToPrevDropDown.Text = ToolBox.LimitString(
"None", canAttachToPrevDropDown.Font, canAttachToPrevDropDown.Rect.Width);
2445 canAttachToPrevGroup.RectTransform.MinSize =
new Point(0, gapPositionGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2450 var maxModuleCountGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true)
2454 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), maxModuleCountGroup.RectTransform),
2455 TextManager.Get(
"OutPostModuleMaxCount"), textAlignment: Alignment.CenterLeft, wrap:
true)
2457 ToolTip = TextManager.Get(
"OutPostModuleMaxCountToolTip")
2459 new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), maxModuleCountGroup.RectTransform),
NumberType.Int)
2461 ToolTip = TextManager.Get(
"OutPostModuleMaxCountToolTip"),
2462 IntValue = MainSub?.Info?.OutpostModuleInfo?.MaxCount ?? 1000,
2465 OnValueChanged = (numberInput) =>
2467 MainSub.Info.OutpostModuleInfo.MaxCount = numberInput.IntValue;
2470 maxModuleCountGroup.RectTransform.MinSize =
new Point(0, maxModuleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2472 var commonnessGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), outpostModuleSettingsContainer.RectTransform), isHorizontal:
true)
2476 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), commonnessGroup.RectTransform),
2477 TextManager.Get(
"subeditor.outpostcommonness"), textAlignment: Alignment.CenterLeft, wrap:
true);
2478 new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), commonnessGroup.RectTransform),
NumberType.Float)
2480 FloatValue = MainSub?.Info?.OutpostModuleInfo?.Commonness ?? 10,
2482 MaxValueFloat = 100,
2483 OnValueChanged = (numberInput) =>
2485 MainSub.Info.OutpostModuleInfo.Commonness = numberInput.FloatValue;
2488 commonnessGroup.RectTransform.MinSize =
new Point(0, commonnessGroup.RectTransform.Children.Max(c => c.MinSize.Y));
2490 outpostModuleSettingsContainer.RectTransform.MinSize =
new Point(0, outpostModuleSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
2494 var extraSettingsContainer =
new GUILayoutGroup(
new RectTransform(
new Vector2(1, 0.5f), subTypeDependentSettingFrame.RectTransform))
2496 CanBeFocused =
true,
2501 var minDifficultyGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), extraSettingsContainer.RectTransform), isHorizontal:
true)
2505 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), minDifficultyGroup.RectTransform),
2506 TextManager.Get(
"minleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap:
true);
2507 var numInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), minDifficultyGroup.RectTransform),
NumberType.Int)
2509 IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MinLevelDifficulty ?? 0),
2512 OnValueChanged = (numberInput) =>
2514 MainSub.Info.GetExtraSubmarineInfo.MinLevelDifficulty = numberInput.IntValue;
2517 minDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
2518 var maxDifficultyGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), extraSettingsContainer.RectTransform), isHorizontal:
true)
2522 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), maxDifficultyGroup.RectTransform),
2523 TextManager.Get(
"maxleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap:
true);
2524 numInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), maxDifficultyGroup.RectTransform),
NumberType.Int)
2526 IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MaxLevelDifficulty ?? 100),
2529 OnValueChanged = (numberInput) =>
2531 MainSub.Info.GetExtraSubmarineInfo.MaxLevelDifficulty = numberInput.IntValue;
2534 maxDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
2539 var outpostSettingsContainer =
new GUILayoutGroup(
new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
2541 CanBeFocused =
true,
2546 var outpostTagsGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), outpostSettingsContainer.RectTransform), isHorizontal:
true)
2550 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), outpostTagsGroup.RectTransform),
2551 TextManager.Get(
"sp.item.tags.name"), textAlignment: Alignment.CenterLeft, wrap:
true);
2552 var outpostTagsBox =
new GUITextBox(
new RectTransform(
new Vector2(0.4f, 1.0f), outpostTagsGroup.RectTransform))
2554 OnEnterPressed = (GUITextBox textBox,
string text) =>
2556 MainSub.Info.OutpostTags = text.ToIdentifiers().ToImmutableHashSet();
2559 OverflowClip =
true,
2562 outpostTagsBox.OnDeselected += (textbox, _) =>
2564 MainSub.Info.OutpostTags = outpostTagsBox.Text.ToIdentifiers().ToImmutableHashSet();
2566 if (MainSub.Info.OutpostTags !=
null)
2568 outpostTagsBox.Text = MainSub.Info.OutpostTags.ConvertToString();
2571 outpostTagsGroup.RectTransform.MaxSize = outpostTagsBox.RectTransform.MaxSize;
2575 var enemySubmarineSettingsContainer =
new GUILayoutGroup(
new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
2577 CanBeFocused =
true,
2584 var enemySubmarineRewardGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal:
true)
2588 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), enemySubmarineRewardGroup.RectTransform),
2589 TextManager.Get(
"enemysub.reward"), textAlignment: Alignment.CenterLeft, wrap:
true);
2590 numInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), enemySubmarineRewardGroup.RectTransform),
NumberType.Int, buttonVisibility: GUINumberInput.ButtonVisibility.ForceHidden)
2592 IntValue = (int)(MainSub?.Info?.EnemySubmarineInfo?.Reward ?? 4000),
2594 MaxValueInt = 999999,
2595 OnValueChanged = (numberInput) =>
2597 MainSub.Info.EnemySubmarineInfo.Reward = numberInput.IntValue;
2600 enemySubmarineRewardGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
2601 var enemySubmarineDifficultyGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal:
true)
2605 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), enemySubmarineDifficultyGroup.RectTransform),
2606 TextManager.Get(
"preferreddifficulty"), textAlignment: Alignment.CenterLeft, wrap:
true);
2607 numInput =
new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), enemySubmarineDifficultyGroup.RectTransform),
NumberType.Int)
2609 IntValue = (int)(MainSub?.Info?.EnemySubmarineInfo?.PreferredDifficulty ?? 50),
2612 OnValueChanged = (numberInput) =>
2614 MainSub.Info.EnemySubmarineInfo.PreferredDifficulty = numberInput.IntValue;
2617 enemySubmarineDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
2618 var enemySubmarineTagsGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal:
true)
2622 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), enemySubmarineTagsGroup.RectTransform),
2623 TextManager.Get(
"sp.item.tags.name"), textAlignment: Alignment.CenterLeft, wrap:
true);
2624 var tagsBox =
new GUITextBox(
new RectTransform(
new Vector2(0.4f, 1.0f), enemySubmarineTagsGroup.RectTransform))
2626 OnEnterPressed = ChangeEnemySubTags,
2627 OverflowClip =
true,
2630 tagsBox.OnDeselected += (textbox, _) => ChangeEnemySubTags(textbox, textbox.Text);
2631 if (MainSub?.Info?.EnemySubmarineInfo?.MissionTags !=
null)
2633 tagsBox.Text =
string.Join(
',', MainSub.Info.EnemySubmarineInfo.MissionTags);
2636 enemySubmarineTagsGroup.RectTransform.MaxSize = tagsBox.RectTransform.MaxSize;
2637 enemySubmarineSettingsContainer.RectTransform.MinSize =
new Point(0, enemySubmarineSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
2641 var beaconSettingsContainer =
new GUILayoutGroup(
new RectTransform(Vector2.One, extraSettingsContainer.RectTransform))
2643 CanBeFocused =
true,
2648 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get(
"allowdamagedwalls"))
2650 Selected = MainSub?.Info?.BeaconStationInfo?.AllowDamagedWalls ??
true,
2651 OnSelected = (tb) =>
2653 MainSub.Info.BeaconStationInfo.AllowDamagedWalls = tb.Selected;
2657 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get(
"allowdamageddevices"))
2659 Selected = MainSub?.Info?.BeaconStationInfo?.AllowDamagedDevices ??
true,
2660 OnSelected = (tb) =>
2662 MainSub.Info.BeaconStationInfo.AllowDamagedDevices = tb.Selected;
2666 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get(
"allowdisconnectedwires"))
2668 Selected = MainSub?.Info?.BeaconStationInfo?.AllowDisconnectedWires ??
true,
2669 OnSelected = (tb) =>
2671 MainSub.Info.BeaconStationInfo.AllowDisconnectedWires = tb.Selected;
2675 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get(
"beaconstationplacement"))
2677 Selected = MainSub.Info.BeaconStationInfo is { Placement: Level.PlacementType.Top },
2678 OnSelected = (tb) =>
2680 MainSub.Info.BeaconStationInfo.Placement = tb.Selected ?
2681 Level.PlacementType.Top :
2682 Level.PlacementType.Bottom;
2686 beaconSettingsContainer.RectTransform.MinSize =
new Point(0, beaconSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
2690 var subSettingsContainer =
new GUILayoutGroup(
new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
2695 var priceGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true)
2699 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), priceGroup.RectTransform),
2700 TextManager.Get(
"subeditor.price"), textAlignment: Alignment.CenterLeft, wrap:
true);
2703 int basePrice = (GameMain.DebugDraw ? 0 : MainSub?.CalculateBasePrice()) ?? 1000;
2704 new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), priceGroup.RectTransform),
NumberType.Int, buttonVisibility: GUINumberInput.ButtonVisibility.ForceHidden)
2706 IntValue = Math.Max(MainSub?.Info?.Price ?? basePrice, basePrice),
2707 MinValueInt = basePrice,
2708 MaxValueInt = 999999,
2709 OnValueChanged = (numberInput) =>
2711 MainSub.Info.Price = numberInput.IntValue;
2714 if (MainSub?.Info !=
null)
2716 MainSub.Info.Price = Math.Max(MainSub.Info.Price, basePrice);
2719 var classGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
2723 var classText =
new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), classGroup.RectTransform),
2724 TextManager.Get(
"submarineclass"), textAlignment: Alignment.CenterLeft, wrap:
true)
2726 ToolTip = TextManager.Get(
"submarineclass.description")
2728 GUIDropDown classDropDown =
new GUIDropDown(
new RectTransform(
new Vector2(0.4f, 1.0f), classGroup.RectTransform));
2729 classDropDown.RectTransform.MinSize =
new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
2730 foreach (SubmarineClass subClass
in Enum.GetValues(typeof(SubmarineClass)))
2732 classDropDown.AddItem(TextManager.Get($
"{nameof(SubmarineClass)}.{subClass}"), subClass, toolTip: TextManager.Get($
"submarineclass.{subClass}.description"));
2735 classDropDown.OnSelected += (selected, userdata) =>
2741 MainSub.Info.SubmarineClass = submarineClass;
2750 classDropDown.SelectItem(!MainSub.Info.HasTag(
SubmarineTag.Shuttle) ? MainSub.Info.SubmarineClass : (object)
SubmarineTag.Shuttle);
2752 var tierGroup =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true)
2756 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), tierGroup.RectTransform),
2757 TextManager.Get(
"subeditor.tier"), textAlignment: Alignment.CenterLeft, wrap:
true)
2759 ToolTip = TextManager.Get(
"submarinetier.description")
2762 new GUINumberInput(
new RectTransform(
new Vector2(0.4f, 1.0f), tierGroup.RectTransform),
NumberType.Int)
2764 IntValue = MainSub.Info.Tier,
2766 MaxValueInt = SubmarineInfo.HighestTier,
2767 OnValueChanged = (numberInput) =>
2769 MainSub.Info.Tier = numberInput.IntValue;
2772 if (MainSub?.Info !=
null)
2774 MainSub.Info.Tier = Math.Clamp(MainSub.Info.Tier, 1, SubmarineInfo.HighestTier);
2777 var crewSizeArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true)
2783 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), crewSizeArea.RectTransform),
2784 TextManager.Get(
"RecommendedCrewSize"), textAlignment: Alignment.CenterLeft, wrap:
true);
2785 var crewSizeMin =
new GUINumberInput(
new RectTransform(
new Vector2(0.17f, 1.0f), crewSizeArea.RectTransform),
NumberType.Int, relativeButtonAreaWidth: 0.25f)
2790 new GUITextBlock(
new RectTransform(
new Vector2(0.06f, 1.0f), crewSizeArea.RectTransform),
"-", textAlignment: Alignment.Center);
2791 var crewSizeMax =
new GUINumberInput(
new RectTransform(
new Vector2(0.17f, 1.0f), crewSizeArea.RectTransform),
NumberType.Int, relativeButtonAreaWidth: 0.25f)
2797 crewSizeMin.OnValueChanged += (numberInput) =>
2799 crewSizeMax.IntValue = Math.Max(crewSizeMax.IntValue, numberInput.IntValue);
2800 MainSub.Info.RecommendedCrewSizeMin = crewSizeMin.IntValue;
2801 MainSub.Info.RecommendedCrewSizeMax = crewSizeMax.IntValue;
2804 crewSizeMax.OnValueChanged += (numberInput) =>
2806 crewSizeMin.IntValue = Math.Min(crewSizeMin.IntValue, numberInput.IntValue);
2807 MainSub.Info.RecommendedCrewSizeMin = crewSizeMin.IntValue;
2808 MainSub.Info.RecommendedCrewSizeMax = crewSizeMax.IntValue;
2811 var crewExpArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true)
2817 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), crewExpArea.RectTransform),
2818 TextManager.Get(
"RecommendedCrewExperience"), textAlignment: Alignment.CenterLeft, wrap:
true);
2820 var toggleExpLeft =
new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), crewExpArea.RectTransform), style:
"GUIButtonToggleLeft");
2821 var experienceText =
new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1.0f), crewExpArea.RectTransform),
2822 text: TextManager.Get(SubmarineInfo.CrewExperienceLevel.CrewExperienceLow.ToIdentifier()), textAlignment: Alignment.Center);
2823 var toggleExpRight =
new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), crewExpArea.RectTransform), style:
"GUIButtonToggleRight");
2825 toggleExpLeft.OnClicked += (btn, userData) =>
2827 MainSub.Info.RecommendedCrewExperience--;
2828 if (MainSub.Info.RecommendedCrewExperience < SubmarineInfo.CrewExperienceLevel.CrewExperienceLow)
2830 MainSub.Info.RecommendedCrewExperience = SubmarineInfo.CrewExperienceLevel.CrewExperienceHigh;
2832 experienceText.Text = TextManager.Get(MainSub.Info.RecommendedCrewExperience.ToIdentifier());
2836 toggleExpRight.OnClicked += (btn, userData) =>
2838 MainSub.Info.RecommendedCrewExperience++;
2839 if (MainSub.Info.RecommendedCrewExperience > SubmarineInfo.CrewExperienceLevel.CrewExperienceHigh)
2841 MainSub.Info.RecommendedCrewExperience = SubmarineInfo.CrewExperienceLevel.CrewExperienceLow;
2843 experienceText.Text = TextManager.Get(MainSub.Info.RecommendedCrewExperience.ToIdentifier());
2847 var hideInMenusArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
2852 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), hideInMenusArea.RectTransform),
2853 TextManager.Get(
"HideInMenus"), textAlignment: Alignment.CenterLeft, wrap:
true);
2855 new GUITickBox(
new RectTransform((0.4f, 1.0f), hideInMenusArea.RectTransform),
"")
2872 var outFittingArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
2877 new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1.0f), outFittingArea.RectTransform),
2878 TextManager.Get(
"ManuallyOutfitted"), textAlignment: Alignment.CenterLeft, wrap:
true)
2880 ToolTip = TextManager.Get(
"manuallyoutfittedtooltip")
2882 new GUITickBox(
new RectTransform((0.4f, 1.0f), outFittingArea.RectTransform),
"")
2884 ToolTip = TextManager.Get(
"manuallyoutfittedtooltip"),
2885 Selected = MainSub.Info.IsManuallyOutfitted,
2888 MainSub.Info.IsManuallyOutfitted = box.Selected;
2893 if (MainSub !=
null)
2895 int min = MainSub.Info.RecommendedCrewSizeMin;
2896 int max = MainSub.Info.RecommendedCrewSizeMax;
2897 crewSizeMin.IntValue = min;
2898 crewSizeMax.IntValue = max;
2899 if (MainSub.Info.RecommendedCrewExperience == SubmarineInfo.CrewExperienceLevel.Unknown)
2901 MainSub.Info.RecommendedCrewExperience = SubmarineInfo.CrewExperienceLevel.CrewExperienceLow;
2903 experienceText.Text = TextManager.Get(MainSub.Info.RecommendedCrewExperience.ToIdentifier());
2906 subTypeDropdown.OnSelected += (selected, userdata) =>
2909 MainSub.Info.Type = type;
2912 MainSub.Info.OutpostModuleInfo ??=
new OutpostModuleInfo(MainSub.Info);
2916 MainSub.Info.BeaconStationInfo ??=
new BeaconStationInfo(MainSub.Info);
2920 MainSub.Info.WreckInfo ??=
new WreckInfo(MainSub.Info);
2924 MainSub.Info.EnemySubmarineInfo ??=
new EnemySubmarineInfo(MainSub.Info);
2926 previewImageButtonHolder.
Children.ForEach(c => c.Enabled = MainSub.Info.AllowPreviewImage);
2927 outpostModuleSettingsContainer.Visible = type ==
SubmarineType.OutpostModule;
2929 beaconSettingsContainer.Visible = type ==
SubmarineType.BeaconStation;
2930 enemySubmarineSettingsContainer.Visible = type ==
SubmarineType.EnemySubmarine;
2931 subSettingsContainer.Visible = type ==
SubmarineType.Player;
2932 outpostSettingsContainer.Visible = type ==
SubmarineType.Outpost;
2935 subSettingsContainer.RectTransform.MinSize =
new Point(0, subSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
2937 int minHeight = subSettingsContainer.Children.First().Children.Max(c => c.RectTransform.MinSize.Y);
2938 foreach (var child
in subSettingsContainer.Children)
2940 child.RectTransform.MinSize =
new Point(0, minHeight);
2945 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get(
"SubPreviewImage"), font: GUIStyle.SubHeadingFont);
2947 var previewImageHolder =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.4f), rightColumn.RectTransform), style:
null) { Color = Color.Black, CanBeFocused =
false };
2948 previewImage =
new GUIImage(
new RectTransform(Vector2.One, previewImageHolder.RectTransform), MainSub?.Info.PreviewImage, scaleToFit:
true);
2950 previewImageButtonHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.05f), rightColumn.RectTransform), isHorizontal:
true) { Stretch =
true, RelativeSpacing = 0.05f };
2952 new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), previewImageButtonHolder.
RectTransform), TextManager.Get(
"SubPreviewImageCreate"), style:
"GUIButtonSmall")
2954 Enabled = MainSub?.Info.AllowPreviewImage ??
false,
2955 OnClicked = (btn, userdata) =>
2957 using (System.IO.MemoryStream imgStream =
new System.IO.MemoryStream())
2959 CreateImage(defaultPreviewImageSize.X, defaultPreviewImageSize.Y, imgStream);
2960 previewImage.
Sprite =
new Sprite(TextureLoader.FromStream(imgStream, compress:
false),
null,
null);
2961 if (MainSub !=
null)
2963 MainSub.Info.PreviewImage = previewImage.
Sprite;
2970 new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), previewImageButtonHolder.
RectTransform), TextManager.Get(
"SubPreviewImageBrowse"), style:
"GUIButtonSmall")
2972 Enabled = MainSub?.Info.AllowPreviewImage ??
false,
2973 OnClicked = (btn, userdata) =>
2975 FileSelection.OnFileSelected = (file) =>
2979 new GUIMessageBox(TextManager.Get(
"Error"), TextManager.Get(
"WorkshopItemPreviewImageTooLarge"));
2983 previewImage.
Sprite =
new Sprite(file, sourceRectangle:
null);
2984 if (MainSub !=
null)
2986 MainSub.Info.PreviewImage = previewImage.
Sprite;
2989 FileSelection.ClearFileTypeFilters();
2990 FileSelection.AddFileTypeFilter(
"PNG",
"*.png");
2991 FileSelection.AddFileTypeFilter(
"JPEG",
"*.jpg, *.jpeg");
2992 FileSelection.AddFileTypeFilter(
"All files",
"*.*");
2993 FileSelection.SelectFileTypeFilter(
"*.png");
2994 FileSelection.Open =
true;
3001 var contentPackageTabber =
new GUILayoutGroup(
new RectTransform((1.0f, 0.075f), rightColumn.RectTransform), isHorizontal:
true);
3003 GUIButton createTabberBtn(
string labelTag)
3005 var btn =
new GUIButton(
new RectTransform((0.5f, 1.0f), contentPackageTabber.RectTransform,
Anchor.BottomCenter,
Pivot.BottomCenter), TextManager.Get(labelTag), style:
"GUITabButton");
3006 btn.TextBlock.Wrap =
true;
3007 btn.TextBlock.SetTextPos();
3008 btn.RectTransform.MaxSize = RectTransform.MaxPoint;
3009 btn.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
3010 btn.Font = GUIStyle.SmallFont;
3014 var saveToPackageTabBtn = createTabberBtn(
"SaveToLocalPackage");
3015 saveToPackageTabBtn.Selected =
true;
3016 var reqPackagesTabBtn = createTabberBtn(
"RequiredContentPackages");
3017 reqPackagesTabBtn.Selected =
false;
3019 var horizontalArea =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.45f), rightColumn.RectTransform), style:
null);
3021 var saveInPackageLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One,
3022 horizontalArea.RectTransform,
Anchor.BottomRight))
3027 var packageToSaveInList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 1.0f),
3028 saveInPackageLayout.RectTransform));
3030 var packToSaveInFilter
3031 =
new GUITextBox(
new RectTransform((1.0f, 0.15f), saveInPackageLayout.RectTransform),
3032 createClearButton:
true);
3034 GUILayoutGroup addItemToPackageToSaveList(LocalizedString itemText, ContentPackage p)
3036 var listItem =
new GUIFrame(
new RectTransform((1.0f, 0.15f), packageToSaveInList.Content.RectTransform),
3037 style:
"ListBoxElement")
3041 if (p !=
null && p != ContentPackageManager.VanillaCorePackage) { listItem.ToolTip = p.Dir; }
3043 new GUILayoutGroup(
new RectTransform(Vector2.One, listItem.RectTransform),
3044 isHorizontal:
true) { Stretch =
true };
3047 new RectTransform(Vector2.One, retVal.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
3048 style:
null) { CanBeFocused =
false };
3049 var pkgText =
new GUITextBlock(
new RectTransform(Vector2.One, retVal.RectTransform), itemText)
3050 { CanBeFocused =
false };
3056 var modifyVanillaListItem = addItemToPackageToSaveList(
"Modify Vanilla content package", ContentPackageManager.VanillaCorePackage);
3057 var modifyVanillaListIcon = modifyVanillaListItem.GetChild<GUIFrame>();
3058 GUIStyle.Apply(modifyVanillaListIcon,
"WorkshopMenu.EditButton");
3061 var newPackageListItem = addItemToPackageToSaveList(TextManager.Get(
"CreateNewLocalPackage"),
null);
3062 var newPackageListIcon = newPackageListItem.GetChild<GUIFrame>();
3063 var newPackageListText = newPackageListItem.GetChild<GUITextBlock>();
3064 GUIStyle.Apply(newPackageListIcon,
"NewContentPackageIcon");
3065 new GUICustomComponent(
new RectTransform(Vector2.Zero, saveInPackageLayout.RectTransform),
3066 onUpdate: (f, component) =>
3068 foreach (GUIComponent contentChild in packageToSaveInList.Content.Children)
3070 contentChild.Visible &= !(contentChild.GetChild<GUILayoutGroup>()?.GetChild<GUITextBlock>() is GUITextBlock tb &&
3071 !tb.Text.Contains(packToSaveInFilter.Text, StringComparison.OrdinalIgnoreCase));
3074 ContentPackage ownerPkg =
null;
3075 if (MainSub?.Info !=
null) { ownerPkg = GetLocalPackageThatOwnsSub(MainSub.Info); }
3076 foreach (var p
in ContentPackageManager.LocalPackages)
3078 var packageListItem = addItemToPackageToSaveList(p.Name, p);
3081 var packageListIcon = packageListItem.GetChild<GUIFrame>();
3082 var packageListText = packageListItem.GetChild<GUITextBlock>();
3083 GUIStyle.Apply(packageListIcon,
"WorkshopMenu.EditButton");
3084 packageListText.Text = TextManager.GetWithVariable(
"UpdateExistingLocalPackage",
"[mod]", p.Name);
3087 if (ownerPkg !=
null)
3089 var element = packageToSaveInList.Content.FindChild(ownerPkg);
3090 element?.RectTransform.SetAsFirstChild();
3092 packageToSaveInList.Select(0);
3094 var requiredContentPackagesLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One,
3095 horizontalArea.RectTransform,
Anchor.BottomRight))
3101 var requiredContentPackList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 1.0f),
3102 requiredContentPackagesLayout.RectTransform));
3104 var filterLayout =
new GUILayoutGroup(
3105 new RectTransform((1.0f, 0.15f), requiredContentPackagesLayout.RectTransform),
3106 isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
3108 var contentPackFilter
3109 =
new GUITextBox(
new RectTransform((0.6f, 1.0f), filterLayout.RectTransform),
3110 createClearButton:
true);
3111 contentPackFilter.OnTextChanged += (box, text) =>
3113 requiredContentPackList.Content.Children.ForEach(c
3114 => c.Visible = !(c is GUITickBox tb &&
3115 !tb.Text.Contains(text, StringComparison.OrdinalIgnoreCase)));
3119 var autoDetectBtn =
new GUIButton(
new RectTransform((0.4f, 1.0f), filterLayout.RectTransform),
3120 text: TextManager.Get(
"AutoDetectRequiredPackages"), style:
"GUIButtonSmall")
3122 OnClicked = (button, o) =>
3124 var requiredPackages = MapEntity.MapEntityList.Select(e => e?.Prefab?.ContentPackage)
3125 .Where(cp => cp !=
null)
3126 .Distinct().OfType<ContentPackage>().
Select(p => p.Name).ToHashSet();
3127 var tickboxes = requiredContentPackList.Content.Children.OfType<GUITickBox>().ToArray();
3128 tickboxes.ForEach(tb => tb.Selected = requiredPackages.Contains(tb.UserData as
string ??
""));
3133 if (MainSub !=
null)
3135 List<string> allContentPacks = MainSub.Info.RequiredContentPackages.ToList();
3136 foreach (ContentPackage contentPack
in ContentPackageManager.AllPackages)
3140 if (contentPack.Files.All(f => f is SubmarineFile || f is ItemAssemblyFile)) {
continue; }
3142 if (!allContentPacks.Contains(contentPack.Name))
3144 string altName = contentPack.AltNames.FirstOrDefault(n => allContentPacks.Contains(n));
3145 if (!
string.IsNullOrEmpty(altName))
3147 if (MainSub.Info.RequiredContentPackages.Contains(altName))
3149 MainSub.Info.RequiredContentPackages.Remove(altName);
3150 MainSub.Info.RequiredContentPackages.Add(contentPack.Name);
3152 allContentPacks.Remove(altName);
3154 allContentPacks.Add(contentPack.Name);
3158 foreach (
string contentPackageName
in allContentPacks)
3160 var cpTickBox =
new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.2f), requiredContentPackList.Content.RectTransform), contentPackageName, font: GUIStyle.SmallFont)
3162 Selected = MainSub.Info.RequiredContentPackages.Contains(contentPackageName),
3163 UserData = contentPackageName
3165 cpTickBox.OnSelected += tickBox =>
3167 if (tickBox.Selected)
3169 MainSub.Info.RequiredContentPackages.Add((
string)tickBox.UserData);
3173 MainSub.Info.RequiredContentPackages.Remove((
string)tickBox.UserData);
3180 GUIButton.OnClickedHandler switchToTab(GUIButton tabBtn, GUIComponent tab)
3183 horizontalArea.Children.ForEach(c => c.Visible =
false);
3184 contentPackageTabber.Children.ForEach(c => c.Selected =
false);
3185 tabBtn.Selected =
true;
3190 saveToPackageTabBtn.OnClicked = switchToTab(saveToPackageTabBtn, saveInPackageLayout);
3191 reqPackagesTabBtn.OnClicked = switchToTab(reqPackagesTabBtn, requiredContentPackagesLayout);
3193 var buttonArea =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.05f), paddedSaveFrame.RectTransform,
Anchor.BottomCenter, minSize:
new Point(0, 30)), style:
null);
3195 var cancelButton =
new GUIButton(
new RectTransform(
new Vector2(0.3f, 1.0f), buttonArea.RectTransform,
Anchor.BottomLeft),
3196 TextManager.Get(
"Cancel"))
3198 OnClicked = (GUIButton btn,
object userdata) =>
3205 var saveButton =
new GUIButton(
new RectTransform(
new Vector2(0.3f, 1.0f), buttonArea.RectTransform,
Anchor.BottomRight),
3206 TextManager.Get(
"SaveSubButton").Fallback(TextManager.Get(
"save")))
3208 OnClicked = (button, o) => SaveSub(packageToSaveInList.SelectedData as ContentPackage)
3210 paddedSaveFrame.Recalculate();
3211 leftColumn.Recalculate();
3213 subSettingsContainer.
RectTransform.
MinSize = outpostModuleSettingsContainer.RectTransform.MinSize = beaconSettingsContainer.RectTransform.MinSize =
3214 new Point(0, Math.Max(subSettingsContainer.Rect.Height, outpostModuleSettingsContainer.Rect.Height));
3215 subSettingsContainer.Recalculate();
3216 outpostModuleSettingsContainer.Recalculate();
3217 beaconSettingsContainer.Recalculate();
3218 enemySubmarineSettingsContainer.Recalculate();
3220 descriptionBox.
Text = MainSub ==
null ?
"" : MainSub.Info.Description.Value;
3221 submarineDescriptionCharacterCount.
Text = descriptionBox.
Text.Length +
" / " + submarineDescriptionLimit;
3223 subTypeDropdown.SelectItem(MainSub.Info.Type);
3225 if (quickSave) { SaveSub(packageToSaveInList.SelectedData as ContentPackage); }
3228 private void CreateSaveAssemblyScreen()
3230 SetMode(Mode.Default);
3232 saveFrame =
new GUIButton(
new RectTransform(Vector2.One, GUI.Canvas,
Anchor.Center), style:
null)
3234 OnClicked = (btn, userdata) => {
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) saveFrame =
null;
return true; }
3237 new GUIFrame(
new RectTransform(GUI.Canvas.RelativeSize, saveFrame.
RectTransform,
Anchor.Center), style:
"GUIBackgroundBlocker");
3239 var innerFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.25f, 0.35f), saveFrame.
RectTransform,
Anchor.Center) { MinSize = new Point(400, 350) });
3240 GUILayoutGroup paddedSaveFrame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.9f), innerFrame.RectTransform,
Anchor.Center))
3242 AbsoluteSpacing = GUI.IntScale(5),
3246 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), paddedSaveFrame.RectTransform),
3247 TextManager.Get(
"SaveItemAssemblyDialogHeader"), font: GUIStyle.LargeFont);
3248 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), paddedSaveFrame.RectTransform),
3249 TextManager.Get(
"SaveItemAssemblyDialogName"));
3250 nameBox =
new GUITextBox(
new RectTransform(
new Vector2(0.6f, 0.1f), paddedSaveFrame.RectTransform));
3253 new GUITickBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedSaveFrame.RectTransform), TextManager.Get(
"SaveItemAssemblyHideInMenus"))
3255 UserData =
"hideinmenus"
3259 var descriptionContainer =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.5f), paddedSaveFrame.RectTransform));
3260 descriptionBox =
new GUITextBox(
new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform,
Anchor.TopLeft),
3261 font: GUIStyle.SmallFont, style:
"GUITextBoxNoBorder", wrap:
true, textAlignment: Alignment.TopLeft)
3263 Padding =
new Vector4(10 * GUI.Scale)
3268 Vector2 textSize = textBox.Font.MeasureString(descriptionBox.
WrappedText);
3269 textBox.RectTransform.NonScaledSize =
new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(descriptionContainer.Content.Rect.Height, (
int)textSize.Y + 10));
3270 descriptionContainer.UpdateScrollBarSize();
3271 descriptionContainer.BarScroll = 1.0f;
3275 var buttonArea =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedSaveFrame.RectTransform), style:
null);
3276 new GUIButton(
new RectTransform(
new Vector2(0.25f, 1.0f), buttonArea.RectTransform,
Anchor.BottomLeft),
3277 TextManager.Get(
"Cancel"))
3279 OnClicked = (GUIButton btn,
object userdata) =>
3285 new GUIButton(
new RectTransform(
new Vector2(0.25f, 1.0f), buttonArea.RectTransform,
Anchor.BottomRight),
3286 TextManager.Get(
"SaveSubButton"))
3288 OnClicked = SaveAssembly
3290 buttonArea.
RectTransform.
MinSize =
new Point(0, buttonArea.Children.First().RectTransform.MinSize.Y);
3300 private List<Item> LoadItemAssemblyInventorySafe(ItemAssemblyPrefab assemblyPrefab)
3302 var realItems = assemblyPrefab.CreateInstance(Vector2.Zero, MainSub);
3303 var itemInstance =
new List<Item>();
3304 realItems.ForEach(entity =>
3306 if (entity is Item it && it.ParentInventory ==
null)
3308 itemInstance.Add(it);
3311 return itemInstance;
3314 private bool SaveAssembly(GUIButton button,
object obj)
3316 if (
string.IsNullOrWhiteSpace(nameBox.
Text))
3318 GUI.AddMessage(TextManager.Get(
"ItemAssemblyNameMissingWarning"), GUIStyle.Red);
3324 foreach (
char illegalChar
in Path.GetInvalidFileNameCharsCrossPlatform())
3326 if (nameBox.
Text.Contains(illegalChar))
3328 GUI.AddMessage(TextManager.GetWithVariable(
"ItemAssemblyNameIllegalCharsWarning",
"[illegalchar]", illegalChar.ToString()), GUIStyle.Red);
3334 nameBox.
Text = nameBox.
Text.Trim();
3337 string saveFolder = Path.Combine(ContentPackage.LocalModsDir, nameBox.
Text);
3338 string filePath = Path.Combine(saveFolder, $
"{nameBox.Text}.xml").CleanUpPathCrossPlatform();
3339 if (File.Exists(filePath))
3341 var msgBox =
new GUIMessageBox(TextManager.Get(
"Warning"), TextManager.Get(
"ItemAssemblyFileExistsWarning"),
new[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
3342 msgBox.Buttons[0].OnClicked = (btn, userdata) =>
3348 msgBox.Buttons[1].OnClicked = msgBox.Close;
3357 ContentPackage existingContentPackage = ContentPackageManager.LocalPackages.Regular.FirstOrDefault(p => p.Files.Any(f => f.Path == filePath));
3358 if (existingContentPackage ==
null)
3361 ModProject modProject =
new ModProject { Name = nameBox.
Text };
3362 var newFile = ModProject.File.FromPath<ItemAssemblyFile>(Path.Combine(ContentPath.ModDirStr, $
"{nameBox.Text}.xml"));
3363 modProject.AddFile(newFile);
3364 string newPackagePath = ContentPackageManager.LocalPackages.SaveRegularMod(modProject);
3365 existingContentPackage = ContentPackageManager.LocalPackages.GetRegularModByPath(newPackagePath);
3368 XDocument doc =
new XDocument(ItemAssemblyPrefab.Save(MapEntity.SelectedList.ToList(), nameBox.
Text, descriptionBox.
Text, hideInMenus));
3371 doc.SaveSafe(filePath);
3375 DebugConsole.ThrowError($
"Failed to save the item assembly to \"{filePath}\".", e);
3379 var result = ContentPackageManager.ReloadContentPackage(existingContentPackage);
3380 if (!result.TryUnwrapSuccess(out var resultPackage))
3382 throw new Exception($
"Failed to reload content package \"{existingContentPackage.Name}\"",
3383 result.TryUnwrapFailure(out var exception) ? exception :
null);
3385 if (resultPackage is RegularPackage regularPackage
3386 && !ContentPackageManager.EnabledPackages.Regular.Contains(regularPackage))
3388 ContentPackageManager.EnabledPackages.EnableRegular(regularPackage);
3389 GameSettings.SaveCurrentConfig();
3393 OpenEntityMenu(selectedCategory);
3400 private static void SnapToGrid()
3403 foreach (MapEntity e
in MapEntity.SelectedList)
3406 Vector2 offset = e.Position;
3407 offset =
new Vector2((MathF.Floor(offset.X /
Submarine.GridSize.X) + .5f) *
Submarine.GridSize.X - offset.X, (MathF.Floor(offset.Y /
Submarine.GridSize.Y) + .5f) *
Submarine.GridSize.Y - offset.Y);
3410 var wire = item.GetComponent<
Wire>();
3411 if (wire !=
null) {
continue; }
3415 linkedGap.Move(item.Position - linkedGap.Position);
3418 else if (e is Structure structure)
3420 structure.Move(offset);
3425 foreach (Item item
in MapEntity.SelectedList.Where(entity => entity is Item).Cast<
Item>())
3427 var wire = item.GetComponent<
Wire>();
3430 for (
int i = 0; i < wire.GetNodes().Count; i++)
3434 offset =
new Vector2((MathF.Floor(offset.X /
Submarine.GridSize.X) + .5f) *
Submarine.GridSize.X - offset.X, (MathF.Floor(offset.Y /
Submarine.GridSize.Y) + .5f) *
Submarine.GridSize.Y - offset.Y);
3435 wire.MoveNode(i, offset);
3441 private static IEnumerable<SubmarineInfo> GetLoadableSubs()
3443 string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
3444 return SubmarineInfo.SavedSubmarines.Where(s
3445 => Path.GetDirectoryName(Path.GetFullPath(s.FilePath)) != downloadFolder);
3448 private void CreateLoadScreen()
3451 SubmarineInfo.RefreshSavedSubs();
3452 SetMode(Mode.Default);
3454 loadFrame =
new GUIFrame(
new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas,
Anchor.Center), style:
"GUIBackgroundBlocker");
3456 new GUIButton(
new RectTransform(Vector2.One, loadFrame.
RectTransform,
Anchor.Center), style:
null)
3458 OnClicked = (_, _) =>
3465 var innerFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.53f, 0.75f), loadFrame.
RectTransform,
Anchor.Center, scaleBasis:
ScaleBasis.Smallest) { MinSize = new Point(350, 500) });
3467 var paddedLoadFrame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.9f), innerFrame.RectTransform,
Anchor.Center)) { Stretch =
true, RelativeSpacing = 0.01f };
3469 var deleteButtonHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.2f), paddedLoadFrame.RectTransform,
Anchor.Center))
3471 RelativeSpacing = 0.1f,
3475 var searchBox =
new GUITextBox(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedLoadFrame.RectTransform), font: GUIStyle.Font, createClearButton:
true);
3476 var searchTitle =
new GUITextBlock(
new RectTransform(Vector2.One, searchBox.RectTransform), TextManager.Get(
"serverlog.filter"),
3477 textAlignment: Alignment.CenterLeft, font: GUIStyle.Font)
3479 CanBeFocused =
false,
3480 IgnoreLayoutGroups =
true
3482 searchTitle.TextColor *= 0.5f;
3484 var subList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.7f), paddedLoadFrame.RectTransform))
3486 PlaySoundOnSelect =
true,
3487 ScrollBarVisible =
true,
3488 OnSelected = (GUIComponent selected,
object userData) =>
3490 if (deleteButtonHolder.FindChild(
"delete") is GUIButton deleteBtn)
3492 deleteBtn.ToolTip =
string.Empty;
3493 if (!(userData is SubmarineInfo subInfo))
3495 deleteBtn.Enabled =
false;
3499 var
package = GetLocalPackageThatOwnsSub(subInfo);
3500 if (package !=
null)
3502 deleteBtn.Enabled =
true;
3506 deleteBtn.Enabled =
false;
3507 if (IsVanillaSub(subInfo))
3509 deleteBtn.ToolTip = TextManager.Get(
"cantdeletevanillasub");
3511 else if (GetPackageThatOwnsSub(subInfo, ContentPackageManager.AllPackages) is ContentPackage subPackage)
3513 deleteBtn.ToolTip = TextManager.GetWithVariable(
"cantdeletemodsub",
"[modname]", subPackage.Name);
3521 searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible =
false; };
3522 searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = sender.Text.IsNullOrEmpty(); };
3523 searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text);
return true; };
3525 var sortedSubs = GetLoadableSubs()
3526 .OrderBy(s => s.Type)
3527 .ThenBy(s => s.Name)
3530 SubmarineInfo prevSub =
null;
3532 foreach (SubmarineInfo sub
in sortedSubs)
3534 if (prevSub ==
null || prevSub.Type != sub.Type)
3536 string textTag =
"SubmarineType." + sub.Type;
3537 if (sub.Type ==
SubmarineType.EnemySubmarine && !TextManager.ContainsTag(textTag))
3539 textTag =
"MissionType.Pirate";
3541 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), subList.Content.RectTransform) { MinSize = new Point(0, 35) },
3542 TextManager.Get(textTag), font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style:
"ListBoxElement")
3544 CanBeFocused =
false
3549 string pathWithoutUserName = Path.GetFullPath(sub.FilePath);
3550 string saveFolder = Path.GetFullPath(SaveUtil.DefaultSaveFolder);
3551 if (pathWithoutUserName.StartsWith(saveFolder))
3553 pathWithoutUserName =
"..." + pathWithoutUserName[saveFolder.Length..];
3557 pathWithoutUserName = sub.FilePath;
3560 GUITextBlock textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), subList.Content.RectTransform) { MinSize = new Point(0, 30) },
3561 ToolBox.LimitString(sub.Name, GUIStyle.Font, subList.Rect.Width - 80))
3564 ToolTip = pathWithoutUserName
3567 if (!(ContentPackageManager.VanillaCorePackage?.Files.Any(f => f.Path == sub.FilePath) ??
false))
3569 if (GetLocalPackageThatOwnsSub(sub) ==
null &&
3570 ContentPackageManager.AllPackages.FirstOrDefault(p => p.Files.Any(f => f.Path == sub.FilePath)) is ContentPackage subPackage)
3573 textBlock.OverrideTextColor(Color.MediumPurple);
3578 textBlock.OverrideTextColor(GUIStyle.TextColorBright);
3584 var shuttleText =
new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1.0f), textBlock.RectTransform,
Anchor.CenterRight),
3585 TextManager.Get(
"Shuttle",
"RespawnShuttle"), textAlignment: Alignment.CenterRight, font: GUIStyle.SmallFont)
3587 TextColor = textBlock.TextColor * 0.8f,
3588 ToolTip = textBlock.ToolTip.SanitizedString
3591 else if (sub.IsPlayer)
3593 var classText =
new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1.0f), textBlock.RectTransform,
Anchor.CenterRight),
3594 TextManager.Get($
"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUIStyle.SmallFont)
3596 TextColor = textBlock.TextColor * 0.8f,
3597 ToolTip = textBlock.ToolTip.SanitizedString
3602 var deleteButton =
new GUIButton(
new RectTransform(Vector2.One, deleteButtonHolder.RectTransform,
Anchor.TopCenter),
3603 TextManager.Get(
"Delete"))
3608 deleteButton.OnClicked = (btn, userdata) =>
3610 if (subList.SelectedComponent !=
null)
3612 TryDeleteSub(subList.SelectedComponent.UserData as SubmarineInfo);
3614 deleteButton.Enabled =
false;
3619 if (AutoSaveInfo?.Root !=
null)
3621 int min = Math.Min(6, AutoSaveInfo.Root.Elements().Count());
3622 var loadAutoSave =
new GUIDropDown(
new RectTransform(Vector2.One, deleteButtonHolder.RectTransform,
Anchor.BottomCenter), TextManager.Get(
"LoadAutoSave"), elementCount: min)
3624 ToolTip = TextManager.Get(
"LoadAutoSaveTooltip"),
3625 UserData =
"loadautosave",
3626 OnSelected = (button, o) =>
3632 foreach (XElement saveElement
in AutoSaveInfo.Root.Elements().Reverse())
3634 DateTime time = DateTime.MinValue.AddSeconds(saveElement.GetAttributeUInt64(
"time", 0));
3635 TimeSpan difference = DateTime.UtcNow - time;
3637 LocalizedString tooltip = TextManager.GetWithVariables(
"subeditor.autosaveage",
3638 (
"[hours]", ((
int)Math.Floor(difference.TotalHours)).ToString()),
3639 (
"[minutes]", difference.Minutes.ToString()),
3640 (
"[seconds]", difference.Seconds.ToString()));
3642 string submarineName = saveElement.GetAttributeString(
"name", TextManager.Get(
"UnspecifiedSubFileName").Value);
3643 LocalizedString timeFormat;
3645 double totalMinutes = difference.TotalMinutes;
3647 if (totalMinutes < 1)
3649 timeFormat = TextManager.Get(
"subeditor.savedjustnow");
3651 else if (totalMinutes > 60)
3653 timeFormat = TextManager.Get(
"subeditor.savedmorethanhour");
3657 timeFormat = TextManager.GetWithVariable(
"subeditor.saveageminutes",
"[minutes]", difference.Minutes.ToString());
3660 LocalizedString entryName = TextManager.GetWithVariables(
"subeditor.autosaveentry", (
"[submarine]", submarineName), (
"[saveage]", timeFormat));
3662 loadAutoSave.AddItem(entryName, saveElement, tooltip);
3666 var controlBtnHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.1f), paddedLoadFrame.RectTransform), isHorizontal:
true) { RelativeSpacing = 0.2f, Stretch =
true };
3668 new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), controlBtnHolder.RectTransform,
Anchor.BottomLeft),
3669 TextManager.Get(
"Cancel"))
3671 OnClicked = (GUIButton btn,
object userdata) =>
3678 new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), controlBtnHolder.RectTransform,
Anchor.BottomRight),
3679 TextManager.Get(
"Load"))
3681 OnClicked = HitLoadSubButton
3684 controlBtnHolder.
RectTransform.
MaxSize =
new Point(
int.MaxValue, controlBtnHolder.Children.First().Rect.Height);
3687 private void FilterSubs(GUIListBox subList,
string filter)
3689 foreach (GUIComponent child
in subList.Content.Children)
3691 if (!(child.UserData is SubmarineInfo sub)) {
continue; }
3692 child.Visible =
string.IsNullOrEmpty(filter) || sub.Name.ToLower().Contains(filter.ToLower());
3696 bool subVisibleInCategory =
false;
3697 foreach (GUIComponent child
in subList.Content.Children.Reverse())
3699 if (!(child.UserData is SubmarineInfo sub))
3703 child.Visible = subVisibleInCategory;
3705 subVisibleInCategory =
false;
3709 subVisibleInCategory |= child.Visible;
3718 private void LoadAutoSave(
object userData)
3720 if (userData is not XElement element) {
return; }
3722 #warning TODO: revise
3723 string filePath = element.GetAttributeStringUnrestricted(
"file",
"");
3724 if (
string.IsNullOrWhiteSpace(filePath)) {
return; }
3726 var loadedSub =
Submarine.Load(
new SubmarineInfo(filePath),
true);
3730 loadedSub.Info.Name = loadedSub.Info.SubmarineElement.GetAttributeString(
"name", loadedSub.Info.Name);
3734 DebugConsole.ThrowError(
"Failed to find a name for the submarine.", e);
3735 var unspecifiedFileName = TextManager.Get(
"UnspecifiedSubFileName");
3736 loadedSub.Info.Name = unspecifiedFileName.Value;
3738 MainSub = loadedSub;
3739 MainSub.SetPrevTransform(MainSub.Position);
3740 MainSub.UpdateTransform();
3741 MainSub.Info.Name = loadedSub.Info.Name;
3742 subNameLabel.
Text = ToolBox.LimitString(loadedSub.Info.Name, subNameLabel.
Font, subNameLabel.
Rect.Width);
3744 ReconstructLayers();
3746 CreateDummyCharacter();
3748 cam.Position = MainSub.Position + MainSub.HiddenSubPosition;
3753 private bool HitLoadSubButton(GUIButton button,
object obj)
3755 if (loadFrame ==
null)
3757 DebugConsole.NewMessage(
"load frame null", Color.Red);
3761 GUIListBox subList = loadFrame.GetAnyChild<GUIListBox>();
3762 if (subList ==
null)
3764 DebugConsole.NewMessage(
"Sublist null", Color.Red);
3768 if (!(subList.SelectedComponent?.UserData is SubmarineInfo selectedSubInfo)) {
return false; }
3770 var ownerPackage = GetLocalPackageThatOwnsSub(selectedSubInfo);
3771 if (ownerPackage is
null)
3773 if (IsVanillaSub(selectedSubInfo))
3776 LoadSub(selectedSubInfo);
3778 AskLoadVanillaSub(selectedSubInfo);
3781 else if (GetWorkshopPackageThatOwnsSub(selectedSubInfo) is ContentPackage workshopPackage)
3783 if (workshopPackage.TryExtractSteamWorkshopId(out var workshopId)
3784 && publishedWorkshopItemIds.Contains(workshopId.Value))
3786 AskLoadPublishedSub(selectedSubInfo, workshopPackage);
3790 AskLoadSubscribedSub(selectedSubInfo);
3796 LoadSub(selectedSubInfo);
3801 void AskLoadSub(SubmarineInfo info, LocalizedString header, LocalizedString desc)
3803 var msgBox =
new GUIMessageBox(
3806 new[] { TextManager.Get(
"LoadAnyway"), TextManager.Get(
"Cancel") });
3807 msgBox.Buttons[0].OnClicked = (button, o) =>
3813 msgBox.Buttons[1].OnClicked = msgBox.Close;
3816 void AskLoadPublishedSub(SubmarineInfo info, ContentPackage pkg)
3818 TextManager.Get(
"LoadingPublishedSubmarineHeader"),
3819 TextManager.GetWithVariable(
"LoadingPublishedSubmarineDesc",
"[modname]", pkg.Name));
3821 void AskLoadSubscribedSub(SubmarineInfo info)
3823 TextManager.Get(
"LoadingSubscribedSubmarineHeader"),
3824 TextManager.Get(
"LoadingSubscribedSubmarineDesc"));
3826 void AskLoadVanillaSub(SubmarineInfo info)
3828 TextManager.Get(
"LoadingVanillaSubmarineHeader"),
3829 TextManager.Get(
"LoadingVanillaSubmarineDesc"));
3836 if (checkIdConflicts)
3838 Dictionary<int, Identifier> entities =
new Dictionary<int, Identifier>();
3841 int id = subElement.GetAttributeInt(
"ID", -1);
3842 if (
id == -1) {
continue; }
3843 Identifier identifier = subElement.GetAttributeIdentifier(
"identifier",
string.Empty);
3844 if (entities.TryGetValue(
id, out Identifier duplicateEntity))
3847 TextManager.Get(
"error"),
3848 TextManager.GetWithVariables(
"subeditor.duplicateiderror",
3849 (
"[entity1]", $
"{duplicateEntity} ({id})"),
3850 (
"[entity2]", $
"{identifier} ({id})")),
3851 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
3852 errorMsg.Buttons[0].OnClicked = (bnt, userdata) =>
3854 subElement.Remove();
3855 LoadSub(info, checkIdConflicts:
false);
3859 errorMsg.Buttons[1].OnClicked = (bnt, userdata) =>
3861 LoadSub(info, checkIdConflicts:
false);
3867 entities.Add(
id, identifier);
3874 MainSub = selectedSub;
3879 DebugConsole.ThrowError(
"Failed to load the submarine. The submarine file might be corrupted.", e);
3883 CreateDummyCharacter();
3885 string name = MainSub.Info.Name;
3886 subNameLabel.
Text = ToolBox.LimitString(name, subNameLabel.
Font, subNameLabel.
Rect.Width);
3888 cam.Position = MainSub.Position + MainSub.HiddenSubPosition;
3894 var adjustLightsPrompt =
new GUIMessageBox(TextManager.Get(
"Warning"), TextManager.Get(
"AdjustLightsPrompt"),
3895 new[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
3896 adjustLightsPrompt.Buttons[0].OnClicked += adjustLightsPrompt.Close;
3897 adjustLightsPrompt.Buttons[0].OnClicked += (btn, userdata) =>
3905 light.
LightColor =
new Color(light.LightColor, light.LightColor.A / 255.0f * 0.5f);
3908 new GUIMessageBox(
"", TextManager.Get(
"AdjustedLightsNotification"));
3911 adjustLightsPrompt.Buttons[1].OnClicked += adjustLightsPrompt.Close;
3914 ReconstructLayers();
3918 => packages.FirstOrDefault(package => package.
Files.Any(f => f.Path == sub.FilePath));
3921 => GetPackageThatOwnsSub(sub, ContentPackageManager.LocalPackages);
3924 => GetPackageThatOwnsSub(sub, ContentPackageManager.WorkshopPackages);
3927 => GetPackageThatOwnsSub(sub, ContentPackageManager.VanillaCorePackage.ToEnumerable()) !=
null;
3931 if (sub ==
null) {
return; }
3936 var subPackage = GetLocalPackageThatOwnsSub(sub);
3937 if (!ContentPackageManager.LocalPackages.Regular.Contains(subPackage)) {
return; }
3939 var msgBox =
new GUIMessageBox(
3940 TextManager.Get(
"DeleteDialogLabel"),
3941 TextManager.GetWithVariable(
"DeleteDialogQuestion",
"[file]", sub.
Name),
3942 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"Cancel") });
3943 msgBox.Buttons[0].OnClicked += (btn, userData) =>
3947 if (subPackage !=
null)
3949 File.Delete(sub.
FilePath, catchUnauthorizedAccessExceptions:
false);
3950 ModProject modProject =
new ModProject(subPackage);
3951 modProject.RemoveFile(modProject.Files.First(f => ContentPath.FromRaw(subPackage, f.Path) == sub.
FilePath));
3952 modProject.Save(subPackage.Path, catchUnauthorizedAccessExceptions:
true);
3953 ReloadModifiedPackage(subPackage);
3954 if (MainSub?.Info !=
null && MainSub.Info.FilePath == sub.
FilePath)
3956 MainSub.Info.FilePath =
null;
3964 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"DeleteFileError",
"[file]", sub.
FilePath), e);
3968 msgBox.Buttons[0].OnClicked += msgBox.Close;
3969 msgBox.Buttons[1].OnClicked += msgBox.Close;
3972 private void OpenEntityMenu(MapEntityCategory? entityCategory)
3976 foreach (GUIButton categoryButton
in entityCategoryButtons)
3978 categoryButton.Selected = entityCategory.HasValue ?
3979 categoryButton.UserData is
MapEntityCategory category && entityCategory.Value == category :
3980 categoryButton.UserData ==
null;
3981 string categoryName = entityCategory.HasValue ? entityCategory.Value.ToString() :
"All";
3982 selectedCategoryText.
Text = TextManager.Get(
"MapEntityCategory." + categoryName);
3983 selectedCategoryButton.
ApplyStyle(GUIStyle.GetComponentStyle(
"CategoryButton." + categoryName));
3986 selectedCategory = entityCategory;
3988 SetMode(Mode.Default);
3993 foreach (GUIComponent child
in toggleEntityMenuButton.
Children)
3995 child.
SpriteEffects = entityMenuOpen ? SpriteEffects.None : SpriteEffects.FlipVertically;
3998 foreach (GUIComponent child
in categorizedEntityList.
Content.
Children)
4000 child.Visible = !entityCategory.HasValue || (
MapEntityCategory)child.UserData == entityCategory;
4001 var innerList = child.GetChild<GUIListBox>();
4002 foreach (GUIComponent grandChild
in innerList.Content.Children)
4004 grandChild.Visible =
true;
4008 if (!
string.IsNullOrEmpty(entityFilterBox.
Text))
4010 FilterEntities(entityFilterBox.
Text);
4019 private void FilterEntities(
string filter)
4021 if (
string.IsNullOrWhiteSpace(filter))
4023 allEntityList.
Visible =
false;
4024 categorizedEntityList.
Visible =
true;
4026 foreach (GUIComponent child
in categorizedEntityList.
Content.
Children)
4028 child.Visible = !selectedCategory.HasValue || selectedCategory == (
MapEntityCategory)child.UserData;
4029 if (!child.Visible) {
return; }
4030 var innerList = child.GetChild<GUIListBox>();
4031 foreach (GUIComponent grandChild
in innerList.Content.Children)
4033 grandChild.Visible = ((MapEntityPrefab)grandChild.UserData).Name.Value.Contains(filter, StringComparison.OrdinalIgnoreCase);
4042 categorizedEntityList.
Visible =
false;
4043 filter = filter.ToLower();
4047 (!selectedCategory.HasValue || ((MapEntityPrefab)child.UserData).Category.HasFlag(selectedCategory)) &&
4048 ((MapEntityPrefab)child.UserData).Name.Value.Contains(filter, StringComparison.OrdinalIgnoreCase);
4054 private void ClearFilter()
4059 entityFilterBox.
Text =
"";
4064 if (newMode == mode) {
return; }
4078 CreateDummyCharacter();
4079 if (newMode ==
Mode.Wiring)
4083 Point wirePos =
new Point((
int)(10 * GUI.Scale), TopPanel.
Rect.Height + entityCountPanel.
Rect.Height + (
int)(10 * GUI.Scale));
4084 wiringToolPanel = CreateWiringPanel(wirePos, SelectWire);
4088 private void RemoveDummyCharacter()
4090 if (dummyCharacter ==
null || dummyCharacter.
Removed) {
return; }
4094 dummyCharacter =
null;
4097 private void CreateContextMenu()
4099 if (GUIContextMenu.CurrentContextMenu !=
null) {
return; }
4101 List<MapEntity> targets = MapEntity.HighlightedEntities.Any(me => !MapEntity.SelectedList.Contains(me)) ?
4102 MapEntity.HighlightedEntities.ToList() :
4103 new List<MapEntity>(MapEntity.SelectedList);
4105 bool allowOpening =
false;
4106 var targetItem = (targets.Count == 1 ? targets.Single() :
null) as Item;
4108 allowOpening = targetItem is not
null && targetItem.Components.Any(
static ic =>
4114 bool hasTargets = targets.Count > 0;
4117 if (PlayerInput.IsShiftDown())
4119 GUIContextMenu.CreateContextMenu(
4120 new ContextMenuOption(
"SubEditor.EditBackgroundColor", isEnabled:
true, onSelected: CreateBackgroundColorPicker),
4121 new ContextMenuOption(
"SubEditor.ToggleTransparency", isEnabled:
true, onSelected: () => TransparentWiringMode = !TransparentWiringMode),
4122 new ContextMenuOption(
"SubEditor.ToggleGrid", isEnabled:
true, onSelected: () => ShouldDrawGrid = !ShouldDrawGrid),
4123 new ContextMenuOption(
"SubEditor.PasteAssembly", isEnabled:
true, () => PasteAssembly()),
4124 new ContextMenuOption(
"Editor.SelectSame", isEnabled: hasTargets, onSelected: delegate
4126 bool doorGapSelected = targets.Any(t => t is Gap gap && gap.ConnectedDoor !=
null);
4127 foreach (MapEntity match in MapEntity.MapEntityList.Where(e => e.Prefab !=
null && targets.Any(t => t.Prefab?.Identifier == e.Prefab.Identifier) && !MapEntity.SelectedList.Contains(e)))
4129 if (MapEntity.SelectedList.Contains(match)) { continue; }
4130 if (match is Gap gap)
4133 if ((gap.ConnectedDoor ==
null) == doorGapSelected) { continue; }
4135 else if (match is Item item)
4138 var door = item.GetComponent<
Door>();
4139 if (door?.LinkedGap !=
null && !MapEntity.SelectedList.Contains(door.LinkedGap))
4141 MapEntity.SelectedList.Add(door.LinkedGap);
4144 MapEntity.SelectedList.Add(match);
4147 new ContextMenuOption(
"SubEditor.AddImage", isEnabled:
true, onSelected: ImageManager.CreateImageWizard),
4148 new ContextMenuOption(
"SubEditor.ToggleImageEditing", isEnabled:
true, onSelected: delegate
4150 ImageManager.EditorMode = !ImageManager.EditorMode;
4151 if (!ImageManager.EditorMode) { GameSettings.SaveCurrentConfig(); }
4156 List<ContextMenuOption> availableLayers =
new List<ContextMenuOption>
4158 new ContextMenuOption(
"editor.layer.nolayer",
true, onSelected: () => { MoveToLayer(
null, targets); })
4160 availableLayers.AddRange(Layers.Select(layer =>
new ContextMenuOption(layer.Key,
true, onSelected: () => { MoveToLayer(layer.Key, targets); })));
4162 List<ContextMenuOption> availableLayerOptions =
new List<ContextMenuOption>
4164 new ContextMenuOption(
"editor.layer.movetolayer", isEnabled: hasTargets, availableLayers.ToArray()),
4165 new ContextMenuOption(
"editor.layer.createlayer", isEnabled: hasTargets, onSelected: () => { CreateNewLayer(
null, targets); }),
4166 new ContextMenuOption(
"editor.layer.selectall", isEnabled: hasTargets, onSelected: () =>
4168 foreach (MapEntity match
in MapEntity.MapEntityList.Where(e => targets.Any(t => !
string.IsNullOrWhiteSpace(t.Layer) && t.Layer == e.Layer && !MapEntity.SelectedList.Contains(e))))
4170 if (MapEntity.SelectedList.Contains(match)) {
continue; }
4171 MapEntity.SelectedList.Add(match);
4175 availableLayerOptions.AddRange(Layers.Select(layer =>
new ContextMenuOption(layer.Key,
true, onSelected: () => { MoveToLayer(layer.Key, targets); })));
4177 GUIContextMenu.CreateContextMenu(
4178 new ContextMenuOption(
"label.openlabel", isEnabled: allowOpening, onSelected: () => OpenItem(targetItem)),
4179 new ContextMenuOption(
"editor.cut", isEnabled: hasTargets, onSelected: () => MapEntity.Cut(targets)),
4180 new ContextMenuOption(
"editor.copytoclipboard", isEnabled: hasTargets, onSelected: () => MapEntity.Copy(targets)),
4181 new ContextMenuOption(
"editor.paste", isEnabled: MapEntity.CopiedList.Any(), onSelected: () => MapEntity.Paste(cam.ScreenToWorld(PlayerInput.MousePosition))),
4182 new ContextMenuOption(
"delete", isEnabled: hasTargets, onSelected: () =>
4184 StoreCommand(
new AddOrDeleteCommand(targets,
true));
4185 foreach (var me
in targets)
4187 if (!me.Removed) { me.Remove(); }
4190 new ContextMenuOption(
string.Empty, isEnabled:
false, onSelected: () => { }),
4191 new ContextMenuOption(
"editor.layer.movetoactivelayer", isEnabled: !(layerList?.SelectedData as
string).IsNullOrEmpty(), onSelected: () => { MoveToLayer(layerList.
SelectedData as
string, targets); }),
4192 new ContextMenuOption(
"editor.layer.removefromlayer", isEnabled: targets.Any(t => t.Layer !=
string.Empty), onSelected: () => { targets.ForEach(t => t.Layer =
string.Empty); }),
4193 new ContextMenuOption(
"editor.layeroptions", isEnabled: hasTargets, availableLayerOptions.ToArray()),
4194 new ContextMenuOption(TextManager.GetWithVariable(
"editortip.shiftforextraoptions",
"[button]", PlayerInput.SecondaryMouseLabel) +
'\n' + TextManager.Get(
"editortip.altforruler"), isEnabled:
false, onSelected:
null));
4198 private void MoveToLayer(
string layer, List<MapEntity> content)
4200 layer ??=
string.Empty;
4202 foreach (MapEntity entity
in content)
4204 if (MapEntity.SelectedList.Contains(entity))
4206 MapEntity.ResetEditingHUD();
4208 entity.Layer = layer;
4212 private void CreateNewLayer(
string name, List<MapEntity> content)
4214 if (
string.IsNullOrWhiteSpace(name))
4216 name = TextManager.Get(
"editor.layer.newlayer").Value;
4219 string incrementedName = name;
4221 for (
int i = 1; Layers.ContainsKey(incrementedName); i++)
4223 incrementedName = $
"{name} ({i})";
4226 name = incrementedName;
4228 if (content !=
null)
4230 MoveToLayer(name, content);
4233 Layers.Add(name,
new LayerData());
4237 private void RenameLayer(
string original,
string newName)
4239 Layers.Remove(original, out LayerData originalData);
4241 foreach (MapEntity entity
in MapEntity.MapEntityList.Where(entity => entity.Layer == original))
4243 entity.Layer = newName ??
string.Empty;
4246 if (!
string.IsNullOrWhiteSpace(newName))
4248 Layers.TryAdd(newName, originalData);
4258 if (!
string.IsNullOrWhiteSpace(entity.
Layer))
4266 private void ClearLayers()
4272 private static void SetLayerVisibility(
string layerName,
bool isVisible)
4274 if (Layers.Remove(layerName, out LayerData layerData))
4276 Layers.Add(layerName, layerData with { IsVisible = isVisible });
4280 Layers.Add(layerName,
new LayerData(isVisible));
4284 private void PasteAssembly(
string text =
null, Vector2? pos =
null)
4286 pos ??= cam.ScreenToWorld(PlayerInput.MousePosition);
4287 text ??= Clipboard.GetText();
4288 if (
string.IsNullOrWhiteSpace(text))
4290 DebugConsole.ThrowError(
"Unable to paste assembly: Clipboard content is empty.");
4294 XElement element =
null;
4298 element = XDocument.Parse(text).Root;
4300 catch (Exception) { }
4302 if (element ==
null)
4304 DebugConsole.ThrowError(
"Unable to paste assembly: Clipboard content is not valid XML.");
4309 List<MapEntity> entities;
4312 entities = ItemAssemblyPrefab.PasteEntities(pos.Value, sub, element, selectInstance:
true);
4316 DebugConsole.ThrowError(
"Unable to paste assembly: Failed to load items.", e);
4320 if (!entities.Any()) {
return; }
4321 StoreCommand(
new AddOrDeleteCommand(entities,
false, handleInventoryBehavior:
false));
4333 foreach (var component
in item.Components)
4335 if (component.GetType() == entity.GetType() && component != entity)
4337 entities.Add((component, (Color)
property.GetValue(component), property));
4342 if (selectedEntity.GetType() == entity.GetType())
4344 entities.Add((selectedEntity, (Color)
property.GetValue(selectedEntity), property));
4346 else if (selectedEntity is { SerializableProperties: { } props} )
4350 entities.Add((selectedEntity, (Color) foundProp.GetValue(selectedEntity), foundProp));
4357 bool setValues =
true;
4358 object sliderMutex =
new object(),
4359 sliderTextMutex =
new object(),
4360 pickerMutex =
new object(),
4361 hexMutex =
new object();
4363 Vector2 relativeSize =
new Vector2(0.4f * GUI.AspectRatioAdjustment, 0.3f);
4367 UserData =
"colorpicker",
4374 AutoScaleVertical =
true
4381 RelativeSpacing = 0.1f,
4392 var (h, s, v) = ToolBox.RGBToHSV(originalColor);
4393 colorPicker.
SelectedHue =
float.IsNaN(h) ? 0f : h;
4401 float currentHue = colorPicker.
SelectedHue / 360f;
4406 inputType:
NumberType.Float) { FloatValue = currentHue, MaxValueFloat = 1f, MinValueFloat = 0f, DecimalsToDisplay = 2 };
4409 new GUITextBlock(
new RectTransform(
new Vector2(0.1f, 0.2f), satSliderLayout.
RectTransform), text:
"S:", font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero, ToolTip =
"Saturation"};
4412 inputType:
NumberType.Float) { FloatValue = colorPicker.
SelectedSaturation, MaxValueFloat = 1f, MinValueFloat = 0f, DecimalsToDisplay = 2 };
4418 inputType:
NumberType.Float) { FloatValue = colorPicker.
SelectedValue, MaxValueFloat = 1f, MinValueFloat = 0f, DecimalsToDisplay = 2 };
4423 RelativeSpacing = 0.1f
4428 Rectangle rect = component.Rect;
4429 Point areaSize = new Point(rect.Width, rect.Height / 2);
4430 Rectangle newColorRect = new Rectangle(rect.Location, areaSize);
4431 Rectangle oldColorRect = new Rectangle(new Point(newColorRect.Left, newColorRect.Bottom), areaSize);
4433 GUI.DrawRectangle(batch, newColorRect, ToolBoxCore.HSVToRGB(colorPicker.SelectedHue, colorPicker.SelectedSaturation, colorPicker.SelectedValue), isFilled: true);
4434 GUI.DrawRectangle(batch, oldColorRect, originalColor, isFilled: true);
4435 GUI.DrawRectangle(batch, rect, Color.Black, isFilled: false);
4440 hueScrollBar.
OnMoved = (bar, scroll) => { SetColor(sliderMutex);
return true; };
4441 hueTextBox.
OnValueChanged = input => { SetColor(sliderTextMutex); };
4443 satScrollBar.
OnMoved = (bar, scroll) => { SetColor(sliderMutex);
return true; };
4444 satTextBox.
OnValueChanged = input => { SetColor(sliderTextMutex); };
4446 valueScrollBar.
OnMoved = (bar, scroll) => { SetColor(sliderMutex);
return true; };
4447 valueTextBox.
OnValueChanged = input => { SetColor(sliderTextMutex); };
4449 colorPicker.
OnColorSelected = (component, color) => { SetColor(pickerMutex);
return true; };
4451 hexValueBox.
OnEnterPressed = (box, text) => { SetColor(hexMutex);
return true; };
4452 hexValueBox.
OnDeselected += (sender, key) => { SetColor(hexMutex); };
4459 Color newColor = SetColor(
null);
4461 if (!IsSubEditor()) {
return true; }
4463 Dictionary<object, List<ISerializableEntity>> oldProperties =
new Dictionary<object, List<ISerializableEntity>>();
4465 foreach (var (sEntity, color, _) in entities)
4467 if (sEntity is
MapEntity { Removed:
true }) {
continue; }
4468 if (!oldProperties.ContainsKey(color))
4470 oldProperties.Add(color,
new List<ISerializableEntity>());
4472 oldProperties[color].Add(sEntity);
4475 List<ISerializableEntity> affected = entities.Select(t => t.Entity).Where(se => se is
MapEntity { Removed:
false } || se is
ItemComponent).ToList();
4476 StoreCommand(
new PropertyCommand(affected, property.Name.ToIdentifier(), newColor, oldProperties));
4489 editor.
UpdateValue(property, newColor, flash:
false);
4503 foreach (var (e, color, prop) in entities)
4505 if (e is
MapEntity { Removed:
true }) {
continue; }
4506 prop.TrySetValue(e, color);
4513 Color SetColor(
object source)
4519 if (source == sliderMutex)
4522 SetSliderTexts(hsv);
4523 SetColorPicker(hsv);
4526 else if (source == sliderTextMutex)
4530 SetColorPicker(hsv);
4533 else if (source == pickerMutex)
4537 SetSliderTexts(hsv);
4540 else if (source == hexMutex)
4542 Vector3 hsv = ToolBox.RGBToHSV(XMLExtensions.ParseColor(hexValueBox.
Text, errorMessages:
false));
4543 if (
float.IsNaN(hsv.X)) { hsv.X = 0f; }
4545 SetSliderTexts(hsv);
4546 SetColorPicker(hsv);
4554 foreach (var (e, origColor, prop) in entities)
4556 if (e is
MapEntity { Removed:
true }) {
continue; }
4557 color.A = origColor.A;
4558 prop.TrySetValue(e, color);
4562 void SetSliders(Vector3 hsv)
4569 void SetSliderTexts(Vector3 hsv)
4576 void SetColorPicker(Vector3 hsv)
4578 bool hueChanged = !MathUtils.NearlyEqual(colorPicker.
SelectedHue, hsv.X);
4582 if (hueChanged) { colorPicker.
RefreshHue(); }
4585 void SetHex(Vector3 hsv)
4587 Color hexColor = ToolBoxCore.HSVToRGB(hsv.X, hsv.Y, hsv.Z);
4588 hexValueBox!.
Text = ColorToHex(hexColor);
4592 static string ColorToHex(Color color) => $
"#{(color.R << 16 | color.G << 8 | color.B):X6}";
4598 { MinSize = new Point(120, 300), AbsoluteOffset = offset });
4602 PlaySoundOnSelect =
true,
4603 OnSelected = onWireSelected,
4604 CanTakeKeyBoardFocus =
false
4607 List<ItemPrefab> wirePrefabs =
new List<ItemPrefab>();
4612 if (!itemPrefab.
Tags.Contains(Tags.WireItem)) {
continue; }
4613 if (CircuitBox.IsInGame() && itemPrefab.
Tags.Contains(Tags.Thalamus)) {
continue; }
4615 wirePrefabs.Add(itemPrefab);
4618 foreach (
ItemPrefab itemPrefab
in wirePrefabs.OrderBy(
static w => !w.CanBeBought).ThenBy(
static w => w.UintIdentifier))
4620 GUIFrame imgFrame =
new GUIFrame(
new RectTransform(
new Point(listBox.Content.Rect.Width, listBox.Rect.Width / 2), listBox.Content.RectTransform), style:
"ListBoxElement")
4622 UserData = itemPrefab
4626 UserData = itemPrefab,
4628 HoverColor = Color.Lerp(itemPrefab.
SpriteColor, Color.White, 0.3f),
4629 SelectedColor = Color.Lerp(itemPrefab.
SpriteColor, Color.White, 0.6f)
4636 private bool SelectLinkedSub(
GUIComponent selected,
object userData)
4638 if (userData is not
SubmarineInfo submarine) {
return false; }
4639 var prefab =
new LinkedSubmarinePrefab(submarine);
4640 MapEntityPrefab.SelectPrefab(prefab);
4644 private bool SelectWire(GUIComponent component,
object userData)
4646 if (dummyCharacter ==
null)
return false;
4649 Item existingWire = dummyCharacter.
HeldItems.FirstOrDefault(i => i.Prefab == userData as ItemPrefab);
4650 if (existingWire !=
null)
4652 existingWire.Drop(
null);
4653 existingWire.Remove();
4657 var wire =
new Item(userData as ItemPrefab, Vector2.Zero,
null);
4663 if (existingWire !=
null && existingWire.Prefab != userData as ItemPrefab)
4665 existingWire.
Drop(
null);
4666 existingWire.Remove();
4679 private void OpenItem(Item item)
4681 if (dummyCharacter ==
null || item ==
null) {
return; }
4683 if ((item.GetComponent<
Holdable>() is { Attached: false } || item.GetComponent<
Wearable>() !=
null) && item.GetComponent<
ItemContainer>() !=
null)
4686 oldItemPosition = item.SimPosition;
4687 TeleportDummyCharacter(oldItemPosition);
4694 List<InvSlotType> allowedSlots =
new List<InvSlotType>();
4695 item.AllowedSlots.ForEach(type =>
4697 if (type !=
InvSlotType.Any) { allowedSlots.Add(type); }
4702 if (success) { OpenedItem = item; }
4706 MapEntity.FilteredSelectedList.Clear();
4707 MapEntity.SelectEntity(item);
4709 FilterEntities(entityFilterBox.
Text);
4710 MapEntity.StopSelection();
4716 private void CloseItem()
4718 if (dummyCharacter ==
null) {
return; }
4720 if (DraggedItemPrefab ==
null && dummyCharacter?.SelectedItem ==
null && OpenedItem ==
null) {
return; }
4721 DraggedItemPrefab =
null;
4723 OpenedItem?.
Drop(dummyCharacter);
4726 FilterEntities(entityFilterBox.
Text);
4733 private void TeleportDummyCharacter(Vector2 pos)
4735 if (dummyCharacter !=
null)
4739 limb.body.SetTransform(pos, 0.0f);
4745 private bool ChangeSubName(GUITextBox textBox,
string text)
4747 if (
string.IsNullOrWhiteSpace(text))
4749 textBox.Flash(GUIStyle.Red);
4753 if (MainSub !=
null) { MainSub.Info.Name = text; }
4755 textBox.Text = text;
4756 textBox.Flash(GUIStyle.Green);
4761 private bool ChangeEnemySubTags(GUITextBox textBox,
string text)
4763 if (
string.IsNullOrWhiteSpace(text))
4765 textBox.Flash(GUIStyle.Red);
4769 if (MainSub.Info.EnemySubmarineInfo is { } enemySubInfo)
4771 enemySubInfo.MissionTags.Clear();
4772 string[] tags = text.Split(
',');
4773 foreach (
string tag
in tags)
4775 enemySubInfo.MissionTags.Add(tag.ToIdentifier());
4778 textBox.Text = text;
4779 textBox.Flash(GUIStyle.Green);
4784 private void ChangeSubDescription(GUITextBox textBox,
string text)
4786 if (MainSub !=
null)
4788 MainSub.Info.Description = text;
4792 textBox.UserData = text;
4795 submarineDescriptionCharacterCount.
Text = text.
Length +
" / " + submarineDescriptionLimit;
4798 private bool SelectPrefab(GUIComponent component,
object obj)
4802 if (GUI.MouseOn is GUIButton || GUI.MouseOn?.Parent is GUIButton) {
return false; }
4804 AddPreviouslyUsed(obj as MapEntityPrefab);
4807 if (obj is CoreEntityPrefab prefab)
4809 var matchingTickBox = showEntitiesTickBoxes.Find(tb => tb.UserData as
string == prefab.Identifier);
4810 if (matchingTickBox !=
null && !matchingTickBox.Selected)
4812 previouslyUsedPanel.
Visible =
false;
4813 showEntitiesPanel.
Visible =
true;
4815 matchingTickBox.Selected =
true;
4816 matchingTickBox.Flash(GUIStyle.Green);
4820 if (dummyCharacter?.SelectedItem !=
null)
4827 case ItemAssemblyPrefab assemblyPrefab when PlayerInput.IsShiftDown():
4829 var itemInstance = LoadItemAssemblyInventorySafe(assemblyPrefab);
4830 var spawnedItem =
false;
4832 itemInstance.ForEach(newItem =>
4834 if (newItem !=
null)
4836 var placedItem = inv.TryPutItem(newItem, dummyCharacter);
4837 spawnedItem |= placedItem;
4842 newItem.OwnInventory?.DeleteAllItems();
4848 List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
4849 if (placedEntities.Any())
4851 StoreCommand(
new AddOrDeleteCommand(placedEntities,
false));
4856 case ItemPrefab itemPrefab when PlayerInput.IsShiftDown():
4858 var item =
new Item(itemPrefab, Vector2.Zero, MainSub);
4859 if (!inv.TryPutItem(item, dummyCharacter))
4872 StoreCommand(
new AddOrDeleteCommand(
new List<MapEntity> { item },
false));
4876 case ItemAssemblyPrefab _:
4880 DraggedItemPrefab = (MapEntityPrefab)obj;
4896 private bool GenerateWaypoints()
4898 if (MainSub ==
null) {
return false; }
4899 return WayPoint.GenerateSubWaypoints(MainSub);
4902 private void AddPreviouslyUsed(MapEntityPrefab mapEntityPrefab)
4904 if (previouslyUsedList ==
null || mapEntityPrefab ==
null) {
return; }
4908 if (previouslyUsedList.
CountChildren == PreviouslyUsedCount)
4916 var textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), previouslyUsedList.
Content.
RectTransform) { MinSize = new Point(0, 15) },
4917 ToolBox.LimitString(mapEntityPrefab.Name.Value, GUIStyle.SmallFont, previouslyUsedList.
Content.
Rect.Width), font: GUIStyle.SmallFont)
4919 UserData = mapEntityPrefab
4921 textBlock.RectTransform.SetAsFirstChild();
4936 List<Vector2> wallPoints =
new List<Vector2>();
4939 List<MapEntity> mapEntityList =
new List<MapEntity>();
4945 Door door = it.GetComponent<
Door>();
4948 int halfW = it.WorldRect.Width / 2;
4949 wallPoints.Add(
new Vector2(it.WorldRect.X + halfW, -it.WorldRect.Y + it.WorldRect.Height));
4950 mapEntityList.Add(it);
4958 mapEntityList.Add(e);
4974 if (wallPoints.Count < 4)
4976 DebugConsole.ThrowError(
"Generating hulls for the submarine failed. Not enough wall structures to generate hulls.");
4980 var min = wallPoints[0];
4981 max = wallPoints[0];
4982 for (
int i = 0; i < wallPoints.Count; i++)
4984 min.X = Math.Min(min.X, wallPoints[i].X);
4985 min.Y = Math.Min(min.Y, wallPoints[i].Y);
4986 max.X = Math.Max(max.X, wallPoints[i].X);
4987 max.Y = Math.Max(max.Y, wallPoints[i].Y);
4990 List<Rectangle> hullRects =
new List<Rectangle>
4992 new Rectangle((
int)min.X, (
int)min.Y, (
int)(max.X - min.X), (
int)(max.Y - min.Y))
4994 foreach (Vector2 point
in wallPoints)
4996 MathUtils.SplitRectanglesHorizontal(hullRects, point);
4997 MathUtils.SplitRectanglesVertical(hullRects, point);
5000 hullRects.Sort((a, b) =>
5002 if (a.Y < b.Y)
return -1;
5003 if (a.Y > b.Y)
return 1;
5004 if (a.X < b.X)
return -1;
5005 if (a.X > b.X)
return 1;
5009 for (
int i = 0; i < hullRects.Count - 1; i++)
5012 if (hullRects[i + 1].Y > rect.Y)
continue;
5014 Vector2 hullRPoint =
new Vector2(rect.X + rect.Width - 8, rect.Y + rect.Height / 2);
5015 Vector2 hullLPoint =
new Vector2(rect.X, rect.Y + rect.Height / 2);
5021 entRect.Y = -entRect.Y;
5022 if (entRect.Contains(hullRPoint))
5024 if (!entRect.Contains(hullLPoint)) container = e;
5028 if (container ==
null)
5030 rect.Width += hullRects[i + 1].Width;
5031 hullRects[i] = rect;
5032 hullRects.RemoveAt(i + 1);
5040 if (entRect.Width < entRect.Height)
continue;
5041 entRect.Y = -entRect.Y - 16;
5042 for (
int i = 0; i < hullRects.Count; i++)
5045 if (entRect.Intersects(hullRect))
5047 if (hullRect.Y < entRect.Y)
5049 hullRect.Height = Math.Max((entRect.Y + 16 + entRect.Height / 2) - hullRect.Y, hullRect.Height);
5050 hullRects[i] = hullRect;
5052 else if (hullRect.Y + hullRect.Height <= entRect.Y + 16 + entRect.Height)
5054 hullRects.RemoveAt(i);
5064 if (entRect.Width < entRect.Height)
continue;
5065 entRect.Y = -entRect.Y;
5066 for (
int i = 0; i < hullRects.Count; i++)
5069 if (entRect.Intersects(hullRect))
5071 if (hullRect.Y >= entRect.Y - 8 && hullRect.Y + hullRect.Height <= entRect.Y + entRect.Height + 8)
5073 hullRects.RemoveAt(i);
5080 for (
int i = 0; i < hullRects.Count;)
5083 Vector2 point =
new Vector2(hullRect.X+2, hullRect.Y+hullRect.Height/2);
5088 entRect.Y = -entRect.Y;
5089 if (entRect.Contains(point))
5095 if (container ==
null)
5097 hullRects.RemoveAt(i);
5101 while (hullRects[i].Y <= hullRect.Y)
5104 if (i >= hullRects.Count)
break;
5108 for (
int i = hullRects.Count-1; i >= 0;)
5111 Vector2 point =
new Vector2(hullRect.X+hullRect.Width-2, hullRect.Y+hullRect.Height/2);
5116 entRect.Y = -entRect.Y;
5117 if (entRect.Contains(point))
5123 if (container ==
null)
5125 hullRects.RemoveAt(i); i--;
5129 while (hullRects[i].Y >= hullRect.Y)
5136 hullRects.Sort((a, b) =>
5138 if (a.X < b.X)
return -1;
5139 if (a.X > b.X)
return 1;
5140 if (a.Y < b.Y)
return -1;
5141 if (a.Y > b.Y)
return 1;
5145 for (
int i = 0; i < hullRects.Count - 1; i++)
5148 if (hullRects[i + 1].Width != rect.Width)
continue;
5149 if (hullRects[i + 1].X > rect.X)
continue;
5151 Vector2 hullBPoint =
new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height - 8);
5152 Vector2 hullUPoint =
new Vector2(rect.X + rect.Width / 2, rect.Y);
5158 entRect.Y = -entRect.Y;
5159 if (entRect.Contains(hullBPoint))
5161 if (!entRect.Contains(hullUPoint)) container = e;
5165 if (container ==
null)
5167 rect.Height += hullRects[i + 1].Height;
5168 hullRects[i] = rect;
5169 hullRects.RemoveAt(i + 1);
5174 for (
int i = 0; i < hullRects.Count;i++)
5179 hullRects[i] = rect;
5182 hullRects.Sort((a, b) =>
5184 if (a.Y < b.Y)
return -1;
5185 if (a.Y > b.Y)
return 1;
5186 if (a.X < b.X)
return -1;
5187 if (a.X > b.X)
return 1;
5191 for (
int i = 0; i < hullRects.Count; i++)
5193 for (
int j = i+1; j < hullRects.Count; j++)
5195 if (hullRects[j].Y <= hullRects[i].Y)
continue;
5196 if (hullRects[j].Intersects(hullRects[i]))
5199 rect.Height = hullRects[j].Y - rect.Y;
5200 hullRects[i] = rect;
5209 hullRect.Y = -hullRect.Y;
5217 if (!(e as
Structure).IsPlatform)
continue;
5221 gapRect.Height = 16;
5228 if (GUI.DisableHUD) {
return; }
5249 if (dummyCharacter !=
null)
5261 if (loadFrame !=
null)
5276 if (GUI.MouseOn ==
null) {
return false; }
5284 private static void Redo(
int amount)
5286 for (
int i = 0; i < amount; i++)
5288 if (commandIndex < Commands.Count)
5290 Command command = Commands[commandIndex++];
5294 GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
5297 private static void Undo(
int amount)
5299 for (
int i = 0; i < amount; i++)
5301 if (commandIndex > 0)
5303 Command command = Commands[--commandIndex];
5304 command.UnExecute();
5307 GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
5310 private static void ClearUndoBuffer()
5312 SerializableEntityEditor.PropertyChangesActive =
false;
5313 SerializableEntityEditor.CommandBuffer =
null;
5314 Commands.ForEach(cmd => cmd.Cleanup());
5317 GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
5322 if (commandIndex != Commands.Count)
5324 Commands.RemoveRange(commandIndex, Commands.Count - commandIndex);
5326 Commands.Add(command);
5330 if (Commands.Count > Math.Clamp(GameSettings.CurrentConfig.SubEditorUndoBuffer, 1, 10240))
5332 Commands.First()?.Cleanup();
5333 Commands.RemoveRange(0, 1);
5334 commandIndex = Commands.Count;
5339 if (command is AddOrDeleteCommand addOrDelete)
5345 private string prevSelectedLayer;
5346 private void EntityAddedOrDeleted(IEnumerable<MapEntity> entities)
5348 if (layerList?.SelectedData is
string selectedLayer)
5351 foreach (var entity
in entities)
5353 if (!entity.Removed)
5355 entity.Layer = selectedLayer;
5359 layerElement?.
Flash(GUIStyle.Green);
5363 private void UpdateLayerPanel()
5365 if (layerPanel is
null || layerList is
null) {
return; }
5370 layerSpecificButtons.ForEach(btn => btn.Enabled =
false);
5371 GUILayoutGroup buttonHeaders =
new GUILayoutGroup(
new RectTransform(
new Vector2(1f, 0.075f), layerList.
Content.
RectTransform), isHorizontal:
true, childAnchor:
Anchor.BottomLeft);
5373 new GUIButton(
new RectTransform(
new Vector2(0.25f, 1f), buttonHeaders.RectTransform), TextManager.Get(
"editor.layer.headervisible"), style:
"GUIButtonSmallFreeScale") {
ForceUpperCase =
ForceUpperCase.Yes };
5374 new GUIButton(
new RectTransform(
new Vector2(0.15f, 1f), buttonHeaders.RectTransform), TextManager.Get(
"editor.layer.headerlink"), style:
"GUIButtonSmallFreeScale")
5377 ToolTip = TextManager.Get(
"editor.layer.headerlink.tooltip")
5379 new GUIButton(
new RectTransform(
new Vector2(0.6f, 1f), buttonHeaders.RectTransform), TextManager.Get(
"name"), style:
"GUIButtonSmallFreeScale") {
ForceUpperCase =
ForceUpperCase.Yes };
5381 foreach ((
string layer, (
bool isVisible,
bool isGrouped)) in Layers)
5383 GUIFrame parent =
new GUIFrame(
new RectTransform(
new Vector2(1f, 0.1f), layerList.
Content.
RectTransform), style:
"ListBoxElement")
5388 GUILayoutGroup layerGroup =
new GUILayoutGroup(
new RectTransform(Vector2.One, parent.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
5390 GUILayoutGroup layerVisibilityLayout =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.25f, 1f), layerGroup.RectTransform), childAnchor:
Anchor.Center);
5391 GUITickBox layerVisibleButton =
new GUITickBox(
new RectTransform(Vector2.One, layerVisibilityLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
string.Empty)
5396 if (!Layers.TryGetValue(layer, out LayerData data))
5401 if (!box.Selected && layerList.
SelectedData as
string == layer)
5409 Layers[layer] = data with { IsVisible = box.Selected };
5414 GUILayoutGroup layerChainLayout =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.15f, 1f), layerGroup.RectTransform), childAnchor:
Anchor.Center);
5415 GUITickBox layerChainButton =
new GUITickBox(
new RectTransform(Vector2.One, layerChainLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
string.Empty)
5420 if (!Layers.TryGetValue(layer, out LayerData data))
5426 Layers[layer] = data with { IsGrouped = box.Selected };
5431 layerGroup.Recalculate();
5433 var textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(0.6f, 1f), layerGroup.RectTransform), layer, textAlignment: Alignment.CenterLeft);
5434 if (textBlock.TextSize.X > textBlock.Rect.Width)
5436 textBlock.ToolTip = textBlock.Text;
5437 textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
5440 layerGroup.Recalculate();
5441 layerChainLayout.Recalculate();
5442 layerVisibilityLayout.Recalculate();
5446 buttonHeaders.Recalculate();
5447 foreach (var child
in buttonHeaders.Children)
5449 var btn = child as GUIButton;
5450 string originalBtnText = btn.Text.Value;
5451 btn.Text = ToolBox.LimitString(btn.Text, btn.Font, btn.Rect.Width);
5452 if (originalBtnText != btn.Text && btn.ToolTip.IsNullOrEmpty())
5454 btn.ToolTip = originalBtnText;
5461 if (undoBufferPanel is
null) {
return; }
5463 undoBufferDisclaimer.
Visible = mode ==
Mode.Wiring;
5470 for (
int i = 0; i < Commands.Count; i++)
5472 Command command = Commands[i];
5474 CreateTextBlock(description, description, i + 1, command).RectTransform.SetAsFirstChild();
5477 CreateTextBlock(TextManager.Get(
"undo.beginning"), TextManager.Get(
"undo.beginningtooltip"), 0,
null);
5482 ToolBox.LimitString(name.
Value, GUIStyle.SmallFont, undoBufferList.
Content.
Rect.Width), font: GUIStyle.SmallFont, textColor: index == commandIndex ? GUIStyle.Green : (Color?)
null)
5485 ToolTip = description
5490 private static void CommitBulkItemBuffer()
5492 if (BulkItemBuffer.Any())
5494 AddOrDeleteCommand master = BulkItemBuffer[0];
5495 for (
int i = 1; i < BulkItemBuffer.Count; i++)
5497 AddOrDeleteCommand command = BulkItemBuffer[i];
5498 command.MergeInto(master);
5501 StoreCommand(master);
5502 BulkItemBuffer.Clear();
5505 bulkItemBufferinUse =
null;
5512 public override void Update(
double deltaTime)
5514 SkipInventorySlotUpdate =
false;
5515 ImageManager.Update((
float)deltaTime);
5523 saveAssemblyFrame =
null;
5524 snapToGridFrame =
null;
5529 if (OpenedItem !=
null && OpenedItem.
Removed)
5534 if (WiringMode && dummyCharacter !=
null)
5540 if (equippedWire ==
null)
5546 foreach (var child
in lBox.Content.Children)
5548 if (child.UserData is
Item item)
5555 var highlightedEntities =
new List<MapEntity>();
5560 var wire = item.GetComponent<
Wire>();
5561 if (wire ==
null || !wire.IsMouseOn()) {
continue; }
5562 highlightedEntities.Add(item);
5571 bool isCircuitBoxOpened = dummyCharacter?.
SelectedItem?.GetComponent<CircuitBox>() is not
null;
5578 if (camTargetFocus != Vector2.Zero)
5580 if (GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.Up].IsDown() || GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.Down].IsDown() ||
5581 GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.Left].IsDown() || GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.Right].IsDown())
5583 camTargetFocus = Vector2.Zero;
5587 var targetWithOffset =
new Vector2(camTargetFocus.X, camTargetFocus.Y - offset / 2);
5588 if (Math.Abs(cam.Position.X - targetWithOffset.X) < 1.0f &&
5589 Math.Abs(cam.Position.Y - targetWithOffset.Y) < 1.0f)
5591 camTargetFocus = Vector2.Zero;
5595 cam.Position += (targetWithOffset - cam.Position) / cam.MoveSmoothness;
5605 if (GUI.KeyboardDispatcher.Subscriber ==
null
5607 && GUI.KeyboardDispatcher.Subscriber is
GUIComponent sub
5626 if (GUI.KeyboardDispatcher.Subscriber ==
null)
5628 if (WiringMode && dummyCharacter !=
null)
5632 if (!dummyCharacter.
HeldItems.Any(it => it.HasTag(Tags.WireItem)))
5641 int index = key == Keys.D0 ? numberKeys.Count : numberKeys.IndexOf(key) - 1;
5642 if (index > -1 && index < listBox.Content.CountChildren)
5644 listBox.Select(index);
5645 SkipInventorySlotUpdate =
true;
5651 if (mode ==
Mode.Default)
5655 if (dummyCharacter !=
null)
5663 var container = item.GetComponents<
ItemContainer>().ToList();
5664 if (!container.Any() || container.Any(ic => ic?.DrawInventory ??
false))
5684 if (selected.Count > 0)
5686 var dRect = selected.First().Rect;
5687 var rect =
new Rectangle(dRect.Left, dRect.Top, dRect.Width, dRect.Height * -1);
5688 if (selected.Count > 1)
5691 selected.Skip(1).ForEach(me =>
5693 var wRect = me.Rect;
5694 rect =
Rectangle.Union(rect,
new Rectangle(wRect.Left, wRect.Top, wRect.Width, wRect.Height * -1));
5697 camTargetFocus = rect.Center.ToVector2();
5702 entityFilterBox.
Select();
5706 if (toggleEntityListBind != GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.ToggleInventory])
5708 toggleEntityMenuButton.
ToolTip =
RichString.
Rich($
"{TextManager.Get("EntityMenuToggleTooltip
")}\n‖color:125,125,125‖{GameSettings.CurrentConfig.KeyMap.Bindings[InputType.ToggleInventory].Name}‖color:end‖");
5709 toggleEntityListBind = GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.ToggleInventory];
5711 if (GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.ToggleInventory].IsHit() && mode ==
Mode.Default)
5713 toggleEntityMenuButton.
OnClicked?.Invoke(toggleEntityMenuButton, toggleEntityMenuButton.
UserData);
5718 cam.MoveCamera((
float) deltaTime, allowMove:
false, allowZoom: GUI.MouseOn ==
null);
5725 CreateSaveScreen(subNameLabel !=
null && subNameLabel.
Text != TextManager.Get(
"unspecifiedsubfilename"));
5748 var wire = item.GetComponent<
Wire>();
5749 if (wire !=
null && wire.Connections.None(c => c ==
null) && !selectables.Contains(item))
5751 selectables.Add(item);
5767 cam.MoveCamera((
float) deltaTime, allowMove: !CircuitBox.IsCircuitBoxSelected(dummyCharacter), allowZoom: GUI.MouseOn ==
null);
5772 cam.MoveCamera((
float) deltaTime, allowMove:
false, allowZoom: GUI.MouseOn ==
null);
5778 moveSpeed.X = -moveSpeed.X;
5779 cam.Position += moveSpeed;
5781 camTargetFocus = Vector2.Zero;
5789 if (lightingEnabled)
5796 lightComponent.
Light.Color =
5816 List<Wire> wires =
new List<Wire>();
5819 var wire = item.GetComponent<
Wire>();
5820 if (wire !=
null) wires.Add(wire);
5832 slot.Rect.Y = EntityMenu.
Rect.Top;
5833 slot.Rect.X = EntityMenu.
Rect.X + (EntityMenu.
Rect.Width / 2) - (slot.Rect.Width /2);
5843 if (equippedWire !=
null && equippedWire.
GetNodes().Count > 0)
5845 Vector2 lastNode = equippedWire.
GetNodes().Last();
5853 bool isHorizontal = Math.Abs(cursorX - lastNode.X) < Math.Abs(cursorY - lastNode.Y);
5859 ?
new Vector2(lastNode.X, roundedY)
5860 :
new Vector2(roundedX, lastNode.Y);
5865 if (OpenedItem !=
null)
5867 TeleportDummyCharacter(oldItemPosition);
5870 if (WiringMode && dummyCharacter?.SelectedItem ==
null)
5872 TeleportDummyCharacter(FarseerPhysics.ConvertUnits.ToSimUnits(dummyCharacter.
CursorPosition));
5879 dummyCharacter.
Control((
float)deltaTime, cam);
5882 cam.TargetPos = Vector2.Zero;
5886 if (dummyCharacter?.SelectedItem !=
null)
5892 TryDragItemsToItem(linkedItem);
5896 void TryDragItemsToItem(
Item item)
5907 void TryDragItemsToInventory(
Inventory inv)
5911 var draggingMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(
PlayerInput.
MousePosition, MouseDragStart) >= GUI.Scale * 20;
5916 switch (DraggedItemPrefab)
5921 bool spawnedItem =
false;
5922 for (var i = 0; i < inv.
Capacity; i++)
5930 var newItem =
new Item(itemPrefab, Vector2.Zero, MainSub);
5934 bool placedItem = inv.
TryPutItem(newItem, i,
false,
true, dummyCharacter);
5935 spawnedItem |= placedItem;
5942 else if (itemContainer !=
null && itemContainer.Inventory.CanBePut(itemPrefab))
5944 bool placedItem = itemContainer.Inventory.TryPutItem(newItem, dummyCharacter);
5945 spawnedItem |= placedItem;
5954 slot.ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f);
5960 slot.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.4f);
5963 if (!newItem.Removed)
5965 BulkItemBufferInUse = ItemAddMutex;
5966 BulkItemBuffer.Add(
new AddOrDeleteCommand(
new List<MapEntity> { newItem },
false));
5980 bool spawnedItems =
false;
5989 var itemInstance = LoadItemAssemblyInventorySafe(assemblyPrefab);
5992 var failedCount = 0;
5994 for (var j = 0; j < itemInstance.Count; j++)
5996 var newItem = itemInstance[j];
5997 var newSpot = i + j - failedCount;
6002 if (inv.
GetItemAt(newSpot) ==
null) {
break; }
6009 var placedItem = inv.
TryPutItem(newItem, newSpot,
false,
true, dummyCharacter);
6010 spawnedItems |= placedItem;
6016 newItem?.OwnInventory?.DeleteAllItems();
6022 var placedItem = inv.
TryPutItem(newItem, dummyCharacter);
6023 spawnedItems |= placedItem;
6029 newItem?.OwnInventory?.DeleteAllItems();
6035 List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<
MapEntity>().ToList();
6036 if (placedEntities.Any())
6038 BulkItemBufferInUse = ItemAddMutex;
6039 BulkItemBuffer.Add(
new AddOrDeleteCommand(placedEntities,
false));
6052 CommitBulkItemBuffer();
6063 if (MouseDragStart == Vector2.Zero)
6070 MouseDragStart = Vector2.Zero;
6073 if ((GUI.MouseOn ==
null || !GUI.MouseOn.IsChildOf(TopPanel))
6077 if (layerList is { Visible:
true } && GUI.KeyboardDispatcher.Subscriber == layerList)
6079 GUI.KeyboardDispatcher.Subscriber =
null;
6087 MeasurePositionStart = Vector2.Zero;
6100 bool shouldCloseHud = dummyCharacter?.
SelectedItem !=
null && HUD.CloseHUD(dummyCharacter.
SelectedItem.
Rect) && DraggedItemPrefab ==
null;
6110 if (GUI.IsMouseOn(entityFilterBox))
6116 if (dummyCharacter?.SelectedItem ==
null)
6118 CreateContextMenu();
6120 DraggedItemPrefab =
null;
6132 entityMenuOpenState = entityMenuOpen && !WiringMode ?
6133 (float)Math.Min(entityMenuOpenState + deltaTime * 5.0f, 1.0f) :
6134 (float)Math.Max(entityMenuOpenState - deltaTime * 5.0f, 0.0f);
6143 if (loadFrame !=
null)
6150 else if (saveFrame !=
null)
6158 if (dummyCharacter !=
null)
6166 if (item.
body !=
null)
6172 Wire wire = item.GetComponent<
Wire>();
6173 wire?.
Update((
float)deltaTime, cam);
6191 item.
UpdateHUD(cam, dummyCharacter, (
float)deltaTime);
6201 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
6203 cam.UpdateTransform();
6204 if (lightingEnabled)
6214 graphics.Clear(BackgroundColor);
6216 ImageManager.Draw(spriteBatch, cam);
6218 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
6222 GUI.DrawLine(spriteBatch,
new Vector2(MainSub.HiddenSubPosition.X, -cam.WorldView.Y),
new Vector2(MainSub.HiddenSubPosition.X, -(cam.WorldView.Y - cam.WorldView.Height)), Color.White * 0.5f, 1.0f, (
int)(2.0f / cam.Zoom));
6223 GUI.DrawLine(spriteBatch,
new Vector2(cam.WorldView.X, -MainSub.HiddenSubPosition.Y),
new Vector2(cam.WorldView.Right, -MainSub.HiddenSubPosition.Y), Color.White * 0.5f, 1.0f, (
int)(2.0f / cam.Zoom));
6228 (e.
SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite !=
null));
6232 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
6236 if (OpenedItem?.GetComponent<Wearable>() !=
null)
6240 GUI.DrawRectangle(spriteBatch,
6242 new Vector2(OpenedItem.
Rect.Width, OpenedItem.
Rect.Height),
6243 Color.White,
false, 0, (
int)Math.Max(2.0f / cam.Zoom, 1.0f));
6251 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
6255 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
6262 if (dummyCharacter !=
null && WiringMode)
6266 heldItem.
Draw(spriteBatch, editing:
false, back:
true);
6270 DrawGrid(spriteBatch);
6273 ImageManager.DrawEditing(spriteBatch, cam);
6277 spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative,
null, DepthStencilState.None);
6289 spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
6291 if (MainSub !=
null && cam.Zoom < 5f)
6293 Vector2 position = MainSub.SubBody !=
null ? MainSub.WorldPosition : MainSub.HiddenSubPosition;
6296 spriteBatch, position, cam,
6297 cam.WorldView.Width,
6298 GUIStyle.SubmarineLocationIcon.Value.Sprite, Color.LightBlue * 0.5f);
6301 var notificationIcon = GUIStyle.GetComponentStyle(
"GUINotificationButton");
6302 var tooltipStyle = GUIStyle.GetComponentStyle(
"GUIToolTip");
6318 if (DrawCharacterInventory)
6320 dummyCharacter.
DrawHUD(spriteBatch, cam,
false);
6325 GUI.Draw(Cam, spriteBatch);
6327 if (MeasurePositionStart != Vector2.Zero)
6329 Vector2 startPos = MeasurePositionStart;
6333 startPos = RoundToGrid(startPos);
6334 mouseWorldPos = RoundToGrid(mouseWorldPos);
6336 static Vector2 RoundToGrid(Vector2 position)
6344 GUI.DrawLine(spriteBatch, cam.WorldToScreen(startPos), cam.WorldToScreen(mouseWorldPos), GUIStyle.Green, width: 4);
6346 decimal realWorldDistance = decimal.Round((decimal) (Vector2.Distance(startPos, mouseWorldPos) * Physics.DisplayToRealWorldRatio), 2);
6348 Vector2 offset =
new Vector2(GUI.IntScale(24));
6349 GUI.DrawString(spriteBatch,
PlayerInput.
MousePosition + offset, $
"{realWorldDistance} m", GUIStyle.TextColorNormal, font: GUIStyle.Font, backgroundColor: Color.Black, backgroundPadding: 4);
6355 private void CreateImage(
int width,
int height, System.IO.Stream stream)
6363 Vector2 viewPos = subDimensions.Center.ToVector2();
6364 float scale = Math.Min(width / (
float)subDimensions.Width, height / (
float)subDimensions.Height);
6366 var viewMatrix = Matrix.CreateTranslation(
new Vector3(width / 2.0f, height / 2.0f, 0));
6367 var transform = Matrix.CreateTranslation(
6368 new Vector3(-viewPos.X, viewPos.Y, 0)) *
6369 Matrix.CreateScale(
new Vector3(scale, scale, 1)) *
6372 using (RenderTarget2D rt =
new RenderTarget2D(
6374 width, height,
false, SurfaceFormat.Color, DepthFormat.None))
6375 using (SpriteBatch spriteBatch =
new SpriteBatch(
GameMain.
Instance.GraphicsDevice))
6380 spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied,
null,
null,
null,
null, transform);
6388 rt.SaveAsPng(stream, width, height);
6396 private static readonly Color gridBaseColor = Color.White * 0.1f;
6398 private void DrawGrid(SpriteBatch spriteBatch)
6401 if (!ShouldDrawGrid) {
return; }
6403 var (gridX, gridY) =
Submarine.GridSize;
6404 DrawGrid(spriteBatch, cam, gridX, gridY, zoomTreshold:
true);
6407 public static void DrawGrid(SpriteBatch spriteBatch,
Camera cam,
float sizeX,
float sizeY,
bool zoomTreshold)
6409 if (zoomTreshold && cam.
Zoom < 0.5f) {
return; }
6410 int scale = Math.Max(1, GUI.IntScale(1));
6411 float zoom = cam.
Zoom / 2f;
6412 float lineThickness = Math.Max(1, scale / zoom);
6414 Color gridColor = gridBaseColor;
6415 if (zoomTreshold && cam.
Zoom < 1.0f)
6418 gridColor *= Math.Max(0, (cam.
Zoom - 0.5f) * 2f);
6423 for (
float x = snapX(camRect.X); x < snapX(camRect.X + camRect.Width) + sizeX; x += sizeX)
6425 spriteBatch.DrawLine(
new Vector2(x, -camRect.Y),
new Vector2(x, -(camRect.Y - camRect.Height)), gridColor, thickness: lineThickness);
6428 for (
float y = snapY(camRect.Y); y >= snapY(camRect.Y - camRect.Height) - sizeY; y -= sizeY)
6430 spriteBatch.DrawLine(
new Vector2(camRect.X, -y),
new Vector2(camRect.Right, -y), gridColor, thickness: lineThickness);
6433 float snapX(
int x) => (float) Math.Floor(x / sizeX) * sizeX;
6434 float snapY(
int y) => (float) Math.Ceiling(y / sizeY) * sizeY;
6441 RectangleF playableArea =
new RectangleF(
6442 -playableAreaSize / 2f,
6443 -playableAreaSize / 2f,
6448 RectangleF topRect =
new(
6452 playableArea.Top + camRect.Top
6456 float camRectBottom = -camRect.Top + camRect.Height;
6458 RectangleF bottomRect =
new(
6460 playableArea.Bottom,
6462 camRectBottom + playableArea.Bottom
6465 RectangleF rightRect =
new(
6468 camRect.Right - playableArea.Right,
6472 RectangleF leftRect =
new(
6475 camRect.Left - playableArea.Left,
6479 GUI.DrawFilledRectangle(spriteBatch, topRect, color);
6480 GUI.DrawFilledRectangle(spriteBatch, leftRect, color);
6481 GUI.DrawFilledRectangle(spriteBatch, rightRect, color);
6482 GUI.DrawFilledRectangle(spriteBatch, bottomRect, color);
6487 System.IO.Stream stream = File.OpenWrite(filePath);
6488 CreateImage(width, height, stream);
6494 if (
string.IsNullOrEmpty(subcategory) || !hiddenSubCategories.ContainsKey(subcategory))
6498 return hiddenSubCategories[subcategory];
6506 if (!IsSubEditor() ||
string.IsNullOrWhiteSpace(entity.
Layer)) {
return true; }
6508 if (!Layers.TryGetValue(entity.
Layer, out LayerData data))
6514 return data.IsVisible;
6519 if (!IsSubEditor() ||
string.IsNullOrWhiteSpace(entity.
Layer)) {
return false; }
6521 if (!Layers.TryGetValue(entity.
Layer, out LayerData data))
6527 return data.IsGrouped;
6532 if (
string.IsNullOrWhiteSpace(entity.
Layer)) {
return ImmutableHashSet<MapEntity>.Empty; }
Vector2 WorldToScreen(Vector2 coords)
static void AddToGUIUpdateList(Character character)
static void Update(float deltaTime, Character character, Camera cam)
Vector2? CursorWorldPosition
override Vector2? SimPosition
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
void Control(float deltaTime, Camera cam)
CharacterInventory Inventory
void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam=true)
Control the Character according to player input
static Character? Controlled
readonly AnimController AnimController
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
void DrawHUD(SpriteBatch spriteBatch, Camera cam, bool drawHealth=true)
override void CreateSlots()
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
InvSlotType[] SlotTypes
Slot type for each inventory slot. Vanilla package has one type for each slot, although it is technic...
int FindLimbSlot(InvSlotType limbSlot)
ImmutableArray< ContentFile > Files
virtual Vector2 WorldPosition
virtual Vector2 DrawPosition
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
OnColorSelectedHandler? OnColorSelected
bool ClampMouseRectToParent
GUIComponent GetChild(int index)
virtual void RemoveChild(GUIComponent child)
virtual void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectInflate=null)
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
virtual Rectangle? MouseRect
GUIComponent GetChildByUserData(object obj)
virtual void ClearChildren()
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
virtual void DrawManually(SpriteBatch spriteBatch, bool alsoChildren=false, bool recursive=true)
By default, all the gui elements are drawn automatically in the same order they appear on the update ...
IEnumerable< GUIComponent > GetAllChildren()
Returns all child elements in the hierarchy.
void DrawToolTip(SpriteBatch spriteBatch)
Creates and draws a tooltip.
RectTransform RectTransform
SpriteEffects SpriteEffects
IEnumerable< GUIComponent > Children
void FadeOut(float duration, bool removeAfter, float wait=0.0f, Action onRemove=null, bool alsoChildren=false)
IEnumerable< GUIComponent > FindChildren(object userData)
GUIComponent that can be used to render custom content on the UI
GUIComponent AddItem(LocalizedString text, object userData=null, LocalizedString toolTip=null, Color? color=null, Color? textColor=null)
OnSelectedHandler OnSelected
Triggers when some item is cliecked from the dropdown. Note that SelectedData is not set yet when thi...
OnSelectedHandler OnDropped
override void ClearChildren()
override void RemoveChild(GUIComponent child)
void RecalculateChildren()
override void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
delegate bool OnSelectedHandler(GUIComponent component, object obj)
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
void UpdateScrollBarSize()
static readonly List< GUIComponent > MessageBoxes
TextBoxEvent OnDeselected
OnTextChangedHandler OnTextChanged
Don't set the Text property on delegates that register to this event, because modifying the Text will...
void Select(int forcedCaretIndex=-1, bool ignoreSelectSound=false)
OnEnterHandler OnEnterPressed
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectOffset=null)
static SubEditorScreen SubEditorScreen
static int GraphicsHeight
static Lights.LightManager LightManager
static Sounds.SoundManager SoundManager
static List< Gap > GapList
static void UpdateCheats(float deltaTime, Camera cam)
virtual bool CanBePutInSlot(Item item, int i, bool ignoreCondition=false)
Can the item be put in the specified slot.
virtual 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
static bool IsMouseOnSlot(VisualSlot slot)
Check if the mouse is hovering on top of the slot
static readonly List< Item > DraggingItems
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
Item GetItemAt(int index)
Get the item stored in the specified inventory slot. If the slot contains a stack of items,...
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
ItemInventory OwnInventory
Inventory ParentInventory
override void AddToGUIUpdateList(int order=0)
void SetTransform(Vector2 simPosition, float rotation, bool findNewHull=true, bool setPrevTransform=true)
override void Draw(SpriteBatch spriteBatch, bool editing, bool back=true)
void UpdateHUD(Camera cam, Character character, float deltaTime)
static readonly List< Item > ItemList
List< ItemComponent > Components
SpriteEffects SpriteEffects
static readonly PrefabCollection< ItemPrefab > Prefabs
override LocalizedString Name
override ImmutableHashSet< Identifier > Tags
The base class for components holding the different functionalities of the item
readonly ItemInventory Inventory
bool KeepOpenWhenEquipped
override void Move(Vector2 amount, bool ignoreContacts=false)
static void UpdateEditing(List< Wire > wires)
List< Vector2 > GetNodes()
override void Update(float deltaTime, Camera cam)
static HashSet< MapEntity > SelectedList
static List< MapEntity > FilteredSelectedList
static GUIListBox HighlightedListBox
static Vector2 StartMovingPos
static IEnumerable< MapEntity > HighlightedEntities
readonly MapEntityPrefab Prefab
static readonly List< MapEntity > MapEntityList
static void AddSelection(MapEntity entity)
static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
static void UpdateEditor(Camera cam, float deltaTime)
static void DeselectAll()
bool IsLayerHidden
Is the layer this entity is in currently hidden? If it is, the entity is not updated and should do no...
static GUIComponent EditingHUD
readonly List< MapEntity > linkedTo
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 Vector2 SelectionPos
static void DrawSelecting(SpriteBatch spriteBatch, Camera cam)
Draw the "selection rectangle" and outlines of entities that are being dragged (if any)
static void ClearHighlightedEntities()
virtual void UpdatePlacing(Camera cam)
virtual void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
static bool SelectPrefab(object selection)
static MapEntityPrefab Selected
static MapEntityPrefab Find(string name, string identifier=null, bool showErrorMessages=true)
Find a matching map entity prefab
bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform=true)
void FindHull(Vector2? worldPosition=null, bool setSubmarine=true)
static RichString Rich(LocalizedString str, Func< string, string >? postProcess=null)
Dictionary< Identifier, GUIComponent[]> Fields
Holds the references to the input fields.
void UpdateValue(SerializableProperty property, object newValue, bool flash=true)
static void CommitCommandBuffer()
static bool PropertyChangesActive
static DateTime NextCommandPush
const string SoundCategoryDefault
const string SoundCategoryWaterAmbience
void SetCategoryGainMultiplier(string category, float gain, int index=0)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
static List< WarningType > SuppressedWarnings
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
This is called when the game should draw itself.
void SetMode(Mode newMode)
void SaveScreenShot(int width, int height, string filePath)
override void Update(double deltaTime)
Allows the game to run logic such as updating the world, checking for collisions, gathering input,...
GUIButton ToggleEntityMenuButton
void Select(bool enableAutoSave=true)
static bool IsLayerVisible(MapEntity entity)
const int MaxShadowCastingLights
static XDocument AutoSaveInfo
void UpdateUndoHistoryPanel()
static readonly List< Command > Commands
Global undo/redo state for the sub editor and a selector index for it Command
static GUIMessageBox CreatePropertyColorPicker(Color originalColor, SerializableProperty property, ISerializableEntity entity)
static List< AddOrDeleteCommand > BulkItemBuffer
static void DrawOutOfBoundsArea(SpriteBatch spriteBatch, Camera cam, float playableAreaSize, Color color)
override void AddToGUIUpdateList()
By default, submits the screen's main GUIFrame and, if requested upon construction,...
bool IsMouseOnEditorGUI()
GUI.MouseOn doesn't get updated while holding primary mouse and we need it to
void ClearBackedUpSubInfo()
bool IsSubcategoryHidden(string subcategory)
static bool IsWiringMode()
static bool TransparentWiringMode
void LoadSub(SubmarineInfo info, bool checkIdConflicts=true)
static readonly EditorImageManager ImageManager
static void StoreCommand(Command command)
static readonly object ItemAddMutex
override void DeselectEditorSpecific()
static GUIFrame CreateWiringPanel(Point offset, GUIListBox.OnSelectedHandler onWireSelected)
static ImmutableHashSet< MapEntity > GetEntitiesInSameLayer(MapEntity entity)
static bool IsSubEditor()
static bool IsLayerLinked(MapEntity entity)
override void OnFileDropped(string filePath, string extension)
static Type DetermineSubFileType(SubmarineType type)
static bool ShouldDrawGrid
static void DrawGrid(SpriteBatch spriteBatch, Camera cam, float sizeX, float sizeY, bool zoomTreshold)
GUIComponent showEntitiesPanel
static object BulkItemBufferInUse
bool DrawCharacterInventory
static MapEntityPrefab DraggedItemPrefab
Prefab used for dragging from the item catalog into inventories GUI.Draw
static bool SkipInventorySlotUpdate
static Vector2 MouseDragStart
static void DrawFront(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
static void DrawPaintedColors(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
static void DrawDamageable(SpriteBatch spriteBatch, Effect damageEffect, bool editing=false, Predicate< MapEntity > predicate=null)
static void Draw(SpriteBatch spriteBatch, bool editing=false)
Rectangle CalculateDimensions(bool onlyHulls=true)
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
static List< Submarine > Loaded
Vector2 HiddenSubPosition
static void DrawBack(SpriteBatch spriteBatch, bool editing=false, Predicate< MapEntity > predicate=null)
void UpdateTransform(bool interpolate=true)
static readonly Vector2 GridSize
override Vector2? Position
XElement SubmarineElement
LocalizedString Description
OutpostModuleInfo OutpostModuleInfo
static bool ShowWayPoints
@ Character
Characters only
@ Structure
Structures and hulls, but also items (for backwards support)!