Unstable 0.17.0.0
This commit is contained in:
@@ -109,6 +109,14 @@ namespace OpenAL
|
||||
public const int CaptureDefaultDeviceSpecifier = 0x311;
|
||||
public const int EnumCaptureSamples = 0x312;
|
||||
public const int EnumConnected = 0x313;
|
||||
|
||||
|
||||
public const int OutputDevicesSpecifier =
|
||||
#if OSX
|
||||
DeviceSpecifier;
|
||||
#else
|
||||
AllDevicesSpecifier;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -214,7 +222,7 @@ namespace OpenAL
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public static IList<string> GetStringList(IntPtr device, int param)
|
||||
public static IReadOnlyList<string> GetStringList(IntPtr device, int param)
|
||||
{
|
||||
List<string> retVal = new List<string>();
|
||||
IntPtr strPtr = _GetString(device, param);
|
||||
@@ -224,7 +232,8 @@ namespace OpenAL
|
||||
byte currChar = Marshal.ReadByte(strPtr, strEnd);
|
||||
if (currChar == '\0') { return retVal; }
|
||||
byte prevChar = 255;
|
||||
while (true) {
|
||||
while (true)
|
||||
{
|
||||
strEnd++;
|
||||
prevChar = currChar;
|
||||
currChar = Marshal.ReadByte(strPtr, strEnd);
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace Barotrauma.Sounds
|
||||
int alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set source's gain: " + debugName + ", " + Al.GetErrorString(alError), appendStackTrace: true);
|
||||
DebugConsole.ThrowError($"Failed to set source's gain to {gain} (effective gain {effectiveGain}): {debugName}, {Al.GetErrorString(alError)}", appendStackTrace: true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Barotrauma.Sounds
|
||||
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
|
||||
playingChannels[(int)SourcePoolIndex.Voice] = new SoundChannel[16];
|
||||
|
||||
string deviceName = GameMain.Config.AudioOutputDevice;
|
||||
string deviceName = GameSettings.CurrentConfig.Audio.AudioOutputDevice;
|
||||
|
||||
if (string.IsNullOrEmpty(deviceName))
|
||||
{
|
||||
@@ -221,7 +221,10 @@ namespace Barotrauma.Sounds
|
||||
deviceName = audioDeviceNames[0];
|
||||
}
|
||||
#endif
|
||||
GameMain.Config.AudioOutputDevice = deviceName;
|
||||
if (GameSettings.CurrentConfig.Audio.AudioOutputDevice != deviceName)
|
||||
{
|
||||
SetAudioOutputDevice(deviceName);
|
||||
}
|
||||
|
||||
InitializeAlcDevice(deviceName);
|
||||
|
||||
@@ -232,6 +235,13 @@ namespace Barotrauma.Sounds
|
||||
CompressionDynamicRangeGain = 1.0f;
|
||||
}
|
||||
|
||||
private void SetAudioOutputDevice(string deviceName)
|
||||
{
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.Audio.AudioOutputDevice = deviceName;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
|
||||
public bool InitializeAlcDevice(string deviceName)
|
||||
{
|
||||
ReleaseResources(true);
|
||||
@@ -351,11 +361,11 @@ namespace Barotrauma.Sounds
|
||||
return newSound;
|
||||
}
|
||||
|
||||
public Sound LoadSound(XElement element, bool stream = false, string overrideFilePath = null)
|
||||
public Sound LoadSound(ContentXElement element, bool stream = false, string overrideFilePath = null)
|
||||
{
|
||||
if (Disabled) { return null; }
|
||||
|
||||
string filePath = overrideFilePath ?? element.GetAttributeString("file", "");
|
||||
string filePath = overrideFilePath ?? element.GetAttributeContentPath("file")?.Value ?? "";
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new System.IO.FileNotFoundException("Sound file \"" + filePath + "\" doesn't exist!");
|
||||
@@ -631,13 +641,13 @@ namespace Barotrauma.Sounds
|
||||
if (isConnected == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Playback device has been disconnected. You can select another available device in the settings.");
|
||||
GameMain.Config.AudioOutputDevice = "<disconnected>";
|
||||
SetAudioOutputDevice("<disconnected>");
|
||||
Disconnected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.Client != null && GameMain.Config.VoipAttenuationEnabled)
|
||||
if (GameMain.Client != null && GameSettings.CurrentConfig.Audio.VoipAttenuationEnabled)
|
||||
{
|
||||
if (Timing.TotalTime > lastAttenuationTime+0.2)
|
||||
{
|
||||
@@ -653,7 +663,7 @@ namespace Barotrauma.Sounds
|
||||
SetCategoryGainMultiplier("waterambience", VoipAttenuatedGain, 1);
|
||||
SetCategoryGainMultiplier("music", VoipAttenuatedGain, 1);
|
||||
|
||||
if (GameMain.Config.DynamicRangeCompressionEnabled)
|
||||
if (GameSettings.CurrentConfig.Audio.DynamicRangeCompressionEnabled)
|
||||
{
|
||||
float targetGain = (Math.Min(1.0f, 1.0f / PlaybackAmplitude) - 1.0f) * 0.5f + 1.0f;
|
||||
if (targetGain < CompressionDynamicRangeGain)
|
||||
@@ -695,6 +705,15 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySettings()
|
||||
{
|
||||
SetCategoryGainMultiplier("default", GameSettings.CurrentConfig.Audio.SoundVolume, 0);
|
||||
SetCategoryGainMultiplier("ui", GameSettings.CurrentConfig.Audio.SoundVolume, 0);
|
||||
SetCategoryGainMultiplier("waterambience", GameSettings.CurrentConfig.Audio.SoundVolume, 0);
|
||||
SetCategoryGainMultiplier("music", GameSettings.CurrentConfig.Audio.MusicVolume, 0);
|
||||
SetCategoryGainMultiplier("voip", Math.Min(GameSettings.CurrentConfig.Audio.VoiceChatVolume, 1.0f), 0);
|
||||
}
|
||||
|
||||
public void InitStreamThread()
|
||||
{
|
||||
if (Disabled) { return; }
|
||||
|
||||
@@ -9,78 +9,28 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public struct DamageSound
|
||||
{
|
||||
//the range of inflicted damage where the sound can be played
|
||||
//(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
|
||||
public readonly Vector2 damageRange;
|
||||
|
||||
public readonly string damageType;
|
||||
|
||||
public readonly Sound sound;
|
||||
|
||||
public readonly string requiredTag;
|
||||
|
||||
public bool ignoreMuffling;
|
||||
|
||||
public DamageSound(Sound sound, Vector2 damageRange, string damageType, bool ignoreMuffling, string requiredTag = "")
|
||||
{
|
||||
this.sound = sound;
|
||||
this.damageRange = damageRange;
|
||||
this.damageType = damageType;
|
||||
this.ignoreMuffling = ignoreMuffling;
|
||||
this.requiredTag = requiredTag;
|
||||
}
|
||||
}
|
||||
|
||||
public class BackgroundMusic
|
||||
{
|
||||
public readonly string File;
|
||||
public readonly string Type;
|
||||
public readonly bool DuckVolume;
|
||||
public readonly float Volume;
|
||||
|
||||
public readonly Vector2 IntensityRange;
|
||||
|
||||
public readonly bool ContinueFromPreviousTime;
|
||||
public int PreviousTime;
|
||||
|
||||
public readonly XElement Element;
|
||||
|
||||
public BackgroundMusic(XElement element)
|
||||
{
|
||||
this.File = Path.GetFullPath(element.GetAttributeString("file", "")).CleanUpPath();
|
||||
this.Type = element.GetAttributeString("type", "").ToLowerInvariant();
|
||||
this.IntensityRange = element.GetAttributeVector2("intensityrange", new Vector2(0.0f, 100.0f));
|
||||
this.DuckVolume = element.GetAttributeBool("duckvolume", false);
|
||||
this.Volume = element.GetAttributeFloat("volume", 1.0f);
|
||||
this.ContinueFromPreviousTime = element.GetAttributeBool("continuefromprevioustime", false);
|
||||
this.Element = element;
|
||||
}
|
||||
}
|
||||
|
||||
static class SoundPlayer
|
||||
{
|
||||
private static ILookup<string, Sound> miscSounds;
|
||||
|
||||
//music
|
||||
private const float MusicLerpSpeed = 1.0f;
|
||||
private const float UpdateMusicInterval = 5.0f;
|
||||
|
||||
const int MaxMusicChannels = 6;
|
||||
|
||||
private readonly static Sound[] currentMusic = new Sound[MaxMusicChannels];
|
||||
private readonly static BackgroundMusic[] currentMusic = new BackgroundMusic[MaxMusicChannels];
|
||||
private readonly static SoundChannel[] musicChannel = new SoundChannel[MaxMusicChannels];
|
||||
private readonly static BackgroundMusic[] targetMusic = new BackgroundMusic[MaxMusicChannels];
|
||||
private static List<BackgroundMusic> musicClips;
|
||||
private static IEnumerable<BackgroundMusic> musicClips => BackgroundMusic.BackgroundMusicPrefabs;
|
||||
|
||||
private static BackgroundMusic previousDefaultMusic;
|
||||
|
||||
private static float updateMusicTimer;
|
||||
|
||||
//ambience
|
||||
private static Sound waterAmbienceIn, waterAmbienceOut, waterAmbienceMoving;
|
||||
private static readonly SoundChannel[] waterAmbienceChannels = new SoundChannel[3];
|
||||
private static Sound waterAmbienceIn => SoundPrefab.WaterAmbienceIn.ActivePrefab.Sound;
|
||||
private static Sound waterAmbienceOut => SoundPrefab.WaterAmbienceOut.ActivePrefab.Sound;
|
||||
private static Sound waterAmbienceMoving => SoundPrefab.WaterAmbienceMoving.ActivePrefab.Sound;
|
||||
private static readonly HashSet<SoundChannel> waterAmbienceChannels = new HashSet<SoundChannel>();
|
||||
|
||||
private static float ambientSoundTimer;
|
||||
private static Vector2 ambientSoundInterval = new Vector2(20.0f, 40.0f); //x = min, y = max
|
||||
@@ -92,8 +42,8 @@ namespace Barotrauma
|
||||
|
||||
//misc
|
||||
private static float[] targetFlowLeft, targetFlowRight;
|
||||
public static List<Sound> FlowSounds = new List<Sound>();
|
||||
public static List<Sound> SplashSounds = new List<Sound>();
|
||||
public static IReadOnlyList<SoundPrefab> FlowSounds => SoundPrefab.FlowSounds;
|
||||
public static IReadOnlyList<SoundPrefab> SplashSounds => SoundPrefab.SplashSounds;
|
||||
private static SoundChannel[] flowSoundChannels;
|
||||
private static float[] flowVolumeLeft;
|
||||
private static float[] flowVolumeRight;
|
||||
@@ -112,17 +62,13 @@ namespace Barotrauma
|
||||
private static string[] fireSoundTags = new string[fireSizes] { "fire", "firemedium", "firelarge" };
|
||||
|
||||
// TODO: could use a dictionary to split up the list into smaller lists of same type?
|
||||
private static List<DamageSound> damageSounds;
|
||||
|
||||
private static Dictionary<GUISoundType, List<Sound>> guiSounds;
|
||||
private static IEnumerable<DamageSound> damageSounds => DamageSound.DamageSoundPrefabs;
|
||||
|
||||
private static bool firstTimeInMainMenu = true;
|
||||
|
||||
private static Sound startUpSound;
|
||||
private static Sound startUpSound => SoundPrefab.StartupSound.ActivePrefab.Sound;
|
||||
|
||||
public static bool Initialized;
|
||||
|
||||
public static string OverrideMusicType
|
||||
public static Identifier OverrideMusicType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -130,291 +76,35 @@ namespace Barotrauma
|
||||
|
||||
public static float? OverrideMusicDuration;
|
||||
|
||||
public static int SoundCount;
|
||||
|
||||
private static List<XElement> loadedSoundElements;
|
||||
|
||||
private static bool SoundElementsEquivalent(XElement a, XElement b)
|
||||
{
|
||||
string filePathA = a.GetAttributeString("file", "").CleanUpPath();
|
||||
float baseGainA = a.GetAttributeFloat("volume", 1.0f);
|
||||
float rangeA = a.GetAttributeFloat("range", 1000.0f);
|
||||
string filePathB = b.GetAttributeString("file", "").CleanUpPath();
|
||||
float baseGainB = b.GetAttributeFloat("volume", 1.0f);
|
||||
float rangeB = b.GetAttributeFloat("range", 1000.0f);
|
||||
return a.Name.ToString().Equals(b.Name.ToString(), StringComparison.OrdinalIgnoreCase) &&
|
||||
filePathA == filePathB && MathUtils.NearlyEqual(baseGainA, baseGainB) &&
|
||||
MathUtils.NearlyEqual(rangeA, rangeB);
|
||||
}
|
||||
|
||||
public static IEnumerable<CoroutineStatus> Init()
|
||||
{
|
||||
OverrideMusicType = null;
|
||||
|
||||
var soundFiles = GameMain.Instance.GetFilesOfType(ContentType.Sounds).ToList();
|
||||
|
||||
List<XElement> soundElements = new List<XElement>();
|
||||
foreach (ContentFile soundFile in soundFiles)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(soundFile.Path);
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
DebugConsole.NewMessage($"Overriding all sounds with {soundFile.Path}", Color.Yellow);
|
||||
soundElements.Clear();
|
||||
}
|
||||
soundElements.AddRange(mainElement.Elements());
|
||||
}
|
||||
|
||||
SoundCount = 1 + soundElements.Count();
|
||||
|
||||
var startUpSoundElement = soundElements.Find(e => e.Name.ToString().Equals("startupsound", StringComparison.OrdinalIgnoreCase));
|
||||
if (startUpSoundElement != null)
|
||||
{
|
||||
startUpSound = GameMain.SoundManager.LoadSound(startUpSoundElement, false);
|
||||
startUpSound?.Play();
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
List<KeyValuePair<string, Sound>> miscSoundList = new List<KeyValuePair<string, Sound>>();
|
||||
damageSounds ??= new List<DamageSound>();
|
||||
musicClips ??= new List<BackgroundMusic>();
|
||||
guiSounds ??= new Dictionary<GUISoundType, List<Sound>>();
|
||||
|
||||
bool firstWaterAmbienceLoaded = false;
|
||||
|
||||
foreach (XElement soundElement in soundElements)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (loadedSoundElements != null && loadedSoundElements.Any(e => SoundElementsEquivalent(e, soundElement)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (soundElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "music":
|
||||
var newMusicClip = new BackgroundMusic(soundElement);
|
||||
if (File.Exists(newMusicClip.File))
|
||||
{
|
||||
musicClips.AddIfNotNull(newMusicClip);
|
||||
if (loadedSoundElements != null)
|
||||
{
|
||||
if (newMusicClip.Type.Equals("menu", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
targetMusic[0] = newMusicClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.NewMessage($"Music file \"{newMusicClip.File}\" not found.");
|
||||
}
|
||||
break;
|
||||
case "splash":
|
||||
SplashSounds.AddIfNotNull(GameMain.SoundManager.LoadSound(soundElement, false));
|
||||
break;
|
||||
case "flow":
|
||||
FlowSounds.AddIfNotNull(GameMain.SoundManager.LoadSound(soundElement, false));
|
||||
break;
|
||||
case "waterambience":
|
||||
//backwards compatibility (1st waterambience used to be played both inside and outside, 2nd when moving)
|
||||
if (!firstWaterAmbienceLoaded)
|
||||
{
|
||||
waterAmbienceIn?.Dispose();
|
||||
waterAmbienceOut?.Dispose();
|
||||
if (File.Exists(soundElement.GetAttributeString("file", "")))
|
||||
{
|
||||
waterAmbienceIn = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
waterAmbienceOut = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
waterAmbienceIn = GameMain.SoundManager.LoadSound(soundElement, false, "Content/Sounds/Water/WaterAmbienceIn.ogg");
|
||||
waterAmbienceOut = GameMain.SoundManager.LoadSound(soundElement, false, "Content/Sounds/Water/WaterAmbienceOut.ogg");
|
||||
}
|
||||
firstWaterAmbienceLoaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterAmbienceMoving?.Dispose();
|
||||
if (File.Exists(soundElement.GetAttributeString("file", "")))
|
||||
{
|
||||
waterAmbienceMoving = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
waterAmbienceMoving = GameMain.SoundManager.LoadSound(soundElement, false, "Content/Sounds/Water/WaterAmbienceMoving.ogg");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "waterambiencein":
|
||||
waterAmbienceIn?.Dispose();
|
||||
waterAmbienceIn = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
break;
|
||||
case "waterambienceout":
|
||||
waterAmbienceOut?.Dispose();
|
||||
waterAmbienceOut = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
break;
|
||||
case "waterambiencemoving":
|
||||
waterAmbienceMoving?.Dispose();
|
||||
waterAmbienceMoving = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
break;
|
||||
case "damagesound":
|
||||
Sound damageSound = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
if (damageSound == null) { continue; }
|
||||
|
||||
string damageSoundType = soundElement.GetAttributeString("damagesoundtype", "None");
|
||||
damageSounds.Add(new DamageSound(
|
||||
damageSound,
|
||||
soundElement.GetAttributeVector2("damagerange", Vector2.Zero),
|
||||
damageSoundType,
|
||||
soundElement.GetAttributeBool("ignoremuffling", false),
|
||||
soundElement.GetAttributeString("requiredtag", "")));
|
||||
|
||||
break;
|
||||
case "guisound":
|
||||
Sound guiSound = GameMain.SoundManager.LoadSound(soundElement, stream: false);
|
||||
if (guiSound == null) { continue; }
|
||||
if (Enum.TryParse(soundElement.GetAttributeString("guisoundtype", null), true, out GUISoundType soundType))
|
||||
{
|
||||
if (guiSounds.ContainsKey(soundType))
|
||||
{
|
||||
guiSounds[soundType].Add(guiSound);
|
||||
}
|
||||
else
|
||||
{
|
||||
guiSounds.Add(soundType, new List<Sound>() { guiSound });
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Sound sound = GameMain.SoundManager.LoadSound(soundElement, false);
|
||||
if (sound != null)
|
||||
{
|
||||
miscSoundList.Add(new KeyValuePair<string, Sound>(soundElement.Name.ToString().ToLowerInvariant(), sound));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (System.IO.FileNotFoundException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while initializing SoundPlayer.", e);
|
||||
}
|
||||
}
|
||||
|
||||
musicClips.RemoveAll(mc => !soundElements.Any(e => SoundElementsEquivalent(mc.Element, e)));
|
||||
|
||||
for (int i = 0; i < currentMusic.Length; i++)
|
||||
{
|
||||
if (currentMusic[i] != null && !musicClips.Any(mc => mc.File == currentMusic[i].Filename))
|
||||
{
|
||||
DisposeMusicChannel(i);
|
||||
}
|
||||
}
|
||||
|
||||
SplashSounds.ForEach(s =>
|
||||
{
|
||||
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
|
||||
});
|
||||
SplashSounds.RemoveAll(s => s.Disposed);
|
||||
|
||||
FlowSounds.ForEach(s =>
|
||||
{
|
||||
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
|
||||
});
|
||||
FlowSounds.RemoveAll(s => s.Disposed);
|
||||
|
||||
damageSounds.ForEach(s =>
|
||||
{
|
||||
if (!soundElements.Any(e => SoundElementsEquivalent(s.sound.XElement, e))) { s.sound.Dispose(); }
|
||||
});
|
||||
damageSounds.RemoveAll(s => s.sound.Disposed);
|
||||
|
||||
guiSounds.ForEach(kvp =>
|
||||
{
|
||||
kvp.Value?.ForEach(s =>
|
||||
{
|
||||
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
|
||||
});
|
||||
});
|
||||
guiSounds.ForEach(kvp => kvp.Value?.RemoveAll(s => s.Disposed));
|
||||
|
||||
miscSounds?.ForEach(g => g.ForEach(s =>
|
||||
{
|
||||
if (!soundElements.Any(e => SoundElementsEquivalent(s.XElement, e))) { s.Dispose(); }
|
||||
else { miscSoundList.Add(new KeyValuePair<string, Sound>(g.Key, s)); }
|
||||
}));
|
||||
|
||||
flowSoundChannels?.ForEach(ch => ch?.Dispose());
|
||||
flowSoundChannels = new SoundChannel[FlowSounds.Count];
|
||||
flowVolumeLeft = new float[FlowSounds.Count];
|
||||
flowVolumeRight = new float[FlowSounds.Count];
|
||||
targetFlowLeft = new float[FlowSounds.Count];
|
||||
targetFlowRight = new float[FlowSounds.Count];
|
||||
|
||||
fireSoundChannels?.ForEach(ch => ch?.Dispose());
|
||||
fireSoundChannels = new SoundChannel[fireSizes];
|
||||
fireVolumeLeft = new float[fireSizes];
|
||||
fireVolumeRight = new float[fireSizes];
|
||||
|
||||
miscSounds = miscSoundList.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
Initialized = true;
|
||||
|
||||
loadedSoundElements = soundElements;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
{
|
||||
if (!Initialized) { return; }
|
||||
|
||||
UpdateMusic(deltaTime);
|
||||
|
||||
if (startUpSound != null && !GameMain.SoundManager.IsPlaying(startUpSound))
|
||||
if (flowSoundChannels == null || flowSoundChannels.Length != FlowSounds.Count)
|
||||
{
|
||||
startUpSound.Dispose();
|
||||
startUpSound = null;
|
||||
flowSoundChannels = new SoundChannel[FlowSounds.Count];
|
||||
flowVolumeLeft = new float[FlowSounds.Count];
|
||||
flowVolumeRight = new float[FlowSounds.Count];
|
||||
targetFlowLeft = new float[FlowSounds.Count];
|
||||
targetFlowRight = new float[FlowSounds.Count];
|
||||
}
|
||||
|
||||
if (fireSoundChannels == null || fireSoundChannels.Length != fireSizes)
|
||||
{
|
||||
fireSoundChannels = new SoundChannel[fireSizes];
|
||||
fireVolumeLeft = new float[fireSizes];
|
||||
fireVolumeRight = new float[fireSizes];
|
||||
}
|
||||
|
||||
//stop water sounds if no sub is loaded
|
||||
if (Submarine.MainSub == null || Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
for (int i = 0; i < waterAmbienceChannels.Length; i++)
|
||||
foreach (var chn in waterAmbienceChannels.Concat(flowSoundChannels).Concat(fireSoundChannels))
|
||||
{
|
||||
if (waterAmbienceChannels[i] == null) { continue; }
|
||||
waterAmbienceChannels[i].FadeOutAndDispose();
|
||||
waterAmbienceChannels[i] = null;
|
||||
}
|
||||
for (int i = 0; i < FlowSounds.Count; i++)
|
||||
{
|
||||
if (flowSoundChannels[i] == null) { continue; }
|
||||
flowSoundChannels[i].FadeOutAndDispose();
|
||||
flowSoundChannels[i] = null;
|
||||
}
|
||||
for (int i = 0; i < fireSoundChannels.Length; i++)
|
||||
{
|
||||
if (fireSoundChannels[i] == null) { continue; }
|
||||
fireSoundChannels[i].FadeOutAndDispose();
|
||||
fireSoundChannels[i] = null;
|
||||
chn?.FadeOutAndDispose();
|
||||
}
|
||||
fireVolumeLeft[0] = 0.0f; fireVolumeLeft[1] = 0.0f;
|
||||
fireVolumeRight[0] = 0.0f; fireVolumeRight[1] = 0.0f;
|
||||
if (hullSoundChannel != null)
|
||||
{
|
||||
hullSoundChannel.FadeOutAndDispose();
|
||||
hullSoundChannel = null;
|
||||
hullSoundSource = null;
|
||||
}
|
||||
hullSoundChannel?.FadeOutAndDispose();
|
||||
hullSoundSource = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -482,44 +172,29 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
void updateWaterAmbience(Sound sound, float volume)
|
||||
{
|
||||
float volume = 0.0f;
|
||||
Sound sound = null;
|
||||
switch (i)
|
||||
SoundChannel chn = waterAmbienceChannels.FirstOrDefault(c => c.Sound == sound);
|
||||
if (chn is null || !chn.IsPlaying)
|
||||
{
|
||||
case 0:
|
||||
volume = ambienceVolume * (1.0f - movementSoundVolume) * insideSubFactor;
|
||||
sound = waterAmbienceIn;
|
||||
break;
|
||||
case 1:
|
||||
volume = ambienceVolume * movementSoundVolume * insideSubFactor;
|
||||
sound = waterAmbienceMoving;
|
||||
break;
|
||||
case 2:
|
||||
volume = 1.0f - insideSubFactor;
|
||||
sound = waterAmbienceOut;
|
||||
break;
|
||||
if (!(chn is null)) { waterAmbienceChannels.Remove(chn); }
|
||||
chn = sound.Play(volume, "waterambience");
|
||||
chn.Looping = true;
|
||||
waterAmbienceChannels.Add(chn);
|
||||
}
|
||||
|
||||
if (sound == null) { continue; }
|
||||
|
||||
// Consider the volume set in sounds.xml
|
||||
volume *= sound.BaseGain;
|
||||
if ((waterAmbienceChannels[i] == null || !waterAmbienceChannels[i].IsPlaying) && volume > 0.01f)
|
||||
else
|
||||
{
|
||||
waterAmbienceChannels[i] = sound.Play(volume, "waterambience");
|
||||
waterAmbienceChannels[i].Looping = true;
|
||||
}
|
||||
else if (waterAmbienceChannels[i] != null)
|
||||
{
|
||||
waterAmbienceChannels[i].Gain += deltaTime * Math.Sign(volume - waterAmbienceChannels[i].Gain);
|
||||
if (waterAmbienceChannels[i].Gain < 0.01f)
|
||||
chn.Gain += deltaTime * Math.Sign(volume - chn.Gain);
|
||||
if (chn.Gain < 0.01f)
|
||||
{
|
||||
waterAmbienceChannels[i].FadeOutAndDispose();
|
||||
chn.FadeOutAndDispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateWaterAmbience(waterAmbienceIn, ambienceVolume * (1.0f - movementSoundVolume) * insideSubFactor);
|
||||
updateWaterAmbience(waterAmbienceMoving, ambienceVolume * movementSoundVolume * insideSubFactor);
|
||||
updateWaterAmbience(waterAmbienceOut, 1.0f - insideSubFactor);
|
||||
}
|
||||
|
||||
private static void UpdateWaterFlowSounds(float deltaTime)
|
||||
@@ -606,7 +281,7 @@ namespace Barotrauma
|
||||
Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (flowVolumeRight[i] - flowVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y);
|
||||
if (flowSoundChannels[i] == null || !flowSoundChannels[i].IsPlaying)
|
||||
{
|
||||
flowSoundChannels[i] = FlowSounds[i].Play(1.0f, FlowSoundRange, soundPos);
|
||||
flowSoundChannels[i] = FlowSounds[i].Sound.Play(1.0f, FlowSoundRange, soundPos);
|
||||
flowSoundChannels[i].Looping = true;
|
||||
}
|
||||
flowSoundChannels[i].Gain = Math.Min(Math.Max(flowVolumeRight[i], flowVolumeLeft[i]), 1.0f);
|
||||
@@ -624,7 +299,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y);
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
foreach (FireSource fs in hull.FireSources)
|
||||
{
|
||||
@@ -760,10 +435,10 @@ namespace Barotrauma
|
||||
|
||||
public static Sound GetSound(string soundTag)
|
||||
{
|
||||
var matchingSounds = miscSounds[soundTag].ToList();
|
||||
if (matchingSounds.Count == 0) return null;
|
||||
var matchingSounds = SoundPrefab.Prefabs.Where(p => p.ElementName == soundTag);
|
||||
if (!matchingSounds.Any()) return null;
|
||||
|
||||
return matchingSounds[Rand.Int(matchingSounds.Count)];
|
||||
return matchingSounds.GetRandomUnsynced().Sound;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -806,14 +481,14 @@ namespace Barotrauma
|
||||
|
||||
private static void UpdateMusic(float deltaTime)
|
||||
{
|
||||
if (musicClips == null || GameMain.SoundManager.Disabled) { return; }
|
||||
if (musicClips == null || (GameMain.SoundManager?.Disabled ?? true)) { return; }
|
||||
|
||||
if (OverrideMusicType != null && OverrideMusicDuration.HasValue)
|
||||
{
|
||||
OverrideMusicDuration -= deltaTime;
|
||||
if (OverrideMusicDuration <= 0.0f)
|
||||
{
|
||||
OverrideMusicType = null;
|
||||
OverrideMusicType = Identifier.Empty;
|
||||
OverrideMusicDuration = null;
|
||||
}
|
||||
}
|
||||
@@ -824,7 +499,7 @@ namespace Barotrauma
|
||||
if (updateMusicTimer <= 0.0f)
|
||||
{
|
||||
//find appropriate music for the current situation
|
||||
string currentMusicType = GetCurrentMusicType();
|
||||
Identifier currentMusicType = GetCurrentMusicType();
|
||||
float currentIntensity = GameMain.GameSession?.EventManager != null ?
|
||||
GameMain.GameSession.EventManager.MusicIntensity * 100.0f : 0.0f;
|
||||
|
||||
@@ -835,13 +510,13 @@ namespace Barotrauma
|
||||
targetMusic[mainTrackIndex] = null;
|
||||
}
|
||||
//switch the music if nothing playing atm or the currently playing clip is not suitable anymore
|
||||
else if (targetMusic[mainTrackIndex] == null || currentMusic[mainTrackIndex] == null || !currentMusic[mainTrackIndex].IsPlaying() || !suitableMusic.Any(m => m.File == currentMusic[mainTrackIndex].Filename))
|
||||
else if (targetMusic[mainTrackIndex] == null || currentMusic[mainTrackIndex] == null || !currentMusic[mainTrackIndex].IsPlaying() || !suitableMusic.Any(m => m == currentMusic[mainTrackIndex]))
|
||||
{
|
||||
if (currentMusicType == "default")
|
||||
{
|
||||
if (previousDefaultMusic == null)
|
||||
{
|
||||
targetMusic[mainTrackIndex] = previousDefaultMusic = suitableMusic.GetRandom();
|
||||
targetMusic[mainTrackIndex] = previousDefaultMusic = suitableMusic.GetRandomUnsynced();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -850,7 +525,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
targetMusic[mainTrackIndex] = suitableMusic.GetRandom();
|
||||
targetMusic[mainTrackIndex] = suitableMusic.GetRandomUnsynced();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,16 +533,16 @@ namespace Barotrauma
|
||||
{
|
||||
// Find background noise loop for the current biome
|
||||
IEnumerable<BackgroundMusic> suitableNoiseLoops = Screen.Selected == GameMain.GameScreen ?
|
||||
GetSuitableMusicClips(Level.Loaded.LevelData?.Biome?.Identifier, currentIntensity) :
|
||||
GetSuitableMusicClips(Level.Loaded.LevelData.Biome.Identifier, currentIntensity) :
|
||||
Enumerable.Empty<BackgroundMusic>();
|
||||
if (suitableNoiseLoops.Count() == 0)
|
||||
{
|
||||
targetMusic[noiseLoopIndex] = null;
|
||||
}
|
||||
// Switch the noise loop if nothing playing atm or the currently playing clip is not suitable anymore
|
||||
else if (targetMusic[noiseLoopIndex] == null || currentMusic[noiseLoopIndex] == null || !suitableNoiseLoops.Any(m => m.File == currentMusic[noiseLoopIndex].Filename))
|
||||
else if (targetMusic[noiseLoopIndex] == null || currentMusic[noiseLoopIndex] == null || !suitableNoiseLoops.Any(m => m == currentMusic[noiseLoopIndex]))
|
||||
{
|
||||
targetMusic[noiseLoopIndex] = suitableNoiseLoops.GetRandom();
|
||||
targetMusic[noiseLoopIndex] = suitableNoiseLoops.GetRandomUnsynced();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -875,35 +550,36 @@ namespace Barotrauma
|
||||
targetMusic[noiseLoopIndex] = null;
|
||||
}
|
||||
|
||||
IEnumerable<BackgroundMusic> suitableTypeAmbiences = GetSuitableMusicClips($"{currentMusicType}ambience", currentIntensity);
|
||||
IEnumerable<BackgroundMusic> suitableTypeAmbiences = GetSuitableMusicClips($"{currentMusicType}ambience".ToIdentifier(), currentIntensity);
|
||||
int typeAmbienceTrackIndex = 2;
|
||||
if (suitableTypeAmbiences.None())
|
||||
{
|
||||
targetMusic[typeAmbienceTrackIndex] = null;
|
||||
}
|
||||
// Switch the type ambience if nothing playing atm or the currently playing clip is not suitable anymore
|
||||
else if (targetMusic[typeAmbienceTrackIndex] == null || currentMusic[typeAmbienceTrackIndex] == null || !currentMusic[typeAmbienceTrackIndex].IsPlaying() || suitableTypeAmbiences.None(m => m.File == currentMusic[typeAmbienceTrackIndex].Filename))
|
||||
else if (targetMusic[typeAmbienceTrackIndex] == null || currentMusic[typeAmbienceTrackIndex] == null || !currentMusic[typeAmbienceTrackIndex].IsPlaying() || suitableTypeAmbiences.None(m => m == currentMusic[typeAmbienceTrackIndex]))
|
||||
{
|
||||
targetMusic[typeAmbienceTrackIndex] = suitableTypeAmbiences.GetRandom();
|
||||
targetMusic[typeAmbienceTrackIndex] = suitableTypeAmbiences.GetRandomUnsynced();
|
||||
}
|
||||
|
||||
//get the appropriate intensity layers for current situation
|
||||
IEnumerable<BackgroundMusic> suitableIntensityMusic = Screen.Selected == GameMain.GameScreen ?
|
||||
GetSuitableMusicClips("intensity", currentIntensity) :
|
||||
GetSuitableMusicClips("intensity".ToIdentifier(), currentIntensity) :
|
||||
Enumerable.Empty<BackgroundMusic>();
|
||||
int intensityTrackStartIndex = 3;
|
||||
for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
|
||||
{
|
||||
//disable targetmusics that aren't suitable anymore
|
||||
if (targetMusic[i] != null && !suitableIntensityMusic.Any(m => m.File == targetMusic[i].File))
|
||||
if (targetMusic[i] != null && !suitableIntensityMusic.Any(m => m == targetMusic[i]))
|
||||
{
|
||||
targetMusic[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (BackgroundMusic intensityMusic in suitableIntensityMusic)
|
||||
{
|
||||
//already playing, do nothing
|
||||
if (targetMusic.Any(m => m != null && m.File == intensityMusic.File)) { continue; }
|
||||
if (targetMusic.Any(m => m != null && m == intensityMusic)) { continue; }
|
||||
|
||||
for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
|
||||
{
|
||||
@@ -932,12 +608,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
//something should be playing, but the targetMusic is invalid
|
||||
else if (!musicClips.Any(mc => mc.File == targetMusic[i].File))
|
||||
else if (!musicClips.Any(mc => mc == targetMusic[i]))
|
||||
{
|
||||
targetMusic[i] = GetSuitableMusicClips(targetMusic[i].Type, 0.0f).GetRandom();
|
||||
targetMusic[i] = GetSuitableMusicClips(targetMusic[i].Type, 0.0f).GetRandomUnsynced();
|
||||
}
|
||||
//something should be playing, but the channel is playing nothing or an incorrect clip
|
||||
else if (currentMusic[i] == null || targetMusic[i].File != currentMusic[i].Filename)
|
||||
else if (currentMusic[i] == null || targetMusic[i] != currentMusic[i])
|
||||
{
|
||||
//something playing -> mute it first
|
||||
if (musicChannel[i] != null && musicChannel[i].IsPlaying)
|
||||
@@ -949,18 +625,9 @@ namespace Barotrauma
|
||||
if (currentMusic[i] == null || (musicChannel[i] == null || !musicChannel[i].IsPlaying))
|
||||
{
|
||||
DisposeMusicChannel(i);
|
||||
try
|
||||
{
|
||||
currentMusic[i] = GameMain.SoundManager.LoadSound(targetMusic[i].File, true);
|
||||
}
|
||||
catch (System.IO.InvalidDataException e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to load the music clip \"{targetMusic[i].File}\".", e);
|
||||
musicClips.Remove(targetMusic[i]);
|
||||
targetMusic[i] = null;
|
||||
break;
|
||||
}
|
||||
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music");
|
||||
|
||||
currentMusic[i] = targetMusic[i];
|
||||
musicChannel[i] = currentMusic[i].Sound.Play(0.0f, i == noiseLoopIndex ? "default" : "music");
|
||||
if (targetMusic[i].ContinueFromPreviousTime)
|
||||
{
|
||||
musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime;
|
||||
@@ -974,7 +641,7 @@ namespace Barotrauma
|
||||
if (musicChannel[i] == null || !musicChannel[i].IsPlaying)
|
||||
{
|
||||
musicChannel[i]?.Dispose();
|
||||
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music");
|
||||
musicChannel[i] = currentMusic[i].Sound.Play(0.0f, i == noiseLoopIndex ? "default" : "music");
|
||||
musicChannel[i].Looping = true;
|
||||
}
|
||||
float targetGain = targetMusic[i].Volume;
|
||||
@@ -989,17 +656,17 @@ namespace Barotrauma
|
||||
|
||||
private static void DisposeMusicChannel(int index)
|
||||
{
|
||||
var clip = musicClips.Find(m => m.File == musicChannel[index]?.Sound?.Filename);
|
||||
var clip = musicClips.FirstOrDefault(m => m.Sound == musicChannel[index]?.Sound);
|
||||
if (clip != null)
|
||||
{
|
||||
if (clip.ContinueFromPreviousTime) { clip.PreviousTime = musicChannel[index].StreamSeekPos; }
|
||||
}
|
||||
|
||||
musicChannel[index]?.Dispose(); musicChannel[index] = null;
|
||||
currentMusic[index]?.Dispose(); currentMusic[index] = null;
|
||||
currentMusic[index] = null;
|
||||
}
|
||||
|
||||
private static IEnumerable<BackgroundMusic> GetSuitableMusicClips(string musicType, float currentIntensity)
|
||||
private static IEnumerable<BackgroundMusic> GetSuitableMusicClips(Identifier musicType, float currentIntensity)
|
||||
{
|
||||
return musicClips.Where(music =>
|
||||
music != null &&
|
||||
@@ -1008,28 +675,22 @@ namespace Barotrauma
|
||||
currentIntensity <= music.IntensityRange.Y);
|
||||
}
|
||||
|
||||
private static string GetCurrentMusicType()
|
||||
private static Identifier GetCurrentMusicType()
|
||||
{
|
||||
if (OverrideMusicType != null) { return OverrideMusicType; }
|
||||
|
||||
if (Screen.Selected == null) { return "menu"; }
|
||||
if (Screen.Selected == null) { return "menu".ToIdentifier(); }
|
||||
|
||||
if (Screen.Selected == GameMain.CharacterEditorScreen ||
|
||||
Screen.Selected == GameMain.LevelEditorScreen ||
|
||||
Screen.Selected == GameMain.ParticleEditorScreen ||
|
||||
Screen.Selected == GameMain.SpriteEditorScreen ||
|
||||
Screen.Selected == GameMain.SubEditorScreen ||
|
||||
Screen.Selected == GameMain.EventEditorScreen ||
|
||||
(Screen.Selected == GameMain.GameScreen && GameMain.GameSession?.GameMode is TestGameMode) ||
|
||||
Screen.Selected == GameMain.NetLobbyScreen)
|
||||
if ((Screen.Selected?.IsEditor ?? false)
|
||||
|| (Screen.Selected == GameMain.NetLobbyScreen))
|
||||
{
|
||||
return "editor";
|
||||
return "editor".ToIdentifier();
|
||||
}
|
||||
|
||||
if (Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
previousDefaultMusic = null;
|
||||
return firstTimeInMainMenu ? "menu" : "default";
|
||||
return (firstTimeInMainMenu ? "menu" : "default").ToIdentifier();
|
||||
}
|
||||
|
||||
firstTimeInMainMenu = false;
|
||||
@@ -1040,21 +701,21 @@ namespace Barotrauma
|
||||
if (Level.Loaded != null && Level.Loaded.Ruins != null &&
|
||||
Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition)))
|
||||
{
|
||||
return "ruins";
|
||||
return "ruins".ToIdentifier();
|
||||
}
|
||||
|
||||
if (Character.Controlled.Submarine?.Info?.IsWreck ?? false)
|
||||
{
|
||||
return "wreck";
|
||||
return "wreck".ToIdentifier();
|
||||
}
|
||||
|
||||
if (Level.IsLoadedOutpost)
|
||||
{
|
||||
// Only return music type for location types which have music tracks defined
|
||||
var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant();
|
||||
if (!string.IsNullOrEmpty(locationType) && musicClips.Any(c => c.Type == locationType))
|
||||
var locationType = Level.Loaded.StartLocation?.Type?.Identifier;
|
||||
if (locationType.HasValue && locationType != Identifier.Empty && musicClips.Any(c => c.Type == locationType))
|
||||
{
|
||||
return locationType;
|
||||
return locationType.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1062,26 +723,26 @@ namespace Barotrauma
|
||||
Submarine targetSubmarine = Character.Controlled?.Submarine;
|
||||
if (targetSubmarine != null && targetSubmarine.AtDamageDepth)
|
||||
{
|
||||
return "deep";
|
||||
return "deep".ToIdentifier();
|
||||
}
|
||||
if (GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && Submarine.MainSub != null &&
|
||||
Level.Loaded != null && Level.Loaded.GetRealWorldDepth(GameMain.GameScreen.Cam.Position.Y) > Submarine.MainSub.RealWorldCrushDepth)
|
||||
{
|
||||
return "deep";
|
||||
return "deep".ToIdentifier();
|
||||
}
|
||||
|
||||
if (targetSubmarine != null)
|
||||
{
|
||||
float floodedArea = 0.0f;
|
||||
float totalArea = 0.0f;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != targetSubmarine) { continue; }
|
||||
floodedArea += hull.WaterVolume;
|
||||
totalArea += hull.Volume;
|
||||
}
|
||||
|
||||
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) { return "flooded"; }
|
||||
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) { return "flooded".ToIdentifier(); }
|
||||
}
|
||||
|
||||
float enemyDistThreshold = 5000.0f;
|
||||
@@ -1100,14 +761,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, targetSubmarine.WorldPosition) < enemyDistThreshold * enemyDistThreshold)
|
||||
{
|
||||
return "monster";
|
||||
return "monster".ToIdentifier();
|
||||
}
|
||||
}
|
||||
else if (Character.Controlled != null)
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, Character.Controlled.WorldPosition) < enemyDistThreshold * enemyDistThreshold)
|
||||
{
|
||||
return "monster";
|
||||
return "monster".ToIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1116,16 +777,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (Submarine.Loaded != null && Level.Loaded != null && Submarine.MainSub != null && Submarine.MainSub.AtEndExit)
|
||||
{
|
||||
return "levelend";
|
||||
return "levelend".ToIdentifier();
|
||||
}
|
||||
if (Timing.TotalTime < GameMain.GameSession.RoundStartTime + 120.0 &&
|
||||
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
|
||||
{
|
||||
return "start";
|
||||
return "start".ToIdentifier();
|
||||
}
|
||||
}
|
||||
|
||||
return "default";
|
||||
return "default".ToIdentifier();
|
||||
}
|
||||
|
||||
public static bool ShouldMuffleSound(Character listener, Vector2 soundWorldPos, float range, Hull hullGuess)
|
||||
@@ -1159,7 +820,7 @@ namespace Barotrauma
|
||||
if (SplashSounds.Count == 0) { return; }
|
||||
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2.0f, 2.0f)), 0, SplashSounds.Count - 1);
|
||||
float range = 800.0f;
|
||||
var channel = SplashSounds[splashIndex].Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
|
||||
var channel = SplashSounds[splashIndex].Sound.Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
|
||||
}
|
||||
|
||||
public static void PlayDamageSound(string damageType, float damage, PhysicsBody body)
|
||||
@@ -1169,35 +830,29 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private static readonly List<DamageSound> tempList = new List<DamageSound>();
|
||||
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable<string> tags = null)
|
||||
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable<Identifier> tags = null)
|
||||
{
|
||||
damage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
|
||||
tempList.Clear();
|
||||
foreach (var s in damageSounds)
|
||||
{
|
||||
if ((s.damageRange == Vector2.Zero ||
|
||||
(damage >= s.damageRange.X && damage <= s.damageRange.Y)) &&
|
||||
string.Equals(s.damageType, damageType, StringComparison.OrdinalIgnoreCase) &&
|
||||
(string.IsNullOrEmpty(s.requiredTag) || (tags == null ? string.IsNullOrEmpty(s.requiredTag) : tags.Contains(s.requiredTag))))
|
||||
if ((s.DamageRange == Vector2.Zero ||
|
||||
(damage >= s.DamageRange.X && damage <= s.DamageRange.Y)) &&
|
||||
s.DamageType == damageType &&
|
||||
(s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))))
|
||||
{
|
||||
tempList.Add(s);
|
||||
}
|
||||
}
|
||||
var damageSound = tempList.GetRandom();
|
||||
if (damageSound.sound != null)
|
||||
{
|
||||
damageSound.sound.Play(1.0f, range, position, muffle: !damageSound.ignoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
|
||||
}
|
||||
var damageSound = tempList.GetRandomUnsynced();
|
||||
damageSound?.Sound?.Play(1.0f, range, position, muffle: !damageSound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
|
||||
}
|
||||
|
||||
public static void PlayUISound(GUISoundType soundType)
|
||||
{
|
||||
if (guiSounds == null || guiSounds.Count < 1) { return; }
|
||||
if (guiSounds.TryGetValue(soundType, out List<Sound> sounds))
|
||||
{
|
||||
if (sounds == null || sounds.Count < 1) { return; }
|
||||
sounds.GetRandom()?.Play(null, "ui");
|
||||
}
|
||||
GUISound.GUISoundPrefabs
|
||||
.Where(s => s.Type == soundType)
|
||||
.GetRandomUnsynced()?.Sound?.Play(null, "ui");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class TagNames : Attribute
|
||||
{
|
||||
public readonly ImmutableHashSet<Identifier> Names;
|
||||
|
||||
public TagNames(params string[] names)
|
||||
{
|
||||
Names = names.Select(n => n.ToIdentifier()).ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
class SoundPrefab : Prefab
|
||||
{
|
||||
private class PrefabCollectionHandler
|
||||
{
|
||||
public readonly object Collection;
|
||||
public readonly MethodInfo AddMethod;
|
||||
public readonly MethodInfo RemoveMethod;
|
||||
public readonly MethodInfo SortAllMethod;
|
||||
public readonly MethodInfo AddOverrideFileMethod;
|
||||
public readonly MethodInfo RemoveOverrideFileMethod;
|
||||
|
||||
public void Add(SoundPrefab p, bool isOverride)
|
||||
{
|
||||
AddMethod.Invoke(Collection, new object[] { p, isOverride });
|
||||
}
|
||||
|
||||
public void Remove(SoundPrefab p)
|
||||
{
|
||||
RemoveMethod.Invoke(Collection, new object[] { p });
|
||||
}
|
||||
|
||||
public void AddOverrideFile(ContentFile file)
|
||||
{
|
||||
AddOverrideFileMethod.Invoke(Collection, new object[] { file });
|
||||
}
|
||||
|
||||
public void RemoveOverrideFile(ContentFile file)
|
||||
{
|
||||
RemoveOverrideFileMethod.Invoke(Collection, new object[] { file });
|
||||
}
|
||||
|
||||
public void SortAll()
|
||||
{
|
||||
SortAllMethod.Invoke(Collection, null);
|
||||
}
|
||||
|
||||
public PrefabCollectionHandler(Type type)
|
||||
{
|
||||
var collectionField = type.GetField($"{type.Name}Prefabs", BindingFlags.Public | BindingFlags.Static);
|
||||
if (collectionField is null) { throw new InvalidOperationException($"Couldn't determine PrefabCollection for {type.Name}"); }
|
||||
Collection = collectionField.GetValue(null) ?? throw new InvalidOperationException($"PrefabCollection for {type.Name} was null");
|
||||
AddMethod = Collection.GetType().GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
|
||||
RemoveMethod = Collection.GetType().GetMethod("Remove", BindingFlags.Public | BindingFlags.Instance);
|
||||
AddOverrideFileMethod = Collection.GetType().GetMethod("AddOverrideFile", BindingFlags.Public | BindingFlags.Instance);
|
||||
RemoveOverrideFileMethod = Collection.GetType().GetMethod("RemoveOverrideFile", BindingFlags.Public | BindingFlags.Instance);
|
||||
SortAllMethod = Collection.GetType().GetMethod("SortAll", BindingFlags.Public | BindingFlags.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly static PrefabSelector<SoundPrefab> WaterAmbienceIn = new PrefabSelector<SoundPrefab>();
|
||||
public readonly static PrefabSelector<SoundPrefab> WaterAmbienceOut = new PrefabSelector<SoundPrefab>();
|
||||
public readonly static PrefabSelector<SoundPrefab> WaterAmbienceMoving = new PrefabSelector<SoundPrefab>();
|
||||
public readonly static PrefabSelector<SoundPrefab> StartupSound = new PrefabSelector<SoundPrefab>();
|
||||
|
||||
private readonly static List<SoundPrefab> flowSounds = new List<SoundPrefab>();
|
||||
public static IReadOnlyList<SoundPrefab> FlowSounds => flowSounds;
|
||||
private readonly static List<SoundPrefab> splashSounds = new List<SoundPrefab>();
|
||||
public static IReadOnlyList<SoundPrefab> SplashSounds => splashSounds;
|
||||
|
||||
public readonly static ImmutableDictionary<Identifier, Type> TagToDerivedPrefab;
|
||||
private readonly static ImmutableDictionary<Type, PrefabCollectionHandler> derivedPrefabCollections;
|
||||
private readonly static ImmutableDictionary<Identifier, PrefabSelector<SoundPrefab>> prefabSelectors;
|
||||
private readonly static ImmutableDictionary<Identifier, List<SoundPrefab>> prefabsWithTag;
|
||||
public readonly static PrefabCollection<SoundPrefab> Prefabs;
|
||||
|
||||
static SoundPrefab()
|
||||
{
|
||||
var types = ReflectionUtils.GetDerivedNonAbstract<SoundPrefab>();
|
||||
//types.ForEach(t => t.GetProperties(BindingFlags.Public | BindingFlags.Static));
|
||||
TagToDerivedPrefab = types.SelectMany(t =>
|
||||
t.GetCustomAttribute<TagNames>().Names.Select(n => (n, t))).ToImmutableDictionary();
|
||||
derivedPrefabCollections = types.Select(t => (t, new PrefabCollectionHandler(t))).ToImmutableDictionary();
|
||||
|
||||
var prefabSelectorFields = typeof(SoundPrefab).GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(f => f.FieldType == typeof(PrefabSelector<SoundPrefab>));
|
||||
prefabSelectors = prefabSelectorFields.Select(f => (f.Name.ToIdentifier(), (PrefabSelector<SoundPrefab>)f.GetValue(null))).ToImmutableDictionary();
|
||||
|
||||
var prefabsOfTagName = typeof(SoundPrefab).GetFields(BindingFlags.Static | BindingFlags.NonPublic)
|
||||
.Where(f => f.FieldType == typeof(List<SoundPrefab>));
|
||||
prefabsWithTag = prefabsOfTagName.Select(f => (f.Name.Substring(0, f.Name.Length-6).ToIdentifier(), (List<SoundPrefab>)f.GetValue(null))).ToImmutableDictionary();
|
||||
|
||||
Prefabs = new PrefabCollection<SoundPrefab>(
|
||||
onAdd: (SoundPrefab p, bool isOverride) =>
|
||||
{
|
||||
if (derivedPrefabCollections.ContainsKey(p.GetType()))
|
||||
{
|
||||
derivedPrefabCollections[p.GetType()].Add(p, isOverride);
|
||||
}
|
||||
if (prefabSelectors.ContainsKey(p.ElementName)) { prefabSelectors[p.ElementName].Add(p, isOverride); }
|
||||
UpdateSoundsWithTag();
|
||||
},
|
||||
onRemove: (SoundPrefab p) =>
|
||||
{
|
||||
if (derivedPrefabCollections.ContainsKey(p.GetType()))
|
||||
{
|
||||
derivedPrefabCollections[p.GetType()].Remove(p);
|
||||
}
|
||||
if (prefabSelectors.ContainsKey(p.ElementName)) { prefabSelectors[p.ElementName].RemoveIfContains(p); }
|
||||
UpdateSoundsWithTag();
|
||||
},
|
||||
onSort: () =>
|
||||
{
|
||||
derivedPrefabCollections.Values.ForEach(h => h.SortAll());
|
||||
prefabSelectors.Values.ForEach(h => h.Sort());
|
||||
},
|
||||
onAddOverrideFile: (file) => {derivedPrefabCollections.Values.ForEach(h => h.AddOverrideFile(file)); },
|
||||
onRemoveOverrideFile: (file) => { derivedPrefabCollections.Values.ForEach(h => h.RemoveOverrideFile(file)); }
|
||||
);
|
||||
}
|
||||
|
||||
private static void UpdateSoundsWithTag()
|
||||
{
|
||||
foreach (var tag in prefabsWithTag.Keys)
|
||||
{
|
||||
var list = prefabsWithTag[tag];
|
||||
list.Clear();
|
||||
list.AddRange(Prefabs.Where(p => p.ElementName == tag));
|
||||
list.Sort((p1, p2) =>
|
||||
{
|
||||
if (p1.ContentFile.ContentPackage.Index < p2.ContentFile.ContentPackage.Index) { return -1; }
|
||||
if (p1.ContentFile.ContentPackage.Index > p2.ContentFile.ContentPackage.Index) { return 1; }
|
||||
if (p2.Element.ComesAfter(p1.Element)) { return -1; }
|
||||
if (p1.Element.ComesAfter(p2.Element)) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override Identifier DetermineIdentifier(XElement element)
|
||||
{
|
||||
Identifier id = base.DetermineIdentifier(element);
|
||||
if (id.IsEmpty)
|
||||
{
|
||||
if (id.IsEmpty) { id = Path.GetFileNameWithoutExtension(element.GetAttributeStringUnrestricted("path", "")).ToIdentifier(); }
|
||||
if (id.IsEmpty) { id = Path.GetFileNameWithoutExtension(element.GetAttributeStringUnrestricted("file", "")).ToIdentifier(); }
|
||||
|
||||
if (!id.IsEmpty)
|
||||
{
|
||||
id = $"{element.Name}_{id}".ToIdentifier();
|
||||
|
||||
string damageSoundType = element.GetAttributeString("damagesoundtype", "");
|
||||
if (!damageSoundType.IsNullOrEmpty())
|
||||
{
|
||||
id = $"{id}_{damageSoundType}".ToIdentifier();
|
||||
}
|
||||
|
||||
string musicType = element.GetAttributeString("type", "");
|
||||
if (!musicType.IsNullOrEmpty())
|
||||
{
|
||||
id = $"{id}_{musicType}".ToIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public readonly ContentPath SoundPath;
|
||||
public readonly ContentXElement Element;
|
||||
public readonly Identifier ElementName;
|
||||
public Sound Sound { get; private set; }
|
||||
|
||||
public SoundPrefab(ContentXElement element, SoundsFile file, bool stream = false) : base(file, element)
|
||||
{
|
||||
SoundPath = element.GetAttributeContentPath("file") ?? ContentPath.Empty;
|
||||
Element = element;
|
||||
ElementName = element.NameAsIdentifier();
|
||||
Sound = GameMain.SoundManager.LoadSound(element, stream: stream);
|
||||
}
|
||||
|
||||
public bool IsPlaying()
|
||||
{
|
||||
return Sound.IsPlaying();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Sound?.Dispose(); Sound = null;
|
||||
}
|
||||
}
|
||||
|
||||
[TagNames("damagesound")]
|
||||
class DamageSound : SoundPrefab
|
||||
{
|
||||
public readonly static PrefabCollection<DamageSound> DamageSoundPrefabs = new PrefabCollection<DamageSound>();
|
||||
|
||||
//the range of inflicted damage where the sound can be played
|
||||
//(10.0f, 30.0f) would be played when the inflicted damage is between 10 and 30
|
||||
public readonly Vector2 DamageRange;
|
||||
|
||||
public readonly Identifier DamageType;
|
||||
|
||||
public readonly Identifier RequiredTag;
|
||||
|
||||
public bool IgnoreMuffling;
|
||||
|
||||
public DamageSound(ContentXElement element, SoundsFile file) : base(element, file)
|
||||
{
|
||||
DamageRange = element.GetAttributeVector2("damagerange", Vector2.Zero);
|
||||
DamageType = element.GetAttributeIdentifier("damagesoundtype", "None");
|
||||
IgnoreMuffling = element.GetAttributeBool("ignoremuffling", false);
|
||||
RequiredTag = element.GetAttributeIdentifier("requiredtag", "");
|
||||
}
|
||||
}
|
||||
|
||||
[TagNames("music")]
|
||||
class BackgroundMusic : SoundPrefab
|
||||
{
|
||||
public readonly static PrefabCollection<BackgroundMusic> BackgroundMusicPrefabs = new PrefabCollection<BackgroundMusic>();
|
||||
|
||||
public readonly Identifier Type;
|
||||
public readonly bool DuckVolume;
|
||||
public readonly float Volume;
|
||||
|
||||
public readonly Vector2 IntensityRange;
|
||||
|
||||
public readonly bool ContinueFromPreviousTime;
|
||||
public int PreviousTime;
|
||||
|
||||
public BackgroundMusic(ContentXElement element, SoundsFile file) : base(element, file, stream: true)
|
||||
{
|
||||
Type = element.GetAttributeIdentifier("type", "");
|
||||
IntensityRange = element.GetAttributeVector2("intensityrange", new Vector2(0.0f, 100.0f));
|
||||
DuckVolume = element.GetAttributeBool("duckvolume", false);
|
||||
this.Volume = element.GetAttributeFloat("volume", 1.0f);
|
||||
ContinueFromPreviousTime = element.GetAttributeBool("continuefromprevioustime", false);
|
||||
}
|
||||
}
|
||||
|
||||
[TagNames("guisound")]
|
||||
class GUISound : SoundPrefab
|
||||
{
|
||||
//public readonly static Dictionary<GUISoundType, List<GUISound>> GUISoundsByType = new Dictionary<GUISoundType, List<GUISound>>();
|
||||
public readonly static PrefabCollection<GUISound> GUISoundPrefabs = new PrefabCollection<GUISound>();
|
||||
|
||||
public readonly GUISoundType Type;
|
||||
|
||||
public GUISound(ContentXElement element, SoundsFile file) : base(element, file)
|
||||
{
|
||||
Type = element.GetAttributeEnum("guisoundtype", GUISoundType.UIMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
if (soundChannel == null) { return; }
|
||||
gain = value;
|
||||
soundChannel.Gain = value * GameMain.Config.VoiceChatVolume;
|
||||
soundChannel.Gain = value * GameSettings.CurrentConfig.Audio.VoiceChatVolume;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
float fVal = ShortToFloat(buffer[i]);
|
||||
|
||||
if (gain * GameMain.Config.VoiceChatVolume > 1.0f) //TODO: take distance into account?
|
||||
if (gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume > 1.0f) //TODO: take distance into account?
|
||||
{
|
||||
fVal = Math.Clamp(fVal * gain * GameMain.Config.VoiceChatVolume, -1f, 1f);
|
||||
fVal = Math.Clamp(fVal * gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume, -1f, 1f);
|
||||
}
|
||||
|
||||
if (UseMuffleFilter)
|
||||
|
||||
Reference in New Issue
Block a user