(ded4a3e0a) v0.9.0.7

This commit is contained in:
Joonas Rikkonen
2019-06-25 16:00:44 +03:00
parent e5ae622c77
commit 4a51db77b5
1777 changed files with 421528 additions and 917 deletions
@@ -221,6 +221,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\Sprite.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\Sprite.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\StatusEffect.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\StatusEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\CrossThread.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs"> <Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs">
</Compile> </Compile>
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\MathUtils.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Utils\MathUtils.cs" />
@@ -83,8 +83,9 @@
<Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath> <HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference> </Reference>
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="MonoGame.Framework, Version=3.6.0.1625, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\lib\net45\MonoGame.Framework.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Libraries\MonoGame.Framework\DesktopGL\MonoGame.Framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath> <HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
@@ -354,9 +355,7 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" /> <Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" />
<Error Condition="!Exists('..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets'))" />
</Target> </Target>
<Import Project="..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets" Condition="Exists('..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
+2 -4
View File
@@ -82,8 +82,8 @@
<Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath> <HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference> </Reference>
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="MonoGame.Framework">
<HintPath>..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\lib\net45\MonoGame.Framework.dll</HintPath> <HintPath>..\..\Libraries\MonoGame.Framework\DesktopGL\MonoGame.Framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath> <HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
@@ -291,10 +291,8 @@
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets'))" />
<Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" /> <Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" />
</Target> </Target>
<Import Project="..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets" Condition="Exists('..\..\Libraries\NuGet\MonoGame.Framework.DesktopGL.3.7.1.189\build\MonoGame.Framework.DesktopGL.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.6")] [assembly: AssemblyVersion("0.9.0.7")]
[assembly: AssemblyFileVersion("0.9.0.6")] [assembly: AssemblyFileVersion("0.9.0.7")]
@@ -442,7 +442,8 @@ namespace Barotrauma
if (draggingItemToWorld) if (draggingItemToWorld)
{ {
if (item.OwnInventory == null || if (item.OwnInventory == null ||
!item.OwnInventory.CanBePut(CharacterInventory.draggingItem)) !item.OwnInventory.CanBePut(CharacterInventory.draggingItem) ||
!CanAccessInventory(item.OwnInventory))
{ {
continue; continue;
} }
@@ -139,7 +139,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (!item.Repairables.Any(r => item.Condition < r.ShowRepairUIThreshold)) { continue; } if (!item.Repairables.Any(r => item.Condition < r.ShowRepairUIThreshold)) { continue; }
if (!Submarine.VisibleEntities.Contains(item)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition; Vector2 diff = item.WorldPosition - character.WorldPosition;
if (Submarine.CheckVisibility(character.SimPosition, character.SimPosition + ConvertUnits.ToSimUnits(diff)) == null) if (Submarine.CheckVisibility(character.SimPosition, character.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
@@ -391,7 +391,7 @@ namespace Barotrauma
if (!orderIndicatorCount.ContainsKey(target)) { orderIndicatorCount.Add(target, 0); } if (!orderIndicatorCount.ContainsKey(target)) { orderIndicatorCount.Add(target, 0); }
Vector2 drawPos = target.WorldPosition + Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target]; Vector2 drawPos = target.DrawPosition + Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha); GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha);
orderIndicatorCount[target] = orderIndicatorCount[target] + 1; orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
@@ -318,7 +318,7 @@ namespace Barotrauma
if (!(mp is ItemPrefab)) continue; if (!(mp is ItemPrefab)) continue;
sb.AppendLine(mp.Name); sb.AppendLine(mp.Name);
} }
System.Windows.Clipboard.SetText(sb.ToString()); Clipboard.SetText(sb.ToString());
})); }));
#endif #endif
@@ -452,6 +452,10 @@ namespace Barotrauma
commands.Add(new Command("clientlist", "", (string[] args) => { })); commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true); AssignRelayToServer("clientlist", true);
commands.Add(new Command("setmaxplayers|maxplayers", "", (string[] args) => { }));
AssignRelayToServer("setmaxplayers", true);
commands.Add(new Command("setpassword|password", "", (string[] args) => { }));
AssignRelayToServer("setpassword", true);
AssignOnExecute("control", (string[] args) => AssignOnExecute("control", (string[] args) =>
{ {
@@ -6,34 +6,6 @@ using System.Text;
namespace EventInput namespace EventInput
{ {
#if WINDOWS
public class KeyboardLayout
{
const uint KLF_ACTIVATE = 1; //activate the layout
const int KL_NAMELENGTH = 9; // length of the keyboard buffer
const string LANG_EN_US = "00000409";
const string LANG_HE_IL = "0001101A";
[DllImport("user32.dll")]
private static extern long LoadKeyboardLayout(
string pwszKLID, // input locale identifier
uint Flags // input locale identifier options
);
[DllImport("user32.dll")]
private static extern long GetKeyboardLayoutName(
StringBuilder pwszKLID //[out] string that receives the name of the locale identifier
);
public static string getName()
{
StringBuilder name = new StringBuilder(KL_NAMELENGTH);
GetKeyboardLayoutName(name);
return name.ToString();
}
}
#endif
public class CharacterEventArgs : EventArgs public class CharacterEventArgs : EventArgs
{ {
private readonly char character; private readonly char character;
@@ -118,50 +90,6 @@ namespace EventInput
static bool initialized; static bool initialized;
#if WINDOWS
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
static IntPtr prevWndProc;
static WndProc hookProcDelegate;
static IntPtr hIMC;
//various Win32 constants that we need
const int GWL_WNDPROC = -4;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
const int WM_IME_SETCONTEXT = 0x0281;
const int WM_INPUTLANGCHANGE = 0x51;
const int WM_GETDLGCODE = 0x87;
const int WM_IME_COMPOSITION = 0x10f;
const int DLGC_WANTALLKEYS = 4;
//Win32 functions that we're using
[DllImport("Imm32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("Imm32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static IntPtr TrySetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetWindowLong(hWnd, nIndex, dwNewLong);
}
return SetWindowLongPtr(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
#endif
/// <summary> /// <summary>
/// Initialize the TextInput with the given GameWindow. /// Initialize the TextInput with the given GameWindow.
/// </summary> /// </summary>
@@ -172,17 +100,8 @@ namespace EventInput
{ {
return; return;
} }
#if WINDOWS
hookProcDelegate = HookProc;
prevWndProc = TrySetWindowLong(window.Handle, GWL_WNDPROC,
Marshal.GetFunctionPointerForDelegate(hookProcDelegate));
hIMC = ImmGetContext(window.Handle);
#else
window.TextInput += ReceiveInput; window.TextInput += ReceiveInput;
#endif
initialized = true; initialized = true;
} }
@@ -197,63 +116,5 @@ namespace EventInput
{ {
CharEntered?.Invoke(null, new CharacterEventArgs(character, 0)); CharEntered?.Invoke(null, new CharacterEventArgs(character, 0));
} }
#if WINDOWS
static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
break;
case WM_KEYDOWN:
if (KeyDown != null)
KeyDown(null, new KeyEventArgs(HandleKeyInput(wParam)));
break;
case WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new KeyEventArgs(HandleKeyInput(wParam)));
break;
case WM_CHAR:
if (CharEntered != null)
CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt64()));
break;
case WM_IME_SETCONTEXT:
if (wParam.ToInt32() == 1)
ImmAssociateContext(hWnd, hIMC);
break;
case WM_INPUTLANGCHANGE:
ImmAssociateContext(hWnd, hIMC);
returnCode = (IntPtr)1;
break;
}
return returnCode;
}
static Keys HandleKeyInput(IntPtr wParam)
{
// The conversion does not work for Shift or CTRL. Hence the hack.
Keys key = (Keys)wParam;
int k = (int)key;
if (k == 16)
{
// Could also be RightShift
key = Keys.LeftShift;
}
else if (k == 17)
{
// Could also be RightControl
key = Keys.LeftControl;
}
return key;
}
#endif
} }
} }
@@ -82,16 +82,5 @@ namespace EventInput
value.Selected = true; value.Selected = true;
} }
} }
#if WINDOWS
//Thread has to be in Single Thread Apartment state in order to receive clipboard
string _pasteResult = "";
[STAThread]
void PasteThread()
{
_pasteResult = Clipboard.ContainsText() ? Clipboard.GetText() : "";
}
#endif
} }
} }
@@ -11,6 +11,7 @@ namespace Barotrauma
{ {
private static List<ScalableFont> FontList = new List<ScalableFont>(); private static List<ScalableFont> FontList = new List<ScalableFont>();
private static Library Lib = null; private static Library Lib = null;
private static object mutex = new object();
private string filename; private string filename;
private Face face; private Face face;
@@ -63,33 +64,36 @@ namespace Barotrauma
public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false) public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false)
{ {
if (Lib == null) Lib = new Library(); lock (mutex)
this.filename = filename;
this.face = null;
foreach (ScalableFont font in FontList)
{ {
if (font.filename == filename) if (Lib == null) Lib = new Library();
this.filename = filename;
this.face = null;
foreach (ScalableFont font in FontList)
{ {
this.face = font.face; if (font.filename == filename)
break; {
this.face = font.face;
break;
}
} }
} if (this.face == null)
if (this.face == null) {
{ this.face = new Face(Lib, filename);
this.face = new Face(Lib, filename); }
} this.size = size;
this.size = size; this.textures = new List<Texture2D>();
this.textures = new List<Texture2D>(); this.texCoords = new Dictionary<uint, GlyphData>();
this.texCoords = new Dictionary<uint, GlyphData>(); this.DynamicLoading = dynamicLoading;
this.DynamicLoading = dynamicLoading; this.graphicsDevice = gd;
this.graphicsDevice = gd;
if (gd != null && !dynamicLoading) if (gd != null && !dynamicLoading)
{ {
RenderAtlas(gd); RenderAtlas(gd);
} }
FontList.Add(this); FontList.Add(this);
}
} }
/// <summary> /// <summary>
@@ -112,7 +116,10 @@ namespace Barotrauma
this.texDims = texDims; this.texDims = texDims;
this.baseChar = baseChar; this.baseChar = baseChar;
face.SetPixelSizes(0, size); lock (mutex)
{
face.SetPixelSizes(0, size);
}
textures.ForEach(t => t.Dispose()); textures.ForEach(t => t.Dispose());
textures.Clear(); textures.Clear();
texCoords.Clear(); texCoords.Clear();
@@ -123,14 +130,20 @@ namespace Barotrauma
pixelBuffer[i] = 0; pixelBuffer[i] = 0;
} }
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color)); CrossThread.RequestExecutionOnMainThread(() =>
{
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
});
int texIndex = 0; int texIndex = 0;
Vector2 currentCoords = Vector2.Zero; Vector2 currentCoords = Vector2.Zero;
int nextY = 0; int nextY = 0;
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal); lock (mutex)
baseHeight = face.Glyph.Metrics.Height.ToInt32(); {
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
baseHeight = face.Glyph.Metrics.Height.ToInt32();
}
//lineHeight = baseHeight; //lineHeight = baseHeight;
for (int i = 0; i < charRanges.Length; i += 2) for (int i = 0; i < charRanges.Length; i += 2)
{ {
@@ -138,76 +151,85 @@ namespace Barotrauma
uint end = charRanges[i + 1]; uint end = charRanges[i + 1];
for (uint j = start; j <= end; j++) for (uint j = start; j <= end; j++)
{ {
uint glyphIndex = face.GetCharIndex(j); lock (mutex)
if (glyphIndex == 0) continue;
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{ {
if (face.Glyph.Metrics.HorizontalAdvance > 0) uint glyphIndex = face.GetCharIndex(j);
if (glyphIndex == 0) continue;
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{ {
//glyph is empty, but char still applies advance if (face.Glyph.Metrics.HorizontalAdvance > 0)
GlyphData blankData = new GlyphData(); {
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance; //glyph is empty, but char still applies advance
blankData.texIndex = -1; //indicates no texture because the glyph is empty GlyphData blankData = new GlyphData();
texCoords.Add(j, blankData); blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance;
blankData.texIndex = -1; //indicates no texture because the glyph is empty
texCoords.Add(j, blankData);
}
continue;
} }
continue; //stacktrace doesn't really work that well when RenderGlyph throws an exception
} face.Glyph.RenderGlyph(RenderMode.Normal);
//stacktrace doesn't really work that well when RenderGlyph throws an exception byte[] bitmap = face.Glyph.Bitmap.BufferData;
face.Glyph.RenderGlyph(RenderMode.Normal); int glyphWidth = face.Glyph.Bitmap.Width;
byte[] bitmap = face.Glyph.Bitmap.BufferData; int glyphHeight = bitmap.Length / glyphWidth;
int glyphWidth = face.Glyph.Bitmap.Width;
int glyphHeight = bitmap.Length / glyphWidth;
//if (glyphHeight>lineHeight) lineHeight=glyphHeight; //if (glyphHeight>lineHeight) lineHeight=glyphHeight;
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1) if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
throw new Exception(filename + ", " + size.ToString() + ", "+ (char)j + "; Glyph dimensions exceed texture atlas dimensions");
}
nextY = Math.Max(nextY, glyphHeight + 2);
if (currentCoords.X + glyphWidth + 2 > texDims - 1)
{
currentCoords.X = 0;
currentCoords.Y += nextY;
nextY = 0;
}
if (currentCoords.Y + glyphHeight + 2 > texDims - 1)
{
currentCoords.X = 0;
currentCoords.Y = 0;
textures[texIndex].SetData<uint>(pixelBuffer);
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
texIndex++;
for (int k = 0; k < texDims * texDims; k++)
{ {
pixelBuffer[k] = 0; throw new Exception(filename + ", " + size.ToString() + ", " + (char)j + "; Glyph dimensions exceed texture atlas dimensions");
} }
}
GlyphData newData = new GlyphData nextY = Math.Max(nextY, glyphHeight + 2);
{
advance = (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex = texIndex,
texCoords = new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight),
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop)
};
texCoords.Add(j, newData);
for (int y = 0; y < glyphHeight; y++) if (currentCoords.X + glyphWidth + 2 > texDims - 1)
{
for (int x = 0; x < glyphWidth; x++)
{ {
byte byteColor = bitmap[x + y * glyphWidth]; currentCoords.X = 0;
pixelBuffer[((int)currentCoords.X + x) + ((int)currentCoords.Y + y) * texDims] = (uint)(byteColor << 24 | byteColor << 16 | byteColor << 8 | byteColor); currentCoords.Y += nextY;
nextY = 0;
}
if (currentCoords.Y + glyphHeight + 2 > texDims - 1)
{
currentCoords.X = 0;
currentCoords.Y = 0;
CrossThread.RequestExecutionOnMainThread(() =>
{
textures[texIndex].SetData<uint>(pixelBuffer);
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
});
texIndex++;
for (int k = 0; k < texDims * texDims; k++)
{
pixelBuffer[k] = 0;
}
} }
}
currentCoords.X += glyphWidth + 2; GlyphData newData = new GlyphData
{
advance = (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex = texIndex,
texCoords = new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight),
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop)
};
texCoords.Add(j, newData);
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
{
byte byteColor = bitmap[x + y * glyphWidth];
pixelBuffer[((int)currentCoords.X + x) + ((int)currentCoords.Y + y) * texDims] = (uint)(byteColor << 24 | byteColor << 16 | byteColor << 8 | byteColor);
}
}
currentCoords.X += glyphWidth + 2;
}
} }
textures[texIndex].SetData<uint>(pixelBuffer); CrossThread.RequestExecutionOnMainThread(() =>
{
textures[texIndex].SetData<uint>(pixelBuffer);
});
} }
} }
@@ -220,7 +242,10 @@ namespace Barotrauma
face.SetPixelSizes(0, size); face.SetPixelSizes(0, size);
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal); face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
baseHeight = face.Glyph.Metrics.Height.ToInt32(); baseHeight = face.Glyph.Metrics.Height.ToInt32();
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color)); CrossThread.RequestExecutionOnMainThread(() =>
{
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
});
} }
uint glyphIndex = face.GetCharIndex(character); uint glyphIndex = face.GetCharIndex(character);
@@ -264,7 +289,10 @@ namespace Barotrauma
currentDynamicAtlasCoords.X = 0; currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y = 0; currentDynamicAtlasCoords.Y = 0;
currentDynamicAtlasNextY = 0; currentDynamicAtlasNextY = 0;
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color)); CrossThread.RequestExecutionOnMainThread(() =>
{
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
});
currentDynamicPixelBuffer = null; currentDynamicPixelBuffer = null;
} }
@@ -291,7 +319,10 @@ namespace Barotrauma
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | byteColor << 16 | byteColor << 8 | byteColor); currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | byteColor << 16 | byteColor << 8 | byteColor);
} }
} }
textures[newData.texIndex].SetData<uint>(currentDynamicPixelBuffer); CrossThread.RequestExecutionOnMainThread(() =>
{
textures[newData.texIndex].SetData<uint>(currentDynamicPixelBuffer);
});
currentDynamicAtlasCoords.X += glyphWidth + 2; currentDynamicAtlasCoords.X += glyphWidth + 2;
} }
+26 -10
View File
@@ -184,8 +184,12 @@ namespace Barotrauma
sounds[(int)GUISoundType.DropItem] = GameMain.SoundManager.LoadSound("Content/Sounds/DropItem.ogg", false); sounds[(int)GUISoundType.DropItem] = GameMain.SoundManager.LoadSound("Content/Sounds/DropItem.ogg", false);
} }
// create 1x1 texture for line drawing // create 1x1 texture for line drawing
t = new Texture2D(GraphicsDevice, 1, 1); CrossThread.RequestExecutionOnMainThread(() =>
t.SetData(new Color[] { Color.White });// fill the texture with white {
t = new Texture2D(GraphicsDevice, 1, 1);
t.SetData(new Color[] { Color.White });// fill the texture with white
});
SubmarineIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(452, 385, 182, 81), new Vector2(0.5f, 0.5f)); SubmarineIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(452, 385, 182, 81), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/IconAtlas.png", new Rectangle(392, 393, 49, 45), new Vector2(0.5f, 0.5f)); arrow = new Sprite("Content/UI/IconAtlas.png", new Rectangle(392, 393, 49, 45), new Vector2(0.5f, 0.5f));
SpeechBubbleIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f)); SpeechBubbleIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f));
@@ -948,7 +952,6 @@ namespace Barotrauma
public static Texture2D CreateCircle(int radius, bool filled = false) public static Texture2D CreateCircle(int radius, bool filled = false)
{ {
int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius]; Color[] data = new Color[outerRadius * outerRadius];
@@ -986,16 +989,19 @@ namespace Barotrauma
} }
} }
Texture2D texture = null;
texture.SetData(data); CrossThread.RequestExecutionOnMainThread(() =>
{
texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
texture.SetData(data);
});
return texture; return texture;
} }
public static Texture2D CreateCapsule(int radius, int height) public static Texture2D CreateCapsule(int radius, int height)
{ {
int textureWidth = radius * 2, textureHeight = height + radius * 2; int textureWidth = radius * 2, textureHeight = height + radius * 2;
Texture2D texture = new Texture2D(GraphicsDevice, textureWidth, textureHeight);
Color[] data = new Color[textureWidth * textureHeight]; Color[] data = new Color[textureWidth * textureHeight];
// Colour the entire texture transparent first. // Colour the entire texture transparent first.
@@ -1023,13 +1029,17 @@ namespace Barotrauma
TrySetArray(data, y * textureWidth + (textureWidth - 1), Color.White); TrySetArray(data, y * textureWidth + (textureWidth - 1), Color.White);
} }
texture.SetData(data); Texture2D texture = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
texture = new Texture2D(GraphicsDevice, textureWidth, textureHeight);
texture.SetData(data);
});
return texture; return texture;
} }
public static Texture2D CreateRectangle(int width, int height) public static Texture2D CreateRectangle(int width, int height)
{ {
Texture2D texture = new Texture2D(GraphicsDevice, width, height);
Color[] data = new Color[width * height]; Color[] data = new Color[width * height];
for (int i = 0; i < data.Length; i++) for (int i = 0; i < data.Length; i++)
@@ -1047,7 +1057,13 @@ namespace Barotrauma
TrySetArray(data, (height - 1) * width + x, Color.White); TrySetArray(data, (height - 1) * width + x, Color.White);
} }
texture.SetData(data);
Texture2D texture = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
texture = new Texture2D(GraphicsDevice, width, height);
texture.SetData(data);
});
return texture; return texture;
} }
@@ -58,7 +58,6 @@ namespace Barotrauma
private bool isSelecting; private bool isSelecting;
private string selectedText = string.Empty; private string selectedText = string.Empty;
private string clipboard = string.Empty;
private int selectedCharacters; private int selectedCharacters;
private int selectionStartIndex; private int selectionStartIndex;
private int selectionEndIndex; private int selectionEndIndex;
@@ -777,11 +776,7 @@ namespace Barotrauma
private void CopySelectedText() private void CopySelectedText()
{ {
#if WINDOWS Clipboard.SetText(selectedText);
System.Windows.Clipboard.SetText(selectedText);
#else
clipboard = selectedText;
#endif
} }
private void ClearSelection() private void ClearSelection()
@@ -795,11 +790,8 @@ namespace Barotrauma
private string GetCopiedText() private string GetCopiedText()
{ {
string t; string t;
#if WINDOWS t = Clipboard.GetText();
t = System.Windows.Clipboard.GetText();
#else
t = clipboard;
#endif
return t; return t;
} }
@@ -18,8 +18,11 @@ namespace Barotrauma
{ {
if (_whitePixelTexture == null) if (_whitePixelTexture == null)
{ {
_whitePixelTexture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color); CrossThread.RequestExecutionOnMainThread(() =>
_whitePixelTexture.SetData(new[] { Color.White }); {
_whitePixelTexture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
_whitePixelTexture.SetData(new[] { Color.White });
});
} }
return _whitePixelTexture; return _whitePixelTexture;
+23 -5
View File
@@ -48,6 +48,8 @@ namespace Barotrauma
public static Sounds.SoundManager SoundManager; public static Sounds.SoundManager SoundManager;
public static Thread MainThread { get; private set; }
public static HashSet<ContentPackage> SelectedPackages public static HashSet<ContentPackage> SelectedPackages
{ {
get { return Config?.SelectedContentPackages; } get { return Config?.SelectedContentPackages; }
@@ -183,6 +185,8 @@ namespace Barotrauma
FarseerPhysics.Settings.ContinuousPhysics = false; FarseerPhysics.Settings.ContinuousPhysics = false;
FarseerPhysics.Settings.VelocityIterations = 1; FarseerPhysics.Settings.VelocityIterations = 1;
FarseerPhysics.Settings.PositionIterations = 1; FarseerPhysics.Settings.PositionIterations = 1;
MainThread = Thread.CurrentThread;
} }
public void ApplyGraphicsSettings() public void ApplyGraphicsSettings()
@@ -220,8 +224,20 @@ namespace Barotrauma
GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth; GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth;
GraphicsDeviceManager.PreferredBackBufferHeight = GraphicsHeight; GraphicsDeviceManager.PreferredBackBufferHeight = GraphicsHeight;
GraphicsDeviceManager.ApplyChanges(); GraphicsDeviceManager.ApplyChanges();
if (windowMode == WindowMode.BorderlessWindowed)
{
GraphicsWidth = GraphicsDevice.PresentationParameters.Bounds.Width;
GraphicsHeight = GraphicsDevice.PresentationParameters.Bounds.Height;
GraphicsDevice.Viewport = new Viewport(0,0,GraphicsWidth,GraphicsHeight);
GraphicsDevice.ScissorRectangle = new Rectangle(0,0,GraphicsWidth,GraphicsHeight);
GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth;
GraphicsDeviceManager.PreferredBackBufferHeight = GraphicsHeight;
GraphicsDeviceManager.ApplyChanges();
}
} }
public void ResetViewPort() public void ResetViewPort()
@@ -272,10 +288,9 @@ namespace Barotrauma
WaitForLanguageSelection = Config.ShowLanguageSelectionPrompt WaitForLanguageSelection = Config.ShowLanguageSelectionPrompt
}; };
bool canLoadInSeparateThread = false; bool canLoadInSeparateThread = true;
#if WINDOWS
canLoadInSeparateThread = true; ApplyGraphicsSettings();
#endif
loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread); loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread);
} }
@@ -578,6 +593,9 @@ namespace Barotrauma
//otherwise it snowballs and becomes unplayable //otherwise it snowballs and becomes unplayable
Timing.Accumulator = Timing.Step; Timing.Accumulator = Timing.Step;
} }
CrossThread.ProcessTasks();
PlayerInput.UpdateVariable(); PlayerInput.UpdateVariable();
bool paused = true; bool paused = true;
@@ -20,8 +20,8 @@ namespace Barotrauma
/// </summary> /// </summary>
const float CharacterWaitOnSwitch = 10.0f; const float CharacterWaitOnSwitch = 10.0f;
private List<CharacterInfo> characterInfos = new List<CharacterInfo>(); private readonly List<CharacterInfo> characterInfos = new List<CharacterInfo>();
private List<Character> characters = new List<Character>(); private readonly List<Character> characters = new List<Character>();
private Point screenResolution; private Point screenResolution;
@@ -1318,7 +1318,7 @@ namespace Barotrauma
GUIComponent existingPreview = crewFrame.FindChild("SelectedCharacter"); GUIComponent existingPreview = crewFrame.FindChild("SelectedCharacter");
if (existingPreview != null) crewFrame.RemoveChild(existingPreview); if (existingPreview != null) crewFrame.RemoveChild(existingPreview);
var previewPlayer = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.8f), crewFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.05f, 0.0f) }, style: "InnerFrame") var previewPlayer = new GUIFrame(new RectTransform(new Vector2(0.45f, 0.9f), crewFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.05f, 0.0f) }, style: "InnerFrame")
{ {
UserData = "SelectedCharacter" UserData = "SelectedCharacter"
}; };
@@ -186,46 +186,39 @@ namespace Barotrauma
var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.TopRight) var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.TopRight)
{ RelativeOffset = new Vector2(0.025f, 0.02f) }) { RelativeOffset = new Vector2(0.025f, 0.02f) })
{ RelativeSpacing = 0.01f }; { RelativeSpacing = 0.01f };
var supportedDisplayModes = new List<DisplayMode>();
foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
{
if (supportedDisplayModes.Any(m => m.Width == mode.Width && m.Height == mode.Height)) { continue; }
#if OSX
// Monogame currently doesn't support retina displays
// so we need to disable resolutions above the viewport size.
// In a bundled .app you just disable HiDPI in the info.plist
// but that's probably not gonna happen.
if (mode.Width > GameMain.Instance.GraphicsDevice.DisplayMode.Width || mode.Height > GameMain.Instance.GraphicsDevice.DisplayMode.Height) { continue; }
#endif
supportedDisplayModes.Add(mode);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("Resolution")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("Resolution"));
var resolutionDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), elementCount: supportedDisplayModes.Count) var resolutionDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform))
{ {
OnSelected = SelectResolution, OnSelected = SelectResolution,
#if !LINUX ButtonEnabled = GameMain.Config.WindowMode != WindowMode.BorderlessWindowed
ButtonEnabled = GameMain.Config.WindowMode == WindowMode.Windowed };
#endif
};
foreach (DisplayMode mode in supportedDisplayModes)
{
if (mode.Width < MinSupportedResolution.X || mode.Height < MinSupportedResolution.Y) { continue; }
resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode);
if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode);
}
if (resolutionDD.SelectedItemData == null)
{
resolutionDD.SelectItem(GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.Last());
}
var supportedDisplayModes = UpdateResolutionDD(resolutionDD);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("DisplayMode")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("DisplayMode"));
var displayModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform)); var displayModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform));
displayModeDD.OnSelected = (guiComponent, obj) =>
{
UnsavedSettings = true;
GameMain.Config.WindowMode = (WindowMode)guiComponent.UserData;
supportedDisplayModes = UpdateResolutionDD(resolutionDD);
resolutionDD.ButtonEnabled = GameMain.Config.WindowMode != WindowMode.BorderlessWindowed;
GameMain.Instance.ApplyGraphicsSettings();
if (GameMain.Config.WindowMode == WindowMode.BorderlessWindowed)
{
GraphicsWidth = GameMain.GraphicsWidth;
GraphicsHeight = GameMain.GraphicsHeight;
var displayMode = supportedDisplayModes.Find(m => m.Width == GameMain.GraphicsWidth && m.Height == GameMain.GraphicsHeight);
if (displayMode != null)
{
resolutionDD.SelectItem(displayMode);
}
}
return true;
};
displayModeDD.AddItem(TextManager.Get("Fullscreen"), WindowMode.Fullscreen); displayModeDD.AddItem(TextManager.Get("Fullscreen"), WindowMode.Fullscreen);
displayModeDD.AddItem(TextManager.Get("Windowed"), WindowMode.Windowed); displayModeDD.AddItem(TextManager.Get("Windowed"), WindowMode.Windowed);
#if (!OSX) #if (!OSX)
@@ -242,15 +235,6 @@ namespace Barotrauma
displayModeDD.SelectItem(GameMain.Config.WindowMode); displayModeDD.SelectItem(GameMain.Config.WindowMode);
} }
#endif #endif
displayModeDD.OnSelected = (guiComponent, obj) =>
{
UnsavedSettings = true;
GameMain.Config.WindowMode = (WindowMode)guiComponent.UserData;
#if !LINUX
resolutionDD.ButtonEnabled = GameMain.Config.WindowMode == WindowMode.Windowed;
#endif
return true;
};
GUITickBox vsyncTickBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("EnableVSync")) GUITickBox vsyncTickBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("EnableVSync"))
{ {
@@ -811,6 +795,62 @@ namespace Barotrauma
SelectTab(selectedTab); SelectTab(selectedTab);
} }
private List<DisplayMode> UpdateResolutionDD(GUIDropDown resolutionDD)
{
var supportedDisplayModes = new List<DisplayMode>();
foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
{
if (supportedDisplayModes.Any(m => m.Width == mode.Width && m.Height == mode.Height)) { continue; }
#if OSX
// Monogame currently doesn't support retina displays
// so we need to disable resolutions above the viewport size.
// In a bundled .app you just disable HiDPI in the info.plist
// but that's probably not gonna happen.
if (mode.Width > GameMain.Instance.GraphicsDevice.DisplayMode.Width || mode.Height > GameMain.Instance.GraphicsDevice.DisplayMode.Height) { continue; }
#endif
supportedDisplayModes.Add(mode);
}
supportedDisplayModes.Sort((a, b) =>
{
if (a.Width < b.Width)
{
return -1;
}
if (a.Width > b.Width)
{
return 1;
}
if (a.Height < b.Height)
{
return -1;
}
if (a.Height > b.Height)
{
return 1;
}
return 0;
});
resolutionDD.ClearChildren();
foreach (DisplayMode mode in supportedDisplayModes)
{
if (mode.Width < MinSupportedResolution.X || mode.Height < MinSupportedResolution.Y) { continue; }
resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode);
if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode);
}
if (resolutionDD.SelectedItemData == null)
{
resolutionDD.SelectItem(GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.Last());
}
resolutionDD.ListBox.RectTransform.Resize(new Point(resolutionDD.Rect.Width, resolutionDD.Rect.Height * MathHelper.Clamp(supportedDisplayModes.Count, 2, 10)));
return supportedDisplayModes;
}
private string TrimAudioDeviceName(string name) private string TrimAudioDeviceName(string name)
{ {
if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } if (string.IsNullOrWhiteSpace(name)) { return string.Empty; }
@@ -1024,12 +1064,12 @@ namespace Barotrauma
SettingsFrame.Flash(Color.Green); SettingsFrame.Flash(Color.Green);
if (GameMain.WindowMode != GameMain.Config.WindowMode) if (GameMain.WindowMode != GameMain.Config.WindowMode || GameMain.Config.GraphicsWidth != GameMain.GraphicsWidth || GameMain.Config.GraphicsHeight != GameMain.GraphicsHeight)
{ {
GameMain.Instance.ApplyGraphicsSettings(); GameMain.Instance.ApplyGraphicsSettings();
} }
if (GameMain.GraphicsWidth != GameMain.Config.GraphicsWidth || GameMain.GraphicsHeight != GameMain.Config.GraphicsHeight) /*if (GameMain.GraphicsWidth != GameMain.Config.GraphicsWidth || GameMain.GraphicsHeight != GameMain.Config.GraphicsHeight)
{ {
#if OSX #if OSX
if (GameMain.Config.WindowMode != WindowMode.BorderlessWindowed) if (GameMain.Config.WindowMode != WindowMode.BorderlessWindowed)
@@ -1039,7 +1079,7 @@ namespace Barotrauma
#if OSX #if OSX
} }
#endif #endif
} }*/
} }
private bool ApplyClicked(GUIButton button, object userData) private bool ApplyClicked(GUIButton button, object userData)
@@ -290,6 +290,8 @@ namespace Barotrauma.Items.Components
private void DrawGraph(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawGraph(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
if (item.Removed) { return; }
Rectangle graphArea = new Rectangle(container.Rect.X + 30, container.Rect.Y, container.Rect.Width - 30, container.Rect.Height); Rectangle graphArea = new Rectangle(container.Rect.X + 30, container.Rect.Y, container.Rect.Width - 30, container.Rect.Height);
float maxLoad = loadGraph.Max(); float maxLoad = loadGraph.Max();
@@ -359,6 +361,8 @@ namespace Barotrauma.Items.Components
private void DrawFissionRateMeter(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawFissionRateMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
if (item.Removed) { return; }
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End(); spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = container.Rect; spriteBatch.GraphicsDevice.ScissorRectangle = container.Rect;
@@ -384,6 +388,8 @@ namespace Barotrauma.Items.Components
private void DrawTurbineOutputMeter(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawTurbineOutputMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
if (item.Removed) { return; }
DrawMeter(spriteBatch, container.Rect, DrawMeter(spriteBatch, container.Rect,
turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), optimalTurbineOutput, allowedTurbineOutput); turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), optimalTurbineOutput, allowedTurbineOutput);
} }
@@ -514,7 +520,7 @@ namespace Barotrauma.Items.Components
Vector2 newPoint = new Vector2(currX, rect.Bottom - graph[i] * yScale); Vector2 newPoint = new Vector2(currX, rect.Bottom - graph[i] * yScale);
if (graphLine == null) if (graphLine?.Texture == null)
{ {
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color); GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color);
} }
@@ -530,7 +536,7 @@ namespace Barotrauma.Items.Components
Vector2 lastPoint = new Vector2(rect.X, Vector2 lastPoint = new Vector2(rect.X,
rect.Bottom - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale); rect.Bottom - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale);
if (graphLine == null) if (graphLine?.Texture == null)
{ {
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color); GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color);
} }
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
{ {
ProgressGetter = () => ProgressGetter = () =>
{ {
return charge / capacity; return capacity <= 0.0f ? 1.0f : charge / capacity;
} }
}; };
} }
@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
{ {
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
if (equipper == null) if (equipper == null || equipper.Removed)
{ {
IsActive = false; IsActive = false;
return; return;
@@ -620,6 +620,7 @@ namespace Barotrauma
{ {
if (DraggingItemToWorld && if (DraggingItemToWorld &&
Character.Controlled.FocusedItem?.OwnInventory != null && Character.Controlled.FocusedItem?.OwnInventory != null &&
Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled)) Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{ {
GUI.PlayUISound(GUISoundType.PickItem); GUI.PlayUISound(GUISoundType.PickItem);
@@ -451,7 +451,7 @@ namespace Barotrauma
public override void UpdateEditing(Camera cam) public override void UpdateEditing(Camera cam)
{ {
if (editingHUD == null || editingHUD.UserData as Item != this) if (editingHUD == null || editingHUD.UserData == null)
{ {
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen); editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
} }
@@ -82,7 +82,7 @@ namespace Barotrauma
center.Y -= center.Y % Submarine.GridSize.Y; center.Y -= center.Y % Submarine.GridSize.Y;
MapEntity.SelectedList.Clear(); MapEntity.SelectedList.Clear();
MapEntity.SelectedList.AddRange(assemblyEntities); assemblyEntities.ForEach(e => MapEntity.AddSelection(e));
foreach (MapEntity mapEntity in assemblyEntities) foreach (MapEntity mapEntity in assemblyEntities)
{ {
@@ -142,24 +142,27 @@ namespace Barotrauma
backgroundPos.Y = -backgroundPos.Y; backgroundPos.Y = -backgroundPos.Y;
backgroundPos *= 0.05f; backgroundPos *= 0.05f;
if (level.GenerationParams.BackgroundTopSprite != null)
if (backgroundPos.Y < 1024)
{ {
if (backgroundPos.Y < 0 && level.GenerationParams.BackgroundTopSprite != null) int backgroundSize = (int)level.GenerationParams.BackgroundTopSprite.size.Y;
if (backgroundPos.Y < backgroundSize)
{ {
var backgroundTop = level.GenerationParams.BackgroundTopSprite; if (backgroundPos.Y < 0)
backgroundTop.SourceRect = new Rectangle((int)backgroundPos.X, (int)backgroundPos.Y, 1024, (int)Math.Min(-backgroundPos.Y, 1024)); {
backgroundTop.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, Math.Min(-backgroundPos.Y, GameMain.GraphicsHeight)), var backgroundTop = level.GenerationParams.BackgroundTopSprite;
color: level.BackgroundTextureColor); backgroundTop.SourceRect = new Rectangle((int)backgroundPos.X, (int)backgroundPos.Y, backgroundSize, (int)Math.Min(-backgroundPos.Y, backgroundSize));
} backgroundTop.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, Math.Min(-backgroundPos.Y, GameMain.GraphicsHeight)),
if (backgroundPos.Y > -1024 && level.GenerationParams.BackgroundSprite != null) color: level.BackgroundTextureColor);
{ }
var background = level.GenerationParams.BackgroundSprite; if (-backgroundPos.Y < GameMain.GraphicsHeight && level.GenerationParams.BackgroundSprite != null)
background.SourceRect = new Rectangle((int)backgroundPos.X, (int)Math.Max(backgroundPos.Y, 0), 1024, 1024); {
background.DrawTiled(spriteBatch, var background = level.GenerationParams.BackgroundSprite;
(backgroundPos.Y < 0) ? new Vector2(0.0f, (int)-backgroundPos.Y) : Vector2.Zero, background.SourceRect = new Rectangle((int)backgroundPos.X, (int)Math.Max(backgroundPos.Y, 0), backgroundSize, backgroundSize);
new Vector2(GameMain.GraphicsWidth, (int)Math.Ceiling(1024 - backgroundPos.Y)), background.DrawTiled(spriteBatch,
color: level.BackgroundTextureColor); (backgroundPos.Y < 0) ? new Vector2(0.0f, (int)-backgroundPos.Y) : Vector2.Zero,
new Vector2(GameMain.GraphicsWidth, (int)Math.Min(Math.Ceiling(backgroundSize - backgroundPos.Y), backgroundSize)),
color: level.BackgroundTextureColor);
}
} }
} }
@@ -81,29 +81,33 @@ namespace Barotrauma.Lights
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png", preMultiplyAlpha: false); visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png", preMultiplyAlpha: false);
highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png", preMultiplyAlpha: false); highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png", preMultiplyAlpha: false);
CreateRenderTargets(graphics);
GameMain.Instance.OnResolutionChanged += () => GameMain.Instance.OnResolutionChanged += () =>
{ {
CreateRenderTargets(graphics); CreateRenderTargets(graphics);
}; };
CrossThread.RequestExecutionOnMainThread(() =>
{
CreateRenderTargets(graphics);
#if WINDOWS #if WINDOWS
LosEffect = content.Load<Effect>("Effects/losshader"); LosEffect = content.Load<Effect>("Effects/losshader");
SolidColorEffect = content.Load<Effect>("Effects/solidcolor"); SolidColorEffect = content.Load<Effect>("Effects/solidcolor");
#else #else
LosEffect = content.Load<Effect>("Effects/losshader_opengl"); LosEffect = content.Load<Effect>("Effects/losshader_opengl");
SolidColorEffect = content.Load<Effect>("Effects/solidcolor_opengl"); SolidColorEffect = content.Load<Effect>("Effects/solidcolor_opengl");
#endif #endif
if (lightEffect == null) if (lightEffect == null)
{
lightEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
{ {
VertexColorEnabled = true, lightEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
TextureEnabled = true, {
Texture = LightSource.LightTexture VertexColorEnabled = true,
}; TextureEnabled = true,
} Texture = LightSource.LightTexture
};
}
});
hullAmbientLights = new Dictionary<Hull, Color>(); hullAmbientLights = new Dictionary<Hull, Color>();
smoothedHullAmbientLights = new Dictionary<Hull, Color>(); smoothedHullAmbientLights = new Dictionary<Hull, Color>();
@@ -137,8 +137,11 @@ namespace Barotrauma
{ {
if (noiseTexture == null) if (noiseTexture == null)
{ {
noiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution); CrossThread.RequestExecutionOnMainThread(() =>
rawNoiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution); {
noiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution);
rawNoiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution);
});
rawNoiseSprite = new Sprite(rawNoiseTexture, null, null); rawNoiseSprite = new Sprite(rawNoiseTexture, null, null);
} }
@@ -221,8 +224,11 @@ namespace Barotrauma
Color.Lerp(noiseTextureData[i], pathColor, crackTextureData[i].A / 255.0f * 0.5f); Color.Lerp(noiseTextureData[i], pathColor, crackTextureData[i].A / 255.0f * 0.5f);
} }
noiseTexture.SetData(noiseTextureData); CrossThread.RequestExecutionOnMainThread(() =>
rawNoiseTexture.SetData(rawNoiseTextureData); {
noiseTexture.SetData(noiseTextureData);
rawNoiseTexture.SetData(rawNoiseTextureData);
});
} }
private void LocationChanged(Location prevLocation, Location newLocation) private void LocationChanged(Location prevLocation, Location newLocation)
@@ -367,14 +367,19 @@ namespace Barotrauma
foreach (MapEntity e in newSelection) foreach (MapEntity e in newSelection)
{ {
if (selectedList.Contains(e)) if (selectedList.Contains(e))
selectedList.Remove(e); {
RemoveSelection(e);
}
else else
selectedList.Add(e); {
AddSelection(e);
}
} }
} }
else else
{ {
selectedList = newSelection; selectedList.Clear();
newSelection.ForEach(e => AddSelection(e));
} }
//select wire if both items it's connected to are selected //select wire if both items it's connected to are selected
@@ -457,9 +462,13 @@ namespace Barotrauma
PlayerInput.KeyDown(Keys.RightControl)) PlayerInput.KeyDown(Keys.RightControl))
{ {
if (selectedList.Contains(entity)) if (selectedList.Contains(entity))
selectedList.Remove(entity); {
RemoveSelection(entity);
}
else else
selectedList.Add(entity); {
AddSelection(entity);
}
} }
else else
{ {
@@ -469,6 +478,60 @@ namespace Barotrauma
return true; return true;
}; };
} }
public static void AddSelection(MapEntity entity)
{
if (selectedList.Contains(entity)) { return; }
selectedList.Add(entity);
HandleDoorGapLinks(entity,
onGapFound: (door, gap) =>
{
door.RefreshLinkedGap();
if (!selectedList.Contains(gap))
{
selectedList.Add(gap);
}
},
onDoorFound: (door, gap) =>
{
if (!selectedList.Contains(door.Item))
{
selectedList.Add(door.Item);
}
});
}
private static void HandleDoorGapLinks(MapEntity entity, Action<Door, Gap> onGapFound, Action<Door, Gap> onDoorFound)
{
if (entity is Item i)
{
var door = i.GetComponent<Door>();
if (door != null)
{
var gap = door.LinkedGap;
if (gap != null)
{
onGapFound(door, gap);
}
}
}
else if (entity is Gap gap)
{
var door = gap.ConnectedDoor;
if (door != null)
{
onDoorFound(door, gap);
}
}
}
public static void RemoveSelection(MapEntity entity)
{
selectedList.Remove(entity);
HandleDoorGapLinks(entity,
onGapFound: (door, gap) => selectedList.Remove(gap),
onDoorFound: (door, gap) => selectedList.Remove(door.Item));
}
static partial void UpdateAllProjSpecific(float deltaTime) static partial void UpdateAllProjSpecific(float deltaTime)
{ {
@@ -533,13 +596,15 @@ namespace Barotrauma
} }
} }
public static List<MapEntity> FilteredSelectedList { get; private set; } = new List<MapEntity>();
public static void UpdateEditor(Camera cam) public static void UpdateEditor(Camera cam)
{ {
if (highlightedListBox != null) highlightedListBox.UpdateManually((float)Timing.Step); if (highlightedListBox != null) highlightedListBox.UpdateManually((float)Timing.Step);
if (editingHUD != null) if (editingHUD != null)
{ {
if (selectedList.Count == 0 || editingHUD.UserData != selectedList[0]) if (FilteredSelectedList.Count == 0 || editingHUD.UserData != FilteredSelectedList[0])
{ {
foreach (GUIComponent component in editingHUD.Children) foreach (GUIComponent component in editingHUD.Children)
{ {
@@ -550,15 +615,20 @@ namespace Barotrauma
editingHUD = null; editingHUD = null;
} }
} }
FilteredSelectedList.Clear();
if (selectedList.Count == 0) return; if (selectedList.Count == 0) return;
foreach (var e in selectedList)
if (selectedList.Count == 1)
{ {
selectedList[0].UpdateEditing(cam); if (e is Gap) { continue; }
if (selectedList[0].ResizeHorizontal || selectedList[0].ResizeVertical) FilteredSelectedList.Add(e);
}
var first = FilteredSelectedList.FirstOrDefault();
if (first != null)
{
first.UpdateEditing(cam);
if (first.ResizeHorizontal || first.ResizeVertical)
{ {
selectedList[0].UpdateResizing(cam); first.UpdateResizing(cam);
} }
} }
@@ -622,12 +692,10 @@ namespace Barotrauma
selectedList.Clear(); selectedList.Clear();
} }
public static void SelectEntity(MapEntity entity) public static void SelectEntity(MapEntity entity)
{ {
DeselectAll(); DeselectAll();
AddSelection(entity);
selectedList.Add(entity);
} }
/// <summary> /// <summary>
@@ -30,43 +30,14 @@ namespace Barotrauma
return HasBody ? ShowWalls : ShowStructures;; return HasBody ? ShowWalls : ShowStructures;;
} }
} }
// Only for testing in the debug build. Not saved.
protected Vector2 textureScale = Vector2.One;
[Editable(DecimalCount = 3, MinValueFloat = 0.01f, MaxValueFloat = 10f, ValueStep = 0.1f), Serialize("1.0, 1.0", false)]
public Vector2 TextureScale
{
get { return textureScale; }
set
{
textureScale = new Vector2(
MathHelper.Clamp(value.X, 0.01f, 10),
MathHelper.Clamp(value.Y, 0.01f, 10));
}
}
private string specialTag; private string specialTag;
[Editable, Serialize("", true)] [Editable, Serialize("", true)]
public string SpecialTag public string SpecialTag
{ {
get { return specialTag; } get { return specialTag; }
set { specialTag = value; } set { specialTag = value; }
} }
// Only for testing in the debug build. Not saved.
#if DEBUG
[Editable, Serialize(true, false)]
#endif
public bool DrawTiled { get; protected set; } = true;
protected Vector2 textureOffset = Vector2.Zero;
[Editable(MinValueFloat = -1000f, MaxValueFloat = 1000f, ValueStep = 10f), Serialize("0.0, 0.0", true)]
public Vector2 TextureOffset
{
get { return textureOffset; }
set { textureOffset = value; }
}
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
@@ -1,6 +1,9 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -39,7 +42,19 @@ namespace Barotrauma
int iconX = iconIndices[(int)spawnType] * IconSize % iconTexture.Width; int iconX = iconIndices[(int)spawnType] * IconSize % iconTexture.Width;
int iconY = (int)(Math.Floor(iconIndices[(int)spawnType] * IconSize / (float)iconTexture.Width)) * IconSize; int iconY = (int)(Math.Floor(iconIndices[(int)spawnType] * IconSize / (float)iconTexture.Width)) * IconSize;
int iconSize = ConnectedGap == null && Ladders == null ? IconSize : (int)(IconSize * 1.5f); int iconSize = IconSize;
if (ConnectedGap != null)
{
iconSize = (int)(iconSize * 1.5f);
}
if (Ladders != null)
{
iconSize = (int)(iconSize * 1.5f);
}
if (Stairs != null)
{
iconSize = (int)(iconSize * 1.5f);
}
spriteBatch.Draw(iconTexture, spriteBatch.Draw(iconTexture,
new Rectangle((int)(drawPos.X - iconSize / 2), (int)(drawPos.Y - iconSize / 2), iconSize, iconSize), new Rectangle((int)(drawPos.X - iconSize / 2), (int)(drawPos.Y - iconSize / 2), iconSize, iconSize),
@@ -82,10 +97,49 @@ namespace Barotrauma
editingHUD = CreateEditingHUD(); editingHUD = CreateEditingHUD();
} }
if (PlayerInput.LeftButtonClicked()) if (IsSelected && PlayerInput.LeftButtonClicked())
{ {
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
// Update gaps, ladders, and stairs
UpdateLinkedEntity(position, Gap.GapList, gap => ConnectedGap = gap, gap =>
{
if (ConnectedGap == gap)
{
ConnectedGap = null;
}
});
UpdateLinkedEntity(position, Item.ItemList, i =>
{
var ladder = i?.GetComponent<Ladder>();
if (ladder != null)
{
Ladders = ladder;
}
}, i =>
{
var ladder = i?.GetComponent<Ladder>();
if (ladder != null)
{
if (Ladders == ladder)
{
Ladders = null;
}
}
}, inflate: 5);
// TODO: Cannot check the rectangle, since the rectangle is not rotated -> Need to use the collider.
//var stairList = mapEntityList.Where(me => me is Structure s && s.StairDirection != Direction.None).Select(me => me as Structure);
//UpdateLinkedEntity(position, stairList, s =>
//{
// Stairs = s;
//}, s =>
//{
// if (Stairs == s)
// {
// Stairs = null;
// }
//});
foreach (MapEntity e in mapEntityList) foreach (MapEntity e in mapEntityList)
{ {
if (e.GetType() != typeof(WayPoint)) continue; if (e.GetType() != typeof(WayPoint)) continue;
@@ -99,6 +153,23 @@ namespace Barotrauma
} }
} }
private void UpdateLinkedEntity<T>(Vector2 worldPos, IEnumerable<T> list, Action<T> match, Action<T> noMatch, int inflate = 0) where T : MapEntity
{
foreach (var entity in list)
{
var rect = entity.WorldRect;
rect.Inflate(inflate, inflate);
if (Submarine.RectContains(rect, worldPos))
{
match(entity);
}
else
{
noMatch(entity);
}
}
}
private bool ChangeSpawnType(GUIButton button, object obj) private bool ChangeSpawnType(GUIButton button, object obj)
{ {
GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock; GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
@@ -119,7 +119,7 @@ namespace Barotrauma.Media
/*using (var stream = File.OpenRead(filename)) /*using (var stream = File.OpenRead(filename))
{*/ {*/
byte[] filenameBytes = Encoding.UTF8.GetBytes(filename); byte[] filenameBytes = Encoding.UTF8.GetBytes(filename + "\0");
byte[] filenameBytesNullTerminated = new byte[filenameBytes.Length + 1]; byte[] filenameBytesNullTerminated = new byte[filenameBytes.Length + 1];
filenameBytesNullTerminated[filenameBytes.Length] = 0; filenameBytesNullTerminated[filenameBytes.Length] = 0;
Array.Copy(filenameBytes, filenameBytesNullTerminated, filenameBytes.Length); Array.Copy(filenameBytes, filenameBytesNullTerminated, filenameBytes.Length);
@@ -131,7 +131,10 @@ namespace Barotrauma.Media
//LibVlcWrapper.LibVlcMethods.libvlc_media_release(media); //LibVlcWrapper.LibVlcMethods.libvlc_media_release(media);
//LibVlcWrapper.LibVlcMethods.libvlc_video_get_size(mediaPlayer, 0, out width, out height); //LibVlcWrapper.LibVlcMethods.libvlc_video_get_size(mediaPlayer, 0, out width, out height);
texture = new Texture2D(graphicsDevice, (int)width, (int)height); CrossThread.RequestExecutionOnMainThread(() =>
{
texture = new Texture2D(graphicsDevice, (int)width, (int)height);
});
Width = width; Height = height; Width = width; Height = height;
unmanagedData = Marshal.AllocHGlobal(sizeof(int)*2+(int)(width*height*3)); unmanagedData = Marshal.AllocHGlobal(sizeof(int)*2+(int)(width*height*3));
@@ -147,7 +150,7 @@ namespace Barotrauma.Media
IntPtr videoUnlockDelegatePtr = Marshal.GetFunctionPointerForDelegate(VideoUnlockDelegate); IntPtr videoUnlockDelegatePtr = Marshal.GetFunctionPointerForDelegate(VideoUnlockDelegate);
IntPtr videoDisplayDelegatePtr = Marshal.GetFunctionPointerForDelegate(VideoDisplayDelegate); IntPtr videoDisplayDelegatePtr = Marshal.GetFunctionPointerForDelegate(VideoDisplayDelegate);
LibVlcWrapper.LibVlcMethods.libvlc_video_set_callbacks(mediaPlayer, videoLockDelegatePtr, videoUnlockDelegatePtr, videoDisplayDelegatePtr, unmanagedData); LibVlcWrapper.LibVlcMethods.libvlc_video_set_callbacks(mediaPlayer, videoLockDelegatePtr, videoUnlockDelegatePtr, videoDisplayDelegatePtr, unmanagedData);
LibVlcWrapper.LibVlcMethods.libvlc_video_set_format(mediaPlayer, Encoding.UTF8.GetBytes("RV24"), (int)width, (int)height, (int)width * 3); LibVlcWrapper.LibVlcMethods.libvlc_video_set_format(mediaPlayer, Encoding.UTF8.GetBytes("RV24\0"), (int)width, (int)height, (int)width * 3);
IntPtr audioPlayDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioPlayDelegate); IntPtr audioPlayDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioPlayDelegate);
IntPtr audioPauseDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioPauseDelegate); IntPtr audioPauseDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioPauseDelegate);
@@ -156,7 +159,7 @@ namespace Barotrauma.Media
IntPtr audioDrainDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioDrainDelegate); IntPtr audioDrainDelegatePtr = Marshal.GetFunctionPointerForDelegate(AudioDrainDelegate);
LibVlcWrapper.LibVlcMethods.libvlc_audio_set_callbacks(mediaPlayer, audioPlayDelegatePtr, audioPauseDelegatePtr, audioResumeDelegatePtr, audioFlushDelegatePtr, audioDrainDelegatePtr, unmanagedData); LibVlcWrapper.LibVlcMethods.libvlc_audio_set_callbacks(mediaPlayer, audioPlayDelegatePtr, audioPauseDelegatePtr, audioResumeDelegatePtr, audioFlushDelegatePtr, audioDrainDelegatePtr, unmanagedData);
LibVlcWrapper.LibVlcMethods.libvlc_audio_set_format(mediaPlayer, Encoding.UTF8.GetBytes("S16N"), 44100, 2); LibVlcWrapper.LibVlcMethods.libvlc_audio_set_format(mediaPlayer, Encoding.UTF8.GetBytes("S16N\0"), 44100, 2);
LibVlcWrapper.LibVlcMethods.libvlc_audio_set_delay(mediaPlayer, 0); LibVlcWrapper.LibVlcMethods.libvlc_audio_set_delay(mediaPlayer, 0);
@@ -216,7 +219,10 @@ namespace Barotrauma.Media
(byte)255); (byte)255);
} }
} }
texture.SetData(colors); CrossThread.RequestExecutionOnMainThread(() =>
{
texture.SetData(colors);
});
arr[0] = 0; arr[0] = 0;
Marshal.Copy(arr, 0, changedPtr, 1); Marshal.Copy(arr, 0, changedPtr, 1);
} }
@@ -154,8 +154,8 @@ namespace Barotrauma.Networking
Dispose(true); Dispose(true);
} }
} }
const int MaxFileSize = 1000000; const int MaxFileSize = 50000000; //50 MB
public delegate void TransferInDelegate(FileTransferIn fileStreamReceiver); public delegate void TransferInDelegate(FileTransferIn fileStreamReceiver);
public TransferInDelegate OnFinished; public TransferInDelegate OnFinished;
@@ -1170,6 +1170,7 @@ namespace Barotrauma.Networking
if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null);
ServerSettings.ServerDetailsChanged = true;
gameStarted = true; gameStarted = true;
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
@@ -1189,6 +1190,8 @@ namespace Barotrauma.Networking
if (GameMain.GameSession != null) { GameMain.GameSession.GameMode.End(endMessage); } if (GameMain.GameSession != null) { GameMain.GameSession.GameMode.End(endMessage); }
ServerSettings.ServerDetailsChanged = true;
gameStarted = false; gameStarted = false;
Character.Controlled = null; Character.Controlled = null;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
@@ -1884,17 +1887,11 @@ namespace Barotrauma.Networking
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
} }
public bool VoteForKick(GUIButton button, object userdata) public void VoteForKick(Client votedClient)
{ {
var votedClient = userdata is Client ? (Client)userdata : otherClients.Find(c => c.Character == userdata); if (votedClient == null) { return; }
if (votedClient == null) return false;
votedClient.AddKickVote(ConnectedClients.First(c => c.ID == ID)); votedClient.AddKickVote(ConnectedClients.First(c => c.ID == ID));
Vote(VoteType.Kick, votedClient); Vote(VoteType.Kick, votedClient);
button.Enabled = false;
return true;
} }
public override void AddChatMessage(ChatMessage message) public override void AddChatMessage(ChatMessage message)
@@ -2047,13 +2044,15 @@ namespace Barotrauma.Networking
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
} }
public void SetupNewCampaign(Submarine sub, string savePath, string mapSeed) public void SetupNewCampaign(Submarine sub, string saveName, string mapSeed)
{ {
saveName = Path.GetFileNameWithoutExtension(saveName);
NetOutgoingMessage msg = client.CreateMessage(); NetOutgoingMessage msg = client.CreateMessage();
msg.Write((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO); msg.Write((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
msg.Write(true); msg.WritePadBits(); msg.Write(true); msg.WritePadBits();
msg.Write(savePath); msg.Write(saveName);
msg.Write(mapSeed); msg.Write(mapSeed);
msg.Write(sub.Name); msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash); msg.Write(sub.MD5Hash.Hash);
@@ -2377,38 +2376,52 @@ namespace Barotrauma.Networking
public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame) public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame)
{ {
if (character == null) return false; if (character == null) { return false; }
if (character != myCharacter) if (character != myCharacter)
{ {
var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.Character == character); var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.Character == character);
if (client == null) return false; if (client == null) { return false; }
var mute = new GUITickBox(new RectTransform(new Vector2(0.95f, 0.1f), characterFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.1f) },
TextManager.Get("Mute"))
{
Selected = client.MutedLocally,
OnSelected = (tickBox) => { client.MutedLocally = tickBox.Selected; return true; }
};
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.1f), characterFrame.RectTransform, Anchor.BottomCenter), isHorizontal: true)
{
RelativeSpacing = 0.05f,
ChildAnchor = Anchor.CenterLeft,
Stretch = true
};
if (HasPermission(ClientPermissions.Ban)) if (HasPermission(ClientPermissions.Ban))
{ {
var banButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomRight), var banButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.9f), buttonContainer.RectTransform),
TextManager.Get("Ban")) TextManager.Get("Ban"))
{ {
UserData = character.Name, UserData = client,
OnClicked = GameMain.NetLobbyScreen.BanPlayer OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.BanPlayer(client); return false; }
}; };
} }
if (HasPermission(ClientPermissions.Kick)) if (HasPermission(ClientPermissions.Kick))
{ {
var kickButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomLeft), var kickButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.9f), buttonContainer.RectTransform),
TextManager.Get("Kick")) TextManager.Get("Kick"))
{ {
UserData = character.Name, UserData = client,
OnClicked = GameMain.NetLobbyScreen.KickPlayer OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.KickPlayer(client); return false; }
}; };
} }
else if (serverSettings.Voting.AllowVoteKick) else if (serverSettings.Voting.AllowVoteKick)
{ {
var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.16f) }, var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.9f), buttonContainer.RectTransform),
TextManager.Get("VoteToKick")) TextManager.Get("VoteToKick"))
{ {
UserData = character, UserData = client,
OnClicked = VoteForKick OnClicked = (btn, userdata) => { VoteForKick(client); btn.Enabled = false; return true; }
}; };
if (GameMain.NetworkMember.ConnectedClients != null) if (GameMain.NetworkMember.ConnectedClients != null)
{ {
@@ -214,6 +214,11 @@ namespace Barotrauma.Steam
if (s.Rules.ContainsKey("message")) serverInfo.ServerMessage = s.Rules["message"]; if (s.Rules.ContainsKey("message")) serverInfo.ServerMessage = s.Rules["message"];
if (s.Rules.ContainsKey("version")) serverInfo.GameVersion = s.Rules["version"]; if (s.Rules.ContainsKey("version")) serverInfo.GameVersion = s.Rules["version"];
if (s.Rules.ContainsKey("playercount"))
{
if (int.TryParse(s.Rules["playercount"], out int playerCount)) serverInfo.PlayerCount = playerCount;
}
if (s.Rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(s.Rules["contentpackage"].Split(',')); if (s.Rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(s.Rules["contentpackage"].Split(','));
if (s.Rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(s.Rules["contentpackagehash"].Split(',')); if (s.Rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(s.Rules["contentpackagehash"].Split(','));
if (s.Rules.ContainsKey("contentpackageurl")) serverInfo.ContentPackageWorkshopUrls.AddRange(s.Rules["contentpackageurl"].Split(',')); if (s.Rules.ContainsKey("contentpackageurl")) serverInfo.ContentPackageWorkshopUrls.AddRange(s.Rules["contentpackageurl"].Split(','));
@@ -235,6 +240,8 @@ namespace Barotrauma.Steam
if (Enum.TryParse(s.Rules["traitors"], out YesNoMaybe traitorsEnabled)) serverInfo.TraitorsEnabled = traitorsEnabled; if (Enum.TryParse(s.Rules["traitors"], out YesNoMaybe traitorsEnabled)) serverInfo.TraitorsEnabled = traitorsEnabled;
} }
if (s.Rules.ContainsKey("gamestarted")) serverInfo.GameStarted = s.Rules["gamestarted"] == "True";
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count || if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count) serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
{ {
@@ -252,7 +259,8 @@ namespace Barotrauma.Steam
private static bool ValidateServerInfo(ServerList.Server server) private static bool ValidateServerInfo(ServerList.Server server)
{ {
if (string.IsNullOrEmpty(server.Name)) { return false; } if (string.IsNullOrWhiteSpace(server.Name)) { return false; }
if (string.IsNullOrWhiteSpace(server.Name.Replace("\0", ""))) { return false; }
if (server.Address == null) { return false; } if (server.Address == null) { return false; }
return true; return true;
@@ -592,8 +600,15 @@ namespace Barotrauma.Steam
} }
SaveUtil.ClearFolder(WorkshopItemStagingFolder); SaveUtil.ClearFolder(WorkshopItemStagingFolder);
Directory.Delete(WorkshopItemStagingFolder);
File.Delete(PreviewImageName); File.Delete(PreviewImageName);
try
{
Directory.Delete(WorkshopItemStagingFolder);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to delete Workshop item staging folder.", e);
}
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
@@ -925,28 +940,50 @@ namespace Barotrauma.Steam
{ {
if (instance == null || !instance.isInitialized) { return false; } if (instance == null || !instance.isInitialized) { return false; }
bool itemsUpdated = false; bool? itemsUpdated = null;
foreach (ulong subscribedItemId in instance.client.Workshop.GetSubscribedItemIds()) bool timedOut = false;
var query = instance.client.Workshop.CreateQuery();
query.FileId = new List<ulong>(instance.client.Workshop.GetSubscribedItemIds());
query.UploaderAppId = AppID;
query.Run();
query.OnResult = (Workshop.Query q) =>
{ {
//TODO: fix this, GetItem doesn't query item.Modified if (timedOut) { return; }
var item = instance.client.Workshop.GetItem(subscribedItemId); itemsUpdated = false;
if (item.Installed && CheckWorkshopItemEnabled(item) && !CheckWorkshopItemUpToDate(item)) foreach (var item in q.Items)
{ {
if (!UpdateWorkshopItem(item, out string errorMsg)) if (item.Installed && CheckWorkshopItemEnabled(item) && !CheckWorkshopItemUpToDate(item))
{ {
DebugConsole.ThrowError(errorMsg); if (!UpdateWorkshopItem(item, out string errorMsg))
new GUIMessageBox( {
TextManager.Get("Error"), DebugConsole.ThrowError(errorMsg);
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, errorMsg })); new GUIMessageBox(
} TextManager.Get("Error"),
else TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, errorMsg }));
{ }
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", item.Title)); else
itemsUpdated = true; {
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", item.Title));
itemsUpdated = true;
}
} }
} }
};
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10);
while (!itemsUpdated.HasValue)
{
if (DateTime.Now > timeOut)
{
itemsUpdated = false;
timedOut = true;
break;
}
instance.client.Update();
System.Threading.Thread.Sleep(10);
} }
return itemsUpdated;
return itemsUpdated.Value;
} }
public static bool UpdateWorkshopItem(Workshop.Item item, out string errorMsg) public static bool UpdateWorkshopItem(Workshop.Item item, out string errorMsg)
@@ -61,6 +61,7 @@ namespace Barotrauma.Networking
localEnabled = box.Selected; localEnabled = box.Selected;
return true; return true;
} }
}; };
@@ -145,16 +146,12 @@ namespace Barotrauma.Networking
{ {
if (obj is WhiteListedPlayer) if (obj is WhiteListedPlayer)
{ {
WhiteListedPlayer wlp = obj as WhiteListedPlayer; if (!(obj is WhiteListedPlayer wlp)) return false;
if (wlp == null) return false;
if (!localRemoved.Contains(wlp.UniqueIdentifier)) localRemoved.Add(wlp.UniqueIdentifier); if (!localRemoved.Contains(wlp.UniqueIdentifier)) localRemoved.Add(wlp.UniqueIdentifier);
} }
else if (obj is LocalAdded) else if (obj is LocalAdded)
{ {
LocalAdded lad = obj as LocalAdded; if (!(obj is LocalAdded lad)) return false;
if (lad == null) return false;
if (localAdded.Contains(lad)) localAdded.Remove(lad); if (localAdded.Contains(lad)) localAdded.Remove(lad);
} }
@@ -210,7 +207,6 @@ namespace Barotrauma.Networking
{ {
ip = "IP concealed by host"; ip = "IP concealed by host";
} }
DebugConsole.NewMessage("nerd: " + name, Color.Lime);
whitelistedPlayers.Add(new WhiteListedPlayer(name, uniqueIdentifier, ip)); whitelistedPlayers.Add(new WhiteListedPlayer(name, uniqueIdentifier, ip));
} }
@@ -37,7 +37,6 @@ namespace Barotrauma
{ {
#endif #endif
game = new GameMain(); game = new GameMain();
//game.GraphicsDevice.PresentationParameters.IsFullScreen = false;
#if !DEBUG #if !DEBUG
} }
catch (Exception e) catch (Exception e)
@@ -437,42 +437,48 @@ namespace Barotrauma
if (characterPreviewFrame != null) if (characterPreviewFrame != null)
{ {
characterPreviewFrame.Parent.RemoveChild(characterPreviewFrame); characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null; characterPreviewFrame = null;
} }
if (Campaign is SinglePlayerCampaign) if (characterList != null)
{ {
var hireableCharacters = location.GetHireableCharacters(); if (Campaign is SinglePlayerCampaign)
foreach (GUIComponent child in characterList.Content.Children.ToList())
{ {
if (child.UserData is CharacterInfo character) var hireableCharacters = location.GetHireableCharacters();
foreach (GUIComponent child in characterList.Content.Children.ToList())
{ {
if (GameMain.GameSession.CrewManager.GetCharacterInfos().Contains(character)) { continue; } if (child.UserData is CharacterInfo character)
{
if (GameMain.GameSession.CrewManager != null)
{
if (GameMain.GameSession.CrewManager.GetCharacterInfos().Contains(character)) { continue; }
}
}
else if (child.UserData as string == "mycrew" || child.UserData as string == "hire")
{
continue;
}
characterList.RemoveChild(child);
} }
else if (child.UserData as string == "mycrew" || child.UserData as string == "hire") if (!hireableCharacters.Any())
{ {
continue; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), characterList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
} }
characterList.RemoveChild(child); else
}
if (!hireableCharacters.Any())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), characterList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{ {
CanBeFocused = false foreach (CharacterInfo c in hireableCharacters)
}; {
} var frame = c.CreateCharacterFrame(characterList.Content, c.Name + " (" + c.Job.Name + ")", c);
else new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.TopRight), c.Salary.ToString(), textAlignment: Alignment.CenterRight);
{ }
foreach (CharacterInfo c in hireableCharacters)
{
var frame = c.CreateCharacterFrame(characterList.Content, c.Name + " (" + c.Job.Name + ")", c);
new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.TopRight), c.Salary.ToString(), textAlignment: Alignment.CenterRight);
} }
} }
characterList.UpdateScrollBarSize();
} }
characterList.UpdateScrollBarSize();
RefreshMyItems(); RefreshMyItems();
@@ -14,7 +14,7 @@ namespace Barotrauma
{ {
class MainMenuScreen : Screen class MainMenuScreen : Screen
{ {
public enum Tab { NewGame = 1, LoadGame = 2, HostServer = 3, Settings = 4, Tutorials = 5, JoinServer = 6, CharacterEditor = 7, SubmarineEditor = 8, QuickStartDev = 9, SteamWorkshop = 10, Credits = 11 } public enum Tab { NewGame = 1, LoadGame = 2, HostServer = 3, Settings = 4, Tutorials = 5, JoinServer = 6, CharacterEditor = 7, SubmarineEditor = 8, QuickStartDev = 9, SteamWorkshop = 10, Credits = 11, Empty = 12 }
private GUIComponent buttonsParent; private GUIComponent buttonsParent;
@@ -37,7 +37,11 @@ namespace Barotrauma
private GUIComponent titleText; private GUIComponent titleText;
private CreditsPlayer creditsPlayer; private CreditsPlayer creditsPlayer;
#if OSX
private bool firstLoadOnMac = true;
#endif
#region Creation #region Creation
public MainMenuScreen(GameMain game) public MainMenuScreen(GameMain game)
{ {
@@ -360,9 +364,9 @@ namespace Barotrauma
OnClicked = SelectTab OnClicked = SelectTab
}; };
} }
#endregion #endregion
#region Selection #region Selection
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
@@ -378,6 +382,25 @@ namespace Barotrauma
ResetButtonStates(null); ResetButtonStates(null);
GameAnalyticsManager.SetCustomDimension01(""); GameAnalyticsManager.SetCustomDimension01("");
#if OSX
// Hack for adjusting the viewport properly after splash screens on older Macs
if (firstLoadOnMac)
{
firstLoadOnMac = false;
menuTabs[(int)Tab.Empty] = new GUIFrame(new RectTransform(new Vector2(1f, 1f), GUI.Canvas), "", Color.Transparent)
{
CanBeFocused = false
};
var emptyList = new GUIListBox(new RectTransform(new Vector2(0.0f, 0.0f), menuTabs[(int)Tab.Empty].RectTransform))
{
CanBeFocused = false
};
SelectTab(null, Tab.Empty);
}
#endif
} }
public override void Deselect() public override void Deselect()
@@ -701,7 +724,14 @@ namespace Barotrauma
GameMain.NetLobbyScreen = new NetLobbyScreen(); GameMain.NetLobbyScreen = new NetLobbyScreen();
try try
{ {
int ownerKey = Math.Max(CryptoRandom.Instance.Next(),1); string exeName = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.ServerExecutable)?.FirstOrDefault();
if (string.IsNullOrEmpty(exeName))
{
DebugConsole.ThrowError("No server executable defined in the selected content packages. Attempting to use the default executable...");
exeName = "DedicatedServer.exe";
}
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" + string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
" -port " + port.ToString() + " -port " + port.ToString() +
@@ -711,16 +741,15 @@ namespace Barotrauma
" -maxplayers " + maxPlayersBox.Text + " -maxplayers " + maxPlayersBox.Text +
" -ownerkey " + ownerKey.ToString(); " -ownerkey " + ownerKey.ToString();
string filename = "DedicatedServer.exe"; string filename = exeName;
#if LINUX || OSX #if LINUX || OSX
filename = "./DedicatedServer"; filename = "./" + Path.GetFileNameWithoutExtension(exeName);
#endif #endif
var processInfo = new ProcessStartInfo var processInfo = new ProcessStartInfo
{ {
FileName = filename, FileName = filename,
Arguments = arguments Arguments = arguments,
#if !DEBUG #if !DEBUG
,
WindowStyle = ProcessWindowStyle.Hidden WindowStyle = ProcessWindowStyle.Hidden
#endif #endif
}; };
@@ -1537,7 +1537,7 @@ namespace Barotrauma
{ {
UserData = selectedClient UserData = selectedClient
}; };
banButton.OnClicked += BanPlayer; banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; };
banButton.OnClicked += ClosePlayerFrame; banButton.OnClicked += ClosePlayerFrame;
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform), var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform),
@@ -1545,7 +1545,7 @@ namespace Barotrauma
{ {
UserData = selectedClient UserData = selectedClient
}; };
rangebanButton.OnClicked += BanPlayerRange; rangebanButton.OnClicked = (bt, userdata) => { BanPlayerRange(selectedClient); return true; };
rangebanButton.OnClicked += ClosePlayerFrame; rangebanButton.OnClicked += ClosePlayerFrame;
} }
@@ -1557,7 +1557,7 @@ namespace Barotrauma
TextManager.Get("VoteToKick")) TextManager.Get("VoteToKick"))
{ {
Enabled = !selectedClient.HasKickVoteFromID(GameMain.Client.ID), Enabled = !selectedClient.HasKickVoteFromID(GameMain.Client.ID),
OnClicked = GameMain.Client.VoteForKick, OnClicked = (btn, userdata) => { GameMain.Client.VoteForKick(selectedClient); btn.Enabled = false; return true; },
UserData = selectedClient UserData = selectedClient
}; };
} }
@@ -1570,7 +1570,7 @@ namespace Barotrauma
{ {
UserData = selectedClient UserData = selectedClient
}; };
kickButton.OnClicked = KickPlayer; kickButton.OnClicked = (bt, userdata) => { KickPlayer(selectedClient); return true; };
kickButton.OnClicked += ClosePlayerFrame; kickButton.OnClicked += ClosePlayerFrame;
} }
@@ -1599,28 +1599,22 @@ namespace Barotrauma
return true; return true;
} }
public bool KickPlayer(GUIButton button, object userData) public void KickPlayer(Client client)
{ {
Client client = userData as Client; if (GameMain.NetworkMember == null || client == null) { return; }
if (client == null || GameMain.NetworkMember == null) return false; GameMain.Client.CreateKickReasonPrompt(client.Name, false);
GameMain.Client.CreateKickReasonPrompt(client.Name, false);
return false;
} }
public bool BanPlayer(GUIButton button, object userData) public void BanPlayer(Client client)
{ {
Client client = userData as Client; if (GameMain.NetworkMember == null || client == null) { return; }
if (userData == null || GameMain.NetworkMember == null) return false;
GameMain.Client.CreateKickReasonPrompt(client.Name, true); GameMain.Client.CreateKickReasonPrompt(client.Name, true);
return false;
} }
public bool BanPlayerRange(GUIButton button, object userData) public void BanPlayerRange(Client client)
{ {
Client client = userData as Client; if (GameMain.NetworkMember == null || client == null) { return; }
if (userData == null || GameMain.NetworkMember == null) return false;
GameMain.Client.CreateKickReasonPrompt(client.Name, true, true); GameMain.Client.CreateKickReasonPrompt(client.Name, true, true);
return false;
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
@@ -4,7 +4,6 @@ using Barotrauma.Particles;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
using System.Windows;
using System.Xml; using System.Xml;
using System.Text; using System.Text;
@@ -48,6 +48,8 @@ namespace Barotrauma
public ServerListScreen() public ServerListScreen()
{ {
GameMain.Instance.OnResolutionChanged += OnResolutionChanged;
menu = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); menu = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) });
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.95f), menu.RectTransform, Anchor.Center), isHorizontal: true) var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.95f), menu.RectTransform, Anchor.Center), isHorizontal: true)
@@ -170,6 +172,11 @@ namespace Barotrauma
refreshDisableTimer = DateTime.Now; refreshDisableTimer = DateTime.Now;
} }
private void OnResolutionChanged()
{
menu.RectTransform.MinSize = new Point(GameMain.GraphicsHeight, 0);
}
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
@@ -205,6 +212,8 @@ namespace Barotrauma
UserData = "noresults" UserData = "noresults"
}; };
} }
serverList.UpdateScrollBarSize();
} }
private bool RefreshJoinButtonState(GUIComponent component, object obj) private bool RefreshJoinButtonState(GUIComponent component, object obj)
@@ -47,9 +47,11 @@ namespace Barotrauma
public SteamWorkshopScreen() public SteamWorkshopScreen()
{ {
GameMain.Instance.OnResolutionChanged += OnResolutionChanged;
tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length]; tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length];
menu = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); menu = new GUIFrame(new RectTransform(new Vector2(0.85f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) });
var container = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), menu.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { Stretch = true }; var container = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), menu.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { Stretch = true };
@@ -211,6 +213,11 @@ namespace Barotrauma
SelectTab(Tab.Mods); SelectTab(Tab.Mods);
} }
private void OnResolutionChanged()
{
menu.RectTransform.MinSize = new Point(GameMain.GraphicsHeight, 0);
}
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
@@ -394,6 +401,7 @@ namespace Barotrauma
{ {
IsHorizontal = true, IsHorizontal = true,
Stretch = true, Stretch = true,
RelativeSpacing = 0.05f,
CanBeFocused = false CanBeFocused = false
}; };
@@ -405,32 +413,6 @@ namespace Barotrauma
if (item.Installed) if (item.Installed)
{ {
if (listBox != publishedItemList && SteamManager.CheckWorkshopItemEnabled(item) && !SteamManager.CheckWorkshopItemUpToDate(item))
{
new GUIButton(new RectTransform(new Vector2(0.4f, 0.5f), rightColumn.RectTransform, Anchor.BottomLeft), text: "Update")
{
UserData = "updatebutton",
IgnoreLayoutGroups = true,
OnClicked = (btn, userdata) =>
{
if (SteamManager.UpdateWorkshopItem(item, out string errorMsg))
{
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", TextManager.EnsureUTF8(item.Title)));
}
else
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { TextManager.EnsureUTF8(item.Title), errorMsg }));
}
btn.Enabled = false;
btn.Visible = false;
return true;
}
};
}
GUITickBox enabledTickBox = null; GUITickBox enabledTickBox = null;
try try
{ {
@@ -476,6 +458,33 @@ namespace Barotrauma
}; };
} }
} }
if (listBox != publishedItemList && SteamManager.CheckWorkshopItemEnabled(item) && !SteamManager.CheckWorkshopItemUpToDate(item))
{
new GUIButton(new RectTransform(new Vector2(0.4f, 0.5f), rightColumn.RectTransform, Anchor.BottomLeft), text: TextManager.Get("WorkshopItemUpdate"))
{
UserData = "updatebutton",
Font = GUI.SmallFont,
OnClicked = (btn, userdata) =>
{
if (SteamManager.UpdateWorkshopItem(item, out string errorMsg))
{
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", TextManager.EnsureUTF8(item.Title)));
}
else
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { TextManager.EnsureUTF8(item.Title), errorMsg }));
}
btn.Enabled = false;
btn.Visible = false;
return true;
}
};
}
} }
else if (item.Downloading) else if (item.Downloading)
{ {
@@ -900,7 +909,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), topRightColumn.RectTransform), TextManager.Get("WorkshopItemDescription")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), topRightColumn.RectTransform), TextManager.Get("WorkshopItemDescription"));
var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), topRightColumn.RectTransform)); var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), topRightColumn.RectTransform));
var descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform), itemEditor.Description, textAlignment: Alignment.TopLeft, wrap: true); var descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform), itemEditor.Description, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true);
descriptionBox.OnTextChanged += (textBox, text) => descriptionBox.OnTextChanged += (textBox, text) =>
{ {
Vector2 textSize = textBox.Font.MeasureString(descriptionBox.WrappedText); Vector2 textSize = textBox.Font.MeasureString(descriptionBox.WrappedText);
@@ -1081,6 +1090,7 @@ namespace Barotrauma
{ {
InitialDirectory = Path.GetFullPath(SteamManager.WorkshopItemStagingFolder), InitialDirectory = Path.GetFullPath(SteamManager.WorkshopItemStagingFolder),
Title = TextManager.Get("workshopitemaddfiles"), Title = TextManager.Get("workshopitemaddfiles"),
Multiselect = true
}; };
if (ofd.ShowDialog() == DialogResult.OK) if (ofd.ShowDialog() == DialogResult.OK)
{ {
@@ -1228,7 +1238,7 @@ namespace Barotrauma
private void OnAddFilesSelected(string[] fileNames) private void OnAddFilesSelected(string[] fileNames)
{ {
if (fileNames == null) { return; } if (fileNames == null) { return; }
for(int i = 0; i < fileNames.Length; i++) for (int i = 0; i < fileNames.Length; i++)
{ {
string file = fileNames[i]; string file = fileNames[i];
if (string.IsNullOrEmpty(file)) { continue; } if (string.IsNullOrEmpty(file)) { continue; }
@@ -1258,6 +1268,7 @@ namespace Barotrauma
itemContentPackage.AddFile(filePathRelativeToStagingFolder, ContentType.None); itemContentPackage.AddFile(filePathRelativeToStagingFolder, ContentType.None);
} }
} }
itemContentPackage.Save(itemContentPackage.Path);
RefreshCreateItemFileList(); RefreshCreateItemFileList();
} }
@@ -1331,6 +1342,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
itemContentPackage.RemoveFile(contentFile); itemContentPackage.RemoveFile(contentFile);
itemContentPackage.Save(itemContentPackage.Path);
RefreshCreateItemFileList(); RefreshCreateItemFileList();
return true; return true;
} }
@@ -1346,7 +1358,7 @@ namespace Barotrauma
{ {
if (itemContentPackage == null || itemEditor == null) return; if (itemContentPackage == null || itemEditor == null) return;
SteamManager.StartPublishItem(itemContentPackage, itemEditor); SteamManager.StartPublishItem(itemContentPackage, itemEditor);
CoroutineManager.StartCoroutine(WaitForPublish(itemEditor), "WaitForPublish"); CoroutineManager.StartCoroutine(WaitForPublish(itemEditor), "WaitForPublish");
} }
@@ -23,7 +23,7 @@ namespace Barotrauma
"CrewExperienceHigh" "CrewExperienceHigh"
}; };
private readonly Point defaultPreviewImageSize = new Point(256, 128); private readonly Point defaultPreviewImageSize = new Point(512, 368);
private Camera cam; private Camera cam;
@@ -2109,10 +2109,7 @@ namespace Barotrauma
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
if (MapEntity.SelectedList.Count == 1) MapEntity.FilteredSelectedList.FirstOrDefault()?.AddToGUIUpdateList();
{
MapEntity.SelectedList[0].AddToGUIUpdateList();
}
if (MapEntity.HighlightedListBox != null) if (MapEntity.HighlightedListBox != null)
{ {
MapEntity.HighlightedListBox.AddToGUIUpdateList(); MapEntity.HighlightedListBox.AddToGUIUpdateList();
@@ -2293,9 +2290,9 @@ namespace Barotrauma
dummyCharacter.SelectedConstruction = null; dummyCharacter.SelectedConstruction = null;
}*/ }*/
} }
else if (MapEntity.SelectedList.Count == 1) else if (MapEntity.FilteredSelectedList.Count == 1)
{ {
(MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime); (MapEntity.FilteredSelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
} }
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam); CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
@@ -196,7 +196,7 @@ namespace OpenAL
public static IntPtr CaptureOpenDevice(string devicename, uint frequency, int format, int buffersize) public static IntPtr CaptureOpenDevice(string devicename, uint frequency, int format, int buffersize)
{ {
byte[] devicenameBytes = Encoding.UTF8.GetBytes(devicename); byte[] devicenameBytes = Encoding.UTF8.GetBytes(devicename + "\0");
GCHandle devicenameHandle = GCHandle.Alloc(devicenameBytes, GCHandleType.Pinned); GCHandle devicenameHandle = GCHandle.Alloc(devicenameBytes, GCHandleType.Pinned);
IntPtr retVal = CaptureOpenDevice(devicenameHandle.AddrOfPinnedObject(), frequency, format, buffersize); IntPtr retVal = CaptureOpenDevice(devicenameHandle.AddrOfPinnedObject(), frequency, format, buffersize);
devicenameHandle.Free(); devicenameHandle.Free();
@@ -325,7 +325,9 @@ namespace Barotrauma.Sounds
if (ALSourceIndex < 0) return false; if (ALSourceIndex < 0) return false;
if (IsStream && !reachedEndSample) return true; if (IsStream && !reachedEndSample) return true;
int state; int state;
Al.GetSourcei(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.SourceState, out state); uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
if (!Al.IsSource(alSource)) return false;
Al.GetSourcei(alSource, Al.SourceState, out state);
bool playing = state == Al.Playing; bool playing = state == Al.Playing;
int alError = Al.GetError(); int alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -174,8 +174,8 @@ namespace Barotrauma.Sounds
sourcePools[(int)SourcePoolIndex.Default] = new SoundSourcePool(SOURCE_COUNT); sourcePools[(int)SourcePoolIndex.Default] = new SoundSourcePool(SOURCE_COUNT);
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT]; playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
sourcePools[(int)SourcePoolIndex.Voice] = new SoundSourcePool(8); sourcePools[(int)SourcePoolIndex.Voice] = new SoundSourcePool(16);
playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[8]; playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[16];
Al.DistanceModel(Al.LinearDistanceClamped); Al.DistanceModel(Al.LinearDistanceClamped);
@@ -202,7 +202,10 @@ namespace Barotrauma.Sounds
} }
Sound newSound = new OggSound(this, filename, stream); Sound newSound = new OggSound(this, filename, stream);
loadedSounds.Add(newSound); lock (loadedSounds)
{
loadedSounds.Add(newSound);
}
return newSound; return newSound;
} }
@@ -225,7 +228,10 @@ namespace Barotrauma.Sounds
newSound.BaseFar = range; newSound.BaseFar = range;
} }
loadedSounds.Add(newSound); lock (loadedSounds)
{
loadedSounds.Add(newSound);
}
return newSound; return newSound;
} }
@@ -352,12 +358,15 @@ namespace Barotrauma.Sounds
public void RemoveSound(Sound sound) public void RemoveSound(Sound sound)
{ {
for (int i = 0; i < loadedSounds.Count; i++) lock (loadedSounds)
{ {
if (loadedSounds[i] == sound) for (int i = 0; i < loadedSounds.Count; i++)
{ {
loadedSounds.RemoveAt(i); if (loadedSounds[i] == sound)
return; {
loadedSounds.RemoveAt(i);
return;
}
} }
} }
} }
@@ -97,7 +97,15 @@ namespace Barotrauma
public static Texture2D LoadTexture(string file, bool preMultiplyAlpha = true) public static Texture2D LoadTexture(string file, bool preMultiplyAlpha = true)
{ {
if (string.IsNullOrWhiteSpace(file)) { return new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, 1, 1); } if (string.IsNullOrWhiteSpace(file))
{
Texture2D t = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
t = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, 1, 1);
});
return t;
}
file = Path.GetFullPath(file); file = Path.GetFullPath(file);
foreach (Sprite s in list) foreach (Sprite s in list)
{ {
@@ -305,16 +313,22 @@ namespace Barotrauma
//check if another sprite is using the same texture //check if another sprite is using the same texture
if (!string.IsNullOrEmpty(FilePath)) //file can be empty if the sprite is created directly from a Texture2D instance if (!string.IsNullOrEmpty(FilePath)) //file can be empty if the sprite is created directly from a Texture2D instance
{ {
foreach (Sprite s in list) lock (list)
{ {
if (s.FullPath == FullPath) return; foreach (Sprite s in list)
{
if (s.FullPath == FullPath) return;
}
} }
} }
//if not, free the texture //if not, free the texture
if (texture != null) if (texture != null)
{ {
texture.Dispose(); CrossThread.RequestExecutionOnMainThread(() =>
{
texture.Dispose();
});
texture = null; texture = null;
} }
} }
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Threading;
namespace Barotrauma
{
public static class CrossThread
{
public delegate void TaskDelegate();
private class Task
{
public TaskDelegate Deleg;
public ManualResetEvent Mre;
public bool Done;
public Task(TaskDelegate d)
{
Deleg = d;
Mre = new ManualResetEvent(false);
Done = false;
}
public void PerformWait()
{
if (!Done) { Mre.WaitOne(); }
}
}
private static List<Task> enqueuedTasks;
static CrossThread() { enqueuedTasks = new List<Task>(); }
public static void ProcessTasks()
{
lock (enqueuedTasks)
{
foreach (var task in enqueuedTasks)
{
task.Deleg();
task.Mre.Set();
task.Done = true;
}
enqueuedTasks.Clear();
}
}
public static void RequestExecutionOnMainThread(TaskDelegate deleg)
{
if (Thread.CurrentThread == GameMain.MainThread)
{
deleg();
}
else
{
Task newTask = new Task(deleg);
lock (enqueuedTasks)
{
enqueuedTasks.Add(newTask);
}
newTask.PerformWait();
}
}
}
}
@@ -18,34 +18,26 @@ namespace Barotrauma
public string FileName { get; private set; } public string FileName { get; private set; }
public string[] FileNames { get; private set; } public string[] FileNames { get; private set; }
public OpenFileDialog() public OpenFileDialog() { }
{
ofd = new System.Windows.Forms.OpenFileDialog();
}
public System.Windows.Forms.DialogResult ShowDialog() public System.Windows.Forms.DialogResult ShowDialog()
{ {
ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Multiselect = Multiselect; ofd.Multiselect = Multiselect;
ofd.InitialDirectory = InitialDirectory; ofd.InitialDirectory = InitialDirectory;
ofd.Filter = Filter; ofd.Filter = Filter;
ofd.Title = Title; ofd.Title = Title;
#if LINUX System.Windows.Forms.DialogResult result;
var wrapperForm = new WrapperForm(ofd); var wrapperForm = new WrapperForm(ofd);
System.Windows.Forms.Application.Run(wrapperForm); System.Windows.Forms.Application.Run(wrapperForm);
System.Windows.Forms.Application.Exit();
FileName = wrapperForm.FileName; FileName = wrapperForm.FileName;
FileNames = wrapperForm.FileNames; FileNames = wrapperForm.FileNames;
return wrapperForm.Result; result = wrapperForm.Result;
#else ofd = null;
var result = ofd.ShowDialog();
FileName = ofd.FileName;
FileNames = ofd.FileNames;
return result; return result;
#endif
} }
#if LINUX
private class WrapperForm : System.Windows.Forms.Form private class WrapperForm : System.Windows.Forms.Form
{ {
private System.Windows.Forms.OpenFileDialog ofd; private System.Windows.Forms.OpenFileDialog ofd;
@@ -66,9 +58,8 @@ namespace Barotrauma
FileName = ofd.FileName; FileName = ofd.FileName;
FileNames = ofd.FileNames; FileNames = ofd.FileNames;
System.Threading.Thread.Sleep(100); System.Threading.Thread.Sleep(100);
this.Close(); System.Windows.Forms.Application.Exit();
} }
} }
#endif
} }
} }
@@ -1,7 +1,7 @@
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading.Tasks;
using Color = Microsoft.Xna.Framework.Color; using Color = Microsoft.Xna.Framework.Color;
namespace Barotrauma namespace Barotrauma
@@ -43,15 +43,18 @@ namespace Barotrauma
_graphicsDevice = graphicsDevice; _graphicsDevice = graphicsDevice;
_needsBmp = needsBmp; _needsBmp = needsBmp;
_spriteBatch = new SpriteBatch(_graphicsDevice); _spriteBatch = new SpriteBatch(_graphicsDevice);
PlaceHolderTexture = new Texture2D(graphicsDevice, 32, 32);
Color[] data = new Color[32 * 32]; Color[] data = new Color[32 * 32];
for (int i = 0; i < 32 * 32; i++) for (int i = 0; i < 32 * 32; i++)
{ {
data[i] = Color.Magenta; data[i] = Color.Magenta;
} }
PlaceHolderTexture.SetData(data);
CrossThread.RequestExecutionOnMainThread(() =>
{
PlaceHolderTexture = new Texture2D(graphicsDevice, 32, 32);
PlaceHolderTexture.SetData(data);
});
} }
public static Texture2D FromFile(string path, bool preMultiplyAlpha = true) public static Texture2D FromFile(string path, bool preMultiplyAlpha = true)
@@ -60,12 +63,7 @@ namespace Barotrauma
{ {
using (Stream fileStream = File.OpenRead(path)) using (Stream fileStream = File.OpenRead(path))
{ {
var texture = Texture2D.FromStream(_graphicsDevice, fileStream); return FromStream(fileStream, preMultiplyAlpha, path);
if (preMultiplyAlpha)
{
PreMultiplyAlpha(ref texture);
}
return texture;
} }
} }
@@ -76,16 +74,33 @@ namespace Barotrauma
} }
} }
public static Texture2D FromStream(Stream fileStream, bool preMultiplyAlpha = true) public static Texture2D FromStream(Stream fileStream, bool preMultiplyAlpha = true, string path=null)
{ {
try try
{ {
var texture = Texture2D.FromStream(_graphicsDevice, fileStream); int width = 0; int height = 0; int channels = 0;
byte[] textureData = null;
Task loadTask = Task.Run(() =>
{
textureData = Texture2D.TextureDataFromStream(fileStream, out width, out height, out channels);
});
bool success = loadTask.Wait(10000);
if (!success)
{
DebugConsole.ThrowError("Failed to load texture data from " + (path ?? "stream") + ": timed out");
return null;
}
if (preMultiplyAlpha) if (preMultiplyAlpha)
{ {
PreMultiplyAlpha(ref texture); PreMultiplyAlpha(ref textureData);
} }
return texture; Texture2D tex = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
tex = new Texture2D(_graphicsDevice, width, height);
tex.SetData(textureData);
});
return tex;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -94,40 +109,35 @@ namespace Barotrauma
} }
} }
private static void PreMultiplyAlpha(ref Texture2D texture) private static void PreMultiplyAlpha(ref byte[] data)
{ {
UInt32[] data = new UInt32[texture.Width * texture.Height]; for (int i = 0; i < data.Length; i+=4)
texture.GetData(data);
for (int i = 0; i < data.Length; i++)
{ {
uint a = (data[i] & 0xff000000) >> 24; uint a = data[i+3];
if (a == 0) if (a == 0)
{ {
data[i] = 0; data[i + 0] = 0;
data[i + 1] = 0;
data[i + 2] = 0;
continue; continue;
} }
else if (a == uint.MaxValue) else if (a == uint.MaxValue)
{ {
continue; continue;
} }
uint r = (data[i] & 0x00ff0000) >> 16; uint r = data[i+0];
uint g = (data[i] & 0x0000ff00) >> 8; uint g = data[i+1];
uint b = (data[i] & 0x000000ff); uint b = data[i+2];
// Monogame 3.7 needs the line below. // Monogame 3.7 needs the line below.
a *= a; a /= 255; a *= a; a /= 255;
b *= a; b /= 255; b *= a; b /= 255;
g *= a; g /= 255; g *= a; g /= 255;
r *= a; r /= 255; r *= a; r /= 255;
data[i] = (a << 24) | (r << 16) | (g << 8) | b; data[i + 0] = (byte)r;
data[i + 1] = (byte)g;
data[i + 2] = (byte)b;
data[i + 3] = (byte)a;
} }
//not sure why this is needed, but it seems to cut the memory usage of the game almost in half
//GetData/SetData might be leaking memory?
int width = texture.Width; int height = texture.Height;
texture.Dispose();
texture = new Texture2D(_graphicsDevice, width, height);
texture.SetData(data);
} }
@@ -138,4 +148,4 @@ namespace Barotrauma
private static SpriteBatch _spriteBatch; private static SpriteBatch _spriteBatch;
private static bool _needsBmp; private static bool _needsBmp;
} }
} }
@@ -83,8 +83,9 @@
<Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="GameAnalytics.Mono, Version=1.0.7018.15293, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath> <HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference> </Reference>
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="MonoGame.Framework, Version=3.6.0.1625, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.7.1.189\lib\net45\MonoGame.Framework.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Libraries\MonoGame.Framework\Windows\MonoGame.Framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath> <HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
@@ -224,6 +225,9 @@
<Content Include="soft_oal_x86.dll"> <Content Include="soft_oal_x86.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="x64\SDL2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0"> <BootstrapperPackage Include=".NETFramework,Version=v4.0">
@@ -310,9 +314,7 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" /> <Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.2.1.6\build\net45\GameAnalytics.Mono.SDK.targets'))" />
<Error Condition="!Exists('..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.7.1.189\build\MonoGame.Framework.WindowsDX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.7.1.189\build\MonoGame.Framework.WindowsDX.targets'))" />
</Target> </Target>
<Import Project="..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.7.1.189\build\MonoGame.Framework.WindowsDX.targets" Condition="Exists('..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.7.1.189\build\MonoGame.Framework.WindowsDX.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -2,8 +2,6 @@
<packages> <packages>
<package id="GameAnalytics.Mono.SDK" version="2.1.6" targetFramework="net45" /> <package id="GameAnalytics.Mono.SDK" version="2.1.6" targetFramework="net45" />
<package id="Microsoft.NETCore.Platforms" version="3.0.0-preview.19073.11" targetFramework="net45" /> <package id="Microsoft.NETCore.Platforms" version="3.0.0-preview.19073.11" targetFramework="net45" />
<package id="MonoGame.Framework.DesktopGL" version="3.7.1.189" targetFramework="net45" />
<package id="MonoGame.Framework.WindowsDX" version="3.7.1.189" targetFramework="net45" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net45" /> <package id="NETStandard.Library" version="2.0.3" targetFramework="net45" />
<package id="NLog" version="4.3.8" targetFramework="net45" /> <package id="NLog" version="4.3.8" targetFramework="net45" />
<package id="nVLC" version="3.0.0" targetFramework="net45" /> <package id="nVLC" version="3.0.0" targetFramework="net45" />
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.0.6")] [assembly: AssemblyVersion("0.9.0.7")]
[assembly: AssemblyFileVersion("0.9.0.6")] [assembly: AssemblyFileVersion("0.9.0.7")]
@@ -90,7 +90,16 @@ namespace Barotrauma
while (queuedMessages.Count > 0) while (queuedMessages.Count > 0)
{ {
ColoredText msg = queuedMessages.Dequeue(); ColoredText msg = queuedMessages.Dequeue();
if (GameSettings.SaveDebugConsoleLogs)
{
unsavedMessages.Add(msg);
if (unsavedMessages.Count >= messagesPerFile)
{
SaveLogs();
unsavedMessages.Clear();
}
}
string msgTxt = msg.Text; string msgTxt = msg.Text;
if (msg.IsCommand) commandMemory.Add(msgTxt); if (msg.IsCommand) commandMemory.Add(msgTxt);
@@ -167,7 +176,8 @@ namespace Barotrauma
RewriteInputToCommandLine(input); RewriteInputToCommandLine(input);
} }
Thread.Yield(); //TODO: be more clever about it
Thread.Sleep(10); //sleep for 10ms to not pin the CPU super hard
} }
} }
catch (ThreadAbortException) catch (ThreadAbortException)
@@ -778,11 +788,47 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", client); GameMain.Server.SendConsoleMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", client);
}); });
commands.Add(new Command("setpassword|setserverpassword", "setpassword [password]: Changes the password of the server that's being hosted.", (string[] args) => commands.Add(new Command("setpassword|setserverpassword|password", "setpassword [password]: Changes the password of the server that's being hosted.", (string[] args) =>
{
if (GameMain.Server == null) { return; }
GameMain.Server.ServerSettings.SetPassword(args.Length > 0 ? args[0] : "");
NewMessage(GameMain.Server.ServerSettings.HasPassword ? "Changed the server password." : "Removed password protection from the server.");
}));
AssignOnClientRequestExecute("setpassword", (Client client, Vector2 cursorPos, string[] args) =>
{
if (GameMain.Server == null) { return; }
GameMain.Server.ServerSettings.SetPassword(args.Length > 0 ? args[0] : "");
NewMessage(client.Name + " " + (GameMain.Server.ServerSettings.HasPassword ? " changed the server password to \"" + args[0] + "\"." : " removed password protection from the server."));
GameMain.Server.SendConsoleMessage(GameMain.Server.ServerSettings.HasPassword ? "Changed the server password." : "Removed password protection from the server.", client);
});
commands.Add(new Command("setmaxplayers|maxplayers", "setmaxplayers [max players]: Sets the maximum player count of the server that's being hosted.", (string[] args) =>
{ {
if (GameMain.Server == null || args.Length == 0) return; if (GameMain.Server == null || args.Length == 0) return;
GameMain.Server.ServerSettings.SetPassword(args[0]); if (!int.TryParse(args[0], out int maxPlayers))
{
NewMessage(args[0] + " is not a valid player count.");
}
else
{
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
NewMessage("Set the maximum player count to " + maxPlayers + ".");
}
})); }));
AssignOnClientRequestExecute("setmaxplayers", (Client client, Vector2 cursorPos, string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
if (!int.TryParse(args[0], out int maxPlayers))
{
GameMain.Server.SendConsoleMessage(args[0] + " is not a valid player count.", client);
}
else
{
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
NewMessage(client.Name + " set the maximum player count to " + maxPlayers + ".");
GameMain.Server.SendConsoleMessage("Set the maximum player count to " + maxPlayers + ".", client);
}
});
commands.Add(new Command("restart|reset", "restart/reset: Close and restart the server.", (string[] args) => commands.Add(new Command("restart|reset", "restart/reset: Close and restart the server.", (string[] args) =>
{ {
@@ -1579,12 +1625,6 @@ namespace Barotrauma
} }
})); }));
commands.Add(new Command("setpassword|setserverpassword", "setpassword [password]: Changes the password of the server that's being hosted.", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
GameMain.Server.ServerSettings.SetPassword(args[0]);
}));
#if DEBUG #if DEBUG
commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) => commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) =>
{ {
@@ -165,13 +165,27 @@ namespace Barotrauma.Networking
expirationTime = DateTime.Now + duration.Value; expirationTime = DateTime.Now + duration.Value;
} }
bannedPlayers.Add(new BannedPlayer(name, ip, reason, expirationTime)); if (!string.IsNullOrEmpty(ip))
{
bannedPlayers.Add(new BannedPlayer(name, ip, reason, expirationTime));
}
else if (steamID > 0)
{
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
}
else
{
DebugConsole.ThrowError("Failed to ban a client (no valid IP or Steam ID given)");
return;
}
Save(); Save();
} }
public void UnbanPlayer(string name) public void UnbanPlayer(string name)
{ {
var player = bannedPlayers.Find(bp => bp.Name == name); name = name.ToLower();
var player = bannedPlayers.Find(bp => bp.Name.ToLower() == name);
if (player == null) if (player == null)
{ {
DebugConsole.Log("Could not unban player \"" + name + "\". Matching player not found."); DebugConsole.Log("Could not unban player \"" + name + "\". Matching player not found.");
@@ -26,6 +26,10 @@ namespace Barotrauma.Networking
//for keeping track of disconnected clients in case the reconnect shortly after //for keeping track of disconnected clients in case the reconnect shortly after
private List<Client> disconnectedClients = new List<Client>(); private List<Client> disconnectedClients = new List<Client>();
//keeps track of players who've previously been playing on the server
//so kick votes persist during the session and the server can let the clients know what name this client used previously
private readonly List<PreviousPlayer> previousPlayers = new List<PreviousPlayer>();
private int roundStartSeed; private int roundStartSeed;
//is the server running //is the server running
@@ -34,7 +38,7 @@ namespace Barotrauma.Networking
private NetServer server; private NetServer server;
private DateTime refreshMasterTimer; private DateTime refreshMasterTimer;
private TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 30); private TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 60);
private bool registeredToMaster; private bool registeredToMaster;
private DateTime roundStartTime; private DateTime roundStartTime;
@@ -657,23 +661,25 @@ namespace Barotrauma.Networking
updateTimer = DateTime.Now + updateInterval; updateTimer = DateTime.Now + updateInterval;
} }
if (!registeredToMaster || refreshMasterTimer >= DateTime.Now) return; if (registeredToMaster && (DateTime.Now > refreshMasterTimer || serverSettings.ServerDetailsChanged))
if (GameMain.Config.UseSteamMatchmaking)
{ {
bool refreshSuccessful = SteamManager.RefreshServerDetails(this); if (GameMain.Config.UseSteamMatchmaking)
if (GameSettings.VerboseLogging)
{ {
Log(refreshSuccessful ? bool refreshSuccessful = SteamManager.RefreshServerDetails(this);
"Refreshed server info on the server list." : if (GameSettings.VerboseLogging)
"Refreshing server info on the server list failed.", ServerLog.MessageType.ServerMessage); {
Log(refreshSuccessful ?
"Refreshed server info on the server list." :
"Refreshing server info on the server list failed.", ServerLog.MessageType.ServerMessage);
}
} }
else
{
CoroutineManager.StartCoroutine(RefreshMaster());
}
refreshMasterTimer = DateTime.Now + refreshMasterInterval;
serverSettings.ServerDetailsChanged = false;
} }
else
{
CoroutineManager.StartCoroutine(RefreshMaster());
}
refreshMasterTimer = DateTime.Now + refreshMasterInterval;
} }
private void ReadDataMessage(NetIncomingMessage inc) private void ReadDataMessage(NetIncomingMessage inc)
@@ -724,7 +730,7 @@ namespace Barotrauma.Networking
bool isNew = inc.ReadBoolean(); inc.ReadPadBits(); bool isNew = inc.ReadBoolean(); inc.ReadPadBits();
if (isNew) if (isNew)
{ {
string savePath = inc.ReadString(); string saveName = inc.ReadString();
string seed = inc.ReadString(); string seed = inc.ReadString();
string subName = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString(); string subHash = inc.ReadString();
@@ -739,7 +745,11 @@ namespace Barotrauma.Networking
} }
else else
{ {
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed); string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
if (connectedClient.HasPermission(ClientPermissions.SelectMode))
{
MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed);
}
} }
} }
else else
@@ -2182,7 +2192,6 @@ namespace Barotrauma.Networking
public override void UnbanPlayer(string playerName, string playerIP) public override void UnbanPlayer(string playerName, string playerIP)
{ {
playerName = playerName.ToLowerInvariant();
if (!string.IsNullOrEmpty(playerIP)) if (!string.IsNullOrEmpty(playerIP))
{ {
serverSettings.BanList.UnbanIP(playerIP); serverSettings.BanList.UnbanIP(playerIP);
@@ -2230,6 +2239,19 @@ namespace Barotrauma.Networking
if (client.SteamID > 0) { SteamManager.StopAuthSession(client.SteamID); } if (client.SteamID > 0) { SteamManager.StopAuthSession(client.SteamID); }
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(client));
if (previousPlayer == null)
{
previousPlayer = new PreviousPlayer(client);
previousPlayers.Add(previousPlayer);
}
previousPlayer.Name = client.Name;
previousPlayer.KickVoters.Clear();
foreach (Client c in connectedClients)
{
if (client.HasKickVoteFrom(c)) { previousPlayer.KickVoters.Add(c); }
}
client.Connection.Disconnect(targetmsg); client.Connection.Disconnect(targetmsg);
client.Dispose(); client.Dispose();
connectedClients.Remove(client); connectedClients.Remove(client);
@@ -2240,6 +2262,7 @@ namespace Barotrauma.Networking
UpdateCrewFrame(); UpdateCrewFrame();
serverSettings.ServerDetailsChanged = true;
refreshMasterTimer = DateTime.Now; refreshMasterTimer = DateTime.Now;
} }
@@ -2558,6 +2581,13 @@ namespace Barotrauma.Networking
c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio); c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio);
foreach (Client c in clientsToKick) foreach (Client c in clientsToKick)
{ {
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(c));
if (previousPlayer != null)
{
//reset the client's kick votes (they can rejoin after their ban expires)
previousPlayer.KickVoters.Clear();
}
SendChatMessage($"ServerMessage.KickedFromServer~[client]={c.Name}", ChatMessageType.Server, null); SendChatMessage($"ServerMessage.KickedFromServer~[client]={c.Name}", ChatMessageType.Server, null);
KickClient(c, "ServerMessage.KickedByVote"); KickClient(c, "ServerMessage.KickedByVote");
BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime)); BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime));
@@ -3030,4 +3060,25 @@ namespace Barotrauma.Networking
//do nothing //do nothing
} }
} }
partial class PreviousPlayer
{
public string Name;
public string IP;
public UInt64 SteamID;
public readonly List<Client> KickVoters = new List<Client>();
public PreviousPlayer(Client c)
{
Name = c.Name;
IP = c.Connection?.RemoteEndPoint?.Address?.ToString() ?? "";
SteamID = c.SteamID;
}
public bool MatchesClient(Client c)
{
if (c.SteamID > 0 && SteamID > 0) { return c.SteamID == SteamID; }
return c.IPMatches(IP);
}
}
} }
@@ -418,7 +418,7 @@ namespace Barotrauma.Networking
return "\"" + nameAndHash.First + "\" (hash " + Md5Hash.GetShortHash(nameAndHash.Second) + ")"; return "\"" + nameAndHash.First + "\" (hash " + Md5Hash.GetShortHash(nameAndHash.Second) + ")";
} }
if (!serverSettings.Whitelist.IsWhiteListed(clName, inc.SenderConnection.RemoteEndPoint.Address)) if (inc.SenderConnection != OwnerConnection && !serverSettings.Whitelist.IsWhiteListed(clName, inc.SenderConnection.RemoteEndPoint.Address))
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NotOnWhitelist, ""); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NotOnWhitelist, "");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (not in whitelist)", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (not in whitelist)", ServerLog.MessageType.Error);
@@ -470,6 +470,17 @@ namespace Barotrauma.Networking
unauthenticatedClients.Remove(unauthClient); unauthenticatedClients.Remove(unauthClient);
unauthClient = null; unauthClient = null;
ConnectedClients.Add(newClient); ConnectedClients.Add(newClient);
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(newClient));
if (previousPlayer != null)
{
foreach (Client c in previousPlayer.KickVoters)
{
if (!connectedClients.Contains(c)) { continue; }
newClient.AddKickVote(c);
}
}
LastClientListUpdateID++; LastClientListUpdateID++;
if (newClient.Connection == OwnerConnection) if (newClient.Connection == OwnerConnection)
@@ -482,6 +493,13 @@ namespace Barotrauma.Networking
} }
GameMain.Server.SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null); GameMain.Server.SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null);
serverSettings.ServerDetailsChanged = true;
if (previousPlayer != null && previousPlayer.Name != newClient.Name)
{
GameMain.Server.SendChatMessage($"ServerMessage.PreviousClientName~[client]={clName}~[previousname]={previousPlayer.Name}", ChatMessageType.Server, null);
previousPlayer.Name = newClient.Name;
}
var savedPermissions = serverSettings.ClientPermissions.Find(cp => var savedPermissions = serverSettings.ClientPermissions.Find(cp =>
cp.SteamID > 0 ? cp.SteamID > 0 ?
@@ -42,6 +42,8 @@ namespace Barotrauma.Steam
return false; return false;
} }
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
// These server state variables may be changed at any time. Note that there is no longer a mechanism // These server state variables may be changed at any time. Note that there is no longer a mechanism
// to send the player count. The player count is maintained by steam and you should use the player // to send the player count. The player count is maintained by steam and you should use the player
// creation/authentication functions to maintain your player count. // creation/authentication functions to maintain your player count.
@@ -51,9 +53,10 @@ namespace Barotrauma.Steam
instance.server.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? ""; instance.server.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? "";
Instance.server.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText); Instance.server.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText);
Instance.server.SetKey("version", GameMain.Version.ToString()); Instance.server.SetKey("version", GameMain.Version.ToString());
Instance.server.SetKey("contentpackage", string.Join(",", GameMain.Config.SelectedContentPackages.Select(cp => cp.Name))); Instance.server.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
Instance.server.SetKey("contentpackagehash", string.Join(",", GameMain.Config.SelectedContentPackages.Select(cp => cp.MD5hash.Hash))); Instance.server.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
Instance.server.SetKey("contentpackageurl", string.Join(",", GameMain.Config.SelectedContentPackages.Select(cp => cp.SteamWorkshopUrl ?? ""))); Instance.server.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
Instance.server.SetKey("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
Instance.server.SetKey("usingwhitelist", (server.ServerSettings.Whitelist != null && server.ServerSettings.Whitelist.Enabled).ToString()); Instance.server.SetKey("usingwhitelist", (server.ServerSettings.Whitelist != null && server.ServerSettings.Whitelist.Enabled).ToString());
Instance.server.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString()); Instance.server.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
Instance.server.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString()); Instance.server.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString());
@@ -72,7 +75,7 @@ namespace Barotrauma.Steam
public static bool StartAuthSession(byte[] authTicketData, ulong clientSteamID) public static bool StartAuthSession(byte[] authTicketData, ulong clientSteamID)
{ {
if (instance == null || !instance.isInitialized || instance.server == null) return false; if (instance == null || !instance.isInitialized || instance.server == null) return false;
DebugConsole.Log("SteamManager authenticating Steam client " + clientSteamID); DebugConsole.Log("SteamManager authenticating Steam client " + clientSteamID);
if (!instance.server.Auth.StartSession(authTicketData, clientSteamID)) if (!instance.server.Auth.StartSession(authTicketData, clientSteamID))
{ {
@@ -77,9 +77,10 @@ namespace Barotrauma.Networking
bool recipientSpectating = recipient.Character == null || recipient.Character.IsDead; bool recipientSpectating = recipient.Character == null || recipient.Character.IsDead;
bool senderSpectating = sender.Character == null || sender.Character.IsDead; bool senderSpectating = sender.Character == null || sender.Character.IsDead;
//spectators cannot speak with in-game players or vice versa //TODO: only allow spectators to hear the voice chat if close enough to the speaker?
//TODO: allow spectators to hear the voice chat if close enough to the speaker?
if (recipientSpectating != senderSpectating) { return false; } //non-spectators cannot hear spectators
if (senderSpectating && !recipientSpectating) { return false; }
//both spectating, no need to do radio/distance checks //both spectating, no need to do radio/distance checks
if (recipientSpectating && senderSpectating) { return true; } if (recipientSpectating && senderSpectating) { return true; }
@@ -25,47 +25,45 @@ namespace Barotrauma.Networking
{ {
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
if (File.Exists(SavePath)) if (!File.Exists(SavePath)) { return; }
string[] lines;
try
{ {
string[] lines; lines = File.ReadAllLines(SavePath);
try }
{ catch (Exception e)
lines = File.ReadAllLines(SavePath); {
} DebugConsole.ThrowError("Failed to open whitelist in " + SavePath, e);
catch (Exception e) return;
{ }
DebugConsole.ThrowError("Failed to open whitelist in " + SavePath, e);
return;
}
foreach (string line in lines) foreach (string line in lines)
{
if (line[0] == '#')
{ {
if (line[0] == '#') string lineval = line.Substring(1, line.Length - 1);
Int32.TryParse(lineval, out int intVal);
if (lineval.ToLower() == "true" || intVal != 0)
{ {
string lineval = line.Substring(1, line.Length - 1); Enabled = true;
int intVal = 0;
Int32.TryParse(lineval, out intVal);
if (lineval.ToLower() == "true" || intVal != 0)
{
Enabled = true;
}
else
{
Enabled = false;
}
} }
else else
{ {
string[] separatedLine = line.Split(','); Enabled = false;
if (separatedLine.Length < 2) continue;
string name = String.Join(",", separatedLine.Take(separatedLine.Length - 1));
string ip = separatedLine.Last();
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
} }
} }
} else
{
string[] separatedLine = line.Split(',');
if (separatedLine.Length < 2) continue;
string name = string.Join(",", separatedLine.Take(separatedLine.Length - 1));
string ip = separatedLine.Last();
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
}
}
} }
public void Save() public void Save()
@@ -119,9 +117,7 @@ namespace Barotrauma.Networking
private void RemoveFromWhiteList(WhiteListedPlayer wlp) private void RemoveFromWhiteList(WhiteListedPlayer wlp)
{ {
GameServer.Log("Removing " + wlp.Name + " from whitelist", ServerLog.MessageType.ServerMessage); GameServer.Log("Removing " + wlp.Name + " from whitelist", ServerLog.MessageType.ServerMessage);
whitelistedPlayers.Remove(wlp); whitelistedPlayers.Remove(wlp);
Save();
} }
private void AddToWhiteList(string name, string ip) private void AddToWhiteList(string name, string ip)
@@ -129,7 +125,6 @@ namespace Barotrauma.Networking
if (string.IsNullOrWhiteSpace(name)) return; if (string.IsNullOrWhiteSpace(name)) return;
if (whitelistedPlayers.Any(x => x.Name.ToLower() == name.ToLower() && x.IP == ip)) return; if (whitelistedPlayers.Any(x => x.Name.ToLower() == name.ToLower() && x.IP == ip)) return;
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip)); whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
Save();
} }
public void ServerAdminWrite(NetBuffer outMsg, Client c) public void ServerAdminWrite(NetBuffer outMsg, Client c)
@@ -201,8 +196,10 @@ namespace Barotrauma.Networking
GameServer.Log(c.Name + " added " + name + " to whitelist (" + ip + ")", ServerLog.MessageType.ConsoleUsage); GameServer.Log(c.Name + " added " + name + " to whitelist (" + ip + ")", ServerLog.MessageType.ConsoleUsage);
AddToWhiteList(name, ip); AddToWhiteList(name, ip);
} }
return removeCount > 0 || addCount > 0 || prevEnabled!=enabled; bool changed = removeCount > 0 || addCount > 0 || prevEnabled != enabled;
if (changed) { Save(); }
return changed;
} }
} }
} }
@@ -14,7 +14,15 @@ namespace Barotrauma
public Submarine SelectedSub public Submarine SelectedSub
{ {
get { return selectedSub; } get { return selectedSub; }
set { selectedSub = value; lastUpdateID++; } set
{
selectedSub = value;
lastUpdateID++;
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
}
} }
public Submarine SelectedShuttle public Submarine SelectedShuttle
{ {
@@ -32,6 +40,11 @@ namespace Barotrauma
{ {
lastUpdateID++; lastUpdateID++;
selectedModeIndex = MathHelper.Clamp(value, 0, GameModes.Length - 1); selectedModeIndex = MathHelper.Clamp(value, 0, GameModes.Length - 1);
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.GameModeIdentifier = SelectedModeIdentifier;
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
} }
} }
@@ -48,6 +61,11 @@ namespace Barotrauma
break; break;
} }
} }
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.GameModeIdentifier = SelectedModeIdentifier;
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
} }
} }
@@ -196,7 +196,7 @@ namespace Barotrauma
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; } if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
if (unreachable.Contains(hull)) { continue; } if (unreachable.Contains(hull)) { continue; }
float hullSafety = 0; float hullSafety = 0;
if (character.CurrentHull != null) if (character.CurrentHull != null && character.Submarine != null)
{ {
// Inside // Inside
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; } if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; }
@@ -1101,7 +1101,7 @@ namespace Barotrauma
//prevent the hands from going above the top of the ladders //prevent the hands from going above the top of the ladders
handPos.Y = Math.Min(-0.5f, handPos.Y); handPos.Y = Math.Min(-0.5f, handPos.Y);
if (!PlayerInput.KeyDown(InputType.Aim) || Math.Abs(movement.Y) > 0.01f) if (!character.IsKeyDown(InputType.Aim) || Math.Abs(movement.Y) > 0.01f)
{ {
MoveLimb(leftHand, MoveLimb(leftHand,
new Vector2(handPos.X, new Vector2(handPos.X,
@@ -836,13 +836,21 @@ namespace Barotrauma
private static string humanConfigFile; private static string humanConfigFile;
public static string HumanConfigFile public static string HumanConfigFile
{ {
get get
{ {
if (string.IsNullOrEmpty(humanConfigFile)) if (string.IsNullOrEmpty(humanConfigFile))
{ {
humanConfigFile = GetConfigFile("Human"); humanConfigFile = GameMain.Instance.GetFilesOfType(ContentType.Character)?
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == "human.xml");
if (humanConfigFile == null)
{
DebugConsole.ThrowError($"Couldn't find a human config file from the selected content packages!");
DebugConsole.ThrowError($"(The config file must end with \"human.xml\")");
return string.Empty;
}
} }
return humanConfigFile; return humanConfigFile;
} }
} }
@@ -859,12 +867,16 @@ namespace Barotrauma
} }
} }
/// <summary>
/// Searches for a character config file from all currently selected content packages,
/// or from a specific package if the contentPackage parameter is given.
/// </summary>
public static string GetConfigFile(string speciesName, ContentPackage contentPackage = null) public static string GetConfigFile(string speciesName, ContentPackage contentPackage = null)
{ {
string configFile = null; string configFile = null;
if (contentPackage == null) if (contentPackage == null)
{ {
configFile = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true) configFile = GameMain.Instance.GetFilesOfType(ContentType.Character)
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml"); .FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml");
} }
else else
@@ -1519,10 +1531,9 @@ namespace Barotrauma
if (inventory.Owner is Item) if (inventory.Owner is Item)
{ {
var owner = (Item)inventory.Owner; var owner = (Item)inventory.Owner;
if (!CanInteractWith(owner)) if (!CanInteractWith(owner)) { return false; }
{ ItemContainer container = owner.GetComponents<ItemContainer>().FirstOrDefault(ic => ic.Inventory == inventory);
return false; if (container != null && !container.HasRequiredItems(this, addMessage: false)) { return false; }
}
} }
return true; return true;
} }
@@ -747,7 +747,7 @@ namespace Barotrauma
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 worldPos) public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 worldPos)
{ {
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)) return; if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
float prevLevel = Job.GetSkillLevel(skillIdentifier); float prevLevel = Job.GetSkillLevel(skillIdentifier);
Job.IncreaseSkillLevel(skillIdentifier, increase); Job.IncreaseSkillLevel(skillIdentifier, increase);
@@ -444,6 +444,8 @@ namespace Barotrauma
public bool SectorHit(Vector2 armorSector, Vector2 simPosition) public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
{ {
if (armorSector == Vector2.Zero) { return false; } if (armorSector == Vector2.Zero) { return false; }
//sector 360 degrees or more -> always hits
if (Math.Abs(armorSector.Y - armorSector.X) >= MathHelper.TwoPi) { return true; }
float rotation = body.TransformedRotation; float rotation = body.TransformedRotation;
float offset = (MathHelper.PiOver2 - GetArmorSectorRotationOffset(armorSector)) * Dir; float offset = (MathHelper.PiOver2 - GetArmorSectorRotationOffset(armorSector)) * Dir;
float hitAngle = VectorExtensions.Angle(VectorExtensions.Forward(rotation + offset), SimPosition - simPosition); float hitAngle = VectorExtensions.Angle(VectorExtensions.Forward(rotation + offset), SimPosition - simPosition);
@@ -460,9 +462,7 @@ namespace Barotrauma
protected float GetArmorSectorSize(Vector2 armorSector) protected float GetArmorSectorSize(Vector2 armorSector)
{ {
float min = Math.Min(armorSector.X, armorSector.Y); return Math.Abs(armorSector.X - armorSector.Y);
float max = Math.Max(armorSector.X, armorSector.Y);
return max - min;
} }
public void Update(float deltaTime) public void Update(float deltaTime)
@@ -498,7 +498,7 @@ namespace Barotrauma
{ {
Path = path; Path = path;
#if OSX #if OSX || LINUX
Path = Path.Replace("\\", "/"); Path = Path.Replace("\\", "/");
#endif #endif
@@ -328,7 +328,7 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) => commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server. You can see the IDs of the clients using the command \"clientlist\".", (string[] args) =>
{ {
if (GameMain.NetworkMember == null || args.Length == 0) return; if (GameMain.NetworkMember == null || args.Length == 0) return;
@@ -380,7 +380,7 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) => commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server. You can see the IDs of the clients using the command \"clientlist\".", (string[] args) =>
{ {
if (GameMain.NetworkMember == null || args.Length == 0) return; if (GameMain.NetworkMember == null || args.Length == 0) return;
@@ -66,12 +66,13 @@ namespace Barotrauma
if (GameMain.Config?.SelectedContentPackages.Count > 0) if (GameMain.Config?.SelectedContentPackages.Count > 0)
{ {
StringBuilder sb = new StringBuilder("ContentPackage:"); StringBuilder sb = new StringBuilder("ContentPackage: ");
int i = 0; int i = 0;
foreach (ContentPackage cp in GameMain.Config.SelectedContentPackages) foreach (ContentPackage cp in GameMain.Config.SelectedContentPackages)
{ {
sb.Append(cp.Name.Replace(":", "").Substring(0, Math.Min(32, cp.Name.Length))); string trimmedName = cp.Name.Replace(":", "").Replace(" ", "");
if (i < GameMain.Config.SelectedContentPackages.Count - 1) sb.Append(","); sb.Append(trimmedName.Substring(0, Math.Min(32, trimmedName.Length)));
if (i < GameMain.Config.SelectedContentPackages.Count - 1) { sb.Append(" "); }
} }
GameAnalytics.AddDesignEvent(sb.ToString()); GameAnalytics.AddDesignEvent(sb.ToString());
} }
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System.Xml; using System.Xml;
using System.IO;
#if CLIENT #if CLIENT
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Tutorials; using Barotrauma.Tutorials;
@@ -142,6 +143,8 @@ namespace Barotrauma
public bool CrewMenuOpen { get; set; } = true; public bool CrewMenuOpen { get; set; } = true;
public bool ChatOpen { get; set; } = true; public bool ChatOpen { get; set; } = true;
private string overrideSaveFolder, overrideMultiplayerSaveFolder;
private bool unsavedSettings; private bool unsavedSettings;
public bool UnsavedSettings public bool UnsavedSettings
{ {
@@ -277,7 +280,6 @@ namespace Barotrauma
public GameSettings() public GameSettings()
{ {
SelectedContentPackages = new HashSet<ContentPackage>();
ContentPackage.LoadAll(ContentPackage.Folder); ContentPackage.LoadAll(ContentPackage.Folder);
CompletedTutorialNames = new List<string>(); CompletedTutorialNames = new List<string>();
@@ -605,6 +607,17 @@ namespace Barotrauma
LoadControls(doc); LoadControls(doc);
LoadContentPackages(doc); LoadContentPackages(doc);
//allow overriding the save paths in the config file
if (doc.Root.Attribute("overridesavefolder") != null)
{
overrideSaveFolder = SaveUtil.SaveFolder = doc.Root.GetAttributeString("overridesavefolder", "");
overrideMultiplayerSaveFolder = SaveUtil.MultiplayerSaveFolder = Path.Combine(overrideSaveFolder, "Multiplayer");
}
if (doc.Root.Attribute("overridemultiplayersavefolder") != null)
{
overrideMultiplayerSaveFolder = SaveUtil.MultiplayerSaveFolder = doc.Root.GetAttributeString("overridemultiplayersavefolder", "");
}
XElement tutorialsElement = doc.Root.Element("tutorials"); XElement tutorialsElement = doc.Root.Element("tutorials");
if (tutorialsElement != null) if (tutorialsElement != null)
{ {
@@ -732,6 +745,15 @@ namespace Barotrauma
new XAttribute("campaigndisclaimershown", CampaignDisclaimerShown), new XAttribute("campaigndisclaimershown", CampaignDisclaimerShown),
new XAttribute("editordisclaimershown", EditorDisclaimerShown)); new XAttribute("editordisclaimershown", EditorDisclaimerShown));
if (!string.IsNullOrEmpty(overrideSaveFolder))
{
doc.Root.Add(new XAttribute("overridesavefolder", overrideSaveFolder));
}
if (!string.IsNullOrEmpty(overrideMultiplayerSaveFolder))
{
doc.Root.Add(new XAttribute("overridemultiplayersavefolder", overrideMultiplayerSaveFolder));
}
if (!ShowUserStatisticsPrompt) if (!ShowUserStatisticsPrompt)
{ {
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics)); doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
@@ -22,7 +22,6 @@ namespace Barotrauma.Items.Components
private float openState; private float openState;
private Sprite doorSprite, weldedSprite, brokenSprite; private Sprite doorSprite, weldedSprite, brokenSprite;
private bool scaleBrokenSprite, fadeBrokenSprite; private bool scaleBrokenSprite, fadeBrokenSprite;
private bool createdNewGap;
private bool autoOrientGap; private bool autoOrientGap;
private bool isStuck; private bool isStuck;
@@ -87,17 +86,19 @@ namespace Barotrauma.Items.Components
{ {
get get
{ {
if (linkedGap != null) return linkedGap; if (linkedGap == null)
foreach (MapEntity e in item.linkedTo)
{ {
linkedGap = e as Gap; GetLinkedGap();
if (linkedGap != null)
{
linkedGap.PassAmbientLight = Window != Rectangle.Empty;
return linkedGap;
}
} }
return linkedGap;
}
}
private void GetLinkedGap()
{
linkedGap = item.linkedTo.FirstOrDefault(e => e is Gap) as Gap;
if (linkedGap == null)
{
Rectangle rect = item.Rect; Rectangle rect = item.Rect;
if (IsHorizontal) if (IsHorizontal)
{ {
@@ -109,17 +110,13 @@ namespace Barotrauma.Items.Components
rect.X -= 5; rect.X -= 5;
rect.Width += 10; rect.Width += 10;
} }
linkedGap = new Gap(rect, !IsHorizontal, Item.Submarine) linkedGap = new Gap(rect, !IsHorizontal, Item.Submarine)
{ {
Submarine = item.Submarine, Submarine = item.Submarine
PassAmbientLight = Window != Rectangle.Empty,
Open = openState
}; };
item.linkedTo.Add(linkedGap); item.linkedTo.Add(linkedGap);
createdNewGap = true;
return linkedGap;
} }
RefreshLinkedGap();
} }
public bool IsHorizontal { get; private set; } public bool IsHorizontal { get; private set; }
@@ -162,14 +159,14 @@ namespace Barotrauma.Items.Components
get; get;
set; set;
} }
public Door(Item item, XElement element) public Door(Item item, XElement element)
: base(item, element) : base(item, element)
{ {
IsHorizontal = element.GetAttributeBool("horizontal", false); IsHorizontal = element.GetAttributeBool("horizontal", false);
canBePicked = element.GetAttributeBool("canbepicked", false); canBePicked = element.GetAttributeBool("canbepicked", false);
autoOrientGap = element.GetAttributeBool("autoorientgap", false); autoOrientGap = element.GetAttributeBool("autoorientgap", false);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
string texturePath = subElement.GetAttributeString("texture", ""); string texturePath = subElement.GetAttributeString("texture", "");
@@ -370,12 +367,20 @@ namespace Barotrauma.Items.Components
#endif #endif
} }
public override void OnMapLoaded() public void RefreshLinkedGap()
{ {
LinkedGap.ConnectedDoor = this; LinkedGap.ConnectedDoor = this;
if (autoOrientGap)
{
LinkedGap.AutoOrient();
}
LinkedGap.Open = openState; LinkedGap.Open = openState;
if (createdNewGap && autoOrientGap) linkedGap.AutoOrient(); LinkedGap.PassAmbientLight = Window != Rectangle.Empty;
}
public override void OnMapLoaded()
{
RefreshLinkedGap();
#if CLIENT #if CLIENT
Vector2[] corners = GetConvexHullCorners(Rectangle.Empty); Vector2[] corners = GetConvexHullCorners(Rectangle.Empty);
@@ -386,6 +391,18 @@ namespace Barotrauma.Items.Components
#endif #endif
} }
public override void OnScaleChanged()
{
#if CLIENT
UpdateConvexHulls();
#endif
if (linkedGap != null)
{
RefreshLinkedGap();
linkedGap.Rect = item.Rect;
}
}
protected override void RemoveComponentSpecific() protected override void RemoveComponentSpecific()
{ {
base.RemoveComponentSpecific(); base.RemoveComponentSpecific();
@@ -53,12 +53,6 @@ namespace Barotrauma.Items.Components
set; set;
} }
/// <summary>
/// How useful the weapon is in combat? Used by AI to sort the used weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
[Serialize(0f, false)]
public float CombatPriority { get; private set; }
public MeleeWeapon(Item item, XElement element) public MeleeWeapon(Item item, XElement element)
: base(item, element) : base(item, element)
{ {
@@ -43,12 +43,6 @@ namespace Barotrauma.Items.Components
set; set;
} }
/// <summary>
/// How useful the weapon is in combat? Used by AI to sort the used weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
[Serialize(0f, false)]
public float CombatPriority { get; private set; }
public Vector2 TransformedBarrelPos public Vector2 TransformedBarrelPos
{ {
get get
@@ -170,6 +164,7 @@ namespace Barotrauma.Items.Components
projectile.Item.SetTransform(projectilePos, rotation); projectile.Item.SetTransform(projectilePos, rotation);
projectile.Use(deltaTime); projectile.Use(deltaTime);
if (projectile.Item.Removed) { return true; }
projectile.User = character; projectile.User = character;
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f)); projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
@@ -157,7 +157,8 @@ namespace Barotrauma.Items.Components
partial void UseProjSpecific(float deltaTime); partial void UseProjSpecific(float deltaTime);
private List<FireSource> fireSourcesInRange = new List<FireSource>(); private readonly HashSet<Character> hitCharacters = new HashSet<Character>();
private readonly List<FireSource> fireSourcesInRange = new List<FireSource>();
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies) private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
{ {
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
@@ -165,6 +166,7 @@ namespace Barotrauma.Items.Components
{ {
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true); var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
Type lastHitType = null; Type lastHitType = null;
hitCharacters.Clear();
foreach (Body body in bodies) foreach (Body body in bodies)
{ {
Type bodyType = body.UserData?.GetType(); Type bodyType = body.UserData?.GetType();
@@ -173,6 +175,23 @@ namespace Barotrauma.Items.Components
//stop the ray if it already hit a door/wall and is now about to hit some other type of entity //stop the ray if it already hit a door/wall and is now about to hit some other type of entity
if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; } if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; }
} }
Character hitCharacter = null;
if (body.UserData is Limb limb)
{
hitCharacter = limb.character;
}
else if (body.UserData is Character character)
{
hitCharacter = character;
}
//only do damage once to each character even if they ray hit multiple limbs
if (hitCharacter != null)
{
if (hitCharacters.Contains(hitCharacter)) { continue; }
hitCharacters.Add(hitCharacter);
}
if (FixBody(user, deltaTime, degreeOfSuccess, body)) if (FixBody(user, deltaTime, degreeOfSuccess, body))
{ {
if (bodyType != null) { lastHitType = bodyType; } if (bodyType != null) { lastHitType = bodyType; }
@@ -545,7 +545,6 @@ namespace Barotrauma.Items.Components
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return 0.0f; return 0.0f;
} }
float average = skillSuccessSum / requiredSkills.Count;
float skillSuccessSum = 0.0f; float skillSuccessSum = 0.0f;
for (int i = 0; i < requiredSkills.Count; i++) for (int i = 0; i < requiredSkills.Count; i++)
@@ -693,6 +692,8 @@ namespace Barotrauma.Items.Components
/// </summary> /// </summary>
public virtual void OnItemLoaded() { } public virtual void OnItemLoaded() { }
public virtual void OnScaleChanged() { }
// TODO: Consider using generics, interfaces, or inheritance instead of reflection -> would be easier to debug when something changes/goes wrong. // TODO: Consider using generics, interfaces, or inheritance instead of reflection -> would be easier to debug when something changes/goes wrong.
// For example, currently we can edit the constructors but they will fail in runtime because the parameters are not changed here. // For example, currently we can edit the constructors but they will fail in runtime because the parameters are not changed here.
// It's also painful to find where the constructors are used, because the references exist only at runtime. // It's also painful to find where the constructors are used, because the references exist only at runtime.
@@ -97,6 +97,7 @@ namespace Barotrauma.Items.Components
RelatedItem ri = containableItems.Find(x => x.MatchesItem(containedItem)); RelatedItem ri = containableItems.Find(x => x.MatchesItem(containedItem));
if (ri != null) if (ri != null)
{ {
itemsWithStatusEffects.RemoveAll(i => i.First == containedItem);
foreach (StatusEffect effect in ri.statusEffects) foreach (StatusEffect effect in ri.statusEffects)
{ {
itemsWithStatusEffects.Add(new Pair<Item, StatusEffect>(containedItem, effect)); itemsWithStatusEffects.Add(new Pair<Item, StatusEffect>(containedItem, effect));
@@ -107,12 +108,12 @@ namespace Barotrauma.Items.Components
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null; IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
} }
public void OnItemRemoved(Item item) public void OnItemRemoved(Item containedItem)
{ {
itemsWithStatusEffects.RemoveAll(i => i.First == item); itemsWithStatusEffects.RemoveAll(i => i.First == containedItem);
//deactivate if the inventory is empty //deactivate if the inventory is empty
IsActive = itemsWithStatusEffects.Count > 0 || item.body != null; IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
} }
public bool CanBeContained(Item item) public bool CanBeContained(Item item)
@@ -37,6 +37,8 @@ namespace Barotrauma.Items.Components
private Item focusTarget; private Item focusTarget;
private float targetRotation; private float targetRotation;
private bool state;
public Vector2 UserPos public Vector2 UserPos
{ {
get { return userPos; } get { return userPos; }
@@ -48,6 +50,13 @@ namespace Barotrauma.Items.Components
get { return user; } get { return user; }
} }
[Serialize(false, false), Editable(ToolTip = "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.")]
public bool IsToggle
{
get;
set;
}
public Controller(Item item, XElement element) public Controller(Item item, XElement element)
: base(item, element) : base(item, element)
{ {
@@ -55,7 +64,7 @@ namespace Barotrauma.Items.Components
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero); userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
Enum.TryParse<Direction>(element.GetAttributeString("direction", "None"), out dir); Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
foreach (XElement el in element.Elements()) foreach (XElement el in element.Elements())
{ {
@@ -83,7 +92,12 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
this.cam = cam; this.cam = cam;
if (IsToggle)
{
item.SendSignal(0, state ? "1" : "0", "signal_out", sender: null);
}
if (user == null if (user == null
|| user.Removed || user.Removed
|| user.SelectedConstruction != item || user.SelectedConstruction != item
@@ -94,7 +108,7 @@ namespace Barotrauma.Items.Components
CancelUsing(user); CancelUsing(user);
user = null; user = null;
} }
IsActive = false; if (!IsToggle) { IsActive = false; }
return; return;
} }
@@ -169,7 +183,7 @@ namespace Barotrauma.Items.Components
} }
item.SendSignal(0, "1", "trigger_out", user); item.SendSignal(0, "1", "trigger_out", user);
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator); ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
return true; return true;
@@ -254,7 +268,14 @@ namespace Barotrauma.Items.Components
public override bool Pick(Character picker) public override bool Pick(Character picker)
{ {
item.SendSignal(0, "1", "signal_out", picker); if (IsToggle)
{
state = !state;
}
else
{
item.SendSignal(0, "1", "signal_out", picker);
}
#if CLIENT #if CLIENT
PlaySound(ActionType.OnUse, item.WorldPosition, picker); PlaySound(ActionType.OnUse, item.WorldPosition, picker);
@@ -137,10 +137,10 @@ namespace Barotrauma.Items.Components
public Vector2 AvoidStrength; public Vector2 AvoidStrength;
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength) public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength, Vector2 translation)
{ {
Point1 = edge.Point1; Point1 = edge.Point1 + translation;
Point2 = edge.Point2; Point2 = edge.Point2 + translation;
Intersection = intersection; Intersection = intersection;
Dot = dot; Dot = dot;
AvoidStrength = avoidStrength; AvoidStrength = avoidStrength;
@@ -210,6 +210,11 @@ namespace Barotrauma.Items.Components
if (voltage < minVoltage && currPowerConsumption > 0.0f) { return; } if (voltage < minVoltage && currPowerConsumption > 0.0f) { return; }
if (user != null && user.Removed)
{
user = null;
}
ApplyStatusEffects(ActionType.OnActive, deltaTime, null); ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (autoPilot) if (autoPilot)
@@ -333,14 +338,14 @@ namespace Barotrauma.Items.Components
{ {
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, controlledSub.WorldPosition, cell.Center, out Vector2 intersection)) if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
{ {
Vector2 diff = controlledSub.WorldPosition - intersection; Vector2 diff = controlledSub.WorldPosition - intersection;
//far enough -> ignore //far enough -> ignore
if (Math.Abs(diff.X) > avoidDist.X && Math.Abs(diff.Y) > avoidDist.Y) if (Math.Abs(diff.X) > avoidDist.X && Math.Abs(diff.Y) > avoidDist.Y)
{ {
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero)); debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero, Vector2.Zero));
continue; continue;
} }
if (diff.LengthSquared() < 1.0f) diff = Vector2.UnitY; if (diff.LengthSquared() < 1.0f) diff = Vector2.UnitY;
@@ -352,13 +357,13 @@ namespace Barotrauma.Items.Components
//not heading towards the wall -> ignore //not heading towards the wall -> ignore
if (dot < 0.5) if (dot < 0.5)
{ {
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero)); debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero, cell.Translation));
continue; continue;
} }
Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius; Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius;
newAvoidStrength += change * dot; newAvoidStrength += change * dot;
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, change * dot)); debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, change * dot, cell.Translation));
} }
} }
} }
@@ -275,7 +275,14 @@ namespace Barotrauma.Items.Components
//the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost //the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething) if (!hitSomething)
{ {
Entity.Spawner.AddToRemoveQueue(item); if (Entity.Spawner == null)
{
item.Remove();
}
else
{
Entity.Spawner.AddToRemoveQueue(item);
}
} }
} }
@@ -9,12 +9,14 @@ namespace Barotrauma.Items.Components
{ {
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{ {
public static float SkillIncreaseMultiplier = 0.4f; public static float SkillIncreasePerRepair = 5.0f;
private string header; private string header;
private float deteriorationTimer; private float deteriorationTimer;
bool wasBroken;
public float LastActiveTime; public float LastActiveTime;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How fast the condition of the item deteriorates per second.")] [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How fast the condition of the item deteriorates per second.")]
@@ -173,16 +175,13 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f; float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
foreach (Skill skill in requiredSkills)
//item must have been below 50% condition for the player to get an achievement or XP for repairing it
if (item.Condition < ShowRepairUIThreshold)
{ {
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier); wasBroken = true;
if (characterSkillLevel >= skill.Level) successFactor += 1.0f / requiredSkills.Count;
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreaseMultiplier * deltaTime / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
} }
bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor); float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f) if (fixDuration <= 0.0f)
{ {
@@ -195,8 +194,16 @@ namespace Barotrauma.Items.Components
if (wasBroken && item.IsFullCondition) if (wasBroken && item.IsFullCondition)
{ {
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, currentFixer); SteamAchievementManager.OnItemRepaired(item, currentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay); deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
#if SERVER #if SERVER
item.CreateServerEvent(this); item.CreateServerEvent(this);
#endif #endif
@@ -19,7 +19,8 @@ namespace Barotrauma.Items.Components
private string prevSignal; private string prevSignal;
public Character.TeamType TeamID; [Serialize(Character.TeamType.None, false)]
public Character.TeamType TeamID { get; set; }
[Serialize(20000.0f, false)] [Serialize(20000.0f, false)]
public float Range public float Range
@@ -82,8 +83,11 @@ namespace Barotrauma.Items.Components
public bool CanReceive(WifiComponent sender) public bool CanReceive(WifiComponent sender)
{ {
if (sender == null || sender.channel != channel || sender.TeamID != TeamID) return false; if (sender == null || sender.channel != channel) { return false; }
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) return false; if (sender.TeamID == Character.TeamType.Team1 && TeamID == Character.TeamType.Team2) { return false; }
if (sender.TeamID == Character.TeamType.Team2 && TeamID == Character.TeamType.Team1) { return false; }
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
return HasRequiredContainedItems(false); return HasRequiredContainedItems(false);
} }
@@ -70,7 +70,9 @@ namespace Barotrauma
private Inventory parentInventory; private Inventory parentInventory;
private Inventory ownInventory; private Inventory ownInventory;
private Rectangle defaultRect;
private Dictionary<string, Connection> connections; private Dictionary<string, Connection> connections;
private List<Repairable> repairables; private List<Repairable> repairables;
@@ -198,6 +200,34 @@ namespace Barotrauma
} }
} }
private float scale = 1.0f;
public override float Scale
{
get { return scale; }
set
{
if (scale == value) { return; }
scale = MathHelper.Clamp(value, 0.1f, 10.0f);
float relativeScale = scale / prefab.Scale;
if (!ResizeHorizontal || !ResizeVertical)
{
int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale);
int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale);
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
}
if (components != null)
{
foreach (ItemComponent component in components)
{
component.OnScaleChanged();
}
}
}
}
public float PositionUpdateInterval public float PositionUpdateInterval
{ {
get; get;
@@ -499,9 +529,10 @@ namespace Barotrauma
drawableComponents = new List<IDrawableComponent>(); drawableComponents = new List<IDrawableComponent>();
tags = new HashSet<string>(); tags = new HashSet<string>();
repairables = new List<Repairable>(); repairables = new List<Repairable>();
defaultRect = newRect;
rect = newRect; rect = newRect;
condition = itemPrefab.Health; condition = itemPrefab.Health;
lastSentCondition = condition; lastSentCondition = condition;
@@ -626,13 +657,18 @@ namespace Barotrauma
ic.OnItemLoaded(); ic.OnItemLoaded();
} }
} }
DebugConsole.Log("Created " + Name + " (" + ID + ")");
} }
partial void InitProjSpecific(); partial void InitProjSpecific();
public override MapEntity Clone() public override MapEntity Clone()
{ {
Item clone = new Item(rect, Prefab, Submarine, callOnItemLoaded: false); Item clone = new Item(rect, Prefab, Submarine, callOnItemLoaded: false)
{
defaultRect = defaultRect
};
foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties) foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties)
{ {
if (!property.Value.Attributes.OfType<Editable>().Any()) continue; if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
@@ -1552,7 +1588,7 @@ namespace Barotrauma
public void Use(float deltaTime, Character character = null, Limb targetLimb = null) public void Use(float deltaTime, Character character = null, Limb targetLimb = null)
{ {
if (RequireAimToUse && !character.IsKeyDown(InputType.Aim)) if (RequireAimToUse && (character == null || !character.IsKeyDown(InputType.Aim)))
{ {
return; return;
} }
@@ -2050,11 +2086,11 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Submarine != null || rootContainer.ParentInventory?.Owner is Character); System.Diagnostics.Debug.Assert(Submarine != null || rootContainer.ParentInventory?.Owner is Character);
Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition; Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition;
element.Add(new XAttribute("rect", element.Add(new XAttribute("rect",
(int)(rect.X - subPosition.X) + "," + (int)(rect.X - subPosition.X) + "," +
(int)(rect.Y - subPosition.Y) + "," + (int)(rect.Y - subPosition.Y) + "," +
rect.Width + "," + rect.Height)); defaultRect.Width + "," + defaultRect.Height));
if (linkedTo != null && linkedTo.Count > 0) if (linkedTo != null && linkedTo.Count > 0)
{ {
@@ -9,6 +9,7 @@ namespace Barotrauma
class Entity : ISpatialEntity class Entity : ISpatialEntity
{ {
public const ushort NullEntityID = 0; public const ushort NullEntityID = 0;
public const ushort EntitySpawnerID = ushort.MaxValue;
private static Dictionary<ushort, Entity> dictionary = new Dictionary<ushort, Entity>(); private static Dictionary<ushort, Entity> dictionary = new Dictionary<ushort, Entity>();
public static List<Entity> GetEntityList() public static List<Entity> GetEntityList()
@@ -43,12 +44,19 @@ namespace Barotrauma
} }
set set
{ {
if (this is EntitySpawner) { return; }
if (value == NullEntityID) if (value == NullEntityID)
{ {
DebugConsole.ThrowError("Cannot set the ID of an entity to " + NullEntityID + DebugConsole.ThrowError("Cannot set the ID of an entity to " + NullEntityID +
"! The value is reserved for entity events referring to a non-existent (e.g. removed) entity.\n" + Environment.StackTrace); "! The value is reserved for entity events referring to a non-existent (e.g. removed) entity.\n" + Environment.StackTrace);
return; return;
} }
if (value == EntitySpawnerID)
{
DebugConsole.ThrowError("Cannot set the ID of an entity to " + EntitySpawnerID +
"! The value is reserved for EntitySpawner.\n" + Environment.StackTrace);
return;
}
if (dictionary.TryGetValue(id, out Entity thisEntity) && thisEntity == this) if (dictionary.TryGetValue(id, out Entity thisEntity) && thisEntity == this)
{ {
@@ -107,15 +115,17 @@ namespace Barotrauma
this.Submarine = submarine; this.Submarine = submarine;
//give a unique ID //give a unique ID
id = FindFreeID(submarine == null ? (ushort)1 : submarine.IdOffset); id = this is EntitySpawner ?
EntitySpawnerID :
FindFreeID(submarine == null ? (ushort)1 : submarine.IdOffset);
dictionary.Add(id, this); dictionary.Add(id, this);
} }
public static ushort FindFreeID(ushort idOffset = 0) public static ushort FindFreeID(ushort idOffset = 0)
{ {
//ushort.MaxValue - 1 because 0 is a reserved value //ushort.MaxValue - 2 because 0 and ushort.MaxValue are reserved values
if (dictionary.Count >= ushort.MaxValue - 1) if (dictionary.Count >= ushort.MaxValue - 2)
{ {
throw new Exception("Maximum amount of entities (" + (ushort.MaxValue - 1) + ") reached!"); throw new Exception("Maximum amount of entities (" + (ushort.MaxValue - 1) + ") reached!");
} }
@@ -228,7 +228,11 @@ namespace Barotrauma
attacker = item.GetComponent<Projectile>()?.User; attacker = item.GetComponent<Projectile>()?.User;
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User; if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
} }
c.AddDamage(limb.WorldPosition, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
//use a position slightly from the limb's position towards the explosion
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
if (attack.StatusEffects != null && attack.StatusEffects.Any()) if (attack.StatusEffects != null && attack.StatusEffects.Any())
{ {
@@ -122,6 +122,8 @@ namespace Barotrauma
GapList.Add(this); GapList.Add(this);
InsertToList(); InsertToList();
DebugConsole.Log("Created gap (" + ID + ")");
} }
public override MapEntity Clone() public override MapEntity Clone()
@@ -242,6 +242,8 @@ namespace Barotrauma
WaterVolume = 0.0f; WaterVolume = 0.0f;
InsertToList(); InsertToList();
DebugConsole.Log("Created hull (" + ID + ")");
} }
public static Rectangle GetBorders() public static Rectangle GetBorders()
@@ -107,7 +107,7 @@ namespace Barotrauma
if (Screen.Selected == GameMain.SubEditorScreen) if (Screen.Selected == GameMain.SubEditorScreen)
{ {
MapEntity.SelectedList.Clear(); MapEntity.SelectedList.Clear();
MapEntity.SelectedList.AddRange(entities); entities.ForEach(e => MapEntity.AddSelection(e));
} }
#endif #endif
return entities; return entities;
@@ -1274,6 +1274,8 @@ namespace Barotrauma
} }
} }
DebugConsole.Log("Generating level resources...");
for (int i = 0; i < generationParams.ItemCount; i++) for (int i = 0; i < generationParams.ItemCount; i++)
{ {
var selectedPrefab = ToolBox.SelectWeightedRandom( var selectedPrefab = ToolBox.SelectWeightedRandom(
@@ -1286,6 +1288,7 @@ namespace Barotrauma
var selectedEdge = selectedCell.Edges.GetRandom(e => e.IsSolid && !e.OutsideLevel, Rand.RandSync.Server); var selectedEdge = selectedCell.Edges.GetRandom(e => e.IsSolid && !e.OutsideLevel, Rand.RandSync.Server);
if (selectedEdge == null) continue; if (selectedEdge == null) continue;
float edgePos = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server); float edgePos = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
Vector2 selectedPos = Vector2.Lerp(selectedEdge.Point1, selectedEdge.Point2, edgePos); Vector2 selectedPos = Vector2.Lerp(selectedEdge.Point1, selectedEdge.Point2, edgePos);
Vector2 edgeNormal = selectedEdge.GetNormal(selectedCell); Vector2 edgeNormal = selectedEdge.GetNormal(selectedCell);
@@ -1306,6 +1309,8 @@ namespace Barotrauma
#endif #endif
} }
} }
DebugConsole.Log("Level resources generated");
} }
public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f) public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f)
@@ -1022,12 +1022,12 @@ namespace Barotrauma.RuinGeneration
{ {
targetEntity = ruinEntities.GetRandom(e => targetEntity = ruinEntities.GetRandom(e =>
e.Room == targetRoom && e.Room == targetRoom &&
e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier)?.Entity; e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier, Rand.RandSync.Server)?.Entity;
} }
} }
else else
{ {
targetEntity = ruinEntities.GetRandom(e => e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier)?.Entity; targetEntity = ruinEntities.GetRandom(e => e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier, Rand.RandSync.Server)?.Entity;
} }
if (targetEntity == null) continue; if (targetEntity == null) continue;
@@ -57,6 +57,8 @@ namespace Barotrauma
linkedToID = new List<ushort>(); linkedToID = new List<ushort>();
InsertToList(); InsertToList();
DebugConsole.Log("Created linked submarine (" + ID + ")");
} }
public static LinkedSubmarine CreateDummy(Submarine mainSub, Submarine linkedSub) public static LinkedSubmarine CreateDummy(Submarine mainSub, Submarine linkedSub)
@@ -207,20 +207,22 @@ namespace Barotrauma
//clone links between the entities //clone links between the entities
for (int i = 0; i < clones.Count; i++) for (int i = 0; i < clones.Count; i++)
{ {
if (entitiesToClone[i].linkedTo == null) continue; if (entitiesToClone[i].linkedTo == null) { continue; }
foreach (MapEntity linked in entitiesToClone[i].linkedTo) foreach (MapEntity linked in entitiesToClone[i].linkedTo)
{ {
if (!entitiesToClone.Contains(linked)) continue; if (!entitiesToClone.Contains(linked)) { continue; }
clones[i].linkedTo.Add(clones[entitiesToClone.IndexOf(linked)]); clones[i].linkedTo.Add(clones[entitiesToClone.IndexOf(linked)]);
} }
} }
//connect clone wires to the clone items //connect clone wires to the clone items and refresh links between doors and gaps
for (int i = 0; i < clones.Count; i++) for (int i = 0; i < clones.Count; i++)
{ {
var cloneItem = clones[i] as Item; var cloneItem = clones[i] as Item;
if (cloneItem == null) continue; if (cloneItem == null) { continue; }
var door = cloneItem.GetComponent<Door>();
if (door != null) { door.RefreshLinkedGap(); }
var cloneWire = cloneItem.GetComponent<Wire>(); var cloneWire = cloneItem.GetComponent<Wire>();
if (cloneWire == null) continue; if (cloneWire == null) continue;
@@ -231,7 +233,7 @@ namespace Barotrauma
for (int n = 0; n < 2; n++) for (int n = 0; n < 2; n++)
{ {
if (originalWire.Connections[n] == null) continue; if (originalWire.Connections[n] == null) { continue; }
var connectedItem = originalWire.Connections[n].Item; var connectedItem = originalWire.Connections[n].Item;
if (connectedItem == null) continue; if (connectedItem == null) continue;
@@ -555,7 +557,7 @@ namespace Barotrauma
} }
} }
[Serialize(1f, false), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)] [Serialize(1f, true), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)]
public virtual float Scale { get; set; } = 1; public virtual float Scale { get; set; } = 1;
#endregion #endregion
} }
@@ -183,6 +183,29 @@ namespace Barotrauma
} }
} }
protected Vector2 textureScale = Vector2.One;
[Editable(DecimalCount = 3, MinValueFloat = 0.01f, MaxValueFloat = 10f, ValueStep = 0.1f), Serialize("1.0, 1.0", false)]
public Vector2 TextureScale
{
get { return textureScale; }
set
{
textureScale = new Vector2(
MathHelper.Clamp(value.X, 0.01f, 10),
MathHelper.Clamp(value.Y, 0.01f, 10));
}
}
protected Vector2 textureOffset = Vector2.Zero;
[Editable(MinValueFloat = -1000f, MaxValueFloat = 1000f, ValueStep = 10f), Serialize("0.0, 0.0", true)]
public Vector2 TextureOffset
{
get { return textureOffset; }
set { textureOffset = value; }
}
private Rectangle defaultRect; private Rectangle defaultRect;
public override Rectangle Rect public override Rectangle Rect
@@ -296,9 +319,8 @@ namespace Barotrauma
defaultRect = rectangle; defaultRect = rectangle;
rect = rectangle; rect = rectangle;
#if CLIENT
TextureScale = sp.TextureScale; TextureScale = sp.TextureScale;
#endif
spriteColor = prefab.SpriteColor; spriteColor = prefab.SpriteColor;
if (sp.IsHorizontal.HasValue) if (sp.IsHorizontal.HasValue)
{ {
@@ -355,6 +377,8 @@ namespace Barotrauma
} }
InsertToList(); InsertToList();
DebugConsole.Log("Created " + Name + " (" + ID + ")");
} }
partial void InitProjSpecific(); partial void InitProjSpecific();
@@ -365,7 +365,9 @@ namespace Barotrauma
{ {
using (MemoryStream mem = new MemoryStream(Convert.FromBase64String(previewImageData))) using (MemoryStream mem = new MemoryStream(Convert.FromBase64String(previewImageData)))
{ {
PreviewImage = new Sprite(TextureLoader.FromStream(mem, preMultiplyAlpha: false), null, null); var texture = TextureLoader.FromStream(mem, preMultiplyAlpha: false, path: filePath);
if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
PreviewImage = new Sprite(texture, null, null);
} }
} }
catch (Exception e) catch (Exception e)
@@ -382,7 +384,6 @@ namespace Barotrauma
DockedTo = new List<Submarine>(); DockedTo = new List<Submarine>();
ID = ushort.MaxValue;
FreeID(); FreeID();
} }
@@ -1405,7 +1406,7 @@ namespace Barotrauma
} }
ID = (ushort)(ushort.MaxValue - Submarine.loaded.IndexOf(this)); ID = (ushort)(ushort.MaxValue - 1 - Submarine.loaded.IndexOf(this));
} }
public static Submarine Load(XElement element, bool unloadPrevious) public static Submarine Load(XElement element, bool unloadPrevious)
@@ -1553,7 +1554,7 @@ namespace Barotrauma
if (MainSub == this) MainSub = null; if (MainSub == this) MainSub = null;
if (MainSubs[1] == this) MainSubs[1] = null; if (MainSubs[1] == this) MainSubs[1] = null;
DockedTo.Clear(); DockedTo?.Clear();
} }
public void Dispose() public void Dispose()
@@ -125,6 +125,8 @@ namespace Barotrauma
InsertToList(); InsertToList();
WayPointList.Add(this); WayPointList.Add(this);
DebugConsole.Log("Created waypoint (" + ID + ")");
currentHull = Hull.FindHull(WorldPosition); currentHull = Hull.FindHull(WorldPosition);
} }
@@ -48,6 +48,11 @@ namespace Barotrauma.Networking
get { return "ServerSettings"; } get { return "ServerSettings"; }
} }
/// <summary>
/// Have some of the properties listed in the server list changed
/// </summary>
public bool ServerDetailsChanged;
public class SavedClientPermission public class SavedClientPermission
{ {
public readonly string IP; public readonly string IP;
@@ -277,7 +282,17 @@ namespace Barotrauma.Networking
public string ServerName; public string ServerName;
public string ServerMessageText; private string serverMessageText;
public string ServerMessageText
{
get { return serverMessageText; }
set
{
if (serverMessageText == value) { return; }
serverMessageText = value;
ServerDetailsChanged = true;
}
}
public int Port; public int Port;
@@ -370,19 +385,19 @@ namespace Barotrauma.Networking
private set; private set;
} }
private bool allowSpectating;
[Serialize(true, true)] [Serialize(true, true)]
public bool AllowSpectating public bool AllowSpectating
{ {
get; get { return allowSpectating; }
private set; private set
{
if (allowSpectating == value) { return; }
allowSpectating = value;
ServerDetailsChanged = true;
}
} }
[Serialize(true, true)]
public bool VoipEnabled {
get;
private set;
}
[Serialize(true, true)] [Serialize(true, true)]
public bool EndRoundAtLevelEnd public bool EndRoundAtLevelEnd
{ {
@@ -411,11 +426,17 @@ namespace Barotrauma.Networking
private set; private set;
} }
private bool voiceChatEnabled;
[Serialize(true, true)] [Serialize(true, true)]
public bool VoiceChatEnabled public bool VoiceChatEnabled
{ {
get; get { return voiceChatEnabled; }
set; set
{
if (voiceChatEnabled == value) { return; }
voiceChatEnabled = value;
ServerDetailsChanged = true;
}
} }
[Serialize(800, true)] [Serialize(800, true)]
@@ -473,11 +494,17 @@ namespace Barotrauma.Networking
} }
} }
private bool allowRespawn;
[Serialize(true, true)] [Serialize(true, true)]
public bool AllowRespawn public bool AllowRespawn
{ {
get; get { return allowRespawn; ; }
set; set
{
if (allowRespawn == value) { return; }
allowRespawn = value;
ServerDetailsChanged = true;
}
} }
[Serialize(0, true)] [Serialize(0, true)]
@@ -513,10 +540,16 @@ namespace Barotrauma.Networking
set; set;
} }
private YesNoMaybe traitorsEnabled;
public YesNoMaybe TraitorsEnabled public YesNoMaybe TraitorsEnabled
{ {
get; get { return traitorsEnabled; }
set; set
{
if (traitorsEnabled == value) { return; }
traitorsEnabled = value;
ServerDetailsChanged = true;
}
} }
private SelectionMode subSelectionMode; private SelectionMode subSelectionMode;
@@ -528,6 +561,7 @@ namespace Barotrauma.Networking
{ {
subSelectionMode = value; subSelectionMode = value;
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote; Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
} }
} }
@@ -540,6 +574,7 @@ namespace Barotrauma.Networking
{ {
modeSelectionMode = value; modeSelectionMode = value;
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote; Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
} }
} }
@@ -611,6 +646,7 @@ namespace Barotrauma.Networking
public int MaxPlayers public int MaxPlayers
{ {
get { return maxPlayers; } get { return maxPlayers; }
set { maxPlayers = MathHelper.Clamp(value, 1, NetConfig.MaxPlayers); }
} }
public List<MissionType> AllowedRandomMissionTypes public List<MissionType> AllowedRandomMissionTypes
@@ -635,7 +671,14 @@ namespace Barotrauma.Networking
public void SetPassword(string password) public void SetPassword(string password)
{ {
this.password = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password))); if (string.IsNullOrEmpty(password))
{
this.password = "";
}
else
{
this.password = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)));
}
} }
public bool IsPasswordCorrect(string input, int nonce) public bool IsPasswordCorrect(string input, int nonce)
@@ -198,7 +198,7 @@ namespace Voronoi2
{ {
foreach (GraphEdge edge in Edges) foreach (GraphEdge edge in Edges)
{ {
if (MathUtils.LinesIntersect(point, Center, edge.Point1, edge.Point2)) return false; if (MathUtils.LinesIntersect(point, Center, edge.Point1 + Translation, edge.Point2 + Translation)) return false;
} }
return true; return true;
@@ -102,6 +102,14 @@ namespace Barotrauma
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn, bool premultiplyAlpha = true); partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn, bool premultiplyAlpha = true);
partial void CalculateSourceRect(); partial void CalculateSourceRect();
private static void AddToList(Sprite elem)
{
lock (list)
{
list.Add(elem);
}
}
public Sprite(XElement element, string path = "", string file = "", bool? preMultiplyAlpha = null, bool lazyLoad = false) public Sprite(XElement element, string path = "", string file = "", bool? preMultiplyAlpha = null, bool lazyLoad = false)
{ {
this.lazyLoad = lazyLoad; this.lazyLoad = lazyLoad;
@@ -128,7 +136,7 @@ namespace Barotrauma
RelativeOrigin = SourceElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f)); RelativeOrigin = SourceElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
Depth = SourceElement.GetAttributeFloat("depth", 0.001f); Depth = SourceElement.GetAttributeFloat("depth", 0.001f);
ID = GetID(SourceElement); ID = GetID(SourceElement);
list.Add(this); AddToList(this);
} }
internal void LoadParams(SpriteParams spriteParams, bool isFlipped) internal void LoadParams(SpriteParams spriteParams, bool isFlipped)
@@ -147,13 +155,13 @@ namespace Barotrauma
public Sprite(string newFile, Vector2 newOrigin, bool preMultiplyAlpha = true) public Sprite(string newFile, Vector2 newOrigin, bool preMultiplyAlpha = true)
{ {
Init(newFile, newOrigin: newOrigin, preMultiplyAlpha: preMultiplyAlpha); Init(newFile, newOrigin: newOrigin, preMultiplyAlpha: preMultiplyAlpha);
list.Add(this); AddToList(this);
} }
public Sprite(string newFile, Rectangle? sourceRectangle, Vector2? origin = null, float rotation = 0, bool preMultiplyAlpha = true) public Sprite(string newFile, Rectangle? sourceRectangle, Vector2? origin = null, float rotation = 0, bool preMultiplyAlpha = true)
{ {
Init(newFile, sourceRectangle: sourceRectangle, newOrigin: origin, newRotation: rotation, preMultiplyAlpha: preMultiplyAlpha); Init(newFile, sourceRectangle: sourceRectangle, newOrigin: origin, newRotation: rotation, preMultiplyAlpha: preMultiplyAlpha);
list.Add(this); AddToList(this);
} }
private void Init(string newFile, Rectangle? sourceRectangle = null, Vector2? newOrigin = null, Vector2? newOffset = null, float newRotation = 0, private void Init(string newFile, Rectangle? sourceRectangle = null, Vector2? newOrigin = null, Vector2? newOffset = null, float newRotation = 0,
@@ -198,7 +206,10 @@ namespace Barotrauma
public void Remove() public void Remove()
{ {
list.Remove(this); lock (list)
{
list.Remove(this);
}
DisposeTexture(); DisposeTexture();
} }
@@ -640,14 +640,14 @@ namespace Barotrauma
character.LastDamageSource = entity; character.LastDamageSource = entity;
foreach (Limb limb in character.AnimController.Limbs) foreach (Limb limb in character.AnimController.Limbs)
{ {
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f); limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
//only apply non-limb-specific afflictions to the first limb //only apply non-limb-specific afflictions to the first limb
if (!affliction.Prefab.LimbSpecific) { break; } if (!affliction.Prefab.LimbSpecific) { break; }
} }
} }
else if (target is Limb limb) else if (target is Limb limb)
{ {
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f); limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
} }
} }
@@ -311,11 +311,9 @@ namespace Barotrauma
{ {
if (gameSession.Mission is CombatMission combatMission) if (gameSession.Mission is CombatMission combatMission)
{ {
#if CLIENT
//all characters that are alive and in the winning team get an achievement //all characters that are alive and in the winning team get an achievement
UnlockAchievement(gameSession.Mission.Prefab.AchievementIdentifier + (int)GameMain.GameSession.WinningTeam, true, UnlockAchievement(gameSession.Mission.Prefab.AchievementIdentifier + (int)GameMain.GameSession.WinningTeam, true,
c => c != null && !c.IsDead && !c.IsUnconscious && combatMission.IsInWinningTeam(c)); c => c != null && !c.IsDead && !c.IsUnconscious && combatMission.IsInWinningTeam(c));
#endif
} }
else if (gameSession.Mission.Completed) else if (gameSession.Mission.Completed)
{ {
@@ -210,8 +210,8 @@ namespace Barotrauma
/// </summary> /// </summary>
public static float GetMidAngle(float from, float to) public static float GetMidAngle(float from, float to)
{ {
float max = MathHelper.Max(from, to); float max = Math.Max(from, to);
float min = MathHelper.Min(from, to); float min = Math.Min(from, to);
float diff = max - min; float diff = max - min;
if (from < to) if (from < to)
{ {
@@ -378,8 +378,6 @@ namespace Barotrauma
Thread.Sleep(250); Thread.Sleep(250);
} }
} }
return true; return true;
} }
@@ -470,7 +468,20 @@ namespace Barotrauma
foreach (DirectoryInfo di in dir.GetDirectories()) foreach (DirectoryInfo di in dir.GetDirectories())
{ {
ClearFolder(di.FullName, ignoredFileNames); ClearFolder(di.FullName, ignoredFileNames);
di.Delete(); int maxRetries = 4;
for (int i = 0; i <= maxRetries; i++)
{
try
{
di.Delete();
break;
}
catch (IOException)
{
if (i >= maxRetries) { throw; }
Thread.Sleep(250);
}
}
} }
} }
} }
Binary file not shown.

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