using Barotrauma.Extensions; using Barotrauma.Items.Components; using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace Barotrauma { class OutpostModuleInfo : ISerializableEntity { [Flags] public enum GapPosition { None = 0, Right = 1, Left = 2, Top = 4, Bottom = 8 } private readonly HashSet moduleFlags = new HashSet(); public IEnumerable ModuleFlags { get { return moduleFlags; } } private readonly HashSet allowAttachToModules = new HashSet(); public IEnumerable AllowAttachToModules { get { return allowAttachToModules; } } private readonly HashSet allowedLocationTypes = new HashSet(); public IEnumerable AllowedLocationTypes { get { return allowedLocationTypes; } } [Serialize(100, IsPropertySaveable.Yes, description: "How many instances of this module can be used in one outpost."), Editable] public int MaxCount { get; set; } [Serialize(10.0f, IsPropertySaveable.Yes, description: "How likely it is for the module to get picked when selecting from a set of modules during the outpost generation."), Editable] public float Commonness { get; set; } [Serialize(GapPosition.None, IsPropertySaveable.Yes, description: "Which sides of the module have gaps on them (i.e. from which sides the module can be attached to other modules). Center = no gaps available.")] public GapPosition GapPositions { get; set; } [Serialize(GapPosition.Right | GapPosition.Left | GapPosition.Bottom | GapPosition.Top, IsPropertySaveable.Yes, description: "Which sides of this module are allowed to attach to the previously placed module. E.g. if you want a module to always attach to the left side of the docking module, you could set this to Right.")] public GapPosition CanAttachToPrevious { get; set; } public string Name { get; private set; } public Dictionary SerializableProperties { get; private set; } public OutpostModuleInfo(SubmarineInfo submarineInfo, XElement element) { Name = $"OutpostModuleInfo ({submarineInfo.Name})"; SerializableProperties = SerializableProperty.DeserializeProperties(this, element); SetFlags( element.GetAttributeIdentifierArray("flags", null) ?? element.GetAttributeIdentifierArray("moduletypes", Array.Empty())); SetAllowAttachTo(element.GetAttributeIdentifierArray("allowattachto", Array.Empty())); allowedLocationTypes = new HashSet(element.GetAttributeIdentifierArray("allowedlocationtypes", Array.Empty())); } public OutpostModuleInfo(SubmarineInfo submarineInfo) { Name = $"OutpostModuleInfo ({submarineInfo.Name})"; SerializableProperties = SerializableProperty.DeserializeProperties(this); } public OutpostModuleInfo(OutpostModuleInfo original) { Name = original.Name; moduleFlags = new HashSet(original.moduleFlags); allowAttachToModules = new HashSet(original.allowAttachToModules); allowedLocationTypes = new HashSet(original.allowedLocationTypes); SerializableProperties = new Dictionary(); GapPositions = original.GapPositions; foreach (KeyValuePair kvp in original.SerializableProperties) { SerializableProperties.Add(kvp.Key, kvp.Value); if (SerializableProperty.GetSupportedTypeName(kvp.Value.PropertyType) != null) { kvp.Value.TrySetValue(this, kvp.Value.GetValue(original)); } } } public void SetFlags(IEnumerable newFlags) { moduleFlags.Clear(); if (newFlags.Contains("hallwayhorizontal".ToIdentifier())) { moduleFlags.Add("hallwayhorizontal".ToIdentifier()); if (newFlags.Contains("ruin".ToIdentifier())) { moduleFlags.Add("ruin".ToIdentifier()); } } if (newFlags.Contains("hallwayvertical".ToIdentifier())) { moduleFlags.Add("hallwayvertical".ToIdentifier()); if (newFlags.Contains("ruin".ToIdentifier())) { moduleFlags.Add("ruin".ToIdentifier()); } } if (!newFlags.Any()) { moduleFlags.Add("none".ToIdentifier()); } foreach (Identifier flag in newFlags) { if (flag == "none" && newFlags.Count() > 1) { continue; } moduleFlags.Add(flag); } } public void SetAllowAttachTo(IEnumerable allowAttachTo) { allowAttachToModules.Clear(); if (!allowAttachTo.Any()) { allowAttachToModules.Add("any".ToIdentifier()); } foreach (Identifier flag in allowAttachTo) { if (flag == "any" && allowAttachTo.Count() > 1) { continue; } allowAttachToModules.Add(flag); } } public void SetAllowedLocationTypes(IEnumerable allowedLocationTypes) { this.allowedLocationTypes.Clear(); foreach (Identifier locationType in allowedLocationTypes) { if (locationType == "any") { continue; } this.allowedLocationTypes.Add(locationType); } } public bool IsAllowedInAnyLocationType() { return allowedLocationTypes.None() || allowedLocationTypes.Contains("Any".ToIdentifier()); } /// Does the module need to be explicitly configured as suitable for the location type, or is it ok if it's allowed for any location type? public bool IsAllowedInLocationType(LocationType locationType, bool requireLocationTypeSpecific = false) { if (locationType == null) { return true; } if (!requireLocationTypeSpecific && IsAllowedInAnyLocationType()) { return true; } return allowedLocationTypes.Contains(locationType.Identifier) || (!locationType.UseOutpostModulesOfLocationType.IsEmpty && allowedLocationTypes.Contains(locationType.UseOutpostModulesOfLocationType)); } public void DetermineGapPositions(Submarine sub) { GapPositions = GapPosition.None; foreach (Gap gap in Gap.GapList) { if (gap.Submarine != sub || gap.linkedTo.Count != 1) { continue; } if (gap.ConnectedDoor != null && !gap.ConnectedDoor.UseBetweenOutpostModules) { continue; } //ignore gaps that are at a docking port bool portFound = false; foreach (DockingPort port in DockingPort.List) { if (Submarine.RectContains(gap.WorldRect, port.Item.WorldPosition)) { portFound = true; break; } } if (portFound) { continue; } GapPositions |= gap.IsHorizontal ? gap.linkedTo[0].WorldPosition.X < gap.WorldPosition.X ? GapPosition.Right : GapPosition.Left : gap.linkedTo[0].WorldPosition.Y < gap.WorldPosition.Y ? GapPosition.Top : GapPosition.Bottom; } } public void Save(XElement element) { SerializableProperty.SerializeProperties(this, element); element.SetAttributeValue("flags", string.Join(",", ModuleFlags)); element.SetAttributeValue("allowattachto", string.Join(",", AllowAttachToModules)); element.SetAttributeValue("allowedlocationtypes", string.Join(",", AllowedLocationTypes)); } } }