Unstable 1.8.4.0
This commit is contained in:
@@ -18,17 +18,40 @@ namespace Barotrauma
|
||||
public readonly ChatManager ChatManager = new ChatManager();
|
||||
|
||||
public bool IsSinglePlayer { get; private set; }
|
||||
|
||||
|
||||
private bool _toggleOpen = true;
|
||||
public bool ToggleOpen
|
||||
{
|
||||
get { return _toggleOpen; }
|
||||
set
|
||||
{
|
||||
_toggleOpen = PreferChatBoxOpen = value;
|
||||
if (value) { hideableElements.Visible = true; }
|
||||
}
|
||||
get => _toggleOpen;
|
||||
set => SetToggleOpenState(value, setPreference: true);
|
||||
}
|
||||
|
||||
public static ChatBox GetChatBox()
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is not GameMode gameMode) { return null; }
|
||||
return gameMode.IsSinglePlayer ? GameMain.GameSession.CrewManager?.ChatBox : GameMain.Client?.ChatBox;
|
||||
}
|
||||
|
||||
public static void AutoHideChatBox() => SetChatBoxOpen(false);
|
||||
|
||||
private void SetToggleOpenState(bool value, bool setPreference = true)
|
||||
{
|
||||
_toggleOpen = value;
|
||||
if (setPreference)
|
||||
{
|
||||
PreferChatBoxOpen = value;
|
||||
}
|
||||
if (value) { hideableElements.Visible = true; }
|
||||
}
|
||||
|
||||
public static void ResetChatBoxOpenState() => GetChatBox()?.ResetOpenState();
|
||||
|
||||
public void ResetOpenState() => SetOpen(PreferChatBoxOpen);
|
||||
|
||||
private static void SetChatBoxOpen(bool isOpen) => GetChatBox()?.SetOpen(isOpen);
|
||||
|
||||
private void SetOpen(bool value) => SetToggleOpenState(value, setPreference: false);
|
||||
|
||||
private float openState;
|
||||
|
||||
public static bool PreferChatBoxOpen = true;
|
||||
@@ -199,7 +222,7 @@ namespace Barotrauma
|
||||
if (channelMemPending)
|
||||
{
|
||||
int.TryParse(channelText.Text, out int newChannel);
|
||||
radio.SetChannelMemory(index, newChannel);
|
||||
SetChannelMemory(index, newChannel);
|
||||
btn.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
|
||||
("[index]", index.ToString()),
|
||||
("[channel]", radio.GetChannelMemory(index).ToString()));
|
||||
@@ -330,7 +353,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
showNewMessagesButton.Visible = false;
|
||||
ToggleOpen = PreferChatBoxOpen = GameSettings.CurrentConfig.ChatOpen;
|
||||
SetToggleOpenState(GameSettings.CurrentConfig.ChatOpen, setPreference: true);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
@@ -796,6 +819,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetChannelMemory(int index, int channel)
|
||||
{
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
{
|
||||
radio.SetChannelMemory(index, channel);
|
||||
radio.Item.CreateClientEvent(radio);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySelectionInputs() => ApplySelectionInputs(InputBox, true, ChatKeyStates.GetChatKeyStates());
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@ internal class DeathPrompt
|
||||
{
|
||||
private static CoroutineHandle? createPromptCoroutine;
|
||||
|
||||
private GUIFrame? deathPromptFrame;
|
||||
private GUIComponent? skillPanel;
|
||||
private GUIComponent? newCharacterPanel;
|
||||
private GUIComponent? takeOverBotPanel;
|
||||
|
||||
private GUIComponent? content;
|
||||
|
||||
public static GUIComponent? takeOverBotPanelFrame;
|
||||
|
||||
private static GUIComponent? takeOverBotPanelFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor, because these should only be created using the Show method
|
||||
@@ -58,7 +59,7 @@ internal class DeathPrompt
|
||||
const float FadeInDuration = 1.0f;
|
||||
|
||||
bool permadeath = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironman = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
bool ironman = GameMain.NetworkMember is { ServerSettings.IronmanModeActive: true };
|
||||
|
||||
var background = new GUICustomComponent(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), onDraw: DrawBackground)
|
||||
{
|
||||
@@ -73,11 +74,11 @@ internal class DeathPrompt
|
||||
foreground.FadeIn(wait: 0, duration: 5.0f);
|
||||
foreground.Pulsate(startScale: Vector2.One, Vector2.One * 0.8f, duration: 25.0f);
|
||||
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), background.RectTransform, Anchor.Center))
|
||||
deathPromptFrame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), background.RectTransform, Anchor.Center))
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
frame.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
deathPromptFrame.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), background.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.2f) }, string.Empty, font: GUIStyle.LargeFont, textAlignment: Alignment.TopCenter)
|
||||
{
|
||||
@@ -90,7 +91,7 @@ internal class DeathPrompt
|
||||
}
|
||||
};
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), frame.RectTransform, Anchor.Center))
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), deathPromptFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
@@ -188,7 +189,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (takeOverBotPanel == null)
|
||||
{
|
||||
CreateTakeOverBotPanel(frame, this);
|
||||
CreateTakeOverBotPanel(deathPromptFrame, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -202,7 +203,7 @@ internal class DeathPrompt
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.respawnnow"))
|
||||
var respawnNowButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.respawnnow"))
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
@@ -211,7 +212,13 @@ internal class DeathPrompt
|
||||
return true;
|
||||
},
|
||||
Enabled = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.MidRound }
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
};
|
||||
if (GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.BetweenRounds })
|
||||
{
|
||||
respawnNowButton.ToolTip = TextManager.Get("respawnnotavailable.respawnmode.betweenrounds");
|
||||
}
|
||||
|
||||
respawnNowButton.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
|
||||
//"info buttons" at the bottom
|
||||
@@ -249,7 +256,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (skillPanel == null)
|
||||
{
|
||||
CreateSkillPanel(frame, GameMain.Client?.Character?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
CreateSkillPanel(deathPromptFrame, GameMain.Client?.Character?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -266,7 +273,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (newCharacterPanel == null)
|
||||
{
|
||||
CreateNewCharacterPanel(frame);
|
||||
CreateNewCharacterPanel(deathPromptFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -279,15 +286,6 @@ internal class DeathPrompt
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
/*new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), infoButtonContainer.RectTransform), "Respawn settings", style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);*/
|
||||
|
||||
this.content = background;
|
||||
}
|
||||
|
||||
@@ -382,8 +380,10 @@ internal class DeathPrompt
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(onYes: () =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(onYes: () =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
GameMain.NetLobbyScreen.CampaignCharacterDiscarded = false;
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
newCharacterPanel = null;
|
||||
});
|
||||
@@ -465,6 +465,11 @@ internal class DeathPrompt
|
||||
{
|
||||
if (botList.SelectedData is CharacterInfo selectedCharacter && GameMain.Client is GameClient client)
|
||||
{
|
||||
if (!GetAvailableBots().Contains(selectedCharacter)) // Someone may have taken over the bot while the list was open, etc
|
||||
{
|
||||
CreateTakeOverBotPanel(frame, deathPrompt); // Update
|
||||
return true;
|
||||
}
|
||||
client.SendTakeOverBotRequest(selectedCharacter);
|
||||
GUIMessageBox.MessageBoxes.Remove(frame.Parent);
|
||||
deathPrompt?.Close();
|
||||
@@ -484,15 +489,26 @@ internal class DeathPrompt
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void UpdateBotList()
|
||||
{
|
||||
if (deathPromptFrame != null && takeOverBotPanelFrame != null)
|
||||
{
|
||||
CloseBotPanel();
|
||||
CreateTakeOverBotPanel(deathPromptFrame, deathPrompt: this);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<CharacterInfo> GetAvailableBots()
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager is { } crewManager)
|
||||
{
|
||||
return crewManager.GetCharacterInfos().Where(c =>
|
||||
/*either an alive bot */
|
||||
c is { Character.IsBot: true, Character.IsDead: false } ||
|
||||
/* or a newly hired bot that hasn't spawned yet */
|
||||
(c.IsNewHire && c.Character == null));
|
||||
return crewManager.GetCharacterInfos(includeReserveBench: true).Where(c =>
|
||||
// a bot on reserve bench
|
||||
c.IsOnReserveBench ||
|
||||
// an alive bot
|
||||
(c.Character != null && c.Character is { IsBot: true, IsDead: false }) ||
|
||||
// a newly hired bot that hasn't spawned yet
|
||||
(c.Character == null && c.IsNewHire));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -84,16 +83,31 @@ namespace Barotrauma
|
||||
{
|
||||
currentDirectory += "/";
|
||||
}
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = new System.IO.FileSystemWatcher(currentDirectory)
|
||||
try
|
||||
{
|
||||
Filter = "*",
|
||||
NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
|
||||
};
|
||||
fileSystemWatcher.Created += OnFileSystemChanges;
|
||||
fileSystemWatcher.Deleted += OnFileSystemChanges;
|
||||
fileSystemWatcher.Renamed += OnFileSystemChanges;
|
||||
fileSystemWatcher.EnableRaisingEvents = true;
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = new System.IO.FileSystemWatcher(currentDirectory)
|
||||
{
|
||||
Filter = "*",
|
||||
NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
|
||||
};
|
||||
fileSystemWatcher.Created += OnFileSystemChanges;
|
||||
fileSystemWatcher.Deleted += OnFileSystemChanges;
|
||||
fileSystemWatcher.Renamed += OnFileSystemChanges;
|
||||
fileSystemWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
catch (System.IO.FileNotFoundException exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory, possibly due to insufficient access permissions.", exception);
|
||||
}
|
||||
catch (ArgumentException exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory, possibly because it was deleted.", exception);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory for an unknown reason.", exception);
|
||||
}
|
||||
RefreshFileList();
|
||||
}
|
||||
}
|
||||
@@ -218,6 +232,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (Directory.Exists(txt))
|
||||
{
|
||||
var attributes = System.IO.File.GetAttributes(txt);
|
||||
if (attributes.HasAnyFlag(System.IO.FileAttributes.System) || attributes.HasAnyFlag(System.IO.FileAttributes.Hidden))
|
||||
{
|
||||
// System and hidden folders should be filtered out when populating the options, but the user can still write or copy-paste the path in the text field,
|
||||
// which will throw a file not found exception when the file system watcher starts. Therefore, this extra check.
|
||||
tb.Text = CurrentDirectory;
|
||||
return false;
|
||||
}
|
||||
CurrentDirectory = txt;
|
||||
return true;
|
||||
}
|
||||
@@ -354,20 +376,19 @@ namespace Barotrauma
|
||||
var directories = Directory.EnumerateDirectories(currentDirectory, "*" + filterBox!.Text + "*");
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
//get directory info
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||
try
|
||||
{
|
||||
//this will throw an exception if the directory can't be opened
|
||||
Directory.GetDirectories(directory);
|
||||
//this will intentionally throw an exception if the directory can't be opened
|
||||
System.IO.Directory.GetDirectories(directory);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// Skip the folders that can't be accessed.
|
||||
continue;
|
||||
}
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = ItemIsDirectory.Yes
|
||||
|
||||
@@ -106,12 +106,35 @@ namespace Barotrauma
|
||||
public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth;
|
||||
public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y);
|
||||
public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the difference of the current aspect ratio to the reference aspect ratio (16:9).
|
||||
/// E.g. if the aspect ratio is 16:9, returns 0; if it's 4:3, returns 0.444; if the aspect ratio is 12:5, returns -0.623.
|
||||
/// </summary>
|
||||
public static float AspectRatioDifference
|
||||
{
|
||||
get
|
||||
{
|
||||
// ~ 1.777
|
||||
float referenceAspectRatio = ReferenceResolution.X / ReferenceResolution.Y;
|
||||
float aspectRatioDifference = referenceAspectRatio - HorizontalAspectRatio;
|
||||
if (MathUtils.NearlyEqual(aspectRatioDifference, 0))
|
||||
{
|
||||
// Handle possible rounding errors, so that we can trust that this returns 0 when the aspect ratio matches the reference aspect ratio.
|
||||
return 0;
|
||||
}
|
||||
return aspectRatioDifference;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A horizontal scaling factor for low aspect ratios (small width relative to height)
|
||||
/// </summary>
|
||||
public static float AspectRatioAdjustment => HorizontalAspectRatio < 1.4f ? (1.0f - (1.4f - HorizontalAspectRatio)) : 1.0f;
|
||||
|
||||
public static bool IsUltrawide => HorizontalAspectRatio > 2.3f;
|
||||
|
||||
public static bool IsHUDScaled => GameSettings.CurrentConfig.Graphics.HUDScale > 1 || GameSettings.CurrentConfig.Graphics.InventoryScale > 1;
|
||||
|
||||
public static int UIWidth
|
||||
{
|
||||
@@ -469,7 +492,7 @@ namespace Barotrauma
|
||||
"Loaded sounds: " + GameMain.SoundManager.LoadedSoundCount + " (" + GameMain.SoundManager.UniqueLoadedSoundCount + " unique)", Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
soundTextY += yStep;
|
||||
|
||||
for (int i = 0; i < SoundManager.SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < SoundManager.SourceCount; i++)
|
||||
{
|
||||
Color clr = Color.White;
|
||||
string soundStr = i + ": ";
|
||||
@@ -625,9 +648,13 @@ namespace Barotrauma
|
||||
|
||||
DrawMessages(spriteBatch, cam);
|
||||
|
||||
if (MouseOn != null && !MouseOn.ToolTip.IsNullOrWhiteSpace())
|
||||
{
|
||||
MouseOn.DrawToolTip(spriteBatch);
|
||||
if (MouseOn != null)
|
||||
{
|
||||
if (!MouseOn.ToolTip.IsNullOrWhiteSpace())
|
||||
{
|
||||
MouseOn.DrawToolTip(spriteBatch);
|
||||
}
|
||||
MouseOn.OnDrawToolTip?.Invoke(MouseOn);
|
||||
}
|
||||
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
@@ -1546,7 +1573,7 @@ namespace Barotrauma
|
||||
private static readonly VertexPositionColorTexture[] donutVerts = new VertexPositionColorTexture[DonutSegments * 4];
|
||||
|
||||
public static void DrawDonutSection(
|
||||
SpriteBatch sb, Vector2 center, Range<float> radii, float sectionRad, Color clr, float depth = 0.0f)
|
||||
SpriteBatch sb, Vector2 center, Range<float> radii, float sectionRad, Color clr, float depth = 0.0f, float rotationRad = 0.0f)
|
||||
{
|
||||
float getRadius(int vertexIndex)
|
||||
=> (vertexIndex % 4) switch
|
||||
@@ -1589,7 +1616,7 @@ namespace Barotrauma
|
||||
for (int vertexIndex = 0; vertexIndex < maxDirectionIndex * 4; vertexIndex++)
|
||||
{
|
||||
donutVerts[vertexIndex].Color = clr;
|
||||
donutVerts[vertexIndex].Position = new Vector3(center + getDirection(vertexIndex) * getRadius(vertexIndex), 0.0f);
|
||||
donutVerts[vertexIndex].Position = new Vector3(center + Vector2.Transform(getDirection(vertexIndex) * getRadius(vertexIndex), Matrix.CreateRotationZ(rotationRad)), 0.0f);
|
||||
}
|
||||
sb.Draw(solidWhiteTexture, donutVerts, depth, count: maxDirectionIndex);
|
||||
}
|
||||
@@ -1856,9 +1883,16 @@ namespace Barotrauma
|
||||
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
|
||||
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
public static void DrawCapsule(SpriteBatch sb, Vector2 origin, float length, float radius, float rotation, Color clr, float depth = 0, float thickness = 1)
|
||||
{
|
||||
DrawDonutSection(sb, origin + Vector2.Transform(-new Vector2(length / 2, 0), Matrix.CreateRotationZ(rotation)), new Range<float>(radius - thickness / 2, radius + thickness / 2), MathHelper.Pi, clr, depth, rotation - MathHelper.Pi);
|
||||
DrawRectangle(sb, origin, new Vector2(length, radius * 2), new Vector2(length / 2, radius), rotation, clr, depth, thickness);
|
||||
DrawDonutSection(sb, origin + Vector2.Transform(new Vector2(length / 2, 0), Matrix.CreateRotationZ(rotation)), new Range<float>(radius - thickness / 2, radius + thickness / 2), MathHelper.Pi, clr, depth, rotation);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
|
||||
public static Texture2D CreateCircle(int radius, bool filled = false)
|
||||
{
|
||||
@@ -2488,7 +2522,12 @@ namespace Barotrauma
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.Get("bugreportbutton") + $" (v{GameMain.Version})",
|
||||
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (PauseMenuOpen) { TogglePauseMenu(); }
|
||||
GameMain.Instance.ShowBugReporter();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateButton("PauseMenuResume", buttonContainer, null);
|
||||
@@ -2524,23 +2563,37 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.EndRound("");
|
||||
});
|
||||
}
|
||||
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
|
||||
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null)
|
||||
{
|
||||
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
|
||||
if (canSave)
|
||||
//server owner (host) can't return to the lobby without ending the round for everyone
|
||||
if (!GameMain.Client.IsServerOwner)
|
||||
{
|
||||
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestRoundEnd(save: true);
|
||||
});
|
||||
CreateButton("ReturnToServerlobby", buttonContainer,
|
||||
verificationTextTag: "PauseMenuReturnToServerLobbyVerificationSelf",
|
||||
action: () =>
|
||||
{
|
||||
GameMain.Client?.EndRoundForSelf();
|
||||
});
|
||||
}
|
||||
|
||||
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
|
||||
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
action: () =>
|
||||
if (GameMain.Client.HasPermission(ClientPermissions.ManageRound))
|
||||
{
|
||||
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
|
||||
if (canSave)
|
||||
{
|
||||
GameMain.Client?.RequestRoundEnd(save: false);
|
||||
});
|
||||
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestEndRound(save: true);
|
||||
}, color: GUIStyle.Red);
|
||||
}
|
||||
|
||||
CreateButton("EndRound", buttonContainer,
|
||||
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestEndRound(save: false);
|
||||
}, color: GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2568,9 +2621,9 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
void CreateButton(string textTag, GUIComponent parent, Action action, string verificationTextTag = null)
|
||||
void CreateButton(string textTag, GUIComponent parent, Action action, string verificationTextTag = null, Color? color = null)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), TextManager.Get(textTag))
|
||||
var button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), TextManager.Get(textTag))
|
||||
{
|
||||
OnClicked = (btn, userData) =>
|
||||
{
|
||||
@@ -2586,25 +2639,29 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (color.HasValue)
|
||||
{
|
||||
button.Color = color.Value;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateVerificationPrompt(string textTag, Action confirmAction)
|
||||
}
|
||||
public static void CreateVerificationPrompt(string textTag, Action confirmAction)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.Get(textTag),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.Get(textTag),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
UserData = "verificationprompt",
|
||||
DrawOnTop = true
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (_, __) =>
|
||||
{
|
||||
PauseMenuOpen = false;
|
||||
confirmAction?.Invoke();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
UserData = "verificationprompt",
|
||||
DrawOnTop = true
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (_, __) =>
|
||||
{
|
||||
PauseMenuOpen = false;
|
||||
confirmAction?.Invoke();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
|
||||
private static bool TogglePauseMenu(GUIButton button, object obj)
|
||||
@@ -2690,12 +2747,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFourByThree()
|
||||
{
|
||||
float aspectRatio = HorizontalAspectRatio;
|
||||
return aspectRatio > 1.3f && aspectRatio < 1.4f;
|
||||
}
|
||||
|
||||
public static void SetSavingIndicatorState(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
|
||||
@@ -159,6 +159,34 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent holdOverlay;
|
||||
|
||||
private bool requireHold;
|
||||
public bool RequireHold
|
||||
{
|
||||
get => requireHold;
|
||||
set
|
||||
{
|
||||
requireHold = value;
|
||||
if (value)
|
||||
{
|
||||
holdOverlay ??= new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), Frame.RectTransform, Anchor.CenterLeft), style: null)
|
||||
{
|
||||
Color = GUIStyle.Yellow * 0.33f,
|
||||
CanBeFocused = false,
|
||||
IgnoreLayoutGroups = true,
|
||||
Visible = true
|
||||
};
|
||||
}
|
||||
else if (holdOverlay != null)
|
||||
{
|
||||
holdOverlay.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public float HoldDurationSeconds { get; set; } = 5f;
|
||||
private float holdTimer;
|
||||
|
||||
public bool Pulse { get; set; }
|
||||
private float pulseTimer;
|
||||
private float pulseExpand;
|
||||
@@ -220,7 +248,7 @@ namespace Barotrauma
|
||||
Rectangle expandRect = Rect;
|
||||
float expand = (pulseExpand * 20.0f) * GUI.Scale;
|
||||
expandRect.Inflate(expand, expand);
|
||||
|
||||
|
||||
GUIStyle.EndRoundButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
|
||||
}
|
||||
}
|
||||
@@ -240,6 +268,11 @@ namespace Barotrauma
|
||||
}
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if (RequireHold)
|
||||
{
|
||||
holdTimer += deltaTime;
|
||||
}
|
||||
|
||||
if (OnPressed != null)
|
||||
{
|
||||
if (OnPressed())
|
||||
@@ -254,25 +287,34 @@ namespace Barotrauma
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (PlaySoundOnSelect)
|
||||
if (!RequireHold || holdTimer > HoldDurationSeconds)
|
||||
{
|
||||
SoundPlayer.PlayUISound(ClickSound);
|
||||
}
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData))
|
||||
if (PlaySoundOnSelect)
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
SoundPlayer.PlayUISound(ClickSound);
|
||||
}
|
||||
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData))
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holdTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holdTimer = 0.0f;
|
||||
if (!ExternalHighlight)
|
||||
{
|
||||
State = Selected ? ComponentState.Selected : ComponentState.None;
|
||||
@@ -283,6 +325,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (RequireHold)
|
||||
{
|
||||
float width = MathHelper.Clamp(holdTimer / HoldDurationSeconds, 0f, 1f);
|
||||
if (!MathUtils.NearlyEqual(width, holdOverlay.RectTransform.RelativeSize.X))
|
||||
{
|
||||
holdOverlay.RectTransform.RelativeSize = new Vector2(width, 1f);
|
||||
}
|
||||
|
||||
holdOverlay.Color =
|
||||
holdTimer >= HoldDurationSeconds
|
||||
? Color.Green * 0.33f
|
||||
: Color.Red * 0.33f;
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in Children)
|
||||
{
|
||||
child.State = State;
|
||||
|
||||
@@ -9,6 +9,7 @@ using Barotrauma.IO;
|
||||
using RestSharp;
|
||||
using System.Net;
|
||||
using Barotrauma.Steam;
|
||||
using Steamworks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -158,6 +159,12 @@ namespace Barotrauma
|
||||
|
||||
public Action<GUIComponent> OnAddedToGUIUpdateList;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when a tooltip should be draw on the component.
|
||||
/// Note that the callback triggers even if the item has no tooltip (which can be useful for e.g. only contructing the tooltip when needed).
|
||||
/// </summary>
|
||||
public Action<GUIComponent> OnDrawToolTip;
|
||||
|
||||
public enum ComponentState { None, Hover, Pressed, Selected, HoverSelected };
|
||||
|
||||
protected Alignment alignment;
|
||||
@@ -246,6 +253,57 @@ namespace Barotrauma
|
||||
{
|
||||
get { return new Vector2(Rect.Center.X, Rect.Center.Y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the component's rect position to the specified area. Does not resize the component.
|
||||
/// </summary>
|
||||
/// <param name="clampArea">Area to contain the Rect of this component to</param>
|
||||
public void ClampToArea(Rectangle clampArea)
|
||||
{
|
||||
Rectangle componentRect = Rect;
|
||||
|
||||
int x = componentRect.X;
|
||||
int y = componentRect.Y;
|
||||
|
||||
// Adjust the X position
|
||||
if (componentRect.Width <= clampArea.Width)
|
||||
{
|
||||
if (componentRect.Left < clampArea.Left)
|
||||
{
|
||||
x = clampArea.Left;
|
||||
}
|
||||
else if (componentRect.Right > clampArea.Right)
|
||||
{
|
||||
x = clampArea.Right - componentRect.Width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Component is wider than clamp area, osition it to overlap as much as possible
|
||||
x = clampArea.Left - (componentRect.Width - clampArea.Width) / 2;
|
||||
}
|
||||
|
||||
// Adjust the Y position
|
||||
if (componentRect.Height <= clampArea.Height)
|
||||
{
|
||||
if (componentRect.Top < clampArea.Top)
|
||||
{
|
||||
y = clampArea.Top;
|
||||
}
|
||||
else if (componentRect.Bottom > clampArea.Bottom)
|
||||
{
|
||||
y = clampArea.Bottom - componentRect.Height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Component is taller than clamp area, osition it to overlap as much as possible
|
||||
y = clampArea.Top - (componentRect.Height - clampArea.Height) / 2;
|
||||
}
|
||||
|
||||
Point moveAmount = new Point(x - componentRect.X, y - componentRect.Y);
|
||||
RectTransform.ScreenSpaceOffset += moveAmount;
|
||||
}
|
||||
|
||||
protected Rectangle ClampRect(Rectangle r)
|
||||
{
|
||||
@@ -1087,6 +1145,8 @@ namespace Barotrauma
|
||||
FromXML(subElement, component is GUIListBox listBox ? listBox.Content.RectTransform : component.RectTransform);
|
||||
}
|
||||
|
||||
component.toolTip = element.GetAttributeString("tooltip", string.Empty);
|
||||
|
||||
if (element.GetAttributeBool("resizetofitchildren", false))
|
||||
{
|
||||
Vector2 relativeResizeScale = element.GetAttributeVector2("relativeresizescale", Vector2.One);
|
||||
@@ -1129,7 +1189,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
switch (attribute.Name.ToString().ToLowerInvariant())
|
||||
string conditionName = attribute.Name.ToString().ToLowerInvariant();
|
||||
switch (conditionName)
|
||||
{
|
||||
case "language":
|
||||
var languages = element.GetAttributeIdentifierArray(attribute.Name.ToString(), Array.Empty<Identifier>())
|
||||
@@ -1171,6 +1232,20 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
case "mingamelaunches":
|
||||
if (int.TryParse(attribute.Value, out int minLaunches))
|
||||
{
|
||||
return SteamManager.GetStatInt(AchievementStat.GameLaunchCount) > minLaunches;
|
||||
}
|
||||
return false;
|
||||
case "appsubscribed":
|
||||
case "appnotsubscribed":
|
||||
if (SteamManager.IsInitialized &&
|
||||
int.TryParse(attribute.Value, out int appId))
|
||||
{
|
||||
return SteamApps.IsSubscribedToApp(appId) == (conditionName == "appsubscribed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,21 @@ namespace Barotrauma
|
||||
{
|
||||
public class GUIDropDown : GUIComponent, IKeyboardSubscriber
|
||||
{
|
||||
/// <param name="selected">The component that was selected from the dropdown.</param>
|
||||
/// <param name="obj"><see cref="GUIComponent.UserData"/> of the component selected from the dropdown.</param>
|
||||
public delegate bool OnSelectedHandler(GUIComponent selected, object obj = null);
|
||||
/// <summary>
|
||||
/// Triggers when some item is cliecked from the dropdown.
|
||||
/// Note that <see cref="SelectedData"/> is not set yet when this callback triggers, and returning false from the callback disallows selecting it.
|
||||
/// If you want to access the new value, use the obj argument.
|
||||
/// </summary>
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers after an item has been selected from the dropdown, all validation has been done and the new value has been set.
|
||||
/// </summary>
|
||||
public OnSelectedHandler AfterSelected;
|
||||
|
||||
public OnSelectedHandler OnDropped;
|
||||
|
||||
private readonly GUIButton button;
|
||||
@@ -166,7 +179,7 @@ namespace Barotrauma
|
||||
|
||||
public Vector4 Padding => button.TextBlock.Padding;
|
||||
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft, float listBoxScale = 1) : base(style, rectT)
|
||||
{
|
||||
text ??= LocalizedString.EmptyString;
|
||||
|
||||
@@ -185,13 +198,21 @@ namespace Barotrauma
|
||||
|
||||
Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
|
||||
Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter;
|
||||
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
|
||||
listBox = new GUIListBox(new RectTransform(new Point((int)(Rect.Width * listBoxScale), Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
|
||||
{ IsFixedSize = false }, style: null)
|
||||
{
|
||||
Enabled = !selectMultiple,
|
||||
PlaySoundOnSelect = true,
|
||||
};
|
||||
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
|
||||
if (!selectMultiple)
|
||||
{
|
||||
listBox.AfterSelected = (component, obj) =>
|
||||
{
|
||||
SelectItem(component, obj);
|
||||
AfterSelected?.Invoke(component, obj);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
GUIStyle.Apply(listBox, "GUIListBox", this);
|
||||
GUIStyle.Apply(listBox.ContentBackground, "GUIListBox", this);
|
||||
|
||||
@@ -199,6 +220,8 @@ namespace Barotrauma
|
||||
{
|
||||
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), button.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, null, scaleToFit: true);
|
||||
icon.ApplyStyle(button.Style.ChildStyles["dropdownicon".ToIdentifier()]);
|
||||
//move the text away from the icon
|
||||
button.TextBlock.Padding += new Vector4(0, 0, icon.Rect.Width, 0);
|
||||
}
|
||||
|
||||
currentHighestParent = FindHighestParent();
|
||||
@@ -249,12 +272,12 @@ namespace Barotrauma
|
||||
toolTip ??= "";
|
||||
if (selectMultiple)
|
||||
{
|
||||
var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
|
||||
var frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip
|
||||
};
|
||||
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
|
||||
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip,
|
||||
@@ -266,6 +289,11 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OnSelected != null && !OnSelected.Invoke(tb.Parent, tb.Parent.UserData))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<LocalizedString> texts = new List<LocalizedString>();
|
||||
selectedDataMultiple.Clear();
|
||||
selectedIndexMultiple.Clear();
|
||||
@@ -282,8 +310,7 @@ namespace Barotrauma
|
||||
i++;
|
||||
}
|
||||
button.Text = LocalizedString.Join(", ", texts);
|
||||
// TODO: The callback is called at least twice, remove this?
|
||||
OnSelected?.Invoke(tb.Parent, tb.Parent.UserData);
|
||||
AfterSelected?.Invoke(tb.Parent, SelectedData);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -291,7 +318,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
return new GUITextBlock(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
|
||||
return new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip
|
||||
@@ -328,9 +355,8 @@ namespace Barotrauma
|
||||
}
|
||||
button.Text = textBlock?.Text ?? "";
|
||||
}
|
||||
OnSelected?.Invoke(component, obj);
|
||||
Dropped = false;
|
||||
// TODO: OnSelected can be called multiple times and when it shouldn't be called -> turn into an event so that nobody else can call it.
|
||||
OnSelected?.Invoke(component, component.UserData);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,6 +370,7 @@ namespace Barotrauma
|
||||
{
|
||||
listBox.Select(userData);
|
||||
}
|
||||
AfterSelected?.Invoke(SelectedComponent, SelectedData);
|
||||
}
|
||||
|
||||
public void Select(int index)
|
||||
@@ -360,6 +387,7 @@ namespace Barotrauma
|
||||
{
|
||||
listBox.Select(index);
|
||||
}
|
||||
AfterSelected?.Invoke(this, SelectedData);
|
||||
}
|
||||
|
||||
private bool wasOpened;
|
||||
|
||||
@@ -14,8 +14,17 @@ namespace Barotrauma
|
||||
protected List<GUIComponent> selected;
|
||||
|
||||
public delegate bool OnSelectedHandler(GUIComponent component, object obj);
|
||||
/// <summary>
|
||||
/// Triggers when some element is clicked on the listbox.
|
||||
/// Note that <see cref="SelectedData"/> is not set yet when this callback triggers, and returning false from the callback disallows selecting it.
|
||||
/// </summary>
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers after some element has been selected from the listbox.
|
||||
/// </summary>
|
||||
public OnSelectedHandler AfterSelected;
|
||||
|
||||
public delegate object CheckSelectedHandler();
|
||||
public CheckSelectedHandler CheckSelected;
|
||||
|
||||
@@ -1021,7 +1030,7 @@ namespace Barotrauma
|
||||
while (index < Content.CountChildren)
|
||||
{
|
||||
GUIComponent child = Content.GetChild(index);
|
||||
if (child.Visible)
|
||||
if (child.Visible && child.CanBeFocused)
|
||||
{
|
||||
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
|
||||
if (SmoothScroll)
|
||||
@@ -1040,7 +1049,7 @@ namespace Barotrauma
|
||||
while (index >= 0)
|
||||
{
|
||||
GUIComponent child = Content.GetChild(index);
|
||||
if (child.Visible)
|
||||
if (child.Visible && child.CanBeFocused)
|
||||
{
|
||||
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
|
||||
if (SmoothScroll)
|
||||
@@ -1151,6 +1160,8 @@ namespace Barotrauma
|
||||
{
|
||||
SoundPlayer.PlayUISound(GUISoundType.Select);
|
||||
}
|
||||
|
||||
AfterSelected?.Invoke(child, SelectedData);
|
||||
}
|
||||
|
||||
public void Select(IEnumerable<GUIComponent> children)
|
||||
@@ -1160,8 +1171,9 @@ namespace Barotrauma
|
||||
selected.Clear();
|
||||
selected.AddRange(children.Where(c => Content.Children.Contains(c)));
|
||||
foreach (var child in selected) { OnSelected?.Invoke(child, child.UserData); }
|
||||
AfterSelected?.Invoke(children.FirstOrDefault(), SelectedData);
|
||||
}
|
||||
|
||||
|
||||
public void Deselect()
|
||||
{
|
||||
Selected = false;
|
||||
@@ -1172,6 +1184,15 @@ namespace Barotrauma
|
||||
selected.Clear();
|
||||
}
|
||||
|
||||
public void DeselectElement(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return; }
|
||||
if (selected.Contains(child))
|
||||
{
|
||||
selected.Remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateScrollBarSize()
|
||||
{
|
||||
scrollBarNeedsRecalculation = false;
|
||||
@@ -1268,7 +1289,7 @@ namespace Barotrauma
|
||||
ContentBackground.DrawManually(spriteBatch, alsoChildren: false);
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
if (HideChildrenOutsideFrame)
|
||||
if (HideChildrenOutsideFrame && Content.CountChildren > 0)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, Content.Rect);
|
||||
@@ -1306,7 +1327,7 @@ namespace Barotrauma
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, Color.White * 0.5f, thickness: 2f);
|
||||
}
|
||||
|
||||
if (HideChildrenOutsideFrame)
|
||||
if (HideChildrenOutsideFrame && Content.CountChildren > 0)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Barotrauma
|
||||
GUIStyle.Apply(Text, "", this);
|
||||
Content.Recalculate();
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
new Point(Text.Rect.Width, Text.Rect.Height);
|
||||
new Point(Text.Rect.Width, Math.Min(Text.Rect.Height, GameMain.GraphicsHeight));
|
||||
Text.RectTransform.IsFixedSize = true;
|
||||
if (headerText.IsNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
@@ -51,6 +51,16 @@ namespace Barotrauma
|
||||
public readonly static GUISprite InteractionLabelBackground = new GUISprite("InteractionLabelBackground");
|
||||
public readonly static GUISprite BrokenIcon = new GUISprite("BrokenIcon");
|
||||
public readonly static GUISprite YouAreHereCircle = new GUISprite("YouAreHereCircle");
|
||||
|
||||
public readonly static GUISprite SubLocationIcon = new GUISprite("SubLocationIcon");
|
||||
public readonly static GUISprite ShuttleIcon = new GUISprite("ShuttleIcon");
|
||||
public readonly static GUISprite WreckIcon = new GUISprite("WreckIcon");
|
||||
public readonly static GUISprite CaveIcon = new GUISprite("CaveIcon");
|
||||
public readonly static GUISprite OutpostIcon = new GUISprite("OutpostIcon");
|
||||
public readonly static GUISprite RuinIcon = new GUISprite("RuinIcon");
|
||||
public readonly static GUISprite EnemyIcon = new GUISprite("EnemyIcon");
|
||||
public readonly static GUISprite CorpseIcon = new GUISprite("CorpseIcon");
|
||||
public readonly static GUISprite BeaconIcon = new GUISprite("BeaconIcon");
|
||||
|
||||
public readonly static GUISprite Radiation = new GUISprite("Radiation");
|
||||
public readonly static GUISpriteSheet RadiationAnimSpriteSheet = new GUISpriteSheet("RadiationAnimSpriteSheet");
|
||||
@@ -71,7 +81,7 @@ namespace Barotrauma
|
||||
public readonly static GUISprite EndRoundButtonPulse = new GUISprite("EndRoundButtonPulse");
|
||||
|
||||
public readonly static GUISpriteSheet FocusIndicator = new GUISpriteSheet("FocusIndicator");
|
||||
|
||||
|
||||
public readonly static GUISprite IconOverflowIndicator = new GUISprite("IconOverflowIndicator");
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -918,5 +918,12 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"GUITextBox: Invalid selection: ({exception})");
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetDelegates()
|
||||
{
|
||||
OnKeyHit = null;
|
||||
OnEnterPressed = null;
|
||||
OnTextChanged = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace Barotrauma
|
||||
private static bool ReplacingPermanentlyDeadCharacter =>
|
||||
GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false } &&
|
||||
GameMain.Client?.CharacterInfo is { PermanentlyDead: true };
|
||||
|
||||
private static bool ReserveBenchEnabled => GameMain.GameSession?.Campaign is MultiPlayerCampaign;
|
||||
|
||||
private bool hadPermissionToHire;
|
||||
private static bool HasPermissionToHire => ReplacingPermanentlyDeadCharacter ?
|
||||
@@ -277,13 +279,42 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci));
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci, createNetworkMessage: false));
|
||||
}
|
||||
SetTotalHireCost();
|
||||
}
|
||||
UpdateCrew();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will simply update each of the HR view lists (hireables, pending hires, and crew) from the most up to date information.
|
||||
/// It is a sane version of UpdateLocationView that won't break things even if used outside of whatever arbitrary conditions that one was made for.
|
||||
/// </summary>
|
||||
public void RefreshHRView()
|
||||
{
|
||||
if (campaign?.CurrentLocation is not Location currentLocation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (characterPreviewFrame != null)
|
||||
{
|
||||
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
|
||||
characterPreviewFrame = null;
|
||||
}
|
||||
|
||||
UpdateHireables(currentLocation);
|
||||
|
||||
if (pendingList != null)
|
||||
{
|
||||
pendingList.Content.ClearChildren();
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci, checkCrewSizeLimit: false, createNetworkMessage: false)); // don't check limits here, just display the data as it is
|
||||
SetTotalHireCost();
|
||||
}
|
||||
|
||||
UpdateCrew();
|
||||
}
|
||||
|
||||
public void UpdateHireables()
|
||||
{
|
||||
UpdateHireables(campaign?.CurrentLocation);
|
||||
@@ -329,10 +360,11 @@ namespace Barotrauma
|
||||
public void UpdateCrew()
|
||||
{
|
||||
crewList.Content.Children.ToList().ForEach(c => crewList.Content.RemoveChild(c));
|
||||
foreach (CharacterInfo c in GameMain.GameSession.CrewManager.GetCharacterInfos())
|
||||
foreach (CharacterInfo ci in GameMain.GameSession.CrewManager.GetCharacterInfos(includeReserveBench: true))
|
||||
{
|
||||
if (c == null || !((c.Character?.IsBot ?? true) || campaign is SinglePlayerCampaign)) { continue; }
|
||||
CreateCharacterFrame(c, crewList);
|
||||
// CrewManager is used to store info on all characters including players, but we only want bots in HR
|
||||
if (ci.Character != null && (ci.Character.IsRemotePlayer || !ci.Character.IsBot)) { continue; }
|
||||
CreateCharacterFrame(ci, crewList);
|
||||
}
|
||||
SortCharacters(crewList, SortingMethod.JobAsc);
|
||||
crewList.UpdateScrollBarSize();
|
||||
@@ -369,6 +401,10 @@ namespace Barotrauma
|
||||
((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
|
||||
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
|
||||
}
|
||||
|
||||
// Always apply this in the end to group by reserve bench status (does nothing if there are no reserve benched bots)
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.BotStatus.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.BotStatus));
|
||||
|
||||
int? CompareReputationRequirement(GUIComponent c1, GUIComponent c2)
|
||||
{
|
||||
@@ -401,6 +437,8 @@ namespace Barotrauma
|
||||
|
||||
public GUIComponent CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox, bool hideSalary = false)
|
||||
{
|
||||
string characterName = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
||||
|
||||
Skill skill = null;
|
||||
Color? jobColor = null;
|
||||
if (characterInfo.Job != null)
|
||||
@@ -415,6 +453,7 @@ namespace Barotrauma
|
||||
};
|
||||
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
@@ -428,13 +467,15 @@ namespace Barotrauma
|
||||
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
|
||||
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
|
||||
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
|
||||
characterName,
|
||||
textColor: jobColor, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
|
||||
const float smallColumnWidth = 0.6f / 3;
|
||||
const float skillColumnWidth = smallColumnWidth * 0.7f;
|
||||
const float buttonWidth = 0.12f;
|
||||
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
||||
characterInfo.Title ?? characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
@@ -449,33 +490,28 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
var fullJobText = jobBlock.Text;
|
||||
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
||||
if (jobBlock.Text != fullJobText)
|
||||
{
|
||||
jobBlock.ToolTip = fullJobText;
|
||||
jobBlock.CanBeFocused = true;
|
||||
}
|
||||
float width = 0.6f / 3;
|
||||
if (characterInfo.Job != null && skill != null)
|
||||
{
|
||||
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
||||
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(skillColumnWidth, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
||||
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(),
|
||||
textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
CanBeFocused = false
|
||||
};
|
||||
GUIImage skillIcon = new GUIImage(new RectTransform(Vector2.One, skillGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), skill.Icon, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
if (jobColor.HasValue) { skillIcon.Color = jobColor.Value; }
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
if (!hideSalary)
|
||||
{
|
||||
if (listBox != crewList)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
|
||||
new GUITextBlock(new RectTransform(new Vector2(smallColumnWidth, 1.0f), mainGroup.RectTransform),
|
||||
TextManager.FormatCurrency(ReplacingPermanentlyDeadCharacter ? campaign.NewCharacterCost(characterInfo) : HireManager.GetSalaryFor(characterInfo)),
|
||||
textAlignment: Alignment.Center)
|
||||
{
|
||||
@@ -485,19 +521,24 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Just a bit of padding to make list layouts similar
|
||||
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
new GUIFrame(new RectTransform(new Vector2(smallColumnWidth, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
}
|
||||
}
|
||||
|
||||
if (listBox == hireableList)
|
||||
{
|
||||
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
||||
var hireButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("hirebutton"),
|
||||
ToolTip = TextManager.Get(ReserveBenchEnabled ? "hirebutton.crew" : "hirebutton"),
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo) && !ReplacingPermanentlyDeadCharacter,
|
||||
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
|
||||
OnClicked = (b, o) =>
|
||||
{
|
||||
var currentCharacterInfo = (CharacterInfo)o;
|
||||
currentCharacterInfo.BotStatus = BotStatus.PendingHireToActiveService;
|
||||
return AddPendingHire(currentCharacterInfo);
|
||||
}
|
||||
};
|
||||
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
@@ -505,7 +546,7 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
if (PendingHires.Count(ci => ci.BotStatus == BotStatus.PendingHireToActiveService) + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
{
|
||||
if (btn.Enabled)
|
||||
{
|
||||
@@ -523,7 +564,7 @@ namespace Barotrauma
|
||||
if (ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
bool canHire = CanHire(characterInfo) && campaign.CanAffordNewCharacter(characterInfo);
|
||||
var takeoverButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementTakeControlButton")
|
||||
var takeoverButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementTakeControlButton")
|
||||
{
|
||||
ToolTip = canHire ? TextManager.Get("hireandtakecontrol") : TextManager.Get("hireandtakecontroldisabled"),
|
||||
ClickSound = GUISoundType.ConfirmTransaction,
|
||||
@@ -554,25 +595,90 @@ namespace Barotrauma
|
||||
btn.Enabled = canHireCurrently;
|
||||
};
|
||||
}
|
||||
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
var hireToReserveBenchButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddAsReserveButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("hirebutton.reservebench"),
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo),
|
||||
OnClicked = (b, o) =>
|
||||
{
|
||||
var currentCharacterInfo = (CharacterInfo)o;
|
||||
currentCharacterInfo.BotStatus = BotStatus.PendingHireToReserveBench;
|
||||
return AddPendingHire(currentCharacterInfo, checkCrewSizeLimit: false);
|
||||
}
|
||||
};
|
||||
hireToReserveBenchButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
btn.Visible = ReserveBenchEnabled;
|
||||
btn.Enabled = CanHire(characterInfo) && !ReplacingPermanentlyDeadCharacter;
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (listBox == pendingList)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform),
|
||||
style: characterInfo.BotStatus == BotStatus.PendingHireToActiveService ? "CrewManagementReserveBenchButtonActive" : "CrewManagementReserveBenchButtonReserve")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
ToolTip = TextManager.Get(characterInfo.BotStatus == BotStatus.PendingHireToActiveService ? "ReserveBenchTogglePendingHire.Active" : "ReserveBenchTogglePendingHire.Reserve"),
|
||||
Enabled = CanHire(characterInfo) && (characterInfo.BotStatus == BotStatus.PendingHireToActiveService || !ActiveServiceFull()), // note that this is a toggle
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
SelectCharacter(null, null, null);
|
||||
var currentCharacterInfo = (CharacterInfo)obj;
|
||||
GameMain.Client?.ToggleReserveBench(currentCharacterInfo, pendingHire: true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
|
||||
{
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo),
|
||||
Enabled = CanHire(characterInfo), // =just check user's rights
|
||||
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
|
||||
};
|
||||
}
|
||||
else if (listBox == crewList && campaign != null)
|
||||
{
|
||||
var currentCrew = GameMain.GameSession.CrewManager.GetCharacterInfos();
|
||||
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform),
|
||||
style: characterInfo.BotStatus == BotStatus.ActiveService ? "CrewManagementReserveBenchButtonActive" : "CrewManagementReserveBenchButtonReserve")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
ToolTip = TextManager.Get(characterInfo.BotStatus == BotStatus.ActiveService ? "ReserveBenchToggle.Active" : "ReserveBenchToggle.Reserve"),
|
||||
Enabled = CanHire(characterInfo) && (characterInfo.BotStatus == BotStatus.ActiveService || !ActiveServiceFull()), // note that this is a toggle
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
SelectCharacter(null, null, null);
|
||||
var currentCharacterInfo = (CharacterInfo)obj;
|
||||
if (currentCharacterInfo.BotStatus == BotStatus.ActiveService && // switching to reserve bench
|
||||
characterInfo.Character != null) // may not have a Character to remove if not spawned this round
|
||||
{
|
||||
GameMain.GameSession.CrewManager.RemoveCharacter(characterInfo.Character, removeInfo: true, resetCrewListIndex: true);
|
||||
}
|
||||
GameMain.Client?.ToggleReserveBench(currentCharacterInfo); // update changes to server
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cm = GameMain.GameSession.CrewManager;
|
||||
// Can't fire if there's only one character in active service
|
||||
var fireButtonEnabled = HasPermissionToHire && (characterInfo.IsOnReserveBench ||
|
||||
(cm.GetCharacterInfos().Contains(characterInfo) && cm.GetCharacterInfos().Count() > 1));
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
//can't fire if there's only one character in the crew
|
||||
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermissionToHire,
|
||||
Enabled = fireButtonEnabled,
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
var confirmDialog = new GUIMessageBox(
|
||||
@@ -587,11 +693,25 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ReserveBenchEnabled && characterInfo.IsOnReserveBench) // Applies to unspecified listings like the death prompt and the bot list after permadeath
|
||||
{
|
||||
new GUIImage(new RectTransform(new Vector2(smallColumnWidth / 2, 0.6f), mainGroup.RectTransform), style: "CrewManagementReserveBenchIconReserve")
|
||||
{
|
||||
ToolTip = TextManager.Get("ReserveBenchStatus.Reserve.WillSpawn")
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUILayoutGroup(new RectTransform(new Vector2(smallColumnWidth / 2, 0.6f), mainGroup.RectTransform)) { CanBeFocused = false };
|
||||
}
|
||||
}
|
||||
|
||||
if (listBox == pendingList || listBox == crewList)
|
||||
{
|
||||
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
|
||||
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
|
||||
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
|
||||
@@ -605,6 +725,16 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
//recalculate everything and truncate texts if needed
|
||||
mainGroup.Recalculate();
|
||||
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
|
||||
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
||||
if (jobBlock.Text != fullJobText)
|
||||
{
|
||||
jobBlock.ToolTip = fullJobText;
|
||||
jobBlock.CanBeFocused = true;
|
||||
}
|
||||
|
||||
bool CanHire(CharacterInfo thisCharacterInfo)
|
||||
{
|
||||
if (!HasPermissionToHire) { return false; }
|
||||
@@ -613,6 +743,15 @@ namespace Barotrauma
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is there (going to be) no space left in active service?
|
||||
/// </summary>
|
||||
private bool ActiveServiceFull()
|
||||
{
|
||||
return (PendingHires.Count(ci => ci.BotStatus == BotStatus.PendingHireToActiveService) + campaign.CrewManager.GetCharacterInfos().Count())
|
||||
>= CrewManager.MaxCrewSize;
|
||||
}
|
||||
|
||||
private bool EnoughReputationToHire(CharacterInfo characterInfo)
|
||||
{
|
||||
@@ -656,7 +795,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup infoLabelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
|
||||
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
|
||||
float blockHeight = 1.0f / 4;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"), textColor: GUIStyle.TextColorBright);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), "");
|
||||
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
||||
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
|
||||
@@ -664,17 +803,17 @@ namespace Barotrauma
|
||||
if (characterInfo.HasSpecifierTags)
|
||||
{
|
||||
var menuCategoryVar = characterInfo.Prefab.MenuCategoryVar;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.ReplaceVars($"[{menuCategoryVar}]")));
|
||||
}
|
||||
if (characterInfo.Job is Job job)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), job.Name);
|
||||
}
|
||||
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), trait.DisplayName);
|
||||
}
|
||||
infoLabelGroup.Recalculate();
|
||||
@@ -727,9 +866,9 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
|
||||
private bool AddPendingHire(CharacterInfo characterInfo, bool checkCrewSizeLimit = true, bool createNetworkMessage = true)
|
||||
{
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
if (checkCrewSizeLimit && characterInfo.BotStatus == BotStatus.PendingHireToActiveService && ActiveServiceFull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -792,7 +931,7 @@ namespace Barotrauma
|
||||
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
|
||||
hires.ForEach(hireInfo =>
|
||||
{
|
||||
if (campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
if (campaign.CrewManager.GetCharacterInfos(includeReserveBench: true).None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
{
|
||||
nonDuplicateHires.Add(hireInfo);
|
||||
}
|
||||
@@ -806,12 +945,21 @@ namespace Barotrauma
|
||||
if (!campaign.CanAfford(total)) { return false; }
|
||||
}
|
||||
|
||||
bool atLeastOneHired = false;
|
||||
bool atLeastOneHiredToActiveDuty = false;
|
||||
bool atLeastOneHiredToReserveBench = false;
|
||||
foreach (CharacterInfo ci in nonDuplicateHires)
|
||||
{
|
||||
bool toReserveBench = ci.BotStatus == BotStatus.PendingHireToReserveBench;
|
||||
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci, takeMoney: takeMoney))
|
||||
{
|
||||
atLeastOneHired = true;
|
||||
if (toReserveBench)
|
||||
{
|
||||
atLeastOneHiredToReserveBench = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
atLeastOneHiredToActiveDuty = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -819,15 +967,27 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (atLeastOneHired)
|
||||
if (atLeastOneHiredToActiveDuty || atLeastOneHiredToReserveBench)
|
||||
{
|
||||
UpdateLocationView(campaign.Map.CurrentLocation, true);
|
||||
SelectCharacter(null, null, null);
|
||||
if (createNotification)
|
||||
{
|
||||
LocalizedString msg = string.Empty;
|
||||
if (atLeastOneHiredToActiveDuty)
|
||||
{
|
||||
msg += TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName);
|
||||
}
|
||||
if (atLeastOneHiredToReserveBench)
|
||||
{
|
||||
if (!msg.IsNullOrEmpty()) { msg += "\n\n"; }
|
||||
msg += GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false } ?
|
||||
TextManager.Get("crewhiredmessage.reservebench.permadeath") :
|
||||
TextManager.Get( "crewhiredmessage.reservebench");
|
||||
}
|
||||
|
||||
var dialog = new GUIMessageBox(
|
||||
TextManager.Get("newcrewmembers"),
|
||||
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
|
||||
TextManager.Get("newcrewmembers"), msg,
|
||||
new LocalizedString[] { TextManager.Get("Ok") });
|
||||
dialog.Buttons[0].OnClicked += dialog.Close;
|
||||
}
|
||||
@@ -1034,7 +1194,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPendingHires(List<UInt16> characterInfos, Location location)
|
||||
public void SetPendingHires(List<UInt16> characterInfos, bool[] characterInfoReserveBenchStatuses, Location location, bool checkCrewSizeLimit)
|
||||
{
|
||||
List<CharacterInfo> oldHires = PendingHires.ToList();
|
||||
foreach (CharacterInfo pendingHire in oldHires)
|
||||
@@ -1042,18 +1202,25 @@ namespace Barotrauma
|
||||
RemovePendingHire(pendingHire, createNetworkMessage: false);
|
||||
}
|
||||
PendingHires.Clear();
|
||||
int i = 0;
|
||||
foreach (UInt16 identifier in characterInfos)
|
||||
{
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.ID == identifier);
|
||||
if (match != null)
|
||||
{
|
||||
AddPendingHire(match, createNetworkMessage: false);
|
||||
match.BotStatus = characterInfoReserveBenchStatuses[i] ? BotStatus.PendingHireToReserveBench : BotStatus.PendingHireToActiveService;
|
||||
AddPendingHire(match, checkCrewSizeLimit: checkCrewSizeLimit, createNetworkMessage: false);
|
||||
if (!PendingHires.Contains(match))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to add a pending hire");
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(PendingHires.Contains(match));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Received a hire that doesn't exist.");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1064,7 +1231,7 @@ namespace Barotrauma
|
||||
/// <param name="renameCharacter">When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew.</param>
|
||||
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
|
||||
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
|
||||
public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
public void SendCrewState(bool updatePending = false, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
{
|
||||
if (campaign is MultiPlayerCampaign)
|
||||
{
|
||||
@@ -1078,6 +1245,7 @@ namespace Barotrauma
|
||||
foreach (CharacterInfo pendingHire in PendingHires)
|
||||
{
|
||||
msg.WriteUInt16(pendingHire.ID);
|
||||
msg.WriteBoolean(pendingHire.BotStatus == BotStatus.PendingHireToReserveBench);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,7 +1257,9 @@ namespace Barotrauma
|
||||
{
|
||||
msg.WriteUInt16(renameCharacter.info.ID);
|
||||
msg.WriteString(renameCharacter.newName);
|
||||
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.ID == renameCharacter.info.ID) ?? false;
|
||||
bool existingCrewMember =
|
||||
campaign.CrewManager is CrewManager crewManager &&
|
||||
crewManager.GetCharacterInfos(includeReserveBench: true).Any(ci => ci.ID == renameCharacter.info.ID);
|
||||
msg.WriteBoolean(existingCrewMember);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -1562,7 +1563,7 @@ namespace Barotrauma
|
||||
bool locationHasDealOnItem = isSellingRelatedList ?
|
||||
ActiveStore.RequestedGoods.Contains(pi.ItemPrefab) : ActiveStore.DailySpecials.Contains(pi.ItemPrefab);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
|
||||
pi.ItemPrefab.Name, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft)
|
||||
RichString.Rich(pi.ItemPrefab.Name), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Shadow = locationHasDealOnItem,
|
||||
@@ -1573,15 +1574,24 @@ namespace Barotrauma
|
||||
if (locationHasDealOnItem)
|
||||
{
|
||||
var relativeWidth = (0.9f * nameAndQuantityFrame.Rect.Height) / nameAndQuantityFrame.Rect.Width;
|
||||
Vector2 dealIconSize = new Vector2(relativeWidth, 0.9f) * 0.5f;
|
||||
var dealIcon = new GUIImage(
|
||||
new RectTransform(new Vector2(relativeWidth, 0.9f), nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterLeft)
|
||||
new RectTransform(dealIconSize, nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteOffset = new Point((int)nameBlock.Padding.X, 0)
|
||||
},
|
||||
"StoreDealIcon", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
CanBeFocused = false,
|
||||
UserData = "StoreDealIcon"
|
||||
};
|
||||
var dealIconColor = dealIcon.Color;
|
||||
if (forceDisable)
|
||||
{
|
||||
dealIconColor.A = 0;
|
||||
}
|
||||
|
||||
dealIcon.Color = dealIconColor;
|
||||
dealIcon.SetAsFirstChild();
|
||||
}
|
||||
bool isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
|
||||
@@ -1713,7 +1723,7 @@ namespace Barotrauma
|
||||
mainGroup.Recalculate();
|
||||
mainGroup.RectTransform.RecalculateChildren(true, true);
|
||||
amountInput?.LayoutGroup.Recalculate();
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text.SanitizedString, nameBlock.Font, nameBlock.Rect.Width);
|
||||
mainGroup.RectTransform.Children.ForEach(c => c.IsFixedSize = true);
|
||||
|
||||
return frame;
|
||||
@@ -1795,6 +1805,9 @@ namespace Barotrauma
|
||||
|
||||
private void SetItemFrameStatus(GUIComponent itemFrame, bool enabled)
|
||||
{
|
||||
float full = 1f;
|
||||
float dim = 0.7f;
|
||||
float alpha = (enabled ? full : dim);
|
||||
if (itemFrame?.UserData is not PurchasedItem pi) { return; }
|
||||
bool refreshFrameStatus = !pi.IsStoreComponentEnabled.HasValue || pi.IsStoreComponentEnabled.Value != enabled;
|
||||
if (!refreshFrameStatus) { return; }
|
||||
@@ -1802,14 +1815,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (pi.ItemPrefab?.InventoryIcon != null)
|
||||
{
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * (enabled ? 1.0f : 0.5f);
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * alpha;
|
||||
}
|
||||
else if (pi.ItemPrefab?.Sprite != null)
|
||||
{
|
||||
icon.Color = pi.ItemPrefab.SpriteColor * (enabled ? 1.0f : 0.5f);
|
||||
icon.Color = pi.ItemPrefab.SpriteColor * alpha;
|
||||
}
|
||||
};
|
||||
var color = Color.White * (enabled ? 1.0f : 0.5f);
|
||||
var color = Color.White * alpha;
|
||||
if (itemFrame.FindChild("name", recursive: true) is GUITextBlock name)
|
||||
{
|
||||
name.TextColor = color;
|
||||
@@ -1835,7 +1848,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
|
||||
{
|
||||
priceBlock.TextColor = isDiscounted ? storeSpecialColor * (enabled ? 1.0f : 0.5f) : color;
|
||||
priceBlock.TextColor = isDiscounted ? storeSpecialColor * alpha : color;
|
||||
}
|
||||
if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton)
|
||||
{
|
||||
@@ -1845,6 +1858,10 @@ namespace Barotrauma
|
||||
{
|
||||
removeButton.Enabled = enabled;
|
||||
}
|
||||
if (itemFrame.FindChild("StoreDealIcon", recursive: true) is GUIImage dealIcon)
|
||||
{
|
||||
dealIcon.Color = dealIcon.Color * alpha;
|
||||
}
|
||||
pi.IsStoreComponentEnabled = enabled;
|
||||
itemFrame.UserData = pi;
|
||||
}
|
||||
@@ -2271,6 +2288,15 @@ namespace Barotrauma
|
||||
{
|
||||
updateStopwatch.Restart();
|
||||
|
||||
if (GameMain.DevMode)
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.D0))
|
||||
{
|
||||
CreateUI();
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
|
||||
{
|
||||
CreateUI();
|
||||
|
||||
@@ -742,8 +742,8 @@ namespace Barotrauma
|
||||
|
||||
private (LocalizedString header, LocalizedString body) GetItemTransferWarningText()
|
||||
{
|
||||
var header = TextManager.Get("itemtransferheader").Fallback("lowfuelheader", useDefaultLanguageIfFound: false);
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback("lowfuelwarning", useDefaultLanguageIfFound: false);
|
||||
var header = TextManager.Get("itemtransferheader").Fallback(TextManager.Get("lowfuelheader"), useDefaultLanguageIfFound: false);
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback(TextManager.Get("lowfuelwarning"), useDefaultLanguageIfFound: false);
|
||||
return (header, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,9 +120,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (Client == null) { return; }
|
||||
if (currentPing == Client.Ping) { return; }
|
||||
currentPing = Client.Ping;
|
||||
textBlock.Text = currentPing.ToString();
|
||||
textBlock.TextColor = GetPingColor();
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.ConnectedClients.Contains(Client))
|
||||
{
|
||||
currentPing = Client.Ping;
|
||||
textBlock.Text = currentPing.ToString();
|
||||
textBlock.TextColor = GetPingColor();
|
||||
textBlock.ToolTip = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPing = 0;
|
||||
textBlock.Text = "-";
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
textBlock.ToolTip = TextManager.Get("causeofdeathdescription.disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
public void TryPermissionIconRefresh(Sprite icon)
|
||||
@@ -416,7 +427,10 @@ namespace Barotrauma
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", $"{(int)MathF.Round(value)}");
|
||||
}
|
||||
|
||||
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
}
|
||||
|
||||
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
|
||||
talentsButton.OnAddedToGUIUpdateList += (component) =>
|
||||
@@ -458,17 +472,19 @@ namespace Barotrauma
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
case InfoFrameTab.Talents:
|
||||
talentMenu.CreateGUI(infoFrameHolder, Character.Controlled ?? GameMain.Client?.Character);
|
||||
talentMenu.CreateGUI(infoFrameHolder, Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private const float jobColumnWidthPercentage = 0.138f,
|
||||
characterColumnWidthPercentage = 0.45f,
|
||||
pingColumnWidthPercentage = 0.206f,
|
||||
walletColumnWidthPercentage = 0.206f;
|
||||
private const float JobColumnWidthPercentage = 0.138f,
|
||||
CharacterColumnWidthPercentage = 0.45f,
|
||||
KillColumnWidthPercentage = 0.1f,
|
||||
DeathColumnWidthPercentage = 0.1f,
|
||||
PingColumnWidthPercentage = 0.15f,
|
||||
WalletColumnWidthPercentage = 0.206f;
|
||||
|
||||
private int jobColumnWidth, characterColumnWidth, pingColumnWidth, walletColumnWidth;
|
||||
private int jobColumnWidth, characterColumnWidth, pingColumnWidth, walletColumnWidth, deathColumnWidth, killColumnWidth;
|
||||
|
||||
private void CreateCrewListFrame(GUIFrame crewFrame)
|
||||
{
|
||||
@@ -496,11 +512,21 @@ namespace Barotrauma
|
||||
{
|
||||
if (teamIDs.Count > 1)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, nameHeight), content.RectTransform), CombatMission.GetTeamName(teamIDs[i]), textColor: i == 0 ? GUIStyle.Green : GUIStyle.Orange) { ForceUpperCase = ForceUpperCase.Yes };
|
||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, nameHeight), content.RectTransform), CombatMission.GetTeamName(teamIDs[i]), textColor: CombatMission.GetTeamColor(teamIDs[i]))
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes
|
||||
};
|
||||
var teamIcon = new GUIImage(new RectTransform(Vector2.One, nameText.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight),
|
||||
style: teamIDs[i] == CharacterTeamType.Team2 ? "SeparatistIcon" : "CoalitionIcon")
|
||||
{
|
||||
Color = nameText.TextColor
|
||||
};
|
||||
nameText.Padding = new Vector4(teamIcon.Rect.Width + nameText.Padding.X, nameText.Padding.Y, nameText.Padding.Z, nameText.Padding.W);
|
||||
}
|
||||
|
||||
headerFrames[i] = new GUILayoutGroup(new RectTransform(Vector2.Zero, content.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(2, -1) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 2,
|
||||
UserData = i
|
||||
};
|
||||
@@ -587,8 +613,8 @@ namespace Barotrauma
|
||||
|
||||
sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((1f - jobColumnWidthPercentage * sizeMultiplier) * sizeMultiplier, 1f);
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(JobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((1f - JobColumnWidthPercentage * sizeMultiplier) * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = false;
|
||||
@@ -626,7 +652,8 @@ namespace Barotrauma
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => character.Info.DrawJobIcon(sb, component.Rect))
|
||||
@@ -639,21 +666,29 @@ namespace Barotrauma
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
|
||||
|
||||
paddedFrame.Recalculate();
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame, textBlock: null));
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerListContentHolder(GUILayoutGroup headerFrame)
|
||||
{
|
||||
bool isCampaign = GameMain.GameSession?.Campaign is MultiPlayerCampaign;
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(JobColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(CharacterColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
var killButton = new GUIButton(new RectTransform(new Vector2(KillColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("killcount"), style: "GUIButtonSmallFreeScale");
|
||||
killColumnWidth = killButton.Rect.Width;
|
||||
var deathButton = new GUIButton(new RectTransform(new Vector2(DeathColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("deathcount"), style: "GUIButtonSmallFreeScale");
|
||||
deathColumnWidth = deathButton.Rect.Width;
|
||||
}
|
||||
|
||||
GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(PingColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale");
|
||||
if (isCampaign)
|
||||
{
|
||||
GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform)
|
||||
{
|
||||
RelativeSize = new Vector2(walletColumnWidthPercentage * sizeMultiplier, 1f)
|
||||
}, TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale")
|
||||
GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(WalletColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale")
|
||||
{
|
||||
TextBlock = { Font = GUIStyle.HotkeyFont },
|
||||
CanBeFocused = false,
|
||||
@@ -662,15 +697,12 @@ namespace Barotrauma
|
||||
walletColumnWidth = walletButton.Rect.Width;
|
||||
}
|
||||
|
||||
sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((characterColumnWidthPercentage + (isCampaign ? 0 : walletColumnWidthPercentage)) * sizeMultiplier, 1f);
|
||||
pingButton.RectTransform.RelativeSize = new Vector2(pingColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = false;
|
||||
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = ForceUpperCase.Yes;
|
||||
foreach (var btn in headerFrame.GetAllChildren<GUIButton>())
|
||||
{
|
||||
btn.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
btn.CanBeFocused = false;
|
||||
btn.ForceUpperCase = ForceUpperCase.Yes;
|
||||
}
|
||||
|
||||
jobColumnWidth = jobButton.Rect.Width;
|
||||
characterColumnWidth = characterButton.Rect.Width;
|
||||
@@ -688,45 +720,68 @@ namespace Barotrauma
|
||||
|
||||
var connectedClients = GameMain.Client.ConnectedClients;
|
||||
|
||||
for (int i = 0; i < teamIDs.Count; i++)
|
||||
for (int teamID = 0; teamID < teamIDs.Count; teamID++)
|
||||
{
|
||||
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[i]))
|
||||
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[teamID]))
|
||||
{
|
||||
if (!(character is AICharacter) && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
|
||||
CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), i);
|
||||
if (character is not AICharacter && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
|
||||
CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), teamID);
|
||||
}
|
||||
|
||||
foreach (CharacterInfo characterInfo in GameMain.GameSession.CrewManager?.GetReserveBenchInfos() ?? Enumerable.Empty<CharacterInfo>())
|
||||
{
|
||||
CreateMultiPlayerCharacterElement(character: null, client: null, teamID, justCharacterInfo: characterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < connectedClients.Count; j++)
|
||||
{
|
||||
Client client = connectedClients[j];
|
||||
if (!client.InGame || client.Character == null || client.Character.IsDead)
|
||||
if (client.Character == null || client.Character.IsDead)
|
||||
{
|
||||
CreateMultiPlayerClientElement(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerCharacterElement(Character character, Client client, int i)
|
||||
|
||||
/// <param name="justCharacterInfo">The character element can be generated based on just a CharacterInfo, and Character and Client can be left null. Otherwise, those are required and the CharacterInfo of the Character is used.</param>
|
||||
private void CreateMultiPlayerCharacterElement(Character character, Client client, int teamID, CharacterInfo justCharacterInfo = null)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
|
||||
CharacterInfo characterInfo = justCharacterInfo ?? character.Info;
|
||||
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[teamID].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[teamID].Content.RectTransform), style: "ListBoxElement")
|
||||
{
|
||||
UserData = character,
|
||||
UserData = character != null ? character : characterInfo,
|
||||
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? OwnCharacterBGColor : Color.Transparent
|
||||
};
|
||||
|
||||
frame.OnSecondaryClicked += (component, data) =>
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
};
|
||||
frame.OnSecondaryClicked += (component, data) =>
|
||||
{
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => character.Info.DrawJobIcon(sb, component.Rect))
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
|
||||
onDraw: (sb, component) =>
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
characterInfo?.DrawJobIcon(sb, component.Rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawClientJobIcon(sb, component.Rect, client);
|
||||
}
|
||||
})
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
@@ -736,6 +791,19 @@ namespace Barotrauma
|
||||
if (client != null)
|
||||
{
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientKillCount(client) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientDeathCount(client) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
@@ -743,27 +811,62 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
|
||||
ToolBox.LimitString(characterInfo.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetBotKillCount(characterInfo) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetBotDeathCount(characterInfo) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
if (character is AICharacter)
|
||||
{
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
|
||||
// "BOT" instead of ping (which isn't relevant for bots)
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
|
||||
}
|
||||
else
|
||||
else if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
linkedGUIList.Add(new LinkedGUI(client: null, frame, textBlock: null, permissionIcon: null));
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => DrawDisconnectedIcon(sb, component.Rect))
|
||||
// Reserve bench icon
|
||||
new GUIImage(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height - 4), paddedFrame.RectTransform), style: "CrewManagementReserveBenchIconReserve", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
SelectedColor = Color.White
|
||||
ToolTip = TextManager.Get("ReserveBenchStatus.Reserve")
|
||||
};
|
||||
}
|
||||
|
||||
if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
//black bar to dim out the elements (1px shorter and to the right so it won't dim the left border too)
|
||||
new GUIFrame(
|
||||
new RectTransform(new Point(paddedFrame.Rect.Width - 1, frame.Rect.Height), paddedFrame.RectTransform, Anchor.Center)
|
||||
{
|
||||
AbsoluteOffset = new Point(1, 0)
|
||||
},
|
||||
style: null, color: Color.Black * 0.7f)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
}
|
||||
else if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
// Empty column for reserve benched bots
|
||||
new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center) { CanBeFocused = false };
|
||||
}
|
||||
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
paddedFrame.Recalculate();
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerClientElement(Client client)
|
||||
@@ -785,11 +888,12 @@ namespace Barotrauma
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
|
||||
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client))
|
||||
onDraw: (sb, component) => DrawClientJobIcon(sb, component.Rect, client))
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
@@ -797,14 +901,26 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientKillCount(client) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientDeathCount(client) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
|
||||
if (client.Character is { } character)
|
||||
{
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
}
|
||||
CreateWalletCrewFrame(client.Character, paddedFrame);
|
||||
|
||||
paddedFrame.Recalculate();
|
||||
}
|
||||
|
||||
private int GetTeamIndex(Client client)
|
||||
@@ -837,12 +953,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return teamIDs.IndexOf(client.TeamID);
|
||||
}
|
||||
|
||||
private void CreateWalletCrewFrame(Character character, GUILayoutGroup paddedFrame)
|
||||
{
|
||||
if (!(GameMain.GameSession?.Campaign is MultiPlayerCampaign)) { return; }
|
||||
if (GameMain.GameSession?.Campaign is not MultiPlayerCampaign) { return; }
|
||||
|
||||
GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center)
|
||||
{
|
||||
@@ -860,7 +976,7 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("walletdescription")
|
||||
};
|
||||
|
||||
if (character.IsBot) { return; }
|
||||
if (character == null || character.IsBot) { return; }
|
||||
|
||||
Sprite walletSprite = GUIStyle.CrewWalletIconSmall.Value.Sprite;
|
||||
|
||||
@@ -947,7 +1063,6 @@ namespace Barotrauma
|
||||
float iconWidth = iconSize.X / (float)characterColumnWidth;
|
||||
int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUIStyle.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width);
|
||||
permissionIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1f), paddedFrame.RectTransform) { AbsoluteOffset = new Point(xOffset + 2, 0) }, permissionIconSprite) { IgnoreLayoutGroups = true };
|
||||
|
||||
|
||||
if (client.Character != null && client.Character.IsDead)
|
||||
{
|
||||
@@ -969,18 +1084,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNotInGameIcon(SpriteBatch spriteBatch, Rectangle area, Client client)
|
||||
private void DrawClientJobIcon(SpriteBatch spriteBatch, Rectangle area, Client client)
|
||||
{
|
||||
if (client.Spectating)
|
||||
{
|
||||
spectateIcon.Draw(spriteBatch, area, Color.White);
|
||||
}
|
||||
else if (client.Character != null && client.Character.IsDead)
|
||||
else if (client.Character != null && client.InGame)
|
||||
{
|
||||
if (client.Character.Info != null)
|
||||
{
|
||||
client.Character.Info.DrawJobIcon(spriteBatch, area);
|
||||
}
|
||||
client.Character.Info?.DrawJobIcon(spriteBatch, area);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1004,7 +1116,13 @@ namespace Barotrauma
|
||||
|
||||
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
|
||||
if (existingPreview != null) { infoFrameHolder.RemoveChild(existingPreview); }
|
||||
|
||||
|
||||
if (userData is CharacterInfo { IsOnReserveBench: true })
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Modal info panel that pops up on the right
|
||||
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.69f), infoFrameHolder.RectTransform, Anchor.TopRight, Pivot.TopLeft) { RelativeOffset = new Vector2(-0.061f, 0) })
|
||||
{
|
||||
UserData = "SelectedCharacter"
|
||||
@@ -1028,7 +1146,7 @@ namespace Barotrauma
|
||||
{
|
||||
talentButton.OnClicked = (button, o) =>
|
||||
{
|
||||
talentMenu.CreateGUI(infoFrameHolder, character);
|
||||
talentMenu.CreateGUI(infoFrameHolder, character.Info);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -1413,7 +1531,7 @@ namespace Barotrauma
|
||||
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.322f), paddedFrame.RectTransform), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.425f, 1.0f), headerArea.RectTransform),
|
||||
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client));
|
||||
onDraw: (sb, component) => DrawClientJobIcon(sb, component.Rect, client));
|
||||
|
||||
GUIFont font = paddedFrame.Rect.Width < 280 ? GUIStyle.SmallFont : GUIStyle.Font;
|
||||
|
||||
@@ -1503,6 +1621,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
linkedGUIList.Clear();
|
||||
|
||||
foreach (GUIListBox crewList in crewListArray)
|
||||
{
|
||||
crewList.Content.ClearChildren();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLineToLog(string line, PlayerConnectionChangeType type)
|
||||
@@ -1587,7 +1710,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.DisplayName, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.GetLocationTypeToDisplay().Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (location.Faction?.Prefab != null)
|
||||
{
|
||||
@@ -1633,6 +1756,7 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
if (missionIcon != null)
|
||||
{
|
||||
@@ -1663,6 +1787,8 @@ namespace Barotrauma
|
||||
|
||||
private static void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
|
||||
{
|
||||
if (sub == null) { return; }
|
||||
|
||||
GUIFrame subInfoFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(Vector2.One * 0.97f, subInfoFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
@@ -1765,7 +1891,7 @@ namespace Barotrauma
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
List<GUITextBlock> skillNames = new List<GUITextBlock>();
|
||||
foreach (Skill skill in info.Job.GetSkills())
|
||||
foreach (Skill skill in info.Job.GetSkills().OrderByDescending(static s => s.Level))
|
||||
{
|
||||
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.0f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = true };
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value));
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using static Barotrauma.TalentTree;
|
||||
using static Barotrauma.TalentTree.TalentStages;
|
||||
@@ -83,17 +84,21 @@ namespace Barotrauma
|
||||
private GUIButton? talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
public void CreateGUI(GUIFrame parent, Character? targetCharacter)
|
||||
private delegate void StartAnimation(RectangleF start, RectangleF end, float duration);
|
||||
private StartAnimation? startAnimation;
|
||||
private GUIComponent? talentMainArea;
|
||||
|
||||
public void CreateGUI(GUIFrame parent, CharacterInfo? characterInfo)
|
||||
{
|
||||
this.characterInfo = characterInfo;
|
||||
character = characterInfo?.Character;
|
||||
|
||||
parent.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentShowCaseButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
showCaseTalentFrames.Clear();
|
||||
|
||||
character = targetCharacter;
|
||||
characterInfo = targetCharacter?.Info;
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), parent.RectTransform, Anchor.Center), style: null);
|
||||
@@ -136,7 +141,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup playerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), containerFrame.RectTransform, Anchor.TopCenter));
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
if (!GameMain.NetLobbyScreen.PermadeathMode)
|
||||
if (!GameMain.NetLobbyScreen.PermadeathMode && GameMain.GameSession?.GameMode is not PvPMode)
|
||||
{
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
@@ -294,7 +299,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ImmutableHashSet<TalentPrefab?> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(static e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e)).ToImmutableHashSet();
|
||||
if (talentsOutsideTree.Any())
|
||||
if (talentsOutsideTree.Any(static t => t != null && !t.IsHiddenExtraTalent))
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), nameLayout.RectTransform), style: null);
|
||||
@@ -319,6 +324,7 @@ namespace Barotrauma
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
if (extraTalent is null) { continue; }
|
||||
if (extraTalent.IsHiddenExtraTalent) { continue; }
|
||||
GUIImage talentImg = new GUIImage(new RectTransform(Vector2.One, extraTalentList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + ToolBox.ExtendColorToPercentageSigns(extraTalent.Description.Value)),
|
||||
@@ -341,7 +347,15 @@ namespace Barotrauma
|
||||
|
||||
private void CreateTalentMenu(GUIComponent parent, CharacterInfo info, TalentTree tree)
|
||||
{
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, anchor: Anchor.TopCenter));
|
||||
talentMainArea = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, Anchor.TopCenter), style: null );
|
||||
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(Vector2.One, talentMainArea.RectTransform));
|
||||
startAnimation = CreatePopupAnimationHandler(talentMainArea);
|
||||
|
||||
if (info is { TalentRefundPoints: > 0, ShowTalentResetPopupOnOpen: true })
|
||||
{
|
||||
CreateTalentResetPopup(talentMainArea);
|
||||
}
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
||||
|
||||
@@ -425,6 +439,130 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTalentResetPopup(GUIComponent parent)
|
||||
{
|
||||
int talentResetCount = 0;
|
||||
if (character?.Info != null)
|
||||
{
|
||||
talentResetCount = Math.Min(character.Info.TalentResetCount, character.Info.GetCurrentLevel());
|
||||
}
|
||||
bool hasResetTalentsBefore = talentResetCount > 0;
|
||||
var bgBlocker = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, anchor: Anchor.Center), style: "GUIBackgroundBlocker")
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
var popup = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.8f), bgBlocker.RectTransform, Anchor.Center));
|
||||
|
||||
var popupLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(popup.RectTransform, 0.95f), popup.RectTransform, Anchor.Center), isHorizontal: false);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), popupLayout.RectTransform), TextManager.Get("talentresetheader"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, hasResetTalentsBefore ? 0.25f : 0.5f), popupLayout.RectTransform), TextManager.Get("talentresetprompt"), wrap: true);
|
||||
|
||||
if (hasResetTalentsBefore)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), popupLayout.RectTransform),
|
||||
TextManager.GetWithVariable("talentresetpromptwarning", "[count]", talentResetCount.ToString()), wrap: true)
|
||||
{
|
||||
TextColor = GUIStyle.Red
|
||||
};
|
||||
}
|
||||
|
||||
var buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), popupLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true);
|
||||
|
||||
var confirmButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform), TextManager.Get("holdtoconfirm"))
|
||||
{
|
||||
RequireHold = true,
|
||||
HoldDurationSeconds = 1.5f,
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
if (character is null || characterInfo is null) { return false; }
|
||||
|
||||
characterInfo.RefundTalents();
|
||||
selectedTalents.Clear();
|
||||
UpdateTalentInfo();
|
||||
bgBlocker.Visible = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var denyButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform), TextManager.Get("decidelater"))
|
||||
{
|
||||
RequireHold = false,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (talentResetButton is not { } resetButton) { return false; }
|
||||
startAnimation?.Invoke(popup.Rect, resetButton.Rect, 0.25f);
|
||||
resetButton.Flash(GUIStyle.Green);
|
||||
bgBlocker.Visible = false;
|
||||
if (characterInfo != null)
|
||||
{
|
||||
characterInfo.ShowTalentResetPopupOnOpen = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static StartAnimation CreatePopupAnimationHandler(GUIComponent parent)
|
||||
{
|
||||
bool drawAnimation = false;
|
||||
|
||||
float animDur = 1f,
|
||||
animTimer = 0f;
|
||||
|
||||
RectangleF drawRect = RectangleF.Empty,
|
||||
animStartRect = RectangleF.Empty,
|
||||
animEndRect = RectangleF.Empty;
|
||||
|
||||
void StartAnimation(RectangleF start, RectangleF end, float duration)
|
||||
{
|
||||
animStartRect = start;
|
||||
animEndRect = end;
|
||||
animTimer = 0;
|
||||
animDur = duration;
|
||||
drawRect = start;
|
||||
drawAnimation = true;
|
||||
}
|
||||
|
||||
void OnDraw(SpriteBatch batch, GUICustomComponent component)
|
||||
{
|
||||
if (!drawAnimation) { return; }
|
||||
|
||||
GUIComponentStyle style = GUIStyle.GetComponentStyle("GUIFrame");
|
||||
|
||||
style.Sprites[GUIComponent.ComponentState.None][0].Draw(batch, drawRect, Color.White);
|
||||
}
|
||||
|
||||
void OnUpdate(float f, GUICustomComponent component)
|
||||
{
|
||||
if (!drawAnimation) { return; }
|
||||
|
||||
animTimer += f;
|
||||
if (animTimer > animDur)
|
||||
{
|
||||
drawRect = animEndRect;
|
||||
drawAnimation = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float lerp = animTimer / animDur;
|
||||
|
||||
drawRect = new RectangleF(
|
||||
MathHelper.Lerp(animStartRect.X, animEndRect.X, lerp),
|
||||
MathHelper.Lerp(animStartRect.Y, animEndRect.Y, lerp),
|
||||
MathHelper.Lerp(animStartRect.Width, animEndRect.Width, lerp),
|
||||
MathHelper.Lerp(animStartRect.Height, animEndRect.Height, lerp));
|
||||
}
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, parent.RectTransform), onDraw: OnDraw, onUpdate: OnUpdate)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
return StartAnimation;
|
||||
}
|
||||
|
||||
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info, int specializationCount)
|
||||
{
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
@@ -679,6 +817,15 @@ namespace Barotrauma
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
if (characterInfo is null) { return false; }
|
||||
|
||||
int newTalentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
// if we don't have talents selected, and we have points to refund, show the refund popup
|
||||
if (characterInfo.TalentRefundPoints > 0 && newTalentCount == 0)
|
||||
{
|
||||
CreateTalentResetPopup(talentMainArea!);
|
||||
return true;
|
||||
}
|
||||
|
||||
selectedTalents = characterInfo.GetUnlockedTalentsInTree().ToHashSet();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
@@ -844,12 +991,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly LocalizedString refundText = TextManager.Get("refund"),
|
||||
resetText = TextManager.Get("reset");
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (characterInfo is null || talentResetButton is null || talentApplyButton is null) { return; }
|
||||
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
talentApplyButton.Enabled = character != null && talentCount > 0;
|
||||
talentResetButton.Enabled = character != null && (talentCount > 0 || characterInfo.TalentRefundPoints > 0);
|
||||
|
||||
if (talentCount == 0 && characterInfo.TalentRefundPoints > 0)
|
||||
{
|
||||
if (talentResetButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentResetButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
|
||||
talentResetButton.Text = refundText;
|
||||
}
|
||||
else
|
||||
{
|
||||
talentResetButton.Text = resetText;
|
||||
}
|
||||
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
@@ -893,6 +1059,22 @@ namespace Barotrauma
|
||||
return info.GetIdentifierUsingOriginalName() == ownCharacterInfo.GetIdentifierUsingOriginalName();
|
||||
}
|
||||
|
||||
private static bool IsOnSameTeam(CharacterInfo? info)
|
||||
{
|
||||
if (info is null) { return false; }
|
||||
|
||||
CharacterTeamType? ownCharacterTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID;
|
||||
if (ownCharacterTeam is null) { return false; }
|
||||
|
||||
return info.TeamID == ownCharacterTeam;
|
||||
}
|
||||
|
||||
private static bool IsSpectatingInMultiplayer()
|
||||
{
|
||||
if (GameMain.Client?.MyClient is not { } myClient) { return false; }
|
||||
return myClient.Spectating;
|
||||
}
|
||||
|
||||
public static bool CanManageTalents(CharacterInfo targetInfo)
|
||||
{
|
||||
// in singleplayer we can do whatever we want
|
||||
@@ -901,10 +1083,16 @@ namespace Barotrauma
|
||||
// always allow managing talents for own character
|
||||
if (IsOwnCharacter(targetInfo)) { return true; }
|
||||
|
||||
// disallow managing talents while spectating
|
||||
if (IsSpectatingInMultiplayer()) { return false; }
|
||||
|
||||
// don't allow controlling non-bot characters
|
||||
if (targetInfo.Character is not { IsBot: true }) { return false; }
|
||||
|
||||
// lastly check if we have the permission to do this
|
||||
// only allow managing talents for bots on the same team
|
||||
if (!IsOnSameTeam(targetInfo)) { return false; }
|
||||
|
||||
// lastly, check if we have the permission to do this
|
||||
return GameMain.Client is { } client && client.HasPermission(ClientPermissions.ManageBotTalents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +127,10 @@ namespace Barotrauma
|
||||
Campaign.OnMoneyChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh());
|
||||
}
|
||||
|
||||
public void RequestRefresh()
|
||||
public void RequestRefresh(bool refreshUpgrades = false)
|
||||
{
|
||||
needsRefresh = true;
|
||||
if (refreshUpgrades) { SelectTab(UpgradeTab.Upgrade); }
|
||||
}
|
||||
|
||||
private void RefreshAll()
|
||||
@@ -673,6 +674,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!upgrades.ContainsKey(category) && HasSwappableItems(category))
|
||||
{
|
||||
upgrades.Add(category, new List<UpgradePrefab>());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (category, prefabs) in upgrades)
|
||||
@@ -771,19 +776,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (Submarine.MainSub == null) { return false; }
|
||||
subItems ??= GetSubItems();
|
||||
return subItems.Any(i =>
|
||||
i.Prefab.SwappableItem != null &&
|
||||
!i.IsHidden && i.AllowSwapping &&
|
||||
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
|
||||
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
|
||||
return subItems.Any(item => HasSwappableItems(category, item));
|
||||
}
|
||||
|
||||
private static bool HasSwappableItems(UpgradeCategory category, Item item)
|
||||
{
|
||||
if (Submarine.MainSub == null) { return false; }
|
||||
return
|
||||
item.Prefab.SwappableItem != null &&
|
||||
!item.IsHidden && item.AllowSwapping &&
|
||||
(item.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == item.Prefab.Identifier)) &&
|
||||
Submarine.MainSub.IsEntityFoundOnThisSub(item, true) && category.ItemTags.Any(t => item.HasTag(t));
|
||||
}
|
||||
private static List<Item> GetSubItems() => Submarine.MainSub?.GetItems(true) ?? new List<Item>();
|
||||
|
||||
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
|
||||
{
|
||||
if (selectedUpgradeCategoryLayout == null) { return; }
|
||||
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
bool hasUpgradeModules = prefabs.Count > 0;
|
||||
|
||||
customizeTabOpen = !hasUpgradeModules && hasSwappableItems;
|
||||
|
||||
customizeTabOpen = false;
|
||||
|
||||
GUIComponent[] categoryFrames = GetFrames(category);
|
||||
@@ -799,9 +814,7 @@ namespace Barotrauma
|
||||
GUIFrame frame = new GUIFrame(rectT(1.0f, 0.4f, selectedUpgradeCategoryLayout));
|
||||
GUIFrame paddedFrame = new GUIFrame(rectT(0.93f, 0.9f, frame, Anchor.Center), style: null);
|
||||
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
|
||||
float listHeight = hasSwappableItems ? 0.9f : 1.0f;
|
||||
float listHeight = hasSwappableItems && hasUpgradeModules ? 0.9f : 1.0f;
|
||||
|
||||
GUIListBox prefabList = new GUIListBox(rectT(1.0f, listHeight, paddedFrame, Anchor.BottomLeft))
|
||||
{
|
||||
@@ -810,7 +823,8 @@ namespace Barotrauma
|
||||
ScrollBarVisible = true
|
||||
};
|
||||
|
||||
if (hasSwappableItems)
|
||||
//both swappable items and upgrade modules -> create 2 tabs
|
||||
if (hasSwappableItems && hasUpgradeModules)
|
||||
{
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1.0f, 0.1f, paddedFrame, anchor: Anchor.TopLeft), isHorizontal: true);
|
||||
|
||||
@@ -852,8 +866,15 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
|
||||
//only either upgrade modules or swappable items -> just create the list
|
||||
else if (hasUpgradeModules)
|
||||
{
|
||||
CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
|
||||
}
|
||||
else if (hasSwappableItems)
|
||||
{
|
||||
CreateSwappableItemList(prefabList, category, submarine);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUpgradePrefabList(GUIListBox parent, UpgradeCategory category, List<UpgradePrefab> prefabs, Submarine submarine)
|
||||
@@ -1370,9 +1391,10 @@ namespace Barotrauma
|
||||
Item[] entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToArray();
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
//hide categories with no upgrades in them
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) { continue; }
|
||||
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
//hide categories with no upgrades or swappables in them
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
if (!hasSwappableItems && UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) { continue; }
|
||||
if (hasSwappableItems || entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
{
|
||||
yield return category;
|
||||
}
|
||||
@@ -1534,7 +1556,7 @@ namespace Barotrauma
|
||||
|
||||
description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
|
||||
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs)
|
||||
where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
|
||||
where (category.CanBeApplied(item, null) || HasSwappableItems(category, item)) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
|
||||
|
||||
List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>();
|
||||
pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
|
||||
@@ -1799,7 +1821,7 @@ namespace Barotrauma
|
||||
// Disables the parent and only re-enables if the submarine contains valid items
|
||||
if (!category.IsWallUpgrade && drawnSubmarine?.Info != null)
|
||||
{
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0))
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0) && !HasSwappableItems(category))
|
||||
{
|
||||
parent.ToolTip = TextManager.Get("upgradecategorynotapplicable");
|
||||
parent.Enabled = false;
|
||||
|
||||
Reference in New Issue
Block a user