388 lines
13 KiB
C#
388 lines
13 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
partial class LevelObjectPrefab : ISerializableEntity
|
|
{
|
|
private static List<LevelObjectPrefab> list = new List<LevelObjectPrefab>();
|
|
public static List<LevelObjectPrefab> List
|
|
{
|
|
get { return list; }
|
|
}
|
|
|
|
public class ChildObject
|
|
{
|
|
public List<string> AllowedNames;
|
|
public int MinCount, MaxCount;
|
|
|
|
public ChildObject()
|
|
{
|
|
AllowedNames = new List<string>();
|
|
MinCount = 1;
|
|
MaxCount = 1;
|
|
}
|
|
|
|
public ChildObject(XElement element)
|
|
{
|
|
AllowedNames = element.GetAttributeStringArray("names", new string[0]).ToList();
|
|
MinCount = element.GetAttributeInt("mincount", 1);
|
|
MaxCount = Math.Max(element.GetAttributeInt("maxcount", 1), MinCount);
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
public enum SpawnPosType
|
|
{
|
|
None = 0,
|
|
Wall = 1,
|
|
RuinWall = 2,
|
|
SeaFloor = 4,
|
|
MainPath = 8
|
|
}
|
|
|
|
public Sprite Sprite
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public Sprite SpecularSprite
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public DeformableSprite DeformableSprite
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(1.0f, false), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
|
|
public float MinSize
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
[Serialize(1.0f, false), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
|
|
public float MaxSize
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Which sides of a wall the object can appear on.
|
|
/// </summary>
|
|
[Serialize((Alignment.Top | Alignment.Bottom | Alignment.Left | Alignment.Right), true), Editable(ToolTip = "Which sides of a wall the object can spawn on.")]
|
|
public Alignment Alignment
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(SpawnPosType.Wall, false), Editable()]
|
|
public SpawnPosType SpawnPos
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public XElement Config
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public readonly List<XElement> LevelTriggerElements;
|
|
|
|
/// <summary>
|
|
/// Overrides the commonness of the object in a specific level type.
|
|
/// Key = name of the level type, value = commonness in that level type.
|
|
/// </summary>
|
|
public Dictionary<string, float> OverrideCommonness;
|
|
|
|
public XElement PhysicsBodyElement
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
public int PhysicsBodyTriggerIndex
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize("0.0,1.0", true), Editable()]
|
|
public Vector2 DepthRange
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f,
|
|
ToolTip = "The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are used to determine the probability for an object to spawn at a specific position.")]
|
|
/// <summary>
|
|
/// The tendency for the prefab to form clusters. Used as an exponent for perlin noise values
|
|
/// that are used to determine the probability for an object to spawn at a specific position.
|
|
/// </summary>
|
|
public float ClusteringAmount
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f,
|
|
ToolTip = "A value between 0-1 that determines the z-coordinate to sample perlin noise from when determining the probability " +
|
|
" for an object to spawn at a specific position. Using the same (or close) value for different objects means the objects tend " +
|
|
"to form clusters in the same areas.")]
|
|
/// <summary>
|
|
/// A value between 0-1 that determines the z-coordinate to sample perlin noise from when
|
|
/// determining the probability for an object to spawn at a specific position.
|
|
/// Using the same (or close) value for different objects means the objects tend to form clusters
|
|
/// in the same areas.
|
|
/// </summary>
|
|
public float ClusteringGroup
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(false, true), Editable(ToolTip = "Should the object be rotated to align it with the wall surface it spawns on.")]
|
|
public bool AlignWithSurface
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f,
|
|
ToolTip = "Minimum length of a graph edge the object can spawn on.")]
|
|
/// <summary>
|
|
/// Minimum length of a graph edge the object can spawn on.
|
|
/// </summary>
|
|
public float MinSurfaceWidth
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
private Vector2 randomRotation;
|
|
[Serialize("0.0,0.0", true), Editable(ToolTip = "How much the rotation of the object can vary (min and max values in degrees).")]
|
|
public Vector2 RandomRotation
|
|
{
|
|
get { return new Vector2(MathHelper.ToDegrees(randomRotation.X), MathHelper.ToDegrees(randomRotation.Y)); }
|
|
private set
|
|
{
|
|
randomRotation = new Vector2(MathHelper.ToRadians(value.X), MathHelper.ToRadians(value.Y));
|
|
}
|
|
}
|
|
|
|
public Vector2 RandomRotationRad => randomRotation;
|
|
|
|
private float swingAmount;
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 360.0f, ToolTip = "How much the object swings (in degrees).")]
|
|
public float SwingAmount
|
|
{
|
|
get { return MathHelper.ToDegrees(swingAmount); }
|
|
private set
|
|
{
|
|
swingAmount = MathHelper.ToRadians(value);
|
|
}
|
|
}
|
|
|
|
public float SwingAmountRad => swingAmount;
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How fast the object swings.")]
|
|
public float SwingFrequency
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize("0.0,0.0", true), Editable(ToolTip = "How much the scale of the object oscillates on each axis. A value of 0.5,0.5 would make the object's scale oscillate from 100% to 150%.")]
|
|
public Vector2 ScaleOscillation
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How fast the object's scale oscillates.")]
|
|
public float ScaleOscillationFrequency
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(1.0f, true), Editable(ToolTip = "How likely it is for the object to spawn in a level. "+
|
|
"This is relative to the commonness of the other objects - for example, having an object with "+
|
|
"a commonness of 1 and another with a commonness of 10 would mean the latter appears in levels 10 times as frequently as the former. "+
|
|
"The commonness value can be overridden on specific level types.")]
|
|
public float Commonness
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How much the object disrupts submarine's sonar.")]
|
|
public float SonarDisruption
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public List<ChildObject> ChildObjects
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public Dictionary<string, SerializableProperty> SerializableProperties
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A list of prefabs whose properties override this one's properties when a trigger is active.
|
|
/// E.g. if a trigger in the index 1 of the trigger list is active, the properties in index 1 in this list are used (unless it's null)
|
|
/// </summary>
|
|
public List<LevelObjectPrefab> OverrideProperties
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "LevelObjectPrefab (" + Name + ")";
|
|
}
|
|
|
|
public static void LoadAll()
|
|
{
|
|
var files = GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs);
|
|
if (files.Count() > 0)
|
|
{
|
|
foreach (var file in files)
|
|
{
|
|
LoadConfig(file);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LoadConfig("Content/LevelObjects/LevelObject/Prefabs.xml");
|
|
}
|
|
}
|
|
|
|
private static void LoadConfig(string configPath)
|
|
{
|
|
try
|
|
{
|
|
XDocument doc = XMLExtensions.TryLoadXml(configPath);
|
|
if (doc == null || doc.Root == null) return;
|
|
|
|
foreach (XElement element in doc.Root.Elements())
|
|
{
|
|
list.Add(new LevelObjectPrefab(element));
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugConsole.ThrowError(String.Format("Failed to load LevelObject prefabs from {0}", configPath), e);
|
|
}
|
|
}
|
|
|
|
public LevelObjectPrefab(XElement element)
|
|
{
|
|
ChildObjects = new List<ChildObject>();
|
|
LevelTriggerElements = new List<XElement>();
|
|
OverrideProperties = new List<LevelObjectPrefab>();
|
|
OverrideCommonness = new Dictionary<string, float>();
|
|
|
|
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
|
if (element != null)
|
|
{
|
|
Config = element;
|
|
Name = element.Name.ToString();
|
|
LoadElements(element, -1);
|
|
InitProjSpecific(element);
|
|
}
|
|
|
|
//use the maximum width of the sprite as the minimum surface width if no value is given
|
|
if (element != null && !element.Attributes("minsurfacewidth").Any())
|
|
{
|
|
if (Sprite != null) MinSurfaceWidth = Sprite.size.X * MaxSize;
|
|
if (DeformableSprite != null) MinSurfaceWidth = Math.Max(MinSurfaceWidth, DeformableSprite.Size.X * MaxSize);
|
|
}
|
|
}
|
|
|
|
private void LoadElements(XElement element, int parentTriggerIndex)
|
|
{
|
|
foreach (XElement subElement in element.Elements())
|
|
{
|
|
switch (subElement.Name.ToString().ToLowerInvariant())
|
|
{
|
|
case "sprite":
|
|
Sprite = new Sprite(subElement, lazyLoad: true);
|
|
break;
|
|
case "specularsprite":
|
|
SpecularSprite = new Sprite(subElement, lazyLoad: true);
|
|
break;
|
|
case "deformablesprite":
|
|
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
|
|
break;
|
|
case "overridecommonness":
|
|
string levelType = subElement.GetAttributeString("leveltype", "");
|
|
if (!OverrideCommonness.ContainsKey(levelType))
|
|
{
|
|
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
|
|
}
|
|
break;
|
|
case "leveltrigger":
|
|
case "trigger":
|
|
OverrideProperties.Add(null);
|
|
LevelTriggerElements.Add(subElement);
|
|
LoadElements(subElement, LevelTriggerElements.Count - 1);
|
|
break;
|
|
case "childobject":
|
|
ChildObjects.Add(new ChildObject(subElement));
|
|
break;
|
|
case "overrideproperties":
|
|
var propertyOverride = new LevelObjectPrefab(subElement);
|
|
OverrideProperties[OverrideProperties.Count - 1] = propertyOverride;
|
|
if (propertyOverride.Sprite == null && propertyOverride.DeformableSprite == null)
|
|
{
|
|
propertyOverride.Sprite = Sprite;
|
|
propertyOverride.DeformableSprite = DeformableSprite;
|
|
}
|
|
break;
|
|
case "body":
|
|
case "physicsbody":
|
|
PhysicsBodyElement = subElement;
|
|
PhysicsBodyTriggerIndex = parentTriggerIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
partial void InitProjSpecific(XElement element);
|
|
|
|
public float GetCommonness(string levelType)
|
|
{
|
|
if (!OverrideCommonness.TryGetValue(levelType, out float commonness))
|
|
{
|
|
return Commonness;
|
|
}
|
|
return commonness;
|
|
}
|
|
}
|
|
}
|