1 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
22 public string Name => $
"Decorative Sprite";
38 private float blinkTimer = 0.0f;
41 public Vector2
Offset {
get;
private set; }
61 private float rotationSpeedRadians;
62 private float absRotationSpeedRadians;
69 return MathHelper.ToDegrees(rotationSpeedRadians);
73 rotationSpeedRadians = MathHelper.ToRadians(value);
74 absRotationSpeedRadians = Math.Abs(rotationSpeedRadians);
78 private float rotationRadians;
84 return MathHelper.ToDegrees(rotationRadians);
88 rotationRadians = MathHelper.ToRadians(value);
92 private Vector2 randomRotationRadians;
98 return new Vector2(MathHelper.ToDegrees(randomRotationRadians.X), MathHelper.ToDegrees(randomRotationRadians.Y));
102 randomRotationRadians =
new Vector2(MathHelper.ToRadians(value.X), MathHelper.ToRadians(value.Y));
110 get {
return scale; }
111 private set { scale = MathHelper.Clamp(value, 0.0f, 10.0f); }
131 public Color Color {
get;
set; }
136 internal List<PropertyConditional> IsActiveConditionals {
get;
private set; } =
new List<PropertyConditional>();
140 internal List<PropertyConditional> AnimationConditionals {
get;
private set; } =
new List<PropertyConditional>();
144 Sprite =
new Sprite(element, path, file, lazyLoad: lazyLoad);
147 foreach (var subElement
in element.Elements())
150 List<PropertyConditional> conditionalList;
151 switch (subElement.Name.ToString().ToLowerInvariant())
154 case "isactiveconditional":
155 conditionalList = IsActiveConditionals;
157 case "animationconditional":
158 conditionalList = AnimationConditionals;
167 public Vector2
GetOffset(ref
float offsetState, Vector2 randomOffsetMultiplier,
float rotation = 0.0f)
187 offset +=
new Vector2(
190 if (Math.Abs(rotation) > 0.01f)
192 Matrix transform = Matrix.CreateRotationZ(rotation);
193 offset = Vector2.Transform(offset, transform);
198 public float GetRotation(ref
float rotationState,
float randomRotationFactor)
203 rotationState %= MathHelper.TwoPi / absRotationSpeedRadians;
205 rotationRadians * MathF.Sin(rotationState * rotationSpeedRadians)
206 + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
208 rotationState %= 1.0f / absRotationSpeedRadians;
210 rotationRadians * (PerlinNoise.GetPerlin(rotationState * absRotationSpeedRadians, rotationState * absRotationSpeedRadians) - 0.5f)
211 + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
215 rotationState * rotationSpeedRadians
216 + MathHelper.Lerp(randomRotationRadians.X, randomRotationRadians.Y, randomRotationFactor);
220 public Vector2
GetScale(ref
float scaleState,
float randomScaleModifier)
222 Vector2 currentScale = Vector2.One *
240 private static Vector2 GetNoiseVector(ref
float state,
float speed)
244 float modifiedSpeed = speed * 0.1f;
246 state %= 1.0f / modifiedSpeed;
247 float t = state * modifiedSpeed;
248 Vector2 noiseValue =
new Vector2(
249 PerlinNoise.GetPerlin(t, t),
251 PerlinNoise.GetPerlin(t + 0.5f, t + 0.5f));
253 return noiseValue -
new Vector2(0.5f, 0.5f);
256 public static void UpdateSpriteStates(ImmutableDictionary<
int, ImmutableArray<DecorativeSprite>> spriteGroups, Dictionary<DecorativeSprite, State> animStates,
257 int entityID,
float deltaTime, Func<PropertyConditional, bool> checkConditional)
259 foreach (
int spriteGroup
in spriteGroups.Keys)
261 for (
int i = 0; i < spriteGroups[spriteGroup].Length; i++)
263 var decorativeSprite = spriteGroups[spriteGroup][i];
264 if (decorativeSprite ==
null) {
continue; }
267 int activeSpriteIndex = entityID % spriteGroups[spriteGroup].Length;
268 if (i != activeSpriteIndex)
270 animStates[decorativeSprite].IsActive =
false;
276 var spriteState = animStates[decorativeSprite];
277 spriteState.IsActive =
true;
280 if (!checkConditional(conditional))
282 spriteState.IsActive =
false;
286 if (!spriteState.IsActive) {
continue; }
287 if (decorativeSprite.BlinkFrequency > 0.0f)
289 decorativeSprite.blinkTimer += deltaTime * decorativeSprite.BlinkFrequency;
290 decorativeSprite.blinkTimer %= 1.0f;
291 if (decorativeSprite.blinkTimer > 0.5f)
293 spriteState.IsActive =
false;
301 if (!checkConditional(conditional)) { animate =
false;
break; }
303 if (!animate) {
continue; }
304 spriteState.ScaleState += deltaTime;
305 spriteState.OffsetState += deltaTime;
306 spriteState.RotationState += deltaTime;
float RandomRotationFactor
Vector2 RandomOffsetMultiplier
AnimationType RotationAnim
Vector2 GetOffset(ref float offsetState, Vector2 randomOffsetMultiplier, float rotation=0.0f)
Vector2 GetScale(ref float scaleState, float randomScaleModifier)
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)
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)