Unstable v0.1300.0.1

This commit is contained in:
Markus Isberg
2021-03-05 17:00:56 +02:00
parent 64cdb32078
commit cb969c959f
199 changed files with 6043 additions and 3911 deletions
@@ -257,7 +257,7 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool Freeze { get; set; } public bool Freeze { get; set; }
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true) public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, Rectangle? overrideMouseOn = null)
{ {
prevPosition = position; prevPosition = position;
prevZoom = zoom; prevZoom = zoom;
@@ -294,7 +294,7 @@ namespace Barotrauma
} }
} }
if (allowZoom && GUI.MouseOn == null) if (allowZoom && (GUI.MouseOn == null || (overrideMouseOn?.Contains(PlayerInput.MousePosition) ?? false)))
{ {
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition); Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
Vector2 diffViewCenter; Vector2 diffViewCenter;
@@ -529,6 +529,7 @@ namespace Barotrauma
{ {
ushort infoID = inc.ReadUInt16(); ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString(); string newName = inc.ReadString();
string originalName = inc.ReadString();
int gender = inc.ReadByte(); int gender = inc.ReadByte();
int race = inc.ReadByte(); int race = inc.ReadByte();
int headSpriteID = inc.ReadByte(); int headSpriteID = inc.ReadByte();
@@ -556,7 +557,7 @@ namespace Barotrauma
} }
// TODO: animations // TODO: animations
CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile, variant) CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant)
{ {
ID = infoID, ID = infoID,
}; };
@@ -240,6 +240,8 @@ namespace Barotrauma
Character.Controlled.SelectedConstruction = null; Character.Controlled.SelectedConstruction = null;
} }
} }
HintManager.OnShowHealthInterface();
} }
} }
@@ -718,6 +720,7 @@ namespace Barotrauma
int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond); int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond);
return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(a1.Strength - a1.Strength); return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(a1.Strength - a1.Strength);
}); });
HintManager.OnAfflictionDisplayed(Character, currentDisplayedAfflictions.FirstOrDefault());
updateDisplayedAfflictionsTimer = UpdateDisplayedAfflictionsInterval; updateDisplayedAfflictionsTimer = UpdateDisplayedAfflictionsInterval;
} }
@@ -1188,7 +1191,7 @@ namespace Barotrauma
} }
} }
private Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction) public static Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction)
{ {
// No specific colors, use generic // No specific colors, use generic
if (prefab.IconColors == null) if (prefab.IconColors == null)
@@ -1208,6 +1211,8 @@ namespace Barotrauma
} }
} }
public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction);
private void UpdateAfflictionContainer(LimbHealth selectedLimb) private void UpdateAfflictionContainer(LimbHealth selectedLimb)
{ {
selectedLimbText.Text = selectedLimb == null ? "" : selectedLimb.Name; selectedLimbText.Text = selectedLimb == null ? "" : selectedLimb.Name;
@@ -1275,7 +1280,7 @@ namespace Barotrauma
var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true) var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true)
{ {
Color = GetAfflictionIconColor(affliction.Prefab, affliction), Color = GetAfflictionIconColor(affliction),
CanBeFocused = false CanBeFocused = false
}; };
afflictionIcon.PressedColor = afflictionIcon.Color; afflictionIcon.PressedColor = afflictionIcon.Color;
@@ -1911,7 +1916,7 @@ namespace Barotrauma
float alpha = MathHelper.Lerp(0.3f, 1.0f, float alpha = MathHelper.Lerp(0.3f, 1.0f,
(affliction.Strength - showIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - showIconThreshold, 10.0f)); (affliction.Strength - showIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - showIconThreshold, 10.0f));
affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction.Prefab, affliction) * alpha, 0, iconScale); affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction) * alpha, 0, iconScale);
iconPos += new Vector2(10.0f, 20.0f) * iconScale; iconPos += new Vector2(10.0f, 20.0f) * iconScale;
} }
@@ -1534,7 +1534,9 @@ namespace Barotrauma
List<string> lines = missingTags.Select(t => "\"" + t.Key + "\"\n missing from " + string.Join(", ", t.Value)).ToList(); List<string> lines = missingTags.Select(t => "\"" + t.Key + "\"\n missing from " + string.Join(", ", t.Value)).ToList();
string filePath = "missingloca.txt"; string filePath = "missingloca.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines); File.WriteAllLines(filePath, lines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
TextManager.Language = "English"; TextManager.Language = "English";
})); }));
@@ -1543,7 +1545,9 @@ namespace Barotrauma
{ {
var debugLines = EventSet.GetDebugStatistics(); var debugLines = EventSet.GetDebugStatistics();
string filePath = "eventstats.txt"; string filePath = "eventstats.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, debugLines); File.WriteAllLines(filePath, debugLines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
})); }));
@@ -1824,7 +1828,9 @@ namespace Barotrauma
lines.Add("<EntityName." + me.Identifier + ">" + me.Name + "</EntityName." + me.Identifier + ">"); lines.Add("<EntityName." + me.Identifier + ">" + me.Name + "</EntityName." + me.Identifier + ">");
lines.Add("<EntityDescription." + me.Identifier + ">" + me.Description + "</EntityDescription." + me.Identifier + ">"); lines.Add("<EntityDescription." + me.Identifier + ">" + me.Description + "</EntityDescription." + me.Identifier + ">");
} }
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines); File.WriteAllLines(filePath, lines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
})); }));
commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) => commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) =>
@@ -1843,16 +1849,15 @@ namespace Barotrauma
docs.Add(eventPrefab.ConfigElement.Document); docs.Add(eventPrefab.ConfigElement.Document);
getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier); getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier);
} }
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines); File.WriteAllLines(filePath, lines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{ {
Indent = true, Indent = true,
NewLineOnAttributes = false NewLineOnAttributes = false
}; };
foreach (XDocument doc in docs) foreach (XDocument doc in docs)
{ {
using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings)) using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings))
@@ -1861,6 +1866,7 @@ namespace Barotrauma
writer.Flush(); writer.Flush();
} }
} }
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
void getTextsFromElement(XElement element, List<string> list, string parentName) void getTextsFromElement(XElement element, List<string> list, string parentName)
{ {
@@ -2007,7 +2013,9 @@ namespace Barotrauma
lines.Add("[/table]"); lines.Add("[/table]");
lines.Add(""); lines.Add("");
} }
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines); File.WriteAllLines(filePath, lines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
})); }));
#if DEBUG #if DEBUG
@@ -2049,7 +2057,9 @@ namespace Barotrauma
commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) => commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) =>
{ {
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\propertylocalization.txt"; string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\propertylocalization.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(path, SerializableEntityEditor.MissingLocalizations); File.WriteAllLines(path, SerializableEntityEditor.MissingLocalizations);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
})); }));
commands.Add(new Command("getproperties", "Goes through the MapEntity prefabs and checks their serializable properties for localization issues.", (string[] args) => commands.Add(new Command("getproperties", "Goes through the MapEntity prefabs and checks their serializable properties for localization issues.", (string[] args) =>
@@ -560,6 +560,22 @@ namespace Barotrauma
}; };
} }
break; break;
case NetworkEventType.UNLOCKPATH:
UInt16 connectionIndex = msg.ReadUInt16();
if (GameMain.GameSession?.Map?.Connections != null)
{
if (connectionIndex >= GameMain.GameSession.Map.Connections.Count)
{
DebugConsole.ThrowError($"Failed to unlock a path on the campaign map. Connection index out of bounds (index: {connectionIndex}, number of connections: {GameMain.GameSession.Map.Connections.Count})");
}
else
{
GameMain.GameSession.Map.Connections[connectionIndex].Locked = false;
new GUIMessageBox(string.Empty, TextManager.Get("pathunlockedgeneric"),
new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128));
}
}
break;
} }
} }
} }
@@ -10,7 +10,16 @@ namespace Barotrauma
for (int i = 0; i < characterCount; i++) for (int i = 0; i < characterCount; i++)
{ {
characters.Add(Character.ReadSpawnData(msg)); Character character = Character.ReadSpawnData(msg);
characters.Add(character);
if (msg.ReadBoolean()) { requireKill.Add(character); }
if (msg.ReadBoolean())
{
requireRescue.Add(character);
#if CLIENT
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
#endif
}
ushort itemCount = msg.ReadUInt16(); ushort itemCount = msg.ReadUInt16();
for (int j = 0; j < itemCount; j++) for (int j = 0; j < itemCount; j++)
{ {
@@ -23,7 +23,7 @@ namespace Barotrauma
{ {
yield return new WaitForSeconds(1.0f); yield return new WaitForSeconds(1.0f);
} }
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon) new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon, parseRichText: true)
{ {
IconColor = Prefab.IconColor IconColor = Prefab.IconColor
}; };
@@ -6,7 +6,7 @@
{ {
foreach (Mission mission in missions) foreach (Mission mission in missions)
{ {
new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon) new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
{ {
IconColor = mission.Prefab.IconColor, IconColor = mission.Prefab.IconColor,
UserData = "missionstartmessage" UserData = "missionstartmessage"
@@ -422,12 +422,12 @@ namespace Barotrauma
} }
} }
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData) public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
{ {
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData); DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData, rtdOffset);
} }
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData) public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
{ {
if (textures.Count == 0 && !DynamicLoading) { return; } if (textures.Count == 0 && !DynamicLoading) { return; }
@@ -457,13 +457,13 @@ namespace Barotrauma
Color currentTextColor; Color currentTextColor;
if (currentRichTextData != null && i > currentRichTextData.EndIndex + lineNum) while (currentRichTextData != null && i + rtdOffset > currentRichTextData.EndIndex + lineNum)
{ {
richTextDataIndex++; richTextDataIndex++;
currentRichTextData = richTextDataIndex < richTextData.Count ? richTextData[richTextDataIndex] : null; currentRichTextData = richTextDataIndex < richTextData.Count ? richTextData[richTextDataIndex] : null;
} }
if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i && i <= currentRichTextData.EndIndex + lineNum) if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i + rtdOffset && i + rtdOffset <= currentRichTextData.EndIndex + lineNum)
{ {
currentTextColor = currentRichTextData.Color ?? color; currentTextColor = currentRichTextData.Color ?? color;
if (!string.IsNullOrEmpty(currentRichTextData.Metadata)) if (!string.IsNullOrEmpty(currentRichTextData.Metadata))
@@ -321,6 +321,10 @@ namespace Barotrauma
float prevSize = chatBox.BarSize; float prevSize = chatBox.BarSize;
string displayedText = message.TranslatedText; string displayedText = message.TranslatedText;
if (message.Type == ChatMessageType.Server)
{
RichTextData.GetRichTextData(displayedText, out displayedText);
}
string senderName = ""; string senderName = "";
Color senderColor = Color.White; Color senderColor = Color.White;
if (!string.IsNullOrWhiteSpace(message.SenderName)) if (!string.IsNullOrWhiteSpace(message.SenderName))
@@ -1,10 +1,10 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma namespace Barotrauma
{ {
@@ -189,7 +189,7 @@ namespace Barotrauma
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont);
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, (8)* height), pendingAndCrewGroup.RectTransform)) crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{ {
Spacing = 1 Spacing = 1
}; };
@@ -208,7 +208,7 @@ namespace Barotrauma
{ {
ClickSound = GUISoundType.HireRepairClick, ClickSound = GUISoundType.HireRepairClick,
ForceUpperCase = true, ForceUpperCase = true,
OnClicked = (b, o) => ValidatePendingHires(true) OnClicked = (b, o) => ValidateHires(PendingHires, true)
}; };
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall")) clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{ {
@@ -338,7 +338,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement") GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
{ {
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f) UserData = new Tuple<CharacterInfo, float>(characterInfo, skill?.Level ?? 0.0f)
}; };
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{ {
@@ -346,19 +346,22 @@ namespace Barotrauma
}; };
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width; float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform), var icon = new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2())) onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
{ {
CanBeFocused = false CanBeFocused = false
}; };
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)); GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft) GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
textColor: jobColor, textAlignment: Alignment.BottomLeft)
{ {
CanBeFocused = false CanBeFocused = false
}; };
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width); nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft) characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
{ {
@@ -384,11 +387,18 @@ namespace Barotrauma
if (listBox != crewList) if (listBox != crewList)
{ {
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center) new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
FormatCurrency(characterInfo.Salary),
textAlignment: Alignment.Center)
{ {
CanBeFocused = false CanBeFocused = false
}; };
} }
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 };
}
if (listBox == hireableList) if (listBox == hireableList)
{ {
@@ -447,6 +457,23 @@ namespace Barotrauma
} }
}; };
} }
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.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 };
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
{
Enabled = HasPermission,
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", TextManager.Get($"input.{(PlayerInput.MouseButtonsSwapped() ? "rightmouse" : "leftmouse")}")),
UserData = characterInfo,
OnClicked = CreateRenamingComponent
};
}
} }
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo) private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
@@ -479,7 +506,10 @@ namespace Barotrauma
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 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; 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"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), characterInfo.Name); 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);
if (characterInfo.HasGenders) if (characterInfo.HasGenders)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender")); new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
@@ -554,9 +584,18 @@ namespace Barotrauma
if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); } if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo)); pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
pendingList.UpdateScrollBarSize(); pendingList.UpdateScrollBarSize();
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData); // Server will reset the names to originals in multiplayer
hireableList.UpdateScrollBarSize(); if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) &&
hireableList.Content.Children.None(c => c.UserData is Tuple<CharacterInfo, float> userData && userData.Item1.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
{
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
hireableList.UpdateScrollBarSize();
}
if (setTotalHireCost) { SetTotalHireCost(); } if (setTotalHireCost) { SetTotalHireCost(); }
if (createNetworkMessage) { SendCrewState(true); } if (createNetworkMessage) { SendCrewState(true); }
return true; return true;
@@ -573,36 +612,41 @@ namespace Barotrauma
{ {
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; } if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
int total = 0; int total = 0;
pendingList.Content.Children.ForEach(c => total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary); pendingList.Content.Children.ForEach(c =>
{
total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary;
});
totalBlock.Text = FormatCurrency(total); totalBlock.Text = FormatCurrency(total);
bool enoughMoney = campaign != null ? total <= campaign.Money : true; bool enoughMoney = campaign != null ? total <= campaign.Money : true;
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red; totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any(); validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
} }
public bool ValidatePendingHires(bool createNetworkEvent = false) public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
{ {
List<CharacterInfo> hires = new List<CharacterInfo>(); if (hires == null || hires.None()) { return false; }
int total = 0;
foreach (GUIComponent c in pendingList.Content.Children.ToList())
{
if (c.UserData is Tuple<CharacterInfo, float> info)
{
hires.Add(info.Item1);
total += info.Item1.Salary;
}
}
if (hires.None() || total > campaign.Money) { return false; } List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
hires.ForEach(hireInfo =>
{
if(campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
{
nonDuplicateHires.Add(hireInfo);
}
});
if (nonDuplicateHires.None()) { return false; }
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
if (total > campaign.Money) { return false; }
bool atLeastOneHired = false; bool atLeastOneHired = false;
foreach (CharacterInfo ci in hires) foreach (CharacterInfo ci in nonDuplicateHires)
{ {
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci)) if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
{ {
atLeastOneHired = true; atLeastOneHired = true;
PendingHires.Remove(ci);
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == ci));
} }
else else
{ {
@@ -629,6 +673,93 @@ namespace Barotrauma
return false; return false;
} }
private bool CreateRenamingComponent(GUIButton button, object userData)
{
if (!HasPermission || !(userData is CharacterInfo characterInfo)) { return false; }
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black * 0.7f);
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
{
MaxSize = new Point(400, 300).Multiply(GUI.Scale)
});
var layoutGroup = new GUILayoutGroup(new RectTransform((frame.Rect.Size - GUIStyle.ItemFrameMargin).Multiply(new Vector2(0.75f, 1.0f)), frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.02f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
var groupElementSize = new Vector2(1.0f, 0.25f);
var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform))
{
MaxTextLength = Client.MaxNameLength
};
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm"))
{
OnClicked = (button, userData) =>
{
if (RenameCharacter(characterInfo, nameBox.Text?.Trim()))
{
parentComponent.RemoveChild(outerGlowFrame);
return true;
}
else
{
nameBox.Flash(color: Color.Red);
return false;
}
}
};
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("cancel"))
{
OnClicked = (button, userData) =>
{
parentComponent.RemoveChild(outerGlowFrame);
return true;
}
};
layoutGroup.Recalculate();
return true;
}
public bool RenameCharacter(CharacterInfo characterInfo, string newName)
{
if (characterInfo == null || string.IsNullOrEmpty(newName)) { return false; }
if (newName == characterInfo.Name) { return false; }
if (GameMain.IsMultiplayer)
{
SendCrewState(false, renameCharacter: (characterInfo, newName));
}
else
{
var crewComponent = crewList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
if (crewComponent != null)
{
crewList.Content.RemoveChild(crewComponent);
campaign.CrewManager.RenameCharacter(characterInfo, newName);
CreateCharacterFrame(characterInfo, crewList);
SortCharacters(crewList, SortingMethod.JobAsc);
}
else
{
var pendingComponent = pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
if (pendingComponent != null)
{
pendingList.Content.RemoveChild(pendingComponent);
campaign.Map.CurrentLocation.HireManager.RenameCharacter(characterInfo, newName);
CreateCharacterFrame(characterInfo, pendingList);
SortCharacters(pendingList, SortingMethod.JobAsc);
SetTotalHireCost();
}
else
{
return false;
}
}
}
return true;
}
private bool FireCharacter(GUIButton button, object selection) private bool FireCharacter(GUIButton button, object selection)
{ {
if (!(selection is CharacterInfo characterInfo)) { return false; } if (!(selection is CharacterInfo characterInfo)) { return false; }
@@ -649,9 +780,10 @@ namespace Barotrauma
UpdateLocationView(campaign.Map.CurrentLocation, false); UpdateLocationView(campaign.Map.CurrentLocation, false);
} }
if ((GUI.MouseOn?.UserData as Tuple<CharacterInfo, float>)?.Item1 is CharacterInfo characterInfo) (GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
if (highlightedFrame != null && highlightedInfo != null)
{ {
if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData) if (characterPreviewFrame == null || highlightedInfo != characterPreviewFrame.UserData)
{ {
GUIComponent component = GUI.MouseOn; GUIComponent component = GUI.MouseOn;
GUIListBox listBox = null; GUIListBox listBox = null;
@@ -674,7 +806,7 @@ namespace Barotrauma
if (listBox != null) if (listBox != null)
{ {
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo); SelectCharacter(listBox, highlightedFrame as GUIFrame, highlightedInfo);
} }
} }
else else
@@ -688,6 +820,27 @@ namespace Barotrauma
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame); characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null; characterPreviewFrame = null;
} }
static (GUIComponent, CharacterInfo) FindHighlightedCharacter(GUIComponent c)
{
if (c == null)
{
return default;
}
if (c.UserData is Tuple<CharacterInfo, float> highlightedData)
{
return (c, highlightedData.Item1);
}
if (c.Parent != null)
{
if (c.Parent is GUIListBox)
{
return default;
}
return FindHighlightedCharacter(c.Parent);
}
return default;
}
} }
public void SetPendingHires(List<int> characterInfos, Location location) public void SetPendingHires(List<int> characterInfos, Location location)
@@ -700,7 +853,7 @@ namespace Barotrauma
PendingHires.Clear(); PendingHires.Clear();
foreach (int identifier in characterInfos) foreach (int identifier in characterInfos)
{ {
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier); CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier);
if (match != null) if (match != null)
{ {
PendingHires.Add(match); PendingHires.Add(match);
@@ -717,9 +870,10 @@ namespace Barotrauma
/// Notify the server of crew changes /// Notify the server of crew changes
/// </summary> /// </summary>
/// <param name="updatePending">When set to true will tell the server to update the pending hires</param> /// <param name="updatePending">When set to true will tell the server to update the pending hires</param>
/// <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="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> /// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false) public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
{ {
if (campaign is MultiPlayerCampaign) if (campaign is MultiPlayerCampaign)
{ {
@@ -732,12 +886,23 @@ namespace Barotrauma
msg.Write((ushort)PendingHires.Count); msg.Write((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires) foreach (CharacterInfo pendingHire in PendingHires)
{ {
msg.Write(pendingHire.GetIdentifier()); msg.Write(pendingHire.GetIdentifierUsingOriginalName());
} }
} }
msg.Write(validateHires); msg.Write(validateHires);
bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName);
msg.Write(validRenaming);
if (validRenaming)
{
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
msg.Write(identifier);
msg.Write(renameCharacter.newName);
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
msg.Write(existingCrewMember);
}
msg.Write(firedCharacter != null); msg.Write(firedCharacter != null);
if (firedCharacter != null) if (firedCharacter != null)
{ {
@@ -1338,9 +1338,9 @@ namespace Barotrauma
} }
} }
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1) public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
{ {
DrawLine(sb, t, start, end, clr, depth, width); DrawLine(sb, t, start, end, clr, depth, (int)width);
} }
public static void DrawLine(SpriteBatch sb, Sprite sprite, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1) public static void DrawLine(SpriteBatch sb, Sprite sprite, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
@@ -1407,7 +1407,7 @@ namespace Barotrauma
font.DrawStringWithColors(sb, text, pos, color, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, depth, richTextData); font.DrawStringWithColors(sb, text, pos, color, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, depth, richTextData);
} }
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
{ {
if (size.X < 0) if (size.X < 0)
{ {
@@ -1422,7 +1422,7 @@ namespace Barotrauma
DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness); DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness);
} }
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
{ {
if (isFilled) if (isFilled)
{ {
@@ -1430,15 +1430,15 @@ namespace Barotrauma
} }
else else
{ {
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); Rectangle srcRect = new Rectangle(0, 0, 1, 1);
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); sb.Draw(t, new Vector2(rect.X, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height), SpriteEffects.None, depth);
sb.Draw(t, new Vector2(rect.X + thickness, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); sb.Draw(t, new Vector2(rect.X + thickness, rect.Bottom - thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + rect.Width - thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); sb.Draw(t, new Vector2(rect.Right - thickness, rect.Y + thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height - thickness * 2f), SpriteEffects.None, depth);
} }
} }
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, int thickness = 1) public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1)
{ {
Matrix rotate = Matrix.CreateRotationZ(rotation); Matrix rotate = Matrix.CreateRotationZ(rotation);
@@ -1455,7 +1455,7 @@ namespace Barotrauma
DrawLine(sb, bottomLeft, topLeft, clr, depth, thickness); DrawLine(sb, bottomLeft, topLeft, clr, depth, thickness);
} }
public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, int thickness = 1) public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, float thickness = 1)
{ {
if (corners.Length != 4) if (corners.Length != 4)
{ {
@@ -705,6 +705,29 @@ namespace Barotrauma
if (!Visible) return; if (!Visible) return;
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData); DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
} }
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Vector2 pos, List<RichTextData> richTextData = null)
{
if (Tutorials.Tutorial.ContentRunning) { return; }
int width = (int)(400 * GUI.Scale);
int height = (int)(18 * GUI.Scale);
Point padding = new Point((int)(10 * GUI.Scale));
if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip)
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
}
toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint();
toolTipBlock.SetTextPos();
toolTipBlock.DrawManually(spriteBatch);
}
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List<RichTextData> richTextData = null) public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List<RichTextData> richTextData = null)
{ {
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -22,11 +23,11 @@ namespace Barotrauma
{ {
Default, Default,
InGame, InGame,
Vote Vote,
Hint
} }
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>(); public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
//public GUIFrame BackgroundFrame { get; private set; }
public GUILayoutGroup Content { get; private set; } public GUILayoutGroup Content { get; private set; }
public GUIFrame InnerFrame { get; private set; } public GUIFrame InnerFrame { get; private set; }
public GUITextBlock Header { get; private set; } public GUITextBlock Header { get; private set; }
@@ -58,8 +59,6 @@ namespace Barotrauma
public bool AutoClose; public bool AutoClose;
private readonly bool alwaysVisible;
private float openState; private float openState;
private float iconState; private float iconState;
private bool iconSwitching; private bool iconSwitching;
@@ -77,10 +76,17 @@ namespace Barotrauma
this.Buttons[0].OnClicked = Close; this.Buttons[0].OnClicked = Close;
} }
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null) public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, bool parseRichText = false)
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox") : base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
{ {
int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0; int width = (int)(DefaultWidth * type switch
{
Type.Default => 1.0f,
Type.Hint => 1.25f,
_ => 1.5f
});
int height = 0;
if (relativeSize.HasValue) if (relativeSize.HasValue)
{ {
width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X); width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X);
@@ -107,6 +113,7 @@ namespace Barotrauma
Anchor anchor = type switch Anchor anchor = type switch
{ {
Type.InGame => Anchor.TopCenter, Type.InGame => Anchor.TopCenter,
Type.Hint => Anchor.TopRight,
Type.Vote => Anchor.TopRight, Type.Vote => Anchor.TopRight,
_ => Anchor.Center _ => Anchor.Center
}; };
@@ -127,13 +134,13 @@ namespace Barotrauma
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true); headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this); GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text)) if (!string.IsNullOrWhiteSpace(text))
{ {
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this); GUI.Style.Apply(Text, "", this);
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height); new Point(Text.Rect.Width, Text.Rect.Height);
@@ -180,7 +187,6 @@ namespace Barotrauma
else if (type == Type.InGame) else if (type == Type.InGame)
{ {
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight); InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
alwaysVisible = true;
CanBeFocused = false; CanBeFocused = false;
AutoClose = true; AutoClose = true;
GUI.Style.Apply(InnerFrame, "", this); GUI.Style.Apply(InnerFrame, "", this);
@@ -235,13 +241,13 @@ namespace Barotrauma
}; };
} }
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true); Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this); GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text)) if (!string.IsNullOrWhiteSpace(text))
{ {
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this); GUI.Style.Apply(Text, "", this);
Content.Recalculate(); Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
@@ -266,30 +272,204 @@ namespace Barotrauma
} }
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y); Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
} }
else if (type == Type.Hint)
{
CanBeFocused = false;
GUI.Style.Apply(InnerFrame, "", this);
Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = absoluteSpacing.Y,
Stretch = true
};
var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
int iconMaxHeight = 0;
if (icon != null)
{
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
iconMaxHeight = (int)Icon.Sprite.size.Y;
}
else
{
bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle);
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform),
iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true);
if (!iconStyleDefined)
{
Icon.Color = Color.Orange;
}
iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40);
}
iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight);
Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight);
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform))
{
AbsoluteSpacing = absoluteSpacing.Y,
};
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null);
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.dontshowagain"))
{
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
UserData = "dontshowagain"
};
//var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform),
// TextManager.Get("hintmessagebox.disablehints"))
//{
// ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
// UserData = "disablehints"
//};
Buttons = new List<GUIButton>(1)
{
new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight),
text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall")
{
OnClicked = Close
}
};
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("hintmessagebox.dismiss")} ([InputType.{closeInput.Value}])");
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
}
};
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
GUI.Style.Apply(Text, "", this);
Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
if (string.IsNullOrWhiteSpace(headerText))
{
Header.RectTransform.Parent = null;
Content.ChildAnchor = Anchor.Center;
}
}
if (height == 0)
{
height = absoluteSpacing.Y;
int upperContainerHeight = absoluteSpacing.Y;
if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; }
if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; }
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
height += upperContainerHeight;
height += absoluteSpacing.Y;
height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight);
height += absoluteSpacing.Y;
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
verticalLayoutGroup.Recalculate();
topHorizontalLayoutGroup.Recalculate();
Content.Recalculate();
tickBoxLayoutGroup.Recalculate();
}
InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height);
Point GetVerticalLayoutGroupSize()
{
return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2);
}
}
MessageBoxes.Add(this); MessageBoxes.Add(this);
} }
/// <summary>
/// Use to create a message box of Hint type
/// </summary>
public GUIMessageBox(string hintIdentifier, string text, Sprite icon) : this("", text, new string[0], textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
{
if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
{
dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain;
dontShowAgainTickBox.UserData = hintIdentifier;
}
if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox)
{
disableHintsTickBox.OnSelected = HintManager.OnDisableHints;
disableHintsTickBox.UserData = hintIdentifier;
}
}
private static Type[] messageBoxTypes;
public static void AddActiveToGUIUpdateList() public static void AddActiveToGUIUpdateList()
{ {
for (int i = 0; i < MessageBoxes.Count; i++) messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type));
foreach (var type in messageBoxTypes)
{ {
if (MessageBoxes[i] is GUIMessageBox alwaysVisibleMsgBox && alwaysVisibleMsgBox.alwaysVisible) for (int i = 0; i < MessageBoxes.Count; i++)
{ {
alwaysVisibleMsgBox.AddToGUIUpdateList(); if (MessageBoxes[i] == null) { continue; }
break; if (!(MessageBoxes[i] is GUIMessageBox messageBox))
} {
} if (type == Type.Default)
for (int i = MessageBoxes.Count - 1; i >= 0; i--) {
{ // Message box not of type GUIMessageBox is likely the round summary
if (MessageBoxes[i].UserData as string == "verificationprompt" || MessageBoxes[i].AddToGUIUpdateList();
MessageBoxes[i].UserData as string == "bugreporter") break;
{ }
continue; continue;
} }
if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible) if (messageBox.type != type) { continue; }
{
MessageBoxes[i].AddToGUIUpdateList(); // These are handled separately in GUI.HandlePersistingElements()
if (MessageBoxes[i].UserData as string == "verificationprompt") { continue; }
if (MessageBoxes[i].UserData as string == "bugreporter") { continue; }
messageBox.AddToGUIUpdateList();
break; break;
} }
} }
@@ -337,11 +517,21 @@ namespace Barotrauma
} }
} }
if (type == Type.InGame) if (type == Type.InGame || type == Type.Hint)
{ {
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight); Vector2 initialPos, defaultPos, endPos;
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale); if (type == Type.InGame)
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y); {
initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
}
else
{
initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height);
defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.MessageAreaTop.Height + GUI.IntScale(64));
endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y);
}
if (!closing) if (!closing)
{ {
@@ -428,7 +618,7 @@ namespace Barotrauma
public void Close() public void Close()
{ {
if (type == Type.InGame) if (type == Type.InGame || type == Type.Hint)
{ {
closing = true; closing = true;
} }
@@ -35,6 +35,7 @@ namespace Barotrauma
public readonly Sprite[] CursorSprite = new Sprite[7]; public readonly Sprite[] CursorSprite = new Sprite[7];
public UISprite RadiationSprite { get; private set; } public UISprite RadiationSprite { get; private set; }
public SpriteSheet RadiationAnimSpriteSheet { get; private set; }
public UISprite UIGlow { get; private set; } public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; } public UISprite UIGlowCircular { get; private set; }
@@ -214,6 +215,9 @@ namespace Barotrauma
case "radiation": case "radiation":
RadiationSprite = new UISprite(subElement); RadiationSprite = new UISprite(subElement);
break; break;
case "radiationanimspritesheet":
RadiationAnimSpriteSheet = new SpriteSheet(subElement);
break;
case "uiglowcircular": case "uiglowcircular":
UIGlowCircular = new UISprite(subElement); UIGlowCircular = new UISprite(subElement);
break; break;
@@ -279,7 +279,8 @@ namespace Barotrauma
/// If the rectT height is set 0, the height is calculated from the text. /// If the rectT height is set 0, the height is calculated from the text.
/// </summary> /// </summary>
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null, public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false) Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
bool playerInput = false, bool parseRichText = false)
: base(style, rectT) : base(style, rectT)
{ {
if (color.HasValue) if (color.HasValue)
@@ -289,7 +290,13 @@ namespace Barotrauma
if (textColor.HasValue) if (textColor.HasValue)
{ {
OverrideTextColor(textColor.Value); OverrideTextColor(textColor.Value);
} }
if (parseRichText)
{
richTextData = RichTextData.GetRichTextData(text, out text);
hasColorHighlight = richTextData != null;
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font, //if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
//use the default CJK font as a fallback //use the default CJK font as a fallback
@@ -70,6 +70,14 @@ namespace Barotrauma
} }
private string selectedTip; private string selectedTip;
private List<RichTextData> selectedTipRichTextData;
private bool selectedTipRichTextUnparsed;
private void SetSelectedTip(string tip)
{
selectedTip = tip;
selectedTipRichTextData = null;
selectedTipRichTextUnparsed = true;
}
private readonly object loadMutex = new object(); private readonly object loadMutex = new object();
private float? loadState; private float? loadState;
@@ -115,7 +123,7 @@ namespace Barotrauma
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png"); overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero); noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true; DrawLoadingText = true;
selectedTip = TextManager.Get("LoadingScreenTip", true); SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
} }
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime) public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
@@ -215,14 +223,34 @@ namespace Barotrauma
if (GUI.Font != null && selectedTip != null) if (GUI.Font != null && selectedTip != null)
{ {
if (selectedTipRichTextUnparsed)
{
selectedTipRichTextData = RichTextData.GetRichTextData(selectedTip, out selectedTip);
selectedTipRichTextUnparsed = false;
}
string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font); string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font);
string[] lines = wrappedTip.Split('\n'); string[] lines = wrappedTip.Split('\n');
float lineHeight = GUI.Font.MeasureString(selectedTip).Y; float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
for (int i = 0; i < lines.Length; i++) if (selectedTipRichTextData != null)
{ {
GUI.Font.DrawString(spriteBatch, lines[i], int rtdOffset = 0;
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White); for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawStringWithColors(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White,
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTipRichTextData, rtdOffset);
rtdOffset += lines[i].Length;
}
}
else
{
for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawString(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
}
} }
} }
@@ -302,7 +330,7 @@ namespace Barotrauma
{ {
GameMain.Config.Language = language; GameMain.Config.Language = language;
//reload tip in the selected language //reload tip in the selected language
selectedTip = TextManager.Get("LoadingScreenTip", true); SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
GameMain.Config.SetDefaultBindings(legacy: false); GameMain.Config.SetDefaultBindings(legacy: false);
GameMain.Config.CheckBindings(useDefaults: true); GameMain.Config.CheckBindings(useDefaults: true);
WaitForLanguageSelection = false; WaitForLanguageSelection = false;
@@ -364,7 +392,7 @@ namespace Barotrauma
{ {
drawn = false; drawn = false;
LoadState = null; LoadState = null;
selectedTip = TextManager.Get("LoadingScreenTip", true); SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture; currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn) while (!drawn)
@@ -1284,7 +1284,8 @@ namespace Barotrauma
// Add items on the sub(s) // Add items on the sub(s)
Submarine.MainSub?.GetItems(true) Submarine.MainSub?.GetItems(true)
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) && .Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
ItemAndAllContainersInteractable(i))
.ForEach(i => AddToOwnedItems(i.Prefab)); .ForEach(i => AddToOwnedItems(i.Prefab));
// Add items in character inventories // Add items in character inventories
@@ -1302,6 +1303,16 @@ namespace Barotrauma
ownedItemsUpdateTimer = 0.0f; ownedItemsUpdateTimer = 0.0f;
static bool ItemAndAllContainersInteractable(Item item)
{
do
{
if (!item.IsPlayerTeamInteractable) { return false; }
item = item.Container;
} while (item != null);
return true;
}
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1) void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
{ {
if (OwnedItems.ContainsKey(itemPrefab)) if (OwnedItems.ContainsKey(itemPrefab))
@@ -882,10 +882,15 @@ namespace Barotrauma
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null); GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft); GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft);
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward)); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
var missionDescriptionRichTextData = RichTextData.GetRichTextData(mission.Description, out string missionDescriptionString);
var missionRewardRichTextData = RichTextData.GetRichTextData(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), out string missionRewardString);
missionNameString = ToolBox.WrapText(missionNameString, missionTextGroup.Rect.Width, GUI.LargeFont);
missionDescriptionString = ToolBox.WrapText(missionDescriptionString, missionTextGroup.Rect.Width, GUI.Font);
missionRewardString = ToolBox.WrapText(missionRewardString, missionTextGroup.Rect.Width, GUI.Font);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString); Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString); Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
@@ -901,12 +906,12 @@ namespace Barotrauma
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio)); int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight); Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor, HoverColor = mission.Prefab.IconColor }; new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor };
} }
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameRichTextData, missionNameString, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardRichTextData, missionRewardString);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionRichTextData, missionDescriptionString);
} }
} }
else else
{ {
@@ -465,7 +465,28 @@ namespace Barotrauma
while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; } while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; }
} }
#if DEBUG
if (Config.ModBreakerMode)
{
Config.SelectCorePackage(ContentPackage.CorePackages.GetRandom());
foreach (var regularPackage in ContentPackage.RegularPackages)
{
if (Rand.Range(0.0, 1.0) <= 0.5)
{
Config.EnableRegularPackage(regularPackage);
}
else
{
Config.DisableRegularPackage(regularPackage);
}
}
ContentPackage.SortContentPackages(p =>
{
return Rand.Int(int.MaxValue);
});
}
#endif
if (Config.AllEnabledPackages.None()) if (Config.AllEnabledPackages.None())
{ {
@@ -535,6 +556,7 @@ namespace Barotrauma
Order.Init(); Order.Init();
EventManagerSettings.Init(); EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature)); BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
HintManager.Init();
TitleScreen.LoadState = 50.0f; TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -920,6 +942,8 @@ namespace Barotrauma
Client.AddToGUIUpdateList(); Client.AddToGUIUpdateList();
} }
SubmarinePreview.AddToGUIUpdateList();
FileSelection.AddToGUIUpdateList(); FileSelection.AddToGUIUpdateList();
DebugConsole.AddToGUIUpdateList(); DebugConsole.AddToGUIUpdateList();
@@ -330,7 +330,10 @@ namespace Barotrauma
if (removeInfo) { characterInfos.Remove(character.Info); } if (removeInfo) { characterInfos.Remove(character.Info); }
} }
private void AddCharacterToCrewList(Character character) /// <summary>
/// Add character to the list without actually adding it to the crew
/// </summary>
public void AddCharacterToCrewList(Character character)
{ {
if (character == null) { return; } if (character == null) { return; }
@@ -416,7 +419,8 @@ namespace Barotrauma
font: font, font: font,
textColor: character.Info?.Job?.Prefab?.UIColor) textColor: character.Info?.Job?.Prefab?.UIColor)
{ {
CanBeFocused = false CanBeFocused = false,
UserData = "name"
}; };
nameBlock.Text = ToolBox.LimitString(character.Name, font, (int)nameBlock.Rect.Width); nameBlock.Text = ToolBox.LimitString(character.Name, font, (int)nameBlock.Rect.Width);
@@ -429,22 +433,7 @@ namespace Barotrauma
{ {
UserData = character UserData = character
}; };
SetCharacterButtonTooltip(characterButton);
// Only create a tooltip if the name doesn't fit the name block
if (nameBlock.Text.EndsWith("..."))
{
var characterTooltip = character.Name;
if (character.Info?.Job?.Name != null) { characterTooltip += " (" + character.Info.Job.Name + ")"; };
characterButton.ToolTip = characterTooltip;
if (character.Info?.Job?.Prefab != null)
{
characterButton.TooltipRichTextData = new List<RichTextData>() { new RichTextData()
{
Color = character.Info.Job.Prefab.UIColor,
EndIndex = characterTooltip.Length - 1
}};
}
}
if (IsSinglePlayer) if (IsSinglePlayer)
{ {
@@ -482,6 +471,14 @@ namespace Barotrauma
UserData = character UserData = character
}; };
currentOrderList.RectTransform.IsFixedSize = true; currentOrderList.RectTransform.IsFixedSize = true;
currentOrderList.OnAddedToGUIUpdateList += (component) =>
{
if (component is GUIListBox list)
{
list.CanBeFocused = CanIssueOrders;
list.CanDragElements = CanIssueOrders && list.Content.CountChildren > 1;
}
};
// Previous orders // Previous orders
new GUILayoutGroup(new RectTransform(Vector2.One, parent: orderGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) new GUILayoutGroup(new RectTransform(Vector2.One, parent: orderGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
@@ -526,14 +523,26 @@ namespace Barotrauma
}; };
} }
private void SetCharacterButtonTooltip(GUIButton characterButton)
{
var character = (Character)characterButton.UserData;
if (character?.Info?.Job?.Prefab == null) { return; }
string color = XMLExtensions.ColorToString(character.Info.Job.Prefab.UIColor);
string tooltip = $"‖color:{color}‖{character.Name} ({character.Info.Job.Name})‖color:end‖";
var richTextData = RichTextData.GetRichTextData(tooltip, out string sanitizedTooltip);
characterButton.ToolTip = sanitizedTooltip;
characterButton.TooltipRichTextData = richTextData;
}
/// <summary> /// <summary>
/// Sets which character is selected in the crew UI (highlight effect etc) /// Sets which character is selected in the crew UI (highlight effect etc)
/// </summary> /// </summary>
public bool CharacterClicked(GUIComponent component, object selection) public bool CharacterClicked(GUIComponent component, object selection)
{ {
if (!AllowCharacterSwitch) { return false; } if (!AllowCharacterSwitch) { return false; }
Character character = selection as Character; if (!(selection is Character character) || character.IsDead || character.IsUnconscious) { return false; }
if (character == null || character.IsDead || character.IsUnconscious) { return false; } if (!character.IsOnPlayerTeam) { return false; }
SelectCharacter(character); SelectCharacter(character);
if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; } if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; }
return true; return true;
@@ -588,6 +597,15 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
partial void RenameCharacterProjSpecific(CharacterInfo characterInfo)
{
if (!(crewList.Content.GetChildByUserData(characterInfo?.Character) is GUIComponent characterComponent)) { return; }
if (!(characterComponent.FindChild("name", recursive: true) is GUITextBlock nameBlock)) { return; }
nameBlock.Text = ToolBox.LimitString(characterInfo.Name, nameBlock.Font, nameBlock.Rect.Width);
if (!(characterComponent.FindChild(c => c is GUIButton && c.UserData == characterInfo?.Character) is GUIButton characterButton)) { return; }
SetCharacterButtonTooltip(characterButton);
}
#endregion #endregion
#region Dialog #region Dialog
@@ -835,7 +853,6 @@ namespace Barotrauma
if (order == null || order.Identifier == dismissedOrderPrefab.Identifier || updatedExistingIcon) if (order == null || order.Identifier == dismissedOrderPrefab.Identifier || updatedExistingIcon)
{ {
currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1;
RearrangeIcons(); RearrangeIcons();
return; return;
} }
@@ -874,7 +891,6 @@ namespace Barotrauma
nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex); nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex);
} }
currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1;
RearrangeIcons(); RearrangeIcons();
void RearrangeIcons() void RearrangeIcons()
@@ -1276,6 +1292,7 @@ namespace Barotrauma
} }
DisableCommandUI(); DisableCommandUI();
Character.Controlled = character; Character.Controlled = character;
HintManager.OnChangeCharacter();
} }
private int TryAdjustIndex(int amount) private int TryAdjustIndex(int amount)
@@ -1537,6 +1554,11 @@ namespace Barotrauma
if (characterComponent.UserData is Character character) if (characterComponent.UserData is Character character)
{ {
characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID; characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID;
if (character.TeamID == CharacterTeamType.FriendlyNPC && Character.Controlled != null &&
(character.CurrentHull == Character.Controlled.CurrentHull || Vector2.DistanceSquared(Character.Controlled.WorldPosition, character.WorldPosition) < 500.0f * 500.0f))
{
characterComponent.Visible = true;
}
if (characterComponent.Visible) if (characterComponent.Visible)
{ {
if (character == Character.Controlled && characterComponent.State != GUIComponent.ComponentState.Selected) if (character == Character.Controlled && characterComponent.State != GUIComponent.ComponentState.Selected)
@@ -1817,6 +1839,8 @@ namespace Barotrauma
{ {
Character.Controlled.dontFollowCursor = true; Character.Controlled.dontFollowCursor = true;
} }
HintManager.OnShowCommandInterface();
} }
private void ToggleCommandUI() private void ToggleCommandUI()
@@ -3218,14 +3242,14 @@ namespace Barotrauma
#endif #endif
if (order.Identifier == dismissedOrderPrefab.Identifier) if (order.Identifier == dismissedOrderPrefab.Identifier)
{ {
return characters.FindAll(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList(); return characters.Union(GetOrderableFriendlyNPCs()).Where(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList();
} }
return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList(); return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList();
} }
private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf) private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf)
{ {
return characters.FindAll(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)) return characters.Where(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)).Union(GetOrderableFriendlyNPCs())
// 1. Prioritize those who are on the same submarine than the controlled character // 1. Prioritize those who are on the same submarine than the controlled character
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine) .OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
// 2. Prioritize those who are already ordered to operate the item target of the new 'operate' order, or given the same maintenance order as now issued // 2. Prioritize those who are already ordered to operate the item target of the new 'operate' order, or given the same maintenance order as now issued
@@ -3242,6 +3266,12 @@ namespace Barotrauma
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill)); .ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
} }
private IEnumerable<Character> GetOrderableFriendlyNPCs()
{
return crewList.Content.Children.Where(c => c.UserData is Character character && character.TeamID == CharacterTeamType.FriendlyNPC).Select(c => (Character)c.UserData);
}
#endregion #endregion
#endregion #endregion
@@ -63,7 +63,7 @@ namespace Barotrauma
{ {
new GUIMessageBox( new GUIMessageBox(
mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name, mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,
mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon) mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
{ {
IconColor = mission.Prefab.IconColor, IconColor = mission.Prefab.IconColor,
UserData = "missionstartmessage" UserData = "missionstartmessage"
@@ -437,8 +437,10 @@ namespace Barotrauma
{ {
ShowCampaignUI = false; ShowCampaignUI = false;
} }
HintManager.OnAvailableTransition(transitionType);
} }
} }
public override void End(TransitionType transitionType = TransitionType.None) public override void End(TransitionType transitionType = TransitionType.None)
{ {
base.End(transitionType); base.End(transitionType);
@@ -649,7 +651,7 @@ namespace Barotrauma
{ {
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer); string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed); GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure, mapSeed);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode; campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID; campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -777,16 +779,29 @@ namespace Barotrauma
{ {
pendingHires.Add(msg.ReadInt32()); pendingHires.Add(msg.ReadInt32());
} }
bool validateHires = msg.ReadBoolean(); ushort hiredLength = msg.ReadUInt16();
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
for (int i = 0; i < hiredLength; i++)
{
CharacterInfo hired = CharacterInfo.ClientRead("human", msg);
hired.Salary = msg.ReadInt32();
hiredCharacters.Add(hired);
}
bool renameCrewMember = msg.ReadBoolean();
if (renameCrewMember)
{
int renamedIdentifier = msg.ReadInt32();
string newName = msg.ReadString();
CharacterInfo renamedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); }
}
bool fireCharacter = msg.ReadBoolean(); bool fireCharacter = msg.ReadBoolean();
int firedIdentifier = -1;
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
if (fireCharacter) if (fireCharacter)
{ {
int firedIdentifier = msg.ReadInt32();
CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier); CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
// this one might and is allowed to be null since the character is already fired on the original sender's game // 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 (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
@@ -794,10 +809,10 @@ namespace Barotrauma
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null) if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
{ {
CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires); CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); } if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters); }
CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation); CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); } if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
} }
} }
@@ -57,11 +57,12 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Instantiates a new single player campaign /// Instantiates a new single player campaign
/// </summary> /// </summary>
private SinglePlayerCampaign(string mapSeed) : base(GameModePreset.SinglePlayerCampaign) private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign)
{ {
CampaignMetadata = new CampaignMetadata(this); CampaignMetadata = new CampaignMetadata(this);
UpgradeManager = new UpgradeManager(this); UpgradeManager = new UpgradeManager(this);
map = new Map(this, mapSeed); map = new Map(this, mapSeed, settings);
Settings = settings;
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs) foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{ {
for (int i = 0; i < jobPrefab.InitialCount; i++) for (int i = 0; i < jobPrefab.InitialCount; i++)
@@ -85,11 +86,14 @@ namespace Barotrauma
{ {
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "campaignsettings":
Settings = new CampaignSettings(subElement);
break;
case "crew": case "crew":
GameMain.GameSession.CrewManager = new CrewManager(subElement, true); GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
break; break;
case "map": case "map":
map = Map.Load(this, subElement); map = Map.Load(this, subElement, Settings);
break; break;
case "metadata": case "metadata":
CampaignMetadata = new CampaignMetadata(this, subElement); CampaignMetadata = new CampaignMetadata(this, subElement);
@@ -141,9 +145,9 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Start a completely new single player campaign /// Start a completely new single player campaign
/// </summary> /// </summary>
public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub) public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings)
{ {
var campaign = new SinglePlayerCampaign(mapSeed); var campaign = new SinglePlayerCampaign(mapSeed, settings);
return campaign; return campaign;
} }
@@ -608,6 +612,7 @@ namespace Barotrauma
{ {
ShowCampaignUI = false; ShowCampaignUI = false;
} }
HintManager.OnAvailableTransition(transitionType);
} }
if (!crewDead) if (!crewDead)
@@ -629,9 +634,9 @@ namespace Barotrauma
if (nextLevel == null) if (nextLevel == null)
{ {
//no level selected -> force the player to select one //no level selected -> force the player to select one
ForceMapUI = true;
CampaignUI.SelectTab(InteractionType.Map); CampaignUI.SelectTab(InteractionType.Map);
map.SelectLocation(-1); map.SelectLocation(-1);
ForceMapUI = true;
return false; return false;
} }
else if (transitionType == TransitionType.ProgressToNextEmptyLocation) else if (transitionType == TransitionType.ProgressToNextEmptyLocation)
@@ -704,6 +709,7 @@ namespace Barotrauma
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs), new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs), new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled)); new XAttribute("cheatsenabled", CheatsEnabled));
modeElement.Add(Settings.Save());
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well //save and remove all items that are in someone's inventory so they don't get included in the sub file as well
foreach (Character c in Character.CharacterList) foreach (Character c in Character.CharacterList)
@@ -99,7 +99,7 @@ namespace Barotrauma.Tutorials
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition; captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>(); tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>(); tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor")); var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("medicaldoctor"));
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor"); captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
captain_medic.TeamID = CharacterTeamType.Team1; captain_medic.TeamID = CharacterTeamType.Team1;
captain_medic.GiveJobItems(null); captain_medic.GiveJobItems(null);
@@ -122,17 +122,17 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_lockedDoor_1, null, false); SetDoorAccess(tutorial_lockedDoor_1, null, false);
SetDoorAccess(tutorial_lockedDoor_2, null, false); SetDoorAccess(tutorial_lockedDoor_2, null, false);
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic")); var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic"));
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic"); captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.TeamID = CharacterTeamType.Team1; captain_mechanic.TeamID = CharacterTeamType.Team1;
captain_mechanic.GiveJobItems(); captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer"); captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.TeamID = CharacterTeamType.Team1; captain_security.TeamID = CharacterTeamType.Team1;
captain_security.GiveJobItems(); captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer"); captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer.TeamID = CharacterTeamType.Team1; captain_engineer.TeamID = CharacterTeamType.Team1;
captain_engineer.GiveJobItems(); captain_engineer.GiveJobItems();
@@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull; var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull;
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull; medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull;
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1"); patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
patient1.TeamID = CharacterTeamType.Team1; patient1.TeamID = CharacterTeamType.Team1;
patient1.GiveJobItems(null); patient1.GiveJobItems(null);
@@ -86,26 +86,26 @@ namespace Barotrauma.Tutorials
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false); patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false);
patient1.AIController.Enabled = false; patient1.AIController.Enabled = false;
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2"); patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
patient2.TeamID = CharacterTeamType.Team1; patient2.TeamID = CharacterTeamType.Team1;
patient2.GiveJobItems(null); patient2.GiveJobItems(null);
patient2.CanSpeak = false; patient2.CanSpeak = false;
patient2.AIController.Enabled = false; patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient1.TeamID = CharacterTeamType.Team1; subPatient1.TeamID = CharacterTeamType.Team1;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false); subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient1); subPatients.Add(subPatient1);
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = CharacterTeamType.Team1; subPatient2.TeamID = CharacterTeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false); subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient2); subPatients.Add(subPatient2);
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient3.TeamID = CharacterTeamType.Team1; subPatient3.TeamID = CharacterTeamType.Team1;
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false); subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
@@ -110,7 +110,7 @@ namespace Barotrauma.Tutorials
} }
CharacterInfo charInfo = configElement.Element("Character") == null ? CharacterInfo charInfo = configElement.Element("Character") == null ?
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) : new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")) :
new CharacterInfo(configElement.Element("Character")); new CharacterInfo(configElement.Element("Character"));
WayPoint wayPoint = GetSpawnPoint(charInfo); WayPoint wayPoint = GetSpawnPoint(charInfo);
@@ -535,15 +535,15 @@ namespace Barotrauma.Tutorials
titleBlock.RectTransform.IsFixedSize = true; titleBlock.RectTransform.IsFixedSize = true;
} }
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text); List<RichTextData> richTextData = RichTextData.GetRichTextData(" " + text, out text);
GUITextBlock textBlock; GUITextBlock textBlock;
if (richTextData == null) if (richTextData == null)
{ {
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), " " + text, wrap: true); textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
} }
else else
{ {
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, " " + text, wrap: true); textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, text, wrap: true);
} }
textBlock.RectTransform.IsFixedSize = true; textBlock.RectTransform.IsFixedSize = true;
@@ -88,6 +88,8 @@ namespace Barotrauma
} }
} }
} }
HintManager.Update();
} }
public void Draw(SpriteBatch spriteBatch) public void Draw(SpriteBatch spriteBatch)
@@ -336,7 +336,7 @@ namespace Barotrauma
{ {
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward)); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.GetWithVariable("MissionReward", "[reward]", rewardText)); TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), parseRichText: true);
} }
if (displayedMission != missionsToDisplay.Last()) if (displayedMission != missionsToDisplay.Last())
@@ -20,6 +20,7 @@ namespace Barotrauma
Audio, Audio,
VoiceChat, VoiceChat,
Controls, Controls,
Gameplay,
#if DEBUG #if DEBUG
Debug Debug
#endif #endif
@@ -532,29 +533,19 @@ namespace Barotrauma
UserData = tab UserData = tab
}; };
float tabWidth = 0.25f; float tabWidth = 1.0f / tabs.Length;
#if DEBUG #if DEBUG
tabWidth = 0.2f; string buttonText = tab != Tab.Debug ? TextManager.Get("SettingsTab." + tab.ToString()) : "Debug";
if (tab != Tab.Debug) #else
{ string buttonText = TextManager.Get("SettingsTab." + tab.ToString());
#endif #endif
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform),
TextManager.Get("SettingsTab." + tab.ToString()), style: "GUITabButton") tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), style: "GUITabButton")
{
UserData = tab,
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
};
#if DEBUG
}
else
{ {
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), "Debug", style: "GUITabButton") UserData = tab,
{ OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
UserData = tab, };
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; } tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width));
};
}
#endif
} }
new GUIButton(new RectTransform(new Vector2(0.05f, 0.75f), tabButtonHolder.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.2f) }, style: "GUIBugButton") new GUIButton(new RectTransform(new Vector2(0.05f, 0.75f), tabButtonHolder.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.2f) }, style: "GUIBugButton")
@@ -669,19 +660,6 @@ namespace Barotrauma
Selected = TextureCompressionEnabled Selected = TextureCompressionEnabled
}; };
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform),
TextManager.Get("PauseOnFocusLost"))
{
Selected = PauseOnFocusLost,
ToolTip = TextManager.Get("PauseOnFocusLostToolTip"),
OnSelected = (tickBox) =>
{
PauseOnFocusLost = tickBox.Selected;
UnsavedSettings = true;
return true;
}
};
GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont, wrap: true); GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider", GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider",
barSize: 0.1f) barSize: 0.1f)
@@ -773,56 +751,6 @@ namespace Barotrauma
} }
}; };
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = HUDScaleText,
BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale),
OnMoved = (scrollBar, scroll) =>
{
HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll);
ChangeSliderText(scrollBar, HUDScale);
OnHUDScaleChanged?.Invoke();
return true;
},
Step = 0.02f
};
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont);
GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = inventoryScaleText,
BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale),
OnMoved = (scrollBar, scroll) =>
{
InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll);
ChangeSliderText(scrollBar, InventoryScale);
return true;
},
Step = 0.02f
};
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont);
GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = textScaleText,
BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale),
OnMoved = (scrollBar, scroll) =>
{
TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll);
textScaleDirty = true;
ChangeSliderText(scrollBar, TextScale);
return true;
},
Step = 0.01f
};
textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll);
/// Audio tab ---------------------------------------------------------------- /// Audio tab ----------------------------------------------------------------
var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
@@ -1380,6 +1308,88 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(defaultBindingsButton.TextBlock, legacyBindingsButton.TextBlock); GUITextBlock.AutoScaleAndNormalize(defaultBindingsButton.TextBlock, legacyBindingsButton.TextBlock);
}; };
/// Gameplay tab -------------------------------------------------------------
var gameplaySettingsGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Gameplay].RectTransform, Anchor.TopLeft)
{ RelativeOffset = new Vector2(0.025f, 0.02f) })
{ RelativeSpacing = 0.01f };
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform),
TextManager.Get("PauseOnFocusLost"))
{
Selected = PauseOnFocusLost,
ToolTip = TextManager.Get("PauseOnFocusLostToolTip"),
OnSelected = (tickBox) =>
{
PauseOnFocusLost = tickBox.Selected;
UnsavedSettings = true;
return true;
}
};
GUITickBox disableInGameHintsBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform),
TextManager.Get("DisableInGameHints"))
{
Selected = DisableInGameHints,
ToolTip = TextManager.Get("DisableInGameHintsToolTip"),
OnSelected = (tickBox) =>
{
DisableInGameHints = tickBox.Selected;
UnsavedSettings = true;
return true;
}
};
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = HUDScaleText,
BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale),
OnMoved = (scrollBar, scroll) =>
{
HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll);
ChangeSliderText(scrollBar, HUDScale);
OnHUDScaleChanged?.Invoke();
return true;
},
Step = 0.02f
};
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont);
GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = inventoryScaleText,
BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale),
OnMoved = (scrollBar, scroll) =>
{
InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll);
ChangeSliderText(scrollBar, InventoryScale);
return true;
},
Step = 0.02f
};
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont);
GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = textScaleText,
BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale),
OnMoved = (scrollBar, scroll) =>
{
TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll);
textScaleDirty = true;
ChangeSliderText(scrollBar, TextScale);
return true;
},
Step = 0.01f
};
textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll);
/// Bottom buttons -------------------------------------------------------------
new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft),
TextManager.Get("Cancel")) TextManager.Get("Cancel"))
{ {
@@ -1465,55 +1475,54 @@ namespace Barotrauma
{ RelativeOffset = new Vector2(0.02f, 0.02f) }) { RelativeOffset = new Vector2(0.02f, 0.02f) })
{ RelativeSpacing = 0.01f }; { RelativeSpacing = 0.01f };
var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic quickstart enabled", style: "GUITickBox"); void addDebugTickBox(bool initialValue, Action<bool> set, string label, string tooltip)
automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled;
automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched";
automaticQuickStartTickBox.OnSelected = (tickBox) =>
{ {
AutomaticQuickStartEnabled = tickBox.Selected; var tickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), label, style: "GUITickBox");
UnsavedSettings = true; tickBox.Selected = initialValue;
return true; tickBox.ToolTip = tooltip;
}; tickBox.OnSelected = (tickBox) =>
{
set(tickBox.Selected);
UnsavedSettings = true;
return true;
};
}
var automaticCampaignLoadTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic campaign load enabled", style: "GUITickBox"); addDebugTickBox(
automaticCampaignLoadTickBox.Selected = AutomaticCampaignLoadEnabled; AutomaticQuickStartEnabled,
automaticCampaignLoadTickBox.ToolTip = "Will the game automatically load the latest campaign save when the game is launched"; (b) => AutomaticQuickStartEnabled = b,
automaticCampaignLoadTickBox.OnSelected = (tickBox) => "Automatic quickstart enabled",
{ "Will the game automatically move on to Quickstart when the game is launched");
AutomaticCampaignLoadEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox"); addDebugTickBox(
showSplashScreenTickBox.Selected = EnableSplashScreen; AutomaticCampaignLoadEnabled,
showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched"; (b) => AutomaticCampaignLoadEnabled = b,
showSplashScreenTickBox.OnSelected = (tickBox) => "Automatic campaign load enabled",
{ "Will the game automatically load the latest campaign save when the game is launched");
EnableSplashScreen = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Verbose logging enabled", style: "GUITickBox"); addDebugTickBox(
verboseLoggingTickBox.Selected = VerboseLogging; EnableSplashScreen,
verboseLoggingTickBox.ToolTip = "Should verbose logging be used"; (b) => EnableSplashScreen = b,
verboseLoggingTickBox.OnSelected = (tickBox) => "Splash screen enabled",
{ "Are the splash screens shown when the game is launched");
VerboseLogging = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var textManagerDebugModeTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "TextManager debug mode enabled", style: "GUITickBox"); addDebugTickBox(
textManagerDebugModeTickBox.Selected = TextManagerDebugModeEnabled; VerboseLogging,
textManagerDebugModeTickBox.ToolTip = "Does the TextManager return the text tags for debug purposes?"; (b) => VerboseLogging = b,
textManagerDebugModeTickBox.OnSelected = (tickBox) => "Verbose logging enabled",
{ "Should verbose logging be used");
TextManagerDebugModeEnabled = tickBox.Selected;
UnsavedSettings = true; addDebugTickBox(
return true; TextManagerDebugModeEnabled,
}; (b) => TextManagerDebugModeEnabled = b,
"TextManager debug mode enabled",
"Does the TextManager return the text tags for debug purposes?");
addDebugTickBox(
ModBreakerMode,
(b) => ModBreakerMode = b,
"Mod breaker mode enabled",
"Do horrible things when loading mods to see if it breaks?");
#endif #endif
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
@@ -639,7 +639,13 @@ namespace Barotrauma
foreach (Item doubleClickedItem in doubleClickedItems) foreach (Item doubleClickedItem in doubleClickedItems)
{ {
QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First()); QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First());
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment || !IsInLimbSlot(doubleClickedItem, InvSlotType.Any)) //only use one item if we're equipping or using it as a treatment
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
{
break;
}
//if the item was put in a limb slot, only put one item from the stack
if (doubleClickedItem.ParentInventory == this && !IsInLimbSlot(doubleClickedItem, InvSlotType.Any))
{ {
break; break;
} }
@@ -810,6 +816,8 @@ namespace Barotrauma
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f; highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
} }
} }
HintManager.OnShowSubInventory(slotRef?.Item);
} }
public void AssignQuickUseNumKeys() public void AssignQuickUseNumKeys()
@@ -39,6 +39,8 @@ namespace Barotrauma.Items.Components
private Pair<Rectangle, string> tooltip; private Pair<Rectangle, string> tooltip;
private GUITextBlock requiredTimeBlock;
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
CreateGUI(); CreateGUI();
@@ -522,7 +524,7 @@ namespace Barotrauma.Items.Components
AutoScaleHorizontal = true, AutoScaleHorizontal = true,
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime), requiredTimeBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime),
font: GUI.SmallFont); font: GUI.SmallFont);
return true; return true;
} }
@@ -606,6 +608,12 @@ namespace Barotrauma.Items.Components
} }
} }
partial void UpdateRequiredTimeProjSpecific()
{
if (requiredTimeBlock == null) { return; }
requiredTimeBlock.Text = ToolBox.SecondsToReadableTime(timeUntilReady > 0.0f ? timeUntilReady : requiredTime);
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{ {
int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem); int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem);
@@ -1380,6 +1380,7 @@ namespace Barotrauma.Items.Components
MathHelper.Clamp(c.Mass * 0.03f, 0.1f, 2.0f)); MathHelper.Clamp(c.Mass * 0.03f, 0.1f, 2.0f));
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip); sonarBlips.Add(blip);
HintManager.OnSonarSpottedCharacter(Item, c);
} }
continue; continue;
} }
@@ -1399,6 +1400,7 @@ namespace Barotrauma.Items.Components
MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f)); MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f));
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip); sonarBlips.Add(blip);
HintManager.OnSonarSpottedCharacter(Item, c);
} }
} }
} }
@@ -357,8 +357,8 @@ namespace Barotrauma.Items.Components
DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false))) DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
{ {
// Undocking from an outpost // Undocking from an outpost
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
campaign.ShowCampaignUI = true; campaign.ShowCampaignUI = true;
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
return false; return false;
} }
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null && else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
@@ -748,7 +748,7 @@ namespace Barotrauma.Items.Components
} }
if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null) if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
{ {
steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f); steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
Vector2 input = Vector2.Zero; Vector2 input = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; } if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; } if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
@@ -98,8 +98,11 @@ namespace Barotrauma.Items.Components
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
float chargeRatio = charge / capacity; if (chargeIndicator != null)
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green); {
float chargeRatio = charge / capacity;
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
}
} }
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
@@ -87,7 +87,7 @@ namespace Barotrauma.Items.Components
SpriteEffects.None, SpriteEffects.None,
depth); depth);
} }
} }
private static Sprite defaultWireSprite; private static Sprite defaultWireSprite;
private Sprite overrideSprite; private Sprite overrideSprite;
private Sprite wireSprite; private Sprite wireSprite;
@@ -167,11 +167,11 @@ namespace Barotrauma.Items.Components
{ {
if (DockingTarget.joint != null) if (DockingTarget.joint != null)
{ {
DockingTarget.Lock(isNetworkMessage: true, forcePosition: true); DockingTarget.Lock(isNetworkMessage: true);
} }
else else
{ {
Lock(isNetworkMessage: true, forcePosition: true); Lock(isNetworkMessage: true);
} }
} }
} }
@@ -327,10 +327,11 @@ namespace Barotrauma
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom; Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
if (!rect.Contains(pos)) { continue; } if (!rect.Contains(pos)) { continue; }
float iconScale = generationParams.LocationIconSize / location.Type.Sprite.size.X; Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
if (location == CurrentDisplayLocation) { iconScale *= 1.2f; } if (location == CurrentDisplayLocation) { iconScale *= 1.2f; }
Rectangle drawRect = location.Type.Sprite.SourceRect; Rectangle drawRect = locationSprite.SourceRect;
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f); drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f); drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
drawRect.X = (int)pos.X - drawRect.Width / 2; drawRect.X = (int)pos.X - drawRect.Width / 2;
@@ -375,13 +376,18 @@ namespace Barotrauma
foreach (LocationConnection connection in Connections) foreach (LocationConnection connection in Connections)
{ {
if (HighlightedLocation != CurrentDisplayLocation && if (HighlightedLocation != CurrentDisplayLocation &&
connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation)) connection.Locations.Contains(HighlightedLocation) &&
connection.Locations.Contains(CurrentDisplayLocation))
{ {
if (PlayerInput.PrimaryMouseButtonClicked() && if (PlayerInput.PrimaryMouseButtonClicked() &&
SelectedLocation != HighlightedLocation && HighlightedLocation != null) SelectedLocation != HighlightedLocation && HighlightedLocation != null)
{ {
if (connection.Locked)
{
new GUIMessageBox(string.Empty, TextManager.Get("LockedPathTooltip"));
}
//clients aren't allowed to select the location without a permission //clients aren't allowed to select the location without a permission
if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false) else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
{ {
connectionHighlightState = 0.0f; connectionHighlightState = 0.0f;
SelectedConnection = connection; SelectedConnection = connection;
@@ -517,8 +523,6 @@ namespace Barotrauma
float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
Radiation.Draw(spriteBatch, rect, zoom);
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(), noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)), startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color : Color.White * cameraNoiseStrength * 0.1f, color : Color.White * cameraNoiseStrength * 0.1f,
@@ -534,6 +538,8 @@ namespace Barotrauma
color: Color.White * cameraNoiseStrength * 0.1f, color: Color.White * cameraNoiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale); textureScale: Vector2.One * noiseScale);
Radiation.Draw(spriteBatch, rect, zoom);
Pair<Rectangle, string> tooltip = null; Pair<Rectangle, string> tooltip = null;
if (generationParams.ShowLocations) if (generationParams.ShowLocations)
{ {
@@ -548,8 +554,10 @@ namespace Barotrauma
Location location = Locations[i]; Location location = Locations[i];
if (IsInFogOfWar(location)) { continue; } if (IsInFogOfWar(location)) { continue; }
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom; Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
Rectangle drawRect = location.Type.Sprite.SourceRect; Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
Rectangle drawRect = locationSprite.SourceRect;
drawRect.X = (int)pos.X - drawRect.Width / 2; drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2; drawRect.Y = (int)pos.Y - drawRect.Width / 2;
@@ -562,20 +570,14 @@ namespace Barotrauma
color *= 0.5f; color *= 0.5f;
} }
// TODO proper visualization of this
if (location.Type.HasOutpost && !location.HasOutpost())
{
color = GUI.Style.Red;
}
float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f; float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f;
if (location == HighlightedLocation) if (location == HighlightedLocation)
{ {
iconScale *= 1.2f; iconScale *= 1.2f;
} }
location.Type.Sprite.Draw(spriteBatch, pos, color, locationSprite.Draw(spriteBatch, pos, color,
scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom); scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom);
if (location == CurrentDisplayLocation) if (location == CurrentDisplayLocation)
{ {
@@ -657,8 +659,11 @@ namespace Barotrauma
DrawDecorativeHUD(spriteBatch, rect); DrawDecorativeHUD(spriteBatch, rect);
bool drawRadiationTooltip = true;
if (HighlightedLocation != null) if (HighlightedLocation != null)
{ {
drawRadiationTooltip = false;
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom; Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
pos.X += 50 * zoom; pos.X += 50 * zoom;
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name); Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
@@ -693,14 +698,23 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor); GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor);
} }
} }
if (tooltip != null) if (tooltip != null)
{ {
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First); GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
drawRadiationTooltip = false;
} }
if (connectionTooltip != null) if (connectionTooltip != null)
{ {
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First); GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
drawRadiationTooltip = false;
} }
if (drawRadiationTooltip)
{
Radiation.DrawFront(spriteBatch);
}
spriteBatch.End(); spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect; GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
@@ -827,6 +841,7 @@ namespace Barotrauma
{ {
if (connection.LevelData.HasBeaconStation) { iconCount++; } if (connection.LevelData.HasBeaconStation) { iconCount++; }
if (connection.LevelData.HasHuntingGrounds) { iconCount++; } if (connection.LevelData.HasHuntingGrounds) { iconCount++; }
if (connection.Locked) { iconCount++; }
string tooltip = null; string tooltip = null;
var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth; var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null) if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
@@ -867,6 +882,11 @@ namespace Barotrauma
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip")); DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"));
} }
if (connection.Locked)
{
DrawIcon("LockIcon", (int)(28 * zoom), TextManager.Get("LockedPathTooltip"));
}
if (connection.LevelData.HasHuntingGrounds) if (connection.LevelData.HasHuntingGrounds)
{ {
DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip")); DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip"));
@@ -1,4 +1,5 @@
#nullable enable #nullable enable
using System;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -6,6 +7,13 @@ namespace Barotrauma
{ {
internal partial class Radiation internal partial class Radiation
{ {
private static readonly string radiationTooltip = TextManager.Get("RadiationTooltip");
private static float spriteIndex;
private readonly SpriteSheet sheet = GUI.Style.RadiationAnimSpriteSheet;
private int maxFrames => sheet.FrameCount + 1;
private bool isHovingOver;
public void Draw(SpriteBatch spriteBatch, Rectangle container, float zoom) public void Draw(SpriteBatch spriteBatch, Rectangle container, float zoom)
{ {
if (!Enabled) { return; } if (!Enabled) { return; }
@@ -19,16 +27,38 @@ namespace Barotrauma
Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y); Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y);
if (size.X < 0) { return; } if (size.X < 0) { return; }
uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, GUI.Style.Red * 0.33f, Vector2.Zero, textureScale: new Vector2(zoom)); Vector2 spriteScale = new Vector2(zoom);
if (container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X) uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, Params.RadiationAreaColor, Vector2.Zero, textureScale: spriteScale);
Vector2 topRight = topLeft + Vector2.UnitX * size.X;
int index = 0;
for (float i = 0; i <= size.Y; i += sheet.FrameSize.Y / 2f * zoom)
{ {
// TODO tooltip? bool isEven = ++index % 2 == 0;
Vector2 origin = new Vector2(0.5f, 0) * sheet.FrameSize.X;
// every other sprite's animation is reversed to make it seem more chaotic
int sprite = (int) MathF.Floor(isEven ? spriteIndex : maxFrames - spriteIndex);
sheet.Draw(spriteBatch, sprite, topRight + new Vector2(0, i), Params.RadiationBorderTint, origin, 0f, spriteScale);
}
isHovingOver = container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X;
}
public void DrawFront(SpriteBatch spriteBatch)
{
if (isHovingOver)
{
GUIComponent.DrawToolTip(spriteBatch, radiationTooltip, PlayerInput.MousePosition + new Vector2(18 * GUI.Scale));
} }
} }
public void MapUpdate(float deltaTime) public void MapUpdate(float deltaTime)
{ {
float spriteStep = Params.BorderAnimationSpeed * deltaTime;
spriteIndex = (spriteIndex + spriteStep) % maxFrames;
if (increasedAmount > 0) if (increasedAmount > 0)
{ {
increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime; increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime;
@@ -805,7 +805,7 @@ namespace Barotrauma
} }
if (selectionPos != null && selectionPos != Vector2.Zero) if (selectionPos != null && selectionPos != Vector2.Zero)
{ {
GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f)); GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, 2f / GameScreen.Selected.Cam.Zoom);
} }
} }
@@ -35,7 +35,10 @@ namespace Barotrauma
public void CreatePreviewWindow(GUIComponent parent) public void CreatePreviewWindow(GUIComponent parent)
{ {
var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null); var content = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform), style: null)
{
OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; }
};
if (PreviewImage == null) if (PreviewImage == null)
{ {
@@ -2064,6 +2064,8 @@ namespace Barotrauma.Networking
bool autoRestartEnabled = inc.ReadBoolean(); bool autoRestartEnabled = inc.ReadBoolean();
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f; float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
bool radiationEnabled = inc.ReadBoolean();
//ignore the message if we already a more up-to-date one //ignore the message if we already a more up-to-date one
//or if we're still waiting for the initial update //or if we're still waiting for the initial update
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) && if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
@@ -2119,7 +2121,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating); GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
GameMain.NetLobbyScreen.LevelSeed = levelSeed; GameMain.NetLobbyScreen.LevelSeed = levelSeed;
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty); GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
GameMain.NetLobbyScreen.SetBotCount(botCount); GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode); GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer); GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
@@ -2564,7 +2566,7 @@ namespace Barotrauma.Networking
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID) if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
{ {
string savePath = transfer.FilePath; string savePath = transfer.FilePath;
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign); GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode; campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID; campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -2912,7 +2914,7 @@ namespace Barotrauma.Networking
clientPeer.Send(msg, DeliveryMethod.Reliable); clientPeer.Send(msg, DeliveryMethod.Reliable);
} }
public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed) public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings)
{ {
GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false; GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false;
GameMain.NetLobbyScreen.CampaignFrame.Visible = false; GameMain.NetLobbyScreen.CampaignFrame.Visible = false;
@@ -2927,6 +2929,7 @@ namespace Barotrauma.Networking
msg.Write(mapSeed); msg.Write(mapSeed);
msg.Write(sub.Name); msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash); msg.Write(sub.MD5Hash.Hash);
settings.Serialize(msg);
clientPeer.Send(msg, DeliveryMethod.Reliable); clientPeer.Send(msg, DeliveryMethod.Reliable);
} }
@@ -147,13 +147,36 @@ namespace Barotrauma.Networking
if (missingPackages.Count > 0) if (missingPackages.Count > 0)
{ {
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0); var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
var mismatchedButDownloaded = missingPackages.Where(p =>
{
var localMatching = ContentPackage.RegularPackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
localMatching ??= ContentPackage.CorePackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
if (nonDownloadable.Any()) return localMatching != null;
});
if (mismatchedButDownloaded.Any())
{
string disconnectMsg;
if (mismatchedButDownloaded.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMod~[incompatiblecontentpackage]={GetPackageStr(mismatchedButDownloaded.First())}";
}
else
{
List<string> packageStrs = new List<string>();
mismatchedButDownloaded.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMods~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
}
else if (nonDownloadable.Any())
{ {
string disconnectMsg; string disconnectMsg;
if (nonDownloadable.Count() == 1) if (nonDownloadable.Count() == 1)
{ {
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"; disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(nonDownloadable.First())}";
} }
else else
{ {
@@ -56,6 +56,7 @@ namespace Barotrauma.Networking
Steamworks.SteamNetworking.AllowP2PPacketRelay(true); Steamworks.SteamNetworking.AllowP2PPacketRelay(true);
ServerConnection = new SteamP2PConnection("Server", hostSteamId); ServerConnection = new SteamP2PConnection("Server", hostSteamId);
ServerConnection.SetOwnerSteamIDIfUnknown(hostSteamId);
incomingInitializationMessages = new List<IReadMessage>(); incomingInitializationMessages = new List<IReadMessage>();
incomingDataMessages = new List<IReadMessage>(); incomingDataMessages = new List<IReadMessage>();
@@ -17,6 +17,7 @@ namespace Barotrauma.Networking
class RemotePeer class RemotePeer
{ {
public UInt64 SteamID; public UInt64 SteamID;
public UInt64 OwnerSteamID;
public double? DisconnectTime; public double? DisconnectTime;
public bool Authenticating; public bool Authenticating;
public bool Authenticated; public bool Authenticated;
@@ -31,6 +32,7 @@ namespace Barotrauma.Networking
public RemotePeer(UInt64 steamId) public RemotePeer(UInt64 steamId)
{ {
SteamID = steamId; SteamID = steamId;
OwnerSteamID = 0;
DisconnectTime = null; DisconnectTime = null;
Authenticating = false; Authenticating = false;
Authenticated = false; Authenticated = false;
@@ -90,10 +92,19 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK) if (status == Steamworks.AuthResponse.OK)
{ {
remotePeer.OwnerSteamID = ownerID;
remotePeer.Authenticated = true; remotePeer.Authenticated = true;
remotePeer.Authenticating = false; remotePeer.Authenticating = false;
foreach (var msg in remotePeer.UnauthedMessages) foreach (var msg in remotePeer.UnauthedMessages)
{ {
//rewrite the owner id before
//forwarding the messages to
//the server, since it's only
//known now
int prevBitPosition = msg.Message.BitPosition;
msg.Message.BitPosition = sizeof(ulong) * 8;
msg.Message.Write(ownerID);
msg.Message.BitPosition = prevBitPosition;
byte[] msgToSend = (byte[])msg.Message.Buffer.Clone(); byte[] msgToSend = (byte[])msg.Message.Buffer.Clone();
Array.Resize(ref msgToSend, msg.Message.LengthBytes); Array.Resize(ref msgToSend, msg.Message.LengthBytes);
ChildServerRelay.Write(msgToSend); ChildServerRelay.Write(msgToSend);
@@ -131,6 +142,7 @@ namespace Barotrauma.Networking
IWriteMessage outMsg = new WriteOnlyMessage(); IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(steamId); outMsg.Write(steamId);
outMsg.Write(remotePeer.OwnerSteamID);
outMsg.Write(data, 1, dataLength - 1); outMsg.Write(data, 1, dataLength - 1);
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0]; DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
@@ -142,34 +154,27 @@ namespace Barotrauma.Networking
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!remotePeer.Authenticated) if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep)
{ {
if (!remotePeer.Authenticating) remotePeer.DisconnectTime = null;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{ {
if (isConnectionInitializationStep) remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{ {
remotePeer.DisconnectTime = null; DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
return;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
//Console.WriteLine("received init step from "+steamId.ToString()+" ("+initializationStep.ToString()+")");
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
}
} }
} }
} }
@@ -336,6 +341,7 @@ namespace Barotrauma.Networking
{ {
IWriteMessage outMsg = new WriteOnlyMessage(); IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(selfSteamID); outMsg.Write(selfSteamID);
outMsg.Write(selfSteamID);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep)); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
outMsg.Write(Name); outMsg.Write(Name);
@@ -428,6 +434,7 @@ namespace Barotrauma.Networking
byte[] msgData = new byte[msg.LengthBytes]; byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length); msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
msgToSend.Write(selfSteamID); msgToSend.Write(selfSteamID);
msgToSend.Write(selfSteamID);
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None)); msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
msgToSend.Write((UInt16)length); msgToSend.Write((UInt16)length);
msgToSend.Write(msgData, 0, length); msgToSend.Write(msgData, 0, length);
@@ -147,7 +147,7 @@ namespace Barotrauma.Networking
} }
} }
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null) public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null)
{ {
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return; if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
@@ -212,6 +212,7 @@ namespace Barotrauma.Networking
outMsg.Write(autoRestart != null); outMsg.Write(autoRestart != null);
outMsg.Write(autoRestart ?? false); outMsg.Write(autoRestart ?? false);
outMsg.Write(radiationEnabled ?? RadiationEnabled);
outMsg.WritePadBits(); outMsg.WritePadBits();
} }
@@ -274,7 +275,7 @@ namespace Barotrauma.Networking
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); } if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
return true; return true;
}; };
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null) new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
{ {
OnClicked = ToggleSettingsFrame OnClicked = ToggleSettingsFrame
@@ -23,7 +23,7 @@ namespace Barotrauma
private GUIButton loadGameButton, deleteMpSaveButton; private GUIButton loadGameButton, deleteMpSaveButton;
public Action<SubmarineInfo, string, string> StartNewGame; public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
public Action<string> LoadGame; public Action<string> LoadGame;
private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 }; private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 };
@@ -40,6 +40,8 @@ namespace Barotrauma
get; get;
private set; private set;
} }
public GUITickBox EnableRadiationToggle { get; set; }
private readonly bool isMultiplayer; private readonly bool isMultiplayer;
@@ -171,6 +173,8 @@ namespace Barotrauma
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text); string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled; bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = new CampaignSettings { RadiationEnabled = EnableRadiationToggle?.Selected ?? GameMain.NetLobbyScreen.IsRadiationEnabled() };
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{ {
if (!hasRequiredContentPackages) if (!hasRequiredContentPackages)
@@ -184,7 +188,7 @@ namespace Barotrauma
{ {
if (GUIMessageBox.MessageBoxes.Count == 0) if (GUIMessageBox.MessageBoxes.Count == 0)
{ {
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer) if (isMultiplayer)
{ {
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -204,7 +208,7 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = (button, obj) => msgBox.Buttons[0].OnClicked = (button, obj) =>
{ {
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer) if (isMultiplayer)
{ {
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -219,7 +223,7 @@ namespace Barotrauma
} }
else else
{ {
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer) if (isMultiplayer)
{ {
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -230,8 +234,7 @@ namespace Barotrauma
} }
}; };
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(isMultiplayer ? 0.6f : 0.3f, 1f), buttonContainer.RectTransform), "", font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
{ {
TextGetter = () => TextGetter = () =>
{ {
@@ -254,6 +257,12 @@ namespace Barotrauma
if (!isMultiplayer) if (!isMultiplayer)
{ {
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 1f), buttonContainer.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
{
Selected = true,
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
};
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton") var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton")
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
@@ -477,8 +477,8 @@ namespace Barotrauma
} }
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward)); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.GetWithVariable("missionreward", "[reward]", rewardText), wrap: true); TextManager.GetWithVariable("missionreward", "[reward]", rewardText), wrap: true, parseRichText: true);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true);
} }
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20)); missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20));
foreach (GUIComponent child in missionTextContent.Children) foreach (GUIComponent child in missionTextContent.Children)
@@ -562,6 +562,11 @@ namespace Barotrauma
public void SelectTab(CampaignMode.InteractionType tab) public void SelectTab(CampaignMode.InteractionType tab)
{ {
if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map))
{
HintManager.OnShowCampaignInterface(tab);
}
selectedTab = tab; selectedTab = tab;
for (int i = 0; i < tabs.Length; i++) for (int i = 0; i < tabs.Length; i++)
{ {
@@ -1761,9 +1761,9 @@ namespace Barotrauma.CharacterEditor
#endif #endif
// Add to the selected content package // Add to the selected content package
contentPackage.AddFile(configFilePath, ContentType.Character); contentPackage.AddFile(configFilePath, ContentType.Character);
Barotrauma.IO.Validation.DevException = true; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
contentPackage.Save(contentPackage.Path); contentPackage.Save(contentPackage.Path);
Barotrauma.IO.Validation.DevException = false; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
// Ragdoll // Ragdoll
@@ -882,7 +882,7 @@ namespace Barotrauma
return false; return false;
} }
GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, null); GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
TestGameMode gameMode = (TestGameMode) gameSession.GameMode; TestGameMode gameMode = (TestGameMode) gameSession.GameMode;
gameMode.SpawnOutpost = true; gameMode.SpawnOutpost = true;
@@ -296,7 +296,7 @@ namespace Barotrauma
subInfo ??= SubmarineInfo.SavedSubmarines.GetRandom(s => subInfo ??= SubmarineInfo.SavedSubmarines.GetRandom(s =>
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) && s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&
!nonPlayerFiles.Any(f => f.Path.CleanUpPath().Equals(s.FilePath.CleanUpPath(), StringComparison.InvariantCultureIgnoreCase))); !nonPlayerFiles.Any(f => f.Path.CleanUpPath().Equals(s.FilePath.CleanUpPath(), StringComparison.InvariantCultureIgnoreCase)));
GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, null); GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
gameSession.StartRound(Level.Loaded.LevelData); gameSession.StartRound(Level.Loaded.LevelData);
(gameSession.GameMode as TestGameMode).OnRoundEnd = () => (gameSession.GameMode as TestGameMode).OnRoundEnd = () =>
{ {
@@ -1043,7 +1043,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
} }
private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed) private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed, CampaignSettings settings)
{ {
if (string.IsNullOrEmpty(saveName)) return; if (string.IsNullOrEmpty(saveName)) return;
@@ -1082,7 +1082,7 @@ namespace Barotrauma
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub")); selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
GameMain.GameSession = new GameSession(selectedSub, saveName, GameModePreset.SinglePlayerCampaign, mapSeed); GameMain.GameSession = new GameSession(selectedSub, saveName, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel(); ((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel();
} }
@@ -1134,6 +1134,7 @@ namespace Barotrauma
(int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f), (int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f),
campaignSetupUI.StartButton.RectTransform.MinSize.Y); campaignSetupUI.StartButton.RectTransform.MinSize.Y);
startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y); startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y);
campaignSetupUI.EnableRadiationToggle.RectTransform.Parent = startButtonContainer.RectTransform;
campaignSetupUI.InitialMoneyText.RectTransform.Parent = startButtonContainer.RectTransform; campaignSetupUI.InitialMoneyText.RectTransform.Parent = startButtonContainer.RectTransform;
} }
@@ -27,6 +27,8 @@ namespace Barotrauma
private GUIComponent jobVariantTooltip; private GUIComponent jobVariantTooltip;
private SubmarinePreview submarinePreview;
private readonly GUITextBox chatInput; private readonly GUITextBox chatInput;
private readonly GUITextBox serverLogFilter; private readonly GUITextBox serverLogFilter;
public GUITextBox ChatInput public GUITextBox ChatInput
@@ -41,6 +43,8 @@ namespace Barotrauma
private readonly GUIScrollBar levelDifficultyScrollBar; private readonly GUIScrollBar levelDifficultyScrollBar;
private readonly GUITickBox radiationEnabledTickBox;
private readonly GUIButton[] traitorProbabilityButtons; private readonly GUIButton[] traitorProbabilityButtons;
private readonly GUITextBlock traitorProbabilityText; private readonly GUITextBlock traitorProbabilityText;
@@ -1095,6 +1099,16 @@ namespace Barotrauma
} }
}; };
radiationEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
{
Selected = true,
OnSelected = box =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, radiationEnabled: box.Selected);
return true;
}
};
List<GUIComponent> settingsElements = settingsContent.Children.ToList(); List<GUIComponent> settingsElements = settingsContent.Children.ToList();
for (int i = 0; i < settingsElements.Count; i++) for (int i = 0; i < settingsElements.Count; i++)
{ {
@@ -1238,6 +1252,7 @@ namespace Barotrauma
} }
SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
radiationEnabledTickBox.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled = traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled =
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
botCountButtons[0].Enabled = botCountButtons[1].Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); botCountButtons[0].Enabled = botCountButtons[1].Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
@@ -2533,8 +2548,14 @@ namespace Barotrauma
chatBox.RemoveChild(chatBox.Content.Children.First()); chatBox.RemoveChild(chatBox.Content.Children.First());
} }
string textWithSender = message.TextWithSender;
if (message.Type == ChatMessageType.Server)
{
RichTextData.GetRichTextData(textWithSender, out textWithSender);
}
GUITextBlock msg = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), chatBox.Content.RectTransform), GUITextBlock msg = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), chatBox.Content.RectTransform),
text: ChatMessage.GetTimeStamp() + (message.Type == ChatMessageType.Private ? TextManager.Get("PrivateMessageTag") + " " : "") + message.TextWithSender, text: ChatMessage.GetTimeStamp() + (message.Type == ChatMessageType.Private ? TextManager.Get("PrivateMessageTag") + " " : "") + textWithSender,
textColor: message.Color, textColor: message.Color,
color: ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, color: ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f,
wrap: true, font: GUI.SmallFont) wrap: true, font: GUI.SmallFont)
@@ -43,6 +43,13 @@ namespace Barotrauma
private Dictionary<ulong, Steamworks.Ugc.Item?> pendingWorkshopDownloads = null; private Dictionary<ulong, Steamworks.Ugc.Item?> pendingWorkshopDownloads = null;
private string autoConnectName; private string autoConnectEndpoint; private string autoConnectName; private string autoConnectEndpoint;
private enum TernaryOption
{
Any,
Enabled,
Disabled
}
private class FriendInfo private class FriendInfo
{ {
public UInt64 SteamID; public UInt64 SteamID;
@@ -146,16 +153,19 @@ namespace Barotrauma
private GUITickBox filterFull; private GUITickBox filterFull;
private GUITickBox filterEmpty; private GUITickBox filterEmpty;
private GUITickBox filterWhitelisted; private GUITickBox filterWhitelisted;
private GUITickBox filterFriendlyFire; private Dictionary<string, GUIDropDown> ternaryFilters;
private GUITickBox filterKarma;
private GUITickBox filterTraitor;
private GUITickBox filterModded;
private GUITickBox filterVoip;
private Dictionary<string, GUITickBox> filterTickBoxes; private Dictionary<string, GUITickBox> filterTickBoxes;
private Dictionary<string, GUITickBox> playStyleTickBoxes; private Dictionary<string, GUITickBox> playStyleTickBoxes;
private Dictionary<string, GUITickBox> gameModeTickBoxes; private Dictionary<string, GUITickBox> gameModeTickBoxes;
private GUITickBox filterOffensive; private GUITickBox filterOffensive;
//GUIDropDown sends the OnSelected event before SelectedData is set, so we have to cache it manually.
private TernaryOption filterFriendlyFireValue = TernaryOption.Any;
private TernaryOption filterKarmaValue = TernaryOption.Any;
private TernaryOption filterTraitorValue = TernaryOption.Any;
private TernaryOption filterVoipValue = TernaryOption.Any;
private TernaryOption filterModdedValue = TernaryOption.Any;
private string sortedBy; private string sortedBy;
private GUIButton serverPreviewToggleButton; private GUIButton serverPreviewToggleButton;
@@ -173,6 +183,49 @@ namespace Barotrauma
CreateUI(); CreateUI();
} }
private void AddTernaryFilter(RectTransform parent, float elementHeight, string tag, Action<TernaryOption> valueSetter)
{
var filterLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementHeight), parent), isHorizontal: true)
{
Stretch = true
};
var box = new GUIFrame(new RectTransform(Vector2.One, filterLayoutGroup.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight)
{
IsFixedSize = true,
}, null)
{
HoverColor = Color.Gray,
SelectedColor = Color.DarkGray,
CanBeFocused = false
};
if (box.RectTransform.MinSize.Y > 0)
{
box.RectTransform.MinSize = new Point(box.RectTransform.MinSize.Y);
box.RectTransform.Resize(box.RectTransform.MinSize);
}
Vector2 textBlockScale = new Vector2((float)(filterLayoutGroup.Rect.Width - filterLayoutGroup.Rect.Height) / (float)Math.Max(filterLayoutGroup.Rect.Width, 1.0), 1.0f);
var filterLabel = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f) * textBlockScale, filterLayoutGroup.RectTransform, Anchor.CenterLeft), TextManager.Get("servertag." + tag + ".label"), textAlignment: Alignment.CenterLeft)
{
UserData = TextManager.Get("servertag." + tag + ".label")
};
GUI.Style.Apply(filterLabel, "GUITextBlock", null);
var dropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f) * textBlockScale, filterLayoutGroup.RectTransform, Anchor.CenterLeft), elementCount: 3);
dropDown.AddItem(TextManager.Get("any"), TernaryOption.Any);
dropDown.AddItem(TextManager.Get("servertag." + tag + ".true"), TernaryOption.Enabled, TextManager.Get("servertagdescription." + tag + ".true"));
dropDown.AddItem(TextManager.Get("servertag." + tag + ".false"), TernaryOption.Disabled, TextManager.Get("servertagdescription." + tag + ".false"));
dropDown.SelectItem(TernaryOption.Any);
dropDown.OnSelected = (_, data) => {
valueSetter((TernaryOption)data);
FilterServers();
return true;
};
ternaryFilters.Add(tag, dropDown);
}
private void CreateUI() private void CreateUI()
{ {
menu = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.85f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); menu = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.85f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) });
@@ -323,6 +376,7 @@ namespace Barotrauma
}; };
filterToggle.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally); filterToggle.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally);
ternaryFilters = new Dictionary<string, GUIDropDown>();
filterTickBoxes = new Dictionary<string, GUITickBox>(); filterTickBoxes = new Dictionary<string, GUITickBox>();
filterSameVersion = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterSameVersion")) filterSameVersion = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterSameVersion"))
@@ -382,40 +436,11 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
filterKarma = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.karma.true")) AddTernaryFilter(filters.Content.RectTransform, elementHeight, "karma", (value) => { filterKarmaValue = value; });
{ AddTernaryFilter(filters.Content.RectTransform, elementHeight, "traitors", (value) => { filterTraitorValue = value; });
UserData = TextManager.Get("servertag.karma.true"), AddTernaryFilter(filters.Content.RectTransform, elementHeight, "friendlyfire", (value) => { filterFriendlyFireValue = value; });
OnSelected = (tickBox) => { FilterServers(); return true; } AddTernaryFilter(filters.Content.RectTransform, elementHeight, "voip", (value) => { filterVoipValue = value; });
}; AddTernaryFilter(filters.Content.RectTransform, elementHeight, "modded", (value) => { filterModdedValue = value; });
filterTickBoxes.Add("servertag.karma", filterKarma);
filterTraitor = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.traitors.true"))
{
UserData = TextManager.Get("servertag.traitors.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTickBoxes.Add("servertag.traitors", filterTraitor);
filterFriendlyFire = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.friendlyfire.false"))
{
UserData = TextManager.Get("servertag.friendlyfire.false"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTickBoxes.Add("servertag.friendlyfire", filterFriendlyFire);
filterVoip = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.voip.false"))
{
UserData = TextManager.Get("servertag.voip.false"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTickBoxes.Add("servertag.voip", filterVoip);
filterModded = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.modded.true"))
{
UserData = TextManager.Get("servertag.modded.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
filterTickBoxes.Add("servertag.modded", filterModded);
// Play Style Selection // Play Style Selection
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), filters.Content.RectTransform), TextManager.Get("ServerSettingsPlayStyle"), font: GUI.SubHeadingFont) new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), filters.Content.RectTransform), TextManager.Get("ServerSettingsPlayStyle"), font: GUI.SubHeadingFont)
@@ -1076,9 +1101,15 @@ namespace Barotrauma
else else
{ {
bool incompatible = bool incompatible =
(!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch()) || (serverInfo.ContentPackageHashes.Any() && !serverInfo.ContentPackagesMatch()) ||
(remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion)); (remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion));
var karmaFilterPassed = filterKarmaValue == TernaryOption.Any|| (filterKarmaValue == TernaryOption.Enabled) == serverInfo.KarmaEnabled;
var friendlyFireFilterPassed = filterFriendlyFireValue == TernaryOption.Any || (filterFriendlyFireValue == TernaryOption.Enabled) == serverInfo.FriendlyFireEnabled;
var traitorsFilterPassed = filterTraitorValue == TernaryOption.Any || (filterTraitorValue == TernaryOption.Enabled) == (serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe);
var voipFilterPassed = filterVoipValue == TernaryOption.Any || (filterVoipValue == TernaryOption.Enabled) == serverInfo.VoipEnabled;
var moddedFilterPassed = filterModdedValue == TernaryOption.Any || (filterModdedValue == TernaryOption.Enabled) == serverInfo.GetPlayStyleTags().Any(t => t.Contains("modded.true"));
child.Visible = child.Visible =
serverInfo.OwnerVerified && serverInfo.OwnerVerified &&
serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase) && serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase) &&
@@ -1089,11 +1120,11 @@ namespace Barotrauma
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) && (!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) && (!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) && (!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) && karmaFilterPassed &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) && friendlyFireFilterPassed &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) && traitorsFilterPassed &&
(!filterVoip.Selected || serverInfo.VoipEnabled == false) && voipFilterPassed &&
(!filterModded.Selected || serverInfo.GetPlayStyleTags().Any(t => t.Contains("modded.true"))) && moddedFilterPassed &&
((selectedTab == ServerListTab.All && (serverInfo.LobbyID != 0 || !string.IsNullOrWhiteSpace(serverInfo.Port))) || ((selectedTab == ServerListTab.All && (serverInfo.LobbyID != 0 || !string.IsNullOrWhiteSpace(serverInfo.Port))) ||
(selectedTab == ServerListTab.Recent && serverInfo.Recent) || (selectedTab == ServerListTab.Recent && serverInfo.Recent) ||
(selectedTab == ServerListTab.Favorites && serverInfo.Favorite)); (selectedTab == ServerListTab.Favorites && serverInfo.Favorite));
@@ -2334,6 +2365,10 @@ namespace Barotrauma
{ {
element.Add(new XAttribute(filterBox.Key, filterBox.Value.Selected.ToString())); element.Add(new XAttribute(filterBox.Key, filterBox.Value.Selected.ToString()));
} }
foreach (KeyValuePair<string, GUIDropDown> ternaryFilter in ternaryFilters)
{
element.Add(new XAttribute(ternaryFilter.Key, ternaryFilter.Value.SelectedData.ToString()));
}
} }
public void LoadServerFilters(XElement element) public void LoadServerFilters(XElement element)
@@ -2344,6 +2379,15 @@ namespace Barotrauma
{ {
filterBox.Value.Selected = element.GetAttributeBool(filterBox.Key, filterBox.Value.Selected); filterBox.Value.Selected = element.GetAttributeBool(filterBox.Key, filterBox.Value.Selected);
} }
foreach (KeyValuePair<string, GUIDropDown> ternaryFilter in ternaryFilters)
{
string valueStr = element.GetAttributeString(ternaryFilter.Key, "");
TernaryOption ternaryOption = (TernaryOption)ternaryFilter.Value.SelectedData;
Enum.TryParse<TernaryOption>(valueStr, true, out ternaryOption);
var child = ternaryFilter.Value.ListBox.Content.GetChildByUserData(ternaryOption);
ternaryFilter.Value.Select(ternaryFilter.Value.ListBox.Content.GetChildIndex(child));
}
} }
} }
@@ -410,9 +410,9 @@ namespace Barotrauma
if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; } if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; }
string subPath = Path.GetFullPath(sub.FilePath); string subPath = Path.GetFullPath(sub.FilePath);
//ignore subs that are part of the vanilla content package //ignore files that are part of the vanilla content package
if (GameMain.VanillaContent != null && if (GameMain.VanillaContent != null &&
GameMain.VanillaContent.GetFilesOfType(ContentType.Submarine).Any(s => Path.GetFullPath(s) == subPath)) GameMain.VanillaContent.Files.Any(s => Path.GetFullPath(s.Path) == subPath))
{ {
continue; continue;
} }
@@ -870,7 +870,7 @@ namespace Barotrauma
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, null); GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
gameSession.StartRound(null, false); gameSession.StartRound(null, false);
(gameSession.GameMode as TestGameMode).OnRoundEnd = () => (gameSession.GameMode as TestGameMode).OnRoundEnd = () =>
{ {
@@ -897,6 +897,8 @@ namespace Barotrauma
foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory))) foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory)))
{ {
string categoryName = TextManager.Get("MapEntityCategory." + category);
maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(categoryName.Replace(' ', '\n')).X + GUI.IntScale(50));
foreach (MapEntityPrefab ep in MapEntityPrefab.List) foreach (MapEntityPrefab ep in MapEntityPrefab.List)
{ {
if (!ep.Category.HasFlag(category)) { continue; } if (!ep.Category.HasFlag(category)) { continue; }
@@ -907,13 +909,14 @@ namespace Barotrauma
} }
entityLists[category + ep.Subcategory].Add(ep); entityLists[category + ep.Subcategory].Add(ep);
categoryKeys[category + ep.Subcategory] = category; categoryKeys[category + ep.Subcategory] = category;
string categoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory; string subcategoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory;
if (categoryName != null) if (subcategoryName != null)
{ {
maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(categoryName.Replace(' ', '\n')).X + GUI.IntScale(50)); maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(subcategoryName.Replace(' ', '\n')).X + GUI.IntScale(50));
} }
} }
} }
categorizedEntityList.Content.ClampMouseRectToParent = true; categorizedEntityList.Content.ClampMouseRectToParent = true;
int entitiesPerRow = (int)Math.Ceiling(categorizedEntityList.Content.Rect.Width / Math.Max(125 * GUI.Scale, 60)); int entitiesPerRow = (int)Math.Ceiling(categorizedEntityList.Content.Rect.Width / Math.Max(125 * GUI.Scale, 60));
foreach (string categoryKey in entityLists.Keys) foreach (string categoryKey in entityLists.Keys)
@@ -926,19 +929,33 @@ namespace Barotrauma
new GUIFrame(new RectTransform(Vector2.One, categoryFrame.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(Vector2.One, categoryFrame.RectTransform), style: "HorizontalLine");
string categoryName = entityLists[categoryKey].First().Subcategory; string categoryName = TextManager.Get("MapEntityCategory." + entityLists[categoryKey].First().Category);
categoryName = string.IsNullOrEmpty(categoryName) ? string subCategoryName = entityLists[categoryKey].First().Subcategory;
TextManager.Get("mapentitycategory.misc") : if (string.IsNullOrEmpty(subCategoryName))
(TextManager.Get("subcategory." + categoryName, returnNull: true) ?? categoryName);
new GUITextBlock(
new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft),
categoryName,
textAlignment: Alignment.TopLeft,
font: GUI.SubHeadingFont,
wrap: true)
{ {
Padding = new Vector4(GUI.IntScale(10)) new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft),
}; categoryName, textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont, wrap: true)
{
Padding = new Vector4(GUI.IntScale(10))
};
}
else
{
subCategoryName = string.IsNullOrEmpty(subCategoryName) ?
TextManager.Get("mapentitycategory.misc") :
(TextManager.Get("subcategory." + subCategoryName, returnNull: true) ?? subCategoryName);
var categoryTitle = new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft),
categoryName, textAlignment: Alignment.TopLeft, font: GUI.Font, wrap: true)
{
Padding = new Vector4(GUI.IntScale(10))
};
new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(0, (int)(categoryTitle.TextSize.Y + GUI.IntScale(10))) },
subCategoryName, textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont, wrap: true)
{
Padding = new Vector4(GUI.IntScale(10))
};
}
var entityListInner = new GUIListBox(new RectTransform(new Point(categoryFrame.Rect.Width - maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.CenterRight), var entityListInner = new GUIListBox(new RectTransform(new Point(categoryFrame.Rect.Width - maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.CenterRight),
style: null, style: null,
@@ -959,7 +976,6 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (ep.HideInMenus) { continue; } if (ep.HideInMenus) { continue; }
#endif #endif
CreateEntityElement(ep, entitiesPerRow, entityListInner.Content); CreateEntityElement(ep, entitiesPerRow, entityListInner.Content);
} }
@@ -976,6 +992,9 @@ namespace Barotrauma
foreach (MapEntityPrefab ep in MapEntityPrefab.List) foreach (MapEntityPrefab ep in MapEntityPrefab.List)
{ {
#if !DEBUG
if (ep.HideInMenus) { continue; }
#endif
CreateEntityElement(ep, entitiesPerRow, allEntityList.Content); CreateEntityElement(ep, entitiesPerRow, allEntityList.Content);
} }
} }
@@ -1354,7 +1373,7 @@ namespace Barotrauma
{ {
try try
{ {
Barotrauma.IO.Validation.DevException = true; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
TimeSpan time = DateTime.UtcNow - DateTime.MinValue; TimeSpan time = DateTime.UtcNow - DateTime.MinValue;
string filePath = Path.Combine(autoSavePath, $"AutoSave_{(ulong)time.TotalMilliseconds}.sub"); string filePath = Path.Combine(autoSavePath, $"AutoSave_{(ulong)time.TotalMilliseconds}.sub");
SaveUtil.CompressStringToFile(filePath, doc.ToString()); SaveUtil.CompressStringToFile(filePath, doc.ToString());
@@ -1390,7 +1409,7 @@ namespace Barotrauma
} }
}); });
Barotrauma.IO.Validation.DevException = false; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
CrossThread.RequestExecutionOnMainThread(DisplayAutoSavePrompt); CrossThread.RequestExecutionOnMainThread(DisplayAutoSavePrompt);
} }
catch (Exception e) catch (Exception e)
@@ -1558,9 +1577,9 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = (bt, userdata) => msgBox.Buttons[0].OnClicked = (bt, userdata) =>
{ {
contentPackage.AddFile(savePath, ContentType.OutpostModule); contentPackage.AddFile(savePath, ContentType.OutpostModule);
Barotrauma.IO.Validation.DevException = true; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
contentPackage.Save(contentPackage.Path, reload: false); contentPackage.Save(contentPackage.Path, reload: false);
Barotrauma.IO.Validation.DevException = false; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
msgBox.Close(); msgBox.Close();
return true; return true;
}; };
@@ -1635,7 +1654,7 @@ namespace Barotrauma
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
Barotrauma.IO.Validation.DevException = true; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
if (previewImage?.Sprite?.Texture != null && !previewImage.Sprite.Texture.IsDisposed && Submarine.MainSub.Info.Type != SubmarineType.OutpostModule) if (previewImage?.Sprite?.Texture != null && !previewImage.Sprite.Texture.IsDisposed && Submarine.MainSub.Info.Type != SubmarineType.OutpostModule)
{ {
bool savePreviewImage = true; bool savePreviewImage = true;
@@ -1655,7 +1674,7 @@ namespace Barotrauma
{ {
Submarine.MainSub.SaveAs(savePath); Submarine.MainSub.SaveAs(savePath);
} }
Barotrauma.IO.Validation.DevException = false; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
Submarine.MainSub.CheckForErrors(); Submarine.MainSub.CheckForErrors();
@@ -4695,6 +4714,7 @@ namespace Barotrauma
} }
graphics.Clear(backgroundColor); graphics.Clear(backgroundColor);
ImageManager.Draw(spriteBatch, cam); ImageManager.Draw(spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
@@ -63,8 +63,18 @@ namespace Barotrauma.Sounds
ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16; ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16;
SampleRate = reader.SampleRate; SampleRate = reader.SampleRate;
if (Buffers != null && SoundBuffers.BuffersGenerated < SoundBuffers.MaxBuffers)
{
Buffers.RequestAlBuffers(); FillBuffers();
}
}
public override void FillBuffers()
{
if (!Stream) if (!Stream)
{ {
reader.DecodedPosition = 0;
int bufferSize = (int)reader.TotalSamples * reader.Channels; int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] floatBuffer = new float[bufferSize]; float[] floatBuffer = new float[bufferSize];
@@ -86,7 +96,7 @@ namespace Barotrauma.Sounds
CastBuffer(floatBuffer, shortBuffer, readSamples); CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALBuffer, ALFormat, shortBuffer, Al.BufferData(Buffers.AlBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate); readSamples * sizeof(short), SampleRate);
int alError = Al.GetError(); int alError = Al.GetError();
@@ -99,7 +109,7 @@ namespace Barotrauma.Sounds
CastBuffer(floatBuffer, shortBuffer, readSamples); CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALMuffledBuffer, ALFormat, shortBuffer, Al.BufferData(Buffers.AlMuffledBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate); readSamples * sizeof(short), SampleRate);
alError = Al.GetError(); alError = Al.GetError();
@@ -52,16 +52,10 @@ namespace Barotrauma.Sounds
} }
} }
private uint alBuffer; private SoundBuffers buffers;
public uint ALBuffer public SoundBuffers Buffers
{ {
get { return !Stream ? alBuffer : 0; } get { return !Stream ? buffers : null; }
}
private uint alMuffledBuffer;
public uint ALMuffledBuffer
{
get { return !Stream ? alMuffledBuffer : 0; }
} }
public int ALFormat public int ALFormat
@@ -169,69 +163,20 @@ namespace Barotrauma.Sounds
{ {
if (!Stream) if (!Stream)
{ {
Al.GenBuffer(out alBuffer); buffers = new SoundBuffers(this);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
Al.GenBuffer(out alMuffledBuffer);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
if (!Al.IsBuffer(alMuffledBuffer))
{
throw new Exception("Generated OpenAL buffer is invalid!");
}
} }
else else
{ {
alBuffer = 0; buffers = null;
} }
} }
public virtual void FillBuffers() { }
public virtual void DeleteALBuffers() public virtual void DeleteALBuffers()
{ {
Owner.KillChannels(this); Owner.KillChannels(this);
if (alBuffer != 0) buffers?.Dispose();
{
if (!Al.IsBuffer(alBuffer))
{
throw new Exception("Buffer to delete is invalid!");
}
Al.DeleteBuffer(alBuffer); alBuffer = 0;
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
}
if (alMuffledBuffer != 0)
{
if (!Al.IsBuffer(alMuffledBuffer))
{
throw new Exception("Buffer to delete is invalid!");
}
Al.DeleteBuffer(alMuffledBuffer); alMuffledBuffer = 0;
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError));
}
}
} }
public virtual void Dispose() public virtual void Dispose()
@@ -334,7 +334,11 @@ namespace Barotrauma.Sounds
return; return;
} }
Al.Sourcei(alSource, Al.Buffer, muffled ? (int)Sound.ALMuffledBuffer : (int)Sound.ALBuffer); if (Sound.Buffers.RequestAlBuffers())
{
Sound.FillBuffers();
}
Al.Sourcei(alSource, Al.Buffer, muffled ? (int)Sound.Buffers.AlMuffledBuffer : (int)Sound.Buffers.AlBuffer);
alError = Al.GetError(); alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -506,17 +510,26 @@ namespace Barotrauma.Sounds
throw new Exception("Failed to reset source buffer: " + debugName + ", " + Al.GetErrorString(alError)); throw new Exception("Failed to reset source buffer: " + debugName + ", " + Al.GetErrorString(alError));
} }
if (!Al.IsBuffer(sound.ALBuffer)) if (Sound.Buffers.RequestAlBuffers())
{
Sound.FillBuffers();
}
if (!Al.IsBuffer(sound.Buffers.AlBuffer))
{ {
throw new Exception(sound.Filename + " has an invalid buffer!"); throw new Exception(sound.Filename + " has an invalid buffer!");
} }
if (!Al.IsBuffer(sound.Buffers.AlMuffledBuffer))
{
throw new Exception(sound.Filename + " has an invalid muffled buffer!");
}
uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer; uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffled ? Sound.Buffers.AlMuffledBuffer : Sound.Buffers.AlBuffer;
Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer); Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer);
alError = Al.GetError(); alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
{ {
throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + sound.ALBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError)); throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + alBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError));
} }
Al.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex)); Al.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex));
@@ -528,7 +541,7 @@ namespace Barotrauma.Sounds
} }
else else
{ {
uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer; uint alBuffer = 0;
Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer); Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer);
int alError = Al.GetError(); int alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -30,6 +30,8 @@ namespace Barotrauma.Sounds
private readonly SoundSourcePool[] sourcePools; private readonly SoundSourcePool[] sourcePools;
private readonly List<Sound> loadedSounds; private readonly List<Sound> loadedSounds;
public IReadOnlyList<Sound> LoadedSounds => loadedSounds;
private readonly SoundChannel[][] playingChannels = new SoundChannel[2][]; private readonly SoundChannel[][] playingChannels = new SoundChannel[2][];
private readonly object threadDeathMutex = new object(); private readonly object threadDeathMutex = new object();
@@ -172,22 +172,22 @@ namespace Barotrauma
return null; return null;
} }
public void Draw(SpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None) public void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None)
{ {
this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect); this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect);
} }
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{ {
this.Draw(spriteBatch, pos, color, this.origin, rotate, new Vector2(scale, scale), spriteEffect, depth); this.Draw(spriteBatch, pos, color, this.origin, rotate, new Vector2(scale, scale), spriteEffect, depth);
} }
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{ {
this.Draw(spriteBatch, pos, color, origin, rotate, new Vector2(scale, scale), spriteEffect, depth); this.Draw(spriteBatch, pos, color, origin, rotate, new Vector2(scale, scale), spriteEffect, depth);
} }
public virtual void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) public virtual void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
{ {
if (Texture == null) { return; } if (Texture == null) { return; }
//DrawSilhouette(spriteBatch, pos, origin, rotate, scale, spriteEffect, depth); //DrawSilhouette(spriteBatch, pos, origin, rotate, scale, spriteEffect, depth);
@@ -209,7 +209,7 @@ namespace Barotrauma
} }
} }
public void DrawTiled(SpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, public void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize,
Color? color = null, Vector2? startOffset = null, Vector2? textureScale = null, float? depth = null) Color? color = null, Vector2? startOffset = null, Vector2? textureScale = null, float? depth = null)
{ {
if (Texture == null) { return; } if (Texture == null) { return; }
@@ -5,14 +5,14 @@ namespace Barotrauma
{ {
partial class SpriteSheet : Sprite partial class SpriteSheet : Sprite
{ {
public override void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) public override void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
{ {
if (texture == null) return; if (texture == null) return;
spriteBatch.Draw(texture, pos + offset, sourceRects[0], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth); spriteBatch.Draw(texture, pos + offset, sourceRects[0], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
} }
public void Draw(SpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) public void Draw(ISpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
{ {
if (texture == null) return; if (texture == null) return;
@@ -100,38 +100,6 @@ namespace Barotrauma
return q1; return q1;
} }
/// <summary>
/// Convert a HSV value into a RGB value.
/// </summary>
/// <param name="hue">Value between 0 and 360</param>
/// <param name="saturation">Value between 0 and 1</param>
/// <param name="value">Value between 0 and 1</param>
/// <see href="https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB">Reference</see>
/// <returns></returns>
public static Color HSVToRGB(float hue, float saturation, float value)
{
float c = value * saturation;
float h = Math.Clamp(hue, 0, 360) / 60f;
float x = c * (1 - Math.Abs(h % 2 - 1));
float r = 0,
g = 0,
b = 0;
if (0 <= h && h <= 1) { r = c; g = x; b = 0; }
else if (1 < h && h <= 2) { r = x; g = c; b = 0; }
else if (2 < h && h <= 3) { r = 0; g = c; b = x; }
else if (3 < h && h <= 4) { r = 0; g = x; b = c; }
else if (4 < h && h <= 5) { r = x; g = 0; b = c; }
else if (5 < h && h <= 6) { r = c; g = 0; b = x; }
float m = value - c;
return new Color(r + m, g + m, b + m);
}
/// <summary> /// <summary>
/// Convert a RGB value into a HSV value. /// Convert a RGB value into a HSV value.
/// </summary> /// </summary>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+62 -62
View File
@@ -1,62 +1,62 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security> <security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" /> <requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges> </requestedPrivileges>
</security> </security>
</trustInfo> </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>
<!-- A list of the Windows versions that this application has been tested on <!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. --> and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista --> <!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />--> <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 --> <!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 --> <!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 --> <!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 --> <!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application> </application>
</compatibility> </compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings> <windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
</windowsSettings> </windowsSettings>
</application> </application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) --> <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!-- <!--
<dependency> <dependency>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity <assemblyIdentity
type="win32" type="win32"
name="Microsoft.Windows.Common-Controls" name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" version="6.0.0.0"
processorArchitecture="*" processorArchitecture="*"
publicKeyToken="6595b64144ccf1df" publicKeyToken="6595b64144ccf1df"
language="*" language="*"
/> />
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
--> -->
</assembly> </assembly>
View File
View File
View File
+139 -139
View File
@@ -1,139 +1,139 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>DEBUG;TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;LINUX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath> <OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Content Include="DedicatedServer.exe" CopyToOutputDirectory="PreserveNewest" /> <Content Include="DedicatedServer.exe" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
</ItemGroup> </ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<PropertyGroup> <PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)--> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <VerFile>$(IntermediateOutputPath)gitver</VerFile>
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
</PropertyGroup> </PropertyGroup>
<!-- write the hash to the temp file.--> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" /> <Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> </Exec>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<!-- read the version into the GitVersion itemGroup--> <!-- read the version into the GitVersion itemGroup-->
<ReadLinesFromFile File="$(VerFile)"> <ReadLinesFromFile File="$(VerFile)">
<Output TaskParameter="Lines" ItemName="GitVersion" /> <Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildHash>@(GitVersion)</BuildHash> <BuildHash>@(GitVersion)</BuildHash>
</PropertyGroup> </PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <!-- read the branch into the GitBranch itemGroup-->
<ReadLinesFromFile File="$(BranchFile)"> <ReadLinesFromFile File="$(BranchFile)">
<Output TaskParameter="Lines" ItemName="GitBranch" /> <Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch> <BuildBranch>@(GitBranch)</BuildBranch>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
<!-- names the obj/.../CustomAssemblyInfo.cs file --> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<PropertyGroup> <PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
</PropertyGroup> </PropertyGroup>
<!-- includes the CustomAssemblyInfo for compilation into your project --> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<ItemGroup> <ItemGroup>
<Compile Include="$(CustomAssemblyInfoFile)" /> <Compile Include="$(CustomAssemblyInfoFile)" />
</ItemGroup> </ItemGroup>
<!-- defines the AssemblyMetadata attribute that will be written --> <!-- defines the AssemblyMetadata attribute that will be written -->
<ItemGroup> <ItemGroup>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitRevision</_Parameter1> <_Parameter1>GitRevision</_Parameter1>
<_Parameter2>$(BuildHash)</_Parameter2> <_Parameter2>$(BuildHash)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitBranch</_Parameter1> <_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2> <_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1> <_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2> <_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
</ItemGroup> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file --> <!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
</Project> </Project>
+152 -152
View File
@@ -1,152 +1,152 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<ReleaseVersion>0.9.0.0</ReleaseVersion> <ReleaseVersion>0.9.0.0</ReleaseVersion>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\DebugMac</OutputPath> <OutputPath>..\bin\DebugMac</OutputPath>
<ConsolePause>true</ConsolePause> <ConsolePause>true</ConsolePause>
<CheckForOverflowUnderflow></CheckForOverflowUnderflow> <CheckForOverflowUnderflow></CheckForOverflowUnderflow>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType></DebugType> <DebugType></DebugType>
<OutputPath>..\bin\ReleaseMac</OutputPath> <OutputPath>..\bin\ReleaseMac</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType /> <DebugType />
<OutputPath>..\bin\ReleaseMac</OutputPath> <OutputPath>..\bin\ReleaseMac</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE</DefineConstants> <DefineConstants>TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Mac\</OutputPath> <OutputPath>..\bin\$(Configuration)Mac\</OutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\libsteam_api64.dylib" /> <Content Remove="..\BarotraumaShared\libsteam_api64.dylib" />
<Content Remove="..\BarotraumaShared\libsteam_api64.so" /> <Content Remove="..\BarotraumaShared\libsteam_api64.so" />
<Content Remove="DedicatedServer.exe" /> <Content Remove="DedicatedServer.exe" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
</ItemGroup> </ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<ItemGroup> <ItemGroup>
<None Include="..\BarotraumaShared\libsteam_api64.dylib"> <None Include="..\BarotraumaShared\libsteam_api64.dylib">
<Link>libsteam_api64.dylib</Link> <Link>libsteam_api64.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<PropertyGroup> <PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)--> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <VerFile>$(IntermediateOutputPath)gitver</VerFile>
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
</PropertyGroup> </PropertyGroup>
<!-- write the hash to the temp file.--> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" /> <Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> </Exec>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<!-- read the version into the GitVersion itemGroup--> <!-- read the version into the GitVersion itemGroup-->
<ReadLinesFromFile File="$(VerFile)"> <ReadLinesFromFile File="$(VerFile)">
<Output TaskParameter="Lines" ItemName="GitVersion" /> <Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildHash>@(GitVersion)</BuildHash> <BuildHash>@(GitVersion)</BuildHash>
</PropertyGroup> </PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <!-- read the branch into the GitBranch itemGroup-->
<ReadLinesFromFile File="$(BranchFile)"> <ReadLinesFromFile File="$(BranchFile)">
<Output TaskParameter="Lines" ItemName="GitBranch" /> <Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch> <BuildBranch>@(GitBranch)</BuildBranch>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
<!-- names the obj/.../CustomAssemblyInfo.cs file --> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<PropertyGroup> <PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
</PropertyGroup> </PropertyGroup>
<!-- includes the CustomAssemblyInfo for compilation into your project --> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<ItemGroup> <ItemGroup>
<Compile Include="$(CustomAssemblyInfoFile)" /> <Compile Include="$(CustomAssemblyInfoFile)" />
</ItemGroup> </ItemGroup>
<!-- defines the AssemblyMetadata attribute that will be written --> <!-- defines the AssemblyMetadata attribute that will be written -->
<ItemGroup> <ItemGroup>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitRevision</_Parameter1> <_Parameter1>GitRevision</_Parameter1>
<_Parameter2>$(BuildHash)</_Parameter2> <_Parameter2>$(BuildHash)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitBranch</_Parameter1> <_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2> <_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1> <_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2> <_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
</ItemGroup> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file --> <!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
</Project> </Project>
@@ -26,6 +26,7 @@ namespace Barotrauma
{ {
msg.Write(ID); msg.Write(ID);
msg.Write(Name); msg.Write(Name);
msg.Write(OriginalName);
msg.Write((byte)Gender); msg.Write((byte)Gender);
msg.Write((byte)Race); msg.Write((byte)Race);
msg.Write((byte)HeadSpriteId); msg.Write((byte)HeadSpriteId);
@@ -17,6 +17,8 @@ namespace Barotrauma
foreach (Character character in characters) foreach (Character character in characters)
{ {
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false); character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.Write(requireKill.Contains(character));
msg.Write(requireRescue.Contains(character));
msg.Write((ushort)characterItems[character].Count()); msg.Write((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character]) foreach (Item item in characterItems[character])
{ {
@@ -32,11 +32,11 @@ namespace Barotrauma
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); } get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); }
} }
public static void StartNewCampaign(string savePath, string subPath, string seed) public static void StartNewCampaign(string savePath, string subPath, string seed, CampaignSettings settings)
{ {
if (string.IsNullOrWhiteSpace(savePath)) { return; } if (string.IsNullOrWhiteSpace(savePath)) { return; }
GameMain.GameSession = new GameSession(new SubmarineInfo(subPath), savePath, GameModePreset.MultiPlayerCampaign, seed); GameMain.GameSession = new GameSession(new SubmarineInfo(subPath), savePath, GameModePreset.MultiPlayerCampaign, settings, seed);
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
@@ -74,7 +74,7 @@ namespace Barotrauma
DebugConsole.ShowQuestionPrompt("Enter a save name for the campaign:", (string saveName) => DebugConsole.ShowQuestionPrompt("Enter a save name for the campaign:", (string saveName) =>
{ {
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName); string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
StartNewCampaign(savePath, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed); StartNewCampaign(savePath, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed, CampaignSettings.Empty);
}); });
} }
else else
@@ -667,13 +667,24 @@ namespace Barotrauma
} }
bool validateHires = msg.ReadBoolean(); bool validateHires = msg.ReadBoolean();
bool fireCharacter = msg.ReadBoolean();
bool renameCharacter = msg.ReadBoolean();
int renamedIdentifier = -1;
string newName = null;
bool existingCrewMember = false;
if (renameCharacter)
{
renamedIdentifier = msg.ReadInt32();
newName = msg.ReadString();
existingCrewMember = msg.ReadBoolean();
}
bool fireCharacter = msg.ReadBoolean();
int firedIdentifier = -1; int firedIdentifier = -1;
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); } if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
Location location = map?.CurrentLocation; Location location = map?.CurrentLocation;
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
CharacterInfo firedCharacter = null; CharacterInfo firedCharacter = null;
if (location != null && AllowedToManageCampaign(sender)) if (location != null && AllowedToManageCampaign(sender))
@@ -691,13 +702,45 @@ namespace Barotrauma
} }
} }
if (renameCharacter)
{
CharacterInfo characterInfo = null;
if (existingCrewMember && CrewManager != null)
{
characterInfo = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
}
else if(!existingCrewMember && location.HireManager != null)
{
characterInfo = location.HireManager.AvailableCharacters.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
}
if (characterInfo != null && (characterInfo.Character?.IsBot ?? true))
{
if (existingCrewMember)
{
CrewManager.RenameCharacter(characterInfo, newName);
}
else
{
location.HireManager.RenameCharacter(characterInfo, newName);
}
}
else
{
DebugConsole.ThrowError($"Tried to rename an invalid character ({renamedIdentifier})");
}
}
if (location.HireManager != null) if (location.HireManager != null)
{ {
if (validateHires) if (validateHires)
{ {
foreach (CharacterInfo hireInfo in location.HireManager.PendingHires) foreach (CharacterInfo hireInfo in location.HireManager.PendingHires)
{ {
TryHireCharacter(location, hireInfo); if (TryHireCharacter(location, hireInfo))
{
hiredCharacters.Add(hireInfo);
};
} }
} }
@@ -706,10 +749,10 @@ namespace Barotrauma
List<CharacterInfo> pendingHireInfos = new List<CharacterInfo>(); List<CharacterInfo> pendingHireInfos = new List<CharacterInfo>();
foreach (int identifier in pendingHires) foreach (int identifier in pendingHires)
{ {
CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifier() == identifier); CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == identifier);
if (match == null) if (match == null)
{ {
DebugConsole.ThrowError($"Tried to hire a character that doesn't exist ({identifier})"); DebugConsole.ThrowError($"Tried to add a character that doesn't exist ({identifier}) to pending hires");
continue; continue;
} }
@@ -721,25 +764,39 @@ namespace Barotrauma
} }
location.HireManager.PendingHires = pendingHireInfos; location.HireManager.PendingHires = pendingHireInfos;
} }
location.HireManager.AvailableCharacters.ForEachMod(info =>
{
if(!location.HireManager.PendingHires.Contains(info))
{
location.HireManager.RenameCharacter(info, info.OriginalName);
}
});
} }
} }
// bounce back // bounce back
SendCrewState(validateHires, firedCharacter); if (renameCharacter && existingCrewMember)
{
SendCrewState(hiredCharacters, (renamedIdentifier, newName), firedCharacter);
}
else
{
SendCrewState(hiredCharacters, default, firedCharacter);
}
} }
/// <summary> /// <summary>
/// Notifies the clients of the current bot situation like syncing pending and available hires /// Notifies the clients of the current bot situation like syncing pending and available hires
/// available hires are also synced
/// </summary> /// </summary>
/// <param name="validateHires">When set to true notifies the clients that the hires have been validated.</param> /// <param name="hiredCharacters">Inform the clients that these characters have been hired.</param>
/// <param name="firedCharacter">When not null will inform the clients that his character has been fired.</param> /// <param name="firedCharacter">Inform the clients that this character has been fired.</param>
/// <remarks> /// <remarks>
/// It might be obsolete to sync available hires. I found that the available hires are always the same between /// It might be obsolete to sync available hires. I found that the available hires are always the same between
/// the client and the server when there's only one person on the server but when a second person joins both of /// the client and the server when there's only one person on the server but when a second person joins both of
/// their available hires are different from the server. /// their available hires are different from the server.
/// </remarks> /// </remarks>
public void SendCrewState(bool validateHires, CharacterInfo firedCharacter) public void SendCrewState(List<CharacterInfo> hiredCharacters, (int id, string newName) renamedCrewMember, CharacterInfo firedCharacter)
{ {
List<CharacterInfo> availableHires = new List<CharacterInfo>(); List<CharacterInfo> availableHires = new List<CharacterInfo>();
List<CharacterInfo> pendingHires = new List<CharacterInfo>(); List<CharacterInfo> pendingHires = new List<CharacterInfo>();
@@ -765,10 +822,26 @@ namespace Barotrauma
msg.Write((ushort)pendingHires.Count); msg.Write((ushort)pendingHires.Count);
foreach (CharacterInfo pendingHire in pendingHires) foreach (CharacterInfo pendingHire in pendingHires)
{ {
msg.Write(pendingHire.GetIdentifier()); msg.Write(pendingHire.GetIdentifierUsingOriginalName());
}
msg.Write((ushort)(hiredCharacters?.Count ?? 0));
if(hiredCharacters != null)
{
foreach (CharacterInfo info in hiredCharacters)
{
info.ServerWrite(msg);
msg.Write(info.Salary);
}
}
bool validRenaming = renamedCrewMember.id > -1 && !string.IsNullOrEmpty(renamedCrewMember.newName);
msg.Write(validRenaming);
if (validRenaming)
{
msg.Write(renamedCrewMember.id);
msg.Write(renamedCrewMember.newName);
} }
msg.Write(validateHires);
msg.Write(firedCharacter != null); msg.Write(firedCharacter != null);
if (firedCharacter != null) { msg.Write(firedCharacter.GetIdentifier()); } if (firedCharacter != null) { msg.Write(firedCharacter.GetIdentifier()); }
@@ -786,6 +859,7 @@ namespace Barotrauma
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs), new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs), new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled)); new XAttribute("cheatsenabled", CheatsEnabled));
modeElement.Add(Settings.Save());
CampaignMetadata?.Save(modeElement); CampaignMetadata?.Save(modeElement);
Map.Save(modeElement); Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement); CargoManager?.SavePurchasedItems(modeElement);
@@ -121,13 +121,14 @@ namespace Barotrauma.Networking
} }
} }
public bool IsBanned(IPAddress IP, ulong steamID, out string reason) public bool IsBanned(IPAddress IP, ulong steamID, ulong ownerSteamID, out string reason)
{ {
reason = string.Empty; reason = string.Empty;
if (IPAddress.IsLoopback(IP)) { return false; } if (IPAddress.IsLoopback(IP)) { return false; }
var bannedPlayer = bannedPlayers.Find(bp => var bannedPlayer = bannedPlayers.Find(bp =>
bp.CompareTo(IP) || bp.CompareTo(IP) ||
(steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID))); (steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID)) ||
(ownerSteamID > 0 && (bp.SteamID == ownerSteamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == ownerSteamID)));
reason = bannedPlayer?.Reason; reason = bannedPlayer?.Reason;
return bannedPlayer != null; return bannedPlayer != null;
} }
@@ -166,6 +167,7 @@ namespace Barotrauma.Networking
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration) public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
{ {
if (steamID == 0) { return; }
BanPlayer(name, "", steamID, reason, duration); BanPlayer(name, "", steamID, reason, duration);
} }
@@ -284,6 +284,7 @@ namespace Barotrauma.Networking
newClient.Connection = connection; newClient.Connection = connection;
newClient.Connection.Status = NetworkConnectionStatus.Connected; newClient.Connection.Status = NetworkConnectionStatus.Connected;
newClient.SteamID = connection.SteamID; newClient.SteamID = connection.SteamID;
newClient.OwnerSteamID = connection.OwnerSteamID;
newClient.Language = connection.Language; newClient.Language = connection.Language;
ConnectedClients.Add(newClient); ConnectedClients.Add(newClient);
@@ -308,12 +309,13 @@ namespace Barotrauma.Networking
SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient);
} }
SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined); SendChatMessage($"ServerMessage.JoinedServer~[client]={ClientLogName(newClient)}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined);
serverSettings.ServerDetailsChanged = true; serverSettings.ServerDetailsChanged = true;
if (previousPlayer != null && previousPlayer.Name != newClient.Name) if (previousPlayer != null && previousPlayer.Name != newClient.Name)
{ {
SendChatMessage($"ServerMessage.PreviousClientName~[client]={clName}~[previousname]={previousPlayer.Name}", ChatMessageType.Server, null); string prevNameSanitized = previousPlayer.Name.Replace("‖", "");
SendChatMessage($"ServerMessage.PreviousClientName~[client]={ClientLogName(newClient)}~[previousname]={prevNameSanitized}", ChatMessageType.Server, null);
previousPlayer.Name = newClient.Name; previousPlayer.Name = newClient.Name;
} }
@@ -475,12 +477,14 @@ namespace Barotrauma.Networking
} }
else if (isCrewDead && respawnManager == null) else if (isCrewDead && respawnManager == null)
{ {
#if !DEBUG
if (endRoundTimer <= 0.0f) if (endRoundTimer <= 0.0f)
{ {
SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server); SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server);
} }
endRoundDelay = 60.0f; endRoundDelay = 60.0f;
endRoundTimer += deltaTime; endRoundTimer += deltaTime;
#endif
} }
else else
{ {
@@ -752,6 +756,7 @@ namespace Barotrauma.Networking
string seed = inc.ReadString(); string seed = inc.ReadString();
string subName = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString(); string subHash = inc.ReadString();
CampaignSettings settings = new CampaignSettings(inc);
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash); var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
@@ -772,10 +777,10 @@ namespace Barotrauma.Networking
string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName); string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign)) if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign))
{ {
MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed); MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings);
} }
} }
} }
else else
{ {
string saveName = inc.ReadString(); string saveName = inc.ReadString();
@@ -1853,6 +1858,8 @@ namespace Barotrauma.Networking
{ {
outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f); outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f);
} }
outmsg.Write(serverSettings.RadiationEnabled);
} }
else else
{ {
@@ -2034,12 +2041,12 @@ namespace Barotrauma.Networking
} }
} }
startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(selectedSub, selectedShuttle, selectedMode), false); startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(selectedSub, selectedShuttle, selectedMode, CampaignSettings.Unsure), false);
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> StartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode) private IEnumerable<object> StartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode, CampaignSettings settings)
{ {
entityEventManager.Clear(); entityEventManager.Clear();
@@ -2067,7 +2074,7 @@ namespace Barotrauma.Networking
//don't instantiate a new gamesession if we're playing a campaign //don't instantiate a new gamesession if we're playing a campaign
if (campaign == null || GameMain.GameSession == null) if (campaign == null || GameMain.GameSession == null)
{ {
GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, GameMain.NetLobbyScreen.LevelSeed, missionType: GameMain.NetLobbyScreen.MissionType); GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, settings, GameMain.NetLobbyScreen.LevelSeed, missionType: GameMain.NetLobbyScreen.MissionType);
} }
List<Client> playingClients = new List<Client>(connectedClients); List<Client> playingClients = new List<Client>(connectedClients);
@@ -2716,6 +2723,10 @@ namespace Barotrauma.Networking
{ {
serverSettings.BanList.BanPlayer(client.Name, client.SteamID, reason, duration); serverSettings.BanList.BanPlayer(client.Name, client.SteamID, reason, duration);
} }
if (client.OwnerSteamID > 0)
{
serverSettings.BanList.BanPlayer(client.Name, client.OwnerSteamID, reason, duration);
}
} }
public void BanPreviousPlayer(PreviousPlayer previousPlayer, string reason, bool range = false, TimeSpan? duration = null) public void BanPreviousPlayer(PreviousPlayer previousPlayer, string reason, bool range = false, TimeSpan? duration = null)
@@ -2735,6 +2746,10 @@ namespace Barotrauma.Networking
{ {
serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.SteamID, reason, duration); serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.SteamID, reason, duration);
} }
if (previousPlayer.OwnerSteamID > 0)
{
serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.OwnerSteamID, reason, duration);
}
string msg = $"ServerMessage.BannedFromServer~[client]={previousPlayer.Name}"; string msg = $"ServerMessage.BannedFromServer~[client]={previousPlayer.Name}";
if (!string.IsNullOrWhiteSpace(reason)) if (!string.IsNullOrWhiteSpace(reason))
@@ -2784,7 +2799,7 @@ namespace Barotrauma.Networking
client.HasSpawned = false; client.HasSpawned = false;
client.InGame = false; client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={client.Name}"; } if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={ClientLogName(client)}"; }
if (string.IsNullOrWhiteSpace(targetmsg)) { targetmsg = "ServerMessage.YouLeftServer"; } if (string.IsNullOrWhiteSpace(targetmsg)) { targetmsg = "ServerMessage.YouLeftServer"; }
if (!string.IsNullOrWhiteSpace(reason)) if (!string.IsNullOrWhiteSpace(reason))
{ {
@@ -3747,7 +3762,7 @@ namespace Barotrauma.Networking
{ {
retVal += "color:#ff9900;"; retVal += "color:#ff9900;";
} }
retVal += "metadata:" + (client.SteamID != 0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name) + "‖end‖"; retVal += "metadata:" + (client.SteamID != 0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name).Replace("‖","") + "‖end‖";
return retVal; return retVal;
} }
@@ -3826,6 +3841,7 @@ namespace Barotrauma.Networking
public string Name; public string Name;
public string EndPoint; public string EndPoint;
public UInt64 SteamID; public UInt64 SteamID;
public UInt64 OwnerSteamID;
public float Karma; public float Karma;
public int KarmaKickCount; public int KarmaKickCount;
public readonly List<Client> KickVoters = new List<Client>(); public readonly List<Client> KickVoters = new List<Client>();
@@ -3835,6 +3851,7 @@ namespace Barotrauma.Networking
Name = c.Name; Name = c.Name;
EndPoint = c.Connection?.EndPointString ?? ""; EndPoint = c.Connection?.EndPointString ?? "";
SteamID = c.SteamID; SteamID = c.SteamID;
OwnerSteamID = c.OwnerSteamID;
} }
public bool MatchesClient(Client c) public bool MatchesClient(Client c)
@@ -184,7 +184,7 @@ namespace Barotrauma.Networking
return; return;
} }
if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, out string banReason)) if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, 0, out string banReason))
{ {
//IP banned: deny immediately //IP banned: deny immediately
inc.SenderConnection.Deny(DisconnectReason.Banned.ToString() + "/ " + banReason); inc.SenderConnection.Deny(DisconnectReason.Banned.ToString() + "/ " + banReason);
@@ -233,7 +233,7 @@ namespace Barotrauma.Networking
return; return;
} }
if (pendingClient != null) { pendingClients.Remove(pendingClient); } if (pendingClient != null) { pendingClients.Remove(pendingClient); }
if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, out string banReason)) if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, conn.OwnerSteamID, out string banReason))
{ {
Disconnect(conn, DisconnectReason.Banned.ToString() + "/ " + banReason); Disconnect(conn, DisconnectReason.Banned.ToString() + "/ " + banReason);
return; return;
@@ -308,7 +308,8 @@ namespace Barotrauma.Networking
} }
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection; LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, out string banReason)) string banReason;
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, ownerID, out banReason))
{ {
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason); RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return; return;
@@ -316,6 +317,7 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK) if (status == Steamworks.AuthResponse.OK)
{ {
pendingClient.OwnerSteamID = ownerID;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder; pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.UpdateTime = Timing.TotalTime; pendingClient.UpdateTime = Timing.TotalTime;
} }
@@ -452,7 +454,7 @@ namespace Barotrauma.Networking
pendingClient.AuthSessionStarted = true; pendingClient.AuthSessionStarted = true;
} }
} }
else //TODO: could remove since this seems impossible else
{ {
if (pendingClient.SteamID != steamId) if (pendingClient.SteamID != steamId)
{ {
@@ -49,6 +49,16 @@ namespace Barotrauma.Networking
Connection.SetSteamIDIfUnknown(value ?? 0); Connection.SetSteamIDIfUnknown(value ?? 0);
} }
} }
private UInt64? ownerSteamId;
public UInt64? OwnerSteamID
{
get { return ownerSteamId; }
set
{
ownerSteamId = value;
Connection.SetOwnerSteamIDIfUnknown(value ?? 0);
}
}
public Int32? PasswordSalt; public Int32? PasswordSalt;
public bool AuthSessionStarted; public bool AuthSessionStarted;
@@ -59,6 +69,7 @@ namespace Barotrauma.Networking
InitializationStep = ConnectionInitialization.SteamTicketAndVersion; InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
Retries = 0; Retries = 0;
SteamID = null; SteamID = null;
OwnerSteamID = null;
PasswordSalt = null; PasswordSalt = null;
UpdateTime = Timing.TotalTime + Timing.Step * 3.0; UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
TimeOut = NetworkConnection.TimeoutThreshold; TimeOut = NetworkConnection.TimeoutThreshold;
@@ -107,8 +118,8 @@ namespace Barotrauma.Networking
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion, RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}"); $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); GameServer.Log($"{name} ({steamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); DebugConsole.NewMessage($"{name} ({steamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return; return;
} }
@@ -119,7 +130,7 @@ namespace Barotrauma.Networking
if (nameTaken != null) if (nameTaken != null)
{ {
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, ""); RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error); GameServer.Log($"{name} ({steamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return; return;
} }
@@ -172,6 +183,7 @@ namespace Barotrauma.Networking
else if (pendingClient.Connection is SteamP2PConnection s) else if (pendingClient.Connection is SteamP2PConnection s)
{ {
serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration); serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration);
serverSettings.BanList.BanPlayer(pendingClient.Name, s.OwnerSteamID, banReason, duration);
} }
} }
@@ -183,7 +195,8 @@ namespace Barotrauma.Networking
} }
else if (pendingClient.Connection is SteamP2PConnection s) else if (pendingClient.Connection is SteamP2PConnection s)
{ {
return serverSettings.BanList.IsBanned(s.SteamID, out banReason); return serverSettings.BanList.IsBanned(s.SteamID, out banReason) ||
serverSettings.BanList.IsBanned(s.OwnerSteamID, out banReason);
} }
banReason = null; banReason = null;
return false; return false;
@@ -273,6 +286,7 @@ namespace Barotrauma.Networking
{ {
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value); Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
pendingClient.SteamID = null; pendingClient.SteamID = null;
pendingClient.OwnerSteamID = null;
pendingClient.AuthSessionStarted = false; pendingClient.AuthSessionStarted = false;
} }
} }
@@ -123,6 +123,7 @@ namespace Barotrauma.Networking
if (!started) { return; } if (!started) { return; }
UInt64 senderSteamId = inc.ReadUInt64(); UInt64 senderSteamId = inc.ReadUInt64();
UInt64 ownerSteamId = inc.ReadUInt64();
byte incByte = inc.ReadByte(); byte incByte = inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
@@ -145,7 +146,9 @@ namespace Barotrauma.Networking
pendingClient?.Heartbeat(); pendingClient?.Heartbeat();
connectedClient?.Heartbeat(); connectedClient?.Heartbeat();
if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason)) string banReason;
if (serverSettings.BanList.IsBanned(senderSteamId, out banReason) ||
serverSettings.BanList.IsBanned(ownerSteamId, out banReason))
{ {
if (pendingClient != null) if (pendingClient != null)
{ {
@@ -181,6 +184,10 @@ namespace Barotrauma.Networking
if (pendingClient != null) if (pendingClient != null)
{ {
if (ownerSteamId != 0)
{
pendingClient.Connection.SetOwnerSteamIDIfUnknown(ownerSteamId);
}
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null)); ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
} }
else else
@@ -223,6 +230,7 @@ namespace Barotrauma.Networking
{ {
Language = GameMain.Config.Language Language = GameMain.Config.Language
}; };
OwnerConnection.SetOwnerSteamIDIfUnknown(OwnerSteamID);
OnInitializationComplete?.Invoke(OwnerConnection); OnInitializationComplete?.Invoke(OwnerConnection);
} }
@@ -159,6 +159,8 @@ namespace Barotrauma.Networking
AutoRestart = autoRestart; AutoRestart = autoRestart;
} }
RadiationEnabled = incMsg.ReadBoolean();
changed |= true; changed |= true;
} }
@@ -11,6 +11,8 @@ namespace Barotrauma
private SubmarineInfo selectedSub; private SubmarineInfo selectedSub;
private SubmarineInfo selectedShuttle; private SubmarineInfo selectedShuttle;
public bool RadiationEnabled = true;
public SubmarineInfo SelectedSub public SubmarineInfo SelectedSub
{ {
get { return selectedSub; } get { return selectedSub; }
+147 -147
View File
@@ -1,147 +1,147 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.0</Version> <Version>0.1300.0.1</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<Configurations>Debug;Release;Unstable</Configurations> <Configurations>Debug;Release;Unstable</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|AnyCPU'">
<DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unstable|x64'">
<DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants> <DefineConstants>TRACE;SERVER;WINDOWS;X64;USE_STEAM</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<OutputPath>..\bin\$(Configuration)Windows\</OutputPath> <OutputPath>..\bin\$(Configuration)Windows\</OutputPath>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
<Compile Remove="..\BarotraumaShared\SharedSource\Networking\Primitives\Message\WrapperMsg.cs" /> <Compile Remove="..\BarotraumaShared\SharedSource\Networking\Primitives\Message\WrapperMsg.cs" />
<Content Remove="DedicatedServer.exe" /> <Content Remove="DedicatedServer.exe" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Release" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Win64.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Farseer Physics Engine 3.5\Farseer.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\GameAnalytics\GA_SDK_NETSTANDARD\GA_SDK_NETSTANDARD.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Hyper.ComponentModel\Hyper.ComponentModel.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
<ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" /> <ProjectReference Include="..\..\Libraries\Lidgren.Network\Lidgren.NetStandard.csproj" AdditionalProperties="Configuration=Debug" />
</ItemGroup> </ItemGroup>
<!-- Sourced from https://stackoverflow.com/a/45248069 --> <!-- Sourced from https://stackoverflow.com/a/45248069 -->
<Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''"> <Target Name="GetGitRevision" BeforeTargets="WriteGitRevision" Condition="'$(BuildHash)' == ''">
<PropertyGroup> <PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)--> <!-- temp file for the git version (lives in "obj" folder)-->
<VerFile>$(IntermediateOutputPath)gitver</VerFile> <VerFile>$(IntermediateOutputPath)gitver</VerFile>
<BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile> <BranchFile>$(IntermediateOutputPath)gitbranch</BranchFile>
</PropertyGroup> </PropertyGroup>
<!-- write the hash to the temp file.--> <!-- write the hash to the temp file.-->
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true"> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD &gt; $(VerFile)" ContinueOnError="true">
<Output TaskParameter="exitcode" ItemName="exitcodes" /> <Output TaskParameter="exitcode" ItemName="exitcodes" />
</Exec> </Exec>
<Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" /> <Exec Command="git -C $(ProjectDir) rev-parse --short HEAD --symbolic-full-name --abbrev-ref=strict &gt; $(BranchFile)" ContinueOnError="true" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(VerFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" /> <Exec Command="echo GIT_UNAVAILABLE &gt; $(BranchFile)" Condition="'%(exitcodes.identity)'&gt;0" />
<!-- read the version into the GitVersion itemGroup--> <!-- read the version into the GitVersion itemGroup-->
<ReadLinesFromFile File="$(VerFile)"> <ReadLinesFromFile File="$(VerFile)">
<Output TaskParameter="Lines" ItemName="GitVersion" /> <Output TaskParameter="Lines" ItemName="GitVersion" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildHash>@(GitVersion)</BuildHash> <BuildHash>@(GitVersion)</BuildHash>
</PropertyGroup> </PropertyGroup>
<!-- read the branch into the GitBranch itemGroup--> <!-- read the branch into the GitBranch itemGroup-->
<ReadLinesFromFile File="$(BranchFile)"> <ReadLinesFromFile File="$(BranchFile)">
<Output TaskParameter="Lines" ItemName="GitBranch" /> <Output TaskParameter="Lines" ItemName="GitBranch" />
</ReadLinesFromFile> </ReadLinesFromFile>
<!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.--> <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
<PropertyGroup> <PropertyGroup>
<BuildBranch>@(GitBranch)</BuildBranch> <BuildBranch>@(GitBranch)</BuildBranch>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<Target Name="WriteGitRevision" BeforeTargets="CoreCompile"> <Target Name="WriteGitRevision" BeforeTargets="CoreCompile">
<!-- names the obj/.../CustomAssemblyInfo.cs file --> <!-- names the obj/.../CustomAssemblyInfo.cs file -->
<PropertyGroup> <PropertyGroup>
<CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile> <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
</PropertyGroup> </PropertyGroup>
<!-- includes the CustomAssemblyInfo for compilation into your project --> <!-- includes the CustomAssemblyInfo for compilation into your project -->
<ItemGroup> <ItemGroup>
<Compile Include="$(CustomAssemblyInfoFile)" /> <Compile Include="$(CustomAssemblyInfoFile)" />
</ItemGroup> </ItemGroup>
<!-- defines the AssemblyMetadata attribute that will be written --> <!-- defines the AssemblyMetadata attribute that will be written -->
<ItemGroup> <ItemGroup>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitRevision</_Parameter1> <_Parameter1>GitRevision</_Parameter1>
<_Parameter2>$(BuildHash)</_Parameter2> <_Parameter2>$(BuildHash)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>GitBranch</_Parameter1> <_Parameter1>GitBranch</_Parameter1>
<_Parameter2>$(BuildBranch)</_Parameter2> <_Parameter2>$(BuildBranch)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
<AssemblyAttributes Include="AssemblyMetadata"> <AssemblyAttributes Include="AssemblyMetadata">
<_Parameter1>ProjectDir</_Parameter1> <_Parameter1>ProjectDir</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2> <_Parameter2>$(ProjectDir)</_Parameter2>
</AssemblyAttributes> </AssemblyAttributes>
</ItemGroup> </ItemGroup>
<!-- writes the attribute to the customAssemblyInfo file --> <!-- writes the attribute to the customAssemblyInfo file -->
<WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" /> <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target> </Target>
</Project> </Project>
@@ -62,6 +62,7 @@
<Character file="Content/Characters/Balloon/Balloon.xml" /> <Character file="Content/Characters/Balloon/Balloon.xml" />
<Character file="Content/Characters/Carrier/Carrier.xml" /> <Character file="Content/Characters/Carrier/Carrier.xml" />
<Character file="Content/Characters/Charybdis/Charybdis.xml" /> <Character file="Content/Characters/Charybdis/Charybdis.xml" />
<Character file="Content/Characters/Charybdisold/Charybdisold.xml" />
<Character file="Content/Characters/Coelanth/Coelanth.xml" /> <Character file="Content/Characters/Coelanth/Coelanth.xml" />
<Character file="Content/Characters/Crawler/Crawler.xml" /> <Character file="Content/Characters/Crawler/Crawler.xml" />
<Character file="Content/Characters/Crawlerhusk/Crawlerhusk.xml" /> <Character file="Content/Characters/Crawlerhusk/Crawlerhusk.xml" />
@@ -248,4 +249,6 @@
<OutpostModule file="Content/Map/Outposts/Hall4wayModule_01_Abandoned.sub" /> <OutpostModule file="Content/Map/Outposts/Hall4wayModule_01_Abandoned.sub" />
<BeaconStation file="Content/Map/BeaconStations/BeaconStation1.sub" /> <BeaconStation file="Content/Map/BeaconStations/BeaconStation1.sub" />
<OutpostModule file="Content/Map/Outposts/MineModule_04.sub" /> <OutpostModule file="Content/Map/Outposts/MineModule_04.sub" />
<OutpostModule file="Content/Map/Outposts/HallModuleHorizontal_Abandoned.sub" />
<OutpostModule file="Content/Map/Outposts/HallModuleVertical_Abandoned.sub" />
</contentpackage> </contentpackage>
@@ -232,8 +232,7 @@ namespace Barotrauma
public bool IsWithinSector(Vector2 worldPosition) public bool IsWithinSector(Vector2 worldPosition)
{ {
if (sectorRad >= MathHelper.TwoPi) return true; if (sectorRad >= MathHelper.TwoPi) { return true; }
Vector2 diff = worldPosition - WorldPosition; Vector2 diff = worldPosition - WorldPosition;
return MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir)) <= sectorRad * 0.5f; return MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir)) <= sectorRad * 0.5f;
} }
@@ -298,7 +298,7 @@ namespace Barotrauma
private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget)); private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget));
private string GetTargetingTag(AITarget aiTarget) private string GetTargetingTag(AITarget aiTarget)
{ {
if (aiTarget.Entity == null) { return null; } if (aiTarget?.Entity == null) { return null; }
string targetingTag = null; string targetingTag = null;
if (aiTarget.Entity is Character targetCharacter) if (aiTarget.Entity is Character targetCharacter)
{ {
@@ -377,6 +377,7 @@ namespace Barotrauma
{ {
if (DisableEnemyAI) { return; } if (DisableEnemyAI) { return; }
base.Update(deltaTime); base.Update(deltaTime);
UpdateTriggers(deltaTime);
bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));
if (steeringManager == insideSteering) if (steeringManager == insideSteering)
@@ -462,7 +463,7 @@ namespace Barotrauma
{ {
updateTargetsTimer -= deltaTime; updateTargetsTimer -= deltaTime;
} }
else if (avoidTimer <= 0) else if (avoidTimer <= 0 || activeTriggers.Any() && returnTimer <= 0)
{ {
CharacterParams.TargetParams targetingParams = null; CharacterParams.TargetParams targetingParams = null;
UpdateTargets(Character, out targetingParams); UpdateTargets(Character, out targetingParams);
@@ -1448,8 +1449,12 @@ namespace Barotrauma
break; break;
case AttackPattern.Circle: case AttackPattern.Circle:
if (IsCoolDownRunning) { break; } if (IsCoolDownRunning) { break; }
if (IsAttackRunning) { break; } if (IsAttackRunning && CirclePhase != CirclePhase.Strike) { break; }
if (selectedTargetingParams == null) { break; } if (selectedTargetingParams == null) { break; }
var targetSub = SelectedAiTarget.Entity?.Submarine;
if (targetSub == null) { break; }
float subSize = Math.Max(targetSub.Borders.Width, targetSub.Borders.Height) / 2;
float sqrDistToSub = Vector2.DistanceSquared(WorldPosition, targetSub.WorldPosition);
switch (CirclePhase) switch (CirclePhase)
{ {
case CirclePhase.Start: case CirclePhase.Start:
@@ -1471,22 +1476,31 @@ namespace Barotrauma
circleOffset = Rand.Vector(MathHelper.Lerp(selectedTargetingParams.CircleMaxRandomOffset, 0, currentAttackIntensity * Rand.Range(0.9f, 1.1f))); circleOffset = Rand.Vector(MathHelper.Lerp(selectedTargetingParams.CircleMaxRandomOffset, 0, currentAttackIntensity * Rand.Range(0.9f, 1.1f)));
canAttack = false; canAttack = false;
aggressionIntensity = Math.Clamp(aggressionIntensity, AIParams.StartAggression, AIParams.MaxAggression); aggressionIntensity = Math.Clamp(aggressionIntensity, AIParams.StartAggression, AIParams.MaxAggression);
CirclePhase = Vector2.DistanceSquared(WorldPosition, attackWorldPos) > MathUtils.Pow2(circleFallbackDistance) ? CirclePhase.CloseIn : CirclePhase.FallBack; if (targetSub.Borders.Width < 1000)
{
breakCircling = true;
CirclePhase = CirclePhase.CloseIn;
}
else if (sqrDistToSub > MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance))
{
CirclePhase = CirclePhase.CloseIn;
}
else if (sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance))
{
CirclePhase = CirclePhase.FallBack;
}
else
{
CirclePhase = CirclePhase.Advance;
}
break; break;
case CirclePhase.CloseIn: case CirclePhase.CloseIn:
var sub = SelectedAiTarget.Entity?.Submarine; if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * GetStrikeDistanceMultiplier(targetSub.Velocity))
if (sub == null)
{
CirclePhase = CirclePhase.Start;
break;
}
if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * GetStrikeDistanceMultiplier(sub.Velocity))
{ {
strikeTimer = AttackingLimb.attack.CoolDown; strikeTimer = AttackingLimb.attack.CoolDown;
CirclePhase = CirclePhase.Strike; CirclePhase = CirclePhase.Strike;
} }
else if (!breakCircling && Vector2.DistanceSquared(WorldPosition, attackWorldPos) <= MathUtils.Pow2(circleFallbackDistance - 1000) && else if (!breakCircling && sqrDistToSub <= MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance / 2) && targetSub.Velocity.LengthSquared() <= MathUtils.Pow2(GetTargetMaxSpeed()))
sub.Velocity.LengthSquared() <= MathUtils.Pow2(GetTargetMaxSpeed()))
{ {
CirclePhase = CirclePhase.Advance; CirclePhase = CirclePhase.Advance;
} }
@@ -1494,23 +1508,17 @@ namespace Barotrauma
break; break;
case CirclePhase.FallBack: case CirclePhase.FallBack:
bool isBlocked = !UpdateFallBack(attackWorldPos, deltaTime, followThrough: false, checkBlocking: true); bool isBlocked = !UpdateFallBack(attackWorldPos, deltaTime, followThrough: false, checkBlocking: true);
if (isBlocked || Vector2.DistanceSquared(WorldPosition, attackWorldPos) > MathUtils.Pow2(circleFallbackDistance)) if (isBlocked || sqrDistToSub > MathUtils.Pow2(subSize + circleFallbackDistance))
{ {
CirclePhase = CirclePhase.Advance; CirclePhase = CirclePhase.Advance;
break; break;
} }
return; return;
case CirclePhase.Advance: case CirclePhase.Advance:
var targetSub = SelectedAiTarget.Entity?.Submarine;
if (targetSub == null)
{
CirclePhase = CirclePhase.Start;
break;
}
Vector2 subSpeed = targetSub.Velocity; Vector2 subSpeed = targetSub.Velocity;
float requiredDistMultiplier = 1; float requiredDistMultiplier = 1;
// If the target sub is moving fast, just steer towards the target until close enough to strike // If the target sub is moving fast, just steer towards the target until close enough to strike
if (breakCircling || subSpeed.LengthSquared() > MathUtils.Pow2(GetTargetMaxSpeed()) || distance > selectedTargetingParams.CircleStartDistance + 1000) if (breakCircling || subSpeed.LengthSquared() > MathUtils.Pow2(GetTargetMaxSpeed()) || sqrDistToSub > MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance * 1.2f))
{ {
CirclePhase = CirclePhase.CloseIn; CirclePhase = CirclePhase.CloseIn;
} }
@@ -1532,7 +1540,7 @@ namespace Barotrauma
// When the offset position is outside of the sub it happens that the creature sometimes reaches the target point, // When the offset position is outside of the sub it happens that the creature sometimes reaches the target point,
// which makes it continue circling around the point (as supposed) // which makes it continue circling around the point (as supposed)
// But when there is some offset and the offset is too near, this is not what we want. // But when there is some offset and the offset is too near, this is not what we want.
if (targetSub.Borders.ContainsWorld(attackWorldPos + ConvertUnits.ToDisplayUnits(circleOffset))) if (AttackingLimb != null && sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance))
{ {
CirclePhase = CirclePhase.Strike; CirclePhase = CirclePhase.Strike;
strikeTimer = AttackingLimb.attack.CoolDown; strikeTimer = AttackingLimb.attack.CoolDown;
@@ -1646,15 +1654,15 @@ namespace Barotrauma
return; return;
} }
} }
if (UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb)) if (!UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb))
{
CirclePhase = CirclePhase.Start;
}
else
{ {
IgnoreTarget(SelectedAiTarget); IgnoreTarget(SelectedAiTarget);
} }
} }
else if (IsAttackRunning)
{
AttackingLimb.attack.ResetAttackTimer();
}
} }
private readonly List<Limb> attackLimbs = new List<Limb>(); private readonly List<Limb> attackLimbs = new List<Limb>();
@@ -2060,6 +2068,7 @@ namespace Barotrauma
targetValue = 0; targetValue = 0;
selectedTargetMemory = null; selectedTargetMemory = null;
targetingParams = null; targetingParams = null;
bool isAnyTargetClose = false;
foreach (AITarget aiTarget in AITarget.List) foreach (AITarget aiTarget in AITarget.List)
{ {
@@ -2163,6 +2172,14 @@ namespace Barotrauma
continue; continue;
} }
} }
if (door == null)
{
// Ignore items inside ruins, unless we are in the same hull. We can't target the ruin walls.
if (item.Submarine == null && item.CurrentHull != Character.CurrentHull)
{
continue;
}
}
foreach (var prio in AIParams.Targets) foreach (var prio in AIParams.Targets)
{ {
if (item.HasTag(prio.Tag)) if (item.HasTag(prio.Tag))
@@ -2379,18 +2396,16 @@ namespace Barotrauma
} }
} }
} }
if (!aiTarget.IsWithinSector(WorldPosition)) { continue; }
Vector2 toTarget = aiTarget.WorldPosition - character.WorldPosition; Vector2 toTarget = aiTarget.WorldPosition - character.WorldPosition;
float dist = toTarget.Length(); float dist = toTarget.Length();
float nonModifiedDist = dist;
//if the target has been within range earlier, the character will notice it more easily //if the target has been within range earlier, the character will notice it more easily
if (targetMemories.ContainsKey(aiTarget)) if (targetMemories.ContainsKey(aiTarget))
{ {
dist *= 0.9f; dist *= 0.9f;
} }
if (!CanPerceive(aiTarget, dist)) { continue; } if (!CanPerceive(aiTarget, dist)) { continue; }
if (!aiTarget.IsWithinSector(WorldPosition)) { continue; }
//if the target is very close, the distance doesn't make much difference //if the target is very close, the distance doesn't make much difference
// -> just ignore the distance and attack whatever has the highest priority // -> just ignore the distance and attack whatever has the highest priority
@@ -2405,14 +2420,26 @@ namespace Barotrauma
if (targetParams.AttackPattern == AttackPattern.Circle) if (targetParams.AttackPattern == AttackPattern.Circle)
{ {
if (Character.Submarine == null && aiTarget.Entity?.Submarine != null) if (Character.Submarine == null && aiTarget.Entity?.Submarine != null && !isAnyTargetClose)
{ {
if (Submarine.MainSubs.Contains(aiTarget.Entity.Submarine)) if (Submarine.MainSubs.Contains(aiTarget.Entity.Submarine))
{ {
// Prioritize targets that are near the horizontal center of the sub // Prioritize targets that are near the horizontal center of the sub, but only when none of the targets is reachable.
float horizontalDistanceToSubCenter = Math.Abs(aiTarget.WorldPosition.X - aiTarget.Entity.Submarine.WorldPosition.X); float horizontalDistanceToSubCenter = Math.Abs(aiTarget.WorldPosition.X - aiTarget.Entity.Submarine.WorldPosition.X);
dist *= MathHelper.Lerp(1f, 5f, MathUtils.InverseLerp(0, 10000, horizontalDistanceToSubCenter)); dist *= MathHelper.Lerp(1f, 5f, MathUtils.InverseLerp(0, 10000, horizontalDistanceToSubCenter));
} }
else
{
dist *= 5;
}
}
}
// Don't target characters that are outside of the allowed zone, unless attacking or escaping
if (targetParams.State != AIState.Attack && targetParams.State != AIState.Escape && targetParams.State != AIState.Avoid)
{
if (!IsPositionInsideAllowedZone(aiTarget.WorldPosition, out _))
{
continue;
} }
} }
@@ -2486,11 +2513,26 @@ namespace Barotrauma
} }
} }
} }
else if (targetCharacter.Submarine == null && Character.Submarine == null)
{
// Ignore the target when it's far enough and blocked by the level geometry, because the steering avoidance probably can't get us to the target.
if (dist > Math.Clamp(ConvertUnits.ToDisplayUnits(colliderLength) * 10, 1000, 5000))
{
if (Submarine.PickBodies(SimPosition, targetCharacter.SimPosition, collisionCategory: Physics.CollisionLevel).Any())
{
continue;
}
}
}
} }
newTarget = aiTarget; newTarget = aiTarget;
selectedTargetMemory = targetMemory; selectedTargetMemory = targetMemory;
targetValue = valueModifier; targetValue = valueModifier;
targetingParams = targetParams; targetingParams = targetParams;
if (!isAnyTargetClose)
{
isAnyTargetClose = ConvertUnits.ToDisplayUnits(colliderLength) > nonModifiedDist;
}
} }
} }
@@ -2619,7 +2661,7 @@ namespace Barotrauma
} }
} }
} }
if (!Character.AnimController.CanEnterSubmarine && wallTarget == null) if (!Character.AnimController.CanEnterSubmarine && wallTarget == null && selectedTargetingParams?.AttackPattern == AttackPattern.Straight)
{ {
if (closestBody.UserData is Structure w && w.Submarine != null && w.Submarine == SelectedAiTarget.Entity?.Submarine || if (closestBody.UserData is Structure w && w.Submarine != null && w.Submarine == SelectedAiTarget.Entity?.Submarine ||
closestBody.UserData is Item i && i.Submarine != null && i.Submarine == SelectedAiTarget.Entity?.Submarine) closestBody.UserData is Item i && i.Submarine != null && i.Submarine == SelectedAiTarget.Entity?.Submarine)
@@ -2753,6 +2795,44 @@ namespace Barotrauma
private readonly float stateResetCooldown = 10; private readonly float stateResetCooldown = 10;
private float stateResetTimer; private float stateResetTimer;
private bool isStateChanged; private bool isStateChanged;
private readonly Dictionary<AITrigger, CharacterParams.TargetParams> activeTriggers = new Dictionary<AITrigger, CharacterParams.TargetParams>();
private readonly HashSet<AITrigger> inactiveTriggers = new HashSet<AITrigger>();
public void LaunchTrigger(AITrigger trigger)
{
if (trigger.IsTriggered) { return; }
if (activeTriggers.ContainsKey(trigger)) { return; }
if (activeTriggers.ContainsValue(selectedTargetingParams))
{
if (!trigger.AllowToOverride) { return; }
var existingTrigger = activeTriggers.FirstOrDefault(kvp => kvp.Value == selectedTargetingParams && kvp.Key.AllowToBeOverridden);
if (existingTrigger.Key == null) { return; }
activeTriggers.Remove(existingTrigger.Key);
}
trigger.Launch();
activeTriggers.Add(trigger, selectedTargetingParams);
ChangeParams(selectedTargetingParams, trigger.State);
}
private void UpdateTriggers(float deltaTime)
{
foreach (var triggerObject in activeTriggers)
{
AITrigger trigger = triggerObject.Key;
trigger.UpdateTimer(deltaTime);
if (!trigger.IsActive)
{
trigger.Reset();
ResetParams(triggerObject.Value);
inactiveTriggers.Add(trigger);
}
}
foreach (AITrigger trigger in inactiveTriggers)
{
activeTriggers.Remove(trigger);
}
inactiveTriggers.Clear();
}
/// <summary> /// <summary>
/// Resets the target's state to the original value defined in the xml. /// Resets the target's state to the original value defined in the xml.
@@ -2768,11 +2848,7 @@ namespace Barotrauma
tempParams.Values.ForEach(t => AIParams.RemoveTarget(t)); tempParams.Values.ForEach(t => AIParams.RemoveTarget(t));
tempParams.Remove(tag); tempParams.Remove(tag);
} }
targetParams.Reset(); ResetParams(targetParams);
ResetAITarget();
// Enforce the idle state so that we don't keep following the target if there's one
State = AIState.Idle;
PreviousState = AIState.Idle;
return true; return true;
} }
else else
@@ -2784,6 +2860,27 @@ namespace Barotrauma
private readonly Dictionary<string, CharacterParams.TargetParams> modifiedParams = new Dictionary<string, CharacterParams.TargetParams>(); private readonly Dictionary<string, CharacterParams.TargetParams> modifiedParams = new Dictionary<string, CharacterParams.TargetParams>();
private readonly Dictionary<string, CharacterParams.TargetParams> tempParams = new Dictionary<string, CharacterParams.TargetParams>(); private readonly Dictionary<string, CharacterParams.TargetParams> tempParams = new Dictionary<string, CharacterParams.TargetParams>();
private void ChangeParams(CharacterParams.TargetParams targetParams, AIState state, float? priority = null)
{
if (targetParams == null) { return; }
if (priority.HasValue)
{
targetParams.Priority = priority.Value;
}
targetParams.State = state;
}
private void ResetParams(CharacterParams.TargetParams targetParams)
{
targetParams?.Reset();
if (selectedTargetingParams == targetParams || State == AIState.Idle)
{
ResetAITarget();
State = AIState.Idle;
PreviousState = AIState.Idle;
}
}
private void ChangeParams(string tag, AIState state, float? priority = null, bool onlyExisting = false) private void ChangeParams(string tag, AIState state, float? priority = null, bool onlyExisting = false)
{ {
if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams)) if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams))
@@ -2938,45 +3035,56 @@ namespace Barotrauma
} }
} }
private bool IsPositionInsideAllowedZone(Vector2 pos, out Vector2 targetDir)
{
targetDir = Vector2.Zero;
if (AIParams.AvoidAbyss)
{
if (pos.Y < Level.Loaded.AbyssStart)
{
// Too far down
targetDir = Vector2.UnitY;
}
}
if (AIParams.StayInAbyss)
{
if (pos.Y > Level.Loaded.AbyssStart)
{
// Too far up
targetDir = -Vector2.UnitY;
}
else if (pos.Y < Level.Loaded.AbyssEnd)
{
// Too far down
targetDir = Vector2.UnitY;
}
}
float margin = 30000;
if (pos.X < -margin)
{
// Too far left
targetDir = Vector2.UnitX;
}
else if (pos.X > Level.Loaded.Size.X + margin)
{
// Too far right
targetDir = -Vector2.UnitX;
}
return targetDir == Vector2.Zero;
}
private Vector2 returnDir; private Vector2 returnDir;
private float returnTimer; private float returnTimer;
private void SteerInsideLevel(float deltaTime) private void SteerInsideLevel(float deltaTime)
{ {
if (State == AIState.Attack) { return; }
if (SteeringManager is IndoorsSteeringManager) { return; } if (SteeringManager is IndoorsSteeringManager) { return; }
if (Level.Loaded == null) { return; } if (Level.Loaded == null) { return; }
Point levelSize = Level.Loaded.Size; if (State == AIState.Attack && returnTimer <= 0) { return; }
float returnTime = 10; float returnTime = 5;
if (AIParams.AvoidAbyss) if (!IsPositionInsideAllowedZone(WorldPosition, out Vector2 targetDir))
{ {
if (WorldPosition.Y < Level.Loaded.AbyssStart) returnDir = targetDir;
{
// Too far down
returnTimer = returnTime * Rand.Range(0.75f, 1.25f);
returnDir = Vector2.UnitY;
}
}
else if (AIParams.StayInAbyss)
{
if (WorldPosition.Y > Level.Loaded.AbyssStart)
{
// Too far up
returnTimer = returnTime * Rand.Range(0.75f, 1.25f);
returnDir = -Vector2.UnitY;
}
}
float margin = AIParams.AvoidAbyss ? 0 : 30000;
if (WorldPosition.X < margin)
{
// Too far left
returnTimer = returnTime * Rand.Range(0.75f, 1.25f); returnTimer = returnTime * Rand.Range(0.75f, 1.25f);
returnDir = Vector2.UnitX;
}
if (WorldPosition.X > levelSize.X + margin)
{
// Too far right
returnTimer = returnTime * Rand.Range(0.75f, 1.25f);
returnDir = -Vector2.UnitX;
} }
if (returnTimer > 0) if (returnTimer > 0)
{ {
@@ -15,7 +15,7 @@ namespace Barotrauma
private readonly AIObjectiveManager objectiveManager; private readonly AIObjectiveManager objectiveManager;
private float sortTimer; public float SortTimer { get; set; }
private float crouchRaycastTimer; private float crouchRaycastTimer;
private float reactTimer; private float reactTimer;
private float unreachableClearTimer; private float unreachableClearTimer;
@@ -131,7 +131,7 @@ namespace Barotrauma
outsideSteering = new SteeringManager(this); outsideSteering = new SteeringManager(this);
objectiveManager = new AIObjectiveManager(c); objectiveManager = new AIObjectiveManager(c);
reactTimer = GetReactionTime(); reactTimer = GetReactionTime();
sortTimer = Rand.Range(0f, sortObjectiveInterval); SortTimer = Rand.Range(0f, sortObjectiveInterval);
} }
public override void Update(float deltaTime) public override void Update(float deltaTime)
@@ -218,6 +218,7 @@ namespace Barotrauma
foreach (Character c in Character.CharacterList) foreach (Character c in Character.CharacterList)
{ {
if (c.Submarine != Character.Submarine) { continue; } if (c.Submarine != Character.Submarine) { continue; }
if (c.Removed || c.IsDead || c.IsIncapacitated) { continue; }
if (IsFriendly(c)) { continue; } if (IsFriendly(c)) { continue; }
Vector2 toTarget = c.WorldPosition - WorldPosition; Vector2 toTarget = c.WorldPosition - WorldPosition;
float dist = toTarget.LengthSquared(); float dist = toTarget.LengthSquared();
@@ -264,14 +265,14 @@ namespace Barotrauma
CheckCrouching(deltaTime); CheckCrouching(deltaTime);
Character.ClearInputs(); Character.ClearInputs();
if (sortTimer > 0.0f) if (SortTimer > 0.0f)
{ {
sortTimer -= deltaTime; SortTimer -= deltaTime;
} }
else else
{ {
objectiveManager.SortObjectives(); objectiveManager.SortObjectives();
sortTimer = sortObjectiveInterval; SortTimer = sortObjectiveInterval;
} }
objectiveManager.UpdateObjectives(deltaTime); objectiveManager.UpdateObjectives(deltaTime);
@@ -288,14 +289,14 @@ namespace Barotrauma
{ {
if (Character.CurrentHull != null) if (Character.CurrentHull != null)
{ {
if (Character.TeamID == CharacterTeamType.FriendlyNPC) if (Character.IsOnPlayerTeam)
{ {
// Outpost npcs don't inform each other about threats, like crew members do. VisibleHulls.ForEach(h => PropagateHullSafety(Character, h));
VisibleHulls.ForEach(h => RefreshHullSafety(h));
} }
else else
{ {
VisibleHulls.ForEach(h => PropagateHullSafety(Character, h)); // Outpost npcs don't inform each other about threats, like crew members do.
VisibleHulls.ForEach(h => RefreshHullSafety(h));
} }
} }
if (Character.SpeechImpediment < 100.0f) if (Character.SpeechImpediment < 100.0f)
@@ -1065,7 +1066,9 @@ namespace Barotrauma
{ {
if (!IsFriendly(attacker)) if (!IsFriendly(attacker))
{ {
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive; return c.AIController is HumanAIController humanAI &&
(humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders))
? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive;
} }
else else
{ {
@@ -1192,7 +1195,7 @@ namespace Barotrauma
{ {
base.Reset(); base.Reset();
objectiveManager.SortObjectives(); objectiveManager.SortObjectives();
sortTimer = sortObjectiveInterval; SortTimer = sortObjectiveInterval;
float waitDuration = characterWaitOnSwitch; float waitDuration = characterWaitOnSwitch;
if (ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>()) if (ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>())
{ {
@@ -1418,6 +1421,9 @@ namespace Barotrauma
item.StolenDuringRound = true; item.StolenDuringRound = true;
otherCharacter.Speak(TextManager.Get("dialogstealwarning"), null, Rand.Range(0.5f, 1.0f), "thief", 10.0f); otherCharacter.Speak(TextManager.Get("dialogstealwarning"), null, Rand.Range(0.5f, 1.0f), "thief", 10.0f);
someoneSpoke = true; someoneSpoke = true;
#if CLIENT
HintManager.OnStoleItem(thief, item);
#endif
} }
// React if we are security // React if we are security
if (!TriggerSecurity(otherHumanAI)) if (!TriggerSecurity(otherHumanAI))
@@ -1554,7 +1560,7 @@ namespace Barotrauma
targetAdded = true; targetAdded = true;
} }
} }
}, (caller.AIController as HumanAIController)?.ReportRange ?? float.PositiveInfinity); }, range: (caller.AIController as HumanAIController)?.ReportRange ?? float.PositiveInfinity);
return targetAdded; return targetAdded;
} }
@@ -1726,11 +1732,9 @@ namespace Barotrauma
switch (myTeam) switch (myTeam)
{ {
case CharacterTeamType.None: case CharacterTeamType.None:
// Only enemies are in the Team "None"
return false;
case CharacterTeamType.Team1: case CharacterTeamType.Team1:
case CharacterTeamType.Team2: case CharacterTeamType.Team2:
// Team1 is only friendly to Team1 and friendly NPCs // Only friendly to the same team and friendly NPCs
return otherTeam == CharacterTeamType.FriendlyNPC; return otherTeam == CharacterTeamType.FriendlyNPC;
case CharacterTeamType.FriendlyNPC: case CharacterTeamType.FriendlyNPC:
// Friendly NPCs are friendly to both teams // Friendly NPCs are friendly to both teams
@@ -221,6 +221,19 @@ namespace Barotrauma
{ {
currentFlags.Add("CampaignNPC." + speaker.CampaignInteractionType); currentFlags.Add("CampaignNPC." + speaker.CampaignInteractionType);
} }
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode &&
(campaignMode.Map?.CurrentLocation?.Type?.Identifier.Equals("abandoned", StringComparison.OrdinalIgnoreCase) ?? false))
{
if (speaker.TeamID == CharacterTeamType.None)
{
currentFlags.Add("Bandit");
}
else if (speaker.TeamID == CharacterTeamType.FriendlyNPC)
{
currentFlags.Add("Hostage");
}
}
} }
return currentFlags; return currentFlags;
@@ -79,6 +79,7 @@ namespace Barotrauma
private float coolDownTimer; private float coolDownTimer;
private IEnumerable<Body> myBodies; private IEnumerable<Body> myBodies;
private float aimTimer; private float aimTimer;
private float reloadTimer;
private float spreadTimer; private float spreadTimer;
private bool canSeeTarget; private bool canSeeTarget;
@@ -147,6 +148,7 @@ namespace Barotrauma
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
} }
spreadTimer = Rand.Range(-10, 10); spreadTimer = Rand.Range(-10, 10);
HumanAIController.SortTimer = 0;
} }
public override float GetPriority() public override float GetPriority()
@@ -170,6 +172,10 @@ namespace Barotrauma
base.Update(deltaTime); base.Update(deltaTime);
ignoreWeaponTimer -= deltaTime; ignoreWeaponTimer -= deltaTime;
checkWeaponsTimer -= deltaTime; checkWeaponsTimer -= deltaTime;
if (reloadTimer > 0)
{
reloadTimer -= deltaTime;
}
if (ignoreWeaponTimer < 0) if (ignoreWeaponTimer < 0)
{ {
ignoredWeapons.Clear(); ignoredWeapons.Clear();
@@ -219,7 +225,11 @@ namespace Barotrauma
{ {
OperateWeapon(deltaTime); OperateWeapon(deltaTime);
} }
if (!HoldPosition && seekAmmunitionObjective == null && seekWeaponObjective == null) if (HoldPosition)
{
SteeringManager.Reset();
}
else if (seekAmmunitionObjective == null && seekWeaponObjective == null)
{ {
Move(deltaTime); Move(deltaTime);
} }
@@ -641,7 +651,7 @@ namespace Barotrauma
var slots = Weapon.AllowedSlots.Where(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand)); var slots = Weapon.AllowedSlots.Where(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand));
if (character.Inventory.TryPutItem(Weapon, character, slots)) if (character.Inventory.TryPutItem(Weapon, character, slots))
{ {
aimTimer = Rand.Range(1f, 1.5f) / AimSpeed; aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
} }
else else
{ {
@@ -912,7 +922,7 @@ namespace Barotrauma
} }
if (!canSeeTarget) if (!canSeeTarget)
{ {
aimTimer = Rand.Range(0.2f, 1f) / AimSpeed; aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
return; return;
} }
if (Weapon.RequireAimToUse) if (Weapon.RequireAimToUse)
@@ -930,6 +940,7 @@ namespace Barotrauma
aimTimer -= deltaTime; aimTimer -= deltaTime;
return; return;
} }
if (reloadTimer > 0) { return; }
if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 1) { return; } if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 1) { return; }
if (holdFireCondition != null && holdFireCondition()) { return; } if (holdFireCondition != null && holdFireCondition()) { return; }
float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position); float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);
@@ -1010,18 +1021,25 @@ namespace Barotrauma
private void UseWeapon(float deltaTime) private void UseWeapon(float deltaTime)
{ {
character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character);
float reloadTime = 0; float reloadTime = 0;
if (WeaponComponent is RangedWeapon rangedWeapon) if (WeaponComponent is RangedWeapon rangedWeapon)
{ {
reloadTime = rangedWeapon.Reload; // If the weapon is just equipped, we can't shoot just yet.
if (rangedWeapon.ReloadTimer <= 0)
{
reloadTime = rangedWeapon.Reload;
}
} }
if (WeaponComponent is MeleeWeapon mw) if (WeaponComponent is MeleeWeapon mw)
{ {
reloadTime = mw.Reload; if (!((HumanoidAnimController)character.AnimController).Crouching)
{
reloadTime = mw.Reload;
}
} }
aimTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.5f) / AimSpeed); character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character);
reloadTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.25f) / AimSpeed);
} }
protected override void OnCompleted() protected override void OnCompleted()
@@ -1031,10 +1049,7 @@ namespace Barotrauma
{ {
Unequip(); Unequip();
} }
if (!HoldPosition) SteeringManager.Reset();
{
SteeringManager.Reset();
}
} }
protected override void OnAbandon() protected override void OnAbandon()
@@ -1044,10 +1059,7 @@ namespace Barotrauma
{ {
Unequip(); Unequip();
} }
if (!HoldPosition) SteeringManager.Reset();
{
SteeringManager.Reset();
}
} }
public override void Reset() public override void Reset()
@@ -10,6 +10,8 @@ namespace Barotrauma
protected override float IgnoreListClearInterval => 30; protected override float IgnoreListClearInterval => 30;
public override bool IgnoreUnsafeHulls => true; public override bool IgnoreUnsafeHulls => true;
protected override float TargetUpdateTimeMultiplier => 0.2f;
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) { } : base(character, objectiveManager, priorityModifier) { }
@@ -48,7 +50,8 @@ namespace Barotrauma
public static bool IsValidTarget(Character target, Character character) public static bool IsValidTarget(Character target, Character character)
{ {
if (target == null || target.IsDead || target.Removed) { return false; } if (target == null || target.Removed) { return false; }
if (target.IsDead || target.IsUnconscious) { return false; }
if (target == character) { return false; } if (target == character) { return false; }
if (target.Submarine == null) { return false; } if (target.Submarine == null) { return false; }
if (character.Submarine == null) { return false; } if (character.Submarine == null) { return false; }
@@ -555,6 +555,13 @@ namespace Barotrauma
//otherwise characters can let go of the ladders too soon once they're close enough to the target //otherwise characters can let go of the ladders too soon once they're close enough to the target
if (PathSteering.CurrentPath.NextNode != null) { return false; } if (PathSteering.CurrentPath.NextNode != null) { return false; }
} }
if (!character.AnimController.InWater)
{
float yDiff = Math.Abs(Target.WorldPosition.Y - character.WorldPosition.Y);
if (yDiff > CloseEnough) { return false; }
float xDiff = Math.Abs(Target.WorldPosition.X - character.WorldPosition.X);
return xDiff <= CloseEnough;
}
return Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough; return Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough;
} }
} }
@@ -11,6 +11,7 @@ namespace Barotrauma
protected HashSet<T> ignoreList = new HashSet<T>(); protected HashSet<T> ignoreList = new HashSet<T>();
private float ignoreListTimer; private float ignoreListTimer;
protected float targetUpdateTimer; protected float targetUpdateTimer;
protected virtual float TargetUpdateTimeMultiplier { get; } = 1;
private float syncTimer; private float syncTimer;
private readonly float syncTime = 1; private readonly float syncTime = 1;
@@ -61,7 +62,7 @@ namespace Barotrauma
ignoreListTimer += deltaTime; ignoreListTimer += deltaTime;
} }
} }
if (targetUpdateTimer < 0) if (targetUpdateTimer <= 0)
{ {
UpdateTargets(); UpdateTargets();
} }
@@ -69,9 +70,9 @@ namespace Barotrauma
{ {
targetUpdateTimer -= deltaTime; targetUpdateTimer -= deltaTime;
} }
if (syncTimer < 0) if (syncTimer <= 0)
{ {
syncTimer = syncTime * Rand.Range(0.9f, 1.1f); syncTimer = Math.Min(syncTime * Rand.Range(0.9f, 1.1f), targetUpdateTimer);
// Sync objectives, subobjectives and targets // Sync objectives, subobjectives and targets
foreach (var objective in Objectives) foreach (var objective in Objectives)
{ {
@@ -95,7 +96,7 @@ namespace Barotrauma
} }
// the timer is set between 1 and 10 seconds, depending on the priority modifier and a random +-25% // the timer is set between 1 and 10 seconds, depending on the priority modifier and a random +-25%
private float SetTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1); private float CalculateTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1) * TargetUpdateTimeMultiplier;
public override void Reset() public override void Reset()
{ {
@@ -156,7 +157,7 @@ namespace Barotrauma
protected void UpdateTargets() protected void UpdateTargets()
{ {
SetTargetUpdateTimer(); CalculateTargetUpdateTimer();
Targets.Clear(); Targets.Clear();
FindTargets(); FindTargets();
CreateObjectives(); CreateObjectives();
@@ -386,7 +386,9 @@ namespace Barotrauma
Abandon = true; Abandon = true;
return false; return false;
} }
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter); bool isCompleted =
AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) ||
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength < a.Prefab.TreatmentThreshold);
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
{ {
character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name),
@@ -25,8 +25,8 @@ namespace Barotrauma
{ {
// When targeting player characters, always treat them when ordered, else use the threshold so that minor/non-severe damage is ignored. // When targeting player characters, always treat them when ordered, else use the threshold so that minor/non-severe damage is ignored.
// If we ignore any damage when the player orders a bot to do healings, it's observed to cause confusion among the players. // If we ignore any damage when the player orders a bot to do healings, it's observed to cause confusion among the players.
// On the other hand, if the bots too eagerly heal characters when it's not nevessary, it's inefficient and can feel frustrating, because it can't be controlled. // On the other hand, if the bots too eagerly heal characters when it's not necessary, it's inefficient and can feel frustrating, because it can't be controlled.
return character == target || manager.CurrentOrder is AIObjectiveRescueAll ? (target.IsPlayer ? 100 : vitalityThresholdForOrders) : vitalityThreshold; return character == target || manager.HasOrder<AIObjectiveRescueAll>() ? (target.IsPlayer ? 100 : vitalityThresholdForOrders) : vitalityThreshold;
} }
} }
@@ -83,7 +83,7 @@ namespace Barotrauma
if (character.AIController is HumanAIController humanAI) if (character.AIController is HumanAIController humanAI)
{ {
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return false; } if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return false; }
if (!humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveRescueAll>()) if (!humanAI.ObjectiveManager.HasOrder<AIObjectiveRescueAll>())
{ {
if (!character.IsMedic && target != character) if (!character.IsMedic && target != character)
{ {
@@ -281,7 +281,7 @@ namespace Barotrauma
} }
} }
public const float MAX_SPEED = 30; public const float MAX_SPEED = 20;
public Vector2 TargetMovement public Vector2 TargetMovement
{ {
@@ -636,9 +636,12 @@ namespace Barotrauma
//always collides with bodies other than structures //always collides with bodies other than structures
if (!(f2.Body.UserData is Structure structure)) if (!(f2.Body.UserData is Structure structure))
{ {
lock (impactQueue) if (!f2.IsSensor)
{ {
impactQueue.Enqueue(new Impact(f1, f2, contact, velocity)); lock (impactQueue)
{
impactQueue.Enqueue(new Impact(f1, f2, contact, velocity));
}
} }
return true; return true;
} }
@@ -156,6 +156,8 @@ namespace Barotrauma
public Entity LastDamageSource; public Entity LastDamageSource;
public AttackResult LastDamage;
public float InvisibleTimer; public float InvisibleTimer;
private CharacterPrefab prefab; private CharacterPrefab prefab;
@@ -199,7 +201,12 @@ namespace Barotrauma
set => Params.Visibility = value; set => Params.Visibility = value;
} }
public bool IsTraitor; public bool IsTraitor
{
get;
set;
}
public string TraitorCurrentObjective = ""; public string TraitorCurrentObjective = "";
public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase); public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase);
public bool IsMale => Info != null && Info.HasGenders && Info.Gender == Gender.Male; public bool IsMale => Info != null && Info.HasGenders && Info.Gender == Gender.Male;
@@ -333,6 +340,7 @@ namespace Barotrauma
//text displayed when the character is highlighted if custom interact is set //text displayed when the character is highlighted if custom interact is set
public string customInteractHUDText; public string customInteractHUDText;
private Action<Character, Character> onCustomInteract; private Action<Character, Character> onCustomInteract;
public ConversationAction ActiveConversation;
public bool AllowCustomInteract public bool AllowCustomInteract
{ {
@@ -349,6 +357,9 @@ namespace Barotrauma
set set
{ {
lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f); lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f);
#if CLIENT
HintManager.OnHandcuffed(this);
#endif
} }
} }
@@ -605,6 +616,9 @@ namespace Barotrauma
get => _selectedConstruction; get => _selectedConstruction;
set set
{ {
#if CLIENT
HintManager.OnSetSelectedConstruction(this, _selectedConstruction, value);
#endif
_selectedConstruction = value; _selectedConstruction = value;
#if CLIENT #if CLIENT
if (Controlled == this) if (Controlled == this)
@@ -1661,6 +1675,12 @@ namespace Barotrauma
{ {
item.Use(deltaTime, this); item.Use(deltaTime, this);
} }
#if CLIENT
else if (item.RequireAimToUse && !IsKeyDown(InputType.Aim))
{
HintManager.OnShootWithoutAiming(this, item);
}
#endif
} }
} }
} }
@@ -1853,6 +1873,19 @@ namespace Barotrauma
return false; return false;
} }
public Item GetEquippedItem(string tagOrIdentifier)
{
if (Inventory == null) { return null; }
for (int i = 0; i < Inventory.Capacity; i++)
{
if (Inventory.SlotTypes[i] == InvSlotType.Any) { continue; }
var item = Inventory.GetItemAt(i);
if (item == null) { continue; }
if (item.Prefab.Identifier == tagOrIdentifier || item.HasTag(tagOrIdentifier)) { return item; }
}
return null;
}
public bool CanAccessInventory(Inventory inventory) public bool CanAccessInventory(Inventory inventory)
{ {
if (!CanInteract || inventory.Locked) { return false; } if (!CanInteract || inventory.Locked) { return false; }
@@ -2857,6 +2890,7 @@ namespace Barotrauma
{ {
if (character == this) { continue; } if (character == this) { continue; }
if (character.TeamID != TeamID) { continue; } if (character.TeamID != TeamID) { continue; }
if (!HumanAIController.IsActive(character)) { continue; }
foreach (var currentOrder in character.CurrentOrders) foreach (var currentOrder in character.CurrentOrders)
{ {
if (currentOrder.Order == null) { continue; } if (currentOrder.Order == null) { continue; }
@@ -3268,12 +3302,13 @@ namespace Barotrauma
//#endif //#endif
// } // }
SetStun(stun);
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire) if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
{ {
if (attacker.TeamID == TeamID) { return new AttackResult(); } if (attacker.TeamID == TeamID) { return new AttackResult(); }
} }
SetStun(stun);
Vector2 dir = hitLimb.WorldPosition - worldPosition; Vector2 dir = hitLimb.WorldPosition - worldPosition;
if (Math.Abs(attackImpulse) > 0.0f) if (Math.Abs(attackImpulse) > 0.0f)
{ {
@@ -3308,6 +3343,7 @@ namespace Barotrauma
}; };
if (attackResult.Damage > 0) if (attackResult.Damage > 0)
{ {
LastDamage = attackResult;
ApplyStatusEffects(ActionType.OnDamaged, 1.0f); ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f); hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
if (attacker != null) if (attacker != null)
@@ -153,6 +153,8 @@ namespace Barotrauma
private static ushort idCounter; private static ushort idCounter;
private const string disguiseName = "???"; private const string disguiseName = "???";
public bool HasNickname => Name != OriginalName;
public string OriginalName { get; private set; }
public string Name; public string Name;
public string DisplayName public string DisplayName
{ {
@@ -453,7 +455,7 @@ namespace Barotrauma
public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1; public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1;
// Used for creating the data // Used for creating the data
public CharacterInfo(string speciesName, string name = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced) public CharacterInfo(string speciesName, string name = "", string originalName = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced)
{ {
if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{ {
@@ -503,6 +505,7 @@ namespace Barotrauma
} }
} }
} }
OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : Name;
personalityTrait = NPCPersonalityTrait.GetRandom(name + HeadSpriteId); personalityTrait = NPCPersonalityTrait.GetRandom(name + HeadSpriteId);
Salary = CalculateSalary(); Salary = CalculateSalary();
if (ragdollFileName != null) if (ragdollFileName != null)
@@ -518,6 +521,7 @@ namespace Barotrauma
ID = idCounter; ID = idCounter;
idCounter++; idCounter++;
Name = infoElement.GetAttributeString("name", ""); Name = infoElement.GetAttributeString("name", "");
OriginalName = infoElement.GetAttributeString("originalname", null);
string genderStr = infoElement.GetAttributeString("gender", "male").ToLowerInvariant(); string genderStr = infoElement.GetAttributeString("gender", "male").ToLowerInvariant();
Salary = infoElement.GetAttributeInt("salary", 1000); Salary = infoElement.GetAttributeInt("salary", 1000);
Enum.TryParse(infoElement.GetAttributeString("race", "White"), true, out Race race); Enum.TryParse(infoElement.GetAttributeString("race", "White"), true, out Race race);
@@ -576,6 +580,11 @@ namespace Barotrauma
} }
} }
if (string.IsNullOrEmpty(OriginalName))
{
OriginalName = Name;
}
StartItemsGiven = infoElement.GetAttributeBool("startitemsgiven", false); StartItemsGiven = infoElement.GetAttributeBool("startitemsgiven", false);
string personalityName = infoElement.GetAttributeString("personality", ""); string personalityName = infoElement.GetAttributeString("personality", "");
ragdollFileName = infoElement.GetAttributeString("ragdoll", string.Empty); ragdollFileName = infoElement.GetAttributeString("ragdoll", string.Empty);
@@ -622,7 +631,17 @@ namespace Barotrauma
public int GetIdentifier() public int GetIdentifier()
{ {
int id = ToolBox.StringToInt(Name); return GetIdentifier(Name);
}
public int GetIdentifierUsingOriginalName()
{
return GetIdentifier(OriginalName);
}
private int GetIdentifier(string name)
{
int id = ToolBox.StringToInt(name);
id ^= HeadSpriteId; id ^= HeadSpriteId;
id ^= (int)Race << 6; id ^= (int)Race << 6;
id ^= HairIndex << 12; id ^= HairIndex << 12;
@@ -939,12 +958,24 @@ namespace Barotrauma
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos); partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos);
public void Rename(string newName)
{
if (string.IsNullOrEmpty(newName)) { return; }
Name = newName;
}
public void ResetName()
{
Name = OriginalName;
}
public XElement Save(XElement parentElement) public XElement Save(XElement parentElement)
{ {
XElement charElement = new XElement("Character"); XElement charElement = new XElement("Character");
charElement.Add( charElement.Add(
new XAttribute("name", Name), new XAttribute("name", Name),
new XAttribute("originalname", OriginalName),
new XAttribute("speciesname", SpeciesName), new XAttribute("speciesname", SpeciesName),
new XAttribute("gender", Head.gender == Gender.Male ? "male" : "female"), new XAttribute("gender", Head.gender == Gender.Male ? "male" : "female"),
new XAttribute("race", Head.race.ToString()), new XAttribute("race", Head.race.ToString()),
@@ -957,7 +988,7 @@ namespace Barotrauma
new XAttribute("startitemsgiven", StartItemsGiven), new XAttribute("startitemsgiven", StartItemsGiven),
new XAttribute("ragdoll", ragdollFileName), new XAttribute("ragdoll", ragdollFileName),
new XAttribute("personality", personalityTrait == null ? "" : personalityTrait.Name)); new XAttribute("personality", personalityTrait == null ? "" : personalityTrait.Name));
// TODO: animations? // TODO: animations?
if (Character != null) if (Character != null)
@@ -260,10 +260,13 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Use this method to skip clamping and additional logic of the setters. /// Use this method to skip clamping and additional logic of the setters.
/// Intended only to be used when the value is already clamped! (networking code)
/// Ideally we would keep this private, but doing so would require too much refactoring. /// Ideally we would keep this private, but doing so would require too much refactoring.
/// </summary> /// </summary>
public void SetStrength(float strength) => _strength = strength; public void SetStrength(float strength)
{
_nonClampedStrength = strength;
_strength = _nonClampedStrength;
}
public bool ShouldShowIcon(Character afflictedCharacter) public bool ShouldShowIcon(Character afflictedCharacter)
{ {
@@ -290,6 +290,9 @@ namespace Barotrauma
//how high the strength has to be for the affliction icon to be shown with a health scanner //how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f; public readonly float ShowInHealthScannerThreshold = 0.05f;
//how strong the affliction needs to be before bots attempt to treat it
public readonly float TreatmentThreshold = 5.0f;
//how much karma changes when a player applies this affliction to someone (per strength of the affliction) //how much karma changes when a player applies this affliction to someone (per strength of the affliction)
public float KarmaChangeOnApplied; public float KarmaChangeOnApplied;
@@ -376,6 +379,9 @@ namespace Barotrauma
{ {
DebugConsole.ThrowError("Cannot override all afflictions, because many of them are required by the main game! Please try overriding them one by one."); DebugConsole.ThrowError("Cannot override all afflictions, because many of them are required by the main game! Please try overriding them one by one.");
} }
List<(AfflictionPrefab prefab, XElement element)> loadedAfflictions = new List<(AfflictionPrefab prefab, XElement element)>();
foreach (XElement element in mainElement.Elements()) foreach (XElement element in mainElement.Elements())
{ {
bool isOverride = element.IsOverride(); bool isOverride = element.IsOverride();
@@ -510,10 +516,18 @@ namespace Barotrauma
if (prefab != null) if (prefab != null)
{ {
loadedAfflictions.Add((prefab, element));
Prefabs.Add(prefab, isOverride); Prefabs.Add(prefab, isOverride);
prefab.CalculatePrefabUIntIdentifier(Prefabs); prefab.CalculatePrefabUIntIdentifier(Prefabs);
} }
} }
//load the effects after all the afflictions in the file have been instantiated
//otherwise afflictions can't inflict other afflictions that are defined at a later point in the file
foreach ((AfflictionPrefab prefab, XElement element) in loadedAfflictions)
{
prefab.LoadEffects(element);
}
} }
public static void RemoveByFile(string filePath) public static void RemoveByFile(string filePath)
@@ -565,6 +579,7 @@ namespace Barotrauma
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f)); ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
TreatmentThreshold = element.GetAttributeFloat("treatmentthreshold", Math.Max(ActivationThreshold, 5.0f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
@@ -584,9 +599,6 @@ namespace Barotrauma
case "icon": case "icon":
Icon = new Sprite(subElement); Icon = new Sprite(subElement);
break; break;
case "effect":
effects.Add(new Effect(subElement, Name));
break;
case "periodiceffect": case "periodiceffect":
periodicEffects.Add(new PeriodicEffect(subElement, Name)); periodicEffects.Add(new PeriodicEffect(subElement, Name));
break; break;
@@ -614,6 +626,19 @@ namespace Barotrauma
constructor = type.GetConstructor(new[] { typeof(AfflictionPrefab), typeof(float) }); constructor = type.GetConstructor(new[] { typeof(AfflictionPrefab), typeof(float) });
} }
private void LoadEffects(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "effect":
effects.Add(new Effect(subElement, Name));
break;
}
}
}
public override string ToString() public override string ToString()
{ {
return "AfflictionPrefab (" + Name + ")"; return "AfflictionPrefab (" + Name + ")";

Some files were not shown because too many files have changed in this diff Show More