Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -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);
}
}