diff --git a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
index b24d7da67..e2b9c47b5 100644
--- a/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
+++ b/Barotrauma/BarotraumaClient/BarotraumaClient.csproj
@@ -204,6 +204,7 @@
+
diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
index e180eeb9f..91ca080a3 100644
--- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
+++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs
@@ -225,13 +225,18 @@ namespace Barotrauma
}
GameMain.EditMapScreen.Select();
}));
-
+
commands.Add(new Command("editcharacter", "", (string[] args) =>
{
GameMain.EditCharacterScreen.Select();
-
}));
+ commands.Add(new Command("editparticles", "", (string[] args) =>
+ {
+ GameMain.ParticleEditorScreen.Select();
+ }));
+
+
commands.Add(new Command("control|controlcharacter", "control [character name]: Start controlling the specified character.", (string[] args) =>
{
if (args.Length < 1) return;
diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs
index d865b5101..ce95757ca 100644
--- a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs
+++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs
@@ -70,7 +70,25 @@ namespace Barotrauma
listBox.ToolTip = value;
}
}
-
+
+
+ public override Rectangle Rect
+ {
+ get
+ {
+ return base.Rect;
+ }
+
+ set
+ {
+ Point moveAmount = value.Location - rect.Location;
+ base.Rect = value;
+
+ button.Rect = new Rectangle(button.Rect.Location + moveAmount, button.Rect.Size);
+ listBox.Rect = new Rectangle(listBox.Rect.Location + moveAmount, listBox.Rect.Size);
+ }
+ }
+
public GUIDropDown(Rectangle rect, string text, string style, GUIComponent parent = null)
: this(rect, text, style, Alignment.TopLeft, parent)
{
diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs
index 9b58a6288..2fcd92b79 100644
--- a/Barotrauma/BarotraumaClient/Source/GameMain.cs
+++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs
@@ -29,6 +29,7 @@ namespace Barotrauma
public static EditMapScreen EditMapScreen;
public static EditCharacterScreen EditCharacterScreen;
+ public static ParticleEditorScreen ParticleEditorScreen;
public static Lights.LightManager LightManager;
@@ -279,13 +280,14 @@ namespace Barotrauma
TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running;
- MainMenuScreen = new MainMenuScreen(this);
- LobbyScreen = new LobbyScreen();
+ MainMenuScreen = new MainMenuScreen(this);
+ LobbyScreen = new LobbyScreen();
- ServerListScreen = new ServerListScreen();
+ ServerListScreen = new ServerListScreen();
- EditMapScreen = new EditMapScreen();
- EditCharacterScreen = new EditCharacterScreen();
+ EditMapScreen = new EditMapScreen();
+ EditCharacterScreen = new EditCharacterScreen();
+ ParticleEditorScreen = new ParticleEditorScreen();
yield return CoroutineStatus.Running;
diff --git a/Barotrauma/BarotraumaClient/Source/Particles/ParticleManager.cs b/Barotrauma/BarotraumaClient/Source/Particles/ParticleManager.cs
index c597ed299..882c022c0 100644
--- a/Barotrauma/BarotraumaClient/Source/Particles/ParticleManager.cs
+++ b/Barotrauma/BarotraumaClient/Source/Particles/ParticleManager.cs
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Particles
@@ -92,6 +93,11 @@ namespace Barotrauma.Particles
return particles[particleCount - 1];
}
+ public List GetPrefabList()
+ {
+ return prefabs.Values.ToList();
+ }
+
public ParticlePrefab FindPrefab(string prefabName)
{
ParticlePrefab prefab;
diff --git a/Barotrauma/BarotraumaClient/Source/Particles/ParticlePrefab.cs b/Barotrauma/BarotraumaClient/Source/Particles/ParticlePrefab.cs
index afdbdd341..6d9cf98aa 100644
--- a/Barotrauma/BarotraumaClient/Source/Particles/ParticlePrefab.cs
+++ b/Barotrauma/BarotraumaClient/Source/Particles/ParticlePrefab.cs
@@ -5,20 +5,27 @@ using System.Xml.Linq;
namespace Barotrauma.Particles
{
- class ParticlePrefab
+ class ParticlePrefab : ISerializableEntity
{
public enum DrawTargetType { Air = 1, Water = 2, Both = 3 }
- public readonly string Name;
-
public readonly List Sprites;
+ public string Name
+ {
+ get;
+ private set;
+ }
+
+ [Editable(0.0f, float.MaxValue, ToolTip = "How many seconds the particle remains alive."), Serialize(5.0f, false)]
+ public float LifeTime { get; private set; }
+
//movement -----------------------------------------
private float angularVelocityMin;
public float AngularVelocityMinRad { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable, Serialize(0.0f, false)]
public float AngularVelocityMin
{
get { return angularVelocityMin; }
@@ -32,7 +39,7 @@ namespace Barotrauma.Particles
private float angularVelocityMax;
public float AngularVelocityMaxRad { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable, Serialize(0.0f, false)]
public float AngularVelocityMax
{
get { return angularVelocityMax; }
@@ -46,7 +53,7 @@ namespace Barotrauma.Particles
private float startRotationMin;
public float StartRotationMinRad { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable(ToolTip = "The minimum initial rotation of the particle (in degrees)."), Serialize(0.0f, false)]
public float StartRotationMin
{
get { return startRotationMin; }
@@ -60,7 +67,7 @@ namespace Barotrauma.Particles
private float startRotationMax;
public float StartRotationMaxRad { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable(ToolTip = "The maximum initial rotation of the particle (in degrees)."), Serialize(0.0f, false)]
public float StartRotationMax
{
get { return startRotationMax; }
@@ -71,19 +78,19 @@ namespace Barotrauma.Particles
}
}
- [Serialize(false, false)]
+ [Editable(ToolTip = "Should the particle face the direction it's moving towards."), Serialize(false, false)]
public bool RotateToDirection { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable(ToolTip = "Drag applied to the particle when it's moving through air."), Serialize(0.0f, false)]
public float Drag { get; private set; }
- [Serialize(0.0f, false)]
+ [Editable(ToolTip = "Drag applied to the particle when it's moving through water."), Serialize(0.0f, false)]
public float WaterDrag { get; private set; }
private Vector2 velocityChange;
public Vector2 VelocityChangeDisplay { get; private set; }
- [Serialize("0.0,0.0", false)]
+ [Editable(ToolTip = "How much the velocity of the particle changes per second."), Serialize("0.0,0.0", false)]
public Vector2 VelocityChange
{
get { return velocityChange; }
@@ -94,67 +101,74 @@ namespace Barotrauma.Particles
}
}
- [Serialize(0.0f, false)]
+ [Editable(0.0f, 10000.0f, ToolTip = "Drag applied to the particle when it's moving through water."), Serialize(0.0f, false)]
public float CollisionRadius { get; private set; }
- [Serialize(false, false)]
- public bool DeleteOnCollision { get; private set; }
-
- [Serialize(false, false)]
+ [Editable(ToolTip = "Does the particle collide with the walls of the submarine and the level."), Serialize(false, false)]
public bool CollidesWithWalls { get; private set; }
- [Serialize(0.5f, false)]
+ [Editable(ToolTip = "Does the particle disappear when it collides with something."), Serialize(false, false)]
+ public bool DeleteOnCollision { get; private set; }
+
+ [Editable(0.0f, 1.0f, ToolTip = "The friction coefficient of the particle, i.e. how much it slows down when it's sliding against a surface."), Serialize(0.5f, false)]
public float Friction { get; private set; }
+ [Editable(0.0f, 1.0f, ToolTip = "How much of the particle's velocity is conserved when it collides with something, i.e. the \"bounciness\" of the particle. (1.0 = the particle stops completely).")]
[Serialize(0.5f, false)]
public float Restitution { get; private set; }
//size -----------------------------------------
- [Serialize("1.0,1.0", false)]
+ [Editable(ToolTip = "The minimum initial size of the particle."), Serialize("1.0,1.0", false)]
public Vector2 StartSizeMin { get; private set; }
- [Serialize("1.0,1.0", false)]
+ [Editable(ToolTip = "The maximum initial size of the particle."), Serialize("1.0,1.0", false)]
public Vector2 StartSizeMax { get; private set; }
+ [Editable(ToolTip = "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Serialize("0.0,0.0", false)]
public Vector2 SizeChangeMin { get; private set; }
+ [Editable(ToolTip = "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Serialize("0.0,0.0", false)]
public Vector2 SizeChangeMax { get; private set; }
+ [Editable(ToolTip = "How many seconds it takes for the particle to grow to it's initial size.")]
[Serialize(0.0f, false)]
public float GrowTime { get; private set; }
//rendering -----------------------------------------
- [Serialize("1.0,1.0,1.0,1.0", false)]
+ [Editable(ToolTip = "The initial color of the particle"), Serialize("1.0,1.0,1.0,1.0", false)]
public Color StartColor { get; private set; }
- [Serialize(1.0f, false)]
+ [Editable(ToolTip = "The initial alpha value of the particle"), Serialize(1.0f, false)]
public float StartAlpha { get; private set; }
- [Serialize("0.0,0.0,0.0,0.0", false)]
+ [Editable(ToolTip = "How much the color of the particle changes per second."), Serialize("0.0,0.0,0.0,0.0", false)]
public Vector4 ColorChange { get; private set; }
- [Serialize(DrawTargetType.Air, false)]
+ [Editable(ToolTip = "Should the particle be rendered in air, water or both."), Serialize(DrawTargetType.Air, false)]
public DrawTargetType DrawTarget { get; private set; }
- [Serialize(ParticleBlendState.AlphaBlend, false)]
+ [Editable(ToolTip = "The type of blending to use when rendering the particle."), Serialize(ParticleBlendState.AlphaBlend, false)]
public ParticleBlendState BlendState { get; private set; }
//animation -----------------------------------------
- [Serialize(1.0f, false)]
+ [Editable(0.0f, float.MaxValue, ToolTip = "The duration of the particle's animation cycle (if it's animated)."), Serialize(1.0f, false)]
public float AnimDuration { get; private set; }
- [Serialize(true, false)]
+ [Editable(ToolTip = "Should the sprite animation be looped, or stay at the last frame when the animation finishes."), Serialize(true, false)]
public bool LoopAnim { get; private set; }
- //misc -----------------------------------------
+ //----------------------------------------------------
- [Serialize(5.0f, false)]
- public float LifeTime { get; private set; }
+ public Dictionary SerializableProperties
+ {
+ get;
+ private set;
+ }
//----------------------------------------------------
@@ -164,7 +178,7 @@ namespace Barotrauma.Particles
Sprites = new List();
- SerializableProperty.DeserializeProperties(this, element);
+ SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
foreach (XElement subElement in element.Elements())
{
diff --git a/Barotrauma/BarotraumaClient/Source/Screens/ParticleEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/ParticleEditorScreen.cs
new file mode 100644
index 000000000..b41c74f11
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/Source/Screens/ParticleEditorScreen.cs
@@ -0,0 +1,129 @@
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+using Barotrauma.Particles;
+
+namespace Barotrauma
+{
+ class ParticleEditorScreen : Screen
+ {
+ private GUIComponent rightPanel, leftPanel;
+
+ private GUIListBox prefabList;
+
+ private ParticlePrefab selectedPrefab;
+
+ private SerializableEntityEditor particlePrefabEditor;
+
+ private Camera cam;
+
+ public override Camera Cam
+ {
+ get
+ {
+ return cam;
+ }
+ }
+
+ public ParticleEditorScreen()
+ {
+ cam = new Camera();
+
+ leftPanel = new GUIFrame(new Rectangle(0, 0, 150, GameMain.GraphicsHeight), "GUIFrameLeft");
+ leftPanel.Padding = new Vector4(10.0f, 20.0f, 10.0f, 20.0f);
+
+ rightPanel = new GUIFrame(new Rectangle(0, 0, 450, GameMain.GraphicsHeight), null, Alignment.Right, "GUIFrameRight");
+ rightPanel.Padding = new Vector4(10.0f, 20.0f, 0.0f, 20.0f);
+
+ var listBox = new GUIListBox(new Rectangle(0,0,0,0), "", rightPanel);
+
+ prefabList = new GUIListBox(new Rectangle(0, 50, 0, 0), "", leftPanel);
+ prefabList.OnSelected += (GUIComponent component, object obj) =>
+ {
+ selectedPrefab = obj as ParticlePrefab;
+ listBox.ClearChildren();
+ particlePrefabEditor = new SerializableEntityEditor(selectedPrefab, false, listBox, true);
+ return true;
+ };
+ }
+
+ public override void Select()
+ {
+ base.Select();
+ RefreshPrefabList();
+ }
+
+ private void RefreshPrefabList()
+ {
+ prefabList.ClearChildren();
+
+ var particlePrefabs = GameMain.ParticleManager.GetPrefabList();
+ foreach (ParticlePrefab particlePrefab in particlePrefabs)
+ {
+ var prefabText = new GUITextBlock(new Rectangle(0, 0, 0, 20), particlePrefab.Name, "", prefabList);
+ prefabText.UserData = particlePrefab;
+ }
+ }
+
+ public override void AddToGUIUpdateList()
+ {
+ leftPanel.AddToGUIUpdateList();
+ rightPanel.AddToGUIUpdateList();
+ }
+
+ public override void Update(double deltaTime)
+ {
+ cam.MoveCamera((float)deltaTime);
+
+ leftPanel.Update((float)deltaTime);
+ rightPanel.Update((float)deltaTime);
+
+ if (selectedPrefab != null)
+ {
+ GameMain.ParticleManager.CreateParticle(selectedPrefab, cam.WorldViewCenter, Vector2.UnitY * 100.0f);
+ }
+
+ GameMain.ParticleManager.Update((float)deltaTime);
+ }
+
+ public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
+ {
+ cam.UpdateTransform();
+ GameMain.ParticleManager.UpdateTransforms();
+
+ //-------------------------------------------------------
+
+ spriteBatch.Begin(SpriteSortMode.BackToFront,
+ BlendState.AlphaBlend,
+ null, null, null, null,
+ cam.Transform);
+
+ graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f));
+
+ GameMain.ParticleManager.Draw(spriteBatch, false, false, ParticleBlendState.AlphaBlend);
+ GameMain.ParticleManager.Draw(spriteBatch, true, false, ParticleBlendState.AlphaBlend);
+
+ spriteBatch.End();
+
+ spriteBatch.Begin(SpriteSortMode.BackToFront,
+ BlendState.Additive,
+ null, null, null, null,
+ cam.Transform);
+
+ GameMain.ParticleManager.Draw(spriteBatch, false, false, ParticleBlendState.Additive);
+ GameMain.ParticleManager.Draw(spriteBatch, true, false, ParticleBlendState.Additive);
+
+ spriteBatch.End();
+
+ //-------------------------------------------------------
+
+ spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
+
+ leftPanel.Draw(spriteBatch);
+ rightPanel.Draw(spriteBatch);
+
+ GUI.Draw((float)deltaTime, spriteBatch, cam);
+
+ spriteBatch.End();
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs b/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs
index a2d08c22b..180b77228 100644
--- a/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs
+++ b/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs
@@ -95,6 +95,11 @@ namespace Barotrauma
{
SetDimensions(new Point(Rect.Width, 0), false);
}
+
+ if (parent is GUIListBox)
+ {
+ ((GUIListBox)parent).UpdateScrollBarSize();
+ }
}
public void AddCustomContent(GUIComponent component, int childIndex)