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
.gitattributes vendored Normal file
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

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.5")]
[assembly: AssemblyVersion("0.8.2.0")]
[assembly: AssemblyFileVersion("0.8.2.0")]

View File

@@ -99,7 +99,7 @@ namespace Barotrauma
public Camera()
{
zoom = 1.0f;
zoom = prevZoom = 1.0f;
rotation = 0.0f;
position = Vector2.Zero;
@@ -112,7 +112,7 @@ namespace Barotrauma
viewMatrix =
Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
UpdateTransform();
UpdateTransform(false);
}
public Vector2 TargetPos

View File

@@ -81,6 +81,9 @@ namespace Barotrauma
if (Limbs == null)
{
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;
}
@@ -97,7 +100,6 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.pullJoint != null)
{
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)
{
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);

View File

@@ -206,8 +206,8 @@ namespace Barotrauma
{
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;
}

View File

@@ -32,7 +32,7 @@ namespace Barotrauma
public static void TakeDamage(float amount)
{
healthBar.Flash();
healthBar?.Flash();
damageOverlayTimer = MathHelper.Clamp(amount * 0.1f, 0.2f, 1.0f);
}

View File

@@ -150,7 +150,7 @@ namespace Barotrauma
msg.ReadPadBits();
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);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
@@ -188,6 +188,7 @@ namespace Barotrauma
controlled = this;
IsRemotePlayer = false;
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = this;
GameMain.LightManager.LosEnabled = true;
}
@@ -286,6 +287,7 @@ namespace Barotrauma
if (GameMain.Client.ID == ownerId)
{
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = character;
Controlled = character;
@@ -342,7 +344,7 @@ namespace Barotrauma
}
else
{
this.isDead = false;
if (this.isDead) Revive();
health = msg.ReadRangedSingle(minHealth, maxHealth, 8);

View File

@@ -194,10 +194,11 @@ namespace Barotrauma
}
}
Color wearableColor = wearable.WearableComponent.Item.GetSpriteColor();
wearable.Sprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color, origin,
-body.DrawRotation,
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)),
origin, -body.DrawRotation,
Scale, spriteEffect, depth);
}

View File

@@ -355,9 +355,9 @@ namespace Barotrauma
NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange);
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);
}
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("Fullscreen enabled", Color.Green);
GameSettings.ShowUserStatisticsPrompt = true;
GameSettings.VerboseLogging = false;
if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster")

View File

@@ -214,12 +214,9 @@ namespace Barotrauma
if (GameMain.GameSession != null)
{
if (GameSettings.SendUserStatistics)
{
Mission mission = GameMain.GameSession.Mission;
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
}
Mission mission = GameMain.GameSession.Mission;
GameAnalyticsManager.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsManager.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
GameMain.GameSession = null;
}

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using GameAnalyticsSDK.Net;
using System.Threading;
namespace Barotrauma
{
@@ -74,7 +75,8 @@ namespace Barotrauma
get;
private set;
}
private static bool FullscreenOnTabIn;
public static WindowMode WindowMode
{
get;
@@ -134,7 +136,7 @@ namespace Barotrauma
Config.WasGameUpdated = false;
Config.Save("config.xml");
}
ApplyGraphicsSettings();
Content.RootDirectory = "Content";
@@ -231,8 +233,37 @@ namespace Barotrauma
TitleScreen = new LoadingScreen(GraphicsDevice);
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()
{
if (GameSettings.ShowUserStatisticsPrompt)
@@ -244,12 +275,18 @@ namespace Barotrauma
new string[] { "Yes", "No" });
userStatsPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = true;
GameAnalyticsManager.Init();
return true;
};
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;
}
else if (GameSettings.SendUserStatistics)

View File

@@ -92,9 +92,10 @@ namespace Barotrauma
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
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();
UInt16 purchasedItemCount = msg.ReadUInt16();
List<ItemPrefab> purchasedItems = new List<ItemPrefab>();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++)
{
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;

View File

@@ -269,7 +269,7 @@ namespace Barotrauma.Tutorials
}
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.");
while (steering.TargetVelocity == Vector2.Zero && steering.TargetVelocity.Length() < 50.0f)

View File

@@ -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.
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);
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;
yield return CoroutineStatus.Running;
}
if (item.Removed || GameMain.Client == null)
{
yield return CoroutineStatus.Success;
}
((IServerSerializable)this).ClientRead(type, buffer, sendingTime);
correctionTimer = 0.0f;

View File

@@ -6,9 +6,7 @@ namespace Barotrauma.Items.Components
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
var focusTarget = GetFocusTarget();
if (focusTarget == null) return;
if (character.ViewTarget == focusTarget)
if (focusTarget != null && character.ViewTarget == focusTarget)
{
foreach (ItemComponent ic in focusTarget.components)
{

View File

@@ -62,7 +62,7 @@ namespace Barotrauma.Items.Components
if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame);
//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);
@@ -105,36 +105,41 @@ namespace Barotrauma.Items.Components
{
inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level);
}
Color textColor = Color.White;
string text;
if (!inadequateSkills.Any())
string text = TextManager.Get("FabricatorRequiredItems")+ ":\n";
foreach (Tuple<ItemPrefab, int, float, bool> ip in targetItem.RequiredItems)
{
text = TextManager.Get("FabricatorRequiredItems")+ ":\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";
text += " - " + ip.Item1.Name + " x" + ip.Item2 + (ip.Item3 < 1.0f ? ", " + ip.Item3 * 100 + "% " + TextManager.Get("FabricatorRequiredCondition") + "\n" : "\n");
}
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";
foreach (Skill skill in inadequateSkills)
{
text += " - " + skill.Name + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level + "\n";
}
textColor = Color.Red;
new GUITextBlock(
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.OnClicked = StartButtonClicked;

View File

@@ -46,14 +46,14 @@ namespace Barotrauma.Items.Components
levelStartTickBox = new GUITickBox(
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);
levelStartTickBox.Enabled = false;
levelStartTickBox.OnSelected = SelectDestination;
levelEndTickBox = new GUITickBox(
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);
levelEndTickBox.Enabled = false;
levelEndTickBox.OnSelected = SelectDestination;

View File

@@ -34,16 +34,19 @@ namespace Barotrauma.Items.Components
Wire equippedWire = null;
//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++)
if (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)
{
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>();
if (wireComponent != null) equippedWire = wireComponent;
Wire wireComponent = selectedItem.GetComponent<Wire>();
if (wireComponent != null) equippedWire = wireComponent;
}
}
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
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),
rightWirePos,
mouseInRect, equippedWire,
@@ -82,7 +85,7 @@ namespace Barotrauma.Items.Components
}
else
{
c.Draw(spriteBatch, panel.Item, leftPos,
c.Draw(spriteBatch, panel, leftPos,
new Vector2(leftPos.X + 20, leftPos.Y - 12),
leftWirePos,
mouseInRect, equippedWire,
@@ -96,7 +99,7 @@ namespace Barotrauma.Items.Components
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())
{
@@ -121,7 +124,7 @@ namespace Barotrauma.Items.Components
{
DrawWire(spriteBatch, equippedWire, equippedWire.Item,
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;
}
@@ -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);
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);
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;
}
@@ -165,9 +168,9 @@ namespace Barotrauma.Items.Components
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))
{
@@ -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)
{
@@ -225,7 +228,7 @@ namespace Barotrauma.Items.Components
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));
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,
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;
if (!wire.Locked)
if (!wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
{
//start dragging the wire
if (PlayerInput.LeftButtonHeld()) draggingConnected = wire;

View File

@@ -1,5 +1,9 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -24,5 +28,66 @@ namespace Barotrauma.Items.Components
HighlightedWire = null;
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);
}
}
}
}
}

View File

@@ -52,9 +52,16 @@ namespace Barotrauma.Items.Components
}
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);
@@ -76,7 +83,7 @@ namespace Barotrauma.Items.Components
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)

View File

@@ -148,8 +148,10 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
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)
{
DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found");

View File

@@ -1,4 +1,7 @@
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -58,6 +61,43 @@ namespace Barotrauma.Items.Components
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);
}
}
}

View File

@@ -1,4 +1,6 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
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());
}
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;
}
}
}

View File

@@ -377,11 +377,11 @@ namespace Barotrauma
(components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime);
break;
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;
case NetEntityEvent.Type.Status:
condition = msg.ReadRangedSingle(0.0f, prefab.Health, 8);
condition = msg.ReadSingle();
if (FixRequirements.Count > 0)
{
if (Condition <= 0.0f)
@@ -401,10 +401,13 @@ namespace Barotrauma
ushort targetID = msg.ReadUInt16();
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;
case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg);
ReadPropertyChange(msg, false);
break;
case NetEntityEvent.Type.Invalid:
break;
}
}
@@ -423,11 +426,12 @@ namespace Barotrauma
case NetEntityEvent.Type.ComponentState:
int componentIndex = (int)extraData[1];
msg.WriteRangedInteger(0, components.Count - 1, componentIndex);
(components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData);
break;
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;
case NetEntityEvent.Type.Repair:
if (FixRequirements.Count > 0)
@@ -441,7 +445,7 @@ namespace Barotrauma
//on the character of the client who sent the message
break;
case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData);
WritePropertyChange(msg, extraData, true);
break;
}
msg.WritePadBits();
@@ -453,7 +457,7 @@ namespace Barotrauma
float newRotation = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 7);
bool awake = msg.ReadBoolean();
Vector2 newVelocity = Vector2.Zero;
if (awake)
{
newVelocity = new Vector2(
@@ -461,6 +465,19 @@ namespace Barotrauma
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)
{
DebugConsole.ThrowError("Received a position update for an item with no physics body (" + Name + ")");
@@ -470,7 +487,7 @@ namespace Barotrauma
body.FarseerBody.Awake = 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
{
@@ -490,11 +507,12 @@ namespace Barotrauma
if ((newPosition - SimPosition).Length() > body.LinearVelocity.Length() * 2.0f)
{
body.SetTransform(newPosition, newRotation);
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
if (body.SetTransform(newPosition, newRotation))
{
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
}
}
}

View File

@@ -27,7 +27,7 @@ namespace Barotrauma
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;
@@ -80,6 +80,10 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError("Invalid left normal");
#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)
{
GameMain.World.RemoveBody(cell.body);
@@ -106,6 +110,10 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError("Invalid right normal");
#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)
{
GameMain.World.RemoveBody(cell.body);

View File

@@ -27,10 +27,17 @@ namespace Barotrauma
{
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);
}
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);
}
}
}

View File

@@ -1,5 +1,6 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -145,7 +146,7 @@ namespace Barotrauma
"", frame, GUI.SmallFont);
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);
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"));
}
@@ -216,6 +217,33 @@ 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));
}
}
}

View File

@@ -32,18 +32,26 @@ namespace Barotrauma.Networking
if (NetIdUtils.IdMoreRecent(ID, LastID))
{
if (type == ChatMessageType.MessageBox)
switch (type)
{
new GUIMessageBox("", txt);
}
else if (type == ChatMessageType.Console)
{
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
}
else
{
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
case ChatMessageType.MessageBox:
new GUIMessageBox("", txt);
break;
case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
break;
case ChatMessageType.ServerLog:
if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
{
return;
}
GameMain.Client.ServerLog?.WriteLine(txt, messageType);
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
break;
}
LastID = ID;
}
}

View File

@@ -44,6 +44,9 @@ namespace Barotrauma.Networking
private FileReceiver fileReceiver;
//has the client been given a character to control this round
public bool HasSpawned;
public byte ID
{
get { return myID; }
@@ -61,6 +64,17 @@ namespace Barotrauma.Networking
{
get { return fileReceiver; }
}
public bool MidRoundSyncing
{
get { return entityEventManager.MidRoundSyncing; }
}
public bool AllowDisguises
{
get;
private set;
}
public GameClient(string newName)
{
@@ -99,6 +113,8 @@ namespace Barotrauma.Networking
otherClients = new List<Client>();
ServerLog = new ServerLog("");
ChatMessage.LastID = 0;
GameMain.NetLobbyScreen = new NetLobbyScreen();
}
@@ -456,6 +472,9 @@ namespace Barotrauma.Networking
#if DEBUG
if (PlayerInput.GetKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P)) return;
#endif
#if CLIENT
if (ServerLog.LogFrame != null) ServerLog.LogFrame.Update(deltaTime);
#endif
base.Update(deltaTime);
@@ -480,7 +499,7 @@ namespace Barotrauma.Networking
if (gameStarted && Screen.Selected == GameMain.GameScreen)
{
endVoteTickBox.Visible = Voting.AllowEndVoting && myCharacter != null;
endVoteTickBox.Visible = Voting.AllowEndVoting && HasSpawned;
if (respawnManager != null)
{
@@ -656,9 +675,11 @@ namespace Barotrauma.Networking
new GUITextBlock(new Rectangle(0, 0, 0, 15), permittedCommand, "", commandList, GUI.SmallFont).CanBeFocused = false;
}
}
GameMain.NetLobbyScreen.SubList.Enabled = Voting.AllowSubVoting || HasPermission(ClientPermissions.SelectSub);
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);
}
@@ -666,6 +687,7 @@ namespace Barotrauma.Networking
private IEnumerable<object> StartGame(NetIncomingMessage inc)
{
if (Character != null) Character.Remove();
HasSpawned = false;
GameMain.LightManager.LightingEnabled = true;
@@ -696,6 +718,7 @@ namespace Barotrauma.Networking
bool respawnAllowed = inc.ReadBoolean();
bool loadSecondSub = inc.ReadBoolean();
bool disguisesAllowed = inc.ReadBoolean();
bool isTraitor = inc.ReadBoolean();
string traitorTargetName = isTraitor ? inc.ReadString() : null;
@@ -734,6 +757,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.UsingShuttle = usingShuttle;
AllowDisguises = disguisesAllowed;
if (campaign == null)
{
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
@@ -757,7 +782,10 @@ namespace Barotrauma.Networking
else
{
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);
@@ -815,7 +843,7 @@ namespace Barotrauma.Networking
string subName = 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)
{
submarines.Add(matchingSub);
@@ -908,6 +936,8 @@ namespace Barotrauma.Networking
{
GameMain.NetLobbyScreen.LastUpdateID = updateID;
ServerLog.ServerName = serverName;
GameMain.NetLobbyScreen.ServerName = serverName;
GameMain.NetLobbyScreen.ServerMessage.Text = serverText;
@@ -982,6 +1012,9 @@ namespace Barotrauma.Networking
long prevBitPos = 0;
long prevBytePos = 0;
long prevBitLength = 0;
long prevByteLength = 0;
ServerNetObject objHeader;
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
{
@@ -1016,41 +1049,53 @@ namespace Barotrauma.Networking
ChatMessage.ClientRead(inc);
break;
default:
DebugConsole.ThrowError("Error while reading update from server (unknown object header \""+objHeader+"\"!)");
if (prevObjHeader != null)
List<string> errorLines = new List<string>
{
DebugConsole.ThrowError("Previous object type: " + prevObjHeader.ToString());
}
else
{
DebugConsole.ThrowError("Error occurred on the very first object!");
}
DebugConsole.ThrowError("Previous object was " + (inc.Position - prevBitPos) + " bits long (" + (inc.PositionInBytes - prevBytePos) + " bytes)");
"Error while reading update from server (unknown object header \"" + objHeader + "\"!)",
"Message length: " + inc.LengthBits + " (" + inc.LengthBytes + " bytes)",
prevObjHeader != null ? "Previous object type: " + prevObjHeader.ToString() : "Error occurred on the very first object!",
"Previous object was " + (prevBitLength) + " bits long (" + (prevByteLength) + " bytes)"
};
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL)
{
foreach (IServerSerializable ent in entities)
{
if (ent == null)
{
DebugConsole.ThrowError(" - NULL");
errorLines.Add(" - NULL");
continue;
}
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");
FileStream fl = File.Open("crashreport_object.bin", FileMode.Create);
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();
fl.Close();
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;
prevBitPos = inc.Position;
prevBytePos = inc.PositionInBytes;
@@ -1181,8 +1226,12 @@ namespace Barotrauma.Networking
case FileTransferType.Submarine:
new GUIMessageBox("Download finished", "File \"" + transfer.FileName + "\" was downloaded succesfully.");
var newSub = new Submarine(transfer.FilePath);
Submarine.SavedSubmarines.RemoveAll(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash);
Submarine.SavedSubmarines.Add(newSub);
var existingSubs = Submarine.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash).ToList();
foreach (Submarine existingSub in existingSubs)
{
existingSub.Dispose();
}
Submarine.AddToSavedSubs(newSub);
for (int i = 0; i < 2; i++)
{
@@ -1277,6 +1326,23 @@ namespace Barotrauma.Networking
{
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)
{
Vector2 pos = new Vector2(GameMain.NetLobbyScreen.InfoFrame.Rect.X, GameMain.GraphicsHeight - 35);
@@ -1341,6 +1407,17 @@ namespace Barotrauma.Networking
public override void Disconnect()
{
client.Shutdown("");
foreach (var fileTransfer in FileReceiver.ActiveTransfers)
{
fileTransfer.Dispose();
}
if (HasPermission(ClientPermissions.ServerLog))
{
ServerLog?.Save();
}
GameMain.NetworkMember = null;
}
@@ -1560,14 +1637,13 @@ namespace Barotrauma.Networking
{
if (!gameStarted) return false;
if (!Voting.AllowEndVoting || myCharacter==null)
if (!Voting.AllowEndVoting || !HasSpawned)
{
tickBox.Visible = false;
return false;
}
Vote(VoteType.EndRound, tickBox.Selected);
return false;
}
}

View File

@@ -9,45 +9,22 @@ namespace Barotrauma.Networking
{
private NetStats netStats;
private GUIButton showLogButton;
private GUIScrollBar clientListScrollBar;
void InitProjSpecific()
{
//----------------------------------------
var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170, 20, 150, 20), "End round", Alignment.TopLeft, "", inGameHUD);
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);
settingsButton.OnClicked = ToggleSettingsFrame;
settingsButton.UserData = "settingsButton";
//----------------------------------------
}
public override void AddToGUIUpdateList()
{
if (started) base.AddToGUIUpdateList();
base.AddToGUIUpdateList();
if (settingsFrame != null) settingsFrame.AddToGUIUpdateList();
if (log.LogFrame != null) log.LogFrame.AddToGUIUpdateList();
}
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
@@ -58,9 +35,18 @@ namespace Barotrauma.Networking
{
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;

View File

@@ -23,6 +23,11 @@ namespace Barotrauma.Networking
private UInt16 lastReceivedID;
public bool MidRoundSyncing
{
get { return firstNewID.HasValue; }
}
public ClientEntityEventManager(GameClient client)
{
events = new List<ClientEntityEvent>();
@@ -140,7 +145,13 @@ namespace Barotrauma.Networking
if (entityID == 0)
{
if (GameSettings.VerboseLogging)
{
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
Microsoft.Xna.Framework.Color.Orange);
}
msg.ReadPadBits();
entities.Add(null);
if (thisEventID == (UInt16)(lastReceivedID + 1)) lastReceivedID++;
continue;
}
@@ -153,23 +164,24 @@ namespace Barotrauma.Networking
//skip the event if we've already received it or if the entity isn't found
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(
"received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
"Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
thisEventID < lastReceivedID + 1
? Microsoft.Xna.Framework.Color.Yellow
: 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;
}
else
@@ -188,10 +200,19 @@ namespace Barotrauma.Networking
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)
{
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;
}
}

View File

@@ -24,6 +24,7 @@ namespace Barotrauma.Networking
protected GUIFrame inGameHUD;
protected GUIListBox chatBox;
protected GUITextBox chatMsgBox;
protected GUIButton showLogButton;
public GUIFrame InGameHUD
{
@@ -35,6 +36,23 @@ namespace Barotrauma.Networking
inGameHUD = new GUIFrame(new Rectangle(0, 0, 0, 0), null, null);
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 height = (int)MathHelper.Clamp(GameMain.GraphicsHeight * 0.15f, 100, 200);
chatBox = new GUIListBox(new Rectangle(
@@ -109,53 +127,44 @@ namespace Barotrauma.Networking
{
inGameHUD.AddToGUIUpdateList();
}
if (ServerLog.LogFrame != null) ServerLog.LogFrame.AddToGUIUpdateList();
}
public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{
if (!gameStarted || Screen.Selected != GameMain.GameScreen || GUI.DisableHUD) return;
GameMain.GameSession.CrewManager.Draw(spriteBatch);
inGameHUD.Draw(spriteBatch);
if (EndVoteCount > 0)
if (GUI.DisableHUD) return;
if (gameStarted && Screen.Selected == GameMain.GameScreen)
{
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),
"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);
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);
}
}
}
if (respawnManager != null)
{
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);
}
}
ServerLog.LogFrame?.Draw(spriteBatch);
}
public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame)

View File

@@ -2,6 +2,7 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -151,7 +152,12 @@ namespace Barotrauma
{
int votes = inc.ReadByte();
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);
}
}

View File

@@ -203,7 +203,7 @@ namespace Barotrauma
if (GameSettings.SendUserStatistics)
{
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();
}
else

View File

@@ -57,6 +57,14 @@ namespace Barotrauma
Submarine selectedSub = subList.SelectedData as Submarine;
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);
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !selectedSub.CompatibleContentPackages.Contains(GameMain.SelectedPackage.Name))
@@ -144,7 +152,7 @@ namespace Barotrauma
return true;
};
}
if (Submarine.SavedSubmarines.Count > 0) subList.Select(Submarine.SavedSubmarines[0]);
if (Submarine.SavedSubmarines.Any()) subList.Select(Submarine.SavedSubmarines.First());
}
public void UpdateLoadMenu()

View File

@@ -85,7 +85,7 @@ namespace Barotrauma
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.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.OnSelected = BuyItem;
@@ -149,6 +149,8 @@ namespace Barotrauma
null, null,
Alignment.TopRight, "", frame);
}
RefreshItemTab();
}
public void Update(float deltaTime)
@@ -216,55 +218,92 @@ namespace Barotrauma
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);
frame.UserData = ep;
frame.UserData = pi;
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;
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(50, 0, 0, 25),
ep.Name,
pi.itemPrefab.Name,
null, null,
Alignment.Left, Alignment.CenterX | Alignment.Left,
"", frame);
textBlock.Font = font;
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);
img.Color = ep.SpriteColor;
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), pi.itemPrefab.sprite, Alignment.CenterLeft, frame);
img.Color = pi.itemPrefab.SpriteColor;
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
}
textBlock = new GUITextBlock(
new Rectangle(width - 80, 0, 80, 25),
ep.Price.ToString(),
new Rectangle(width - 160, 0, 80, 25),
pi.itemPrefab.Price.ToString(),
null, null, Alignment.TopLeft,
Alignment.TopLeft, "", frame);
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)
{
ItemPrefab prefab = obj as ItemPrefab;
if (prefab == null) return false;
PurchasedItem pi = obj as PurchasedItem;
if (pi == null || pi.itemPrefab == null) return false;
if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
{
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();
return false;
@@ -272,15 +311,15 @@ namespace Barotrauma
private bool SellItem(GUIComponent component, object obj)
{
ItemPrefab prefab = obj as ItemPrefab;
if (prefab == null) return false;
PurchasedItem pi = obj as PurchasedItem;
if (pi == null || pi.itemPrefab == null) return false;
if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
{
return false;
}
campaign.CargoManager.SellItem(prefab);
campaign.CargoManager.SellItem(pi.itemPrefab,1);
GameMain.Client?.SendCampaignState();
return false;
@@ -289,10 +328,13 @@ namespace Barotrauma
private void RefreshItemTab()
{
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)
@@ -311,16 +353,16 @@ namespace Barotrauma
storeItemList.ClearChildren();
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;
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)
{

View File

@@ -244,13 +244,12 @@ namespace Barotrauma
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.losTexture);
#endif
//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
//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
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();
losColorHls.Y = 0.1f;
losColorHls.Y = ambientLightHls.Y;
Color losColor = ToolBox.HLSToRGB(losColorHls);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, GameMain.LightManager.LosEffect, null);

View File

@@ -82,13 +82,15 @@ namespace Barotrauma
locationTitle.Text = TextManager.Get("Location") + ": " + campaign.Map.CurrentLocation.Name;
campaign.Map.SelectLocation(-1);
bottomPanel.ClearChildren();
campaignUI = new CampaignUI(campaign, bottomPanel);
campaignUI.StartRound = StartRound;
campaignUI.OnLocationSelected = SelectLocation;
campaignUI.OnLocationSelected = SelectLocation;
campaignUI.UpdateCharacterLists();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("singleplayer");
GameAnalyticsManager.SetCustomDimension01("singleplayer");
}
public override void AddToGUIUpdateList()
@@ -190,7 +192,10 @@ namespace Barotrauma
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();
yield return CoroutineStatus.Success;

View File

@@ -143,7 +143,7 @@ namespace Barotrauma
SelectTab(null, 0);
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("");
GameAnalyticsManager.SetCustomDimension01("");
}
public bool SelectTab(GUIButton button, object obj)
@@ -368,7 +368,19 @@ namespace Barotrauma
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"), "");

View File

@@ -177,7 +177,7 @@ namespace Barotrauma
if (levelSeed == value) return;
levelSeed = value;
backgroundSprite = LocationType.Random(levelSeed).Background;
backgroundSprite = LocationType.Random(levelSeed)?.Background;
seedBox.Text = levelSeed;
}
}
@@ -379,13 +379,13 @@ namespace Barotrauma
showLogButton.UserData = "showlog";
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
{
GameMain.Server.ServerLog.LogFrame = null;
GameMain.NetworkMember.ServerLog.LogFrame = null;
GUIComponent.KeyboardDispatcher.Subscriber = null;
}
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 == "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)
{
@@ -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);
@@ -737,8 +739,21 @@ namespace Barotrauma
//hash will be null if opening the sub file failed -> don't select the sub
if (string.IsNullOrWhiteSpace(hash))
{
(component as GUITextBlock).TextColor = Color.DarkRed * 0.8f;
component.CanBeFocused = false;
GUITextBlock submarineTextBlock = component.GetChild<GUITextBlock>();
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;
@@ -782,8 +797,8 @@ namespace Barotrauma
CanBeFocused = false
};
var matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash);
if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == sub.Name);
var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash);
if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name);
if (matchingSub == null)
{
@@ -1467,8 +1482,8 @@ namespace Barotrauma
return false;
}
Submarine sub = Submarine.SavedSubmarines.Find(m => m.Name == subName && m.MD5Hash.Hash == md5Hash);
if (sub == null) sub = Submarine.SavedSubmarines.Find(m => m.Name == subName);
Submarine sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName && m.MD5Hash.Hash == md5Hash);
if (sub == null) sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName);
var matchingListSub = subList.children.Find(c => c.UserData == sub);
if (matchingListSub != null)
@@ -1520,7 +1535,7 @@ namespace Barotrauma
requestFileBox.Buttons[0].OnClicked += (GUIButton button, object 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;
};
requestFileBox.Buttons[1].OnClicked += requestFileBox.Close;

View File

@@ -15,9 +15,9 @@ namespace Barotrauma
{
private static string[] crewExperienceLevels = new string[]
{
TextManager.Get("CrewExperienceLow"),
TextManager.Get("CrewExperienceMid"),
TextManager.Get("CrewExperienceHigh")
"CrewExperienceLow",
"CrewExperienceMid",
"CrewExperienceHigh"
};
private Camera cam;
@@ -349,7 +349,7 @@ namespace Barotrauma
cam.UpdateTransform();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("editor");
GameAnalyticsManager.SetCustomDimension01("editor");
}
public override void Deselect()
@@ -587,23 +587,26 @@ namespace Barotrauma
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);
toggleExpLeft.OnClicked += (btn, userData) =>
{
int currentIndex = Array.IndexOf(crewExperienceLevels, experienceText.Text);
int currentIndex = Array.IndexOf(crewExperienceLevels, (string)experienceText.UserData);
currentIndex--;
if (currentIndex < 0) currentIndex = crewExperienceLevels.Length - 1;
experienceText.Text = crewExperienceLevels[currentIndex];
Submarine.MainSub.RecommendedCrewExperience = experienceText.Text;
experienceText.UserData = crewExperienceLevels[currentIndex];
experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData;
return true;
};
toggleExpRight.OnClicked += (btn, userData) =>
{
int currentIndex = Array.IndexOf(crewExperienceLevels, experienceText.Text);
int currentIndex = Array.IndexOf(crewExperienceLevels, (string)experienceText.UserData);
currentIndex++;
if (currentIndex >= crewExperienceLevels.Length) currentIndex = 0;
experienceText.Text = crewExperienceLevels[currentIndex];
Submarine.MainSub.RecommendedCrewExperience = experienceText.Text;
experienceText.UserData = crewExperienceLevels[currentIndex];
experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData;
return true;
};
@@ -613,8 +616,9 @@ namespace Barotrauma
int max = Submarine.MainSub.RecommendedCrewSizeMax;
crewSizeMin.IntValue = min;
crewSizeMax.IntValue = max;
experienceText.Text = string.IsNullOrEmpty(Submarine.MainSub.RecommendedCrewExperience) ?
experienceText.UserData = string.IsNullOrEmpty(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);
@@ -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];
max = wallPoints[0];
for (int i = 0; i < wallPoints.Count; i++)

View File

@@ -39,7 +39,7 @@ namespace Barotrauma.Sounds
using (VorbisReader reader = new VorbisReader(oggFile))
{
int bufferSize = (int)reader.TotalSamples;
int bufferSize = (int)reader.TotalSamples * reader.Channels;
float[] buffer = new float[bufferSize];
sound.castBuffer = new short[bufferSize];
@@ -52,11 +52,13 @@ namespace Barotrauma.Sounds
sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16;
sound.sampleRate = reader.SampleRate;
ALHelper.Check();
//alSourceId = AL.GenSource();
AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer,
readSamples * sizeof(short), reader.SampleRate);
ALHelper.Check();
ALHelper.Check(oggFile);
}
//AL.Source(alSourceId, ALSourcei.Buffer, alBufferId);
@@ -100,12 +102,12 @@ namespace Barotrauma.Sounds
public void Dispose()
{
//var state = AL.GetSourceState(alSourceId);
//if (state == ALSourceState.Playing || state == ALSourceState.Paused)
// Stop();
System.Diagnostics.Debug.WriteLine(alBufferId);
//AL.DeleteSource(alSourceId);
AL.DeleteBuffer(alBufferId);
if (alBufferId > 0)
{
AL.DeleteBuffer(alBufferId);
alBufferId = 0;
}
//if (ALHelper.Efx.IsInitialized)
// ALHelper.Efx.DeleteFilter(alFilterId);

View File

@@ -45,16 +45,24 @@ namespace Barotrauma.Sounds
//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;
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
DebugConsole.ThrowError("OpenAL error: " + AL.GetErrorString(error) + "\n" + Environment.StackTrace);
DebugConsole.ThrowError(errorMsg);
#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
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;
#endif
public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), bufferCount) { }
public OggStream(Stream stream, int bufferCount = DefaultBufferCount)
public string FileName
{
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;
alBufferIds = AL.GenBuffers(bufferCount);
@@ -94,7 +110,7 @@ namespace Barotrauma.Sounds
if (ALHelper.XRam.IsInitialized)
{
ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware);
ALHelper.Check();
ALHelper.Check(fileName);
}
if (ALHelper.Efx.IsInitialized)
@@ -163,7 +179,7 @@ namespace Barotrauma.Sounds
AL.SourcePlay(alSourceId);
this.Volume = volume;
ALHelper.Check();
ALHelper.Check(FileName);
Preparing = false;
@@ -177,7 +193,7 @@ namespace Barotrauma.Sounds
OggStreamer.Instance.RemoveStream(this);
AL.SourcePause(alSourceId);
ALHelper.Check();
ALHelper.Check(FileName);
}
public void Resume()
@@ -187,7 +203,7 @@ namespace Barotrauma.Sounds
OggStreamer.Instance.AddStream(this);
AL.SourcePlay(alSourceId);
ALHelper.Check();
ALHelper.Check(FileName);
}
public void Stop()
@@ -226,7 +242,7 @@ namespace Barotrauma.Sounds
set
{
AL.Source(alSourceId, ALSourcef.Gain, volume = value);
ALHelper.Check();
ALHelper.Check(FileName);
}
}
@@ -256,20 +272,20 @@ namespace Barotrauma.Sounds
/*if (ALHelper.Efx.IsInitialized)
ALHelper.Efx.DeleteFilter(alFilterId);*/
ALHelper.Check();
ALHelper.Check(FileName);
}
void StopPlayback()
{
AL.SourceStop(alSourceId);
ALHelper.Check();
ALHelper.Check(FileName);
}
void Empty()
{
int queued;
AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check();
ALHelper.Check(FileName);
if (queued > 0)
{
@@ -292,12 +308,12 @@ namespace Barotrauma.Sounds
if (processed > 0)
{
AL.SourceUnqueueBuffers(alSourceId, processed, salvaged);
ALHelper.Check();
ALHelper.Check(FileName);
}
// Try turning it off again?
AL.SourceStop(alSourceId);
ALHelper.Check();
ALHelper.Check(FileName);
Empty();
}
@@ -314,7 +330,7 @@ namespace Barotrauma.Sounds
// Fill first buffer synchronously
OggStreamer.Instance.FillBuffer(this, alBufferIds[0]);
AL.SourceQueueBuffer(alSourceId, alBufferIds[0]);
ALHelper.Check();
ALHelper.Check(FileName);
// Schedule the others asynchronously
OggStreamer.Instance.AddStream(this);
@@ -429,7 +445,7 @@ namespace Barotrauma.Sounds
}
AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer,
readSamples * sizeof(short), stream.Reader.SampleRate);
ALHelper.Check();
ALHelper.Check(stream.FileName);
return readSamples != BufferSize;
}
@@ -466,10 +482,10 @@ namespace Barotrauma.Sounds
int queued;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued);
ALHelper.Check();
ALHelper.Check(stream.FileName);
int processed;
AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed);
ALHelper.Check();
ALHelper.Check(stream.FileName);
if (processed == 0 && queued == stream.BufferCount) continue;
@@ -496,7 +512,7 @@ namespace Barotrauma.Sounds
}
AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers);
ALHelper.Check();
ALHelper.Check(stream.FileName);
if (finished && !stream.IsLooped)
continue;
@@ -514,7 +530,7 @@ namespace Barotrauma.Sounds
if (state == ALSourceState.Stopped)
{
AL.SourcePlay(stream.alSourceId);
ALHelper.Check();
ALHelper.Check(stream.FileName);
}
}
}

View File

@@ -54,7 +54,7 @@ namespace Barotrauma
{
DebugConsole.ThrowError("Failed to load sound "+file+"!", e);
}
ALHelper.Check();
ALHelper.Check(file);
}
baseVolume = 1.0f;
@@ -219,7 +219,7 @@ namespace Barotrauma
(SoundManager.IsPlaying(alSourceId) || SoundManager.IsPaused(alSourceId)))
{
SoundManager.Stop(alSourceId);
ALHelper.Check();
ALHelper.Check(filePath);
}
foreach (Sound s in loadedSounds)
@@ -228,7 +228,7 @@ namespace Barotrauma
}
SoundManager.ClearAlSource(AlBufferId);
ALHelper.Check();
ALHelper.Check(filePath);
if (oggSound != null)
{
@@ -246,7 +246,7 @@ namespace Barotrauma
public static void StreamVolume(float volume = 1.0f)
{
if (SoundManager.Disabled) return;
if (SoundManager.Disabled || stream == null) return;
stream.Volume = volume;
}

View File

@@ -143,7 +143,7 @@ namespace Barotrauma.Sounds
volume = 0.0f;
}
if (sourceIndex<1 || soundsPlaying[sourceIndex] != sound)
if (sourceIndex < 1 || soundsPlaying[sourceIndex] != sound)
{
sourceIndex = Play(sound, position, volume, 0.0f, true);
}
@@ -153,7 +153,7 @@ namespace Barotrauma.Sounds
AL.Source(alSources[sourceIndex], ALSourceb.Looping, true);
}
ALHelper.Check();
ALHelper.Check(sound?.FilePath);
return sourceIndex;
}
@@ -165,7 +165,7 @@ namespace Barotrauma.Sounds
return;
AL.SourcePause(alSources[sourceIndex]);
ALHelper.Check();
ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
}
public static void Resume(int sourceIndex)
@@ -176,7 +176,7 @@ namespace Barotrauma.Sounds
return;
AL.SourcePlay(alSources[sourceIndex]);
ALHelper.Check();
ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
}
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.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.BindFilterToSource(alSources[sourceIndex], lowpassFilterId);
ALHelper.Check();
ALHelper.Check(soundsPlaying[sourceIndex]?.FilePath);
}
public static OggStream StartStream(string file, float volume = 1.0f)
@@ -331,7 +331,7 @@ namespace Barotrauma.Sounds
oggStream.Play(volume);
ALHelper.Check();
ALHelper.Check(file);
return oggStream;
}
@@ -361,15 +361,15 @@ namespace Barotrauma.Sounds
for (int i = 0; i < DefaultSourceCount; i++)
{
string soundPath = soundsPlaying[i]?.FilePath;
var state = OpenTK.Audio.OpenAL.AL.GetSourceState(alSources[i]);
if (state == OpenTK.Audio.OpenAL.ALSourceState.Playing || state == OpenTK.Audio.OpenAL.ALSourceState.Paused)
{
Stop(i);
}
OpenTK.Audio.OpenAL.AL.DeleteSource(alSources[i]);
ALHelper.Check();
OpenTK.Audio.OpenAL.AL.DeleteSource(alSources[i]);
ALHelper.Check(soundPath);
}
if (oggStream != null)

View File

@@ -254,11 +254,14 @@ namespace Barotrauma
partial void DisposeTexture()
{
//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 (texture != null)
{
@@ -267,6 +270,5 @@ namespace Barotrauma
}
}
}
}

View File

@@ -46,7 +46,7 @@ namespace Barotrauma
using (Stream fileStream = File.OpenRead(path))
{
var texture = Texture2D.FromStream(_graphicsDevice, fileStream);
texture = PreMultiplyAlpha(texture);
PreMultiplyAlpha(texture);
return texture;
}
@@ -63,7 +63,7 @@ namespace Barotrauma
try
{
var texture = Texture2D.FromStream(_graphicsDevice, fileStream);
texture = PreMultiplyAlpha(texture);
PreMultiplyAlpha(texture);
return texture;
}
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
using (RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, texture.Width, texture.Height))
@@ -103,9 +103,7 @@ namespace Barotrauma
// Unset texture from graphic device and set modified data back to it
_graphicsDevice.Textures[0] = null;
texture.SetData(data);
}
return texture;
}
}

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.5")]
[assembly: AssemblyVersion("0.8.2.0")]
[assembly: AssemblyFileVersion("0.8.2.0")]

View File

@@ -124,7 +124,6 @@ namespace Barotrauma
public void CloseServer()
{
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
Server.Disconnect();
Server = null;
}
@@ -171,7 +170,6 @@ namespace Barotrauma
stopwatch.Stop();
CloseServer();
}
public void ProcessInput()

View File

@@ -31,6 +31,7 @@ namespace Barotrauma
inputThread.Start();
game.Run();
inputThread.Abort(); inputThread.Join();
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
}
catch (Exception e)
{

View File

@@ -119,7 +119,7 @@ namespace Barotrauma
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.");
}
@@ -187,7 +187,7 @@ namespace Barotrauma
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)];
}
if (GameMain.Server.ModeSelectionMode == SelectionMode.Random)

View File

@@ -325,12 +325,6 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\lightsprite.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</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">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@@ -26,7 +26,8 @@
<Item
name="Medicine Cabinet"
linkable="true"
pickdistance ="150">
pickdistance="150"
waterproof="true">
<Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,0,48,64" canflipx="false"/>
@@ -37,9 +38,10 @@
</Item>
<Item
name="Toxic Cabinet"
linkable="true"
pickdistance ="150">
name="Toxic Cabinet"
linkable="true"
pickdistance="150"
waterproof="true">
<Sprite texture ="cabinets.png" depth="0.84" sourcerect="0,64,48,64" canflipx="false"/>
@@ -52,7 +54,7 @@
<Item
name="Metal Crate"
linkable="true"
pickdistance ="150">
pickdistance="150">
<Deconstruct time="20">
<Item name="Steel Bar"/>
@@ -74,7 +76,7 @@
<Item
name="Explosive Crate"
linkable="true"
pickdistance ="150">
pickdistance="150">
<Deconstruct time="20">
<Item name="Steel Bar"/>

View File

@@ -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="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="Contained,Character" OxygenAvailable="1000.0" Condition="-0.5">
<RequiredItem type="Contained" name="Oxygen Tank"/>

View File

@@ -103,8 +103,7 @@
<Item
name="Docking Port"
linkable="true"
>
linkable="true">
<Sprite texture ="dockingport.png" sourcerect="0,0,112,208" depth="0.94" origin="0.5,0.5"/>
@@ -116,14 +115,8 @@
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
</StatusEffect>
</DockingPort>
<fixrequirement name="Electrical repairs">
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/>
<PowerTransfer CanBeOverloaded="false" FireProbability="0.0"/>
<Wire/>
<fixrequirement name="Mechanical repairs">
@@ -143,8 +136,7 @@
<Item
name="Docking Hatch"
linkable="true"
>
linkable="true">
<Sprite texture ="dockingport2.png" sourcerect="0,0,128,112" depth="0.94" origin="0.5,0.5"/>
@@ -157,13 +149,7 @@
</StatusEffect>
</DockingPort>
<fixrequirement name="Electrical repairs">
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/>
<PowerTransfer CanBeOverloaded="false" FireProbability="0.0"/>
<Wire/>
<fixrequirement name="Mechanical repairs">
@@ -183,8 +169,7 @@
<Item
name="Duct Block"
linkable="true"
>
linkable="true">
<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

View File

@@ -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>

View File

@@ -28,7 +28,7 @@
name="Red Wire"
category="Electrical"
Tags="smallitem,wire"
spritecolor="1.0,0.0,0.0,1.0"
spritecolor="0.7,0.06,0.06,1.0"
linkable="true"
canbepicked="true"
cargocontainername="Metal Crate"
@@ -50,7 +50,7 @@
name="Blue Wire"
category="Electrical"
Tags="smallitem,wire"
spritecolor="0.0,0.6,1.0,1.0"
spritecolor="0.1,0.4,0.9,1.0"
linkable="true"
canbepicked="true"
cargocontainername="Metal Crate"
@@ -72,7 +72,7 @@
name="Orange Wire"
category="Electrical"
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"
@@ -90,6 +90,50 @@
<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"
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="FPGA Circuit"
category="Electrical"
@@ -261,7 +305,7 @@
<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"/>
</RelayComponent>

View File

@@ -65,7 +65,7 @@
</Deconstruct>
<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="LeftHand" sourcerect="47,0,15,49" origin="0.45,0.6" depth="0.14" hidelimb="true"/>

View File

@@ -423,10 +423,13 @@
<Body width="8" height="16" density="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"/>
<RequiredItem name="Medical Syringe" type="Container"/>
</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">
<!-- Injecting a still-conscious Husk will only piss it off and kill the "conscious" faster -->
<Conditional HuskInfectionState="eq 1.0"/>

View File

@@ -13,16 +13,13 @@
</fixrequirement>
<OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true">
<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="Oxygenite Tank" Condition="0.0"/>
<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">
<sound file="Content/Items/Reactor/explosion.ogg"/>
<Explosion range="50" damage="0" stun="0" force="3.0" flames="false" shockwave="false" sparks="true"/>
</StatusEffect>
<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">
<sound file="Content/Items/Reactor/explosion.ogg"/>
<Explosion range="50" damage="0" stun="0" force="3.0" flames="false" shockwave="false" sparks="true"/>
</StatusEffect>
</OxygenGenerator>
<trigger/>

View File

@@ -21,7 +21,7 @@
<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"/>
<RequiredSkill name="Construction" level="30"/>
<StatusEffect type="InWater" target="This" Temperature="-500.0"/>

View File

@@ -286,11 +286,11 @@
<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"/>
<!-- 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>
<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">
<Containable name="Battery Cell, Fulgurium Battery Cell">

View File

@@ -9,11 +9,12 @@
<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"
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"/>
<input name="trigger_in"/>
</ConnectionPanel>

View File

@@ -2,13 +2,18 @@
<Item
name="Railgun"
category="Machine"
interactthroughwalls="true"
interactdistance="10"
focusonselected="true"
offsetonselected="700"
linkable="true">
<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"
powerconsumption="20000.0"
showchargeindicator="true"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -40,7 +40,7 @@
<Body width="90" height="30" density="50"/>
<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">
<Sound file="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/>
@@ -119,15 +119,15 @@
<Item name="Aluminium"/>
<Item name="Polycarbonate Bar"/>
</Deconstruct>
<Sprite texture="weapons.png" sourcerect="26,97,25,19" depth="0.55"/>
<Sprite texture="weapons.png" sourcerect="24,102,37,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"
aimpos="75,18" handle1="-10,-5"/>
<RangedWeapon barrelpos="37,3" spread="0" unskilledspread="2">
aimpos="90,10" handle1="-11,-7"/>
<RangedWeapon barrelpos="14,6" spread="0" unskilledspread="2">
<Sound file="Content/Items/Weapons/revolver.ogg" type="OnUse" range="3000"/>
<StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/>

View File

@@ -16,11 +16,11 @@
<Item name="Headset" equip="true">
<Item name="Battery Cell"/>
</Item>
<Item name="Revolver" equip="false">
<Item name="Revolver Round"/>
<Item name="Revolver Round"/>
<Item name="Revolver Round"/>
</Item>
<Item name="Revolver" equip="false">
<Item name="Revolver Round"/>
<Item name="Revolver Round"/>
<Item name="Revolver Round"/>
</Item>
</Items>
</Job>

View File

@@ -13,7 +13,6 @@
<Item file="Content/Items/Diving/divinggear.xml" />
<Item file="Content/Items/Door/doors.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/signalitems.xml" />
<Item file="Content/Items/Engine/engine.xml" />

View File

@@ -7,8 +7,8 @@
<Preset
name="Moderator"
description="Allowed to manage round settings and kick players."
permissions="EndRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands">
description="Allowed to manage round settings, kick players and access server logs."
permissions="EndRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog">
<Command name="clientlist"/>
<Command name="autorestart"/>
<Command name="autorestartinterval"/>
@@ -21,8 +21,8 @@
<Preset
name="Admin"
description="Allowed to ban and kick players, manage round settings and use nearly all console commands."
permissions="EndRound,Kick,Ban,SelectSub,SelectMode,ManageCampaign,ConsoleCommands">
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,ServerLog">
<Command name="clientlist"/>
<Command name="autorestart"/>
<Command name="autorestartinterval"/>

View File

@@ -38,6 +38,9 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace);
#endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace);
return Vector2.Zero;
}
@@ -54,6 +57,9 @@ namespace Barotrauma
#if DEBUG
DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace);
#endif
GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a removed AITarget\n" + Environment.StackTrace);
return Vector2.Zero;
}

View File

@@ -274,7 +274,15 @@ namespace Barotrauma
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.SteeringAvoid(deltaTime, 2f);
}
@@ -484,8 +492,10 @@ namespace Barotrauma
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.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);

View File

@@ -63,6 +63,7 @@ namespace Barotrauma
character.SetInput(InputType.Aim, false, true);
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);
Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle));

View File

@@ -30,14 +30,9 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (pathSteering == null) return;
if (pathSteering==null)
{
return;
}
if (character.AnimController.InWater)
{
//attempt to find a safer place if in water
@@ -67,16 +62,16 @@ namespace Barotrauma
newTargetTimer -= deltaTime;
//wander randomly
// - if reached the end of the path
// - if the target is unreachable
// - 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)))
{
//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 rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X;
@@ -113,8 +108,13 @@ namespace Barotrauma
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);
}

View File

@@ -92,7 +92,7 @@ namespace Barotrauma
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)
{

View File

@@ -80,9 +80,9 @@ namespace Barotrauma
}
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;
@@ -97,12 +97,12 @@ namespace Barotrauma
targetVel = Vector2.Normalize(targetVel) * speed;
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();
if (steeringSpeed > Math.Abs(speed))
{
newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed);
newSteering = Vector2.Normalize(newSteering) * Math.Abs(speed);
}
return newSteering;
@@ -151,8 +151,7 @@ namespace Barotrauma
}
else
{
Structure closestStructure = closestBody.UserData as Structure;
if (closestStructure != null)
if (closestBody.UserData is Structure closestStructure)
{
Vector2 obstaclePosition = Submarine.LastPickedPosition;
if (closestStructure.IsHorizontal)
@@ -166,15 +165,17 @@ namespace Barotrauma
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);
}
else
{
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);
}
}

View File

@@ -68,6 +68,7 @@ namespace Barotrauma
public virtual void DragCharacter(Character target) { }
public virtual void UpdateUseItem(bool allowMovement, Vector2 handPos) { }
}
}

View File

@@ -64,8 +64,16 @@ namespace Barotrauma
}
else
{
Collider.LinearVelocity = (MainLimb.SimPosition - Collider.SimPosition) * 60.0f;
Collider.SmoothRotate(MainLimb.Rotation);
Vector2 diff = (MainLimb.SimPosition - Collider.SimPosition);
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)

View File

@@ -24,6 +24,8 @@ namespace Barotrauma
private float inWaterTimer;
private bool swimming;
private float useItemTimer;
protected override float TorsoPosition
{
@@ -92,14 +94,22 @@ namespace Barotrauma
{
levitatingCollider = 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.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;
}
@@ -180,6 +190,12 @@ namespace Barotrauma
case Animation.UsingConstruction:
default:
if (Anim == Animation.UsingConstruction)
{
useItemTimer -= deltaTime;
if (useItemTimer <= 0.0f) Anim = Animation.None;
}
if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter);
//0.5 second delay for switching between swimming and walking
@@ -1067,8 +1083,31 @@ namespace Barotrauma
}
}
}
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)
{
targetLimb.pullJoint.Enabled = false;
@@ -1204,25 +1243,29 @@ namespace Barotrauma
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 (rightHand.IsSevered) return;
transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0];
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
{
if (character.SelectedItems[0] == item)
{
if (rightHand.IsSevered) return;
rightHand.Disabled = true;
}
if (character.SelectedItems[1] == item)
{
if (leftHand.IsSevered) return;
leftHand.Disabled = true;
}
@@ -1235,6 +1278,24 @@ namespace Barotrauma
Vector2 currItemPos = (character.SelectedItems[0] == item) ?
rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0] :
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(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);
}
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()
{
base.Flip();

View File

@@ -3,6 +3,7 @@ using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -25,6 +26,11 @@ namespace Barotrauma
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));
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 limbs;
@@ -42,6 +48,7 @@ namespace Barotrauma
frozen = value;
Collider.PhysEnabled = !frozen;
if (frozen && MainLimb != null) MainLimb.pullJoint.WorldAnchorB = MainLimb.SimPosition;
}
}
@@ -87,7 +94,11 @@ namespace Barotrauma
protected List<PhysicsBody> collider;
protected int colliderIndex = 0;
private Category prevCollisionCategory = Category.None;
private Body outsideCollisionBlocker;
public PhysicsBody Collider
{
get
@@ -269,11 +280,7 @@ namespace Barotrauma
get { return ignorePlatforms; }
set
{
if (ignorePlatforms == value) return;
ignorePlatforms = value;
UpdateCollisionCategories();
}
}
@@ -352,21 +359,29 @@ namespace Barotrauma
if (collider[0] == null)
{
DebugConsole.ThrowError("No collider configured for \"" + character.Name + "\"!");
collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f);
collider[0].UserData = character;
collider[0].BodyType = BodyType.Dynamic;
collider[0].CollisionCategories = Physics.CollisionCharacter;
collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f)
{
UserData = character,
BodyType = BodyType.Dynamic,
CollisionCategories = Physics.CollisionCharacter
};
collider[0].FarseerBody.AngularDamping = 5.0f;
collider[0].FarseerBody.FixedRotation = true;
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();
foreach (var joint in LimbJoints)
{
joint.BodyB.SetTransform(
joint.BodyA.Position + (joint.LocalAnchorA - joint.LocalAnchorB)*0.1f,
joint.LimbB?.body?.SetTransform(
joint.BodyA.Position + (joint.LocalAnchorA - joint.LocalAnchorB) * 0.1f,
(joint.LowerLimit + joint.UpperLimit) / 2.0f);
}
@@ -391,7 +406,7 @@ namespace Barotrauma
Limb torso = GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head);
MainLimb = torso == null ? head : torso;
MainLimb = torso ?? head;
}
public void AddJoint(XElement subElement, float scale = 1.0f)
@@ -488,7 +503,10 @@ namespace Barotrauma
Structure structure = f2.Body.UserData as Structure;
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
if (structure == null)
{
@@ -739,7 +757,15 @@ namespace Barotrauma
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);
@@ -754,10 +780,13 @@ namespace Barotrauma
//this is preferable to the cost of using continuous collision detection for the character collider
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
Vector2? intersection = MathUtils.GetLineRectangleIntersection(
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));
if (intersection != null)
@@ -774,15 +803,7 @@ namespace Barotrauma
//in -> out
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;
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity);
}
//out -> in
@@ -799,10 +820,49 @@ namespace Barotrauma
}
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)
@@ -842,7 +902,10 @@ namespace Barotrauma
Category collisionCategory = (ignorePlatforms) ?
wall | Physics.CollisionProjectile | Physics.CollisionStairs
: wall | Physics.CollisionProjectile | Physics.CollisionPlatform | Physics.CollisionStairs;
if (collisionCategory == prevCollisionCategory) return;
prevCollisionCategory = collisionCategory;
Collider.CollidesWith = collisionCategory;
foreach (Limb limb in Limbs)
@@ -868,10 +931,12 @@ namespace Barotrauma
UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider();
UpdateCollisionCategories();
Vector2 flowForce = Vector2.Zero;
FindHull();
PreventOutsideCollision();
splashSoundTimer -= deltaTime;
@@ -1089,22 +1154,19 @@ namespace Barotrauma
float tfloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction;
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;
newSpeed.Y = (targetY - Collider.SimPosition.Y)*5.0f;
Collider.LinearVelocity = newSpeed;
}
else
{
Vector2 newSpeed = Collider.LinearVelocity;
newSpeed.Y = 0.0f;
Collider.LinearVelocity = newSpeed;
Vector2 newPos = Collider.SimPosition;
newPos.Y = targetY;
Collider.SetTransform(newPos, Collider.Rotation);
}
if (forceImmediate)
{
Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, 0);
Collider.SetTransform(new Vector2(Collider.SimPosition.X, targetY), Collider.Rotation);
}
else
{
Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, (targetY - Collider.SimPosition.Y) * 5.0f);
}
}
}
}
}
@@ -1165,6 +1227,16 @@ namespace Barotrauma
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;
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
SetPosition(Collider.SimPosition, true);
UpdateCollisionCategories();
collisionsDisabled = false;
//force collision categories to be updated
prevCollisionCategory = Category.None;
}
}
@@ -1312,8 +1384,24 @@ namespace Barotrauma
character.AnimController.Anim = AnimController.Animation.None;
}
Collider.LinearVelocity = Vector2.Zero;
Collider.CorrectPosition(character.MemState, deltaTime, out overrideTargetMovement);
Vector2 newVelocity = Vector2.Zero;
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
// -> we need to correct it manually
@@ -1431,10 +1519,18 @@ namespace Barotrauma
}
}
Collider.SetTransform(Collider.SimPosition + positionError, Collider.Rotation + rotationError);
foreach (Limb limb in Limbs)
float errorMagnitude = positionError.Length();
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()
{
Vector2 limbPos = ConvertUnits.ToDisplayUnits(Limbs[0].SimPosition);
Vector2 limbPos = Limbs[0].Position;
Vector2 force = Vector2.Zero;
foreach (MapEntity e in MapEntity.mapEntityList)
foreach (Gap gap in Gap.GapList)
{
Gap gap = e as Gap;
if (gap == null || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce == Vector2.Zero) continue;
if (gap.Open <= 0.0f || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce.LengthSquared() < 0.01f) continue;
Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos);
force += Vector2.Normalize(gap.LerpedFlowForce) * (Math.Max(gap.LerpedFlowForce.Length() - dist, 0.0f) / 500.0f);
}
if (force.Length() > 20.0f) return force;
return force;
}
@@ -1526,6 +1617,12 @@ namespace Barotrauma
b.Remove();
}
if (outsideCollisionBlocker != null)
{
GameMain.World.RemoveBody(outsideCollisionBlocker);
outsideCollisionBlocker = null;
}
if (LimbJoints != null)
{
foreach (RevoluteJoint joint in LimbJoints)

View File

@@ -144,6 +144,10 @@ namespace Barotrauma
{
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;
}
}
@@ -1611,12 +1615,20 @@ namespace Barotrauma
//Health effects
if (needsAir) UpdateOxygen(deltaTime);
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
if (DoesBleed)
{
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
}
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
UpdateSightRange();
if (aiTarget != null) aiTarget.SoundRange = 0.0f;
if (!IsDead) LockHands = false;
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
//ragdoll button
if (IsRagdolled)
@@ -1643,21 +1655,7 @@ namespace Barotrauma
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;
//CPR stuff is handled in the UpdateCPR function in HumanoidAnimController
}
partial void UpdateControlled(float deltaTime, Camera cam);
@@ -1960,7 +1958,7 @@ namespace Barotrauma
characterType = "Enemy";
else if (AIController is HumanAIController)
characterType = "AICrew";
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath);
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath);
}
if (OnDeath != null) OnDeath(this, causeOfDeath);
@@ -1997,7 +1995,7 @@ namespace Barotrauma
}
partial void KillProjSpecific();
public void Revive(bool isNetworkMessage)
public void Revive()
{
if (Removed)
{
@@ -2024,6 +2022,7 @@ namespace Barotrauma
foreach (Limb limb in AnimController.Limbs)
{
limb.body.Enabled = true;
limb.IsSevered = false;
}
@@ -2044,6 +2043,9 @@ namespace Barotrauma
base.Remove();
if (selectedItems[0] != null) selectedItems[0].Drop(this);
if (selectedItems[1] != null) selectedItems[1].Drop(this);
if (info != null) info.Remove();
CharacterList.Remove(this);
@@ -2054,9 +2056,6 @@ namespace Barotrauma
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)
{
if (c.focusedCharacter == this) c.focusedCharacter = null;

View File

@@ -17,11 +17,16 @@ namespace Barotrauma
get
{
string disguiseName = "?";
if (Character == null || !Character.HideFace)
if (Character == null || !Character.HideFace || (GameMain.Server != null && !GameMain.Server.AllowDisguises))
{
return Name;
}
#if CLIENT
if (GameMain.Client != null && !GameMain.Client.AllowDisguises)
{
return Name;
}
#endif
if (Character.Inventory != null)
{
int cardSlotIndex = Character.Inventory.FindLimbSlot(InvSlotType.Card);

View File

@@ -79,11 +79,16 @@ namespace Barotrauma
private List<CharacterStateInfo> memState = new List<CharacterStateInfo>();
private List<CharacterStateInfo> memLocalState = new List<CharacterStateInfo>();
private bool networkUpdateSent;
public bool isSynced = false;
public string OwnerClientIP;
public string OwnerClientName;
public bool ClientDisconnected;
public float KillDisconnectedTimer;
public List<CharacterStateInfo> MemState
{
get { return memState; }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -135,6 +136,11 @@ namespace Barotrauma
if (!string.IsNullOrWhiteSpace(spawnPoint.IdCardDesc))
item.Description = spawnPoint.IdCardDesc;
}
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = character.TeamID;
}
if (parentItem != null) parentItem.Combine(item);

View File

@@ -85,9 +85,10 @@ namespace Barotrauma
set
{
isSevered = value;
if (!isSevered) severedFadeOutTimer = 0.0f;
#if CLIENT
if (isSevered) damage = 100.0f;
#endif
#endif
}
}

View File

@@ -190,6 +190,17 @@ namespace Barotrauma
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);
},
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) =>
@@ -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) =>
{
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);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
revivedCharacter.Revive();
if (GameMain.Server != null)
{
foreach (Client c in GameMain.Server.ConnectedClients)
@@ -1342,7 +1353,7 @@ namespace Barotrauma
Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
revivedCharacter.Revive();
if (GameMain.Server != null)
{
foreach (Client c in GameMain.Server.ConnectedClients)
@@ -1502,6 +1513,13 @@ namespace Barotrauma
{
Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
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) =>
@@ -1712,7 +1730,7 @@ namespace Barotrauma
var itemContainer = item.GetComponent<ItemContainer>();
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 });
@@ -1895,10 +1913,19 @@ namespace Barotrauma
return;
}
if (string.IsNullOrWhiteSpace(command)) return;
if (string.IsNullOrWhiteSpace(command) || command == "\\" || command == "\n") return;
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"))
{
NewMessage(command, Color.White, true);
@@ -1976,6 +2003,13 @@ namespace Barotrauma
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
{
matchingCommand.ServerExecuteOnClientRequest(client, cursorWorldPos, splitCommand.Skip(1).ToArray());
@@ -2139,57 +2173,55 @@ namespace Barotrauma
Vector2? spawnPos = null;
Inventory spawnInventory = null;
if (args.Length > 1)
int extraParams = 0;
switch (args.Last().ToLowerInvariant())
{
switch (args[1])
{
case "cursor":
spawnPos = cursorPos;
break;
case "inventory":
spawnInventory = controlledCharacter?.Inventory;
break;
default:
//Check if last arg matches the name of an in-game player
if (GameMain.Server != null)
{
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower());
if (client == null)
{
NewMessage("No player found with the name \"" + args.Last() + "\". Spawning item at random location. If the player you want to give the item to has a space in their name, try surrounding their name with quotes (\").", Color.Red);
break;
}
else if (client.Character == null)
{
errorMsg = "The player \"" + args.Last() + "\" is connected, but hasn't spawned yet.";
return;
}
else
{
//If the last arg matches the name of an in-game player, set the destination to their inventory.
spawnInventory = client.Character.Inventory;
break;
}
}
else
{
var matchingCharacter = FindMatchingCharacter(args.Skip(1).ToArray());
if (matchingCharacter?.Inventory != null) spawnInventory = matchingCharacter.Inventory;
}
break;
}
case "cursor":
extraParams = 1;
spawnPos = cursorPos;
break;
case "inventory":
extraParams = 1;
spawnInventory = controlledCharacter == null ? null : controlledCharacter.Inventory;
break;
case "cargo":
var wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub);
spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
break;
//Dont do a thing, random is basically Human points anyways - its in the help description.
case "random":
extraParams = 1;
return;
default:
extraParams = 0;
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)
{
errorMsg = "Item \"" + itemName + "\" not found!";
return;
}
if (spawnPos == null && spawnInventory == null)
if ((spawnPos == null || spawnPos == Vector2.Zero) && spawnInventory == null)
{
var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub);
spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition;
@@ -2198,6 +2230,7 @@ namespace Barotrauma
if (spawnPos != null)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
}
else if (spawnInventory != null)
{

View File

@@ -37,7 +37,6 @@ namespace Barotrauma
Level.PositionType.Cave | Level.PositionType.MainPath | Level.PositionType.Ruin, 500.0f, 10000.0f, 30.0f);
item = new Item(itemPrefab, position, null);
item.MoveWithLevel = true;
item.body.FarseerBody.IsKinematic = true;
//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)
{
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;
state = 1;

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
@@ -72,7 +73,7 @@ namespace Barotrauma
descriptions[i] = descriptions[i].Replace("[location" + (n + 1) + "]", locations[n].Name);
}
}
teamNames = new string[]
{
prefab.ConfigElement.GetAttributeString("teamname1", "Team A"),
@@ -145,6 +146,23 @@ namespace Barotrauma
subs[1].SetPosition(Level.Loaded.EndPosition - new Vector2(0.0f, 2000.0f));
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>() };
foreach (Submarine submarine in Submarine.Loaded)
@@ -208,7 +226,7 @@ namespace Barotrauma
}
else
{
if (winner>=0 && subs[winner] != null &&
if (winner >= 0 && subs[winner] != null &&
(winner == 0 && subs[winner].AtStartPosition) || (winner == 1 && subs[winner].AtEndPosition) &&
crews[winner].Any(c => !c.IsDead && c.Submarine == subs[winner]))
{

View File

@@ -50,7 +50,6 @@ namespace Barotrauma
Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
item = new Item(itemPrefab, position, null);
item.MoveWithLevel = true;
item.body.FarseerBody.IsKinematic = true;
if (item.HasTag("alien"))

View File

@@ -1,19 +1,51 @@
using GameAnalyticsSDK.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
namespace Barotrauma
{
public static class GameAnalyticsManager
{
private static HashSet<string> sentEventIdentifiers = new HashSet<string>();
public static void Init()
{
#if DEBUB
#if DEBUG
GameAnalytics.SetEnabledInfoLog(true);
#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.Initialize("a3a073c20982de7c15d21e840e149122", "9010ad9a671233b8d9610d76cec8c897d9ff3ba7");
string contentPackageName = GameMain.Config?.SelectedContentPackage?.Name;
if (!string.IsNullOrEmpty(contentPackageName))
{
@@ -21,5 +53,47 @@ namespace Barotrauma
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);
}
}
}

View File

@@ -6,26 +6,38 @@ using System.Linq;
namespace Barotrauma
{
class PurchasedItem
{
public ItemPrefab itemPrefab;
public int quantity;
public PurchasedItem(ItemPrefab itemPrefab, int quantity)
{
this.itemPrefab = itemPrefab;
this.quantity = quantity;
}
}
class CargoManager
{
private readonly List<ItemPrefab> purchasedItems;
private readonly List<PurchasedItem> purchasedItems;
private readonly CampaignMode campaign;
public Action OnItemsChanged;
public List<ItemPrefab> PurchasedItems
public List<PurchasedItem> PurchasedItems
{
get { return purchasedItems; }
}
public CargoManager(CampaignMode campaign)
{
purchasedItems = new List<ItemPrefab>();
purchasedItems = new List<PurchasedItem>();
this.campaign = campaign;
}
public void SetPurchasedItems(List<ItemPrefab> items)
public void SetPurchasedItems(List<PurchasedItem> items)
{
purchasedItems.Clear();
purchasedItems.AddRange(items);
@@ -33,33 +45,53 @@ namespace Barotrauma
OnItemsChanged?.Invoke();
}
public void PurchaseItem(ItemPrefab item)
public void PurchaseItem(ItemPrefab item, int Quantity = 1)
{
campaign.Money -= item.Price;
purchasedItems.Add(item);
PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.itemPrefab == 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();
}
public void SellItem(ItemPrefab item)
public void SellItem(ItemPrefab item, int quantity = 1)
{
campaign.Money += item.Price;
purchasedItems.Remove(item);
campaign.Money += (item.Price * quantity);
PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.itemPrefab == item);
if (purchasedItem != null && purchasedItem.quantity - quantity > 0)
{
purchasedItem.quantity -= quantity;
}
else
{
PurchasedItems.Remove(purchasedItem);
}
OnItemsChanged?.Invoke();
}
public int GetTotalItemCost()
{
return purchasedItems.Sum(i => i.Price);
return purchasedItems.Sum(i => (i.itemPrefab.Price * i.quantity));
}
public void CreateItems()
{
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);
@@ -78,24 +110,25 @@ namespace Barotrauma
}
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(
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;
if (!string.IsNullOrEmpty(prefab.CargoContainerName))
if (!string.IsNullOrEmpty(pi.itemPrefab.CargoContainerName))
{
itemContainer = availableContainers.Keys.ToList().Find(ac =>
ac.Item.Prefab.NameMatches(prefab.CargoContainerName) ||
ac.Item.Prefab.Tags.Contains(prefab.CargoContainerName.ToLowerInvariant()));
ac.Item.Prefab.NameMatches(pi.itemPrefab.CargoContainerName) ||
ac.Item.Prefab.Tags.Contains(pi.itemPrefab.CargoContainerName.ToLowerInvariant()));
if (itemContainer == null)
{
var containerPrefab = MapEntityPrefab.List.Find(ep =>
ep.NameMatches(prefab.CargoContainerName) ||
(ep.Tags != null && ep.Tags.Contains(prefab.CargoContainerName.ToLowerInvariant()))) as ItemPrefab;
containerPrefab = MapEntityPrefab.List.Find(ep =>
ep.NameMatches(pi.itemPrefab.CargoContainerName) ||
(ep.Tags != null && ep.Tags.Contains(pi.itemPrefab.CargoContainerName.ToLowerInvariant()))) as ItemPrefab;
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
if (GameMain.Server != null)
{
Entity.Spawner.AddToSpawnQueue(prefab, itemContainer.Inventory);
Entity.Spawner.AddToSpawnQueue(pi.itemPrefab, itemContainer.Inventory);
}
else
{
var item = new Item(prefab, position, wp.Submarine);
var item = new Item(pi.itemPrefab, position, wp.Submarine);
itemContainer.Inventory.TryPutItem(item, null);
}
//reduce the number of available slots in the container
availableContainers[itemContainer]--;
if (availableContainers[itemContainer] <= 0)
//if there is a container
if (availableContainers.ContainsKey(itemContainer))
{
availableContainers[itemContainer]--;
}
if (availableContainers.ContainsKey(itemContainer) && availableContainers[itemContainer] <= 0)
{
availableContainers.Remove(itemContainer);
}
}
}
}
itemsToSpawn.Clear();
}
}

View File

@@ -194,9 +194,10 @@ namespace Barotrauma
msg.Write(Money);
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 purchasedItemCount = msg.ReadUInt16();
List<ItemPrefab> purchasedItems = new List<ItemPrefab>();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++)
{
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))
@@ -220,15 +222,15 @@ namespace Barotrauma
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
List<ItemPrefab> currentItems = new List<ItemPrefab>(CargoManager.PurchasedItems);
foreach (ItemPrefab ip in currentItems)
List<PurchasedItem> currentItems = new List<PurchasedItem>(CargoManager.PurchasedItems);
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);
}
}
}

View File

@@ -182,7 +182,7 @@ namespace Barotrauma
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
GameMain.LightManager.LosEnabled = GameMain.NetworkMember == null || GameMain.NetworkMember.CharacterInfo != null;
@@ -210,11 +210,10 @@ namespace Barotrauma
Submarine.MainSubs[1].Load(false);
}
}
if (level != null)
{
level.Generate();
level.Generate(mirrorLevel);
submarine.SetPosition(submarine.FindSpawnPos(level.StartPosition - new Vector2(0.0f, 2000.0f)));
}
@@ -229,18 +228,17 @@ namespace Barotrauma
if (GameMode != null)
{
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)
{
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Submarine:" + submarine.Name);
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameAnalyticsManager.AddDesignEvent("Submarine:" + submarine.Name);
GameAnalyticsManager.AddDesignEvent("Level", ToolBox.StringToInt(level.Seed));
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
#if CLIENT
roundSummary = new RoundSummary(this);
@@ -253,11 +251,10 @@ namespace Barotrauma
public void EndRound(string endMessage)
{
if (Mission != null) Mission.End();
if (GameSettings.SendUserStatistics)
{
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent((Mission == null || Mission.Completed) ? GameAnalyticsSDK.Net.EGAProgressionStatus.Complete : GameAnalyticsSDK.Net.EGAProgressionStatus.Fail,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
GameAnalyticsManager.AddProgressionEvent(
(Mission == null || Mission.Completed) ? GameAnalyticsSDK.Net.EGAProgressionStatus.Complete : GameAnalyticsSDK.Net.EGAProgressionStatus.Fail,
GameMode.Name,
(Mission == null ? "None" : Mission.GetType().ToString()));
#if CLIENT
if (roundSummary != null)

View File

@@ -164,7 +164,7 @@ namespace Barotrauma
GameMain.Config.Save("config.xml");
}
}
public static bool ShowUserStatisticsPrompt { get; private set; }
public static bool ShowUserStatisticsPrompt { get; set; }
public GameSettings(string filePath)
{
@@ -334,8 +334,12 @@ namespace Barotrauma
new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
new XAttribute("enablesplashscreen", EnableSplashScreen),
new XAttribute("senduserstatistics", sendUserStatistics));
new XAttribute("enablesplashscreen", EnableSplashScreen));
if (!ShowUserStatisticsPrompt)
{
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
}
if (WasGameUpdated)
{
@@ -398,7 +402,16 @@ namespace Barotrauma
new XAttribute("gender", characterGender));
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()

View File

@@ -50,6 +50,10 @@ namespace Barotrauma
}
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)
{
if (ic.DeleteOnUse)

View File

@@ -27,18 +27,14 @@ namespace Barotrauma.Items.Components
private Joint joint;
private Hull[] hulls;
private ushort?[] hullIds;
private readonly Hull[] hulls = new Hull[2];
private Gap gap;
private Door door;
private Body[] bodies;
private Body doorBody;
private Gap gap;
private ushort? gapId;
private bool docked;
public int DockingDir
@@ -114,9 +110,7 @@ namespace Barotrauma.Items.Components
}
IsActive = true;
hullIds = new ushort?[2];
list.Add(this);
}
@@ -129,7 +123,7 @@ namespace Barotrauma.Items.Components
if (joint != null)
{
CreateJoint(joint is WeldJoint);
LinkHullsToGap();
LinkHullsToGaps();
}
else if (dockingTarget.joint != null)
{
@@ -138,7 +132,7 @@ namespace Barotrauma.Items.Components
{
dockingTarget.CreateJoint(dockingTarget.joint is WeldJoint);
}
dockingTarget.LinkHullsToGap();
dockingTarget.LinkHullsToGaps();
}
}
}
@@ -207,22 +201,16 @@ namespace Barotrauma.Items.Components
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir;
foreach (WayPoint wp in WayPoint.WayPointList)
if (door != null && dockingTarget.door != null)
{
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;
foreach (WayPoint wp2 in WayPoint.WayPointList)
if (myWayPoint != null && targetWayPoint != null)
{
if (wp2.Submarine != dockingTarget.item.Submarine || wp2.SpawnType != SpawnType.Path) continue;
if (!Submarine.RectContains(dockingTarget.item.Rect, wp2.Position)) continue;
wp.linkedTo.Add(wp2);
wp2.linkedTo.Add(wp);
myWayPoint.linkedTo.Add(targetWayPoint);
targetWayPoint.linkedTo.Add(myWayPoint);
}
}
@@ -238,35 +226,37 @@ namespace Barotrauma.Items.Components
{
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");
return;
}
else if (joint is WeldJoint)
{
//DebugConsole.ThrowError("Error - attempted to lock a docking port that's already locked");
return;
}
dockingDir = IsHorizontal ?
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir;
if (!(joint is WeldJoint))
{
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
PlaySound(ActionType.OnSecondaryUse, item.WorldPosition);
PlaySound(ActionType.OnSecondaryUse, item.WorldPosition);
#endif
ConnectWireBetweenPorts();
ConnectWireBetweenPorts();
CreateJoint(true);
CreateJoint(true);
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
if (GameMain.Server != null)
{
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))
{
CreateHull();
@@ -345,17 +335,31 @@ namespace Barotrauma.Items.Components
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,
dockingTarget.door.Body.width,
dockingTarget.door.Body.height,
1.0f,
position,
dockingTarget.door);
doorBody.CollisionCategories = Physics.CollisionWall;
doorBody.BodyType = BodyType.Static;
doorBody.SetTransform(
ConvertUnits.ToSimUnits(item.Position + (dockingTarget.door.Item.WorldPosition - item.WorldPosition)),
0.0f);
}
private void CreateHull()
@@ -363,7 +367,6 @@ namespace Barotrauma.Items.Components
var hullRects = new Rectangle[] { item.WorldRect, dockingTarget.item.WorldRect };
var subs = new Submarine[] { item.Submarine, dockingTarget.item.Submarine };
hulls = new Hull[2];
bodies = new Body[4];
if (dockingTarget.door != null)
@@ -392,6 +395,7 @@ namespace Barotrauma.Items.Components
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]);
hulls[i].AddToGrid(subs[i]);
hulls[i].FreeID();
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]);
if (gapId != null) gap.ID = (ushort)gapId;
LinkHullsToGap();
}
else
{
@@ -422,25 +423,22 @@ namespace Barotrauma.Items.Components
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
hulls[i] = new Hull(MapEntityPrefab.Find("Hull"), hullRects[i], subs[i]);
hulls[i].AddToGrid(subs[i]);
if (hullIds[i] != null) hulls[i].ID = (ushort)hullIds[i];
hulls[i].FreeID();
}
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[1]);
hullIds[0] = hulls[0].ID;
hullIds[1] = hulls[1].ID;
gap.FreeID();
gap.DisableHullRechecks = true;
gapId = gap.ID;
gap.ShouldBeSaved = false;
item.linkedTo.Add(gap);
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)
{
@@ -491,6 +489,37 @@ namespace Barotrauma.Items.Components
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()
@@ -503,25 +532,19 @@ namespace Barotrauma.Items.Components
dockingTarget.item.Submarine.DockedTo.Remove(item.Submarine);
item.Submarine.DockedTo.Remove(dockingTarget.item.Submarine);
//remove all waypoint links between this sub and the dockingtarget
foreach (WayPoint wp in WayPoint.WayPointList)
if (door != null && dockingTarget.door != null)
{
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;
if (wp2 == null) continue;
if (wp.Submarine == dockingTarget.item.Submarine)
{
wp.linkedTo.RemoveAt(i);
wp2.linkedTo.Remove(wp);
}
myWayPoint.linkedTo.Remove(targetWayPoint);
targetWayPoint.linkedTo.Remove(myWayPoint);
}
}
item.linkedTo.Clear();
docked = false;
@@ -546,26 +569,17 @@ namespace Barotrauma.Items.Components
GameMain.World.RemoveJoint(joint);
joint = null;
}
if (hulls != null)
{
hulls[0].Remove();
hulls[1].Remove();
hulls = null;
}
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
if (gap != null)
{
gap.Remove();
gap = null;
}
hullIds[0] = null;
hullIds[1] = null;
gapId = null;
if (bodies!=null)
if (bodies != null)
{
foreach (Body body in bodies)
{
@@ -627,6 +641,9 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
list.Remove(this);
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
gap?.Remove(); gap = null;
}
public override void OnMapLoaded()
@@ -715,19 +732,8 @@ namespace Barotrauma.Items.Components
if (docked)
{
msg.Write(dockingTarget.item.ID);
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);
}
msg.Write(dockingTarget.item.ID);
msg.Write(hulls != null && hulls[0] != null && hulls[1] != null && gap != null);
}
}
@@ -735,19 +741,27 @@ namespace Barotrauma.Items.Components
{
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)
{
ushort dockingTargetID = msg.ReadUInt16();
bool isLocked = msg.ReadBoolean();
if (isLocked)
{
hullIds[0] = msg.ReadUInt16();
hullIds[1] = msg.ReadUInt16();
gapId = msg.ReadUInt16();
}
Entity targetEntity = Entity.FindEntityByID(dockingTargetID);
if (targetEntity == null || !(targetEntity is Item))
{
@@ -767,10 +781,6 @@ namespace Barotrauma.Items.Components
if (isLocked)
{
Lock(true);
hulls[0].ID = (ushort)hullIds[0];
hulls[1].ID = (ushort)hullIds[1];
gap.ID = (ushort)gapId;
}
}
else

View File

@@ -334,9 +334,13 @@ namespace Barotrauma.Items.Components
body.Remove();
body = null;
}
if (linkedGap != null) linkedGap.Remove();
//no need to remove the gap if we're unloading the whole submarine
//otherwise the gap will be removed twice and cause console warnings
if (!Submarine.Unloading)
{
if (linkedGap != null) linkedGap.Remove();
}
doorSprite.Remove();
if (weldedSprite != null) weldedSprite.Remove();
@@ -349,6 +353,14 @@ namespace Barotrauma.Items.Components
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
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)
{
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);
List<PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList();
@@ -368,17 +390,24 @@ namespace Barotrauma.Items.Components
foreach (PhysicsBody body in bodies)
{
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 (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue;
diff = body.SimPosition.Y - item.SimPosition.Y;
}
else
{
if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y) continue;
diff = body.SimPosition.X - item.SimPosition.X;
}
@@ -442,8 +471,10 @@ namespace Barotrauma.Items.Components
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)
{
//clients can "predict" that the door opens/closes when a signal is received

View File

@@ -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)
{
Drop(true, dropper);
@@ -160,7 +171,24 @@ namespace Barotrauma.Items.Components
if (item.body != null)
{
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);
@@ -214,6 +242,14 @@ namespace Barotrauma.Items.Components
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)
{
if (!attachable)
@@ -221,6 +257,8 @@ namespace Barotrauma.Items.Components
return base.Pick(picker);
}
if (!CanBeDeattached()) return false;
if (Attached)
{
return base.Pick(picker);
@@ -330,7 +368,7 @@ namespace Barotrauma.Items.Components
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
{
@@ -410,7 +448,7 @@ namespace Barotrauma.Items.Components
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());
if (!attachable)
@@ -419,7 +457,7 @@ namespace Barotrauma.Items.Components
return;
}
if (isAttached)
if (shouldBeAttached)
{
Drop(false, null);
item.SetTransform(simPosition, 0.0f);
@@ -427,16 +465,19 @@ namespace Barotrauma.Items.Components
}
else
{
DropConnectedWires(null);
if (body != null)
if (attached)
{
item.body = body;
item.body.Enabled = true;
}
IsActive = false;
DropConnectedWires(null);
DeattachFromWall();
if (body != null)
{
item.body = body;
item.body.Enabled = true;
}
IsActive = false;
DeattachFromWall();
}
}
}
}

View File

@@ -3,6 +3,7 @@ using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -24,6 +25,8 @@ namespace Barotrauma.Items.Components
private float reloadTimer;
private HashSet<Entity> hitTargets = new HashSet<Entity>();
[Serialize(0.0f, false)]
public float Range
{
@@ -94,6 +97,7 @@ namespace Barotrauma.Items.Components
}
hitting = true;
hitTargets.Clear();
IsActive = true;
return false;
@@ -131,8 +135,7 @@ namespace Barotrauma.Items.Components
{
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);
}
else
@@ -142,28 +145,13 @@ namespace Barotrauma.Items.Components
}
else
{
//Vector2 diff = Vector2.Normalize(picker.CursorPosition - ac.RefLimb.Position);
//diff.X = diff.X * ac.Dir;
hitPos -= deltaTime*15.0f;
//angl = -hitPos * 2.0f;
// System.Diagnostics.Debug.WriteLine("<1.0f "+hitPos);
hitPos -= deltaTime * 15.0f;
ac.HoldItem(deltaTime, item, handlePos, new Vector2(0.6f, -0.1f), new Vector2(-0.3f, 0.2f), false, hitPos);
//}
//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)
if (hitPos < -MathHelper.PiOver4 * 1.2f)
{
RestoreCollision();
hitting = false;
hitTargets.Clear();
}
}
}
@@ -230,15 +218,22 @@ namespace Barotrauma.Items.Components
targetLimb = (Limb)f2.Body.UserData;
if (targetLimb.IsSevered || targetLimb.character == null) return false;
targetCharacter = targetLimb.character;
if (hitTargets.Contains(targetCharacter)) return false;
hitTargets.Add(targetCharacter);
}
else if (f2.Body.UserData is Character)
{
targetCharacter = (Character)f2.Body.UserData;
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)
{
targetStructure = (Structure)f2.Body.UserData;
if (hitTargets.Contains(targetStructure)) return false;
hitTargets.Add(targetStructure);
}
else
{
@@ -266,10 +261,7 @@ namespace Barotrauma.Items.Components
return false;
}
}
RestoreCollision();
hitting = false;
if (GameMain.Client != null) return true;
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits

View File

@@ -127,18 +127,8 @@ namespace Barotrauma.Items.Components
Color.Red, Color.Green);
#endif
picker.AnimController.Anim = AnimController.Animation.UsingConstruction;
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);
picker.AnimController.UpdateUseItem(true, item.SimPosition + Vector2.UnitY * ((pickTimer / 10.0f) % 0.1f));
pickTimer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;

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