From 0d8d0d62ecdb2ebb3187e77da94594fb7394b57c Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 6 Dec 2017 14:48:47 -0300 Subject: [PATCH 1/6] Fixed ragdolling in multiplayer --- .../Source/Characters/Character.cs | 8 +++--- .../Source/Characters/CharacterNetworking.cs | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index b38a85f58..c29fb7eaf 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -303,7 +303,7 @@ namespace Barotrauma private float stunTimer; public float Stun { - get { return stunTimer; } + get { return IsRagdolled ? 1.0f : stunTimer; } set { if (GameMain.Client != null) return; @@ -686,7 +686,7 @@ namespace Barotrauma case InputType.Use: return !(dequeuedInput.HasFlag(InputNetFlags.Use)) && (prevDequeuedInput.HasFlag(InputNetFlags.Use)); case InputType.Ragdoll: - return !(dequeuedInput.HasFlag(InputType.Ragdoll)) && (prevDequeuedInput.HasFlag(InputType.Ragdoll)); + return !(dequeuedInput.HasFlag(InputNetFlags.Ragdoll)) && (prevDequeuedInput.HasFlag(InputNetFlags.Ragdoll)); default: return false; } @@ -722,6 +722,7 @@ namespace Barotrauma case InputType.Attack: return dequeuedInput.HasFlag(InputNetFlags.Attack); case InputType.Ragdoll: + if (dequeuedInput.HasFlag(InputNetFlags.Ragdoll)) DebugConsole.NewMessage("RAGDOLL", Color.Lime); return dequeuedInput.HasFlag(InputNetFlags.Ragdoll); } return false; @@ -1448,7 +1449,7 @@ namespace Barotrauma UpdateControlled(deltaTime, cam); - if (Stun > 0.0f) + if (stunTimer > 0.0f) { stunTimer -= deltaTime; if (stunTimer < 0.0f && GameMain.Server != null) @@ -1472,7 +1473,6 @@ namespace Barotrauma if (IsRagdolled) { ((HumanoidAnimController)AnimController).Crouching = false; - Stun = Math.Max(0.1f, Stun); AnimController.ResetPullJoints(); selectedConstruction = null; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs index 5abc8331e..217156842 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs @@ -128,6 +128,12 @@ namespace Barotrauma if (!AllowInput) { AnimController.Frozen = false; + if (memInput.Count > 0) + { + prevDequeuedInput = dequeuedInput; + dequeuedInput = memInput[memInput.Count - 1].states; + memInput.RemoveAt(memInput.Count - 1); + } } else if (memInput.Count == 0) { @@ -289,20 +295,20 @@ namespace Barotrauma newInteract = msg.ReadUInt16(); } - if (AllowInput) + //if (AllowInput) + //{ + if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) { - if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) - { - NetInputMem newMem = new NetInputMem(); - newMem.states = newInput; - newMem.intAim = newAim; - newMem.interact = newInteract; + NetInputMem newMem = new NetInputMem(); + newMem.states = newInput; + newMem.intAim = newAim; + newMem.interact = newInteract; - newMem.networkUpdateID = (ushort)(networkUpdateID - i); + newMem.networkUpdateID = (ushort)(networkUpdateID - i); - memInput.Insert(i, newMem); - } + memInput.Insert(i, newMem); } + //} } if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID)) From 1e81a5acf1bb2cbce8413c1e9671148ccd512a29 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 6 Dec 2017 14:49:37 -0300 Subject: [PATCH 2/6] Removed "RAGDOLL" console spam --- Barotrauma/BarotraumaShared/Source/Characters/Character.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index c29fb7eaf..d78e6061f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -722,7 +722,6 @@ namespace Barotrauma case InputType.Attack: return dequeuedInput.HasFlag(InputNetFlags.Attack); case InputType.Ragdoll: - if (dequeuedInput.HasFlag(InputNetFlags.Ragdoll)) DebugConsole.NewMessage("RAGDOLL", Color.Lime); return dequeuedInput.HasFlag(InputNetFlags.Ragdoll); } return false; From a272d22de8288e5cc20b72448c567fb019d69eaa Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 6 Dec 2017 17:57:58 -0300 Subject: [PATCH 3/6] Players can now pick up corpses to eat them --- .../Source/Characters/CharacterHUD.cs | 4 +- .../Animation/FishAnimController.cs | 75 +++++++++++++++++++ .../Source/Characters/Character.cs | 27 +++---- 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index f5d37e0b2..a9f4d0dee 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -114,7 +114,7 @@ namespace Barotrauma } } - if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) + if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) { character.SelectedCharacter.Inventory.Update(deltaTime); } @@ -164,7 +164,7 @@ namespace Barotrauma character.Inventory.DrawOwn(spriteBatch); } - if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) + if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) { character.SelectedCharacter.Inventory.DrawOffset = new Vector2(320.0f, 0.0f); character.SelectedCharacter.Inventory.DrawOwn(spriteBatch); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs index 31a73d3fb..af427b259 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs @@ -154,6 +154,8 @@ namespace Barotrauma } } + if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter); + if (!flip) return; flipTimer += deltaTime; @@ -169,6 +171,79 @@ namespace Barotrauma } } + private float eatTimer = 0.0f; + + public override void DragCharacter(Character target) + { + if (target == null) return; + + Limb mouthLimb = Array.Find(Limbs, l => l != null && l.MouthPos.HasValue); + if (mouthLimb == null) mouthLimb = GetLimb(LimbType.Head); + + if (mouthLimb == null) + { + DebugConsole.ThrowError("Character \"" + character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)"); + return; + } + + Character targetCharacter = target; + float eatSpeed = character.Mass / targetCharacter.Mass * 0.1f; + + eatTimer += (float)Timing.Step * eatSpeed; + + Vector2 mouthPos = mouthLimb.SimPosition; + if (mouthLimb.MouthPos.HasValue) + { + float cos = (float)Math.Cos(mouthLimb.Rotation); + float sin = (float)Math.Sin(mouthLimb.Rotation); + + mouthPos += new Vector2( + mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin, + mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos); + } + + Vector2 attackSimPosition = character.Submarine == null ? ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition; + + Vector2 limbDiff = attackSimPosition - mouthPos; + float limbDist = limbDiff.Length(); + if (limbDist < 1.0f) + { + //pull the target character to the position of the mouth + //(+ make the force fluctuate to waggle the character a bit) + targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); + targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation); + targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); + + //pull the character's mouth to the target character (again with a fluctuating force) + float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f)); + mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength); + + if (eatTimer % 1.0f < 0.5f && (eatTimer - (float)Timing.Step * eatSpeed) % 1.0f > 0.5f) + { + //apply damage to the target character to get some blood particles flying + targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), 10.0f, false); + + //keep severing joints until there is only one limb left + LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered); + if (nonSeveredJoints.Length == 0) + { + //only one limb left, the character is now full eaten + Entity.Spawner.AddToRemoveQueue(targetCharacter); + character.SelectedCharacter = null; + character.Health += 10.0f; + } + else //sever a random joint + { + targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]); + } + } + } + else + { + character.SelectedCharacter = null; + } + } + void UpdateSineAnim(float deltaTime) { movement = TargetMovement*swimSpeed; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index d78e6061f..c0f415aa9 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -175,7 +175,7 @@ namespace Barotrauma { get { return !IsUnconscious && Stun <= 0.0f && !isDead; } } - + public bool CanInteract { get { return AllowInput && IsHumanoid && !LockHands; } @@ -206,6 +206,7 @@ namespace Barotrauma get { return selectedCharacter; } set { + if (value == selectedCharacter) return; if (selectedCharacter != null) selectedCharacter.selectedBy = null; selectedCharacter = value; @@ -1030,7 +1031,7 @@ namespace Barotrauma public bool CanInteractWith(Character c, float maxDist = 200.0f) { - if (c == this || !c.Enabled || c.info == null || !c.IsHumanoid || !c.CanBeSelected) return false; + if (c == this || !c.Enabled || !c.CanBeSelected) return false; maxDist = ConvertUnits.ToSimUnits(maxDist); if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist) return false; @@ -1242,7 +1243,7 @@ namespace Barotrauma public void SelectCharacter(Character character) { if (character == null) return; - + SelectedCharacter = character; } @@ -1264,27 +1265,27 @@ namespace Barotrauma public void DoInteractionUpdate(float deltaTime, Vector2 mouseSimPos) { bool isLocalPlayer = (controlled == this); - if (!isLocalPlayer && (this is AICharacter || !IsRemotePlayer)) + if (!isLocalPlayer && (this is AICharacter && !IsRemotePlayer)) { return; } if (!CanInteract) { - if (SelectedCharacter != null) - { - DeselectCharacter(); - } selectedConstruction = null; focusedItem = null; - focusedCharacter = null; - return; + if (!AllowInput) + { + focusedCharacter = null; + if (SelectedCharacter != null) DeselectCharacter(); + return; + } } if ((!isLocalPlayer && IsKeyHit(InputType.Select) && GameMain.Server == null) || (isLocalPlayer && (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen))) { focusedCharacter = FindCharacterAtPosition(mouseSimPos); - focusedItem = FindItemAtPosition(mouseSimPos, AnimController.InWater ? 0.5f : 0.25f); + focusedItem = CanInteract ? FindItemAtPosition(mouseSimPos, AnimController.InWater ? 0.5f : 0.25f) : null; if (focusedCharacter != null && focusedItem != null) { @@ -1303,7 +1304,7 @@ namespace Barotrauma { findFocusedTimer -= deltaTime; } - + if (SelectedCharacter != null && IsKeyHit(InputType.Select)) { DeselectCharacter(); @@ -1471,7 +1472,7 @@ namespace Barotrauma if (IsRagdolled) { - ((HumanoidAnimController)AnimController).Crouching = false; + if (AnimController is HumanoidAnimController) ((HumanoidAnimController)AnimController).Crouching = false; AnimController.ResetPullJoints(); selectedConstruction = null; From 26216a0d994ee1fc1e70861b193b2b16014a7c68 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Thu, 7 Dec 2017 00:01:48 -0300 Subject: [PATCH 4/6] Fixed fish being selectable Closes #91 --- .../BarotraumaClient/Source/Characters/CharacterHUD.cs | 9 +++++++-- .../BarotraumaShared/Source/Characters/Character.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index a9f4d0dee..9a3ac7b23 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -205,10 +205,15 @@ namespace Barotrauma Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f; startPos = cam.WorldToScreen(startPos); + string focusName = character.FocusedCharacter.SpeciesName; + if (character.FocusedCharacter.Info != null) + { + focusName = character.FocusedCharacter.Info.Name; + } Vector2 textPos = startPos; - textPos -= new Vector2(GUI.Font.MeasureString(character.FocusedCharacter.Info.Name).X / 2, 20); + textPos -= new Vector2(GUI.Font.MeasureString(focusName).X / 2, 20); - GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.Info.Name, Color.White, Color.Black, 2); + GUI.DrawString(spriteBatch, textPos, focusName, Color.White, Color.Black, 2); } else if (character.SelectedCharacter == null && character.FocusedItem != null && character.SelectedConstruction == null) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index c0f415aa9..954cfd211 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -1031,7 +1031,7 @@ namespace Barotrauma public bool CanInteractWith(Character c, float maxDist = 200.0f) { - if (c == this || !c.Enabled || !c.CanBeSelected) return false; + if (c == this || !c.Enabled || !c.IsHumanoid || !c.CanBeSelected) return false; maxDist = ConvertUnits.ToSimUnits(maxDist); if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist) return false; From 7026da2748f2adc160548159da1bc3f9cc8f6730 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Thu, 7 Dec 2017 19:34:19 +0200 Subject: [PATCH 5/6] Health Scanner HUD shows the status of all nearby characters without having to highlight them. Fixes being unable to scan characters that aren't dead or unconscious. Non-human characters can also be scanned now. Closes #73 --- .../Source/Items/Components/StatusHUD.cs | 97 ++++++++++++++++++- .../Source/Items/Components/StatusHUD.cs | 6 -- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs index 1d3622731..148e1a4e3 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework; +using FarseerPhysics; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; @@ -7,6 +8,81 @@ namespace Barotrauma.Items.Components { partial class StatusHUD : ItemComponent { + private static readonly string[] BleedingTexts = { "Minor bleeding", "Bleeding", "Bleeding heavily", "Catastrophic Bleeding" }; + + private static readonly string[] HealthTexts = { "No visible injuries", "Minor injuries", "Injured", "Major injuries", "Critically injured" }; + + private static readonly string[] OxygenTexts = { "Oxygen level normal", "Gasping for air", "Signs of oxygen deprivation", "Not breathing" }; + + [Serialize(500.0f, false)] + public float Range + { + get; + private set; + } + + [Serialize(50.0f, false)] + public float FadeOutRange + { + get; + private set; + } + + private List visibleCharacters = new List(); + + private const float UpdateInterval = 0.5f; + private float updateTimer; + + private Character equipper; + + public override void Update(float deltaTime, Camera cam) + { + base.Update(deltaTime, cam); + + if (equipper == null) + { + IsActive = false; + return; + } + + if (updateTimer > 0.0f) + { + updateTimer -= deltaTime; + return; + } + + visibleCharacters.Clear(); + foreach (Character c in Character.CharacterList) + { + if (c == equipper) continue; + + float dist = Vector2.DistanceSquared(equipper.WorldPosition, c.WorldPosition); + if (dist < Range * Range) + { + Vector2 diff = c.WorldPosition - equipper.WorldPosition; + if (Submarine.CheckVisibility(equipper.SimPosition, equipper.SimPosition + ConvertUnits.ToSimUnits(diff)) == null) + { + visibleCharacters.Add(c); + } + } + } + + updateTimer = UpdateInterval; + } + + public override void Equip(Character character) + { + updateTimer = 0.0f; + equipper = character; + IsActive = true; + } + + public override void Unequip(Character character) + { + equipper = null; + IsActive = false; + } + public override void DrawHUD(SpriteBatch spriteBatch, Character character) { if (character == null) return; @@ -14,16 +90,27 @@ namespace Barotrauma.Items.Components GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Green * 0.1f, true); - if (character.FocusedCharacter == null) return; + foreach (Character c in visibleCharacters) + { + if (c == character) continue; - var target = character.FocusedCharacter; + float dist = Vector2.Distance(character.WorldPosition, c.WorldPosition); + DrawCharacterInfo(spriteBatch, c, 1.0f - MathHelper.Max((dist - (Range - FadeOutRange)) / FadeOutRange, 0.0f)); + } + } + + private void DrawCharacterInfo(SpriteBatch spriteBatch, Character target, float alpha = 1.0f) + { Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.WorldPosition); hudPos += Vector2.UnitX * 50.0f; List texts = new List(); - texts.Add(target.Name); + if (target.Info != null) + { + texts.Add(target.Name); + } if (target.IsDead) { @@ -63,7 +150,7 @@ namespace Barotrauma.Items.Components foreach (string text in texts) { - GUI.DrawString(spriteBatch, hudPos, text, Color.LightGreen, Color.Black * 0.7f, 2); + GUI.DrawString(spriteBatch, hudPos, text, Color.LightGreen * alpha, Color.Black * 0.7f * alpha, 2); hudPos.Y += 24.0f; } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/StatusHUD.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/StatusHUD.cs index 8d6335e1d..833b66c2e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/StatusHUD.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/StatusHUD.cs @@ -4,12 +4,6 @@ namespace Barotrauma.Items.Components { partial class StatusHUD : ItemComponent { - private static readonly string[] BleedingTexts = {"Minor bleeding", "Bleeding", "Bleeding heavily", "Catastrophic Bleeding"}; - - private static readonly string[] HealthTexts = { "No visible injuries", "Minor injuries", "Injured", "Major injuries", "Critically injured" }; - - private static readonly string[] OxygenTexts = { "Oxygen level normal", "Gasping for air", "Signs of oxygen deprivation", "Not breathing" }; - public StatusHUD(Item item, XElement element) : base(item, element) { From db7d5539e05b40ab78296bcafb7e094be7b36dda Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Thu, 7 Dec 2017 20:20:05 +0200 Subject: [PATCH 6/6] Fixed modified clients being able to disconnect locked wires --- .../BarotraumaShared/Source/Characters/Character.cs | 3 +++ .../Source/Items/Components/Signal/ConnectionPanel.cs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 954cfd211..a506e62d1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -1059,6 +1059,9 @@ namespace Barotrauma Wire wire = item.GetComponent(); if (wire != null) { + //locked wires are never interactable + if (wire.Locked) return false; + //wires are interactable if the character has selected either of the items the wire is connected to if (wire.Connections[0]?.Item != null && selectedConstruction == wire.Connections[0].Item) return true; if (wire.Connections[1]?.Item != null && selectedConstruction == wire.Connections[1].Item) return true; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs index 9b47514de..7b25c669b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs @@ -212,6 +212,14 @@ namespace Barotrauma.Items.Components //existing wire not in the list of new wires -> disconnect it if (!wires[i].Contains(existingWire)) { + if (existingWire.Locked) + { + //this should not be possible unless the client is running a modified version of the game + GameServer.Log(c.Character.Name + " attempted to disconnect a locked wire from " + + Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Error); + continue; + } + existingWire.RemoveConnection(item); if (existingWire.Connections[0] == null && existingWire.Connections[1] == null)