Skip to content

Commit

Permalink
parse sfxevent
Browse files Browse the repository at this point in the history
  • Loading branch information
AnakinRaW committed Jul 6, 2024
1 parent e2c4b23 commit 0b3ce55
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 76 deletions.
210 changes: 185 additions & 25 deletions src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/SfxEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,44 @@
using System.Collections.Generic;
using System.Linq;
using PG.Commons.Hashing;
using PG.StarWarsGame.Engine.Xml.Parsers.Data;
using PG.StarWarsGame.Engine.Xml.Tags;
using PG.StarWarsGame.Files.XML;

namespace PG.StarWarsGame.Engine.DataTypes;

public sealed class SfxEvent : XmlObject
{
public const byte MaxVolumeValue = 100;
public const byte MaxPitchValue = 200;
public const byte MinPitchValue = 50;
public const byte MaxPan2dValue = 100;
public const byte MinPriorityValue = 1;
public const byte MaxPriorityValue = 5;
public const byte MaxProbability = 100;
public const sbyte MinMaxInstances = 0;
public const sbyte InfinitivePlayCount = -1;
public const float MinLoopSeconds = 0.0f;
public const float MinVolumeSaturation = 0.0f;

// Default values which are not the default value of the type
public const byte DefaultPriority = 3;
public const bool DefaultIs3d = true;
public const byte DefaultProbability = 100;
public const sbyte DefaultPlayCount = 1;
public const sbyte DefaultMaxInstances = 1;
public const byte DefaultMinVolume = 100;
public const byte DefaultMaxVolume = 100;
public const byte DefaultMinPitch = 100;
public const byte DefaultMaxPitch = 100;
public const byte DefaultMinPan2d = 50;
public const byte DefaultMaxPan2d = 50;
public const float DefaultVolumeSaturationDistance = 300.0f;

private SfxEvent? _preset;
private string? _presetName;
private string? _overlapTestName;
private string? _chainedSfxEvent;
private IReadOnlyList<string>? _textIds;
private IReadOnlyList<string>? _preSamples;
private IReadOnlyList<string>? _samples;
private IReadOnlyList<string>? _postSamples;
Expand All @@ -20,10 +51,70 @@ public sealed class SfxEvent : XmlObject
private bool? _isUnitResponseVo;
private bool? _isAmbientVo;
private bool? _isLocalized;
private bool? _playSequentially;
private bool? _killsPreviousObjectsSfx;
private byte? _priority;
private byte? _probability;
private sbyte? _playCount;
private sbyte? _maxInstances;
private uint? _minPredelay;
private uint? _maxPredelay;
private uint? _minPostdelay;
private uint? _maxPostdelay;
private float? _loopFadeInSeconds;
private float? _loopFadeOutSeconds;
private float? _volumeSaturationDistance;

private static readonly Func<byte?, byte?> PriorityCoercion = priority =>
{
if (!priority.HasValue)
return DefaultPriority;
if (priority < MinPriorityValue)
return MinPriorityValue;
if (priority > MaxPriorityValue)
return MaxPriorityValue;
return priority;
};

private static readonly Func<sbyte?, sbyte?> MaxInstancesCoercion = maxInstances =>
{
if (!maxInstances.HasValue)
return DefaultMaxInstances;
if (maxInstances < MinMaxInstances)
return MinMaxInstances;
return maxInstances;
};

private static readonly Func<byte?, byte?> ProbabilityCoercion = probability =>
{
if (!probability.HasValue)
return DefaultProbability;
if (probability > MaxProbability)
return MaxProbability;
return probability;
};

private static readonly Func<sbyte?, sbyte?> PlayCountCoercion = playCount =>
{
if (!playCount.HasValue)
return DefaultPlayCount;
if (playCount < InfinitivePlayCount)
return InfinitivePlayCount;
return playCount;
};

private static readonly Func<float?, float?> LoopAndSaturationCoercion = loopSeconds =>
{
if (!loopSeconds.HasValue)
return DefaultVolumeSaturationDistance;
if (loopSeconds < MinLoopSeconds)
return MinLoopSeconds;
return loopSeconds;
};

public bool IsPreset { get; }
public bool IsPreset => LazyInitValue(ref _isPreset, SfxEventXmlTags.IsPreset, false)!.Value;

public bool Is3D => LazyInitValue(ref _is3D, SfxEventXmlTags.Is3D, false)!.Value;
public bool Is3D => LazyInitValue(ref _is3D, SfxEventXmlTags.Is3D, DefaultIs3d)!.Value;

public bool Is2D => LazyInitValue(ref _is2D, SfxEventXmlTags.Is2D, false)!.Value;

Expand All @@ -37,9 +128,11 @@ public sealed class SfxEvent : XmlObject

public bool IsLocalized => LazyInitValue(ref _isLocalized, SfxEventXmlTags.Localize, false)!.Value;

public string? UsePresetName { get; }
public SfxEvent? Preset => LazyInitValue(ref _preset, SfxEventXmlTags.PresetXRef, null);

public string? UsePresetName => LazyInitValue(ref _presetName, SfxEventXmlTags.UsePreset, null);

public bool PlaySequentially => LazyInitValue(ref _is3D, SfxEventXmlTags.Is3D, false)!.Value;
public bool PlaySequentially => LazyInitValue(ref _playSequentially, SfxEventXmlTags.PlaySequentially, false)!.Value;

public IEnumerable<string> AllSamples => PreSamples.Concat(Samples).Concat(PostSamples);

Expand All @@ -49,19 +142,36 @@ public sealed class SfxEvent : XmlObject

public IReadOnlyList<string> PostSamples => LazyInitValue(ref _postSamples, SfxEventXmlTags.PostSamples, Array.Empty<string>());

public IReadOnlyList<string> LocalizedTextIDs { get; }
public IReadOnlyList<string> LocalizedTextIDs => LazyInitValue(ref _textIds, SfxEventXmlTags.TextID, Array.Empty<string>());

public byte Priority => LazyInitValue(ref _priority, SfxEventXmlTags.Priority, DefaultPriority, PriorityCoercion)!.Value;

public byte Probability => LazyInitValue(ref _probability, SfxEventXmlTags.Probability, DefaultProbability, ProbabilityCoercion)!.Value;

public byte Priority { get; }
public sbyte PlayCount => LazyInitValue(ref _playCount, SfxEventXmlTags.PlayCount, DefaultPlayCount, PlayCountCoercion)!.Value;

public byte Probability { get; }
public float LoopFadeInSeconds => LazyInitValue(ref _loopFadeInSeconds, SfxEventXmlTags.LoopFadeInSeconds, 0f, LoopAndSaturationCoercion)!.Value;

public sbyte PlayCount { get; }
public float LoopFadeOutSeconds => LazyInitValue(ref _loopFadeOutSeconds, SfxEventXmlTags.LoopFadeOutSeconds, 0f, LoopAndSaturationCoercion)!.Value;

public float LoopFadeInSeconds { get; }
public sbyte MaxInstances => LazyInitValue(ref _maxInstances, SfxEventXmlTags.MaxInstances, DefaultMaxInstances, MaxInstancesCoercion)!.Value;

public float LoopFadeOutInSeconds { get; }
public uint MinPredelay => LazyInitValue(ref _minPredelay, SfxEventXmlTags.MinPredelay, 0u)!.Value;

public sbyte MaxInstances { get; }
public uint MaxPredelay => LazyInitValue(ref _maxPredelay, SfxEventXmlTags.MaxPredelay, 0u)!.Value;

public uint MinPostdelay => LazyInitValue(ref _minPostdelay, SfxEventXmlTags.MinPostdelay, 0u)!.Value;

public uint MaxPostdelay => LazyInitValue(ref _maxPostdelay, SfxEventXmlTags.MaxPostdelay, 0u)!.Value;

public float VolumeSaturationDistance => LazyInitValue(ref _volumeSaturationDistance,
SfxEventXmlTags.VolumeSaturationDistance, DefaultVolumeSaturationDistance, LoopAndSaturationCoercion)!.Value;

public bool KillsPreviousObjectsSfx => LazyInitValue(ref _killsPreviousObjectsSfx, SfxEventXmlTags.KillsPreviousObjectSFX, false)!.Value;

public string? OverlapTestName => LazyInitValue(ref _overlapTestName, SfxEventXmlTags.OverlapTest, null);

public string? ChainedSfxEventName => LazyInitValue(ref _chainedSfxEvent, SfxEventXmlTags.ChainedSfxEvent, null);

public byte MinVolume { get; }

Expand All @@ -75,26 +185,76 @@ public sealed class SfxEvent : XmlObject

public byte MaxPan2D { get; }

public uint MinPredelay { get; }

public uint MaxPredelay { get; }
internal SfxEvent(string name, Crc32 nameCrc, IReadOnlyValueListDictionary<string, object?> properties,
XmlLocationInfo location)
: base(name, nameCrc, properties, location)
{
var minMaxVolume = GetMinMaxVolume(properties);
MinVolume = minMaxVolume.min;
MaxVolume = minMaxVolume.max;

public uint MinPostdelay { get; }
var minMaxPitch = GetMinMaxPitch(properties);
MinPitch = minMaxPitch.min;
MaxPitch = minMaxPitch.max;

public uint MaxPostdelay { get; }
var minMaxPan = GetMinMaxPan2d(properties);
MinPan2D = minMaxPan.min;
MaxPan2D = minMaxPan.max;
}

public float VolumeSaturationDistance { get; }
private static (byte min, byte max) GetMinMaxVolume(IReadOnlyValueListDictionary<string, object?> properties)
{
return GetMinMaxValues(properties, SfxEventXmlTags.MinVolume, SfxEventXmlTags.MaxVolume, DefaultMinVolume,
DefaultMaxVolume, null, MaxVolumeValue);
}

public bool KillsPreviousObjectsSfx { get; }
private static (byte min, byte max) GetMinMaxPitch(IReadOnlyValueListDictionary<string, object?> properties)
{
return GetMinMaxValues(properties, SfxEventXmlTags.MinPitch, SfxEventXmlTags.MaxPitch, DefaultMinPitch,
DefaultMaxPitch, MinPitchValue, MaxPitchValue);
}

public string? OverlapTestName { get; }
private static (byte min, byte max) GetMinMaxPan2d(IReadOnlyValueListDictionary<string, object?> properties)
{
return GetMinMaxValues(properties, SfxEventXmlTags.MinPan2D, SfxEventXmlTags.MaxPan2D, DefaultMinPan2d,
DefaultMaxPan2d, null, MaxPan2dValue);
}

public string? ChainedSfxEventName { get; }

internal SfxEvent(string name, Crc32 nameCrc, IReadOnlyValueListDictionary<string, object?> properties,
XmlLocationInfo location)
: base(name, nameCrc, properties, location)
private static (byte min, byte max) GetMinMaxValues(
IReadOnlyValueListDictionary<string, object?> properties,
string minTag,
string maxTag,
byte defaultMin,
byte defaultMax,
byte? totalMinValue,
byte? totalMaxValue)
{

var minValue = !properties.TryGetLastValue(minTag, out var minObj) ? defaultMin : Convert.ToByte(minObj);
var maxValue = !properties.TryGetLastValue(maxTag, out var maxObj) ? defaultMax : Convert.ToByte(maxObj);

if (totalMaxValue.HasValue)
{
if (minValue > totalMaxValue)
minValue = totalMaxValue.Value;
if (maxValue > totalMaxValue)
maxValue = totalMaxValue.Value;
}

if (totalMinValue.HasValue)
{
if (minValue < totalMinValue)
minValue = totalMinValue.Value;
if (maxValue < totalMinValue)
maxValue = totalMinValue.Value;
}

if (minValue > maxValue)
minValue = maxValue;

if (maxValue < minValue)
maxValue = minValue;

return (minValue, maxValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\PetroglyphTools\PG.StarWarsGame.Files.MEG\PG.StarWarsGame.Files.MEG\PG.StarWarsGame.Files.MEG.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,12 @@
using Microsoft.Extensions.Logging;
using PG.Commons.Hashing;
using PG.StarWarsGame.Engine.DataTypes;
using PG.StarWarsGame.Engine.Xml.Tags;
using PG.StarWarsGame.Files.XML;
using PG.StarWarsGame.Files.XML.Parsers;

namespace PG.StarWarsGame.Engine.Xml.Parsers.Data;

public static class SfxEventXmlTags
{
public const string IsPreset = "Is_Preset";
public const string UsePreset = "Use_Preset";
public const string Samples = "Samples";
public const string PreSamples = "Pre_Samples";
public const string PostSamples = "Post_Samples";
public const string TextID = "Text_ID";
public const string PlaySequentially = "Play_Sequentially";
public const string Priority = "Priority";
public const string Probability = "Probability";
public const string PlayCount = "Play_Count";
public const string LoopFadeInSeconds = "Loop_Fade_In_Seconds";
public const string LoopFadeOutSeconds = "Loop_Fade_Out_Seconds";
public const string MaxInstances = "Max_Instances";
public const string MinVolume = "Min_Volume";
public const string MaxVolume = "Max_Volume";
public const string MinPitch = "Min_Pitch";
public const string MaxPitch = "Max_Pitch";
public const string MinPan2D = "Min_Pan2D";
public const string MaxPan2D = "Max_Pan2D";
public const string MinPredelay = "Min_Predelay";
public const string MaxPredelay = "Max_Predelay";
public const string MinPostdelay = "Min_Postdelay";
public const string MaxPostdelay = "Max_Postdelay";
public const string VolumeSaturationDistance = "Volume_Saturation_Distance";
public const string KillsPreviousObjectSFX = "Kills_Previous_Object_SFX";
public const string OverlapTest = "Overlap_Test";
public const string Localize = "Localize";
public const string Is2D = "Is_2D";
public const string Is3D = "Is_3D";
public const string IsGui = "Is_GUI";
public const string IsHudVo = "Is_HUD_VO";
public const string IsUnitResponseVo = "Is_Unit_Response_VO";
public const string IsAmbientVo = "Is_Ambient_VO";
public const string ChainedSfxEvent = "Chained_SFXEvent";
}

public sealed class SfxEventParser(
IReadOnlyValueListDictionary<Crc32, SfxEvent> parsedElements,
IServiceProvider serviceProvider)
Expand Down Expand Up @@ -86,7 +49,7 @@ public sealed class SfxEventParser(
case SfxEventXmlTags.Probability:
case SfxEventXmlTags.MinVolume:
case SfxEventXmlTags.MaxVolume:
return PrimitiveParserProvider.ByteParser;
return PrimitiveParserProvider.Max100ByteParser;
case SfxEventXmlTags.MinPredelay:
case SfxEventXmlTags.MaxPredelay:
case SfxEventXmlTags.MinPostdelay:
Expand All @@ -101,9 +64,7 @@ public sealed class SfxEventParser(
}
}

public override SfxEvent Parse(
XElement element,
out Crc32 nameCrc)
public override SfxEvent Parse(XElement element, out Crc32 nameCrc)
{
var name = GetNameAttributeValue(element);
nameCrc = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.PGCrc32Encoding);
Expand All @@ -119,19 +80,15 @@ protected override bool OnParsed(string tag, object value, ValueListDictionary<s
{
var presetName = value as string;
var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.PGCrc32Encoding);
if (presetNameCrc == default || !ParsedElements.TryGetFirstValue(presetNameCrc, out var preset))
{
if (presetNameCrc != default && ParsedElements.TryGetFirstValue(presetNameCrc, out var preset))
CopySfxPreset(properties, preset);
else
Logger?.LogWarning($"Cannot to find preset '{presetName}' for SFXEvent '{elementName ?? "NONE"}'");
return false;
}
CopySfxPreset(properties, preset);
}

return true;
}


private void CopySfxPreset(ValueListDictionary<string, object?> currentXmlProperties, SfxEvent preset)
private static void CopySfxPreset(ValueListDictionary<string, object?> currentXmlProperties, SfxEvent preset)
{
/*
* The engine also copies the Use_Preset *of* the preset, (which almost most cases is null)
Expand All @@ -153,8 +110,9 @@ private void CopySfxPreset(ValueListDictionary<string, object?> currentXmlProper
continue;
currentXmlProperties.Add(keyValuePair.Key, keyValuePair.Value);
}
}

currentXmlProperties.Add(SfxEventXmlTags.PresetXRef, preset);
}

public override SfxEvent Parse(XElement element) => throw new NotSupportedException();
}
Loading

0 comments on commit 0b3ce55

Please sign in to comment.