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>
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;
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 diffViewCenter;
@@ -529,6 +529,7 @@ namespace Barotrauma
{
ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString();
string originalName = inc.ReadString();
int gender = inc.ReadByte();
int race = inc.ReadByte();
int headSpriteID = inc.ReadByte();
@@ -556,7 +557,7 @@ namespace Barotrauma
}
// TODO: animations
CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile, variant)
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant)
{
ID = infoID,
};
@@ -240,6 +240,8 @@ namespace Barotrauma
Character.Controlled.SelectedConstruction = null;
}
}
HintManager.OnShowHealthInterface();
}
}
@@ -718,6 +720,7 @@ namespace Barotrauma
int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond);
return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(a1.Strength - a1.Strength);
});
HintManager.OnAfflictionDisplayed(Character, currentDisplayedAfflictions.FirstOrDefault());
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
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)
{
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)
{
Color = GetAfflictionIconColor(affliction.Prefab, affliction),
Color = GetAfflictionIconColor(affliction),
CanBeFocused = false
};
afflictionIcon.PressedColor = afflictionIcon.Color;
@@ -1911,7 +1916,7 @@ namespace Barotrauma
float alpha = MathHelper.Lerp(0.3f, 1.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;
}
@@ -1534,7 +1534,9 @@ namespace Barotrauma
List<string> lines = missingTags.Select(t => "\"" + t.Key + "\"\n missing from " + string.Join(", ", t.Value)).ToList();
string filePath = "missingloca.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
TextManager.Language = "English";
}));
@@ -1543,7 +1545,9 @@ namespace Barotrauma
{
var debugLines = EventSet.GetDebugStatistics();
string filePath = "eventstats.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, debugLines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
@@ -1824,7 +1828,9 @@ namespace Barotrauma
lines.Add("<EntityName." + me.Identifier + ">" + me.Name + "</EntityName." + me.Identifier + ">");
lines.Add("<EntityDescription." + me.Identifier + ">" + me.Description + "</EntityDescription." + me.Identifier + ">");
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
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) =>
@@ -1843,16 +1849,15 @@ namespace Barotrauma
docs.Add(eventPrefab.ConfigElement.Document);
getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier);
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = false
};
};
foreach (XDocument doc in docs)
{
using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings))
@@ -1861,6 +1866,7 @@ namespace Barotrauma
writer.Flush();
}
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
void getTextsFromElement(XElement element, List<string> list, string parentName)
{
@@ -2007,7 +2013,9 @@ namespace Barotrauma
lines.Add("[/table]");
lines.Add("");
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
File.WriteAllLines(filePath, lines);
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
#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) =>
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\propertylocalization.txt";
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
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) =>
@@ -560,6 +560,22 @@ namespace Barotrauma
};
}
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++)
{
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();
for (int j = 0; j < itemCount; j++)
{
@@ -23,7 +23,7 @@ namespace Barotrauma
{
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
};
@@ -6,7 +6,7 @@
{
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,
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; }
@@ -457,13 +457,13 @@ namespace Barotrauma
Color currentTextColor;
if (currentRichTextData != null && i > currentRichTextData.EndIndex + lineNum)
while (currentRichTextData != null && i + rtdOffset > currentRichTextData.EndIndex + lineNum)
{
richTextDataIndex++;
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;
if (!string.IsNullOrEmpty(currentRichTextData.Metadata))
@@ -321,6 +321,10 @@ namespace Barotrauma
float prevSize = chatBox.BarSize;
string displayedText = message.TranslatedText;
if (message.Type == ChatMessageType.Server)
{
RichTextData.GetRichTextData(displayedText, out displayedText);
}
string senderName = "";
Color senderColor = Color.White;
if (!string.IsNullOrWhiteSpace(message.SenderName))
@@ -1,10 +1,10 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Barotrauma.Networking;
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);
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
};
@@ -208,7 +208,7 @@ namespace Barotrauma
{
ClickSound = GUISoundType.HireRepairClick,
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"))
{
@@ -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")
{
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)
{
@@ -346,19 +346,22 @@ namespace Barotrauma
};
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()))
{
CanBeFocused = false
};
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform));
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft)
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
textColor: jobColor, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false
};
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),
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
{
@@ -384,11 +387,18 @@ namespace Barotrauma
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
};
}
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)
{
@@ -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)
@@ -479,7 +506,10 @@ namespace Barotrauma
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
float blockHeight = 1.0f / 4;
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), 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)
{
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); }
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
pendingList.UpdateScrollBarSize();
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
hireableList.UpdateScrollBarSize();
// Server will reset the names to originals in multiplayer
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 (createNetworkMessage) { SendCrewState(true); }
return true;
@@ -573,36 +612,41 @@ namespace Barotrauma
{
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
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);
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
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>();
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 == null || hires.None()) { return false; }
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;
foreach (CharacterInfo ci in hires)
foreach (CharacterInfo ci in nonDuplicateHires)
{
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
{
atLeastOneHired = true;
PendingHires.Remove(ci);
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == ci));
}
else
{
@@ -629,6 +673,93 @@ namespace Barotrauma
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)
{
if (!(selection is CharacterInfo characterInfo)) { return false; }
@@ -649,9 +780,10 @@ namespace Barotrauma
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;
GUIListBox listBox = null;
@@ -674,7 +806,7 @@ namespace Barotrauma
if (listBox != null)
{
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo);
SelectCharacter(listBox, highlightedFrame as GUIFrame, highlightedInfo);
}
}
else
@@ -688,6 +820,27 @@ namespace Barotrauma
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
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)
@@ -700,7 +853,7 @@ namespace Barotrauma
PendingHires.Clear();
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)
{
PendingHires.Add(match);
@@ -717,9 +870,10 @@ namespace Barotrauma
/// Notify the server of crew changes
/// </summary>
/// <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="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)
{
@@ -732,12 +886,23 @@ namespace Barotrauma
msg.Write((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires)
{
msg.Write(pendingHire.GetIdentifier());
msg.Write(pendingHire.GetIdentifierUsingOriginalName());
}
}
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);
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)
@@ -1407,7 +1407,7 @@ namespace Barotrauma
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)
{
@@ -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);
}
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)
{
@@ -1430,15 +1430,15 @@ namespace Barotrauma
}
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);
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 Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, 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);
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
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 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 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);
@@ -1455,7 +1455,7 @@ namespace Barotrauma
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)
{
@@ -705,6 +705,29 @@ namespace Barotrauma
if (!Visible) return;
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)
{
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -22,11 +23,11 @@ namespace Barotrauma
{
Default,
InGame,
Vote
Vote,
Hint
}
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
//public GUIFrame BackgroundFrame { get; private set; }
public GUILayoutGroup Content { get; private set; }
public GUIFrame InnerFrame { get; private set; }
public GUITextBlock Header { get; private set; }
@@ -58,8 +59,6 @@ namespace Barotrauma
public bool AutoClose;
private readonly bool alwaysVisible;
private float openState;
private float iconState;
private bool iconSwitching;
@@ -77,10 +76,17 @@ namespace Barotrauma
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")
{
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)
{
width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X);
@@ -107,6 +113,7 @@ namespace Barotrauma
Anchor anchor = type switch
{
Type.InGame => Anchor.TopCenter,
Type.Hint => Anchor.TopRight,
Type.Vote => Anchor.TopRight,
_ => 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 };
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);
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);
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);
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
@@ -180,7 +187,6 @@ namespace Barotrauma
else if (type == Type.InGame)
{
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
alwaysVisible = true;
CanBeFocused = false;
AutoClose = true;
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);
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);
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);
Content.Recalculate();
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);
}
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);
}
/// <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()
{
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();
break;
}
}
for (int i = MessageBoxes.Count - 1; i >= 0; i--)
{
if (MessageBoxes[i].UserData as string == "verificationprompt" ||
MessageBoxes[i].UserData as string == "bugreporter")
{
continue;
}
if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible)
{
MessageBoxes[i].AddToGUIUpdateList();
if (MessageBoxes[i] == null) { continue; }
if (!(MessageBoxes[i] is GUIMessageBox messageBox))
{
if (type == Type.Default)
{
// Message box not of type GUIMessageBox is likely the round summary
MessageBoxes[i].AddToGUIUpdateList();
break;
}
continue;
}
if (messageBox.type != type) { continue; }
// 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;
}
}
@@ -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 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
Vector2 initialPos, defaultPos, endPos;
if (type == Type.InGame)
{
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)
{
@@ -428,7 +618,7 @@ namespace Barotrauma
public void Close()
{
if (type == Type.InGame)
if (type == Type.InGame || type == Type.Hint)
{
closing = true;
}
@@ -35,6 +35,7 @@ namespace Barotrauma
public readonly Sprite[] CursorSprite = new Sprite[7];
public UISprite RadiationSprite { get; private set; }
public SpriteSheet RadiationAnimSpriteSheet { get; private set; }
public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; }
@@ -214,6 +215,9 @@ namespace Barotrauma
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
case "radiationanimspritesheet":
RadiationAnimSpriteSheet = new SpriteSheet(subElement);
break;
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
@@ -279,7 +279,8 @@ namespace Barotrauma
/// If the rectT height is set 0, the height is calculated from the text.
/// </summary>
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)
{
if (color.HasValue)
@@ -289,7 +290,13 @@ namespace Barotrauma
if (textColor.HasValue)
{
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,
//use the default CJK font as a fallback
@@ -70,6 +70,14 @@ namespace Barotrauma
}
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 float? loadState;
@@ -115,7 +123,7 @@ namespace Barotrauma
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true;
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
@@ -215,14 +223,34 @@ namespace Barotrauma
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[] lines = wrappedTip.Split('\n');
float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
for (int i = 0; i < lines.Length; i++)
if (selectedTipRichTextData != null)
{
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);
int rtdOffset = 0;
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;
//reload tip in the selected language
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
GameMain.Config.SetDefaultBindings(legacy: false);
GameMain.Config.CheckBindings(useDefaults: true);
WaitForLanguageSelection = false;
@@ -364,7 +392,7 @@ namespace Barotrauma
{
drawn = false;
LoadState = null;
selectedTip = TextManager.Get("LoadingScreenTip", true);
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn)
@@ -1284,7 +1284,8 @@ namespace Barotrauma
// Add items on the sub(s)
Submarine.MainSub?.GetItems(true)
.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));
// Add items in character inventories
@@ -1302,6 +1303,16 @@ namespace Barotrauma
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)
{
if (OwnedItems.ContainsKey(itemPrefab))
@@ -882,10 +882,15 @@ namespace Barotrauma
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);
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 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 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
@@ -901,12 +906,12 @@ namespace Barotrauma
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
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), 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), missionNameRichTextData, missionNameString, font: GUI.LargeFont);
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), missionDescriptionRichTextData, missionDescriptionString);
}
}
else
{
@@ -465,7 +465,28 @@ namespace Barotrauma
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())
{
@@ -535,6 +556,7 @@ namespace Barotrauma
Order.Init();
EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
HintManager.Init();
TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running;
@@ -920,6 +942,8 @@ namespace Barotrauma
Client.AddToGUIUpdateList();
}
SubmarinePreview.AddToGUIUpdateList();
FileSelection.AddToGUIUpdateList();
DebugConsole.AddToGUIUpdateList();
@@ -330,7 +330,10 @@ namespace Barotrauma
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; }
@@ -416,7 +419,8 @@ namespace Barotrauma
font: font,
textColor: character.Info?.Job?.Prefab?.UIColor)
{
CanBeFocused = false
CanBeFocused = false,
UserData = "name"
};
nameBlock.Text = ToolBox.LimitString(character.Name, font, (int)nameBlock.Rect.Width);
@@ -429,22 +433,7 @@ namespace Barotrauma
{
UserData = character
};
// 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
}};
}
}
SetCharacterButtonTooltip(characterButton);
if (IsSinglePlayer)
{
@@ -482,6 +471,14 @@ namespace Barotrauma
UserData = character
};
currentOrderList.RectTransform.IsFixedSize = true;
currentOrderList.OnAddedToGUIUpdateList += (component) =>
{
if (component is GUIListBox list)
{
list.CanBeFocused = CanIssueOrders;
list.CanDragElements = CanIssueOrders && list.Content.CountChildren > 1;
}
};
// Previous orders
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>
/// Sets which character is selected in the crew UI (highlight effect etc)
/// </summary>
public bool CharacterClicked(GUIComponent component, object selection)
{
if (!AllowCharacterSwitch) { return false; }
Character character = selection as Character;
if (character == null || character.IsDead || character.IsUnconscious) { return false; }
if (!(selection is Character character) || character.IsDead || character.IsUnconscious) { return false; }
if (!character.IsOnPlayerTeam) { return false; }
SelectCharacter(character);
if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; }
return true;
@@ -588,6 +597,15 @@ namespace Barotrauma
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
#region Dialog
@@ -835,7 +853,6 @@ namespace Barotrauma
if (order == null || order.Identifier == dismissedOrderPrefab.Identifier || updatedExistingIcon)
{
currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1;
RearrangeIcons();
return;
}
@@ -874,7 +891,6 @@ namespace Barotrauma
nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex);
}
currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1;
RearrangeIcons();
void RearrangeIcons()
@@ -1276,6 +1292,7 @@ namespace Barotrauma
}
DisableCommandUI();
Character.Controlled = character;
HintManager.OnChangeCharacter();
}
private int TryAdjustIndex(int amount)
@@ -1537,6 +1554,11 @@ namespace Barotrauma
if (characterComponent.UserData is Character character)
{
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 (character == Character.Controlled && characterComponent.State != GUIComponent.ComponentState.Selected)
@@ -1817,6 +1839,8 @@ namespace Barotrauma
{
Character.Controlled.dontFollowCursor = true;
}
HintManager.OnShowCommandInterface();
}
private void ToggleCommandUI()
@@ -3218,14 +3242,14 @@ namespace Barotrauma
#endif
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();
}
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
.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
@@ -3242,6 +3266,12 @@ namespace Barotrauma
.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
@@ -63,7 +63,7 @@ namespace Barotrauma
{
new GUIMessageBox(
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,
UserData = "missionstartmessage"
@@ -437,8 +437,10 @@ namespace Barotrauma
{
ShowCampaignUI = false;
}
HintManager.OnAvailableTransition(transitionType);
}
}
public override void End(TransitionType transitionType = TransitionType.None)
{
base.End(transitionType);
@@ -649,7 +651,7 @@ namespace Barotrauma
{
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.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -777,16 +779,29 @@ namespace Barotrauma
{
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();
int firedIdentifier = -1;
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
if (fireCharacter)
{
int firedIdentifier = msg.ReadInt32();
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
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
@@ -794,10 +809,10 @@ namespace Barotrauma
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
{
CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires);
if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); }
CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation);
if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); }
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters); }
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
}
}
@@ -57,11 +57,12 @@ namespace Barotrauma
/// <summary>
/// Instantiates a new single player campaign
/// </summary>
private SinglePlayerCampaign(string mapSeed) : base(GameModePreset.SinglePlayerCampaign)
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign)
{
CampaignMetadata = new CampaignMetadata(this);
UpgradeManager = new UpgradeManager(this);
map = new Map(this, mapSeed);
map = new Map(this, mapSeed, settings);
Settings = settings;
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
@@ -85,11 +86,14 @@ namespace Barotrauma
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "campaignsettings":
Settings = new CampaignSettings(subElement);
break;
case "crew":
GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
break;
case "map":
map = Map.Load(this, subElement);
map = Map.Load(this, subElement, Settings);
break;
case "metadata":
CampaignMetadata = new CampaignMetadata(this, subElement);
@@ -141,9 +145,9 @@ namespace Barotrauma
/// <summary>
/// Start a completely new single player campaign
/// </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;
}
@@ -608,6 +612,7 @@ namespace Barotrauma
{
ShowCampaignUI = false;
}
HintManager.OnAvailableTransition(transitionType);
}
if (!crewDead)
@@ -629,9 +634,9 @@ namespace Barotrauma
if (nextLevel == null)
{
//no level selected -> force the player to select one
ForceMapUI = true;
CampaignUI.SelectTab(InteractionType.Map);
map.SelectLocation(-1);
ForceMapUI = true;
return false;
}
else if (transitionType == TransitionType.ProgressToNextEmptyLocation)
@@ -704,6 +709,7 @@ namespace Barotrauma
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
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
foreach (Character c in Character.CharacterList)
@@ -99,7 +99,7 @@ namespace Barotrauma.Tutorials
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
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.TeamID = CharacterTeamType.Team1;
captain_medic.GiveJobItems(null);
@@ -122,17 +122,17 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_lockedDoor_1, 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.TeamID = CharacterTeamType.Team1;
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.TeamID = CharacterTeamType.Team1;
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.TeamID = CharacterTeamType.Team1;
captain_engineer.GiveJobItems();
@@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").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.TeamID = CharacterTeamType.Team1;
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.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.TeamID = CharacterTeamType.Team1;
patient2.GiveJobItems(null);
patient2.CanSpeak = 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");
subPatient1.TeamID = CharacterTeamType.Team1;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
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");
subPatient2.TeamID = CharacterTeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
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");
subPatient3.TeamID = CharacterTeamType.Team1;
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 ?
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")) :
new CharacterInfo(configElement.Element("Character"));
WayPoint wayPoint = GetSpawnPoint(charInfo);
@@ -535,15 +535,15 @@ namespace Barotrauma.Tutorials
titleBlock.RectTransform.IsFixedSize = true;
}
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
List<RichTextData> richTextData = RichTextData.GetRichTextData(" " + text, out text);
GUITextBlock textBlock;
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
{
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;
@@ -88,6 +88,8 @@ namespace Barotrauma
}
}
}
HintManager.Update();
}
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));
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())
@@ -20,6 +20,7 @@ namespace Barotrauma
Audio,
VoiceChat,
Controls,
Gameplay,
#if DEBUG
Debug
#endif
@@ -532,29 +533,19 @@ namespace Barotrauma
UserData = tab
};
float tabWidth = 0.25f;
float tabWidth = 1.0f / tabs.Length;
#if DEBUG
tabWidth = 0.2f;
if (tab != Tab.Debug)
{
string buttonText = tab != Tab.Debug ? TextManager.Get("SettingsTab." + tab.ToString()) : "Debug";
#else
string buttonText = TextManager.Get("SettingsTab." + tab.ToString());
#endif
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform),
TextManager.Get("SettingsTab." + tab.ToString()), 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), style: "GUITabButton")
{
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; }
};
}
#endif
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));
}
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
};
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);
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider",
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 ----------------------------------------------------------------
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);
};
/// 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),
TextManager.Get("Cancel"))
{
@@ -1465,55 +1475,54 @@ namespace Barotrauma
{ RelativeOffset = new Vector2(0.02f, 0.02f) })
{ RelativeSpacing = 0.01f };
var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic quickstart enabled", style: "GUITickBox");
automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled;
automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched";
automaticQuickStartTickBox.OnSelected = (tickBox) =>
void addDebugTickBox(bool initialValue, Action<bool> set, string label, string tooltip)
{
AutomaticQuickStartEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var tickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), label, style: "GUITickBox");
tickBox.Selected = initialValue;
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");
automaticCampaignLoadTickBox.Selected = AutomaticCampaignLoadEnabled;
automaticCampaignLoadTickBox.ToolTip = "Will the game automatically load the latest campaign save when the game is launched";
automaticCampaignLoadTickBox.OnSelected = (tickBox) =>
{
AutomaticCampaignLoadEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
addDebugTickBox(
AutomaticQuickStartEnabled,
(b) => AutomaticQuickStartEnabled = b,
"Automatic quickstart enabled",
"Will the game automatically move on to Quickstart when the game is launched");
var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox");
showSplashScreenTickBox.Selected = EnableSplashScreen;
showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched";
showSplashScreenTickBox.OnSelected = (tickBox) =>
{
EnableSplashScreen = tickBox.Selected;
UnsavedSettings = true;
return true;
};
addDebugTickBox(
AutomaticCampaignLoadEnabled,
(b) => AutomaticCampaignLoadEnabled = b,
"Automatic campaign load enabled",
"Will the game automatically load the latest campaign save when the game is launched");
var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Verbose logging enabled", style: "GUITickBox");
verboseLoggingTickBox.Selected = VerboseLogging;
verboseLoggingTickBox.ToolTip = "Should verbose logging be used";
verboseLoggingTickBox.OnSelected = (tickBox) =>
{
VerboseLogging = tickBox.Selected;
UnsavedSettings = true;
return true;
};
addDebugTickBox(
EnableSplashScreen,
(b) => EnableSplashScreen = b,
"Splash screen enabled",
"Are the splash screens shown when the game is launched");
var textManagerDebugModeTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "TextManager debug mode enabled", style: "GUITickBox");
textManagerDebugModeTickBox.Selected = TextManagerDebugModeEnabled;
textManagerDebugModeTickBox.ToolTip = "Does the TextManager return the text tags for debug purposes?";
textManagerDebugModeTickBox.OnSelected = (tickBox) =>
{
TextManagerDebugModeEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
addDebugTickBox(
VerboseLogging,
(b) => VerboseLogging = b,
"Verbose logging enabled",
"Should verbose logging be used");
addDebugTickBox(
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
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
@@ -639,7 +639,13 @@ namespace Barotrauma
foreach (Item doubleClickedItem in doubleClickedItems)
{
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;
}
@@ -810,6 +816,8 @@ namespace Barotrauma
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
}
HintManager.OnShowSubInventory(slotRef?.Item);
}
public void AssignQuickUseNumKeys()
@@ -39,6 +39,8 @@ namespace Barotrauma.Items.Components
private Pair<Rectangle, string> tooltip;
private GUITextBlock requiredTimeBlock;
partial void InitProjSpecific()
{
CreateGUI();
@@ -522,7 +524,7 @@ namespace Barotrauma.Items.Components
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);
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)
{
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));
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip);
HintManager.OnSonarSpottedCharacter(Item, c);
}
continue;
}
@@ -1399,6 +1400,7 @@ namespace Barotrauma.Items.Components
MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f));
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
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)))
{
// Undocking from an outpost
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
campaign.ShowCampaignUI = true;
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
return false;
}
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
@@ -748,7 +748,7 @@ namespace Barotrauma.Items.Components
}
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;
if (PlayerInput.KeyDown(InputType.Left)) { 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)
{
float chargeRatio = charge / capacity;
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
if (chargeIndicator != null)
{
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)
@@ -87,7 +87,7 @@ namespace Barotrauma.Items.Components
SpriteEffects.None,
depth);
}
}
}
private static Sprite defaultWireSprite;
private Sprite overrideSprite;
private Sprite wireSprite;
@@ -167,11 +167,11 @@ namespace Barotrauma.Items.Components
{
if (DockingTarget.joint != null)
{
DockingTarget.Lock(isNetworkMessage: true, forcePosition: true);
DockingTarget.Lock(isNetworkMessage: true);
}
else
{
Lock(isNetworkMessage: true, forcePosition: true);
Lock(isNetworkMessage: true);
}
}
}
@@ -327,10 +327,11 @@ namespace Barotrauma
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
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; }
Rectangle drawRect = location.Type.Sprite.SourceRect;
Rectangle drawRect = locationSprite.SourceRect;
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
drawRect.X = (int)pos.X - drawRect.Width / 2;
@@ -375,13 +376,18 @@ namespace Barotrauma
foreach (LocationConnection connection in Connections)
{
if (HighlightedLocation != CurrentDisplayLocation &&
connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation))
connection.Locations.Contains(HighlightedLocation) &&
connection.Locations.Contains(CurrentDisplayLocation))
{
if (PlayerInput.PrimaryMouseButtonClicked() &&
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
if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
{
connectionHighlightState = 0.0f;
SelectedConnection = connection;
@@ -517,8 +523,6 @@ namespace Barotrauma
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));
Radiation.Draw(spriteBatch, rect, zoom);
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)),
color : Color.White * cameraNoiseStrength * 0.1f,
@@ -534,6 +538,8 @@ namespace Barotrauma
color: Color.White * cameraNoiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
Radiation.Draw(spriteBatch, rect, zoom);
Pair<Rectangle, string> tooltip = null;
if (generationParams.ShowLocations)
{
@@ -548,8 +554,10 @@ namespace Barotrauma
Location location = Locations[i];
if (IsInFogOfWar(location)) { continue; }
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.Y = (int)pos.Y - drawRect.Width / 2;
@@ -562,20 +570,14 @@ namespace Barotrauma
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;
if (location == HighlightedLocation)
{
iconScale *= 1.2f;
}
location.Type.Sprite.Draw(spriteBatch, pos, color,
scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom);
locationSprite.Draw(spriteBatch, pos, color,
scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom);
if (location == CurrentDisplayLocation)
{
@@ -657,8 +659,11 @@ namespace Barotrauma
DrawDecorativeHUD(spriteBatch, rect);
bool drawRadiationTooltip = true;
if (HighlightedLocation != null)
{
drawRadiationTooltip = false;
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
pos.X += 50 * zoom;
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);
}
}
if (tooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
drawRadiationTooltip = false;
}
if (connectionTooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
drawRadiationTooltip = false;
}
if (drawRadiationTooltip)
{
Radiation.DrawFront(spriteBatch);
}
spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
@@ -827,6 +841,7 @@ namespace Barotrauma
{
if (connection.LevelData.HasBeaconStation) { iconCount++; }
if (connection.LevelData.HasHuntingGrounds) { iconCount++; }
if (connection.Locked) { iconCount++; }
string tooltip = null;
var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
@@ -867,6 +882,11 @@ namespace Barotrauma
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)
{
DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip"));
@@ -1,4 +1,5 @@
#nullable enable
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -6,6 +7,13 @@ namespace Barotrauma
{
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)
{
if (!Enabled) { return; }
@@ -19,16 +27,38 @@ namespace Barotrauma
Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y);
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)
{
float spriteStep = Params.BorderAnimationSpeed * deltaTime;
spriteIndex = (spriteIndex + spriteStep) % maxFrames;
if (increasedAmount > 0)
{
increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime;
@@ -805,7 +805,7 @@ namespace Barotrauma
}
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)
{
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)
{
@@ -2064,6 +2064,8 @@ namespace Barotrauma.Networking
bool autoRestartEnabled = inc.ReadBoolean();
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
bool radiationEnabled = inc.ReadBoolean();
//ignore the message if we already a more up-to-date one
//or if we're still waiting for the initial update
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
@@ -2119,7 +2121,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
GameMain.NetLobbyScreen.SetBotCount(botCount);
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
@@ -2564,7 +2566,7 @@ namespace Barotrauma.Networking
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
{
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.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -2912,7 +2914,7 @@ namespace Barotrauma.Networking
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.CampaignFrame.Visible = false;
@@ -2927,6 +2929,7 @@ namespace Barotrauma.Networking
msg.Write(mapSeed);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
settings.Serialize(msg);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -147,13 +147,36 @@ namespace Barotrauma.Networking
if (missingPackages.Count > 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;
if (nonDownloadable.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(nonDownloadable.First())}";
}
else
{
@@ -56,6 +56,7 @@ namespace Barotrauma.Networking
Steamworks.SteamNetworking.AllowP2PPacketRelay(true);
ServerConnection = new SteamP2PConnection("Server", hostSteamId);
ServerConnection.SetOwnerSteamIDIfUnknown(hostSteamId);
incomingInitializationMessages = new List<IReadMessage>();
incomingDataMessages = new List<IReadMessage>();
@@ -17,6 +17,7 @@ namespace Barotrauma.Networking
class RemotePeer
{
public UInt64 SteamID;
public UInt64 OwnerSteamID;
public double? DisconnectTime;
public bool Authenticating;
public bool Authenticated;
@@ -31,6 +32,7 @@ namespace Barotrauma.Networking
public RemotePeer(UInt64 steamId)
{
SteamID = steamId;
OwnerSteamID = 0;
DisconnectTime = null;
Authenticating = false;
Authenticated = false;
@@ -90,10 +92,19 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK)
{
remotePeer.OwnerSteamID = ownerID;
remotePeer.Authenticated = true;
remotePeer.Authenticating = false;
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();
Array.Resize(ref msgToSend, msg.Message.LengthBytes);
ChildServerRelay.Write(msgToSend);
@@ -131,6 +142,7 @@ namespace Barotrauma.Networking
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(steamId);
outMsg.Write(remotePeer.OwnerSteamID);
outMsg.Write(data, 1, dataLength - 1);
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
@@ -142,34 +154,27 @@ namespace Barotrauma.Networking
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 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;
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;
}
}
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();
outMsg.Write(selfSteamID);
outMsg.Write(selfSteamID);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
outMsg.Write(Name);
@@ -428,6 +434,7 @@ namespace Barotrauma.Networking
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
msgToSend.Write(selfSteamID);
msgToSend.Write(selfSteamID);
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
msgToSend.Write((UInt16)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;
@@ -212,6 +212,7 @@ namespace Barotrauma.Networking
outMsg.Write(autoRestart != null);
outMsg.Write(autoRestart ?? false);
outMsg.Write(radiationEnabled ?? RadiationEnabled);
outMsg.WritePadBits();
}
@@ -274,7 +275,7 @@ namespace Barotrauma.Networking
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
return true;
};
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
{
OnClicked = ToggleSettingsFrame
@@ -23,7 +23,7 @@ namespace Barotrauma
private GUIButton loadGameButton, deleteMpSaveButton;
public Action<SubmarineInfo, string, string> StartNewGame;
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
public Action<string> LoadGame;
private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 };
@@ -40,6 +40,8 @@ namespace Barotrauma
get;
private set;
}
public GUITickBox EnableRadiationToggle { get; set; }
private readonly bool isMultiplayer;
@@ -171,6 +173,8 @@ namespace Barotrauma
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = new CampaignSettings { RadiationEnabled = EnableRadiationToggle?.Selected ?? GameMain.NetLobbyScreen.IsRadiationEnabled() };
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{
if (!hasRequiredContentPackages)
@@ -184,7 +188,7 @@ namespace Barotrauma
{
if (GUIMessageBox.MessageBoxes.Count == 0)
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -204,7 +208,7 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = (button, obj) =>
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -219,7 +223,7 @@ namespace Barotrauma
}
else
{
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
if (isMultiplayer)
{
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
@@ -230,8 +234,7 @@ namespace Barotrauma
}
};
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "",
font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
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)
{
TextGetter = () =>
{
@@ -254,6 +257,12 @@ namespace Barotrauma
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")
{
IgnoreLayoutGroups = true,
@@ -477,8 +477,8 @@ namespace Barotrauma
}
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),
TextManager.GetWithVariable("missionreward", "[reward]", rewardText), wrap: true);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, 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, parseRichText: true);
}
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)
@@ -562,6 +562,11 @@ namespace Barotrauma
public void SelectTab(CampaignMode.InteractionType tab)
{
if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map))
{
HintManager.OnShowCampaignInterface(tab);
}
selectedTab = tab;
for (int i = 0; i < tabs.Length; i++)
{
@@ -1761,9 +1761,9 @@ namespace Barotrauma.CharacterEditor
#endif
// Add to the selected content package
contentPackage.AddFile(configFilePath, ContentType.Character);
Barotrauma.IO.Validation.DevException = true;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
contentPackage.Save(contentPackage.Path);
Barotrauma.IO.Validation.DevException = false;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
// Ragdoll
@@ -882,7 +882,7 @@ namespace Barotrauma
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;
gameMode.SpawnOutpost = true;
@@ -296,7 +296,7 @@ namespace Barotrauma
subInfo ??= SubmarineInfo.SavedSubmarines.GetRandom(s =>
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&
!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.GameMode as TestGameMode).OnRoundEnd = () =>
{
@@ -1043,7 +1043,7 @@ namespace Barotrauma
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;
@@ -1082,7 +1082,7 @@ namespace Barotrauma
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();
}
@@ -1134,6 +1134,7 @@ namespace Barotrauma
(int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f),
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;
}
@@ -27,6 +27,8 @@ namespace Barotrauma
private GUIComponent jobVariantTooltip;
private SubmarinePreview submarinePreview;
private readonly GUITextBox chatInput;
private readonly GUITextBox serverLogFilter;
public GUITextBox ChatInput
@@ -41,6 +43,8 @@ namespace Barotrauma
private readonly GUIScrollBar levelDifficultyScrollBar;
private readonly GUITickBox radiationEnabledTickBox;
private readonly GUIButton[] traitorProbabilityButtons;
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();
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);
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 =
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && 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());
}
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),
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,
color: ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f,
wrap: true, font: GUI.SmallFont)
@@ -43,6 +43,13 @@ namespace Barotrauma
private Dictionary<ulong, Steamworks.Ugc.Item?> pendingWorkshopDownloads = null;
private string autoConnectName; private string autoConnectEndpoint;
private enum TernaryOption
{
Any,
Enabled,
Disabled
}
private class FriendInfo
{
public UInt64 SteamID;
@@ -146,16 +153,19 @@ namespace Barotrauma
private GUITickBox filterFull;
private GUITickBox filterEmpty;
private GUITickBox filterWhitelisted;
private GUITickBox filterFriendlyFire;
private GUITickBox filterKarma;
private GUITickBox filterTraitor;
private GUITickBox filterModded;
private GUITickBox filterVoip;
private Dictionary<string, GUIDropDown> ternaryFilters;
private Dictionary<string, GUITickBox> filterTickBoxes;
private Dictionary<string, GUITickBox> playStyleTickBoxes;
private Dictionary<string, GUITickBox> gameModeTickBoxes;
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 GUIButton serverPreviewToggleButton;
@@ -173,6 +183,49 @@ namespace Barotrauma
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()
{
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);
ternaryFilters = new Dictionary<string, GUIDropDown>();
filterTickBoxes = new Dictionary<string, GUITickBox>();
filterSameVersion = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterSameVersion"))
@@ -382,40 +436,11 @@ namespace Barotrauma
CanBeFocused = false
};
filterKarma = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.karma.true"))
{
UserData = TextManager.Get("servertag.karma.true"),
OnSelected = (tickBox) => { FilterServers(); return true; }
};
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);
AddTernaryFilter(filters.Content.RectTransform, elementHeight, "karma", (value) => { filterKarmaValue = value; });
AddTernaryFilter(filters.Content.RectTransform, elementHeight, "traitors", (value) => { filterTraitorValue = value; });
AddTernaryFilter(filters.Content.RectTransform, elementHeight, "friendlyfire", (value) => { filterFriendlyFireValue = value; });
AddTernaryFilter(filters.Content.RectTransform, elementHeight, "voip", (value) => { filterVoipValue = value; });
AddTernaryFilter(filters.Content.RectTransform, elementHeight, "modded", (value) => { filterModdedValue = value; });
// Play Style Selection
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
{
bool incompatible =
(!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch()) ||
(serverInfo.ContentPackageHashes.Any() && !serverInfo.ContentPackagesMatch()) ||
(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 =
serverInfo.OwnerVerified &&
serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase) &&
@@ -1089,11 +1120,11 @@ namespace Barotrauma
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) &&
(!filterVoip.Selected || serverInfo.VoipEnabled == false) &&
(!filterModded.Selected || serverInfo.GetPlayStyleTags().Any(t => t.Contains("modded.true"))) &&
karmaFilterPassed &&
friendlyFireFilterPassed &&
traitorsFilterPassed &&
voipFilterPassed &&
moddedFilterPassed &&
((selectedTab == ServerListTab.All && (serverInfo.LobbyID != 0 || !string.IsNullOrWhiteSpace(serverInfo.Port))) ||
(selectedTab == ServerListTab.Recent && serverInfo.Recent) ||
(selectedTab == ServerListTab.Favorites && serverInfo.Favorite));
@@ -2334,6 +2365,10 @@ namespace Barotrauma
{
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)
@@ -2344,6 +2379,15 @@ namespace Barotrauma
{
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; }
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 &&
GameMain.VanillaContent.GetFilesOfType(ContentType.Submarine).Any(s => Path.GetFullPath(s) == subPath))
GameMain.VanillaContent.Files.Any(s => Path.GetFullPath(s.Path) == subPath))
{
continue;
}
@@ -870,7 +870,7 @@ namespace Barotrauma
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.GameMode as TestGameMode).OnRoundEnd = () =>
{
@@ -897,6 +897,8 @@ namespace Barotrauma
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)
{
if (!ep.Category.HasFlag(category)) { continue; }
@@ -907,13 +909,14 @@ namespace Barotrauma
}
entityLists[category + ep.Subcategory].Add(ep);
categoryKeys[category + ep.Subcategory] = category;
string categoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory;
if (categoryName != null)
string subcategoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory;
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;
int entitiesPerRow = (int)Math.Ceiling(categorizedEntityList.Content.Rect.Width / Math.Max(125 * GUI.Scale, 60));
foreach (string categoryKey in entityLists.Keys)
@@ -926,19 +929,33 @@ namespace Barotrauma
new GUIFrame(new RectTransform(Vector2.One, categoryFrame.RectTransform), style: "HorizontalLine");
string categoryName = entityLists[categoryKey].First().Subcategory;
categoryName = string.IsNullOrEmpty(categoryName) ?
TextManager.Get("mapentitycategory.misc") :
(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)
string categoryName = TextManager.Get("MapEntityCategory." + entityLists[categoryKey].First().Category);
string subCategoryName = entityLists[categoryKey].First().Subcategory;
if (string.IsNullOrEmpty(subCategoryName))
{
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),
style: null,
@@ -959,7 +976,6 @@ namespace Barotrauma
#if !DEBUG
if (ep.HideInMenus) { continue; }
#endif
CreateEntityElement(ep, entitiesPerRow, entityListInner.Content);
}
@@ -976,6 +992,9 @@ namespace Barotrauma
foreach (MapEntityPrefab ep in MapEntityPrefab.List)
{
#if !DEBUG
if (ep.HideInMenus) { continue; }
#endif
CreateEntityElement(ep, entitiesPerRow, allEntityList.Content);
}
}
@@ -1354,7 +1373,7 @@ namespace Barotrauma
{
try
{
Barotrauma.IO.Validation.DevException = true;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
TimeSpan time = DateTime.UtcNow - DateTime.MinValue;
string filePath = Path.Combine(autoSavePath, $"AutoSave_{(ulong)time.TotalMilliseconds}.sub");
SaveUtil.CompressStringToFile(filePath, doc.ToString());
@@ -1390,7 +1409,7 @@ namespace Barotrauma
}
});
Barotrauma.IO.Validation.DevException = false;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
CrossThread.RequestExecutionOnMainThread(DisplayAutoSavePrompt);
}
catch (Exception e)
@@ -1558,9 +1577,9 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = (bt, userdata) =>
{
contentPackage.AddFile(savePath, ContentType.OutpostModule);
Barotrauma.IO.Validation.DevException = true;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
contentPackage.Save(contentPackage.Path, reload: false);
Barotrauma.IO.Validation.DevException = false;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
msgBox.Close();
return true;
};
@@ -1635,7 +1654,7 @@ namespace Barotrauma
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)
{
bool savePreviewImage = true;
@@ -1655,7 +1674,7 @@ namespace Barotrauma
{
Submarine.MainSub.SaveAs(savePath);
}
Barotrauma.IO.Validation.DevException = false;
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
Submarine.MainSub.CheckForErrors();
@@ -4695,6 +4714,7 @@ namespace Barotrauma
}
graphics.Clear(backgroundColor);
ImageManager.Draw(spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform);
@@ -63,8 +63,18 @@ namespace Barotrauma.Sounds
ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16;
SampleRate = reader.SampleRate;
if (Buffers != null && SoundBuffers.BuffersGenerated < SoundBuffers.MaxBuffers)
{
Buffers.RequestAlBuffers(); FillBuffers();
}
}
public override void FillBuffers()
{
if (!Stream)
{
reader.DecodedPosition = 0;
int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] floatBuffer = new float[bufferSize];
@@ -86,7 +96,7 @@ namespace Barotrauma.Sounds
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALBuffer, ALFormat, shortBuffer,
Al.BufferData(Buffers.AlBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
int alError = Al.GetError();
@@ -99,7 +109,7 @@ namespace Barotrauma.Sounds
CastBuffer(floatBuffer, shortBuffer, readSamples);
Al.BufferData(ALMuffledBuffer, ALFormat, shortBuffer,
Al.BufferData(Buffers.AlMuffledBuffer, ALFormat, shortBuffer,
readSamples * sizeof(short), SampleRate);
alError = Al.GetError();
@@ -52,16 +52,10 @@ namespace Barotrauma.Sounds
}
}
private uint alBuffer;
public uint ALBuffer
private SoundBuffers buffers;
public SoundBuffers Buffers
{
get { return !Stream ? alBuffer : 0; }
}
private uint alMuffledBuffer;
public uint ALMuffledBuffer
{
get { return !Stream ? alMuffledBuffer : 0; }
get { return !Stream ? buffers : null; }
}
public int ALFormat
@@ -169,69 +163,20 @@ namespace Barotrauma.Sounds
{
if (!Stream)
{
Al.GenBuffer(out alBuffer);
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!");
}
buffers = new SoundBuffers(this);
}
else
{
alBuffer = 0;
buffers = null;
}
}
public virtual void FillBuffers() { }
public virtual void DeleteALBuffers()
{
Owner.KillChannels(this);
if (alBuffer != 0)
{
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));
}
}
buffers?.Dispose();
}
public virtual void Dispose()
@@ -334,7 +334,11 @@ namespace Barotrauma.Sounds
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();
if (alError != Al.NoError)
@@ -506,17 +510,26 @@ namespace Barotrauma.Sounds
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!");
}
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);
alError = Al.GetError();
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));
@@ -528,7 +541,7 @@ namespace Barotrauma.Sounds
}
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);
int alError = Al.GetError();
if (alError != Al.NoError)
@@ -30,6 +30,8 @@ namespace Barotrauma.Sounds
private readonly SoundSourcePool[] sourcePools;
private readonly List<Sound> loadedSounds;
public IReadOnlyList<Sound> LoadedSounds => loadedSounds;
private readonly SoundChannel[][] playingChannels = new SoundChannel[2][];
private readonly object threadDeathMutex = new object();
@@ -172,22 +172,22 @@ namespace Barotrauma
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);
}
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);
}
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);
}
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; }
//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)
{
if (Texture == null) { return; }
@@ -5,14 +5,14 @@ namespace Barotrauma
{
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;
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;
@@ -100,38 +100,6 @@ namespace Barotrauma
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>
/// Convert a RGB value into a HSV value.
/// </summary>