Server LuaCsForBarotrauma
ItemAssemblyPrefab.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using Barotrauma.IO;
6 using System.Linq;
7 using System.Xml.Linq;
8 using System.Collections.Immutable;
9 using System.Net;
10 
11 namespace Barotrauma
12 {
13  #warning TODO: MapEntityPrefab should be constrained further to not include item assemblies, as assemblies are effectively not entities at all
15  {
17 
18  private readonly XElement configElement;
19 
20  public readonly record struct DisplayEntity(
22  Rectangle Rect,
23  float RotationRad);
24 
25  public readonly ImmutableArray<DisplayEntity> DisplayEntities;
26 
27  public readonly Rectangle Bounds;
28 
29  public override LocalizedString Name { get; }
30 
31  public override Sprite Sprite => null;
32 
33  public override string OriginalName => Name.Value;
34 
35  public override ImmutableHashSet<Identifier> Tags { get; }
36 
37  public override ImmutableHashSet<Identifier> AllowedLinks => null;
38 
39  public override MapEntityCategory Category => MapEntityCategory.ItemAssembly;
40 
41  public override ImmutableHashSet<string> Aliases => null;
42 
43  protected override Identifier DetermineIdentifier(XElement element)
44  {
45  return element.GetAttributeIdentifier("identifier", element.GetAttributeIdentifier("name", ""));
46  }
47 
48  public ItemAssemblyPrefab(ContentXElement element, ItemAssemblyFile file) : base(element, file)
49  {
50  configElement = element;
51 
52  SerializableProperty.DeserializeProperties(this, configElement);
53 
54  Name = TextManager.Get($"EntityName.{Identifier}").Fallback(element.GetAttributeString("name", ""));
55  Description = TextManager.Get($"EntityDescription.{Identifier}");
56  Tags = Enumerable.Empty<Identifier>().ToImmutableHashSet();
57 
58  string description = element.GetAttributeString("description", string.Empty);
59  if (!description.IsNullOrEmpty())
60  {
61  Description = Description.Fallback(description);
62  }
63 
64  List<ushort> containedItemIDs = new List<ushort>();
65  foreach (XElement entityElement in element.Elements())
66  {
67  var containerElement = entityElement.GetChildElement("itemcontainer");
68  if (containerElement == null) { continue; }
69 
70  string containedString = containerElement.GetAttributeString("contained", "");
71  string[] itemIdStrings = containedString.Split(',');
72  var itemIds = new List<ushort>[itemIdStrings.Length];
73  for (int i = 0; i < itemIdStrings.Length; i++)
74  {
75  itemIds[i] ??= new List<ushort>();
76  foreach (string idStr in itemIdStrings[i].Split(';'))
77  {
78  if (int.TryParse(idStr, out int id))
79  {
80  itemIds[i].Add((ushort)id);
81  containedItemIDs.Add((ushort)id);
82  }
83  }
84  }
85  }
86 
87  int minX = int.MaxValue, minY = int.MaxValue;
88  int maxX = int.MinValue, maxY = int.MinValue;
89  var displayEntities = new List<DisplayEntity>();
90  foreach (XElement entityElement in element.Elements())
91  {
92  ushort id = (ushort)entityElement.GetAttributeInt("ID", 0);
93  if (id > 0 && containedItemIDs.Contains(id)) { continue; }
94 
95  if (entityElement.Elements().Any(e => e.Name.LocalName.Equals("wire", StringComparison.OrdinalIgnoreCase))) { continue; }
96 
97  Identifier identifier = entityElement.GetAttributeIdentifier("identifier", entityElement.Name.ToString().ToLowerInvariant());
98  Rectangle rect = entityElement.GetAttributeRect("rect", Rectangle.Empty);
99  float scale = entityElement.GetAttributeFloat("scale", 1.0f);
100  float rotation = MathHelper.ToRadians(entityElement.GetAttributeFloat("rotation", 0.0f));
101  if (!entityElement.GetAttributeBool("hideinassemblypreview", false))
102  {
103  displayEntities.Add(new DisplayEntity(identifier, rect, rotation));
104  }
105  minX = Math.Min(minX, rect.X);
106  minY = Math.Min(minY, rect.Y - rect.Height);
107  maxX = Math.Max(maxX, rect.Right);
108  maxY = Math.Max(maxY, rect.Y);
109  }
110  DisplayEntities = displayEntities.ToImmutableArray();
111 
112  Bounds = minX == int.MaxValue ?
113  new Rectangle(0, 0, 1, 1) :
114  new Rectangle(minX, minY, maxX - minX, maxY - minY);
115  }
116 
117  protected override void CreateInstance(Rectangle rect)
118  {
119 #if CLIENT
120  var loaded = CreateInstance(rect.Location.ToVector2(), Submarine.MainSub, selectInstance: Screen.Selected == GameMain.SubEditorScreen);
121  if (Screen.Selected is SubEditorScreen && loaded.Any())
122  {
123  SubEditorScreen.StoreCommand(new AddOrDeleteCommand(loaded, false, handleInventoryBehavior: false));
124  }
125 #else
126  var loaded = CreateInstance(rect.Location.ToVector2(), Submarine.MainSub);
127 #endif
128  }
129 
130  public List<MapEntity> CreateInstance(Vector2 position, Submarine sub, bool selectInstance = false)
131  {
132  var retVal = PasteEntities(position, sub, configElement, ContentFile.Path.Value, selectInstance);
133 #if CLIENT
134  GameMain.SubEditorScreen?.ReconstructLayers();
135 #endif
136  return retVal;
137  }
138 
139  public static List<MapEntity> PasteEntities(Vector2 position, Submarine sub, XElement configElement, string filePath = null, bool selectInstance = false)
140  {
141  int idOffset = Entity.FindFreeIdBlock(configElement.Elements().Count());
142  List<MapEntity> entities = MapEntity.LoadAll(sub, configElement, filePath, idOffset);
143  if (entities.Count == 0) { return entities; }
144 
145  Vector2 offset = sub?.HiddenSubPosition ?? Vector2.Zero;
146 
147  foreach (MapEntity me in entities)
148  {
149  me.Move(position);
150  me.Submarine = sub;
151  if (me is not Item item) { continue; }
152  Wire wire = item.GetComponent<Wire>();
153  //Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition;
154  if (wire != null)
155  {
156  //fix wires that have been erroneously saved at the "hidden position"
157  if (sub != null && Vector2.Distance(me.Position, sub.HiddenSubPosition) > sub.HiddenSubPosition.Length() / 2)
158  {
159  me.Move(position);
160  }
161  wire.MoveNodes(position - offset);
162  }
163  }
164 
165  MapEntity.MapLoaded(entities, true);
166 #if CLIENT
167  if (Screen.Selected == GameMain.SubEditorScreen && selectInstance)
168  {
169  MapEntity.SelectedList.Clear();
170  entities.ForEach(MapEntity.AddSelection);
171  }
172 #endif
173  return entities;
174  }
175 
176  public void Delete()
177  {
178  Prefabs.Remove(this);
179  try
180  {
181  if (ContentPackage is { Files: { Length: 1 } }
182  && ContentPackageManager.LocalPackages.Contains(ContentPackage))
183  {
184  Directory.Delete(ContentPackage.Dir, recursive: true, catchUnauthorizedAccessExceptions: false);
185  ContentPackageManager.LocalPackages.Refresh();
186  ContentPackageManager.EnabledPackages.DisableRemovedMods();
187  }
188  }
189  catch (Exception e)
190  {
191  DebugConsole.ThrowErrorLocalized("Deleting item assembly \"" + Name + "\" failed.", e);
192  }
193  }
194 
195  public override void Dispose() { }
196  }
197 }
Base class for content file types, which are loaded from filelist.xml via reflection....
Definition: ContentFile.cs:23
readonly ContentPath Path
Definition: ContentFile.cs:137
string??????????? Value
Definition: ContentPath.cs:27
string? GetAttributeString(string key, string? def)
static int FindFreeIdBlock(int minBlockSize)
Finds a contiguous block of free IDs of at least the given size
Definition: Entity.cs:180
Submarine Submarine
Definition: Entity.cs:53
static readonly Screen SubEditorScreen
Definition: GameMain.cs:62
override Identifier DetermineIdentifier(XElement element)
List< MapEntity > CreateInstance(Vector2 position, Submarine sub, bool selectInstance=false)
static List< MapEntity > PasteEntities(Vector2 position, Submarine sub, XElement configElement, string filePath=null, bool selectInstance=false)
override ImmutableHashSet< Identifier > Tags
static readonly PrefabCollection< ItemAssemblyPrefab > Prefabs
override LocalizedString Name
override MapEntityCategory Category
override ImmutableHashSet< Identifier > AllowedLinks
readonly record struct DisplayEntity(Identifier Identifier, Rectangle Rect, float RotationRad)
override ImmutableHashSet< string > Aliases
override void CreateInstance(Rectangle rect)
ItemAssemblyPrefab(ContentXElement element, ItemAssemblyFile file)
readonly ImmutableArray< DisplayEntity > DisplayEntities
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
static void MapLoaded(List< MapEntity > entities, bool updateHulls)
Definition: MapEntity.cs:856
virtual void Move(Vector2 amount, bool ignoreContacts=true)
Definition: MapEntity.cs:329
override Vector2 Position
Definition: MapEntity.cs:214
static List< MapEntity > LoadAll(Submarine submarine, XElement parentElement, string filePath, int idOffset)
Definition: MapEntity.cs:741
readonly Identifier Identifier
Definition: Prefab.cs:34
static Screen Selected
Definition: Screen.cs:5
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.