using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; using Barotrauma.Particles; using System; using System.Collections.Generic; using System.Xml.Linq; using System.Text; using Barotrauma.Extensions; #if DEBUG using System.IO; using System.Xml; #else using Barotrauma.IO; #endif namespace Barotrauma { class ParticleEditorScreen : Screen { class Emitter : ISerializableEntity { public float EmitTimer; public float BurstTimer; [Editable(), Serialize("0.0,360.0", false)] public Vector2 AngleRange { get; private set; } [Editable(), Serialize("0.0,0.0", false)] public Vector2 VelocityRange { get; private set; } [Editable(), Serialize("1.0,1.0", false)] public Vector2 ScaleRange { get; private set; } [Editable(), Serialize(0, false)] public int ParticleBurstAmount { get; private set; } [Editable(), Serialize(1.0f, false)] public float ParticleBurstInterval { get; private set; } [Editable(), Serialize(1.0f, false)] public float ParticlesPerSecond { get; private set; } public string Name { get { return TextManager.Get("particleeditor.emitter"); } } public Dictionary SerializableProperties { get; private set; } public Emitter() { ScaleRange = Vector2.One; AngleRange = new Vector2(0.0f, 360.0f); ParticleBurstAmount = 1; ParticleBurstInterval = 1.0f; SerializableProperties = SerializableProperty.GetProperties(this); } } private GUIComponent rightPanel, leftPanel; private GUIListBox prefabList; private GUITextBox filterBox; private GUITextBlock filterLabel; private ParticlePrefab selectedPrefab; private Emitter emitter; private readonly Camera cam; public override Camera Cam { get { return cam; } } public ParticleEditorScreen() { cam = new Camera(); GameMain.Instance.OnResolutionChanged += CreateUI; CreateUI(); } private void CreateUI() { Frame.ClearChildren(); leftPanel = new GUIFrame(new RectTransform(new Vector2(0.125f, 1.0f), Frame.RectTransform) { MinSize = new Point(150, 0) }, style: "GUIFrameLeft"); var paddedLeftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), leftPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.02f, 0.0f) }) { RelativeSpacing = 0.01f, Stretch = true }; rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), Frame.RectTransform, Anchor.TopRight) { MinSize = new Point(350, 0) }, style: "GUIFrameRight"); var paddedRightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), rightPanel.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.02f, 0.0f) }) { RelativeSpacing = 0.01f, Stretch = true }; var saveAllButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform), TextManager.Get("editor.saveall")) { OnClicked = (btn, obj) => { SerializeAll(); return true; } }; var serializeToClipBoardButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform), TextManager.Get("editor.copytoclipboard")) { OnClicked = (btn, obj) => { SerializeToClipboard(selectedPrefab); return true; } }; emitter = new Emitter(); var emitterEditorContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform), style: null); var emitterEditor = new SerializableEntityEditor(emitterEditorContainer.RectTransform, emitter, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont); emitterEditor.RectTransform.RelativeSize = Vector2.One; emitterEditorContainer.RectTransform.Resize(new Point(emitterEditorContainer.RectTransform.NonScaledSize.X, emitterEditor.ContentHeight), false); var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), paddedRightPanel.RectTransform)); var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true) { Stretch = true, UserData = "filterarea" }; filterLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font) { IgnoreLayoutGroups = true }; filterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUI.Font); filterBox.OnTextChanged += (textBox, text) => { FilterEmitters(text); return true; }; new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUICancelButton") { OnClicked = (btn, userdata) => { FilterEmitters(""); filterBox.Text = ""; filterBox.Flash(Color.White); return true; } }; prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform)); prefabList.OnSelected += (GUIComponent component, object obj) => { selectedPrefab = obj as ParticlePrefab; listBox.ClearChildren(); new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont); //listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize; //listBox.UpdateScrollBarSize(); return true; }; if (GameMain.ParticleManager != null) { RefreshPrefabList(); } } public override void Select() { base.Select(); GameMain.ParticleManager.Camera = cam; RefreshPrefabList(); } public override void Deselect() { base.Deselect(); GameMain.ParticleManager.Camera = GameMain.GameScreen.Cam; filterBox.Text = ""; } private void RefreshPrefabList() { prefabList.ClearChildren(); var particlePrefabs = GameMain.ParticleManager.GetPrefabList(); foreach (ParticlePrefab particlePrefab in particlePrefabs) { var prefabText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), prefabList.Content.RectTransform) { MinSize = new Point(0, 20) }, particlePrefab.DisplayName) { Padding = Vector4.Zero, UserData = particlePrefab }; } } private void Emit(Vector2 position) { float angle = MathHelper.ToRadians(Rand.Range(emitter.AngleRange.X, emitter.AngleRange.Y)); Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(emitter.VelocityRange.X, emitter.VelocityRange.Y); var particle = GameMain.ParticleManager.CreateParticle(selectedPrefab, position, velocity, 0.0f); if (particle != null) { particle.Size *= Rand.Range(emitter.ScaleRange.X, emitter.ScaleRange.Y); } } private void FilterEmitters(string text) { if (string.IsNullOrWhiteSpace(text)) { filterLabel.Visible = true; prefabList.Content.Children.ForEach(c => c.Visible = true); return; } text = text.ToLower(); filterLabel.Visible = false; foreach (GUIComponent child in prefabList.Content.Children) { if (!(child is GUITextBlock textBlock)) { continue; } textBlock.Visible = textBlock.Text.ToLower().Contains(text); } } private void SerializeAll() { foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.Particles)) { XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); if (doc == null) { continue; } var prefabList = GameMain.ParticleManager.GetPrefabList(); foreach (ParticlePrefab prefab in prefabList) { foreach (XElement element in doc.Root.Elements()) { if (!element.Name.ToString().Equals(prefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; } SerializableProperty.SerializeProperties(prefab, element, true); } } System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, NewLineOnAttributes = true }; using (var writer = XmlWriter.Create(configFile.Path, settings)) { doc.WriteTo(writer); writer.Flush(); } } } private void SerializeToClipboard(ParticlePrefab prefab) { #if WINDOWS if (prefab == null) { return; } System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, NewLineOnAttributes = true }; XElement originalElement = null; foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.Particles)) { XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); if (doc == null) { continue; } var prefabList = GameMain.ParticleManager.GetPrefabList(); foreach (ParticlePrefab otherPrefab in prefabList) { foreach (XElement subElement in doc.Root.Elements()) { if (!subElement.Name.ToString().Equals(prefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; } SerializableProperty.SerializeProperties(prefab, subElement, true); originalElement = subElement; break; } } } if (originalElement == null) { originalElement = new XElement(prefab.Name); SerializableProperty.SerializeProperties(prefab, originalElement, true); } StringBuilder sb = new StringBuilder(); using (var writer = System.Xml.XmlWriter.Create(sb, settings)) { originalElement.WriteTo(writer); writer.Flush(); } Clipboard.SetText(sb.ToString()); #endif } public override void Update(double deltaTime) { cam.MoveCamera((float)deltaTime, true); if (selectedPrefab != null) { emitter.EmitTimer += (float)deltaTime; emitter.BurstTimer += (float)deltaTime; if (emitter.ParticlesPerSecond > 0) { float emitInterval = 1.0f / emitter.ParticlesPerSecond; while (emitter.EmitTimer > emitInterval) { Emit(Vector2.Zero); emitter.EmitTimer -= emitInterval; } } if (emitter.BurstTimer > emitter.ParticleBurstInterval) { for (int i = 0; i < emitter.ParticleBurstAmount; i++) { Emit(Vector2.Zero); } emitter.BurstTimer = 0.0f; } } GameMain.ParticleManager.Update((float)deltaTime); } public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) { cam.UpdateTransform(); GameMain.ParticleManager.UpdateTransforms(); //------------------------------------------------------- spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, 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.Deferred, 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.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable); GUI.Draw(Cam, spriteBatch); spriteBatch.End(); } } }