Release 1.9.7.0 - Summer Update 2025
This commit is contained in:
+51
-13
@@ -11,6 +11,14 @@ namespace Barotrauma
|
||||
{
|
||||
abstract class CampaignSetupUI
|
||||
{
|
||||
protected enum SaveSortingType
|
||||
{
|
||||
LastPlayedDescending, LastPlayedAscending,
|
||||
NameDescending, NameAscending
|
||||
}
|
||||
|
||||
private const SaveSortingType DefaultSaveSortingType = SaveSortingType.LastPlayedDescending;
|
||||
|
||||
protected readonly GUIComponent newGameContainer, loadGameContainer;
|
||||
|
||||
protected GUIListBox saveList;
|
||||
@@ -111,24 +119,54 @@ namespace Barotrauma
|
||||
return saveFrame;
|
||||
}
|
||||
|
||||
protected void SortSaveList()
|
||||
protected void SortSaveList(SaveSortingType sortingType = DefaultSaveSortingType) => saveList?.Content.RectTransform.SortChildren((rect1, rect2) =>
|
||||
{
|
||||
saveList.Content.RectTransform.SortChildren((c1, c2) =>
|
||||
if (rect1.GUIComponent.UserData is not CampaignMode.SaveInfo file1 || rect2.GUIComponent.UserData is not CampaignMode.SaveInfo file2) { return 0; }
|
||||
if (!file1.SaveTime.TryUnwrap(out SerializableDateTime file1WriteTime) || !file2.SaveTime.TryUnwrap(out SerializableDateTime file2WriteTime)) { return 0; }
|
||||
return sortingType switch
|
||||
{
|
||||
if (c1.GUIComponent.UserData is not CampaignMode.SaveInfo file1
|
||||
|| c2.GUIComponent.UserData is not CampaignMode.SaveInfo file2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
SaveSortingType.LastPlayedDescending => file2WriteTime.CompareTo(file1WriteTime),
|
||||
SaveSortingType.LastPlayedAscending => file1WriteTime.CompareTo(file2WriteTime),
|
||||
SaveSortingType.NameDescending => string.Compare(Path.GetFileNameWithoutExtension(file1.FilePath), Path.GetFileNameWithoutExtension(file2.FilePath), StringComparison.OrdinalIgnoreCase),
|
||||
SaveSortingType.NameAscending => string.Compare(Path.GetFileNameWithoutExtension(file2.FilePath), Path.GetFileNameWithoutExtension(file1.FilePath), StringComparison.OrdinalIgnoreCase),
|
||||
_ => 0
|
||||
};
|
||||
});
|
||||
|
||||
if (!file1.SaveTime.TryUnwrap(out var file1WriteTime)
|
||||
|| !file2.SaveTime.TryUnwrap(out var file2WriteTime))
|
||||
protected void CreateSaveFilteringHeader(GUIComponent parent)
|
||||
{
|
||||
GUILayoutGroup container = new(new RectTransform(Vector2.UnitX, parent.RectTransform), true) { Stretch = true };
|
||||
|
||||
GUI.CreateFilterBox(new RectTransform(new Vector2(0.6f, 1f), container.RectTransform)).OnTextChanged += (_, filterText) =>
|
||||
{
|
||||
filterText = filterText.Trim();
|
||||
foreach (GUIComponent saveElement in saveList.Content.Children)
|
||||
{
|
||||
return 0;
|
||||
if (saveElement.UserData is not CampaignMode.SaveInfo saveInfo) { continue; }
|
||||
saveElement.Visible = filterText.IsNullOrEmpty()
|
||||
|| Path.GetFileNameWithoutExtension(saveInfo.FilePath).Contains(filterText, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return file2WriteTime.CompareTo(file1WriteTime);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
SaveSortingType[] sortingTypes = Enum.GetValues<SaveSortingType>();
|
||||
GUIDropDown dropDown = new(new RectTransform(new Vector2(0.4f, 1f), container.RectTransform), elementCount: sortingTypes.Length)
|
||||
{
|
||||
OnSelected = (_, data) =>
|
||||
{
|
||||
SortSaveList((SaveSortingType)data);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
foreach (SaveSortingType sortingType in sortingTypes)
|
||||
{
|
||||
dropDown.AddItem(TextManager.Get($"SaveSortingType.{sortingType}"), sortingType);
|
||||
}
|
||||
|
||||
dropDown.SelectItem(DefaultSaveSortingType);
|
||||
|
||||
container.RectTransform.MinSize = (0, container.Children.Max(child => child.Rect.Size.Y));
|
||||
}
|
||||
|
||||
public struct CampaignSettingElements
|
||||
|
||||
+2
@@ -217,6 +217,8 @@ namespace Barotrauma
|
||||
RelativeSpacing = 0.03f
|
||||
};
|
||||
|
||||
CreateSaveFilteringHeader(leftColumn);
|
||||
|
||||
saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform))
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
|
||||
+4
-2
@@ -320,14 +320,14 @@ namespace Barotrauma
|
||||
if (string.IsNullOrWhiteSpace(sender.Text))
|
||||
{
|
||||
characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced);
|
||||
sender.Text = characterInfo.Name;
|
||||
sender.UserData = "random";
|
||||
}
|
||||
else
|
||||
{
|
||||
characterInfo.Name = sender.Text;
|
||||
characterInfo.Rename(sender.Text);
|
||||
sender.UserData = "user";
|
||||
}
|
||||
sender.Text = characterInfo.Name;
|
||||
};
|
||||
characterName.OnEnterPressed += (sender, text) =>
|
||||
{
|
||||
@@ -594,6 +594,8 @@ namespace Barotrauma
|
||||
RelativeSpacing = 0.03f
|
||||
};
|
||||
|
||||
CreateSaveFilteringHeader(leftColumn);
|
||||
|
||||
saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform))
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
|
||||
@@ -352,7 +352,7 @@ namespace Barotrauma
|
||||
Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation();
|
||||
if (connection != null && connection.Locations.Contains(currentDisplayLocation))
|
||||
{
|
||||
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList();
|
||||
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).Where(m => m.Prefab.ShowInMenus || GameMain.DebugDraw).ToList();
|
||||
|
||||
if (!availableMissions.Any()) { availableMissions.Insert(0, null); }
|
||||
|
||||
@@ -389,19 +389,26 @@ namespace Barotrauma
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true);
|
||||
missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
|
||||
LocalizedString missionName = mission?.Name ?? TextManager.Get("NoMission");
|
||||
if (GameMain.DebugDraw && mission != null)
|
||||
{
|
||||
if (!mission.Prefab.ShowInMenus) { missionName = $"[HIDDEN] {missionName}"; }
|
||||
missionName += $" ({mission.Prefab.Identifier})";
|
||||
}
|
||||
|
||||
var missionNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), missionName, font: GUIStyle.SubHeadingFont, wrap: true);
|
||||
missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
|
||||
if (mission == null)
|
||||
{
|
||||
missionTextContent.RectTransform.MinSize = missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(35));
|
||||
missionTextContent.RectTransform.MinSize = missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(35));
|
||||
missionTextContent.ChildAnchor = Anchor.CenterLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUITickBox tickBox = null;
|
||||
if (!isMissionInNextLocation)
|
||||
if (!isMissionInNextLocation && mission.Prefab.ShowInMenus)
|
||||
{
|
||||
tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty)
|
||||
tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionNameBlock.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.X, 0) }, label: string.Empty)
|
||||
{
|
||||
UserData = mission,
|
||||
Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
|
||||
@@ -443,7 +450,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (mission.Difficulty.HasValue)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionName.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionNameBlock.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.Z, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
@@ -465,11 +472,11 @@ namespace Barotrauma
|
||||
|
||||
float extraPadding = 0;// 0.8f * tickBox.Rect.Width;
|
||||
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
|
||||
missionName.Padding = new Vector4(missionName.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding,
|
||||
missionName.Padding.Y,
|
||||
missionName.Padding.Z + extraZPadding + extraPadding,
|
||||
missionName.Padding.W);
|
||||
missionName.CalculateHeightFromText();
|
||||
missionNameBlock.Padding = new Vector4(missionNameBlock.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding,
|
||||
missionNameBlock.Padding.Y,
|
||||
missionNameBlock.Padding.Z + extraZPadding + extraPadding,
|
||||
missionNameBlock.Padding.W);
|
||||
missionNameBlock.CalculateHeightFromText();
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null);
|
||||
@@ -544,7 +551,7 @@ namespace Barotrauma
|
||||
OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
|
||||
missionList.Content.Children.Any(c => c.UserData is Mission mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
|
||||
missionList.Content.Children.Any(c => c.UserData is Mission { Prefab.ShowInMenus: true } mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
|
||||
{
|
||||
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
|
||||
@@ -482,6 +482,11 @@ namespace Barotrauma
|
||||
|
||||
public void TestLevelGenerationForErrors(int amountOfLevelsToGenerate)
|
||||
{
|
||||
if (selectedParams == null)
|
||||
{
|
||||
throw new InvalidOperationException("No level generation parameters selected in the level editor.");
|
||||
}
|
||||
|
||||
CoroutineManager.StartCoroutine(GenerateLevels());
|
||||
|
||||
IEnumerable<CoroutineStatus> GenerateLevels()
|
||||
@@ -520,7 +525,7 @@ namespace Barotrauma
|
||||
errorCatcher.Errors.ToList().ForEach(e => DebugConsole.ThrowError(e.Text));
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
yield return CoroutineStatus.Running;
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,6 +740,30 @@ namespace Barotrauma
|
||||
|
||||
List<Identifier> missionTypes = MissionPrefab.GetAllMultiplayerSelectableMissionTypes().ToList();
|
||||
|
||||
GUILayoutGroup buttonGroup = new(new RectTransform(Vector2.UnitX, missionTypeList.Content.RectTransform), true) { Stretch = true };
|
||||
GUIButton selectAllMissionsButton = new(new RectTransform(new Vector2(0.5f, 1f), buttonGroup.RectTransform), TextManager.Get("selectall"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
IEnumerable<Identifier> validMissions = GetValidMissions();
|
||||
validMissions.ForEach(missionType => GameMain.Client.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: missionType));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
GUIButton deselectAllMissionsButton = new(new RectTransform(new Vector2(0.5f, 1f), buttonGroup.RectTransform), TextManager.Get("deselectall"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
IEnumerable<Identifier> validMissions = GetValidMissions();
|
||||
|
||||
// The server must have at least one mission selected, so ensure the first in the list is enabled.
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: validMissions.First());
|
||||
validMissions.Skip(1).ForEach(missionType => GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, removedMissionType: missionType));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
buttonGroup.RectTransform.MinSize = (0, buttonGroup.Children.Max(child => child.Rect.Height));
|
||||
|
||||
missionTypeTickBoxes = new GUITickBox[missionTypes.Count];
|
||||
int index = 0;
|
||||
foreach (var missionType in missionTypes.OrderBy(t => TextManager.Get("MissionType." + t.Value).Value))
|
||||
@@ -763,6 +787,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Identifier firstValidMission = GetValidMissions().First();
|
||||
if (missionTypeTickBoxes.None(tickBox => tickBox.Selected && tickBox.Parent.Visible))
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: firstValidMission);
|
||||
if ((Identifier)tickbox.UserData == firstValidMission) { return true; }
|
||||
}
|
||||
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, removedMissionType: (Identifier)tickbox.UserData);
|
||||
}
|
||||
return true;
|
||||
@@ -771,9 +802,16 @@ namespace Barotrauma
|
||||
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
|
||||
index++;
|
||||
}
|
||||
|
||||
clientDisabledElements.Add(selectAllMissionsButton);
|
||||
clientDisabledElements.Add(deselectAllMissionsButton);
|
||||
clientDisabledElements.AddRange(missionTypeTickBoxes);
|
||||
|
||||
return gameModeSpecificFrame;
|
||||
|
||||
IEnumerable<Identifier> GetValidMissions() => missionTypeTickBoxes
|
||||
.Where(tickBox => tickBox.Parent.Visible)
|
||||
.Select(tickBox => (Identifier)tickBox.UserData);
|
||||
}
|
||||
|
||||
private GUIFrame gameModeSettingsContent;
|
||||
@@ -2884,6 +2922,11 @@ namespace Barotrauma
|
||||
UserData = new JobVariant(jobPrefab, variant)
|
||||
};
|
||||
jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.Right, parentSlot.Rect.Y);
|
||||
if (jobVariantTooltip.Rect.X < 0)
|
||||
{
|
||||
jobVariantTooltip.RectTransform.SetPosition(anchor: Anchor.TopLeft, pivot: Pivot.BottomLeft);
|
||||
jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.X, parentSlot.Rect.Y);
|
||||
}
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), jobVariantTooltip.RectTransform, Anchor.Center))
|
||||
{
|
||||
@@ -3049,6 +3092,8 @@ namespace Barotrauma
|
||||
|
||||
private void RefreshOutpostDropdown()
|
||||
{
|
||||
Identifier randomOutpostIdentifier = "Random".ToIdentifier();
|
||||
|
||||
outpostDropdown.Parent.Visible = MissionTypeFrame.Visible;
|
||||
if (!outpostDropdown.Parent.Visible) { return; }
|
||||
|
||||
@@ -3057,7 +3102,7 @@ namespace Barotrauma
|
||||
Identifier prevSelected = GameMain.NetworkMember?.ServerSettings.SelectedOutpostName ?? Identifier.Empty;
|
||||
|
||||
outpostDropdown.ClearChildren();
|
||||
outpostDropdown.AddItem(TextManager.Get("Random"), "Random".ToIdentifier());
|
||||
outpostDropdown.AddItem(TextManager.Get("Random"), randomOutpostIdentifier);
|
||||
HashSet<Identifier> validOutpostTagsForMissions = new HashSet<Identifier>();
|
||||
|
||||
IEnumerable<Type> suitableMissionClasses =
|
||||
@@ -3081,12 +3126,22 @@ namespace Barotrauma
|
||||
foreach (var submarineInfo in SubmarineInfo.SavedSubmarines.DistinctBy(s => s.Name))
|
||||
{
|
||||
if (submarineInfo.Type == SubmarineType.Outpost &&
|
||||
validOutpostTagsForMissions.Any(tag => submarineInfo.OutpostTags.Contains(tag)))
|
||||
validOutpostTagsForMissions.Any(submarineInfo.OutpostTags.Contains))
|
||||
{
|
||||
outpostDropdown.AddItem(submarineInfo.DisplayName, userData: submarineInfo.Name.ToIdentifier(), toolTip: submarineInfo.Description);
|
||||
}
|
||||
}
|
||||
outpostDropdown.ListBox.Select(prevSelected);
|
||||
if (!outpostDropdown.ListBox.Select(prevSelected))
|
||||
{
|
||||
//could not select the previously selected outpost (not suitable for the selected missions)
|
||||
// -> choose random instead
|
||||
if (outpostDropdown.SelectedData is Identifier selectedIdentifier &&
|
||||
selectedIdentifier != randomOutpostIdentifier)
|
||||
{
|
||||
outpostDropdown.Flash(GUIStyle.Red);
|
||||
}
|
||||
outpostDropdown.ListBox.Select(randomOutpostIdentifier);
|
||||
}
|
||||
GameMain.Client.ServerSettings.AssignGUIComponent(nameof(ServerSettings.SelectedOutpostName), outpostDropdown);
|
||||
}
|
||||
else
|
||||
@@ -4496,26 +4551,16 @@ namespace Barotrauma
|
||||
|
||||
void PositionJobSelectionFrame()
|
||||
{
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - JobSelectionFrame.Rect.Width, characterInfoFrame.Rect.Bottom);
|
||||
if (characterInfoFrame.Rect.Bottom + JobSelectionFrame.Rect.Height > GameMain.GraphicsHeight)
|
||||
//move to the left side of the info frame
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobList.Rect.Y);
|
||||
if (JobSelectionFrame.Rect.X < 0)
|
||||
{
|
||||
//move to the left side of the info frame if the bottom goes below the screen
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, characterInfoFrame.Rect.Bottom - JobSelectionFrame.Rect.Height / 2);
|
||||
if (JobSelectionFrame.Rect.X < 0)
|
||||
{
|
||||
//scale if goes outside the screen horizontally
|
||||
JobSelectionFrame.RectTransform.Resize(new Point(characterInfoFrame.Rect.X, JobSelectionFrame.Rect.Height));
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobSelectionFrame.RectTransform.AbsoluteOffset.Y);
|
||||
}
|
||||
}
|
||||
//scale if goes outside the screen horizontally
|
||||
JobSelectionFrame.RectTransform.Resize(new Point(characterInfoFrame.Rect.X, JobSelectionFrame.Rect.Height));
|
||||
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobSelectionFrame.RectTransform.AbsoluteOffset.Y);
|
||||
}
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), JobSelectionFrame.RectTransform, anchor: Anchor.Center), style: "OuterGlow", color: Color.Black)
|
||||
{
|
||||
UserData = "outerglow",
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var jobSelectionList = new GUIListBox(new RectTransform(Vector2.One * listBoxRelativeSize, JobSelectionFrame.RectTransform, Anchor.Center), style: "GUIFrameListBox")
|
||||
{
|
||||
Padding = Vector4.One * GUI.IntScale(10)
|
||||
|
||||
@@ -368,6 +368,7 @@ namespace Barotrauma
|
||||
"DecorativeSprite",
|
||||
"BarrelSprite",
|
||||
"RailSprite",
|
||||
"ChargeSprite",
|
||||
"SchematicSprite",
|
||||
"WeldedSprite"
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
@@ -37,6 +38,151 @@ namespace Barotrauma
|
||||
Wiring
|
||||
}
|
||||
|
||||
#region Transform Editor
|
||||
private const float TransformWidgetOffset = 300f;
|
||||
|
||||
private GUITickBox rotateToolToggle, scaleToolToggle;
|
||||
public bool TransformWidgetSelected => TransformWidget.IsSelected;
|
||||
|
||||
private static Vector2 GetSelectionCenter()
|
||||
{
|
||||
IEnumerable<MapEntity> nonWireEntities = MapEntity.FilteredSelectedList.Where(static entity => (entity as Item)?.GetComponent<Wire>() is not { Drawable: true });
|
||||
if (nonWireEntities.None()) { return Vector2.Zero; }
|
||||
|
||||
float minX = nonWireEntities.Min(static entity => entity.DrawPosition.X);
|
||||
float minY = nonWireEntities.Min(static entity => entity.DrawPosition.Y);
|
||||
float maxX = nonWireEntities.Max(static entity => entity.DrawPosition.X);
|
||||
float maxY = nonWireEntities.Max(static entity => entity.DrawPosition.Y);
|
||||
return new Vector2(minX + maxX, minY + maxY) / 2f;
|
||||
}
|
||||
|
||||
private Vector2 oldWidgetWorldPos;
|
||||
|
||||
public record struct TransformData(float Scale, float RotationRad, Vector2 Pos, Rectangle Rect, Vector2? TexOffset,
|
||||
Dictionary<Wire, (List<Vector2> Nodes, float Width)> Wires,
|
||||
Dictionary<ItemLabel, float> TextScales,
|
||||
Dictionary<LightComponent, float> LightRanges,
|
||||
Dictionary<Turret, Vector2> TurretLimits);
|
||||
private TransformToolCommand transformCommand;
|
||||
|
||||
private Widget transformWidget;
|
||||
private Widget TransformWidget
|
||||
{
|
||||
get
|
||||
{
|
||||
if (transformWidget != null) { return transformWidget; }
|
||||
|
||||
int size = GUI.IntScale(16f);
|
||||
transformWidget = new Widget("scale", size, WidgetShape.Rectangle)
|
||||
{
|
||||
Enabled = false,
|
||||
Color = GUIStyle.Yellow,
|
||||
InputAreaMargin = 20,
|
||||
RequireMouseOn = false,
|
||||
TooltipOffset = (size / 2f, -size / 2f),
|
||||
IsFilled = true
|
||||
};
|
||||
transformWidget.PreUpdate += _ =>
|
||||
{
|
||||
transformWidget.Enabled = MapEntity.FilteredSelectedList.Any() && (rotateToolToggle.Selected || scaleToolToggle.Selected);
|
||||
if (transformWidget.IsSelected && PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
if (MapEntity.EditingHUD.GetChild<GUIListBox>() is GUIListBox listBox)
|
||||
{
|
||||
SerializableEntityEditor.LockEditing = true;
|
||||
listBox.Content.Children.OfType<SerializableEntityEditor>().ForEach(editor => editor.RefreshValues());
|
||||
SerializableEntityEditor.LockEditing = false;
|
||||
}
|
||||
Widget.SelectedWidgets.Remove(transformWidget);
|
||||
StoreCommand(transformCommand);
|
||||
transformWidget.Color = Color.Yellow;
|
||||
}
|
||||
};
|
||||
transformWidget.Selected += () =>
|
||||
{
|
||||
transformWidget.Color = GUIStyle.Blue;
|
||||
|
||||
IEnumerable<Item> containedItems = MapEntity.SelectedList.OfType<Item>().SelectManyRecursive(item => item.ContainedItems);
|
||||
IEnumerable<MapEntity> allEntities = MapEntity.SelectedList.Concat(containedItems).Distinct();
|
||||
|
||||
Dictionary<MapEntity, TransformData> oldTransformData = allEntities.ToDictionary(static entity => entity, static entity =>
|
||||
{
|
||||
Item item = entity as Item;
|
||||
Structure structure = entity as Structure;
|
||||
|
||||
float rotation = entity switch
|
||||
{
|
||||
Structure => MathHelper.ToRadians(structure.Rotation),
|
||||
Item => item.RotationRad,
|
||||
_ => 0f
|
||||
};
|
||||
return new TransformData(entity.Scale, rotation, entity.DrawPosition, entity.Rect, structure?.TextureOffset,
|
||||
GetPropertyDict<Wire, (List<Vector2>, float)>(static wire => (wire.GetNodes(), wire.Width)),
|
||||
GetPropertyDict<ItemLabel, float>(static label => label.TextScale),
|
||||
GetPropertyDict<LightComponent, float>(static light => light.Range),
|
||||
GetPropertyDict<Turret, Vector2>(static turret => turret.RotationLimits));
|
||||
|
||||
Dictionary<TComponent, TProperties> GetPropertyDict<TComponent, TProperties>(Func<TComponent, TProperties> propSelector) where TComponent : ItemComponent
|
||||
=> item?.GetComponents<TComponent>().ToDictionary(static comp => comp, propSelector);
|
||||
});
|
||||
|
||||
transformCommand = new TransformToolCommand(oldTransformData, GetSelectionCenter());
|
||||
};
|
||||
transformWidget.MouseHeld += _ =>
|
||||
{
|
||||
MapEntity.DisableSelect = true;
|
||||
|
||||
Vector2 widgetWorldPos = Cam.ScreenToWorld(transformWidget.DrawPos * 2f); // Scale position to account for camera zooming as well.
|
||||
if (MathUtils.NearlyEqual(widgetWorldPos, oldWidgetWorldPos)) { return; }
|
||||
oldWidgetWorldPos = widgetWorldPos;
|
||||
|
||||
transformCommand.RotationRad = null;
|
||||
LocalizedString rotationString = null;
|
||||
if (rotateToolToggle.Selected)
|
||||
{
|
||||
transformCommand.RotationRad = MathUtils.VectorToAngle(PlayerInput.MousePosition - Cam.WorldToScreen(transformCommand.Pivot));
|
||||
rotationString = TextManager.GetWithVariable("SubEditor.TransformWidget.Rotation", "[value]", MathHelper.ToDegrees(transformCommand.RotationRad.Value).ToString("0.000", CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
transformCommand.ScaleMult = null;
|
||||
LocalizedString scaleString = null;
|
||||
if (scaleToolToggle.Selected)
|
||||
{
|
||||
transformCommand.ScaleMult = Math.Clamp(Vector2.Distance(PlayerInput.MousePosition, Cam.WorldToScreen(transformCommand.Pivot)) / (TransformWidgetOffset * GUI.Scale), transformCommand.MinScale, transformCommand.MaxScale);
|
||||
scaleString = TextManager.GetWithVariable("SubEditor.TransformWidget.Scale", "[value]", transformCommand.ScaleMult.Value.ToString("0.000", CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
transformWidget.Tooltip = !rotationString.IsNullOrEmpty() && !scaleString.IsNullOrEmpty()
|
||||
? LocalizedString.Join("\n", rotationString, scaleString)
|
||||
: transformWidget.Tooltip = rotationString ?? scaleString;
|
||||
|
||||
transformCommand.Execute();
|
||||
};
|
||||
transformWidget.PreDraw += (sb, _) =>
|
||||
{
|
||||
Vector2 selectionCenterScreenPos = Cam.WorldToScreen(transformWidget.IsSelected ? transformCommand.Pivot : GetSelectionCenter());
|
||||
|
||||
if (!GameMain.Instance.Paused)
|
||||
{
|
||||
if (transformWidget.IsSelected && scaleToolToggle.Selected)
|
||||
{
|
||||
transformWidget.DrawPos = PlayerInput.MousePosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 dir = transformWidget.IsSelected ? Vector2.Normalize(PlayerInput.MousePosition - Cam.WorldToScreen(transformCommand.Pivot)) : Vector2.UnitX;
|
||||
transformWidget.DrawPos = selectionCenterScreenPos + dir * TransformWidgetOffset * GUI.Scale;
|
||||
}
|
||||
}
|
||||
|
||||
GUI.DrawLine(sb, selectionCenterScreenPos, transformWidget.DrawPos, Color.Black, width: 7f);
|
||||
GUI.DrawLine(sb, selectionCenterScreenPos, transformWidget.DrawPos, Color.Red, width: 3f);
|
||||
};
|
||||
return transformWidget;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public enum WarningType
|
||||
{
|
||||
NoWaypoints,
|
||||
@@ -530,6 +676,16 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
spacing = new GUIFrame(new RectTransform(new Vector2(0.02f, 1.0f), paddedTopPanel.RectTransform), style: null);
|
||||
new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), spacing.RectTransform, Anchor.Center), style: "VerticalLine");
|
||||
|
||||
rotateToolToggle = new GUITickBox(new RectTransform(new Vector2(0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SubEditorRotateToggle")
|
||||
{
|
||||
ToolTip = TextManager.Get("SubEditor.RotateToggleToolTip")
|
||||
};
|
||||
scaleToolToggle = new GUITickBox(new RectTransform(new Vector2(0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SubEditorScaleToggle")
|
||||
{
|
||||
ToolTip = TextManager.Get("SubEditor.ScaleToggleToolTip")
|
||||
};
|
||||
|
||||
var selectedLayerText = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), paddedTopPanel.RectTransform),
|
||||
string.Empty, textAlignment: Alignment.Center);
|
||||
@@ -1099,6 +1255,7 @@ namespace Barotrauma
|
||||
{
|
||||
Submarine.Unload();
|
||||
GameMain.SubEditorScreen.Select();
|
||||
GameMain.GameSession = null;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1449,6 +1606,9 @@ namespace Barotrauma
|
||||
GUI.ForceMouseOn(null);
|
||||
SetMode(Mode.Default);
|
||||
|
||||
rotateToolToggle.Selected = false;
|
||||
scaleToolToggle.Selected = false;
|
||||
|
||||
if (backedUpSubInfo != null)
|
||||
{
|
||||
MainSub = new Submarine(backedUpSubInfo);
|
||||
@@ -1658,6 +1818,9 @@ namespace Barotrauma
|
||||
});
|
||||
|
||||
ClearFilter();
|
||||
|
||||
Widget.SelectedWidgets.Remove(TransformWidget);
|
||||
TransformWidget.Color = Color.Yellow;
|
||||
}
|
||||
|
||||
private void CreateDummyCharacter()
|
||||
@@ -4634,7 +4797,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (itemPrefab.Name.IsNullOrEmpty() || itemPrefab.HideInMenus || itemPrefab.HideInEditors) { continue; }
|
||||
if (!itemPrefab.Tags.Contains(Tags.WireItem)) { continue; }
|
||||
if (CircuitBox.IsInGame() && itemPrefab.Tags.Contains(Tags.Thalamus)) { continue; }
|
||||
if (CircuitBox.IsInGame() && (itemPrefab.Tags.Contains(Tags.Thalamus) || itemPrefab.Tags.Contains("alien")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
wirePrefabs.Add(itemPrefab);
|
||||
}
|
||||
@@ -6219,6 +6385,8 @@ namespace Barotrauma
|
||||
|
||||
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
|
||||
}
|
||||
|
||||
TransformWidget.Update((float)deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -6348,6 +6516,11 @@ namespace Barotrauma
|
||||
}
|
||||
MapEntity.DrawEditor(spriteBatch, cam);
|
||||
|
||||
if (TransformWidget.Enabled)
|
||||
{
|
||||
TransformWidget.Draw(spriteBatch, (float)deltaTime);
|
||||
}
|
||||
|
||||
GUI.Draw(Cam, spriteBatch);
|
||||
|
||||
if (MeasurePositionStart != Vector2.Zero)
|
||||
|
||||
Reference in New Issue
Block a user