UltimateZoneBuilder/Source/Core/ZDoom/AmbientSoundInfo.cs

234 lines
6.4 KiB
C#
Executable file

using System.IO;
namespace CodeImp.DoomBuilder.ZDoom
{
public class AmbientSoundInfo
{
#region ================== Enums
public enum AmbientType
{
NONE,
POINT,
SURROUND,
WORLD
}
public enum AmbientMode
{
NONE,
CONTINUOUS,
RANDOM,
PERIODIC
}
#endregion
#region ================== Variables
private string soundname;
private string sounddescription;
private int index = -1;
private AmbientType type = AmbientType.NONE;
private AmbientMode mode = AmbientMode.NONE;
private float volume = 1.0f;
private float attenuation = 1.0f;
private float minsecs;
private float maxsecs;
private float secs;
// Editor sound radii
private float minradius;
private float maxradius;
#endregion
#region ================== Properties
public string SoundName { get { return soundname; } }
public string SoundDescription { get { return sounddescription; } }
public int Index { get { return index; } } // Ambient sound index
// Sound settings
public AmbientType AmbientSoundType { get { return type; } }
public AmbientMode AmbientSoundMode { get { return mode; } }
public float Volume { get { return volume; } }
// Can be set when AmbientType == POINT
public float Attenuation { get { return attenuation; } }
// Used when AmbientMode == RANDOM
public float SecondsMin { get { return minsecs; } }
public float SecondsMax { get { return maxsecs; } }
// Used when AmbientMode == PERIODIC
public float Seconds { get { return secs; } }
// Editor sound radii
public float MinimumRadius { get { return minradius; } }
public float MaximumRadius { get { return maxradius; } }
#endregion
#region ================== Methods
internal bool Setup(SndInfoParser parser)
{
// Read index
if(!parser.ReadSignedInt(ref index))
{
// Not numeric!
parser.ReportError("Expected $ambient <index> value");
return false;
}
// Check index
//INFO: Up to 64 ambient sounds can be used in the Doom map format and 256 in Hexen format. UDMF maps have no limit.
//TODO: can index be zero/negative in UDMF?
if(General.Map.DOOM && (index < 1 || index > 64))
{
parser.ReportError("$ambient <index> must be in [1..64] range");
return false;
}
if(General.Map.HEXEN && (index < 1 || index > 256))
{
parser.ReportError("$ambient <index> must be in [1..256] range");
return false;
}
// Read name
if(!parser.SkipWhitespace(true)) return false;
soundname = parser.StripTokenQuotes(parser.ReadToken(false));
if(string.IsNullOrEmpty(soundname))
{
parser.ReportError("Expected $ambient <logicalsound> value");
return false;
}
// Next token can be either [type] or <mode>...
if (!parser.SkipWhitespace(true)) return false;
string token = parser.ReadToken(false).ToLowerInvariant();
// Can be [type]
if(token == "point" || token == "surround" || token == "world")
{
// Next token may be attenuation...
if(token == "point")
{
if(!parser.SkipWhitespace(false)) return false;
string next = parser.ReadToken(false);
if(!parser.ReadSignedFloat(next, ref attenuation) || attenuation < 0f)
{
// Rewind so this structure can be read again
parser.DataStream.Seek(-next.Length - 1, SeekOrigin.Current);
}
}
// Store type
switch(token)
{
case "point": type = AmbientType.POINT; break;
case "surround": type = AmbientType.SURROUND; break;
case "world": type = AmbientType.WORLD; break;
}
// Read next token
if(!parser.SkipWhitespace(false)) return false;
token = parser.ReadToken(false).ToLowerInvariant();
}
// Sould be <mode>
if(token == "continuous" || token == "random" || token == "periodic")
{
// Next 2 tokens must be minsecs and maxsecs
if(token == "random")
{
if(!parser.SkipWhitespace(false)) return false;
if(!parser.ReadSignedFloat(ref minsecs) || minsecs < 0f)
{
parser.ReportError("Expected $ambient <minsecs> value");
return false;
}
if(!parser.SkipWhitespace(false)) return false;
if(!parser.ReadSignedFloat(ref maxsecs) || maxsecs < 0f)
{
parser.ReportError("Expected $ambient <maxsecs> value");
return false;
}
}
// Next token must be secs
else if(token == "periodic")
{
if(!parser.SkipWhitespace(false)) return false;
if(!parser.ReadSignedFloat(ref secs) || secs < 0f)
{
parser.ReportError("Expected $ambient <secs> value");
return false;
}
}
// Store mode
switch(token)
{
case "continuous": mode = AmbientMode.CONTINUOUS; break;
case "random": mode = AmbientMode.RANDOM; break;
case "periodic": mode = AmbientMode.PERIODIC; break;
}
}
else
{
parser.ReportError("Expected ambient sound <mode> or [type]");
return false;
}
// Read volume
if(!parser.SkipWhitespace(false)) return false;
if(!parser.ReadSignedFloat(ref volume) || volume < 0f)
{
parser.ReportError("Expected ambient sound <volume> value");
return false;
}
// There can be multiple different ambient sounds with the same sound name, so build a description containing the index
// and the name to differentiate them. See https://github.com/jewalky/UltimateDoomBuilder/issues/390
sounddescription = index.ToString() + ": " + soundname + " (" + type.ToString().ToLowerInvariant();
if (type == AmbientType.POINT)
sounddescription += ", attenuation: " + attenuation;
if (mode == AmbientMode.CONTINUOUS)
sounddescription += ", " + mode.ToString().ToLowerInvariant();
else if (mode == AmbientMode.RANDOM)
sounddescription += ", every " + minsecs + " to " + maxsecs + " seconds";
else if (mode == AmbientMode.PERIODIC)
sounddescription += ", every " + secs + " seconds";
sounddescription += ", " + volume * 100 + "% volume)";
return true;
}
internal void SetupSound(SoundInfo info)
{
// Store radii
minradius = info.MinimumDistance / info.Attenuation;
if(info.Rolloff == SoundInfo.RolloffType.LOG)
{
// Calculate from RolloffFactor
maxradius = info.MinimumDistance + info.RolloffFactor * info.MinimumDistance;
}
else
{
maxradius = info.MaximumDistance;
}
maxradius /= info.Attenuation;
}
#endregion
}
}