diff --git a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
index b8d454aa4..c3fbd723d 100644
--- a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
+++ b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
@@ -29,6 +29,8 @@
v4.5
0.7.0.1
+
+
..\BarotraumaShared\Icon.ico
@@ -231,9 +233,15 @@
+
+ ..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll
+
..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.6.0.1625\lib\net40\MonoGame.Framework.dll
+
+ ..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll
+
False
@@ -247,9 +255,12 @@
..\..\Libraries\NuGet\OpenTK.2.0.0\lib\net20\OpenTK.dll
+
+ ..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\System.Data.SQLite.dll
+
-
+
..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll
@@ -324,6 +335,13 @@
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
@@ -35,7 +35,7 @@
-
+
@@ -48,7 +48,7 @@
-
+
@@ -98,7 +98,7 @@
-
+
@@ -164,7 +164,7 @@
-
+
@@ -330,7 +330,7 @@
-
+
diff --git a/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgun.xml b/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgun.xml
index c1db65de7..11f441901 100644
--- a/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgun.xml
+++ b/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgun.xml
@@ -35,8 +35,7 @@
category="Machine"
type="Controller"
linkable="true"
- disableitemusagewhenselected="true"
- >
+ disableitemusagewhenselected="true">
@@ -55,11 +54,34 @@
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ linkable="true">
@@ -69,6 +91,48 @@
+
+ -
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
-
+
@@ -94,7 +158,9 @@
-
+
+
+
diff --git a/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgunetc2.png b/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgunetc2.png
new file mode 100644
index 000000000..1c0d61764
Binary files /dev/null and b/Barotrauma/BarotraumaShared/Content/Items/Weapons/railgunetc2.png differ
diff --git a/Barotrauma/BarotraumaShared/Content/Items/Weapons/weapons.xml b/Barotrauma/BarotraumaShared/Content/Items/Weapons/weapons.xml
index ec7ea9d76..d9d44e6c9 100644
--- a/Barotrauma/BarotraumaShared/Content/Items/Weapons/weapons.xml
+++ b/Barotrauma/BarotraumaShared/Content/Items/Weapons/weapons.xml
@@ -43,8 +43,8 @@
holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/>
-
-
+
+
@@ -78,7 +78,7 @@
aimpos="90,10" handle1="-10,-7"/>
-
+
@@ -128,7 +128,7 @@
aimpos="90,10" handle1="-11,-7"/>
-
+
@@ -300,7 +300,7 @@
-
+
diff --git a/Barotrauma/BarotraumaShared/Content/Jobs.xml b/Barotrauma/BarotraumaShared/Content/Jobs.xml
index 315a1df39..d5e86f074 100644
--- a/Barotrauma/BarotraumaShared/Content/Jobs.xml
+++ b/Barotrauma/BarotraumaShared/Content/Jobs.xml
@@ -103,10 +103,10 @@
-
-
-
-
+
+
+
+
@@ -117,4 +117,4 @@
-
\ No newline at end of file
+
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
index 0cbe6be2b..495add3f1 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
@@ -78,6 +78,10 @@ namespace Barotrauma
public EnemyAIController(Character c, string file) : base(c)
{
targetMemories = new Dictionary();
+ outsideSteering = new SteeringManager(this);
+ insideSteering = new IndoorsSteeringManager(this, false);
+ steeringManager = outsideSteering;
+ state = AIState.None;
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
@@ -103,13 +107,6 @@ namespace Barotrauma
fleeHealthThreshold = aiElement.GetAttributeFloat("fleehealththreshold", 0.0f);
attachToWalls = aiElement.GetAttributeBool("attachtowalls", false);
-
- outsideSteering = new SteeringManager(this);
- insideSteering = new IndoorsSteeringManager(this, false);
-
- steeringManager = outsideSteering;
-
- state = AIState.None;
}
public override void SelectTarget(AITarget target)
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/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
index 8d681550d..dbf3e7e0c 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs
@@ -1948,8 +1948,21 @@ namespace Barotrauma
AnimController.Frozen = false;
- GameServer.Log(LogName+" has died (Cause of death: "+causeOfDeath+")", ServerLog.MessageType.Attack);
-
+ GameServer.Log(LogName + " has died (Cause of death: " + causeOfDeath + ")", ServerLog.MessageType.Attack);
+ if (GameSettings.SendUserStatistics)
+ {
+ string characterType = "Unknown";
+ if (this == controlled)
+ characterType = "Player";
+ else if (IsRemotePlayer)
+ characterType = "RemotePlayer";
+ else if (AIController is EnemyAIController)
+ characterType = "Enemy";
+ else if (AIController is HumanAIController)
+ characterType = "AICrew";
+ GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath);
+ }
+
if (OnDeath != null) OnDeath(this, causeOfDeath);
KillProjSpecific();
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
index 3e0ef35fc..4507569c1 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
@@ -51,12 +51,6 @@ namespace Barotrauma
//the physics body of the limb
public PhysicsBody body;
- private readonly int refJointIndex;
-
- private readonly float steerForce;
-
- private readonly bool doesFlip;
-
protected readonly Vector2 stepOffset;
public Sprite sprite, damagedSprite;
@@ -69,8 +63,6 @@ namespace Barotrauma
public readonly bool ignoreCollisions;
- private float damage, burnt;
-
private bool isSevered;
private float severedFadeOutTimer;
@@ -81,16 +73,9 @@ namespace Barotrauma
public const float SoundInterval = 0.4f;
public readonly Attack attack;
+ private List damageModifiers;
private Direction dir;
-
- private List wearingItems;
-
- private Vector2 animTargetPos;
-
- private float scale;
-
- private List damageModifiers;
public float AttackTimer;
@@ -100,17 +85,13 @@ namespace Barotrauma
set
{
isSevered = value;
- if (isSevered)
- {
- damage = 100.0f;
- }
+#if CLIENT
+ if (isSevered) damage = 100.0f;
+#endif
}
}
- public bool DoesFlip
- {
- get { return doesFlip; }
- }
+ public bool DoesFlip { get; private set; }
public Vector2 WorldPosition
{
@@ -132,21 +113,12 @@ namespace Barotrauma
get { return body.Rotation; }
}
- public float Scale
- {
- get { return scale; }
- }
+ public float Scale { get; private set; }
//where an animcontroller is trying to pull the limb, only used for debug visualization
- public Vector2 AnimTargetPos
- {
- get { return animTargetPos; }
- }
+ public Vector2 AnimTargetPos { get; private set; }
- public float SteerForce
- {
- get { return steerForce; }
- }
+ public float SteerForce { get; private set; }
public float Mass
{
@@ -166,38 +138,25 @@ namespace Barotrauma
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
}
- public int RefJointIndex
- {
- get { return refJointIndex; }
- }
+ public int RefJointIndex { get; private set; }
public Vector2 StepOffset
{
get { return stepOffset; }
}
- public float Burnt
- {
- get { return burnt; }
- protected set { burnt = MathHelper.Clamp(value, 0.0f, 100.0f); }
- }
-
- public List WearingItems
- {
- get { return wearingItems; }
- }
-
+ public List WearingItems { get; private set; }
+
public Limb (Character character, XElement element, float scale = 1.0f)
{
this.character = character;
- wearingItems = new List();
+ WearingItems = new List();
dir = Direction.Right;
+ DoesFlip = element.GetAttributeBool("flip", false);
- doesFlip = element.GetAttributeBool("flip", false);
-
- this.scale = scale;
+ Scale = scale;
body = new PhysicsBody(element, scale);
@@ -217,7 +176,7 @@ namespace Barotrauma
body.UserData = this;
- refJointIndex = -1;
+ RefJointIndex = -1;
Vector2 pullJointPos = Vector2.Zero;
@@ -240,7 +199,7 @@ namespace Barotrauma
stepOffset = element.GetAttributeVector2("stepoffset", Vector2.Zero) * scale;
stepOffset = ConvertUnits.ToSimUnits(stepOffset);
- refJointIndex = element.GetAttributeInt("refjoint", -1);
+ RefJointIndex = element.GetAttributeInt("refjoint", -1);
}
else
@@ -254,7 +213,7 @@ namespace Barotrauma
GameMain.World.AddJoint(pullJoint);
- steerForce = element.GetAttributeFloat("steerforce", 0.0f);
+ SteerForce = element.GetAttributeFloat("steerforce", 0.0f);
if (element.Attribute("mouthpos") != null)
{
@@ -329,12 +288,12 @@ namespace Barotrauma
public void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false)
{
Vector2 pullPos = body.SimPosition;
- if (pullJoint!=null && !pullFromCenter)
+ if (pullJoint != null && !pullFromCenter)
{
pullPos = pullJoint.WorldAnchorA;
}
- animTargetPos = pos;
+ AnimTargetPos = pos;
body.MoveToPos(pos, force, pullPos);
}
@@ -352,7 +311,7 @@ namespace Barotrauma
}
}
- foreach (WearableSprite wearable in wearingItems)
+ foreach (WearableSprite wearable in WearingItems)
{
foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
{
@@ -370,54 +329,13 @@ namespace Barotrauma
bleedingAmount *= damageModifier.BleedingMultiplier;
}
-#if CLIENT
- if (playSound)
- {
- string damageSoundType = (damageType == DamageType.Blunt) ? "LimbBlunt" : "LimbSlash";
-
- foreach (DamageModifier damageModifier in appliedDamageModifiers)
- {
- if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound))
- {
- damageSoundType = damageModifier.DamageSound;
- break;
- }
- }
-
- SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
- }
-
- if (character.UseBloodParticles)
- {
- float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
- float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
-
- for (int i = 0; i < bloodParticleAmount; i++)
- {
- var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
- if (blood != null)
- {
- blood.Size *= bloodParticleSize;
- }
- }
-
- if (bloodParticleAmount > 0 && character.CurrentHull != null)
- {
- character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
- }
- }
-#endif
-
- if (damageType == DamageType.Burn)
- {
- Burnt += amount * 10.0f;
- }
-
- damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
+ AddDamageProjSpecific(position, damageType, amount, bleedingAmount, playSound, appliedDamageModifiers);
return new AttackResult(amount, bleedingAmount, appliedDamageModifiers);
}
+ partial void AddDamageProjSpecific(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound, List appliedDamageModifiers);
+
public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
{
if (armorSector == Vector2.Zero) return false;
@@ -435,10 +353,7 @@ namespace Barotrauma
public void Update(float deltaTime)
{
- UpdateProjSpecific();
-
- if (!character.IsDead) damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
- if (burnt > 0.0f) Burnt -= deltaTime;
+ UpdateProjSpecific(deltaTime);
if (LinearVelocity.X > 500.0f)
{
@@ -463,17 +378,11 @@ namespace Barotrauma
if (character.IsDead) return;
- damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
SoundTimer -= deltaTime;
}
- partial void UpdateProjSpecific();
-
- public void ActivateDamagedSprite()
- {
- damage = 100.0f;
- }
-
+ partial void UpdateProjSpecific(float deltaTime);
+
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
{
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
index f84fa55e1..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;
@@ -1216,7 +1249,7 @@ namespace Barotrauma
Submarine.LockX = !Submarine.LockX;
}, null, null));
- commands.Add(new Command("locky", "loxky: Lock vertical movement of the main submarine.", (string[] args) =>
+ commands.Add(new Command("locky", "locky: Lock vertical movement of the main submarine.", (string[] args) =>
{
Submarine.LockY = !Submarine.LockY;
}, null, null));
@@ -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/Events/Missions/CargoMission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/CargoMission.cs
index 7f6c8006c..6f2fd6199 100644
--- a/Barotrauma/BarotraumaShared/Source/Events/Missions/CargoMission.cs
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/CargoMission.cs
@@ -13,12 +13,12 @@ namespace Barotrauma
private int requiredDeliveryAmount;
- public CargoMission(XElement element, Location[] locations)
- : base(element, locations)
+ public CargoMission(MissionPrefab prefab, Location[] locations)
+ : base(prefab, locations)
{
- itemConfig = element.Element("Items");
+ itemConfig = prefab.ConfigElement.Element("Items");
- requiredDeliveryAmount = element.GetAttributeInt("requireddeliveryamount", 0);
+ requiredDeliveryAmount = prefab.ConfigElement.GetAttributeInt("requireddeliveryamount", 0);
}
private void InitItems()
diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/CombatMission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/CombatMission.cs
index 3ed0593b2..55133d72a 100644
--- a/Barotrauma/BarotraumaShared/Source/Events/Missions/CombatMission.cs
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/CombatMission.cs
@@ -29,6 +29,8 @@ namespace Barotrauma
{
get
{
+ if (descriptions == null) return "";
+
if (GameMain.NetworkMember==null || GameMain.NetworkMember.Character==null)
{
//non-team-specific description
@@ -47,20 +49,20 @@ namespace Barotrauma
{
if (winner == -1) return "";
- return successMessage
+ return base.SuccessMessage
.Replace("[loser]", teamNames[1 - winner])
.Replace("[winner]", teamNames[winner]);
}
}
- public CombatMission(XElement element, Location[] locations)
- : base(element, locations)
+ public CombatMission(MissionPrefab prefab, Location[] locations)
+ : base(prefab, locations)
{
descriptions = new string[]
{
- element.GetAttributeString("descriptionneutral", ""),
- element.GetAttributeString("description1", ""),
- element.GetAttributeString("description2", "")
+ prefab.ConfigElement.GetAttributeString("descriptionneutral", ""),
+ prefab.ConfigElement.GetAttributeString("description1", ""),
+ prefab.ConfigElement.GetAttributeString("description2", "")
};
for (int i = 0; i < descriptions.Length; i++)
@@ -73,8 +75,8 @@ namespace Barotrauma
teamNames = new string[]
{
- element.GetAttributeString("teamname1", "Team A"),
- element.GetAttributeString("teamname2", "Team B")
+ prefab.ConfigElement.GetAttributeString("teamname1", "Team A"),
+ prefab.ConfigElement.GetAttributeString("teamname2", "Team B")
};
}
diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs
index c8d8b2b23..ab981d6f3 100644
--- a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs
@@ -1,45 +1,18 @@
using Microsoft.Xna.Framework;
-using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
-using System.Xml.Linq;
namespace Barotrauma
{
partial class Mission
- {
- public static List MissionTypes = new List() { "Random" };
-
- private string name;
-
- private string description;
-
+ {
protected bool completed;
- protected string successMessage;
- protected string failureMessage;
+ private readonly MissionPrefab prefab;
- protected string radarLabel;
-
- protected List headers;
- protected List messages;
-
- private int reward;
-
public string Name
{
- get { return name; }
- }
-
- public virtual string Description
- {
- get { return description; }
- }
-
- public int Reward
- {
- get { return reward; }
+ get { return prefab.Name; }
}
public bool Completed
@@ -48,109 +21,101 @@ namespace Barotrauma
set { completed = value; }
}
+ public int Reward
+ {
+ get { return prefab.Reward; }
+ }
+
public virtual bool AllowRespawn
{
get { return true; }
}
- public virtual string RadarLabel
- {
- get { return radarLabel; }
- }
-
public virtual Vector2 RadarPosition
{
get { return Vector2.Zero; }
}
- virtual public string SuccessMessage
+ public string RadarLabel
{
- get { return successMessage; }
+ get { return prefab.RadarLabel; }
+ }
+
+ public List Headers
+ {
+ get; private set;
+ }
+
+ public List Messages
+ {
+ get; private set;
+ }
+
+ public virtual string SuccessMessage
+ {
+ get;
+ protected set;
}
public string FailureMessage
{
- get { return failureMessage; }
+ get;
+ protected set;
}
- public static void Init()
+ public virtual string Description
{
- var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
- foreach (string file in files)
- {
- XDocument doc = XMLExtensions.TryLoadXml(file);
- if (doc == null || doc.Root == null) continue;
-
- foreach (XElement element in doc.Root.Elements())
- {
- string missionTypeName = element.Name.ToString();
- missionTypeName = missionTypeName.Replace("Mission", "");
-
- if (!MissionTypes.Contains(missionTypeName)) MissionTypes.Add(missionTypeName);
- }
-
- }
+ get;
+ protected set;
}
- public Mission(XElement element, Location[] locations)
+ public MissionPrefab Prefab
{
- name = element.GetAttributeString("name", "");
+ get { return prefab; }
+ }
+
+ public Mission(MissionPrefab prefab, Location[] locations)
+ {
+ System.Diagnostics.Debug.Assert(locations.Length == 2);
- description = element.GetAttributeString("description", "");
+ this.prefab = prefab;
- reward = element.GetAttributeInt("reward", 1);
-
- successMessage = element.GetAttributeString("successmessage",
- "Mission completed successfully");
- failureMessage = element.GetAttributeString("failuremessage",
- "Mission failed");
-
- radarLabel = element.GetAttributeString("radarlabel", "");
-
- messages = new List();
- headers = new List();
- foreach (XElement subElement in element.Elements())
- {
- if (subElement.Name.ToString().ToLowerInvariant() != "message") continue;
- headers.Add(subElement.GetAttributeString("header", ""));
- messages.Add(subElement.GetAttributeString("text", ""));
- }
+ Description = prefab.Description;
+ SuccessMessage = prefab.SuccessMessage;
+ FailureMessage = prefab.FailureMessage;
+ Headers = new List(prefab.Headers);
+ Messages = new List(prefab.Messages);
for (int n = 0; n < 2; n++)
{
- description = description.Replace("[location" + (n + 1) + "]", locations[n].Name);
-
- successMessage = successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
- failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
-
- for (int m = 0; m < messages.Count; m++)
+ if (Description != null) Description = Description.Replace("[location" + (n + 1) + "]", locations[n].Name);
+ if (SuccessMessage != null) SuccessMessage = SuccessMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
+ if (FailureMessage != null) FailureMessage = FailureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
+ for (int m = 0; m < Messages.Count; m++)
{
- messages[m] = messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name);
+ Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name);
}
}
}
+ public static Mission LoadRandom(Location[] locations, string seed, string missionType = "", bool isSinglePlayer = false)
+ {
+ return LoadRandom(locations, new MTRandom(ToolBox.StringToInt(seed)), missionType, isSinglePlayer);
+ }
+
public static Mission LoadRandom(Location[] locations, MTRandom rand, string missionType = "", bool isSinglePlayer = false)
{
+ //todo: use something else than strings to define the mission type
missionType = missionType.ToLowerInvariant();
- var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
- string configFile = files[rand.Next(files.Count)];
-
- XDocument doc = XMLExtensions.TryLoadXml(configFile);
- if (doc == null) return null;
-
- int eventCount = doc.Root.Elements().Count();
- //int[] commonness = new int[eventCount];
- float[] eventProbability = new float[eventCount];
-
- float probabilitySum = 0.0f;
-
- List matchingElements = new List();
-
+ List allowedMissions = new List();
if (missionType == "random")
{
- matchingElements = doc.Root.Elements().ToList();
+ allowedMissions.AddRange(MissionPrefab.List);
+ if (GameMain.Server != null)
+ {
+ allowedMissions.RemoveAll(mission => !GameMain.Server.AllowedRandomMissionTypes.Any(a => mission.TypeMatches(a)));
+ }
}
else if (missionType == "none")
{
@@ -158,68 +123,31 @@ namespace Barotrauma
}
else if (string.IsNullOrWhiteSpace(missionType))
{
- matchingElements = doc.Root.Elements().ToList();
+ allowedMissions.AddRange(MissionPrefab.List);
}
else
{
- matchingElements = doc.Root.Elements().ToList().FindAll(m => m.Name.ToString().ToLowerInvariant().Replace("mission", "") == missionType);
+ allowedMissions = MissionPrefab.List.FindAll(m => m.TypeMatches(missionType));
}
if (isSinglePlayer)
{
- matchingElements.RemoveAll(m => m.GetAttributeBool("multiplayeronly", false));
+ allowedMissions.RemoveAll(m => m.MultiplayerOnly);
}
else
{
- matchingElements.RemoveAll(m => m.GetAttributeBool("singleplayeronly", false));
+ allowedMissions.RemoveAll(m => m.SingleplayerOnly);
}
- int i = 0;
- foreach (XElement element in matchingElements)
- {
- eventProbability[i] = element.GetAttributeInt("commonness", 1);
-
- probabilitySum += eventProbability[i];
-
- i++;
- }
-
+ float probabilitySum = allowedMissions.Sum(m => m.Commonness);
float randomNumber = (float)rand.NextDouble() * probabilitySum;
-
- i = 0;
- foreach (XElement element in matchingElements)
+ foreach (MissionPrefab missionPrefab in allowedMissions)
{
- if (randomNumber <= eventProbability[i])
+ if (randomNumber <= missionPrefab.Commonness)
{
- Type t;
- string type = element.Name.ToString();
-
- try
- {
- t = Type.GetType("Barotrauma." + type, true, true);
- if (t == null)
- {
- DebugConsole.ThrowError("Error in " + configFile + "! Could not find a mission class of the type \"" + type + "\".");
- continue;
- }
- }
- catch
- {
- DebugConsole.ThrowError("Error in " + configFile + "! Could not find a mission class of the type \"" + type + "\".");
- continue;
- }
-
- ConstructorInfo constructor = t.GetConstructor(new[] { typeof(XElement), typeof(Location[]) });
-
- object instance = constructor.Invoke(new object[] { element, locations });
-
- Mission mission = (Mission)instance;
-
- return mission;
+ return missionPrefab.Instantiate(locations);
}
-
- randomNumber -= eventProbability[i];
- i++;
+ randomNumber -= missionPrefab.Commonness;
}
return null;
@@ -251,7 +179,7 @@ namespace Barotrauma
var mode = GameMain.GameSession.GameMode as CampaignMode;
if (mode == null) return;
- mode.Money += reward;
+ mode.Money += Reward;
}
}
}
diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/MissionPrefab.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/MissionPrefab.cs
new file mode 100644
index 000000000..169105c9a
--- /dev/null
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/MissionPrefab.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Xml.Linq;
+
+namespace Barotrauma
+{
+ class MissionPrefab
+ {
+ public static List List = new List();
+ public static List MissionTypes = new List() { "Random" };
+
+ private string name;
+
+ public string Name
+ {
+ get { return name; }
+ }
+
+ private Type missionType;
+ private ConstructorInfo constructor;
+
+ public virtual string Description { get; private set; }
+
+ public bool MultiplayerOnly { get; private set; }
+ public bool SingleplayerOnly { get; private set; }
+
+ public float Commonness { get; private set; }
+
+ public int Reward { get; private set; }
+
+ public string RadarLabel { get; private set; }
+
+ public List Headers { get; private set; }
+ public List Messages { get; private set; }
+
+ public string SuccessMessage { get; private set; }
+ public string FailureMessage { get; private set; }
+
+ public XElement ConfigElement { get; private set; }
+
+ public static void Init()
+ {
+ var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
+ foreach (string file in files)
+ {
+ XDocument doc = XMLExtensions.TryLoadXml(file);
+ if (doc?.Root == null) continue;
+
+ foreach (XElement element in doc.Root.Elements())
+ {
+ string missionTypeName = element.Name.ToString();
+ missionTypeName = missionTypeName.Replace("Mission", "");
+
+ List.Add(new MissionPrefab(element));
+ if (!MissionTypes.Contains(missionTypeName)) MissionTypes.Add(missionTypeName);
+ }
+
+ }
+ }
+
+ public MissionPrefab(XElement element)
+ {
+ ConfigElement = element;
+
+ name = element.GetAttributeString("name", "");
+ Description = element.GetAttributeString("description", "");
+ Commonness = element.GetAttributeFloat("commonness", 1.0f);
+ SingleplayerOnly = element.GetAttributeBool("singleplayeronly", false);
+ MultiplayerOnly = element.GetAttributeBool("multiplayeronly", false);
+
+ Reward = element.GetAttributeInt("reward", 1);
+
+ SuccessMessage = element.GetAttributeString("successmessage", "Mission completed successfully");
+ FailureMessage = element.GetAttributeString("failuremessage", "Mission failed");
+ RadarLabel = element.GetAttributeString("radarlabel", "");
+
+ Messages = new List();
+ Headers = new List();
+ foreach (XElement subElement in element.Elements())
+ {
+ if (subElement.Name.ToString().ToLowerInvariant() != "message") continue;
+ Headers.Add(subElement.GetAttributeString("header", ""));
+ Messages.Add(subElement.GetAttributeString("text", ""));
+ }
+
+ string type = element.Name.ToString();
+
+ try
+ {
+ missionType = Type.GetType("Barotrauma." + type, true, true);
+ if (missionType == null)
+ {
+ DebugConsole.ThrowError("Error in mission prefab " + Name + "! Could not find a mission class of the type \"" + type + "\".");
+ return;
+ }
+ }
+ catch
+ {
+ DebugConsole.ThrowError("Error in mission prefab " + Name + "! Could not find a mission class of the type \"" + type + "\".");
+ return;
+ }
+ constructor = missionType.GetConstructor(new[] { typeof(MissionPrefab), typeof(Location[]) });
+ }
+
+ public Mission Instantiate(Location[] locations)
+ {
+ return constructor?.Invoke(new object[] { this, locations }) as Mission;
+ }
+
+ public bool TypeMatches(string typeName)
+ {
+ //TODO: use enums instead of strings?
+ typeName = typeName.ToLowerInvariant();
+ return missionType.Name.ToString().Replace("Mission", "").ToLowerInvariant() == typeName;
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs
index bb2b00e00..71259e252 100644
--- a/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs
@@ -18,10 +18,10 @@ namespace Barotrauma
get { return monster != null && !monster.IsDead ? radarPosition : Vector2.Zero; }
}
- public MonsterMission(XElement element, Location[] locations)
- : base(element, locations)
+ public MonsterMission(MissionPrefab prefab, Location[] locations)
+ : base(prefab, locations)
{
- monsterFile = element.GetAttributeString("monsterfile", "");
+ monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
}
diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/SalvageMission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/SalvageMission.cs
index 5ae148b83..b9fc7bb10 100644
--- a/Barotrauma/BarotraumaShared/Source/Events/Missions/SalvageMission.cs
+++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/SalvageMission.cs
@@ -1,7 +1,6 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
-using System.Xml.Linq;
namespace Barotrauma
{
@@ -23,10 +22,10 @@ namespace Barotrauma
}
}
- public SalvageMission(XElement element, Location[] locations)
- : base(element, locations)
+ public SalvageMission(MissionPrefab prefab, Location[] locations)
+ : base(prefab, locations)
{
- string itemName = element.GetAttributeString("itemname", "");
+ string itemName = prefab.ConfigElement.GetAttributeString("itemname", "");
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null)
@@ -35,10 +34,10 @@ namespace Barotrauma
return;
}
- string spawnPositionTypeStr = element.GetAttributeString("spawntype", "");
+ string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) ||
- !Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType))
+ !Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType))
{
spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
}
diff --git a/Barotrauma/BarotraumaShared/Source/GameAnalyticsManager.cs b/Barotrauma/BarotraumaShared/Source/GameAnalyticsManager.cs
new file mode 100644
index 000000000..fa24b69e9
--- /dev/null
+++ b/Barotrauma/BarotraumaShared/Source/GameAnalyticsManager.cs
@@ -0,0 +1,25 @@
+using GameAnalyticsSDK.Net;
+using System;
+
+namespace Barotrauma
+{
+ public static class GameAnalyticsManager
+ {
+ public static void Init()
+ {
+#if DEBUB
+ GameAnalytics.SetEnabledInfoLog(true);
+#endif
+ GameAnalytics.ConfigureBuild(GameMain.Version.ToString());
+ GameAnalytics.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor");
+ GameAnalytics.Initialize("a3a073c20982de7c15d21e840e149122", "9010ad9a671233b8d9610d76cec8c897d9ff3ba7");
+
+ string contentPackageName = GameMain.Config?.SelectedContentPackage?.Name;
+ if (!string.IsNullOrEmpty(contentPackageName))
+ {
+ GameAnalytics.AddDesignEvent("ContentPackage:" +
+ contentPackageName.Replace(":", "").Substring(0, Math.Min(32, contentPackageName.Length)));
+ }
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MissionMode.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MissionMode.cs
index 77a49fbc3..57fae1264 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MissionMode.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MissionMode.cs
@@ -16,9 +16,22 @@
: base(preset, param)
{
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
-
- MTRandom rand = new MTRandom(ToolBox.StringToInt(GameMain.NetLobbyScreen.LevelSeed));
- mission = Mission.LoadRandom(locations, rand, param as string);
+ if (param is string)
+ {
+ mission = Mission.LoadRandom(locations, GameMain.NetLobbyScreen.LevelSeed, (string)param);
+ }
+ else if (param is MissionPrefab)
+ {
+ mission = ((MissionPrefab)param).Instantiate(locations);
+ }
+ else if (param is Mission)
+ {
+ mission = (Mission)param;
+ }
+ else
+ {
+ throw new System.ArgumentException("Unrecognized MissionMode parameter \"" + param + "\"");
+ }
}
}
}
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiplayerCampaign.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs
similarity index 98%
rename from Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiplayerCampaign.cs
rename to 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 d0ad84284..ef9c4f3fb 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs
@@ -91,27 +91,35 @@ namespace Barotrauma
set { savePath = value; }
}
- public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset = null, string missionType = "")
+
+ public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset, string missionType = "")
+ : this(submarine, savePath)
+ {
+ GameMode = gameModePreset.Instantiate(missionType);
+ }
+
+ public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset, MissionPrefab missionPrefab)
+ : this(submarine, savePath)
+ {
+ GameMode = gameModePreset.Instantiate(missionPrefab);
+ }
+
+ private GameSession(Submarine submarine, string savePath)
{
Submarine.MainSub = submarine;
-
+ this.submarine = submarine;
GameMain.GameSession = this;
-
EventManager = new EventManager(this);
-
this.savePath = savePath;
-
#if CLIENT
CrewManager = new CrewManager();
infoButton = new GUIButton(new Rectangle(10, 10, 100, 20), "Info", "", null);
infoButton.OnClicked = ToggleInfoFrame;
#endif
-
- if (gameModePreset != null) GameMode = gameModePreset.Instantiate(missionType);
- this.submarine = submarine;
}
-
+
+
public GameSession(Submarine selectedSub, string saveFile, XDocument doc)
: this(selectedSub, saveFile)
{
@@ -218,8 +226,22 @@ 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)
+ {
+ GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Submarine:" + submarine.Name);
+ GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
+ GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
+ }
+
#if CLIENT
roundSummary = new RoundSummary(this);
@@ -231,6 +253,11 @@ 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()));
+ }
#if CLIENT
if (roundSummary != null)
diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs
index e201d1d40..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
@@ -125,6 +154,18 @@ namespace Barotrauma
public static bool VerboseLogging { get; set; }
public static bool SaveDebugConsoleLogs { get; set; }
+ private static bool sendUserStatistics;
+ public static bool SendUserStatistics
+ {
+ get { return sendUserStatistics; }
+ set
+ {
+ sendUserStatistics = value;
+ GameMain.Config.Save("config.xml");
+ }
+ }
+ public static bool ShowUserStatisticsPrompt { get; private set; }
+
public GameSettings(string filePath)
{
ContentPackage.LoadAll(ContentPackage.Folder);
@@ -143,6 +184,14 @@ namespace Barotrauma
VerboseLogging = doc.Root.GetAttributeBool("verboselogging", false);
SaveDebugConsoleLogs = doc.Root.GetAttributeBool("savedebugconsolelogs", false);
+ if (doc.Root.Attribute("senduserstatistics") == null)
+ {
+ ShowUserStatisticsPrompt = true;
+ }
+ else
+ {
+ sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", true);
+ }
if (doc == null)
{
@@ -210,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);
}
@@ -238,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;
}
}
@@ -259,10 +308,8 @@ namespace Barotrauma
{
case "contentpackage":
string path = subElement.GetAttributeString("path", "");
-
-
+
SelectedContentPackage = ContentPackage.list.Find(cp => cp.Path == path);
-
if (SelectedContentPackage == null) SelectedContentPackage = new ContentPackage(path);
break;
}
@@ -287,7 +334,8 @@ namespace Barotrauma
new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
- new XAttribute("enablesplashscreen", EnableSplashScreen));
+ new XAttribute("enablesplashscreen", EnableSplashScreen),
+ new XAttribute("senduserstatistics", sendUserStatistics));
if (WasGameUpdated)
{
@@ -344,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/ItemContainer.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs
index e9fae6c86..416082a19 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs
@@ -179,7 +179,6 @@ namespace Barotrauma.Items.Components
return (picker != null);
}
-
public override bool Combine(Item item)
{
if (!containableItems.Any(x => x.MatchesItem(item))) return false;
@@ -195,6 +194,42 @@ namespace Barotrauma.Items.Components
return false;
}
+ public void SetContainedItemPositions()
+ {
+ Vector2 simPos = item.SimPosition;
+ Vector2 displayPos = item.Position;
+
+ foreach (Item contained in Inventory.Items)
+ {
+ if (contained == null) continue;
+
+ if (contained.body != null)
+ {
+ try
+ {
+ contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f);
+ }
+ catch (Exception e)
+ {
+#if DEBUG
+ DebugConsole.ThrowError("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions", e);
+#endif
+ }
+ }
+
+ contained.Rect =
+ new Rectangle(
+ (int)(displayPos.X - contained.Rect.Width / 2.0f),
+ (int)(displayPos.Y + contained.Rect.Height / 2.0f),
+ contained.Rect.Width, contained.Rect.Height);
+
+ contained.Submarine = item.Submarine;
+ contained.CurrentHull = item.CurrentHull;
+
+ contained.SetContainedItemPositions();
+ }
+ }
+
public override void OnMapLoaded()
{
if (itemIds == null) return;
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Pump.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Pump.cs
index b99f8ef31..b161ddff9 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Pump.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Pump.cs
@@ -99,7 +99,9 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
- if (hull1 == null) return;
+ //check the hull if the item is movable
+ if (item.body != null) GetHull();
+ if (hull1 == null) return;
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : voltage;
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/Components/Signal/AdderComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/AdderComponent.cs
new file mode 100644
index 000000000..457a91f35
--- /dev/null
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/AdderComponent.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Xml.Linq;
+
+namespace Barotrauma.Items.Components
+{
+ class AdderComponent : ItemComponent
+ {
+ //an array to keep track of how long ago a signal was received on both inputs
+ protected float[] timeSinceReceived;
+
+ protected float[] receivedSignal;
+
+
+ //the output is sent if both inputs have received a signal within the timeframe
+ protected float timeFrame;
+
+ [InGameEditable, Serialize(0.0f, true)]
+ public float TimeFrame
+ {
+ get { return timeFrame; }
+ set
+ {
+ timeFrame = Math.Max(0.0f, value);
+ }
+ }
+
+ public AdderComponent(Item item, XElement element)
+ : base(item, element)
+ {
+ timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
+ receivedSignal = new float[2];
+ IsActive = true;
+ }
+
+ public override void Update(float deltaTime, Camera cam)
+ {
+ bool sendOutput = true;
+ for (int i = 0; i < timeSinceReceived.Length; i++)
+ {
+ if (timeSinceReceived[i] > timeFrame) sendOutput = false;
+ timeSinceReceived[i] += deltaTime;
+ }
+ if (sendOutput)
+ {
+ item.SendSignal(0, (receivedSignal[0] + receivedSignal[1]).ToString(), "signal_out", null);
+ }
+ }
+
+ public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
+ {
+ switch (connection.Name)
+ {
+ case "signal_in1":
+ float.TryParse(signal, out receivedSignal[0]);
+ timeSinceReceived[0] = 0.0f;
+ break;
+ case "signal_in2":
+ float.TryParse(signal, out receivedSignal[1]);
+ timeSinceReceived[1] = 0.0f;
+ break;
+ }
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/DelayComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/DelayComponent.cs
index 68761a18c..eb6331673 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/DelayComponent.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/DelayComponent.cs
@@ -10,49 +10,51 @@ namespace Barotrauma.Items.Components
{
const int SignalQueueSize = 500;
- //the output is sent if both inputs have received a signal within the timeframe
- private TimeSpan delay;
-
- private Queue> signalQueue;
+ private Queue> signalQueue;
- [InGameEditable, Serialize(1.0f, true)]
+ [InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f), Serialize(1.0f, true)]
public float Delay
{
- get { return (float)delay.TotalSeconds; }
- set
- {
- float seconds = MathHelper.Clamp(value, 0.0f, 60.0f);
-
- delay = new TimeSpan(0,0,0,0, (int)(seconds*1000.0f));
- }
+ get;
+ set;
}
-
+
+ [InGameEditable(ToolTip = "Should the component discard previously received signals when a new one is received."), Serialize(false, true)]
+ public bool ResetWhenSignalReceived
+ {
+ get;
+ set;
+ }
+
public DelayComponent(Item item, XElement element)
: base (item, element)
{
- signalQueue = new Queue>();
-
+ signalQueue = new Queue>();
IsActive = true;
}
public override void Update(float deltaTime, Camera cam)
{
- while (signalQueue.Any() && signalQueue.Peek().Item2 + delay <= DateTime.Now)
+ foreach (var val in signalQueue)
+ {
+ val.Second -= deltaTime;
+ }
+
+ while (signalQueue.Count > 0 && signalQueue.Peek().Second <= 0.0f)
{
var signalOut = signalQueue.Dequeue();
-
- item.SendSignal(0, signalOut.Item1, "signal_out", null);
+ item.SendSignal(0, signalOut.First, "signal_out", null);
}
}
- public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
+ public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f)
{
switch (connection.Name)
{
case "signal_in":
if (signalQueue.Count >= SignalQueueSize) return;
-
- signalQueue.Enqueue(new Tuple(signal, DateTime.Now));
+ if (ResetWhenSignalReceived) signalQueue.Clear();
+ signalQueue.Enqueue(Pair.Create(signal, Delay));
break;
}
}
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/MotionSensor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/MotionSensor.cs
index a3581bd0f..3015cfb50 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/MotionSensor.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/MotionSensor.cs
@@ -49,14 +49,9 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
- if (motionDetected)
- {
- item.SendSignal(1, output, "state_out", null);
- }
- else if (!string.IsNullOrWhiteSpace(falseOutput))
- {
- item.SendSignal(1, falseOutput, "state_out", null);
- }
+ string signalOut = motionDetected ? output : falseOutput;
+
+ if (!string.IsNullOrEmpty(signalOut)) item.SendSignal(1, signalOut, "state_out", null);
updateTimer -= deltaTime;
if (updateTimer > 0.0f) return;
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs
index 21548bfcb..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
}
@@ -602,7 +602,7 @@ namespace Barotrauma
foreach (Item item in ItemList) item.FindHull();
}
- public virtual Hull FindHull()
+ public Hull FindHull()
{
if (parentInventory != null && parentInventory.Owner != null)
{
@@ -648,39 +648,9 @@ namespace Barotrauma
public void SetContainedItemPositions()
{
- if (ownInventory == null) return;
-
- Vector2 simPos = SimPosition;
- Vector2 displayPos = Position;
-
- foreach (Item contained in ownInventory.Items)
+ foreach (ItemComponent component in components)
{
- if (contained == null) continue;
-
- if (contained.body != null)
- {
- try
- {
- contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f);
- }
- catch (Exception e)
- {
-#if DEBUG
- DebugConsole.ThrowError("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions", e);
-#endif
- }
- }
-
- contained.Rect =
- new Rectangle(
- (int)(displayPos.X - contained.Rect.Width / 2.0f),
- (int)(displayPos.Y + contained.Rect.Height / 2.0f),
- contained.Rect.Width, contained.Rect.Height);
-
- contained.Submarine = Submarine;
- contained.CurrentHull = CurrentHull;
-
- contained.SetContainedItemPositions();
+ (component as ItemContainer)?.SetContainedItemPositions();
}
}
@@ -804,7 +774,7 @@ namespace Barotrauma
private bool IsInWater()
{
if (CurrentHull == null) return true;
-
+
float surfaceY = CurrentHull.Surface;
return CurrentHull.WaterVolume > 0.0f && Position.Y < surfaceY;
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/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs
index 1e371a465..13fbcd339 100644
--- a/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs
+++ b/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs
@@ -490,7 +490,7 @@ namespace Barotrauma
return wayPoints[Rand.Int(wayPoints.Count, (useSyncedRand ? Rand.RandSync.Server : Rand.RandSync.Unsynced))];
}
- public static WayPoint[] SelectCrewSpawnPoints(List crew, Submarine submarine, bool tryAssignWayPoint = true)
+ public static WayPoint[] SelectCrewSpawnPoints(List crew, Submarine submarine)
{
List subWayPoints = WayPointList.FindAll(wp => wp.Submarine == submarine);
@@ -524,29 +524,24 @@ namespace Barotrauma
assignedWayPoints[i] = wp;
break;
}
-
if (assignedWayPoints[i] != null) continue;
- if (tryAssignWayPoint)
+ //try to assign a spawnpoint that isn't meant for any specific job
+ var nonJobSpecificPoints = subWayPoints.FindAll(wp => wp.spawnType == SpawnType.Human && wp.assignedJob == null);
+ if (nonJobSpecificPoints.Any())
{
- //try to assign a spawnpoint that isn't meant for any specific job
- var nonJobSpecificPoints = subWayPoints.FindAll(wp => wp.spawnType == SpawnType.Human && wp.assignedJob == null);
-
- if (nonJobSpecificPoints.Any())
- {
- assignedWayPoints[i] = nonJobSpecificPoints[Rand.Int(nonJobSpecificPoints.Count, Rand.RandSync.Server)];
- }
+ assignedWayPoints[i] = nonJobSpecificPoints[Rand.Int(nonJobSpecificPoints.Count, Rand.RandSync.Server)];
}
if (assignedWayPoints[i] != null) continue;
- //everything else failed -> just give a random spawnpoint
- assignedWayPoints[i] = GetRandom(SpawnType.Human);
+ //everything else failed -> just give a random spawnpoint inside the sub
+ assignedWayPoints[i] = GetRandom(SpawnType.Human, null, submarine, true);
}
- for (int i = 0; i < assignedWayPoints.Length; i++ )
+ for (int i = 0; i < assignedWayPoints.Length; i++)
{
- if (assignedWayPoints[i]==null)
+ if (assignedWayPoints[i] == null)
{
DebugConsole.ThrowError("Couldn't find a waypoint for " + crew[i].Name + "!");
assignedWayPoints[i] = WayPointList[0];
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 44683fc11..6dbd84ce9 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
@@ -209,6 +209,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.RandomizeSettings();
started = true;
+ if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:Start");
+
yield return CoroutineStatus.Success;
}
@@ -778,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:
@@ -993,7 +1005,7 @@ namespace Barotrauma.Networking
outmsg.WriteRangedInteger(0, 2, (int)TraitorsEnabled);
- outmsg.WriteRangedInteger(0, Mission.MissionTypes.Count - 1, (GameMain.NetLobbyScreen.MissionTypeIndex));
+ outmsg.WriteRangedInteger(0, MissionPrefab.MissionTypes.Count - 1, (GameMain.NetLobbyScreen.MissionTypeIndex));
outmsg.Write((byte)GameMain.NetLobbyScreen.SelectedModeIndex);
outmsg.Write(GameMain.NetLobbyScreen.LevelSeed);
@@ -1213,7 +1225,7 @@ namespace Barotrauma.Networking
//don't instantiate a new gamesession if we're playing a campaign
if (campaign == null || GameMain.GameSession == null)
{
- GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, Mission.MissionTypes[GameMain.NetLobbyScreen.MissionTypeIndex]);
+ GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, MissionPrefab.MissionTypes[GameMain.NetLobbyScreen.MissionTypeIndex]);
}
if (GameMain.GameSession.GameMode.Mission != null &&
@@ -1341,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)
@@ -1357,6 +1370,8 @@ namespace Barotrauma.Networking
}
}
+ if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Traitors:" + (TraitorManager == null ? "Disabled" : "Enabled"));
+
SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients);
yield return CoroutineStatus.Running;
@@ -1402,6 +1417,8 @@ namespace Barotrauma.Networking
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash);
msg.Write(selectedMode.Name);
+ msg.Write((short)(GameMain.GameSession.GameMode?.Mission == null ?
+ -1 : MissionPrefab.List.IndexOf(GameMain.GameSession.GameMode.Mission.Prefab)));
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
@@ -1495,6 +1512,7 @@ namespace Barotrauma.Networking
foreach (Client client in connectedClients)
{
client.Character = null;
+ client.HasSpawned = false;
client.InGame = false;
}
}
@@ -1614,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";
@@ -2063,9 +2082,17 @@ namespace Barotrauma.Networking
if (jobPrefab != null) jobPreferences.Add(jobPrefab);
}
- sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender);
- sender.CharacterInfo.HeadSpriteId = headSpriteId;
- sender.JobPreferences = jobPreferences;
+ sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender)
+ {
+ HeadSpriteId = headSpriteId
+ };
+
+ //if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor
+ Debug.Assert(sender.JobPreferences.Count > 0);
+ if (jobPreferences.Count > 0)
+ {
+ sender.JobPreferences = jobPreferences;
+ }
}
public void AssignJobs(List unassigned, bool assignHost)
@@ -2110,6 +2137,7 @@ namespace Barotrauma.Networking
//if any of the players has chosen a job that is Always Allowed, give them that job
for (int i = unassigned.Count - 1; i >= 0; i--)
{
+ if (unassigned[i].JobPreferences.Count == 0) continue;
if (!unassigned[i].JobPreferences[0].AllowAlways) continue;
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
unassigned.RemoveAt(i);
@@ -2138,47 +2166,49 @@ namespace Barotrauma.Networking
}
}
- //find a suitable job for the rest of the players
- foreach (Client c in unassigned)
+ //attempt to give the clients a job they have in their job preferences
+ for (int i = unassigned.Count - 1; i >= 0; i--)
{
- foreach (JobPrefab preferredJob in c.JobPreferences)
+ foreach (JobPrefab preferredJob in unassigned[i].JobPreferences)
{
//the maximum number of players that can have this job hasn't been reached yet
// -> assign it to the client
- if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && c.Karma >= preferredJob.MinKarma)
+ if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && unassigned[i].Karma >= preferredJob.MinKarma)
{
- c.AssignedJob = preferredJob;
+ unassigned[i].AssignedJob = preferredJob;
assignedClientCount[preferredJob]++;
+ unassigned.RemoveAt(i);
break;
}
- //none of the jobs the client prefers are available anymore
- else if (preferredJob == c.JobPreferences.Last())
- {
- //find all jobs that are still available
- var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[preferredJob] < jp.MaxNumber && c.Karma >= jp.MinKarma);
+ }
+ }
- //all jobs taken, give a random job
- if (remainingJobs.Count == 0)
- {
- DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
- int jobIndex = Rand.Range(0,JobPrefab.List.Count);
- int skips = 0;
- while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
- {
- jobIndex++;
- skips++;
- if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
- if (skips >= JobPrefab.List.Count) break;
- }
- c.AssignedJob = JobPrefab.List[jobIndex];
- assignedClientCount[c.AssignedJob]++;
- }
- else //some jobs still left, choose one of them by random
- {
- c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
- assignedClientCount[c.AssignedJob]++;
- }
+ //give random jobs to rest of the clients
+ foreach (Client c in unassigned)
+ {
+ //find all jobs that are still available
+ var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
+
+ //all jobs taken, give a random job
+ if (remainingJobs.Count == 0)
+ {
+ DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
+ int jobIndex = Rand.Range(0, JobPrefab.List.Count);
+ int skips = 0;
+ while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
+ {
+ jobIndex++;
+ skips++;
+ if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
+ if (skips >= JobPrefab.List.Count) break;
}
+ c.AssignedJob = JobPrefab.List[jobIndex];
+ assignedClientCount[c.AssignedJob]++;
+ }
+ else //some jobs still left, choose one of them by random
+ {
+ c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
+ assignedClientCount[c.AssignedJob]++;
}
}
}
@@ -2236,7 +2266,11 @@ namespace Barotrauma.Networking
Log("Shutting down the server...", ServerLog.MessageType.ServerMessage);
log.Save();
}
-
+
+ if (GameSettings.SendUserStatistics)
+ {
+ GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:ShutDown");
+ }
server.Shutdown("The server has been shut down");
}
}
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/GameServerSettings.cs b/Barotrauma/BarotraumaShared/Source/Networking/GameServerSettings.cs
index a148dde48..63af136a5 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/GameServerSettings.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/GameServerSettings.cs
@@ -254,6 +254,12 @@ namespace Barotrauma.Networking
set;
}
+ public List AllowedRandomMissionTypes
+ {
+ get;
+ set;
+ }
+
[Serialize(60f, true)]
public float AutoBanTime
{
@@ -287,6 +293,8 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString());
+ doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes));
+
#if SERVER
doc.Root.SetAttributeValue("password", password);
#endif
@@ -334,18 +342,22 @@ namespace Barotrauma.Networking
#endif
subSelectionMode = SelectionMode.Manual;
- Enum.TryParse(doc.Root.GetAttributeString("SubSelection", "Manual"), out subSelectionMode);
+ Enum.TryParse(doc.Root.GetAttributeString("SubSelection", "Manual"), out subSelectionMode);
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
modeSelectionMode = SelectionMode.Manual;
- Enum.TryParse(doc.Root.GetAttributeString("ModeSelection", "Manual"), out modeSelectionMode);
+ Enum.TryParse(doc.Root.GetAttributeString("ModeSelection", "Manual"), out modeSelectionMode);
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
var traitorsEnabled = TraitorsEnabled;
- Enum.TryParse(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
+ Enum.TryParse(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
TraitorsEnabled = traitorsEnabled;
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
+ AllowedRandomMissionTypes = doc.Root.GetAttributeStringArray(
+ "AllowedRandomMissionTypes",
+ MissionPrefab.MissionTypes.ToArray()).ToList();
+
if (GameMain.NetLobbyScreen != null
#if CLIENT
&& GameMain.NetLobbyScreen.ServerMessage != null
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs
index 913795949..f32d873ce 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs
@@ -204,8 +204,11 @@ namespace Barotrauma.Networking
respawnShuttle.Velocity = Vector2.Zero;
- shuttleSteering.AutoPilot = false;
- shuttleSteering.MaintainPos = false;
+ if (shuttleSteering != null)
+ {
+ shuttleSteering.AutoPilot = false;
+ shuttleSteering.MaintainPos = false;
+ }
}
private void UpdateTransporting(float deltaTime)
@@ -264,7 +267,10 @@ namespace Barotrauma.Networking
respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
- shuttleSteering.SetDestinationLevelStart();
+ if (shuttleSteering != null)
+ {
+ shuttleSteering.SetDestinationLevelStart();
+ }
foreach (Door door in shuttleDoors)
{
@@ -284,7 +290,7 @@ namespace Barotrauma.Networking
if (!CoroutineManager.IsCoroutineRunning("forcepos"))
{
- if (shuttleSteering.SteeringPath != null && shuttleSteering.SteeringPath.Finished
+ if ((shuttleSteering?.SteeringPath != null && shuttleSteering.SteeringPath.Finished)
|| (respawnShuttle.WorldPosition.Y + respawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f))
{
@@ -323,7 +329,10 @@ namespace Barotrauma.Networking
ResetShuttle();
- shuttleSteering.TargetVelocity = Vector2.Zero;
+ if (shuttleSteering != null)
+ {
+ shuttleSteering.TargetVelocity = Vector2.Zero;
+ }
GameServer.Log("Dispatching the respawn shuttle.", ServerLog.MessageType.Spawning);
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ServerLog.cs b/Barotrauma/BarotraumaShared/Source/Networking/ServerLog.cs
index 74954b977..8d26d9caf 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/ServerLog.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/ServerLog.cs
@@ -139,7 +139,7 @@ namespace Barotrauma.Networking
}
}
- string fileName = serverName + "_" + DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString() + ".txt";
+ string fileName = serverName + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH:mm") + ".txt";
var invalidChars = Path.GetInvalidFileNameChars();
foreach (char invalidChar in invalidChars)
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 7c3a4f556..3c33067bd 100644
--- a/Barotrauma/BarotraumaShared/changelog.txt
+++ b/Barotrauma/BarotraumaShared/changelog.txt
@@ -1,3 +1,64 @@
+---------------------------------------------------------------------------------------------------------
+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
+---------------------------------------------------------------------------------------------------------
+
+- Fixed clients getting assigned random jobs regardless of job preferences.
+
+---------------------------------------------------------------------------------------------------------
+v0.8.1.3
+---------------------------------------------------------------------------------------------------------
+
+- Fixed server-side crashes during job assignment if a client hasn't sent any job preferences.
+- Fixed crashing if the selected respawn shuttle doesn't have a navigation terminal or any other item
+with a Steering component.
+- Fixed InWater status effects triggering when an item is fabricated, causing issues such as
+water-sensitive items to breaking/exploding immediately after being fabricated.
+- Fixed motion sensors sending out signals even if the output is set to nothing.
+- Fixed crashing when a round starts if the sub has been saved while a fabricator was running.
+- Fixed explosives not detonating inside railgun shells.
+- Fixed characters spawning inside the respawn shuttle if no suitable spawnpoint is found inside the
+main submarine.
+- Fixed a timing bug in the dedicated server which prevented clients from ever timing out, AI characters
+from fixing leaks and reactor usage from being logged.
+- Changed the format of the server log filenames to make them more easily sortable.
+- Increased assistant's skill levels.
+- Added oxygenite tanks.
+- Added a rearward facing variant of railgun controller.
+- Added railgun loader variants with 1 shell capacity.
+- Added railgun shell rack.
+- Decreased the skill level requirements for fixing docking ports.
+
---------------------------------------------------------------------------------------------------------
v0.8.1.2
---------------------------------------------------------------------------------------------------------