From f585235887610a7d89140f99a592558ce0608a7c Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 25 Mar 2016 15:34:40 +0200 Subject: [PATCH] Performing CPR on unconscious players, a "use on self" button for some items --- .../Source/Characters/AI/HumanAIController.cs | 2 +- .../AI/Objectives/AIObjectiveFindSafety.cs | 11 ++- .../Characters/Animation/AnimController.cs | 2 +- .../Animation/HumanoidAnimController.cs | 85 ++++++++++++++----- Subsurface/Source/Characters/Character.cs | 51 ++++++++--- Subsurface/Source/Characters/CharacterHUD.cs | 42 +++++++-- Subsurface/Source/Items/CharacterInventory.cs | 63 +++++++++++--- Subsurface/Source/Items/Inventory.cs | 2 +- Subsurface/Source/Items/Item.cs | 6 +- Subsurface/Source/Items/ItemPrefab.cs | 8 ++ Subsurface/Source/Items/RelatedItem.cs | 8 +- 11 files changed, 225 insertions(+), 55 deletions(-) diff --git a/Subsurface/Source/Characters/AI/HumanAIController.cs b/Subsurface/Source/Characters/AI/HumanAIController.cs index 34ecde408..07b194cb3 100644 --- a/Subsurface/Source/Characters/AI/HumanAIController.cs +++ b/Subsurface/Source/Characters/AI/HumanAIController.cs @@ -48,7 +48,7 @@ namespace Barotrauma public override void Update(float deltaTime) { - if (DisableCrewAI) return; + if (DisableCrewAI || Character.IsUnconscious) return; Character.ClearInputs(); diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 440723081..744559acc 100644 --- a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -44,7 +44,16 @@ namespace Barotrauma if (currenthullSafety > MinSafety) { - character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f); + if (Math.Abs(currentHull.WorldPosition.X - character.WorldPosition.X) > 100.0f) + { + character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f); + } + else + { + + character.AIController.SteeringManager.Reset(); + } + character.AIController.SelectTarget(null); goToObjective = null; diff --git a/Subsurface/Source/Characters/Animation/AnimController.cs b/Subsurface/Source/Characters/Animation/AnimController.cs index cc3d82144..7231ac355 100644 --- a/Subsurface/Source/Characters/Animation/AnimController.cs +++ b/Subsurface/Source/Characters/Animation/AnimController.cs @@ -58,6 +58,6 @@ namespace Barotrauma public virtual void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle) { } - public virtual void DragCharacter(Character target) { } + public virtual void DragCharacter(Character target, LimbType rightHandTarget = LimbType.RightHand, LimbType leftHandTarget = LimbType.LeftHand) { } } } diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index bfc4c5287..9d07a29df 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -19,6 +19,8 @@ namespace Barotrauma private float thighTorque; + private float cprAnimState; + protected override float HeadPosition { get @@ -210,16 +212,12 @@ namespace Barotrauma UpdateStanding(); break; case Animation.CPR: - if (character.SelectedCharacter == null) - { - Anim = Animation.None; - return; - } - - DragCharacter(character.SelectedCharacter); - + UpdateCPR(deltaTime); break; default: + + if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter); + if (inWater) UpdateSwimming(); else @@ -257,8 +255,6 @@ namespace Barotrauma Limb leftLeg = GetLimb(LimbType.LeftLeg); Limb rightLeg = GetLimb(LimbType.RightLeg); - - if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter); float getUpSpeed = 0.3f; float walkCycleSpeed = head.LinearVelocity.X * walkAnimSpeed; @@ -541,8 +537,6 @@ namespace Barotrauma Limb leftFoot = GetLimb(LimbType.LeftFoot); Limb rightFoot = GetLimb(LimbType.RightFoot); - Limb leftLeg = GetLimb(LimbType.LeftLeg); - Limb rightLeg = GetLimb(LimbType.RightLeg); float rotation = MathHelper.WrapAngle(torso.Rotation); rotation = MathHelper.ToDegrees(rotation); @@ -716,8 +710,6 @@ namespace Barotrauma //} movement = MathUtils.SmoothStep(movement, tempTargetMovement, 0.3f); - Vector2 footPos, handPos; - Limb leftFoot = GetLimb(LimbType.LeftFoot); Limb rightFoot = GetLimb(LimbType.RightFoot); Limb head = GetLimb(LimbType.Head); @@ -744,7 +736,7 @@ namespace Barotrauma MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, waist.SimPosition.Y), 10.5f); - handPos = new Vector2( + Vector2 handPos = new Vector2( ladderSimPos.X, head.SimPosition.Y + 0.0f + movement.Y * 0.1f - ladderSimPos.Y); @@ -761,7 +753,7 @@ namespace Barotrauma leftHand.body.ApplyTorque(Dir * 2.0f); rightHand.body.ApplyTorque(Dir * 2.0f); - footPos = new Vector2( + Vector2 footPos = new Vector2( handPos.X - Dir * 0.05f, head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); @@ -829,6 +821,46 @@ namespace Barotrauma } + private void UpdateCPR(float deltaTime) + { + if (character.SelectedCharacter == null) + { + Anim = Animation.None; + return; + } + + Crouching = true; + + Vector2 diff = character.SelectedCharacter.SimPosition - character.SimPosition; + var targetHead = character.SelectedCharacter.AnimController.GetLimb(LimbType.Head); + + Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition; + + targetMovement = new Vector2(diff.X, 0.0f); + TargetDir = headDiff.X > 0.0f ? Direction.Right : Direction.Left; + + UpdateStanding(); + + Vector2 handPos = character.SelectedCharacter.AnimController.GetLimb(LimbType.Torso).SimPosition + Vector2.UnitY*0.2f; + + Grab(handPos, handPos); + + float yPos = (float)Math.Sin(cprAnimState) * 0.1f; + cprAnimState += deltaTime*8.0f; + + var head = GetLimb(LimbType.Head); + head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.6f + yPos); + head.pullJoint.Enabled = true; + + + //RefLimb.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X - Math.Sign(headDiff.X) * 0.5f, targetHead.SimPosition.Y + 0.4f + yPos); + //head.pullJoint.Enabled = true; + + + + //DragCharacter(character.SelectedCharacter, LimbType.Torso, LimbType.Head); + } + //float punchTimer; //bool punching; @@ -876,7 +908,7 @@ namespace Barotrauma // } //} - public override void DragCharacter(Character target) + public override void DragCharacter(Character target, LimbType rightHandTarget = LimbType.RightHand, LimbType leftHandTarget = LimbType.LeftHand) { if (target == null) return; @@ -888,10 +920,10 @@ namespace Barotrauma for (int i = 0; i < 2; i++ ) { - LimbType type = i == 0 ? LimbType.RightHand : LimbType.LeftHand; + LimbType type = i == 0 ? rightHandTarget : leftHandTarget; Limb targetLimb = target.AnimController.GetLimb(type); - - Limb pullLimb = GetLimb(type); + + Limb pullLimb = GetLimb(i == 0 ? LimbType.RightHand : LimbType.LeftHand); pullLimb.pullJoint.Enabled = true; pullLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition; @@ -912,7 +944,20 @@ namespace Barotrauma { target.AnimController.TargetMovement = Vector2.Lerp(target.AnimController.TargetMovement, (character.SimPosition + Vector2.UnitX*Dir) - target.SimPosition, 0.5f); } + } + public void Grab(Vector2 rightHandPos, Vector2 leftHandPos) + { + for (int i = 0; i < 2; i++) + { + Limb pullLimb = (i == 0) ? GetLimb(LimbType.LeftHand) : GetLimb(LimbType.RightHand); + + pullLimb.Disabled = true; + + pullLimb.pullJoint.Enabled = true; + pullLimb.pullJoint.WorldAnchorB = (i==0) ? rightHandPos : leftHandPos; + pullLimb.pullJoint.MaxForce = 500.0f; + } } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 91fc5539a..b0a65ac6c 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -244,7 +244,7 @@ namespace Barotrauma { get { return needsAir; } } - + public float Oxygen { get { return oxygen; } @@ -670,7 +670,8 @@ namespace Barotrauma if (AnimController.onGround && !AnimController.InWater && - AnimController.Anim != AnimController.Animation.UsingConstruction) + AnimController.Anim != AnimController.Animation.UsingConstruction && + AnimController.Anim != AnimController.Animation.CPR) { Limb head = AnimController.GetLimb(LimbType.Head); @@ -831,10 +832,6 @@ namespace Barotrauma /// public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true) { - Limb head = AnimController.GetLimb(LimbType.Head); - - //Lights.LightManager.ViewPos = WorldPosition; - if (!DisableControls) { for (int i = 0; i < keys.Length; i++ ) @@ -1039,7 +1036,12 @@ namespace Barotrauma return; } - if (controlled==this || !(this is AICharacter)) Control(deltaTime, cam); + if (controlled == this || !(this is AICharacter)) Control(deltaTime, cam); + + if (selectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR) + { + if (GameMain.Client == null) selectedCharacter.Oxygen += (GetSkillLevel("Medical") / 10.0f) * deltaTime; + } UpdateSightRange(); if (aiTarget != null) aiTarget.SoundRange = 0.0f; @@ -1422,6 +1424,7 @@ namespace Barotrauma return true; case NetworkEventType.SelectCharacter: + message.Write(AnimController.Anim == AnimController.Animation.CPR); message.Write((ushort)data); return true; case NetworkEventType.KillCharacter: @@ -1482,6 +1485,9 @@ namespace Barotrauma message.Write(keys[(int)InputType.Down].Held); message.Write(keys[(int)InputType.Run].Held); + + message.Write(keys[(int)InputType.Crouch].Held); + if (secondaryHeld) { @@ -1564,18 +1570,35 @@ namespace Barotrauma return; case NetworkEventType.SelectCharacter: + bool performingCPR = message.ReadBoolean(); + ushort characterId = message.ReadUInt16(); data = characterId; if (characterId==0) { DeselectCharacter(false); + return; } - else + + Character character = FindEntityByID(characterId) as Character; + if (character == null || !character.IsHumanoid) return; + + SelectCharacter(character, false); + if (performingCPR) { - Character character = FindEntityByID(characterId) as Character; - if (character != null && character.IsHumanoid) SelectCharacter(character, false); + AnimController.Anim = AnimController.Animation.CPR; + + foreach (Limb limb in selectedCharacter.AnimController.Limbs) + { + limb.pullJoint.Enabled = false; + } } + else if (AnimController.Anim == AnimController.Animation.CPR) + { + AnimController.Anim = AnimController.Animation.None; + } + return; case NetworkEventType.KillCharacter: if (GameMain.Server != null) @@ -1641,7 +1664,7 @@ namespace Barotrauma bool actionKeyState, secondaryKeyState; bool leftKeyState, rightKeyState, upKeyState, downKeyState; - bool runState; + bool runState, crouchState; try { @@ -1656,6 +1679,7 @@ namespace Barotrauma downKeyState = message.ReadBoolean(); runState = message.ReadBoolean(); + crouchState = message.ReadBoolean(); } catch (Exception e) @@ -1680,7 +1704,10 @@ namespace Barotrauma keys[(int)InputType.Up].Held = upKeyState; keys[(int)InputType.Down].Held = downKeyState; - keys[(int)InputType.Run].Held = runState; + keys[(int)InputType.Run].Held = runState; + + keys[(int)InputType.Crouch].Held = crouchState; + float dir = 1.0f; Vector2 pos = Vector2.Zero; diff --git a/Subsurface/Source/Characters/CharacterHUD.cs b/Subsurface/Source/Characters/CharacterHUD.cs index 72d9fbd10..4b22da7c3 100644 --- a/Subsurface/Source/Characters/CharacterHUD.cs +++ b/Subsurface/Source/Characters/CharacterHUD.cs @@ -1,9 +1,7 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Networking; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Barotrauma { @@ -13,6 +11,8 @@ namespace Barotrauma private static Sprite noiseOverlay, damageOverlay; + private static GUIButton cprButton; + private static GUIProgressBar drowningBar, healthBar; private static float damageOverlayTimer; @@ -33,6 +33,8 @@ namespace Barotrauma } if (healthBar != null) healthBar.Update(deltaTime); + if (cprButton != null && cprButton.Visible) cprButton.Update(deltaTime); + if (damageOverlayTimer > 0.0f) damageOverlayTimer -= deltaTime; } @@ -55,11 +57,39 @@ namespace Barotrauma DrawStatusIcons(spriteBatch, character); - if (character.Inventory != null && !character.LockHands) character.Inventory.DrawOwn(spriteBatch, Vector2.Zero); + if (character.Inventory != null && !character.LockHands && + !character.IsUnconscious && character.Stun >= -0.1f) character.Inventory.DrawOwn(spriteBatch, Vector2.Zero); if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory!=null) { character.SelectedCharacter.Inventory.DrawOwn(spriteBatch, new Vector2(320.0f, 0.0f)); + + if (cprButton == null) + { + cprButton = new GUIButton( + new Rectangle(character.SelectedCharacter.Inventory.SlotPositions[0].ToPoint() + new Point(150+320, 0), new Point(130, 20)), "Perform CPR", GUI.Style); + + cprButton.OnClicked = (button, userData) => + { + if (Character.Controlled == null || Character.Controlled.SelectedCharacter == null) return false; + + Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ? + AnimController.Animation.None : AnimController.Animation.CPR; + + foreach (Limb limb in Character.Controlled.SelectedCharacter.AnimController.Limbs) + { + limb.pullJoint.Enabled = false; + } + + new NetworkEvent(NetworkEventType.SelectCharacter, Character.Controlled.ID, true, Character.Controlled.SelectedCharacter); + + return true; + }; + } + + //cprButton.Visible = character.GetSkillLevel("Medical") > 20.0f; + + if (cprButton.Visible) cprButton.Draw(spriteBatch); } if (character.ClosestCharacter != null && character.ClosestCharacter.CanBeSelected) @@ -76,7 +106,7 @@ namespace Barotrauma { var hudTexts = character.ClosestItem.GetHUDTexts(character); - Vector2 startPos = new Vector2((int)(GameMain.GraphicsWidth / 2.0f), (int)(GameMain.GraphicsHeight)); + Vector2 startPos = new Vector2((int)(GameMain.GraphicsWidth / 2.0f), GameMain.GraphicsHeight); startPos.Y -= 50 + hudTexts.Count * 25; Vector2 textPos = startPos; diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index 82646c61e..5f25a8b44 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -27,11 +27,15 @@ namespace Barotrauma public Vector2[] SlotPositions; + private GUIButton[] useOnSelfButton; + public CharacterInventory(int capacity, Character character) : base(character, capacity) { this.character = character; + useOnSelfButton = new GUIButton[2]; + if (icons == null) icons = TextureLoader.FromFile("Content/UI/inventoryIcons.png"); SlotPositions = new Vector2[limbSlots.Length]; @@ -56,6 +60,16 @@ namespace Barotrauma SlotPositions[i] = new Vector2( spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 3), GameMain.GraphicsHeight - (spacing + rectHeight)*3); + + useOnSelfButton[i - 3] = new GUIButton( + new Rectangle((int) SlotPositions[i].X, (int) (SlotPositions[i].Y - spacing - rectHeight), + rectWidth, rectHeight), "Use", GUI.Style) + { + UserData = i, + OnClicked = UseItemOnSelf + }; + + break; default: SlotPositions[i] = new Vector2( @@ -66,6 +80,19 @@ namespace Barotrauma } } + private bool UseItemOnSelf(GUIButton button, object obj) + { + if (!(obj is int)) return false; + + int slotIndex = (int)obj; + + if (Items[slotIndex] == null) return false; + + Items[slotIndex].ApplyStatusEffects(ActionType.OnUse, 0.016f, character); + + return true; + } + protected override void DropItem(Item item) { bool enabled = item.body!=null && item.body.Enabled; @@ -240,13 +267,14 @@ namespace Barotrauma string toolTip = ""; Rectangle highlightedSlot = Rectangle.Empty; + if (doubleClickedItem!=null && doubleClickedItem.ParentInventory!=this) { TryPutItem(doubleClickedItem, doubleClickedItem.AllowedSlots, true); } doubleClickedItem = null; - int rectWidth = 40, rectHeight = 40; + const int rectWidth = 40, rectHeight = 40; Rectangle slotRect = new Rectangle(0, 0, rectWidth, rectHeight); Rectangle draggingItemSlot = slotRect; @@ -340,15 +368,28 @@ namespace Barotrauma } } - if (!multiSlot) continue; - if (Items[i] != null && slotRect.Contains(PlayerInput.MousePosition)) + + if (multiSlot) { - toolTip = string.IsNullOrEmpty(Items[i].Description) ? Items[i].Name : Items[i].Name + '\n' + Items[i].Description; - highlightedSlot = slotRect; + if (Items[i] != null && slotRect.Contains(PlayerInput.MousePosition)) + { + toolTip = string.IsNullOrEmpty(Items[i].Description) ? Items[i].Name : Items[i].Name + '\n' + Items[i].Description; + highlightedSlot = slotRect; + } + + UpdateSlot(spriteBatch, slotRect, i, Items[i], i > 4); } - UpdateSlot(spriteBatch, slotRect, i, Items[i], i > 4); + + if (character==Character.Controlled && selectedSlot != i && + Items[i] != null && Items[i].CanUseOnSelf && character.HasSelectedItem(Items[i])) + { + useOnSelfButton[i - 3].Update(0.016f); + useOnSelfButton[i - 3].Draw(spriteBatch); + } + + } slotRect.Width = rectWidth; @@ -359,7 +400,9 @@ namespace Barotrauma DrawToolTip(spriteBatch, toolTip, highlightedSlot); } - if (draggingItem != null && !draggingItemSlot.Contains(PlayerInput.MousePosition)) + if (draggingItem == null) return; + + if (!draggingItemSlot.Contains(PlayerInput.MousePosition)) { if (PlayerInput.LeftButtonHeld()) { @@ -372,10 +415,10 @@ namespace Barotrauma { DropItem(draggingItem); - new Networking.NetworkEvent(Barotrauma.Networking.NetworkEventType.DropItem, draggingItem.ID, true); + new NetworkEvent(NetworkEventType.DropItem, draggingItem.ID, true); //draggingItem = null; - } - } + } + } } public override bool FillNetworkData(NetworkEventType type, NetBuffer message, object data) diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index 0cc646934..ef220341f 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -241,7 +241,7 @@ namespace Barotrauma Vector2 textSize = GUI.Font.MeasureString(toolTip); Vector2 rectSize = textSize * 1.2f; - Vector2 pos = new Vector2(highlightedSlot.Center.X, highlightedSlot.Y-rectSize.Y); + Vector2 pos = new Vector2(highlightedSlot.Right, highlightedSlot.Y-rectSize.Y); pos.X = (int)pos.X; pos.Y = (int)pos.Y; diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 82c65df84..5615b91b3 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -189,6 +189,11 @@ namespace Barotrauma get { return prefab.FireProof; } } + public bool CanUseOnSelf + { + get { return prefab.CanUseOnSelf; } + } + public bool InWater { get @@ -1056,7 +1061,6 @@ namespace Barotrauma public bool Pick(Character picker, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceActionKey=false) { - bool hasRequiredSkills = true; bool picked = false, selected = false; diff --git a/Subsurface/Source/Items/ItemPrefab.cs b/Subsurface/Source/Items/ItemPrefab.cs index 6f7d92597..f33ba443c 100644 --- a/Subsurface/Source/Items/ItemPrefab.cs +++ b/Subsurface/Source/Items/ItemPrefab.cs @@ -87,6 +87,12 @@ namespace Barotrauma get { return offsetOnSelected; } } + public bool CanUseOnSelf + { + get; + private set; + } + public Vector2 Size { get { return size; } @@ -204,6 +210,8 @@ namespace Barotrauma focusOnSelected = ToolBox.GetAttributeBool(element, "focusonselected", false); offsetOnSelected = ToolBox.GetAttributeFloat(element, "offsetonselected", 0.0f); + + CanUseOnSelf = ToolBox.GetAttributeBool(element, "canuseonself", false); FireProof = ToolBox.GetAttributeBool(element, "fireproof", false); diff --git a/Subsurface/Source/Items/RelatedItem.cs b/Subsurface/Source/Items/RelatedItem.cs index c0c9db549..9f666c1b7 100644 --- a/Subsurface/Source/Items/RelatedItem.cs +++ b/Subsurface/Source/Items/RelatedItem.cs @@ -77,9 +77,13 @@ namespace Barotrauma { case RelationType.Contained: if (parentItem == null) return false; - foreach (Item contained in parentItem.ContainedItems) + + var containedItems = parentItem.ContainedItems; + if (containedItems == null) return false; + + foreach (Item contained in containedItems) { - if (contained.Condition>0.0f && MatchesItem(contained)) return true; + if (contained.Condition > 0.0f && MatchesItem(contained)) return true; } break; case RelationType.Equipped: