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  public Sound Sound { get; private set; }
187 
188  public SoundPrefab(ContentXElement element, SoundsFile file, bool stream = false) : base(file, element)
189  {
190  SoundPath = element.GetAttributeContentPath("file") ?? ContentPath.Empty;
191  Element = element;
192  ElementName = element.NameAsIdentifier();
193  Sound = GameMain.SoundManager.LoadSound(element, stream: stream);
194  }
195 
196  public bool IsPlaying()
197  {
198  return Sound.IsPlaying();
199  }
200 
201  public override void Dispose()
202  {
203  Sound?.Dispose(); Sound = null;
204  }
205  }
206 
207  [TagNames("damagesound")]
209  {
211 
212  //the range of inflicted damage where the sound can be played
213  //(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
214  public readonly Vector2 DamageRange;
215 
216  public readonly Identifier DamageType;
217 
218  public readonly Identifier RequiredTag;
219 
220  public bool IgnoreMuffling;
221 
222  public DamageSound(ContentXElement element, SoundsFile file) : base(element, file)
223  {
224  DamageRange = element.GetAttributeVector2("damagerange", Vector2.Zero);
225  DamageType = element.GetAttributeIdentifier("damagesoundtype", "None");
226  IgnoreMuffling = element.GetAttributeBool("ignoremuffling", false);
227  RequiredTag = element.GetAttributeIdentifier("requiredtag", "");
228  }
229  }
230 
231  [TagNames("music")]
233  {
235 
236  public readonly Identifier Type;
237  public readonly bool DuckVolume;
238  public readonly float Volume;
239 
240  public readonly Vector2 IntensityRange;
241  public readonly bool MuteIntensityTracks;
242  public readonly float? ForceIntensityTrack;
243 
244  public readonly bool StartFromRandomTime;
245  public readonly bool ContinueFromPreviousTime;
246  public int PreviousTime;
247 
248  public BackgroundMusic(ContentXElement element, SoundsFile file) : base(element, file, stream: true)
249  {
250  Type = element.GetAttributeIdentifier(nameof(Type), "");
251  IntensityRange = element.GetAttributeVector2(nameof(IntensityRange), new Vector2(0.0f, 100.0f));
252  DuckVolume = element.GetAttributeBool(nameof(DuckVolume), false);
253  MuteIntensityTracks = element.GetAttributeBool(nameof(MuteIntensityTracks), false);
254  if (element.GetAttribute(nameof(ForceIntensityTrack)) != null)
255  {
257  }
258  Volume = element.GetAttributeFloat(nameof(Volume), 1.0f);
259  StartFromRandomTime = element.GetAttributeBool(nameof(StartFromRandomTime), false);
261  }
262  }
263 
264  [TagNames("guisound")]
266  {
267  //public readonly static Dictionary<GUISoundType, List<GUISound>> GUISoundsByType = new Dictionary<GUISoundType, List<GUISound>>();
269 
270  public readonly GUISoundType Type;
271 
272  public GUISound(ContentXElement element, SoundsFile file) : base(element, file)
273  {
274  Type = element.GetAttributeEnum("guisoundtype", GUISoundType.UIMessage);
275  }
276  }
277 }
readonly bool MuteIntensityTracks
Definition: SoundPrefab.cs:241
BackgroundMusic(ContentXElement element, SoundsFile file)
Definition: SoundPrefab.cs:248
readonly Vector2 IntensityRange
Definition: SoundPrefab.cs:240
readonly bool ContinueFromPreviousTime
Definition: SoundPrefab.cs:245
readonly bool StartFromRandomTime
Definition: SoundPrefab.cs:244
readonly? float ForceIntensityTrack
Definition: SoundPrefab.cs:242
static readonly PrefabCollection< BackgroundMusic > BackgroundMusicPrefabs
Definition: SoundPrefab.cs:234
readonly Identifier Type
Definition: SoundPrefab.cs:236
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:222
readonly Identifier RequiredTag
Definition: SoundPrefab.cs:218
static readonly PrefabCollection< DamageSound > DamageSoundPrefabs
Definition: SoundPrefab.cs:210
readonly Identifier DamageType
Definition: SoundPrefab.cs:216
readonly Vector2 DamageRange
Definition: SoundPrefab.cs:214
static readonly PrefabCollection< GUISound > GUISoundPrefabs
Definition: SoundPrefab.cs:268
GUISound(ContentXElement element, SoundsFile file)
Definition: SoundPrefab.cs:272
readonly GUISoundType Type
Definition: SoundPrefab.cs:270
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:188
static IReadOnlyList< SoundPrefab > FlowSounds
Definition: SoundPrefab.cs:80
static readonly PrefabSelector< SoundPrefab > WaterAmbienceIn
Definition: SoundPrefab.cs:74
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:201
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