diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index 5fa47c8fb..17f75eb98 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -13,7 +13,7 @@ namespace Barotrauma { float volume = Math.Min(impact - 3.0f, 1.0f); - if (body.UserData is Limb) + if (body.UserData is Limb && character.Stun <= 0f) { Limb limb = (Limb)body.UserData; @@ -23,7 +23,7 @@ namespace Barotrauma limb.HitSound.Play(volume, impact * 100.0f, limb.WorldPosition); } } - else if (body == Collider.FarseerBody) + else if (body.UserData is Limb || body == Collider.FarseerBody) { if (!character.IsRemotePlayer || GameMain.Server != null) { diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs index 2ca2c5f43..ec44d08af 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs @@ -109,6 +109,10 @@ namespace Barotrauma TransformCursorPos(); } + bool ragdollInput = msg.ReadBoolean(); + keys[(int)InputType.Ragdoll].Held = ragdollInput; + keys[(int)InputType.Ragdoll].SetState(false, ragdollInput); + facingRight = msg.ReadBoolean(); } @@ -123,7 +127,7 @@ namespace Barotrauma if (selectedEntity is Character) { bool doingCpr = msg.ReadBoolean(); - if (doingCpr && selectedCharacter != null) + if (doingCpr && SelectedCharacter != null) { animation = AnimController.Animation.CPR; } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index 6114b2e7a..e7f481e0e 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -259,14 +259,14 @@ namespace Barotrauma { if (character == null) return false; - GUIComponent existingFrame = crewList.Parent.FindChild("selectedcharacter"); + GUIComponent existingFrame = crewList.Parent.FindChild("SelectedCharacter"); if (existingFrame != null) crewList.Parent.RemoveChild(existingFrame); var previewPlayer = new GUIFrame( new Rectangle(0, 0, 230, 300), new Color(0.0f, 0.0f, 0.0f, 0.8f), Alignment.TopRight, "", crewList.Parent); previewPlayer.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); - previewPlayer.UserData = "selectedcharacter"; + previewPlayer.UserData = "SelectedCharacter"; character.Info.CreateInfoFrame(previewPlayer); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index 243de5af4..9c9a70b4a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -237,7 +237,7 @@ namespace Barotrauma float getUpSpeed = 0.3f; float walkCycleSpeed = movement.X * walkAnimSpeed; - if (stairs != null) + if (Stairs != null) { TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.5f, 1.5f), TargetMovement.Y); @@ -328,7 +328,7 @@ namespace Barotrauma float floorPos = GetFloorY(colliderPos + new Vector2(Math.Sign(movement.X) * 0.5f, 1.0f)); bool onSlope = floorPos > GetColliderBottom().Y + 0.05f; - if (stairs != null || onSlope) + if (Stairs != null || onSlope) { torso.pullJoint.WorldAnchorB = new Vector2( MathHelper.SmoothStep(torso.SimPosition.X, footMid + movement.X * 0.25f, getUpSpeed * 0.8f), @@ -382,7 +382,7 @@ namespace Barotrauma Vector2 footPos = stepSize * -i; if (stepSize.Y < 0.0f) stepSize.Y = -0.15f; - if (onSlope && stairs == null) + if (onSlope && Stairs == null) { footPos.Y *= 2.0f; } @@ -462,7 +462,7 @@ namespace Barotrauma footPos = new Vector2(GetCenterOfMass().X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f); } - if (stairs == null) + if (Stairs == null) { footPos.Y = Math.Max(Math.Min(floorPos, footPos.Y + 0.5f), footPos.Y); } @@ -987,7 +987,7 @@ namespace Barotrauma } //if on stairs, make the dragged character "climb up" (= collide with stairs) - if (stairs != null) target.AnimController.TargetMovement = new Vector2 (target.AnimController.TargetMovement.X, 1.0f); + //if (Stairs != null) target.AnimController.TargetMovement = new Vector2 (target.AnimController.TargetMovement.X, 1.0f); } public void Grab(Vector2 rightHandPos, Vector2 leftHandPos) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index 92579ee7c..2feb08dd3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -67,7 +67,7 @@ namespace Barotrauma protected float colliderHeightFromFloor; - protected Structure stairs; + public Structure Stairs; protected Direction dir; @@ -452,7 +452,7 @@ namespace Barotrauma } else if (structure.StairDirection != Direction.None) { - stairs = null; + Stairs = null; //don't collider with stairs if @@ -475,8 +475,15 @@ namespace Barotrauma if (inWater && targetMovement.Y < 0.5f) return false; //--------------- - - stairs = structure; + + //set stairs to that of the one dragging us + if (character.SelectedBy != null) + Stairs = character.SelectedBy.AnimController.Stairs; + else + Stairs = structure; + + if (Stairs == null) + return false; } CalculateImpact(f1, f2, contact); @@ -504,10 +511,7 @@ namespace Barotrauma ImpactProjSpecific(impact,f1.Body); - if (f1.Body.UserData is Limb) - { - } - else if (f1.Body == Collider.FarseerBody) + if ((f1.Body.UserData is Limb && character.Stun > 0f) || f1.Body == Collider.FarseerBody) { if (!character.IsRemotePlayer || GameMain.Server != null) { @@ -898,8 +902,8 @@ namespace Barotrauma limb.Update(deltaTime); } - bool onStairs = stairs != null; - stairs = null; + bool onStairs = Stairs != null; + Stairs = null; var contacts = Collider.FarseerBody.ContactList; while (Collider.FarseerBody.Enabled && contacts != null && contacts.Contact != null) @@ -917,7 +921,7 @@ namespace Barotrauma Structure structure = contacts.Contact.FixtureA.Body.UserData as Structure; if (structure != null && onStairs) { - stairs = structure; + Stairs = structure; } break; } @@ -966,7 +970,7 @@ namespace Barotrauma rayEnd.Y -= Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor*1.2f; Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom()); - if (!inWater && !character.IsDead && !character.IsUnconscious && levitatingCollider && Collider.LinearVelocity.Y>-ImpactTolerance) + if (!inWater && !character.IsDead && character.Stun <= 0f && levitatingCollider && Collider.LinearVelocity.Y>-ImpactTolerance) { float closestFraction = 1.0f; Fixture closestFixture = null; @@ -978,6 +982,7 @@ namespace Barotrauma Structure structure = fixture.Body.UserData as Structure; if (inWater && targetMovement.Y < 0.5f) return -1; if (colliderBottomDisplay.Y < structure.Rect.Y - structure.Rect.Height + 30 && TargetMovement.Y < 0.5f) return -1; + if (character.SelectedBy != null) return -1; break; case Physics.CollisionPlatform: Structure platform = fixture.Body.UserData as Structure; @@ -1007,7 +1012,7 @@ namespace Barotrauma switch (closestFixture.CollisionCategories) { case Physics.CollisionStairs: - stairs = closestFixture.Body.UserData as Structure; + Stairs = closestFixture.Body.UserData as Structure; onStairs = true; forceImmediate = true; break; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 40ba7328f..b38a85f58 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -13,9 +13,9 @@ namespace Barotrauma partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerSerializable { public static List CharacterList = new List(); - + public static bool DisableControls; - + private bool enabled = true; public bool Enabled { @@ -68,7 +68,7 @@ namespace Barotrauma } protected Key[] keys; - + private Item selectedConstruction; private Item[] selectedItems; @@ -94,12 +94,12 @@ namespace Barotrauma protected float minHealth, maxHealth; protected Item focusedItem; - private Character focusedCharacter, selectedCharacter; + private Character focusedCharacter, selectedCharacter, selectedBy; private bool isDead; private CauseOfDeath lastAttackCauseOfDeath; private CauseOfDeath causeOfDeath; - + public readonly bool IsHumanoid; //the name of the species (e.q. human) @@ -113,16 +113,16 @@ namespace Barotrauma { get; set; - } + } private CharacterInfo info; public CharacterInfo Info { get - { + { return info; } - set + set { if (info != null && info != value) info.Remove(); @@ -154,7 +154,7 @@ namespace Barotrauma { get { return inventory; } } - + private Color speechBubbleColor; private float speechBubbleTimer; @@ -184,8 +184,8 @@ namespace Barotrauma public Vector2 CursorPosition { get { return cursorPosition; } - set - { + set + { if (!MathUtils.IsValid(value)) return; cursorPosition = value; } @@ -204,6 +204,27 @@ namespace Barotrauma public Character SelectedCharacter { get { return selectedCharacter; } + set + { + if (selectedCharacter != null) + selectedCharacter.selectedBy = null; + selectedCharacter = value; + if (selectedCharacter != null) + selectedCharacter.selectedBy = this; + } + } + + public Character SelectedBy + { + get { return selectedBy; } + set + { + if (selectedBy != null) + selectedBy.selectedCharacter = null; + selectedBy = value; + if (selectedBy != null) + selectedBy.selectedCharacter = this; + } } private float lowPassMultiplier; @@ -246,6 +267,9 @@ namespace Barotrauma } } + public bool IsRagdolled; + public bool IsForceRagdolled; + public bool IsUnconscious { get { return (needsAir && oxygen <= 0.0f) || health <= 0.0f; } @@ -661,6 +685,8 @@ namespace Barotrauma return dequeuedInput.HasFlag(InputNetFlags.Select); //TODO: clean up the way this input is registered case InputType.Use: return !(dequeuedInput.HasFlag(InputNetFlags.Use)) && (prevDequeuedInput.HasFlag(InputNetFlags.Use)); + case InputType.Ragdoll: + return !(dequeuedInput.HasFlag(InputType.Ragdoll)) && (prevDequeuedInput.HasFlag(InputType.Ragdoll)); default: return false; } @@ -695,6 +721,8 @@ namespace Barotrauma return dequeuedInput.HasFlag(InputNetFlags.Use); case InputType.Attack: return dequeuedInput.HasFlag(InputNetFlags.Attack); + case InputType.Ragdoll: + return dequeuedInput.HasFlag(InputNetFlags.Ragdoll); } return false; } @@ -765,7 +793,7 @@ namespace Barotrauma // - dragging someone // - crouching // - moving backwards - if (selectedCharacter == null && + if (SelectedCharacter == null && (!(AnimController is HumanoidAnimController) || !((HumanoidAnimController)AnimController).Crouching) && Math.Sign(targetMovement.X) != -Math.Sign(AnimController.Dir)) { @@ -912,9 +940,9 @@ namespace Barotrauma if (selectedConstruction != null && IsKeyDown(InputType.Aim)) selectedConstruction.SecondaryUse(deltaTime, this); } - if (selectedCharacter != null) + if (SelectedCharacter != null) { - if (Vector2.DistanceSquared(selectedCharacter.WorldPosition, WorldPosition) > 90000.0f || !selectedCharacter.CanBeSelected) + if (Vector2.DistanceSquared(SelectedCharacter.WorldPosition, WorldPosition) > 90000.0f || !SelectedCharacter.CanBeSelected) { DeselectCharacter(); } @@ -986,7 +1014,7 @@ namespace Barotrauma var owner = (Character)inventory.Owner; //can only be accessed if the character is incapacitated and has been selected - return selectedCharacter == owner && (!owner.CanInteract); + return SelectedCharacter == owner && (!owner.CanInteract); } if (inventory.Owner is Item) @@ -1215,22 +1243,22 @@ namespace Barotrauma { if (character == null) return; - selectedCharacter = character; + SelectedCharacter = character; } public void DeselectCharacter() { - if (selectedCharacter == null) return; + if (SelectedCharacter == null) return; if (SelectedCharacter.AnimController != null) { - foreach (Limb limb in selectedCharacter.AnimController.Limbs) + foreach (Limb limb in SelectedCharacter.AnimController.Limbs) { if (limb.pullJoint != null) limb.pullJoint.Enabled = false; } } - selectedCharacter = null; + SelectedCharacter = null; } public void DoInteractionUpdate(float deltaTime, Vector2 mouseSimPos) @@ -1243,7 +1271,7 @@ namespace Barotrauma if (!CanInteract) { - if (selectedCharacter != null) + if (SelectedCharacter != null) { DeselectCharacter(); } @@ -1276,7 +1304,7 @@ namespace Barotrauma findFocusedTimer -= deltaTime; } - if (selectedCharacter != null && IsKeyHit(InputType.Select)) + if (SelectedCharacter != null && IsKeyHit(InputType.Select)) { DeselectCharacter(); } @@ -1435,7 +1463,22 @@ namespace Barotrauma UpdateUnconscious(deltaTime); return; } - + + if (IsForceRagdolled) + IsRagdolled = IsForceRagdolled; + else if (!IsRagdolled || AnimController.Collider.LinearVelocity.Length() < 1f) //Keep us ragdolled if we were forced or we're too speedy to unragdoll + IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves + + if (IsRagdolled) + { + ((HumanoidAnimController)AnimController).Crouching = false; + Stun = Math.Max(0.1f, Stun); + + AnimController.ResetPullJoints(); + selectedConstruction = null; + return; + } + Control(deltaTime, cam); if (controlled != this && (!(this is AICharacter) || IsRemotePlayer)) { @@ -1448,9 +1491,9 @@ namespace Barotrauma selectedConstruction = null; } - if (selectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR) + if (SelectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR) { - if (GameMain.Client == null) selectedCharacter.Oxygen += (GetSkillLevel("Medical") / 10.0f) * deltaTime; + if (GameMain.Client == null) SelectedCharacter.Oxygen += (GetSkillLevel("Medical") / 10.0f) * deltaTime; } UpdateSightRange(); @@ -1839,7 +1882,7 @@ namespace Barotrauma foreach (Character c in CharacterList) { if (c.focusedCharacter == this) c.focusedCharacter = null; - if (c.selectedCharacter == this) c.selectedCharacter = null; + if (c.SelectedCharacter == this) c.SelectedCharacter = null; } } partial void DisposeProjSpecific(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs index bd6d8c1c8..5abc8331e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterNetworking.cs @@ -52,8 +52,9 @@ namespace Barotrauma Use = 0x100, Aim = 0x200, Attack = 0x400, + Ragdoll = 0x800, - MaxVal = 0x7FF + MaxVal = 0xFFF } private InputNetFlags dequeuedInput = 0; private InputNetFlags prevDequeuedInput = 0; @@ -192,7 +193,7 @@ namespace Barotrauma SimPosition, LastNetworkUpdateID, AnimController.TargetDir, - selectedCharacter == null ? (Entity)selectedConstruction : (Entity)selectedCharacter, + SelectedCharacter == null ? (Entity)selectedConstruction : (Entity)SelectedCharacter, AnimController.Anim); memLocalState.Add(posInfo); @@ -208,6 +209,7 @@ namespace Barotrauma if (IsKeyDown(InputType.Use)) newInput |= InputNetFlags.Use; if (IsKeyDown(InputType.Aim)) newInput |= InputNetFlags.Aim; if (IsKeyDown(InputType.Attack)) newInput |= InputNetFlags.Attack; + if (IsKeyDown(InputType.Ragdoll)) newInput |= InputNetFlags.Ragdoll; if (AnimController.TargetDir == Direction.Left) newInput |= InputNetFlags.FacingLeft; @@ -438,15 +440,16 @@ namespace Barotrauma Vector2 relativeCursorPos = cursorPosition - (ViewTarget == null ? AnimController.AimSourcePos : ViewTarget.Position); tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI))); } + tempBuffer.Write(IsRagdolled); tempBuffer.Write(AnimController.TargetDir == Direction.Right); } - if (selectedCharacter != null || selectedConstruction != null) + if (SelectedCharacter != null || selectedConstruction != null) { tempBuffer.Write(true); - tempBuffer.Write(selectedCharacter != null ? selectedCharacter.ID : selectedConstruction.ID); - if (selectedCharacter != null) + tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : selectedConstruction.ID); + if (SelectedCharacter != null) { tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR); } diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index 9c1fd0d0c..15b2e7ffa 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -658,6 +658,24 @@ namespace Barotrauma if (Character.Controlled != null) Character.Controlled.AnimController.Frozen = !Character.Controlled.AnimController.Frozen; })); + 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) + { + ragdolledCharacter = Character.Controlled; + } + else + { + ragdolledCharacter = FindMatchingCharacter(args); + } + + if (ragdolledCharacter != null) + { + ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled; + } + })); + commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) => { Character.Controlled = null; diff --git a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index 6c7938757..93d14d3f2 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -11,7 +11,8 @@ namespace Barotrauma Up, Down, Left, Right, Attack, Run, Crouch, - Chat, RadioChat, CrewOrders + Chat, RadioChat, CrewOrders, + Ragdoll } public class KeyOrMouse diff --git a/Barotrauma/BarotraumaShared/config.xml b/Barotrauma/BarotraumaShared/config.xml index 336ba9c4c..ec9028ff6 100644 --- a/Barotrauma/BarotraumaShared/config.xml +++ b/Barotrauma/BarotraumaShared/config.xml @@ -2,7 +2,7 @@ - + />