(f417b026f) Fetched: Changes for playing video tutorial from local branch

This commit is contained in:
Joonas Rikkonen
2019-03-27 20:45:14 +02:00
parent e65c669eaa
commit 3f82c9a2cb
125 changed files with 1317 additions and 3166 deletions

View File

@@ -18,13 +18,14 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\Ragdoll.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Attack.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Character.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\CharacterHealth.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterHealth.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterHUD.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterNetworking.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterSound.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\AfflictionHusk.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\AfflictionPsychosis.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\CharacterHealth.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\DamageModifier.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\HUDProgressBar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Jobs\JobPrefab.cs" />

View File

@@ -31,7 +31,6 @@
<BootstrapperEnabled>true</BootstrapperEnabled>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<ApplicationIcon>..\BarotraumaShared\IconBmp.bmp</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
@@ -192,11 +191,6 @@
<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" />

View File

@@ -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.7")]
[assembly: AssemblyFileVersion("0.8.9.7")]
[assembly: AssemblyVersion("0.8.9.6")]
[assembly: AssemblyFileVersion("0.8.9.6")]

View File

@@ -14,7 +14,6 @@ float2 uvTopLeft;
float2 uvBottomRight;
float4 tintColor;
float4 solidColor;
struct VertexShaderInput
{
@@ -75,11 +74,6 @@ float4 mainPS(VertexShaderOutput input) : COLOR
return xTexture.Sample(TextureSampler, input.TexCoords) * input.Color;
}
float4 solidColorPS(VertexShaderOutput input) : COLOR
{
return solidColor * xTexture.Sample(TextureSampler, input.TexCoords).a;
}
technique DeformShader
{
pass Pass1
@@ -87,13 +81,4 @@ technique DeformShader
VertexShader = compile vs_4_0_level_9_1 mainVS();
PixelShader = compile ps_4_0_level_9_1 mainPS();
}
}
technique DeformShaderSolidColor
{
pass Pass1
{
VertexShader = compile vs_4_0_level_9_1 mainVS();
PixelShader = compile ps_4_0_level_9_1 solidColorPS();
}
}

View File

@@ -14,7 +14,6 @@ float2 uvTopLeft;
float2 uvBottomRight;
float4 tintColor;
float4 solidColor;
struct VertexShaderInput
{
@@ -75,11 +74,6 @@ float4 mainPS(VertexShaderOutput input) : COLOR
return xTexture.Sample(TextureSampler, input.TexCoords) * input.Color;
}
float4 solidColorPS(VertexShaderOutput input) : COLOR
{
return solidColor * xTexture.Sample(TextureSampler, input.TexCoords).a;
}
technique DeformShader
{
pass Pass1
@@ -87,13 +81,4 @@ technique DeformShader
VertexShader = compile vs_3_0 mainVS();
PixelShader = compile ps_3_0 mainPS();
}
}
technique DeformShaderSolidColor
{
pass Pass1
{
VertexShader = compile vs_3_0 mainVS();
PixelShader = compile ps_3_0 solidColorPS();
}
}

View File

@@ -13,39 +13,13 @@ namespace Barotrauma
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
if (soundRange > 0.0f)
{
Color color;
if (Entity is Character)
{
color = Color.Yellow;
}
else if (Entity is Item)
{
color = Color.Orange;
}
else
{
color = Color.OrangeRed;
}
Color color = Entity is Character ? Color.Yellow : Color.Orange;
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
}
if (sightRange > 0.0f)
{
Color color;
if (Entity is Character)
{
color = Color.CornflowerBlue;
}
else if (Entity is Item)
{
color = Color.CadetBlue;
}
else
{
color = Color.WhiteSmoke;
}
Color color = Entity is Character ? Color.CornflowerBlue : Color.CadetBlue;
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
}
}
}

View File

@@ -16,17 +16,18 @@ namespace Barotrauma
if (SelectedAiTarget?.Entity != null)
{
GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.5f, 0, 4);
GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.3f, 0, 5);
if (wallTarget != null)
{
Vector2 wallTargetPos = wallTarget.Position;
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
if (wallTarget.Structure.Submarine != null) wallTargetPos += wallTarget.Structure.Submarine.Position;
wallTargetPos.Y = -wallTargetPos.Y;
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Red, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.FormatZeroDecimal()})", Color.Red, Color.Black);
GUI.Font.DrawString(spriteBatch, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.ToString()})", pos - Vector2.UnitY * 20.0f, Color.Red);
}
/*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, Color.Red);
@@ -57,9 +58,18 @@ namespace Barotrauma
{
GUI.DrawLine(spriteBatch,
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)),
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Green, 0, 4);
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Orange * 0.6f, 0, 5);
}
if (latchOntoAI.WallAttachPos.HasValue)
{
GUI.DrawLine(spriteBatch, pos,
ConvertUnits.ToDisplayUnits(new Vector2(latchOntoAI.WallAttachPos.Value.X, -latchOntoAI.WallAttachPos.Value.Y)), Color.Orange * 0.6f, 0, 3);
}
}
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
if (latchOntoAI.WallAttachPos.HasValue)
{
GUI.DrawLine(spriteBatch, pos,
@@ -67,34 +77,22 @@ namespace Barotrauma
}
}
if (steeringManager is IndoorsSteeringManager pathSteering)
GUI.DrawLine(spriteBatch,
new Vector2(Character.DrawPosition.X, -Character.DrawPosition.Y),
new Vector2(pathSteering.CurrentPath.CurrentNode.DrawPosition.X, -pathSteering.CurrentPath.CurrentNode.DrawPosition.Y),
Color.Orange * 0.6f, 0, 3);
for (int i = 1; i < pathSteering.CurrentPath.Nodes.Count; i++)
{
var path = pathSteering.CurrentPath;
if (path != null)
{
if (path.CurrentNode != null)
{
GUI.DrawLine(spriteBatch, pos,
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.DarkViolet, 0, 3);
GUI.DrawLine(spriteBatch,
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y),
new Vector2(pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.Y),
Color.Orange * 0.6f, 0, 3);
GUI.DrawString(spriteBatch, pos - new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
}
for (int i = 1; i < path.Nodes.Count; i++)
{
var previousNode = path.Nodes[i - 1];
var currentNode = path.Nodes[i];
GUI.DrawLine(spriteBatch,
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
Color.Red * 0.5f, 0, 3);
GUI.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20),
Color.Red);
}
}
GUI.SmallFont.DrawString(spriteBatch,
pathSteering.CurrentPath.Nodes[i].ID.ToString(),
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y - 10),
Color.LightGreen);
}
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);

View File

@@ -75,12 +75,11 @@ namespace Barotrauma
GUI.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20),
Color.SkyBlue);
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y - 10),
Color.LightGreen);
}
}
}
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
//if (Character.IsKeyDown(InputType.Aim))

View File

@@ -210,9 +210,7 @@ namespace Barotrauma
else
{
Vector2 positionError = serverPos.Position - localPos.Position;
float rotationError = serverPos.Rotation.HasValue && localPos.Rotation.HasValue ?
serverPos.Rotation.Value - localPos.Rotation.Value :
0.0f;
float rotationError = serverPos.Rotation - localPos.Rotation;
for (int i = localPosIndex; i < character.MemLocalState.Count; i++)
{

View File

@@ -544,10 +544,7 @@ 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) ?
(MaxVitality > 0.0f ? Vitality / MaxVitality : 0.0f) :
(Math.Abs(MinVitality) > 0.0f ? 1.0f - Vitality / MinVitality : 0.0f);
healthBar.BarSize = healthWindowHealthBar.BarSize = (Vitality > 0.0f) ? Vitality / MaxVitality : 1.0f - Vitality / MinVitality;
if (healthBarPulsateTimer > 0.0f)
{

View File

@@ -214,8 +214,8 @@ namespace Barotrauma
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
bool fixedRotation = msg.ReadBoolean();
float? rotation = null;
float? angularVelocity = null;
float rotation = AnimController.Collider.Rotation;
float angularVelocity = AnimController.Collider.AngularVelocity;
if (!fixedRotation)
{
rotation = msg.ReadFloat();

View File

@@ -403,11 +403,6 @@ namespace Barotrauma
AssignRelayToServer("help", false);
AssignRelayToServer("verboselogging", false);
AssignRelayToServer("freecam", false);
#if DEBUG
AssignRelayToServer("simulatedlatency", false);
AssignRelayToServer("simulatedloss", false);
AssignRelayToServer("simulatedduplicateschance", false);
#endif
commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true);
@@ -444,7 +439,7 @@ namespace Barotrauma
AssignOnExecute("ambientlight", (string[] args) =>
{
Color color = XMLExtensions.ParseColor(string.Join(",", args));
Color color = XMLExtensions.ParseColor(string.Join("", args));
if (Level.Loaded != null)
{
Level.Loaded.GenerationParams.AmbientLightColor = color;
@@ -533,27 +528,44 @@ namespace Barotrauma
}
}, isCheat: true));
commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args =>
commands.Add(new Command("alpha", "Change the alpha (as bytes from 0 to 255) of the selected item/structure instances. Applied only in the subeditor.", (string[] args) =>
{
if (Screen.Selected == GameMain.SubEditorScreen)
{
Item.ItemList.ForEach(i => i.Reset());
Structure.WallList.ForEach(s => s.Reset());
foreach (MapEntity entity in MapEntity.SelectedList)
if (!MapEntity.SelectedAny)
{
if (entity is Item item)
ThrowError("You have to select item(s)/structure(s) first!");
}
else
{
if (args.Length > 0)
{
item.CreateEditingHUD();
break;
if (!byte.TryParse(args[0], out byte a))
{
ThrowError($"Failed to parse value for ALPHA from {args[0]}");
}
else
{
foreach (var mapEntity in MapEntity.SelectedList)
{
if (mapEntity is Structure s)
{
s.SpriteColor = new Color(s.SpriteColor.R, s.SpriteColor.G, s.SpriteColor.G, a);
}
else if (mapEntity is Item i)
{
i.SpriteColor = new Color(i.SpriteColor.R, i.SpriteColor.G, i.SpriteColor.G, a);
}
}
}
}
else if (entity is Structure structure)
else
{
structure.CreateEditingHUD();
break;
ThrowError("Not enough arguments provided! One required!");
}
}
}
}));
}, isCheat: true));
commands.Add(new Command("alpha", "Change the alpha (as bytes from 0 to 255) of the selected item/structure instances. Applied only in the subeditor.", (string[] args) =>
{

View File

@@ -0,0 +1,215 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
using System.Collections.Generic;
using Barotrauma.Media;
namespace Barotrauma
{
class VideoPlayer
{
private Video currentVideo;
private List<PreloadedContent> preloadedVideos;
private GUIFrame background, videoFrame;
private GUITextBlock title;
private GUICustomComponent videoView;
private Color backgroundColor = new Color(0f, 0f, 0f, 1f);
private Action callbackOnStop;
private bool isPlaying;
public bool IsPlaying()
{
return isPlaying;
/*if (currentVideo == null) return false;
return currentVideo.IsPlaying;*/
}
private readonly Point defaultResolution = new Point(520, 300);
private readonly int borderSize = 20;
private class PreloadedContent
{
public string ContentName;
public string ContentTag;
public Video Video;
public Point Resolution;
public PreloadedContent(string name, string tag, Video video, Point resolution)
{
ContentName = name;
ContentTag = tag;
Video = video;
Resolution = resolution;
}
}
public VideoPlayer()
{
int width = defaultResolution.X;
int height = defaultResolution.Y;
background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), "InnerFrame", backgroundColor);
videoFrame = new GUIFrame(new RectTransform(new Point(width + borderSize, height + borderSize), background.RectTransform, Anchor.Center), "SonarFrame");
videoView = new GUICustomComponent(new RectTransform(new Point(width, height), videoFrame.RectTransform, Anchor.Center),
(spriteBatch, guiCustomComponent) => { DrawVideo(spriteBatch, guiCustomComponent.Rect); });
title = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), videoFrame.RectTransform, Anchor.TopCenter, Pivot.BottomCenter), string.Empty, font: GUI.LargeFont, textAlignment: Alignment.Center);
preloadedVideos = new List<PreloadedContent>();
}
public void PreloadContent(string contentPath, string contentTag, string contentId, XElement contentElement)
{
if (preloadedVideos.Find(s => s.ContentName == contentId) != null) return; // Already loaded
Point resolution = new Point(contentElement.GetAttributeInt("width", 0), contentElement.GetAttributeInt("height", 0));
if (resolution.X == 0 || resolution.Y == 0)
{
resolution = defaultResolution;
}
preloadedVideos.Add(new PreloadedContent(contentId, contentTag, CreateVideo(contentPath, resolution), resolution));
}
public void RemoveAllPreloaded()
{
if (preloadedVideos == null || preloadedVideos.Count == 0) return;
for (int i = 0; i < preloadedVideos.Count; i++)
{
preloadedVideos[i] = null;
}
preloadedVideos.Clear();
}
public void RemovePreloadedByTag(string tag)
{
if (preloadedVideos == null || preloadedVideos.Count == 0) return;
for (int i = 0; i < preloadedVideos.Count; i++)
{
if (preloadedVideos[i].ContentTag != tag) continue;
preloadedVideos[i] = null;
preloadedVideos.RemoveAt(i);
i--;
}
}
public void Play()
{
isPlaying = true;
}
public void Stop()
{
isPlaying = false;
if (currentVideo == null) return;
currentVideo.Dispose();
currentVideo = null;
}
private bool OKButtonClicked(GUIButton button, object userData)
{
Stop();
callbackOnStop?.Invoke();
return true;
}
public void AddToGUIUpdateList()
{
if (!IsPlaying()) return;
background.AddToGUIUpdateList();
}
public void LoadContent(string contentPath, XElement videoElement, string contentId, bool startPlayback, bool hasButton, Action callback = null)
{
if (currentVideo != null)
{
currentVideo.Dispose();
currentVideo = null;
}
PreloadedContent preloaded = null;
Point resolution = new Point(0, 0);
if (preloadedVideos != null && preloadedVideos.Count > 0)
{
preloaded = preloadedVideos.Find(s => s.ContentName == contentId);
if (preloaded != null)
{
currentVideo = preloaded.Video;
resolution = preloaded.Resolution;
}
}
if (currentVideo == null) // No preloaded sheets found, create sheets
{
resolution = new Point(videoElement.GetAttributeInt("width", 0), videoElement.GetAttributeInt("height", 0));
if (resolution.X == 0 || resolution.Y == 0)
{
resolution = defaultResolution;
}
currentVideo = CreateVideo(contentPath, resolution);
}
videoFrame.RectTransform.NonScaledSize = resolution + new Point(borderSize, borderSize);
videoView.RectTransform.NonScaledSize = resolution;
title.Text = TextManager.Get(contentId);
title.RectTransform.NonScaledSize = new Point(resolution.X, 30);
callbackOnStop = callback;
if (hasButton)
{
var okButton = new GUIButton(new RectTransform(new Point(160, 50), videoFrame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, -10) },
TextManager.Get("OK"))
{
OnClicked = OKButtonClicked
};
}
if (startPlayback) Play();
}
private Video CreateVideo(string contentPath, Point resolution)
{
Video video = null;
try
{
video = new Video(GameMain.Instance.GraphicsDevice, GameMain.SoundManager, contentPath, (uint)resolution.X, (uint)resolution.Y);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error loading video content " + contentPath + "!", e);
}
return video;
}
private void DrawVideo(SpriteBatch spriteBatch, Rectangle rect)
{
if (!isPlaying) return;
spriteBatch.Draw(currentVideo.GetTexture(), rect, Color.White);
}
public void Remove()
{
if (currentVideo != null)
{
currentVideo.Dispose();
currentVideo = null;
}
RemoveAllPreloaded();
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Barotrauma
{
private UInt16 startWatchmanID, endWatchmanID;
public static GUIComponent StartCampaignSetup( IEnumerable<Submarine> submarines, IEnumerable<string> saveFiles)
public static GUIComponent StartCampaignSetup(IEnumerable<string> saveFiles)
{
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker");
@@ -35,7 +35,7 @@ namespace Barotrauma
var newCampaignContainer = new GUIFrame(new RectTransform(Vector2.One, campaignContainer.RectTransform, Anchor.BottomLeft), style: null);
var loadCampaignContainer = new GUIFrame(new RectTransform(Vector2.One, campaignContainer.RectTransform, Anchor.BottomLeft), style: null);
var campaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, submarines, saveFiles);
var campaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, saveFiles);
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform),
TextManager.Get("NewCampaign"))
@@ -92,7 +92,8 @@ namespace Barotrauma
if (endWatchman != null) { InitializeWatchman(endWatchman); }
}
}
protected override void WatchmanInteract(Character watchman, Character interactor)
{
if ((watchman.Submarine == Level.Loaded.StartOutpost && !Submarine.MainSub.AtStartPosition) ||
@@ -106,7 +107,8 @@ namespace Barotrauma
return;
}
if (GameMain.Client != null && interactor == Character.Controlled)
if (GameMain.Client != null && interactor == Character.Controlled &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) || GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)))
{
var msgBox = new GUIMessageBox("", TextManager.Get("CampaignEnterOutpostPrompt")
.Replace("[locationname]", Submarine.MainSub.AtStartPosition ? Map.CurrentLocation.Name : Map.SelectedLocation.Name),
@@ -120,7 +122,8 @@ namespace Barotrauma
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
}

View File

@@ -19,6 +19,8 @@ namespace Barotrauma.Tutorials
private List<TutorialSegment> segments;
private SpriteSheetPlayer spriteSheetPlayer;
private VideoPlayer videoPlayer;
private Steering navConsole;
private Reactor reactor;
private Sonar sonar;
@@ -29,7 +31,7 @@ namespace Barotrauma.Tutorials
private bool started = false;
private string playableContentPath;
private float tutorialTimer;
private float degrading2ActivationCountdown;
@@ -82,6 +84,7 @@ namespace Barotrauma.Tutorials
base.Initialize();
spriteSheetPlayer = new SpriteSheetPlayer();
videoPlayer = new VideoPlayer();
characterTimeOnSonar = new List<Pair<Character, float>>();
for (int i = 0; i < segments.Count; i++)
@@ -210,6 +213,8 @@ namespace Barotrauma.Tutorials
started = ContentRunning = Initialized = false;
spriteSheetPlayer.Remove();
spriteSheetPlayer = null;
videoPlayer.Remove();
videoPlayer = null;
characterTimeOnSonar = null;
}
@@ -220,6 +225,10 @@ namespace Barotrauma.Tutorials
{
spriteSheetPlayer.AddToGUIUpdateList();
}
if (videoPlayer != null)
{
videoPlayer.AddToGUIUpdateList();
}
}
public override void Update(float deltaTime)
@@ -325,7 +334,7 @@ namespace Barotrauma.Tutorials
foreach (Item item in Item.ItemList)
{
if (!item.Repairables.Any() || item.ConditionPercentage > 50) continue;
if (!item.Repairables.Any() || item.Condition > 50.0f) continue;
degradedEquipmentFound = true;
break;
}
@@ -461,10 +470,18 @@ namespace Barotrauma.Tutorials
case ContentTypes.None:
break;
case ContentTypes.Video:
spriteSheetPlayer.LoadContent(playableContentPath, activeSegment.Content, activeSegment.Name, true, true, CurrentSegmentStopCallback);
string fileName = activeSegment.Content.GetAttributeString("file", "");
if (fileName != "")
{
videoPlayer.LoadContent(playableContentPath + fileName, activeSegment.Content, activeSegment.Name, true, true, CurrentSegmentStopCallback);
}
else
{
spriteSheetPlayer.LoadContent(playableContentPath, activeSegment.Content, activeSegment.Name, true, true, CurrentSegmentStopCallback);
}
break;
case ContentTypes.Text:
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.GetFormatted(activeSegment.Content.GetAttributeString("tag", ""), false, args),
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.Get(activeSegment.Content.GetAttributeString("tag", ""), false, args),
activeSegment.Content.GetAttributeInt("width", 300),
activeSegment.Content.GetAttributeInt("height", 80),
activeSegment.Content.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback);

View File

@@ -47,7 +47,7 @@ namespace Barotrauma
//if "%" is found
if (index > 0)
{
while (index > 0)
while (true)
{
//search for end of label
index -= 1;
@@ -439,6 +439,7 @@ namespace Barotrauma
}
};
#endif
var radioButtonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), voiceSettings.RectTransform))
{
Stretch = true,
@@ -457,24 +458,6 @@ 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),
@@ -486,6 +469,7 @@ 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 = () =>
@@ -525,13 +509,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

View File

@@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing)
{
Color color = item.SpriteColor;
Color color = Color.White;
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
weldSpritePos.Y = -weldSpritePos.Y;
weldedSprite.Draw(spriteBatch,
weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale);
weldSpritePos, Color.White * (stuck / 100.0f), scale: item.Scale);
}
if (openState == 1.0f)

View File

@@ -313,7 +313,7 @@ namespace Barotrauma.Items.Components
if (sound == null) { return 0.0f; }
if (sound.VolumeProperty == "") { return sound.VolumeMultiplier; }
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
if (properties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
{
float newVolume = 0.0f;
try

View File

@@ -331,18 +331,8 @@ namespace Barotrauma.Items.Components
spriteBatch.GraphicsDevice.ScissorRectangle = container.Rect;
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
//make the pointer jitter a bit if it's at the upper limit of the fission rate
float jitter = 0.0f;
if (FissionRate > allowedFissionRate.Y - 5.0f)
{
float jitterAmount = Math.Min(targetFissionRate - allowedFissionRate.Y, 10.0f);
float t = graphTimer / updateGraphInterval;
jitter = (PerlinNoise.GetPerlin(t * 0.5f, t * 0.1f) - 0.5f) * jitterAmount;
}
DrawMeter(spriteBatch, container.Rect,
fissionRateMeter, FissionRate + jitter, new Vector2(0.0f, 100.0f), optimalFissionRate, allowedFissionRate);
fissionRateMeter, FissionRate, new Vector2(0.0f, 100.0f), optimalFissionRate, allowedFissionRate);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
@@ -402,12 +392,12 @@ namespace Barotrauma.Items.Components
Vector2 pos = new Vector2(rect.Center.X, rect.Y + meterSprite.Origin.Y * scale);
Vector2 optimalRangeNormalized = new Vector2(
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));
(optimalRange.X - range.X) / (range.Y - range.X),
(optimalRange.Y - range.X) / (range.Y - range.X));
Vector2 allowedRangeNormalized = new Vector2(
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));
(allowedRange.X - range.X) / (range.Y - range.X),
(allowedRange.Y - range.X) / (range.Y - range.X));
Vector2 sectorRad = new Vector2(-1.57f, 1.57f);
@@ -427,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) - 3);
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0,0,GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale));
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale);
sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Orange, optimalSectorRad.X, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Red, allowedSectorRad.X, scale);

View File

@@ -785,9 +785,8 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in c.AnimController.Limbs)
{
if (!limb.body.Enabled) { continue; }
float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared();
if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue;
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)

View File

@@ -484,14 +484,26 @@ namespace Barotrauma.Items.Components
user = Character.Controlled;
}
}
if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
if (!AutoPilot && Character.DisableControls)
{
steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
Vector2 input = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Up)) { input += Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Down)) { input -= Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Left))
{
input -= Vector2.UnitX;
}
if (PlayerInput.KeyDown(InputType.Right))
{
input += Vector2.UnitX;
}
if (PlayerInput.KeyDown(InputType.Up))
{
input += Vector2.UnitY;
}
if (PlayerInput.KeyDown(InputType.Down))
{
input -= Vector2.UnitY;
}
if (PlayerInput.KeyDown(Keys.LeftShift))
{
SteeringInput += input * deltaTime * 200;

View File

@@ -1,6 +1,4 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Lidgren.Network;
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
@@ -114,7 +112,8 @@ namespace Barotrauma.Items.Components
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{
if (!(c.UserData is Skill skill)) continue;
Skill skill = c.UserData as Skill;
if (skill == null) continue;
GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < skill.Level)
@@ -127,15 +126,5 @@ namespace Barotrauma.Items.Components
}
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
}
}

View File

@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions)
{
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue;
if (affliction.Strength < affliction.Prefab.ActivationThreshold || affliction.Strength <= 0.0f) continue;
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;

View File

@@ -237,13 +237,13 @@ namespace Barotrauma.Items.Components
railSprite?.Draw(spriteBatch,
drawPos,
item.SpriteColor,
Color.White,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
barrelSprite?.Draw(spriteBatch,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
item.SpriteColor,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
Color.White,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
@@ -288,10 +288,7 @@ namespace Barotrauma.Items.Components
availableAmmo.AddRange(itemContainer.Inventory.Items);
}
float chargeRate =
powerConsumption <= 0.0f ?
1.0f :
batteryCapacity > 0.0f ? batteryCharge / batteryCapacity : 0.0f;
float chargeRate = powerConsumption <= 0.0f ? 1.0f : batteryCharge / batteryCapacity;
bool charged = batteryCharge * 3600.0f > powerConsumption;
bool readyToFire = reload <= 0.0f && charged && availableAmmo.Any(p => p != null);
if (ShowChargeIndicator && PowerConsumption > 0.0f)

View File

@@ -448,7 +448,7 @@ namespace Barotrauma
}
}
public GUIComponent CreateEditingHUD(bool inGame = false)
private GUIComponent CreateEditingHUD(bool inGame = false)
{
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)
@@ -457,38 +457,40 @@ namespace Barotrauma
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true);
if (!inGame)
if (!inGame && Linkable)
{
if (Linkable)
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("HoldToLink"), font: GUI.SmallFont);
var itemsText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("AllowedLinks") + ": ", font: GUI.SmallFont);
if (AllowedLinks.None())
{
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("HoldToLink"), font: GUI.SmallFont);
var itemsText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("AllowedLinks") + ": ", font: GUI.SmallFont);
if (AllowedLinks.None())
itemsText.Text += TextManager.Get("None");
}
else
{
for (int i = 0; i < AllowedLinks.Count; i++)
{
itemsText.Text += TextManager.Get("None");
}
else
{
for (int i = 0; i < AllowedLinks.Count; i++)
itemsText.Text += AllowedLinks[i];
if (i < AllowedLinks.Count - 1)
{
itemsText.Text += AllowedLinks[i];
if (i < AllowedLinks.Count - 1)
{
itemsText.Text += ", ";
}
itemsText.Text += ", ";
}
}
itemEditor.AddCustomContent(linkText, 1);
itemEditor.AddCustomContent(itemsText, 2);
linkText.TextColor = Color.Yellow;
itemsText.TextColor = Color.Yellow;
}
itemEditor.AddCustomContent(linkText, 1);
itemEditor.AddCustomContent(itemsText, 2);
linkText.TextColor = Color.Yellow;
itemsText.TextColor = Color.Yellow;
}
if (!inGame)
{
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.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
OnClicked = (button, data) =>
@@ -497,7 +499,7 @@ namespace Barotrauma
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
{
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
OnClicked = (button, data) =>
@@ -508,7 +510,7 @@ namespace Barotrauma
};
if (Sprite != null)
{
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"));
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"));
reloadTextureButton.OnClicked += (button, data) =>
{
Sprite.ReloadXML();
@@ -516,15 +518,6 @@ namespace Barotrauma
return true;
};
}
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ResetToPrefab"))
{
OnClicked = (button, data) =>
{
Reset();
CreateEditingHUD();
return true;
}
};
itemEditor.AddCustomContent(buttonContainer, itemEditor.ContentCount);
}

View File

@@ -1,13 +1,14 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using System.Linq;
using Lidgren.Network;
namespace Barotrauma
{
@@ -18,8 +19,6 @@ namespace Barotrauma
private List<Decal> decals = new List<Decal>();
private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage;
private List<Vector3> remoteFireSources;
private bool networkUpdatePending;
private float networkUpdateTimer;
@@ -140,10 +139,6 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
ApplyRemoteState();
}
if (networkUpdatePending)
{
@@ -552,18 +547,18 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime)
{
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
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;
remoteFireSources = new List<Vector3>();
List<Vector3> newFireSources = new List<Vector3>();
if (hasFireSources)
{
fireSourceCount = message.ReadRangedInteger(0, 16);
for (int i = 0; i < fireSourceCount; i++)
{
remoteFireSources.Add(new Vector3(
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)));
@@ -572,6 +567,41 @@ namespace Barotrauma
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);
}
}
if (serverUpdateDelay > 0.0f) { return; }
ApplyRemoteState();
}

View File

@@ -404,15 +404,12 @@ namespace Barotrauma.Lights
}
}
if (highlightedEntities.Count == 0) { return false; }
//draw characters in light blue first
//draw characters in solid white 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)
{
@@ -429,12 +426,9 @@ namespace Barotrauma.Lights
//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.Parameters["color"].SetValue(Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase));
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)
{
@@ -456,8 +450,6 @@ namespace Barotrauma.Lights
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;
}
@@ -479,10 +471,6 @@ namespace Barotrauma.Lights
Level.Loaded?.Renderer?.RenderWalls(graphics, cam, specular: true);
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());
@@ -499,9 +487,6 @@ namespace Barotrauma.Lights
}
spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
graphics.SetRenderTarget(null);
graphics.BlendState = BlendState.AlphaBlend;
}

View File

@@ -364,23 +364,15 @@ namespace Barotrauma
}
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
float moveSpeed = 1000.0f;
Vector2 moveAmount = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { moveAmount += Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { moveAmount -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Up)) { moveAmount += Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Down)) { moveAmount -= Vector2.UnitY; }
drawOffset += moveAmount * moveSpeed / zoom * deltaTime;
}
if (GUI.MouseOn == mapContainer)
{
zoom += PlayerInput.ScrollWheelSpeed / 1000.0f;
zoom = MathHelper.Clamp(zoom, 1.0f, 4.0f);
if (PlayerInput.MidButtonHeld()) { drawOffset += PlayerInput.MouseSpeed / zoom; }
if (PlayerInput.MidButtonHeld())
{
drawOffset += PlayerInput.MouseSpeed / zoom;
}
#if DEBUG
if (PlayerInput.DoubleClicked() && highlightedLocation != null)
{
@@ -559,6 +551,11 @@ namespace Barotrauma
null, connectionColor * MathHelper.Clamp(a, 0.1f, 0.5f), MathUtils.VectorToAngle(end - start),
new Vector2(0, 16), SpriteEffects.None, 0.01f);
}
}
rect.Inflate(8, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.Black, false, 0.0f, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.LightGray);
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
{

View File

@@ -98,7 +98,7 @@ namespace Barotrauma
Stretch = true,
RelativeSpacing = 0.02f
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"))
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
OnClicked = (button, data) =>
@@ -107,7 +107,7 @@ namespace Barotrauma
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"))
{
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
OnClicked = (button, data) =>
@@ -116,7 +116,7 @@ namespace Barotrauma
return true;
}
};
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"))
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"))
{
OnClicked = (button, data) =>
{
@@ -125,15 +125,6 @@ namespace Barotrauma
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ResetToPrefab"))
{
OnClicked = (button, data) =>
{
Reset();
CreateEditingHUD();
return true;
}
};
editor.AddCustomContent(buttonContainer, editor.ContentCount);
PositionEditingHUD();
@@ -292,7 +283,8 @@ namespace Barotrauma
{
if (damageEffect != null)
{
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health);
float newCutoff = Sections[i].damage > 0 ?
MathHelper.Lerp(0.2f, 0.65f, Sections[i].damage / Prefab.Health) : 0.0f;
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
{
@@ -359,7 +351,6 @@ namespace Barotrauma
-Bodies[i].Rotation, Color.White);
}
}
AiTarget?.Draw(spriteBatch);
}
}

View File

@@ -132,7 +132,6 @@ namespace Barotrauma.Networking
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, inGameHUD.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
AbsoluteSpacing = 5,
CanBeFocused = false
};
@@ -141,32 +140,16 @@ namespace Barotrauma.Networking
{
OnClicked = (btn, userdata) =>
{
if (!permissions.HasFlag(ClientPermissions.ManageRound)) { return false; }
if (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition)
{
var msgBox = new GUIMessageBox("", TextManager.Get("EndRoundSubNotAtLevelEnd"),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = (_, __) =>
{
GameMain.Client.RequestRoundEnd();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
else
{
RequestRoundEnd();
}
if (!permissions.HasFlag(ClientPermissions.ManageRound)) return false;
RequestRoundEnd();
return true;
},
Visible = false
};
EndVoteTickBox = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.4f), buttonContainer.RectTransform) { MinSize = new Point(150, 0) },
EndVoteTickBox = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.6f), buttonContainer.RectTransform) { MinSize = new Point(150, 0) },
TextManager.Get("EndRound"))
{
UserData = TextManager.Get("EndRound"),
OnSelected = ToggleEndRoundVote,
Visible = false
};
@@ -340,17 +323,18 @@ 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("Connecting");
string connectingText = TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP);
while (!CanStart && !connectCancelled)
{
if (reconnectBox == null)
{
reconnectBox = new GUIMessageBox(connectingText, TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP), new string[] { TextManager.Get("Cancel") });
reconnectBox = new GUIMessageBox(TextManager.Get("Connecting"), connectingText, new string[] { TextManager.Get("Cancel") });
reconnectBox.Buttons[0].OnClicked += CancelConnect;
reconnectBox.Buttons[0].OnClicked += reconnectBox.Close;
}
reconnectBox.Header.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1));
reconnectBox.Text.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1));
if (DateTime.Now > reqAuthTime)
{
@@ -637,7 +621,7 @@ namespace Barotrauma.Networking
if (gameStarted) SetRadioButtonColor();
if (ShowNetStats && client?.ServerConnection != null)
if (ShowNetStats)
{
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes);
netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes);
@@ -798,7 +782,7 @@ namespace Barotrauma.Networking
saveFiles.Add(inc.ReadString());
}
GameMain.NetLobbyScreen.CampaignSetupUI = MultiPlayerCampaign.StartCampaignSetup(serverSubmarines, saveFiles);
GameMain.NetLobbyScreen.CampaignSetupUI = MultiPlayerCampaign.StartCampaignSetup(saveFiles);
break;
case ServerPacketHeader.PERMISSIONS:
ReadPermissions(inc);
@@ -851,8 +835,6 @@ 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
@@ -882,6 +864,14 @@ namespace Barotrauma.Networking
waitInServerQueueBox = null;
CoroutineManager.StopCoroutines("WaitInServerQueue");
}
else
{
//disconnected/denied for some other reason than the server being full
// -> stop queuing and show a message box
waitInServerQueueBox?.Close();
waitInServerQueueBox = null;
CoroutineManager.StopCoroutines("WaitInServerQueue");
}
if (allowReconnect && disconnectReason == DisconnectReason.Unknown)
{
@@ -911,6 +901,25 @@ namespace Barotrauma.Networking
DebugConsole.NewMessage("Do not attempt to reconnect (DisconnectReason doesn't allow reconnection).");
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString());
if (allowReconnect && disconnectReason == DisconnectReason.Unknown)
{
reconnectBox = new GUIMessageBox(
TextManager.Get("ConnectionLost"),
TextManager.Get("ConnectionLostReconnecting"), new string[0]);
connected = false;
ConnectToServer(serverIP);
}
else
{
string msg = "";
if (disconnectReason == DisconnectReason.Unknown)
{
msg = disconnectMsg;
}
else
{
msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString());
for (int i = 1; i < splitMsg.Length; i++)
{
msg += TextManager.GetServerMessage(splitMsg[i]);
@@ -1048,10 +1057,9 @@ namespace Barotrauma.Networking
EndVoteTickBox.Selected = false;
int seed = inc.ReadInt32();
string levelSeed = inc.ReadString();
int levelEqualityCheckVal = inc.ReadInt32();
float levelDifficulty = inc.ReadFloat();
int seed = inc.ReadInt32();
string levelSeed = inc.ReadString();
float levelDifficulty = inc.ReadFloat();
byte losMode = inc.ReadByte();
@@ -1120,15 +1128,6 @@ namespace Barotrauma.Networking
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
}
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure;
}
if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null);
gameStarted = true;
@@ -1198,8 +1197,8 @@ namespace Barotrauma.Networking
}
}
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, serverSubmarines);
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.ShuttleList.ListBox, serverSubmarines);
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, submarines);
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.ShuttleList.ListBox, submarines);
gameStarted = inc.ReadBoolean();
bool allowSpectating = inc.ReadBoolean();
@@ -1392,6 +1391,9 @@ namespace Barotrauma.Networking
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc);
break;
@@ -1715,8 +1717,6 @@ namespace Barotrauma.Networking
SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession);
campaign.LastSaveID = campaign.PendingSaveID;
DebugConsole.Log("Campaign save received, save ID " + campaign.LastSaveID);
//decrement campaign update ID so the server will send us the latest data
//(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--;
@@ -1734,7 +1734,7 @@ namespace Barotrauma.Networking
public override void CreateEntityEvent(INetSerializable entity, object[] extraData)
{
if (!(entity is IClientSerializable)) throw new InvalidCastException("Entity is not IClientSerializable");
if (!(entity is IClientSerializable)) throw new InvalidCastException("entity is not IClientSerializable");
entityEventManager.CreateEvent(entity as IClientSerializable, extraData);
}
@@ -2003,8 +2003,7 @@ namespace Barotrauma.Networking
msg.Write(true); msg.WritePadBits();
msg.Write(savePath);
msg.Write(mapSeed);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
msg.Write(sub.FilePath);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
@@ -2242,25 +2241,13 @@ namespace Barotrauma.Networking
if (EndVoteCount > 0)
{
if (EndVoteTickBox.Visible)
{
EndVoteTickBox.Text =
(EndVoteTickBox.UserData as string) + " " + EndVoteCount + "/" + EndVoteMax;
}
else
{
string endVoteText = TextManager.Get("EndRoundVotes")
.Replace("[votes]", EndVoteCount.ToString())
.Replace("[max]", EndVoteMax.ToString());
GUI.DrawString(spriteBatch, EndVoteTickBox.Rect.Center.ToVector2() - GUI.SmallFont.MeasureString(endVoteText) / 2,
endVoteText,
Color.White,
font: GUI.SmallFont);
}
}
else
{
EndVoteTickBox.Text = EndVoteTickBox.UserData as string;
string endVoteText = TextManager.Get("EndRoundVotes")
.Replace("[y]", EndVoteCount.ToString())
.Replace("[n]", (EndVoteMax - EndVoteCount).ToString());
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 10.0f - GUI.SmallFont.MeasureString(endVoteText).X, 40),
endVoteText,
Color.White,
font: GUI.SmallFont);
}
if (respawnManager != null)

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using OpenTK.Audio.OpenAL;
using System;
using System.Linq;
@@ -27,11 +26,6 @@ namespace Barotrauma.Networking
get;
private set;
}
public float Gain
{
get { return GameMain.Config?.MicrophoneVolume ?? 1.0f; }
}
public DateTime LastEnqueueAudio;
@@ -75,7 +69,6 @@ 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"
@@ -167,9 +160,8 @@ namespace Barotrauma.Networking
double maxAmplitude = 0.0f;
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
{
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 sampleVal = (double)uncompressedBuffer[i] / (double)short.MaxValue;
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
}
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);

View File

@@ -150,10 +150,10 @@ namespace Barotrauma
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
Vector2 newPosition = SimPosition;
float? newRotation = null;
float newRotation = Rotation;
bool awake = body.Awake;
Vector2 newVelocity = LinearVelocity;
float? newAngularVelocity = null;
float newAngularVelocity = AngularVelocity;
newPosition = new Vector2(
msg.ReadFloat(),
@@ -179,12 +179,12 @@ namespace Barotrauma
msg.ReadPadBits();
if (!MathUtils.IsValid(newPosition) ||
!MathUtils.IsValid(newRotation) ||
!MathUtils.IsValid(newVelocity) ||
(newRotation.HasValue && !MathUtils.IsValid(newRotation.Value)) ||
(newAngularVelocity.HasValue && !MathUtils.IsValid(newAngularVelocity.Value)))
!MathUtils.IsValid(newAngularVelocity))
{
string errorMsg = "Received invalid position data for \"" + parentDebugName
+ "\" (position: " + newPosition + ", rotation: " + (newRotation ?? 0) + ", velocity: " + newVelocity + ", angular velocity: " + (newAngularVelocity ?? 0) + ")";
+ "\" (position: " + newPosition + ", rotation: " + newRotation + ", velocity: " + newVelocity + ", angular velocity: " + newAngularVelocity + ")";
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif

View File

@@ -33,12 +33,12 @@ namespace Barotrauma
private readonly bool isMultiplayer;
public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<Submarine> submarines, IEnumerable<string> saveFiles = null)
public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<string> saveFiles=null)
{
this.isMultiplayer = isMultiplayer;
this.newGameContainer = newGameContainer;
this.loadGameContainer = loadGameContainer;
var columnContainer = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: true)
{
Stretch = true,
@@ -60,7 +60,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), leftColumn.RectTransform), TextManager.Get("SelectedSub") + ":", textAlignment: Alignment.BottomLeft);
subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform));
UpdateSubList(submarines);
UpdateSubList();
// New game right side
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), TextManager.Get("SaveName") + ":", textAlignment: Alignment.BottomLeft);
@@ -87,8 +87,9 @@ namespace Barotrauma
return false;
}
if (!(subList.SelectedData is Submarine selectedSub)) { return false; }
Submarine selectedSub = subList.SelectedData as Submarine;
if (selectedSub == null) return false;
if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash))
{
((GUITextBlock)subList.SelectedComponent).TextColor = Color.DarkRed * 0.8f;
@@ -195,9 +196,9 @@ namespace Barotrauma
public void UpdateSubList(IEnumerable<Submarine> submarines)
{
#if DEBUG
var subsToShow = submarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus));
var subsToShow = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus));
#else
var subsToShow = submarines;
var subsToShow = Submarine.SavedSubmarines;
#endif
subList.ClearChildren();
@@ -262,6 +263,11 @@ namespace Barotrauma
saveFiles = SaveUtil.GetSaveFiles(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer);
}
saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft))
{
OnSelected = SelectSaveFile
};
saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft))
{
OnSelected = SelectSaveFile
@@ -269,9 +275,7 @@ namespace Barotrauma
foreach (string saveFile in saveFiles)
{
string fileName = saveFile;
string subName = "";
string saveTime = "";
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform), style: "ListBoxElement")
{
UserData = saveFile
@@ -279,38 +283,25 @@ namespace Barotrauma
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform),
text: Path.GetFileNameWithoutExtension(saveFile));
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.Color = Color.Red;
continue;
}
if (!isMultiplayer)
{
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.Color = Color.Red;
continue;
}
subName = doc.Root.GetAttributeString("submarine", "");
saveTime = doc.Root.GetAttributeString("savetime", "");
}
else
{
string[] splitSaveFile = saveFile.Split(';');
saveFrame.UserData = splitSaveFile[0];
fileName = nameText.Text = Path.GetFileNameWithoutExtension(splitSaveFile[0]);
if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; }
if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; }
}
string submarineName = doc.Root.GetAttributeString("submarine", "");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
text: subName, font: GUI.SmallFont)
text: submarineName, font: GUI.SmallFont)
{
UserData = fileName
UserData = saveFile
};
string saveTime = doc.Root.GetAttributeString("savetime", "");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont)
{
UserData = fileName
UserData = saveFile
};
}

View File

@@ -459,12 +459,6 @@ namespace Barotrauma
OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; },
Enabled = true
};
if (GameMain.Client != null)
{
startButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign));
}
}
OnLocationSelected?.Invoke(location, connection);
@@ -508,13 +502,7 @@ namespace Barotrauma
CanBeFocused = false
};
if (startButton != null)
{
startButton.Enabled = true;
startButton.Visible = GameMain.Client == null ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign);
}
if (startButton != null) { startButton.Enabled = true; }
}
private void CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, int width)

View File

@@ -118,19 +118,6 @@ namespace Barotrauma
instance = this;
}
private void Reset()
{
AnimParams.ForEach(a => a.Reset(true));
RagdollParams.Reset(true);
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
{
character.Remove();
}
character = null;
}
public override void Deselect()
{
base.Deselect();
@@ -141,7 +128,15 @@ namespace Barotrauma
isEndlessRunner = false;
if (character != null)
{
Reset();
AnimParams.ForEach(a => a.Reset(true));
RagdollParams.Reset(true);
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
{
character.Remove();
}
character = null;
}
GameMain.World.ProcessChanges();
}
@@ -398,12 +393,6 @@ namespace Barotrauma
}
if (!isFreezed)
{
if (character.AnimController.Invalid)
{
Reset();
SpawnCharacter(currentCharacterConfig);
}
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.Update((float)deltaTime);
@@ -1226,7 +1215,7 @@ namespace Barotrauma
Cam.Position = character.WorldPosition;
}
private bool CreateCharacter(string name, string mainFolder, bool isHumanoid, params object[] ragdollConfig)
private bool CreateCharacter(string name, bool isHumanoid, params object[] ragdollConfig)
{
var contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault();
if (contentPackage == null)
@@ -1245,16 +1234,17 @@ namespace Barotrauma
#endif
string speciesName = name;
string mainFolder = $"Content/Characters/{speciesName}";
// Config file
string configFilePath = Path.Combine(mainFolder, $"{speciesName}.xml").Replace(@"\", @"/");
string configFilePath = $"{mainFolder}/{speciesName}.xml";
if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).None(path => path.Contains(speciesName)))
{
// Create the config file
XElement mainElement = new XElement("Character",
new XAttribute("name", speciesName),
new XAttribute("humanoid", isHumanoid),
new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))),
new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))),
new XElement("ragdolls"),
new XElement("animations"),
new XElement("health"),
new XElement("ai"));
XDocument doc = new XDocument(mainElement);
@@ -1269,13 +1259,13 @@ namespace Barotrauma
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
}
// Ragdoll
string ragdollFolder = RagdollParams.GetFolder(speciesName);
string ragdollFolder = RagdollParams.GetDefaultFolder(speciesName);
string ragdollPath = RagdollParams.GetDefaultFile(speciesName);
RagdollParams ragdollParams = isHumanoid
? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, speciesName, ragdollConfig)
: RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, speciesName, ragdollConfig) as RagdollParams;
// Animations
string animFolder = AnimationParams.GetFolder(speciesName);
string animFolder = AnimationParams.GetDefaultFolder(speciesName);
foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType)))
{
if (animType != AnimationType.NotDefined)
@@ -4662,7 +4652,7 @@ namespace Barotrauma
LimbXElements.Values,
JointXElements
};
if (CharacterEditorScreen.instance.CreateCharacter(Name, Path.GetDirectoryName(XMLPath), IsHumanoid, ragdollParams))
if (CharacterEditorScreen.instance.CreateCharacter(Name, IsHumanoid, ragdollParams))
{
GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font);
}

View File

@@ -282,7 +282,7 @@ namespace Barotrauma
menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize));
var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center), style: null);
campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines)
campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame)
{
LoadGame = LoadGame,
StartNewGame = StartGame
@@ -336,7 +336,7 @@ namespace Barotrauma
UpdateTutorialList();
campaignSetupUI.UpdateSubList(Submarine.SavedSubmarines);
campaignSetupUI.UpdateSubList();
ResetButtonStates(null);

View File

@@ -632,13 +632,6 @@ namespace Barotrauma
Visible = false
};
campaignViewButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), rightInfoColumn.RectTransform),
TextManager.Get("CampaignView"), style: "GUIButtonLarge")
{
OnClicked = (btn, obj) => { ToggleCampaignView(true); return true; },
Visible = false
};
StartButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight),
TextManager.Get("StartGameButton"), style: "GUIButtonLarge")
{
@@ -651,6 +644,13 @@ namespace Barotrauma
};
clientHiddenElements.Add(StartButton);
campaignViewButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.06f) },
TextManager.Get("CampaignView"), style: "GUIButtonLarge")
{
OnClicked = (btn, obj) => { ToggleCampaignView(true); return true; },
Visible = false
};
spectateButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight),
TextManager.Get("SpectateButton"), style: "GUIButtonLarge");
}
@@ -726,12 +726,6 @@ namespace Barotrauma
spectateButton.Visible = GameMain.Client.GameStarted;
ReadyToStartBox.Visible = !GameMain.Client.GameStarted;
ReadyToStartBox.Selected = false;
if (campaignUI?.StartButton != null)
{
campaignUI.StartButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign));
}
GameMain.Client.SetReadyToStart(ReadyToStartBox);
}
else
@@ -850,13 +844,6 @@ namespace Barotrauma
GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
GameMain.Client.EndRoundButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound);
if (campaignUI?.StartButton != null)
{
campaignUI.StartButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign));
}
}
public void ShowSpectateButton()
@@ -866,21 +853,18 @@ namespace Barotrauma
spectateButton.Enabled = true;
}
public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo)
{
if (newCampaignCharacterInfo != null)
public void SetCampaignCharacterInfo(CharacterInfo characterInfo)
{
if (CampaignCharacterDiscarded) return;
campaignCharacterInfo = characterInfo;
if (campaignCharacterInfo != null)
{
if (CampaignCharacterDiscarded) { return; }
if (campaignCharacterInfo != newCampaignCharacterInfo)
{
campaignCharacterInfo = newCampaignCharacterInfo;
UpdatePlayerFrame(campaignCharacterInfo, false);
}
UpdatePlayerFrame(campaignCharacterInfo, false);
}
else if (campaignCharacterInfo != null)
else
{
campaignCharacterInfo = null;
UpdatePlayerFrame(campaignCharacterInfo, false);
UpdatePlayerFrame(null, true);
}
}
@@ -1207,7 +1191,7 @@ namespace Barotrauma
if (sub.HasTag(SubmarineTag.Shuttle))
{
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.1f, 0.0f) },
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight),
TextManager.Get("Shuttle"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{
TextColor = subTextBlock.TextColor * 0.8f,

View File

@@ -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.Warning, errorMsg);
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
#if DEBUG
DebugConsole.NewMessage(errorMsg, Color.Red);
#endif

View File

@@ -564,9 +564,6 @@ namespace Barotrauma
MapEntityPrefab.Selected = null;
saveFrame = null;
loadFrame = null;
MapEntity.DeselectAll();
MapEntity.SelectionGroups.Clear();
@@ -2043,10 +2040,6 @@ namespace Barotrauma
dummyCharacter.SelectedConstruction = null;
}
}
else if (MapEntity.SelectedList.Count == 1)
{
(MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
}
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
}

View File

@@ -26,10 +26,6 @@ namespace Barotrauma
private int subDivX, subDivY;
private static Effect effect;
public static Effect Effect
{
get { return effect; }
}
private Point spritePos;
private Point spriteSize;

View File

@@ -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.7")]
[assembly: AssemblyFileVersion("0.8.9.7")]
[assembly: AssemblyVersion("0.8.9.6")]
[assembly: AssemblyFileVersion("0.8.9.6")]

View File

@@ -4,6 +4,11 @@ namespace Barotrauma
{
partial class CharacterInfo
{
partial void SpawnInventoryItemProjSpecific(Item item)
{
Entity.Spawner.CreateNetworkEvent(item, false);
}
public void ServerWrite(NetBuffer msg)
{
msg.Write(ID);

View File

@@ -81,12 +81,11 @@ namespace Barotrauma
//dequeue messages
lock (queuedMessages)
{
if (queuedMessages.Count > 0)
if (queuedMessages.Count>0)
{
int inputLines = Math.Max((int)Math.Ceiling(input.Length / (float)Console.WindowWidth), 1);
Console.CursorLeft = 0;
Console.Write(new string(' ', consoleWidth));
Console.CursorTop -= inputLines; Console.CursorLeft = 0;
Console.CursorTop--; Console.CursorLeft = 0;
while (queuedMessages.Count > 0)
{
ColoredText msg = queuedMessages.Dequeue();
@@ -178,11 +177,11 @@ namespace Barotrauma
private static void RewriteInputToCommandLine(string input)
{
int consoleWidth = Math.Max(Console.WindowWidth, 5);
int inputLines = Math.Max((int)Math.Ceiling(input.Length / (float)consoleWidth), 1);
int cursorLine = Math.Max((int)Math.Ceiling((input.Length + 1) / (float)consoleWidth), 1);
Console.WriteLine(""); Console.CursorTop -= inputLines;
Console.WriteLine(""); Console.CursorTop--;
int consoleWidth = Console.WindowWidth;
if (consoleWidth < 5) consoleWidth = 5;
int consoleHeight = Console.WindowHeight;
if (consoleHeight < 5) consoleHeight = 5;
string ln = input.Length > 0 ? AutoComplete(input, 0) : "";
ln += new string(' ', consoleWidth - (ln.Length % consoleWidth));
@@ -191,9 +190,9 @@ namespace Barotrauma
Console.Write(ln);
Console.ForegroundColor = ConsoleColor.White;
Console.CursorLeft = 0;
Console.CursorTop -= cursorLine;
Console.CursorTop--;
Console.Write(input);
Console.CursorLeft = input.Length % Console.WindowWidth;
Console.CursorLeft = input.Length;
}
private static void AssignOnClientRequestExecute(string names, Action<Client, Vector2, string[]> onClientRequestExecute)
@@ -1555,9 +1554,9 @@ namespace Barotrauma
}));
#if DEBUG
commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) =>
commands.Add(new Command("spamevents", "A debug command that immediately creates entity events for all items, characters and structures.", (string[] args) =>
{
/*foreach (Item item in Item.ItemList)
foreach (Item item in Item.ItemList)
{
foreach (ItemComponent component in item.Components)
{
@@ -1574,18 +1573,16 @@ namespace Barotrauma
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status });
}
}
foreach (Character c in Character.CharacterList)
{
GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status });
}*/
foreach (Hull hull in Hull.hullList)
{
GameMain.Server.CreateEntityEvent(hull);
}
foreach (Structure wall in Structure.WallList)
{
GameMain.Server.CreateEntityEvent(wall);
}
}
}));
#endif
}

View File

@@ -11,11 +11,11 @@ namespace Barotrauma
{
private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>();
public static void StartNewCampaign(string savePath, string subPath, string seed)
public static void StartNewCampaign(string savePath, string subName, string seed)
{
if (string.IsNullOrWhiteSpace(savePath)) return;
GameMain.GameSession = new GameSession(new Submarine(subPath, ""), savePath,
GameMain.GameSession = new GameSession(new Submarine(subName, ""), savePath,
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(seed);
@@ -72,27 +72,6 @@ namespace Barotrauma
});
}
public bool AllowedToEndRound(Character interactor)
{
if (interactor == null || Level.Loaded?.StartOutpost == null || Level.Loaded?.EndOutpost == null)
{
return false;
}
if (interactor.Submarine == Level.Loaded.StartOutpost &&
interactor.CanInteractWith(startWatchman))
{
return true;
}
if (interactor.Submarine == Level.Loaded.EndOutpost &&
interactor.CanInteractWith(endWatchman))
{
return true;
}
return false;
}
protected override void WatchmanInteract(Character watchman, Character interactor)
{
if ((watchman.Submarine == Level.Loaded.StartOutpost && !Submarine.MainSub.AtStartPosition) ||
@@ -106,7 +85,8 @@ namespace Barotrauma
if (GameMain.Server != null)
{
var client = GameMain.Server.ConnectedClients.Find(c => c.Character == interactor);
hasPermissions = client != null;
hasPermissions = client != null &&
(client.HasPermission(ClientPermissions.ManageRound) || client.HasPermission(ClientPermissions.ManageCampaign));
CreateDialog(new List<Character> { watchman }, hasPermissions ? "WatchmanInteract" : "WatchmanInteractNotAllowed", 1.0f);
}
}
@@ -251,7 +231,6 @@ namespace Barotrauma
}
lastSaveID++;
DebugConsole.Log("Campaign saved, save ID " + lastSaveID);
}
}
}

View File

@@ -1,5 +1,9 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma.Items.Components
{
@@ -10,16 +14,5 @@ namespace Barotrauma.Items.Components
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
}
}

View File

@@ -335,14 +335,6 @@ namespace Barotrauma
{
if (GameMain.Server == null) return;
if (!ItemList.Contains(this))
{
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
int index = components.IndexOf(ic);
if (index == -1) return;

View File

@@ -20,16 +20,6 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
if (IdFreed) { return; }
//don't create updates if all clients are very far from the hull
float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance;
if (!GameMain.Server.ConnectedClients.Any(c =>
c.Character != null &&
Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr))
{
return;
}
//update client hulls if the amount of water has changed by >10%
//or if oxygen percentage has changed by 5%
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f ||
@@ -41,8 +31,8 @@ namespace Barotrauma
GameMain.NetworkMember.CreateEntityEvent(this);
lastSentVolume = waterVolume;
lastSentOxygen = OxygenPercentage;
sendUpdateTimer = NetConfig.HullUpdateInterval;
}
sendUpdateTimer = NetworkUpdateInterval;
}
}
}

View File

@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
//when was a specific entity event last sent to the client
// key = event id, value = NetTime.Now when sending
public readonly Dictionary<UInt16, double> EntityEventLastSent = new Dictionary<UInt16, double>();
public readonly Dictionary<UInt16, float> EntityEventLastSent = new Dictionary<UInt16, float>();
//when was a position update for a given entity last sent to the client
// key = entity id, value = NetTime.Now when sending

View File

@@ -187,7 +187,7 @@ namespace Barotrauma.Networking
transfer.WaitTimer -= deltaTime;
if (transfer.WaitTimer > 0.0f) continue;
if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel)) continue;
if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) continue;
transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime;
@@ -202,6 +202,15 @@ namespace Barotrauma.Networking
{
message = peer.CreateMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Initiate);
message.Write((byte)transfer.FileType);
message.Write((ushort)chunkLen);
message.Write((ulong)transfer.Data.Length);
message.Write(transfer.FileName);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.Status = FileTransferStatus.Sending;
//if the recipient is the owner of the server (= a client running the server from the main exe)
//we don't need to send anything, the client can just read the file directly

View File

@@ -10,7 +10,6 @@ using System.Text;
using System.IO.Compression;
using System.IO;
using Barotrauma.Steam;
using System.Xml.Linq;
namespace Barotrauma.Networking
{
@@ -44,7 +43,6 @@ namespace Barotrauma.Networking
private IRestResponse masterServerResponse;
private bool autoRestartTimerRunning;
private float endRoundTimer;
public VoipServer VoipServer
{
@@ -407,38 +405,20 @@ namespace Barotrauma.Networking
}
}
if (isCrewDead && respawnManager == null)
{
if (endRoundTimer <= 0.0f)
{
SendChatMessage(TextManager.Get("CrewDeadNoRespawns").Replace("[time]", "60"), ChatMessageType.Server);
}
endRoundTimer += deltaTime;
}
else
{
endRoundTimer = 0.0f;
}
//restart if all characters are dead or submarine is at the end of the level
if ((serverSettings.AutoRestart && isCrewDead)
||
(serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
||
(isCrewDead && respawnManager == null && endRoundTimer >= 60.0f))
(serverSettings.EndRoundAtLevelEnd && subAtLevelEnd))
{
if (serverSettings.AutoRestart && isCrewDead)
{
Log("Ending round (entire crew dead)", ServerLog.MessageType.ServerMessage);
}
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
else
{
Log("Ending round (submarine reached the end of the level)", ServerLog.MessageType.ServerMessage);
}
else
{
Log("Ending round (no living players left and respawning is not enabled during this round)", ServerLog.MessageType.ServerMessage);
}
EndGame();
return;
}
@@ -717,24 +697,13 @@ namespace Barotrauma.Networking
string savePath = inc.ReadString();
string seed = inc.ReadString();
string subName = inc.ReadString();
string subHash = inc.ReadString();
var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
if (matchingSub == null)
{
SendDirectChatMessage(
TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subname]", subName),
connectedClient, ChatMessageType.MessageBox);
}
else
{
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed);
}
}
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, subName, seed);
}
else
{
string saveName = inc.ReadString();
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName);
}
break;
@@ -765,64 +734,6 @@ namespace Barotrauma.Networking
fileSender.ReadFileRequest(inc, connectedClient);
}
break;
case ClientPacketHeader.ERROR:
HandleClientError(inc, connectedClient);
break;
}
}
private void HandleClientError(NetIncomingMessage inc, Client c)
{
string errorStr = "Unhandled error report";
ClientNetError error = (ClientNetError)inc.ReadByte();
int levelEqualityCheckVal = inc.ReadInt32();
switch (error)
{
case ClientNetError.MISSING_EVENT:
UInt16 expectedID = inc.ReadUInt16();
UInt16 receivedID = inc.ReadUInt16();
errorStr = "Expecting event id " + expectedID.ToString() + ", received " + receivedID.ToString();
break;
case ClientNetError.MISSING_ENTITY:
UInt16 eventID = inc.ReadUInt16();
UInt16 entityID = inc.ReadUInt16();
Entity entity = Entity.FindEntityByID(entityID);
if (entity == null)
{
errorStr = "Received an update for an entity that doesn't exist (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
}
else if (entity is Character character)
{
errorStr = "Missing character " + character.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
}
else if (entity is Item item)
{
errorStr = "Missing item " + item.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
}
else
{
errorStr = "Missing entity " + entity.ToString() + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
}
break;
}
if (Level.Loaded != null && levelEqualityCheckVal != Level.Loaded.EqualityCheckVal)
{
errorStr += " Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
}
Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr);
if (c.Connection == OwnerConnection)
{
SendDirectChatMessage(errorStr, c, ChatMessageType.MessageBox);
EndGame();
}
else
{
KickClient(c, errorStr);
}
}
@@ -880,8 +791,8 @@ namespace Barotrauma.Networking
//(the server started a new campaign and the client isn't aware of it yet?)
if (campaign.CampaignID != campaignID)
{
c.LastRecvCampaignSave = (ushort)(campaign.LastSaveID - 1);
c.LastRecvCampaignUpdate = (ushort)(campaign.LastUpdateID - 1);
c.LastRecvCampaignSave = 0;
c.LastRecvCampaignUpdate = 0;
}
}
}
@@ -1035,17 +946,7 @@ namespace Barotrauma.Networking
return;
}
//clients are allowed to end the round by talking with the watchman in multiplayer
//campaign even if they don't have the special permission
if (command == ClientPermissions.ManageRound && inc.PeekBoolean() &&
GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
{
if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command))
{
return;
}
}
else if (!sender.HasPermission(command))
if (!sender.HasPermission(command))
{
Log("Client \"" + sender.Name + "\" sent a server command \"" + command + "\". Permission denied.", ServerLog.MessageType.ServerMessage);
return;
@@ -1126,22 +1027,9 @@ namespace Barotrauma.Networking
UInt16 modeIndex = inc.ReadUInt16();
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier.ToLowerInvariant() == "multiplayercampaign")
{
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer);
for (int i = 0; i < saveFiles.Length; i++)
{
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFiles[i]);
if (doc?.Root != null)
{
saveFiles[i] =
string.Join(";",
saveFiles[i].Replace(';', ' '),
doc.Root.GetAttributeString("submarine", ""),
doc.Root.GetAttributeString("savetime", ""));
}
}
NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer);
msg.Write((UInt16)saveFiles.Count());
foreach (string saveFile in saveFiles)
{
@@ -1226,7 +1114,6 @@ namespace Barotrauma.Networking
ClientWriteLobby(c);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign &&
GameMain.NetLobbyScreen.SelectedMode == campaign.Preset &&
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
{
//already sent an up-to-date campaign save
@@ -1361,6 +1248,10 @@ namespace Barotrauma.Networking
WriteClientList(c, outmsg);
clientListBytes = outmsg.LengthBytes - clientListBytes;
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
int chatMessageBytes = outmsg.LengthBytes;
WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
@@ -1409,55 +1300,25 @@ namespace Barotrauma.Networking
errorMsg +=
" Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" +
" Event size: " + eventManagerBytes + " bytes\n" +
" Position update size: " + positionUpdateBytes + " bytes\n\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
//---------------------------------------------------------------------------
for (int i = 0; i < NetConfig.MaxEventPacketsPerUpdate; i++)
{
outmsg = server.CreateMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)NetTime.Now);
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
if (sentEvents.Count == 0)
{
break;
}
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
if (outmsg.LengthBytes > NetPeerConfiguration.MaximumTransmissionUnit)
{
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + NetPeerConfiguration.MaximumTransmissionUnit + ")\n";
errorMsg +=
" Event size: " + eventManagerBytes + " bytes\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
}
}
private void WriteClientList(Client c, NetOutgoingMessage outmsg)
@@ -1544,8 +1405,7 @@ namespace Barotrauma.Networking
}
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
if (campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode &&
NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate))
if (campaign != null && NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate))
{
outmsg.Write(true);
outmsg.WritePadBits();
@@ -1702,23 +1562,9 @@ namespace Barotrauma.Networking
Rand.SetSyncedSeed(roundStartSeed);
int teamCount = 1;
MultiPlayerCampaign campaign = selectedMode == GameMain.GameSession?.GameMode.Preset ?
MultiPlayerCampaign campaign = GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset ?
GameMain.GameSession?.GameMode as MultiPlayerCampaign : null;
if (campaign != null && campaign.Map == null)
{
initiatedStartGame = false;
startGameCoroutine = null;
string errorMsg = "Starting the round failed. Campaign was still active, but the map has been disposed. Try selecting another game mode.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.StartGame:InvalidCampaignState", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
if (OwnerConnection != null)
{
SendDirectChatMessage(errorMsg, connectedClients.Find(c => c.Connection == OwnerConnection), ChatMessageType.Error);
}
yield return CoroutineStatus.Failure;
}
//don't instantiate a new gamesession if we're playing a campaign
if (campaign == null || GameMain.GameSession == null)
{
@@ -1743,17 +1589,15 @@ namespace Barotrauma.Networking
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
campaign.AssignClientCharacterInfos(connectedClients);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Submarine: " + GameMain.GameSession.Submarine.Name, ServerLog.MessageType.ServerMessage);
Log("Level seed: " + campaign.Map.SelectedConnection.Level.Seed, ServerLog.MessageType.ServerMessage);
}
else
{
GameMain.GameSession.StartRound(GameMain.NetLobbyScreen.LevelSeed, serverSettings.SelectedLevelDifficulty, teamCount > 1);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage);
Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, ServerLog.MessageType.ServerMessage);
}
}
Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, ServerLog.MessageType.ServerMessage);
bool missionAllowRespawn = campaign == null &&
(!(GameMain.GameSession.GameMode is MissionMode) ||
@@ -1915,7 +1759,6 @@ namespace Barotrauma.Networking
msg.Write(seed);
msg.Write(GameMain.GameSession.Level.Seed);
msg.Write(GameMain.GameSession.Level.EqualityCheckVal);
msg.Write(serverSettings.SelectedLevelDifficulty);
msg.Write((byte)GameMain.Config.LosMode);
@@ -1977,8 +1820,6 @@ namespace Barotrauma.Networking
Mission mission = GameMain.GameSession.Mission;
GameMain.GameSession.GameMode.End(endMessage);
endRoundTimer = 0.0f;
if (serverSettings.AutoRestart)
{
@@ -2056,7 +1897,14 @@ namespace Barotrauma.Networking
public override void AddChatMessage(ChatMessage message)
{
if (string.IsNullOrEmpty(message.Text)) { return; }
Log(message.TextWithSender, ServerLog.MessageType.Chat);
if (message.Sender != null)
{
Log($"{message.Sender}: {message.Text}", ServerLog.MessageType.Chat);
}
else
{
Log($"{message.Text}", ServerLog.MessageType.Chat);
}
base.AddChatMessage(message);
}
@@ -2519,8 +2367,6 @@ 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);
@@ -2573,10 +2419,7 @@ namespace Barotrauma.Networking
recipients.Add(otherClient.Connection);
}
}
if (recipients.Any())
{
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
}
server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0);
serverSettings.SaveClientPermissions();
}
@@ -2610,15 +2453,13 @@ 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);
}

View File

@@ -158,15 +158,15 @@ namespace Barotrauma.Networking
}
//kick connected client if status becomes invalid (e.g. VAC banned, not connected to steam)
/*if (status != Facepunch.Steamworks.ServerAuth.Status.OK && GameMain.Config.RequireSteamAuthentication)
if (status != Facepunch.Steamworks.ServerAuth.Status.OK && GameMain.Config.RequireSteamAuthentication)
{
var connectedClient = connectedClients.Find(c => c.SteamID == ownerID);
if (connectedClient != null)
{
Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage);
KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid~[status]={status.ToString()}");
KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid_[status]={status.ToString()}");
}
}*/
}
}
private bool IsServerOwner(NetIncomingMessage inc, NetConnection senderConnection)
@@ -342,7 +342,7 @@ namespace Barotrauma.Networking
if (clVersion != GameMain.Version.ToString())
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={clVersion}");
$"DisconnectMessage.InvalidVersion_[version]={GameMain.Version.ToString()}_[clientversion]={clVersion}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red);
@@ -368,7 +368,7 @@ namespace Barotrauma.Networking
if (missingPackages.Count == 1)
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage_[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
@@ -376,7 +376,7 @@ namespace Barotrauma.Networking
{
List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages_[missingcontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
@@ -399,7 +399,7 @@ namespace Barotrauma.Networking
if (incompatiblePackages.Count == 1)
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}");
$"DisconnectMessage.IncompatibleContentPackage_[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
@@ -408,7 +408,7 @@ namespace Barotrauma.Networking
List<string> packageStrs = new List<string>();
incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
$"DisconnectMessage.IncompatibleContentPackages_[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
@@ -500,7 +500,7 @@ namespace Barotrauma.Networking
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message)
{
inc.SenderConnection.Disconnect(reason.ToString() + "/ " + TextManager.GetServerMessage(message));
inc.SenderConnection.Disconnect(reason.ToString() + "/ " + message);
if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); }
if (unauthClient != null)
{

View File

@@ -1,5 +1,4 @@
using Barotrauma.Extensions;
using Lidgren.Network;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -209,6 +208,7 @@ namespace Barotrauma.Networking
lastSentToAll = owner.LastRecvEntityEventID;
}
}
inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID)) lastSentToAll = c.LastRecvEntityEventID; });
clients.Where(c => c.NeedsMidRoundSync).ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.FirstNewEventID)) lastSentToAll = (ushort)(c.FirstNewEventID - 1); });
@@ -228,8 +228,8 @@ namespace Barotrauma.Networking
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
+ (c.LastRecvEntityEventID + 1).ToString() +
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesync");
}
);
}
@@ -242,8 +242,8 @@ namespace Barotrauma.Networking
toKick.ForEach(c =>
{
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.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
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");
});
}
}
@@ -251,7 +251,7 @@ namespace Barotrauma.Networking
var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);
foreach (Client timedOutClient in timedOutClients)
{
GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.ServerMessage);
GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout");
}
@@ -305,7 +305,19 @@ namespace Barotrauma.Networking
}
//too many events for one packet
if (eventsToSync.Count > 200)
if (eventsToSync.Count > MaxEventsPerWrite)
{
if (eventsToSync.Count > MaxEventsPerWrite * 3 && !client.NeedsMidRoundSync)
{
Color color = eventsToSync.Count > MaxEventsPerWrite * 20 ? Color.Red : Color.Orange;
if (eventsToSync.Count < MaxEventsPerWrite * 5) { color = Color.Yellow; }
DebugConsole.NewMessage("WARNING: event count very high: " + eventsToSync.Count + "/" + MaxEventsPerWrite, color);
}
eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite);
}
foreach (NetEntityEvent entityEvent in eventsToSync)
{
if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync)
{
@@ -334,11 +346,19 @@ namespace Barotrauma.Networking
if (client.NeedsMidRoundSync)
{
msg.Write((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
msg.Write((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
//how many (unique) events the clients had missed before joining
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;
//ID of the first event sent after the client joined
//(after the client has been synced they'll switch their lastReceivedID
//to the one before this, and the eventmanagers will start to function "normally")
client.FirstNewEventID = events.Count == 0 ? (UInt16)0 : events[events.Count - 1].ID;
msg.Write(client.UnreceivedEntityEventCount);
msg.Write(client.FirstNewEventID);
Write(msg, eventsToSync, out sentEvents, client);
Write(msg, eventsToSync, client);
}
else
{
@@ -351,6 +371,7 @@ namespace Barotrauma.Networking
(entityEvent as ServerEntityEvent).Sent = true;
client.EntityEventLastSent[entityEvent.ID] = NetTime.Now;
}
sentEvents = eventsToSync;
}
/// <summary>
@@ -374,10 +395,10 @@ namespace Barotrauma.Networking
for (int i = startIndex; i < eventList.Count; i++)
{
//find the first event that hasn't been sent in roundtriptime or at all
client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent);
//find the first event that hasn't been sent in 1.5 * roundtriptime or at all
client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent);
float minInterval = Math.Max(client.Connection.AverageRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2);
float minInterval = Math.Max(client.Connection.AverageRoundtripTime * 1.5f, (float)server.UpdateInterval.TotalSeconds * 2);
if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f))
{
@@ -403,7 +424,7 @@ namespace Barotrauma.Networking
}
else
{
double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds;
double midRoundSyncTimeOut = uniqueEvents.Count / MaxEventsPerWrite * server.UpdateInterval.TotalSeconds;
midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f);
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;

View File

@@ -78,7 +78,6 @@ namespace Barotrauma.Networking
bool senderSpectating = sender.Character == null || sender.Character.IsDead;
//spectators cannot speak with in-game players or vice versa
//TODO: allow spectators to hear the voice chat if close enough to the speaker?
if (recipientSpectating != senderSpectating) { return false; }
//both spectating, no need to do radio/distance checks

View File

@@ -116,8 +116,8 @@ namespace Barotrauma
msg.Write(AllowEndVoting);
if (AllowEndVoting)
{
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
msg.Write((byte)GameMain.Server.ConnectedClients.Count(v => v.GetVote<bool>(VoteType.EndRound)));
msg.Write((byte)GameMain.Server.ConnectedClients.Count);
}
msg.Write(AllowVoteKick);

View File

@@ -56,7 +56,6 @@
<Character file="Content/Characters/Husk/Husk.xml" />
<Character file="Content/Characters/Humanhusk/Humanhusk.xml" />
<Character file="Content/Characters/Legacyhusk/Legacyhusk.xml" />
<Character file="Content/Characters/Legacycharybdis/Legacycharybdis.xml" />
<Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" />
<Character file="Content/Characters/Legacymoloch/Legacymoloch.xml" />
<Character file="Content/Characters/Mudraptor/Mudraptor.xml" />
@@ -71,12 +70,9 @@
<Outpost file="Content/Map/Outposts/Outpost.sub" />
<Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Typhon.sub" />
<Submarine file="Submarines/Selkie.sub" />
<Submarine file="Submarines/Muikku.sub" />
<Submarine file="Submarines/Bunyip.sub" />
<Submarine file="Submarines/Humpback.sub" />
<Submarine file="Submarines/Dugong.sub" />
<Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" />
<Text file="Content/Texts/EnglishVanilla.xml" />
<UIStyle file="Content/UI/style.xml"/>
<Afflictions file="Content/Afflictions.xml"/>

View File

@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycrawler\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Endworm\Animations\" />
@@ -45,27 +45,12 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Ragdolls\CarrierDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Charybdis.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Charybdis.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Ragdolls\CharybdisDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Legacycharybdis.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Ragdolls\LegacycharybdisDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Coelanth.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -912,13 +897,13 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\CarrierSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdis.png">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdis.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimFast.xml">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimSlow.xml">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\CoelanthSwimFast.xml">
@@ -1658,12 +1643,6 @@
<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>
@@ -2371,7 +2350,7 @@
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Mudraptor\MUDRAPTOR_idle3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdisattack.ogg">
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdisattack.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\attack1.ogg">
@@ -3129,7 +3108,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Muikku.sub">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Orca.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -3144,13 +3123,8 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Remora.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\RemoraDrone.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Selkie.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Typhon.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@@ -110,7 +110,7 @@ namespace Barotrauma
SonarLabel = element.GetAttributeString("sonarlabel", "");
}
public AITarget(Entity e, float sightRange = 3000, float soundRange = 0)
public AITarget(Entity e)
{
Entity = e;
SightRange = sightRange;

View File

@@ -26,6 +26,7 @@ namespace Barotrauma
}
}
public class TargetingPriority
{
public string TargetTag;
@@ -99,9 +100,6 @@ namespace Barotrauma
private readonly float aggressiongreed;
private readonly float aggressionhurt;
// TODO: expose?
private readonly float priorityFearIncreasement = 2;
private readonly float memoryFadeTime = 0.5f;
public bool AttackHumans
{
@@ -251,7 +249,8 @@ namespace Barotrauma
public override void SelectTarget(AITarget target)
{
SelectedAiTarget = target;
selectedTargetMemory = GetTargetMemory(target);
selectedTargetMemory = FindTargetMemory(target);
targetValue = 100.0f;
}
@@ -288,7 +287,8 @@ namespace Barotrauma
}
else
{
UpdateTargets(Character, out TargetingPriority targetingPriority);
TargetingPriority targetingPriority = null;
UpdateTargets(Character, out targetingPriority);
updateTargetsTimer = UpdateTargetsInterval;
if (SelectedAiTarget == null)
@@ -395,44 +395,13 @@ namespace Barotrauma
State = AIState.Idle;
return;
}
else if (selectedTargetMemory != null)
Vector2 escapeDir = Vector2.Normalize(SimPosition - SelectedAiTarget.SimPosition);
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
SteeringManager.SteeringManual(deltaTime, escapeDir);
SteeringManager.SteeringWander();
if (Character.CurrentHull == null)
{
selectedTargetMemory.Priority += deltaTime * priorityFearIncreasement;
}
if (Character.CurrentHull != null)
{
// Seek exit, if inside
if (SteeringManager is IndoorsSteeringManager indoorSteering && escapePoint == Vector2.Zero)
{
foreach (Gap gap in Gap.GapList)
{
if (gap.Submarine != Character.Submarine) { continue; }
if (gap.Open < 1 || gap.IsRoomToRoom) { continue; }
var path = indoorSteering.PathFinder.FindPath(Character.SimPosition, gap.SimPosition);
if (!path.Unreachable)
{
if (escapePoint != Vector2.Zero)
{
// Ignore the gap if it's further away than the previously assigned escape point
if (Vector2.DistanceSquared(Character.SimPosition, gap.SimPosition) > Vector2.DistanceSquared(Character.SimPosition, escapePoint)) { continue; }
}
escapePoint = gap.SimPosition;
}
}
}
}
if (escapePoint != Vector2.Zero && Vector2.DistanceSquared(Character.SimPosition, escapePoint) > 1)
{
SteeringManager.SteeringSeek(escapePoint);
}
else
{
// If outside or near enough the escapePoint, steer away
escapePoint = Vector2.Zero;
Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition);
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
SteeringManager.SteeringManual(deltaTime, escapeDir);
SteeringManager.SteeringWander();
SteeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f);
}
}
@@ -449,8 +418,14 @@ namespace Barotrauma
return;
}
Vector2 attackWorldPos = SelectedAiTarget.WorldPosition;
Vector2 attackSimPos = SelectedAiTarget.SimPosition;
selectedTargetMemory.Priority -= deltaTime * 0.1f;
Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition) : SelectedAiTarget.SimPosition;
if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine != null && Character.Submarine != SelectedAiTarget.Entity.Submarine)
{
attackSimPosition = ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition - Character.Submarine.Position);
}
if (SelectedAiTarget.Entity is Item item)
{
@@ -466,27 +441,22 @@ namespace Barotrauma
}
}
if (raycastTimer > 0.0)
if (wallTarget != null)
{
raycastTimer -= deltaTime;
}
else
{
if (!IsProperlyLatched)
attackSimPosition = ConvertUnits.ToSimUnits(wallTarget.Position);
if (Character.Submarine == null && SelectedAiTarget.Entity?.Submarine != null)
{
UpdateWallTarget();
attackSimPosition += ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
}
raycastTimer = RaycastInterval;
}
if (SelectedAiTarget.Entity is Character c)
else if (SelectedAiTarget.Entity is Character c)
{
//target the closest limb if the target is a character
float closestDist = Vector2.DistanceSquared(SelectedAiTarget.WorldPosition, WorldPosition) * 10.0f;
foreach (Limb limb in c.AnimController.Limbs)
float closestDist = Vector2.DistanceSquared(SelectedAiTarget.SimPosition, SimPosition) * 10.0f;
foreach (Limb limb in ((Character)SelectedAiTarget.Entity).AnimController.Limbs)
{
if (limb == null) continue;
float dist = Vector2.DistanceSquared(limb.WorldPosition, WorldPosition) / Math.Max(limb.AttackPriority, 0.1f);
float dist = Vector2.DistanceSquared(limb.SimPosition, SimPosition) / Math.Max(limb.AttackPriority, 0.1f);
if (dist < closestDist)
{
closestDist = dist;
@@ -518,12 +488,7 @@ namespace Barotrauma
}
}
if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
{
Character.AnimController.TargetDir = Character.WorldPosition.X < attackWorldPos.X ? Direction.Right : Direction.Left;
}
if (aggressiveBoarding)
if (raycastTimer > 0.0)
{
//targeting a wall section that can be passed through -> steer manually through the hole
if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex))
@@ -582,6 +547,74 @@ namespace Barotrauma
}
}
bool canAttack = true;
if (IsCoolDownRunning)
{
UpdateWallTarget();
raycastTimer = RaycastInterval;
}
if (aggressiveBoarding)
{
//targeting a wall section that can be passed through -> steer manually through the hole
if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex))
{
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
Hull targetHull = section.gap?.FlowTargetHull;
if (targetHull != null && !section.gap.IsRoomToRoom)
{
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
if (wallTarget.Structure.IsHorizontal)
{
targetPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
}
else
{
targetPos.X = targetHull.WorldRect.Center.X;
}
latchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs();
if (steeringManager is IndoorsSteeringManager)
{
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetPos - Character.WorldPosition));
}
else
{
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetPos));
}
return;
}
}
else if (SelectedAiTarget.Entity is Item)
{
var door = ((Item)SelectedAiTarget.Entity).GetComponent<Door>();
//steer through the door manually if it's open or broken
if (door?.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom && (door.IsOpen || door.Item.Condition <= 0.0f))
{
var velocity = Vector2.Normalize(door.LinkedGap.FlowTargetHull.WorldPosition - Character.WorldPosition);
if (door.LinkedGap.IsHorizontal)
{
if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height)
{
velocity.Y = 0;
steeringManager.SteeringManual(deltaTime, velocity);
return;
}
}
else
{
if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right)
{
velocity.X = 0;
steeringManager.SteeringManual(deltaTime, velocity);
return;
}
}
}
}
}
bool canAttack = true;
if (IsCoolDownRunning)
{
@@ -598,7 +631,7 @@ namespace Barotrauma
}
else
{
UpdateFallBack(attackWorldPos, deltaTime);
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
}
@@ -606,14 +639,14 @@ namespace Barotrauma
{
if (attackingLimb.attack.SecondaryCoolDownTimer <= 0)
{
// Don't allow attacking when the attack target has just changed.
// Don't allow attacking when the attack target has changed.
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{
canAttack = false;
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack)
{
// Fall back if cannot attack.
UpdateFallBack(attackWorldPos, deltaTime);
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
attackingLimb = null;
@@ -622,7 +655,7 @@ namespace Barotrauma
{
// If the secondary cooldown is defined and expired, check if we can switch the attack
var previousLimb = attackingLimb;
var newLimb = GetAttackLimb(attackWorldPos, previousLimb);
var newLimb = GetAttackLimb(attackSimPosition, previousLimb);
if (newLimb != null)
{
attackingLimb = newLimb;
@@ -636,7 +669,7 @@ namespace Barotrauma
}
else
{
UpdateFallBack(attackWorldPos, deltaTime);
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
}
@@ -651,15 +684,15 @@ namespace Barotrauma
break;
case AIBehaviorAfterAttack.FallBack:
default:
UpdateFallBack(attackWorldPos, deltaTime);
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
}
if (attackingLimb == null || _previousAiTarget != SelectedAiTarget)
if (attackingLimb == null)
{
attackingLimb = GetAttackLimb(attackWorldPos);
attackingLimb = GetAttackLimb(attackSimPosition);
}
if (canAttack)
{
@@ -669,39 +702,24 @@ namespace Barotrauma
if (canAttack)
{
// Check that we can reach the target
distance = Vector2.Distance(attackingLimb.WorldPosition, attackWorldPos);
distance = ConvertUnits.ToDisplayUnits(Vector2.Distance(attackingLimb.SimPosition, attackSimPosition));
canAttack = distance < attackingLimb.attack.Range;
}
// If the attacking limb is a hand or claw, for example, using it as the steering limb can end in the result where the character circles around the target. For example the Hammerhead steering with the claws when it should use the torso.
// If we always use the main limb, this causes the character to seek the target with it's torso/head, when it should not. For example Mudraptor steering with it's belly, when it should use it's head.
// So let's use the one that's closer to the attacking limb.
Limb steeringLimb;
var torso = Character.AnimController.GetLimb(LimbType.Torso);
var head = Character.AnimController.GetLimb(LimbType.Head);
if (attackingLimb == null)
{
steeringLimb = head ?? torso;
}
else
{
if (head != null && torso != null)
{
steeringLimb = Vector2.DistanceSquared(attackingLimb.SimPosition, head.SimPosition) < Vector2.DistanceSquared(attackingLimb.SimPosition, torso.SimPosition) ? head : torso;
}
else
{
steeringLimb = head ?? torso;
}
}
Limb steeringLimb = Character.AnimController.MainLimb;
if (steeringLimb != null)
{
Vector2 offset = Character.SimPosition - steeringLimb.SimPosition;
// Offset so that we don't overshoot the movement
Vector2 steerPos = attackSimPos + offset;
SteeringManager.SteeringSeek(steerPos, 10);
Vector2 steeringVector = attackSimPosition - steeringLimb.SimPosition;
Vector2 targetingVector = Vector2.Normalize(steeringVector) * attackingLimb.attack.Range;
// Offset the position a bit so that we don't overshoot the movement.
Vector2 steerPos = attackSimPosition + targetingVector;
steeringManager.SteeringSeek(steerPos, 10);
if (Character.CurrentHull == null)
{
SteeringManager.SteeringAvoid(deltaTime, colliderSize * 1.5f);
}
if (SteeringManager is IndoorsSteeringManager indoorsSteering)
if (steeringManager is IndoorsSteeringManager indoorsSteering)
{
if (indoorsSteering.CurrentPath != null && !indoorsSteering.IsPathDirty)
{
@@ -716,7 +734,7 @@ namespace Barotrauma
}
else if (indoorsSteering.CurrentPath.Finished)
{
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(steeringVector));
}
else if (indoorsSteering.CurrentPath.CurrentNode?.ConnectedDoor != null)
{
@@ -730,42 +748,40 @@ namespace Barotrauma
}
}
}
else if (Character.CurrentHull == null)
{
SteeringManager.SteeringAvoid(deltaTime, colliderSize * 1.5f);
}
}
if (canAttack)
{
UpdateLimbAttack(deltaTime, attackingLimb, attackSimPos, distance);
UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition, distance);
}
}
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
private Limb GetAttackLimb(Vector2 attackSimPosition, Limb ignoredLimb = null)
{
Hull targetHull = section.gap?.FlowTargetHull;
if (targetHull != null)
{
if (wall.IsHorizontal)
{
targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
}
else
{
targetWorldPos.X = targetHull.WorldRect.Center.X;
}
latchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs();
if (steeringManager is IndoorsSteeringManager)
{
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
}
else
{
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
}
return true;
AttackContext currentContext = Character.GetAttackContext();
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity;
var limbs = Character.AnimController.Limbs
.Where(l =>
l != ignoredLimb &&
l.attack != null &&
!l.IsSevered &&
!l.IsStuck &&
l.attack.IsValidContext(currentContext) &&
l.attack.IsValidTarget(target) &&
l.attack.Conditionals.All(c => (target is ISerializableEntity se && c.Matches(se)) || !(target is ISerializableEntity) || !(target is Character)))
.OrderByDescending(l => l.attack.Priority)
.ThenBy(l => ConvertUnits.ToDisplayUnits(Vector2.Distance(l.SimPosition, attackSimPosition)));
// TODO: priority should probably not override the distance -> use values instead of booleans
return limbs.FirstOrDefault();
}
private void UpdateWallTarget()
{
wallTarget = null;
if (Character.AnimController.CurrentHull != null)
{
return;
}
return false;
}
@@ -794,66 +810,84 @@ namespace Barotrauma
wallTarget = null;
//check if there's a wall between the target and the Character
Vector2 rayStart = SimPosition;
Vector2 rayStart = Character.SimPosition;
Vector2 rayEnd = SelectedAiTarget.SimPosition;
bool offset = SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null;
if (offset)
if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
{
rayStart -= SelectedAiTarget.Entity.Submarine.SimPosition;
rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
}
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);
if (Submarine.LastPickedFraction == 1.0f || closestBody == null)
{
return;
}
if (closestBody.UserData is Structure wall && wall.Submarine != null)
Structure wall = closestBody.UserData as Structure;
if (wall?.Submarine == null)
{
return;
/*if (selectedAiTarget.Entity.Submarine != null)
{
wallTarget = new WallTarget(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition), selectedAiTarget.Entity.Submarine);
latchOntoAI?.SetAttachTarget(closestBody, selectedAiTarget.Entity.Submarine, Submarine.LastPickedPosition);
}*/
//if (selectedAiTarget.Entity.Submarine != null && Character.Submarine == null) wallAttackPos += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
}
else
{
int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition));
int passableHoleCount = GetMinimumPassableHoleCount();
float sectionDamage = wall.SectionDamage(sectionIndex);
for (int i = sectionIndex - 2; i <= sectionIndex + 2; i++)
{
if (wall.SectionBodyDisabled(i))
{
if (aggressiveBoarding && CanPassThroughHole(wall, i))
if (aggressiveBoarding && CanPassThroughHole(wall, i)) //aggressive boarders always target holes they can pass through
{
//aggressive boarders always target holes they can pass through
sectionIndex = i;
break;
}
else
else //otherwise ignore and keep breaking other sections
{
//otherwise ignore and keep breaking other sections
continue;
}
}
if (wall.SectionDamage(i) > sectionDamage) sectionIndex = i;
}
Vector2 sectionPos = wall.SectionPosition(sectionIndex);
Vector2 sectionPos = ConvertUnits.ToSimUnits(wall.SectionPosition(sectionIndex));
Vector2 attachTargetNormal;
if (wall.IsHorizontal)
{
attachTargetNormal = new Vector2(0.0f, Math.Sign(WorldPosition.Y - wall.WorldPosition.Y));
sectionPos.Y += (wall.BodyHeight <= 0.0f ? wall.Rect.Height : wall.BodyHeight) / 2 * attachTargetNormal.Y;
attachTargetNormal = new Vector2(0.0f, Math.Sign(Character.WorldPosition.Y - wall.WorldPosition.Y));
sectionPos.Y += ConvertUnits.ToSimUnits((wall.BodyHeight <= 0.0f ? wall.Rect.Height : wall.BodyHeight) / 2) * attachTargetNormal.Y;
}
else
{
attachTargetNormal = new Vector2(Math.Sign(WorldPosition.X - wall.WorldPosition.X), 0.0f);
sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
attachTargetNormal = new Vector2(Math.Sign(Character.WorldPosition.X - wall.WorldPosition.X), 0.0f);
sectionPos.X += ConvertUnits.ToSimUnits((wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2) * attachTargetNormal.X;
}
latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
wallTarget = new WallTarget(ConvertUnits.ToDisplayUnits(sectionPos), wall, sectionIndex);
latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, sectionPos, attachTargetNormal);
}
}
public override void OnAttacked(Character attacker, AttackResult attackResult)
{
updateTargetsTimer = Math.Min(updateTargetsTimer, 0.1f);
// Reduce the cooldown so that the character can react
foreach (var limb in Character.AnimController.Limbs)
{
if (limb.attack != null)
{
limb.attack.CoolDownTimer *= 0.1f;
// secondary cooldown?
}
}
if (attackResult.Damage > 0.0f && attackWhenProvoked)
{
@@ -868,60 +902,37 @@ namespace Barotrauma
Character.AnimController.ReleaseStuckLimbs();
if (attacker == null || attacker.AiTarget == null) return;
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget);
AITargetMemory targetMemory = FindTargetMemory(attacker.AiTarget);
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * aggressionhurt;
// Reduce the cooldown so that the character can react
// Only allow to react once. Otherwise would attack the target with only a fraction of cooldown
if (SelectedAiTarget != attacker.AiTarget)
{
foreach (var limb in Character.AnimController.Limbs)
{
if (limb.attack != null)
{
limb.attack.CoolDownTimer *= 0.1f;
}
}
}
}
// 10 dmg, 100 health -> 0.1
private float GetRelativeDamage(float dmg, float vitality) => dmg / Math.Max(vitality, 1.0f);
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackSimPos, float distance = -1)
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition, float distance = -1)
{
if (SelectedAiTarget == null) { return; }
if (wallTarget != null)
var damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable;
if (damageTarget == null) return;
float prevHealth = damageTarget.Health;
if (limb.UpdateAttack(deltaTime, attackPosition, damageTarget, out AttackResult attackResult, distance))
{
// If the selected target is not the wall target, make the wall target the selected target.
var aiTarget = wallTarget.Structure.AiTarget;
if (aiTarget != null && SelectedAiTarget != aiTarget)
if (damageTarget.Health > 0)
{
SelectTarget(aiTarget);
// Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * aggressiongreed;
}
}
if (SelectedAiTarget.Entity is IDamageable damageTarget)
if (!limb.attack.IsRunning)
{
float prevHealth = damageTarget.Health;
if (limb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance))
{
if (damageTarget.Health > 0)
{
// Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * aggressiongreed;
}
else
{
selectedTargetMemory.Priority = 0;
}
}
wallTarget = null;
}
}
private void UpdateFallBack(Vector2 attackWorldPos, float deltaTime)
private void UpdateFallBack(Vector2 attackPosition, float deltaTime)
{
Vector2 attackVector = attackWorldPos - WorldPosition;
float dist = attackVector.Length();
float dist = Vector2.Distance(attackPosition, Character.SimPosition);
float desiredDist = colliderSize * 2.0f;
if (dist < desiredDist)
{
@@ -929,6 +940,7 @@ namespace Barotrauma
if (!MathUtils.IsValid(attackDir)) attackDir = Vector2.UnitY;
steeringManager.SteeringManual(deltaTime, attackDir * (1.0f - (dist / 500.0f)));
}
steeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f);
}
@@ -979,20 +991,15 @@ namespace Barotrauma
//goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the Character can see/hear the target and chooses the most preferable target within
//sight/hearing range
public AITarget UpdateTargets(Character character, out TargetingPriority priority)
public void UpdateTargets(Character character, out TargetingPriority targetingPriority)
{
if (IsProperlyLatched)
{
// If attached to a valid target, just keep the target.
// Priority not used in this case.
priority = null;
return SelectedAiTarget;
}
AITarget newTarget = null;
priority = null;
targetingPriority = null;
SelectedAiTarget = null;
selectedTargetMemory = null;
targetValue = 0.0f;
UpdateTargetMemories();
foreach (AITarget target in AITarget.List)
{
if (!target.Enabled) continue;
@@ -1001,57 +1008,29 @@ namespace Barotrauma
continue;
}
float valueModifier = 1.0f;
float dist = 0.0f;
Character targetCharacter = target.Entity as Character;
//ignore the aitarget if it is the Character itself
if (targetCharacter == character) continue;
float valueModifier = 1;
string targetingTag = null;
if (targetCharacter != null)
{
if (targetCharacter.IsDead)
if (targetCharacter.Submarine != null && Character.Submarine == null)
{
//target inside, AI outside -> we'll be attacking a wall between the characters so use the priority for attacking rooms
targetingTag = "room";
}
else if (targetCharacter.IsDead)
{
targetingTag = "dead";
if (targetCharacter.Submarine != Character.Submarine)
{
// In a different sub or the target is outside when we are inside or vice versa -> Ignore the target
continue;
}
else if (targetCharacter.CurrentHull != Character.CurrentHull)
{
// In the same sub, halve the priority, if not in the same hull.
valueModifier = 0.5f;
}
}
else if (targetCharacter.AIController is EnemyAIController enemy)
else if (targetingPriorities.ContainsKey(targetCharacter.SpeciesName.ToLowerInvariant()))
{
if (enemy.combatStrength > combatStrength)
{
targetingTag = "stronger";
}
else if (enemy.combatStrength < combatStrength)
{
targetingTag = "weaker";
}
if (State == AIState.Escape && targetingTag == "stronger")
{
// Frightened
valueModifier = 2;
}
else
{
if (targetCharacter.Submarine != Character.Submarine)
{
// In a different sub or the target is outside when we are inside or vice versa -> Ignore the target
continue;
}
else if (targetCharacter.CurrentHull != Character.CurrentHull)
{
// In the same sub, halve the priority, if not in the same hull.
valueModifier = 0.5f;
}
}
targetingTag = targetCharacter.SpeciesName.ToLowerInvariant();
}
else if (targetCharacter.Submarine != null && Character.Submarine == null)
{
@@ -1068,6 +1047,42 @@ namespace Barotrauma
//skip the target if it's a room and the character is already inside a sub
if (character.CurrentHull != null && target.Entity is Hull) continue;
Door door = null;
if (target.Entity is Item item)
{
if (targetCharacter.AIController is EnemyAIController enemy)
{
targetingTag = "room";
}
door = item.GetComponent<Door>();
foreach (TargetingPriority prio in targetingPriorities.Values)
{
if (item.HasTag(prio.TargetTag))
{
targetingTag = "stronger";
}
}
}
else if (target.Entity is Structure s)
{
targetingTag = "wall";
if (aggressiveBoarding)
{
// Ignore walls when inside.
valueModifier = character.CurrentHull == null ? 2 : 0;
if (valueModifier > 0)
{
targetingTag = "weaker";
}
}
}
}
else if (target.Entity != null)
{
//skip the target if it's a room and the character is already inside a sub
if (character.CurrentHull != null && target.Entity is Hull) continue;
Door door = null;
if (target.Entity is Item item)
{
@@ -1087,63 +1102,19 @@ namespace Barotrauma
}
}
}
else if (target.Entity is Structure s)
{
targetingTag = "wall";
if (aggressiveBoarding)
{
// Ignore walls when inside.
valueModifier = character.CurrentHull == null ? 2 : 0;
if (valueModifier > 0)
{
// Ignore structures that doesn't have a body (not walls)
valueModifier *= s.HasBody ? 1 : 0;
}
for (int i = 0; i < s.Sections.Length; i++)
{
var section = s.Sections[i];
if (CanPassThroughHole(s, i))
{
// Ignore walls that can be passed through
valueModifier = 0;
break;
}
else if (section.gap != null)
{
// up to 100% priority increase for every gap in the wall
valueModifier *= 1 + section.gap.Open;
}
}
}
}
else
{
targetingTag = "room";
}
if (door != null)
{
// If there's not a more specific tag for the door
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
{
targetingTag = "door";
}
bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom;
bool isOpen = door.IsOpen || door.Item.Condition <= 0.0f;
//increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside
if (aggressiveBoarding)
if (character.CurrentHull == null && aggressiveBoarding && !door.LinkedGap.IsRoomToRoom)
{
if (character.CurrentHull == null)
{
valueModifier = isOutdoor ? 1 : 0;
valueModifier *= isOpen ? 5 : 1;
}
else
{
valueModifier = isOutdoor ? 0 : 1;
valueModifier *= isOpen ? 0 : 1;
}
valueModifier = door.IsOpen ? 10 : 5;
}
else if (isOpen) //ignore broken and open doors
else if (door.IsOpen || door.Item.Condition <= 0.0f) //ignore broken and open doors
{
continue;
}
@@ -1162,7 +1133,7 @@ namespace Barotrauma
if (valueModifier == 0.0f) continue;
Vector2 toTarget = target.WorldPosition - character.WorldPosition;
float dist = toTarget.Length();
dist = toTarget.Length();
//if the target has been within range earlier, the character will notice it more easily
//(i.e. remember where the target was)
@@ -1176,29 +1147,28 @@ namespace Barotrauma
// -> just ignore the distance and attack whatever has the highest priority
dist = Math.Max(dist, 100.0f);
AITargetMemory targetMemory = GetTargetMemory(target);
AITargetMemory targetMemory = FindTargetMemory(target);
if (Character.CurrentHull != null && Math.Abs(toTarget.Y) > Character.CurrentHull.Size.Y)
{
// Inside the sub, treat objects that are up or down, as they were farther away.
dist *= 3;
}
valueModifier *= targetMemory.Priority / (float)Math.Sqrt(dist);
valueModifier = valueModifier * targetMemory.Priority / (float)Math.Sqrt(dist);
if (valueModifier > targetValue)
{
newTarget = target;
SelectedAiTarget = target;
selectedTargetMemory = targetMemory;
priority = targetingPriorities[targetingTag];
targetingPriority = targetingPriorities[targetingTag];
targetValue = valueModifier;
}
}
SelectedAiTarget = newTarget;
if (SelectedAiTarget != _previousAiTarget)
{
wallTarget = null;
}
return SelectedAiTarget;
_previousAiTarget = SelectedAiTarget;
}
private AITargetMemory GetTargetMemory(AITarget target)
@@ -1208,21 +1178,24 @@ namespace Barotrauma
memory = new AITargetMemory(10);
targetMemories.Add(target, memory);
}
memory = new AITargetMemory(10.0f);
targetMemories.Add(target, memory);
return memory;
}
private List<AITarget> removals = new List<AITarget>();
private void UpdateTargetMemories(float deltaTime)
{
removals.Clear();
foreach (var memory in targetMemories)
List<AITarget> toBeRemoved = null;
foreach (KeyValuePair<AITarget, AITargetMemory> memory in targetMemories)
{
// Slowly decrease all memories
memory.Value.Priority -= memoryFadeTime * deltaTime;
// Remove targets that have no priority or have been removed
if (memory.Value.Priority <= 1 || !AITarget.List.Contains(memory.Key))
memory.Value.Priority += 0.1f;
if (Math.Abs(memory.Value.Priority) < 1.0f || !AITarget.List.Contains(memory.Key))
{
removals.Add(memory.Key);
if (toBeRemoved == null) toBeRemoved = new List<AITarget>();
toBeRemoved.Add(memory.Key);
}
}
removals.ForEach(r => targetMemories.Remove(r));
@@ -1238,9 +1211,25 @@ namespace Barotrauma
wallTarget = null;
}
if (toBeRemoved != null)
{
foreach (AITarget target in toBeRemoved)
{
targetMemories.Remove(target);
}
}
#endregion
protected override void OnStateChanged(AIState from, AIState to)
{
latchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs();
}
private int GetMinimumPassableHoleCount()
{
return (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderSize) / Structure.WallSectionSize);
return (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderSize) / Structure.WallSectionSize);
}
private bool CanPassThroughHole(Structure wall, int sectionIndex)

View File

@@ -245,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 && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC)))
(c.AIController is EnemyAIController || c.TeamID != Character.TeamID))
{
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders");
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
@@ -290,7 +290,7 @@ 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)
{
if (objectiveManager.CurrentOrder == null)
@@ -466,9 +466,7 @@ 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 && character.TeamID != Character.TeamType.FriendlyNPC && e.TeamID != Character.TeamType.FriendlyNPC)));
int enemyCount = Character.CharacterList.Count(e => e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious && (e.AIController is EnemyAIController || e.TeamID != character.TeamID));
// 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;

View File

@@ -193,7 +193,7 @@ namespace Barotrauma
// is not attached or is attached to something else
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
{
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.Range * enemyAI.AttackingLimb.attack.Range)
{
AttachToBody(character.AnimController.Collider, attachLimb, attachTargetBody, transformedAttachPos);
}

View File

@@ -95,7 +95,7 @@ namespace Barotrauma
getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers)
{
GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = ignoredContainerIdentifiers
IgnoreContainedItems = IgnoreAlreadyContainedItems
};
AddSubObjective(getItemObjective);
return;

View File

@@ -115,7 +115,6 @@ namespace Barotrauma
unreachable.Add(goToObjective.Target as Hull);
}
goToObjective = null;
SteeringManager.SteeringWander();
}
}
else if (currentHull != null)
@@ -132,11 +131,6 @@ 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))
{

View File

@@ -17,7 +17,7 @@ namespace Barotrauma
private string[] itemIdentifiers;
private Item targetItem, moveToTarget;
private int currSearchIndex;
public string[] ignoredContainerIdentifiers;
public bool IgnoreContainedItems;
private AIObjectiveGoTo goToObjective;
private float currItemPriority;
private bool equip;
@@ -99,12 +99,11 @@ namespace Barotrauma
FindTargetItem();
if (targetItem == null || moveToTarget == null)
{
SteeringManager.SteeringWander();
SteeringManager.Reset();
return;
}
if (moveToTarget.CurrentHull == character.CurrentHull &&
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2))
if (Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2))
{
int targetSlot = -1;
if (equip)
@@ -197,12 +196,8 @@ namespace Barotrauma
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { 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 (IgnoreContainedItems && item.Container != null) { continue; }
if (!itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; }
//if the item is inside a character's inventory, don't steal it unless the character is dead
if (item.ParentInventory is CharacterInventory)

View File

@@ -369,16 +369,7 @@ namespace Barotrauma
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2;
float mainLimbAngle = 0;
if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
{
mainLimbAngle = TorsoAngle.Value;
}
else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
mainLimbAngle = HeadAngle.Value;
}
mainLimbAngle *= Dir;
float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle.Value : HeadAngle.Value) * Dir;
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
{
movementAngle += MathHelper.TwoPi;
@@ -417,7 +408,7 @@ namespace Barotrauma
}
}
}
else
else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
@@ -692,6 +683,12 @@ 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)

View File

@@ -99,7 +99,7 @@ namespace Barotrauma
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml";
public static string GetFolder(string speciesName)
protected static string GetFolder(string speciesName)
{
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")

View File

@@ -79,7 +79,7 @@ namespace Barotrauma
new XAttribute("sourcerect", $"0, 0, 1, 1")))
};
public static string GetFolder(string speciesName)
protected static string GetFolder(string speciesName)
{
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")

View File

@@ -1035,6 +1035,8 @@ namespace Barotrauma
CheckValidity();
CheckValidity();
UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider();
UpdateCollisionCategories();
@@ -1295,42 +1297,17 @@ namespace Barotrauma
UpdateProjSpecific(deltaTime);
}
public bool Invalid { get; private set; }
private int validityResets;
private bool CheckValidity()
private void CheckValidity()
{
bool isColliderValid = CheckValidity(Collider);
bool limbsValid = true;
CheckValidity(Collider);
foreach (Limb limb in limbs)
{
if (limb.body == null || !limb.body.Enabled) { continue; }
if (!CheckValidity(limb.body))
{
limbsValid = false;
break;
}
CheckValidity(limb.body);
}
bool isValid = isColliderValid && limbsValid;
if (!isValid)
{
validityResets++;
if (validityResets > 1)
{
Invalid = true;
DebugConsole.ThrowError("Invalid ragdoll physics. Ragdoll freezed to prevent crashes.");
Collider.SetTransform(Vector2.Zero, 0.0f);
foreach (Limb limb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
}
Frozen = true;
}
}
return isValid;
}
private bool CheckValidity(PhysicsBody body)
private void CheckValidity(PhysicsBody body)
{
string errorMsg = null;
string bodyName = body.UserData is Limb ? "Limb" : "Collider";
@@ -1352,19 +1329,6 @@ 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
@@ -1382,7 +1346,7 @@ namespace Barotrauma
limb.body.ResetDynamics();
}
SetInitialLimbPositions();
return false;
return;
}
return true;
}

View File

@@ -808,7 +808,6 @@ namespace Barotrauma
public void LoadHeadAttachments()
{
if (Info == null) { return; }
if (AnimController == null) { return; }
var head = AnimController.GetLimb(LimbType.Head);
if (head == null) { return; }
@@ -1111,15 +1110,13 @@ namespace Barotrauma
ViewTarget = null;
if (!AllowInput) return;
if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer))
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
if (Controlled == this)
{
SmoothedCursorPosition = cursorPosition;
}
else
{
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
}
@@ -1667,12 +1664,10 @@ namespace Barotrauma
focusedItem = null;
}
findFocusedTimer -= deltaTime;
}
}
#endif
//climb ladders automatically when pressing up/down inside their trigger area
Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
if ((SelectedConstruction == null || currentLadder != null) &&
!AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
if (SelectedConstruction == null && !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
{
bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down);
bool isControlled = Controlled == this;
@@ -1683,19 +1678,6 @@ namespace Barotrauma
float minDist = float.PositiveInfinity;
foreach (Ladder ladder in Ladder.List)
{
if (ladder == currentLadder)
{
continue;
}
else if (currentLadder != null)
{
//only switch from ladder to another if the ladders are above the current ladders and pressing up, or vice versa
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y != IsKeyDown(InputType.Up))
{
continue;
}
}
if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist)
{
minDist = dist;
@@ -1894,6 +1876,8 @@ namespace Barotrauma
}
speechImpedimentSet = false;
if (needsAir)
{
bool protectedFromPressure = PressureProtection > 0.0f;
@@ -1950,23 +1934,9 @@ 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;
}
//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; }
}
}
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
UpdateSightRange();
UpdateSoundRange();
@@ -2548,14 +2518,7 @@ namespace Barotrauma
{
item.Submarine = inventory.Owner.Submarine;
var itemElement = item.Save(parentElement);
List<int> slotIndices = new List<int>();
for (int i = 0; i < inventory.Capacity; i++)
{
if (inventory.Items[i] == item) { slotIndices.Add(i); }
}
itemElement.Add(new XAttribute("i", string.Join(",", slotIndices)));
itemElement.Add(new XAttribute("i", Array.IndexOf(inventory.Items, item)));
foreach (ItemContainer container in item.GetComponents<ItemContainer>())
{

View File

@@ -801,37 +801,13 @@ namespace Barotrauma
{
foreach (XElement itemElement in element.Elements())
{
var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true);
if (newItem == null) { continue; }
var newItem = Item.Load(itemElement, inventory.Owner.Submarine);
int slotIndex = itemElement.GetAttributeInt("i", 0);
if (newItem == null) continue;
if (!MathUtils.NearlyEqual(newItem.Condition, newItem.MaxCondition))
{
GameMain.NetworkMember.CreateEntityEvent(newItem, new object[] { NetEntityEvent.Type.Status });
}
SpawnInventoryItemProjSpecific(newItem);
int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 });
if (!slotIndices.Any())
{
DebugConsole.ThrowError("Invalid inventory data in character \"" + Name + "\" - no slot indices found");
continue;
}
inventory.TryPutItem(newItem, slotIndices[0], false, false, null);
//force the item to the correct slots
// e.g. putting the item in a hand slot will also put it in the first available Any-slot,
// which may not be where it actually was
for (int i = 0; i < inventory.Capacity; i++)
{
if (slotIndices.Contains(i))
{
inventory.Items[i] = newItem;
}
else if (inventory.Items[i] == newItem)
{
inventory.Items[i] = null;
}
}
inventory.TryPutItem(newItem, slotIndex, false, false, null);
int itemContainerIndex = 0;
var itemContainers = newItem.GetComponents<ItemContainer>().ToList();
@@ -845,6 +821,8 @@ namespace Barotrauma
}
}
partial void SpawnInventoryItemProjSpecific(Item item);
public void ReloadHeadAttachments()
{
ResetLoadedAttachments();

View File

@@ -13,17 +13,17 @@ namespace Barotrauma
public readonly AnimController.Animation Animation;
public CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, float time, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
public CharacterStateInfo(Vector2 pos, float rotation, Vector2 velocity, float angularVelocity, float time, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
: this(pos, rotation, velocity, angularVelocity, 0, time, dir, interact, animation)
{
}
public CharacterStateInfo(Vector2 pos, float? rotation, UInt16 ID, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
public CharacterStateInfo(Vector2 pos, float rotation, UInt16 ID, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
: this(pos, rotation, Vector2.Zero, 0.0f, ID, 0.0f, dir, interact, animation)
{
}
protected CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, UInt16 ID, float time, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
protected CharacterStateInfo(Vector2 pos, float rotation, Vector2 velocity, float angularVelocity, UInt16 ID, float time, Direction dir, Entity interact, AnimController.Animation animation = AnimController.Animation.None)
: base(pos, rotation, velocity, angularVelocity, ID, time)
{
Direction = dir;

View File

@@ -151,12 +151,9 @@ namespace Barotrauma
//how high the strength has to be for the affliction to take affect
public readonly float ActivationThreshold = 0.0f;
//how high the strength has to be for the affliction icon to be shown in the UI
public readonly float ShowIconThreshold = 0.05f;
public readonly float ShowIconThreshold = 0.0f;
public readonly float MaxStrength = 100.0f;
//how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f;
public float BurnOverlayAlpha;
public float DamageOverlayAlpha;
@@ -257,11 +254,9 @@ namespace Barotrauma
}
ActivationThreshold = element.GetAttributeFloat("activationthreshold", 0.0f);
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", ActivationThreshold);
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);

View File

@@ -460,7 +460,6 @@ namespace Barotrauma
{
affliction.Strength = 0.0f;
}
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction)

View File

@@ -491,10 +491,10 @@ namespace Barotrauma
/// <summary>
/// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
/// </summary>
public bool UpdateAttack(float deltaTime, Vector2 attackSimPos, IDamageable damageTarget, out AttackResult attackResult, float distance = -1)
public bool UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget, out AttackResult attackResult, float distance = -1)
{
attackResult = default(AttackResult);
float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackSimPos));
float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
bool wasRunning = attack.IsRunning;
attack.UpdateAttackTimer(deltaTime);
@@ -511,7 +511,7 @@ namespace Barotrauma
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
structureBody = Submarine.PickBody(
SimPosition, attackSimPos,
SimPosition, attackPosition,
ignoredBodies, Physics.CollisionWall);
if (damageTarget is Item)
@@ -520,15 +520,14 @@ namespace Barotrauma
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
wasHit = true;
}
else if (damageTarget is Structure wall && structureBody != null &&
(structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine)))
else if (damageTarget is Structure && structureBody?.UserData is Structure)
{
// If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful
// If the attack is aimed to a structure and hits a structure, it's successful
wasHit = true;
}
else
{
// If there is nothing between, the hit is successful
// If the attack is aimed to a character but hits a structure, the hit is blocked.
wasHit = structureBody == null;
}
}
@@ -608,7 +607,7 @@ namespace Barotrauma
attack.SetCoolDown();
}
Vector2 diff = attackSimPos - SimPosition;
Vector2 diff = attackPosition - SimPosition;
bool applyForces = (!attack.ApplyForcesOnlyOnce || !wasRunning) && diff.LengthSquared() > 0.00001f;
if (applyForces)
{
@@ -621,13 +620,13 @@ namespace Barotrauma
Limb limb = character.AnimController.Limbs[limbIndex];
Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
}
}
else
{
Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
}
}
return wasHit;

View File

@@ -912,7 +912,7 @@ namespace Barotrauma
ThrowError(args[0] + " is not a valid latency value.");
return;
}
if (!float.TryParse(args[1], NumberStyles.Any, CultureInfo.InvariantCulture, out float randomLatency))
if (!float.TryParse(args[0], NumberStyles.Any, CultureInfo.InvariantCulture, out float randomLatency))
{
ThrowError(args[1] + " is not a valid latency value.");
return;

View File

@@ -103,6 +103,12 @@ namespace Barotrauma
if (missionType == MissionType.Random)
{
allowedMissions.AddRange(MissionPrefab.List);
#if SERVER
if (GameMain.Server != null)
{
allowedMissions.RemoveAll(mission => !GameMain.Server.ServerSettings.AllowedRandomMissionTypes.Contains(mission.type));
}
#endif
}
else if (missionType == MissionType.None)
{
@@ -118,11 +124,6 @@ namespace Barotrauma
{
allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1]));
}
if (allowedMissions.Count == 0)
{
return null;
}
int probabilitySum = allowedMissions.Sum(m => m.Commonness);
int randomNumber = rand.NextInt32() % probabilitySum;

View File

@@ -208,7 +208,7 @@ namespace Barotrauma
DockingPort myPort = null, outPostPort = null;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.IsHorizontal) { continue; }
if (port.Item.Submarine == level.StartOutpost)
{
outPostPort = port;

View File

@@ -168,7 +168,7 @@ namespace Barotrauma
}
}
private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f, microphoneVolume = 1.0f;
private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f;
public float SoundVolume
{
@@ -211,14 +211,6 @@ namespace Barotrauma
}
}
public float MicrophoneVolume
{
get { return microphoneVolume; }
set
{
microphoneVolume = MathHelper.Clamp(value, 0.1f, 5.0f);
}
}
public string Language
{
get { return TextManager.Language; }

View File

@@ -51,11 +51,6 @@ 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

View File

@@ -105,7 +105,7 @@ namespace Barotrauma.Items.Components
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
item.Drop(picker, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.Drop(picker);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f);

View File

@@ -208,9 +208,8 @@ namespace Barotrauma.Items.Components
public ItemComponent(Item item, XElement element)
{
this.item = item;
originalElement = element;
name = element.Name.ToString();
SerializableProperties = SerializableProperty.GetProperties(this);
properties = SerializableProperty.GetProperties(this);
requiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>();
requiredSkills = new List<Skill>();
@@ -244,9 +243,18 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError("Invalid pick key in " + element + "!", e);
}
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ParseMsg();
properties = SerializableProperty.DeserializeProperties(this, element);
#if CLIENT
string msg = TextManager.Get(Msg, true);
if (msg != null)
{
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
}
Msg = msg;
}
#endif
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -534,6 +542,7 @@ 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++)
@@ -622,11 +631,53 @@ namespace Barotrauma.Items.Components
public virtual void Load(XElement componentElement)
{
if (componentElement == null) return;
foreach (XAttribute attribute in componentElement.Attributes())
{
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
if (!properties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
property.TrySetValue(this, attribute.Value);
}
#if CLIENT
string msg = TextManager.Get(Msg, true);
if (msg != null)
{
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
}
Msg = msg;
}
#endif
var prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
bool overrideRequiredItems = false;
foreach (XElement subElement in componentElement.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "requireditem":
if (!overrideRequiredItems) requiredItems.Clear();
overrideRequiredItems = true;
RelatedItem newRequiredItem = RelatedItem.Load(subElement, item.Name);
if (newRequiredItem == null) continue;
var prevRequiredItem = prevRequiredItems.ContainsKey(newRequiredItem.Type) ?
prevRequiredItems[newRequiredItem.Type].Find(ri => ri.JoinedIdentifiers == newRequiredItem.JoinedIdentifiers) : null;
if (prevRequiredItem != null)
{
newRequiredItem.statusEffects = prevRequiredItem.statusEffects;
newRequiredItem.Msg = prevRequiredItem.Msg;
}
if (!requiredItems.ContainsKey(newRequiredItem.Type))
{
requiredItems[newRequiredItem.Type] = new List<RelatedItem>();
}
requiredItems[newRequiredItem.Type].Add(newRequiredItem);
break;
}
}
ParseMsg();
OverrideRequiredItems(componentElement);
}

View File

@@ -201,19 +201,19 @@ namespace Barotrauma.Items.Components
tolerance = MathHelper.Lerp(5.0f, 20.0f, degreeOfSuccess);
allowedTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);
float temperatureTolerance = MathHelper.Lerp(10.0f, 20.0f, degreeOfSuccess);
optimalTemperature = Vector2.Lerp(new Vector2(40.0f, 60.0f), new Vector2(30.0f, 70.0f), degreeOfSuccess);
allowedTemperature = Vector2.Lerp(new Vector2(30.0f, 70.0f), new Vector2(10.0f, 90.0f), degreeOfSuccess);
optimalFissionRate = Vector2.Lerp(new Vector2(30, AvailableFuel - 20), new Vector2(20, AvailableFuel - 10), degreeOfSuccess);
optimalFissionRate.X = Math.Min(optimalFissionRate.X, optimalFissionRate.Y - 10);
allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10);
float fissionRateTolerance = MathHelper.Lerp(10.0f, 20.0f, degreeOfSuccess);
optimalFissionRate = Vector2.Lerp(new Vector2(40.0f, 70.0f), new Vector2(30.0f, 85.0f), degreeOfSuccess);
allowedFissionRate = Vector2.Lerp(new Vector2(30.0f, 85.0f), new Vector2(20.0f, 98.0f), degreeOfSuccess);
float heatAmount = fissionRate * (AvailableFuel / 100.0f) * 2.0f;
float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime);
TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime);
@@ -364,7 +364,7 @@ namespace Barotrauma.Items.Components
}
else if (-currPowerConsumption < load)
{
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f);
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, allowedFissionRate.Y);
}
targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f);
}

View File

@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
}
continue;
}
if (!pt.IsActive || !pt.CanTransfer) { continue; }
if (!pt.IsActive) { continue; }
gridLoad += pt.PowerLoad;
gridPower -= pt.CurrPowerConsumption;
@@ -209,9 +209,9 @@ namespace Barotrauma.Items.Components
Charge -= CurrPowerOutput / 3600.0f;
}
item.SendSignal(0, ((int)Charge).ToString(), "charge", null);
item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null);
item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
item.SendSignal(0, Charge.ToString(), "charge", null);
item.SendSignal(0, ((Charge / capacity) * 100).ToString(), "charge_%", null);
item.SendSignal(0, ((RechargeSpeed / maxRechargeSpeed) * 100).ToString(), "charge_rate", null);
foreach (Pair<Powered, Connection> connected in directlyConnected)
{

View File

@@ -319,13 +319,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
//float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
RelayComponent thisRelayComponent = this as RelayComponent;
if (thisRelayComponent != null)
{
clampPower = Math.Min(Math.Min(clampPower, thisRelayComponent.MaxPower), powerLoad);
clampLoad = Math.Min(clampLoad, thisRelayComponent.MaxPower);
}
float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
foreach (Connection c in PowerConnections)
{
@@ -363,8 +357,6 @@ namespace Barotrauma.Items.Components
continue;
}
float addLoad = 0.0f;
float addPower = 0.0f;
if (powered is PowerContainer powerContainer)
{
if (recipient.Name == "power_in")
@@ -373,7 +365,7 @@ namespace Barotrauma.Items.Components
}
else
{
addPower = powerContainer.CurrPowerOutput;
fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower);
}
}
else
@@ -388,16 +380,10 @@ namespace Barotrauma.Items.Components
//negative power consumption = the construction is a
//generator/battery or another junction box
{
addPower -= powered.CurrPowerConsumption;
fullPower -= Math.Max(powered.CurrPowerConsumption, -maxPower);
}
}
if (addPower + fullPower > clampPower) { addPower -= (addPower + fullPower) - clampPower; };
if (addPower > 0) { fullPower += addPower; }
if (addLoad + fullLoad > clampLoad) { addLoad -= (addLoad + fullLoad) - clampLoad; };
if (addLoad > 0) { fullLoad += addLoad; }
}
}
}
}
}

View File

@@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f;
private string header;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer;
@@ -50,20 +52,17 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")]
public float FixDurationLowSkill
/*private float repairProgress;
public float RepairProgress
{
get;
set;
}
[Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")]
public float FixDurationHighSkill
{
get;
set;
}
get { return repairProgress; }
set
{
repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f);
if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
}
}*/
private Character currentFixer;
public Character CurrentFixer
{
@@ -84,6 +83,8 @@ namespace Barotrauma.Items.Components
this.item = item;
header = element.GetAttributeString("name", "");
fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f);
fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f);
InitProjSpecific(element);
}
@@ -159,7 +160,7 @@ namespace Barotrauma.Items.Components
}
bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition;
@@ -185,5 +186,26 @@ namespace Barotrauma.Items.Components
{
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
}
}

View File

@@ -77,7 +77,7 @@ namespace Barotrauma.Items.Components
IsActive = value;
#if SERVER
if (GameMain.Server != null && GameMain.Server.GameStarted) { item.CreateServerEvent(this); }
if (GameMain.Server != null) item.CreateServerEvent(this);
#endif
}
}

View File

@@ -177,21 +177,14 @@ 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;
//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);
}

View File

@@ -433,10 +433,9 @@ 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)
{
containerItem = e as Item;
var containerItem = e as Item;
if (containerItem == null) continue;
container = containerItem.GetComponent<ItemContainer>();
@@ -454,7 +453,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.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
containShellObjective.IgnoreAlreadyContainedItems = true;
objective.AddSubObjective(containShellObjective);
return false;
}

View File

@@ -59,7 +59,8 @@ namespace Barotrauma
public PhysicsBody body;
public readonly XElement StaticBodyConfig;
private bool needsPositionUpdate;
private float lastSentCondition;
private float sendConditionUpdateTimer;
private bool conditionUpdatePending;
@@ -88,7 +89,7 @@ namespace Barotrauma
if (hasInGameEditableProperties == null)
{
hasInGameEditableProperties = false;
if (SerializableProperties.Values.Any(p => p.Attributes.OfType<InGameEditable>().Any()))
if (properties.Values.Any(p => p.Attributes.OfType<InGameEditable>().Any()))
{
hasInGameEditableProperties = true;
}
@@ -97,7 +98,7 @@ namespace Barotrauma
foreach (ItemComponent component in components)
{
if (!component.AllowInGameEditing) { continue; }
if (component.SerializableProperties.Values.Any(p => p.Attributes.OfType<InGameEditable>().Any()))
if (component.properties.Values.Any(p => p.Attributes.OfType<InGameEditable>().Any()))
{
hasInGameEditableProperties = true;
break;
@@ -211,14 +212,14 @@ namespace Barotrauma
set { spriteColor = value; }
}
[Serialize("1.0,1.0,1.0,1.0", true), Editable]
[Serialize("1.0,1.0,1.0,1.0", false), Editable]
public Color InventoryIconColor
{
get;
protected set;
}
[Serialize("1.0,1.0,1.0,1.0", true), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
[Serialize("1.0,1.0,1.0,1.0", false), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
public Color ContainerColor
{
get;
@@ -274,11 +275,12 @@ namespace Barotrauma
SetActiveSprite();
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition))
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition)
{
if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health)
{
conditionUpdatePending = true;
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
}
}
}
@@ -583,10 +585,10 @@ namespace Barotrauma
public override MapEntity Clone()
{
Item clone = new Item(rect, Prefab, Submarine, callOnItemLoaded: false);
foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties)
foreach (KeyValuePair<string, SerializableProperty> property in properties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
clone.SerializableProperties[property.Key].TrySetValue(clone, property.Value.GetValue(this));
clone.properties[property.Key].TrySetValue(clone, property.Value.GetValue(this));
}
if (components.Count != clone.components.Count)
@@ -603,7 +605,7 @@ namespace Barotrauma
foreach (KeyValuePair<string, SerializableProperty> property in components[i].SerializableProperties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
clone.components[i].SerializableProperties[property.Key].TrySetValue(clone.components[i], property.Value.GetValue(components[i]));
clone.components[i].properties[property.Key].TrySetValue(clone.components[i], property.Value.GetValue(components[i]));
}
//clone requireditem identifiers
@@ -995,21 +997,6 @@ namespace Barotrauma
aiTarget.SightRange -= deltaTime * 1000.0f;
aiTarget.SoundRange -= deltaTime * 1000.0f;
}
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending)
{
if (sendConditionUpdateTimer <= 0.0f)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval;
conditionUpdatePending = false;
}
}
}
ApplyStatusEffects(ActionType.Always, deltaTime, null);
@@ -1574,15 +1561,12 @@ namespace Barotrauma
return isCombined;
}
public void Drop(Character dropper, bool createNetworkEvent = true)
public void Drop(Character dropper)
{
if (createNetworkEvent)
if (parentInventory != null && !parentInventory.Owner.Removed && !Removed &&
GameMain.NetworkMember != null && (GameMain.NetworkMember.IsServer || Character.Controlled == dropper))
{
if (parentInventory != null && !parentInventory.Owner.Removed && !Removed &&
GameMain.NetworkMember != null && (GameMain.NetworkMember.IsServer || Character.Controlled == dropper))
{
parentInventory.CreateNetworkEvent();
}
parentInventory.CreateNetworkEvent();
}
foreach (ItemComponent ic in components) { ic.Drop(dropper); }
@@ -1825,20 +1809,8 @@ namespace Barotrauma
}
partial void UpdateNetPosition(float deltaTime);
public static Item Load(XElement element, Submarine submarine)
{
return Load(element, submarine, createNetworkEvent: false);
}
/// <summary>
/// Instantiate a new item and load its data from the XML element.
/// </summary>
/// <param name="element">The element containing the data of the item</param>
/// <param name="submarine">The submarine to spawn the item in (can be null)</param>
/// <param name="createNetworkEvent">Should an EntitySpawner event be created to notify clients about the item being created.</param>
/// <returns></returns>
public static Item Load(XElement element, Submarine submarine, bool createNetworkEvent)
{
string name = element.Attribute("name").Value;
string identifier = element.GetAttributeString("identifier", "");
@@ -1884,16 +1856,9 @@ namespace Barotrauma
linkedToID = new List<ushort>()
};
#if SERVER
if (createNetworkEvent)
{
Spawner.CreateNetworkEvent(item, remove: false);
}
#endif
foreach (XAttribute attribute in element.Attributes())
{
if (!item.SerializableProperties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) continue;
if (!item.properties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) continue;
bool shouldBeLoaded = false;
foreach (var propertyAttribute in property.Attributes.OfType<Serialize>())
{
@@ -1937,7 +1902,7 @@ namespace Barotrauma
{
component.OnItemLoaded();
}
return item;
}

View File

@@ -91,7 +91,8 @@ namespace Barotrauma
public override void CreateNetworkEvent()
{
if (!Item.ItemList.Contains(container.Item))
int componentIndex = container.Item.GetComponentIndex(container);
if (componentIndex == -1)
{
string errorMsg = "Attempted to create a network event for an item (" + container.Item.Name + ") that hasn't been fully initialized yet.";
DebugConsole.ThrowError(errorMsg);
@@ -100,13 +101,6 @@ namespace Barotrauma
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
int componentIndex = container.Item.GetComponentIndex(container);
if (componentIndex == -1)
{
DebugConsole.Log("Creating a network event for the item \"" + container.Item + "\" failed, ItemContainer not found in components");
return;
}
if (GameMain.NetworkMember != null)
{

View File

@@ -28,10 +28,8 @@ namespace Barotrauma
public Explosion(float range, float force, float damage, float structureDamage, float empStrength = 0.0f)
{
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range)
{
SeverLimbsProbability = 1.0f
};
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range);
attack.SeverLimbsProbability = 1.0f;
this.force = force;
this.empStrength = empStrength;
sparks = true;
@@ -185,6 +183,9 @@ namespace Barotrauma
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
bool underWater = hull == null || explosionPos.Y < hull.Surface;
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();

View File

@@ -175,11 +175,12 @@ namespace Barotrauma
LimitSize();
UpdateProjSpecific(growModifier);
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
}
partial void UpdateProjSpecific(float growModifier);
@@ -292,6 +293,10 @@ namespace Barotrauma
//evaporate some of the water
hull.WaterVolume -= extinguishAmount;
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
@@ -320,11 +325,12 @@ namespace Barotrauma
size.X -= extinguishAmount;
hull.WaterVolume -= extinguishAmount;
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
#if CLIENT
if (GameMain.Client != null) return;
#endif
if (size.X < 1.0f) Remove();
}
public void Extinguish(float deltaTime, float amount, Vector2 worldPosition)

View File

@@ -231,7 +231,7 @@ namespace Barotrauma
public static Level CreateRandom(LocationConnection locationConnection)
{
string seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName;
string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name;
float sizeFactor = MathUtils.InverseLerp(
MapGenerationParams.Instance.SmallLevelConnectionLength,
@@ -1522,40 +1522,14 @@ namespace Barotrauma
outpost.MakeOutpost();
Point? minSize = null;
DockingPort subPort = null;
if (Submarine.MainSub != null)
{
Point subSize = Submarine.MainSub.GetDockedBorders().Size;
Point outpostSize = outpost.GetDockedBorders().Size;
minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y);
float closestDistance = float.MaxValue;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine != Submarine.MainSub) { continue; }
//the submarine port has to be at the top of the sub
if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; }
float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X);
if (dist < closestDistance)
{
subPort = port;
closestDistance = dist;
}
}
}
float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X;
//don't try to compensate if the port is very far from the sub's center of mass
if (Math.Abs(subDockingPortOffset) > 2000.0f)
{
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f);
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
DebugConsole.NewMessage(warningMsg, Color.Orange);
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg);
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset));
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize));
if ((i == 0) == !Mirrored)
{
StartOutpost = outpost;

View File

@@ -15,6 +15,7 @@ namespace Barotrauma
public Vector3 Position;
public float NetworkUpdateTimer;
public const float NetworkUpdateInterval = 0.2f;
public float Scale;

View File

@@ -343,7 +343,7 @@ namespace Barotrauma
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj });
obj.NeedsNetworkSyncing = false;
obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval;
obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval;
}
}

View File

@@ -432,16 +432,12 @@ namespace Barotrauma
{
if (ForceFluctuationStrength > 0.0f)
{
//no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers
if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any())
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
{
forceFluctuationTimer += deltaTime;
if (forceFluctuationTimer > ForceFluctuationInterval)
{
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
}
NeedsNetworkSyncing = true;
currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f);
forceFluctuationTimer = 0.0f;
}
}

View File

@@ -15,8 +15,6 @@ namespace Barotrauma
public int TypeChangeTimer;
public string BaseName { get => baseName; }
public string Name { get; private set; }
public Vector2 MapPosition { get; private set; }
@@ -33,10 +31,10 @@ namespace Barotrauma
get
{
CheckMissionCompleted();
for (int i = availableMissions.Count; i < Connections.Count * 2; i++)
{
int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue;
int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue;
MTRandom rand = new MTRandom(seed);
LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count];
@@ -47,7 +45,7 @@ namespace Barotrauma
if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; }
if (GameSettings.VerboseLogging && mission != null)
{
DebugConsole.NewMessage("Generated a new mission for a location (location: " + Name + ", seed: " + seed.ToString("X") + ", missions completed: " + MissionsCompleted + ", type: " + mission.Name + ")", Color.White);
DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White);
}
availableMissions.Add(mission);
}
@@ -100,16 +98,7 @@ namespace Barotrauma
public void ChangeType(LocationType newType)
{
if (newType == Type) { return; }
//clear missions from this and adjacent locations (they may be invalid now)
availableMissions.Clear();
foreach (LocationConnection connection in Connections)
{
connection.OtherLocation(this)?.availableMissions.Clear();
}
DebugConsole.Log("Location " + baseName + " changed it's type from " + Type + " to " + newType);
if (newType == Type) return;
Type = newType;
Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName);
@@ -121,7 +110,6 @@ namespace Barotrauma
{
if (mission.Completed)
{
DebugConsole.Log("Mission \"" + mission.Name + "\" completed in \"" + Name + "\".");
MissionsCompleted++;
}
}

View File

@@ -578,12 +578,10 @@ namespace Barotrauma
location.MissionsCompleted = missionsCompleted;
if (showNotifications && prevLocationType != location.Type)
{
var change = prevLocationType.CanChangeTo.Find(c =>
c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant());
if (change != null)
{
ChangeLocationType(location, prevLocationName, change);
}
ChangeLocationType(
location,
prevLocationName,
prevLocationType.CanChangeTo.Find(c => c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant()));
}
break;
case "connection":

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