Client LuaCsForBarotrauma
SoundPrefab.cs
2 using Barotrauma.IO;
3 using Barotrauma.Sounds;
4 using Microsoft.Xna.Framework;
5 using System;
6 using System.Collections.Generic;
7 using System.Collections.Immutable;
8 using System.Linq;
9 using System.Reflection;
10 using System.Text;
11 using System.Xml.Linq;
12 
13 namespace Barotrauma
14 {
15  public class TagNames : Attribute
16  {
17  public readonly ImmutableHashSet<Identifier> Names;
18 
19  public TagNames(params string[] names)
20  {
21  Names = names.Select(n => n.ToIdentifier()).ToImmutableHashSet();
22  }
23  }
24 
26  {
27  private class PrefabCollectionHandler
28  {
29  public readonly object Collection;
30  public readonly MethodInfo AddMethod;
31  public readonly MethodInfo RemoveMethod;
32  public readonly MethodInfo SortAllMethod;
33  public readonly MethodInfo AddOverrideFileMethod;
34  public readonly MethodInfo RemoveOverrideFileMethod;
35 
36  public void Add(SoundPrefab p, bool isOverride)
37  {
38  AddMethod.Invoke(Collection, new object[] { p, isOverride });
39  }
40 
41  public void Remove(SoundPrefab p)
42  {
43  RemoveMethod.Invoke(Collection, new object[] { p });
44  }
45 
46  public void AddOverrideFile(ContentFile file)
47  {
48  AddOverrideFileMethod.Invoke(Collection, new object[] { file });
49  }
50 
51  public void RemoveOverrideFile(ContentFile file)
52  {
53  RemoveOverrideFileMethod.Invoke(Collection, new object[] { file });
54  }
55 
56  public void SortAll()
57  {
58  SortAllMethod.Invoke(Collection, null);
59  }
60 
61  public PrefabCollectionHandler(Type type)
62  {
63  var collectionField = type.GetField($"{type.Name}Prefabs", BindingFlags.Public | BindingFlags.Static);
64  if (collectionField is null) { throw new InvalidOperationException($"Couldn't determine PrefabCollection for {type.Name}"); }
65  Collection = collectionField.GetValue(null) ?? throw new InvalidOperationException($"PrefabCollection for {type.Name} was null");
66  AddMethod = Collection.GetType().GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
67  RemoveMethod = Collection.GetType().GetMethod("Remove", BindingFlags.Public | BindingFlags.Instance);
68  AddOverrideFileMethod = Collection.GetType().GetMethod("AddOverrideFile", BindingFlags.Public | BindingFlags.Instance);
69  RemoveOverrideFileMethod = Collection.GetType().GetMethod("RemoveOverrideFile", BindingFlags.Public | BindingFlags.Instance);
70  SortAllMethod = Collection.GetType().GetMethod("SortAll", BindingFlags.Public | BindingFlags.Instance);
71  }
72  }
73 
78 
79  private readonly static List<SoundPrefab> flowSounds = new List<SoundPrefab>();
80  public static IReadOnlyList<SoundPrefab> FlowSounds => flowSounds;
81  private readonly static List<SoundPrefab> splashSounds = new List<SoundPrefab>();
82  public static IReadOnlyList<SoundPrefab> SplashSounds => splashSounds;
83 
84  public readonly static ImmutableDictionary<Identifier, Type> TagToDerivedPrefab;
85  private readonly static ImmutableDictionary<Type, PrefabCollectionHandler> derivedPrefabCollections;
86  private readonly static ImmutableDictionary<Identifier, PrefabSelector<SoundPrefab>> prefabSelectors;
87  private readonly static ImmutableDictionary<Identifier, List<SoundPrefab>> prefabsWithTag;
88  public readonly static PrefabCollection<SoundPrefab> Prefabs;
89 
90  static SoundPrefab()
91  {
92  var types = ReflectionUtils.GetDerivedNonAbstract<SoundPrefab>();
93  //types.ForEach(t => t.GetProperties(BindingFlags.Public | BindingFlags.Static));
94  TagToDerivedPrefab = types.SelectMany(t =>
95  t.GetCustomAttribute<TagNames>().Names.Select(n => (n, t))).ToImmutableDictionary();
96  derivedPrefabCollections = types.Select(t => (t, new PrefabCollectionHandler(t))).ToImmutableDictionary();
97 
98  var prefabSelectorFields = typeof(SoundPrefab).GetFields(BindingFlags.Public | BindingFlags.Static)
99  .Where(f => f.FieldType == typeof(PrefabSelector<SoundPrefab>));
100  prefabSelectors = prefabSelectorFields.Select(f => (f.Name.ToIdentifier(), (PrefabSelector<SoundPrefab>)f.GetValue(null))).ToImmutableDictionary();
101 
102  var prefabsOfTagName = typeof(SoundPrefab).GetFields(BindingFlags.Static | BindingFlags.NonPublic)
103  .Where(f => f.FieldType == typeof(List<SoundPrefab>));
104  prefabsWithTag = prefabsOfTagName.Select(f => (f.Name.Substring(0, f.Name.Length-6).ToIdentifier(), (List<SoundPrefab>)f.GetValue(null))).ToImmutableDictionary();
105 
107  onAdd: (SoundPrefab p, bool isOverride) =>
108  {
109  if (derivedPrefabCollections.ContainsKey(p.GetType()))
110  {
111  derivedPrefabCollections[p.GetType()].Add(p, isOverride);
112  }
113  if (prefabSelectors.ContainsKey(p.ElementName)) { prefabSelectors[p.ElementName].Add(p, isOverride); }
114  UpdateSoundsWithTag();
115  },
116  onRemove: (SoundPrefab p) =>
117  {
118  if (derivedPrefabCollections.ContainsKey(p.GetType()))
119  {
120  derivedPrefabCollections[p.GetType()].Remove(p);
121  }
122  if (prefabSelectors.ContainsKey(p.ElementName)) { prefabSelectors[p.ElementName].RemoveIfContains(p); }
123  UpdateSoundsWithTag();
124  SoundPlayer.DisposeDisabledMusic();
125  },
126  onSort: () =>
127  {
128  derivedPrefabCollections.Values.ForEach(h => h.SortAll());
129  prefabSelectors.Values.ForEach(h => h.Sort());
130  },
131  onAddOverrideFile: (file) => {derivedPrefabCollections.Values.ForEach(h => h.AddOverrideFile(file)); },
132  onRemoveOverrideFile: (file) => { derivedPrefabCollections.Values.ForEach(h => h.RemoveOverrideFile(file)); }
133  );
134  }
135 
136  private static void UpdateSoundsWithTag()
137  {
138  foreach (var tag in prefabsWithTag.Keys)
139  {
140  var list = prefabsWithTag[tag];
141  list.Clear();
142  list.AddRange(Prefabs.Where(p => p.ElementName == tag));
143  list.Sort((p1, p2) =>
144  {
145  if (p1.ContentFile.ContentPackage.Index < p2.ContentFile.ContentPackage.Index) { return -1; }
146  if (p1.ContentFile.ContentPackage.Index > p2.ContentFile.ContentPackage.Index) { return 1; }
147  if (p2.Element.ComesAfter(p1.Element)) { return -1; }
148  if (p1.Element.ComesAfter(p2.Element)) { return 1; }
149  return 0;
150  });
151  }
152  }
153 
154  protected override Identifier DetermineIdentifier(XElement element)
155  {
156  Identifier id = base.DetermineIdentifier(element);
157  if (id.IsEmpty)
158  {
159  if (id.IsEmpty) { id = Path.GetFileNameWithoutExtension(element.GetAttributeStringUnrestricted("path", "")).ToIdentifier(); }
160  if (id.IsEmpty) { id = Path.GetFileNameWithoutExtension(element.GetAttributeStringUnrestricted("file", "")).ToIdentifier(); }
161 
162  if (!id.IsEmpty)
163  {
164  id = $"{element.Name}_{id}".ToIdentifier();
165 
166  string damageSoundType = element.GetAttributeString("damagesoundtype", "");
167  if (!damageSoundType.IsNullOrEmpty())
168  {
169  id = $"{id}_{damageSoundType}".ToIdentifier();
170  }
171 
172  string musicType = element.GetAttributeString("type", "");
173  if (!musicType.IsNullOrEmpty())
174  {
175  id = $"{id}_{musicType}".ToIdentifier();
176  }
177  }
178  }
179 
180  return id;
181  }
182 
183  public readonly ContentPath SoundPath;
184  public readonly ContentXElement Element;
185  public readonly Identifier ElementName;
186 
187  public readonly float Volume;
188 
189  public Sound Sound { get; private set; }
190 
191  public SoundPrefab(ContentXElement element, SoundsFile file, bool stream = false) : base(file, element)
192  {
193  SoundPath = element.GetAttributeContentPath("file") ?? ContentPath.Empty;
194  Element = element;
195  ElementName = element.NameAsIdentifier();
196  Sound = GameMain.SoundManager.LoadSound(element, stream: stream);
197 
198  Volume = element.GetAttributeFloat(nameof(Volume), 1.0f);
199  }
200 
201  public bool IsPlaying()
202  {
203  return Sound.IsPlaying();
204  }
205 
206  public override void Dispose()
207  {
208  Sound?.Dispose(); Sound = null;
209  }
210  }
211 
212  [TagNames("damagesound")]
214  {
216 
217  //the range of inflicted damage where the sound can be played
218  //(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
219  public readonly Vector2 DamageRange;
220 
221  public readonly Identifier DamageType;
222 
223  public readonly Identifier RequiredTag;
224 
225  public bool IgnoreMuffling;
226 
227  public DamageSound(ContentXElement element, SoundsFile file) : base(element, file)
228  {
229  DamageRange = element.GetAttributeVector2("damagerange", Vector2.Zero);
230  DamageType = element.GetAttributeIdentifier("damagesoundtype", "None");
231  IgnoreMuffling = element.GetAttributeBool("ignoremuffling", false);
232  RequiredTag = element.GetAttributeIdentifier("requiredtag", "");
233  }
234  }
235 
236  [TagNames("music")]
238  {
240 
241  public readonly Identifier Type;
242  public readonly bool DuckVolume;
243 
244  public readonly Vector2 IntensityRange;
245  public readonly bool MuteIntensityTracks;
246  public readonly float? ForceIntensityTrack;
247 
248  public readonly bool StartFromRandomTime;
249  public readonly bool ContinueFromPreviousTime;
250  public int PreviousTime;
251 
252  public BackgroundMusic(ContentXElement element, SoundsFile file) : base(element, file, stream: true)
253  {
254  Type = element.GetAttributeIdentifier(nameof(Type), "");
255  IntensityRange = element.GetAttributeVector2(nameof(IntensityRange), new Vector2(0.0f, 100.0f));
256  DuckVolume = element.GetAttributeBool(nameof(DuckVolume), false);
257  MuteIntensityTracks = element.GetAttributeBool(nameof(MuteIntensityTracks), false);
258  if (element.GetAttribute(nameof(ForceIntensityTrack)) != null)
259  {
261  }
262  StartFromRandomTime = element.GetAttributeBool(nameof(StartFromRandomTime), false);
264  }
265  }
266 
267  [TagNames("guisound")]
269  {
270  //public readonly static Dictionary<GUISoundType, List<GUISound>> GUISoundsByType = new Dictionary<GUISoundType, List<GUISound>>();
272 
273  public readonly GUISoundType Type;
274 
275  public GUISound(ContentXElement element, SoundsFile file) : base(element, file)
276  {
277  Type = element.GetAttributeEnum("guisoundtype", GUISoundType.UIMessage);
278  }
279  }
280 }
readonly bool MuteIntensityTracks
Definition: SoundPrefab.cs:245
BackgroundMusic(ContentXElement element, SoundsFile file)
Definition: SoundPrefab.cs:252
readonly Vector2 IntensityRange
Definition: SoundPrefab.cs:244
readonly bool ContinueFromPreviousTime
Definition: SoundPrefab.cs:249
readonly bool StartFromRandomTime
Definition: SoundPrefab.cs:248
readonly? float ForceIntensityTrack
Definition: SoundPrefab.cs:246
static readonly PrefabCollection< BackgroundMusic > BackgroundMusicPrefabs
Definition: SoundPrefab.cs:239
readonly Identifier Type
Definition: SoundPrefab.cs:241
Base class for content file types, which are loaded from filelist.xml via reflection....
Definition: ContentFile.cs:23
static readonly ContentPath Empty
Definition: ContentPath.cs:12
float GetAttributeFloat(string key, float def)
Identifier NameAsIdentifier()
Vector2 GetAttributeVector2(string key, in Vector2 def)
ContentPath? GetAttributeContentPath(string key)
bool GetAttributeBool(string key, bool def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
DamageSound(ContentXElement element, SoundsFile file)
Definition: SoundPrefab.cs:227
readonly Identifier RequiredTag
Definition: SoundPrefab.cs:223
static readonly PrefabCollection< DamageSound > DamageSoundPrefabs
Definition: SoundPrefab.cs:215
readonly Identifier DamageType
Definition: SoundPrefab.cs:221
readonly Vector2 DamageRange
Definition: SoundPrefab.cs:219
static readonly PrefabCollection< GUISound > GUISoundPrefabs
Definition: SoundPrefab.cs:271
GUISound(ContentXElement element, SoundsFile file)
Definition: SoundPrefab.cs:275
readonly GUISoundType Type
Definition: SoundPrefab.cs:273
static Sounds.SoundManager SoundManager
Definition: GameMain.cs:80
readonly Identifier Identifier
Definition: Prefab.cs:34
SoundPrefab(ContentXElement element, SoundsFile file, bool stream=false)
Definition: SoundPrefab.cs:191
static IReadOnlyList< SoundPrefab > FlowSounds
Definition: SoundPrefab.cs:80
static readonly PrefabSelector< SoundPrefab > WaterAmbienceIn
Definition: SoundPrefab.cs:74
readonly float Volume
Definition: SoundPrefab.cs:187
static readonly PrefabCollection< SoundPrefab > Prefabs
Definition: SoundPrefab.cs:88
readonly ContentPath SoundPath
Definition: SoundPrefab.cs:183
static readonly ImmutableDictionary< Identifier, Type > TagToDerivedPrefab
Definition: SoundPrefab.cs:84
static readonly PrefabSelector< SoundPrefab > WaterAmbienceOut
Definition: SoundPrefab.cs:75
static IReadOnlyList< SoundPrefab > SplashSounds
Definition: SoundPrefab.cs:82
override Identifier DetermineIdentifier(XElement element)
Definition: SoundPrefab.cs:154
static readonly PrefabSelector< SoundPrefab > WaterAmbienceMoving
Definition: SoundPrefab.cs:76
override void Dispose()
Definition: SoundPrefab.cs:206
static readonly PrefabSelector< SoundPrefab > StartupSound
Definition: SoundPrefab.cs:77
readonly Identifier ElementName
Definition: SoundPrefab.cs:185
readonly ContentXElement Element
Definition: SoundPrefab.cs:184
virtual void Dispose()
Definition: Sound.cs:158
virtual bool IsPlaying()
Definition: Sound.cs:84
Sound LoadSound(string filename, bool stream=false)
TagNames(params string[] names)
Definition: SoundPrefab.cs:19
readonly ImmutableHashSet< Identifier > Names
Definition: SoundPrefab.cs:17
GUISoundType
Definition: GUI.cs:21