Client LuaCsForBarotrauma
DecorativeSprite.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Xml.Linq;
6 
7 namespace Barotrauma
8 {
10  {
11  public class State
12  {
13  public float RotationState;
14  public float OffsetState;
15  public Vector2 RandomOffsetMultiplier = new Vector2(Rand.Range(-1.0f, 1.0f), Rand.Range(-1.0f, 1.0f));
16  public float RandomRotationFactor = Rand.Range(0.0f, 1.0f);
17  public float RandomScaleFactor = Rand.Range(0.0f, 1.0f);
18  public bool IsActive = true;
19  }
20 
21  public string Name => $"Decorative Sprite";
22  public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; set; }
23 
24  public Sprite Sprite { get; private set; }
25 
26  public enum AnimationType
27  {
28  None,
29  Sine,
30  Noise
31  }
32 
34  public float BlinkFrequency { get; private set; }
35 
36  private float blinkTimer = 0.0f;
37 
39  public Vector2 Offset { get; private set; }
40 
42  public Vector2 RandomOffset { get; private set; }
43 
45  public AnimationType OffsetAnim { get; private set; }
46 
48  public float OffsetAnimSpeed { get; private set; }
49 
50  private float rotationSpeedRadians;
51  private float absRotationSpeedRadians;
52 
54  public float RotationSpeed
55  {
56  get
57  {
58  return MathHelper.ToDegrees(rotationSpeedRadians);
59  }
60  private set
61  {
62  rotationSpeedRadians = MathHelper.ToRadians(value);
63  absRotationSpeedRadians = Math.Abs(rotationSpeedRadians);
64  }
65  }
66 
67  private float rotationRadians;
69  public float Rotation
70  {
71  get
72  {
73  return MathHelper.ToDegrees(rotationRadians);
74  }
75  private set
76  {
77  rotationRadians = MathHelper.ToRadians(value);
78  }
79  }
80 
81  private Vector2 randomRotationRadians;
83  public Vector2 RandomRotation
84  {
85  get
86  {
87  return new Vector2(MathHelper.ToDegrees(randomRotationRadians.X), MathHelper.ToDegrees(randomRotationRadians.Y));
88  }
89  private set
90  {
91  randomRotationRadians = new Vector2(MathHelper.ToRadians(value.X), MathHelper.ToRadians(value.Y));
92  }
93  }
94 
95  private float scale;
97  public float Scale
98  {
99  get { return scale; }
100  private set { scale = MathHelper.Clamp(value, 0.0f, 10.0f); }
101  }
102 
103  [Serialize("0,0", IsPropertySaveable.Yes), Editable]
104  public Vector2 RandomScale
105  {
106  get;
107  private set;
108  }
109 
111  public AnimationType RotationAnim { get; private set; }
112 
116  [Serialize(0, IsPropertySaveable.No, description: "If > 0, only one sprite of the same group is used (chosen randomly)"), Editable(ReadOnly = true)]
117  public int RandomGroupID { get; private set; }
118 
119  [Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes), Editable()]
120  public Color Color { get; set; }
121 
125  internal List<PropertyConditional> IsActiveConditionals { get; private set; } = new List<PropertyConditional>();
129  internal List<PropertyConditional> AnimationConditionals { get; private set; } = new List<PropertyConditional>();
130 
131  public DecorativeSprite(ContentXElement element, string path = "", string file = "", bool lazyLoad = false)
132  {
133  Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad);
135  // load property conditionals
136  foreach (var subElement in element.Elements())
137  {
138  //choose which list the new conditional should be placed to
139  List<PropertyConditional> conditionalList = null;
140  switch (subElement.Name.ToString().ToLowerInvariant())
141  {
142  case "conditional":
143  case "isactiveconditional":
144  conditionalList = IsActiveConditionals;
145  break;
146  case "animationconditional":
147  conditionalList = AnimationConditionals;
148  break;
149  default:
150  continue;
151  }
152  conditionalList.AddRange(PropertyConditional.FromXElement(subElement));
153  }
154  }
155 
156  public Vector2 GetOffset(ref float offsetState, Vector2 randomOffsetMultiplier, float rotation = 0.0f)
157  {
158  Vector2 offset = Offset;
159  if (OffsetAnimSpeed > 0.0f)
160  {
161  switch (OffsetAnim)
162  {
163  case AnimationType.Sine:
164  offsetState %= (MathHelper.TwoPi / OffsetAnimSpeed);
165  offset *= (float)Math.Sin(offsetState * OffsetAnimSpeed);
166  break;
167  case AnimationType.Noise:
168  offsetState %= 1.0f / (OffsetAnimSpeed * 0.1f);
169 
170  float t = offsetState * 0.1f * OffsetAnimSpeed;
171  offset = new Vector2(
172  offset.X * (PerlinNoise.GetPerlin(t, t) - 0.5f),
173  offset.Y * (PerlinNoise.GetPerlin(t + 0.5f, t + 0.5f) - 0.5f));
174  break;
175  }
176  }
177  offset += new Vector2(
178  RandomOffset.X * randomOffsetMultiplier.X,
179  RandomOffset.Y * randomOffsetMultiplier.Y);
180  if (Math.Abs(rotation) > 0.01f)
181  {
182  Matrix transform = Matrix.CreateRotationZ(rotation);
183  offset = Vector2.Transform(offset, transform);
184  }
185  return offset;
186  }
187 
188  public float GetRotation(ref float rotationState, float randomRotationFactor)
189  {
190  switch (RotationAnim)
191  {
192  case AnimationType.Sine:
193  rotationState %= MathHelper.TwoPi / absRotationSpeedRadians;
194  return
195  rotationRadians * (float)Math.Sin(rotationState * rotationSpeedRadians)
196  + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
197  case AnimationType.Noise:
198  rotationState %= 1.0f / absRotationSpeedRadians;
199  return
200  rotationRadians * (PerlinNoise.GetPerlin(rotationState * absRotationSpeedRadians, rotationState * absRotationSpeedRadians) - 0.5f)
201  + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
202  default:
203  return
204  rotationRadians +
205  rotationState * rotationSpeedRadians
206  + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
207  }
208  }
209 
210  public float GetScale(float randomScaleModifier)
211  {
212  if (RandomScale == Vector2.Zero)
213  {
214  return scale;
215  }
216  return MathHelper.Lerp(RandomScale.X, RandomScale.Y, randomScaleModifier);
217  }
218 
219  public static void UpdateSpriteStates(ImmutableDictionary<int, ImmutableArray<DecorativeSprite>> spriteGroups, Dictionary<DecorativeSprite, State> animStates,
220  int entityID, float deltaTime, Func<PropertyConditional, bool> checkConditional)
221  {
222  foreach (int spriteGroup in spriteGroups.Keys)
223  {
224  for (int i = 0; i < spriteGroups[spriteGroup].Length; i++)
225  {
226  var decorativeSprite = spriteGroups[spriteGroup][i];
227  if (decorativeSprite == null) { continue; }
228  if (spriteGroup > 0)
229  {
230  int activeSpriteIndex = entityID % spriteGroups[spriteGroup].Length;
231  if (i != activeSpriteIndex)
232  {
233  animStates[decorativeSprite].IsActive = false;
234  continue;
235  }
236  }
237 
238  //check if the sprite is active (whether it should be drawn or not)
239  var spriteState = animStates[decorativeSprite];
240  spriteState.IsActive = true;
241  foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
242  {
243  if (!checkConditional(conditional))
244  {
245  spriteState.IsActive = false;
246  break;
247  }
248  }
249  if (!spriteState.IsActive) { continue; }
250  if (decorativeSprite.BlinkFrequency > 0.0f)
251  {
252  decorativeSprite.blinkTimer += deltaTime * decorativeSprite.BlinkFrequency;
253  decorativeSprite.blinkTimer %= 1.0f;
254  if (decorativeSprite.blinkTimer > 0.5f)
255  {
256  spriteState.IsActive = false;
257  continue;
258  }
259  }
260  //check if the sprite should be animated
261  bool animate = true;
262  foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
263  {
264  if (!checkConditional(conditional)) { animate = false; break; }
265  }
266  if (!animate) { continue; }
267  spriteState.OffsetState += deltaTime;
268  spriteState.RotationState += deltaTime;
269  }
270  }
271  }
272 
273  public void Remove()
274  {
275  Sprite?.Remove();
276  Sprite = null;
277  }
278  }
279 }
Vector2 GetOffset(ref float offsetState, Vector2 randomOffsetMultiplier, float rotation=0.0f)
DecorativeSprite(ContentXElement element, string path="", string file="", bool lazyLoad=false)
Dictionary< Identifier, SerializableProperty > SerializableProperties
static void UpdateSpriteStates(ImmutableDictionary< int, ImmutableArray< DecorativeSprite >> spriteGroups, Dictionary< DecorativeSprite, State > animStates, int entityID, float deltaTime, Func< PropertyConditional, bool > checkConditional)
float GetRotation(ref float rotationState, float randomRotationFactor)
int RandomGroupID
If > 0, only one sprite of the same group is used (chosen randomly)
float GetScale(float randomScaleModifier)
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)