diff --git a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs
index 652acf037..40fceb2b3 100644
--- a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs
+++ b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs
@@ -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.4")]
-[assembly: AssemblyFileVersion("0.8.1.4")]
+[assembly: AssemblyVersion("0.8.1.5")]
+[assembly: AssemblyFileVersion("0.8.1.5")]
diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
index 19d86a62f..b095508b2 100644
--- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
+++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
@@ -394,7 +394,7 @@ namespace Barotrauma
NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.White);
}));
- commands.Add(new Command("followsub", "followsub: Toggle whether the ", (string[] args) =>
+ commands.Add(new Command("followsub", "followsub: Toggle whether the camera should follow the nearest submarine.", (string[] args) =>
{
Camera.FollowSub = !Camera.FollowSub;
NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White);
diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
index 020657672..47c0a4e7a 100644
--- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
+++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
@@ -822,7 +822,7 @@ namespace Barotrauma.Networking
}
else
{
- submarines.Add(new Submarine(Path.Combine(Submarine.SavePath, subName), subHash, false));
+ submarines.Add(new Submarine(Path.Combine(Submarine.SavePath, subName) + ".sub", subHash, false));
}
}
@@ -1441,6 +1441,8 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientPermissions.Ban);
msg.Write(kickedName);
msg.Write(reason);
+ msg.Write(range);
+ msg.Write(duration.HasValue ? duration.Value.TotalSeconds : 0.0); //0 = permaban
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
diff --git a/Barotrauma/BarotraumaClient/Source/Program.cs b/Barotrauma/BarotraumaClient/Source/Program.cs
index 910b4e60d..4cb2af6c2 100644
--- a/Barotrauma/BarotraumaClient/Source/Program.cs
+++ b/Barotrauma/BarotraumaClient/Source/Program.cs
@@ -3,10 +3,10 @@
using System;
using System.IO;
using System.Text;
+using GameAnalyticsSDK.Net;
#if WINDOWS
using System.Windows.Forms;
-using GameAnalyticsSDK.Net;
using Microsoft.Xna.Framework.Graphics;
#endif
diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs
index eee9771d2..6f5edabac 100644
--- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs
+++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs
@@ -561,14 +561,18 @@ namespace Barotrauma
var playYourself = new GUITickBox(new Rectangle(0, 0, 20, 20), TextManager.Get("PlayYourself"), Alignment.TopLeft, myPlayerFrame);
playYourself.Selected = GameMain.NetworkMember.CharacterInfo != null;
playYourself.OnSelected = TogglePlayYourself;
- playYourself.UserData = "playyourself";
-
- GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame);
- toggleHead.UserData = -1;
- toggleHead.OnClicked = ToggleHead;
- toggleHead = new GUIButton(new Rectangle(60, 50, 15, 15), ">", "", myPlayerFrame);
- toggleHead.UserData = 1;
- toggleHead.OnClicked = ToggleHead;
+ playYourself.UserData = "playyourself";
+
+ GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame)
+ {
+ UserData = -1,
+ OnClicked = ToggleHead
+ };
+ toggleHead = new GUIButton(new Rectangle(60, 50, 15, 15), ">", "", myPlayerFrame)
+ {
+ UserData = 1,
+ OnClicked = ToggleHead
+ };
new GUITextBlock(new Rectangle(100, 30, 200, 30), TextManager.Get("Gender"), "", myPlayerFrame);
@@ -631,7 +635,10 @@ namespace Barotrauma
{
if (tickBox.Selected)
{
- GameMain.NetworkMember.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, GameMain.NetworkMember.Name, Gender.None, null);
+ GameMain.NetworkMember.CharacterInfo =
+ new CharacterInfo(Character.HumanConfigFile, GameMain.NetworkMember.Name, GameMain.Config.CharacterGender, null);
+ GameMain.NetworkMember.CharacterInfo.HeadSpriteId = GameMain.Config.CharacterHeadIndex;
+
UpdatePlayerFrame(GameMain.NetworkMember.CharacterInfo);
}
else
@@ -1249,14 +1256,12 @@ namespace Barotrauma
private bool ToggleHead(GUIButton button, object userData)
{
- int dir = (int)userData;
-
if (GameMain.NetworkMember.CharacterInfo == null) return true;
+ int dir = (int)userData;
GameMain.NetworkMember.CharacterInfo.HeadSpriteId += dir;
-
+ GameMain.Config.CharacterHeadIndex = GameMain.NetworkMember.CharacterInfo.HeadSpriteId;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
-
return true;
}
@@ -1264,7 +1269,7 @@ namespace Barotrauma
{
Gender gender = (Gender)obj;
GameMain.NetworkMember.CharacterInfo.Gender = gender;
-
+ GameMain.Config.CharacterGender = GameMain.NetworkMember.CharacterInfo.Gender;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
return true;
}
diff --git a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs
index 420567bf2..66089601e 100644
--- a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs
+++ b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs
@@ -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.4")]
-[assembly: AssemblyFileVersion("0.8.1.4")]
+[assembly: AssemblyVersion("0.8.1.5")]
+[assembly: AssemblyFileVersion("0.8.1.5")]
diff --git a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs
index 0d31c1d48..7204c5899 100644
--- a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs
+++ b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs
@@ -14,7 +14,7 @@ namespace Barotrauma
{
lock (QueuedCommands)
{
- while (QueuedCommands.Count>0)
+ while (QueuedCommands.Count > 0)
{
ExecuteCommand(QueuedCommands[0]);
QueuedCommands.RemoveAt(0);
@@ -190,6 +190,16 @@ namespace Barotrauma
NewMessage(ent.ToString(), Color.Lime);
}
}));
+
+ //"dummy commands" that only exist so that the server can give clients permissions to use them
+ commands.Add(new Command("control|controlcharacter", "control [character name]: Start controlling the specified character (client-only).", null));
+ commands.Add(new Command("los", "Toggle the line of sight effect on/off (client-only).", null));
+ commands.Add(new Command("lighting|lights", "Toggle lighting on/off (client-only).", null));
+ commands.Add(new Command("debugdraw", "Toggle the debug drawing mode on/off (client-only).", null));
+ commands.Add(new Command("togglehud|hud", "Toggle the character HUD (inventories, icons, buttons, etc) on/off (client-only).", null));
+ commands.Add(new Command("followsub", "Toggle whether the camera should follow the nearest submarine (client-only).", null));
+ commands.Add(new Command("toggleaitargets|aitargets", "Toggle the visibility of AI targets (= targets that enemies can detect and attack/escape from) (client-only).", null));
+
#if DEBUG
commands.Add(new Command("eventdata", "", (string[] args) =>
{
diff --git a/Barotrauma/BarotraumaShared/Content/Items/Diving/divinggear.xml b/Barotrauma/BarotraumaShared/Content/Items/Diving/divinggear.xml
index 59d9ee794..5b08da1d5 100644
--- a/Barotrauma/BarotraumaShared/Content/Items/Diving/divinggear.xml
+++ b/Barotrauma/BarotraumaShared/Content/Items/Diving/divinggear.xml
@@ -17,9 +17,11 @@
+
+
diff --git a/Barotrauma/BarotraumaShared/Content/Items/OxygenGenerator/oxygengenerator.xml b/Barotrauma/BarotraumaShared/Content/Items/OxygenGenerator/oxygengenerator.xml
index bdf428f3f..4c23d5fe4 100644
--- a/Barotrauma/BarotraumaShared/Content/Items/OxygenGenerator/oxygengenerator.xml
+++ b/Barotrauma/BarotraumaShared/Content/Items/OxygenGenerator/oxygengenerator.xml
@@ -13,6 +13,7 @@
+
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs
index 78bfb3145..bc498df98 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs
@@ -52,7 +52,7 @@ namespace Barotrauma
public float Range { get; private set; }
[Serialize(0.0f, false)]
- public float DamageRange { get; private set; }
+ public float DamageRange { get; set; }
[Serialize(0.0f, false)]
public float Duration { get; private set; }
diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
index 3a9a9a714..ffb676918 100644
--- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
+++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
@@ -88,6 +88,7 @@ namespace Barotrauma
public void Execute(string[] args)
{
+ if (onExecute == null) return;
onExecute(args);
}
@@ -100,6 +101,7 @@ namespace Barotrauma
{
if (onClientRequestExecute == null)
{
+ if (onExecute == null) return;
onExecute(args);
}
else
@@ -247,11 +249,10 @@ namespace Barotrauma
};
}));
- commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory]: Spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted.",
+ 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\".",
(string[] args) =>
{
- string errorMsg;
- SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out errorMsg);
+ SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Character.Controlled, out string errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg))
{
ThrowError(errorMsg);
@@ -260,20 +261,18 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
- string errorMsg;
- SpawnItem(args, cursorWorldPos, out errorMsg);
+ SpawnItem(args, cursorWorldPos, client.Character, out string errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg))
{
- ThrowError(errorMsg);
+ GameMain.Server.SendConsoleMessage(errorMsg, client);
}
- },
+ },
() =>
{
List itemNames = new List();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{
- ItemPrefab itemPrefab = prefab as ItemPrefab;
- if (itemPrefab != null) itemNames.Add(itemPrefab.Name);
+ if (prefab is ItemPrefab itemPrefab) itemNames.Add(itemPrefab.Name);
}
return new string[][]
@@ -283,6 +282,7 @@ namespace Barotrauma
};
}));
+
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
{
HumanAIController.DisableCrewAI = true;
@@ -849,8 +849,7 @@ namespace Barotrauma
{
if (args.Length < 2) return;
- int id;
- int.TryParse(args[0], out id);
+ int.TryParse(args[0], out int id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
@@ -891,8 +890,7 @@ namespace Barotrauma
return;
}
- int id;
- int.TryParse(args[0], out id);
+ int.TryParse(args[0], out int id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
@@ -1019,11 +1017,10 @@ namespace Barotrauma
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) =>
{
- if (GameMain.Server == null || args.Length == 0) return;
+ if (GameMain.NetworkMember == null || args.Length == 0) return;
- int id = 0;
- int.TryParse(args[0], out id);
- var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
+ int.TryParse(args[0], out int id);
+ var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
ThrowError("Client id \"" + id + "\" not found.");
@@ -1032,7 +1029,7 @@ namespace Barotrauma
ShowQuestionPrompt("Reason for kicking \"" + client.Name + "\"?", (reason) =>
{
- GameMain.Server.KickPlayer(client.Name, reason);
+ GameMain.NetworkMember.KickPlayer(client.Name, reason);
});
}));
@@ -1073,17 +1070,16 @@ namespace Barotrauma
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) =>
{
- if (GameMain.Server == null || args.Length == 0) return;
+ if (GameMain.NetworkMember == null || args.Length == 0) return;
- int id = 0;
- int.TryParse(args[0], out id);
- var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
+ int.TryParse(args[0], out int id);
+ var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
ThrowError("Client id \"" + id + "\" not found.");
return;
}
-
+
ShowQuestionPrompt("Reason for banning \"" + client.Name + "\"?", (reason) =>
{
ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) =>
@@ -1091,8 +1087,7 @@ namespace Barotrauma
TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration))
{
- TimeSpan parsedBanDuration;
- if (!TryParseTimeSpan(duration, out parsedBanDuration))
+ if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
{
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return;
@@ -1100,7 +1095,7 @@ namespace Barotrauma
banDuration = parsedBanDuration;
}
- GameMain.Server.BanPlayer(client.Name, reason, false, banDuration);
+ GameMain.NetworkMember.BanPlayer(client.Name, reason, false, banDuration);
});
});
}));
@@ -1117,8 +1112,7 @@ namespace Barotrauma
TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration))
{
- TimeSpan parsedBanDuration;
- if (!TryParseTimeSpan(duration, out parsedBanDuration))
+ if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
{
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return;
@@ -1139,23 +1133,72 @@ namespace Barotrauma
}
}
});
+ });
+ },
+ (string[] args) =>
+ {
+#if CLIENT
+ if (GameMain.Client == null || args.Length == 0) return;
+ ShowQuestionPrompt("Reason for banning the ip \"" + args[0] + "\"?", (reason) =>
+ {
+ ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) =>
+ {
+ TimeSpan? banDuration = null;
+ if (!string.IsNullOrWhiteSpace(duration))
+ {
+ if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
+ {
+ ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
+ return;
+ }
+ banDuration = parsedBanDuration;
+ }
+
+ GameMain.Client.SendConsoleCommand(
+ "banip " +
+ args[0] + " " +
+ (banDuration.HasValue ? banDuration.Value.TotalSeconds.ToString() : "0") + " " +
+ reason);
+ });
});
-
+#endif
+ },
+ (Client client, Vector2 cursorPos, string[] args) =>
+ {
+ if (args.Length < 1) return;
+ var clients = GameMain.Server.ConnectedClients.FindAll(c => c.Connection.RemoteEndPoint.Address.ToString() == args[0]);
+ TimeSpan? duration = null;
+ if (args.Length > 1)
+ {
+ if (double.TryParse(args[1], out double durationSeconds))
+ {
+ if (durationSeconds > 0) duration = TimeSpan.FromSeconds(durationSeconds);
+ }
+ else
+ {
+ GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid ban duration.", client);
+ return;
+ }
+ }
+ string reason = "";
+ if (args.Length > 2) reason = string.Join(" ", args.Skip(2));
+
+ if (clients.Count == 0)
+ {
+ GameMain.Server.BanList.BanPlayer("Unnamed", args[0], reason, duration);
+ }
+ else
+ {
+ foreach (Client cl in clients)
+ {
+ GameMain.Server.BanClient(cl, reason, false, duration);
+ }
+ }
}));
commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) =>
{
- Character tpCharacter = null;
-
- if (args.Length == 0)
- {
- tpCharacter = Character.Controlled;
- }
- else
- {
- tpCharacter = FindMatchingCharacter(args, false);
- }
-
+ Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false);
if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam;
@@ -1167,17 +1210,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
- Character tpCharacter = null;
-
- if (args.Length == 0)
- {
- tpCharacter = client.Character;
- }
- else
- {
- tpCharacter = FindMatchingCharacter(args, false);
- }
-
+ Character tpCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam;
@@ -1234,18 +1267,29 @@ namespace Barotrauma
}
}));
+ commands.Add(new Command("findentityids", "findentityids [entityname]", (string[] args) =>
+ {
+ if (args.Length == 0) return;
+ args[0] = args[0].ToLowerInvariant();
+ foreach (MapEntity mapEntity in MapEntity.mapEntityList)
+ {
+ if (mapEntity.Name.ToLowerInvariant() == args[0])
+ {
+ ThrowError(mapEntity.ID + ": " + mapEntity.Name.ToString());
+ }
+ }
+ foreach (Character character in Character.CharacterList)
+ {
+ if (character.Name.ToLowerInvariant() == args[0] || character.SpeciesName.ToLowerInvariant() == args[0])
+ {
+ ThrowError(character.ID + ": " + character.Name.ToString());
+ }
+ }
+ }));
+
commands.Add(new Command("heal", "heal [character name]: Restore the specified character to full health. If the name parameter is omitted, the controlled character will be healed.", (string[] args) =>
{
- Character healedCharacter = null;
- if (args.Length == 0)
- {
- healedCharacter = Character.Controlled;
- }
- else
- {
- healedCharacter = FindMatchingCharacter(args);
- }
-
+ Character healedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (healedCharacter != null)
{
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null);
@@ -1257,16 +1301,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
- Character healedCharacter = null;
- if (args.Length == 0)
- {
- healedCharacter = client.Character;
- }
- else
- {
- healedCharacter = FindMatchingCharacter(args);
- }
-
+ Character healedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (healedCharacter != null)
{
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null);
@@ -1285,16 +1320,7 @@ namespace Barotrauma
commands.Add(new Command("revive", "revive [character name]: Bring the specified character back from the dead. If the name parameter is omitted, the controlled character will be revived.", (string[] args) =>
{
- Character revivedCharacter = null;
- if (args.Length == 0)
- {
- revivedCharacter = Character.Controlled;
- }
- else
- {
- revivedCharacter = FindMatchingCharacter(args);
- }
-
+ Character revivedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
@@ -1313,16 +1339,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
- Character revivedCharacter = null;
- if (args.Length == 0)
- {
- revivedCharacter = client.Character;
- }
- else
- {
- revivedCharacter = FindMatchingCharacter(args);
- }
-
+ Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
@@ -1358,16 +1375,16 @@ namespace Barotrauma
commands.Add(new Command("ragdoll", "ragdoll [character name]: Force-ragdoll the specified character. If the name parameter is omitted, the controlled character will be ragdolled.", (string[] args) =>
{
- Character ragdolledCharacter = null;
- if (args.Length == 0)
+ Character ragdolledCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
+ if (ragdolledCharacter != null)
{
- ragdolledCharacter = Character.Controlled;
+ ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
}
- else
- {
- ragdolledCharacter = FindMatchingCharacter(args);
- }
-
+ },
+ null,
+ (Client client, Vector2 cursorWorldPos, string[] args) =>
+ {
+ Character ragdolledCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (ragdolledCharacter != null)
{
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
@@ -1477,20 +1494,14 @@ namespace Barotrauma
commands.Add(new Command("kill", "kill [character]: Immediately kills the specified character.", (string[] args) =>
{
- Character killedCharacter = null;
- if (args.Length == 0)
- {
- killedCharacter = Character.Controlled;
- }
- else
- {
- killedCharacter = FindMatchingCharacter(args);
- }
-
- if (killedCharacter != null)
- {
- killedCharacter.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
- }
+ Character killedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
+ killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
+ },
+ null,
+ (Client client, Vector2 cursorWorldPos, string[] args) =>
+ {
+ Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
+ killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
}));
commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) =>
@@ -2120,7 +2131,7 @@ namespace Barotrauma
}
}
- private static void SpawnItem(string[] args, Vector2 cursorPos, out string errorMsg)
+ private static void SpawnItem(string[] args, Vector2 cursorPos, Character controlledCharacter, out string errorMsg)
{
errorMsg = "";
if (args.Length < 1) return;
@@ -2128,23 +2139,48 @@ namespace Barotrauma
Vector2? spawnPos = null;
Inventory spawnInventory = null;
- int extraParams = 0;
- switch (args.Last())
+ if (args.Length > 1)
{
- case "cursor":
- extraParams = 1;
- spawnPos = cursorPos;
- break;
- case "inventory":
- extraParams = 1;
- spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory;
- break;
- default:
- extraParams = 0;
- break;
+ 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;
+ }
}
- string itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant();
+ string itemName = args[0];
var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null)
@@ -2162,7 +2198,6 @@ namespace Barotrauma
if (spawnPos != null)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
-
}
else if (spawnInventory != null)
{
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs
index ffcbcf961..54155e44a 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs
@@ -55,13 +55,7 @@ namespace Barotrauma
public override void Start()
{
- base.Start();
-
- if (GameMain.Server != null)
- {
- CargoManager.CreateItems();
- }
-
+ base.Start();
lastUpdateID++;
}
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
index 0e87247bc..b071b3a27 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
@@ -108,10 +108,9 @@ namespace Barotrauma
codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
- while (traitorCount-- >= 0)
+ while (traitorCount-- > 0)
{
- if (traitorCandidates.Count <= 0)
- break;
+ if (traitorCandidates.Count <= 0) break;
int traitorIndex = Rand.Int(traitorCandidates.Count);
Character traitorCharacter = traitorCandidates[traitorIndex];
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs
index 855db7f5f..ef9c4f3fb 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs
@@ -226,7 +226,14 @@ namespace Barotrauma
EventManager.StartRound(level);
- if (GameMode != null) GameMode.MsgBox();
+ if (GameMode != null)
+ {
+ GameMode.MsgBox();
+ if (GameMode is MultiPlayerCampaign campaign && GameMain.Server != null)
+ {
+ campaign.CargoManager.CreateItems();
+ }
+ }
if (GameSettings.SendUserStatistics)
{
@@ -234,7 +241,7 @@ namespace Barotrauma
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
-
+
#if CLIENT
roundSummary = new RoundSummary(this);
diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs
index 718e320bf..a476f143f 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs
@@ -25,7 +25,7 @@ namespace Barotrauma
public bool EnableSplashScreen { get; set; }
//public bool FullScreenEnabled { get; set; }
-
+
private KeyOrMouse[] keyMapping;
private WindowMode windowMode;
@@ -51,8 +51,37 @@ namespace Barotrauma
}
}
- private bool unsavedSettings;
+ private int characterHeadIndex;
+ public int CharacterHeadIndex
+ {
+ get { return characterHeadIndex; }
+ set
+ {
+ if (value == characterHeadIndex) return;
+ // Begin saving coroutine. Remove any existing save coroutines if one is running.
+ if (CoroutineManager.IsCoroutineRunning("saveCoroutine")) { CoroutineManager.StopCoroutines("saveCoroutine"); }
+ CoroutineManager.StartCoroutine(ApplyUnsavedChanges(), "saveCoroutine");
+ characterHeadIndex = value;
+ }
+ }
+
+ private Gender characterGender;
+ public Gender CharacterGender
+ {
+ get { return characterGender; }
+ set
+ {
+ if (value == characterGender) return;
+ // Begin saving coroutine. Remove any existing save coroutines if one is running.
+ if (CoroutineManager.IsCoroutineRunning("saveCoroutine")) { CoroutineManager.StopCoroutines("saveCoroutine"); }
+ CoroutineManager.StartCoroutine(ApplyUnsavedChanges(), "saveCoroutine");
+
+ characterGender = value;
+ }
+ }
+
+ private bool unsavedSettings;
public bool UnsavedSettings
{
get
@@ -230,18 +259,15 @@ namespace Barotrauma
case "keymapping":
foreach (XAttribute attribute in subElement.Attributes())
{
- InputType inputType;
- if (Enum.TryParse(attribute.Name.ToString(), true, out inputType))
+ if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType))
{
- int mouseButton;
- if (int.TryParse(attribute.Value.ToString(), out mouseButton))
+ if (int.TryParse(attribute.Value.ToString(), out int mouseButton))
{
keyMapping[(int)inputType] = new KeyOrMouse(mouseButton);
}
else
{
- Keys key;
- if (Enum.TryParse(attribute.Value.ToString(), true, out key))
+ if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
{
keyMapping[(int)inputType] = new KeyOrMouse(key);
}
@@ -258,6 +284,9 @@ namespace Barotrauma
break;
case "player":
defaultPlayerName = subElement.GetAttributeString("name", "");
+ characterHeadIndex = subElement.GetAttributeInt("headindex", Rand.Int(10));
+ characterGender = subElement.GetAttributeString("gender", Rand.Range(0.0f, 1.0f) < 0.5f ? "male" : "female")
+ .ToLowerInvariant() == "male" ? Gender.Male : Gender.Female;
break;
}
}
@@ -363,8 +392,10 @@ namespace Barotrauma
gameplay.Add(jobPreferences);
doc.Root.Add(gameplay);
- var playerElement = new XElement("player");
- playerElement.Add(new XAttribute("name", defaultPlayerName ?? ""));
+ var playerElement = new XElement("player",
+ new XAttribute("name", defaultPlayerName ?? ""),
+ new XAttribute("headindex", characterHeadIndex),
+ new XAttribute("gender", characterGender));
doc.Root.Add(playerElement);
doc.Save(filePath);
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs
index 3d6e36af4..5ee79b5d7 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs
@@ -93,6 +93,26 @@ namespace Barotrauma.Items.Components
}
}
+ public override void OnItemLoaded()
+ {
+ if (attack != null && attack.DamageRange <= 0.0f && item.body != null)
+ {
+ switch (item.body.BodyShape)
+ {
+ case PhysicsBody.Shape.Circle:
+ attack.DamageRange = item.body.radius;
+ break;
+ case PhysicsBody.Shape.Capsule:
+ attack.DamageRange = item.body.height / 2 + item.body.radius;
+ break;
+ case PhysicsBody.Shape.Rectangle:
+ attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
+ break;
+ }
+ attack.DamageRange = ConvertUnits.ToDisplayUnits(attack.DamageRange);
+ }
+ }
+
public override bool Use(float deltaTime, Character character = null)
{
if (character != null && !characterUsable) return false;
@@ -196,7 +216,6 @@ namespace Barotrauma.Items.Components
if (OnProjectileCollision(fixture, normal))
{
hitSomething = true;
- //Character.Controlled.AnimController.Teleport(point - Character.Controlled.SimPosition, Vector2.Zero);
break;
}
}
@@ -281,8 +300,7 @@ namespace Barotrauma.Items.Components
Character character = null;
if (attack != null)
{
- var submarine = target.Body.UserData as Submarine;
- if (submarine != null)
+ if (target.Body.UserData is Submarine submarine)
{
item.Move(-submarine.Position);
item.Submarine = submarine;
@@ -290,9 +308,8 @@ namespace Barotrauma.Items.Components
return true;
}
- Limb limb = target.Body.UserData as Limb;
Structure structure;
- if (limb != null)
+ if (target.Body.UserData is Limb limb)
{
attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f);
if (limb.character != null)
@@ -350,13 +367,12 @@ namespace Barotrauma.Items.Components
{
contained.SetTransform(item.SimPosition, contained.body.Rotation);
}
- //contained.Condition = 0.0f; //Let the freaking .xml handle it jeez
}
}
if (RemoveOnHit)
{
- Item.Spawner.AddToRemoveQueue(item);
+ Entity.Spawner.AddToRemoveQueue(item);
}
return true;
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs
index 3248be891..32da8efeb 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs
@@ -315,9 +315,9 @@ namespace Barotrauma
public override string ToString()
{
#if CLIENT
- return (GameMain.DebugDraw) ? Name + "(ID: " + ID + ")" : Name;
+ return (GameMain.DebugDraw) ? Name + " (ID: " + ID + ")" : Name;
#elif SERVER
- return Name + "(ID: " + ID + ")";
+ return Name + " (ID: " + ID + ")";
#endif
}
diff --git a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs
index 02bd404de..a11ce2b95 100644
--- a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs
+++ b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs
@@ -80,7 +80,7 @@ namespace Barotrauma
if (distSqr > displayRangeSqr) continue;
//ignore reactors (don't want to blow them up)
- if (item.GetComponent() == null) continue;
+ if (item.GetComponent() != null) continue;
float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;
diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs
index 1b5a52e8a..7348e0aa5 100644
--- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs
+++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs
@@ -664,19 +664,20 @@ namespace Barotrauma
float damageAmount = 0.0f;
for (int i = 0; i < SectionCount; i++)
{
- if (Vector2.DistanceSquared(SectionPosition(i, true), worldPosition) <= attack.DamageRange * attack.DamageRange)
+ Rectangle sectionRect = sections[i].rect;
+ sectionRect.Y -= sections[i].rect.Height;
+ if (MathUtils.CircleIntersectsRectangle(transformedPos, attack.DamageRange, sectionRect))
{
damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker);
-
#if CLIENT
- GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
+ GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
#endif
}
}
#if CLIENT
- if (playSound)// && !SectionBodyDisabled(i))
+ if (playSound)
{
string damageSoundType = (attack.DamageType == DamageType.Blunt) ? "StructureBlunt" : "StructureSlash";
SoundPlayer.PlayDamageSound(damageSoundType, damageAmount, worldPosition, tags: Tags);
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Client.cs b/Barotrauma/BarotraumaShared/Source/Networking/Client.cs
index fff97a991..80965f69a 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/Client.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/Client.cs
@@ -29,10 +29,20 @@ namespace Barotrauma.Networking
public byte TeamID = 0;
- public Character Character;
+ private Character character;
+ public Character Character
+ {
+ get { return character; }
+ set
+ {
+ character = value;
+ if (character != null) HasSpawned = true;
+ }
+ }
public CharacterInfo CharacterInfo;
public NetConnection Connection { get; set; }
- public bool InGame;
+ public bool InGame;
+ public bool HasSpawned; //has the client spawned as a character during the current round
public UInt16 LastRecvGeneralUpdate = 0;
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
index 81d633979..6dbd84ce9 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
@@ -780,11 +780,21 @@ namespace Barotrauma.Networking
case ClientPermissions.Ban:
string bannedName = inc.ReadString().ToLowerInvariant();
string banReason = inc.ReadString();
+ bool range = inc.ReadBoolean();
+ double durationSeconds = inc.ReadDouble();
+
var bannedClient = connectedClients.Find(cl => cl != sender && cl.Name.ToLowerInvariant() == bannedName);
if (bannedClient != null)
{
Log("Client \"" + sender.Name + "\" banned \"" + bannedClient.Name + "\".", ServerLog.MessageType.ServerMessage);
- BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, false);
+ if (durationSeconds > 0)
+ {
+ BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range, TimeSpan.FromSeconds(durationSeconds));
+ }
+ else
+ {
+ BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range);
+ }
}
break;
case ClientPermissions.EndRound:
@@ -1343,11 +1353,12 @@ namespace Barotrauma.Networking
List characters = new List();
foreach (Client client in ConnectedClients)
{
- if (client.Character != null)
- characters.Add(client.Character);
+ if (client.Character != null) characters.Add(client.Character);
}
- var max = (int)Math.Round(characters.Count * 0.2f, 1);
- var traitorCount = Math.Max(1, TraitorsEnabled == YesNoMaybe.Maybe ? Rand.Int(max) + 1 : max);
+ if (Character != null) characters.Add(Character);
+
+ int max = Math.Max(TraitorUseRatio ? (int)Math.Round(characters.Count * TraitorRatio, 1) : 1, 1);
+ int traitorCount = Rand.Int(max + 1);
TraitorManager = new TraitorManager(this, traitorCount);
if (TraitorManager.TraitorList.Count > 0)
@@ -1501,6 +1512,7 @@ namespace Barotrauma.Networking
foreach (Client client in connectedClients)
{
client.Character = null;
+ client.HasSpawned = false;
client.InGame = false;
}
}
@@ -1620,6 +1632,7 @@ namespace Barotrauma.Networking
}
client.Character = null;
+ client.HasSpawned = false;
client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) msg = client.Name + " has left the server";
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs b/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs
index 28d430888..c78a1bede 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs
@@ -111,11 +111,11 @@ namespace Barotrauma
#endif
break;
case VoteType.EndRound:
- if (sender.Character == null) return;
+ if (!sender.HasSpawned) return;
sender.SetVote(voteType, inc.ReadBoolean());
- GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.Character != null && c.GetVote(VoteType.EndRound));
- GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.Character != null);
+ GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote(VoteType.EndRound));
+ GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
break;
case VoteType.Kick:
diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt
index f1fc4f36e..3c33067bd 100644
--- a/Barotrauma/BarotraumaShared/changelog.txt
+++ b/Barotrauma/BarotraumaShared/changelog.txt
@@ -1,3 +1,34 @@
+---------------------------------------------------------------------------------------------------------
+v0.8.1.5
+---------------------------------------------------------------------------------------------------------
+
+- Added the option to automatically send crash reports and anonymous usage statistics to the developers.
+The usage statistics include information such as the selected game mode, selected submarine, causes of
+death and mission outcomes. When the game is started for the first time, a message box prompts you to
+select whether you want to send the information or not.
+- Fixed a bug that caused clients to get desynced when purchasing items in the multiplayer campaign.
+- Added a signal component that adds the received signals together.
+- Devices outside the submarine can be rewired in-game (not just in the sub editor).
+- Fixed a crash caused by vision obstruction logic.
+- Fixed clients being unable to give non-permanent or range bans.
+- Clients are allowed to vote to end the round if they have spawned at some point during the round,
+even if the character they controlled doesn't exist anymore.
+- Dedicated servers can give clients the permission to use console commands that aren't available in
+for dedicated server (e.g. los, lights, control)
+- Banip, banid & kickid commands can be used by clients now (if they have the permission to do so).
+- Spawnitem [item] inventory, ragdoll and kill commands target the character of the client using
+the command instead of the host's character.
+- Spawnitem can be used to spawn items in the inventory of a specific character.
+- Fixed explosions with an EMP value only damaging reactors (when they should only ignore reactors).
+- Fire can only explode oxygen tanks that are >25% full (otherwise the condition of the tank just drops
+to 0). Prevents infinite explosions when an oxygen generator is on fire with oxygen tanks inside.
+- Fixed projectiles with a damage range of 0 not applying their structuredamage value to structures.
+- Items with a physics body can be used as pumps, so now it's possible to make portable items that remove
+water from inside the sub.
+- Fixed traitor ratio setting being ignored and the minimum number of traitors being 2.
+- Fixed crashes caused by custom characters with no AI configuration.
+- Character head and gender settings are saved.
+
---------------------------------------------------------------------------------------------------------
v0.8.1.4
---------------------------------------------------------------------------------------------------------