Merge remote-tracking branch 'upstream/master' into develop
This commit is contained in:
@@ -678,12 +678,12 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Adds the message to the single player chatbox.
|
||||
/// </summary>
|
||||
public void AddSinglePlayerChatMessage(LocalizedString senderName, LocalizedString text, ChatMessageType messageType, Character sender)
|
||||
public void AddSinglePlayerChatMessage(LocalizedString senderName, LocalizedString text, ChatMessageType messageType, Entity sender)
|
||||
{
|
||||
AddSinglePlayerChatMessage(senderName.Value, text.Value, messageType, sender);
|
||||
}
|
||||
|
||||
public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Character sender)
|
||||
public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Entity sender)
|
||||
{
|
||||
if (!IsSinglePlayer)
|
||||
{
|
||||
@@ -692,9 +692,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (string.IsNullOrEmpty(text)) { return; }
|
||||
|
||||
if (sender != null)
|
||||
if (sender is Character character)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterSpeaking(sender);
|
||||
GameMain.GameSession.CrewManager?.SetCharacterSpeaking(character);
|
||||
if (!character.IsBot)
|
||||
{
|
||||
character.TextChatVolume = 1f;
|
||||
}
|
||||
}
|
||||
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
|
||||
}
|
||||
@@ -708,9 +712,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
|
||||
if (message.Sender != null)
|
||||
if (message.SenderCharacter != null)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterSpeaking(message.Sender);
|
||||
GameMain.GameSession.CrewManager?.SetCharacterSpeaking(message.SenderCharacter);
|
||||
}
|
||||
ChatBox.AddMessage(message);
|
||||
}
|
||||
@@ -3688,6 +3692,9 @@ namespace Barotrauma
|
||||
crewList.ClearChildren();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current crew. Note that this is client-only code (only used in the single player campaign) - saving in multiplayer is handled in the server-side code of <see cref="MultiPlayerCampaign"/>.
|
||||
/// </summary>
|
||||
public XElement Save(XElement parentElement)
|
||||
{
|
||||
var element = new XElement("crew");
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Barotrauma
|
||||
buttonText = TextManager.Get("map");
|
||||
}
|
||||
else if (prevCampaignUIAutoOpenType != availableTransition &&
|
||||
(availableTransition == TransitionType.ProgressToNextEmptyLocation || availableTransition == TransitionType.ReturnToPreviousEmptyLocation))
|
||||
availableTransition == TransitionType.ProgressToNextEmptyLocation)
|
||||
{
|
||||
HintManager.OnAvailableTransition(availableTransition);
|
||||
//opening the campaign map pauses the game and prevents HintManager from running -> update it manually to get the hint to show up immediately
|
||||
|
||||
+28
-15
@@ -162,7 +162,7 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private void InitCampaignUI()
|
||||
public void InitCampaignUI()
|
||||
{
|
||||
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
|
||||
CampaignUI = new CampaignUI(this, campaignUIContainer)
|
||||
@@ -720,7 +720,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
|
||||
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, isNetworkMessage: true);
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList.ToList())
|
||||
@@ -906,6 +906,8 @@ namespace Barotrauma
|
||||
|
||||
public void ClientReadCrew(IReadMessage msg)
|
||||
{
|
||||
bool createNotification = msg.ReadBoolean();
|
||||
|
||||
ushort availableHireLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> availableHires = new List<CharacterInfo>();
|
||||
for (int i = 0; i < availableHireLength; i++)
|
||||
@@ -916,10 +918,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ushort pendingHireLength = msg.ReadUInt16();
|
||||
List<int> pendingHires = new List<int>();
|
||||
List<UInt16> pendingHires = new List<UInt16>();
|
||||
for (int i = 0; i < pendingHireLength; i++)
|
||||
{
|
||||
pendingHires.Add(msg.ReadInt32());
|
||||
pendingHires.Add(msg.ReadUInt16());
|
||||
}
|
||||
|
||||
ushort hiredLength = msg.ReadUInt16();
|
||||
@@ -934,30 +936,40 @@ namespace Barotrauma
|
||||
bool renameCrewMember = msg.ReadBoolean();
|
||||
if (renameCrewMember)
|
||||
{
|
||||
int renamedIdentifier = msg.ReadInt32();
|
||||
UInt16 renamedIdentifier = msg.ReadUInt16();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); }
|
||||
}
|
||||
|
||||
bool fireCharacter = msg.ReadBoolean();
|
||||
if (fireCharacter)
|
||||
{
|
||||
int firedIdentifier = msg.ReadInt32();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
|
||||
UInt16 firedIdentifier = msg.ReadUInt16();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
}
|
||||
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null &&
|
||||
/*can't apply until we have the latest save file*/
|
||||
!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
|
||||
{
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters, takeMoney: false); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
//can't apply until we have the latest save file
|
||||
if (!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
|
||||
{
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters, takeMoney: false, createNotification: createNotification); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is pretty nasty: setting hireables is handled through CrewManagement,
|
||||
//which is part of the Campaign UI that might not exist when the client is still initializing the round.
|
||||
//If that's the case, let's force the available hires here so they're available when the UI is created
|
||||
CurrentLocation?.ForceHireableCharacters(availableHires);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void ClientReadMoney(IReadMessage inc)
|
||||
@@ -979,6 +991,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
Bank.Balance = info.Balance;
|
||||
Bank.RewardDistribution = info.RewardDistribution;
|
||||
TryInvokeEvent(Bank, transaction.ChangedData, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Barotrauma.Abilities;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -43,13 +45,19 @@ namespace Barotrauma
|
||||
private GUIButton crewListButton, commandButton, tabMenuButton;
|
||||
private GUIImage talentPointNotification;
|
||||
|
||||
private GUIComponent respawnInfoFrame, respawnButtonContainer;
|
||||
private GUIComponent deathChoiceInfoFrame, deathChoiceButtonContainer;
|
||||
private GUITextBlock respawnInfoText;
|
||||
private GUITickBox respawnTickBox;
|
||||
private GUITickBox deathChoiceTickBox;
|
||||
private GUIButton takeOverBotButton;
|
||||
private GUIButton hrManagerButton;
|
||||
public DeathPrompt DeathPrompt;
|
||||
|
||||
private GUIImage eventLogNotification;
|
||||
|
||||
private Point prevTopLeftButtonsResolution;
|
||||
|
||||
public bool AllowHrManagerBotTakeover => GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false }
|
||||
&& Level.IsLoadedFriendlyOutpost;
|
||||
|
||||
private void CreateTopLeftButtons()
|
||||
{
|
||||
@@ -96,30 +104,63 @@ namespace Barotrauma
|
||||
|
||||
talentPointNotification = CreateNotificationIcon(tabMenuButton);
|
||||
eventLogNotification = CreateNotificationIcon(tabMenuButton);
|
||||
|
||||
respawnInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
|
||||
// The visibility of the following contents of deathChoiceInfoFrame is controlled by SetRespawnInfo()
|
||||
|
||||
deathChoiceInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform), "", wrap: true);
|
||||
respawnButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true);
|
||||
deathChoiceButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
Stretch = true,
|
||||
Visible = false
|
||||
};
|
||||
respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn"))
|
||||
|
||||
takeOverBotButton = new GUIButton(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("takeoverbotquestionprompttakeoverbot"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.GetWithVariable(
|
||||
"respawnquestionprompt", "[percentage]",
|
||||
(Math.Round(Networking.RespawnManager.SkillLossPercentageOnImmediateRespawn).ToString())),
|
||||
OnSelected = (tickbox) =>
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
DeathPrompt.CreateTakeOverBotPanel();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
takeOverBotButton.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
hrManagerButton = new GUIButton(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("npctitle.hrmanager"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is { } campaign)
|
||||
{
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI?.SelectTab(CampaignMode.InteractionType.Crew);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
hrManagerButton.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
var questionText =
|
||||
TextManager.GetWithVariable(
|
||||
"respawnquestionprompt", "[percentage]",
|
||||
((int)Math.Round(RespawnManager.SkillLossPercentageOnImmediateRespawn)).ToString());
|
||||
deathChoiceTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("respawnquestionpromptrespawn"))
|
||||
{
|
||||
ToolTip = questionText,
|
||||
OnSelected = (tickbox) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
prevTopLeftButtonsResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
}
|
||||
|
||||
@@ -150,6 +191,8 @@ namespace Barotrauma
|
||||
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
DeathPrompt?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public static GUIImage CreateNotificationIcon(GUIComponent parent, bool offset = true)
|
||||
@@ -230,16 +273,67 @@ namespace Barotrauma
|
||||
HintManager.Update();
|
||||
ObjectiveManager.VideoPlayer.Update();
|
||||
}
|
||||
|
||||
public void SetRespawnInfo(bool visible, string text, Color textColor, bool buttonsVisible, bool waitForNextRoundRespawn)
|
||||
|
||||
/// <summary>
|
||||
/// This method controls the content and visibility logic of the respawn-related GUI elements at the top left of the game screen.
|
||||
/// </summary>
|
||||
/// <param name="waitForNextRoundRespawn">Has the player chosen to wait until next round</param>
|
||||
/// <param name="hideButtons">Hide the respawn buttons even if they would otherwise be visible</param>
|
||||
public void SetRespawnInfo(string text, Color textColor, bool waitForNextRoundRespawn, bool hideButtons = false)
|
||||
{
|
||||
if (topLeftButtonGroup == null) { return; }
|
||||
respawnInfoFrame.Visible = visible;
|
||||
if (!visible) { return; }
|
||||
|
||||
bool permadeathMode = GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironmanMode = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
|
||||
bool hasRespawnOptions;
|
||||
if (permadeathMode)
|
||||
{
|
||||
// In permadeath mode you can (in ironman, must) always at least wait, and possibly buy a new character from HR or take control of a bot
|
||||
hasRespawnOptions = !ironmanMode &&
|
||||
GameMain.Client is GameClient client && (client.CharacterInfo == null || client.CharacterInfo.PermanentlyDead);
|
||||
}
|
||||
else // "classic" respawn modes
|
||||
{
|
||||
//can choose between midround respawning with a penalty or waiting
|
||||
//if we're in a non-outpost level, and either don't have an existing character or have already spawned during the round
|
||||
//(otherwise, e.g. when joining a campaign in which we have an existing character, we can respawn mid-round "for free" and there's no reason to make a choice)
|
||||
hasRespawnOptions = Level.Loaded?.Type != LevelData.LevelType.Outpost &&
|
||||
(GameMain.Client is GameClient client && (client.CharacterInfo == null || client.HasSpawned));
|
||||
}
|
||||
|
||||
// Are the death choice elements shown at all, at least with the text?
|
||||
deathChoiceInfoFrame.Visible = !text.IsNullOrEmpty() || hasRespawnOptions;
|
||||
if (!deathChoiceInfoFrame.Visible) { return; }
|
||||
respawnInfoText.Text = text;
|
||||
respawnInfoText.TextColor = textColor;
|
||||
respawnButtonContainer.Visible = buttonsVisible;
|
||||
respawnTickBox.Selected = !waitForNextRoundRespawn;
|
||||
|
||||
// Determine if we even bother considering showing the buttons
|
||||
if (GameMain.GameSession.GameMode is not CampaignMode || Character.Controlled != null)
|
||||
{
|
||||
// Disable the button container in case it was left visible earlier
|
||||
deathChoiceButtonContainer.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
deathChoiceButtonContainer.Visible = hasRespawnOptions && !hideButtons;
|
||||
if (deathChoiceButtonContainer.Visible)
|
||||
{
|
||||
hrManagerButton.Visible = AllowHrManagerBotTakeover;
|
||||
|
||||
if (permadeathMode && ironmanMode)
|
||||
{
|
||||
takeOverBotButton.Visible = false;
|
||||
deathChoiceTickBox.Visible = false;
|
||||
deathChoiceTickBox.Selected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
takeOverBotButton.Visible = permadeathMode && GameMain.NetworkMember?.ServerSettings is { AllowBotTakeoverOnPermadeath: true };
|
||||
deathChoiceTickBox.Visible = !permadeathMode;
|
||||
deathChoiceTickBox.Selected = !waitForNextRoundRespawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -81,6 +81,22 @@ namespace Barotrauma
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
processAfflictionChangesTimer -= deltaTime;
|
||||
if (processAfflictionChangesTimer <= 0.0f)
|
||||
{
|
||||
foreach (var character in charactersWithAfflictionChanges)
|
||||
{
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
ImmutableArray<NetAffliction> afflictions = GetAllAfflictions(character.CharacterHealth);
|
||||
ui?.UpdateAfflictions(new NetCrewMember(character.Info, afflictions));
|
||||
}
|
||||
ui?.UpdateCrewPanel();
|
||||
}
|
||||
charactersWithAfflictionChanges.Clear();
|
||||
processAfflictionChangesTimer = ProcessAfflictionChangesInterval;
|
||||
}
|
||||
|
||||
DateTimeOffset now = DateTimeOffset.Now;
|
||||
UpdateQueue(afflictionRequests, now, onTimeout: static callback => { callback(new AfflictionRequest(RequestResult.Timeout, ImmutableArray<NetAffliction>.Empty)); });
|
||||
UpdateQueue(pendingHealRequests, now, onTimeout: static callback => { callback(new PendingRequest(RequestResult.Timeout, NetCollection<NetCrewMember>.Empty)); });
|
||||
|
||||
Reference in New Issue
Block a user