using Barotrauma.Networking; using FarseerPhysics; using Lidgren.Network; using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Xml.Linq; namespace Barotrauma.Items.Components { class Holdable : Pickable, IServerSerializable { //the position(s) in the item that the Character grabs protected Vector2[] handlePos; private InputType prevPickKey; private string prevMsg; private List prevRequiredItems; //the distance from the holding characters elbow to center of the physics body of the item protected Vector2 holdPos; protected Vector2 aimPos; //protected bool aimable; private bool attachable, attached, attachedByDefault; private PhysicsBody body; //the angle in which the Character holds the item protected float holdAngle; [Serialize(false, true)] public bool Attached { get { return attached && item.ParentInventory == null; } set { attached = value; } } [Serialize(false, false)] public bool ControlPose { get; set; } [Serialize(false, false)] public bool Attachable { get { return attachable; } set { attachable = value; } } [Serialize(false, false)] public bool AttachedByDefault { get { return attachedByDefault; } set { attachedByDefault = value; } } [Serialize("0.0,0.0", false)] public Vector2 HoldPos { get { return ConvertUnits.ToDisplayUnits(holdPos); } set { holdPos = ConvertUnits.ToSimUnits(value); } } [Serialize("0.0,0.0", false)] public Vector2 AimPos { get { return ConvertUnits.ToDisplayUnits(aimPos); } set { aimPos = ConvertUnits.ToSimUnits(value); } } [Serialize(0.0f, false)] public float HoldAngle { get { return MathHelper.ToDegrees(holdAngle); } set { holdAngle = MathHelper.ToRadians(value); } } public Holdable(Item item, XElement element) : base(item, element) { body = item.body; handlePos = new Vector2[2]; for (int i = 1; i < 3; i++) { handlePos[i - 1] = element.GetAttributeVector2("handle" + i, Vector2.Zero); handlePos[i - 1] = ConvertUnits.ToSimUnits(handlePos[i - 1]); } canBePicked = true; if (attachable) { prevMsg = Msg; prevPickKey = PickKey; prevRequiredItems = new List(requiredItems); if (item.Submarine != null) { if (item.Submarine.Loading) { AttachToWall(); attached = false; } else //the submarine is not being loaded, which means we're either in the sub editor or the item has been spawned mid-round { if (Screen.Selected == GameMain.SubEditorScreen) { //in the sub editor, attach AttachToWall(); } else { //spawned mid-round, deattach DeattachFromWall(); } } } } } public override void Drop(Character dropper) { Drop(true, dropper); } private void Drop(bool dropConnectedWires, Character dropper) { if (dropConnectedWires) { DropConnectedWires(dropper); } if (attachable) { DeattachFromWall(); if (body != null) { item.body = body; } } if (item.body != null) item.body.Enabled = true; IsActive = false; if (picker == null) { if (dropper == null) return; picker = dropper; } if (picker.Inventory == null) return; item.Submarine = picker.Submarine; if (item.body != null) { item.body.ResetDynamics(); item.SetTransform(picker.SimPosition, 0.0f); } picker.DeselectItem(item); picker.Inventory.RemoveItem(item); picker = null; } public override void Equip(Character character) { picker = character; if (character != null) item.Submarine = character.Submarine; if (item.body == null) { if (body != null) { item.body = body; } else { return; } } if (!item.body.Enabled) { Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand); item.SetTransform(rightHand.SimPosition, 0.0f); } bool alreadySelected = character.HasEquippedItem(item); if (picker.TrySelectItem(item) || picker.HasEquippedItem(item)) { item.body.Enabled = true; IsActive = true; if (!alreadySelected) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction); } } public override void Unequip(Character character) { if (picker == null) return; picker.DeselectItem(item); GameServer.Log(character.LogName + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction); item.body.Enabled = false; IsActive = false; } public override bool Pick(Character picker) { if (!attachable) { return base.Pick(picker); } if (Attached) { return base.Pick(picker); } else { //not attached -> pick the item instantly, ignoring picking time return OnPicked(picker); } } public override bool OnPicked(Character picker) { if (base.OnPicked(picker)) { DeattachFromWall(); if (GameMain.Server != null && attachable) { item.CreateServerEvent(this); if (picker != null) { Networking.GameServer.Log(picker.LogName + " detached " + item.Name + " from a wall", ServerLog.MessageType.ItemInteraction); } } return true; } return false; } private void AttachToWall() { if (!attachable) return; var containedItems = item.ContainedItems; if (containedItems != null) { foreach (Item contained in containedItems) { if (contained.body == null) continue; contained.SetTransform(item.SimPosition, contained.body.Rotation); } } body.Enabled = false; item.body = null; Msg = prevMsg; PickKey = prevPickKey; requiredItems = new List(prevRequiredItems); attached = true; } private void DeattachFromWall() { if (!attachable) return; attached = false; //make the item pickable with the default pick key and with no specific tools/items when it's deattached requiredItems.Clear(); Msg = ""; PickKey = InputType.Select; } public override bool Use(float deltaTime, Character character = null) { if (!attachable || item.body == null) return true; if (character != null) { if (!character.IsKeyDown(InputType.Aim)) return false; if (character.CurrentHull == null) return false; if (GameMain.Server != null) { item.CreateServerEvent(this); GameServer.Log(character.LogName + " attached " + item.Name+" to a wall", ServerLog.MessageType.ItemInteraction); } item.Drop(); } AttachToWall(); return true; } public override void UpdateBroken(float deltaTime, Camera cam) { Update(deltaTime, cam); } public override void Update(float deltaTime, Camera cam) { if (item.body == null || !item.body.Enabled) return; if (picker == null || !picker.HasEquippedItem(item)) { IsActive = false; return; } ApplyStatusEffects(ActionType.OnActive, deltaTime, picker); if (item.body.Dir != picker.AnimController.Dir) Flip(item); item.Submarine = picker.Submarine; if (picker.HasSelectedItem(item)) { picker.AnimController.HoldItem(deltaTime, item, handlePos, holdPos, aimPos, picker.IsKeyDown(InputType.Aim), holdAngle); } else { Limb equipLimb = null; if (picker.Inventory.IsInLimbSlot(item, InvSlotType.Face) || picker.Inventory.IsInLimbSlot(item, InvSlotType.Head)) { equipLimb = picker.AnimController.GetLimb(LimbType.Head); } else if (picker.Inventory.IsInLimbSlot(item, InvSlotType.Torso)) { equipLimb = picker.AnimController.GetLimb(LimbType.Torso); } else if (picker.Inventory.IsInLimbSlot(item, InvSlotType.Legs)) { equipLimb = picker.AnimController.GetLimb(LimbType.Waist); } if (equipLimb != null) { float itemAngle = (equipLimb.Rotation + holdAngle * picker.AnimController.Dir); Matrix itemTransfrom = Matrix.CreateRotationZ(equipLimb.Rotation); Vector2 transformedHandlePos = Vector2.Transform(handlePos[0], itemTransfrom); item.body.ResetDynamics(); item.SetTransform(equipLimb.SimPosition - transformedHandlePos, itemAngle); } } } protected void Flip(Item item) { handlePos[0].X = -handlePos[0].X; handlePos[1].X = -handlePos[1].X; item.body.Dir = -item.body.Dir; } public override void OnItemLoaded() { if (item.Submarine != null && item.Submarine.Loading) return; OnMapLoaded(); } public override void OnMapLoaded() { if (!attachable) return; if (Attached) { AttachToWall(); } else { if (item.ParentInventory != null) { if (body != null) { item.body = body; body.Enabled = false; } } DeattachFromWall(); } } public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) { if (!attachable || body == null) { DebugConsole.ThrowError("Sent an attachment event for an item that's not attachable."); } msg.Write(Attached); msg.Write(body.SimPosition.X); msg.Write(body.SimPosition.Y); } public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { bool isAttached = msg.ReadBoolean(); Vector2 simPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat()); if (!attachable) { DebugConsole.ThrowError("Received an attachment event for an item that's not attachable."); return; } if (isAttached) { Drop(false, null); item.SetTransform(simPosition, 0.0f); AttachToWall(); } else { DropConnectedWires(null); if (body != null) { item.body = body; item.body.Enabled = true; } IsActive = false; DeattachFromWall(); } } } }