using Barotrauma.Networking; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Xml.Linq; namespace Barotrauma.Items.Components { partial class Rope : ItemComponent, IDrawableComponent { private Sprite sprite, startSprite, endSprite; [Serialize(5, IsPropertySaveable.No)] public int SpriteWidth { get; set; } [Serialize("255,255,255,255", IsPropertySaveable.No)] public Color SpriteColor { get; set; } [Serialize(false, IsPropertySaveable.No)] public bool Tile { get; set; } [Serialize("0.5,0.5)", IsPropertySaveable.No)] public Vector2 Origin { get; set; } = new Vector2(0.5f, 0.5f); [Serialize(true, IsPropertySaveable.No, description: "")] public bool BreakFromMiddle { get; set; } public Vector2 DrawSize { get { if (target == null || source == null) { return Vector2.Zero; } Vector2 sourcePos = GetSourcePos(); return new Vector2( Math.Abs(target.DrawPosition.X - sourcePos.X), Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 1.5f; } } private Vector2 GetSourcePos() { Vector2 sourcePos = source.WorldPosition; if (source is Item sourceItem) { sourcePos = sourceItem.DrawPosition; } else if (source is Limb sourceLimb && sourceLimb.body != null) { sourcePos = sourceLimb.body.DrawPosition; } return sourcePos; } partial void InitProjSpecific(ContentXElement element) { foreach (var subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "sprite": sprite = new Sprite(subElement); break; case "startsprite": startSprite = new Sprite(subElement); break; case "endsprite": endSprite = new Sprite(subElement); break; } } } public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) { if (target == null || target.Removed) { return; } if (target.ParentInventory != null) { return; } if (source is Limb limb && limb.Removed) { return; } if (source is Entity e && e.Removed) { return; } Vector2 startPos = GetSourcePos(); startPos.Y = -startPos.Y; if (source is Item sourceItem && !sourceItem.Removed) { var turret = sourceItem.GetComponent(); var weapon = sourceItem.GetComponent(); if (turret != null) { startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y)); if (turret.BarrelSprite != null) { startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f; } } else if (weapon != null) { Vector2 barrelPos = FarseerPhysics.ConvertUnits.ToDisplayUnits(weapon.TransformedBarrelPos); barrelPos.Y = -barrelPos.Y; startPos += barrelPos; } } Vector2 endPos = new Vector2(target.DrawPosition.X, target.DrawPosition.Y); Vector2 flippedPos = target.Sprite.size * target.Scale * (Origin - new Vector2(0.5f)); if (target.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; } endPos += Vector2.Transform(flippedPos, Matrix.CreateRotationZ(target.body.Rotation)); endPos.Y = -endPos.Y; if (Snapped) { float snapState = 1.0f - snapTimer / SnapAnimDuration; Vector2 diff = target.DrawPosition - new Vector2(startPos.X, -startPos.Y); diff.Y = -diff.Y; int width = (int)(SpriteWidth * snapState); if (width > 0.0f) { float positionMultiplier = snapState; if (BreakFromMiddle) { positionMultiplier /= 2; DrawRope(spriteBatch, endPos - diff * positionMultiplier, endPos, width); } DrawRope(spriteBatch, startPos, startPos + diff * positionMultiplier, width); } } else { DrawRope(spriteBatch, startPos, endPos, SpriteWidth); } if (startSprite != null || endSprite != null) { Vector2 dir = endPos - startPos; float angle = (float)Math.Atan2(dir.Y, dir.X); if (startSprite != null) { float depth = Math.Min(item.GetDrawDepth() + (startSprite.Depth - item.Sprite.Depth), 0.999f); startSprite?.Draw(spriteBatch, startPos, SpriteColor, angle, depth: depth); } if (endSprite != null && (!Snapped || BreakFromMiddle)) { float depth = Math.Min(item.GetDrawDepth() + (endSprite.Depth - item.Sprite.Depth), 0.999f); endSprite?.Draw(spriteBatch, endPos, SpriteColor, angle, depth: depth); } } } private void DrawRope(SpriteBatch spriteBatch, Vector2 startPos, Vector2 endPos, int width) { float depth = sprite == null ? item.Sprite.Depth + 0.001f : Math.Min(item.GetDrawDepth() + (sprite.Depth - item.Sprite.Depth), 0.999f); if (sprite?.Texture == null) { GUI.DrawLine(spriteBatch, startPos, endPos, SpriteColor, depth: depth, width: width); return; } if (Tile) { float length = Vector2.Distance(startPos, endPos); Vector2 dir = (endPos - startPos) / length; float x; for (x = 0.0f; x <= length - sprite.size.X; x += sprite.size.X) { GUI.DrawLine(spriteBatch, sprite, startPos + dir * (x - 5.0f), startPos + dir * (x + sprite.size.X), SpriteColor, depth: depth, width: width); } float leftOver = length - x; if (leftOver > 0.0f) { GUI.DrawLine(spriteBatch, sprite, startPos + dir * (x - 5.0f), endPos, SpriteColor, depth: depth, width: width); } } else { GUI.DrawLine(spriteBatch, sprite, startPos, endPos, SpriteColor, depth: depth, width: width); } } public void ClientEventRead(IReadMessage msg, float sendingTime) { snapped = msg.ReadBoolean(); if (!snapped) { UInt16 targetId = msg.ReadUInt16(); UInt16 sourceId = msg.ReadUInt16(); byte limbIndex = msg.ReadByte(); Item target = Entity.FindEntityByID(targetId) as Item; if (target == null) { return; } var source = Entity.FindEntityByID(sourceId); if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length) { Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex]; Attach(sourceLimb, target); } else if (source is ISpatialEntity spatialEntity) { Attach(spatialEntity, target); } } } protected override void RemoveComponentSpecific() { sprite?.Remove(); sprite = null; startSprite?.Remove(); startSprite = null; endSprite?.Remove(); endSprite = null; } } }