Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2025-04-10 10:37:09 -03:00
296 changed files with 8420 additions and 2945 deletions
@@ -632,7 +632,7 @@ namespace Barotrauma
saveFrame.GetChild<GUITextBlock>().TextColor = GUIStyle.Red;
continue;
}
if (docRoot.GetChildElement("multiplayercampaign") != null)
if (docRoot.GetAttributeBool("ismultiplayer", false))
{
//multiplayer campaign save in the wrong folder -> don't show the save
saveList.Content.RemoveChild(saveFrame);
@@ -259,7 +259,7 @@ namespace Barotrauma
{
AutoScaleHorizontal = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.GetLocationTypeToDisplay().Name, font: GUIStyle.SubHeadingFont);
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
portrait.EnsureLazyLoaded();
@@ -1790,7 +1790,7 @@ namespace Barotrauma.CharacterEditor
XElement element = animation.MainElement;
if (element == null) { continue; }
element.SetAttributeValue("type", name);
string fullPath = AnimationParams.GetDefaultFile(name, animation.AnimationType);
string fullPath = AnimationParams.GetDefaultFilePath(name, animation.AnimationType);
element.Name = AnimationParams.GetDefaultFileName(name, animation.AnimationType);
#if DEBUG
element.Save(fullPath);
@@ -1818,7 +1818,7 @@ namespace Barotrauma.CharacterEditor
default: continue;
}
Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid);
string fullPath = AnimationParams.GetDefaultFile(name, animType);
string fullPath = AnimationParams.GetDefaultFilePath(name, animType);
AnimationParams.Create(fullPath, name, animType, type);
}
}
@@ -318,9 +318,15 @@ namespace Barotrauma
graphics.SetRenderTarget(renderTargetDamageable);
graphics.Clear(Color.Transparent);
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
DamageEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, effect: DamageEffect, transformMatrix: cam.Transform);
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
DamageEffect.Parameters["aCutoff"].SetValue(0.0f);
DamageEffect.Parameters["cCutoff"].SetValue(0.0f);
Submarine.DamageEffectCutoff = 0.0f;
DamageEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.End();
sw.Stop();
@@ -22,13 +22,16 @@ namespace Barotrauma
public override Camera Cam { get; }
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private Point prevResolution;
private LevelGenerationParams selectedParams;
private RuinGenerationParams selectedRuinGenerationParams;
private OutpostGenerationParams selectedOutpostGenerationParams;
private LevelObjectPrefab selectedLevelObject;
private BackgroundCreaturePrefab selectedBackgroundCreature;
private GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList;
private GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList, backgroundCreatureList;
private GUIListBox editorContainer;
private GUIButton spriteEditDoneButton;
@@ -69,10 +72,12 @@ namespace Barotrauma
UpdateCaveParamsList();
UpdateOutpostParamsList();
UpdateLevelObjectsList();
UpdateBackgroundCreatureList();
}
private void CreateUI()
{
Frame.ClearChildren();
leftPanel?.ClearChildren();
rightPanel?.ClearChildren();
leftPanel = new GUIFrame(new RectTransform(new Vector2(0.125f, 0.8f), Frame.RectTransform) { MinSize = new Point(150, 0) });
@@ -92,6 +97,7 @@ namespace Barotrauma
currentLevelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
editorContainer.ClearChildren();
SortLevelObjectsList(currentLevelData);
SortBackgroundCreaturesList(currentLevelData);
new SerializableEntityEditor(editorContainer.Content.RectTransform, selectedParams, inGame: false, showName: true, elementHeight: 20, titleFont: GUIStyle.LargeFont);
forceDifficultyInput.FloatValue = (selectedParams.MinLevelDifficulty + selectedParams.MaxLevelDifficulty) / 2f;
return true;
@@ -310,6 +316,7 @@ namespace Barotrauma
currentLevelData.AllowInvalidOutpost = allowInvalidOutpost.Selected;
var dummyLocations = GameSession.CreateDummyLocations(currentLevelData);
Level.Generate(currentLevelData, mirror: mirrorLevel.Selected, startLocation: dummyLocations[0], endLocation: dummyLocations[1]);
UpdateBackgroundCreatureList();
if (Submarine.MainSub != null)
{
@@ -385,9 +392,42 @@ namespace Barotrauma
};
bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.22f), Frame.RectTransform, Anchor.BottomLeft)
{ MaxSize = new Point(GameMain.GraphicsWidth - rightPanel.Rect.Width, 1000) }, style: "GUIFrameBottom");
{ MaxSize = new Point(GameMain.GraphicsWidth - rightPanel.Rect.Width, 1000) }, style: "GUIFrameBottom");
levelObjectList = new GUIListBox(new RectTransform(new Vector2(0.99f, 0.85f), bottomPanel.RectTransform, Anchor.Center))
var bottomPanelContents = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.9f), bottomPanel.RectTransform, Anchor.Center))
{
Stretch = true
};
var bottomPanelButtons = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), bottomPanelContents.RectTransform), isHorizontal: true);
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), bottomPanelButtons.RectTransform), TextManager.Get("leveleditor.levelobjects"), style: "GUITabButton")
{
Selected = true,
OnClicked = (btn, __) =>
{
bottomPanelButtons.Children.ForEach(c => c.Selected = c == btn);
levelObjectList.Visible = true;
levelObjectList.IgnoreLayoutGroups = false;
backgroundCreatureList.Visible = false;
backgroundCreatureList.IgnoreLayoutGroups = true;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), bottomPanelButtons.RectTransform), TextManager.Get("leveleditor.backgroundcreatures"), style: "GUITabButton")
{
OnClicked = (btn, __) =>
{
bottomPanelButtons.Children.ForEach(c => c.Selected = c == btn);
backgroundCreatureList.Visible = true;
backgroundCreatureList.IgnoreLayoutGroups = false;
levelObjectList.Visible = false;
levelObjectList.IgnoreLayoutGroups = true;
return true;
}
};
bottomPanelButtons.RectTransform.NonScaledSize = new Point(bottomPanelButtons.Rect.Width, bottomPanelButtons.Children.First().Rect.Height);
levelObjectList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), bottomPanelContents.RectTransform))
{
PlaySoundOnSelect = true,
UseGridLayout = true
@@ -399,6 +439,20 @@ namespace Barotrauma
return true;
};
backgroundCreatureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), bottomPanelContents.RectTransform))
{
PlaySoundOnSelect = true,
UseGridLayout = true,
Visible = false,
IgnoreLayoutGroups = true
};
backgroundCreatureList.OnSelected += (GUIComponent component, object obj) =>
{
selectedBackgroundCreature = obj as BackgroundCreaturePrefab;
CreateBackgroundCreatureEditor(selectedBackgroundCreature);
return true;
};
spriteEditDoneButton = new GUIButton(new RectTransform(new Point(200, 30), anchor: Anchor.BottomRight) { AbsoluteOffset = new Point(20, 20) },
TextManager.Get("leveleditor.spriteeditdone"))
{
@@ -411,6 +465,8 @@ namespace Barotrauma
topPanel = new GUIFrame(new RectTransform(new Point(400, 100), GUI.Canvas)
{ RelativeOffset = new Vector2(leftPanel.RectTransform.RelativeSize.X * 2, 0.0f) }, style: "GUIFrameTop");
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
public LevelEditorScreen()
@@ -585,6 +641,44 @@ namespace Barotrauma
}
}
private void UpdateBackgroundCreatureList()
{
editorContainer.ClearChildren();
backgroundCreatureList.Content.ClearChildren();
int objectsPerRow = (int)Math.Ceiling(backgroundCreatureList.Content.Rect.Width / Math.Max(100 * GUI.Scale, 100));
float relWidth = 1.0f / objectsPerRow;
foreach (BackgroundCreaturePrefab backgroundCreaturePrefab in BackgroundCreaturePrefab.Prefabs)
{
var frame = new GUIFrame(new RectTransform(
new Vector2(relWidth, relWidth * ((float)backgroundCreatureList.Content.Rect.Width / backgroundCreatureList.Content.Rect.Height)),
backgroundCreatureList.Content.RectTransform)
{ MinSize = new Point(0, 60) }, style: "ListBoxElementSquare")
{
UserData = backgroundCreaturePrefab
};
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
text: ToolBox.LimitString(backgroundCreaturePrefab.Name, GUIStyle.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
{
CanBeFocused = false,
ToolTip = backgroundCreaturePrefab.Name
};
Sprite sprite = backgroundCreaturePrefab.Sprite ?? backgroundCreaturePrefab.DeformableSprite?.Sprite;
new GUIImage(new RectTransform(new Point(paddedFrame.Rect.Height, paddedFrame.Rect.Height - textBlock.Rect.Height),
paddedFrame.RectTransform, Anchor.TopCenter), sprite, scaleToFit: true)
{
LoadAsynchronously = true,
CanBeFocused = false
};
}
SortBackgroundCreaturesList(currentLevelData);
}
private void CreateCaveParamsEditor(CaveGenerationParams caveGenerationParams)
{
editorContainer.ClearChildren();
@@ -913,6 +1007,99 @@ namespace Barotrauma
});
}
private void SortBackgroundCreaturesList(LevelData levelData)
{
if (levelData == null) { return; }
//fade out levelobjects that don't spawn in this type of level
foreach (GUIComponent child in backgroundCreatureList.Content.Children)
{
if (child.UserData is not BackgroundCreaturePrefab creature) { continue; }
SetElementColorBasedOnCommonness(child, creature.GetCommonness(levelData));
}
//sort the levelobjects according to commonness in this level
backgroundCreatureList.Content.RectTransform.SortChildren((c1, c2) =>
{
var creature1 = c1.GUIComponent.UserData as BackgroundCreaturePrefab;
var creature2 = c2.GUIComponent.UserData as BackgroundCreaturePrefab;
return Math.Sign(creature2.GetCommonness(levelData) - creature1.GetCommonness(levelData));
});
}
private static void SetElementColorBasedOnCommonness(GUIComponent element, float commonness)
{
element.Color = commonness > 0.0f ? GUIStyle.Green * 0.4f : Color.Transparent;
element.SelectedColor = commonness > 0.0f ? GUIStyle.Green * 0.6f : Color.White * 0.5f;
element.HoverColor = commonness > 0.0f ? GUIStyle.Green * 0.7f : Color.White * 0.6f;
element.GetAnyChild<GUIImage>().Color = commonness > 0.0f ? Color.White : Color.DarkGray;
if (commonness <= 0.0f)
{
element.GetAnyChild<GUITextBlock>().TextColor = Color.DarkGray;
}
}
private void CreateBackgroundCreatureEditor(BackgroundCreaturePrefab backgroundCreaturePrefab)
{
editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, backgroundCreaturePrefab, false, true, elementHeight: 20, titleFont: GUIStyle.LargeFont);
if (selectedParams != null)
{
List<Identifier> availableIdentifiers = new List<Identifier>() { selectedParams.Identifier };
foreach (Identifier paramsId in availableIdentifiers)
{
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = 5,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), commonnessContainer.RectTransform),
TextManager.GetWithVariable("leveleditor.levelobjcommonness", "[leveltype]", paramsId.Value), textAlignment: Alignment.Center);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 0.4f), commonnessContainer.RectTransform), NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = backgroundCreaturePrefab.GetCommonness(currentLevelData),
OnValueChanged = (numberInput) =>
{
backgroundCreaturePrefab.OverrideCommonness[paramsId] = numberInput.FloatValue;
}
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1);
}
}
Sprite sprite = backgroundCreaturePrefab.Sprite ?? backgroundCreaturePrefab.DeformableSprite?.Sprite;
if (sprite != null)
{
editor.AddCustomContent(new GUIButton(new RectTransform(new Point(editor.Rect.Width / 2, (int)(25 * GUI.Scale))) { IsFixedSize = true },
TextManager.Get("leveleditor.editsprite"))
{
OnClicked = (btn, userdata) =>
{
editingSprite = sprite;
GameMain.SpriteEditorScreen.SelectSprite(editingSprite);
return true;
}
}, 1);
}
if (backgroundCreaturePrefab.DeformableSprite != null)
{
var deformEditor = backgroundCreaturePrefab.DeformableSprite.CreateEditor(editor, backgroundCreaturePrefab.SpriteDeformations, backgroundCreaturePrefab.Name);
deformEditor.GetChild<GUIDropDown>().OnSelected += (selected, userdata) =>
{
CreateBackgroundCreatureEditor(backgroundCreaturePrefab);
return true;
};
editor.AddCustomContent(deformEditor, editor.ContentCount);
}
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
@@ -1083,6 +1270,11 @@ namespace Barotrauma
public override void Update(double deltaTime)
{
if (GameMain.GraphicsWidth != prevResolution.X || GameMain.GraphicsHeight != prevResolution.Y)
{
RefreshUI(forceCreate: true);
}
if (lightingEnabled.Selected)
{
foreach (Item item in Item.ItemList)
@@ -23,6 +23,8 @@ namespace Barotrauma
{
sealed class MainMenuScreen : Screen
{
public static HashSet<Identifier> DismissedNotifications = new HashSet<Identifier>();
private enum Tab
{
NewGame = 0,
@@ -512,6 +514,22 @@ namespace Barotrauma
return true;
}
};
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(40, 230) },
"Local MP Quickstart", style: "GUIButtonLarge", color: GUIStyle.Red)
{
IgnoreLayoutGroups = true,
UserData = Tab.Empty,
ToolTip = "Starts a server and another client and connects both to localhost, using names 'client1' and 'client2'.",
OnClicked = (tb, userdata) =>
{
SelectTab(tb, userdata);
DebugConsole.StartLocalMPSession(numClients: 2);
return true;
}
};
#endif
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(40, 50) },
$"Open LuaCs Settings", style: "MainMenuGUIButton", color: GUIStyle.Red)
@@ -590,6 +608,12 @@ namespace Barotrauma
SelectTab(Tab.Empty);
}
public static void AddDismissedNotification(Identifier id)
{
DismissedNotifications.Add(id);
GameSettings.SaveCurrentConfig();
}
private void SetMenuTabPositioning()
{
foreach (GUIFrame menuTab in menuTabs.Values)
@@ -618,7 +642,7 @@ namespace Barotrauma
};
var tutorialPreview = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), tutorialContent.RectTransform)) { RelativeSpacing = 0.05f, Stretch = true };
var imageContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style: "InnerFrame");
tutorialBanner = new GUIImage(new RectTransform(Vector2.One, imageContainer.RectTransform), style: null, scaleToFit: true);
tutorialBanner = new GUIImage(new RectTransform(Vector2.One, imageContainer.RectTransform), style: null, scaleToFit: GUIImage.ScalingMode.ScaleToFitSmallestExtent);
var infoContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style: "GUIFrameListBox");
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), infoContainer.RectTransform, Anchor.Center), childAnchor: Anchor.TopLeft)
@@ -949,6 +973,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Failed to find the job \"" + job + "\"!");
}
gamesession.CrewManager.AddCharacterInfo(characterInfo);
characterInfo.SetNameBasedOnJob();
}
gamesession.CrewManager.InitSinglePlayerRound();
}
@@ -1320,11 +1345,9 @@ namespace Barotrauma
catch (Exception e)
{
DebugConsole.ThrowError("Loading save \"" + path + "\" failed", e);
GameMain.GameSession = null;
return;
}
//TODO
//GameMain.LobbyScreen.Select();
}
#region UI Methods
@@ -1357,7 +1380,7 @@ namespace Barotrauma
var serverSettings = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile, out _)?.Root ?? new XElement("serversettings");
var name = serverSettings.GetAttributeString("name", "");
var name = serverSettings.GetAttributeString(nameof(ServerSettings.ServerName), "");
var password = serverSettings.GetAttributeString("password", "");
var isPublic = serverSettings.GetAttributeBool("IsPublic", true);
var banAfterWrongPassword = serverSettings.GetAttributeBool("banafterwrongpassword", false);
@@ -1391,7 +1414,7 @@ namespace Barotrauma
var playstyleContainer = new GUIFrame(new RectTransform(new Vector2(1.35f, 0.1f), parent.RectTransform), style: null, color: Color.Black);
playstyleBanner = new GUIImage(new RectTransform(new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform),
GUIStyle.GetComponentStyle($"PlayStyleBanner.{PlayStyle.Serious}").GetSprite(GUIComponent.ComponentState.None), scaleToFit: true)
GUIStyle.GetComponentStyle($"PlayStyleBanner.{PlayStyle.Serious}").GetSprite(GUIComponent.ComponentState.None), scaleToFit: GUIImage.ScalingMode.ScaleToFitSmallestExtent)
{
UserData = PlayStyle.Serious
};
@@ -67,8 +67,9 @@ namespace Barotrauma
public GUIButton ServerMessageButton { get; private set; }
public static GUIButton JobInfoFrame { get; set; }
private GUITickBox spectateBox;
private GUITickBox spectateBox, afkBox;
public bool Spectating => spectateBox is { Selected: true, Visible: true };
public bool AFKSelected => afkBox is { Selected: true, Visible: true };
public bool PermadeathMode => GameMain.Client?.ServerSettings?.RespawnMode == RespawnMode.Permadeath;
public bool PermanentlyDead => campaignCharacterInfo?.PermanentlyDead ?? false;
@@ -112,6 +113,8 @@ namespace Barotrauma
private readonly List<GUIComponent> respawnSettings = new();
public CharacterInfo.AppearanceCustomizationMenu CharacterAppearanceCustomizationMenu { get; set; }
private Point prevResolutionForJobSelectionFrame;
public GUIFrame JobSelectionFrame { get; private set; }
public GUIFrame JobPreferenceContainer { get; private set; }
@@ -209,6 +212,7 @@ namespace Barotrauma
private CharacterTeamType TeamPreference => SelectedMode == GameModePreset.PvP ? MultiplayerPreferences.Instance.TeamPreference : CharacterTeamType.Team1;
public GUIButton StartButton { get; private set; }
public GUIButton EndButton { get; private set; }
public GUITickBox ReadyToStartBox { get; private set; }
@@ -224,7 +228,7 @@ namespace Barotrauma
public bool UsingShuttle
{
get { return shuttleTickBox.Selected && !PermadeathMode; }
get { return shuttleTickBox.Selected; }
set { shuttleTickBox.Selected = value; }
}
@@ -473,10 +477,20 @@ namespace Barotrauma
AbsoluteSpacing = GUI.IntScale(5)
};
Favorite = new GUITickBox(new RectTransform(new Vector2(0.5f, 0.5f), serverInfoContent.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothHeight),
var topRightContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.5f), serverInfoContent.RectTransform, Anchor.TopRight),
isHorizontal: true, childAnchor: Anchor.TopRight)
{
AbsoluteSpacing = GUI.IntScale(5),
CanBeFocused = true
};
SettingsButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), topRightContainer.RectTransform, Anchor.TopRight),
TextManager.Get("ServerSettingsButton"), style: "GUIButtonFreeScale");
Favorite = new GUITickBox(new RectTransform(Vector2.One, topRightContainer.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothHeight),
"", null, "GUIServerListFavoriteTickBox")
{
IgnoreLayoutGroups = true,
Selected = false,
ToolTip = TextManager.Get("addtofavorites"),
OnSelected = (tickbox) =>
@@ -496,8 +510,6 @@ namespace Barotrauma
}
};
SettingsButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.4f), serverInfoContent.RectTransform, Anchor.TopRight),
TextManager.Get("ServerSettingsButton"), style: "GUIButtonFreeScale");
}
private void CreateServerMessagePopup(string serverName, string message)
@@ -698,11 +710,11 @@ namespace Barotrauma
if (GameMain.Client == null) { return false; }
if (GameMain.Client.GameStarted)
{
GameMain.Client.RequestRoundEnd(save: false);
GameMain.Client.RequestEndRound(save: false);
}
else
{
GameMain.Client.RequestRoundEnd(save: false, quitCampaign: true);
GameMain.Client.RequestEndRound(save: false, quitCampaign: true);
}
return true;
}
@@ -1216,7 +1228,7 @@ namespace Barotrauma
shuttleTickBox = new GUITickBox(new RectTransform(Vector2.One, shuttleHolder.RectTransform), TextManager.Get("RespawnShuttle"))
{
ToolTip = TextManager.Get("RespawnShuttleExplanation"),
Selected = !PermadeathMode,
Selected = true,
OnSelected = (GUITickBox box) =>
{
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
@@ -1241,7 +1253,10 @@ namespace Barotrauma
{
OnSelected = (component, obj) =>
{
SelectShuttle((SubmarineInfo)obj);
SubmarineInfo subInfo = (SubmarineInfo)obj;
ShuttleList.Text = subInfo.DisplayName;
ShuttleList.ToolTip = subInfo.Description;
SelectShuttle(subInfo);
return true;
}
};
@@ -1539,8 +1554,8 @@ namespace Barotrauma
foreach (var disembarkPerkPrefab in DisembarkPerkPrefab.Prefabs
.OrderBy(static p => p.SortCategory)
.ThenBy(static p => p.Cost)
.ThenBy(static p => p.SortKey))
.ThenBy(static p => p.SortKey)
.ThenBy(static p => p.Cost))
{
if (disembarkPerkCategory != disembarkPerkPrefab.SortCategory)
{
@@ -1888,13 +1903,25 @@ namespace Barotrauma
Stretch = true
};
spectateBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 0.06f), myCharacterContent.RectTransform),
var checkBoxContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), myCharacterContent.RectTransform), isHorizontal: true)
{
Stretch = true
};
spectateBox = new GUITickBox(new RectTransform(new Vector2(0.6f, 1.0f), checkBoxContainer.RectTransform),
TextManager.Get("spectatebutton"))
{
Selected = false,
OnSelected = ToggleSpectate,
UserData = "spectate"
};
afkBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 1.0f), checkBoxContainer.RectTransform),
TextManager.Get("afkbutton"))
{
Selected = false,
ToolTip = TextManager.Get("afkbutton.tooltip")
};
checkBoxContainer.RectTransform.MinSize = new Point(0, spectateBox.RectTransform.MinSize.Y);
playerInfoContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), myCharacterContent.RectTransform))
{
@@ -2125,6 +2152,25 @@ namespace Barotrauma
joinOnGoingRoundButton = new GUIButton(new RectTransform(Vector2.One, roundControlsHolder.RectTransform),
TextManager.Get("ServerListJoin"));
EndButton = new GUIButton(new RectTransform(Vector2.One, roundControlsHolder.RectTransform),
TextManager.Get("endround"))
{
//spooky red color for a destructive action
Color = GUIStyle.Red,
OnClicked = (btn, obj) =>
{
if (GameMain.Client == null) { return true; }
GUI.CreateVerificationPrompt(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
() =>
{
GameMain.Client?.RequestEndRound(save: false);
});
return true;
},
Visible = false,
IgnoreLayoutGroups = true
};
// Start button
StartButton = new GUIButton(new RectTransform(Vector2.One, roundControlsHolder.RectTransform),
TextManager.Get("StartGameButton"))
@@ -2132,6 +2178,14 @@ namespace Barotrauma
OnClicked = (btn, obj) =>
{
if (GameMain.Client == null) { return true; }
//the player presumably no longer wants to be afk if they clicked the start button
if (afkBox.Selected)
{
afkBox.Flash(GUIStyle.Green);
}
afkBox.Selected = false;
if (CampaignSetupFrame.Visible && CampaignSetupUI != null)
{
CampaignSetupUI.StartGameClicked(btn, obj);
@@ -2146,6 +2200,7 @@ namespace Barotrauma
}
};
clientHiddenElements.Add(StartButton);
bottomBar.RectTransform.MinSize =
new Point(0, (int)Math.Max(ReadyToStartBox.RectTransform.MinSize.Y / 0.75f, StartButton.RectTransform.MinSize.Y));
@@ -2244,6 +2299,9 @@ namespace Barotrauma
RefreshEnabledElements();
createPendingChangesText = false;
TabMenu.PendingChanges = false;
if (GameMain.Client != null)
{
joinOnGoingRoundButton.Visible = GameMain.Client.GameStarted;
@@ -2258,8 +2316,18 @@ namespace Barotrauma
if (GameMain.Client != null)
{
afkBox.Visible = !GameMain.Client.IsServerOwner && GameMain.Client.ServerSettings.AllowAFK;
GameMain.Client.Voting.ResetVotes(GameMain.Client.ConnectedClients);
joinOnGoingRoundButton.OnClicked = GameMain.Client.JoinOnGoingClicked;
joinOnGoingRoundButton.OnClicked = (btn, userdata) =>
{
if (afkBox is { Selected: true })
{
afkBox.Selected = false;
afkBox.Flash(GUIStyle.Green);
}
GameMain.Client.SendJoinOngoingRequest(btn);
return true;
};
ReadyToStartBox.OnSelected = GameMain.Client.SetReadyToStart;
}
@@ -2293,7 +2361,7 @@ namespace Barotrauma
traitorElements.ForEach(e => e.Enabled &= settings.TraitorProbability > 0);
SetTraitorDangerIndicators(settings.TraitorDangerLevel);
respawnModeSelection.Enabled = respawnModeLabel.Enabled = manageSettings && !gameStarted;
midRoundRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode == RespawnMode.MidRound);
midRoundRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode != RespawnMode.BetweenRounds);
permadeathDisabledRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode != RespawnMode.Permadeath);
permadeathEnabledRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode == RespawnMode.Permadeath && !gameStarted);
ironmanDisabledRespawnSettings.ForEach(e => e.Enabled &= !settings.IronmanMode);
@@ -2366,6 +2434,7 @@ namespace Barotrauma
if (campaignCharacterInfo != newCampaignCharacterInfo)
{
campaignCharacterInfo = newCampaignCharacterInfo;
SaveAppearance();
UpdatePlayerFrame(campaignCharacterInfo, false);
}
}
@@ -2391,7 +2460,7 @@ namespace Barotrauma
createPendingText: createPendingText);
}
private void UpdatePlayerFrame(CharacterInfo characterInfo, bool allowEditing, GUIComponent parent, bool createPendingText = true)
private void UpdatePlayerFrame(CharacterInfo characterInfo, bool allowEditing, GUIComponent parent, bool createPendingText = false)
{
if (GameMain.Client == null) { return; }
@@ -2402,7 +2471,7 @@ namespace Barotrauma
if (characterInfo == null || CampaignCharacterDiscarded)
{
characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, GameMain.Client.Name, null);
characterInfo.RecreateHead(MultiplayerPreferences.Instance);
characterInfo.RecreateHead(MultiplayerPreferences.Instance); // not necessarily the head of the last character
GameMain.Client.CharacterInfo = characterInfo;
characterInfo.OmitJobInMenus = true;
}
@@ -2824,8 +2893,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.GetWithVariable("startingequipmentname", "[number]", (variant + 1).ToString()), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center);
var itemIdentifiers = jobPrefab.JobItems[variant]
.Where(it => it.ShowPreview)
var itemIdentifiers = jobPrefab.GetJobItems(variant, it => it.ShowPreview)
.Select(it => it.GetItemIdentifier(team, isPvPMode))
.Distinct();
@@ -2958,6 +3026,16 @@ namespace Barotrauma
spectateBox.Visible = allowSpectating;
}
public void SetAllowAFK(bool allowAFK)
{
if (afkBox.Visible != allowAFK)
{
//reset selection when the AFK option becomes available or unavailable
afkBox.Selected = false;
afkBox.Visible = allowAFK;
}
}
public void SetAutoRestart(bool enabled, float timer = 0.0f)
{
autoRestartBox.Selected = enabled;
@@ -3059,20 +3137,26 @@ namespace Barotrauma
var subTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), frameLayout.RectTransform, Anchor.CenterLeft),
ToolBox.LimitString(sub.DisplayName.Value, GUIStyle.Font, subList.Rect.Width - 65), textAlignment: Alignment.CenterLeft)
{
ToolTip = sub.Description,
UserData = "nametext",
CanBeFocused = true
};
var pvpContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 1f), frameLayout.RectTransform, Anchor.CenterRight), style: null);
var pvpContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 1f), frameLayout.RectTransform, Anchor.CenterRight), style: null)
{
CanBeFocused = false
};
var coalitionIcon = new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), pvpContainer.RectTransform, Anchor.CenterLeft), style: "CoalitionIcon")
{
Visible = false,
UserData = CoalitionIconUserData,
CanBeFocused = false
};
var separatistsIcon = new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), pvpContainer.RectTransform, Anchor.CenterRight), style: "SeparatistIcon")
{
Visible = false,
UserData = SeparatistsIconUserData,
CanBeFocused = false
};
var matchingSub =
@@ -3973,7 +4057,7 @@ namespace Barotrauma
JobInfoFrame?.AddToGUIUpdateList();
CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
JobSelectionFrame?.AddToGUIUpdateList();
JobSelectionFrame?.AddToGUIUpdateList(order: 1);
}
public override void Update(double deltaTime)
@@ -4119,10 +4203,10 @@ namespace Barotrauma
}
}
private static void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, JobVariant jobPrefab, CharacterTeamType team, bool isPvPMode, int itemsPerRow)
private static void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, JobVariant jobVariant, CharacterTeamType team, bool isPvPMode, int itemsPerRow)
{
var itemIdentifiers = jobPrefab.Prefab.JobItems[jobPrefab.Variant]
.Where(it => it.ShowPreview)
var allJobItems = jobVariant.Prefab.GetJobItems(jobVariant.Variant, it => it.ShowPreview);
var itemIdentifiers = allJobItems
.Select(it => it.GetItemIdentifier(team, isPvPMode))
.Distinct();
@@ -4162,7 +4246,7 @@ namespace Barotrauma
float iconScale = Math.Min(Math.Min(slotSize.X / icon.size.X, slotSize.Y / icon.size.Y), 2.0f) * 0.9f;
icon.Draw(spriteBatch, slotPos + slotSize.ToVector2() * 0.5f, scale: iconScale);
int count = jobPrefab.Prefab.JobItems[jobPrefab.Variant].Where(it => it.ShowPreview && it.ItemIdentifier == itemIdentifier).Sum(it => it.Amount);
int count = allJobItems.Where(it => it.GetItemIdentifier(team, isPvPMode) == itemIdentifier).Sum(it => it.Amount);
if (count > 1)
{
string itemCountText = "x" + count;
@@ -4352,57 +4436,103 @@ namespace Barotrauma
private bool OpenJobSelection(GUIComponent _, object __)
{
//recreate if resolution has changed
if (GameMain.GraphicsWidth != prevResolutionForJobSelectionFrame.X ||
GameMain.GraphicsHeight != prevResolutionForJobSelectionFrame.Y)
{
JobSelectionFrame = null;
}
prevResolutionForJobSelectionFrame = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
if (JobSelectionFrame != null)
{
JobSelectionFrame.Visible = true;
return true;
}
Point frameSize = new Point(characterInfoFrame.Rect.Width, (int)(characterInfoFrame.Rect.Height * 2 * 0.6f));
JobSelectionFrame = new GUIFrame(new RectTransform(frameSize, GUI.Canvas, Anchor.TopLeft)
{ AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - frameSize.X, characterInfoFrame.Rect.Bottom) }, style:"GUIFrameListBox");
var allJobs = JobPrefab.Prefabs.Where(jobPrefab => !jobPrefab.HiddenJob && jobPrefab.MaxNumber > 0);
//find the jobs that aren't currently visible in the job list, create a preview of the first variant
var availableJobs =
allJobs.Where(jobPrefab => JobList.Content.Children.All(c => c.UserData is not JobVariant prefab || prefab.Prefab != jobPrefab))
.Select(j => new JobVariant(j, 0));
//find the jobs that are currently visible in the job list, create a preview of the variant chosen in the list
availableJobs = availableJobs.Concat(
allJobs.Where(jobPrefab => JobList.Content.Children.Any(c => (c.UserData is JobVariant prefab) && prefab.Prefab == jobPrefab))
.Select(j => (JobVariant)JobList.Content.FindChild(c => (c.UserData is JobVariant prefab) && prefab.Prefab == j).UserData));
availableJobs = availableJobs.ToList();
const int JobsPerRow = 3;
const int MaxRows = 4;
int rowCount = (int)Math.Ceiling(availableJobs.Count() / (float)JobsPerRow);
int jobButtonSize = GUI.IntScale(150);
const float listBoxRelativeSize = 0.95f;
Point frameSize = new Point(characterInfoFrame.Rect.Width, (int)(jobButtonSize * Math.Min(rowCount, MaxRows) / listBoxRelativeSize));
JobSelectionFrame = new GUIFrame(new RectTransform(frameSize, GUI.Canvas, Anchor.TopLeft), style: "GUIFrameListBox");
PositionJobSelectionFrame();
characterInfoFrame.RectTransform.SizeChanged += () =>
{
if (characterInfoFrame == null || JobSelectionFrame?.RectTransform == null) { return; }
Point size = new Point(characterInfoFrame.Rect.Width, (int)(characterInfoFrame.Rect.Height * 2 * 0.6f));
Point size = new Point(characterInfoFrame.Rect.Width, (int)(jobButtonSize * Math.Min(rowCount, MaxRows) / listBoxRelativeSize));
JobSelectionFrame.RectTransform.Resize(size);
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - size.X, characterInfoFrame.Rect.Bottom);
PositionJobSelectionFrame();
};
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 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);
}
}
}
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 rows = new GUILayoutGroup(new RectTransform(Vector2.One, JobSelectionFrame.RectTransform)) { Stretch = true };
var row = new GUILayoutGroup(new RectTransform(Vector2.One, rows.RectTransform), true);
var jobSelectionList = new GUIListBox(new RectTransform(Vector2.One * listBoxRelativeSize, JobSelectionFrame.RectTransform, Anchor.Center), style: "GUIFrameListBox")
{
Padding = Vector4.One * GUI.IntScale(10)
};
var row = new GUILayoutGroup(new RectTransform(new Point(jobSelectionList.Content.Rect.Width, jobButtonSize), jobSelectionList.Content.RectTransform), isHorizontal: true)
{
Stretch = true
};
GUIButton jobButton = null;
var availableJobs = JobPrefab.Prefabs.Where(jobPrefab =>
!jobPrefab.HiddenJob && jobPrefab.MaxNumber > 0 && JobList.Content.Children.All(c => c.UserData is not JobVariant prefab || prefab.Prefab != jobPrefab)
).Select(j => new JobVariant(j, 0));
availableJobs = availableJobs.Concat(
JobPrefab.Prefabs.Where(jobPrefab =>
!jobPrefab.HiddenJob && jobPrefab.MaxNumber > 0 && JobList.Content.Children.Any(c => (c.UserData is JobVariant prefab) && prefab.Prefab == jobPrefab)
).Select(j => (JobVariant)JobList.Content.FindChild(c => (c.UserData is JobVariant prefab) && prefab.Prefab == j).UserData));
availableJobs = availableJobs.ToList();
int itemsInRow = 0;
foreach (var jobPrefab in availableJobs)
{
if (itemsInRow >= 3)
if (itemsInRow >= JobsPerRow)
{
row = new GUILayoutGroup(new RectTransform(Vector2.One, rows.RectTransform), true);
row = new GUILayoutGroup(new RectTransform(new Point(jobSelectionList.Content.Rect.Width, jobButtonSize), jobSelectionList.Content.RectTransform), isHorizontal: true)
{
Stretch = true
};
itemsInRow = 0;
}
jobButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), row.RectTransform), style: "ListBoxElementSquare")
jobButton = new GUIButton(new RectTransform(new Point(jobButtonSize), row.RectTransform), style: "ListBoxElementSquare")
{
UserData = jobPrefab,
OnClicked = (btn, usdt) =>
@@ -4725,6 +4855,7 @@ namespace Barotrauma
public void RefreshStartButtonVisibility()
{
bool campaignActive = GameMain.GameSession?.GameMode is CampaignMode;
if (CampaignSetupUI != null && CampaignSetupFrame is { Visible: true })
{
//setting up a campaign -> start button only visible if we're in the "new game" tab (load game menu not visible)
@@ -4736,7 +4867,6 @@ namespace Barotrauma
else
{
//if a campaign is currently running, we must show the start button to allow continuing
bool campaignActive = GameMain.GameSession?.GameMode is CampaignMode;
StartButton.Visible =
(SelectedMode != GameModePreset.MultiPlayerCampaign || campaignActive) &&
!GameMain.Client.GameStarted && GameMain.Client.HasPermission(ClientPermissions.ManageRound);
@@ -4752,6 +4882,14 @@ namespace Barotrauma
? TextManager.Get("DisembarkPointsNotValid")
: string.Empty;
}
StartButton.IgnoreLayoutGroups = !StartButton.Visible;
//can end the round if round is running
EndButton.Visible =
!StartButton.Visible &&
GameMain.Client is { GameStarted: true } &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) || (campaignActive && GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)));
EndButton.IgnoreLayoutGroups = !EndButton.Visible;
}
public void RefreshChatrow()
@@ -4999,7 +5137,7 @@ namespace Barotrauma
private static GUIButton CreateJobVariantButton(JobVariant jobPrefab, int variantIndex, int variantCount, GUIComponent slot)
{
float relativeSize = 0.15f;
float relativeSize = 0.18f;
var btn = new GUIButton(new RectTransform(new Vector2(relativeSize), slot.RectTransform, Anchor.TopCenter, scaleBasis: ScaleBasis.BothHeight)
{ RelativeOffset = new Vector2(relativeSize * 1.3f * (variantIndex - (variantCount - 1) / 2.0f), 0.02f) },
@@ -5375,6 +5513,7 @@ namespace Barotrauma
text: sub.DisplayName)
{
UserData = "nametext",
ToolTip = sub.Description,
CanBeFocused = false
};
@@ -2568,9 +2568,34 @@ namespace Barotrauma
{
outpostTagsBox.Text = MainSub.Info.OutpostTags.ConvertToString();
}
outpostTagsGroup.RectTransform.MaxSize = outpostTagsBox.RectTransform.MaxSize;
var triggerMissionTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), outpostSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), triggerMissionTagsGroup.RectTransform),
TextManager.Get("outpost.triggeroutpostmissionevents"), textAlignment: Alignment.CenterLeft, wrap: true);
var triggerMissionTagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), triggerMissionTagsGroup.RectTransform))
{
OnEnterPressed = (GUITextBox textBox, string text) =>
{
MainSub.Info.TriggerOutpostMissionEvents = text.ToIdentifiers().ToImmutableHashSet();
return true;
},
ToolTip = TextManager.Get("outpost.triggeroutpostmissionevents.tooltip"),
OverflowClip = true,
Text = "default"
};
triggerMissionTagsBox.OnDeselected += (textbox, _) =>
{
MainSub.Info.TriggerOutpostMissionEvents = triggerMissionTagsBox.Text.ToIdentifiers().ToImmutableHashSet();
};
if (MainSub.Info.TriggerOutpostMissionEvents != null)
{
triggerMissionTagsBox.Text = MainSub.Info.TriggerOutpostMissionEvents.ConvertToString();
}
triggerMissionTagsGroup.RectTransform.MaxSize = triggerMissionTagsBox.RectTransform.MaxSize;
//---------------------------------------
var enemySubmarineSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
@@ -5794,12 +5819,14 @@ namespace Barotrauma
{
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
{
lightComponent.Light.Color =
(item.body == null || item.body.Enabled || item.ParentInventory is ItemInventory { Container.HideItems: false }) &&
bool visibleInContainer = item.FindParentInventory(static it => it is ItemInventory { Container.HideItems: true }) == null;
lightComponent.Light.Color =
((item.body == null || !item.body.Enabled) && !visibleInContainer) ||
/*the light is only visible when worn -> can't be visible in the editor*/
lightComponent.Parent is not Wearable ?
lightComponent.LightColor :
Color.Transparent;
lightComponent.Parent is Wearable ?
Color.Transparent :
lightComponent.LightColor;
lightComponent.Light.LightSpriteEffect = lightComponent.Item.SpriteEffects;
}
}