Merge pull request #4 from Regalis11/master

Synced
This commit is contained in:
Blue
2018-09-14 16:08:54 +02:00
committed by GitHub
163 changed files with 3896 additions and 1726 deletions
+4
View File
@@ -0,0 +1,4 @@
# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf
*.cs text eol=crlf
*.xml text eol=crlf
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.5")] [assembly: AssemblyVersion("0.8.2.0")]
[assembly: AssemblyFileVersion("0.8.1.5")] [assembly: AssemblyFileVersion("0.8.2.0")]
+2 -2
View File
@@ -99,7 +99,7 @@ namespace Barotrauma
public Camera() public Camera()
{ {
zoom = 1.0f; zoom = prevZoom = 1.0f;
rotation = 0.0f; rotation = 0.0f;
position = Vector2.Zero; position = Vector2.Zero;
@@ -112,7 +112,7 @@ namespace Barotrauma
viewMatrix = viewMatrix =
Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0)); Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
UpdateTransform(); UpdateTransform(false);
} }
public Vector2 TargetPos public Vector2 TargetPos
@@ -81,6 +81,9 @@ namespace Barotrauma
if (Limbs == null) if (Limbs == null)
{ {
DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace); DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace);
GameAnalyticsManager.AddErrorEventOnce("Ragdoll.Draw:LimbsRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace);
return; return;
} }
@@ -97,7 +100,6 @@ namespace Barotrauma
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
if (limb.pullJoint != null) if (limb.pullJoint != null)
{ {
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA); Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA);
@@ -134,6 +136,16 @@ namespace Barotrauma
} }
} }
if (outsideCollisionBlocker.Enabled && currentHull.Submarine != null)
{
var edgeShape = outsideCollisionBlocker.FixtureList[0].Shape as FarseerPhysics.Collision.Shapes.EdgeShape;
Vector2 startPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex1)) + currentHull.Submarine.Position;
Vector2 endPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex2)) + currentHull.Submarine.Position;
startPos.Y = -startPos.Y;
endPos.Y = -endPos.Y;
GUI.DrawLine(spriteBatch, startPos, endPos, Color.Gray, 0, 5);
}
if (character.MemState.Count > 1) if (character.MemState.Count > 1)
{ {
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position); Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);
@@ -207,7 +207,7 @@ namespace Barotrauma
GameMain.GameSession.CrewManager.RemoveCharacter(this); GameMain.GameSession.CrewManager.RemoveCharacter(this);
} }
if (GameMain.Client != null && GameMain.Client.Character == this) GameMain.Client.Character = null; if (GameMain.NetworkMember?.Character == this) GameMain.NetworkMember.Character = null;
if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null; if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null;
} }
@@ -32,7 +32,7 @@ namespace Barotrauma
public static void TakeDamage(float amount) public static void TakeDamage(float amount)
{ {
healthBar.Flash(); healthBar?.Flash();
damageOverlayTimer = MathHelper.Clamp(amount * 0.1f, 0.2f, 1.0f); damageOverlayTimer = MathHelper.Clamp(amount * 0.1f, 0.2f, 1.0f);
} }
@@ -150,7 +150,7 @@ namespace Barotrauma
msg.ReadPadBits(); msg.ReadPadBits();
int index = 0; int index = 0;
if (GameMain.NetworkMember.Character == this) if (GameMain.NetworkMember.Character == this && AllowInput)
{ {
var posInfo = new CharacterStateInfo(pos, rotation, networkUpdateID, facingRight ? Direction.Right : Direction.Left, selectedEntity, animation); var posInfo = new CharacterStateInfo(pos, rotation, networkUpdateID, facingRight ? Direction.Right : Direction.Left, selectedEntity, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID)) while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
@@ -188,6 +188,7 @@ namespace Barotrauma
controlled = this; controlled = this;
IsRemotePlayer = false; IsRemotePlayer = false;
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = this; GameMain.Client.Character = this;
GameMain.LightManager.LosEnabled = true; GameMain.LightManager.LosEnabled = true;
} }
@@ -286,6 +287,7 @@ namespace Barotrauma
if (GameMain.Client.ID == ownerId) if (GameMain.Client.ID == ownerId)
{ {
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = character; GameMain.Client.Character = character;
Controlled = character; Controlled = character;
@@ -342,7 +344,7 @@ namespace Barotrauma
} }
else else
{ {
this.isDead = false; if (this.isDead) Revive();
health = msg.ReadRangedSingle(minHealth, maxHealth, 8); health = msg.ReadRangedSingle(minHealth, maxHealth, 8);
@@ -194,10 +194,11 @@ namespace Barotrauma
} }
} }
Color wearableColor = wearable.WearableComponent.Item.GetSpriteColor();
wearable.Sprite.Draw(spriteBatch, wearable.Sprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color, origin, new Color((color.R * wearableColor.R) / (255.0f * 255.0f), (color.G * wearableColor.G) / (255.0f * 255.0f), (color.B * wearableColor.B) / (255.0f * 255.0f)) * ((color.A * wearableColor.A) / (255.0f * 255.0f)),
-body.DrawRotation, origin, -body.DrawRotation,
Scale, spriteEffect, depth); Scale, spriteEffect, depth);
} }
@@ -355,9 +355,9 @@ namespace Barotrauma
NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange); NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange);
MapEntity.mapEntityList.RemoveAt(i); MapEntity.mapEntityList.RemoveAt(i);
} }
else if (me.MoveWithLevel) else if (!me.ShouldBeSaved)
{ {
NewMessage("Removed " + me.Name + " (MoveWithLevel==true)", Color.Orange); NewMessage("Removed " + me.Name + " (!ShouldBeSaved)", Color.Orange);
MapEntity.mapEntityList.RemoveAt(i); MapEntity.mapEntityList.RemoveAt(i);
} }
else if (me is Item) else if (me is Item)
@@ -438,6 +438,8 @@ namespace Barotrauma
NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green); NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green);
NewMessage("Fullscreen enabled", Color.Green); NewMessage("Fullscreen enabled", Color.Green);
GameSettings.ShowUserStatisticsPrompt = true;
GameSettings.VerboseLogging = false; GameSettings.VerboseLogging = false;
if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster") if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster")
@@ -214,12 +214,9 @@ namespace Barotrauma
if (GameMain.GameSession != null) if (GameMain.GameSession != null)
{ {
if (GameSettings.SendUserStatistics) Mission mission = GameMain.GameSession.Mission;
{ GameAnalyticsManager.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
Mission mission = GameMain.GameSession.Mission; GameAnalyticsManager.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
}
GameMain.GameSession = null; GameMain.GameSession = null;
} }
+38 -1
View File
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using GameAnalyticsSDK.Net; using GameAnalyticsSDK.Net;
using System.Threading;
namespace Barotrauma namespace Barotrauma
{ {
@@ -74,6 +75,7 @@ namespace Barotrauma
get; get;
private set; private set;
} }
private static bool FullscreenOnTabIn;
public static WindowMode WindowMode public static WindowMode WindowMode
{ {
@@ -231,8 +233,37 @@ namespace Barotrauma
TitleScreen = new LoadingScreen(GraphicsDevice); TitleScreen = new LoadingScreen(GraphicsDevice);
loadingCoroutine = CoroutineManager.StartCoroutine(Load()); loadingCoroutine = CoroutineManager.StartCoroutine(Load());
var myForm = (System.Windows.Forms.Form)System.Windows.Forms.Form.FromHandle(Window.Handle);
myForm.Deactivate += new EventHandler(HandleDefocus);
myForm.Activated += new EventHandler(HandleFocus);
} }
private void HandleDefocus(object sender, EventArgs e)
{
if (GraphicsDeviceManager.IsFullScreen && GraphicsDeviceManager.HardwareModeSwitch)
{
GraphicsDeviceManager.IsFullScreen = false;
GraphicsDeviceManager.ApplyChanges();
FullscreenOnTabIn = true;
Thread.Sleep(500);
}
}
private void HandleFocus(object sender, EventArgs e)
{
if (FullscreenOnTabIn)
{
GraphicsDeviceManager.HardwareModeSwitch = true;
GraphicsDeviceManager.IsFullScreen = true;
GraphicsDeviceManager.ApplyChanges();
FullscreenOnTabIn = false;
Thread.Sleep(500);
}
}
private void InitUserStats() private void InitUserStats()
{ {
if (GameSettings.ShowUserStatisticsPrompt) if (GameSettings.ShowUserStatisticsPrompt)
@@ -244,12 +275,18 @@ namespace Barotrauma
new string[] { "Yes", "No" }); new string[] { "Yes", "No" });
userStatsPrompt.Buttons[0].OnClicked += (btn, userdata) => userStatsPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{ {
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = true; GameSettings.SendUserStatistics = true;
GameAnalyticsManager.Init(); GameAnalyticsManager.Init();
return true; return true;
}; };
userStatsPrompt.Buttons[0].OnClicked += userStatsPrompt.Close; userStatsPrompt.Buttons[0].OnClicked += userStatsPrompt.Close;
userStatsPrompt.Buttons[1].OnClicked += (btn, userdata) => { GameSettings.SendUserStatistics = false; return true; }; userStatsPrompt.Buttons[1].OnClicked += (btn, userdata) =>
{
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = false;
return true;
};
userStatsPrompt.Buttons[1].OnClicked += userStatsPrompt.Close; userStatsPrompt.Buttons[1].OnClicked += userStatsPrompt.Close;
} }
else if (GameSettings.SendUserStatistics) else if (GameSettings.SendUserStatistics)
@@ -92,9 +92,10 @@ namespace Barotrauma
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex); msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.Write((UInt16)CargoManager.PurchasedItems.Count); msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (ItemPrefab ip in CargoManager.PurchasedItems) foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{ {
msg.Write((UInt16)MapEntityPrefab.List.IndexOf(ip)); msg.Write((UInt16)MapEntityPrefab.List.IndexOf(pi.itemPrefab));
msg.Write((UInt16)pi.quantity);
} }
} }
@@ -111,11 +112,12 @@ namespace Barotrauma
int money = msg.ReadInt32(); int money = msg.ReadInt32();
UInt16 purchasedItemCount = msg.ReadUInt16(); UInt16 purchasedItemCount = msg.ReadUInt16();
List<ItemPrefab> purchasedItems = new List<ItemPrefab>(); List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++) for (int i = 0; i < purchasedItemCount; i++)
{ {
UInt16 itemPrefabIndex = msg.ReadUInt16(); UInt16 itemPrefabIndex = msg.ReadUInt16();
purchasedItems.Add(MapEntityPrefab.List[itemPrefabIndex] as ItemPrefab); UInt16 itemQuantity = msg.ReadUInt16();
purchasedItems.Add(new PurchasedItem(MapEntityPrefab.List[itemPrefabIndex] as ItemPrefab, itemQuantity));
} }
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
@@ -269,7 +269,7 @@ namespace Barotrauma.Tutorials
} }
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("The green rectangle in the middle is the submarine, and the flickering shapes outside it are the walls of an underwater cavern. " infoBox = CreateInfoFrame("The blue rectangle in the middle is the submarine, and the flickering shapes outside it are the walls of an underwater cavern. "
+ "Try moving the submarine by clicking somewhere on the monitor and dragging the pointer to the direction you want to go to."); + "Try moving the submarine by clicking somewhere on the monitor and dragging the pointer to the direction you want to go to.");
while (steering.TargetVelocity == Vector2.Zero && steering.TargetVelocity.Length() < 50.0f) while (steering.TargetVelocity == Vector2.Zero && steering.TargetVelocity.Length() < 50.0f)
@@ -224,21 +224,27 @@ namespace Barotrauma.Items.Components
} }
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero. //Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime) protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync = false)
{ {
if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime)); delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
} }
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime) private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync)
{ {
while (correctionTimer > 0.0f) while (GameMain.Client != null &&
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))
{ {
correctionTimer -= CoroutineManager.DeltaTime; correctionTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
if (item.Removed || GameMain.Client == null)
{
yield return CoroutineStatus.Success;
}
((IServerSerializable)this).ClientRead(type, buffer, sendingTime); ((IServerSerializable)this).ClientRead(type, buffer, sendingTime);
correctionTimer = 0.0f; correctionTimer = 0.0f;
@@ -6,9 +6,7 @@ namespace Barotrauma.Items.Components
{ {
public override void DrawHUD(SpriteBatch spriteBatch, Character character) public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{ {
var focusTarget = GetFocusTarget(); if (focusTarget != null && character.ViewTarget == focusTarget)
if (focusTarget == null) return;
if (character.ViewTarget == focusTarget)
{ {
foreach (ItemComponent ic in focusTarget.components) foreach (ItemComponent ic in focusTarget.components)
{ {
@@ -62,7 +62,7 @@ namespace Barotrauma.Items.Components
if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame); if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame);
//int width = 200, height = 150; //int width = 200, height = 150;
selectedItemFrame = new GUIFrame(new Rectangle(0, 0, (int)(GuiFrame.Rect.Width * 0.4f), 300), Color.Black * 0.8f, Alignment.CenterY | Alignment.Right, null, GuiFrame); selectedItemFrame = new GUIFrame(new Rectangle(0, 0, (int)(GuiFrame.Rect.Width * 0.4f), 350), Color.Black * 0.8f, Alignment.CenterY | Alignment.Right, null, GuiFrame);
selectedItemFrame.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); selectedItemFrame.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f);
@@ -106,35 +106,40 @@ namespace Barotrauma.Items.Components
inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level); inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level);
} }
Color textColor = Color.White; string text = TextManager.Get("FabricatorRequiredItems")+ ":\n";
string text; foreach (Tuple<ItemPrefab, int, float, bool> ip in targetItem.RequiredItems)
if (!inadequateSkills.Any())
{ {
text = TextManager.Get("FabricatorRequiredItems")+ ":\n"; text += " - " + ip.Item1.Name + " x" + ip.Item2 + (ip.Item3 < 1.0f ? ", " + ip.Item3 * 100 + "% " + TextManager.Get("FabricatorRequiredCondition") + "\n" : "\n");
foreach (Tuple<ItemPrefab, int, float, bool> ip in targetItem.RequiredItems)
{
text += " - " + ip.Item1.Name + " x" + ip.Item2 + (ip.Item3 < 1.0f ? ", " + ip.Item3 * 100 + "% " + TextManager.Get("FabricatorRequiredCondition") + "\n" : "\n");
}
text += TextManager.Get("FabricatorRequiredTime") + ": " + targetItem.RequiredTime + " s";
} }
else text += TextManager.Get("FabricatorRequiredTime") + ": " + targetItem.RequiredTime + " s\n";
var requiredItemsText = new GUITextBlock(
new Rectangle(0, y, 0, 0),
text,
Color.Transparent, Color.White,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame,
font: GUI.SmallFont);
if (targetItem.RequiredSkills.Any())
{ {
text = TextManager.Get("FabricatorRequiredSkills") + ":\n"; text = TextManager.Get("FabricatorRequiredSkills") + ":\n";
foreach (Skill skill in inadequateSkills) foreach (Skill skill in inadequateSkills)
{ {
text += " - " + skill.Name + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level + "\n"; text += " - " + skill.Name + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level + "\n";
} }
new GUITextBlock(
textColor = Color.Red; new Rectangle(0, y + requiredItemsText.Rect.Height, 0, 0),
text,
Color.Transparent, inadequateSkills.Any() ? Color.Red : Color.White,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame,
font: GUI.SmallFont);
} }
new GUITextBlock(
new Rectangle(0, y, 0, 25),
text,
Color.Transparent, textColor,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame);
activateButton = new GUIButton(new Rectangle(0, -30, 100, 20), TextManager.Get("FabricatorCreate"), Color.White, Alignment.CenterX | Alignment.Bottom, "", selectedItemFrame); activateButton = new GUIButton(new Rectangle(0, -30, 100, 20), TextManager.Get("FabricatorCreate"), Color.White, Alignment.CenterX | Alignment.Bottom, "", selectedItemFrame);
activateButton.OnClicked = StartButtonClicked; activateButton.OnClicked = StartButtonClicked;
@@ -46,14 +46,14 @@ namespace Barotrauma.Items.Components
levelStartTickBox = new GUITickBox( levelStartTickBox = new GUITickBox(
new Rectangle(5, 70, 15, 15), new Rectangle(5, 70, 15, 15),
GameMain.GameSession == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, 20), GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, 20),
Alignment.TopLeft, GUI.SmallFont, GuiFrame); Alignment.TopLeft, GUI.SmallFont, GuiFrame);
levelStartTickBox.Enabled = false; levelStartTickBox.Enabled = false;
levelStartTickBox.OnSelected = SelectDestination; levelStartTickBox.OnSelected = SelectDestination;
levelEndTickBox = new GUITickBox( levelEndTickBox = new GUITickBox(
new Rectangle(5, 90, 15, 15), new Rectangle(5, 90, 15, 15),
GameMain.GameSession == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, 20), GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, 20),
Alignment.TopLeft, GUI.SmallFont, GuiFrame); Alignment.TopLeft, GUI.SmallFont, GuiFrame);
levelEndTickBox.Enabled = false; levelEndTickBox.Enabled = false;
levelEndTickBox.OnSelected = SelectDestination; levelEndTickBox.OnSelected = SelectDestination;
@@ -34,16 +34,19 @@ namespace Barotrauma.Items.Components
Wire equippedWire = null; Wire equippedWire = null;
//if the Character using the panel has a wire item equipped if (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)
//and the wire hasn't been connected yet, draw it on the panel
for (int i = 0; i < character.SelectedItems.Length; i++)
{ {
Item selectedItem = character.SelectedItems[i]; //if the Character using the panel has a wire item equipped
//and the wire hasn't been connected yet, draw it on the panel
for (int i = 0; i < character.SelectedItems.Length; i++)
{
Item selectedItem = character.SelectedItems[i];
if (selectedItem == null) continue; if (selectedItem == null) continue;
Wire wireComponent = selectedItem.GetComponent<Wire>(); Wire wireComponent = selectedItem.GetComponent<Wire>();
if (wireComponent != null) equippedWire = wireComponent; if (wireComponent != null) equippedWire = wireComponent;
}
} }
Vector2 rightPos = new Vector2(x + width - 130, y + 50); Vector2 rightPos = new Vector2(x + width - 130, y + 50);
@@ -71,7 +74,7 @@ namespace Barotrauma.Items.Components
//outputs are drawn at the right side of the panel, inputs at the left //outputs are drawn at the right side of the panel, inputs at the left
if (c.IsOutput) if (c.IsOutput)
{ {
c.Draw(spriteBatch, panel.Item, rightPos, c.Draw(spriteBatch, panel, rightPos,
new Vector2(rightPos.X - GUI.SmallFont.MeasureString(c.Name).X - 20, rightPos.Y + 3), new Vector2(rightPos.X - GUI.SmallFont.MeasureString(c.Name).X - 20, rightPos.Y + 3),
rightWirePos, rightWirePos,
mouseInRect, equippedWire, mouseInRect, equippedWire,
@@ -82,7 +85,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
c.Draw(spriteBatch, panel.Item, leftPos, c.Draw(spriteBatch, panel, leftPos,
new Vector2(leftPos.X + 20, leftPos.Y - 12), new Vector2(leftPos.X + 20, leftPos.Y - 12),
leftWirePos, leftWirePos,
mouseInRect, equippedWire, mouseInRect, equippedWire,
@@ -96,7 +99,7 @@ namespace Barotrauma.Items.Components
if (draggingConnected != null) if (draggingConnected != null)
{ {
DrawWire(spriteBatch, draggingConnected, draggingConnected.Item, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height), mouseInRect, null); DrawWire(spriteBatch, draggingConnected, draggingConnected.Item, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height), mouseInRect, null, panel);
if (!PlayerInput.LeftButtonHeld()) if (!PlayerInput.LeftButtonHeld())
{ {
@@ -121,7 +124,7 @@ namespace Barotrauma.Items.Components
{ {
DrawWire(spriteBatch, equippedWire, equippedWire.Item, DrawWire(spriteBatch, equippedWire, equippedWire.Item,
new Vector2(x + width / 2, y + height - 100), new Vector2(x + width / 2, y + height - 100),
new Vector2(x + width / 2, y + height), mouseInRect, null); new Vector2(x + width / 2, y + height), mouseInRect, null, panel);
if (draggingConnected == equippedWire) Inventory.draggingItem = equippedWire.Item; if (draggingConnected == equippedWire) Inventory.draggingItem = equippedWire.Item;
} }
@@ -135,7 +138,7 @@ namespace Barotrauma.Items.Components
} }
private void Draw(SpriteBatch spriteBatch, Item item, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval) private void Draw(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
{ {
//spriteBatch.DrawString(GUI.SmallFont, Name, new Vector2(labelPos.X, labelPos.Y-10), Color.White); //spriteBatch.DrawString(GUI.SmallFont, Name, new Vector2(labelPos.X, labelPos.Y-10), Color.White);
GUI.DrawString(spriteBatch, labelPos, Name, IsPower ? Color.Red : Color.White, Color.Black, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, labelPos, Name, IsPower ? Color.Red : Color.White, Color.Black, 0, GUI.SmallFont);
@@ -149,7 +152,7 @@ namespace Barotrauma.Items.Components
Connection recipient = Wires[i].OtherConnection(this); Connection recipient = Wires[i].OtherConnection(this);
DrawWire(spriteBatch, Wires[i], (recipient == null) ? Wires[i].Item : recipient.item, position, wirePosition, mouseIn, equippedWire); DrawWire(spriteBatch, Wires[i], (recipient == null) ? Wires[i].Item : recipient.item, position, wirePosition, mouseIn, equippedWire, panel);
wirePosition.Y += wireInterval; wirePosition.Y += wireInterval;
} }
@@ -165,9 +168,9 @@ namespace Barotrauma.Items.Components
if (index > -1 && !Wires.Contains(draggingConnected)) if (index > -1 && !Wires.Contains(draggingConnected))
{ {
bool alreadyConnected = draggingConnected.IsConnectedTo(item); bool alreadyConnected = draggingConnected.IsConnectedTo(panel.Item);
draggingConnected.RemoveConnection(item); draggingConnected.RemoveConnection(panel.Item);
if (draggingConnected.Connect(this, !alreadyConnected, true)) if (draggingConnected.Connect(this, !alreadyConnected, true))
{ {
@@ -198,7 +201,7 @@ namespace Barotrauma.Items.Components
} }
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Item item, Vector2 end, Vector2 start, bool mouseIn, Wire equippedWire) private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Item item, Vector2 end, Vector2 start, bool mouseIn, Wire equippedWire, ConnectionPanel panel)
{ {
if (draggingConnected == wire) if (draggingConnected == wire)
{ {
@@ -225,7 +228,7 @@ namespace Barotrauma.Items.Components
Vector2.Distance(end, PlayerInput.MousePosition) < 20.0f || Vector2.Distance(end, PlayerInput.MousePosition) < 20.0f ||
new Rectangle((start.X < end.X) ? textX - 100 : textX, (int)start.Y - 5, 100, 14).Contains(PlayerInput.MousePosition)); new Rectangle((start.X < end.X) ? textX - 100 : textX, (int)start.Y - 5, 100, 14).Contains(PlayerInput.MousePosition));
string label = wire.Locked ? item.Name + "\n" + TextManager.Get("ConnectionLocked") : item.Name; string label = wire.Locked || panel.Locked ? item.Name + "\n" + TextManager.Get("ConnectionLocked") : item.Name;
GUI.DrawString(spriteBatch, GUI.DrawString(spriteBatch,
new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f), new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f),
@@ -261,7 +264,7 @@ namespace Barotrauma.Items.Components
{ {
ConnectionPanel.HighlightedWire = wire; ConnectionPanel.HighlightedWire = wire;
if (!wire.Locked) if (!wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//start dragging the wire //start dragging the wire
if (PlayerInput.LeftButtonHeld()) draggingConnected = wire; if (PlayerInput.LeftButtonHeld()) draggingConnected = wire;
@@ -1,5 +1,9 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -24,5 +28,66 @@ namespace Barotrauma.Items.Components
HighlightedWire = null; HighlightedWire = null;
Connection.DrawConnections(spriteBatch, this, character); Connection.DrawConnections(spriteBatch, this, character);
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
if (GameMain.Client.MidRoundSyncing)
{
//delay reading the state until midround syncing is done
//because some of the wires connected to the panel may not exist yet
int bitsToRead = Connections.Count * Connection.MaxLinked * 16;
StartDelayedCorrection(type, msg.ExtractBits(bitsToRead), sendingTime, waitForMidRoundSync: true);
}
else
{
ApplyRemoteState(msg);
}
}
private void ApplyRemoteState(NetBuffer msg)
{
List<Wire> prevWires = Connections.SelectMany(c => Array.FindAll(c.Wires, w => w != null)).ToList();
List<Wire> newWires = new List<Wire>();
foreach (Connection connection in Connections)
{
connection.ClearConnections();
}
foreach (Connection connection in Connections)
{
for (int i = 0; i < Connection.MaxLinked; i++)
{
ushort wireId = msg.ReadUInt16();
Item wireItem = Entity.FindEntityByID(wireId) as Item;
if (wireItem == null) continue;
Wire wireComponent = wireItem.GetComponent<Wire>();
if (wireComponent == null) continue;
newWires.Add(wireComponent);
connection.Wires[i] = wireComponent;
wireComponent.Connect(connection, false);
}
}
foreach (Wire wire in prevWires)
{
if (wire.Connections[0] == null && wire.Connections[1] == null)
{
wire.Item.Drop(null);
}
//wires that are not in anyone's inventory (i.e. not currently being rewired) can never be connected to only one connection
// -> someone must have dropped the wire from the connection panel
else if (wire.Item.ParentInventory == null &&
(wire.Connections[0] != null ^ wire.Connections[1] != null))
{
wire.Item.Drop(null);
}
}
}
} }
} }
@@ -52,9 +52,16 @@ namespace Barotrauma.Items.Components
} }
Vector2 drawOffset = Vector2.Zero; Vector2 drawOffset = Vector2.Zero;
if (item.Submarine != null) Submarine sub = item.Submarine;
if (IsActive && sub == null) // currently being rewired, we need to get the sub from the connections in case the wire has been taken outside
{ {
drawOffset = item.Submarine.DrawPosition + item.Submarine.HiddenSubPosition; if (connections[0] != null && connections[0].Item.Submarine != null) sub = connections[0].Item.Submarine;
if (connections[1] != null && connections[1].Item.Submarine != null) sub = connections[1].Item.Submarine;
}
if (sub != null)
{
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
} }
float depth = item.IsSelected ? 0.0f : wireSprite.Depth + ((item.ID % 100) * 0.00001f); float depth = item.IsSelected ? 0.0f : wireSprite.Depth + ((item.ID % 100) * 0.00001f);
@@ -76,7 +83,7 @@ namespace Barotrauma.Items.Components
foreach (WireSection section in sections) foreach (WireSection section in sections)
{ {
section.Draw(spriteBatch, item.Submarine == null ? Color.Green : item.Color, drawOffset, depth, 0.3f); section.Draw(spriteBatch, item.Color, drawOffset, depth, 0.3f);
} }
if (IsActive && nodes.Count > 0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) if (IsActive && nodes.Count > 0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
@@ -148,8 +148,10 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{ {
UInt16 projectileID = msg.ReadUInt16(); UInt16 projectileID = msg.ReadUInt16();
Item projectile = Entity.FindEntityByID(projectileID) as Item; //projectile removed, do nothing
if (projectileID == 0) return;
Item projectile = Entity.FindEntityByID(projectileID) as Item;
if (projectile == null) if (projectile == null)
{ {
DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found"); DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found");
@@ -1,4 +1,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -58,6 +61,43 @@ namespace Barotrauma.Items.Components
rect.Width, (int)(rect.Height / 2 * dockingState)), Color.White); rect.Width, (int)(rect.Height / 2 * dockingState)), Color.White);
} }
} }
if (!GameMain.DebugDraw) return;
if (bodies != null)
{
for (int i = 0; i < bodies.Length; i++)
{
var body = bodies[i];
if (body == null) continue;
body.FixtureList[0].GetAABB(out AABB aabb, 0);
Vector2 bodyDrawPos = ConvertUnits.ToDisplayUnits(new Vector2(aabb.LowerBound.X, aabb.UpperBound.Y));
if ((i == 1 || i == 3) && dockingTarget?.item?.Submarine != null) bodyDrawPos += dockingTarget.item.Submarine.Position;
if ((i == 0 || i == 2) && item.Submarine != null) bodyDrawPos += item.Submarine.Position;
bodyDrawPos.Y = -bodyDrawPos.Y;
GUI.DrawRectangle(spriteBatch,
bodyDrawPos,
ConvertUnits.ToDisplayUnits(aabb.Extents * 2),
Color.Gray, false, 0.0f, 4);
}
}
if (doorBody != null && doorBody.Enabled)
{
doorBody.FixtureList[0].GetAABB(out AABB aabb, 0);
Vector2 bodyDrawPos = ConvertUnits.ToDisplayUnits(new Vector2(aabb.LowerBound.X, aabb.UpperBound.Y));
if (item?.Submarine != null) bodyDrawPos += item.Submarine.Position;
bodyDrawPos.Y = -bodyDrawPos.Y;
GUI.DrawRectangle(spriteBatch,
bodyDrawPos,
ConvertUnits.ToDisplayUnits(aabb.Extents * 2),
Color.Gray, false, 0, 8);
}
} }
} }
@@ -1,4 +1,6 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -464,5 +466,75 @@ namespace Barotrauma
item.Sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), item.GetSpriteColor()); item.Sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), item.GetSpriteColor());
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
receivedItemIDs = new ushort[capacity];
for (int i = 0; i < capacity; i++)
{
receivedItemIDs[i] = msg.ReadUInt16();
}
//delay applying the new state if less than 1 second has passed since this client last sent a state to the server
//prevents the inventory from briefly reverting to an old state if items are moved around in quick succession
//also delay if we're still midround syncing, some of the items in the inventory may not exist yet
if (syncItemsDelay > 0.0f || GameMain.Client.MidRoundSyncing)
{
if (syncItemsCoroutine != null) CoroutineManager.StopCoroutines(syncItemsCoroutine);
syncItemsCoroutine = CoroutineManager.StartCoroutine(SyncItemsAfterDelay());
}
else
{
if (syncItemsCoroutine != null)
{
CoroutineManager.StopCoroutines(syncItemsCoroutine);
syncItemsCoroutine = null;
}
ApplyReceivedState();
}
}
private IEnumerable<object> SyncItemsAfterDelay()
{
while (syncItemsDelay > 0.0f || (GameMain.Client != null && GameMain.Client.MidRoundSyncing))
{
yield return CoroutineStatus.Running;
}
if (Owner.Removed || GameMain.Client == null)
{
yield return CoroutineStatus.Success;
}
ApplyReceivedState();
yield return CoroutineStatus.Success;
}
private void ApplyReceivedState()
{
for (int i = 0; i < capacity; i++)
{
if (receivedItemIDs[i] == 0 || (Entity.FindEntityByID(receivedItemIDs[i]) as Item != Items[i]))
{
if (Items[i] != null) Items[i].Drop();
System.Diagnostics.Debug.Assert(Items[i] == null);
}
}
for (int i = 0; i < capacity; i++)
{
if (receivedItemIDs[i] > 0)
{
var item = Entity.FindEntityByID(receivedItemIDs[i]) as Item;
if (item == null || item == Items[i]) continue;
TryPutItem(item, i, true, true, null, false);
}
}
receivedItemIDs = null;
}
} }
} }
@@ -377,11 +377,11 @@ namespace Barotrauma
(components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime); (components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime);
break; break;
case NetEntityEvent.Type.InventoryState: case NetEntityEvent.Type.InventoryState:
ownInventory.ClientRead(type, msg, sendingTime); int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
(components[containerIndex] as ItemContainer).Inventory.ClientRead(type, msg, sendingTime);
break; break;
case NetEntityEvent.Type.Status: case NetEntityEvent.Type.Status:
condition = msg.ReadRangedSingle(0.0f, prefab.Health, 8); condition = msg.ReadSingle();
if (FixRequirements.Count > 0) if (FixRequirements.Count > 0)
{ {
if (Condition <= 0.0f) if (Condition <= 0.0f)
@@ -401,10 +401,13 @@ namespace Barotrauma
ushort targetID = msg.ReadUInt16(); ushort targetID = msg.ReadUInt16();
Character target = FindEntityByID(targetID) as Character; Character target = FindEntityByID(targetID) as Character;
ApplyStatusEffects(actionType, (float)Timing.Step, target, true); //ignore deltatime - using an item with the useOnSelf buttons is instantaneous
ApplyStatusEffects(actionType, 1.0f, target, true);
break; break;
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg); ReadPropertyChange(msg, false);
break;
case NetEntityEvent.Type.Invalid:
break; break;
} }
} }
@@ -423,11 +426,12 @@ namespace Barotrauma
case NetEntityEvent.Type.ComponentState: case NetEntityEvent.Type.ComponentState:
int componentIndex = (int)extraData[1]; int componentIndex = (int)extraData[1];
msg.WriteRangedInteger(0, components.Count - 1, componentIndex); msg.WriteRangedInteger(0, components.Count - 1, componentIndex);
(components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData); (components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData);
break; break;
case NetEntityEvent.Type.InventoryState: case NetEntityEvent.Type.InventoryState:
ownInventory.ClientWrite(msg, extraData); int containerIndex = (int)extraData[1];
msg.WriteRangedInteger(0, components.Count - 1, containerIndex);
(components[containerIndex] as ItemContainer).Inventory.ClientWrite(msg, extraData);
break; break;
case NetEntityEvent.Type.Repair: case NetEntityEvent.Type.Repair:
if (FixRequirements.Count > 0) if (FixRequirements.Count > 0)
@@ -441,7 +445,7 @@ namespace Barotrauma
//on the character of the client who sent the message //on the character of the client who sent the message
break; break;
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData); WritePropertyChange(msg, extraData, true);
break; break;
} }
msg.WritePadBits(); msg.WritePadBits();
@@ -461,6 +465,19 @@ namespace Barotrauma
msg.ReadRangedSingle(-MaxVel, MaxVel, 12)); msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
} }
if (!MathUtils.IsValid(newPosition) || !MathUtils.IsValid(newRotation) || !MathUtils.IsValid(newVelocity))
{
string errorMsg = "Received invalid position data for the item \"" + Name
+ "\" (position: " + newPosition + ", rotation: " + newRotation + ", velocity: " + newVelocity + ")";
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif
GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:InvalidData" + ID,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
return;
}
if (body == null) if (body == null)
{ {
DebugConsole.ThrowError("Received a position update for an item with no physics body (" + Name + ")"); DebugConsole.ThrowError("Received a position update for an item with no physics body (" + Name + ")");
@@ -470,7 +487,7 @@ namespace Barotrauma
body.FarseerBody.Awake = awake; body.FarseerBody.Awake = awake;
if (body.FarseerBody.Awake) if (body.FarseerBody.Awake)
{ {
if ((newVelocity - body.LinearVelocity).Length() > 8.0f) body.LinearVelocity = newVelocity; if ((newVelocity - body.LinearVelocity).LengthSquared() > 8.0f * 8.0f) body.LinearVelocity = newVelocity;
} }
else else
{ {
@@ -490,11 +507,12 @@ namespace Barotrauma
if ((newPosition - SimPosition).Length() > body.LinearVelocity.Length() * 2.0f) if ((newPosition - SimPosition).Length() > body.LinearVelocity.Length() * 2.0f)
{ {
body.SetTransform(newPosition, newRotation); if (body.SetTransform(newPosition, newRotation))
{
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f); rect.X = (int)(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
}
} }
} }
@@ -27,7 +27,7 @@ namespace Barotrauma
return verticeList; return verticeList;
} }
public static VertexPositionTexture[] GenerateWallShapes(List<VoronoiCell> cells) public static VertexPositionTexture[] GenerateWallShapes(List<VoronoiCell> cells, Level level)
{ {
float inwardThickness = 500.0f, outWardThickness = 30.0f; float inwardThickness = 500.0f, outWardThickness = 30.0f;
@@ -80,6 +80,10 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Invalid left normal"); DebugConsole.ThrowError("Invalid left normal");
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
"Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");
if (cell.body != null) if (cell.body != null)
{ {
GameMain.World.RemoveBody(cell.body); GameMain.World.RemoveBody(cell.body);
@@ -106,6 +110,10 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Invalid right normal"); DebugConsole.ThrowError("Invalid right normal");
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
"Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");
if (cell.body != null) if (cell.body != null)
{ {
GameMain.World.RemoveBody(cell.body); GameMain.World.RemoveBody(cell.body);
@@ -28,9 +28,16 @@ namespace Barotrauma
color = Color.LightGray; color = Color.LightGray;
} }
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true); GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
} }
foreach (RuinGeneration.Ruin ruin in ruins)
{
Rectangle ruinArea = ruin.Area;
ruinArea.Y = -ruinArea.Y - ruinArea.Height;
GUI.DrawRectangle(spriteBatch, ruinArea, Color.DarkSlateBlue, false, 0, 5);
}
} }
} }
@@ -1,5 +1,6 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using FarseerPhysics; using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -145,7 +146,7 @@ namespace Barotrauma
"", frame, GUI.SmallFont); "", frame, GUI.SmallFont);
new GUITextBlock(new Rectangle(246, 100, 100, 20), new GUITextBlock(new Rectangle(246, 100, 100, 20),
TextManager.Get("RecommendedCrewExperience") + ": " + (string.IsNullOrEmpty(RecommendedCrewExperience) ? TextManager.Get("unknown") : RecommendedCrewExperience), TextManager.Get("RecommendedCrewExperience") + ": " + (string.IsNullOrEmpty(RecommendedCrewExperience) ? TextManager.Get("unknown") : TextManager.Get(RecommendedCrewExperience)),
"", frame, GUI.SmallFont); "", frame, GUI.SmallFont);
new GUITextBlock(new Rectangle(246, 120, 0, 20), new GUITextBlock(new Rectangle(246, 120, 0, 20),
@@ -179,7 +180,7 @@ namespace Barotrauma
} }
} }
if (WayPoint.WayPointList.Find(wp => !wp.MoveWithLevel && wp.SpawnType == SpawnType.Path) == null) if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{ {
errorMsgs.Add(TextManager.Get("NoWaypointsWarning")); errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
} }
@@ -217,5 +218,32 @@ namespace Barotrauma
} }
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
var newTargetPosition = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
//already interpolating with more up-to-date data -> ignore
if (subBody.MemPos.Count > 1 && subBody.MemPos[0].Timestamp > sendingTime)
{
return;
}
int index = 0;
while (index < subBody.MemPos.Count && sendingTime > subBody.MemPos[index].Timestamp)
{
index++;
}
//position with the same timestamp already in the buffer (duplicate packet?)
// -> no need to add again
if (index < subBody.MemPos.Count && sendingTime == subBody.MemPos[index].Timestamp)
{
return;
}
subBody.MemPos.Insert(index, new PosInfo(newTargetPosition, 0.0f, sendingTime));
}
} }
} }
@@ -32,18 +32,26 @@ namespace Barotrauma.Networking
if (NetIdUtils.IdMoreRecent(ID, LastID)) if (NetIdUtils.IdMoreRecent(ID, LastID))
{ {
if (type == ChatMessageType.MessageBox) switch (type)
{ {
new GUIMessageBox("", txt); case ChatMessageType.MessageBox:
} new GUIMessageBox("", txt);
else if (type == ChatMessageType.Console) break;
{ case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]); DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
} break;
else case ChatMessageType.ServerLog:
{ if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter); {
return;
}
GameMain.Client.ServerLog?.WriteLine(txt, messageType);
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
break;
} }
LastID = ID; LastID = ID;
} }
} }
@@ -44,6 +44,9 @@ namespace Barotrauma.Networking
private FileReceiver fileReceiver; private FileReceiver fileReceiver;
//has the client been given a character to control this round
public bool HasSpawned;
public byte ID public byte ID
{ {
get { return myID; } get { return myID; }
@@ -62,6 +65,17 @@ namespace Barotrauma.Networking
get { return fileReceiver; } get { return fileReceiver; }
} }
public bool MidRoundSyncing
{
get { return entityEventManager.MidRoundSyncing; }
}
public bool AllowDisguises
{
get;
private set;
}
public GameClient(string newName) public GameClient(string newName)
{ {
endVoteTickBox = new GUITickBox(new Rectangle(GameMain.GraphicsWidth - 170, 20, 20, 20), "End round", Alignment.TopLeft, inGameHUD); endVoteTickBox = new GUITickBox(new Rectangle(GameMain.GraphicsWidth - 170, 20, 20, 20), "End round", Alignment.TopLeft, inGameHUD);
@@ -99,6 +113,8 @@ namespace Barotrauma.Networking
otherClients = new List<Client>(); otherClients = new List<Client>();
ServerLog = new ServerLog("");
ChatMessage.LastID = 0; ChatMessage.LastID = 0;
GameMain.NetLobbyScreen = new NetLobbyScreen(); GameMain.NetLobbyScreen = new NetLobbyScreen();
} }
@@ -456,6 +472,9 @@ namespace Barotrauma.Networking
#if DEBUG #if DEBUG
if (PlayerInput.GetKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P)) return; if (PlayerInput.GetKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P)) return;
#endif #endif
#if CLIENT
if (ServerLog.LogFrame != null) ServerLog.LogFrame.Update(deltaTime);
#endif
base.Update(deltaTime); base.Update(deltaTime);
@@ -480,7 +499,7 @@ namespace Barotrauma.Networking
if (gameStarted && Screen.Selected == GameMain.GameScreen) if (gameStarted && Screen.Selected == GameMain.GameScreen)
{ {
endVoteTickBox.Visible = Voting.AllowEndVoting && myCharacter != null; endVoteTickBox.Visible = Voting.AllowEndVoting && HasSpawned;
if (respawnManager != null) if (respawnManager != null)
{ {
@@ -659,6 +678,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SubList.Enabled = Voting.AllowSubVoting || HasPermission(ClientPermissions.SelectSub); GameMain.NetLobbyScreen.SubList.Enabled = Voting.AllowSubVoting || HasPermission(ClientPermissions.SelectSub);
GameMain.NetLobbyScreen.ModeList.Enabled = Voting.AllowModeVoting || HasPermission(ClientPermissions.SelectMode); GameMain.NetLobbyScreen.ModeList.Enabled = Voting.AllowModeVoting || HasPermission(ClientPermissions.SelectMode);
GameMain.NetLobbyScreen.InfoFrame.FindChild("showlog").Visible = HasPermission(ClientPermissions.ServerLog);
showLogButton.Visible = HasPermission(ClientPermissions.ServerLog);
endRoundButton.Visible = HasPermission(ClientPermissions.EndRound); endRoundButton.Visible = HasPermission(ClientPermissions.EndRound);
} }
@@ -666,6 +687,7 @@ namespace Barotrauma.Networking
private IEnumerable<object> StartGame(NetIncomingMessage inc) private IEnumerable<object> StartGame(NetIncomingMessage inc)
{ {
if (Character != null) Character.Remove(); if (Character != null) Character.Remove();
HasSpawned = false;
GameMain.LightManager.LightingEnabled = true; GameMain.LightManager.LightingEnabled = true;
@@ -696,6 +718,7 @@ namespace Barotrauma.Networking
bool respawnAllowed = inc.ReadBoolean(); bool respawnAllowed = inc.ReadBoolean();
bool loadSecondSub = inc.ReadBoolean(); bool loadSecondSub = inc.ReadBoolean();
bool disguisesAllowed = inc.ReadBoolean();
bool isTraitor = inc.ReadBoolean(); bool isTraitor = inc.ReadBoolean();
string traitorTargetName = isTraitor ? inc.ReadString() : null; string traitorTargetName = isTraitor ? inc.ReadString() : null;
@@ -734,6 +757,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.UsingShuttle = usingShuttle; GameMain.NetLobbyScreen.UsingShuttle = usingShuttle;
AllowDisguises = disguisesAllowed;
if (campaign == null) if (campaign == null)
{ {
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList)) if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
@@ -757,7 +782,10 @@ namespace Barotrauma.Networking
else else
{ {
if (GameMain.GameSession?.CrewManager != null) GameMain.GameSession.CrewManager.Reset(); if (GameMain.GameSession?.CrewManager != null) GameMain.GameSession.CrewManager.Reset();
GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level, true, false); GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level,
reloadSub: true,
loadSecondSub: false,
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
} }
if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null);
@@ -815,7 +843,7 @@ namespace Barotrauma.Networking
string subName = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString(); string subHash = inc.ReadString();
var matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == subName && s.MD5Hash.Hash == subHash); var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
if (matchingSub != null) if (matchingSub != null)
{ {
submarines.Add(matchingSub); submarines.Add(matchingSub);
@@ -908,6 +936,8 @@ namespace Barotrauma.Networking
{ {
GameMain.NetLobbyScreen.LastUpdateID = updateID; GameMain.NetLobbyScreen.LastUpdateID = updateID;
ServerLog.ServerName = serverName;
GameMain.NetLobbyScreen.ServerName = serverName; GameMain.NetLobbyScreen.ServerName = serverName;
GameMain.NetLobbyScreen.ServerMessage.Text = serverText; GameMain.NetLobbyScreen.ServerMessage.Text = serverText;
@@ -982,6 +1012,9 @@ namespace Barotrauma.Networking
long prevBitPos = 0; long prevBitPos = 0;
long prevBytePos = 0; long prevBytePos = 0;
long prevBitLength = 0;
long prevByteLength = 0;
ServerNetObject objHeader; ServerNetObject objHeader;
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE) while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
{ {
@@ -1016,41 +1049,53 @@ namespace Barotrauma.Networking
ChatMessage.ClientRead(inc); ChatMessage.ClientRead(inc);
break; break;
default: default:
DebugConsole.ThrowError("Error while reading update from server (unknown object header \""+objHeader+"\"!)"); List<string> errorLines = new List<string>
if (prevObjHeader != null)
{ {
DebugConsole.ThrowError("Previous object type: " + prevObjHeader.ToString()); "Error while reading update from server (unknown object header \"" + objHeader + "\"!)",
} "Message length: " + inc.LengthBits + " (" + inc.LengthBytes + " bytes)",
else prevObjHeader != null ? "Previous object type: " + prevObjHeader.ToString() : "Error occurred on the very first object!",
{ "Previous object was " + (prevBitLength) + " bits long (" + (prevByteLength) + " bytes)"
DebugConsole.ThrowError("Error occurred on the very first object!"); };
}
DebugConsole.ThrowError("Previous object was " + (inc.Position - prevBitPos) + " bits long (" + (inc.PositionInBytes - prevBytePos) + " bytes)");
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL) if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL)
{ {
foreach (IServerSerializable ent in entities) foreach (IServerSerializable ent in entities)
{ {
if (ent == null) if (ent == null)
{ {
DebugConsole.ThrowError(" - NULL"); errorLines.Add(" - NULL");
continue; continue;
} }
Entity e = ent as Entity; Entity e = ent as Entity;
DebugConsole.ThrowError(" - "+e.ToString()); errorLines.Add(" - " + e.ToString());
} }
} }
foreach (string line in errorLines)
{
DebugConsole.ThrowError(line);
}
errorLines.Add("Last console messages:");
for (int i = DebugConsole.Messages.Count - 1; i > Math.Max(0, DebugConsole.Messages.Count - 20); i--)
{
errorLines.Add("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
}
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, string.Join("\n", errorLines));
DebugConsole.ThrowError("Writing object data to \"crashreport_object.bin\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues"); DebugConsole.ThrowError("Writing object data to \"crashreport_object.bin\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
FileStream fl = File.Open("crashreport_object.bin", FileMode.Create); FileStream fl = File.Open("crashreport_object.bin", FileMode.Create);
BinaryWriter sw = new BinaryWriter(fl); BinaryWriter sw = new BinaryWriter(fl);
sw.Write(inc.Data, (int)prevBytePos, (int)(inc.LengthBytes - prevBytePos)); sw.Write(inc.Data, (int)(prevBytePos - prevByteLength), (int)(prevByteLength));
sw.Close(); sw.Close();
fl.Close(); fl.Close();
throw new Exception("Error while reading update from server: please send us \"crashreport_object.bin\"!"); throw new Exception("Error while reading update from server: please send us \"crashreport_object.bin\"!");
} }
prevBitLength = inc.Position - prevBitPos;
prevByteLength = inc.PositionInBytes - prevByteLength;
prevObjHeader = objHeader; prevObjHeader = objHeader;
prevBitPos = inc.Position; prevBitPos = inc.Position;
prevBytePos = inc.PositionInBytes; prevBytePos = inc.PositionInBytes;
@@ -1181,8 +1226,12 @@ namespace Barotrauma.Networking
case FileTransferType.Submarine: case FileTransferType.Submarine:
new GUIMessageBox("Download finished", "File \"" + transfer.FileName + "\" was downloaded succesfully."); new GUIMessageBox("Download finished", "File \"" + transfer.FileName + "\" was downloaded succesfully.");
var newSub = new Submarine(transfer.FilePath); var newSub = new Submarine(transfer.FilePath);
Submarine.SavedSubmarines.RemoveAll(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash); var existingSubs = Submarine.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash).ToList();
Submarine.SavedSubmarines.Add(newSub); foreach (Submarine existingSub in existingSubs)
{
existingSub.Dispose();
}
Submarine.AddToSavedSubs(newSub);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
@@ -1277,6 +1326,23 @@ namespace Barotrauma.Networking
{ {
base.Draw(spriteBatch); base.Draw(spriteBatch);
if (Screen.Selected == GameMain.GameScreen && !GUI.DisableHUD)
{
if (EndVoteCount > 0)
{
if (!HasSpawned)
{
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 180.0f, 40),
"Votes to end the round (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont);
}
else
{
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 140.0f, 40),
"Votes (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont);
}
}
}
if (fileReceiver != null && fileReceiver.ActiveTransfers.Count > 0) if (fileReceiver != null && fileReceiver.ActiveTransfers.Count > 0)
{ {
Vector2 pos = new Vector2(GameMain.NetLobbyScreen.InfoFrame.Rect.X, GameMain.GraphicsHeight - 35); Vector2 pos = new Vector2(GameMain.NetLobbyScreen.InfoFrame.Rect.X, GameMain.GraphicsHeight - 35);
@@ -1341,6 +1407,17 @@ namespace Barotrauma.Networking
public override void Disconnect() public override void Disconnect()
{ {
client.Shutdown(""); client.Shutdown("");
foreach (var fileTransfer in FileReceiver.ActiveTransfers)
{
fileTransfer.Dispose();
}
if (HasPermission(ClientPermissions.ServerLog))
{
ServerLog?.Save();
}
GameMain.NetworkMember = null; GameMain.NetworkMember = null;
} }
@@ -1560,14 +1637,13 @@ namespace Barotrauma.Networking
{ {
if (!gameStarted) return false; if (!gameStarted) return false;
if (!Voting.AllowEndVoting || myCharacter==null) if (!Voting.AllowEndVoting || !HasSpawned)
{ {
tickBox.Visible = false; tickBox.Visible = false;
return false; return false;
} }
Vote(VoteType.EndRound, tickBox.Selected); Vote(VoteType.EndRound, tickBox.Selected);
return false; return false;
} }
} }
@@ -9,45 +9,22 @@ namespace Barotrauma.Networking
{ {
private NetStats netStats; private NetStats netStats;
private GUIButton showLogButton;
private GUIScrollBar clientListScrollBar; private GUIScrollBar clientListScrollBar;
void InitProjSpecific() void InitProjSpecific()
{ {
//----------------------------------------
var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170, 20, 150, 20), "End round", Alignment.TopLeft, "", inGameHUD); var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170, 20, 150, 20), "End round", Alignment.TopLeft, "", inGameHUD);
endRoundButton.OnClicked = (btn, userdata) => { EndGame(); return true; }; endRoundButton.OnClicked = (btn, userdata) => { EndGame(); return true; };
showLogButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170 - 170, 20, 150, 20), "Server Log", Alignment.TopLeft, "", inGameHUD);
showLogButton.OnClicked = (GUIButton button, object userData) =>
{
if (log.LogFrame == null)
{
log.CreateLogFrame();
}
else
{
log.LogFrame = null;
GUIComponent.KeyboardDispatcher.Subscriber = null;
}
return true;
};
GUIButton settingsButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170 - 170 - 170, 20, 150, 20), "Settings", Alignment.TopLeft, "", inGameHUD); GUIButton settingsButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170 - 170 - 170, 20, 150, 20), "Settings", Alignment.TopLeft, "", inGameHUD);
settingsButton.OnClicked = ToggleSettingsFrame; settingsButton.OnClicked = ToggleSettingsFrame;
settingsButton.UserData = "settingsButton"; settingsButton.UserData = "settingsButton";
//----------------------------------------
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
if (started) base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
if (settingsFrame != null) settingsFrame.AddToGUIUpdateList(); if (settingsFrame != null) settingsFrame.AddToGUIUpdateList();
if (log.LogFrame != null) log.LogFrame.AddToGUIUpdateList();
} }
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
@@ -58,9 +35,18 @@ namespace Barotrauma.Networking
{ {
settingsFrame.Draw(spriteBatch); settingsFrame.Draw(spriteBatch);
} }
else if (log.LogFrame != null) else if (ServerLog.LogFrame != null)
{ {
log.LogFrame.Draw(spriteBatch); ServerLog.LogFrame.Draw(spriteBatch);
}
if (Screen.Selected == GameMain.GameScreen && !GUI.DisableHUD)
{
if (EndVoteCount > 0)
{
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 180.0f, 40),
"Votes to end the round (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont);
}
} }
if (!ShowNetStats) return; if (!ShowNetStats) return;
@@ -23,6 +23,11 @@ namespace Barotrauma.Networking
private UInt16 lastReceivedID; private UInt16 lastReceivedID;
public bool MidRoundSyncing
{
get { return firstNewID.HasValue; }
}
public ClientEntityEventManager(GameClient client) public ClientEntityEventManager(GameClient client)
{ {
events = new List<ClientEntityEvent>(); events = new List<ClientEntityEvent>();
@@ -140,7 +145,13 @@ namespace Barotrauma.Networking
if (entityID == 0) if (entityID == 0)
{ {
if (GameSettings.VerboseLogging)
{
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
Microsoft.Xna.Framework.Color.Orange);
}
msg.ReadPadBits(); msg.ReadPadBits();
entities.Add(null);
if (thisEventID == (UInt16)(lastReceivedID + 1)) lastReceivedID++; if (thisEventID == (UInt16)(lastReceivedID + 1)) lastReceivedID++;
continue; continue;
} }
@@ -153,23 +164,24 @@ namespace Barotrauma.Networking
//skip the event if we've already received it or if the entity isn't found //skip the event if we've already received it or if the entity isn't found
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null) if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
{ {
if (GameSettings.VerboseLogging) if (thisEventID != (UInt16) (lastReceivedID + 1))
{ {
if (thisEventID != (UInt16) (lastReceivedID + 1)) if (GameSettings.VerboseLogging)
{ {
DebugConsole.NewMessage( DebugConsole.NewMessage(
"received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")", "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
thisEventID < lastReceivedID + 1 thisEventID < lastReceivedID + 1
? Microsoft.Xna.Framework.Color.Yellow ? Microsoft.Xna.Framework.Color.Yellow
: Microsoft.Xna.Framework.Color.Red); : Microsoft.Xna.Framework.Color.Red);
} }
else if (entity == null)
{
DebugConsole.NewMessage(
"received msg " + thisEventID + ", entity " + entityID + " not found",
Microsoft.Xna.Framework.Color.Red);
}
} }
else if (entity == null)
{
DebugConsole.NewMessage(
"Received msg " + thisEventID + ", entity " + entityID + " not found",
Microsoft.Xna.Framework.Color.Red);
}
msg.Position += msgLength * 8; msg.Position += msgLength * 8;
} }
else else
@@ -188,10 +200,19 @@ namespace Barotrauma.Networking
catch (Exception e) catch (Exception e)
{ {
string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace;
errorMsg += "\nPrevious entities:";
for (int j = entities.Count - 2; j >= 0; j--)
{
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
}
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
{ {
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e); DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
} }
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
msg.Position = msgPosition + msgLength * 8; msg.Position = msgPosition + msgLength * 8;
} }
} }
@@ -24,6 +24,7 @@ namespace Barotrauma.Networking
protected GUIFrame inGameHUD; protected GUIFrame inGameHUD;
protected GUIListBox chatBox; protected GUIListBox chatBox;
protected GUITextBox chatMsgBox; protected GUITextBox chatMsgBox;
protected GUIButton showLogButton;
public GUIFrame InGameHUD public GUIFrame InGameHUD
{ {
@@ -35,6 +36,23 @@ namespace Barotrauma.Networking
inGameHUD = new GUIFrame(new Rectangle(0, 0, 0, 0), null, null); inGameHUD = new GUIFrame(new Rectangle(0, 0, 0, 0), null, null);
inGameHUD.CanBeFocused = false; inGameHUD.CanBeFocused = false;
showLogButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170 - 170, 20, 150, 20), "Server Log", Alignment.TopLeft, "", inGameHUD)
{
OnClicked = (GUIButton button, object userData) =>
{
if (ServerLog.LogFrame == null)
{
ServerLog.CreateLogFrame();
}
else
{
ServerLog.LogFrame = null;
GUIComponent.KeyboardDispatcher.Subscriber = null;
}
return true;
}
};
int width = (int)MathHelper.Clamp(GameMain.GraphicsWidth * 0.35f, 350, 500); int width = (int)MathHelper.Clamp(GameMain.GraphicsWidth * 0.35f, 350, 500);
int height = (int)MathHelper.Clamp(GameMain.GraphicsHeight * 0.15f, 100, 200); int height = (int)MathHelper.Clamp(GameMain.GraphicsHeight * 0.15f, 100, 200);
chatBox = new GUIListBox(new Rectangle( chatBox = new GUIListBox(new Rectangle(
@@ -109,53 +127,44 @@ namespace Barotrauma.Networking
{ {
inGameHUD.AddToGUIUpdateList(); inGameHUD.AddToGUIUpdateList();
} }
if (ServerLog.LogFrame != null) ServerLog.LogFrame.AddToGUIUpdateList();
} }
public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{ {
if (!gameStarted || Screen.Selected != GameMain.GameScreen || GUI.DisableHUD) return; if (GUI.DisableHUD) return;
GameMain.GameSession.CrewManager.Draw(spriteBatch); if (gameStarted && Screen.Selected == GameMain.GameScreen)
inGameHUD.Draw(spriteBatch);
if (EndVoteCount > 0)
{ {
if (GameMain.NetworkMember.myCharacter == null) GameMain.GameSession.CrewManager.Draw(spriteBatch);
inGameHUD.Draw(spriteBatch);
if (respawnManager != null)
{ {
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 180.0f, 40), string respawnInfo = "";
"Votes to end the round (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont);
} if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
else respawnManager.CountdownStarted)
{ {
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 140.0f, 40), respawnInfo = respawnManager.UsingShuttle ? "Respawn Shuttle dispatching in " : "Respawning players in ";
"Votes (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont); respawnInfo = respawnManager.RespawnTimer <= 0.0f ? "" : respawnInfo + ToolBox.SecondsToReadableTime(respawnManager.RespawnTimer);
}
else if (respawnManager.CurrentState == RespawnManager.State.Transporting)
{
respawnInfo = respawnManager.TransportTimer <= 0.0f ? "" : "Shuttle leaving in " + ToolBox.SecondsToReadableTime(respawnManager.TransportTimer);
}
if (!string.IsNullOrEmpty(respawnInfo))
{
GUI.DrawString(spriteBatch,
new Vector2(120.0f, 10),
respawnInfo, Color.White, null, 0, GUI.SmallFont);
}
} }
} }
if (respawnManager != null) ServerLog.LogFrame?.Draw(spriteBatch);
{
string respawnInfo = "";
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
respawnManager.CountdownStarted)
{
respawnInfo = respawnManager.UsingShuttle ? "Respawn Shuttle dispatching in " : "Respawning players in ";
respawnInfo = respawnManager.RespawnTimer <= 0.0f ? "" : respawnInfo + ToolBox.SecondsToReadableTime(respawnManager.RespawnTimer);
}
else if (respawnManager.CurrentState == RespawnManager.State.Transporting)
{
respawnInfo = respawnManager.TransportTimer <= 0.0f ? "" : "Shuttle leaving in " + ToolBox.SecondsToReadableTime(respawnManager.TransportTimer);
}
if (!string.IsNullOrEmpty(respawnInfo))
{
GUI.DrawString(spriteBatch,
new Vector2(120.0f, 10),
respawnInfo, Color.White, null, 0, GUI.SmallFont);
}
}
} }
public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame) public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame)
@@ -2,6 +2,7 @@
using Lidgren.Network; using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -151,7 +152,12 @@ namespace Barotrauma
{ {
int votes = inc.ReadByte(); int votes = inc.ReadByte();
string subName = inc.ReadString(); string subName = inc.ReadString();
Submarine sub = Submarine.SavedSubmarines.Find(sm => sm.Name == subName); List<Submarine> serversubs = new List<Submarine>();
foreach (GUIComponent item in GameMain.NetLobbyScreen?.SubList?.children)
{
if (item.UserData != null && item.UserData is Submarine) serversubs.Add(item.UserData as Submarine);
}
Submarine sub = serversubs.FirstOrDefault(sm => sm.Name == subName);
SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes); SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes);
} }
} }
@@ -203,7 +203,7 @@ namespace Barotrauma
if (GameSettings.SendUserStatistics) if (GameSettings.SendUserStatistics)
{ {
CrashMessageBox( "A crash report (\"crashreport.log\") was saved in the root folder of the game and sent to the developers."); CrashMessageBox( "A crash report (\"crashreport.log\") was saved in the root folder of the game and sent to the developers.");
GameAnalytics.AddErrorEvent(EGAErrorSeverity.Error, crashReport); GameAnalytics.AddErrorEvent(EGAErrorSeverity.Critical, crashReport);
GameAnalytics.OnStop(); GameAnalytics.OnStop();
} }
else else
@@ -58,6 +58,14 @@ namespace Barotrauma
Submarine selectedSub = subList.SelectedData as Submarine; Submarine selectedSub = subList.SelectedData as Submarine;
if (selectedSub == null) return false; if (selectedSub == null) return false;
if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash))
{
((GUITextBlock)subList.Selected).TextColor = Color.DarkRed * 0.8f;
subList.Selected.CanBeFocused = false;
subList.Deselect();
return false;
}
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text); string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !selectedSub.CompatibleContentPackages.Contains(GameMain.SelectedPackage.Name)) if (selectedSub.HasTag(SubmarineTag.Shuttle) || !selectedSub.CompatibleContentPackages.Contains(GameMain.SelectedPackage.Name))
{ {
@@ -144,7 +152,7 @@ namespace Barotrauma
return true; return true;
}; };
} }
if (Submarine.SavedSubmarines.Count > 0) subList.Select(Submarine.SavedSubmarines[0]); if (Submarine.SavedSubmarines.Any()) subList.Select(Submarine.SavedSubmarines.First());
} }
public void UpdateLoadMenu() public void UpdateLoadMenu()
@@ -85,7 +85,7 @@ namespace Barotrauma
int sellColumnWidth = (tabs[(int)Tab.Store].Rect.Width - 40) / 2 - 20; int sellColumnWidth = (tabs[(int)Tab.Store].Rect.Width - 40) / 2 - 20;
selectedItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, tabs[(int)Tab.Store].Rect.Height - 80), Color.White * 0.7f, "", tabs[(int)Tab.Store]); selectedItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, tabs[(int)Tab.Store].Rect.Height - 80), Color.White * 0.7f, "", tabs[(int)Tab.Store]);
selectedItemList.OnSelected = SellItem; //selectedItemList.OnSelected = SellItem;
storeItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, tabs[(int)Tab.Store].Rect.Height - 80), Color.White * 0.7f, Alignment.TopRight, "", tabs[(int)Tab.Store]); storeItemList = new GUIListBox(new Rectangle(0, 30, sellColumnWidth, tabs[(int)Tab.Store].Rect.Height - 80), Color.White * 0.7f, Alignment.TopRight, "", tabs[(int)Tab.Store]);
storeItemList.OnSelected = BuyItem; storeItemList.OnSelected = BuyItem;
@@ -149,6 +149,8 @@ namespace Barotrauma
null, null, null, null,
Alignment.TopRight, "", frame); Alignment.TopRight, "", frame);
} }
RefreshItemTab();
} }
public void Update(float deltaTime) public void Update(float deltaTime)
@@ -216,55 +218,92 @@ namespace Barotrauma
OnLocationSelected?.Invoke(location, connection); OnLocationSelected?.Invoke(location, connection);
} }
private void CreateItemFrame(MapEntityPrefab ep, GUIListBox listBox, int width) private void CreateItemFrame(PurchasedItem pi, GUIListBox listBox, int width)
{ {
GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), "ListBoxElement", listBox); GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), "ListBoxElement", listBox);
frame.UserData = ep; frame.UserData = pi;
frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
frame.ToolTip = ep.Description; frame.ToolTip = pi.itemPrefab.Description;
ScalableFont font = listBox.Rect.Width < 280 ? GUI.SmallFont : GUI.Font; ScalableFont font = listBox.Rect.Width < 280 ? GUI.SmallFont : GUI.Font;
GUITextBlock textBlock = new GUITextBlock( GUITextBlock textBlock = new GUITextBlock(
new Rectangle(50, 0, 0, 25), new Rectangle(50, 0, 0, 25),
ep.Name, pi.itemPrefab.Name,
null, null, null, null,
Alignment.Left, Alignment.CenterX | Alignment.Left, Alignment.Left, Alignment.CenterX | Alignment.Left,
"", frame); "", frame);
textBlock.Font = font; textBlock.Font = font;
textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f);
textBlock.ToolTip = ep.Description; textBlock.ToolTip = pi.itemPrefab.Description;
if (ep.sprite != null) if (pi.itemPrefab.sprite != null)
{ {
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), ep.sprite, Alignment.CenterLeft, frame); GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), pi.itemPrefab.sprite, Alignment.CenterLeft, frame);
img.Color = ep.SpriteColor; img.Color = pi.itemPrefab.SpriteColor;
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
} }
textBlock = new GUITextBlock( textBlock = new GUITextBlock(
new Rectangle(width - 80, 0, 80, 25), new Rectangle(width - 160, 0, 80, 25),
ep.Price.ToString(), pi.itemPrefab.Price.ToString(),
null, null, Alignment.TopLeft, null, null, Alignment.TopLeft,
Alignment.TopLeft, "", frame); Alignment.TopLeft, "", frame);
textBlock.Font = font; textBlock.Font = font;
textBlock.ToolTip = ep.Description; textBlock.ToolTip = pi.itemPrefab.Description;
//If its the store menu, quantity will always be 0
if (pi.quantity > 0)
{
var amountInput = new GUINumberInput(new Rectangle(width - 80, 0, 50, 40), "", GUINumberInput.NumberType.Int, frame);
amountInput.MinValueInt = 0;
amountInput.MaxValueInt = 1000;
amountInput.UserData = pi;
amountInput.IntValue = pi.quantity;
amountInput.OnValueChanged += (numberInput) =>
{
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
//Attempting to buy
if (numberInput.IntValue > purchasedItem.quantity)
{
int quantity = numberInput.IntValue - purchasedItem.quantity;
//Cap the numberbox based on the amount we can afford.
quantity = campaign.Money <= 0 ?
0 : Math.Min((int)(Campaign.Money / (float)purchasedItem.itemPrefab.Price), quantity);
for (int i = 0; i < quantity; i++)
{
BuyItem(numberInput, purchasedItem);
}
numberInput.IntValue = purchasedItem.quantity;
}
//Attempting to sell
else
{
int quantity = purchasedItem.quantity - numberInput.IntValue;
for (int i = 0; i < quantity; i++)
{
SellItem(numberInput, purchasedItem);
}
}
};
}
} }
private bool BuyItem(GUIComponent component, object obj) private bool BuyItem(GUIComponent component, object obj)
{ {
ItemPrefab prefab = obj as ItemPrefab; PurchasedItem pi = obj as PurchasedItem;
if (prefab == null) return false; if (pi == null || pi.itemPrefab == null) return false;
if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)) if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
{ {
return false; return false;
} }
if (prefab.Price > campaign.Money) return false; if (pi.itemPrefab.Price > campaign.Money) return false;
campaign.CargoManager.PurchaseItem(prefab); campaign.CargoManager.PurchaseItem(pi.itemPrefab, 1);
GameMain.Client?.SendCampaignState(); GameMain.Client?.SendCampaignState();
return false; return false;
@@ -272,15 +311,15 @@ namespace Barotrauma
private bool SellItem(GUIComponent component, object obj) private bool SellItem(GUIComponent component, object obj)
{ {
ItemPrefab prefab = obj as ItemPrefab; PurchasedItem pi = obj as PurchasedItem;
if (prefab == null) return false; if (pi == null || pi.itemPrefab == null) return false;
if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)) if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
{ {
return false; return false;
} }
campaign.CargoManager.SellItem(prefab); campaign.CargoManager.SellItem(pi.itemPrefab,1);
GameMain.Client?.SendCampaignState(); GameMain.Client?.SendCampaignState();
return false; return false;
@@ -289,10 +328,13 @@ namespace Barotrauma
private void RefreshItemTab() private void RefreshItemTab()
{ {
selectedItemList.ClearChildren(); selectedItemList.ClearChildren();
foreach (ItemPrefab ip in campaign.CargoManager.PurchasedItems) foreach (PurchasedItem pi in campaign.CargoManager.PurchasedItems)
{ {
CreateItemFrame(ip, selectedItemList, selectedItemList.Rect.Width); CreateItemFrame(pi, selectedItemList, selectedItemList.Rect.Width);
} }
selectedItemList.children.Sort((x, y) => (x.UserData as PurchasedItem).itemPrefab.Name.CompareTo((y.UserData as PurchasedItem).itemPrefab.Name));
selectedItemList.children.Sort((x, y) => (x.UserData as PurchasedItem).itemPrefab.Category.CompareTo((y.UserData as PurchasedItem).itemPrefab.Category));
selectedItemList.UpdateScrollBarSize();
} }
public void SelectTab(Tab tab) public void SelectTab(Tab tab)
@@ -311,16 +353,16 @@ namespace Barotrauma
storeItemList.ClearChildren(); storeItemList.ClearChildren();
MapEntityCategory category = (MapEntityCategory)selection; MapEntityCategory category = (MapEntityCategory)selection;
var items = MapEntityPrefab.List.FindAll(ep => ep.Price > 0.0f && ep.Category.HasFlag(category)); var items = MapEntityPrefab.List.FindAll(ep => ep.Price > 0.0f && ep.Category.HasFlag(category) && ep is ItemPrefab);
int width = storeItemList.Rect.Width; int width = storeItemList.Rect.Width;
foreach (MapEntityPrefab ep in items) foreach (ItemPrefab ep in items)
{ {
CreateItemFrame(ep, storeItemList, width); CreateItemFrame(new PurchasedItem((ItemPrefab)ep,0), storeItemList, width);
} }
storeItemList.children.Sort((x, y) => (x.UserData as MapEntityPrefab).Name.CompareTo((y.UserData as MapEntityPrefab).Name)); storeItemList.children.Sort((x, y) => (x.UserData as PurchasedItem).itemPrefab.Name.CompareTo((y.UserData as PurchasedItem).itemPrefab.Name));
foreach (GUIComponent child in button.Parent.children) foreach (GUIComponent child in button.Parent.children)
{ {
@@ -244,13 +244,12 @@ namespace Barotrauma
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground); GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.losTexture); GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.losTexture);
#endif #endif
//convert the los color to HLS and make sure the luminance of the color is always the same
//as the luminance of the ambient light color
//convert the los color to HLS and make sure the luminance of the color is always the same regardless
//of the ambient light color and the luminance of the damage overlight color
float r = Math.Min(CharacterHUD.damageOverlayTimer * 0.5f, 0.5f); float r = Math.Min(CharacterHUD.damageOverlayTimer * 0.5f, 0.5f);
Vector3 ambientLightHls = GameMain.LightManager.AmbientLight.RgbToHLS();
Vector3 losColorHls = Color.Lerp(GameMain.LightManager.AmbientLight, Color.Red, r).RgbToHLS(); Vector3 losColorHls = Color.Lerp(GameMain.LightManager.AmbientLight, Color.Red, r).RgbToHLS();
losColorHls.Y = 0.1f; losColorHls.Y = ambientLightHls.Y;
Color losColor = ToolBox.HLSToRGB(losColorHls); Color losColor = ToolBox.HLSToRGB(losColorHls);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, GameMain.LightManager.LosEffect, null); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, GameMain.LightManager.LosEffect, null);
@@ -82,13 +82,15 @@ namespace Barotrauma
locationTitle.Text = TextManager.Get("Location") + ": " + campaign.Map.CurrentLocation.Name; locationTitle.Text = TextManager.Get("Location") + ": " + campaign.Map.CurrentLocation.Name;
campaign.Map.SelectLocation(-1);
bottomPanel.ClearChildren(); bottomPanel.ClearChildren();
campaignUI = new CampaignUI(campaign, bottomPanel); campaignUI = new CampaignUI(campaign, bottomPanel);
campaignUI.StartRound = StartRound; campaignUI.StartRound = StartRound;
campaignUI.OnLocationSelected = SelectLocation; campaignUI.OnLocationSelected = SelectLocation;
campaignUI.UpdateCharacterLists(); campaignUI.UpdateCharacterLists();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("singleplayer"); GameAnalyticsManager.SetCustomDimension01("singleplayer");
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
@@ -190,7 +192,10 @@ namespace Barotrauma
private IEnumerable<object> LoadRound() private IEnumerable<object> LoadRound()
{ {
GameMain.GameSession.StartRound(campaignUI.SelectedLevel, true); GameMain.GameSession.StartRound(campaignUI.SelectedLevel,
reloadSub: true,
loadSecondSub: false,
mirrorLevel: GameMain.GameSession.Map.CurrentLocation != GameMain.GameSession.Map.SelectedConnection.Locations[0]);
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
@@ -143,7 +143,7 @@ namespace Barotrauma
SelectTab(null, 0); SelectTab(null, 0);
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01(""); GameAnalyticsManager.SetCustomDimension01("");
} }
public bool SelectTab(GUIButton button, object obj) public bool SelectTab(GUIButton button, object obj)
@@ -368,7 +368,19 @@ namespace Barotrauma
Directory.CreateDirectory(SaveUtil.TempPath); Directory.CreateDirectory(SaveUtil.TempPath);
} }
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true); try
{
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true);
}
catch (IOException e)
{
DebugConsole.ThrowError("Copying the file \"" + selectedSub.FilePath + "\" failed. The file may have been deleted or in use by another process. Try again or select another submarine.", e);
GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.StartGame:IOException" + selectedSub.Name,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Copying the file \"" + selectedSub.FilePath + "\" failed.\n" + e.Message + "\n" + Environment.StackTrace);
return;
}
selectedSub = new Submarine(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), ""); selectedSub = new Submarine(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), "");
@@ -177,7 +177,7 @@ namespace Barotrauma
if (levelSeed == value) return; if (levelSeed == value) return;
levelSeed = value; levelSeed = value;
backgroundSprite = LocationType.Random(levelSeed).Background; backgroundSprite = LocationType.Random(levelSeed)?.Background;
seedBox.Text = levelSeed; seedBox.Text = levelSeed;
} }
} }
@@ -379,13 +379,13 @@ namespace Barotrauma
showLogButton.UserData = "showlog"; showLogButton.UserData = "showlog";
showLogButton.OnClicked = (GUIButton button, object userData) => showLogButton.OnClicked = (GUIButton button, object userData) =>
{ {
if (GameMain.Server.ServerLog.LogFrame == null) if (GameMain.NetworkMember.ServerLog.LogFrame == null)
{ {
GameMain.Server.ServerLog.CreateLogFrame(); GameMain.NetworkMember.ServerLog.CreateLogFrame();
} }
else else
{ {
GameMain.Server.ServerLog.LogFrame = null; GameMain.NetworkMember.ServerLog.LogFrame = null;
GUIComponent.KeyboardDispatcher.Subscriber = null; GUIComponent.KeyboardDispatcher.Subscriber = null;
} }
return true; return true;
@@ -435,7 +435,9 @@ namespace Barotrauma
infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "settingsButton")); infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "settingsButton"));
infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "spectateButton")); infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "spectateButton"));
InfoFrame.FindChild("showlog").Visible = GameMain.Server != null; InfoFrame.FindChild("showlog").Visible =
GameMain.Server != null ||
(GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ServerLog));
if (campaignViewButton == null) if (campaignViewButton == null)
{ {
@@ -501,7 +503,7 @@ namespace Barotrauma
} }
} }
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("multiplayer"); GameAnalyticsManager.SetCustomDimension01("multiplayer");
if (GameModePreset.list.Count > 0 && modeList.Selected == null) modeList.Select(0); if (GameModePreset.list.Count > 0 && modeList.Selected == null) modeList.Select(0);
@@ -737,8 +739,21 @@ namespace Barotrauma
//hash will be null if opening the sub file failed -> don't select the sub //hash will be null if opening the sub file failed -> don't select the sub
if (string.IsNullOrWhiteSpace(hash)) if (string.IsNullOrWhiteSpace(hash))
{ {
(component as GUITextBlock).TextColor = Color.DarkRed * 0.8f; GUITextBlock submarineTextBlock = component.GetChild<GUITextBlock>();
component.CanBeFocused = false; if (submarineTextBlock != null)
{
submarineTextBlock.TextColor = Color.DarkRed * 0.8f;
submarineTextBlock.CanBeFocused = false;
}
else
{
DebugConsole.ThrowError("Failed to select submarine. Selected GUIComponent was of the type \"" + (component == null ? "null" : component.GetType().ToString()) + "\".");
GameAnalyticsManager.AddErrorEventOnce(
"NetLobbyScreen.SelectSub:InvalidComponent",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to select submarine. Selected GUIComponent was of the type \"" + (component == null ? "null" : component.GetType().ToString()) + "\".");
}
StartButton.Enabled = false; StartButton.Enabled = false;
@@ -782,8 +797,8 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
var matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash); var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash);
if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == sub.Name); if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name);
if (matchingSub == null) if (matchingSub == null)
{ {
@@ -1467,8 +1482,8 @@ namespace Barotrauma
return false; return false;
} }
Submarine sub = Submarine.SavedSubmarines.Find(m => m.Name == subName && m.MD5Hash.Hash == md5Hash); Submarine sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName && m.MD5Hash.Hash == md5Hash);
if (sub == null) sub = Submarine.SavedSubmarines.Find(m => m.Name == subName); if (sub == null) sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName);
var matchingListSub = subList.children.Find(c => c.UserData == sub); var matchingListSub = subList.children.Find(c => c.UserData == sub);
if (matchingListSub != null) if (matchingListSub != null)
@@ -1520,7 +1535,7 @@ namespace Barotrauma
requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) => requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) =>
{ {
string[] fileInfo = (string[])userdata; string[] fileInfo = (string[])userdata;
GameMain.Client.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]); GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]);
return true; return true;
}; };
requestFileBox.Buttons[1].OnClicked += requestFileBox.Close; requestFileBox.Buttons[1].OnClicked += requestFileBox.Close;
@@ -15,9 +15,9 @@ namespace Barotrauma
{ {
private static string[] crewExperienceLevels = new string[] private static string[] crewExperienceLevels = new string[]
{ {
TextManager.Get("CrewExperienceLow"), "CrewExperienceLow",
TextManager.Get("CrewExperienceMid"), "CrewExperienceMid",
TextManager.Get("CrewExperienceHigh") "CrewExperienceHigh"
}; };
private Camera cam; private Camera cam;
@@ -349,7 +349,7 @@ namespace Barotrauma
cam.UpdateTransform(); cam.UpdateTransform();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("editor"); GameAnalyticsManager.SetCustomDimension01("editor");
} }
public override void Deselect() public override void Deselect()
@@ -587,23 +587,26 @@ namespace Barotrauma
var toggleExpRight = new GUIButton(new Rectangle(350, y, 20, 20), ">", "", saveFrame); var toggleExpRight = new GUIButton(new Rectangle(350, y, 20, 20), ">", "", saveFrame);
var experienceText = new GUITextBlock(new Rectangle(250, y, 100, 20), crewExperienceLevels[0], "", Alignment.TopLeft, Alignment.Center, saveFrame); var experienceText = new GUITextBlock(new Rectangle(250, y, 100, 20), crewExperienceLevels[0], "", Alignment.TopLeft, Alignment.Center, saveFrame);
toggleExpLeft.OnClicked += (btn, userData) => toggleExpLeft.OnClicked += (btn, userData) =>
{ {
int currentIndex = Array.IndexOf(crewExperienceLevels, experienceText.Text); int currentIndex = Array.IndexOf(crewExperienceLevels, (string)experienceText.UserData);
currentIndex--; currentIndex--;
if (currentIndex < 0) currentIndex = crewExperienceLevels.Length - 1; if (currentIndex < 0) currentIndex = crewExperienceLevels.Length - 1;
experienceText.Text = crewExperienceLevels[currentIndex]; experienceText.UserData = crewExperienceLevels[currentIndex];
Submarine.MainSub.RecommendedCrewExperience = experienceText.Text; experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData;
return true; return true;
}; };
toggleExpRight.OnClicked += (btn, userData) => toggleExpRight.OnClicked += (btn, userData) =>
{ {
int currentIndex = Array.IndexOf(crewExperienceLevels, experienceText.Text); int currentIndex = Array.IndexOf(crewExperienceLevels, (string)experienceText.UserData);
currentIndex++; currentIndex++;
if (currentIndex >= crewExperienceLevels.Length) currentIndex = 0; if (currentIndex >= crewExperienceLevels.Length) currentIndex = 0;
experienceText.Text = crewExperienceLevels[currentIndex]; experienceText.UserData = crewExperienceLevels[currentIndex];
Submarine.MainSub.RecommendedCrewExperience = experienceText.Text; experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData;
return true; return true;
}; };
@@ -613,8 +616,9 @@ namespace Barotrauma
int max = Submarine.MainSub.RecommendedCrewSizeMax; int max = Submarine.MainSub.RecommendedCrewSizeMax;
crewSizeMin.IntValue = min; crewSizeMin.IntValue = min;
crewSizeMax.IntValue = max; crewSizeMax.IntValue = max;
experienceText.Text = string.IsNullOrEmpty(Submarine.MainSub.RecommendedCrewExperience) ? experienceText.UserData = string.IsNullOrEmpty(Submarine.MainSub.RecommendedCrewExperience) ?
crewExperienceLevels[0] : Submarine.MainSub.RecommendedCrewExperience; crewExperienceLevels[0] : Submarine.MainSub.RecommendedCrewExperience;
experienceText.Text = TextManager.Get((string)experienceText.UserData);
} }
var saveButton = new GUIButton(new Rectangle(-90, 0, 80, 20), TextManager.Get("SaveSubButton"), Alignment.Right | Alignment.Bottom, "", saveFrame); var saveButton = new GUIButton(new Rectangle(-90, 0, 80, 20), TextManager.Get("SaveSubButton"), Alignment.Right | Alignment.Bottom, "", saveFrame);
@@ -1082,6 +1086,12 @@ namespace Barotrauma
} }
} }
if (wallPoints.Count < 4)
{
DebugConsole.ThrowError("Generating hulls for the submarine failed. Not enough wall structures to generate hulls.");
return;
}
min = wallPoints[0]; min = wallPoints[0];
max = wallPoints[0]; max = wallPoints[0];
for (int i = 0; i < wallPoints.Count; i++) for (int i = 0; i < wallPoints.Count; i++)
@@ -39,7 +39,7 @@ namespace Barotrauma.Sounds
using (VorbisReader reader = new VorbisReader(oggFile)) using (VorbisReader reader = new VorbisReader(oggFile))
{ {
int bufferSize = (int)reader.TotalSamples; int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] buffer = new float[bufferSize]; float[] buffer = new float[bufferSize];
sound.castBuffer = new short[bufferSize]; sound.castBuffer = new short[bufferSize];
@@ -52,11 +52,13 @@ namespace Barotrauma.Sounds
sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16; sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16;
sound.sampleRate = reader.SampleRate; sound.sampleRate = reader.SampleRate;
ALHelper.Check();
//alSourceId = AL.GenSource(); //alSourceId = AL.GenSource();
AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer, AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer,
readSamples * sizeof(short), reader.SampleRate); readSamples * sizeof(short), reader.SampleRate);
ALHelper.Check(); ALHelper.Check(oggFile);
} }
//AL.Source(alSourceId, ALSourcei.Buffer, alBufferId); //AL.Source(alSourceId, ALSourcei.Buffer, alBufferId);
@@ -100,12 +102,12 @@ namespace Barotrauma.Sounds
public void Dispose() public void Dispose()
{ {
//var state = AL.GetSourceState(alSourceId);
//if (state == ALSourceState.Playing || state == ALSourceState.Paused)
// Stop();
System.Diagnostics.Debug.WriteLine(alBufferId); System.Diagnostics.Debug.WriteLine(alBufferId);
//AL.DeleteSource(alSourceId); if (alBufferId > 0)
AL.DeleteBuffer(alBufferId); {
AL.DeleteBuffer(alBufferId);
alBufferId = 0;
}
//if (ALHelper.Efx.IsInitialized) //if (ALHelper.Efx.IsInitialized)
// ALHelper.Efx.DeleteFilter(alFilterId); // ALHelper.Efx.DeleteFilter(alFilterId);
@@ -45,16 +45,24 @@ namespace Barotrauma.Sounds
//logHandler(String.Format("Total memory : {0:0.###} {1} ", usedHeap, sizes[order]), 0, 6); //logHandler(String.Format("Total memory : {0:0.###} {1} ", usedHeap, sizes[order]), 0, 6);
} }
public static void Check() public static void Check(string extraErrorMsg = "")
{ {
ALError error; ALError error;
if ((error = AL.GetError()) != ALError.NoError) if ((error = AL.GetError()) != ALError.NoError)
{ {
string errorMsg = "OpenAL error: " + AL.GetErrorString(error);
if (!string.IsNullOrEmpty(extraErrorMsg)) errorMsg += " {" + extraErrorMsg + "} ";
errorMsg += "\n" + Environment.StackTrace;
#if DEBUG #if DEBUG
DebugConsole.ThrowError("OpenAL error: " + AL.GetErrorString(error) + "\n" + Environment.StackTrace); DebugConsole.ThrowError(errorMsg);
#else #else
DebugConsole.NewMessage("OpenAL error: " + AL.GetErrorString(error) + "\n" + Environment.StackTrace, Microsoft.Xna.Framework.Color.Red); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce(
"OggStream.Check:" + AL.GetErrorString(error) + extraErrorMsg,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
} }
} }
} }
@@ -83,9 +91,17 @@ namespace Barotrauma.Sounds
public Action<string, int, int> LogHandler; public Action<string, int, int> LogHandler;
#endif #endif
public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), bufferCount) { } public string FileName
public OggStream(Stream stream, int bufferCount = DefaultBufferCount)
{ {
get;
private set;
}
public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), filename, bufferCount) { }
public OggStream(Stream stream, string fileName, int bufferCount = DefaultBufferCount)
{
this.FileName = fileName;
BufferCount = bufferCount; BufferCount = bufferCount;
alBufferIds = AL.GenBuffers(bufferCount); alBufferIds = AL.GenBuffers(bufferCount);
@@ -94,7 +110,7 @@ namespace Barotrauma.Sounds
if (ALHelper.XRam.IsInitialized) if (ALHelper.XRam.IsInitialized)
{ {
ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware); ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware);
ALHelper.Check(); ALHelper.Check(fileName);
} }
if (ALHelper.Efx.IsInitialized) if (ALHelper.Efx.IsInitialized)
@@ -163,7 +179,7 @@ namespace Barotrauma.Sounds
AL.SourcePlay(alSourceId); AL.SourcePlay(alSourceId);
this.Volume = volume; this.Volume = volume;
ALHelper.Check(); ALHelper.Check(FileName);
Preparing = false; Preparing = false;
@@ -177,7 +193,7 @@ namespace Barotrauma.Sounds
OggStreamer.Instance.RemoveStream(this); OggStreamer.Instance.RemoveStream(this);
AL.SourcePause(alSourceId); AL.SourcePause(alSourceId);
ALHelper.Check(); ALHelper.Check(FileName);
} }
public void Resume() public void Resume()
@@ -187,7 +203,7 @@ namespace Barotrauma.Sounds
OggStreamer.Instance.AddStream(this); OggStreamer.Instance.AddStream(this);
AL.SourcePlay(alSourceId); AL.SourcePlay(alSourceId);
ALHelper.Check(); ALHelper.Check(FileName);
} }
public void Stop() public void Stop()
@@ -226,7 +242,7 @@ namespace Barotrauma.Sounds
set set
{ {
AL.Source(alSourceId, ALSourcef.Gain, volume = value); AL.Source(alSourceId, ALSourcef.Gain, volume = value);
ALHelper.Check(); ALHelper.Check(FileName);
} }
} }
@@ -256,20 +272,20 @@ namespace Barotrauma.Sounds
/*if (ALHelper.Efx.IsInitialized) /*if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(alFilterId);*/ ALHelper.Efx.DeleteFilter(alFilterId);*/
ALHelper.Check(); ALHelper.Check(FileName);
} }
void StopPlayback() void StopPlayback()
{ {
AL.SourceStop(alSourceId); AL.SourceStop(alSourceId);
ALHelper.Check(); ALHelper.Check(FileName);
} }
void Empty() void Empty()
{ {
int queued; int queued;
AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued); AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check(); ALHelper.Check(FileName);
if (queued > 0) if (queued > 0)
{ {
@@ -292,12 +308,12 @@ namespace Barotrauma.Sounds
if (processed > 0) if (processed > 0)
{ {
AL.SourceUnqueueBuffers(alSourceId, processed, salvaged); AL.SourceUnqueueBuffers(alSourceId, processed, salvaged);
ALHelper.Check(); ALHelper.Check(FileName);
} }
// Try turning it off again? // Try turning it off again?
AL.SourceStop(alSourceId); AL.SourceStop(alSourceId);
ALHelper.Check(); ALHelper.Check(FileName);
Empty(); Empty();
} }
@@ -314,7 +330,7 @@ namespace Barotrauma.Sounds
// Fill first buffer synchronously // Fill first buffer synchronously
OggStreamer.Instance.FillBuffer(this, alBufferIds[0]); OggStreamer.Instance.FillBuffer(this, alBufferIds[0]);
AL.SourceQueueBuffer(alSourceId, alBufferIds[0]); AL.SourceQueueBuffer(alSourceId, alBufferIds[0]);
ALHelper.Check(); ALHelper.Check(FileName);
// Schedule the others asynchronously // Schedule the others asynchronously
OggStreamer.Instance.AddStream(this); OggStreamer.Instance.AddStream(this);
@@ -429,7 +445,7 @@ namespace Barotrauma.Sounds
} }
AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer, AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer,
readSamples * sizeof(short), stream.Reader.SampleRate); readSamples * sizeof(short), stream.Reader.SampleRate);
ALHelper.Check(); ALHelper.Check(stream.FileName);
return readSamples != BufferSize; return readSamples != BufferSize;
} }
@@ -466,10 +482,10 @@ namespace Barotrauma.Sounds
int queued; int queued;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued); AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check(); ALHelper.Check(stream.FileName);
int processed; int processed;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed); AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed);
ALHelper.Check(); ALHelper.Check(stream.FileName);
if (processed == 0 && queued == stream.BufferCount) continue; if (processed == 0 && queued == stream.BufferCount) continue;
@@ -496,7 +512,7 @@ namespace Barotrauma.Sounds
} }
AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers); AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers);
ALHelper.Check(); ALHelper.Check(stream.FileName);
if (finished && !stream.IsLooped) if (finished && !stream.IsLooped)
continue; continue;
@@ -514,7 +530,7 @@ namespace Barotrauma.Sounds
if (state == ALSourceState.Stopped) if (state == ALSourceState.Stopped)
{ {
AL.SourcePlay(stream.alSourceId); AL.SourcePlay(stream.alSourceId);
ALHelper.Check(); ALHelper.Check(stream.FileName);
} }
} }
} }
@@ -54,7 +54,7 @@ namespace Barotrauma
{ {
DebugConsole.ThrowError("Failed to load sound "+file+"!", e); DebugConsole.ThrowError("Failed to load sound "+file+"!", e);
} }
ALHelper.Check(); ALHelper.Check(file);
} }
baseVolume = 1.0f; baseVolume = 1.0f;
@@ -219,7 +219,7 @@ namespace Barotrauma
(SoundManager.IsPlaying(alSourceId) || SoundManager.IsPaused(alSourceId))) (SoundManager.IsPlaying(alSourceId) || SoundManager.IsPaused(alSourceId)))
{ {
SoundManager.Stop(alSourceId); SoundManager.Stop(alSourceId);
ALHelper.Check(); ALHelper.Check(filePath);
} }
foreach (Sound s in loadedSounds) foreach (Sound s in loadedSounds)
@@ -228,7 +228,7 @@ namespace Barotrauma
} }
SoundManager.ClearAlSource(AlBufferId); SoundManager.ClearAlSource(AlBufferId);
ALHelper.Check(); ALHelper.Check(filePath);
if (oggSound != null) if (oggSound != null)
{ {
@@ -246,7 +246,7 @@ namespace Barotrauma
public static void StreamVolume(float volume = 1.0f) public static void StreamVolume(float volume = 1.0f)
{ {
if (SoundManager.Disabled) return; if (SoundManager.Disabled || stream == null) return;
stream.Volume = volume; stream.Volume = volume;
} }
@@ -143,7 +143,7 @@ namespace Barotrauma.Sounds
volume = 0.0f; volume = 0.0f;
} }
if (sourceIndex<1 || soundsPlaying[sourceIndex] != sound) if (sourceIndex < 1 || soundsPlaying[sourceIndex] != sound)
{ {
sourceIndex = Play(sound, position, volume, 0.0f, true); sourceIndex = Play(sound, position, volume, 0.0f, true);
} }
@@ -153,7 +153,7 @@ namespace Barotrauma.Sounds
AL.Source(alSources[sourceIndex], ALSourceb.Looping, true); AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
} }
ALHelper.Check(); ALHelper.Check(sound?.FilePath);
return sourceIndex; return sourceIndex;
} }
@@ -165,7 +165,7 @@ namespace Barotrauma.Sounds
return; return;
AL.SourcePause(alSources[sourceIndex]); AL.SourcePause(alSources[sourceIndex]);
ALHelper.Check(); ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
} }
public static void Resume(int sourceIndex) public static void Resume(int sourceIndex)
@@ -176,7 +176,7 @@ namespace Barotrauma.Sounds
return; return;
AL.SourcePlay(alSources[sourceIndex]); AL.SourcePlay(alSources[sourceIndex]);
ALHelper.Check(); ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
} }
public static void Stop(int sourceIndex) public static void Stop(int sourceIndex)
@@ -291,7 +291,7 @@ namespace Barotrauma.Sounds
ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassHfGain = value); ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassHfGain = value);
ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId); ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId);
ALHelper.Check(); ALHelper.Check(soundsPlaying[i]?.FilePath);
} }
} }
} }
@@ -316,7 +316,7 @@ namespace Barotrauma.Sounds
ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassGain); ALHelper.Efx.Filter(lowpassFilterId, OpenTK.Audio.OpenAL.EfxFilterf.LowpassGainHF, lowPassGain);
ALHelper.Efx.BindFilterToSource(alSources[sourceIndex], lowpassFilterId); ALHelper.Efx.BindFilterToSource(alSources[sourceIndex], lowpassFilterId);
ALHelper.Check(); ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
} }
public static OggStream StartStream(string file, float volume = 1.0f) public static OggStream StartStream(string file, float volume = 1.0f)
@@ -331,7 +331,7 @@ namespace Barotrauma.Sounds
oggStream.Play(volume); oggStream.Play(volume);
ALHelper.Check(); ALHelper.Check(file);
return oggStream; return oggStream;
} }
@@ -361,6 +361,7 @@ namespace Barotrauma.Sounds
for (int i = 0; i < DefaultSourceCount; i++) for (int i = 0; i < DefaultSourceCount; i++)
{ {
string soundPath = soundsPlaying[i]?.FilePath;
var state = OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]); var state = OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]);
if (state == OpenTK.Audio.OpenAL.ALSourceState.Playing || state == OpenTK.Audio.OpenAL.ALSourceState.Paused) if (state == OpenTK.Audio.OpenAL.ALSourceState.Playing || state == OpenTK.Audio.OpenAL.ALSourceState.Paused)
{ {
@@ -368,8 +369,7 @@ namespace Barotrauma.Sounds
} }
OpenTK.Audio.OpenAL.AL.DeleteSource(alSources[i]); OpenTK.Audio.OpenAL.AL.DeleteSource(alSources[i]);
ALHelper.Check(soundPath);
ALHelper.Check();
} }
if (oggStream != null) if (oggStream != null)
@@ -254,9 +254,12 @@ namespace Barotrauma
partial void DisposeTexture() partial void DisposeTexture()
{ {
//check if another sprite is using the same texture //check if another sprite is using the same texture
foreach (Sprite s in list) if (!string.IsNullOrEmpty(file)) //file can be empty if the sprite is created directly from a Texture2D instance
{ {
if (s.file == file) return; foreach (Sprite s in list)
{
if (s.file == file) return;
}
} }
//if not, free the texture //if not, free the texture
@@ -267,6 +270,5 @@ namespace Barotrauma
} }
} }
} }
} }
@@ -46,7 +46,7 @@ namespace Barotrauma
using (Stream fileStream = File.OpenRead(path)) using (Stream fileStream = File.OpenRead(path))
{ {
var texture = Texture2D.FromStream(_graphicsDevice, fileStream); var texture = Texture2D.FromStream(_graphicsDevice, fileStream);
texture = PreMultiplyAlpha(texture); PreMultiplyAlpha(texture);
return texture; return texture;
} }
@@ -63,7 +63,7 @@ namespace Barotrauma
try try
{ {
var texture = Texture2D.FromStream(_graphicsDevice, fileStream); var texture = Texture2D.FromStream(_graphicsDevice, fileStream);
texture = PreMultiplyAlpha(texture); PreMultiplyAlpha(texture);
return texture; return texture;
} }
catch (Exception e) catch (Exception e)
@@ -73,7 +73,7 @@ namespace Barotrauma
} }
} }
private static Texture2D PreMultiplyAlpha(Texture2D texture) private static void PreMultiplyAlpha(Texture2D texture)
{ {
// Setup a render target to hold our final texture which will have premulitplied alpha values // Setup a render target to hold our final texture which will have premulitplied alpha values
using (RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, texture.Width, texture.Height)) using (RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, texture.Width, texture.Height))
@@ -104,8 +104,6 @@ namespace Barotrauma
_graphicsDevice.Textures[0] = null; _graphicsDevice.Textures[0] = null;
texture.SetData(data); texture.SetData(data);
} }
return texture;
} }
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.5")] [assembly: AssemblyVersion("0.8.2.0")]
[assembly: AssemblyFileVersion("0.8.1.5")] [assembly: AssemblyFileVersion("0.8.2.0")]
@@ -124,7 +124,6 @@ namespace Barotrauma
public void CloseServer() public void CloseServer()
{ {
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
Server.Disconnect(); Server.Disconnect();
Server = null; Server = null;
} }
@@ -171,7 +170,6 @@ namespace Barotrauma
stopwatch.Stop(); stopwatch.Stop();
CloseServer(); CloseServer();
} }
public void ProcessInput() public void ProcessInput()
@@ -31,6 +31,7 @@ namespace Barotrauma
inputThread.Start(); inputThread.Start();
game.Run(); game.Run();
inputThread.Abort(); inputThread.Join(); inputThread.Abort(); inputThread.Join();
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -119,7 +119,7 @@ namespace Barotrauma
subs = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus)).ToList(); subs = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus)).ToList();
if (subs == null || subs.Count()==0) if (subs == null || subs.Count() == 0)
{ {
throw new Exception("No submarines are available."); throw new Exception("No submarines are available.");
} }
@@ -187,7 +187,7 @@ namespace Barotrauma
if (GameMain.Server.SubSelectionMode == SelectionMode.Random) if (GameMain.Server.SubSelectionMode == SelectionMode.Random)
{ {
var nonShuttles = Submarine.SavedSubmarines.FindAll(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus)); var nonShuttles = Submarine.SavedSubmarines.Where(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus)).ToList();
SelectedSub = nonShuttles[Rand.Range(0, nonShuttles.Count)]; SelectedSub = nonShuttles[Rand.Range(0, nonShuttles.Count)];
} }
if (GameMain.Server.ModeSelectionMode == SelectionMode.Random) if (GameMain.Server.ModeSelectionMode == SelectionMode.Random)
@@ -325,12 +325,6 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\lightsprite.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\lightsprite.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\monitor.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\monitors.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\poweritems.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\poweritems.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -26,7 +26,8 @@
<Item <Item
name="Medicine Cabinet" name="Medicine Cabinet"
linkable="true" linkable="true"
pickdistance ="150"> pickdistance="150"
waterproof="true">
<Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,0,48,64" canflipx="false"/> <Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,0,48,64" canflipx="false"/>
@@ -37,9 +38,10 @@
</Item> </Item>
<Item <Item
name="Toxic Cabinet" name="Toxic Cabinet"
linkable="true" linkable="true"
pickdistance ="150"> pickdistance="150"
waterproof="true">
<Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,64,48,64" canflipx="false"/> <Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,64,48,64" canflipx="false"/>
@@ -52,7 +54,7 @@
<Item <Item
name="Metal Crate" name="Metal Crate"
linkable="true" linkable="true"
pickdistance ="150"> pickdistance="150">
<Deconstruct time="20"> <Deconstruct time="20">
<Item name="Steel Bar"/> <Item name="Steel Bar"/>
@@ -74,7 +76,7 @@
<Item <Item
name="Explosive Crate" name="Explosive Crate"
linkable="true" linkable="true"
pickdistance ="150"> pickdistance="150">
<Deconstruct time="20"> <Deconstruct time="20">
<Item name="Steel Bar"/> <Item name="Steel Bar"/>
@@ -139,6 +139,8 @@
<sprite texture="DivingSuit.png" limb="RightFoot" sound="footstep_armor" sourcerect="30,100,20,25" origin="0.5,0.5" depth="0.013" inheritlimbdepth="false" hidelimb="true"/> <sprite texture="DivingSuit.png" limb="RightFoot" sound="footstep_armor" sourcerect="30,100,20,25" origin="0.5,0.5" depth="0.013" inheritlimbdepth="false" hidelimb="true"/>
<sprite texture="DivingSuit.png" limb="LeftFoot" sound="footstep_armor" sourcerect="30,100,20,25" origin="0.5,0.5" depth="0.013" inheritlimbdepth="false" hidelimb="true"/> <sprite texture="DivingSuit.png" limb="LeftFoot" sound="footstep_armor" sourcerect="30,100,20,25" origin="0.5,0.5" depth="0.013" inheritlimbdepth="false" hidelimb="true"/>
<sprite texture="DivingSuit.png" limb="Waist" sourcerect="0,0,1,1" origin="0.5,0.5" depth="0.0" inheritlimbdepth="false" hidelimb="true"/>
<StatusEffect type="OnWearing" target="Character" HideFace="true" ObstructVision="true" PressureProtection="100.0" SpeedMultiplier="0.6" LowPassMultiplier="0.2" setvalue="true" disabledeltatime="true"/> <StatusEffect type="OnWearing" target="Character" HideFace="true" ObstructVision="true" PressureProtection="100.0" SpeedMultiplier="0.6" LowPassMultiplier="0.2" setvalue="true" disabledeltatime="true"/>
<StatusEffect type="OnWearing" target="Contained,Character" OxygenAvailable="1000.0" Condition="-0.5"> <StatusEffect type="OnWearing" target="Contained,Character" OxygenAvailable="1000.0" Condition="-0.5">
<RequiredItem type="Contained" name="Oxygen Tank"/> <RequiredItem type="Contained" name="Oxygen Tank"/>
@@ -103,8 +103,7 @@
<Item <Item
name="Docking Port" name="Docking Port"
linkable="true" linkable="true">
>
<Sprite texture ="dockingport.png" sourcerect="0,0,112,208" depth="0.94" origin="0.5,0.5"/> <Sprite texture ="dockingport.png" sourcerect="0,0,112,208" depth="0.94" origin="0.5,0.5"/>
@@ -117,13 +116,7 @@
</StatusEffect> </StatusEffect>
</DockingPort> </DockingPort>
<fixrequirement name="Electrical repairs"> <PowerTransfer CanBeOverloaded="false" FireProbability="0.0"/>
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/>
<Wire/> <Wire/>
<fixrequirement name="Mechanical repairs"> <fixrequirement name="Mechanical repairs">
@@ -143,8 +136,7 @@
<Item <Item
name="Docking Hatch" name="Docking Hatch"
linkable="true" linkable="true">
>
<Sprite texture ="dockingport2.png" sourcerect="0,0,128,112" depth="0.94" origin="0.5,0.5"/> <Sprite texture ="dockingport2.png" sourcerect="0,0,128,112" depth="0.94" origin="0.5,0.5"/>
@@ -157,13 +149,7 @@
</StatusEffect> </StatusEffect>
</DockingPort> </DockingPort>
<fixrequirement name="Electrical repairs"> <PowerTransfer CanBeOverloaded="false" FireProbability="0.0"/>
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/>
<Wire/> <Wire/>
<fixrequirement name="Mechanical repairs"> <fixrequirement name="Mechanical repairs">
@@ -183,8 +169,7 @@
<Item <Item
name="Duct Block" name="Duct Block"
linkable="true" linkable="true">
>
<Sprite texture ="duct.png" sourcerect="0,0,33,33" depth="0.01" origin="0.5,0.5"/> <Sprite texture ="duct.png" sourcerect="0,0,33,33" depth="0.01" origin="0.5,0.5"/>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Item
name="Monitor"
linkable="true">
<Sprite texture ="monitor.png" depth="0.85" sourcerect="0,0,16,16"/>
<Body width="16" height="16" density="30"/>
<Holdable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]"
aimpos="35,-10" handle1="0,0" attachable="true" aimable="true">
<requireditem name="Wrench" type="Equipped"/>
</Holdable>
<Label canbeselected = "true">
<GuiFrame rect="0,0,0.4,0.4" alignment="Center" style="ItemUI"/>
</Label>
</Item>
@@ -28,7 +28,7 @@
name="Red Wire" name="Red Wire"
category="Electrical" category="Electrical"
Tags="smallitem,wire" Tags="smallitem,wire"
spritecolor="1.0,0.0,0.0,1.0" spritecolor="0.7,0.06,0.06,1.0"
linkable="true" linkable="true"
canbepicked="true" canbepicked="true"
cargocontainername="Metal Crate" cargocontainername="Metal Crate"
@@ -50,7 +50,7 @@
name="Blue Wire" name="Blue Wire"
category="Electrical" category="Electrical"
Tags="smallitem,wire" Tags="smallitem,wire"
spritecolor="0.0,0.6,1.0,1.0" spritecolor="0.1,0.4,0.9,1.0"
linkable="true" linkable="true"
canbepicked="true" canbepicked="true"
cargocontainername="Metal Crate" cargocontainername="Metal Crate"
@@ -72,7 +72,51 @@
name="Orange Wire" name="Orange Wire"
category="Electrical" category="Electrical"
Tags="smallitem,wire" Tags="smallitem,wire"
spritecolor="1.0,0.5,0.0,1.0" spritecolor="0.9,0.58,0.07,1.0"
linkable="true"
canbepicked="true"
cargocontainername="Metal Crate"
price="10">
<Deconstruct time="5">
<Item name="Copper Bar"/>
</Deconstruct>
<Sprite texture ="signalcomp.png" depth="0.55" sourcerect="83,105,44,22"/>
<Body radius="7" width="25" density="30"/>
<Holdable slots="Any,RightHand,LeftHand" handle1="0,0"/>
<Wire/>
</Item>
<Item
name="Green Wire"
category="Electrical"
Tags="smallitem,wire"
spritecolor="0.17,0.45,0.05,1.0"
linkable="true"
canbepicked="true"
cargocontainername="Metal Crate"
price="10">
<Deconstruct time="5">
<Item name="Copper Bar"/>
</Deconstruct>
<Sprite texture ="signalcomp.png" depth="0.55" sourcerect="83,105,44,22"/>
<Body radius="7" width="25" density="30"/>
<Holdable slots="Any,RightHand,LeftHand" handle1="0,0"/>
<Wire/>
</Item>
<Item
name="Black Wire"
category="Electrical"
Tags="smallitem,wire"
spritecolor="0.1,0.1,0.1,1.0"
linkable="true" linkable="true"
canbepicked="true" canbepicked="true"
cargocontainername="Metal Crate" cargocontainername="Metal Crate"
@@ -261,7 +305,7 @@
<Sprite texture="signalcomp.png" depth="0.8" sourcerect="48,16,16,16"/> <Sprite texture="signalcomp.png" depth="0.8" sourcerect="48,16,16,16"/>
<RelayComponent canbeselected = "true"> <RelayComponent canbeselected="true" vulnerabletoemp="false">
<GuiFrame rect="0,0,200,160" alignment="Center" style="ItemUI"/> <GuiFrame rect="0,0,200,160" alignment="Center" style="ItemUI"/>
</RelayComponent> </RelayComponent>
@@ -65,7 +65,7 @@
</Deconstruct> </Deconstruct>
<Wearable slots="Any,Torso+Legs"> <Wearable slots="Any,Torso+Legs">
<sprite texture="clownshirt.png" limb="Torso" sourcerect="0,3,30,58" origin="0.5,0.48" depth="0.02"/> <sprite texture="clownshirt.png" limb="Torso" sourcerect="0,3,30,58" origin="0.5,0.48" depth="0.02" hidelimb="true"/>
<sprite texture="clownshirt.png" limb="RightHand" sourcerect="47,0,15,49" origin="0.45,0.6" hidelimb="true"/> <sprite texture="clownshirt.png" limb="RightHand" sourcerect="47,0,15,49" origin="0.45,0.6" hidelimb="true"/>
<sprite texture="clownshirt.png" limb="LeftHand" sourcerect="47,0,15,49" origin="0.45,0.6" depth="0.14" hidelimb="true"/> <sprite texture="clownshirt.png" limb="LeftHand" sourcerect="47,0,15,49" origin="0.45,0.6" depth="0.14" hidelimb="true"/>
@@ -423,10 +423,13 @@
<Body width="8" height="16" density="10"/> <Body width="8" height="16" density="10"/>
<Throwable characterusable="true" canBeCombined="true" slots="Any,RightHand,LeftHand" throwforce="4.0" aimpos="35,-10"> <Throwable characterusable="true" canBeCombined="true" slots="Any,RightHand,LeftHand" throwforce="4.0" aimpos="35,-10">
<StatusEffect type="OnUse" target="Character" HuskInfectionState="-0.2"> <StatusEffect type="OnUse" target="Character" HuskInfectionState="-0.4">
<Conditional HuskInfectionState="lt 1.0"/> <Conditional HuskInfectionState="lt 1.0"/>
<RequiredItem name="Medical Syringe" type="Container"/> <RequiredItem name="Medical Syringe" type="Container"/>
</StatusEffect> </StatusEffect>
<StatusEffect type="OnUse" target="Character" HuskInfectionState="-0.1">
<Conditional HuskInfectionState="lt 1.0"/>
</StatusEffect>
<StatusEffect tags="poison,calyxanide" type="OnUse" target="Character" Health="-3.0" duration="10.0"> <StatusEffect tags="poison,calyxanide" type="OnUse" target="Character" Health="-3.0" duration="10.0">
<!-- Injecting a still-conscious Husk will only piss it off and kill the "conscious" faster --> <!-- Injecting a still-conscious Husk will only piss it off and kill the "conscious" faster -->
<Conditional HuskInfectionState="eq 1.0"/> <Conditional HuskInfectionState="eq 1.0"/>
@@ -13,16 +13,13 @@
</fixrequirement> </fixrequirement>
<OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true"> <OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true">
<StatusEffect type="OnFire" target="This" Condition="-0.5"/> <StatusEffect type="OnFire" target="This" Condition="-0.5"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="2.0"/> <StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="2.0"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygenite Tank" Condition="0.0"/>
<sound file="Content/Items/OxygenGenerator/oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/> <sound file="Content/Items/OxygenGenerator/oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/>
<StatusEffect type="OnBroken" target="This" disabledeltatime="true">
<StatusEffect type="OnBroken" target="This" disabledeltatime="true"> <sound file="Content/Items/Reactor/explosion.ogg"/>
<sound file="Content/Items/Reactor/explosion.ogg"/> <Explosion range="50" damage="0" stun="0" force="3.0" flames="false" shockwave="false" sparks="true"/>
<Explosion range="50" damage="0" stun="0" force="3.0" flames="false" shockwave="false" sparks="true"/> </StatusEffect>
</StatusEffect>
</OxygenGenerator> </OxygenGenerator>
<trigger/> <trigger/>
@@ -21,7 +21,7 @@
<Sprite texture = "reactor.png" sourcerect="0,0,321,192" depth="0.8"/> <Sprite texture = "reactor.png" sourcerect="0,0,321,192" depth="0.8"/>
<Reactor canbeselected = "true"> <Reactor canbeselected="true" vulnerabletoemp="false">
<GuiFrame rect="0,0,760,460" alignment="Center" style="ItemUI"/> <GuiFrame rect="0,0,760,460" alignment="Center" style="ItemUI"/>
<RequiredSkill name="Construction" level="30"/> <RequiredSkill name="Construction" level="30"/>
<StatusEffect type="InWater" target="This" Temperature="-500.0"/> <StatusEffect type="InWater" target="This" Temperature="-500.0"/>
@@ -286,12 +286,12 @@
<Holdable slots="Any,RightHand,LeftHand,Face" holdpos="30,-15" aimpos="100,0" handle1="-13,0" handle2="-13,0"> <Holdable slots="Any,RightHand,LeftHand,Face" holdpos="30,-15" aimpos="100,0" handle1="-13,0" handle2="-13,0">
<StatusEffect type="OnActive" target="Contained" Condition="-0.2"/> <StatusEffect type="OnActive" target="Contained" Condition="-0.2"/>
<!-- the child LightComponent is only active when the Holdable is active, i.e. when the item is being held -->
<LightComponent LightColor="1.0,1.0,1.0,1.0" Flicker="0.1" range="800" powerconsumption="10" IsOn="true">
<LightTexture texture="Content/Lights/lightcone.png" origin="0.0, 0.5" size="2.0,1.0"/>
</LightComponent>
</Holdable> </Holdable>
<LightComponent LightColor="1.0,1.0,1.0,1.0" Flicker="0.1" range="800" powerconsumption="10" IsOn="true">
<LightTexture texture="Content/Lights/lightcone.png" origin="0.0, 0.5" size="2.0,1.0"/>
</LightComponent>
<ItemContainer capacity="1" hideitems="true"> <ItemContainer capacity="1" hideitems="true">
<Containable name="Battery Cell, Fulgurium Battery Cell"> <Containable name="Battery Cell, Fulgurium Battery Cell">
<StatusEffect type="OnContaining" target="This" Voltage="1.0" setvalue="true"/> <StatusEffect type="OnContaining" target="This" Voltage="1.0" setvalue="true"/>
@@ -9,11 +9,12 @@
<Sprite texture ="railgunetc.png" depth="0.01" sourcerect="64,180,47,76"/> <Sprite texture ="railgunetc.png" depth="0.01" sourcerect="64,180,47,76"/>
<Turret canbeselected = "true" linkable="true" barrelpos="23, 76" <Turret canbeselected="false" linkable="true" characterusable="false"
barrelpos="23, 76"
rotationlimits="90,90" rotationlimits="90,90"
powerconsumption="0.0"/> powerconsumption="0.0"/>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected="true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/> <requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="trigger_in"/> <input name="trigger_in"/>
</ConnectionPanel> </ConnectionPanel>
@@ -2,13 +2,18 @@
<Item <Item
name="Railgun" name="Railgun"
category="Machine" category="Machine"
interactthroughwalls="true"
interactdistance="10"
focusonselected="true" focusonselected="true"
offsetonselected="700" offsetonselected="700"
linkable="true"> linkable="true">
<Sprite texture="railgunbase.png" depth="0.01" sourcerect="0,0,256,256"/> <Sprite texture="railgunbase.png" depth="0.01" sourcerect="0,0,256,256"/>
<Turret barrelsprite="railgunbarrel.png" canbeselected = "true" linkable="true" origin="0.5, 0.85" barrelpos="128, 128" <StaticBody width="80" radius="80"/>
<Turret barrelsprite="railgunbarrel.png" canbeselected="false" characterusable="false" linkable="true"
origin="0.5, 0.85" barrelpos="128, 128"
rotationlimits="180,360" rotationlimits="180,360"
powerconsumption="20000.0" powerconsumption="20000.0"
showchargeindicator="true" showchargeindicator="true"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

@@ -40,7 +40,7 @@
<Body width="90" height="30" density="50"/> <Body width="90" height="30" density="50"/>
<Holdable slots="Any,RightHand+LeftHand" controlpose="true" <Holdable slots="Any,RightHand+LeftHand" controlpose="true"
holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/> holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7" holdangle="-40"/>
<RangedWeapon barrelpos="49,10" spread="1" unskilledspread="10"> <RangedWeapon barrelpos="49,10" spread="1" unskilledspread="10">
<Sound file="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/> <Sound file="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/>
@@ -120,14 +120,14 @@
<Item name="Polycarbonate Bar"/> <Item name="Polycarbonate Bar"/>
</Deconstruct> </Deconstruct>
<Sprite texture="weapons.png" sourcerect="24,102,37,19" depth="0.55"/> <Sprite texture="weapons.png" sourcerect="26,97,25,19" depth="0.55"/>
<Body width="37" height="18" density="50"/> <Body width="25" height="15" density="50"/>
<Holdable slots="Any,RightHand,LeftHand" controlpose="true" <Holdable slots="Any,RightHand,LeftHand" controlpose="true"
aimpos="75,18" handle1="-10,-5"/> aimpos="90,10" handle1="-11,-7"/>
<RangedWeapon barrelpos="37,3" spread="0" unskilledspread="2"> <RangedWeapon barrelpos="14,6" spread="0" unskilledspread="2">
<Sound file="Content/Items/Weapons/revolver.ogg" type="OnUse" range="3000"/> <Sound file="Content/Items/Weapons/revolver.ogg" type="OnUse" range="3000"/>
<StatusEffect type="OnUse"> <StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/> <Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/>
+5 -5
View File
@@ -16,11 +16,11 @@
<Item name="Headset" equip="true"> <Item name="Headset" equip="true">
<Item name="Battery Cell"/> <Item name="Battery Cell"/>
</Item> </Item>
<Item name="Revolver" equip="false"> <Item name="Revolver" equip="false">
<Item name="Revolver Round"/> <Item name="Revolver Round"/>
<Item name="Revolver Round"/> <Item name="Revolver Round"/>
<Item name="Revolver Round"/> <Item name="Revolver Round"/>
</Item> </Item>
</Items> </Items>
</Job> </Job>
@@ -13,7 +13,6 @@
<Item file="Content/Items/Diving/divinggear.xml" /> <Item file="Content/Items/Diving/divinggear.xml" />
<Item file="Content/Items/Door/doors.xml" /> <Item file="Content/Items/Door/doors.xml" />
<Item file="Content/Items/Electricity/lights.xml" /> <Item file="Content/Items/Electricity/lights.xml" />
<Item file="Content/Items/Electricity/monitors.xml" />
<Item file="Content/Items/Electricity/poweritems.xml" /> <Item file="Content/Items/Electricity/poweritems.xml" />
<Item file="Content/Items/Electricity/signalitems.xml" /> <Item file="Content/Items/Electricity/signalitems.xml" />
<Item file="Content/Items/Engine/engine.xml" /> <Item file="Content/Items/Engine/engine.xml" />
@@ -7,8 +7,8 @@
<Preset <Preset
name="Moderator" name="Moderator"
description="Allowed to manage round settings and kick players." description="Allowed to manage round settings, kick players and access server logs."
permissions="EndRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands"> permissions="EndRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog">
<Command name="clientlist"/> <Command name="clientlist"/>
<Command name="autorestart"/> <Command name="autorestart"/>
<Command name="autorestartinterval"/> <Command name="autorestartinterval"/>
@@ -21,8 +21,8 @@
<Preset <Preset
name="Admin" name="Admin"
description="Allowed to ban and kick players, manage round settings and use nearly all console commands." description="Allowed to ban and kick players, manage round settings, access server logs and use nearly all console commands."
permissions="EndRound,Kick,Ban,SelectSub,SelectMode,ManageCampaign,ConsoleCommands"> permissions="EndRound,Kick,Ban,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog">
<Command name="clientlist"/> <Command name="clientlist"/>
<Command name="autorestart"/> <Command name="autorestart"/>
<Command name="autorestartinterval"/> <Command name="autorestartinterval"/>
@@ -38,6 +38,9 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace); DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace);
return Vector2.Zero; return Vector2.Zero;
} }
@@ -54,6 +57,9 @@ namespace Barotrauma
#if DEBUG #if DEBUG
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace); DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace);
#endif #endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace);
return Vector2.Zero; return Vector2.Zero;
} }
@@ -274,7 +274,15 @@ namespace Barotrauma
private void UpdateEscape(float deltaTime) private void UpdateEscape(float deltaTime)
{ {
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition) * 5); if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
{
state = AIState.None;
return;
}
Vector2 escapeDir = Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition);
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
SteeringManager.SteeringManual(deltaTime, escapeDir * 5);
SteeringManager.SteeringWander(1.0f); SteeringManager.SteeringWander(1.0f);
SteeringManager.SteeringAvoid(deltaTime, 2f); SteeringManager.SteeringAvoid(deltaTime, 2f);
} }
@@ -484,8 +492,10 @@ namespace Barotrauma
if (dist < ConvertUnits.ToSimUnits(500.0f)) if (dist < ConvertUnits.ToSimUnits(500.0f))
{ {
Vector2 attackDir = Vector2.Normalize(Character.SimPosition - attackPosition);
if (!MathUtils.IsValid(attackDir)) attackDir = Vector2.UnitY;
steeringManager.SteeringSeek(attackPosition, -0.8f); steeringManager.SteeringSeek(attackPosition, -0.8f);
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(Character.SimPosition - attackPosition) * (1.0f - (dist / 500.0f))); steeringManager.SteeringManual(deltaTime, attackDir * (1.0f - (dist / 500.0f)));
} }
steeringManager.SteeringAvoid(deltaTime, 1.0f); steeringManager.SteeringAvoid(deltaTime, 1.0f);
@@ -63,6 +63,7 @@ namespace Barotrauma
character.SetInput(InputType.Aim, false, true); character.SetInput(InputType.Aim, false, true);
Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position); Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position);
if (!MathUtils.IsValid(enemyDiff)) enemyDiff = Rand.Vector(1.0f);
float weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi); float weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi);
Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle)); Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle));
@@ -30,13 +30,8 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager; var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (pathSteering == null) return;
if (pathSteering==null)
{
return;
}
if (character.AnimController.InWater) if (character.AnimController.InWater)
{ {
@@ -72,11 +67,11 @@ namespace Barotrauma
// - if reached the end of the path // - if reached the end of the path
// - if the target is unreachable // - if the target is unreachable
// - if the path requires going outside // - if the path requires going outside
if (pathSteering==null || (pathSteering.CurrentPath != null && if (pathSteering == null || (pathSteering.CurrentPath != null &&
(pathSteering.CurrentPath.NextNode == null || pathSteering.CurrentPath.Unreachable || pathSteering.CurrentPath.HasOutdoorsNodes))) (pathSteering.CurrentPath.NextNode == null || pathSteering.CurrentPath.Unreachable || pathSteering.CurrentPath.HasOutdoorsNodes)))
{ {
//steer away from edges of the hull //steer away from edges of the hull
if (character.AnimController.CurrentHull!=null) if (character.AnimController.CurrentHull != null)
{ {
float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X; float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X;
float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X; float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X;
@@ -114,7 +109,12 @@ namespace Barotrauma
return; return;
} }
if (currentTarget == null) return; if (currentTarget?.Entity == null) return;
if (currentTarget.Entity.Removed)
{
currentTarget = null;
return;
}
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition, 2.0f); character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition, 2.0f);
} }
@@ -92,7 +92,7 @@ namespace Barotrauma
public PathFinder(List<WayPoint> wayPoints, bool insideSubmarine = false) public PathFinder(List<WayPoint> wayPoints, bool insideSubmarine = false)
{ {
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine)); nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => (w.Submarine != null) == insideSubmarine));
foreach (WayPoint wp in wayPoints) foreach (WayPoint wp in wayPoints)
{ {
@@ -80,9 +80,9 @@ namespace Barotrauma
} }
float steeringSpeed = steering.Length(); float steeringSpeed = steering.Length();
if (steeringSpeed>speed) if (steeringSpeed > speed)
{ {
steering = Vector2.Normalize(steering) * Math.Abs(speed); steering = Vector2.Normalize(steering) * Math.Abs(speed);
} }
host.Steering = steering; host.Steering = steering;
@@ -97,12 +97,12 @@ namespace Barotrauma
targetVel = Vector2.Normalize(targetVel) * speed; targetVel = Vector2.Normalize(targetVel) * speed;
Vector2 newSteering = targetVel - host.Steering; Vector2 newSteering = targetVel - host.Steering;
if (newSteering==Vector2.Zero) return Vector2.Zero; if (newSteering == Vector2.Zero) return Vector2.Zero;
float steeringSpeed = (newSteering + host.Steering).Length(); float steeringSpeed = (newSteering + host.Steering).Length();
if (steeringSpeed > Math.Abs(speed)) if (steeringSpeed > Math.Abs(speed))
{ {
newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed); newSteering = Vector2.Normalize(newSteering) * Math.Abs(speed);
} }
return newSteering; return newSteering;
@@ -151,8 +151,7 @@ namespace Barotrauma
} }
else else
{ {
Structure closestStructure = closestBody.UserData as Structure; if (closestBody.UserData is Structure closestStructure)
if (closestStructure != null)
{ {
Vector2 obstaclePosition = Submarine.LastPickedPosition; Vector2 obstaclePosition = Submarine.LastPickedPosition;
if (closestStructure.IsHorizontal) if (closestStructure.IsHorizontal)
@@ -166,15 +165,17 @@ namespace Barotrauma
avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition); avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition);
} }
else if (closestBody.UserData is Item) else if (closestBody.UserData is Item item)
{ {
Item item = (Item)closestBody.UserData;
avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition); avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition);
} }
else else
{ {
avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition); avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition);
} }
//failed to normalize (the obstacle to avoid is at the same position as the character?)
// -> move to a random direction
if (!MathUtils.IsValid(avoidSteering)) avoidSteering = Rand.Vector(1.0f);
} }
} }
@@ -68,6 +68,7 @@ namespace Barotrauma
public virtual void DragCharacter(Character target) { } public virtual void DragCharacter(Character target) { }
public virtual void UpdateUseItem(bool allowMovement, Vector2 handPos) { }
} }
} }
@@ -64,8 +64,16 @@ namespace Barotrauma
} }
else else
{ {
Collider.LinearVelocity = (MainLimb.SimPosition - Collider.SimPosition) * 60.0f; Vector2 diff = (MainLimb.SimPosition - Collider.SimPosition);
Collider.SmoothRotate(MainLimb.Rotation); if (diff.LengthSquared() > 10.0f * 10.0f)
{
Collider.SetTransform(MainLimb.SimPosition, MainLimb.Rotation);
}
else
{
Collider.LinearVelocity = diff * 60.0f;
Collider.SmoothRotate(MainLimb.Rotation);
}
} }
if (character.IsDead && deathAnimTimer < deathAnimDuration) if (character.IsDead && deathAnimTimer < deathAnimDuration)
@@ -25,6 +25,8 @@ namespace Barotrauma
private float inWaterTimer; private float inWaterTimer;
private bool swimming; private bool swimming;
private float useItemTimer;
protected override float TorsoPosition protected override float TorsoPosition
{ {
get get
@@ -93,13 +95,21 @@ namespace Barotrauma
levitatingCollider = false; levitatingCollider = false;
Collider.FarseerBody.FixedRotation = false; Collider.FarseerBody.FixedRotation = false;
if (Math.Abs(Collider.Rotation-GetLimb(LimbType.Torso).Rotation)>Math.PI*0.6f) if (Math.Abs(Collider.Rotation - GetLimb(LimbType.Torso).Rotation) > Math.PI * 0.6f)
{ {
Collider.SetTransform(Collider.SimPosition, MathHelper.WrapAngle(Collider.Rotation + (float)Math.PI)); Collider.SetTransform(Collider.SimPosition, MathHelper.WrapAngle(Collider.Rotation + (float)Math.PI));
} }
Collider.SmoothRotate(GetLimb(LimbType.Torso).Rotation);
Collider.LinearVelocity = (GetLimb(LimbType.Waist).SimPosition - Collider.SimPosition) * 20.0f;
Vector2 diff = GetLimb(LimbType.Waist).SimPosition - Collider.SimPosition;
if (diff.LengthSquared() > 10.0f * 10.0f)
{
Collider.SetTransform(GetLimb(LimbType.Waist).SimPosition, GetLimb(LimbType.Torso).Rotation);
}
else
{
Collider.LinearVelocity = diff * 20.0f;
Collider.SmoothRotate(GetLimb(LimbType.Torso).Rotation);
}
return; return;
} }
@@ -180,6 +190,12 @@ namespace Barotrauma
case Animation.UsingConstruction: case Animation.UsingConstruction:
default: default:
if (Anim == Animation.UsingConstruction)
{
useItemTimer -= deltaTime;
if (useItemTimer <= 0.0f) Anim = Animation.None;
}
if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter); if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter);
//0.5 second delay for switching between swimming and walking //0.5 second delay for switching between swimming and walking
@@ -1067,8 +1083,31 @@ namespace Barotrauma
} }
} }
} }
Limb pullLimb = i == 0 ? leftHand : rightHand; Limb pullLimb = i == 0 ? leftHand : rightHand;
if (GameMain.Client == null)
{
//stop dragging if there's something between the pull limb and the target limb
Vector2 sourceSimPos = pullLimb.SimPosition;
Vector2 targetSimPos = targetLimb.SimPosition;
if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
{
targetSimPos -= character.Submarine.SimPosition;
}
else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
{
sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
}
var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
if (body != null)
{
character.DeselectCharacter();
return;
}
}
if (i == 1 && inWater) if (i == 1 && inWater)
{ {
targetLimb.pullJoint.Enabled = false; targetLimb.pullJoint.Enabled = false;
@@ -1204,25 +1243,29 @@ namespace Barotrauma
if (itemPos == Vector2.Zero || Anim == Animation.Climbing || usingController) if (itemPos == Vector2.Zero || Anim == Animation.Climbing || usingController)
{ {
if (character.SelectedItems[1] == item)
{
transformedHoldPos = leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
if (character.SelectedItems[0] == item) if (character.SelectedItems[0] == item)
{ {
if (rightHand.IsSevered) return;
transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0]; transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0];
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
} }
else if (character.SelectedItems[1] == item)
{
if (leftHand.IsSevered) return;
transformedHoldPos = leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
} }
else else
{ {
if (character.SelectedItems[0] == item) if (character.SelectedItems[0] == item)
{ {
if (rightHand.IsSevered) return;
rightHand.Disabled = true; rightHand.Disabled = true;
} }
if (character.SelectedItems[1] == item) if (character.SelectedItems[1] == item)
{ {
if (leftHand.IsSevered) return;
leftHand.Disabled = true; leftHand.Disabled = true;
} }
@@ -1235,6 +1278,24 @@ namespace Barotrauma
Vector2 currItemPos = (character.SelectedItems[0] == item) ? Vector2 currItemPos = (character.SelectedItems[0] == item) ?
rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0] : rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0] :
leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1]; leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
if (!MathUtils.IsValid(currItemPos))
{
string errorMsg = "Attempted to move the item \"" + item + "\" to an invalid position in HumanidAnimController.HoldItem: " +
currItemPos + ", rightHandPos: " + rightHand.pullJoint.WorldAnchorA + ", leftHandPos: " + leftHand.pullJoint.WorldAnchorA +
", handlePos[0]: " + handlePos[0] + ", handlePos[1]: " + handlePos[1] +
", transformedHandlePos[0]: " + transformedHandlePos[0] + ", transformedHandlePos[1]:" + transformedHandlePos[1] +
", item pos: " + item.SimPosition + ", itemAngle: " + itemAngle +
", collider pos: " + character.SimPosition;
DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"HumanoidAnimController.HoldItem:InvalidPos:" + character.Name + item.Name,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
return;
}
item.SetTransform(currItemPos, itemAngle); item.SetTransform(currItemPos, itemAngle);
//item.SetTransform(MathUtils.SmoothStep(item.body.SimPosition, transformedHoldPos + bodyVelocity, 0.5f), itemAngle); //item.SetTransform(MathUtils.SmoothStep(item.body.SimPosition, transformedHoldPos + bodyVelocity, 0.5f), itemAngle);
@@ -1277,6 +1338,33 @@ namespace Barotrauma
hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force); hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force);
} }
public override void UpdateUseItem(bool allowMovement, Vector2 handPos)
{
var leftHand = GetLimb(LimbType.LeftHand);
var rightHand = GetLimb(LimbType.RightHand);
useItemTimer = 0.5f;
Anim = Animation.UsingConstruction;
if (!allowMovement)
{
TargetMovement = Vector2.Zero;
TargetDir = handPos.X > character.SimPosition.X ? Direction.Right : Direction.Left;
if (Vector2.Distance(character.SimPosition, handPos) > 1.0f)
{
TargetMovement = Vector2.Normalize(handPos - character.SimPosition);
}
}
leftHand.Disabled = true;
leftHand.pullJoint.Enabled = true;
leftHand.pullJoint.WorldAnchorB = handPos;
rightHand.Disabled = true;
rightHand.pullJoint.Enabled = true;
rightHand.pullJoint.WorldAnchorB = handPos;
}
public override void Flip() public override void Flip()
{ {
base.Flip(); base.Flip();
@@ -3,6 +3,7 @@ using FarseerPhysics;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints; using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -25,6 +26,11 @@ namespace Barotrauma
if (limbs == null) if (limbs == null)
{ {
DebugConsole.ThrowError("Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this)); DebugConsole.ThrowError("Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this));
GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.Limbs:AccessRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this) + "\n" + Environment.StackTrace);
return new Limb[0]; return new Limb[0];
} }
return limbs; return limbs;
@@ -42,6 +48,7 @@ namespace Barotrauma
frozen = value; frozen = value;
Collider.PhysEnabled = !frozen; Collider.PhysEnabled = !frozen;
if (frozen && MainLimb != null) MainLimb.pullJoint.WorldAnchorB = MainLimb.SimPosition;
} }
} }
@@ -88,6 +95,10 @@ namespace Barotrauma
protected List<PhysicsBody> collider; protected List<PhysicsBody> collider;
protected int colliderIndex = 0; protected int colliderIndex = 0;
private Category prevCollisionCategory = Category.None;
private Body outsideCollisionBlocker;
public PhysicsBody Collider public PhysicsBody Collider
{ {
get get
@@ -269,11 +280,7 @@ namespace Barotrauma
get { return ignorePlatforms; } get { return ignorePlatforms; }
set set
{ {
if (ignorePlatforms == value) return;
ignorePlatforms = value; ignorePlatforms = value;
UpdateCollisionCategories();
} }
} }
@@ -352,21 +359,29 @@ namespace Barotrauma
if (collider[0] == null) if (collider[0] == null)
{ {
DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!"); DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!");
collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f); collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f)
collider[0].UserData = character; {
collider[0].BodyType = BodyType.Dynamic; UserData = character,
collider[0].CollisionCategories = Physics.CollisionCharacter; BodyType = BodyType.Dynamic,
CollisionCategories = Physics.CollisionCharacter
};
collider[0].FarseerBody.AngularDamping = 5.0f; collider[0].FarseerBody.AngularDamping = 5.0f;
collider[0].FarseerBody.FixedRotation = true; collider[0].FarseerBody.FixedRotation = true;
collider[0].FarseerBody.OnCollision += OnLimbCollision; collider[0].FarseerBody.OnCollision += OnLimbCollision;
} }
outsideCollisionBlocker = BodyFactory.CreateEdge(GameMain.World, -Vector2.UnitX * 2.0f, Vector2.UnitX * 2.0f, "blocker");
outsideCollisionBlocker.BodyType = BodyType.Static;
outsideCollisionBlocker.CollisionCategories = Physics.CollisionWall;
outsideCollisionBlocker.CollidesWith = Physics.CollisionCharacter;
outsideCollisionBlocker.Enabled = false;
UpdateCollisionCategories(); UpdateCollisionCategories();
foreach (var joint in LimbJoints) foreach (var joint in LimbJoints)
{ {
joint.BodyB.SetTransform( joint.LimbB?.body?.SetTransform(
joint.BodyA.Position + (joint.LocalAnchorA - joint.LocalAnchorB)*0.1f, joint.BodyA.Position + (joint.LocalAnchorA - joint.LocalAnchorB) * 0.1f,
(joint.LowerLimit + joint.UpperLimit) / 2.0f); (joint.LowerLimit + joint.UpperLimit) / 2.0f);
} }
@@ -391,7 +406,7 @@ namespace Barotrauma
Limb torso = GetLimb(LimbType.Torso); Limb torso = GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head); Limb head = GetLimb(LimbType.Head);
MainLimb = torso == null ? head : torso; MainLimb = torso ?? head;
} }
public void AddJoint(XElement subElement, float scale = 1.0f) public void AddJoint(XElement subElement, float scale = 1.0f)
@@ -489,6 +504,9 @@ namespace Barotrauma
if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) return false; if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) return false;
//only collide with the ragdoll's own blocker
if (f2.Body.UserData as string == "blocker" && f2.Body != outsideCollisionBlocker) return false;
//always collides with bodies other than structures //always collides with bodies other than structures
if (structure == null) if (structure == null)
{ {
@@ -739,7 +757,15 @@ namespace Barotrauma
public void FindHull(Vector2? worldPosition = null, bool setSubmarine = true) public void FindHull(Vector2? worldPosition = null, bool setSubmarine = true)
{ {
Vector2 findPos = worldPosition==null ? this.WorldPosition : (Vector2)worldPosition; Vector2 findPos = worldPosition == null ? this.WorldPosition : (Vector2)worldPosition;
if (!MathUtils.IsValid(findPos))
{
GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.FindHull:InvalidPosition",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to find a hull at an invalid position (" + findPos + ")\n" + Environment.StackTrace);
return;
}
Hull newHull = Hull.FindHull(findPos, currentHull); Hull newHull = Hull.FindHull(findPos, currentHull);
@@ -754,10 +780,13 @@ namespace Barotrauma
//this is preferable to the cost of using continuous collision detection for the character collider //this is preferable to the cost of using continuous collision detection for the character collider
if (newHull != null) if (newHull != null)
{ {
Vector2 hullDiff = WorldPosition - newHull.WorldPosition;
Vector2 moveDir = hullDiff.LengthSquared() < 0.001f ? Vector2.UnitY : Vector2.Normalize(hullDiff);
//find a position 32 units away from the hull //find a position 32 units away from the hull
Vector2? intersection = MathUtils.GetLineRectangleIntersection( Vector2? intersection = MathUtils.GetLineRectangleIntersection(
newHull.WorldPosition, newHull.WorldPosition,
newHull.WorldPosition + Vector2.Normalize(WorldPosition - newHull.WorldPosition) * Math.Max(newHull.Rect.Width, newHull.Rect.Height), newHull.WorldPosition + moveDir * Math.Max(newHull.Rect.Width, newHull.Rect.Height),
new Rectangle(newHull.WorldRect.X - 32, newHull.WorldRect.Y + 32, newHull.WorldRect.Width + 64, newHull.Rect.Height + 64)); new Rectangle(newHull.WorldRect.X - 32, newHull.WorldRect.Y + 32, newHull.WorldRect.Width + 64, newHull.Rect.Height + 64));
if (intersection != null) if (intersection != null)
@@ -774,15 +803,7 @@ namespace Barotrauma
//in -> out //in -> out
if (newHull == null && currentHull.Submarine != null) if (newHull == null && currentHull.Submarine != null)
{ {
for (int i = -1; i < 2; i += 2)
{
//don't teleport outside the sub if right next to a hull
if (Hull.FindHull(findPos + new Vector2(Submarine.GridSize.X * 4.0f * i, 0.0f), currentHull) != null) return;
if (Hull.FindHull(findPos + new Vector2(0.0f, Submarine.GridSize.Y * 4.0f * i), currentHull) != null) return;
}
if (Gap.FindAdjacent(currentHull.ConnectedGaps, findPos, 150.0f) != null) return; if (Gap.FindAdjacent(currentHull.ConnectedGaps, findPos, 150.0f) != null) return;
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity); Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity);
} }
//out -> in //out -> in
@@ -799,10 +820,49 @@ namespace Barotrauma
} }
CurrentHull = newHull; CurrentHull = newHull;
character.Submarine = currentHull?.Submarine;
}
character.Submarine = currentHull == null ? null : currentHull.Submarine; private void PreventOutsideCollision()
{
if (currentHull?.Submarine == null)
{
outsideCollisionBlocker.Enabled = false;
return;
}
UpdateCollisionCategories(); var connectedGaps = currentHull.ConnectedGaps.Where(g => !g.IsRoomToRoom);
foreach (Gap gap in connectedGaps)
{
if (gap.IsHorizontal)
{
if (character.Position.Y > gap.Rect.Y || character.Position.Y < gap.Rect.Y - gap.Rect.Height) continue;
if (Math.Sign(gap.Rect.Center.X - currentHull.Rect.Center.X) !=
Math.Sign(character.Position.X - currentHull.Rect.Center.X))
{
continue;
}
}
else
{
if (character.Position.X < gap.Rect.X || character.Position.X > gap.Rect.Right) continue;
if (Math.Sign((gap.Rect.Y - gap.Rect.Height / 2) - (currentHull.Rect.Center.X - currentHull.Rect.Height / 2)) !=
Math.Sign(character.Position.X - (currentHull.Rect.Center.X - currentHull.Rect.Height / 2)))
{
continue;
}
}
if (!gap.GetOutsideCollider(out Vector2? outsideColliderPos, out Vector2? outsideColliderNormal)) continue;
outsideCollisionBlocker.SetTransform(
outsideColliderPos.Value - currentHull.Submarine.SimPosition,
MathUtils.VectorToAngle(outsideColliderNormal.Value) - MathHelper.PiOver2);
outsideCollisionBlocker.Enabled = true;
return;
}
outsideCollisionBlocker.Enabled = false;
} }
public void Teleport(Vector2 moveAmount, Vector2 velocityChange) public void Teleport(Vector2 moveAmount, Vector2 velocityChange)
@@ -843,6 +903,9 @@ namespace Barotrauma
wall | Physics.CollisionProjectile | Physics.CollisionStairs wall | Physics.CollisionProjectile | Physics.CollisionStairs
: wall | Physics.CollisionProjectile | Physics.CollisionPlatform | Physics.CollisionStairs; : wall | Physics.CollisionProjectile | Physics.CollisionPlatform | Physics.CollisionStairs;
if (collisionCategory == prevCollisionCategory) return;
prevCollisionCategory = collisionCategory;
Collider.CollidesWith = collisionCategory; Collider.CollidesWith = collisionCategory;
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
@@ -868,10 +931,12 @@ namespace Barotrauma
UpdateNetPlayerPosition(deltaTime); UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider(); CheckDistFromCollider();
UpdateCollisionCategories();
Vector2 flowForce = Vector2.Zero; Vector2 flowForce = Vector2.Zero;
FindHull(); FindHull();
PreventOutsideCollision();
splashSoundTimer -= deltaTime; splashSoundTimer -= deltaTime;
@@ -1090,20 +1155,17 @@ namespace Barotrauma
float tfloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction; float tfloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction;
float targetY = tfloorY + Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor; float targetY = tfloorY + Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor;
if (Math.Abs(Collider.SimPosition.Y - targetY) > 0.01f && Collider.SimPosition.Y<targetY && !forceImmediate) if (Math.Abs(Collider.SimPosition.Y - targetY) > 0.01f)
{ {
Vector2 newSpeed = Collider.LinearVelocity; if (forceImmediate)
newSpeed.Y = (targetY - Collider.SimPosition.Y)*5.0f; {
Collider.LinearVelocity = newSpeed; Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, 0);
} Collider.SetTransform(new Vector2(Collider.SimPosition.X, targetY), Collider.Rotation);
else }
{ else
Vector2 newSpeed = Collider.LinearVelocity; {
newSpeed.Y = 0.0f; Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, (targetY - Collider.SimPosition.Y) * 5.0f);
Collider.LinearVelocity = newSpeed; }
Vector2 newPos = Collider.SimPosition;
newPos.Y = targetY;
Collider.SetTransform(newPos, Collider.Rotation);
} }
} }
} }
@@ -1165,6 +1227,16 @@ namespace Barotrauma
public void SetPosition(Vector2 simPosition, bool lerp = false) public void SetPosition(Vector2 simPosition, bool lerp = false)
{ {
if (!MathUtils.IsValid(simPosition))
{
DebugConsole.ThrowError("Attempted to move a ragdoll (" + character.Name + ") to an invalid position (" + simPosition + "). " + Environment.StackTrace);
GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.SetPosition:InvalidPosition",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to move a ragdoll (" + character.Name + ") to an invalid position (" + simPosition + "). " + Environment.StackTrace);
return;
}
Vector2 limbMoveAmount = simPosition - MainLimb.SimPosition; Vector2 limbMoveAmount = simPosition - MainLimb.SimPosition;
Collider.SetTransform(simPosition, Collider.Rotation); Collider.SetTransform(simPosition, Collider.Rotation);
@@ -1245,9 +1317,9 @@ namespace Barotrauma
{ {
//set the position of the ragdoll to make sure limbs don't get stuck inside walls when re-enabling collisions //set the position of the ragdoll to make sure limbs don't get stuck inside walls when re-enabling collisions
SetPosition(Collider.SimPosition, true); SetPosition(Collider.SimPosition, true);
UpdateCollisionCategories();
collisionsDisabled = false; collisionsDisabled = false;
//force collision categories to be updated
prevCollisionCategory = Category.None;
} }
} }
@@ -1312,8 +1384,24 @@ namespace Barotrauma
character.AnimController.Anim = AnimController.Animation.None; character.AnimController.Anim = AnimController.Animation.None;
} }
Collider.LinearVelocity = Vector2.Zero; Vector2 newVelocity = Vector2.Zero;
Collider.CorrectPosition(character.MemState, deltaTime, out overrideTargetMovement); Vector2 newPosition = Collider.SimPosition;
Collider.CorrectPosition(character.MemState, deltaTime, out newVelocity, out newPosition);
newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity)) newVelocity = Vector2.Zero;
overrideTargetMovement = newVelocity;
Collider.LinearVelocity = newVelocity;
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
if (distSqrd > 10.0f)
{
SetPosition(newPosition);
}
else if (distSqrd > 0.01f)
{
Collider.SetTransform(newPosition, Collider.Rotation);
}
//unconscious/dead characters can't correct their position using AnimController movement //unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually // -> we need to correct it manually
@@ -1431,10 +1519,18 @@ namespace Barotrauma
} }
} }
Collider.SetTransform(Collider.SimPosition + positionError, Collider.Rotation + rotationError); float errorMagnitude = positionError.Length();
foreach (Limb limb in Limbs) if (errorMagnitude > 0.01f)
{ {
limb.body.SetTransform(limb.body.SimPosition + positionError, limb.body.Rotation); Collider.SetTransform(Collider.SimPosition + positionError, Collider.Rotation + rotationError);
if (errorMagnitude > 0.5f)
{
character.MemLocalState.Clear();
foreach (Limb limb in Limbs)
{
limb.body.SetTransform(limb.body.SimPosition + positionError, limb.body.Rotation);
}
}
} }
} }
@@ -1445,22 +1541,17 @@ namespace Barotrauma
private Vector2 GetFlowForce() private Vector2 GetFlowForce()
{ {
Vector2 limbPos = ConvertUnits.ToDisplayUnits(Limbs[0].SimPosition); Vector2 limbPos = Limbs[0].Position;
Vector2 force = Vector2.Zero; Vector2 force = Vector2.Zero;
foreach (MapEntity e in MapEntity.mapEntityList) foreach (Gap gap in Gap.GapList)
{ {
Gap gap = e as Gap; if (gap.Open <= 0.0f || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce.LengthSquared() < 0.01f) continue;
if (gap == null || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce == Vector2.Zero) continue;
Vector2 gapPos = gap.SimPosition; Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos); float dist = Vector2.Distance(limbPos, gapPos);
force += Vector2.Normalize(gap.LerpedFlowForce) * (Math.Max(gap.LerpedFlowForce.Length() - dist, 0.0f) / 500.0f); force += Vector2.Normalize(gap.LerpedFlowForce) * (Math.Max(gap.LerpedFlowForce.Length() - dist, 0.0f) / 500.0f);
} }
if (force.Length() > 20.0f) return force;
return force; return force;
} }
@@ -1526,6 +1617,12 @@ namespace Barotrauma
b.Remove(); b.Remove();
} }
if (outsideCollisionBlocker != null)
{
GameMain.World.RemoveBody(outsideCollisionBlocker);
outsideCollisionBlocker = null;
}
if (LimbJoints != null) if (LimbJoints != null)
{ {
foreach (RevoluteJoint joint in LimbJoints) foreach (RevoluteJoint joint in LimbJoints)
@@ -144,6 +144,10 @@ namespace Barotrauma
{ {
get get
{ {
if (GameMain.Server != null && !GameMain.Server.AllowDisguises) return Name;
#if CLIENT
if (GameMain.Client != null && !GameMain.Client.AllowDisguises) return Name;
#endif
return info != null && !string.IsNullOrWhiteSpace(info.Name) ? info.Name + (info.DisplayName != info.Name ? " (as " + info.DisplayName + ")" : "") : SpeciesName; return info != null && !string.IsNullOrWhiteSpace(info.Name) ? info.Name + (info.DisplayName != info.Name ? " (as " + info.DisplayName + ")" : "") : SpeciesName;
} }
} }
@@ -1611,12 +1615,20 @@ namespace Barotrauma
//Health effects //Health effects
if (needsAir) UpdateOxygen(deltaTime); if (needsAir) UpdateOxygen(deltaTime);
Health -= bleeding * deltaTime; if (DoesBleed)
Bleeding -= BleedingDecreaseSpeed * deltaTime; {
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
}
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss); if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
if (!IsDead) LockHands = false; UpdateSightRange();
if (aiTarget != null) aiTarget.SoundRange = 0.0f;
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
//ragdoll button //ragdoll button
if (IsRagdolled) if (IsRagdolled)
@@ -1643,21 +1655,7 @@ namespace Barotrauma
selectedConstruction = null; selectedConstruction = null;
} }
UpdateSightRange();
if (aiTarget != null) aiTarget.SoundRange = 0.0f;
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
if (DoesBleed)
{
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
}
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
if (!IsDead) LockHands = false; if (!IsDead) LockHands = false;
//CPR stuff is handled in the UpdateCPR function in HumanoidAnimController
} }
partial void UpdateControlled(float deltaTime, Camera cam); partial void UpdateControlled(float deltaTime, Camera cam);
@@ -1960,7 +1958,7 @@ namespace Barotrauma
characterType = "Enemy"; characterType = "Enemy";
else if (AIController is HumanAIController) else if (AIController is HumanAIController)
characterType = "AICrew"; characterType = "AICrew";
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath); GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath);
} }
if (OnDeath != null) OnDeath(this, causeOfDeath); if (OnDeath != null) OnDeath(this, causeOfDeath);
@@ -1997,7 +1995,7 @@ namespace Barotrauma
} }
partial void KillProjSpecific(); partial void KillProjSpecific();
public void Revive(bool isNetworkMessage) public void Revive()
{ {
if (Removed) if (Removed)
{ {
@@ -2024,6 +2022,7 @@ namespace Barotrauma
foreach (Limb limb in AnimController.Limbs) foreach (Limb limb in AnimController.Limbs)
{ {
limb.body.Enabled = true;
limb.IsSevered = false; limb.IsSevered = false;
} }
@@ -2044,6 +2043,9 @@ namespace Barotrauma
base.Remove(); base.Remove();
if (selectedItems[0] != null) selectedItems[0].Drop(this);
if (selectedItems[1] != null) selectedItems[1].Drop(this);
if (info != null) info.Remove(); if (info != null) info.Remove();
CharacterList.Remove(this); CharacterList.Remove(this);
@@ -2054,9 +2056,6 @@ namespace Barotrauma
if (AnimController != null) AnimController.Remove(); if (AnimController != null) AnimController.Remove();
if (selectedItems[0] != null) selectedItems[0].Drop(this);
if (selectedItems[1] != null) selectedItems[1].Drop(this);
foreach (Character c in CharacterList) foreach (Character c in CharacterList)
{ {
if (c.focusedCharacter == this) c.focusedCharacter = null; if (c.focusedCharacter == this) c.focusedCharacter = null;
@@ -17,11 +17,16 @@ namespace Barotrauma
get get
{ {
string disguiseName = "?"; string disguiseName = "?";
if (Character == null || !Character.HideFace) if (Character == null || !Character.HideFace || (GameMain.Server != null && !GameMain.Server.AllowDisguises))
{ {
return Name; return Name;
} }
#if CLIENT
if (GameMain.Client != null && !GameMain.Client.AllowDisguises)
{
return Name;
}
#endif
if (Character.Inventory != null) if (Character.Inventory != null)
{ {
int cardSlotIndex = Character.Inventory.FindLimbSlot(InvSlotType.Card); int cardSlotIndex = Character.Inventory.FindLimbSlot(InvSlotType.Card);
@@ -84,6 +84,11 @@ namespace Barotrauma
public bool isSynced = false; public bool isSynced = false;
public string OwnerClientIP;
public string OwnerClientName;
public bool ClientDisconnected;
public float KillDisconnectedTimer;
public List<CharacterStateInfo> MemState public List<CharacterStateInfo> MemState
{ {
get { return memState; } get { return memState; }
@@ -1,4 +1,5 @@
using System.Collections.Generic; using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -136,6 +137,11 @@ namespace Barotrauma
item.Description = spawnPoint.IdCardDesc; item.Description = spawnPoint.IdCardDesc;
} }
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = character.TeamID;
}
if (parentItem != null) parentItem.Combine(item); if (parentItem != null) parentItem.Combine(item);
foreach (XElement childItemElement in itemElement.Elements()) foreach (XElement childItemElement in itemElement.Elements())
@@ -85,6 +85,7 @@ namespace Barotrauma
set set
{ {
isSevered = value; isSevered = value;
if (!isSevered) severedFadeOutTimer = 0.0f;
#if CLIENT #if CLIENT
if (isSevered) damage = 100.0f; if (isSevered) damage = 100.0f;
#endif #endif
@@ -190,6 +190,17 @@ namespace Barotrauma
NewMessage("- Traitor " + t.Character.Name + "'s target is " + t.TargetCharacter.Name + ".", Color.Cyan); NewMessage("- Traitor " + t.Character.Name + "'s target is " + t.TargetCharacter.Name + ".", Color.Cyan);
} }
NewMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", Color.Cyan); NewMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", Color.Cyan);
},
null,
(Client client, Vector2 cursorPos, string[] args) =>
{
TraitorManager traitorManager = GameMain.Server.TraitorManager;
if (traitorManager == null) return;
foreach (Traitor t in traitorManager.TraitorList)
{
GameMain.Server.SendConsoleMessage("- Traitor " + t.Character.Name + "'s target is " + t.TargetCharacter.Name + ".", client);
}
GameMain.Server.SendConsoleMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", client);
})); }));
commands.Add(new Command("itemlist", "itemlist: List all the item prefabs available for spawning.", (string[] args) => commands.Add(new Command("itemlist", "itemlist: List all the item prefabs available for spawning.", (string[] args) =>
@@ -249,7 +260,7 @@ namespace Barotrauma
}; };
})); }));
commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory/random/[name]]: Spawn an item at the position of the cursor, in the inventory of the controlled character, in the inventory of the client with the given name, or at a random spawnpoint if the last parameter is omitted or \"random\".", commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory/cargo/random/[name]]: Spawn an item at the position of the cursor, in the inventory of the controlled character, in the inventory of the client with the given name, or at a random spawnpoint if the last parameter is omitted or \"random\".",
(string[] args) => (string[] args) =>
{ {
SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Character.Controlled, out string errorMsg); SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Character.Controlled, out string errorMsg);
@@ -1323,7 +1334,7 @@ namespace Barotrauma
Character revivedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args); Character revivedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (revivedCharacter == null) return; if (revivedCharacter == null) return;
revivedCharacter.Revive(false); revivedCharacter.Revive();
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
foreach (Client c in GameMain.Server.ConnectedClients) foreach (Client c in GameMain.Server.ConnectedClients)
@@ -1342,7 +1353,7 @@ namespace Barotrauma
Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args); Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (revivedCharacter == null) return; if (revivedCharacter == null) return;
revivedCharacter.Revive(false); revivedCharacter.Revive();
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
foreach (Client c in GameMain.Server.ConnectedClients) foreach (Client c in GameMain.Server.ConnectedClients)
@@ -1502,6 +1513,13 @@ namespace Barotrauma
{ {
Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args); Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null); killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
})); }));
commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) => commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) =>
@@ -1712,7 +1730,7 @@ namespace Barotrauma
var itemContainer = item.GetComponent<ItemContainer>(); var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null) if (itemContainer != null)
{ {
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState }); GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState, 0 });
} }
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status }); GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status });
@@ -1895,9 +1913,18 @@ namespace Barotrauma
return; return;
} }
if (string.IsNullOrWhiteSpace(command)) return; if (string.IsNullOrWhiteSpace(command) || command == "\\" || command == "\n") return;
string[] splitCommand = SplitCommand(command); string[] splitCommand = SplitCommand(command);
if (splitCommand.Length == 0)
{
ThrowError("Failed to execute command \"" + command + "\"!");
GameAnalyticsManager.AddErrorEventOnce(
"DebugConsole.ExecuteCommand:LengthZero",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to execute command \"" + command + "\"!");
return;
}
if (!splitCommand[0].ToLowerInvariant().Equals("admin")) if (!splitCommand[0].ToLowerInvariant().Equals("admin"))
{ {
@@ -1976,6 +2003,13 @@ namespace Barotrauma
return; return;
} }
if (!MathUtils.IsValid(cursorWorldPos))
{
GameMain.Server.SendConsoleMessage("Could not execute command \"" + command + "\" - invalid cursor position.", client);
NewMessage(client.Name + " attempted to execute the console command \"" + command + "\" with invalid cursor position.", Color.White);
return;
}
try try
{ {
matchingCommand.ServerExecuteOnClientRequest(client, cursorWorldPos, splitCommand.Skip(1).ToArray()); matchingCommand.ServerExecuteOnClientRequest(client, cursorWorldPos, splitCommand.Skip(1).ToArray());
@@ -2139,57 +2173,55 @@ namespace Barotrauma
Vector2? spawnPos = null; Vector2? spawnPos = null;
Inventory spawnInventory = null; Inventory spawnInventory = null;
if (args.Length > 1) int extraParams = 0;
switch (args.Last().ToLowerInvariant())
{ {
switch (args[1]) case "cursor":
{ extraParams = 1;
case "cursor": spawnPos = cursorPos;
spawnPos = cursorPos; break;
break; case "inventory":
case "inventory": extraParams = 1;
spawnInventory = controlledCharacter?.Inventory; spawnInventory = controlledCharacter == null ? null : controlledCharacter.Inventory;
break; break;
default: case "cargo":
//Check if last arg matches the name of an in-game player var wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub);
if (GameMain.Server != null) spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
{ break;
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower()); //Dont do a thing, random is basically Human points anyways - its in the help description.
if (client == null) case "random":
{ extraParams = 1;
NewMessage("No player found with the name \"" + args.Last() + "\". Spawning item at random location. If the player you want to give the item to has a space in their name, try surrounding their name with quotes (\").", Color.Red); return;
break; default:
} extraParams = 0;
else if (client.Character == null) break;
{
errorMsg = "The player \"" + args.Last() + "\" is connected, but hasn't spawned yet.";
return;
}
else
{
//If the last arg matches the name of an in-game player, set the destination to their inventory.
spawnInventory = client.Character.Inventory;
break;
}
}
else
{
var matchingCharacter = FindMatchingCharacter(args.Skip(1).ToArray());
if (matchingCharacter?.Inventory != null) spawnInventory = matchingCharacter.Inventory;
}
break;
}
} }
string itemName = args[0]; string itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant();
var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab; ItemPrefab itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null && extraParams == 0)
{
if (GameMain.Server != null)
{
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower());
if (client != null)
{
extraParams += 1;
itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant();
if (client.Character != null && client.Character.Name == args.Last().ToLower()) spawnInventory = client.Character.Inventory;
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
}
}
}
//Check again if the item can be found again after having checked for a character
if (itemPrefab == null) if (itemPrefab == null)
{ {
errorMsg = "Item \"" + itemName + "\" not found!"; errorMsg = "Item \"" + itemName + "\" not found!";
return; return;
} }
if (spawnPos == null && spawnInventory == null) if ((spawnPos == null || spawnPos == Vector2.Zero) && spawnInventory == null)
{ {
var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub);
spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
@@ -2198,6 +2230,7 @@ namespace Barotrauma
if (spawnPos != null) if (spawnPos != null)
{ {
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
} }
else if (spawnInventory != null) else if (spawnInventory != null)
{ {
@@ -37,7 +37,6 @@ namespace Barotrauma
Level.PositionType.Cave | Level.PositionType.MainPath | Level.PositionType.Ruin, 500.0f, 10000.0f, 30.0f); Level.PositionType.Cave | Level.PositionType.MainPath | Level.PositionType.Ruin, 500.0f, 10000.0f, 30.0f);
item = new Item(itemPrefab, position, null); item = new Item(itemPrefab, position, null);
item.MoveWithLevel = true;
item.body.FarseerBody.IsKinematic = true; item.body.FarseerBody.IsKinematic = true;
//try to find a nearby artifact holder (or any alien itemcontainer) and place the artifact inside it //try to find a nearby artifact holder (or any alien itemcontainer) and place the artifact inside it
@@ -66,7 +65,7 @@ namespace Barotrauma
switch (state) switch (state)
{ {
case 0: case 0:
if (item.ParentInventory!=null) item.body.FarseerBody.IsKinematic = false; if (item.ParentInventory != null) item.body.FarseerBody.IsKinematic = false;
if (item.CurrentHull == null) return; if (item.CurrentHull == null) return;
state = 1; state = 1;
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -145,6 +146,23 @@ namespace Barotrauma
subs[1].SetPosition(Level.Loaded.EndPosition - new Vector2(0.0f, 2000.0f)); subs[1].SetPosition(Level.Loaded.EndPosition - new Vector2(0.0f, 2000.0f));
subs[1].FlipX(); subs[1].FlipX();
//prevent wifi components from communicating between subs
List<WifiComponent> wifiComponents = new List<WifiComponent>();
foreach (Item item in Item.ItemList)
{
wifiComponents.AddRange(item.GetComponents<WifiComponent>());
}
foreach (WifiComponent wifiComponent in wifiComponents)
{
for (int i = 0; i < 2; i++)
{
if (wifiComponent.Item.Submarine == subs[i] || subs[i].DockedTo.Contains(wifiComponent.Item.Submarine))
{
wifiComponent.TeamID = subs[i].TeamID;
}
}
}
crews = new List<Character>[] { new List<Character>(), new List<Character>() }; crews = new List<Character>[] { new List<Character>(), new List<Character>() };
foreach (Submarine submarine in Submarine.Loaded) foreach (Submarine submarine in Submarine.Loaded)
@@ -208,7 +226,7 @@ namespace Barotrauma
} }
else else
{ {
if (winner>=0 && subs[winner] != null && if (winner >= 0 && subs[winner] != null &&
(winner == 0 && subs[winner].AtStartPosition) || (winner == 1 && subs[winner].AtEndPosition) && (winner == 0 && subs[winner].AtStartPosition) || (winner == 1 && subs[winner].AtEndPosition) &&
crews[winner].Any(c => !c.IsDead && c.Submarine == subs[winner])) crews[winner].Any(c => !c.IsDead && c.Submarine == subs[winner]))
{ {
@@ -50,7 +50,6 @@ namespace Barotrauma
Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f); Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
item = new Item(itemPrefab, position, null); item = new Item(itemPrefab, position, null);
item.MoveWithLevel = true;
item.body.FarseerBody.IsKinematic = true; item.body.FarseerBody.IsKinematic = true;
if (item.HasTag("alien")) if (item.HasTag("alien"))
@@ -1,16 +1,48 @@
using GameAnalyticsSDK.Net; using GameAnalyticsSDK.Net;
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
namespace Barotrauma namespace Barotrauma
{ {
public static class GameAnalyticsManager public static class GameAnalyticsManager
{ {
private static HashSet<string> sentEventIdentifiers = new HashSet<string>();
public static void Init() public static void Init()
{ {
#if DEBUB #if DEBUG
GameAnalytics.SetEnabledInfoLog(true); GameAnalytics.SetEnabledInfoLog(true);
#endif #endif
GameAnalytics.ConfigureBuild(GameMain.Version.ToString());
string exePath = Assembly.GetEntryAssembly().Location;
string exeName = null;
Md5Hash exeHash = null;
exeName = Path.GetFileNameWithoutExtension(exePath).Replace(":", "");
var md5 = MD5.Create();
try
{
using (var stream = File.OpenRead(exePath))
{
exeHash = new Md5Hash(stream);
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Error while calculating MD5 hash for the executable \"" + exePath + "\"", e);
}
GameAnalytics.ConfigureBuild(GameMain.Version.ToString()
+ (string.IsNullOrEmpty(exeName) ? "Unknown" : exeName) + ":"
+ ((exeHash?.ShortHash == null) ? "Unknown" : exeHash.ShortHash));
GameAnalytics.AddDesignEvent("Executable:"
+ (string.IsNullOrEmpty(exeName) ? "Unknown" : exeName) + ":"
+ ((exeHash?.ShortHash == null) ? "Unknown" : exeHash.ShortHash));
GameAnalytics.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor"); GameAnalytics.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor");
GameAnalytics.Initialize("a3a073c20982de7c15d21e840e149122", "9010ad9a671233b8d9610d76cec8c897d9ff3ba7"); GameAnalytics.Initialize("a3a073c20982de7c15d21e840e149122", "9010ad9a671233b8d9610d76cec8c897d9ff3ba7");
@@ -21,5 +53,47 @@ namespace Barotrauma
contentPackageName.Replace(":", "").Substring(0, Math.Min(32, contentPackageName.Length))); contentPackageName.Replace(":", "").Substring(0, Math.Min(32, contentPackageName.Length)));
} }
} }
/// <summary>
/// Adds an error event to GameAnalytics if an event with the same identifier has not been added yet.
/// </summary>
public static void AddErrorEventOnce(string identifier, EGAErrorSeverity errorSeverity, string message)
{
if (!GameSettings.SendUserStatistics) return;
if (sentEventIdentifiers.Contains(identifier)) return;
GameAnalytics.AddErrorEvent(errorSeverity, message);
sentEventIdentifiers.Add(identifier);
}
public static void AddDesignEvent(string eventID)
{
if (!GameSettings.SendUserStatistics) return;
GameAnalytics.AddDesignEvent(eventID);
}
public static void AddDesignEvent(string eventID, double value)
{
if (!GameSettings.SendUserStatistics) return;
GameAnalytics.AddDesignEvent(eventID, value);
}
public static void AddProgressionEvent(EGAProgressionStatus progressionStatus, string progression01)
{
if (!GameSettings.SendUserStatistics) return;
GameAnalytics.AddProgressionEvent(progressionStatus, progression01);
}
public static void AddProgressionEvent(EGAProgressionStatus progressionStatus, string progression01, string progression02)
{
if (!GameSettings.SendUserStatistics) return;
GameAnalytics.AddProgressionEvent(progressionStatus, progression01, progression02);
}
public static void SetCustomDimension01(string dimension)
{
if (!GameSettings.SendUserStatistics) return;
GameAnalytics.SetCustomDimension01(dimension);
}
} }
} }
@@ -6,26 +6,38 @@ using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class PurchasedItem
{
public ItemPrefab itemPrefab;
public int quantity;
public PurchasedItem(ItemPrefab itemPrefab, int quantity)
{
this.itemPrefab = itemPrefab;
this.quantity = quantity;
}
}
class CargoManager class CargoManager
{ {
private readonly List<ItemPrefab> purchasedItems; private readonly List<PurchasedItem> purchasedItems;
private readonly CampaignMode campaign; private readonly CampaignMode campaign;
public Action OnItemsChanged; public Action OnItemsChanged;
public List<ItemPrefab> PurchasedItems public List<PurchasedItem> PurchasedItems
{ {
get { return purchasedItems; } get { return purchasedItems; }
} }
public CargoManager(CampaignMode campaign) public CargoManager(CampaignMode campaign)
{ {
purchasedItems = new List<ItemPrefab>(); purchasedItems = new List<PurchasedItem>();
this.campaign = campaign; this.campaign = campaign;
} }
public void SetPurchasedItems(List<ItemPrefab> items) public void SetPurchasedItems(List<PurchasedItem> items)
{ {
purchasedItems.Clear(); purchasedItems.Clear();
purchasedItems.AddRange(items); purchasedItems.AddRange(items);
@@ -33,33 +45,53 @@ namespace Barotrauma
OnItemsChanged?.Invoke(); OnItemsChanged?.Invoke();
} }
public void PurchaseItem(ItemPrefab item) public void PurchaseItem(ItemPrefab item, int Quantity = 1)
{ {
campaign.Money -= item.Price; PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.itemPrefab == item);
purchasedItems.Add(item);
if(purchasedItem != null && Quantity == 1)
{
campaign.Money -= item.Price;
purchasedItem.quantity += 1;
}
else
{
campaign.Money -= (item.Price * Quantity);
purchasedItem = new PurchasedItem(item, Quantity);
purchasedItems.Add(purchasedItem);
}
OnItemsChanged?.Invoke(); OnItemsChanged?.Invoke();
} }
public void SellItem(ItemPrefab item) public void SellItem(ItemPrefab item, int quantity = 1)
{ {
campaign.Money += item.Price; campaign.Money += (item.Price * quantity);
purchasedItems.Remove(item); PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.itemPrefab == item);
if (purchasedItem != null && purchasedItem.quantity - quantity > 0)
{
purchasedItem.quantity -= quantity;
}
else
{
PurchasedItems.Remove(purchasedItem);
}
OnItemsChanged?.Invoke(); OnItemsChanged?.Invoke();
} }
public int GetTotalItemCost() public int GetTotalItemCost()
{ {
return purchasedItems.Sum(i => i.Price); return purchasedItems.Sum(i => (i.itemPrefab.Price * i.quantity));
} }
public void CreateItems() public void CreateItems()
{ {
CreateItems(purchasedItems); CreateItems(purchasedItems);
OnItemsChanged?.Invoke();
} }
public static void CreateItems(List<ItemPrefab> itemsToSpawn) public static void CreateItems(List<PurchasedItem> itemsToSpawn)
{ {
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub); WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub);
@@ -78,24 +110,25 @@ namespace Barotrauma
} }
Dictionary<ItemContainer, int> availableContainers = new Dictionary<ItemContainer, int>(); Dictionary<ItemContainer, int> availableContainers = new Dictionary<ItemContainer, int>();
foreach (ItemPrefab prefab in itemsToSpawn) ItemPrefab containerPrefab = null;
foreach (PurchasedItem pi in itemsToSpawn)
{ {
Vector2 position = new Vector2( Vector2 position = new Vector2(
Rand.Range(cargoRoom.Rect.X + 20, cargoRoom.Rect.Right - 20), Rand.Range(cargoRoom.Rect.X + 20, cargoRoom.Rect.Right - 20),
cargoRoom.Rect.Y - cargoRoom.Rect.Height + prefab.Size.Y / 2); cargoRoom.Rect.Y - cargoRoom.Rect.Height + pi.itemPrefab.Size.Y / 2);
ItemContainer itemContainer = null; ItemContainer itemContainer = null;
if (!string.IsNullOrEmpty(prefab.CargoContainerName)) if (!string.IsNullOrEmpty(pi.itemPrefab.CargoContainerName))
{ {
itemContainer = availableContainers.Keys.ToList().Find(ac => itemContainer = availableContainers.Keys.ToList().Find(ac =>
ac.Item.Prefab.NameMatches(prefab.CargoContainerName) || ac.Item.Prefab.NameMatches(pi.itemPrefab.CargoContainerName) ||
ac.Item.Prefab.Tags.Contains(prefab.CargoContainerName.ToLowerInvariant())); ac.Item.Prefab.Tags.Contains(pi.itemPrefab.CargoContainerName.ToLowerInvariant()));
if (itemContainer == null) if (itemContainer == null)
{ {
var containerPrefab = MapEntityPrefab.List.Find(ep => containerPrefab = MapEntityPrefab.List.Find(ep =>
ep.NameMatches(prefab.CargoContainerName) || ep.NameMatches(pi.itemPrefab.CargoContainerName) ||
(ep.Tags != null && ep.Tags.Contains(prefab.CargoContainerName.ToLowerInvariant()))) as ItemPrefab; (ep.Tags != null && ep.Tags.Contains(pi.itemPrefab.CargoContainerName.ToLowerInvariant()))) as ItemPrefab;
if (containerPrefab == null) if (containerPrefab == null)
{ {
@@ -117,41 +150,57 @@ namespace Barotrauma
} }
} }
} }
for (int i = 0; i < pi.quantity; i++)
{
if (itemContainer == null)
{
//no container, place at the waypoint
if (GameMain.Server != null)
{
Entity.Spawner.AddToSpawnQueue(pi.itemPrefab, position, wp.Submarine);
}
else
{
new Item(pi.itemPrefab, position, wp.Submarine);
}
continue;
}
//if the intial container has been removed due to it running out of space, add a new container
//of the same type and begin filling it
if (!availableContainers.ContainsKey(itemContainer))
{
Item containerItemOverFlow = new Item(containerPrefab, position, wp.Submarine);
itemContainer = containerItemOverFlow.GetComponent<ItemContainer>();
availableContainers.Add(itemContainer, itemContainer.Capacity);
if (GameMain.Server != null)
{
Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false);
}
}
if (itemContainer == null)
{
//no container, place at the waypoint
if (GameMain.Server != null)
{
Entity.Spawner.AddToSpawnQueue(prefab, position, wp.Submarine);
}
else
{
new Item(prefab, position, wp.Submarine);
}
}
else
{
//place in the container //place in the container
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
Entity.Spawner.AddToSpawnQueue(prefab, itemContainer.Inventory); Entity.Spawner.AddToSpawnQueue(pi.itemPrefab, itemContainer.Inventory);
} }
else else
{ {
var item = new Item(prefab, position, wp.Submarine); var item = new Item(pi.itemPrefab, position, wp.Submarine);
itemContainer.Inventory.TryPutItem(item, null); itemContainer.Inventory.TryPutItem(item, null);
} }
//reduce the number of available slots in the container //reduce the number of available slots in the container
availableContainers[itemContainer]--; //if there is a container
if (availableContainers[itemContainer] <= 0) if (availableContainers.ContainsKey(itemContainer))
{
availableContainers[itemContainer]--;
}
if (availableContainers.ContainsKey(itemContainer) && availableContainers[itemContainer] <= 0)
{ {
availableContainers.Remove(itemContainer); availableContainers.Remove(itemContainer);
} }
} }
} }
itemsToSpawn.Clear(); itemsToSpawn.Clear();
} }
} }
@@ -194,9 +194,10 @@ namespace Barotrauma
msg.Write(Money); msg.Write(Money);
msg.Write((UInt16)CargoManager.PurchasedItems.Count); msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (ItemPrefab ip in CargoManager.PurchasedItems) foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{ {
msg.Write((UInt16)MapEntityPrefab.List.IndexOf(ip)); msg.Write((UInt16)MapEntityPrefab.List.IndexOf(pi.itemPrefab));
msg.Write((UInt16)pi.quantity);
} }
} }
@@ -205,11 +206,12 @@ namespace Barotrauma
UInt16 selectedLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16();
UInt16 purchasedItemCount = msg.ReadUInt16(); UInt16 purchasedItemCount = msg.ReadUInt16();
List<ItemPrefab> purchasedItems = new List<ItemPrefab>(); List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++) for (int i = 0; i < purchasedItemCount; i++)
{ {
UInt16 itemPrefabIndex = msg.ReadUInt16(); UInt16 itemPrefabIndex = msg.ReadUInt16();
purchasedItems.Add(MapEntityPrefab.List[itemPrefabIndex] as ItemPrefab); UInt16 itemQuantity = msg.ReadUInt16();
purchasedItems.Add(new PurchasedItem(MapEntityPrefab.List[itemPrefabIndex] as ItemPrefab, itemQuantity));
} }
if (!sender.HasPermission(ClientPermissions.ManageCampaign)) if (!sender.HasPermission(ClientPermissions.ManageCampaign))
@@ -220,15 +222,15 @@ namespace Barotrauma
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
List<ItemPrefab> currentItems = new List<ItemPrefab>(CargoManager.PurchasedItems); List<PurchasedItem> currentItems = new List<PurchasedItem>(CargoManager.PurchasedItems);
foreach (ItemPrefab ip in currentItems) foreach (PurchasedItem pi in currentItems)
{ {
CargoManager.SellItem(ip); CargoManager.SellItem(pi.itemPrefab, pi.quantity);
} }
foreach (ItemPrefab ip in purchasedItems) foreach (PurchasedItem pi in purchasedItems)
{ {
CargoManager.PurchaseItem(ip); CargoManager.PurchaseItem(pi.itemPrefab, pi.quantity);
} }
} }
} }
@@ -182,7 +182,7 @@ namespace Barotrauma
StartRound(randomLevel, true, loadSecondSub); StartRound(randomLevel, true, loadSecondSub);
} }
public void StartRound(Level level, bool reloadSub = true, bool loadSecondSub = false) public void StartRound(Level level, bool reloadSub = true, bool loadSecondSub = false, bool mirrorLevel = false)
{ {
#if CLIENT #if CLIENT
GameMain.LightManager.LosEnabled = GameMain.NetworkMember == null || GameMain.NetworkMember.CharacterInfo != null; GameMain.LightManager.LosEnabled = GameMain.NetworkMember == null || GameMain.NetworkMember.CharacterInfo != null;
@@ -213,8 +213,7 @@ namespace Barotrauma
if (level != null) if (level != null)
{ {
level.Generate(); level.Generate(mirrorLevel);
submarine.SetPosition(submarine.FindSpawnPos(level.StartPosition - new Vector2(0.0f, 2000.0f))); submarine.SetPosition(submarine.FindSpawnPos(level.StartPosition - new Vector2(0.0f, 2000.0f)));
} }
@@ -229,18 +228,17 @@ namespace Barotrauma
if (GameMode != null) if (GameMode != null)
{ {
GameMode.MsgBox(); GameMode.MsgBox();
if (GameMode is MultiPlayerCampaign campaign && GameMain.Server != null) if (GameMode is MultiPlayerCampaign mpCampaign && GameMain.Server != null)
{ {
campaign.CargoManager.CreateItems(); mpCampaign.CargoManager.CreateItems();
} }
} }
if (GameSettings.SendUserStatistics) GameAnalyticsManager.AddDesignEvent("Submarine:" + submarine.Name);
{ GameAnalyticsManager.AddDesignEvent("Level", ToolBox.StringToInt(level.Seed));
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Submarine:" + submarine.Name); GameAnalyticsManager.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString())); GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
#if CLIENT #if CLIENT
roundSummary = new RoundSummary(this); roundSummary = new RoundSummary(this);
@@ -253,11 +251,10 @@ namespace Barotrauma
public void EndRound(string endMessage) public void EndRound(string endMessage)
{ {
if (Mission != null) Mission.End(); if (Mission != null) Mission.End();
if (GameSettings.SendUserStatistics) GameAnalyticsManager.AddProgressionEvent(
{ (Mission == null || Mission.Completed) ? GameAnalyticsSDK.Net.EGAProgressionStatus.Complete : GameAnalyticsSDK.Net.EGAProgressionStatus.Fail,
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent((Mission == null || Mission.Completed) ? GameAnalyticsSDK.Net.EGAProgressionStatus.Complete : GameAnalyticsSDK.Net.EGAProgressionStatus.Fail, GameMode.Name,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString())); (Mission == null ? "None" : Mission.GetType().ToString()));
}
#if CLIENT #if CLIENT
if (roundSummary != null) if (roundSummary != null)
@@ -164,7 +164,7 @@ namespace Barotrauma
GameMain.Config.Save("config.xml"); GameMain.Config.Save("config.xml");
} }
} }
public static bool ShowUserStatisticsPrompt { get; private set; } public static bool ShowUserStatisticsPrompt { get; set; }
public GameSettings(string filePath) public GameSettings(string filePath)
{ {
@@ -334,8 +334,12 @@ namespace Barotrauma
new XAttribute("soundvolume", soundVolume), new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging), new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs), new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
new XAttribute("enablesplashscreen", EnableSplashScreen), new XAttribute("enablesplashscreen", EnableSplashScreen));
new XAttribute("senduserstatistics", sendUserStatistics));
if (!ShowUserStatisticsPrompt)
{
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
}
if (WasGameUpdated) if (WasGameUpdated)
{ {
@@ -398,7 +402,16 @@ namespace Barotrauma
new XAttribute("gender", characterGender)); new XAttribute("gender", characterGender));
doc.Root.Add(playerElement); doc.Root.Add(playerElement);
doc.Save(filePath); try
{
doc.Save(filePath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving game settings failed.", e);
GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace);
}
} }
private IEnumerable<object> ApplyUnsavedChanges() private IEnumerable<object> ApplyUnsavedChanges()
@@ -50,6 +50,10 @@ namespace Barotrauma
} }
Items[slotIndex].ApplyStatusEffects(ActionType.OnUse, 1.0f, character); Items[slotIndex].ApplyStatusEffects(ActionType.OnUse, 1.0f, character);
//item may have been removed by a status effect
if (Items[slotIndex] == null) return true;
foreach (ItemComponent ic in Items[slotIndex].components) foreach (ItemComponent ic in Items[slotIndex].components)
{ {
if (ic.DeleteOnUse) if (ic.DeleteOnUse)
@@ -27,18 +27,14 @@ namespace Barotrauma.Items.Components
private Joint joint; private Joint joint;
private Hull[] hulls; private readonly Hull[] hulls = new Hull[2];
private ushort?[] hullIds; private Gap gap;
private Door door; private Door door;
private Body[] bodies; private Body[] bodies;
private Body doorBody; private Body doorBody;
private Gap gap;
private ushort? gapId;
private bool docked; private bool docked;
public int DockingDir public int DockingDir
@@ -115,8 +111,6 @@ namespace Barotrauma.Items.Components
IsActive = true; IsActive = true;
hullIds = new ushort?[2];
list.Add(this); list.Add(this);
} }
@@ -129,7 +123,7 @@ namespace Barotrauma.Items.Components
if (joint != null) if (joint != null)
{ {
CreateJoint(joint is WeldJoint); CreateJoint(joint is WeldJoint);
LinkHullsToGap(); LinkHullsToGaps();
} }
else if (dockingTarget.joint != null) else if (dockingTarget.joint != null)
{ {
@@ -138,7 +132,7 @@ namespace Barotrauma.Items.Components
{ {
dockingTarget.CreateJoint(dockingTarget.joint is WeldJoint); dockingTarget.CreateJoint(dockingTarget.joint is WeldJoint);
} }
dockingTarget.LinkHullsToGap(); dockingTarget.LinkHullsToGaps();
} }
} }
} }
@@ -208,21 +202,15 @@ namespace Barotrauma.Items.Components
Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y); Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir; dockingTarget.dockingDir = -dockingDir;
if (door != null && dockingTarget.door != null)
foreach (WayPoint wp in WayPoint.WayPointList)
{ {
if (wp.Submarine != item.Submarine || wp.SpawnType != SpawnType.Path) continue; WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => dockingTarget.door.LinkedGap == wp.ConnectedGap);
if (!Submarine.RectContains(item.Rect, wp.Position)) continue; if (myWayPoint != null && targetWayPoint != null)
foreach (WayPoint wp2 in WayPoint.WayPointList)
{ {
if (wp2.Submarine != dockingTarget.item.Submarine || wp2.SpawnType != SpawnType.Path) continue; myWayPoint.linkedTo.Add(targetWayPoint);
targetWayPoint.linkedTo.Add(myWayPoint);
if (!Submarine.RectContains(dockingTarget.item.Rect, wp2.Position)) continue;
wp.linkedTo.Add(wp2);
wp2.linkedTo.Add(wp);
} }
} }
@@ -238,35 +226,37 @@ namespace Barotrauma.Items.Components
{ {
if (GameMain.Client != null && !isNetworkMessage) return; if (GameMain.Client != null && !isNetworkMessage) return;
if (dockingTarget==null) if (dockingTarget == null)
{ {
DebugConsole.ThrowError("Error - attempted to lock a docking port that's not connected to anything"); DebugConsole.ThrowError("Error - attempted to lock a docking port that's not connected to anything");
return; return;
} }
else if (joint is WeldJoint)
{
//DebugConsole.ThrowError("Error - attempted to lock a docking port that's already locked");
return;
}
dockingDir = IsHorizontal ? if (!(joint is WeldJoint))
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) : {
Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir; dockingDir = IsHorizontal ?
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir;
#if CLIENT #if CLIENT
PlaySound(ActionType.OnSecondaryUse, item.WorldPosition); PlaySound(ActionType.OnSecondaryUse, item.WorldPosition);
#endif #endif
ConnectWireBetweenPorts(); ConnectWireBetweenPorts();
CreateJoint(true);
CreateJoint(true); if (GameMain.Server != null)
{
if (GameMain.Server != null) item.CreateServerEvent(this);
{ }
item.CreateServerEvent(this);
} }
List<MapEntity> removedEntities = item.linkedTo.Where(e => e.Removed).ToList();
foreach (MapEntity removed in removedEntities) item.linkedTo.Remove(removed);
if (!item.linkedTo.Any(e => e is Hull) && !dockingTarget.item.linkedTo.Any(e => e is Hull)) if (!item.linkedTo.Any(e => e is Hull) && !dockingTarget.item.linkedTo.Any(e => e is Hull))
{ {
CreateHull(); CreateHull();
@@ -345,17 +335,31 @@ namespace Barotrauma.Items.Components
private void CreateDoorBody() private void CreateDoorBody()
{ {
Vector2 position = ConvertUnits.ToSimUnits(item.Position + (dockingTarget.door.Item.WorldPosition - item.WorldPosition));
if (!MathUtils.IsValid(position))
{
string errorMsg =
"Attempted to create a door body at an invalid position (item pos: " + item.Position
+ ", item world pos: " + item.WorldPosition
+ ", docking target world pos: " + DockingTarget.door.Item.WorldPosition + ")\n" + Environment.StackTrace;
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"DockingPort.CreateDoorBody:InvalidPosition",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
position = Vector2.Zero;
}
doorBody = BodyFactory.CreateRectangle(GameMain.World, doorBody = BodyFactory.CreateRectangle(GameMain.World,
dockingTarget.door.Body.width, dockingTarget.door.Body.width,
dockingTarget.door.Body.height, dockingTarget.door.Body.height,
1.0f, 1.0f,
position,
dockingTarget.door); dockingTarget.door);
doorBody.CollisionCategories = Physics.CollisionWall; doorBody.CollisionCategories = Physics.CollisionWall;
doorBody.BodyType = BodyType.Static; doorBody.BodyType = BodyType.Static;
doorBody.SetTransform(
ConvertUnits.ToSimUnits(item.Position + (dockingTarget.door.Item.WorldPosition - item.WorldPosition)),
0.0f);
} }
private void CreateHull() private void CreateHull()
@@ -363,7 +367,6 @@ namespace Barotrauma.Items.Components
var hullRects = new Rectangle[] { item.WorldRect, dockingTarget.item.WorldRect }; var hullRects = new Rectangle[] { item.WorldRect, dockingTarget.item.WorldRect };
var subs = new Submarine[] { item.Submarine, dockingTarget.item.Submarine }; var subs = new Submarine[] { item.Submarine, dockingTarget.item.Submarine };
hulls = new Hull[2];
bodies = new Body[4]; bodies = new Body[4];
if (dockingTarget.door != null) if (dockingTarget.door != null)
@@ -392,6 +395,7 @@ namespace Barotrauma.Items.Components
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]); hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]);
hulls[i].AddToGrid(subs[i]); hulls[i].AddToGrid(subs[i]);
hulls[i].FreeID();
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
@@ -402,9 +406,6 @@ namespace Barotrauma.Items.Components
} }
gap = new Gap(new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height), true, subs[0]); gap = new Gap(new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height), true, subs[0]);
if (gapId != null) gap.ID = (ushort)gapId;
LinkHullsToGap();
} }
else else
{ {
@@ -422,25 +423,22 @@ namespace Barotrauma.Items.Components
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]); hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]);
hulls[i].AddToGrid(subs[i]); hulls[i].AddToGrid(subs[i]);
hulls[i].FreeID();
if (hullIds[i] != null) hulls[i].ID = (ushort)hullIds[i];
} }
gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y+2, hullRects[0].Width, 4), false, subs[0]); gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y+2, hullRects[0].Width, 4), false, subs[0]);
if (gapId != null) gap.ID = (ushort)gapId;
LinkHullsToGap();
} }
LinkHullsToGaps();
hulls[0].ShouldBeSaved = false;
hulls[1].ShouldBeSaved = false;
item.linkedTo.Add(hulls[0]); item.linkedTo.Add(hulls[0]);
item.linkedTo.Add(hulls[1]); item.linkedTo.Add(hulls[1]);
hullIds[0] = hulls[0].ID; gap.FreeID();
hullIds[1] = hulls[1].ID;
gap.DisableHullRechecks = true; gap.DisableHullRechecks = true;
gapId = gap.ID; gap.ShouldBeSaved = false;
item.linkedTo.Add(gap); item.linkedTo.Add(gap);
foreach (Body body in bodies) foreach (Body body in bodies)
@@ -453,7 +451,7 @@ namespace Barotrauma.Items.Components
} }
} }
private void LinkHullsToGap() private void LinkHullsToGaps()
{ {
if (gap == null || hulls == null || hulls[0] == null || hulls[1] == null) if (gap == null || hulls == null || hulls[0] == null || hulls[1] == null)
{ {
@@ -491,6 +489,37 @@ namespace Barotrauma.Items.Components
gap.linkedTo.Add(hulls[0]); gap.linkedTo.Add(hulls[0]);
} }
} }
for (int i = 0; i < 2; i++)
{
Gap doorGap = i == 0 ? door?.LinkedGap : dockingTarget?.door?.LinkedGap;
if (doorGap == null) continue;
doorGap.DisableHullRechecks = true;
if (doorGap.linkedTo.Count >= 2) continue;
if (IsHorizontal)
{
if (item.WorldPosition.X < dockingTarget.item.WorldPosition.X)
{
if (!doorGap.linkedTo.Contains(hulls[0])) doorGap.linkedTo.Add(hulls[0]);
}
else
{
if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]);
}
}
else
{
if (item.WorldPosition.Y < dockingTarget.item.WorldPosition.Y)
{
if (!doorGap.linkedTo.Contains(hulls[0])) doorGap.linkedTo.Add(hulls[0]);
}
else
{
if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]);
}
}
}
} }
public void Undock() public void Undock()
@@ -504,21 +533,15 @@ namespace Barotrauma.Items.Components
dockingTarget.item.Submarine.DockedTo.Remove(item.Submarine); dockingTarget.item.Submarine.DockedTo.Remove(item.Submarine);
item.Submarine.DockedTo.Remove(dockingTarget.item.Submarine); item.Submarine.DockedTo.Remove(dockingTarget.item.Submarine);
//remove all waypoint links between this sub and the dockingtarget if (door != null && dockingTarget.door != null)
foreach (WayPoint wp in WayPoint.WayPointList)
{ {
if (wp.Submarine != item.Submarine || wp.SpawnType != SpawnType.Path) continue; WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => dockingTarget.door.LinkedGap == wp.ConnectedGap);
for (int i = wp.linkedTo.Count - 1; i >= 0; i--) if (myWayPoint != null && targetWayPoint != null)
{ {
var wp2 = wp.linkedTo[i] as WayPoint; myWayPoint.linkedTo.Remove(targetWayPoint);
if (wp2 == null) continue; targetWayPoint.linkedTo.Remove(myWayPoint);
if (wp.Submarine == dockingTarget.item.Submarine)
{
wp.linkedTo.RemoveAt(i);
wp2.linkedTo.Remove(wp);
}
} }
} }
@@ -547,12 +570,8 @@ namespace Barotrauma.Items.Components
joint = null; joint = null;
} }
if (hulls != null) hulls[0]?.Remove(); hulls[0] = null;
{ hulls[1]?.Remove(); hulls[1] = null;
hulls[0].Remove();
hulls[1].Remove();
hulls = null;
}
if (gap != null) if (gap != null)
{ {
@@ -560,12 +579,7 @@ namespace Barotrauma.Items.Components
gap = null; gap = null;
} }
hullIds[0] = null; if (bodies != null)
hullIds[1] = null;
gapId = null;
if (bodies!=null)
{ {
foreach (Body body in bodies) foreach (Body body in bodies)
{ {
@@ -627,6 +641,9 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific() protected override void RemoveComponentSpecific()
{ {
list.Remove(this); list.Remove(this);
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
gap?.Remove(); gap = null;
} }
public override void OnMapLoaded() public override void OnMapLoaded()
@@ -716,18 +733,7 @@ namespace Barotrauma.Items.Components
if (docked) if (docked)
{ {
msg.Write(dockingTarget.item.ID); msg.Write(dockingTarget.item.ID);
msg.Write(hulls != null && hulls[0] != null && hulls[1] != null && gap != null);
if (hulls != null && hulls[0] != null && hulls[1] != null && gap != null)
{
msg.Write(true);
msg.Write(hulls[0].ID);
msg.Write(hulls[1].ID);
msg.Write(gap.ID);
}
else
{
msg.Write(false);
}
} }
} }
@@ -735,19 +741,27 @@ namespace Barotrauma.Items.Components
{ {
bool isDocked = msg.ReadBoolean(); bool isDocked = msg.ReadBoolean();
for (int i = 0; i < 2; i++)
{
if (hulls[i] == null) continue;
item.linkedTo.Remove(hulls[i]);
hulls[i].Remove();
hulls[i] = null;
}
if (gap != null)
{
item.linkedTo.Remove(gap);
gap.Remove();
gap = null;
}
if (isDocked) if (isDocked)
{ {
ushort dockingTargetID = msg.ReadUInt16(); ushort dockingTargetID = msg.ReadUInt16();
bool isLocked = msg.ReadBoolean(); bool isLocked = msg.ReadBoolean();
if (isLocked)
{
hullIds[0] = msg.ReadUInt16();
hullIds[1] = msg.ReadUInt16();
gapId = msg.ReadUInt16();
}
Entity targetEntity = Entity.FindEntityByID(dockingTargetID); Entity targetEntity = Entity.FindEntityByID(dockingTargetID);
if (targetEntity == null || !(targetEntity is Item)) if (targetEntity == null || !(targetEntity is Item))
{ {
@@ -767,10 +781,6 @@ namespace Barotrauma.Items.Components
if (isLocked) if (isLocked)
{ {
Lock(true); Lock(true);
hulls[0].ID = (ushort)hullIds[0];
hulls[1].ID = (ushort)hullIds[1];
gap.ID = (ushort)gapId;
} }
} }
else else
@@ -335,8 +335,12 @@ namespace Barotrauma.Items.Components
body = null; body = null;
} }
//no need to remove the gap if we're unloading the whole submarine
if (linkedGap != null) linkedGap.Remove(); //otherwise the gap will be removed twice and cause console warnings
if (!Submarine.Unloading)
{
if (linkedGap != null) linkedGap.Remove();
}
doorSprite.Remove(); doorSprite.Remove();
if (weldedSprite != null) weldedSprite.Remove(); if (weldedSprite != null) weldedSprite.Remove();
@@ -349,6 +353,14 @@ namespace Barotrauma.Items.Components
private void PushCharactersAway() private void PushCharactersAway()
{ {
if (!MathUtils.IsValid(item.SimPosition))
{
DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:DoorPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ").");
return;
}
//push characters out of the doorway when the door is closing/opening //push characters out of the doorway when the door is closing/opening
Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y)); Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y));
@@ -360,6 +372,16 @@ namespace Barotrauma.Items.Components
foreach (Character c in Character.CharacterList) foreach (Character c in Character.CharacterList)
{ {
if (!c.Enabled) continue;
if (!MathUtils.IsValid(c.SimPosition))
{
DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")." +
" Removed: " + c.Removed +
" Remoteplayer: " + c.IsRemotePlayer);
continue;
}
int dir = isHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X); int dir = isHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);
List<PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList(); List<PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList();
@@ -368,17 +390,24 @@ namespace Barotrauma.Items.Components
foreach (PhysicsBody body in bodies) foreach (PhysicsBody body in bodies)
{ {
float diff = 0.0f; float diff = 0.0f;
if (!MathUtils.IsValid(body.SimPosition))
{
DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:LimbPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + body.SimPosition + ")." +
" Removed: " + c.Removed +
" Remoteplayer: " + c.IsRemotePlayer);
continue;
}
if (isHorizontal) if (isHorizontal)
{ {
if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue; if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue;
diff = body.SimPosition.Y - item.SimPosition.Y; diff = body.SimPosition.Y - item.SimPosition.Y;
} }
else else
{ {
if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y) continue; if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y) continue;
diff = body.SimPosition.X - item.SimPosition.X; diff = body.SimPosition.X - item.SimPosition.X;
} }
@@ -442,7 +471,9 @@ namespace Barotrauma.Items.Components
public void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage = false) public void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage = false)
{ {
if (isStuck || (predictedState == null && isOpen == open) || (predictedState != null && isOpen == predictedState.Value)) return; if (isStuck ||
(predictedState == null && isOpen == open) ||
(predictedState != null && isOpen == predictedState.Value && isOpen == open)) return;
if (GameMain.Client != null && !isNetworkMessage) if (GameMain.Client != null && !isNetworkMessage)
{ {
@@ -124,6 +124,17 @@ namespace Barotrauma.Items.Components
} }
} }
public override void Load(XElement componentElement)
{
base.Load(componentElement);
if (attachable)
{
prevMsg = Msg;
prevPickKey = PickKey;
prevRequiredItems = new List<RelatedItem>(requiredItems);
}
}
public override void Drop(Character dropper) public override void Drop(Character dropper)
{ {
Drop(true, dropper); Drop(true, dropper);
@@ -160,7 +171,24 @@ namespace Barotrauma.Items.Components
if (item.body != null) if (item.body != null)
{ {
item.body.ResetDynamics(); item.body.ResetDynamics();
item.SetTransform(picker.SimPosition, 0.0f); Limb heldHand;
Limb arm;
if (picker.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
{
heldHand = picker.AnimController.GetLimb(LimbType.LeftHand);
arm = picker.AnimController.GetLimb(LimbType.LeftArm);
}
else
{
heldHand = picker.AnimController.GetLimb(LimbType.RightHand);
arm = picker.AnimController.GetLimb(LimbType.RightArm);
}
float xDif = (heldHand.SimPosition.X - arm.SimPosition.X) / 2f;
float yDif = (heldHand.SimPosition.Y - arm.SimPosition.Y) / 2.5f;
//hand simPosition is actually in the wrist so need to move the item out from it slightly
item.SetTransform(heldHand.SimPosition + new Vector2(xDif,yDif), 0.0f);
} }
picker.DeselectItem(item); picker.DeselectItem(item);
@@ -214,6 +242,14 @@ namespace Barotrauma.Items.Components
IsActive = false; IsActive = false;
} }
public bool CanBeDeattached()
{
if (!attachable || !attached) return true;
//don't allow deattaching if outside hulls and not in sub editor
return item.CurrentHull != null || Screen.Selected == GameMain.SubEditorScreen;
}
public override bool Pick(Character picker) public override bool Pick(Character picker)
{ {
if (!attachable) if (!attachable)
@@ -221,6 +257,8 @@ namespace Barotrauma.Items.Components
return base.Pick(picker); return base.Pick(picker);
} }
if (!CanBeDeattached()) return false;
if (Attached) if (Attached)
{ {
return base.Pick(picker); return base.Pick(picker);
@@ -330,7 +368,7 @@ namespace Barotrauma.Items.Components
if (picker.HasSelectedItem(item)) if (picker.HasSelectedItem(item))
{ {
picker.AnimController.HoldItem(deltaTime, item, handlePos, holdPos, aimPos, picker.IsKeyDown(InputType.Aim), holdAngle); picker.AnimController.HoldItem(deltaTime, item, handlePos, holdPos, aimPos, picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero, holdAngle);
} }
else else
{ {
@@ -410,7 +448,7 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{ {
bool isAttached = msg.ReadBoolean(); bool shouldBeAttached = msg.ReadBoolean();
Vector2 simPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat()); Vector2 simPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat());
if (!attachable) if (!attachable)
@@ -419,7 +457,7 @@ namespace Barotrauma.Items.Components
return; return;
} }
if (isAttached) if (shouldBeAttached)
{ {
Drop(false, null); Drop(false, null);
item.SetTransform(simPosition, 0.0f); item.SetTransform(simPosition, 0.0f);
@@ -427,16 +465,19 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
DropConnectedWires(null); if (attached)
if (body != null)
{ {
item.body = body; DropConnectedWires(null);
item.body.Enabled = true;
}
IsActive = false;
DeattachFromWall(); if (body != null)
{
item.body = body;
item.body.Enabled = true;
}
IsActive = false;
DeattachFromWall();
}
} }
} }
} }
@@ -3,6 +3,7 @@ using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -24,6 +25,8 @@ namespace Barotrauma.Items.Components
private float reloadTimer; private float reloadTimer;
private HashSet<Entity> hitTargets = new HashSet<Entity>();
[Serialize(0.0f, false)] [Serialize(0.0f, false)]
public float Range public float Range
{ {
@@ -94,6 +97,7 @@ namespace Barotrauma.Items.Components
} }
hitting = true; hitting = true;
hitTargets.Clear();
IsActive = true; IsActive = true;
return false; return false;
@@ -131,8 +135,7 @@ namespace Barotrauma.Items.Components
{ {
if (picker.IsKeyDown(InputType.Aim)) if (picker.IsKeyDown(InputType.Aim))
{ {
hitPos = Math.Min(hitPos+deltaTime*5.0f, MathHelper.Pi*0.7f); hitPos = Math.Min(hitPos + deltaTime * 5.0f, MathHelper.Pi * 0.7f);
ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.6f, -0.1f), new Vector2(-0.3f, 0.2f), false, hitPos); ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.6f, -0.1f), new Vector2(-0.3f, 0.2f), false, hitPos);
} }
else else
@@ -142,28 +145,13 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
//Vector2 diff = Vector2.Normalize(picker.CursorPosition - ac.RefLimb.Position); hitPos -= deltaTime * 15.0f;
//diff.X = diff.X * ac.Dir;
hitPos -= deltaTime*15.0f;
//angl = -hitPos * 2.0f;
// System.Diagnostics.Debug.WriteLine("<1.0f "+hitPos);
ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.6f, -0.1f), new Vector2(-0.3f, 0.2f), false, hitPos); ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.6f, -0.1f), new Vector2(-0.3f, 0.2f), false, hitPos);
//} if (hitPos < -MathHelper.PiOver4 * 1.2f)
//else
//{
// System.Diagnostics.Debug.WriteLine(">1.0f " + hitPos);
// ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.5f, 0.2f), new Vector2(1.0f, 0.2f), false, 0.0f);
//}
if (hitPos < -MathHelper.PiOver4*1.2f)
{ {
RestoreCollision(); RestoreCollision();
hitting = false; hitting = false;
hitTargets.Clear();
} }
} }
} }
@@ -230,15 +218,22 @@ namespace Barotrauma.Items.Components
targetLimb = (Limb)f2.Body.UserData; targetLimb = (Limb)f2.Body.UserData;
if (targetLimb.IsSevered || targetLimb.character == null) return false; if (targetLimb.IsSevered || targetLimb.character == null) return false;
targetCharacter = targetLimb.character; targetCharacter = targetLimb.character;
if (hitTargets.Contains(targetCharacter)) return false;
hitTargets.Add(targetCharacter);
} }
else if (f2.Body.UserData is Character) else if (f2.Body.UserData is Character)
{ {
targetCharacter = (Character)f2.Body.UserData; targetCharacter = (Character)f2.Body.UserData;
targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways
if (hitTargets.Contains(targetCharacter)) return false;
hitTargets.Add(targetCharacter);
} }
else if (f2.Body.UserData is Structure) else if (f2.Body.UserData is Structure)
{ {
targetStructure = (Structure)f2.Body.UserData; targetStructure = (Structure)f2.Body.UserData;
if (hitTargets.Contains(targetStructure)) return false;
hitTargets.Add(targetStructure);
} }
else else
{ {
@@ -267,9 +262,6 @@ namespace Barotrauma.Items.Components
} }
} }
RestoreCollision();
hitting = false;
if (GameMain.Client != null) return true; if (GameMain.Client != null) return true;
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
@@ -127,17 +127,7 @@ namespace Barotrauma.Items.Components
Color.Red, Color.Green); Color.Red, Color.Green);
#endif #endif
picker.AnimController.Anim = AnimController.Animation.UsingConstruction; picker.AnimController.UpdateUseItem(true, item.SimPosition + Vector2.UnitY * ((pickTimer / 10.0f) % 0.1f));
picker.AnimController.TargetMovement = Vector2.Zero;
leftHand.Disabled = true;
leftHand.pullJoint.Enabled = true;
leftHand.pullJoint.WorldAnchorB = item.SimPosition + Vector2.UnitY * ((pickTimer / 10.0f) % 0.1f);
rightHand.Disabled = true;
rightHand.pullJoint.Enabled = true;
rightHand.pullJoint.WorldAnchorB = item.SimPosition + Vector2.UnitY * ((pickTimer / 10.0f) % 0.1f);
pickTimer += CoroutineManager.DeltaTime; pickTimer += CoroutineManager.DeltaTime;

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