f8b0295...0671290
This commit is contained in:
BIN
Barotrauma/BarotraumaClient/Icon.bmp
Normal file
BIN
Barotrauma/BarotraumaClient/Icon.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -31,6 +31,7 @@
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<ApplicationIcon>..\BarotraumaShared\IconBmp.bmp</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
|
||||
@@ -103,6 +104,7 @@
|
||||
<Content Include="OpenTK.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Shaders\solidcolor_opengl.fx" />
|
||||
<Content Include="SharpFont.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -152,13 +154,16 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="app.config" />
|
||||
<Content Include="Shaders\blurshader_opengl.fx" />
|
||||
<Content Include="Shaders\damageshader_opengl.fx" />
|
||||
<Content Include="Shaders\deformshader_opengl.fx" />
|
||||
<Content Include="Shaders\losshader_opengl.fx" />
|
||||
<Content Include="Shaders\postprocess_opengl.fx" />
|
||||
<Content Include="Shaders\watershader_opengl.fx" />
|
||||
<None Include="Content\Effects\solidcolor_opengl.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Shaders\Content_opengl.mgcb" />
|
||||
<None Include="Content\Effects\blurshader_opengl.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@@ -187,6 +192,11 @@
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Icon.bmp">
|
||||
<LogicalName>Icon.bmp</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="ClientCode.projitems" Label="Shared" />
|
||||
<Import Project="..\BarotraumaShared\SharedCode.projitems" Label="Shared" />
|
||||
<Import Project="..\BarotraumaShared\SharedContent.projitems" Label="Shared" />
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
<Content Include="OpenTK.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Shaders\solidcolor_opengl.fx" />
|
||||
<Content Include="SharpFont.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -159,6 +160,9 @@
|
||||
<Content Include="Shaders\losshader_opengl.fx" />
|
||||
<Content Include="Shaders\postprocess_opengl.fx" />
|
||||
<Content Include="Shaders\watershader_opengl.fx" />
|
||||
<None Include="Content\Effects\solidcolor_opengl.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Shaders\Content_opengl.mgcb" />
|
||||
<None Include="Content\Effects\blurshader_opengl.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.8.9.5")]
|
||||
[assembly: AssemblyFileVersion("0.8.9.5")]
|
||||
[assembly: AssemblyVersion("0.8.9.6")]
|
||||
[assembly: AssemblyFileVersion("0.8.9.6")]
|
||||
|
||||
57
Barotrauma/BarotraumaClient/Shaders/Content.mgcb
Normal file
57
Barotrauma/BarotraumaClient/Shaders/Content.mgcb
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
#----------------------------- Global Properties ----------------------------#
|
||||
|
||||
/outputDir:bin/$(Platform)
|
||||
/intermediateDir:obj/$(Platform)
|
||||
/platform:Windows
|
||||
/config:
|
||||
/profile:Reach
|
||||
/compress:False
|
||||
|
||||
#-------------------------------- References --------------------------------#
|
||||
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
||||
#begin blurshader.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:blurshader.fx
|
||||
|
||||
#begin damageshader.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:damageshader.fx
|
||||
|
||||
#begin deformshader.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:deformshader.fx
|
||||
|
||||
#begin losshader.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:losshader.fx
|
||||
|
||||
#begin postprocess.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:postprocess.fx
|
||||
|
||||
#begin watershader.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:watershader.fx
|
||||
|
||||
#begin solidcolor.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:solidcolor.fx
|
||||
|
||||
62
Barotrauma/BarotraumaClient/Shaders/Content_opengl.mgcb
Normal file
62
Barotrauma/BarotraumaClient/Shaders/Content_opengl.mgcb
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#----------------------------- Global Properties ----------------------------#
|
||||
|
||||
/outputDir:bin/$(Platform)
|
||||
/intermediateDir:obj/$(Platform)
|
||||
/platform:DesktopGL
|
||||
/config:
|
||||
/profile:Reach
|
||||
/compress:False
|
||||
|
||||
#-------------------------------- References --------------------------------#
|
||||
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
||||
#begin blurshader_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:blurshader_opengl.fx
|
||||
|
||||
#begin Content_opengl.mgcb
|
||||
/importer:
|
||||
/processor:
|
||||
/build:Content_opengl.mgcb
|
||||
|
||||
#begin damageshader_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:damageshader_opengl.fx
|
||||
|
||||
#begin deformshader_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:deformshader_opengl.fx
|
||||
|
||||
#begin losshader_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:losshader_opengl.fx
|
||||
|
||||
#begin postprocess_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:postprocess_opengl.fx
|
||||
|
||||
#begin solidcolor_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:solidcolor_opengl.fx
|
||||
|
||||
#begin watershader_opengl.fx
|
||||
/importer:EffectImporter
|
||||
/processor:EffectProcessor
|
||||
/processorParam:DebugMode=Auto
|
||||
/build:watershader_opengl.fx
|
||||
|
||||
38
Barotrauma/BarotraumaClient/Shaders/solidcolor.fx
Normal file
38
Barotrauma/BarotraumaClient/Shaders/solidcolor.fx
Normal file
@@ -0,0 +1,38 @@
|
||||
Texture2D xTexture;
|
||||
sampler TextureSampler = sampler_state { Texture = <xTexture>; };
|
||||
|
||||
float blurDistance;
|
||||
float4 color;
|
||||
|
||||
float4 solidColor(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float a = tex2D(TextureSampler, texCoord).a;
|
||||
return color * a;
|
||||
}
|
||||
|
||||
float4 solidColorBlur(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float sample;
|
||||
sample = tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y + blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y - blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y - blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y + blurDistance)).a;
|
||||
sample = sample * 0.25f;
|
||||
|
||||
return color * sample;
|
||||
}
|
||||
|
||||
technique SolidColor
|
||||
{
|
||||
pass Pass1
|
||||
{
|
||||
PixelShader = compile ps_4_0_level_9_1 solidColor();
|
||||
}
|
||||
}
|
||||
technique SolidColorBlur
|
||||
{
|
||||
pass Pass1
|
||||
{
|
||||
PixelShader = compile ps_4_0_level_9_1 solidColorBlur();
|
||||
}
|
||||
}
|
||||
38
Barotrauma/BarotraumaClient/Shaders/solidcolor_opengl.fx
Normal file
38
Barotrauma/BarotraumaClient/Shaders/solidcolor_opengl.fx
Normal file
@@ -0,0 +1,38 @@
|
||||
Texture xTexture;
|
||||
sampler TextureSampler = sampler_state { Texture = <xTexture>; };
|
||||
|
||||
float blurDistance;
|
||||
float4 color;
|
||||
|
||||
float4 solidColor(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float a = tex2D(TextureSampler, texCoord).a;
|
||||
return color * a;
|
||||
}
|
||||
|
||||
float4 solidColorBlur(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float sample;
|
||||
sample = tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y + blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y - blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y - blurDistance)).a;
|
||||
sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y + blurDistance)).a;
|
||||
sample = sample * 0.25f;
|
||||
|
||||
return color * sample;
|
||||
}
|
||||
|
||||
technique SolidColor
|
||||
{
|
||||
pass Pass1
|
||||
{
|
||||
PixelShader = compile ps_3_0 solidColor();
|
||||
}
|
||||
}
|
||||
technique SolidColorBlur
|
||||
{
|
||||
pass Pass1
|
||||
{
|
||||
PixelShader = compile ps_3_0 solidColorBlur();
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ namespace Barotrauma
|
||||
GUI.Style.FocusIndicator.Draw(spriteBatch,
|
||||
(int)((focusedItemOverlayTimer - 1.0f) * GUI.Style.FocusIndicator.FrameCount * 3.0f),
|
||||
circlePos,
|
||||
Color.Orange * 0.3f,
|
||||
Color.LightBlue * 0.3f,
|
||||
origin: GUI.Style.FocusIndicator.FrameSize.ToVector2() / 2,
|
||||
rotate: (float)Timing.TotalTime,
|
||||
scale: scale);
|
||||
@@ -274,9 +274,7 @@ namespace Barotrauma
|
||||
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont);
|
||||
textPos.Y += 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
foreach (HUDProgressBar progressBar in character.HUDProgressBars.Values)
|
||||
|
||||
@@ -544,7 +544,10 @@ namespace Barotrauma
|
||||
{
|
||||
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green);
|
||||
healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f;
|
||||
healthBar.BarSize = healthWindowHealthBar.BarSize = (Vitality > 0.0f) ? Vitality / MaxVitality : 1.0f - Vitality / MinVitality;
|
||||
healthBar.BarSize = healthWindowHealthBar.BarSize =
|
||||
(Vitality > 0.0f) ?
|
||||
(MaxVitality > 0.0f ? Vitality / MaxVitality : 0.0f) :
|
||||
(Math.Abs(MinVitality) > 0.0f ? 1.0f - Vitality / MinVitality : 0.0f);
|
||||
|
||||
if (healthBarPulsateTimer > 0.0f)
|
||||
{
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Barotrauma
|
||||
character.ID = id;
|
||||
character.TeamID = (TeamType)teamID;
|
||||
|
||||
if (configPath == HumanConfigFile)
|
||||
if (configPath == HumanConfigFile && character.TeamID != TeamType.FriendlyNPC)
|
||||
{
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
|
||||
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -210,12 +210,7 @@ namespace Barotrauma
|
||||
public void SetWindowMode(WindowMode windowMode)
|
||||
{
|
||||
WindowMode = windowMode;
|
||||
#if !(OSX)
|
||||
GraphicsDeviceManager.HardwareModeSwitch = Config.WindowMode != WindowMode.BorderlessWindowed;
|
||||
#else
|
||||
// Force borderless on macOS.
|
||||
GraphicsDeviceManager.HardwareModeSwitch = Config.WindowMode != WindowMode.BorderlessWindowed && Config.WindowMode != WindowMode.Fullscreen;
|
||||
#endif
|
||||
GraphicsDeviceManager.IsFullScreen = Config.WindowMode == WindowMode.Fullscreen || Config.WindowMode == WindowMode.BorderlessWindowed;
|
||||
Window.IsBorderless = !GraphicsDeviceManager.HardwareModeSwitch;
|
||||
|
||||
|
||||
@@ -61,120 +61,9 @@ namespace Barotrauma
|
||||
public CrewManager(XElement element, bool isSinglePlayer)
|
||||
: this(isSinglePlayer)
|
||||
{
|
||||
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent)
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
Point scrollButtonSize = new Point((int)(200 * GUI.Scale), (int)(30 * GUI.Scale));
|
||||
|
||||
crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), "", Color.Transparent)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
toggleCrewButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)
|
||||
{ AbsoluteOffset = HUDLayoutSettings.CrewArea.Location },
|
||||
"", style: "UIToggleButton");
|
||||
toggleCrewButton.OnClicked += (GUIButton btn, object userdata) =>
|
||||
{
|
||||
toggleCrewAreaOpen = !toggleCrewAreaOpen;
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
characterListBox = new GUIListBox(new RectTransform(new Point(100, (int)(crewArea.Rect.Height - scrollButtonSize.Y * 1.6f)), crewArea.RectTransform, Anchor.CenterLeft), false, Color.Transparent, null)
|
||||
{
|
||||
//Spacing = (int)(3 * GUI.Scale),
|
||||
ScrollBarEnabled = false,
|
||||
ScrollBarVisible = false,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
scrollButtonUp = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.TopLeft, Pivot.TopLeft), "", Alignment.Center, "GUIButtonVerticalArrow")
|
||||
{
|
||||
Visible = false,
|
||||
UserData = -1,
|
||||
OnClicked = ScrollCharacterList
|
||||
};
|
||||
scrollButtonDown = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), "", Alignment.Center, "GUIButtonVerticalArrow")
|
||||
{
|
||||
Visible = false,
|
||||
UserData = 1,
|
||||
OnClicked = ScrollCharacterList
|
||||
};
|
||||
scrollButtonDown.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipVertically);
|
||||
|
||||
if (isSinglePlayer)
|
||||
{
|
||||
chatBox = new ChatBox(guiFrame, isSinglePlayer: true)
|
||||
{
|
||||
OnEnterMessage = (textbox, text) =>
|
||||
{
|
||||
if (Character.Controlled == null) { return true; }
|
||||
|
||||
textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
|
||||
AddSinglePlayerChatMessage(
|
||||
Character.Controlled.Info.Name,
|
||||
msg,
|
||||
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
|
||||
Character.Controlled);
|
||||
var headset = GetHeadset(Character.Controlled, true);
|
||||
if (headset != null && headset.CanTransmit())
|
||||
{
|
||||
headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sendToChat: false);
|
||||
}
|
||||
}
|
||||
textbox.Deselect();
|
||||
textbox.Text = "";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
chatBox.InputBox.OnTextChanged += chatBox.TypingChatMessage;
|
||||
}
|
||||
|
||||
var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null);
|
||||
reportButtonFrame = new GUILayoutGroup(new RectTransform(
|
||||
new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = (int)(5 * GUI.Scale),
|
||||
UserData = "reportbuttons",
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
//report buttons
|
||||
foreach (Order order in reports)
|
||||
{
|
||||
if (!order.TargetAllCharacters || order.SymbolSprite == null) continue;
|
||||
var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null)
|
||||
{
|
||||
OnClicked = (GUIButton button, object userData) =>
|
||||
{
|
||||
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false;
|
||||
SetCharacterOrder(null, order, null, Character.Controlled);
|
||||
HumanAIController.PropagateHullSafety(Character.Controlled, Character.Controlled.CurrentHull);
|
||||
return true;
|
||||
},
|
||||
UserData = order,
|
||||
ToolTip = order.Name
|
||||
};
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow")
|
||||
{
|
||||
Color = Color.Red * 0.8f,
|
||||
HoverColor = Color.Red * 1.0f,
|
||||
PressedColor = Color.Red * 0.6f,
|
||||
UserData = "highlighted",
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "character") continue;
|
||||
|
||||
var characterInfo = new CharacterInfo(subElement);
|
||||
characterInfos.Add(characterInfo);
|
||||
@@ -185,20 +74,6 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
|
||||
prevUIScale = GUI.Scale;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Character list management
|
||||
|
||||
public Rectangle GetCharacterListArea()
|
||||
{
|
||||
return characterListBox.Rect;
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
@@ -713,7 +588,7 @@ namespace Barotrauma
|
||||
characterListBox.BarScroll -= characterListBox.BarScroll % step;
|
||||
characterListBox.BarScroll += dir * step;
|
||||
|
||||
return radioItem.GetComponent<WifiComponent>();
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<object> KillCharacterAnim(GUIComponent component)
|
||||
@@ -726,12 +601,6 @@ namespace Barotrauma
|
||||
{
|
||||
comp.Color = Color.DarkRed;
|
||||
}
|
||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||
c.AIController is HumanAIController &&
|
||||
!c.IsDead &&
|
||||
c.SpeechImpediment <= 100.0f);
|
||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(1.0f);
|
||||
|
||||
@@ -1384,16 +1253,6 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember != null) GameMain.Client.SelectCrewCharacter(character, previewPlayer);
|
||||
|
||||
private bool ReportButtonClicked(GUIButton button, object userData)
|
||||
{
|
||||
//order targeted to all characters
|
||||
Order order = userData as Order;
|
||||
if (order.TargetAllCharacters)
|
||||
{
|
||||
if (Character.Controlled == null || Character.Controlled.CurrentHull == null) return false;
|
||||
AddOrder(new Order(order.Prefab, Character.Controlled.CurrentHull, null), order.Prefab.FadeOutTime);
|
||||
SetCharacterOrder(null, order, "", Character.Controlled);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -229,7 +230,10 @@ namespace Barotrauma
|
||||
campaign.lastUpdateID = updateID;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -141,9 +141,6 @@ namespace Barotrauma
|
||||
infoFrame?.UpdateManually(deltaTime);
|
||||
}
|
||||
|
||||
infoFrame?.UpdateManually(deltaTime);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma
|
||||
//if "%" is found
|
||||
if (index > 0)
|
||||
{
|
||||
while (true)
|
||||
while (index > 0)
|
||||
{
|
||||
//search for end of label
|
||||
index -= 1;
|
||||
@@ -439,7 +439,6 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
var radioButtonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), voiceSettings.RectTransform))
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -458,6 +457,24 @@ namespace Barotrauma
|
||||
voiceMode.AddRadioButton((VoiceMode)i, tick);
|
||||
}
|
||||
|
||||
var micVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.18f), voiceSettings.RectTransform), TextManager.Get("MicrophoneVolume"));
|
||||
var micVolumeSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.18f), voiceSettings.RectTransform),
|
||||
barSize: 0.1f)
|
||||
{
|
||||
UserData = micVolumeText,
|
||||
BarScroll = (float)Math.Sqrt(MathUtils.InverseLerp(0.2f, 5.0f, MicrophoneVolume)),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
MicrophoneVolume = MathHelper.Lerp(0.2f, 5.0f, scroll * scroll);
|
||||
MicrophoneVolume = (float)Math.Round(MicrophoneVolume, 1);
|
||||
ChangeSliderText(scrollBar, MicrophoneVolume);
|
||||
scrollBar.Step = 0.05f;
|
||||
return true;
|
||||
},
|
||||
Step = 0.05f
|
||||
};
|
||||
micVolumeSlider.OnMoved(micVolumeSlider, micVolumeSlider.BarScroll);
|
||||
|
||||
var voiceInputContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.2f), voiceSettings.RectTransform, Anchor.BottomCenter));
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), voiceInputContainer.RectTransform), TextManager.Get("InputType.Voice") + ": ");
|
||||
var voiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), voiceInputContainer.RectTransform, Anchor.TopRight),
|
||||
@@ -469,7 +486,6 @@ namespace Barotrauma
|
||||
voiceKeyBox.SelectedColor = Color.Gold * 0.3f;
|
||||
|
||||
var voiceActivityGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), voiceSettings.RectTransform));
|
||||
|
||||
GUITextBlock noiseGateText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), voiceActivityGroup.RectTransform), TextManager.Get("NoiseGateThreshold"))
|
||||
{
|
||||
TextGetter = () =>
|
||||
@@ -509,13 +525,13 @@ namespace Barotrauma
|
||||
if (GameMain.Client == null && VoipCapture.Instance == null)
|
||||
{
|
||||
VoipCapture.Create(GameMain.Config.VoiceCaptureDevice);
|
||||
}
|
||||
if (VoipCapture.Instance == null)
|
||||
{
|
||||
VoiceSetting = vMode = VoiceMode.Disabled;
|
||||
voiceInputContainer.Visible = false;
|
||||
voiceActivityGroup.Visible = false;
|
||||
return;
|
||||
if (VoipCapture.Instance == null)
|
||||
{
|
||||
VoiceSetting = vMode = VoiceMode.Disabled;
|
||||
voiceInputContainer.Visible = false;
|
||||
voiceActivityGroup.Visible = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -615,16 +631,6 @@ namespace Barotrauma
|
||||
.Replace("[missingfiletypes]", string.Join(", ", missingContentTypes));
|
||||
}
|
||||
}
|
||||
languageDD.SelectItem(TextManager.Language);
|
||||
languageDD.OnSelected = (guiComponent, obj) =>
|
||||
{
|
||||
string newLanguage = obj as string;
|
||||
if (newLanguage == Language) return true;
|
||||
|
||||
UnsavedSettings = true;
|
||||
Language = newLanguage;
|
||||
|
||||
new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage"));
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null);
|
||||
|
||||
@@ -25,15 +25,6 @@ namespace Barotrauma.Items.Components
|
||||
public readonly ActionType Type;
|
||||
|
||||
public string VolumeProperty;
|
||||
public float VolumeMultiplier
|
||||
{
|
||||
get { return RoundSound.Volume; }
|
||||
}
|
||||
|
||||
public float Range
|
||||
{
|
||||
get { return RoundSound.Range; }
|
||||
}
|
||||
|
||||
public float VolumeMultiplier
|
||||
{
|
||||
@@ -198,20 +189,20 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
|
||||
{
|
||||
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
|
||||
new Vector3(position.X, position.Y, 0.0f),
|
||||
GetSoundVolume(loopingSound),
|
||||
0.01f,
|
||||
SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
//TODO: tweak
|
||||
@@ -226,7 +217,9 @@ namespace Barotrauma.Items.Components
|
||||
lastMuffleCheckTime = (float)Timing.TotalTime;
|
||||
}
|
||||
loopingSoundChannel.Muffled = shouldMuffleLooping;
|
||||
loopingSoundChannel.Gain = GetSoundVolume(loopingSound);
|
||||
float targetGain = GetSoundVolume(loopingSound);
|
||||
float gainDiff = targetGain - loopingSoundChannel.Gain;
|
||||
loopingSoundChannel.Gain += Math.Abs(gainDiff) < 0.1f ? gainDiff : Math.Sign(gainDiff) * 0.1f;
|
||||
loopingSoundChannel.Position = new Vector3(position.X, position.Y, 0.0f);
|
||||
}
|
||||
return;
|
||||
@@ -279,13 +272,13 @@ namespace Barotrauma.Items.Components
|
||||
loopingSound = itemSound;
|
||||
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
|
||||
{
|
||||
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
|
||||
new Vector3(position.X, position.Y, 0.0f),
|
||||
GetSoundVolume(loopingSound),
|
||||
0.01f,
|
||||
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
//TODO: tweak
|
||||
@@ -309,7 +302,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.Dispose();
|
||||
loopingSoundChannel.FadeOutAndDispose();
|
||||
loopingSoundChannel = null;
|
||||
loopingSound = null;
|
||||
}
|
||||
|
||||
@@ -392,12 +392,12 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 pos = new Vector2(rect.Center.X, rect.Y + meterSprite.Origin.Y * scale);
|
||||
|
||||
Vector2 optimalRangeNormalized = new Vector2(
|
||||
(optimalRange.X - range.X) / (range.Y - range.X),
|
||||
(optimalRange.Y - range.X) / (range.Y - range.X));
|
||||
MathHelper.Clamp((optimalRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f),
|
||||
MathHelper.Clamp((optimalRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f));
|
||||
|
||||
Vector2 allowedRangeNormalized = new Vector2(
|
||||
(allowedRange.X - range.X) / (range.Y - range.X),
|
||||
(allowedRange.Y - range.X) / (range.Y - range.X));
|
||||
MathHelper.Clamp((allowedRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f),
|
||||
MathHelper.Clamp((allowedRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f));
|
||||
|
||||
Vector2 sectorRad = new Vector2(-1.57f, 1.57f);
|
||||
|
||||
@@ -417,10 +417,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
spriteBatch.End();
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0,0,GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale));
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
|
||||
|
||||
sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2, scale);
|
||||
sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale);
|
||||
|
||||
sectorSprite.Draw(spriteBatch, pos, Color.Orange, optimalSectorRad.X, scale);
|
||||
sectorSprite.Draw(spriteBatch, pos, Color.Red, allowedSectorRad.X, scale);
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (moveSound != null)
|
||||
{
|
||||
moveSoundChannel.Dispose();
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, moveSound.Volume, moveSound.Range, item.WorldPosition);
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = true;
|
||||
}
|
||||
@@ -168,13 +168,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound)
|
||||
{
|
||||
moveSoundChannel.Dispose();
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, endMoveSound.Volume, endMoveSound.Range, item.WorldPosition);
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = false;
|
||||
}
|
||||
else if (!moveSoundChannel.IsPlaying)
|
||||
{
|
||||
moveSoundChannel.Dispose();
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = null;
|
||||
|
||||
}
|
||||
@@ -288,7 +288,10 @@ namespace Barotrauma.Items.Components
|
||||
availableAmmo.AddRange(itemContainer.Inventory.Items);
|
||||
}
|
||||
|
||||
float chargeRate = powerConsumption <= 0.0f ? 1.0f : batteryCharge / batteryCapacity;
|
||||
float chargeRate =
|
||||
powerConsumption <= 0.0f ?
|
||||
1.0f :
|
||||
batteryCapacity > 0.0f ? batteryCharge / batteryCapacity : 0.0f;
|
||||
bool charged = batteryCharge * 3600.0f > powerConsumption;
|
||||
bool readyToFire = reload <= 0.0f && charged && availableAmmo.Any(p => p != null);
|
||||
if (ShowChargeIndicator && PowerConsumption > 0.0f)
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace Barotrauma
|
||||
if (!Visible || (!editing && hiddenInGame)) return;
|
||||
if (editing && !ShowItems) return;
|
||||
|
||||
Color color = isHighlighted && !GUI.DisableItemHighlights ? Color.Orange : GetSpriteColor();
|
||||
Color color = isHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? Color.Orange : GetSpriteColor();
|
||||
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
|
||||
|
||||
Sprite activeSprite = prefab.sprite;
|
||||
@@ -410,8 +410,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateEditing(Camera cam)
|
||||
@@ -483,17 +482,44 @@ namespace Barotrauma
|
||||
linkText.TextColor = Color.Yellow;
|
||||
itemsText.TextColor = Color.Yellow;
|
||||
}
|
||||
if (!inGame && Sprite != null)
|
||||
|
||||
if (!inGame)
|
||||
{
|
||||
var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite"));
|
||||
reloadTextureButton.OnClicked += (button, data) =>
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, 20)), isHorizontal: true)
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
return true;
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
itemEditor.AddCustomContent(reloadTextureButton, itemEditor.ContentCount);
|
||||
}
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipX(relativeToSub: false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipY(relativeToSub: false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (Sprite != null)
|
||||
{
|
||||
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"));
|
||||
reloadTextureButton.OnClicked += (button, data) =>
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
itemEditor.AddCustomContent(buttonContainer, itemEditor.ContentCount);
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
|
||||
@@ -599,61 +599,5 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime)
|
||||
{
|
||||
float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
|
||||
float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
|
||||
bool hasFireSources = message.ReadBoolean();
|
||||
int fireSourceCount = 0;
|
||||
List<Vector3> newFireSources = new List<Vector3>();
|
||||
if (hasFireSources)
|
||||
{
|
||||
fireSourceCount = message.ReadRangedInteger(0, 16);
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
newFireSources.Add(new Vector3(
|
||||
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
message.ReadRangedSingle(0.0f, 1.0f, 8)));
|
||||
}
|
||||
}
|
||||
|
||||
if (serverUpdateDelay > 0.0f) { return; }
|
||||
|
||||
WaterVolume = newWaterVolume;
|
||||
OxygenPercentage = newOxygenPercentage;
|
||||
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
Vector2 pos = new Vector2(
|
||||
rect.X + rect.Width * newFireSources[i].X,
|
||||
rect.Y - rect.Height + (rect.Height * newFireSources[i].Y));
|
||||
float size = newFireSources[i].Z * rect.Width;
|
||||
|
||||
var newFire = i < FireSources.Count ?
|
||||
FireSources[i] :
|
||||
new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
|
||||
newFire.Position = pos;
|
||||
newFire.Size = new Vector2(size, newFire.Size.Y);
|
||||
|
||||
//ignore if the fire wasn't added to this room (invalid position)?
|
||||
if (!FireSources.Contains(newFire))
|
||||
{
|
||||
newFire.Remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = FireSources.Count - 1; i >= fireSourceCount; i--)
|
||||
{
|
||||
FireSources[i].Remove();
|
||||
if (i < FireSources.Count)
|
||||
{
|
||||
FireSources.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (SoundChannels[i] != null && SoundChannels[i].IsPlaying)
|
||||
{
|
||||
SoundChannels[i].Dispose();
|
||||
SoundChannels[i].FadeOutAndDispose();
|
||||
SoundChannels[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,19 @@ namespace Barotrauma.Lights
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public RenderTarget2D HighlightMap
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private readonly Texture2D highlightRaster;
|
||||
|
||||
private BasicEffect lightEffect;
|
||||
|
||||
public Effect LosEffect { get; private set; }
|
||||
|
||||
public Effect SolidColorEffect { get; private set; }
|
||||
|
||||
private List<LightSource> lights;
|
||||
|
||||
public bool LosEnabled = true;
|
||||
@@ -58,7 +66,6 @@ namespace Barotrauma.Lights
|
||||
public bool ObstructVision;
|
||||
|
||||
private Texture2D visionCircle;
|
||||
|
||||
|
||||
private Dictionary<Hull, Color> hullAmbientLights;
|
||||
private Dictionary<Hull, Color> smoothedHullAmbientLights;
|
||||
@@ -73,6 +80,8 @@ namespace Barotrauma.Lights
|
||||
|
||||
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
|
||||
|
||||
highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png");
|
||||
|
||||
CreateRenderTargets(graphics);
|
||||
GameMain.Instance.OnResolutionChanged += () =>
|
||||
{
|
||||
@@ -81,8 +90,10 @@ namespace Barotrauma.Lights
|
||||
|
||||
#if WINDOWS
|
||||
LosEffect = content.Load<Effect>("Effects/losshader");
|
||||
SolidColorEffect = content.Load<Effect>("Effects/solidcolor");
|
||||
#else
|
||||
LosEffect = content.Load<Effect>("Effects/losshader_opengl");
|
||||
SolidColorEffect = content.Load<Effect>("Effects/solidcolor_opengl");
|
||||
#endif
|
||||
|
||||
if (lightEffect == null)
|
||||
@@ -117,6 +128,12 @@ namespace Barotrauma.Lights
|
||||
pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount,
|
||||
RenderTargetUsage.DiscardContents);
|
||||
|
||||
HighlightMap?.Dispose();
|
||||
HighlightMap = new RenderTarget2D(graphics,
|
||||
(int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale), (int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false,
|
||||
pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount,
|
||||
RenderTargetUsage.DiscardContents);
|
||||
|
||||
LosTexture?.Dispose();
|
||||
LosTexture = new RenderTarget2D(graphics, (int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale), (int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false, SurfaceFormat.Color, DepthFormat.None);
|
||||
}
|
||||
@@ -176,7 +193,7 @@ namespace Barotrauma.Lights
|
||||
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
|
||||
{
|
||||
if (!LightingEnabled) return;
|
||||
|
||||
|
||||
if (Math.Abs(currLightMapScale - GameMain.Config.LightMapScale) > 0.01f)
|
||||
{
|
||||
//lightmap scale has changed -> recreate render targets
|
||||
@@ -187,6 +204,8 @@ namespace Barotrauma.Lights
|
||||
Matrix transform = cam.ShaderTransform
|
||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
||||
|
||||
bool highlightsVisible = UpdateHighlights(graphics, spriteBatch, spriteBatchTransform, cam);
|
||||
|
||||
if (GameMain.Config.SpecularityEnabled)
|
||||
{
|
||||
UpdateSpecularMap(graphics, spriteBatch, spriteBatchTransform, cam, backgroundObstructor);
|
||||
@@ -211,7 +230,6 @@ namespace Barotrauma.Lights
|
||||
graphics.Clear(Color.Black);
|
||||
graphics.BlendState = BlendState.Additive;
|
||||
|
||||
|
||||
//draw background lights
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool backgroundSpritesDrawn = false;
|
||||
@@ -260,31 +278,7 @@ namespace Barotrauma.Lights
|
||||
//draw the focused item and character to highlight them,
|
||||
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (Character.Controlled.FocusedItem != null)
|
||||
{
|
||||
Character.Controlled.FocusedItem.IsHighlighted = true;
|
||||
}
|
||||
if (Character.Controlled.FocusedCharacter != null)
|
||||
{
|
||||
Character.Controlled.FocusedCharacter.Draw(spriteBatch, cam);
|
||||
}
|
||||
|
||||
if (!GUI.DisableItemHighlights)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.IsHighlighted)
|
||||
{
|
||||
item.Draw(spriteBatch, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
foreach (LightSource light in activeLights)
|
||||
{
|
||||
if (light.IsBackground) continue;
|
||||
@@ -292,6 +286,13 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
if (highlightsVisible)
|
||||
{
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
|
||||
spriteBatch.Draw(HighlightMap, Vector2.Zero, Color.White);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
//draw characters to obstruct the highlighted items/characters and light sprites
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, transformMatrix: spriteBatchTransform);
|
||||
@@ -377,13 +378,95 @@ namespace Barotrauma.Lights
|
||||
graphics.BlendState = BlendState.AlphaBlend;
|
||||
}
|
||||
|
||||
private readonly List<Entity> highlightedEntities = new List<Entity>();
|
||||
|
||||
private bool UpdateHighlights(GraphicsDevice graphics, SpriteBatch spriteBatch, Matrix spriteBatchTransform, Camera cam)
|
||||
{
|
||||
if (GUI.DisableItemHighlights) { return false; }
|
||||
|
||||
highlightedEntities.Clear();
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (Character.Controlled.FocusedItem != null)
|
||||
{
|
||||
highlightedEntities.Add(Character.Controlled.FocusedItem);
|
||||
}
|
||||
if (Character.Controlled.FocusedCharacter != null)
|
||||
{
|
||||
highlightedEntities.Add(Character.Controlled.FocusedCharacter);
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.IsHighlighted && !highlightedEntities.Contains(item))
|
||||
{
|
||||
highlightedEntities.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (highlightedEntities.Count == 0) { return false; }
|
||||
|
||||
//draw characters in light blue first
|
||||
graphics.SetRenderTarget(HighlightMap);
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
|
||||
SolidColorEffect.Parameters["color"].SetValue(Color.LightBlue.ToVector4());
|
||||
SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidColor"];
|
||||
DeformableSprite.Effect.Parameters["solidColor"].SetValue(Color.LightBlue.ToVector4());
|
||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||
foreach (Entity highlighted in highlightedEntities)
|
||||
{
|
||||
if (highlighted is Item item)
|
||||
{
|
||||
item.Draw(spriteBatch, false, true);
|
||||
}
|
||||
else if (highlighted is Character character)
|
||||
{
|
||||
character.Draw(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
//draw characters in black with a bit of blur, leaving the white edges visible
|
||||
float phase = (float)(Math.Sin(Timing.TotalTime * 3.0f) + 1.0f) / 2.0f; //phase oscillates between 0 and 1
|
||||
Vector4 overlayColor = Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase);
|
||||
SolidColorEffect.Parameters["color"].SetValue(overlayColor);
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColorBlur"];
|
||||
SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
DeformableSprite.Effect.Parameters["solidColor"].SetValue(overlayColor);
|
||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||
foreach (Entity highlighted in highlightedEntities)
|
||||
{
|
||||
if (highlighted is Item item)
|
||||
{
|
||||
SolidColorEffect.Parameters["blurDistance"].SetValue(0.02f);
|
||||
item.Draw(spriteBatch, false, true);
|
||||
}
|
||||
else if (highlighted is Character character)
|
||||
{
|
||||
SolidColorEffect.Parameters["blurDistance"].SetValue(0.05f);
|
||||
character.Draw(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
//raster pattern on top of everything
|
||||
spriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap);
|
||||
spriteBatch.Draw(highlightRaster, new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), Color.White * 0.5f);
|
||||
spriteBatch.End();
|
||||
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateSpecularMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Matrix spriteBatchTransform, Camera cam, RenderTarget2D backgroundObstructor = null)
|
||||
{
|
||||
graphics.SetRenderTarget(SpecularMap);
|
||||
|
||||
//clear the lightmap
|
||||
graphics.Clear(Color.Black);
|
||||
graphics.Clear(Color.Gray);
|
||||
graphics.BlendState = BlendState.AlphaBlend;
|
||||
|
||||
spriteBatch.Begin(sortMode: SpriteSortMode.Deferred, blendState: BlendState.AlphaBlend, transformMatrix: spriteBatchTransform);
|
||||
@@ -394,20 +477,31 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
//TODO: specular maps for level walls
|
||||
Level.Loaded?.Renderer?.RenderWalls(graphics, cam, specular: true);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidColor"];
|
||||
DeformableSprite.Effect.Parameters["solidColor"].SetValue(Color.Gray.ToVector4());
|
||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||
|
||||
//obstruct specular maps behind the sub and characters by drawing them on the map in solid gray
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
|
||||
SolidColorEffect.Parameters["color"].SetValue(Color.Gray.ToVector4());
|
||||
SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: SolidColorEffect);
|
||||
if (backgroundObstructor != null)
|
||||
{
|
||||
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
|
||||
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
|
||||
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.White);
|
||||
}
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Enabled) { c.Draw(spriteBatch, cam); }
|
||||
}
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0,
|
||||
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)),
|
||||
Color.White * 0.4f, isFilled: true);
|
||||
spriteBatch.End();
|
||||
|
||||
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
|
||||
|
||||
graphics.SetRenderTarget(null);
|
||||
graphics.BlendState = BlendState.AlphaBlend;
|
||||
}
|
||||
|
||||
@@ -549,7 +549,6 @@ namespace Barotrauma
|
||||
|
||||
if ((PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)))
|
||||
{
|
||||
//TODO: a UI button for flipping entities
|
||||
if (PlayerInput.KeyHit(Keys.N))
|
||||
{
|
||||
float minX = selectedList[0].WorldRect.X, maxX = selectedList[0].WorldRect.Right;
|
||||
|
||||
@@ -92,15 +92,41 @@ namespace Barotrauma
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null);
|
||||
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, elementHeight: 20);
|
||||
var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite"));
|
||||
reloadTextureButton.OnClicked += (button, data) =>
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
return true;
|
||||
};
|
||||
editor.AddCustomContent(reloadTextureButton, editor.ContentCount);
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, 20)), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipX(relativeToSub: false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipY(relativeToSub: false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"))
|
||||
{
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
editor.AddCustomContent(buttonContainer, editor.ContentCount);
|
||||
|
||||
PositionEditingHUD();
|
||||
|
||||
return editingHUD;
|
||||
|
||||
@@ -405,6 +405,11 @@ namespace Barotrauma
|
||||
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
|
||||
}
|
||||
|
||||
if (Gap.GapList.Any(g => g.linkedTo.Count == 0))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning"));
|
||||
}
|
||||
|
||||
if (errorMsgs.Any())
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("Warning"), string.Join("\n\n", errorMsgs), 400, 0);
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get { return fileReceiver; }
|
||||
}
|
||||
|
||||
|
||||
public bool MidRoundSyncing
|
||||
{
|
||||
get { return entityEventManager.MidRoundSyncing; }
|
||||
@@ -321,18 +321,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
// Loop until we are approved
|
||||
//TODO: show the name of the server instead of IP when connecting through the server list (more streamer-friendly)
|
||||
string connectingText = TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP);
|
||||
string connectingText = TextManager.Get("Connecting");
|
||||
while (!CanStart && !connectCancelled)
|
||||
{
|
||||
if (reconnectBox == null)
|
||||
{
|
||||
reconnectBox = new GUIMessageBox(TextManager.Get("Connecting"), connectingText, new string[] { TextManager.Get("Cancel") });
|
||||
|
||||
reconnectBox = new GUIMessageBox(connectingText, TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP), new string[] { TextManager.Get("Cancel") });
|
||||
reconnectBox.Buttons[0].OnClicked += CancelConnect;
|
||||
reconnectBox.Buttons[0].OnClicked += reconnectBox.Close;
|
||||
}
|
||||
|
||||
reconnectBox.Text.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1));
|
||||
reconnectBox.Header.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1));
|
||||
|
||||
if (DateTime.Now > reqAuthTime)
|
||||
{
|
||||
@@ -540,6 +539,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
}
|
||||
connected = true;
|
||||
chatBox.InputBox.Enabled = true;
|
||||
if (GameMain.NetLobbyScreen?.TextBox != null)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TextBox.Enabled = true;
|
||||
@@ -832,6 +832,8 @@ namespace Barotrauma.Networking
|
||||
DisconnectReason disconnectReason = DisconnectReason.Unknown;
|
||||
if (splitMsg.Length > 0) Enum.TryParse(splitMsg[0], out disconnectReason);
|
||||
|
||||
DebugConsole.NewMessage("Received a disconnect message (" + disconnectMsg + ")");
|
||||
|
||||
if (disconnectReason == DisconnectReason.ServerFull)
|
||||
{
|
||||
//already waiting for a slot to free up, do nothing
|
||||
@@ -861,22 +863,19 @@ namespace Barotrauma.Networking
|
||||
waitInServerQueueBox = null;
|
||||
CoroutineManager.StopCoroutines("WaitInServerQueue");
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = "";
|
||||
if (disconnectReason == DisconnectReason.Unknown)
|
||||
{
|
||||
msg = disconnectMsg;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString());
|
||||
|
||||
if (allowReconnect && disconnectReason == DisconnectReason.Unknown)
|
||||
{
|
||||
DebugConsole.NewMessage("Attempting to reconnect...");
|
||||
|
||||
string msg = TextManager.GetServerMessage(disconnectMsg);
|
||||
msg = string.IsNullOrWhiteSpace(msg) ?
|
||||
TextManager.Get("ConnectionLostReconnecting") :
|
||||
msg + '\n' + TextManager.Get("ConnectionLostReconnecting");
|
||||
|
||||
reconnectBox = new GUIMessageBox(
|
||||
TextManager.Get("ConnectionLost"),
|
||||
TextManager.Get("ConnectionLostReconnecting"), new string[0]);
|
||||
msg, new string[0]);
|
||||
connected = false;
|
||||
ConnectToServer(serverIP);
|
||||
}
|
||||
@@ -885,10 +884,12 @@ namespace Barotrauma.Networking
|
||||
string msg = "";
|
||||
if (disconnectReason == DisconnectReason.Unknown)
|
||||
{
|
||||
DebugConsole.NewMessage("Do not attempt reconnect (not allowed).");
|
||||
msg = disconnectMsg;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.NewMessage("Do not attempt to reconnect (DisconnectReason doesn't allow reconnection).");
|
||||
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString());
|
||||
|
||||
for (int i = 1; i < splitMsg.Length; i++)
|
||||
@@ -923,25 +924,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
waitInServerQueueBox?.Close();
|
||||
waitInServerQueueBox = null;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
|
||||
private void ReadAchievement(NetIncomingMessage inc)
|
||||
{
|
||||
string achievementIdentifier = inc.ReadString();
|
||||
SteamAchievementManager.UnlockAchievement(achievementIdentifier);
|
||||
}
|
||||
|
||||
private void ReadPermissions(NetIncomingMessage inc)
|
||||
{
|
||||
List<string> permittedConsoleCommands = new List<string>();
|
||||
byte clientID = inc.ReadByte();
|
||||
|
||||
waitInServerQueueBox?.Close();
|
||||
waitInServerQueueBox = null;
|
||||
@@ -1192,7 +1174,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
gameStarted = inc.ReadBoolean();
|
||||
bool allowSpectating = inc.ReadBoolean();
|
||||
|
||||
|
||||
ReadPermissions(inc);
|
||||
|
||||
if (gameStarted)
|
||||
@@ -1324,12 +1306,6 @@ namespace Barotrauma.Networking
|
||||
byte botCount = inc.ReadByte();
|
||||
BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal;
|
||||
|
||||
byte botCount = inc.ReadByte();
|
||||
BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal;
|
||||
|
||||
byte botCount = inc.ReadByte();
|
||||
BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal;
|
||||
|
||||
bool autoRestartEnabled = inc.ReadBoolean();
|
||||
float autoRestartTimer = autoRestartEnabled ? inc.ReadFloat() : 0.0f;
|
||||
|
||||
@@ -1496,7 +1472,7 @@ namespace Barotrauma.Networking
|
||||
prevBytePos = inc.PositionInBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SendLobbyUpdate()
|
||||
{
|
||||
NetOutgoingMessage outmsg = client.CreateMessage();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -26,6 +27,11 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float Gain
|
||||
{
|
||||
get { return GameMain.Config?.MicrophoneVolume ?? 1.0f; }
|
||||
}
|
||||
|
||||
public DateTime LastEnqueueAudio;
|
||||
|
||||
@@ -69,6 +75,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "capturedevicenotfound"))
|
||||
{
|
||||
GUI.SettingsMenuOpen = false;
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("VoipCaptureDeviceNotFound"))
|
||||
{
|
||||
UserData = "capturedevicenotfound"
|
||||
@@ -160,8 +167,9 @@ namespace Barotrauma.Networking
|
||||
double maxAmplitude = 0.0f;
|
||||
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
|
||||
{
|
||||
double sampleVal = (double)uncompressedBuffer[i] / (double)short.MaxValue;
|
||||
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
|
||||
uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue);
|
||||
double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue;
|
||||
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
|
||||
}
|
||||
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ namespace Barotrauma
|
||||
GameMain.Instance.OnResolutionChanged += OnResolutionChanged;
|
||||
instance = this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void Deselect()
|
||||
{
|
||||
@@ -196,6 +195,7 @@ namespace Barotrauma
|
||||
{
|
||||
ParamsEditor.Instance.EditorBox.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
@@ -351,91 +351,6 @@ namespace Barotrauma
|
||||
{
|
||||
CopyLimb(selectedLimb);
|
||||
}
|
||||
idToCodeName.TryGetValue(id, out string notes);
|
||||
LimbXElements.Add(id.ToString(), new XElement("limb",
|
||||
new XAttribute("id", id),
|
||||
new XAttribute("name", limbName),
|
||||
new XAttribute("type", limbType.ToString()),
|
||||
colliderAttributes,
|
||||
new XElement("sprite",
|
||||
new XAttribute("texture", TexturePath),
|
||||
new XAttribute("sourcerect", $"{rectInputs[0].IntValue}, {rectInputs[1].IntValue}, {width}, {height}")),
|
||||
new XAttribute("notes", null ?? string.Empty)
|
||||
));
|
||||
}
|
||||
}
|
||||
UpdateJointCreation();
|
||||
if (PlayerInput.KeyHit(Keys.Left))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.X--;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Right))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.X++;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Down))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.Y++;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Up))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.Y--;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
UpdateJointCreation();
|
||||
if (PlayerInput.KeyHit(Keys.Left))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.X--;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Right))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.X++;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Down))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.Y++;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
if (PlayerInput.KeyHit(Keys.Up))
|
||||
{
|
||||
foreach (var limb in selectedLimbs)
|
||||
{
|
||||
var newRect = limb.ActiveSprite.SourceRect;
|
||||
newRect.Y--;
|
||||
UpdateSourceRect(limb, newRect);
|
||||
}
|
||||
}
|
||||
UpdateJointCreation();
|
||||
|
||||
@@ -51,10 +51,6 @@ namespace Barotrauma
|
||||
damageEffect.Parameters["xStencil"].SetValue(damageStencil);
|
||||
damageEffect.Parameters["aMultiplier"].SetValue(50.0f);
|
||||
damageEffect.Parameters["cMultiplier"].SetValue(200.0f);
|
||||
|
||||
distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png");
|
||||
postProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture);
|
||||
}
|
||||
|
||||
distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png");
|
||||
postProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Tutorials;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Diagnostics;
|
||||
using Lidgren.Network;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -34,10 +34,13 @@ namespace Barotrauma
|
||||
private Tab selectedTab;
|
||||
|
||||
private Sprite backgroundSprite;
|
||||
private Sprite backgroundVignette;
|
||||
|
||||
#region Creation
|
||||
public MainMenuScreen(GameMain game)
|
||||
{
|
||||
backgroundVignette = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero);
|
||||
|
||||
new GUIImage(new RectTransform(new Vector2(0.35f, 0.2f), Frame.RectTransform, Anchor.BottomRight)
|
||||
{ RelativeOffset = new Vector2(0.05f, 0.05f), AbsoluteOffset = new Point(-5, -5) },
|
||||
style: "TitleText")
|
||||
@@ -688,6 +691,11 @@ namespace Barotrauma
|
||||
blurAmount: 0.0f,
|
||||
aberrationStrength: 0.0f);
|
||||
}
|
||||
|
||||
spriteBatch.Begin(blendState: BlendState.AlphaBlend);
|
||||
backgroundVignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f,
|
||||
new Vector2(GameMain.GraphicsWidth / backgroundVignette.size.X, GameMain.GraphicsHeight / backgroundVignette.size.Y));
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
|
||||
@@ -431,7 +431,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
shuttleList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), elementCount: 10);
|
||||
|
||||
//gamemode ------------------------------------------------------------------
|
||||
|
||||
@@ -1050,10 +1049,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayYourself(bool playYourself)
|
||||
public bool TogglePlayYourself(GUITickBox tickBox)
|
||||
{
|
||||
this.playYourself.Selected = playYourself;
|
||||
if (playYourself)
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
UpdatePlayerFrame(campaignCharacterInfo, allowEditing: campaignCharacterInfo == null);
|
||||
}
|
||||
@@ -1068,6 +1066,7 @@ namespace Barotrauma
|
||||
TextManager.Get("PlayingAsSpectator"),
|
||||
textAlignment: Alignment.Center);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetPlayYourself(bool playYourself)
|
||||
@@ -1097,7 +1096,6 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//show the player config menu if spectating is not allowed
|
||||
if (!playYourself.Selected && !allowSpectating)
|
||||
|
||||
@@ -716,7 +716,7 @@ namespace Barotrauma
|
||||
catch (PingException ex)
|
||||
{
|
||||
string errorMsg = "Failed to ping a server (" + serverInfo.ServerName + ", " + serverInfo.IP + ") - " + (ex?.InnerException?.Message ?? ex.Message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
#endif
|
||||
|
||||
@@ -1913,8 +1913,6 @@ namespace Barotrauma
|
||||
CreateUI();
|
||||
}
|
||||
|
||||
if (tutorial != null) tutorial.Update((float)deltaTime);
|
||||
|
||||
hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull);
|
||||
saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
int bufferSize = (int)reader.TotalSamples*reader.Channels;
|
||||
int bufferSize = (int)reader.TotalSamples * reader.Channels;
|
||||
|
||||
float[] floatBuffer = new float[bufferSize];
|
||||
short[] shortBuffer = new short[bufferSize];
|
||||
@@ -82,7 +82,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
if (!muffleFilters.TryGetValue(sampleRate, out BiQuad filter))
|
||||
{
|
||||
filter = new LowpassFilter(sampleRate, 400);
|
||||
filter = new LowpassFilter(sampleRate, 800);
|
||||
muffleFilters.Add(sampleRate, filter);
|
||||
}
|
||||
filter.Process(buffer);
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Barotrauma.Sounds
|
||||
get { return gain; }
|
||||
set
|
||||
{
|
||||
gain = Math.Max(Math.Min(value,1.0f),0.0f);
|
||||
gain = Math.Max(Math.Min(value, 1.0f), 0.0f);
|
||||
|
||||
if (ALSourceIndex < 0) return;
|
||||
|
||||
@@ -432,7 +432,13 @@ namespace Barotrauma.Sounds
|
||||
this.Category = category;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool FadingOutAndDisposing;
|
||||
public void FadeOutAndDispose()
|
||||
{
|
||||
FadingOutAndDisposing = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (mutex)
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public bool Disabled
|
||||
{
|
||||
Default = 0,
|
||||
Voice = 1
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private IntPtr alcDevice;
|
||||
@@ -109,12 +109,6 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
get { return loadedSounds.Select(s => s.Filename).Distinct().Count(); }
|
||||
}
|
||||
public int UniqueLoadedSoundCount
|
||||
{
|
||||
get { return loadedSounds.Select(s => s.Filename).Distinct().Count(); }
|
||||
}
|
||||
|
||||
private Dictionary<string, Pair<float, bool>> categoryModifiers;
|
||||
|
||||
private Dictionary<string, Pair<float, bool>> categoryModifiers;
|
||||
|
||||
@@ -322,7 +316,6 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
public SoundChannel GetChannelFromSound(Sound sound)
|
||||
{
|
||||
@@ -420,15 +413,6 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
categoryModifiers[category].Second = muffle;
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryModifiers[category].Second = muffle;
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryModifiers[category].Second = muffle;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < playingChannels.Length; i++)
|
||||
{
|
||||
@@ -477,7 +461,8 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
for (int j = 0; j < playingChannels[i].Length; j++)
|
||||
{
|
||||
if (playingChannels[i][j] != null && playingChannels[i][j].IsStream)
|
||||
if (playingChannels[i][j] == null) { continue; }
|
||||
if (playingChannels[i][j].IsStream)
|
||||
{
|
||||
if (playingChannels[i][j].IsPlaying)
|
||||
{
|
||||
@@ -489,10 +474,18 @@ namespace Barotrauma.Sounds
|
||||
playingChannels[i][j].Dispose();
|
||||
}
|
||||
}
|
||||
else if (playingChannels[i][j].FadingOutAndDisposing)
|
||||
{
|
||||
playingChannels[i][j].Gain -= 0.1f;
|
||||
if (playingChannels[i][j].Gain <= 0.0f)
|
||||
{
|
||||
playingChannels[i][j].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Thread.Sleep(50); //TODO: use a separate thread for network audio?
|
||||
Thread.Sleep(10); //TODO: use a separate thread for network audio?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,19 +215,19 @@ namespace Barotrauma
|
||||
for (int i = 0; i < waterAmbienceChannels.Length; i++)
|
||||
{
|
||||
if (waterAmbienceChannels[i] == null) continue;
|
||||
waterAmbienceChannels[i].Dispose();
|
||||
waterAmbienceChannels[i].FadeOutAndDispose();
|
||||
waterAmbienceChannels[i] = null;
|
||||
}
|
||||
for (int i = 0; i < FlowSounds.Count; i++)
|
||||
{
|
||||
if (flowSoundChannels[i] == null) continue;
|
||||
flowSoundChannels[i].Dispose();
|
||||
flowSoundChannels[i].FadeOutAndDispose();
|
||||
flowSoundChannels[i] = null;
|
||||
}
|
||||
for (int i = 0; i < fireSoundChannels.Length; i++)
|
||||
{
|
||||
if (fireSoundChannels[i] == null) continue;
|
||||
fireSoundChannels[i].Dispose();
|
||||
fireSoundChannels[i].FadeOutAndDispose();
|
||||
fireSoundChannels[i] = null;
|
||||
}
|
||||
fireVolumeLeft[0] = 0.0f; fireVolumeLeft[1] = 0.0f;
|
||||
@@ -414,7 +414,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (fireSoundChannels[i] != null)
|
||||
{
|
||||
fireSoundChannels[i].Dispose();
|
||||
fireSoundChannels[i].FadeOutAndDispose();
|
||||
fireSoundChannels[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
private static BiQuad[] muffleFilters = new BiQuad[]
|
||||
{
|
||||
new LowpassFilter(VoipConfig.FREQUENCY, 400)
|
||||
new LowpassFilter(VoipConfig.FREQUENCY, 800)
|
||||
};
|
||||
private static BiQuad[] radioFilters = new BiQuad[]
|
||||
{
|
||||
|
||||
@@ -26,6 +26,10 @@ namespace Barotrauma
|
||||
private int subDivX, subDivY;
|
||||
|
||||
private static Effect effect;
|
||||
public static Effect Effect
|
||||
{
|
||||
get { return effect; }
|
||||
}
|
||||
|
||||
private Point spritePos;
|
||||
private Point spriteSize;
|
||||
|
||||
@@ -279,7 +279,6 @@ namespace Barotrauma
|
||||
//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
|
||||
{
|
||||
string normalizedFilePath = Path.GetFullPath(file);
|
||||
foreach (Sprite s in list)
|
||||
{
|
||||
if (s.FullPath == FullPath) return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#if DEBUG
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@@ -14,10 +15,16 @@ namespace Barotrauma
|
||||
|
||||
private const string conversationsPath = "Content/NPCConversations";
|
||||
private const string infoTextPath = "Content/Texts";
|
||||
private const string xmlHeader = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
|
||||
private const string xmlHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
|
||||
public static void Convert(string language)
|
||||
{
|
||||
if (TextManager.Language != "English")
|
||||
{
|
||||
DebugConsole.ThrowError("Use the english localization when converting .csv to allow copying values");
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> conversationFiles = new List<string>();
|
||||
List<string> infoTextFiles = new List<string>();
|
||||
|
||||
@@ -36,6 +43,11 @@ namespace Barotrauma
|
||||
for (int i = 0; i < conversationFiles.Count; i++)
|
||||
{
|
||||
List<string> xmlContent = ConvertConversationsToXML(File.ReadAllLines(conversationFiles[i], Encoding.UTF8), language);
|
||||
if (xmlContent == null)
|
||||
{
|
||||
DebugConsole.ThrowError("NPCConversation Localization .csv to .xml conversion failed for: " + conversationFiles[i]);
|
||||
continue;
|
||||
}
|
||||
string xmlFileFullPath = $"{conversationsPath}/NPCConversations_{language}_NEW.xml";
|
||||
File.WriteAllLines(xmlFileFullPath, xmlContent);
|
||||
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
|
||||
@@ -44,6 +56,11 @@ namespace Barotrauma
|
||||
for (int i = 0; i < infoTextFiles.Count; i++)
|
||||
{
|
||||
List<string> xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[i], Encoding.UTF8), language);
|
||||
if (xmlContent == null)
|
||||
{
|
||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + infoTextFiles[i]);
|
||||
continue;
|
||||
}
|
||||
string xmlFileFullPath = $"{infoTextPath}/{language}Vanilla_NEW.xml";
|
||||
File.WriteAllLines(xmlFileFullPath, xmlContent);
|
||||
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
|
||||
@@ -61,9 +78,8 @@ namespace Barotrauma
|
||||
xmlContent.Add(xmlHeader);
|
||||
|
||||
xmlContent.Add($"<infotexts language=\"{language}\">");
|
||||
xmlContent.Add(string.Empty);
|
||||
|
||||
for (int i = 0; i < csvContent.Length; i++)
|
||||
for (int i = 1; i < csvContent.Length; i++) // Start at one to ignore header
|
||||
{
|
||||
csvContent[i] = csvContent[i].Trim(separator);
|
||||
|
||||
@@ -105,7 +121,35 @@ namespace Barotrauma
|
||||
xmlContent.Add($"<Conversations identifier=\"vanillaconversations\" Language=\"{language}\">");
|
||||
xmlContent.Add(string.Empty);
|
||||
|
||||
xmlContent.Add("<!-- Personality traits -->");
|
||||
|
||||
int traitStart = -1;
|
||||
for (int i = 0; i < csvContent.Length; i++)
|
||||
{
|
||||
if (csvContent[i].StartsWith("Personality"))
|
||||
{
|
||||
traitStart = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (traitStart == -1)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid formatting of NPCConversations, no traits found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NPCPersonalityTrait.List.Count; i++) // Traits
|
||||
{
|
||||
string[] split = SplitCSV(csvContent[traitStart + i].Trim(separator));
|
||||
xmlContent.Add(
|
||||
$"<PersonalityTrait " +
|
||||
$"{GetVariable("name", split[1])}" +
|
||||
$"{GetVariable("alloweddialogtags", string.Join(",", NPCPersonalityTrait.List[i].AllowedDialogTags))}" +
|
||||
$"{GetVariable("commonness", NPCPersonalityTrait.List[i].Commonness.ToString())}/>");
|
||||
}
|
||||
|
||||
for (int i = traitStart + NPCPersonalityTrait.List.Count; i < csvContent.Length; i++) // Conversations
|
||||
{
|
||||
string[] split = SplitCSV(csvContent[i]);
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
<Content Include="Shaders\blurshader.fx" />
|
||||
<Content Include="Shaders\damageshader.fx" />
|
||||
<Content Include="Shaders\deformshader.fx" />
|
||||
<Content Include="Shaders\solidcolor.fx" />
|
||||
<Content Include="Shaders\losshader.fx" />
|
||||
<Content Include="Shaders\postprocess.fx" />
|
||||
<Content Include="Shaders\watershader.fx" />
|
||||
@@ -252,6 +253,9 @@
|
||||
<None Include="Content\Effects\postprocess.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Effects\solidcolor.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Effects\watershader.xnb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.8.9.5")]
|
||||
[assembly: AssemblyFileVersion("0.8.9.5")]
|
||||
[assembly: AssemblyVersion("0.8.9.6")]
|
||||
[assembly: AssemblyFileVersion("0.8.9.6")]
|
||||
|
||||
@@ -3,11 +3,14 @@ using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MultiPlayerCampaign : CampaignMode
|
||||
{
|
||||
private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>();
|
||||
|
||||
public static void StartNewCampaign(string savePath, string subName, string seed)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(savePath)) return;
|
||||
@@ -202,5 +205,32 @@ namespace Barotrauma
|
||||
CargoManager.PurchaseItem(pi.ItemPrefab, pi.Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
XElement modeElement = new XElement("MultiPlayerCampaign",
|
||||
new XAttribute("money", Money),
|
||||
new XAttribute("cheatsenabled", CheatsEnabled));
|
||||
Map.Save(modeElement);
|
||||
element.Add(modeElement);
|
||||
|
||||
//save character data to a separate file
|
||||
string characterDataPath = GetCharacterDataSavePath();
|
||||
XDocument characterDataDoc = new XDocument(new XElement("CharacterData"));
|
||||
foreach (CharacterCampaignData cd in characterData)
|
||||
{
|
||||
characterDataDoc.Root.Add(cd.Save());
|
||||
}
|
||||
try
|
||||
{
|
||||
characterDataDoc.Save(characterDataPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving multiplayer campaign characters to \"" + characterDataPath + "\" failed!", e);
|
||||
}
|
||||
|
||||
lastSaveID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1897,14 +1897,7 @@ namespace Barotrauma.Networking
|
||||
public override void AddChatMessage(ChatMessage message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
if (message.Sender != null)
|
||||
{
|
||||
Log($"{message.Sender}: {message.Text}", ServerLog.MessageType.Chat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"{message.Text}", ServerLog.MessageType.Chat);
|
||||
}
|
||||
Log(message.TextWithSender, ServerLog.MessageType.Chat);
|
||||
|
||||
base.AddChatMessage(message);
|
||||
}
|
||||
@@ -2367,6 +2360,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void SendVoteStatus(List<Client> recipients)
|
||||
{
|
||||
if (!recipients.Any()) { return; }
|
||||
|
||||
NetOutgoingMessage msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
|
||||
msg.Write((byte)ServerNetObject.VOTE);
|
||||
@@ -2419,7 +2414,10 @@ namespace Barotrauma.Networking
|
||||
recipients.Add(otherClient.Connection);
|
||||
}
|
||||
}
|
||||
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
|
||||
if (recipients.Any())
|
||||
{
|
||||
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
|
||||
}
|
||||
|
||||
serverSettings.SaveClientPermissions();
|
||||
}
|
||||
@@ -2453,13 +2451,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void UpdateCheatsEnabled()
|
||||
{
|
||||
if (!connectedClients.Any()) { return; }
|
||||
|
||||
var msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED);
|
||||
msg.Write(DebugConsole.CheatsEnabled);
|
||||
msg.WritePadBits();
|
||||
|
||||
CompressOutgoingMessage(msg);
|
||||
|
||||
|
||||
server.SendMessage(msg, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0);
|
||||
}
|
||||
|
||||
@@ -2794,747 +2794,6 @@ namespace Barotrauma.Networking
|
||||
server.Shutdown(DisconnectReason.ServerShutdown.ToString());
|
||||
}
|
||||
|
||||
public void SendConsoleMessage(string txt, Client recipient)
|
||||
{
|
||||
ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Console, null);
|
||||
SendDirectChatMessage(msg, recipient);
|
||||
}
|
||||
|
||||
public void SendDirectChatMessage(ChatMessage msg, Client recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
{
|
||||
string errorMsg = "Attempted to send a chat message to a null client.\n" + Environment.StackTrace;
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.NetStateID = recipient.ChatMsgQueue.Count > 0 ?
|
||||
(ushort)(recipient.ChatMsgQueue.Last().NetStateID + 1) :
|
||||
(ushort)(recipient.LastRecvChatMsgID + 1);
|
||||
|
||||
recipient.ChatMsgQueue.Add(msg);
|
||||
recipient.LastChatMsgQueueID = msg.NetStateID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the message to the chatbox and pass it to all clients who can receive it
|
||||
/// </summary>
|
||||
public void SendChatMessage(string message, ChatMessageType? type = null, Client senderClient = null, Character senderCharacter = null)
|
||||
{
|
||||
string senderName = "";
|
||||
|
||||
Client targetClient = null;
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
string command = ChatMessage.GetChatMessageCommand(message, out string tempStr);
|
||||
switch (command.ToLowerInvariant())
|
||||
{
|
||||
case "r":
|
||||
case "radio":
|
||||
type = ChatMessageType.Radio;
|
||||
break;
|
||||
case "d":
|
||||
case "dead":
|
||||
type = ChatMessageType.Dead;
|
||||
break;
|
||||
default:
|
||||
if (command != "")
|
||||
{
|
||||
if (command == name.ToLowerInvariant())
|
||||
{
|
||||
//a private message to the host
|
||||
}
|
||||
else
|
||||
{
|
||||
targetClient = connectedClients.Find(c =>
|
||||
command == c.Name.ToLowerInvariant() ||
|
||||
(c.Character != null && command == c.Character.Name.ToLowerInvariant()));
|
||||
|
||||
if (targetClient == null)
|
||||
{
|
||||
if (senderClient != null)
|
||||
{
|
||||
var chatMsg = ChatMessage.Create(
|
||||
"", $"ServerMessage.PlayerNotFound~[player]={command}",
|
||||
ChatMessageType.Error, null);
|
||||
|
||||
chatMsg.NetStateID = senderClient.ChatMsgQueue.Count > 0 ?
|
||||
(ushort)(senderClient.ChatMsgQueue.Last().NetStateID + 1) :
|
||||
(ushort)(senderClient.LastRecvChatMsgID + 1);
|
||||
|
||||
senderClient.ChatMsgQueue.Add(chatMsg);
|
||||
senderClient.LastChatMsgQueueID = chatMsg.NetStateID;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddChatMessage($"ServerMessage.PlayerNotFound~[player]={command}", ChatMessageType.Error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
type = ChatMessageType.Private;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ChatMessageType.Default;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
message = tempStr;
|
||||
}
|
||||
|
||||
if (gameStarted)
|
||||
{
|
||||
if (senderClient == null)
|
||||
{
|
||||
//msg sent by the server
|
||||
if (senderCharacter == null)
|
||||
{
|
||||
senderName = name;
|
||||
}
|
||||
else //msg sent by an AI character
|
||||
{
|
||||
senderName = senderCharacter.Name;
|
||||
}
|
||||
}
|
||||
else //msg sent by a client
|
||||
{
|
||||
senderCharacter = senderClient.Character;
|
||||
senderName = senderCharacter == null ? senderClient.Name : senderCharacter.Name;
|
||||
|
||||
//sender doesn't have a character or the character can't speak -> only ChatMessageType.Dead allowed
|
||||
if (senderCharacter == null || senderCharacter.IsDead || senderCharacter.SpeechImpediment >= 100.0f)
|
||||
{
|
||||
type = ChatMessageType.Dead;
|
||||
}
|
||||
else if (type == ChatMessageType.Private)
|
||||
{
|
||||
//sender has an alive character, sending private messages not allowed
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (senderClient == null)
|
||||
{
|
||||
//msg sent by the server
|
||||
if (senderCharacter == null)
|
||||
{
|
||||
senderName = name;
|
||||
}
|
||||
else //sent by an AI character, not allowed when the game is not running
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else //msg sent by a client
|
||||
{
|
||||
//game not started -> clients can only send normal and private chatmessages
|
||||
if (type != ChatMessageType.Private) type = ChatMessageType.Default;
|
||||
senderName = senderClient.Name;
|
||||
}
|
||||
}
|
||||
|
||||
//check if the client is allowed to send the message
|
||||
WifiComponent senderRadio = null;
|
||||
switch (type)
|
||||
{
|
||||
case ChatMessageType.Radio:
|
||||
case ChatMessageType.Order:
|
||||
if (senderCharacter == null) return;
|
||||
|
||||
//return if senderCharacter doesn't have a working radio
|
||||
var radio = senderCharacter.Inventory?.Items.FirstOrDefault(i => i != null && i.GetComponent<WifiComponent>() != null);
|
||||
if (radio == null || !senderCharacter.HasEquippedItem(radio)) return;
|
||||
|
||||
senderRadio = radio.GetComponent<WifiComponent>();
|
||||
if (!senderRadio.CanTransmit()) return;
|
||||
break;
|
||||
case ChatMessageType.Dead:
|
||||
//character still alive and capable of speaking -> dead chat not allowed
|
||||
if (senderClient != null && senderCharacter != null && !senderCharacter.IsDead && senderCharacter.SpeechImpediment < 100.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == ChatMessageType.Server)
|
||||
{
|
||||
senderName = null;
|
||||
senderCharacter = null;
|
||||
}
|
||||
else if (type == ChatMessageType.Radio)
|
||||
{
|
||||
//send to chat-linked wifi components
|
||||
senderRadio.TransmitSignal(0, message, senderRadio.Item, senderCharacter, false);
|
||||
}
|
||||
|
||||
//check which clients can receive the message and apply distance effects
|
||||
foreach (Client client in ConnectedClients)
|
||||
{
|
||||
string modifiedMessage = message;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ChatMessageType.Default:
|
||||
case ChatMessageType.Radio:
|
||||
case ChatMessageType.Order:
|
||||
if (senderCharacter != null &&
|
||||
client.Character != null && !client.Character.IsDead)
|
||||
{
|
||||
modifiedMessage = ChatMessage.ApplyDistanceEffect(message, (ChatMessageType)type, senderCharacter, client.Character);
|
||||
|
||||
//too far to hear the msg -> don't send
|
||||
if (string.IsNullOrWhiteSpace(modifiedMessage)) continue;
|
||||
}
|
||||
break;
|
||||
case ChatMessageType.Dead:
|
||||
//character still alive -> don't send
|
||||
if (client != senderClient && client.Character != null && !client.Character.IsDead) continue;
|
||||
break;
|
||||
case ChatMessageType.Private:
|
||||
//private msg sent to someone else than this client -> don't send
|
||||
if (client != targetClient && client != senderClient) continue;
|
||||
break;
|
||||
}
|
||||
|
||||
var chatMsg = ChatMessage.Create(
|
||||
senderName,
|
||||
modifiedMessage,
|
||||
(ChatMessageType)type,
|
||||
senderCharacter);
|
||||
|
||||
SendDirectChatMessage(chatMsg, client);
|
||||
}
|
||||
|
||||
if (type.Value != ChatMessageType.MessageBox)
|
||||
{
|
||||
string myReceivedMessage = type == ChatMessageType.Server || type == ChatMessageType.Error ? TextManager.GetServerMessage(message) : message;
|
||||
if (!string.IsNullOrWhiteSpace(myReceivedMessage) &&
|
||||
(targetClient == null || senderClient == null))
|
||||
{
|
||||
AddChatMessage(myReceivedMessage, (ChatMessageType)type, senderName, senderCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendOrderChatMessage(OrderChatMessage message)
|
||||
{
|
||||
if (message.Sender == null || message.Sender.SpeechImpediment >= 100.0f) return;
|
||||
ChatMessageType messageType = ChatMessage.CanUseRadio(message.Sender) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
|
||||
//check which clients can receive the message and apply distance effects
|
||||
foreach (Client client in ConnectedClients)
|
||||
{
|
||||
string modifiedMessage = message.Text;
|
||||
|
||||
if (message.Sender != null &&
|
||||
client.Character != null && !client.Character.IsDead)
|
||||
{
|
||||
modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Text, messageType, message.Sender, client.Character);
|
||||
|
||||
//too far to hear the msg -> don't send
|
||||
if (string.IsNullOrWhiteSpace(modifiedMessage)) continue;
|
||||
}
|
||||
|
||||
SendDirectChatMessage(message, client);
|
||||
}
|
||||
|
||||
string myReceivedMessage = message.Text;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(myReceivedMessage))
|
||||
{
|
||||
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, myReceivedMessage, message.TargetEntity, message.TargetCharacter, message.Sender));
|
||||
}
|
||||
}
|
||||
|
||||
private void FileTransferChanged(FileSender.FileTransferOut transfer)
|
||||
{
|
||||
Client recipient = connectedClients.Find(c => c.Connection == transfer.Connection);
|
||||
if (transfer.FileType == FileTransferType.CampaignSave &&
|
||||
(transfer.Status == FileTransferStatus.Sending || transfer.Status == FileTransferStatus.Finished) &&
|
||||
recipient.LastCampaignSaveSendTime != null)
|
||||
{
|
||||
recipient.LastCampaignSaveSendTime.Second = (float)NetTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendCancelTransferMsg(FileSender.FileTransferOut transfer)
|
||||
{
|
||||
NetOutgoingMessage msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.FILE_TRANSFER);
|
||||
msg.Write((byte)FileTransferMessageType.Cancel);
|
||||
msg.Write((byte)transfer.SequenceChannel);
|
||||
CompressOutgoingMessage(msg);
|
||||
server.SendMessage(msg, transfer.Connection, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
|
||||
}
|
||||
|
||||
public void UpdateVoteStatus()
|
||||
{
|
||||
if (server.Connections.Count == 0 || connectedClients.Count == 0) return;
|
||||
|
||||
Client.UpdateKickVotes(connectedClients);
|
||||
|
||||
var clientsToKick = connectedClients.FindAll(c =>
|
||||
c.Connection != OwnerConnection &&
|
||||
c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio);
|
||||
foreach (Client c in clientsToKick)
|
||||
{
|
||||
SendChatMessage($"ServerMessage.KickedFromServer~[client]={c.Name}", ChatMessageType.Server, null);
|
||||
KickClient(c, "ServerMessage.KickedByVote");
|
||||
BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime));
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
|
||||
SendVoteStatus(connectedClients);
|
||||
|
||||
if (serverSettings.Voting.AllowEndVoting && EndVoteMax > 0 &&
|
||||
((float)EndVoteCount / (float)EndVoteMax) >= serverSettings.EndVoteRequiredRatio)
|
||||
{
|
||||
Log("Ending round by votes (" + EndVoteCount + "/" + (EndVoteMax - EndVoteCount) + ")", ServerLog.MessageType.ServerMessage);
|
||||
EndGame();
|
||||
}
|
||||
}
|
||||
|
||||
public void SendVoteStatus(List<Client> recipients)
|
||||
{
|
||||
NetOutgoingMessage msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
|
||||
msg.Write((byte)ServerNetObject.VOTE);
|
||||
serverSettings.Voting.ServerWrite(msg);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
CompressOutgoingMessage(msg);
|
||||
|
||||
server.SendMessage(msg, recipients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0);
|
||||
}
|
||||
|
||||
public void UpdateClientPermissions(Client client)
|
||||
{
|
||||
if (client.SteamID > 0)
|
||||
{
|
||||
serverSettings.ClientPermissions.RemoveAll(cp => cp.SteamID == client.SteamID);
|
||||
if (client.Permissions != ClientPermissions.None)
|
||||
{
|
||||
serverSettings.ClientPermissions.Add(new ServerSettings.SavedClientPermission(
|
||||
client.Name,
|
||||
client.SteamID,
|
||||
client.Permissions,
|
||||
client.PermittedConsoleCommands));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSettings.ClientPermissions.RemoveAll(cp => client.IPMatches(cp.IP));
|
||||
if (client.Permissions != ClientPermissions.None)
|
||||
{
|
||||
serverSettings.ClientPermissions.Add(new ServerSettings.SavedClientPermission(
|
||||
client.Name,
|
||||
client.Connection.RemoteEndPoint.Address,
|
||||
client.Permissions,
|
||||
client.PermittedConsoleCommands));
|
||||
}
|
||||
}
|
||||
|
||||
var msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.PERMISSIONS);
|
||||
client.WritePermissions(msg);
|
||||
CompressOutgoingMessage(msg);
|
||||
|
||||
//send the message to the client whose permissions are being modified and the clients who are allowed to modify permissions
|
||||
List<NetConnection> recipients = new List<NetConnection>() { client.Connection };
|
||||
foreach (Client otherClient in connectedClients)
|
||||
{
|
||||
if (otherClient.HasPermission(ClientPermissions.ManagePermissions) && !recipients.Contains(otherClient.Connection))
|
||||
{
|
||||
recipients.Add(otherClient.Connection);
|
||||
}
|
||||
}
|
||||
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
|
||||
|
||||
serverSettings.SaveClientPermissions();
|
||||
}
|
||||
|
||||
public void GiveAchievement(Character character, string achievementIdentifier)
|
||||
{
|
||||
achievementIdentifier = achievementIdentifier.ToLowerInvariant();
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
if (client.Character == character)
|
||||
{
|
||||
GiveAchievement(client, achievementIdentifier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GiveAchievement(Client client, string achievementIdentifier)
|
||||
{
|
||||
if (client.GivenAchievements.Contains(achievementIdentifier)) return;
|
||||
client.GivenAchievements.Add(achievementIdentifier);
|
||||
|
||||
var msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
|
||||
msg.Write(achievementIdentifier);
|
||||
|
||||
CompressOutgoingMessage(msg);
|
||||
|
||||
server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered);
|
||||
}
|
||||
|
||||
public void UpdateCheatsEnabled()
|
||||
{
|
||||
var msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED);
|
||||
msg.Write(DebugConsole.CheatsEnabled);
|
||||
msg.WritePadBits();
|
||||
|
||||
CompressOutgoingMessage(msg);
|
||||
|
||||
server.SendMessage(msg, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0);
|
||||
}
|
||||
|
||||
public void SetClientCharacter(Client client, Character newCharacter)
|
||||
{
|
||||
if (client == null) return;
|
||||
|
||||
//the client's previous character is no longer a remote player
|
||||
if (client.Character != null)
|
||||
{
|
||||
client.Character.IsRemotePlayer = false;
|
||||
client.Character.OwnerClientIP = null;
|
||||
client.Character.OwnerClientName = null;
|
||||
}
|
||||
|
||||
if (newCharacter == null)
|
||||
{
|
||||
if (client.Character != null) //removing control of the current character
|
||||
{
|
||||
CreateEntityEvent(client.Character, new object[] { NetEntityEvent.Type.Control, null });
|
||||
client.Character = null;
|
||||
}
|
||||
}
|
||||
else //taking control of a new character
|
||||
{
|
||||
newCharacter.ClientDisconnected = false;
|
||||
newCharacter.KillDisconnectedTimer = 0.0f;
|
||||
newCharacter.ResetNetState();
|
||||
if (client.Character != null)
|
||||
{
|
||||
newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
newCharacter.OwnerClientIP = client.Connection.RemoteEndPoint.Address.ToString();
|
||||
newCharacter.OwnerClientName = client.Name;
|
||||
newCharacter.IsRemotePlayer = true;
|
||||
newCharacter.Enabled = true;
|
||||
client.Character = newCharacter;
|
||||
CreateEntityEvent(newCharacter, new object[] { NetEntityEvent.Type.Control, client });
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCharacterInfo(NetIncomingMessage message, Client sender)
|
||||
{
|
||||
sender.SpectateOnly = message.ReadBoolean() && (serverSettings.AllowSpectating || sender.Connection == OwnerConnection);
|
||||
if (sender.SpectateOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gender gender = Gender.Male;
|
||||
Race race = Race.White;
|
||||
int headSpriteId = 0;
|
||||
try
|
||||
{
|
||||
gender = (Gender)message.ReadByte();
|
||||
race = (Race)message.ReadByte();
|
||||
headSpriteId = message.ReadByte();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//gender = Gender.Male;
|
||||
//race = Race.White;
|
||||
//headSpriteId = 0;
|
||||
DebugConsole.Log("Received invalid characterinfo from \"" + sender.Name + "\"! { " + e.Message + " }");
|
||||
}
|
||||
int hairIndex = message.ReadByte();
|
||||
int beardIndex = message.ReadByte();
|
||||
int moustacheIndex = message.ReadByte();
|
||||
int faceAttachmentIndex = message.ReadByte();
|
||||
|
||||
List<JobPrefab> jobPreferences = new List<JobPrefab>();
|
||||
int count = message.ReadByte();
|
||||
for (int i = 0; i < Math.Min(count, 3); i++)
|
||||
{
|
||||
string jobIdentifier = message.ReadString();
|
||||
|
||||
JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier);
|
||||
if (jobPrefab != null) jobPreferences.Add(jobPrefab);
|
||||
}
|
||||
|
||||
sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name);
|
||||
sender.CharacterInfo.RecreateHead(headSpriteId, race, gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
|
||||
//if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor
|
||||
Debug.Assert(sender.JobPreferences.Count > 0);
|
||||
if (jobPreferences.Count > 0)
|
||||
{
|
||||
sender.JobPreferences = jobPreferences;
|
||||
}
|
||||
}
|
||||
|
||||
public void AssignJobs(List<Client> unassigned)
|
||||
{
|
||||
unassigned = new List<Client>(unassigned);
|
||||
|
||||
Dictionary<JobPrefab, int> assignedClientCount = new Dictionary<JobPrefab, int>();
|
||||
foreach (JobPrefab jp in JobPrefab.List)
|
||||
{
|
||||
assignedClientCount.Add(jp, 0);
|
||||
}
|
||||
|
||||
Character.TeamType teamID = Character.TeamType.None;
|
||||
if (unassigned.Count > 0) { teamID = unassigned[0].TeamID; }
|
||||
|
||||
//if we're playing a multiplayer campaign, check which clients already have a character and a job
|
||||
//(characters are persistent in campaigns)
|
||||
if (GameMain.GameSession.GameMode is MultiPlayerCampaign multiplayerCampaign)
|
||||
{
|
||||
var campaignAssigned = multiplayerCampaign.GetAssignedJobs(connectedClients);
|
||||
//remove already assigned clients from unassigned
|
||||
unassigned.RemoveAll(u => campaignAssigned.ContainsKey(u));
|
||||
//add up to assigned client count
|
||||
foreach (KeyValuePair<Client, Job> clientJob in campaignAssigned)
|
||||
{
|
||||
assignedClientCount[clientJob.Value.Prefab]++;
|
||||
clientJob.Key.AssignedJob = clientJob.Value.Prefab;
|
||||
}
|
||||
}
|
||||
|
||||
//count the clients who already have characters with an assigned job
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
if (c.TeamID != teamID || unassigned.Contains(c)) continue;
|
||||
if (c.Character?.Info?.Job != null && !c.Character.IsDead)
|
||||
{
|
||||
assignedClientCount[c.Character.Info.Job.Prefab]++;
|
||||
}
|
||||
}
|
||||
|
||||
//if any of the players has chosen a job that is Always Allowed, give them that job
|
||||
for (int i = unassigned.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (unassigned[i].JobPreferences.Count == 0) continue;
|
||||
if (!unassigned[i].JobPreferences[0].AllowAlways) continue;
|
||||
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
|
||||
unassigned.RemoveAt(i);
|
||||
}
|
||||
|
||||
//go throught the jobs whose MinNumber>0 (i.e. at least one crew member has to have the job)
|
||||
bool unassignedJobsFound = true;
|
||||
while (unassignedJobsFound && unassigned.Count > 0)
|
||||
{
|
||||
unassignedJobsFound = false;
|
||||
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.List)
|
||||
{
|
||||
if (unassigned.Count == 0) break;
|
||||
if (jobPrefab.MinNumber < 1 || assignedClientCount[jobPrefab] >= jobPrefab.MinNumber) continue;
|
||||
|
||||
//find the client that wants the job the most, or force it to random client if none of them want it
|
||||
Client assignedClient = FindClientWithJobPreference(unassigned, jobPrefab, true);
|
||||
|
||||
assignedClient.AssignedJob = jobPrefab;
|
||||
assignedClientCount[jobPrefab]++;
|
||||
unassigned.Remove(assignedClient);
|
||||
|
||||
//the job still needs more crew members, set unassignedJobsFound to true to keep the while loop running
|
||||
if (assignedClientCount[jobPrefab] < jobPrefab.MinNumber) unassignedJobsFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//attempt to give the clients a job they have in their job preferences
|
||||
for (int i = unassigned.Count - 1; i >= 0; i--)
|
||||
{
|
||||
foreach (JobPrefab preferredJob in unassigned[i].JobPreferences)
|
||||
{
|
||||
//the maximum number of players that can have this job hasn't been reached yet
|
||||
// -> assign it to the client
|
||||
if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && unassigned[i].Karma >= preferredJob.MinKarma)
|
||||
{
|
||||
unassigned[i].AssignedJob = preferredJob;
|
||||
assignedClientCount[preferredJob]++;
|
||||
unassigned.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//give random jobs to rest of the clients
|
||||
foreach (Client c in unassigned)
|
||||
{
|
||||
//find all jobs that are still available
|
||||
var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
|
||||
|
||||
//all jobs taken, give a random job
|
||||
if (remainingJobs.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
|
||||
int jobIndex = Rand.Range(0, JobPrefab.List.Count);
|
||||
int skips = 0;
|
||||
while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
|
||||
{
|
||||
jobIndex++;
|
||||
skips++;
|
||||
if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
|
||||
if (skips >= JobPrefab.List.Count) break;
|
||||
}
|
||||
c.AssignedJob = JobPrefab.List[jobIndex];
|
||||
assignedClientCount[c.AssignedJob]++;
|
||||
}
|
||||
else //some jobs still left, choose one of them by random
|
||||
{
|
||||
c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
|
||||
assignedClientCount[c.AssignedJob]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AssignBotJobs(List<CharacterInfo> bots, Character.TeamType teamID)
|
||||
{
|
||||
Dictionary<JobPrefab, int> assignedPlayerCount = new Dictionary<JobPrefab, int>();
|
||||
foreach (JobPrefab jp in JobPrefab.List)
|
||||
{
|
||||
assignedPlayerCount.Add(jp, 0);
|
||||
}
|
||||
|
||||
//count the clients who already have characters with an assigned job
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
if (c.TeamID != teamID) continue;
|
||||
if (c.Character?.Info?.Job != null && !c.Character.IsDead)
|
||||
{
|
||||
assignedPlayerCount[c.Character.Info.Job.Prefab]++;
|
||||
}
|
||||
else if (c.CharacterInfo?.Job != null)
|
||||
{
|
||||
assignedPlayerCount[c.CharacterInfo?.Job.Prefab]++;
|
||||
}
|
||||
}
|
||||
|
||||
List<CharacterInfo> unassignedBots = new List<CharacterInfo>(bots);
|
||||
foreach (CharacterInfo bot in bots)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.List)
|
||||
{
|
||||
if (jobPrefab.MinNumber < 1 || assignedPlayerCount[jobPrefab] >= jobPrefab.MinNumber) continue;
|
||||
bot.Job = new Job(jobPrefab);
|
||||
assignedPlayerCount[jobPrefab]++;
|
||||
unassignedBots.Remove(bot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//find a suitable job for the rest of the players
|
||||
foreach (CharacterInfo c in unassignedBots)
|
||||
{
|
||||
//find all jobs that are still available
|
||||
var remainingJobs = JobPrefab.List.FindAll(jp => assignedPlayerCount[jp] < jp.MaxNumber);
|
||||
//all jobs taken, give a random job
|
||||
if (remainingJobs.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to assign a suitable job for bot \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
|
||||
c.Job = new Job(JobPrefab.List[Rand.Range(0, JobPrefab.List.Count)]);
|
||||
assignedPlayerCount[c.Job.Prefab]++;
|
||||
}
|
||||
else //some jobs still left, choose one of them by random
|
||||
{
|
||||
c.Job = new Job(remainingJobs[Rand.Range(0, remainingJobs.Count)]);
|
||||
assignedPlayerCount[c.Job.Prefab]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Client FindClientWithJobPreference(List<Client> clients, JobPrefab job, bool forceAssign = false)
|
||||
{
|
||||
int bestPreference = 0;
|
||||
Client preferredClient = null;
|
||||
foreach (Client c in clients)
|
||||
{
|
||||
if (c.Karma < job.MinKarma) continue;
|
||||
int index = c.JobPreferences.IndexOf(job);
|
||||
if (index == -1) index = 1000;
|
||||
|
||||
if (preferredClient == null || index < bestPreference)
|
||||
{
|
||||
bestPreference = index;
|
||||
preferredClient = c;
|
||||
}
|
||||
}
|
||||
|
||||
//none of the clients wants the job, assign it to random client
|
||||
if (forceAssign && preferredClient == null)
|
||||
{
|
||||
preferredClient = clients[Rand.Int(clients.Count)];
|
||||
}
|
||||
|
||||
return preferredClient;
|
||||
}
|
||||
|
||||
public static void Log(string line, ServerLog.MessageType messageType)
|
||||
{
|
||||
if (GameMain.Server == null || !GameMain.Server.ServerSettings.SaveServerLogs) return;
|
||||
|
||||
GameMain.Server.ServerSettings.ServerLog.WriteLine(line, messageType);
|
||||
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (!client.HasPermission(ClientPermissions.ServerLog)) continue;
|
||||
//use sendername as the message type
|
||||
GameMain.Server.SendDirectChatMessage(
|
||||
ChatMessage.Create(messageType.ToString(), line, ChatMessageType.ServerLog, null),
|
||||
client);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Disconnect()
|
||||
{
|
||||
serverSettings.BanList.Save();
|
||||
serverSettings.SaveSettings();
|
||||
SteamManager.CloseServer();
|
||||
|
||||
if (registeredToMaster)
|
||||
{
|
||||
if (restClient != null)
|
||||
{
|
||||
var request = new RestRequest("masterserver2.php", Method.GET);
|
||||
request.AddParameter("action", "removeserver");
|
||||
request.AddParameter("serverport", Port);
|
||||
restClient.Execute(request);
|
||||
restClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverSettings.SaveServerLogs)
|
||||
{
|
||||
Log("Shutting down the server...", ServerLog.MessageType.ServerMessage);
|
||||
serverSettings.ServerLog.Save();
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("GameServer:ShutDown");
|
||||
server.Shutdown("The server has been shut down");
|
||||
}
|
||||
|
||||
void InitUPnP()
|
||||
{
|
||||
server.UPnP.ForwardPort(NetPeerConfiguration.Port, "barotrauma");
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace Barotrauma.Networking
|
||||
+ (c.LastRecvEntityEventID + 1).ToString() +
|
||||
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
|
||||
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage);
|
||||
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesync");
|
||||
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
|
||||
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.ServerMessage);
|
||||
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesync");
|
||||
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -260,6 +260,7 @@ namespace Barotrauma.Networking
|
||||
//a client could potentially spam events with a much higher character state ID
|
||||
//than the state of their character and/or stop sending character inputs,
|
||||
//so we'll drop some events to make sure no-one blows up our buffer
|
||||
DebugConsole.Log("Excessive amount of events in a client's event buffer. The client may be spamming events or their event IDs might be out of sync. Dropping events...");
|
||||
bufferedEvents.RemoveRange(0, 256);
|
||||
}
|
||||
|
||||
@@ -494,7 +495,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (Client c in server.ConnectedClients)
|
||||
{
|
||||
c.PositionUpdateLastSent.Clear();
|
||||
c.EntityEventLastSent.Clear();
|
||||
c.LastRecvEntityEventID = 0;
|
||||
c.LastSentEntityEventID = 0;
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
<Submarine file="Submarines/Muikku.sub" />
|
||||
<Submarine file="Submarines/Bunyip.sub" />
|
||||
<Submarine file="Submarines/Humpback.sub" />
|
||||
<Submarine file="Submarines/Dugong.sub" />
|
||||
<Text file="Content/Texts/EnglishVanilla.xml" />
|
||||
<Text file="Content/Texts/EnglishVanillaLoadingTips.xml" />
|
||||
<UIStyle file="Content/UI/style.xml"/>
|
||||
<Afflictions file="Content/Afflictions.xml"/>
|
||||
<Structure file="Content/Map/StructurePrefabs.xml" />
|
||||
|
||||
@@ -1005,9 +1005,36 @@
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Lights\pointlight_falloff.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground2.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground3.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground4.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground5.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienBackground6.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienDecals1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienRuins.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienWallSet1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\AlienWallSet2.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Map\bWall_Airlock_A1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -1382,9 +1409,6 @@
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\splashscreen.mp4">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\EnglishVanillaLoadingTips.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\EnglishVanilla.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -1619,6 +1643,12 @@
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Lights\lightcone.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Lights\divinghelmetlight.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Lights\divinghelmetvisorlight.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\Lights\penumbra.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -1852,6 +1882,9 @@
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\Health\MedUI_Torso.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\HighlightRaster.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\IconAtlas.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -1873,6 +1906,9 @@
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\damageOverlay.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\MainMenuVignette.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Content\UI\noise.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -3089,6 +3125,9 @@
|
||||
<None Include="$(MSBuildThisFileDirectory)Content\Items\Door\DoorBreak2.ogg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)Submarines\PAX.sub">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)Submarines\Remora.sub">
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)Submarines\RemoraDrone.sub">
|
||||
|
||||
@@ -359,7 +359,7 @@ namespace Barotrauma
|
||||
if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(Character.CharacterHealth.CrushDepth * 0.75f))
|
||||
{
|
||||
//steer straight up if very deep
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.UnitY * speed);
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.UnitY);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ namespace Barotrauma
|
||||
{
|
||||
//wander around randomly and decrease the priority faster if no path is found
|
||||
if (selectedTargetMemory != null) selectedTargetMemory.Priority -= deltaTime * 10.0f;
|
||||
steeringManager.SteeringWander(speed);
|
||||
steeringManager.SteeringWander();
|
||||
}
|
||||
else if (indoorsSteering.CurrentPath.Finished)
|
||||
{
|
||||
|
||||
@@ -131,23 +131,20 @@ namespace Barotrauma
|
||||
ignorePlatforms = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Character.IsClimbing && PathSteering.InLadders && PathSteering.IsNextLadderSameAsCurrent)
|
||||
{
|
||||
Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
|
||||
}
|
||||
}
|
||||
|
||||
Character.AnimController.IgnorePlatforms = ignorePlatforms;
|
||||
|
||||
// Suspect that this causes issues when trying to exit from the ladders -> could try to check if the next node is ladder?
|
||||
//if (Character.IsClimbing)
|
||||
//{
|
||||
// Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
|
||||
//}
|
||||
|
||||
Vector2 targetMovement = AnimController.TargetMovement;
|
||||
|
||||
if (!Character.AnimController.InWater)
|
||||
{
|
||||
targetMovement = new Vector2(
|
||||
Character.AnimController.TargetMovement.X,
|
||||
MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
|
||||
targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed);
|
||||
@@ -227,10 +224,6 @@ namespace Barotrauma
|
||||
PropagateHullSafety(Character, Character.CurrentHull);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportProblems()
|
||||
{
|
||||
if (GameMain.Client != null) return;
|
||||
|
||||
protected void ReportProblems()
|
||||
{
|
||||
@@ -252,7 +245,7 @@ namespace Barotrauma
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.CurrentHull == Character.CurrentHull && !c.IsDead &&
|
||||
(c.AIController is EnemyAIController || c.TeamID != Character.TeamID))
|
||||
(c.AIController is EnemyAIController || (c.TeamID != Character.TeamID && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC)))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
@@ -297,10 +290,13 @@ namespace Barotrauma
|
||||
public override void OnAttacked(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
float damage = attackResult.Damage;
|
||||
if (damage < 0) { return; }
|
||||
if (damage <= 0) { return; }
|
||||
if (attacker == null || attacker.IsDead || attacker.Removed)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (IsFriendly(attacker))
|
||||
@@ -314,7 +310,10 @@ namespace Barotrauma
|
||||
if (!attacker.IsRemotePlayer && Character.Controlled != attacker && attacker.AIController != null && attacker.AIController.Enabled)
|
||||
{
|
||||
// Don't react to damage done by friendly ai, because we know that it's accidental
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
return;
|
||||
}
|
||||
float currentVitality = Character.CharacterHealth.Vitality;
|
||||
@@ -322,8 +321,10 @@ namespace Barotrauma
|
||||
if (dmgPercentage < currentVitality / 10)
|
||||
{
|
||||
// Don't react to a minor amount of (accidental) dmg done by friendly characters
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
return;
|
||||
if (objectiveManager.CurrentOrder == null)
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
}
|
||||
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective)
|
||||
{
|
||||
@@ -465,7 +466,9 @@ namespace Barotrauma
|
||||
// Even the smallest fire reduces the safety by 50%
|
||||
float fire = hull.FireSources.Count * 0.5f + hull.FireSources.Sum(fs => fs.DamageRange) / hull.Size.X;
|
||||
float fireFactor = ignoreFire ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(fire, 0, 1));
|
||||
int enemyCount = Character.CharacterList.Count(e => e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious && (e.AIController is EnemyAIController || e.TeamID != character.TeamID));
|
||||
int enemyCount = Character.CharacterList.Count(e =>
|
||||
e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious &&
|
||||
(e.AIController is EnemyAIController || (e.TeamID != character.TeamID && character.TeamID != Character.TeamType.FriendlyNPC && e.TeamID != Character.TeamType.FriendlyNPC)));
|
||||
// The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages)
|
||||
float enemyFactor = ignoreEnemies ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1));
|
||||
float safety = oxygenFactor * waterFactor * fireFactor * enemyFactor;
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public bool InLadders => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Ladders != null;
|
||||
private bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null;
|
||||
private bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders;
|
||||
public bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null;
|
||||
public bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders;
|
||||
|
||||
public bool InStairs => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Stairs != null;
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.AnimController.InWater)
|
||||
{
|
||||
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < collider.radius * collider.radius)
|
||||
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2))
|
||||
{
|
||||
currentPath.SkipToNextNode();
|
||||
}
|
||||
@@ -221,11 +221,14 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
|
||||
Vector2 colliderSize = collider.GetSize();
|
||||
// Cannot use the head position, because not all characters have head or it can be below the total height of the character
|
||||
float characterHeight = colliderSize.Y + character.AnimController.ColliderHeightFromFloor;
|
||||
float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X);
|
||||
bool isAboveVerticalPosition = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y;
|
||||
bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + 1.5f;
|
||||
bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y;
|
||||
bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight;
|
||||
|
||||
if (horizontalDistance < collider.radius * 2 && isAboveVerticalPosition && isNotTooHigh)
|
||||
if (horizontalDistance < collider.radius * 3 && isAboveFeet && isNotTooHigh)
|
||||
{
|
||||
currentPath.SkipToNextNode();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Barotrauma
|
||||
List<Tuple<string, string, string>> contentPackageFiles = new List<Tuple<string, string, string>>();
|
||||
foreach (string filePath in filePaths)
|
||||
{
|
||||
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) continue;
|
||||
string language = doc.Root.GetAttributeString("Language", "English");
|
||||
@@ -41,6 +42,7 @@ namespace Barotrauma
|
||||
List<Tuple<string, string, string>> translationFiles = new List<Tuple<string, string, string>>();
|
||||
foreach (string filePath in Directory.GetFiles(Path.Combine("Content", "NPCConversations")))
|
||||
{
|
||||
if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) continue;
|
||||
string language = doc.Root.GetAttributeString("Language", "English");
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
protected virtual bool ShouldInterruptSubObjective(AIObjective subObjective) => false;
|
||||
public virtual void OnSelected() { }
|
||||
|
||||
protected abstract void Act(float deltaTime);
|
||||
|
||||
|
||||
@@ -117,6 +117,17 @@ namespace Barotrauma
|
||||
return weapon;
|
||||
}
|
||||
|
||||
private void Unequip()
|
||||
{
|
||||
if (character.SelectedItems.Contains(Weapon))
|
||||
{
|
||||
if (!Weapon.AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(Weapon, character, new List<InvSlotType>() { InvSlotType.Any }))
|
||||
{
|
||||
Weapon.Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool Equip(float deltaTime)
|
||||
{
|
||||
if (!character.SelectedItems.Contains(Weapon))
|
||||
@@ -241,7 +252,19 @@ namespace Barotrauma
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
|
||||
public override bool IsCompleted() => Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0.0f;
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
bool completed = Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0;
|
||||
if (completed)
|
||||
{
|
||||
if (Weapon != null)
|
||||
{
|
||||
Unequip();
|
||||
}
|
||||
}
|
||||
return completed;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted => !abandon && (reloadWeaponObjective == null || reloadWeaponObjective.CanBeCompleted) && (retreatObjective == null || retreatObjective.CanBeCompleted);
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager) => Enemy == null || Enemy.Removed || Enemy.IsDead ? 0 : 100;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
|
||||
private bool isCompleted;
|
||||
|
||||
public bool IgnoreAlreadyContainedItems;
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Barotrauma
|
||||
getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers)
|
||||
{
|
||||
GetItemPriority = GetItemPriority,
|
||||
IgnoreContainedItems = IgnoreAlreadyContainedItems
|
||||
ignoredContainerIdentifiers = ignoredContainerIdentifiers
|
||||
};
|
||||
AddSubObjective(getItemObjective);
|
||||
return;
|
||||
|
||||
@@ -78,16 +78,18 @@ namespace Barotrauma
|
||||
|
||||
foreach (FireSource fs in targetHull.FireSources)
|
||||
{
|
||||
// TODO: check if in the same room?
|
||||
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
||||
if (!character.IsClimbing && (inRange || useExtinquisherTimer > 0.0f))
|
||||
if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f))
|
||||
{
|
||||
useExtinquisherTimer += deltaTime;
|
||||
if (useExtinquisherTimer > 2.0f) useExtinquisherTimer = 0.0f;
|
||||
|
||||
character.CursorPosition = fs.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
extinguisher.Use(deltaTime, character);
|
||||
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -10,31 +10,26 @@ namespace Barotrauma
|
||||
public override bool ForceRun => true;
|
||||
|
||||
private AIObjective subObjective;
|
||||
|
||||
private string gearTag;
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
{
|
||||
if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) continue;
|
||||
if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) { continue; }
|
||||
if (character.Inventory.Items[i].HasTag(gearTag))
|
||||
{
|
||||
var containedItems = character.Inventory.Items[i].ContainedItems;
|
||||
if (containedItems == null) continue;
|
||||
if (containedItems == null) { continue; }
|
||||
|
||||
var oxygenTank = containedItems.FirstOrDefault(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
|
||||
if (oxygenTank != null) return true;
|
||||
if (oxygenTank != null) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted;
|
||||
|
||||
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit)
|
||||
: base(character, "")
|
||||
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit) : base(character, "")
|
||||
{
|
||||
gearTag = needDivingSuit ? "divingsuit" : "diving";
|
||||
}
|
||||
@@ -54,12 +49,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var containedItems = item.ContainedItems;
|
||||
if (containedItems == null) return;
|
||||
|
||||
if (containedItems == null) { return; }
|
||||
//check if there's an oxygen tank in the mask/suit
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
if (containedItem == null) continue;
|
||||
if (containedItem == null) { continue; }
|
||||
if (containedItem.Condition <= 0.0f)
|
||||
{
|
||||
containedItem.Drop(character);
|
||||
@@ -69,36 +63,21 @@ namespace Barotrauma
|
||||
//we've got an oxygen source inside the mask/suit, all good
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
|
||||
subObjective = new AIObjectiveContainItem(character, new string[] { "oxygentank", "oxygensource" }, item.GetComponent<ItemContainer>());
|
||||
}
|
||||
}
|
||||
|
||||
if (subObjective != null)
|
||||
{
|
||||
subObjective.TryComplete(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager)
|
||||
{
|
||||
if (character.AnimController.CurrentHull == null) return 100.0f;
|
||||
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
|
||||
return 100.0f - character.Oxygen;
|
||||
}
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
return otherObjective is AIObjectiveFindDivingGear;
|
||||
}
|
||||
public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted;
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager) => MathHelper.Clamp(100 - character.OxygenAvailable, 0, 100);
|
||||
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindDivingGear;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -25,7 +24,7 @@ namespace Barotrauma
|
||||
private float searchHullTimer;
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjective divingGearObjective;
|
||||
private AIObjectiveFindDivingGear divingGearObjective;
|
||||
|
||||
public AIObjectiveFindSafety(Character character) : base(character, "") { }
|
||||
|
||||
@@ -35,13 +34,12 @@ namespace Barotrauma
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
var currentHull = character.AnimController.CurrentHull;
|
||||
if (HumanAIController.NeedsDivingGear(currentHull))
|
||||
if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null)
|
||||
{
|
||||
bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90;
|
||||
bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character);
|
||||
if ((divingGearObjective == null || !divingGearObjective.CanBeCompleted) && !hasEquipment)
|
||||
if (!hasEquipment)
|
||||
{
|
||||
// If the previous objective cannot be completed, create a new and try again.
|
||||
divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit);
|
||||
}
|
||||
}
|
||||
@@ -55,9 +53,15 @@ namespace Barotrauma
|
||||
}
|
||||
else if (divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
// If diving gear objective is active, wait for it to complete.
|
||||
// If diving gear objective is active and can be completed, wait for it to complete.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
divingGearObjective = null;
|
||||
// Reset the timer so that we get a safe hull target.
|
||||
searchHullTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (unreachableClearTimer > 0)
|
||||
@@ -83,17 +87,18 @@ namespace Barotrauma
|
||||
{
|
||||
if (goToObjective.Target != bestHull)
|
||||
{
|
||||
goToObjective = new AIObjectiveGoTo(bestHull, character)
|
||||
// If we need diving gear, we should already have it, if possible.
|
||||
goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goToObjective = new AIObjectiveGoTo(bestHull, character)
|
||||
goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
AllowGoingOutside = HumanAIController.HasDivingSuit(character)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -110,6 +115,7 @@ namespace Barotrauma
|
||||
unreachable.Add(goToObjective.Target as Hull);
|
||||
}
|
||||
goToObjective = null;
|
||||
SteeringManager.SteeringWander();
|
||||
}
|
||||
}
|
||||
else if (currentHull != null)
|
||||
@@ -126,6 +132,11 @@ namespace Barotrauma
|
||||
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
//don't run from friendly NPCs
|
||||
if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; }
|
||||
//friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
|
||||
if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; }
|
||||
|
||||
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
|
||||
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
|
||||
{
|
||||
@@ -170,13 +181,15 @@ namespace Barotrauma
|
||||
if (unreachable.Contains(hull)) { continue; }
|
||||
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; }
|
||||
hullSafety = HumanAIController.GetHullSafety(hull, character);
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
hullSafety *= distanceFactor;
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore current hull, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
if (path.Unreachable) { continue; }
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(hull, true))
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
return leak.Open <= 0.0f || leak.Removed || pathUnreachable;
|
||||
return leak.Open <= 0.0f || leak.Removed;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted => !abandon && base.CanBeCompleted;
|
||||
@@ -94,16 +94,15 @@ namespace Barotrauma
|
||||
if (repairTool == null) { return; }
|
||||
|
||||
Vector2 gapDiff = leak.WorldPosition - character.WorldPosition;
|
||||
var humanoidController = character.AnimController as HumanoidAnimController;
|
||||
|
||||
// TODO: use the collider size/reach?
|
||||
if (!character.AnimController.InWater && humanoidController != null && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||
{
|
||||
((HumanoidAnimController)character.AnimController).Crouching = true;
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
|
||||
float armLength = humanoidController != null ? ConvertUnits.ToDisplayUnits(humanoidController.ArmLength) : 100;
|
||||
bool cannotReach = gapDiff.Length() > armLength + repairTool.Range;
|
||||
float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach;
|
||||
if (cannotReach)
|
||||
{
|
||||
if (gotoObjective != null)
|
||||
@@ -116,7 +115,10 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character);
|
||||
gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character)
|
||||
{
|
||||
CloseEnough = reach * 0.75f
|
||||
};
|
||||
if (!subObjectives.Contains(gotoObjective))
|
||||
{
|
||||
AddSubObjective(gotoObjective);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -14,19 +15,15 @@ namespace Barotrauma
|
||||
|
||||
//can be either tags or identifiers
|
||||
private string[] itemIdentifiers;
|
||||
|
||||
private Item targetItem, moveToTarget;
|
||||
|
||||
private int currSearchIndex;
|
||||
|
||||
public bool IgnoreContainedItems;
|
||||
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
|
||||
private float currItemPriority;
|
||||
|
||||
private bool equip;
|
||||
|
||||
private HashSet<Item> ignoredItems = new HashSet<Item>();
|
||||
|
||||
private bool canBeCompleted = true;
|
||||
public override bool CanBeCompleted => canBeCompleted;
|
||||
|
||||
@@ -36,25 +33,19 @@ namespace Barotrauma
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false)
|
||||
: base(character, "")
|
||||
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) : base(character, "")
|
||||
{
|
||||
currSearchIndex = -1;
|
||||
this.equip = equip;
|
||||
this.targetItem = targetItem;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false)
|
||||
: this(character, new string[] { itemIdentifier }, equip)
|
||||
{
|
||||
}
|
||||
public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false) : this(character, new string[] { itemIdentifier }, equip) { }
|
||||
|
||||
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false)
|
||||
: base(character, "")
|
||||
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false) : base(character, "")
|
||||
{
|
||||
currSearchIndex = -1;
|
||||
this.equip = equip;
|
||||
@@ -108,12 +99,12 @@ namespace Barotrauma
|
||||
FindTargetItem();
|
||||
if (targetItem == null || moveToTarget == null)
|
||||
{
|
||||
// TODO: cannot be completed?
|
||||
character?.AIController?.SteeringManager?.Reset();
|
||||
SteeringManager.SteeringWander();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance * 2.0f)
|
||||
if (moveToTarget.CurrentHull == character.CurrentHull &&
|
||||
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2))
|
||||
{
|
||||
int targetSlot = -1;
|
||||
if (equip)
|
||||
@@ -169,9 +160,13 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
goToObjective.TryComplete(deltaTime);
|
||||
if (!goToObjective.CanBeCompleted) targetItem = null;
|
||||
if (!goToObjective.CanBeCompleted)
|
||||
{
|
||||
targetItem = null;
|
||||
moveToTarget = null;
|
||||
ignoredItems.Add(targetItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -196,22 +191,30 @@ namespace Barotrauma
|
||||
currSearchIndex++;
|
||||
|
||||
var item = Item.ItemList[currSearchIndex];
|
||||
if (ignoredItems.Contains(item)) { continue; }
|
||||
if (item.Submarine == null) { continue; }
|
||||
else if (item.Submarine.TeamID != character.TeamID) { continue; }
|
||||
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { continue; }
|
||||
|
||||
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
|
||||
if (IgnoreContainedItems && item.Container != null) continue;
|
||||
if (!itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) continue;
|
||||
if (item.CurrentHull == null || item.Condition <= 0.0f) { continue; }
|
||||
if (itemIdentifiers.None(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; }
|
||||
|
||||
if (ignoredContainerIdentifiers != null && item.Container != null)
|
||||
{
|
||||
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
|
||||
}
|
||||
|
||||
//if the item is inside a character's inventory, don't steal it unless the character is dead
|
||||
if (item.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) continue;
|
||||
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||
}
|
||||
|
||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) continue;
|
||||
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||
}
|
||||
|
||||
float itemPriority = 0.0f;
|
||||
@@ -219,13 +222,13 @@ namespace Barotrauma
|
||||
{
|
||||
//ignore if the item has zero priority
|
||||
itemPriority = GetItemPriority(item);
|
||||
if (itemPriority <= 0.0f) continue;
|
||||
if (itemPriority <= 0.0f) { continue; }
|
||||
}
|
||||
|
||||
itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
|
||||
|
||||
//ignore if the item has a lower priority than the currently selected one
|
||||
if (moveToTarget != null && itemPriority < currItemPriority) continue;
|
||||
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
|
||||
|
||||
currItemPriority = itemPriority;
|
||||
|
||||
|
||||
@@ -186,11 +186,11 @@ namespace Barotrauma
|
||||
|
||||
bool completed = false;
|
||||
|
||||
float allowedDistance = 0.5f;
|
||||
float allowedDistance = CloseEnough;
|
||||
|
||||
if (Target is Item item)
|
||||
{
|
||||
allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), allowedDistance);
|
||||
allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), CloseEnough);
|
||||
if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
|
||||
}
|
||||
else if (Target is Character targetCharacter)
|
||||
|
||||
@@ -200,6 +200,7 @@ namespace Barotrauma
|
||||
// Check that there is no unsafe or forbidden hulls on the way to the target
|
||||
// Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
if (path.Unreachable) { continue; }
|
||||
if (path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; }
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,7 @@ namespace Barotrauma
|
||||
base.Update(objectiveManager, deltaTime);
|
||||
if (ignoreListTimer > ignoreListClearInterval)
|
||||
{
|
||||
ignoreList.Clear();
|
||||
ignoreListTimer = 0;
|
||||
UpdateTargets();
|
||||
Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -67,16 +65,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
ignoreList.Clear();
|
||||
ignoreListTimer = 0;
|
||||
UpdateTargets();
|
||||
}
|
||||
|
||||
public override void OnSelected()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager)
|
||||
{
|
||||
if (character.Submarine == null) { return 0; }
|
||||
if (targets.None()) { return 0; }
|
||||
float avg = targets.Average(t => Average(t));
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority - MathHelper.Max(0, AIObjectiveManager.OrderPriority - avg);
|
||||
}
|
||||
return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority, avg / 100);
|
||||
return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority + 20, avg / 100);
|
||||
}
|
||||
|
||||
protected void UpdateTargets()
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace Barotrauma
|
||||
|
||||
private AIObjective GetCurrentObjective()
|
||||
{
|
||||
var previousObjective = CurrentObjective;
|
||||
var firstObjective = Objectives.FirstOrDefault();
|
||||
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority(this) > firstObjective.GetPriority(this))
|
||||
{
|
||||
@@ -76,6 +77,10 @@ namespace Barotrauma
|
||||
{
|
||||
CurrentObjective = firstObjective;
|
||||
}
|
||||
if (previousObjective != CurrentObjective)
|
||||
{
|
||||
CurrentObjective?.OnSelected();
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -84,7 +85,14 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.CanInteractWith(Item))
|
||||
{
|
||||
OperateRepairTool(deltaTime);
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (repairTool != null)
|
||||
{
|
||||
OperateRepairTool(deltaTime);
|
||||
}
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
if (repairable.CurrentFixer != null && repairable.CurrentFixer != character)
|
||||
@@ -121,13 +129,17 @@ namespace Barotrauma
|
||||
subObjectives.Remove(goToObjective);
|
||||
}
|
||||
goToObjective = new AIObjectiveGoTo(Item, character);
|
||||
if (repairTool != null)
|
||||
{
|
||||
goToObjective.CloseEnough = (HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range)) * 0.75f;
|
||||
}
|
||||
AddSubObjective(goToObjective);
|
||||
}
|
||||
}
|
||||
|
||||
private void OperateRepairTool(float deltaTime)
|
||||
private RepairTool repairTool;
|
||||
private void FindRepairTool()
|
||||
{
|
||||
// Operate repair tool, if required.
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
foreach (var kvp in repairable.requiredItems)
|
||||
@@ -138,19 +150,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (requiredItem.MatchesItem(item))
|
||||
{
|
||||
var repairTool = item.GetComponent<RepairTool>();
|
||||
if (repairTool != null)
|
||||
{
|
||||
character.CursorPosition = Item.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
repairTool.Use(deltaTime, character);
|
||||
return;
|
||||
}
|
||||
repairTool = item.GetComponent<RepairTool>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OperateRepairTool(float deltaTime)
|
||||
{
|
||||
character.CursorPosition = Item.Position;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
Vector2 fromToolToTarget = Item.Position - repairTool.Item.Position;
|
||||
if (fromToolToTarget.LengthSquared() < MathUtils.Pow(repairTool.Range / 2, 2))
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - Item.SimPosition) / 2);
|
||||
}
|
||||
if (character.IsClimbing ||
|
||||
VectorExtensions.Angle(VectorExtensions.Forward(repairTool.Item.body.TransformedRotation), fromToolToTarget) < MathHelper.PiOver4)
|
||||
{
|
||||
repairTool.Use(deltaTime, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,14 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Repairable repairable in item.Repairables)
|
||||
{
|
||||
if (item.Condition > repairable.ShowRepairUIThreshold) { ignore = true; }
|
||||
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character)) { ignore = true; }
|
||||
if (!objectives.ContainsKey(item) && item.Condition > repairable.ShowRepairUIThreshold)
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character))
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
if (ignore) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed);
|
||||
|
||||
return new SteeringPath();
|
||||
return new SteeringPath(true);
|
||||
}
|
||||
|
||||
closestDist = 0.0f;
|
||||
@@ -225,8 +225,12 @@ namespace Barotrauma
|
||||
//if searching for a path inside the sub, make sure the waypoint is visible
|
||||
if (insideSubmarine)
|
||||
{
|
||||
// TODO: for some reason fails to find the path when the sub is flooding. Disabling this check helps fixes it, but we can't disable it
|
||||
var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition);
|
||||
if (body != null && body.UserData is Structure) continue;
|
||||
if (body != null && body.UserData is Structure)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
closestDist = dist;
|
||||
@@ -237,10 +241,9 @@ namespace Barotrauma
|
||||
if (endNode == null)
|
||||
{
|
||||
DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed);
|
||||
return new SteeringPath();
|
||||
return new SteeringPath(true);
|
||||
}
|
||||
|
||||
|
||||
var path = FindPath(startNode, endNode);
|
||||
|
||||
return path;
|
||||
@@ -266,7 +269,7 @@ namespace Barotrauma
|
||||
if (startNode == null || endNode == null)
|
||||
{
|
||||
DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed);
|
||||
return new SteeringPath();
|
||||
return new SteeringPath(true);
|
||||
}
|
||||
|
||||
return FindPath(startNode, endNode);
|
||||
@@ -290,13 +293,12 @@ namespace Barotrauma
|
||||
node.G = 0.0f;
|
||||
node.H = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
start.state = 1;
|
||||
while (true)
|
||||
{
|
||||
|
||||
PathNode currNode = null;
|
||||
float dist = 10000.0f;
|
||||
float dist = float.MaxValue;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
if (node.state != 1) continue;
|
||||
@@ -364,7 +366,7 @@ namespace Barotrauma
|
||||
|
||||
if (end.state == 0 || end.Parent == null)
|
||||
{
|
||||
//path not found
|
||||
//DebugConsole.NewMessage("Pathfinding error: path not found", Color.DarkRed);
|
||||
return new SteeringPath(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -683,12 +683,6 @@ namespace Barotrauma
|
||||
|
||||
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength);
|
||||
}
|
||||
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
|
||||
{
|
||||
angle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
|
||||
|
||||
@@ -1059,6 +1059,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float prevFootPos;
|
||||
|
||||
void UpdateClimbing()
|
||||
{
|
||||
if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent<Ladder>() == null)
|
||||
@@ -1137,15 +1139,34 @@ namespace Barotrauma
|
||||
handPos.X - Dir * 0.05f,
|
||||
bottomPos + ColliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y);
|
||||
|
||||
MoveLimb(leftFoot,
|
||||
new Vector2(footPos.X,
|
||||
(slide ? footPos.Y : MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight) + ladderSimPos.Y),
|
||||
15.5f, true);
|
||||
if (slide)
|
||||
{
|
||||
MoveLimb(leftFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true);
|
||||
MoveLimb(rightFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight;
|
||||
float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight;
|
||||
MoveLimb(leftFoot, new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), 15.5f, true);
|
||||
|
||||
MoveLimb(rightFoot,
|
||||
new Vector2(footPos.X,
|
||||
(slide ? footPos.Y : MathUtils.Round(footPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
|
||||
15.5f, true);
|
||||
float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f);
|
||||
float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f);
|
||||
MoveLimb(rightFoot, new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), 15.5f, true);
|
||||
#if CLIENT
|
||||
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition);
|
||||
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition);
|
||||
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
#endif
|
||||
prevFootPos = footPos.Y;
|
||||
}
|
||||
|
||||
//apply torque to the legs to make the knees bend
|
||||
Limb leftLeg = GetLimb(LimbType.LeftLeg);
|
||||
@@ -1781,14 +1802,24 @@ namespace Barotrauma
|
||||
float c = Vector2.Distance(pos, shoulderPos);
|
||||
c = MathHelper.Clamp(c, Math.Abs(upperArmLength - forearmLength), forearmLength + upperArmLength - 0.01f);
|
||||
|
||||
float ang2 = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
|
||||
float armAngle = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
|
||||
|
||||
float armAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c);
|
||||
float handAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c);
|
||||
float upperArmAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c) * Dir;
|
||||
float lowerArmAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c) * Dir;
|
||||
|
||||
//make sure the arm angle "has the same number of revolutions" as the arm
|
||||
while (arm.Rotation - armAngle > MathHelper.Pi)
|
||||
{
|
||||
armAngle += MathHelper.TwoPi;
|
||||
}
|
||||
while (arm.Rotation - armAngle < -MathHelper.Pi)
|
||||
{
|
||||
armAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
arm?.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f * force * arm.Mass);
|
||||
forearm?.body.SmoothRotate((ang2 + handAngle * Dir), 20.0f * force * forearm.Mass);
|
||||
hand?.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force * hand.Mass);
|
||||
arm?.body.SmoothRotate((armAngle - upperArmAngle), 20.0f * force * arm.Mass, wrapAngle: false);
|
||||
forearm?.body.SmoothRotate((armAngle + lowerArmAngle), 20.0f * force * forearm.Mass, wrapAngle: false);
|
||||
hand?.body.SmoothRotate((armAngle + lowerArmAngle), 100.0f * force * hand.Mass, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
|
||||
|
||||
@@ -86,7 +86,10 @@ namespace Barotrauma
|
||||
public bool onGround;
|
||||
private bool ignorePlatforms;
|
||||
|
||||
protected float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale;
|
||||
/// <summary>
|
||||
/// In sim units. Joint scale applied.
|
||||
/// </summary>
|
||||
public float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale;
|
||||
|
||||
public Structure Stairs;
|
||||
|
||||
@@ -1324,6 +1327,19 @@ namespace Barotrauma
|
||||
}
|
||||
if (errorMsg != null)
|
||||
{
|
||||
if (character.IsRemotePlayer)
|
||||
{
|
||||
errorMsg += " Ragdoll controlled remotely.";
|
||||
}
|
||||
if (SimplePhysicsEnabled)
|
||||
{
|
||||
errorMsg += " Simple physics enabled.";
|
||||
}
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
@@ -1343,7 +1359,6 @@ namespace Barotrauma
|
||||
SetInitialLimbPositions();
|
||||
return;
|
||||
}
|
||||
UpdateProjSpecific(deltaTime);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
@@ -120,8 +120,6 @@ namespace Barotrauma
|
||||
private List<StatusEffect> statusEffects = new List<StatusEffect>();
|
||||
private List<float> speedMultipliers = new List<float>();
|
||||
|
||||
private List<StatusEffect> statusEffects = new List<StatusEffect>();
|
||||
|
||||
public Entity ViewTarget
|
||||
{
|
||||
get;
|
||||
@@ -330,7 +328,8 @@ namespace Barotrauma
|
||||
pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private float ragdollingLockTimer;
|
||||
public bool IsRagdolled;
|
||||
public bool IsForceRagdolled;
|
||||
public bool dontFollowCursor;
|
||||
@@ -1877,8 +1876,6 @@ namespace Barotrauma
|
||||
}
|
||||
speechImpedimentSet = false;
|
||||
|
||||
|
||||
|
||||
if (needsAir)
|
||||
{
|
||||
bool protectedFromPressure = PressureProtection > 0.0f;
|
||||
@@ -1935,9 +1932,23 @@ namespace Barotrauma
|
||||
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
|
||||
bool allowRagdoll = GameMain.NetworkMember != null ? GameMain.NetworkMember.ServerSettings.AllowRagdollButton : true;
|
||||
if (IsForceRagdolled)
|
||||
{
|
||||
IsRagdolled = IsForceRagdolled;
|
||||
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f)) //Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||
}
|
||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f))
|
||||
{
|
||||
if (ragdollingLockTimer > 0.0f)
|
||||
{
|
||||
ragdollingLockTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool wasRagdolled = IsRagdolled;
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.25f; }
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSightRange();
|
||||
UpdateSoundRange();
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace Barotrauma
|
||||
public readonly List<string> AllowedDialogTags;
|
||||
|
||||
private float commonness;
|
||||
public float Commonness
|
||||
{
|
||||
get { return commonness; }
|
||||
}
|
||||
|
||||
public NPCPersonalityTrait(XElement element)
|
||||
{
|
||||
|
||||
@@ -457,20 +457,6 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
#if CLIENT && WINDOWS
|
||||
commands.Add(new Command("copyitemnames", "", (string[] args) =>
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (MapEntityPrefab mp in MapEntityPrefab.List)
|
||||
{
|
||||
if (!(mp is ItemPrefab)) continue;
|
||||
sb.AppendLine(mp.Name);
|
||||
}
|
||||
System.Windows.Clipboard.SetText(sb.ToString());
|
||||
}));
|
||||
#endif
|
||||
|
||||
|
||||
commands.Add(new Command("findentityids", "findentityids [entityname]", (string[] args) =>
|
||||
{
|
||||
if (args.Length == 0) return;
|
||||
@@ -1435,58 +1421,25 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
case "cursor":
|
||||
spawnPos = cursorPos;
|
||||
break;
|
||||
case "inventory":
|
||||
spawnInventory = controlledCharacter?.Inventory;
|
||||
break;
|
||||
case "cargo":
|
||||
var wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub);
|
||||
spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
|
||||
break;
|
||||
default:
|
||||
//Check if last arg matches the name of an in-game player
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower());
|
||||
if (client == null)
|
||||
{
|
||||
NewMessage("No player found with the name \"" + args.Last() + "\". Spawning item at random location. If the player you want to give the item to has a space in their name, try surrounding their name with quotes (\").", Color.Red);
|
||||
break;
|
||||
}
|
||||
else if (client.Character == null)
|
||||
{
|
||||
errorMsg = "The player \"" + args.Last() + "\" is connected, but hasn't spawned yet.";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the last arg matches the name of an in-game player, set the destination to their inventory.
|
||||
spawnInventory = client.Character.Inventory;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var matchingCharacter = FindMatchingCharacter(args.Skip(1).ToArray());
|
||||
if (matchingCharacter?.Inventory != null) spawnInventory = matchingCharacter.Inventory;
|
||||
}
|
||||
break;
|
||||
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower());
|
||||
if (client != null)
|
||||
{
|
||||
extraParams += 1;
|
||||
itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant();
|
||||
if (client.Character != null && client.Character.Name == args.Last().ToLower()) spawnInventory = client.Character.Inventory;
|
||||
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
string itemName = args[0];
|
||||
|
||||
var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
|
||||
//Check again if the item can be found again after having checked for a character
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
errorMsg = "Item \"" + itemName + "\" not found!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (spawnPos == null && spawnInventory == null)
|
||||
if ((spawnPos == null || spawnPos == Vector2.Zero) && spawnInventory == null)
|
||||
{
|
||||
var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub);
|
||||
spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
|
||||
@@ -1495,6 +1448,7 @@ namespace Barotrauma
|
||||
if (spawnPos != null)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
|
||||
|
||||
}
|
||||
else if (spawnInventory != null)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
|
||||
public bool CheatsEnabled;
|
||||
|
||||
const int InitialMoney = 4500;
|
||||
const int InitialMoney = 4700;
|
||||
|
||||
protected bool watchmenSpawned;
|
||||
protected Character startWatchman, endWatchman;
|
||||
|
||||
@@ -45,8 +45,6 @@ namespace Barotrauma
|
||||
|
||||
private static byte currentCampaignID;
|
||||
|
||||
private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>();
|
||||
|
||||
public byte CampaignID
|
||||
{
|
||||
get; private set;
|
||||
@@ -217,7 +215,7 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
characterData.Clear();
|
||||
string characterDataPath = GetCharacterDataSavePath();
|
||||
var characterDataDoc = XMLExtensions.TryLoadXml(characterDataPath);
|
||||
@@ -226,33 +224,8 @@ namespace Barotrauma
|
||||
{
|
||||
characterData.Add(new CharacterCampaignData(subElement));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
XElement modeElement = new XElement("MultiPlayerCampaign",
|
||||
new XAttribute("money", Money),
|
||||
new XAttribute("cheatsenabled", CheatsEnabled));
|
||||
Map.Save(modeElement);
|
||||
element.Add(modeElement);
|
||||
|
||||
//save character data to a separate file
|
||||
string characterDataPath = GetCharacterDataSavePath();
|
||||
XDocument characterDataDoc = new XDocument(new XElement("CharacterData"));
|
||||
foreach (CharacterCampaignData cd in characterData)
|
||||
{
|
||||
characterDataDoc.Root.Add(cd.Save());
|
||||
}
|
||||
try
|
||||
{
|
||||
characterDataDoc.Save(characterDataPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving multiplayer campaign characters to \"" + characterDataPath + "\" failed!", e);
|
||||
}
|
||||
|
||||
lastSaveID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,34 +36,6 @@ namespace Barotrauma
|
||||
public bool VSyncEnabled { get; set; }
|
||||
|
||||
public bool EnableSplashScreen { get; set; }
|
||||
|
||||
public int ParticleLimit { get; set; }
|
||||
|
||||
public int ParticleLimit { get; set; }
|
||||
|
||||
public float LightMapScale { get; set; }
|
||||
public bool SpecularityEnabled { get; set; }
|
||||
public bool ChromaticAberrationEnabled { get; set; }
|
||||
|
||||
public int ParticleLimit { get; set; }
|
||||
|
||||
public float LightMapScale { get; set; }
|
||||
public bool SpecularityEnabled { get; set; }
|
||||
public bool ChromaticAberrationEnabled { get; set; }
|
||||
|
||||
public bool MuteOnFocusLost { get; set; }
|
||||
|
||||
public enum VoiceMode
|
||||
{
|
||||
Disabled,
|
||||
PushToTalk,
|
||||
Activity
|
||||
};
|
||||
|
||||
public VoiceMode VoiceSetting { get; set; }
|
||||
public string VoiceCaptureDevice { get; set; }
|
||||
|
||||
public int ParticleLimit { get; set; }
|
||||
|
||||
public int ParticleLimit { get; set; }
|
||||
|
||||
@@ -196,7 +168,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f;
|
||||
private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f, microphoneVolume = 1.0f;
|
||||
|
||||
public float SoundVolume
|
||||
{
|
||||
@@ -239,6 +211,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float MicrophoneVolume
|
||||
{
|
||||
get { return microphoneVolume; }
|
||||
set
|
||||
{
|
||||
microphoneVolume = MathHelper.Clamp(value, 0.1f, 5.0f);
|
||||
}
|
||||
}
|
||||
public string Language
|
||||
{
|
||||
get { return TextManager.Language; }
|
||||
@@ -397,10 +377,6 @@ namespace Barotrauma
|
||||
|
||||
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f);
|
||||
|
||||
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f);
|
||||
|
||||
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f);
|
||||
|
||||
keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length];
|
||||
keyMapping[(int)InputType.Up] = new KeyOrMouse(Keys.W);
|
||||
keyMapping[(int)InputType.Down] = new KeyOrMouse(Keys.S);
|
||||
@@ -509,70 +485,6 @@ namespace Barotrauma
|
||||
SelectedContentPackages.Add(matchingContentPackage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextManager.LoadTextPacks(SelectedContentPackages);
|
||||
|
||||
//display error messages after all content packages have been loaded
|
||||
//to make sure the package that contains text files has been loaded before we attempt to use TextManager
|
||||
foreach (string missingPackagePath in missingPackagePaths)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath));
|
||||
}
|
||||
foreach (ContentPackage incompatiblePackage in incompatiblePackages)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
|
||||
.Replace("[packagename]", incompatiblePackage.Name)
|
||||
.Replace("[packageversion]", incompatiblePackage.GameVersion.ToString())
|
||||
.Replace("[gameversion]", GameMain.Version.ToString()));
|
||||
}
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
|
||||
TextManager.LoadTextPacks(SelectedContentPackages);
|
||||
|
||||
//display error messages after all content packages have been loaded
|
||||
//to make sure the package that contains text files has been loaded before we attempt to use TextManager
|
||||
foreach (string missingPackagePath in missingPackagePaths)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath));
|
||||
}
|
||||
foreach (ContentPackage incompatiblePackage in incompatiblePackages)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
|
||||
.Replace("[packagename]", incompatiblePackage.Name)
|
||||
.Replace("[packageversion]", incompatiblePackage.GameVersion.ToString())
|
||||
.Replace("[gameversion]", GameMain.Version.ToString()));
|
||||
}
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
if (!SelectedContentPackages.Any())
|
||||
{
|
||||
var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage);
|
||||
if (availablePackage != null)
|
||||
{
|
||||
SelectedContentPackages.Add(availablePackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,369 +933,7 @@ namespace Barotrauma
|
||||
gMode = new XElement("graphicsmode");
|
||||
doc.Root.Add(gMode);
|
||||
}
|
||||
if (GraphicsWidth == 0 || GraphicsHeight == 0)
|
||||
{
|
||||
gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
gMode.ReplaceAttributes(
|
||||
new XAttribute("width", GraphicsWidth),
|
||||
new XAttribute("height", GraphicsHeight),
|
||||
new XAttribute("vsync", VSyncEnabled),
|
||||
new XAttribute("displaymode", windowMode));
|
||||
}
|
||||
|
||||
XElement gSettings = doc.Root.Element("graphicssettings");
|
||||
if (gSettings == null)
|
||||
{
|
||||
gSettings = new XElement("graphicssettings");
|
||||
doc.Root.Add(gSettings);
|
||||
}
|
||||
|
||||
gSettings.ReplaceAttributes(
|
||||
new XAttribute("particlelimit", ParticleLimit),
|
||||
new XAttribute("lightmapscale", LightMapScale),
|
||||
new XAttribute("specularity", SpecularityEnabled),
|
||||
new XAttribute("chromaticaberration", ChromaticAberrationEnabled),
|
||||
new XAttribute("losmode", LosMode),
|
||||
new XAttribute("hudscale", HUDScale),
|
||||
new XAttribute("inventoryscale", InventoryScale));
|
||||
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
if (contentPackage.Path.Contains(vanillaContentPackagePath))
|
||||
{
|
||||
doc.Root.Add(new XElement("contentpackage", new XAttribute("path", contentPackage.Path)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var keyMappingElement = new XElement("keymapping");
|
||||
doc.Root.Add(keyMappingElement);
|
||||
for (int i = 0; i < keyMapping.Length; i++)
|
||||
{
|
||||
if (keyMapping[i].MouseButton == null)
|
||||
{
|
||||
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].MouseButton));
|
||||
}
|
||||
}
|
||||
|
||||
var gameplay = new XElement("gameplay");
|
||||
var jobPreferences = new XElement("jobpreferences");
|
||||
foreach (string jobName in JobPreferences)
|
||||
{
|
||||
jobPreferences.Add(new XElement("job", new XAttribute("identifier", jobName)));
|
||||
}
|
||||
gameplay.Add(jobPreferences);
|
||||
doc.Root.Add(gameplay);
|
||||
|
||||
var playerElement = new XElement("player",
|
||||
new XAttribute("name", defaultPlayerName ?? ""),
|
||||
new XAttribute("headindex", CharacterHeadIndex),
|
||||
new XAttribute("gender", CharacterGender),
|
||||
new XAttribute("race", CharacterRace),
|
||||
new XAttribute("hairindex", CharacterHairIndex),
|
||||
new XAttribute("beardindex", CharacterBeardIndex),
|
||||
new XAttribute("moustacheindex", CharacterMoustacheIndex),
|
||||
new XAttribute("faceattachmentindex", CharacterFaceAttachmentIndex));
|
||||
doc.Root.Add(playerElement);
|
||||
|
||||
XmlWriterSettings settings = new XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
OmitXmlDeclaration = true,
|
||||
NewLineOnAttributes = true
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using (var writer = XmlWriter.Create(savePath, settings))
|
||||
{
|
||||
doc.WriteTo(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving game settings failed.", e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Load PlayerConfig
|
||||
// TODO: DRY
|
||||
public void LoadPlayerConfig()
|
||||
{
|
||||
XDocument doc = XMLExtensions.LoadXml(playerSavePath);
|
||||
|
||||
if (doc == null || doc.Root == null)
|
||||
{
|
||||
ShowUserStatisticsPrompt = true;
|
||||
SaveNewPlayerConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
Language = doc.Root.GetAttributeString("language", Language);
|
||||
AutoCheckUpdates = doc.Root.GetAttributeBool("autocheckupdates", AutoCheckUpdates);
|
||||
sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", true);
|
||||
|
||||
XElement graphicsMode = doc.Root.Element("graphicsmode");
|
||||
GraphicsWidth = graphicsMode.GetAttributeInt("width", GraphicsWidth);
|
||||
GraphicsHeight = graphicsMode.GetAttributeInt("height", GraphicsHeight);
|
||||
VSyncEnabled = graphicsMode.GetAttributeBool("vsync", VSyncEnabled);
|
||||
|
||||
XElement graphicsSettings = doc.Root.Element("graphicssettings");
|
||||
ParticleLimit = graphicsSettings.GetAttributeInt("particlelimit", ParticleLimit);
|
||||
LightMapScale = MathHelper.Clamp(graphicsSettings.GetAttributeFloat("lightmapscale", LightMapScale), 0.1f, 1.0f);
|
||||
SpecularityEnabled = graphicsSettings.GetAttributeBool("specularity", SpecularityEnabled);
|
||||
ChromaticAberrationEnabled = graphicsSettings.GetAttributeBool("chromaticaberration", ChromaticAberrationEnabled);
|
||||
HUDScale = graphicsSettings.GetAttributeFloat("hudscale", HUDScale);
|
||||
InventoryScale = graphicsSettings.GetAttributeFloat("inventoryscale", InventoryScale);
|
||||
var losModeStr = graphicsSettings.GetAttributeString("losmode", "Transparent");
|
||||
if (!Enum.TryParse(losModeStr, out losMode))
|
||||
{
|
||||
losMode = LosMode.Transparent;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (GraphicsWidth == 0 || GraphicsHeight == 0)
|
||||
{
|
||||
GraphicsWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
|
||||
GraphicsHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
|
||||
}
|
||||
#endif
|
||||
|
||||
var windowModeStr = graphicsMode.GetAttributeString("displaymode", "Fullscreen");
|
||||
if (!Enum.TryParse(windowModeStr, out windowMode))
|
||||
{
|
||||
windowMode = WindowMode.Fullscreen;
|
||||
}
|
||||
|
||||
XElement audioSettings = doc.Root.Element("audio");
|
||||
if (audioSettings != null)
|
||||
{
|
||||
SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume);
|
||||
MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume);
|
||||
VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume);
|
||||
string voiceSettingStr = audioSettings.GetAttributeString("voicesetting", "Disabled");
|
||||
VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", "");
|
||||
NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", -45);
|
||||
var voiceSetting = VoiceMode.Disabled;
|
||||
if (Enum.TryParse(voiceSettingStr, out voiceSetting))
|
||||
{
|
||||
VoiceSetting = voiceSetting;
|
||||
}
|
||||
}
|
||||
|
||||
useSteamMatchmaking = doc.Root.GetAttributeBool("usesteammatchmaking", useSteamMatchmaking);
|
||||
requireSteamAuthentication = doc.Root.GetAttributeBool("requiresteamauthentication", requireSteamAuthentication);
|
||||
|
||||
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
|
||||
|
||||
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount);
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "keymapping":
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType))
|
||||
{
|
||||
if (int.TryParse(attribute.Value.ToString(), out int mouseButton))
|
||||
{
|
||||
keyMapping[(int)inputType] = new KeyOrMouse(mouseButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
|
||||
{
|
||||
keyMapping[(int)inputType] = new KeyOrMouse(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "gameplay":
|
||||
jobPreferences = new List<string>();
|
||||
foreach (XElement ele in subElement.Element("jobpreferences").Elements("job"))
|
||||
{
|
||||
string jobIdentifier = ele.GetAttributeString("identifier", "");
|
||||
if (string.IsNullOrEmpty(jobIdentifier)) continue;
|
||||
jobPreferences.Add(jobIdentifier);
|
||||
}
|
||||
break;
|
||||
case "player":
|
||||
defaultPlayerName = subElement.GetAttributeString("name", defaultPlayerName);
|
||||
CharacterHeadIndex = subElement.GetAttributeInt("headindex", CharacterHeadIndex);
|
||||
if (Enum.TryParse(subElement.GetAttributeString("gender", "none"), true, out Gender g))
|
||||
{
|
||||
CharacterGender = g;
|
||||
}
|
||||
if (Enum.TryParse(subElement.GetAttributeString("race", "white"), true, out Race r))
|
||||
{
|
||||
CharacterRace = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterRace = Race.White;
|
||||
}
|
||||
CharacterHairIndex = subElement.GetAttributeInt("hairindex", CharacterHairIndex);
|
||||
CharacterBeardIndex = subElement.GetAttributeInt("beardindex", CharacterBeardIndex);
|
||||
CharacterMoustacheIndex = subElement.GetAttributeInt("moustacheindex", CharacterMoustacheIndex);
|
||||
CharacterFaceAttachmentIndex = subElement.GetAttributeInt("faceattachmentindex", CharacterFaceAttachmentIndex);
|
||||
break;
|
||||
case "tutorials":
|
||||
foreach (XElement tutorialElement in subElement.Elements())
|
||||
{
|
||||
CompletedTutorialNames.Add(tutorialElement.GetAttributeString("name", ""));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
|
||||
{
|
||||
if (keyMapping[(int)inputType] == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Key binding for the input type \"" + inputType + " not set!");
|
||||
keyMapping[(int)inputType] = new KeyOrMouse(Keys.D1);
|
||||
}
|
||||
}
|
||||
|
||||
UnsavedSettings = false;
|
||||
|
||||
selectedContentPackagePaths = new HashSet<string>();
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "contentpackage":
|
||||
string path = System.IO.Path.GetFullPath(subElement.GetAttributeString("path", ""));
|
||||
selectedContentPackagePaths.Add(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LoadContentPackages(selectedContentPackagePaths);
|
||||
}
|
||||
|
||||
public void ReloadContentPackages()
|
||||
{
|
||||
LoadContentPackages(selectedContentPackagePaths);
|
||||
}
|
||||
|
||||
private void LoadContentPackages(IEnumerable<string> contentPackagePaths)
|
||||
{
|
||||
var missingPackagePaths = new List<string>();
|
||||
var incompatiblePackages = new List<ContentPackage>();
|
||||
SelectedContentPackages.Clear();
|
||||
foreach (string path in contentPackagePaths)
|
||||
{
|
||||
var matchingContentPackage = ContentPackage.List.Find(cp => System.IO.Path.GetFullPath(cp.Path) == path);
|
||||
|
||||
if (matchingContentPackage == null)
|
||||
{
|
||||
missingPackagePaths.Add(path);
|
||||
}
|
||||
else if (!matchingContentPackage.IsCompatible())
|
||||
{
|
||||
incompatiblePackages.Add(matchingContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedContentPackages.Add(matchingContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
TextManager.LoadTextPacks(SelectedContentPackages);
|
||||
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
if (!SelectedContentPackages.Any())
|
||||
{
|
||||
var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage);
|
||||
if (availablePackage != null)
|
||||
{
|
||||
SelectedContentPackages.Add(availablePackage);
|
||||
}
|
||||
}
|
||||
|
||||
//save to get rid of the invalid selected packages in the config file
|
||||
if (missingPackagePaths.Count > 0 || incompatiblePackages.Count > 0) { SaveNewPlayerConfig(); }
|
||||
|
||||
//display error messages after all content packages have been loaded
|
||||
//to make sure the package that contains text files has been loaded before we attempt to use TextManager
|
||||
foreach (string missingPackagePath in missingPackagePaths)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath));
|
||||
}
|
||||
foreach (ContentPackage incompatiblePackage in incompatiblePackages)
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
|
||||
.Replace("[packagename]", incompatiblePackage.Name)
|
||||
.Replace("[packageversion]", incompatiblePackage.GameVersion.ToString())
|
||||
.Replace("[gameversion]", GameMain.Version.ToString()));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Save PlayerConfig
|
||||
public void SaveNewPlayerConfig()
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
UnsavedSettings = false;
|
||||
|
||||
if (doc.Root == null)
|
||||
{
|
||||
doc.Add(new XElement("config"));
|
||||
}
|
||||
|
||||
doc.Root.Add(
|
||||
new XAttribute("language", TextManager.Language),
|
||||
new XAttribute("masterserverurl", MasterServerUrl),
|
||||
new XAttribute("autocheckupdates", AutoCheckUpdates),
|
||||
new XAttribute("musicvolume", musicVolume),
|
||||
new XAttribute("soundvolume", soundVolume),
|
||||
new XAttribute("verboselogging", VerboseLogging),
|
||||
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
|
||||
new XAttribute("enablesplashscreen", EnableSplashScreen),
|
||||
new XAttribute("usesteammatchmaking", useSteamMatchmaking),
|
||||
new XAttribute("quickstartsub", QuickStartSubmarineName),
|
||||
new XAttribute("requiresteamauthentication", requireSteamAuthentication),
|
||||
new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems),
|
||||
new XAttribute("aimassistamount", aimAssistAmount));
|
||||
|
||||
if (!ShowUserStatisticsPrompt)
|
||||
{
|
||||
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
|
||||
}
|
||||
|
||||
XElement gMode = doc.Root.Element("graphicsmode");
|
||||
if (gMode == null)
|
||||
{
|
||||
gMode = new XElement("graphicsmode");
|
||||
doc.Root.Add(gMode);
|
||||
}
|
||||
if (GraphicsWidth == 0 || GraphicsHeight == 0)
|
||||
{
|
||||
gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode));
|
||||
|
||||
@@ -641,7 +641,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]);
|
||||
}
|
||||
//make sure the left hull is linked to the gap first (gap logic assumes that the first hull is the one to the left)
|
||||
if (doorGap.linkedTo[0].Rect.X > doorGap.linkedTo[1].Rect.X)
|
||||
if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].Rect.X > doorGap.linkedTo[1].Rect.X)
|
||||
{
|
||||
var temp = doorGap.linkedTo[0];
|
||||
doorGap.linkedTo[0] = doorGap.linkedTo[1];
|
||||
@@ -659,7 +659,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]);
|
||||
}
|
||||
//make sure the upper hull is linked to the gap first (gap logic assumes that the first hull is above the second one)
|
||||
if (doorGap.linkedTo[0].Rect.Y < doorGap.linkedTo[1].Rect.Y)
|
||||
if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].Rect.Y < doorGap.linkedTo[1].Rect.Y)
|
||||
{
|
||||
var temp = doorGap.linkedTo[0];
|
||||
doorGap.linkedTo[0] = doorGap.linkedTo[1];
|
||||
|
||||
@@ -51,6 +51,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public PhysicsBody Body { get; private set; }
|
||||
|
||||
private float RepairThreshold
|
||||
{
|
||||
get { return item.GetComponent<Repairable>()?.ShowRepairUIThreshold ?? 0.0f; }
|
||||
}
|
||||
|
||||
private float stuck;
|
||||
[Serialize(0.0f, false)]
|
||||
public float Stuck
|
||||
@@ -207,7 +212,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool HasRequiredItems(Character character, bool addMessage)
|
||||
{
|
||||
if (item.Condition <= 0.0f) return true; //For repairing
|
||||
if (item.Condition <= RepairThreshold) return true; //For repairing
|
||||
|
||||
//this is a bit pointless atm because if canBePicked is false it won't allow you to do Pick() anyway, however it's still good for future-proofing.
|
||||
return requiredItems.Any() ? base.HasRequiredItems(character, addMessage) : canBePicked;
|
||||
@@ -215,12 +220,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return item.Condition <= 0.0f ? true : base.Pick(picker);
|
||||
return item.Condition <= RepairThreshold ? true : base.Pick(picker);
|
||||
}
|
||||
|
||||
public override bool OnPicked(Character picker)
|
||||
{
|
||||
if (item.Condition <= 0.0f) return true; //repairs
|
||||
if (item.Condition <= RepairThreshold) return true; //repairs
|
||||
|
||||
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function
|
||||
#if CLIENT
|
||||
@@ -232,7 +237,7 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
//can only be selected if the item is broken
|
||||
return item.Condition <= 0.0f;
|
||||
return item.Condition <= RepairThreshold;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UseProjSpecific(float deltaTime);
|
||||
|
||||
private List<FireSource> fireSourcesInRange = new List<FireSource>();
|
||||
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;
|
||||
@@ -159,7 +160,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (ExtinguishAmount > 0.0f && item.CurrentHull != null)
|
||||
{
|
||||
List<FireSource> fireSourcesInRange = new List<FireSource>();
|
||||
fireSourcesInRange.Clear();
|
||||
//step along the ray in 10% intervals, collecting all fire sources in the range
|
||||
for (float x = 0.0f; x <= Submarine.LastPickedFraction; x += 0.1f)
|
||||
{
|
||||
@@ -200,7 +201,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
|
||||
targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess, user);
|
||||
|
||||
|
||||
//if the next section is small enough, apply the effect to it as well
|
||||
//(to make it easier to fix a small "left-over" section)
|
||||
for (int i = -1; i < 2; i += 2)
|
||||
@@ -252,11 +253,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex);
|
||||
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter);
|
||||
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition);
|
||||
|
||||
private float sinTime;
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
Gap leak = objective.OperateTarget as Gap;
|
||||
@@ -280,18 +281,26 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f);
|
||||
standPos = leak.WorldPosition + standPos * Range;
|
||||
// TODO: check if too close to the stand pos -> move away so that the tool can hit the target and not through it?
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: sometimes stuck here, if too close to the target
|
||||
//close enough -> stop moving
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (dist < Range / 2)
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
|
||||
}
|
||||
else if (!character.IsClimbing)
|
||||
{
|
||||
// Close enough -> stop if not in ladders.
|
||||
// In ladders, we most likely want to move back and forth, because we cannot aim -> if the leak is on the side, it should get fixed.
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
character.CursorPosition = leak.Position;
|
||||
sinTime += deltaTime;
|
||||
character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
@@ -300,12 +309,17 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Use(deltaTime, character);
|
||||
}
|
||||
else
|
||||
{
|
||||
sinTime -= deltaTime * 2;
|
||||
}
|
||||
|
||||
// TODO: fix until the wall is fixed?
|
||||
bool leakFixed = leak.Open <= 0.0f || leak.Removed;
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
|
||||
|
||||
if (leakFixed && leak.FlowTargetHull != null)
|
||||
{
|
||||
sinTime = 0;
|
||||
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f);
|
||||
|
||||
@@ -535,7 +535,6 @@ namespace Barotrauma.Items.Components
|
||||
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return 0.0f;
|
||||
}
|
||||
float average = skillSuccessSum / requiredSkills.Count;
|
||||
|
||||
float skillSuccessSum = 0.0f;
|
||||
for (int i = 0; i < requiredSkills.Count; i++)
|
||||
|
||||
@@ -155,29 +155,5 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
//force can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger(-10, 10, (int)(targetForce / 10.0f));
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
||||
{
|
||||
float newTargetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
|
||||
|
||||
if (item.CanClientAccess(c))
|
||||
{
|
||||
if (Math.Abs(newTargetForce - targetForce) > 0.01f)
|
||||
{
|
||||
GameServer.Log(c.Character.LogName + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
targetForce = newTargetForce;
|
||||
}
|
||||
|
||||
//notify all clients of the changed state
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,6 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
if (AITarget != null) AITarget.Enabled = voltage > minVoltage || powerConsumption <= 0.0f;
|
||||
|
||||
#if CLIENT
|
||||
light.ParentSub = item.Submarine;
|
||||
|
||||
@@ -177,12 +177,21 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 nodePos = refSub == null ?
|
||||
newConnection.Item.Position :
|
||||
newConnection.Item.Position - refSub.HiddenSubPosition;
|
||||
|
||||
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == nodePos) break;
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) break;
|
||||
|
||||
if (i == 0)
|
||||
//make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
|
||||
int newNodeIndex = 0;
|
||||
if (nodes.Count > 1)
|
||||
{
|
||||
if (Vector2.DistanceSquared(nodes[nodes.Count-1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
|
||||
{
|
||||
newNodeIndex = nodes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
if (newNodeIndex == 0)
|
||||
{
|
||||
nodes.Insert(0, nodePos);
|
||||
}
|
||||
|
||||
@@ -433,9 +433,10 @@ namespace Barotrauma.Items.Components
|
||||
if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.ToLowerInvariant() != "fireatwill"))
|
||||
{
|
||||
ItemContainer container = null;
|
||||
Item containerItem = null;
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
var containerItem = e as Item;
|
||||
containerItem = e as Item;
|
||||
if (containerItem == null) continue;
|
||||
|
||||
container = containerItem.GetComponent<ItemContainer>();
|
||||
@@ -453,7 +454,7 @@ namespace Barotrauma.Items.Components
|
||||
var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container);
|
||||
character?.Speak(TextManager.Get("DialogLoadTurret").Replace("[itemname]", item.Name), null, 0.0f, "loadturret", 30.0f);
|
||||
containShellObjective.MinContainedAmount = usableProjectileCount + 1;
|
||||
containShellObjective.IgnoreAlreadyContainedItems = true;
|
||||
containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
|
||||
objective.AddSubObjective(containShellObjective);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1631,12 +1631,13 @@ namespace Barotrauma
|
||||
SerializableProperty property = extraData[1] as SerializableProperty;
|
||||
if (property != null)
|
||||
{
|
||||
var propertyOwner = allProperties.Find(p => p.Second == property);
|
||||
if (allProperties.Count > 1)
|
||||
{
|
||||
msg.WriteRangedInteger(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property));
|
||||
}
|
||||
|
||||
object value = property.GetValue(this);
|
||||
object value = property.GetValue(propertyOwner.First);
|
||||
if (value is string)
|
||||
{
|
||||
msg.Write((string)value);
|
||||
|
||||
@@ -89,7 +89,9 @@ namespace Barotrauma
|
||||
}
|
||||
CommonnessPerZone[zoneIndex] = zoneCommonness;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
hireableJobs = new List<Tuple<JobPrefab, float>>();
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
|
||||
@@ -269,9 +269,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//remove orphans
|
||||
Locations.RemoveAll(c => !connectedLocations.Contains(c));
|
||||
|
||||
for (int i = connections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
i = Math.Min(i, connections.Count - 1);
|
||||
@@ -428,7 +425,6 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddErrorEventOnce("Map.SelectLocation:LocationNotFound", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
CurrentLocation.SelectedMissionIndex = missionIndex;
|
||||
|
||||
SelectedLocation = location;
|
||||
SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Barotrauma
|
||||
|
||||
public HashSet<string> Tags
|
||||
{
|
||||
get { return prefab; }
|
||||
get { return prefab.Tags; }
|
||||
}
|
||||
|
||||
protected Color spriteColor;
|
||||
|
||||
@@ -523,30 +523,6 @@ namespace Barotrauma
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
{
|
||||
//no walls found at either side, just use the initial spawnpos and hope for the best
|
||||
}
|
||||
else if (minX < 0)
|
||||
{
|
||||
//no wall found at the left side, spawn to the left from the right-side wall
|
||||
spawnPos.X = maxX - minWidth - 100.0f;
|
||||
}
|
||||
else if (maxX > Level.Loaded.Size.X)
|
||||
{
|
||||
//no wall found at right side, spawn to the right from the left-side wall
|
||||
spawnPos.X = minX + minWidth + 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
//walls found at both sides, use their midpoint
|
||||
spawnPos.X = (minX + maxX) / 2;
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
@@ -1016,26 +992,6 @@ namespace Barotrauma
|
||||
return closest;
|
||||
}
|
||||
|
||||
public List<Hull> GetHulls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Hull.hullList);
|
||||
public List<Gap> GetGaps(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Gap.GapList);
|
||||
public List<Item> GetItems(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Item.ItemList);
|
||||
|
||||
public List<T> GetEntities<T>(bool includingConnectedSubs, List<T> list) where T : MapEntity
|
||||
{
|
||||
return list.FindAll(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
|
||||
}
|
||||
|
||||
public bool IsEntityFoundOnThisSub(MapEntity entity, bool includingConnectedSubs)
|
||||
{
|
||||
if (entity.Submarine == this) { return true; }
|
||||
if (entity.Submarine == null) { return false; }
|
||||
if (includingConnectedSubs)
|
||||
{
|
||||
return GetConnectedSubs().Any(s => s == entity.Submarine && entity.Submarine.TeamID == TeamID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the sub is same as the other.
|
||||
/// </summary>
|
||||
|
||||
@@ -700,6 +700,23 @@ namespace Barotrauma
|
||||
Vector2 impulse = direction * impact * 0.5f;
|
||||
impulse = impulse.ClampLength(5.0f);
|
||||
|
||||
if (!MathUtils.IsValid(impulse))
|
||||
{
|
||||
string errorMsg =
|
||||
"Invalid impulse in SubmarineBody.ApplyImpact: " + impulse +
|
||||
". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + ".";
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
|
||||
}
|
||||
if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"SubmarineBody.ApplyImpact:InvalidImpulse",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
|
||||
{
|
||||
|
||||
@@ -522,7 +522,12 @@ namespace Barotrauma
|
||||
string errorMsg =
|
||||
"Attempted to apply invalid " + valueName +
|
||||
" to a physics body (userdata: " + userData +
|
||||
"), value: " + value + "\n" + Environment.StackTrace;
|
||||
"), value: " + value;
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
|
||||
}
|
||||
errorMsg += "\n" + Environment.StackTrace;
|
||||
|
||||
if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
@@ -544,7 +549,12 @@ namespace Barotrauma
|
||||
string errorMsg =
|
||||
"Attempted to apply invalid " + valueName +
|
||||
" to a physics body (userdata: " + userData +
|
||||
"), value: " + value + "\n" + Environment.StackTrace;
|
||||
"), value: " + value;
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
|
||||
}
|
||||
errorMsg += "\n" + Environment.StackTrace;
|
||||
|
||||
if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
case 6:
|
||||
return PlayerInput.MouseWheelDownClicked();
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,14 +113,7 @@ namespace Barotrauma
|
||||
{
|
||||
private bool hit, hitQueue;
|
||||
private bool held, heldQueue;
|
||||
|
||||
#if CLIENT
|
||||
private InputType inputType;
|
||||
|
||||
public Key(InputType inputType)
|
||||
{
|
||||
this.inputType = inputType;
|
||||
}
|
||||
|
||||
private InputType inputType;
|
||||
|
||||
@@ -138,17 +131,6 @@ namespace Barotrauma
|
||||
{
|
||||
get { return binding; }
|
||||
}
|
||||
#endif
|
||||
|
||||
public void SetState()
|
||||
{
|
||||
hit = binding.IsHit();
|
||||
if (hit) hitQueue = true;
|
||||
|
||||
held = binding.IsDown();
|
||||
if (held) heldQueue = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
public void SetState()
|
||||
{
|
||||
|
||||
@@ -717,11 +717,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isNotClient = true;
|
||||
#if CLIENT
|
||||
isNotClient = GameMain.Client == null;
|
||||
#endif
|
||||
|
||||
if (FireSize > 0.0f && entity != null)
|
||||
{
|
||||
|
||||
@@ -153,49 +153,59 @@ namespace Barotrauma
|
||||
|
||||
string[] messages = serverMessage.Split('/');
|
||||
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
try
|
||||
{
|
||||
if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
{
|
||||
if (messages[i].Contains(" ")) continue; // Spaces found, do not translate
|
||||
|
||||
string msg = Get(messages[i], true);
|
||||
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate
|
||||
{
|
||||
messages[i] = msg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] messageWithVariables = messages[i].Split('~');
|
||||
string msg = Get(messageWithVariables[0], true);
|
||||
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
{
|
||||
messages[i] = msg;
|
||||
if (messages[i].Contains(" ")) continue; // Spaces found, do not translate
|
||||
string msg = Get(messages[i], true);
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
{
|
||||
messages[i] = msg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // No translation found, probably caused by player input -> skip variable handling
|
||||
}
|
||||
string[] messageWithVariables = messages[i].Split('~');
|
||||
string msg = Get(messageWithVariables[0], true);
|
||||
|
||||
// First index is always the message identifier -> start at 1
|
||||
for (int j = 1; j < messageWithVariables.Length; j++)
|
||||
{
|
||||
string[] variableAndValue = messageWithVariables[j].Split('=');
|
||||
messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]);
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
{
|
||||
messages[i] = msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // No translation found, probably caused by player input -> skip variable handling
|
||||
}
|
||||
|
||||
// First index is always the message identifier -> start at 1
|
||||
for (int j = 1; j < messageWithVariables.Length; j++)
|
||||
{
|
||||
string[] variableAndValue = messageWithVariables[j].Split('=');
|
||||
messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string translatedServerMessage = string.Empty;
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
{
|
||||
translatedServerMessage += messages[i];
|
||||
}
|
||||
return translatedServerMessage;
|
||||
}
|
||||
|
||||
string translatedServerMessage = string.Empty;
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
catch (IndexOutOfRangeException exception)
|
||||
{
|
||||
translatedServerMessage += messages[i];
|
||||
string errorMsg = "Failed to translate server message \"" + serverMessage + "\".";
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg, exception);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("TextManager.GetServerMessage:" + serverMessage, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
return translatedServerMessage;
|
||||
}
|
||||
|
||||
public static bool IsServerMessageWithVariables(string message)
|
||||
|
||||
@@ -33,7 +33,11 @@ namespace Barotrauma
|
||||
texts.Add(infoName, infoList);
|
||||
}
|
||||
|
||||
infoList.Add(subElement.ElementInnerText());
|
||||
string text = subElement.ElementInnerText();
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace("<", "<");
|
||||
text = text.Replace(">", ">");
|
||||
infoList.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user