- The range and volume of sounds emitted by StatusEffects can be changed and they can be set to loop.

- StatusEffect sounds are configured as child elements of the StatusEffect (instead of attributes).
- Background sprites can emit sounds.
This commit is contained in:
Joonas Rikkonen
2017-08-24 19:56:31 +03:00
parent 7186a8010a
commit 4bdbf05875
23 changed files with 218 additions and 112 deletions
@@ -0,0 +1,235 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
class BackgroundCreature : ISteerable
{
const float MaxDepth = 100.0f;
const float CheckWallsInterval = 5.0f;
public bool Enabled;
private BackgroundCreaturePrefab prefab;
private Vector2 position;
private Vector3 velocity;
private float depth;
private SteeringManager steeringManager;
private float checkWallsTimer;
public Swarm Swarm;
Vector2 drawPosition;
public Vector2 TransformedPosition
{
get { return drawPosition; }
}
public Vector2 SimPosition
{
get { return FarseerPhysics.ConvertUnits.ToSimUnits(position); }
}
public Vector2 WorldPosition
{
get { return position; }
}
public Vector2 Velocity
{
get { return new Vector2(velocity.X, velocity.Y); }
}
public Vector2 Steering
{
get;
set;
}
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
{
this.prefab = prefab;
this.position = position;
drawPosition = position;
steeringManager = new SteeringManager(this);
velocity = new Vector3(
Rand.Range(-prefab.Speed, prefab.Speed),
Rand.Range(-prefab.Speed, prefab.Speed),
Rand.Range(0.0f, prefab.WanderZAmount));
checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval);
}
float ang;
Vector2 obstacleDiff;
public void Update(float deltaTime)
{
position += new Vector2(velocity.X, velocity.Y) * deltaTime;
depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, 0.0f, MaxDepth);
checkWallsTimer -= deltaTime;
if (checkWallsTimer <= 0.0f && Level.Loaded != null)
{
checkWallsTimer = CheckWallsInterval;
obstacleDiff = Vector2.Zero;
if (position.Y > Level.Loaded.Size.Y)
{
obstacleDiff = Vector2.UnitY;
}
else if (position.Y < 0.0f)
{
obstacleDiff = -Vector2.UnitY;
}
else if (position.X < 0.0f)
{
obstacleDiff = Vector2.UnitX;
}
else if (position.X > Level.Loaded.Size.X)
{
obstacleDiff = -Vector2.UnitX;
}
else
{
var cells = Level.Loaded.GetCells(position, 1);
if (cells.Count > 0)
{
foreach (Voronoi2.VoronoiCell cell in cells)
{
obstacleDiff += cell.Center - position;
}
obstacleDiff /= cells.Count;
obstacleDiff = Vector2.Normalize(obstacleDiff) * prefab.Speed;
}
}
}
if (Swarm!=null)
{
Vector2 midPoint = Swarm.MidPoint();
float midPointDist = Vector2.Distance(SimPosition, midPoint);
if (midPointDist > Swarm.MaxDistance)
{
steeringManager.SteeringSeek(midPoint, (midPointDist / Swarm.MaxDistance) * prefab.Speed);
}
}
if (prefab.WanderAmount > 0.0f)
{
steeringManager.SteeringWander(prefab.Speed);
}
//Level.Loaded.Size
if (obstacleDiff != Vector2.Zero)
{
steeringManager.SteeringSeek(SimPosition-obstacleDiff, prefab.Speed*2.0f);
}
steeringManager.Update(prefab.Speed);
if (prefab.WanderZAmount>0.0f)
{
ang += Rand.Range(-prefab.WanderZAmount, prefab.WanderZAmount);
velocity.Z = (float)Math.Sin(ang)*prefab.Speed;
}
velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime);
}
public void Draw(SpriteBatch spriteBatch)
{
float rotation = 0.0f;
if (!prefab.DisableRotation)
{
rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
if (velocity.X < 0.0f) rotation -= MathHelper.Pi;
}
drawPosition = position;// +Level.Loaded.Position;
if (depth > 0.0f)
{
Vector2 camOffset = drawPosition - GameMain.GameScreen.Cam.WorldViewCenter;
drawPosition -= camOffset * (depth / MaxDepth) * 0.05f;
}
prefab.Sprite.Draw(spriteBatch, new Vector2(drawPosition.X, -drawPosition.Y), Color.Lerp(Color.White, Color.DarkBlue, (depth/MaxDepth)*0.3f),
rotation, 1.0f - (depth / MaxDepth) * 0.2f, velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally, (depth / MaxDepth));
}
}
class Swarm
{
public List<BackgroundCreature> Members;
public readonly float MaxDistance;
public Vector2 MidPoint()
{
if (Members.Count == 0) return Vector2.Zero;
Vector2 midPoint = Vector2.Zero;
foreach (BackgroundCreature member in Members)
{
midPoint += member.SimPosition;
}
midPoint /= Members.Count;
return midPoint;
}
public Vector2 AvgVelocity()
{
if (Members.Count == 0) return Vector2.Zero;
Vector2 avgVel = Vector2.Zero;
foreach (BackgroundCreature member in Members)
{
avgVel += member.Velocity;
}
avgVel /= Members.Count;
return avgVel;
}
public Swarm(List<BackgroundCreature> members, float maxDistance)
{
this.Members = members;
this.MaxDistance = maxDistance;
foreach (BackgroundCreature bgSprite in members)
{
bgSprite.Swarm = this;
}
}
}
}
@@ -0,0 +1,138 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class BackgroundCreatureManager
{
const int MaxSprites = 100;
const float CheckActiveInterval = 1.0f;
private float checkActiveTimer;
private List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>();
private List<BackgroundCreature> activeSprites = new List<BackgroundCreature>();
public BackgroundCreatureManager(string configPath)
{
LoadConfig(configPath);
}
public BackgroundCreatureManager(List<string> files)
{
foreach(var file in files)
{
LoadConfig(file);
}
}
private void LoadConfig(string configPath)
{
try
{
XDocument doc = ToolBox.TryLoadXml(configPath);
if (doc == null || doc.Root == null) return;
foreach (XElement element in doc.Root.Elements())
{
prefabs.Add(new BackgroundCreaturePrefab(element));
};
}
catch (Exception e)
{
DebugConsole.ThrowError(String.Format("Failed to load BackgroundCreatures from {0}", configPath), e);
}
}
public void SpawnSprites(int count, Vector2? position = null)
{
activeSprites.Clear();
if (prefabs.Count == 0) return;
count = Math.Min(count, MaxSprites);
for (int i = 0; i < count; i++ )
{
Vector2 pos = Vector2.Zero;
if (position == null)
{
var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine==null);
if (wayPoints.Any())
{
WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)];
pos = new Vector2(wp.Rect.X, wp.Rect.Y);
pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly);
}
else
{
pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly);
}
}
else
{
pos = (Vector2)position;
}
var prefab = prefabs[Rand.Int(prefabs.Count, Rand.RandSync.ClientOnly)];
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++)
{
var newSprite = new BackgroundCreature(prefab, pos);
activeSprites.Add(newSprite);
swarmMembers.Add(newSprite);
}
if (amount > 0)
{
new Swarm(swarmMembers, prefab.SwarmRadius);
}
}
}
public void ClearSprites()
{
activeSprites.Clear();
}
public void Update(float deltaTime, Camera cam)
{
if (checkActiveTimer < 0.0f)
{
foreach (BackgroundCreature sprite in activeSprites)
{
sprite.Enabled = Math.Abs(sprite.TransformedPosition.X - cam.WorldViewCenter.X) < cam.WorldView.Width &&
Math.Abs(sprite.TransformedPosition.Y - cam.WorldViewCenter.Y) < cam.WorldView.Height;
}
checkActiveTimer = CheckActiveInterval;
}
else
{
checkActiveTimer -= deltaTime;
}
foreach (BackgroundCreature sprite in activeSprites)
{
if (!sprite.Enabled) continue;
sprite.Update(deltaTime);
}
}
public void Draw(SpriteBatch spriteBatch)
{
foreach (BackgroundCreature sprite in activeSprites)
{
if (!sprite.Enabled) continue;
sprite.Draw(spriteBatch);
}
}
}
}
@@ -0,0 +1,47 @@
using System.Xml.Linq;
namespace Barotrauma
{
class BackgroundCreaturePrefab
{
public readonly Sprite Sprite;
public readonly float Speed;
public readonly float WanderAmount;
public readonly float WanderZAmount;
public readonly int SwarmMin, SwarmMax;
public readonly float SwarmRadius;
public readonly bool DisableRotation;
public BackgroundCreaturePrefab(XElement element)
{
Speed = ToolBox.GetAttributeFloat(element, "speed", 1.0f);
WanderAmount = ToolBox.GetAttributeFloat(element, "wanderamount", 0.0f);
WanderZAmount = ToolBox.GetAttributeFloat(element, "wanderzamount", 0.0f);
SwarmMin = ToolBox.GetAttributeInt(element, "swarmmin", 1);
SwarmMax = ToolBox.GetAttributeInt(element, "swarmmax", 1);
SwarmRadius = ToolBox.GetAttributeFloat(element, "swarmradius", 200.0f);
DisableRotation = ToolBox.GetAttributeBool(element, "disablerotation", false);
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "sprite") continue;
Sprite = new Sprite(subElement);
break;
}
}
}
}
@@ -0,0 +1,136 @@
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
partial class BackgroundSprite
{
public ParticleEmitter ParticleEmitter;
public Sound Sound;
}
partial class BackgroundSpriteManager
{
private List<BackgroundSprite> visibleSprites = new List<BackgroundSprite>();
private Rectangle currentGridIndices;
partial void UpdateProjSpecific(float deltaTime)
{
foreach (BackgroundSprite s in visibleSprites)
{
if (s.ParticleEmitter != null)
{
Vector2 emitterPos = s.LocalToWorld(new Vector2(s.Prefab.EmitterPosition.X, s.Prefab.EmitterPosition.Y));
s.ParticleEmitter.Emit(deltaTime, emitterPos);
}
if (s.Sound != null)
{
int sourceIndex;
Vector2 soundPos = s.LocalToWorld(new Vector2(s.Prefab.SoundPosition.X, s.Prefab.SoundPosition.Y));
if (!Sounds.SoundManager.IsPlaying(s.Sound, out sourceIndex))
{
s.Sound.Play(soundPos);
}
else
{
s.Sound.UpdatePosition(soundPos);
}
}
}
}
public void DrawSprites(SpriteBatch spriteBatch, Camera cam)
{
Rectangle indices = Rectangle.Empty;
indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize);
if (indices.X >= spriteGrid.GetLength(0)) return;
indices.Y = (int)Math.Floor((cam.WorldView.Y - cam.WorldView.Height - Level.Loaded.BottomPos) / (float)GridSize);
if (indices.Y >= spriteGrid.GetLength(1)) return;
indices.Width = (int)Math.Floor(cam.WorldView.Right / (float)GridSize)+1;
if (indices.Width < 0) return;
indices.Height = (int)Math.Floor((cam.WorldView.Y - Level.Loaded.BottomPos) / (float)GridSize)+1;
if (indices.Height < 0) return;
indices.X = Math.Max(indices.X, 0);
indices.Y = Math.Max(indices.Y, 0);
indices.Width = Math.Min(indices.Width, spriteGrid.GetLength(0)-1);
indices.Height = Math.Min(indices.Height, spriteGrid.GetLength(1)-1);
float z = 0.0f;
if (currentGridIndices != indices)
{
visibleSprites.Clear();
for (int x = indices.X; x <= indices.Width; x++)
{
for (int y = indices.Y; y <= indices.Height; y++)
{
if (spriteGrid[x, y] == null) continue;
foreach (BackgroundSprite sprite in spriteGrid[x, y])
{
int drawOrderIndex = 0;
for (int i = 0; i < visibleSprites.Count; i++)
{
if (visibleSprites[i] == sprite)
{
drawOrderIndex = -1;
break;
}
if (visibleSprites[i].Position.Z > sprite.Position.Z)
{
break;
}
else
{
drawOrderIndex = i + 1;
}
}
if (drawOrderIndex >= 0)
{
visibleSprites.Insert(drawOrderIndex, sprite);
}
}
}
}
currentGridIndices = indices;
}
foreach (BackgroundSprite sprite in visibleSprites)
{
Vector2 camDiff = new Vector2(sprite.Position.X, sprite.Position.Y) - cam.WorldViewCenter;
camDiff.Y = -camDiff.Y;
sprite.Prefab.Sprite.Draw(
spriteBatch,
new Vector2(sprite.Position.X, -sprite.Position.Y) - camDiff * sprite.Position.Z / 10000.0f,
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, sprite.Position.Z / 5000.0f),
sprite.Rotation + swingState * sprite.Prefab.SwingAmount,
sprite.Scale,
SpriteEffects.None,
z);
if (GameMain.DebugDraw)
{
GUI.DrawRectangle(spriteBatch, new Vector2(sprite.Position.X, -sprite.Position.Y), new Vector2(10.0f, 10.0f), Color.Red, true);
if (sprite.Trigger != null && sprite.Trigger.PhysicsBody != null)
{
sprite.Trigger.PhysicsBody.UpdateDrawPosition();
sprite.Trigger.PhysicsBody.DebugDraw(spriteBatch, Color.Cyan);
}
}
z += 0.0001f;
}
}
}
}
@@ -0,0 +1,14 @@
using Microsoft.Xna.Framework;
using System.Xml.Linq;
namespace Barotrauma
{
partial class BackgroundSpritePrefab
{
public readonly Particles.ParticleEmitterPrefab ParticleEmitterPrefab;
public readonly Vector2 EmitterPosition;
public readonly XElement SoundElement;
public readonly Vector2 SoundPosition;
}
}