diff --git a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs
index 87b0b4133..0ca2fef80 100644
--- a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs
+++ b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs
@@ -24,7 +24,7 @@ namespace Barotrauma
if (wallTarget.Structure.Submarine != null) wallTargetPos += wallTarget.Structure.Submarine.Position;
wallTargetPos.Y = -wallTargetPos.Y;
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Red, false);
- GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
+ GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
}
GUI.Font.DrawString(spriteBatch, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.ToString()})", pos - Vector2.UnitY * 20.0f, Color.Red);
diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
index 96fbc7dfe..a20d1dd42 100644
--- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
+++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs
@@ -1103,8 +1103,9 @@ namespace Barotrauma.Networking
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
{
- string errorMsg = " Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
+ string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true);
+ GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch"+levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure;
}
diff --git a/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs
index a7e0d681b..29f5bade5 100644
--- a/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs
+++ b/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs
@@ -4,11 +4,6 @@ namespace Barotrauma
{
partial class CharacterInfo
{
- partial void SpawnInventoryItemProjSpecific(Item item)
- {
- Entity.Spawner.CreateNetworkEvent(item, false);
- }
-
public void ServerWrite(NetBuffer msg)
{
msg.Write(ID);
diff --git a/Barotrauma/BarotraumaServer/Source/Items/Item.cs b/Barotrauma/BarotraumaServer/Source/Items/Item.cs
index 5f88e71eb..beb831abf 100644
--- a/Barotrauma/BarotraumaServer/Source/Items/Item.cs
+++ b/Barotrauma/BarotraumaServer/Source/Items/Item.cs
@@ -335,6 +335,14 @@ namespace Barotrauma
{
if (GameMain.Server == null) return;
+ if (!ItemList.Contains(this))
+ {
+ string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.";
+ DebugConsole.ThrowError(errorMsg);
+ GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
+ return;
+ }
+
int index = components.IndexOf(ic);
if (index == -1) return;
diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs
index 0bf1f2535..56bd4ee0a 100644
--- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs
+++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs
@@ -782,6 +782,7 @@ namespace Barotrauma.Networking
}
Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
+ GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr);
KickClient(c, errorStr);
}
@@ -839,8 +840,8 @@ namespace Barotrauma.Networking
//(the server started a new campaign and the client isn't aware of it yet?)
if (campaign.CampaignID != campaignID)
{
- c.LastRecvCampaignSave = 0;
- c.LastRecvCampaignUpdate = 0;
+ c.LastRecvCampaignSave = (ushort)(campaign.LastSaveID - 1);
+ c.LastRecvCampaignUpdate = (ushort)(campaign.LastUpdateID - 1);
}
}
}
diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml
index 2a1a400b3..4b478cc34 100644
--- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml
+++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml
@@ -74,6 +74,8 @@
+
+
diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems
index 0a90b8248..619c6b810 100644
--- a/Barotrauma/BarotraumaShared/SharedContent.projitems
+++ b/Barotrauma/BarotraumaShared/SharedContent.projitems
@@ -3129,8 +3129,10 @@
PreserveNewest
+ PreserveNewest
+ PreserveNewest
PreserveNewest
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
index d4dc9e8b0..cf7cc4ea7 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs
@@ -419,12 +419,7 @@ namespace Barotrauma
selectedTargetMemory.Priority -= deltaTime * 0.1f;
- Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition) : SelectedAiTarget.SimPosition;
-
- if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine != null && Character.Submarine != SelectedAiTarget.Entity.Submarine)
- {
- attackSimPosition = ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition - Character.Submarine.Position);
- }
+ Vector2 attackPos = SelectedAiTarget.WorldPosition;
if (SelectedAiTarget.Entity is Item item)
{
@@ -440,35 +435,6 @@ namespace Barotrauma
}
}
- if (wallTarget != null)
- {
- attackSimPosition = ConvertUnits.ToSimUnits(wallTarget.Position);
- if (Character.Submarine == null && SelectedAiTarget.Entity?.Submarine != null)
- {
- attackSimPosition += ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
- }
- }
- else if (SelectedAiTarget.Entity is Character c)
- {
- //target the closest limb if the target is a character
- float closestDist = Vector2.DistanceSquared(SelectedAiTarget.SimPosition, SimPosition) * 10.0f;
- foreach (Limb limb in ((Character)SelectedAiTarget.Entity).AnimController.Limbs)
- {
- if (limb == null) continue;
- float dist = Vector2.DistanceSquared(limb.SimPosition, SimPosition) / Math.Max(limb.AttackPriority, 0.1f);
- if (dist < closestDist)
- {
- closestDist = dist;
- attackSimPosition = limb.SimPosition;
- }
- }
- }
-
- if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
- {
- Character.AnimController.TargetDir = Character.SimPosition.X < attackSimPosition.X ? Direction.Right : Direction.Left;
- }
-
if (raycastTimer > 0.0)
{
raycastTimer -= deltaTime;
@@ -479,6 +445,35 @@ namespace Barotrauma
raycastTimer = RaycastInterval;
}
+ if (wallTarget != null)
+ {
+ attackPos = wallTarget.Position;
+ if (Character.Submarine == null && wallTarget.Structure.Submarine != null)
+ {
+ attackPos += wallTarget.Structure.Submarine.Position;
+ }
+ }
+ else if (SelectedAiTarget.Entity is Character c)
+ {
+ //target the closest limb if the target is a character
+ float closestDist = Vector2.DistanceSquared(SelectedAiTarget.WorldPosition, WorldPosition) * 10.0f;
+ foreach (Limb limb in c.AnimController.Limbs)
+ {
+ if (limb == null) continue;
+ float dist = Vector2.DistanceSquared(limb.WorldPosition, WorldPosition) / Math.Max(limb.AttackPriority, 0.1f);
+ if (dist < closestDist)
+ {
+ closestDist = dist;
+ attackPos = limb.WorldPosition;
+ }
+ }
+ }
+
+ if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
+ {
+ Character.AnimController.TargetDir = Character.WorldPosition.X < attackPos.X ? Direction.Right : Direction.Left;
+ }
+
if (aggressiveBoarding)
{
//targeting a wall section that can be passed through -> steer manually through the hole
@@ -511,9 +506,9 @@ namespace Barotrauma
return;
}
}
- else if (SelectedAiTarget.Entity is Item)
+ else if (SelectedAiTarget.Entity is Item i)
{
- var door = ((Item)SelectedAiTarget.Entity).GetComponent();
+ var door = i.GetComponent();
//steer through the door manually if it's open or broken
if (door?.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom && (door.IsOpen || door.Item.Condition <= 0.0f))
{
@@ -556,7 +551,7 @@ namespace Barotrauma
}
else
{
- UpdateFallBack(attackSimPosition, deltaTime);
+ UpdateFallBack(attackPos, deltaTime);
return;
}
}
@@ -564,14 +559,14 @@ namespace Barotrauma
{
if (attackingLimb.attack.SecondaryCoolDownTimer <= 0)
{
- // Don't allow attacking when the attack target has changed.
+ // Don't allow attacking when the attack target has just changed.
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{
canAttack = false;
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack)
{
// Fall back if cannot attack.
- UpdateFallBack(attackSimPosition, deltaTime);
+ UpdateFallBack(attackPos, deltaTime);
return;
}
attackingLimb = null;
@@ -580,7 +575,7 @@ namespace Barotrauma
{
// If the secondary cooldown is defined and expired, check if we can switch the attack
var previousLimb = attackingLimb;
- var newLimb = GetAttackLimb(attackSimPosition, previousLimb);
+ var newLimb = GetAttackLimb(attackPos, previousLimb);
if (newLimb != null)
{
attackingLimb = newLimb;
@@ -594,7 +589,7 @@ namespace Barotrauma
}
else
{
- UpdateFallBack(attackSimPosition, deltaTime);
+ UpdateFallBack(attackPos, deltaTime);
return;
}
}
@@ -609,7 +604,7 @@ namespace Barotrauma
break;
case AIBehaviorAfterAttack.FallBack:
default:
- UpdateFallBack(attackSimPosition, deltaTime);
+ UpdateFallBack(attackPos, deltaTime);
return;
}
@@ -617,7 +612,7 @@ namespace Barotrauma
if (attackingLimb == null)
{
- attackingLimb = GetAttackLimb(attackSimPosition);
+ attackingLimb = GetAttackLimb(attackPos);
}
if (canAttack)
{
@@ -627,18 +622,18 @@ namespace Barotrauma
if (canAttack)
{
// Check that we can reach the target
- distance = ConvertUnits.ToDisplayUnits(Vector2.Distance(attackingLimb.SimPosition, attackSimPosition));
+ distance = Vector2.Distance(attackingLimb.WorldPosition, attackPos);
canAttack = distance < attackingLimb.attack.Range;
}
Limb steeringLimb = Character.AnimController.MainLimb;
if (steeringLimb != null)
{
- Vector2 steeringVector = attackSimPosition - steeringLimb.SimPosition;
- Vector2 targetingVector = Vector2.Normalize(steeringVector) * attackingLimb.attack.Range;
+ Vector2 toTarget = Vector2.Normalize(attackPos - steeringLimb.WorldPosition);
+ Vector2 targetingVector = toTarget * attackingLimb.attack.Range;
// Offset the position a bit so that we don't overshoot the movement.
- Vector2 steerPos = attackSimPosition + targetingVector;
- steeringManager.SteeringSeek(steerPos, 10);
+ Vector2 steerPos = attackPos + targetingVector;
+ steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(attackPos), 10);
if (Character.CurrentHull == null)
{
SteeringManager.SteeringAvoid(deltaTime, colliderSize * 1.5f);
@@ -656,7 +651,7 @@ namespace Barotrauma
}
else if (indoorsSteering.CurrentPath.Finished)
{
- steeringManager.SteeringManual(deltaTime, Vector2.Normalize(steeringVector));
+ steeringManager.SteeringManual(deltaTime, toTarget);
}
else if (indoorsSteering.CurrentPath.CurrentNode?.ConnectedDoor != null)
{
@@ -674,11 +669,11 @@ namespace Barotrauma
if (canAttack)
{
- UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition, distance);
+ UpdateLimbAttack(deltaTime, attackingLimb, ConvertUnits.ToSimUnits(attackPos), distance);
}
}
- private Limb GetAttackLimb(Vector2 attackSimPosition, Limb ignoredLimb = null)
+ private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null)
{
AttackContext currentContext = Character.GetAttackContext();
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity;
@@ -692,7 +687,7 @@ namespace Barotrauma
l.attack.IsValidTarget(target) &&
l.attack.Conditionals.All(c => (target is ISerializableEntity se && c.Matches(se)) || !(target is ISerializableEntity) || !(target is Character)))
.OrderByDescending(l => l.attack.Priority)
- .ThenBy(l => ConvertUnits.ToDisplayUnits(Vector2.Distance(l.SimPosition, attackSimPosition)));
+ .ThenBy(l => Vector2.Distance(l.WorldPosition, attackWorldPos));
// TODO: priority should probably not override the distance -> use values instead of booleans
return limbs.FirstOrDefault();
}
@@ -705,12 +700,13 @@ namespace Barotrauma
{
return;
}
-
+
//check if there's a wall between the target and the Character
Vector2 rayStart = Character.SimPosition;
Vector2 rayEnd = SelectedAiTarget.SimPosition;
+ bool offset = SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null;
- if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
+ if (offset)
{
rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
}
@@ -721,18 +717,7 @@ namespace Barotrauma
return;
}
- Structure wall = closestBody.UserData as Structure;
- if (wall?.Submarine == null)
- {
- return;
- /*if (selectedAiTarget.Entity.Submarine != null)
- {
- wallTarget = new WallTarget(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition), selectedAiTarget.Entity.Submarine);
- latchOntoAI?.SetAttachTarget(closestBody, selectedAiTarget.Entity.Submarine, Submarine.LastPickedPosition);
- }*/
- //if (selectedAiTarget.Entity.Submarine != null && Character.Submarine == null) wallAttackPos += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
- }
- else
+ if (closestBody.UserData is Structure wall && wall.Submarine != null)
{
int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition));
int passableHoleCount = GetMinimumPassableHoleCount();
@@ -755,36 +740,27 @@ namespace Barotrauma
if (wall.SectionDamage(i) > sectionDamage) sectionIndex = i;
}
- Vector2 sectionPos = ConvertUnits.ToSimUnits(wall.SectionPosition(sectionIndex));
+ Vector2 sectionPos = wall.SectionPosition(sectionIndex);
Vector2 attachTargetNormal;
if (wall.IsHorizontal)
{
attachTargetNormal = new Vector2(0.0f, Math.Sign(Character.WorldPosition.Y - wall.WorldPosition.Y));
- sectionPos.Y += ConvertUnits.ToSimUnits((wall.BodyHeight <= 0.0f ? wall.Rect.Height : wall.BodyHeight) / 2) * attachTargetNormal.Y;
+ sectionPos.Y += (wall.BodyHeight <= 0.0f ? wall.Rect.Height : wall.BodyHeight) / 2 * attachTargetNormal.Y;
}
else
{
attachTargetNormal = new Vector2(Math.Sign(Character.WorldPosition.X - wall.WorldPosition.X), 0.0f);
- sectionPos.X += ConvertUnits.ToSimUnits((wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2) * attachTargetNormal.X;
+ sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
}
- wallTarget = new WallTarget(ConvertUnits.ToDisplayUnits(sectionPos), wall, sectionIndex);
- latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, sectionPos, attachTargetNormal);
+
+ wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
+ latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
}
}
public override void OnAttacked(Character attacker, AttackResult attackResult)
{
updateTargetsTimer = Math.Min(updateTargetsTimer, 0.1f);
-
- // Reduce the cooldown so that the character can react
- foreach (var limb in Character.AnimController.Limbs)
- {
- if (limb.attack != null)
- {
- limb.attack.CoolDownTimer *= 0.1f;
- // secondary cooldown?
- }
- }
if (attackResult.Damage > 0.0f && attackWhenProvoked)
{
@@ -801,18 +777,31 @@ namespace Barotrauma
if (attacker == null || attacker.AiTarget == null) return;
AITargetMemory targetMemory = FindTargetMemory(attacker.AiTarget);
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * aggressionhurt;
+
+ // Reduce the cooldown so that the character can react
+ // Only allow to react once. Otherwise would attack the target with only a fraction of cooldown
+ if (SelectedAiTarget != attacker.AiTarget)
+ {
+ foreach (var limb in Character.AnimController.Limbs)
+ {
+ if (limb.attack != null)
+ {
+ limb.attack.CoolDownTimer *= 0.1f;
+ }
+ }
+ }
}
// 10 dmg, 100 health -> 0.1
private float GetRelativeDamage(float dmg, float vitality) => dmg / Math.Max(vitality, 1.0f);
- private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition, float distance = -1)
+ private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackSimPos, float distance = -1)
{
var damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable;
if (damageTarget == null) return;
float prevHealth = damageTarget.Health;
- if (limb.UpdateAttack(deltaTime, attackPosition, damageTarget, out AttackResult attackResult, distance))
+ if (limb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance))
{
if (damageTarget.Health > 0)
{
@@ -820,24 +809,19 @@ namespace Barotrauma
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * aggressiongreed;
}
}
-
- if (!limb.attack.IsRunning)
- {
- wallTarget = null;
- }
}
private void UpdateFallBack(Vector2 attackPosition, float deltaTime)
{
- float dist = Vector2.Distance(attackPosition, Character.SimPosition);
+ Vector2 attackVector = attackPosition - Character.WorldPosition;
+ float dist = attackVector.Length();
float desiredDist = colliderSize * 2.0f;
if (dist < desiredDist)
{
- Vector2 attackDir = Vector2.Normalize(Character.SimPosition - attackPosition);
+ Vector2 attackDir = Vector2.Normalize(-attackVector);
if (!MathUtils.IsValid(attackDir)) attackDir = Vector2.UnitY;
steeringManager.SteeringManual(deltaTime, attackDir * (1.0f - (dist / 500.0f)));
}
-
steeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f);
}
@@ -888,8 +872,8 @@ namespace Barotrauma
//sight/hearing range
public void UpdateTargets(Character character, out TargetingPriority targetingPriority)
{
+ AITarget newTarget = null;
targetingPriority = null;
- SelectedAiTarget = null;
selectedTargetMemory = null;
targetValue = 0.0f;
@@ -1021,13 +1005,15 @@ namespace Barotrauma
if (valueModifier > targetValue)
{
- SelectedAiTarget = target;
+ newTarget = target;
selectedTargetMemory = targetMemory;
targetingPriority = targetingPriorities[targetingTag];
targetValue = valueModifier;
}
}
+ SelectedAiTarget = newTarget;
+
if (SelectedAiTarget != _previousAiTarget)
{
wallTarget = null;
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs
index f1910b6ab..c5aee6f70 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs
@@ -801,7 +801,7 @@ namespace Barotrauma
{
foreach (XElement itemElement in element.Elements())
{
- var newItem = Item.Load(itemElement, inventory.Owner.Submarine);
+ var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true);
if (newItem == null) { continue; }
int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 });
@@ -810,9 +810,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Invalid inventory data in character \"" + Name + "\" - no slot indices found");
continue;
}
-
- SpawnInventoryItemProjSpecific(newItem);
-
+
inventory.TryPutItem(newItem, slotIndices[0], false, false, null);
//force the item to the correct slots
@@ -842,8 +840,6 @@ namespace Barotrauma
}
}
- partial void SpawnInventoryItemProjSpecific(Item item);
-
public void ReloadHeadAttachments()
{
ResetLoadedAttachments();
diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
index bb595803c..0c1d42432 100644
--- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
+++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs
@@ -491,10 +491,10 @@ namespace Barotrauma
///
/// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
///
- public bool UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget, out AttackResult attackResult, float distance = -1)
+ public bool UpdateAttack(float deltaTime, Vector2 attackSimPos, IDamageable damageTarget, out AttackResult attackResult, float distance = -1)
{
attackResult = default(AttackResult);
- float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
+ float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackSimPos));
bool wasRunning = attack.IsRunning;
attack.UpdateAttackTimer(deltaTime);
@@ -511,7 +511,7 @@ namespace Barotrauma
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
structureBody = Submarine.PickBody(
- SimPosition, attackPosition,
+ SimPosition, attackSimPos,
ignoredBodies, Physics.CollisionWall);
if (damageTarget is Item)
@@ -607,7 +607,7 @@ namespace Barotrauma
attack.SetCoolDown();
}
- Vector2 diff = attackPosition - SimPosition;
+ Vector2 diff = attackSimPos - SimPosition;
bool applyForces = (!attack.ApplyForcesOnlyOnce || !wasRunning) && diff.LengthSquared() > 0.00001f;
if (applyForces)
{
@@ -620,13 +620,13 @@ namespace Barotrauma
Limb limb = character.AnimController.Limbs[limbIndex];
Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
- limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
+ limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
}
}
else
{
Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
- body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
+ body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
}
}
return wasHit;
diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs
index beb21b83d..4a3b39c6a 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs
@@ -60,7 +60,6 @@ namespace Barotrauma
public readonly XElement StaticBodyConfig;
- private bool needsPositionUpdate;
private float lastSentCondition;
private float condition;
@@ -1811,8 +1810,20 @@ namespace Barotrauma
}
partial void UpdateNetPosition(float deltaTime);
-
+
public static Item Load(XElement element, Submarine submarine)
+ {
+ return Load(element, submarine, createNetworkEvent: false);
+ }
+
+ ///
+ /// Instantiate a new item and load its data from the XML element.
+ ///
+ /// The element containing the data of the item
+ /// The submarine to spawn the item in (can be null)
+ /// Should an EntitySpawner event be created to notify clients about the item being created.
+ ///
+ public static Item Load(XElement element, Submarine submarine, bool createNetworkEvent)
{
string name = element.Attribute("name").Value;
string identifier = element.GetAttributeString("identifier", "");
@@ -1858,6 +1869,13 @@ namespace Barotrauma
linkedToID = new List()
};
+#if SERVER
+ if (createNetworkEvent)
+ {
+ Spawner.CreateNetworkEvent(item, remove: false);
+ }
+#endif
+
foreach (XAttribute attribute in element.Attributes())
{
if (!item.properties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) continue;
@@ -1904,7 +1922,7 @@ namespace Barotrauma
{
component.OnItemLoaded();
}
-
+
return item;
}
diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs
index 8a80ec2c6..76866dfdf 100644
--- a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs
+++ b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs
@@ -91,6 +91,16 @@ namespace Barotrauma
public override void CreateNetworkEvent()
{
+ if (!Item.ItemList.Contains(container.Item))
+ {
+ string errorMsg = "Attempted to create a network event for an item (" + container.Item.Name + ") that hasn't been fully initialized yet.";
+ DebugConsole.ThrowError(errorMsg);
+ GameAnalyticsManager.AddErrorEventOnce(
+ "ItemInventory.CreateServerEvent:EventForUninitializedItem" + container.Item.Name + container.Item.ID,
+ GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
+ return;
+ }
+
int componentIndex = container.Item.GetComponentIndex(container);
if (componentIndex == -1)
{
diff --git a/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs b/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs
index b5a5bc508..569b5ccff 100644
--- a/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs
+++ b/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs
@@ -407,7 +407,7 @@ namespace Barotrauma
try
{
- MethodInfo loadMethod = t.GetMethod("Load");
+ MethodInfo loadMethod = t.GetMethod("Load", new [] { typeof(XElement), typeof(Submarine) });
if (loadMethod == null)
{
DebugConsole.ThrowError("Could not find the method \"Load\" in " + t + ".");
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs
index 4dcabf040..f4f450d1c 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs
@@ -70,7 +70,7 @@ namespace Barotrauma
private readonly Queue spawnQueue;
private readonly Queue removeQueue;
- class SpawnOrRemove
+ public class SpawnOrRemove
{
public readonly Entity Entity;
diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub
index 4207a60d2..74c05a493 100644
Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ
diff --git a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub
index 1c829b2d6..57f8fc56f 100644
Binary files a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub and b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub differ