using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class CrewManager
{
///
/// How long the previously selected character waits doing nothing when switching to another character. Only affects idling.
///
const float CharacterWaitOnSwitch = 10.0f;
private readonly List characterInfos = new List();
private readonly List characters = new List();
private Point screenResolution;
#region UI
public GUIComponent ReportButtonFrame { get; set; }
private GUIFrame guiFrame;
private GUIFrame crewArea;
private GUIListBox crewList;
private GUIButton toggleCrewButton;
private float crewListOpenState;
private bool toggleCrewListOpen = true;
///
/// Present only in single player games. In multiplayer. The chatbox is found from GameSession.Client.
///
public ChatBox ChatBox { get; private set; }
private float prevUIScale;
public bool AllowCharacterSwitch = true;
public bool ToggleCrewListOpen
{
get { return toggleCrewListOpen; }
set
{
if (toggleCrewListOpen == value) { return; }
toggleCrewListOpen = GameMain.Config.CrewMenuOpen = value;
toggleCrewButton.Children.ForEach(c => c.SpriteEffects = toggleCrewListOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
}
}
const float CommandNodeAnimDuration = 0.2f;
public List OrderOptionButtons = new List();
private Sprite jobIndicatorBackground, previousOrderArrow, cancelIcon, crewMemberBackground;
#endregion
#region Constructors
public CrewManager(XElement element, bool isSinglePlayer)
: this(isSinglePlayer)
{
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "character") continue;
var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo);
foreach (XElement invElement in subElement.Elements())
{
if (invElement.Name.ToString().ToLowerInvariant() != "inventory") continue;
characterInfo.InventoryData = invElement;
break;
}
}
}
partial void InitProjectSpecific()
{
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent)
{
CanBeFocused = false
};
var crewAreaWithButtons = new GUIFrame(
HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform),
style: null,
color: Color.Transparent)
{
CanBeFocused = false
};
var buttonHeight = (int)(GUI.Scale * 40);
crewArea = new GUIFrame(
new RectTransform(
new Point(crewAreaWithButtons.Rect.Width, crewAreaWithButtons.Rect.Height - 3 * buttonHeight - 2 * HUDLayoutSettings.Padding),
crewAreaWithButtons.RectTransform,
Anchor.BottomLeft),
style: null,
color: Color.Transparent)
{
CanBeFocused = false
};
var buttonSize = new Point((int)(79.0f / 126.0f * (2 * buttonHeight)), 2 * buttonHeight);
var commandButton = new GUIButton(
new RectTransform(buttonSize, parent: crewAreaWithButtons.RectTransform),
style: null)
{
OnClicked = (button, userData) =>
{
ToggleCommandUI();
return true;
}
};
new GUIImage(
new RectTransform(Vector2.One, commandButton.RectTransform),
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(551, 1, 79, 126)),
scaleToFit: true)
{
Color = GUIColorSettings.InventorySlotColor * 0.8f,
HoverColor = GUIColorSettings.InventorySlotColor,
PressedColor = GUIColorSettings.InventorySlotColor,
SelectedColor = GUIColorSettings.InventorySlotColor * 0.8f,
ToolTip = TextManager.Get("inputtype.command")
};
var keybindText = new GUITextBlock(new RectTransform(Vector2.One, commandButton.RectTransform), "", font: GUI.SmallFont,
textAlignment: Alignment.TopLeft, color: GUI.Style.TextColorBright)
{
TextGetter = () =>
{
//hide the text if using a long non-default keybind
string txt = GameMain.Config.KeyBindText(InputType.CrewOrders);
return txt.Length > 2 ? "" : txt;
},
HoverTextColor = GUI.Style.TextColorBright,
Padding = Vector4.One * 3,
CanBeFocused = false
};
// AbsoluteOffset is set in UpdateProjectSpecific based on crewListOpenState
crewList = new GUIListBox(
new RectTransform(
Vector2.One,
crewArea.RectTransform),
style: null,
isScrollBarOnDefaultSide: false)
{
AutoHideScrollBar = false,
Spacing = (int)(GUI.Scale * 10)
};
toggleCrewButton = new GUIButton(
new RectTransform(
new Point(buttonHeight),
parent: crewAreaWithButtons.RectTransform)
{
AbsoluteOffset = new Point(0, 2 * buttonHeight + HUDLayoutSettings.Padding)
},
style: "UIToggleButton")
{
OnClicked = (GUIButton btn, object userdata) =>
{
ToggleCrewListOpen = !ToggleCrewListOpen;
return true;
}
};
jobIndicatorBackground = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(0, 512, 128, 128));
previousOrderArrow = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(128, 512, 128, 128));
cancelIcon = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(512, 384, 128, 128));
crewMemberBackground = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(0, 728, 320, 40));
#region Chatbox
if (IsSinglePlayer)
{
ChatBox = new ChatBox(guiFrame, isSinglePlayer: true)
{
OnEnterMessage = (textbox, text) =>
{
if (Character.Controlled?.Info == null)
{
textbox.Deselect();
textbox.Text = "";
return true;
}
textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
if (!string.IsNullOrWhiteSpace(text))
{
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
// add to local history
ChatBox.ChatManager.Store(text);
AddSinglePlayerChatMessage(
Character.Controlled.Info.Name,
msg,
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
Character.Controlled);
var headset = GetHeadset(Character.Controlled, true);
if (headset != null && headset.CanTransmit())
{
headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sendToChat: false);
}
}
textbox.Deselect();
textbox.Text = "";
if (ChatBox.CloseAfterMessageSent)
{
ChatBox.ToggleOpen = false;
ChatBox.CloseAfterMessageSent = false;
}
return true;
}
};
ChatBox.InputBox.OnTextChanged += ChatBox.TypingChatMessage;
}
#endregion
#region Reports
var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null);
if (reports.None())
{
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
return;
}
ReportButtonFrame = new GUILayoutGroup(new RectTransform(
new Point((HUDLayoutSettings.ChatBoxArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.ChatBoxArea.Height), guiFrame.RectTransform))
{
AbsoluteSpacing = (int)(5 * GUI.Scale),
UserData = "reportbuttons",
CanBeFocused = false
};
//report buttons
foreach (Order order in reports)
{
if (!order.TargetAllCharacters || order.SymbolSprite == null) { continue; }
var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null)
{
OnClicked = (GUIButton button, object userData) =>
{
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; }
SetCharacterOrder(null, order, null, Character.Controlled);
var visibleHulls = new List(Character.Controlled.GetVisibleHulls());
foreach (var hull in visibleHulls)
{
HumanAIController.PropagateHullSafety(Character.Controlled, hull);
HumanAIController.RefreshTargets(Character.Controlled, order, hull);
}
return true;
},
UserData = order,
ToolTip = order.Name
};
new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow")
{
Color = GUI.Style.Red * 0.8f,
HoverColor = GUI.Style.Red * 1.0f,
PressedColor = GUI.Style.Red * 0.6f,
UserData = "highlighted",
CanBeFocused = false,
Visible = false
};
var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite, scaleToFit: true)
{
Color = order.Color,
HoverColor = Color.Lerp(order.Color, Color.White, 0.5f),
ToolTip = order.Name,
SpriteEffects = SpriteEffects.FlipHorizontally
};
}
#endregion
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = GUI.Scale;
ToggleCrewListOpen = GameMain.Config.CrewMenuOpen;
dismissedOrderPrefab ??= Order.GetPrefab("dismissed");
}
#endregion
#region Character list management
public Rectangle GetActiveCrewArea()
{
return crewArea.Rect;
}
public IEnumerable GetCharacters()
{
return characters;
}
public IEnumerable GetCharacterInfos()
{
return characterInfos;
}
public void AddCharacter(Character character)
{
if (character.Removed)
{
DebugConsole.ThrowError("Tried to add a removed character to CrewManager!\n" + Environment.StackTrace);
return;
}
if (character.IsDead)
{
DebugConsole.ThrowError("Tried to add a dead character to CrewManager!\n" + Environment.StackTrace);
return;
}
if (!characters.Contains(character))
{
characters.Add(character);
}
if (!characterInfos.Contains(character.Info))
{
characterInfos.Add(character.Info);
}
AddCharacterToCrewList(character);
if (character is AICharacter)
{
var ai = character.AIController as HumanAIController;
if (ai == null)
{
DebugConsole.ThrowError("Error in crewmanager - attempted to give orders to a character with no HumanAIController");
return;
}
character.SetOrder(ai.CurrentOrder, "", null, false);
}
}
public void AddCharacterInfo(CharacterInfo characterInfo)
{
if (characterInfos.Contains(characterInfo))
{
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
return;
}
characterInfos.Add(characterInfo);
}
///
/// Remove the character from the crew (and crew menus).
///
/// The character to remove
/// If the character info is also removed, the character will not be visible in the round summary.
public void RemoveCharacter(Character character, bool removeInfo = false)
{
if (character == null)
{
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace);
return;
}
characters.Remove(character);
if (removeInfo) { characterInfos.Remove(character.Info); }
}
///
/// Remove info of a selected character. The character will not be visible in any menus or the round summary.
///
///
public void RemoveCharacterInfo(CharacterInfo characterInfo)
{
characterInfos.Remove(characterInfo);
}
private void AddCharacterToCrewList(Character character)
{
int width = crewList.Content.Rect.Width - HUDLayoutSettings.Padding;
int height = Math.Max(45, (int)((1.0f / 8.0f) * width));
var background = new GUIImage(
new RectTransform(new Point(width, height), parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
crewMemberBackground,
scaleToFit: true)
{
UserData = character
};
var iconRelativeSize = (float)height / background.Rect.Width;
var layoutGroup = new GUILayoutGroup(
new RectTransform(Vector2.One, parent: background.RectTransform),
isHorizontal: true,
childAnchor: Anchor.CenterLeft)
{
CanBeFocused = false,
RelativeSpacing = 0.1f * iconRelativeSize,
UserData = character
};
var jobIconBackground = new GUIImage(
new RectTransform(new Vector2(iconRelativeSize, 0.9f), layoutGroup.RectTransform),
jobIndicatorBackground,
scaleToFit: true)
{
CanBeFocused = false,
UserData = "job"
};
if (character?.Info?.Job.Prefab?.Icon != null)
{
new GUIImage(
new RectTransform(Vector2.One, jobIconBackground.RectTransform),
character.Info.Job.Prefab.Icon,
scaleToFit: true)
{
CanBeFocused = false,
Color = character.Info.Job.Prefab.UIColor,
HoverColor = character.Info.Job.Prefab.UIColor,
PressedColor = character.Info.Job.Prefab.UIColor,
SelectedColor = character.Info.Job.Prefab.UIColor
};
}
var relativeWidth = 1.0f - 4.5f * iconRelativeSize - 5 * layoutGroup.RelativeSpacing;
new GUITextBlock(
new RectTransform(
new Vector2(relativeWidth, 1.0f),
layoutGroup.RectTransform),
ToolBox.LimitString(character.Name, GUI.SubHeadingFont, (int)(relativeWidth * layoutGroup.Rect.Width)),
textColor: character.Info?.Job?.Prefab?.UIColor)
{
CanBeFocused = false
};
var characterButton = new GUIButton(
new RectTransform(
new Vector2(iconRelativeSize + layoutGroup.RelativeSpacing + relativeWidth, 1.0f),
background.RectTransform),
style: null)
{
UserData = character
};
if (IsSinglePlayer)
{
characterButton.OnClicked = CharacterClicked;
}
else
{
characterButton.CanBeFocused = false;
characterButton.CanBeSelected = false;
}
new GUIImage(
new RectTransform(new Vector2(0.5f * iconRelativeSize, 0.5f), layoutGroup.RectTransform),
style: "VerticalLine")
{
CanBeFocused = false
};
var soundIcons = new GUIFrame(new RectTransform(new Vector2(iconRelativeSize, 0.8f), layoutGroup.RectTransform), style: null)
{
CanBeFocused = false,
UserData = "soundicons"
};
new GUIImage(
new RectTransform(Vector2.One, soundIcons.RectTransform),
GUI.Style.GetComponentStyle("GUISoundIcon").Sprites[GUIComponent.ComponentState.None].FirstOrDefault().Sprite,
scaleToFit: true)
{
CanBeFocused = false,
UserData = new Pair("soundicon", 0.0f),
Visible = true
};
new GUIImage(
new RectTransform(Vector2.One, soundIcons.RectTransform),
"GUISoundIconDisabled",
scaleToFit: true)
{
CanBeFocused = true,
UserData = "soundicondisabled",
Visible = false
};
}
///
/// Sets which character is selected in the crew UI (highlight effect etc)
///
public bool CharacterClicked(GUIComponent component, object selection)
{
if (!AllowCharacterSwitch) { return false; }
Character character = selection as Character;
if (character == null || character.IsDead || character.IsUnconscious) { return false; }
SelectCharacter(character);
if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; }
return true;
}
public void ReviveCharacter(Character revivedCharacter)
{
if (crewList.Content.GetChildByUserData(revivedCharacter) is GUIComponent characterComponent)
{
crewList.Content.RemoveChild(characterComponent);
}
if (characterInfos.Contains(revivedCharacter.Info)) { AddCharacter(revivedCharacter); }
}
public void KillCharacter(Character killedCharacter)
{
if (crewList.Content.GetChildByUserData(killedCharacter) is GUIComponent characterComponent)
{
CoroutineManager.StartCoroutine(KillCharacterAnim(characterComponent));
}
RemoveCharacter(killedCharacter);
}
private IEnumerable