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