using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Xml.Linq; namespace Barotrauma { partial class StructurePrefab : MapEntityPrefab { public XElement ConfigElement { get; private set; } private bool canSpriteFlipX, canSpriteFlipY; private float health; //default size private Vector2 size; //does the structure have a physics body [Serialize(false, false)] public bool Body { get; private set; } //rotation of the physics body in degrees [Serialize(0.0f, false)] public float BodyRotation { get; private set; } //in display units [Serialize(0.0f, false)] public float BodyWidth { get; private set; } //in display units [Serialize(0.0f, false)] public float BodyHeight { get; private set; } //in display units [Serialize("0.0,0.0", false)] public Vector2 BodyOffset { get; private set; } [Serialize(false, false)] public bool Platform { get; private set; } [Serialize(false, false)] public bool AllowAttachItems { get; private set; } [Serialize(100.0f, false)] public float Health { get { return health; } set { health = Math.Max(value, 0.0f); } } [Serialize(false, false)] public bool CastShadow { get; private set; } [Serialize(Direction.None, false)] public Direction StairDirection { get; private set; } public bool CanSpriteFlipX { get { return canSpriteFlipX; } } public bool CanSpriteFlipY { get { return canSpriteFlipY; } } [Serialize("0,0", true)] public Vector2 Size { get { return size; } private set { size = value; } } public Vector2 ScaledSize => size * Scale; protected Vector2 textureScale = Vector2.One; [Editable(DecimalCount = 3), Serialize("1.0, 1.0", true)] public Vector2 TextureScale { get { return textureScale; } set { textureScale = new Vector2( MathHelper.Clamp(value.X, 0.01f, 10), MathHelper.Clamp(value.Y, 0.01f, 10)); } } public Sprite BackgroundSprite { get; private set; } public static void LoadAll(IEnumerable filePaths) { foreach (string filePath in filePaths) { XDocument doc = XMLExtensions.TryLoadXml(filePath); if (doc == null || doc.Root == null) return; foreach (XElement el in doc.Root.Elements()) { StructurePrefab sp = Load(el); List.Add(sp); } } } public static StructurePrefab Load(XElement element) { StructurePrefab sp = new StructurePrefab { name = element.GetAttributeString("name", "") }; sp.ConfigElement = element; if (string.IsNullOrEmpty(sp.name)) sp.name = element.Name.ToString(); sp.identifier = element.GetAttributeString("identifier", ""); string translatedName = TextManager.Get("EntityName." + sp.identifier, true); if (!string.IsNullOrEmpty(translatedName)) sp.name = translatedName; sp.Tags = new HashSet(); string joinedTags = element.GetAttributeString("tags", ""); if (string.IsNullOrEmpty(joinedTags)) joinedTags = element.GetAttributeString("Tags", ""); foreach (string tag in joinedTags.Split(',')) { sp.Tags.Add(tag.Trim().ToLowerInvariant()); } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString()) { case "sprite": sp.sprite = new Sprite(subElement); if (subElement.Attribute("sourcerect") == null) { DebugConsole.ThrowError("Warning - sprite sourcerect not configured for structure \"" + sp.name + "\"!"); } if (subElement.GetAttributeBool("fliphorizontal", false)) sp.sprite.effects = SpriteEffects.FlipHorizontally; if (subElement.GetAttributeBool("flipvertical", false)) sp.sprite.effects = SpriteEffects.FlipVertically; sp.canSpriteFlipX = subElement.GetAttributeBool("canflipx", true); sp.canSpriteFlipY = subElement.GetAttributeBool("canflipy", true); if (subElement.Attribute("name") == null && !string.IsNullOrWhiteSpace(sp.Name)) { sp.sprite.Name = sp.Name; } sp.sprite.EntityID = sp.identifier; break; case "backgroundsprite": sp.BackgroundSprite = new Sprite(subElement); if (subElement.GetAttributeBool("fliphorizontal", false)) sp.BackgroundSprite.effects = SpriteEffects.FlipHorizontally; if (subElement.GetAttributeBool("flipvertical", false)) sp.BackgroundSprite.effects = SpriteEffects.FlipVertically; break; } } if (!Enum.TryParse(element.GetAttributeString("category", "Structure"), true, out MapEntityCategory category)) { category = MapEntityCategory.Structure; } sp.Category = category; string aliases = element.GetAttributeString("aliases", ""); if (!string.IsNullOrWhiteSpace(aliases)) { sp.Aliases = aliases.Split(','); } SerializableProperty.DeserializeProperties(sp, element); if (sp.Body) { sp.Tags.Add("wall"); } string translatedDescription = TextManager.Get("EntityDescription." + sp.identifier, true); if (!string.IsNullOrEmpty(translatedDescription)) sp.Description = translatedDescription; //backwards compatibility if (element.Attribute("size") == null) { sp.size = Vector2.Zero; if (element.Attribute("width") == null && element.Attribute("height") == null) { sp.size.X = sp.sprite.SourceRect.Width; sp.size.Y = sp.sprite.SourceRect.Height; } else { sp.size.X = element.GetAttributeFloat("width", 0.0f); sp.size.Y = element.GetAttributeFloat("height", 0.0f); } } if (!category.HasFlag(MapEntityCategory.Legacy) && string.IsNullOrEmpty(sp.identifier)) { DebugConsole.ThrowError( "Structure prefab \"" + sp.name + "\" has no identifier. All structure prefabs have a unique identifier string that's used to differentiate between items during saving and loading."); } if (!string.IsNullOrEmpty(sp.identifier)) { MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == sp.identifier); if (existingPrefab != null) { DebugConsole.ThrowError( "Map entity prefabs \"" + sp.name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!"); } } return sp; } public override void UpdatePlacing(Camera cam) { Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); Vector2 size = ScaledSize; Rectangle newRect = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y); if (placePosition == Vector2.Zero) { if (PlayerInput.LeftButtonHeld()) placePosition = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); newRect.X = (int)position.X; newRect.Y = (int)position.Y; } else { Vector2 placeSize = size; if (ResizeHorizontal) placeSize.X = position.X - placePosition.X; if (ResizeVertical) placeSize.Y = placePosition.Y - position.Y; newRect = Submarine.AbsRect(placePosition, placeSize); if (PlayerInput.LeftButtonReleased()) { //don't allow resizing width/height to zero if ((!ResizeHorizontal || placeSize.X != 0.0f) && (!ResizeVertical || placeSize.Y != 0.0f)) { newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position); var structure = new Structure(newRect, this, Submarine.MainSub); structure.Submarine = Submarine.MainSub; } selected = null; return; } } if (PlayerInput.RightButtonHeld()) selected = null; } } }