Client LuaCsForBarotrauma
ParticleEditorScreen.cs
1 using Microsoft.Xna.Framework.Graphics;
2 using Microsoft.Xna.Framework;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Xml.Linq;
8 using System.Text;
10 using FarseerPhysics;
11 #if DEBUG
12 using System.IO;
13 using System.Xml;
14 #else
15 using Barotrauma.IO;
16 #endif
17 
18 namespace Barotrauma
19 {
21  {
22  private GUIComponent rightPanel, leftPanel;
23  private GUIListBox prefabList;
24  private GUITextBox filterBox;
25  private GUITextBlock filterLabel;
26 
27  private ParticlePrefab selectedPrefab;
28 
29  private readonly ParticleEmitterProperties emitterProperties = new ParticleEmitterProperties(null)
30  {
31  ScaleMax = 1f,
32  ScaleMin = 1f,
33  AngleMax = 360f,
34  AngleMin = 0,
35  ParticlesPerSecond = 1f
36  };
37 
38  private ParticleEmitterPrefab emitterPrefab;
39  private ParticleEmitter emitter;
40 
41  private readonly Camera cam;
42 
43  public override Camera Cam => cam;
44 
45  private const string sizeRefFilePath = "Content/size_reference.png";
46  private readonly Texture2D sizeReference;
47  private Vector2 sizeRefPosition = Vector2.Zero;
48  private readonly Vector2 sizeRefOrigin;
49  private bool sizeRefEnabled;
50 
52  {
53  cam = new Camera();
55  CreateUI();
56  if (File.Exists(sizeRefFilePath))
57  {
58  sizeReference = TextureLoader.FromFile(sizeRefFilePath, compress: false);
59  sizeRefOrigin = new Vector2(sizeReference.Width / 2f, sizeReference.Height / 2f);
60  }
61  }
62 
63  private void CreateUI()
64  {
66 
67  leftPanel = new GUIFrame(new RectTransform(new Vector2(0.125f, 1.0f), Frame.RectTransform) { MinSize = new Point(150, 0) },
68  style: "GUIFrameLeft");
69  var paddedLeftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), leftPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.02f, 0.0f) })
70  {
71  RelativeSpacing = 0.01f,
72  Stretch = true
73  };
74 
75  rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), Frame.RectTransform, Anchor.TopRight) { MinSize = new Point(350, 0) },
76  style: "GUIFrameRight");
77  var paddedRightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), rightPanel.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.02f, 0.0f) })
78  {
79  RelativeSpacing = 0.01f,
80  Stretch = true
81  };
82 
83  var saveAllButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
84  TextManager.Get("editor.saveall"))
85  {
86  OnClicked = (btn, obj) =>
87  {
88  SerializeAll();
89  return true;
90  }
91  };
92 
93  new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
94  TextManager.Get("ParticleEditor.CopyPrefabToClipboard"))
95  {
96  OnClicked = (btn, obj) =>
97  {
98  SerializeToClipboard(selectedPrefab);
99  return true;
100  }
101  };
102 
103  new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
104  TextManager.Get("ParticleEditor.CopyEmitterToClipboard"))
105  {
106  OnClicked = (btn, obj) =>
107  {
108  SerializeEmitterToClipboard();
109  return true;
110  }
111  };
112 
113  var emitterListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform));
114  new SerializableEntityEditor(emitterListBox.Content.RectTransform, emitterProperties, false, true, elementHeight: 20, titleFont: GUIStyle.SubHeadingFont);
115 
116  var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), paddedRightPanel.RectTransform));
117 
118  var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
119  {
120  Stretch = true,
121  UserData = "filterarea"
122  };
123 
124  filterLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.Font) { IgnoreLayoutGroups = true };
125  filterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUIStyle.Font);
126  filterBox.OnTextChanged += (textBox, text) => { FilterEmitters(text); return true; };
127  new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUICancelButton")
128  {
129  OnClicked = (btn, userdata) => { FilterEmitters(""); filterBox.Text = ""; filterBox.Flash(Color.White); return true; }
130  };
131 
132  prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform))
133  {
134  PlaySoundOnSelect = true,
135  };
136  prefabList.OnSelected += (GUIComponent component, object obj) =>
137  {
138  cam.Position = Vector2.Zero;
139  selectedPrefab = obj as ParticlePrefab;
140  emitterPrefab = new ParticleEmitterPrefab(selectedPrefab, emitterProperties);
141  emitter = new ParticleEmitter(emitterPrefab);
142  listBox.ClearChildren();
143  new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20, titleFont: GUIStyle.SubHeadingFont);
144  //listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize;
145  //listBox.UpdateScrollBarSize();
146  return true;
147  };
148 
149  if (GameMain.ParticleManager != null) { RefreshPrefabList(); }
150  }
151 
152  public override void Select()
153  {
154  base.Select();
156  RefreshPrefabList();
157  }
158 
159  protected override void DeselectEditorSpecific()
160  {
162  filterBox.Text = "";
163  }
164 
165  private void RefreshPrefabList()
166  {
167  prefabList.ClearChildren();
168 
169  var particlePrefabs = ParticleManager.GetPrefabList();
170  foreach (ParticlePrefab particlePrefab in particlePrefabs)
171  {
172  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), prefabList.Content.RectTransform) { MinSize = new Point(0, 20) },
173  particlePrefab.Name)
174  {
175  Padding = Vector4.Zero,
176  UserData = particlePrefab
177  };
178  }
179  }
180 
181  private void FilterEmitters(string text)
182  {
183  if (string.IsNullOrWhiteSpace(text))
184  {
185  filterLabel.Visible = true;
186  prefabList.Content.Children.ForEach(c => c.Visible = true);
187  return;
188  }
189 
190  text = text.ToLower();
191  filterLabel.Visible = false;
192  foreach (GUIComponent child in prefabList.Content.Children)
193  {
194  if (!(child is GUITextBlock textBlock)) { continue; }
195  textBlock.Visible = textBlock.Text.ToLower().Contains(text);
196  }
197  }
198 
199  private void SerializeAll()
200  {
201  Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
202  foreach (var configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles<ParticlesFile>()))
203  {
204  XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
205  if (doc == null) { continue; }
206 
207  var prefabList = ParticleManager.GetPrefabList();
208  foreach (ParticlePrefab prefab in prefabList)
209  {
210  foreach (XElement element in doc.Root.Elements())
211  {
212  if (!element.Name.ToString().Equals(prefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
213  SerializableProperty.SerializeProperties(prefab, element, true);
214  }
215  }
216 
217  System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
218  {
219  Indent = true,
220  OmitXmlDeclaration = true,
221  NewLineOnAttributes = true
222  };
223 
224  using (var writer = XmlWriter.Create(configFile.Path.Value, settings))
225  {
226  doc.WriteTo(writer);
227  writer.Flush();
228  }
229  }
230  Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
231  }
232 
233  private void SerializeEmitterToClipboard()
234  {
235  XElement element = new XElement(nameof(ParticleEmitter));
236  if (selectedPrefab is { } prefab)
237  {
238  element.Add(new XAttribute("particle", prefab.Identifier));
239  }
240 
241  SerializableProperty.SerializeProperties(emitterProperties, element, saveIfDefault: false, ignoreEditable: true);
242 
243  StringBuilder sb = new StringBuilder();
244 
245  System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
246  {
247  OmitXmlDeclaration = true
248  };
249 
250  using (var writer = System.Xml.XmlWriter.Create(sb, settings))
251  {
252  element.WriteTo(writer);
253  writer.Flush();
254  }
255 
256  Clipboard.SetText(sb.ToString());
257  }
258 
259  private void SerializeToClipboard(ParticlePrefab prefab)
260  {
261  if (prefab == null) { return; }
262 
263  System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
264  {
265  Indent = true,
266  OmitXmlDeclaration = true,
267  NewLineOnAttributes = true
268  };
269 
270  XElement originalElement = null;
271  foreach (var configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles<ParticlesFile>()))
272  {
273  XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
274  if (doc == null) { continue; }
275 
276  var prefabList = ParticleManager.GetPrefabList();
277  foreach (ParticlePrefab otherPrefab in prefabList)
278  {
279  foreach (var subElement in doc.Root.Elements())
280  {
281  if (!subElement.Name.ToString().Equals(prefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
282  SerializableProperty.SerializeProperties(prefab, subElement, true);
283  originalElement = subElement;
284  break;
285  }
286  }
287  }
288 
289  if (originalElement == null)
290  {
291  originalElement = new XElement(prefab.Name);
292  SerializableProperty.SerializeProperties(prefab, originalElement, true);
293  }
294 
295  StringBuilder sb = new StringBuilder();
296  using (var writer = System.Xml.XmlWriter.Create(sb, settings))
297  {
298  originalElement.WriteTo(writer);
299  writer.Flush();
300  }
301 
302  Clipboard.SetText(sb.ToString());
303  }
304 
305  public override void Update(double deltaTime)
306  {
307  cam.MoveCamera((float)deltaTime, allowMove: true, allowZoom: GUI.MouseOn == null);
308 
309  if (GUI.MouseOn is null && PlayerInput.PrimaryMouseButtonHeld())
310  {
311  sizeRefPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
312  }
313 
315  {
316  CreateContextMenu();
317  }
318 
319  if (selectedPrefab != null && emitter != null)
320  {
321  emitter.Emit((float) deltaTime, Vector2.Zero);
322  }
323 
324  GameMain.ParticleManager.Update((float)deltaTime);
325  }
326 
327  private void CreateContextMenu()
328  {
329  GUIContextMenu.CreateContextMenu
330  (
331  new ContextMenuOption("subeditor.editbackgroundcolor", true, CreateBackgroundColorPicker),
332  new ContextMenuOption("editor.togglereferencecharacter", true, delegate { sizeRefEnabled = !sizeRefEnabled; })
333  );
334  }
335 
336  public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
337  {
338  cam.UpdateTransform();
340 
341  //-------------------------------------------------------
342 
343  spriteBatch.Begin(SpriteSortMode.Deferred,
344  BlendState.NonPremultiplied,
345  null, null, null, null,
346  cam.Transform);
347 
348  graphics.Clear(BackgroundColor);
349 
350  GameMain.ParticleManager.Draw(spriteBatch, false, false, ParticleBlendState.AlphaBlend);
351  GameMain.ParticleManager.Draw(spriteBatch, true, false, ParticleBlendState.AlphaBlend);
352 
353  spriteBatch.End();
354 
355  spriteBatch.Begin(SpriteSortMode.Deferred,
356  BlendState.Additive,
357  null, null, null, null,
358  cam.Transform);
359 
360  GameMain.ParticleManager.Draw(spriteBatch, false, false, ParticleBlendState.Additive);
361  GameMain.ParticleManager.Draw(spriteBatch, true, false, ParticleBlendState.Additive);
362 
363  spriteBatch.End();
364 
365  if (sizeRefEnabled && !(sizeReference is null))
366  {
367  spriteBatch.Begin(SpriteSortMode.Deferred,
368  BlendState.NonPremultiplied,
369  null, null, null, null,
370  cam.Transform);
371 
372  Vector2 pos = sizeRefPosition;
373  pos.Y = -pos.Y;
374  spriteBatch.Draw(sizeReference, pos, null, Color.White, 0f, sizeRefOrigin, new Vector2(0.4f), SpriteEffects.None, 0f);
375 
376  spriteBatch.End();
377  }
378 
379  //-------------------------------------------------------
380 
381  spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
382 
383  GUI.Draw(Cam, spriteBatch);
384 
385  spriteBatch.End();
386  }
387  }
388 }
static Color BackgroundColor
Definition: EditorScreen.cs:7
void CreateBackgroundColorPicker()
Definition: EditorScreen.cs:25
virtual void ClearChildren()
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
OnSelectedHandler OnSelected
Definition: GUIListBox.cs:17
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
override void ClearChildren()
Definition: GUIListBox.cs:1243
OnTextChangedHandler OnTextChanged
Don't set the Text property on delegates that register to this event, because modifying the Text will...
Definition: GUITextBox.cs:38
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectOffset=null)
Definition: GUITextBox.cs:411
static RasterizerState ScissorTestEnable
Definition: GameMain.cs:195
Action ResolutionChanged
NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer...
Definition: GameMain.cs:133
static GameScreen GameScreen
Definition: GameMain.cs:52
static ParticleManager ParticleManager
Definition: GameMain.cs:101
static GameMain Instance
Definition: GameMain.cs:144
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:163
override void Update(double deltaTime)
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
void Emit(float deltaTime, Vector2 position, Hull? hullGuess=null, float angle=0.0f, float particleRotation=0.0f, float velocityMultiplier=1.0f, float sizeMultiplier=1.0f, float amountMultiplier=1.0f, Color? colorMultiplier=null, ParticlePrefab? overrideParticle=null, bool mirrorAngle=false, Tuple< Vector2, Vector2 >? tracerPoints=null)
static List< ParticlePrefab > GetPrefabList()
void Draw(SpriteBatch spriteBatch, bool inWater, bool? inSub, ParticleBlendState blendState)
readonly Identifier Identifier
Definition: Prefab.cs:34