Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Linq;
7 
8 namespace Barotrauma
9 {
10  [Flags]
12  {
13  None = 0,
14  Structure = 1,
15  Decorative = 2,
16  Machine = 4,
17  Medical = 8,
18  Weapon = 16,
19  Diving = 32,
20  Equipment = 64,
21  Fuel = 128,
22  Electrical = 256,
23  Material = 1024,
24  Alien = 2048,
25  Wrecked = 4096,
26  ItemAssembly = 8192,
27  Legacy = 16384,
28  Misc = 32768
29  }
30 
31  abstract partial class MapEntityPrefab : PrefabWithUintIdentifier
32  {
33  public static IEnumerable<MapEntityPrefab> List
34  {
35  get
36  {
37  foreach (var ep in CoreEntityPrefab.Prefabs)
38  {
39  yield return ep;
40  }
41 
42  foreach (var ep in StructurePrefab.Prefabs)
43  {
44  yield return ep;
45  }
46 
47  foreach (var ep in ItemPrefab.Prefabs)
48  {
49  yield return ep;
50  }
51 
52  foreach (var ep in ItemAssemblyPrefab.Prefabs)
53  {
54  yield return ep;
55  }
56  }
57  }
58 
59  //which prefab has been selected for placing
60  public static MapEntityPrefab Selected { get; set; }
61 
62  //the position where the structure is being placed (needed when stretching the structure)
63  protected static Vector2 placePosition;
64 
65  public static bool SelectPrefab(object selection)
66  {
67  if ((Selected = selection as MapEntityPrefab) != null)
68  {
69  placePosition = Vector2.Zero;
70  return true;
71  }
72  else
73  {
74  return false;
75  }
76  }
77 
78  //a method that allows the GUIListBoxes to check through a delegate if the entityprefab is still selected
79  public static object GetSelected()
80  {
81  return (object)Selected;
82  }
83 
89  [Obsolete("Prefer MapEntityPrefab.FindByIdentifier or MapEntityPrefab.FindByName")]
90  public static MapEntityPrefab Find(string name, string identifier = null, bool showErrorMessages = true)
91  {
92  return Find(name, (identifier ?? "").ToIdentifier(), showErrorMessages);
93  }
94 
95  [Obsolete("Prefer MapEntityPrefab.FindByIdentifier or MapEntityPrefab.FindByName")]
96  public static MapEntityPrefab Find(string name, Identifier identifier, bool showErrorMessages = true)
97  {
98  //try to search based on identifier first
99  if (string.IsNullOrEmpty(name) && !identifier.IsEmpty)
100  {
101  if (CoreEntityPrefab.Prefabs.ContainsKey(identifier)) { return CoreEntityPrefab.Prefabs[identifier]; }
102  if (StructurePrefab.Prefabs.ContainsKey(identifier)) { return StructurePrefab.Prefabs[identifier]; }
103  if (ItemPrefab.Prefabs.ContainsKey(identifier)) { return ItemPrefab.Prefabs[identifier]; }
104  if (ItemAssemblyPrefab.Prefabs.ContainsKey(identifier)) { return ItemAssemblyPrefab.Prefabs[identifier]; }
105  }
106 
107  foreach (MapEntityPrefab prefab in List)
108  {
109  if (!identifier.IsEmpty)
110  {
111  if (prefab.Identifier != identifier)
112  {
113  if (prefab.Aliases != null && prefab.Aliases.Any(a => a == identifier))
114  {
115  return prefab;
116  }
117  continue;
118  }
119  else
120  {
121  if (string.IsNullOrEmpty(name)) { return prefab; }
122  }
123  }
124  if (!string.IsNullOrEmpty(name))
125  {
126  if (prefab.Name.Equals(name, StringComparison.OrdinalIgnoreCase) ||
127  prefab.OriginalName.Equals(name, StringComparison.OrdinalIgnoreCase) ||
128  (prefab.Aliases != null && prefab.Aliases.Any(a => a.Equals(name, StringComparison.OrdinalIgnoreCase))))
129  {
130  return prefab;
131  }
132  }
133  }
134 
135  if (showErrorMessages)
136  {
137  DebugConsole.ThrowError("Failed to find a matching MapEntityPrefab (name: \"" + name + "\", identifier: \"" + identifier + "\").\n" + Environment.StackTrace.CleanupStackTrace());
138  }
139  return null;
140  }
141 
142  public static MapEntityPrefab GetRandom(Predicate<MapEntityPrefab> predicate, Rand.RandSync sync)
143  {
144  return List.GetRandom(p => predicate(p), sync);
145  }
146 
151  public static MapEntityPrefab Find(Predicate<MapEntityPrefab> predicate)
152  {
153  return List.FirstOrDefault(p => predicate(p));
154  }
155 
156 
157  public static MapEntityPrefab FindByName(string name)
158  {
159  if (name.IsNullOrEmpty()) { throw new ArgumentException($"{nameof(name)} must not be null or empty"); }
160 
161  var matches =
162  List.Where(prefab =>
163  prefab.Name.Equals(name, StringComparison.OrdinalIgnoreCase) ||
164  prefab.OriginalName.Equals(name, StringComparison.OrdinalIgnoreCase) ||
165  (prefab.Aliases != null &&
166  prefab.Aliases.Any(a => a.Equals(name, StringComparison.OrdinalIgnoreCase))));
167  //if there's multiple matches, prefer ones that aren't hidden in menus and base items
168  //(hidden ones and variants are often e.g. some kind of special ones used in an event or versions unlockable with talents)
169  if (matches.Count() > 1)
170  {
171  var bestMatch =
172  matches.FirstOrDefault(prefab => !prefab.HideInMenus) ??
173  matches.FirstOrDefault(prefab => prefab is ItemPrefab ip && ip.VariantOf.IsEmpty);
174  if (bestMatch != null) { return bestMatch; }
175  }
176  return matches.FirstOrDefault();
177  }
178 
179  public static MapEntityPrefab FindByIdentifier(Identifier identifier)
180  => CoreEntityPrefab.Prefabs.TryGet(identifier, out var corePrefab) ? corePrefab
181  : ItemPrefab.Prefabs.TryGet(identifier, out var itemPrefab) ? itemPrefab
182  : StructurePrefab.Prefabs.TryGet(identifier, out var structurePrefab) ? structurePrefab
183  : ItemAssemblyPrefab.Prefabs.TryGet(identifier, out var itemAssemblyPrefab) ? itemAssemblyPrefab
184  : (MapEntityPrefab)null;
185 
186  public abstract Sprite Sprite { get; }
187 
188  public virtual bool CanSpriteFlipX { get; } = false;
189  public virtual bool CanSpriteFlipY { get; } = false;
190 
191  public abstract string OriginalName { get; }
192 
193  public abstract LocalizedString Name { get; }
194 
195  public abstract ImmutableHashSet<Identifier> Tags { get; }
196 
200  public abstract ImmutableHashSet<Identifier> AllowedLinks { get; }
201 
202  public abstract MapEntityCategory Category { get; }
203 
204  //If a matching prefab is not found when loading a sub, the game will attempt to find a prefab with a matching alias.
205  //(allows changing names while keeping backwards compatibility with older sub files)
206  public abstract ImmutableHashSet<string> Aliases { get; }
207 
208  //is it possible to stretch the entity horizontally/vertically
209  [Serialize(false, IsPropertySaveable.No)]
210  public bool ResizeHorizontal { get; protected set; }
211 
212  [Serialize(false, IsPropertySaveable.No)]
213  public bool ResizeVertical { get; protected set; }
214 
215  [Serialize("", IsPropertySaveable.No)]
216  public LocalizedString Description { get; protected set; }
217 
218  [Serialize("", IsPropertySaveable.No)]
219  public string AllowedUpgrades { get; protected set; }
220 
221  [Serialize(false, IsPropertySaveable.No)]
222  public bool HideInMenus { get; protected set; }
223 
224  [Serialize(false, IsPropertySaveable.No)]
225  public bool HideInEditors { get; protected set; }
226 
227  [Serialize("", IsPropertySaveable.No)]
228  public string Subcategory { get; protected set; }
229 
230  [Serialize(false, IsPropertySaveable.No)]
231  public bool Linkable { get; protected set; }
232 
233  [Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.No)]
234  public Color SpriteColor { get; protected set; }
235 
236  [Serialize(1f, IsPropertySaveable.Yes), Editable(0.1f, 10f, DecimalCount = 3)]
237  public float Scale { get; protected set; }
238 
239  protected MapEntityPrefab(Identifier identifier) : base(null, identifier) { }
240 
241  public MapEntityPrefab(ContentXElement element, ContentFile file) : base(file, element) { }
242 
243  public string GetItemNameTextId()
244  {
245  var textId = $"entityname.{Identifier}";
246  return TextManager.ContainsTag(textId) ? textId : null;
247  }
248 
249  public string GetHullNameTextId()
250  {
251  var textId = $"roomname.{Identifier}";
252  return TextManager.ContainsTag(textId) ? textId : null;
253  }
254 
255  private string cachedAllowedUpgrades = "";
256  private ImmutableHashSet<Identifier> allowedUpgradeSet;
257  public IEnumerable<Identifier> GetAllowedUpgrades()
258  {
259  if (string.IsNullOrWhiteSpace(AllowedUpgrades)) { return Enumerable.Empty<Identifier>(); }
260  if (allowedUpgradeSet is null || cachedAllowedUpgrades != AllowedUpgrades)
261  {
262  allowedUpgradeSet = AllowedUpgrades.Split(",").ToIdentifiers().ToImmutableHashSet();
263  cachedAllowedUpgrades = AllowedUpgrades;
264  }
265 
266  return allowedUpgradeSet;
267  }
268 
269  public bool HasSubCategory(string subcategory)
270  {
271  return subcategory?.Equals(this.Subcategory, StringComparison.OrdinalIgnoreCase) ?? false;
272  }
273 
274  protected abstract void CreateInstance(Rectangle rect);
275 
276 #if DEBUG
277  public void DebugCreateInstance()
278  {
279  Rectangle rect = new Rectangle(new Point((int)Screen.Selected.Cam.WorldViewCenter.X, (int)Screen.Selected.Cam.WorldViewCenter.Y), new Point((int)Submarine.GridSize.X, (int)Submarine.GridSize.Y));
280  CreateInstance(rect);
281  }
282 #endif
283 
287  public bool NameMatches(string name, StringComparison comparisonType) => OriginalName.Equals(name, comparisonType) || (Aliases != null && Aliases.Any(a => a.Equals(name, comparisonType)));
288 
289  public bool NameMatches(IEnumerable<string> allowedNames, StringComparison comparisonType) => allowedNames.Any(n => NameMatches(n, comparisonType));
290 
291  public bool IsLinkAllowed(MapEntityPrefab target)
292  {
293  if (target == null) { return false; }
294  if (target is StructurePrefab && AllowedLinks.Contains("structure".ToIdentifier())) { return true; }
295  if (target is ItemPrefab && AllowedLinks.Contains("item".ToIdentifier())) { return true; }
296  if (target is LinkedSubmarinePrefab && Tags.Contains("dock".ToIdentifier())) { return true; }
297  if (this is LinkedSubmarinePrefab && target.Tags.Contains("dock".ToIdentifier())) { return true; }
298  return AllowedLinks.Contains(target.Identifier) || target.AllowedLinks.Contains(Identifier)
299  || target.Tags.Any(t => AllowedLinks.Contains(t)) || Tags.Any(t => target.AllowedLinks.Contains(t));
300  }
301 
302  protected void LoadDescription(ContentXElement element)
303  {
304  Identifier descriptionIdentifier = element.GetAttributeIdentifier("descriptionidentifier", "");
305  Identifier nameIdentifier = element.GetAttributeIdentifier("nameidentifier", "");
306 
307  string originalDescription = Description.Value;
308  if (descriptionIdentifier != Identifier.Empty)
309  {
310  Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}");
311  }
312  else if (nameIdentifier == Identifier.Empty)
313  {
314  Description = TextManager.Get($"EntityDescription.{Identifier}");
315  }
316  else
317  {
318  Description = TextManager.Get($"EntityDescription.{nameIdentifier}");
319  }
320  if (!originalDescription.IsNullOrEmpty())
321  {
322  Description = Description.Fallback(originalDescription);
323  }
324  }
325  }
326 }
Vector2 WorldViewCenter
Definition: Camera.cs:126
Base class for content file types, which are loaded from filelist.xml via reflection....
Definition: ContentFile.cs:23
Identifier GetAttributeIdentifier(string key, string def)
static readonly PrefabCollection< CoreEntityPrefab > Prefabs
static readonly PrefabCollection< ItemAssemblyPrefab > Prefabs
static readonly PrefabCollection< ItemPrefab > Prefabs
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
override bool Equals(object? obj)
MapEntityPrefab(ContentXElement element, ContentFile file)
bool NameMatches(IEnumerable< string > allowedNames, StringComparison comparisonType)
abstract ImmutableHashSet< Identifier > AllowedLinks
Links defined to identifiers.
static MapEntityPrefab GetRandom(Predicate< MapEntityPrefab > predicate, Rand.RandSync sync)
bool NameMatches(string name, StringComparison comparisonType)
Check if the name or any of the aliases of this prefab match the given name.
static MapEntityPrefab FindByIdentifier(Identifier identifier)
static MapEntityPrefab Find(Predicate< MapEntityPrefab > predicate)
Find a matching map entity prefab
abstract void CreateInstance(Rectangle rect)
static MapEntityPrefab Find(string name, Identifier identifier, bool showErrorMessages=true)
static MapEntityPrefab Find(string name, string identifier=null, bool showErrorMessages=true)
Find a matching map entity prefab
readonly Identifier Identifier
Definition: Prefab.cs:34
Prefab that has a property serves as a deterministic hash of a prefab's identifier....
static readonly PrefabCollection< StructurePrefab > Prefabs